dru145 has asked for the wisdom of the Perl Monks concerning the following question:

Fellow Monks,

I completed the below script and have it working fine and dandy, but it is 527 lines long :-(

I explain in the comments what the script is doing. To see an example of the logsum file that is referenced in the $reportfile variable and a little more history on this post, then check out the node Automating Firewall Log Reporting.

Instead of having to us a separate while loop, splice, and email for each ip address, is there a way that I can incorporate all this into just one loop and email to make this script shorter and more efficient? I appreciate any help.

-Dru
#!/usr/bin/perl -w use strict; # Set variables my $today=`date +%d%b%y`; chomp($today); my $reportfile = "/exported/analysis/$today.logsum"; my $topten = 0; my $logfile = "/exported/$today.elog"; my @ips; my $from_addr="Root<root\@mydomain.com>"; my $to_addr="IS Security<Security\@mydomain.com"; # Open up the fwlogsum report and store all of the top ten ip addresse +s into the array @ips open REPORT, "$reportfile" or die "Can't open FWLOGSUM File: $!\n"; while (<REPORT>){ chomp; $topten = 1 if m!^Users/Source Addresses!; next unless $topten; push @ips, [split /\s+/] if /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/; } close REPORT; # Pull out the first ip address and assign it to the variable $ip my $ip1 = qr/\b$ips[0][0]\b/o; my @fwlog1; # Open up the firewall log file and match the ip address and store the + results into the array @fwlog1 open LOG, "$logfile" or die "Can't open $logfile: $!\n"; while (<LOG>){ if ($_ =~ $ip1){ push (@fwlog1, $_); } #end if } #end while close LOG; # Splice the array @fwlog1 and take only the first 15 entries to be in +cluded in the email as "evidence" # Also substitute any firewall ip addresses for x's for add protection splice(@fwlog1, 15); foreach (@fwlog1){ s/192\.168\.13\.2/x.x.x.x/; s/192\.168\.2\.2/x.x.x.x/; s/192\.168\.214\.46/x.x.x.x/; s/192\.168\.148\.2/x.x.x.x/; } # Open sendmail and send Flagged Activity email to the Security Team f +or review. open(SENDMAIL, "|/usr/lib/sendmail -oi -t") or die "Can't fork for sendmail: $!\n"; print SENDMAIL <<EOM; From: $from_addr To: $to_addr Subject: Flagged Activity from $ips[0][0] System Administrator, Hello. I am the Network Security Manager from ACME Inc. While I was sc +anning our firewall logs I discovered an ip from your registered network was trying to gain acc +ess to APC's network. Could you please look into this for me and provide a reason w +hy a computer on your network would be trying to gain access to our network. Here are the details: Source IP Address: $ips[0][0] Number of times: $ips[0][1] Sample from the firewall logs (*note date/time are Eastern Daylight Ti +me -4 GMT): @fwlog1 Thank You, Security Team ACME Inc. EOM close(SENDMAIL) or warn "sendmail didn't close nicely"; # IP2 my $ip2 = qr/\b$ips[1][0]\b/o; my @fwlog2; --------Repeats the while loop, splice, and email 9 more times for eac +h ip addresses-------------

Replies are listed 'Best First'.
Re (tilly) 1: Incorporating each while loop and email into one loop?
by tilly (Archbishop) on Aug 11, 2001 at 17:01 UTC
    I strongly recommend going through your code, trying to identify logical "chunks", and pulling them into functions. After you have done that, you will wind up with more maintainable code.

    As a side benefit it should then be easy to take the body of your code and turn it into a function that takes an IP address as a parameter. Then you can solve your original problem by calling your function 10 times in a loop.

    In case you don't know how to write a function in Perl, you just do it very simply like this:

    # declare a function sub my_function_name { # Your input parameters will be in the array @_... my $first_arg = shift(@_); my @remaining args = @_; # Then do whatever you want here... print "Hello, world\n"; # And return data return "Whatever you want to\n"; } # And then you call it like this: my_function_name("First argument", "the", "rest", "of", "them");
    For more on functions, try perldoc perlsub.

    UPDATE
    BTW rather than manually calling sendmail, I would recommend using Mail::Send.