#!/usr/bin/perl -s 
$VERSION='ipf_04072001';
# ipf parse routine by Ken McKinlay <kmckinlay@home.com> based on material by:
#	Dirk-Willem van Gulik <dirkx@webweaving.org>
##!/usr/bin/perl -s  

# 
#   DShield Client Framework
#
#   Parameters:
#
#   -config  - Name of config file (default /etc/dshield.cnf )
#   -from    - e-mail 'from' information
#   -to      - e-mail 'to' information (default reports@dshield.org )
#   -userid  - dshield numerid userid (default 0)
#   -tz      - timezone (default: date -z)
#   -log     - location of log file (default: STDIN)
#   -verbose - turn on extra output 
#   -sendmail - location of mail software 
#                (default: '/usr/sbin/sendmail -oi -t')
#   -rotate  - Y: copy to 'log.old'
#              N: do nothing, but keep line count file
#              A: append to log.old  
#   -linecnt - name of line count file (if rotate=N). 
#                   Default: /var/log/dshield.cnt
#              this file save the date now, not the number of lines.      
#   -tmp     - specify a temp file name
#   -obfus   - Y: hide target IP (replace first byte with 10.)
#            - N: don't (default).
#   -target_exclude - name of file with excluded target IPs.
#                     (default: none)
#   -source_exclude - name of file with excluded source IPs.
#                     (default: none)
#
#  The idea of this framework is to establish a list of common
# features and command line paramters accross all clients.
#
#  The script uses no perl modules to provide for maximum
# compatibility.
#

%Months=( Jan => 1, Feb => 2, Mar => 3, Apr => 4, May => 5, Jun =>6,
	  Jul => 7, Aug => 8, Sep => 9, Oct => 10, Nov => 11, Dec => 12 );


$config='/etc/dshield.cnf' unless $config;
print "Config File: $config\n" if $verbose;
$ENV{"PATH"}="/usr/bin";
$ENV{"BASH_ENV"}="";



if ( -f $config ) {
    open (CONF,"$config") || die ("Can't open $config for reading\n");
    foreach (<CONF>) {
	if ( ! /^#/ ) {
	     chomp;
	     ($name,$value)=split("=");
	     $name=lc($name);
	     print "name: $name value: $value\n" if $verbose;
	     $$name=$value;
	     
	 }
    }
}

print "$log\n" if ( $verbose eq 'Y');

if ( $target_exclude ) {
    if ( -f $target_exclude ) {
	open (FILE,$target_exclude);
	foreach (<FILE>) {
	    chomp;
	    unless ( /^#/ ) {
		     if ( /^[\d\.]+$/ ) {
			 $excluded_targets .= "|$_|";
		     } else {
			 print "Bad IP address ($_) in $target_exclude\n";
		     }
		 }
	}
    } else {
	die ("Can't find the target exclude file ($target_exclude)\n");
    }
}
if ( $source_exclude ) {
    if ( -f $source_exclude ) {
	open (FILE,$source_exclude);
	foreach (<FILE>) {
	    chomp;
	    unless ( /^#/ ) {
		     if ( /^[\d\.]+$/ ) {
			 $excluded_sources .= "|$_|";
		     } else {
			 print "Bad IP address ($_) in $source_exclude\n";
		     }
		 }
	}
    } else {
	die ("Can't find the source exclude file ($source_exclude)\n");
    }
}

$from='nobody@nowhere.com' unless $from;
$to='report@dshield.org' unless $to;
$userid=0 unless $userid;
$tz=`date +%z` unless $tz;
@the_date=localtime(time());
$cur_year=$the_date[5]+1900;
$cur_month=$the_date[4]+1;
$log='-' unless $log;
$verbose='N' unless $verbose;
$sendmail='/usr/sbin/sendmail -oi -t' unless $sendmail;
$rotate='Y' unless $rotate;
$linecnt='/var/log/dshield.cnt' unless $linecnt;
$obfus='N' unless $obfus;


chomp $tz;

$tmp="dshield.$$.tmp" unless $tmp;
$excluded_sources="=";
$excluded_targets="=";



if ( $log ne '-') {
  die ("Can't find log file at $log\n") unless ( -f $log );
}

if ($rotate eq 'N') {
    print "Opening Line count file $linecnt\n" if ($verbose eq 'Y');
    if ( -f $linecnt) { 
	open (CNT,$linecnt) || die "Can't open line count file ($linecnt)\n";
	$line_count=<CNT>;
	chomp ($line_count);
	close CNT;
    } else {
	$line_count='2000-01-01';
    }
}



print "Opening Log: $log\n" if ( $verbose eq 'Y' );
open (LOG,$log) || die ("Can't open $log for reading\n");

open (TMP,"> $tmp") || die ("Can't open temp file ($tmp) for writing\n"); 



