Jason Gerfen <> wrote:
> Bowie Bailey wrote:
> 
>> Jason Gerfen wrote:
>> 
>> 
>>> Bowie Bailey wrote:
>>> 
>>> 
>>>> Jason Gerfen wrote:
>>>> 
>>>> 
>>>>> Bowie Bailey wrote:
>>>>> 
>>>>> 
>>>>>> Jason Gerfen wrote:
>>>>>> 
>>>>>> 
>>>>>>> I am looking to do something which I am not sure perl can do.
>>>>>>> I would like to be able to issue a system command ex. tail -f
>>>>>>> /file and loop over that command while sorting the output.
>>>>>>> Is this possible?  And if so could I see an example?
>>>>>>> 
>>>>>>> 
>>>>>>  open(LOG, "tail -f /var/log/messages |");  while (<LOG>) {
>>>>>>      if (/something important/) {
>>>>>>          print "Found something...\n";
>>>>>>      }
>>>>>>  }
>>>>>>  close LOG; # Never get here, but for completeness...
>>>>>> 
>>>>>> I'm not sure what you mean by sorting the output.  Since there is
>>>>>> continuous input, there will be continuous output.  You could
>>>>>> sort and display aggregated output at intervals rather than
>>>>>> having a real-time output stream. 
>>>>>> 
>>>>>> 
>>>> I have a few programs that do this exact thing.  They tail the log
>>>> file and page or email me when they find something.  The only
>>>> problem I have had is that it seems like the pipe will eventually
>>>> break if you leave it running for long enough.
>>>> 
>>>> 
>>> Well I am 'attempting' to write a simple perl daemon that will
>>> listen or 'tail -f /var/log/messages' for any iptables log which
>>> matches a 'DROP'ed packet. 
>>> 
>>> 
>> 
>> That's simple enough.  Just follow my code above.
>> 
>> 
>> 
>>> Upon seeing a matching condition it will fork a new process and then
>>> begin sorting the data and send me alerts etc.
>>> 
>>> 
>> 
>> Fork a new process?  Why?
>> 
>> It sounds like you just want to watch for DROPs and send alerts. 
>> Why the complication? 
>> 
>> 
>> 
>>> Does that help explain it a bit more?  I can include code if needed.
>>> 
>>> 
>> 
>> Code might help.  Some sample output would help more.  What exactly
>> are you expecting this program to send you?
>> 
>> 
>> 
> I am going to include code, but for the record I am not a pro perl
> guru 
> nor is my code the most effecient.  This is what I am hoping to
> accomplish: 

I wouldn't worry about it, but I do have a few comments and suggestions.

> 
> Run this perl script in a 'daemon' mode to look for dropped packets
> from the iptables, count 'x' amount of attempts per IP/host then
> depending on 
> a config file or optional arguments the script will do a WHOIS on the
> offender and simply log it to a new file.
> 
> Here is the code thus far:
> 
> #!/usr/bin/perl
> 
> use strict;

Good start, but "use warnings;" as well would be better.

> use Getopt::Long;
> use POSIX qw( strftime );
> use Fcntl ':flock';
> use Net::Whois::ARIN;
> use Carp;
> use IO::Socket;
> 
> my( @config,
>     @data,
>     @hosts,
>     @drop,
>     @date,
>     @in,
>     @out,
>     @src,
>     @dst,
>     @prot,
>     @spt,
>     @dpt,
>     @temp ) = '';
> 

Avoid global variables as much as possible. They are a source of bugs.

> GetOptions( "config=s"      => \my $config,
>         "firewall=s"    => \my $firewall,
>             "report=s"      => \my $report,
>             "thresh-hold=n" => \my $thresh,
>             "whois=s"       => \my $whois,
>         "ports=s"       => \my $ports,
>             "email=s"       => \my $email,
>         "daemon=s"      => \my $daemon );
> 
> if( !defined( $config ) ) {
>     $config = "smnad.conf";
> }

That can be more simply written as '$config ||= "smnad.conf";'

