package eod_functions; use strict; use warnings; use Data::Dumper; use Exporter; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); $VERSION = 1.10; @ISA = qw(Exporter); @EXPORT = (); @EXPORT_OK = qw(help sanatize taint gettime parsetempdb); %EXPORT_TAGS = ( ALL => [qw(&help &sanatize &taint &gettime &parsetempdb)], ); ###################################################################################################################### ## BEGIN FUNCTIONS ################################################################################################# ###################################################################################################################### ###################################################################################################################### ## SUB HELP ######################################################################################################## ###################################################################################################################### sub help { my $NAME = $_[0]; $NAME =~ s/\.pl//; $NAME =~ s/\.\///; print "\nSECTION-USAGE: $0 \nThis script is highly versatile, make sure you familarize yourself with it properly, before using it\n"; print "All options are case sensitive. Option/Argument handling is unix standard.\n mandatory arguments: --readfile|-r read cli commands from file and perform them on all CPEs (default = $NAME.cfg. see section config file for help) --connection-proto|-c define the protocol to use to connect to the devices. (ssh OR telnet. No default setting) optional arguments: --prompt-based-auth|-p provide username + password for device access when asked. (default = enabled) WARNING: Supercedes LOGIN details in config file you provide! --file-based-auth|-f provide username + password for device access via the config-file. (default = disabled) --outfile|-o expects filename of csv to write the output to. (default = $NAME.csv) --quiet|-q log to logfile (default = enabled) --no-quiet|-n log to STDOUT instead of logfile (mandatory if --debug is enabled, default = disabled) --debug|-d activate debug-log-level WARNING: do NOT use with multiple CPEsoutput is massive!!! (default = disabled) --help|-h print this help and exit --verbose|-v enable verbose feedback in outputfile. Depending on the executed commands this can be several hundred characters!!! (default = disabled) --Version|-V display extenensive version information and exit\n"; print " advanced optional arguments (case sensitive): --sendmail|-s expects comma separated list of e-mail addresses to send generated report to as argument (default = disabled) alternatively it can be invoked multiple times with different addresses. (First occurence will always be TO, all others CC) --max-connections|-m run script in forked mode (massively enhances performance) with the specified ammount of max child processes (1-25) (default = disabled) --tacacs|-t verify tacacs functionality on VPN-Hubsite before attempting to process devices. will abort script execution if tacacs service is unresponsive (default = disabled)\n"; print " \nSECTION-CONFIG-FILE: Keep in mind some devices handle commands case sensitive! The file that you provide will be reset to its defaults during script execution!!! open the default config file $NAME.CFG with an editor of your choice and study the instructions it contains. You may modify the default file or create a new one and pass it to the script with option -r \n"; print "Note: please report all unexpected behavior to e-mail ty\n\n"; return 1; } ###################################################################################################################### ## / SUB HELP ###################################################################################################### ###################################################################################################################### ###################################################################################################################### ## SUB SANATIZE ### DOES: substitute ctrl-chars for escaped metachars in $LINE #################################### ###################################################################################################################### sub sanatize { my ($LINE,$METACHARS,$TRANSLATIONS) = @_; for (my $I=0; $I<@$METACHARS;$I++){ $LINE =~ s/\#\Q$$METACHARS[$I]/$$TRANSLATIONS[$I]/g; ## hardcoded ESCSEQ => # } return $LINE; } ###################################################################################################################### ## / SUB SANATIZE ################################################################################################## ###################################################################################################################### ###################################################################################################################### ## SUB TAINT ### DOES: substitute ctrl-chars for unescaped metachars in $LINE #################################### ###################################################################################################################### sub taint { my ($LINE,$METACHARS,$TRANSLATIONS) = @_; for (my $I=0; $I<@$TRANSLATIONS;$I++){ $LINE =~ s/$$TRANSLATIONS[$I]/$$METACHARS[$I]/g; } return $LINE; } ###################################################################################################################### ## / SUB TAINT ##################################################################################################### ###################################################################################################################### ###################################################################################################################### ## SUB GETTIME ### DOES: returns SQL compatible TS in UK format ################################################## ###################################################################################################################### sub gettime { my @months = qw(01 02 03 04 05 06 07 08 09 10 11 12); my @weekDays = qw(Sun Mon Tue Wed Thu Fri Sat Sun); my ($second, $minute, $hour, $dayOfMonth, $month, $yearOffset, $dayOfWeek, $dayOfYear, $daylightSavings) = localtime(); my $year = 1900 + $yearOffset; my $T_GER = "$hour:$minute:$second, $weekDays[$dayOfWeek] $dayOfMonth/$months[$month]/$year"; my $T_UK = "$weekDays[$dayOfWeek] $months[$month]/$dayOfMonth/$year $hour:$minute:$second"; return $T_UK; } ###################################################################################################################### ## / SUB GETTIME ################################################################################################### ###################################################################################################################### ###################################################################################################################### ## SUB PARSETEMPDB ### DOES: parses tempdb and generates humanly readible REPORT in %REPORT ###################### ###################################################################################################################### sub parsetempdb { my ($REPORT,$CONFIG) = @_; my $STRING; open(my $CSV, ">$$CONFIG{OUTCSV}") || die("$0::PARSETEMPDB:ERROR Unable to open $$CONFIG{OUTCSV} for writing!\n"); my $TH = "DATE;IP"; open(my $DB, "<$$CONFIG{TEMPDB}") || die("$0::PARSETEMPDB:ERROR Unable to open $$CONFIG{TEMPDB}!\n"); my @LINES; $LINES[0]="TH"; my ($COUNT,$COUNTREF)=(0,0); while (<$DB>){ $_ =~ s/\|\|\|//; my @TEMP = split(/\|\|/, $_); $COUNT = @TEMP; if ($COUNT > $COUNTREF){$COUNTREF = $COUNT;} if ($_ =~ /h00kme/i ){ $_ =~ s/h00kme/IP is unreachable/; push(@LINES, $_); $$REPORT{UNREACHABLE}++; next; } if ($_ =~ /noble/i ){ $_ =~ s/noble/Changing into privileged mode failed/; push(@LINES, $_); $$REPORT{ERROR}++; next; } $STRING = ""; for (my $C=0; $C <= @TEMP; $C++){ unless(! defined($TEMP[$C])){ $STRING .= "$TEMP[$C];";} } $STRING =~ s/\s+/ /g; push(@LINES, $STRING); if ( $STRING =~ /NOK/ ) { $$REPORT{NOK}++; next; } if ( $STRING =~ /OK/ ) { $$REPORT{OK}++; next; } if ( $STRING =~ /ERROR/ ) { $$REPORT{ERROR}++; } } $TH = "DATE;IP"; for ( my $C = 2; $C <= $COUNTREF; $C++ ){ $TH .= ";COMMAND;RESULT;DETAILS"; if($$CONFIG{VERBOSE} == 1){$TH .= ";VERBOSE FEEDBACK"; $C +=1;} $C += 2; } $LINES[0]=$TH; foreach (@LINES){ print $CSV "$_\n"; } close($DB); close($CSV); return %$REPORT; } ###################################################################################################################### ## / SUB PARSETEMPDB ############################################################################################### ###################################################################################################################### ###################################################################################################################### ## SUB SENDMAIL ### DOES: generates and sends e-mail to all recipients with attached csv report ################## ###################################################################################################################### #####NOT EXPORTED YET sub sendmail { my ($REPORT,$CONFIG,$RESULT) = @_; my $T = gettime(); my $MAIL_TO = $$CONFIG{MAIL_TO}[0]; delete $$CONFIG{MAIL_TO}[0]; my @MAIL_CC = $$CONFIG{MAIL_TO}; if ($RESULT == 1){ ##CASE: Script execution OK $$CONFIG{MAIL_DATA} .= " Overview - completed $T: Pattern matches successful: $$REPORT{OK} Pattern matches unsuccessful: $$REPORT{NOK} DEVICE UNREACHABLE: $$REPORT{UNREACHABLE} ERRORS: $$REPORT{ERROR} command success, but no output: $$REPORT{NOFEEDBACK} "; $$CONFIG{MAIL_SUB} .= "processed $$CONFIG{DEVICES_COUNT} devices. $$REPORT{UNREACHABLE} unreachable. "; my $mh = MIME::Lite->new( From => $$CONFIG{MAIL_FROM}, To => $$MAIL_TO, Cc => @MAIL_CC, Subject => $$CONFIG{MAIL_SUB}, Type => 'multipart/mixed' ) or die("$0::SENDMAIL:ERROR creating new mail object: $!/$?\n"); $mh->attach ( Type => 'TEXT', Data => $$CONFIG{MAIL_DATA}, ) or die("$0::SENDMAIL:ERROR adding the text message part: $!/$?\n"); $mh->attach ( Type => "text/csv", Path => $$CONFIG{OUTCSV}, Filename => $$CONFIG{OUTCSV}, Disposition => 'attachment' ) or die("$0::SENDMAIL:ERROR adding $$CONFIG{OUTCSV} to mail object: $!/$?\n"); $mh->send; }else{ ##CASE: Script execution aborted because tacacs verification failed $$CONFIG{MAIL_DATA} .= "ERROR: TACACS SERVICE VERIFICATION FAILED!\nReason:"; if ($RESULT == 1){ $$CONFIG{MAIL_DATA} .= "HUBSITE: $$CONFIG{HUBSITE} is unreachable\n";} if ($RESULT == 2){ $$CONFIG{MAIL_DATA} .= "Provided Username/password combination failed Authentication\n";} if ($RESULT == 2){ $$CONFIG{MAIL_DATA} .= "Provided Username/password combination passed Authentication, but tacacs profile doesn't allow privileged mode\n";} $$CONFIG{MAIL_SUB} .= "$$CONFIG{NAME}: tacacs service verification failed. script execution aborted"; my $mh = MIME::Lite->new( From => $$CONFIG{MAIL_FROM}, To => $MAIL_TO, Cc => @MAIL_CC, Subject => $$CONFIG{MAIL_SUB}, Type => 'multipart/mixed' ) or die("$0::SENDMAIL:ERROR creating new mail object: $!/$?\n"); $mh->attach ( Type => 'TEXT', Data => $$CONFIG{MAIL_DATA}, ) or die("$0::SENDMAIL:ERROR adding the text message part: $!/$?\n"); $mh->send; } return $RESULT; } ###################################################################################################################### ## / SUB SENDMAIL ################################################################################################## ###################################################################################################################### 1;