foreach $line (<LOG>) {

#
#  @dshield_array:
#
# 0 - time/date/timezone 1 - author 2 - count
# 3 - sourceip, 4 - sourceport, 
# 5 - targetip, 6 - targetport,
# 7 - protocol, 8 - flags

	
    @dshield_array=parse($line);
    if ($dshield_array[0]>$line_count) {
	$lines=$dshield_array[0] if ($dshield_array[0]>$lines);
	if (  ! ( $excluded_sources =~ /\|$dshield_array[3]\|/ )  ) {
	    if (  ! ( $excluded_targets =~ /\|$dshield_array[5]\|/ )  ) {
		
		if ($obfus eq 'Y' ) {
		    $dshield_array[5]=~ s/^\d+\./10./;
		}
		$dshield=join("\t",@dshield_array);
		print "PARSE RESULT $dshield\n" if ($verbose eq 'Y');
		
		if (validate_dshield($dshield) ) {
		    print TMP "$dshield\n";
		    print "WRITTEN: $dshield\n" if ($verbose eq 'Y');
		} else {
		    print "nothing written\n" if ($verbose eq 'Y');
		}
	    } else {
		print "target $dshield_array[5] excluded\n" if ($verbose eq  'Y');
	    }
	} else {
	    print "source $dshield_array[3] excluded\n" if ($verbose eq  'Y');
	}
    } else {
	print "Lines skiped because date ($dshield_array[0]) is too early \n" if ($verbose eq 'Y');
    }
}
close LOG;
close TMP;

if ( $rotate eq 'Y' && $log ne '-' ) {
    if ( -f "$log.bak" ) {
	unlink ("$log.bak");
    }
    `mv $log $log.bak`;
}

if ( $rotate eq 'A' ) {
    `cat $log >> $log.bak`;
    unlink $log;
}

if ( $rotate eq 'N' && $log ne '-' ) {
      open (CNT,"> $linecnt") || die "Can't open line count file for writing($linecnt)\n";
      print CNT $lines;
      close CNT;
  }
  

unless ( -z $tmp ) {
    if ( $verbose eq 'Y' ) {
	print "Sending mail using $sendmail\n";
	print "To: $to, From: $from\n";
    }
    open (MAIL,"| $sendmail");
    print MAIL "To: $to\n";
    print MAIL "From: $from\n";
    print MAIL "Subject: USERID $userid FORMAT DSHIELD TZ $tz VERSION $VERSION\n\n";
    open (TMP,"$tmp") || die ("Can't open temp file ($tmp) for reading\n");
    foreach (<TMP>) {
	print MAIL $_;
    }
    close TMP; 
    close MAIL;
} else {
    print "TMP file empty. Not sending any mail\n" if ( $verbose eq 'Y' );
}
if ( -f $tmp ) {
    unlink ($tmp);
}

sub validate_dshield {

    my $line=shift;
    if ( $line =~ /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [+-]\d{2}:?\d{2}\t\d+\t\d+\t[0-9.]{7,15}\t\d+\t[0-9.]{7,15}\t\d+\t[0-9TCPIMPUD]{0,4}\t?[12UPFSAR]{0,6}$/ ) 
    {
	return 1;
    } else {
	print "INVALID: $line\n";
	return 0;
    }
}


#
#  OpenBSD ipf Parser
#    parser for ipf log files from OpenBSD
#
#  sample:
# 24/06/2001 23:41:01.084778              ep0 @0:11 p 1.2.3.4,2986 -> 5.6.7.8,27374 PR tcp len 20 48 -S IN
# 25/06/2001 01:46:56.062860              ep0 @0:11 p 1.2.3.4,53 -> 5.6.7.8,46996 PR udp len 20 71  IN
# 25/06/2001 13:18:46.614329 2x              ep0 @0:11 p 1.2.3.4,18940 -> 5.6.7.8,27374 PR tcp len 20 48 -S IN


sub parse {
    my $line = shift;

    my ($day,$month,$year,$hour,$minute,$second,$count,$action,$ipSource,$portSource,$ipDestin,$portDestin,$protocol);
    my ($extra,$flags);
    my @rline;

    print "PARSING: $line" if ($verbose eq 'Y');
    return 0 unless ( $line =~ / -> / );

    ($day,$month,$year,$hour,$minute,$second,undef,$count,undef,undef,$action,$ipSource,$portSource,$ipDestin,$portDestin,$protocol,$extra) = ($line =~ m/^\s*(\d+)\/(\d+)\/(\d+)\s+(\d+):(\d+):(\d+)\.(\d+)\s+(\w*)\s+(\w+)\s+(\S+)\s+(\w+)\s+([\.\d]+),?(\d*)\s+\->\s+([\.\d]+),?(\d*)\s+\w+\s+(\w+)\s+(.*)/);

    # parse the count value
    if ( $count ) {
       $count =~ s/\D//g;	# toss away non-digit characters
    } else {
       $count = 1;
    }

    # uppercase the protocol field
    $protocol = uc $protocol;

    # split the extra field if TCP to get the flags and then remove the "-" from the flags
    if ( $protocol eq "TCP" ) {
       (undef,undef,undef,$flags)=split ' ',$extra;
       $flags =~ s/-//g;
    } else {
       $flags="";
    }

    # if the protocol is ICMP, get the type and place in ports fields
    if ( $protocol eq "ICMP" ) {
       ($portSource,$portDestin)=($extra =~ m/^\s*\w+\s+\d+\s+\d+\s+\w+\s+(\d+)\/(\d+)/);
    }

    $rline[0]=sprintf("%0.4d-%0.2d-%0.2d %0.2d:%0.2d:%0.2d %s",$year,$month,$day,$hour,$minute,$second,$tz);
    $rline[1]=$userid;
    $rline[2]=$count;
    $rline[3]=$ipSource;
    $rline[4]=$portSource;
    $rline[5]=$ipDestin;
    $rline[6]=$portDestin;
    $rline[7]=$protocol;
    $rline[8]=$flags;

    return @rline;
}