> if( !defined( $thresh ) ) {
>     $thresh = 3;
> }
> if( !defined( $whois ) ) {
>     $whois = "FALSE";
> }
> if( !defined( $audit ) ) {
>     $audit = "FALSE";
> }
> if( !defined( $config ) ) {
>     $config = "portdefs";
> }
> if( !defined( $daemon ) ) {
>     $daemon = "FALSE";
> }
> if( ( defined( $firewall ) ) || ( defined( $config ) ) ) {
>     print
>    
>    
> "=============================================================\n";
>     &Main; print
> "=============================================================\n"; }
> else { &ShowSyntax; } 
> 
> sub GetDate
> {
>     my $date = strftime( '%b %e', localtime() );
>     return $date;
> }
> 
> sub ChkString( $string )
> {
>     my $string = shift;
>     if( $string =~ /(\w+)/i ) {
>         return 0;
>     } else {
>         return 1;
>     }
> }
> 
> sub ChkFile
> {
>     my $file = shift;
>     if( -e $file ) {
>         if( -w $file ) {
>             return 0;
>         } else {
>             return 1;
>         }
>     } else {
>         return 0;
>     }
> }

Let me see if I understand the above sub. It returns true if the file
exists and is not writable, otherwise false. Not sure what the point of
that is, but a more meaningful name for the sub might help.

> 
> sub ReadConfig
> {
>     my $config = shift; my $ret = 1;
>     if( ChkFile( $config ) == 0 ) {
>     open( FILE, $config ) || print "SMNAD: Could not open '$config'

Its normal to do something like 'die' here rather than print. After all,
what is the point of trying to read from a file that failed to open.

>     file\n"; flock( FILE, LOCK_EX );
>     while( my $data = <FILE> ) {
>         if( $data =! /#/i ) {
>         push( @temp, $data );
>         }
>     }
>     flock( INFILE, LOCK_UN );

You don't need to explicitly unlock as close will do that for you. Also,
unlocking before close can lead to a race condition.

>     close( INFILE );
>     $ret = 0;
>     } else {
>     $ret = 1;
>     }
>     return $ret;
> }
> 
> sub ParseConfig
> {
>     my $ret = 1; my @tmp;
>     for( my $x = 0; $x < $#temp; $x++ ) {
>     if( $temp[$x] =! /#/i ) {

That looks like a typo. Did you mean !~ ?

>         push( @config, chomp( $temp[$x] ) );

You should check 'perldoc -f chomp' for what it returns. It might not be
what you think.

>     }
>     }
>     for( my $x = 0; $x < $#temp; $x++ ) {
>     @tmp = split( /: /, $config[$x] );
>     $ret = 0;
>     }

Its not clear that the above loop does anything that has an effect
outside this sub. Also, this and the previous sub would be considerably
simpler if combined into a single sub.

>     return $ret;
> }
> 
> sub ReadFirewall
> {
>     my $firewall = shift; my $ret = 1;
>     if( ChkFile( $firewall ) == 0 ) {
>     if( $daemon eq "TRUE" ) {
>         open( FILE, "tail -f $firewall |" ) || print "SMNAD: Could not
> open '$firewall' file\n";
>         while( my $data = <FILE> ) {
>         if( $data ne "" ) {
>             chomp( $data );
>             push( @data, $data );
>             $ret = 0;
>         }

When does to above loop terminate? Also, all it does is populate an
array. When does it get to do the sorting? Also, it is usually a good
idea to close file handles.

>         }
>     } else {
>         open( FILE, $firewall ) || print "SMNAD: Could not open
> '$firewall' file\n";
>         while( my $data = <FILE> ) {
>         if( $data ne "" ) {
>             chomp( $data );
>             push( @data, $data );
>             $ret = 0;
>         }
>         }
>     }
>     } else {
>     $ret = 1;
>     }
>     return $ret;
> }
> 
> sub SortData
> {
>     my $ret = 0;
>     my $date = &GetDate;
>     for( my $x = 0; $x < $#data; $x++ ) {
>     if( ( $data[$x] =~ /$date/i ) && ( $data[$x] =~ /DROP/i ) ) {
>             if( $data[$x] =~
> /(($date\s\d+\:\d+\:\d+).*IN=([eth]+[\d+]| ).*OUT=([eth]+[\d+]|
>
).*SRC=([\d+]{1,3}\.[\d+]{1,3}\.[\d+]{1,3}\.[\d+]{1,3}).*DST=([\d+]{1,3}
\.[\d+]{1,3}\.[\d+]{1,3}\.[\d+]{1,3}).*PROTO=(UDP|TCP).*SPT=(\d+).*DPT=(
\d+).*)/i
> ) {

Whew, that's a big regex. I would suggest checking out the qr operator
and the x switch to enable extended regexes that can include white space
and comments. Much easier to understand, and therefore debug.

>                 if( $9 ne "53" ) {
>                     push( @drop, $1 );
>                     push( @date, $2 );
>                     push( @in,   $3 );
>                     push( @out,  $4 );
>                     push( @src,  $5 );
>                     push( @dst,  $6 );
>                     push( @prot, $7 );
>                     push( @spt,  $8 );
>                     push( @dpt,  $9 );
>         }

I would suggest that the above would be of more use in ReadFirewall as
each line is read. Also a single array would be easier to manage that
nine.
For example:

my @keys = qw{drop date in out src dst prot spt dpt};
while (<FILE>) {
        if (/$regex/) {
                my %entry;
                @[EMAIL PROTECTED] = ($1, $2, $3, $4, $5, $6, $7, $8, $9);
                push @data, \%entry;
        }
}

If @data needs to be ordered in some way you can either sort it when you
have finished reading the file, or insert entries in the correct place
rather than pushing.

>         }
>     }
>     }
>     return $ret;
> }
> 
> sub Main
> {
>     if( &ReadConfig( $config ) == 0 ) {
>     if( &ParseConfig == 0 ) {
>         if( &ReadFirewall( $firewall ) == 0 ) {
>         if( &SortData == 0 ) {
> 
> #            for( my $x = 0; $x < $#drop; $x++ ) {
> #            print $drop[$x] . "\n";
> #            }
>         } else {
>             print "SMNAD: Error occured while sorting data from
> '$firewall'.\n";
>         }
>         } else {
>         print "SMNAD: Error opening firewall logs from
>         '$firewall'.\n"; }
>     } else {
>         print "SMNAD: Error sorting configuration data from
>     '$config'.\n"; }
>     } else {
>     print "SMNAD: Error opening config file '$config'.\n";
>     }
> 
> }
> 
> sub ShowSyntax
> {
>     print <<EOF;
> 
> Usage:  ./SMNAD  --config=[path/to/config]          Specify an
> alternate config file
> 
>                  --firewall=[path/to/logfile]       Specify the
> logfile path and filename
> 
>                  --report=[path/to/output/file]     Specify the report
> file to log results to
> 
>                  --thresh-hold=[n](def. 3)          Specify the
> threshold of attempts per IP to be obtained from firewall log
> 
>                  --whois=[TRUE|FALSE]               Perform a WHOIS
> lookup on offending host(s)
> 
>                  [EMAIL PROTECTED]         Send the resulting
> output from the resulting report to offending host(s) ISP
> 
>          --daemon=[TRUE|FALSE]              Run this application in
> DAEMON mode
> 
>
------------------------------------------------------------------------
----
> EOF
> exit;
> }

In spite of the fact that you have a sub named SortData, I can see no
evidence of sorting. Am I missing something?

Also, you seem to use the C style for loop when the more perl-ish
foreach would be simpler in most cases.

HTH

-- 
Brian Raven 


=================================
Atos Euronext Market Solutions Disclaimer
=================================
The information contained in this e-mail is confidential and solely for the 
intended addressee(s). Unauthorised reproduction, disclosure, modification, 
and/or distribution of this email may be unlawful.
If you have received this email in error, please notify the sender immediately 
and delete it from your system. The views expressed in this message do not 
necessarily reflect those of Atos Euronext Market Solutions.

L'information contenue dans cet e-mail est confidentielle et uniquement 
destinee a la (aux) personnes a laquelle (auxquelle(s)) elle est adressee. 
Toute copie, publication ou diffusion de cet email est interdite. Si cet e-mail 
vous parvient par erreur, nous vous prions de bien vouloir prevenir 
l'expediteur immediatement et d'effacer le e-mail et annexes jointes de votre 
systeme. Le contenu de ce message electronique ne represente pas necessairement 
la position ou le point de vue d'Atos Euronext Market Solutions.


_______________________________________________
ActivePerl mailing list
ActivePerl@listserv.ActiveState.com
To unsubscribe: http://listserv.ActiveState.com/mailman/mysubs

Reply via email to