#!/usr/local/bin/perl
#
# $Id: TarPitMonitor.pl,v 1.2 2005/12/02 01:59:22 rdilley Exp $
#
# author: ron dilley
#
# desc: this perl script watches syslog for labrea patterns and sends
#       notifications
#
# Copyright (C) 2005  Ron A. Dilley
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; version 2
# of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# ron.dilley@uberadmin.com
#
############################################################################

#
# modules
#
use Getopt::Std;
use Time::Local;

#
# pragmas
#
use strict;

#
# set environment
#
$ENV{PATH} = "/usr/bin:/bin:/usr/sbin:/sbin:/usr/ucb";

#
# turn on autoflush
#
select STDERR; $| = 1;
select STDOUT; $| = 1;

#
# defines
#
$::TRUE = 1;
$::FALSE = 0;
$::FAILED = -1;

$::VERSION = '$Id: TarPitMonitor.pl,v 1.2 2005/12/02 01:59:22 rdilley Exp $';
$::PROGNAME = "TarPitMonitor.pl";

%::Config = ();
$::Config{'debug'} = $::FALSE;
$::Config{'threshold'} = 60 * 180; # 3 hours

#
# main routine
#
if ( &main() != $::TRUE ) {
  exit( 1 );
}

exit( 0 );

############################################################################
#
# sub-routines
#

#
# main routine
#
sub main {
  my $arg;

  #
  # display script banner
  #
  &show_banner();

  #
  # parse command-line
  #
  &parse_command_line();

  # process args that are left
  if ( scalar( @::ARGV ) > 0 ) {
    while( $arg = shift( @::ARGV ) ) {
      # process file
      &parseLogfile( $arg );
    }
  } else {
    # process stdin
    &parseLogfile();
  }

  # done
  return $::TRUE;
}

#
# display banner info
#
sub show_banner {
  print "$::VERSION\n";
  print "By: Ron Dilley\n";
  print "\n";
  print "$::PROGNAME comes with ABSOLUTELY NO WARRANTY.\n";
  print "This is free software, and you are welcome\n";
  print "to redistribute it under certain conditions;\n";
  print "See the GNU General Public License for details.\n";
  print "\n";

  return $::TRUE;
}

#
# display help info
#
sub show_help {
  print "Syntax:\n";
  print "\n";
  print "$::PROGNAME [options] {file} [{file} ...]\n";
  print "\n";
  print "-d {0-9}   Display debug information during program run\n";
  print "-y {year}  Over-ride default year\n";
  print "\n";

  return $::TRUE;
}

#
# parse command-line arguments
#
sub parse_command_line {
  no strict 'vars';

  if ( getopts( 'd:y:' ) == $::FALSE ) {
    &show_help();
    return $::FAILED;
  }
  if ( defined $opt_d ) {
    if ( $opt_d > 0 ) {
      # set debug mode
      $::Config{'debug'} = $opt_d;
    }
  }
  if ( defined $opt_y ) {
    if ( $opt_y ne "" ) {
      # set year
      $::Config{'year'} = $opt_y;
    }
  }

  if ( ! exists $::Config{'year'} ) {
    # no year defined, use current
    ($::Config{'year'}) = ( localtime( time() ) )[5];
  }

  return $::TRUE;
}

#
# parse log file
#
sub parseLogfile {
  my ( $fname ) = @_;
  my $line;

  if ( $fname ne "" ) {
    if ( ! defined open( LOGFILE, $fname ) ) {
      print "ERROR - Unable to open [$fname]\n";
      return $::FAILED;
    }

    while( $line = <LOGFILE> ) {
      &process_line( $line );
    }

    close( LOGFILE );
  } else {
    while( $line = <STDIN> ) {
      &process_line( $line );
    }
  }

  return $::TRUE;
}

#
# process labrea log line
#
sub process_line {
  my ( $line ) = @_;

  my $month;
  my $day;
  my $time;
  my $sensor;
  my $sIp;
  my $sPort;
  my $dIp;
  my $dPort;
  my $proto;
  my $timeInt;

  if ( $line =~ m/^(.+)\s(\d+)\s(\d+\:\d+\:\d+)\s(\S+)\s.*labrea[0-9\[\]]*\: Responded to a PING: (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s\-\>\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}).*$/ ) {
    # tcp initial connect
    $month = $1;
    $day = $2;
    $time = $3;
    $sensor = $4;
    $sIp = $5;
    $dIp = $6;
    $proto = "ICMP";
    $dPort = "Ping";
    if ( $::Config{'debug'} >= 2 ) {
      print "DEBUG - Ping $sIp $dIp\n";
    }
    $timeInt = time2int( $::Config{'year'}, $month, $day, $time );
    if ( exists $::Traffic{"$sIp-$proto-$dPort"} ) {
      # XXX check to see how long it has been sinse last seen
      if ( $::Traffic{"$sIp-$proto-$dPort"} < ( $timeInt - $::Config{'threshold'} ) ) {
	print "INFO - First seen [$month $day $time] $proto $sIp $dIp $dPort\n";
      }
    } else {
      print "INFO - First seen [$month $day $time] $proto $sIp $dIp $dPort\n";
    }
    $::Traffic{"$sIp-$proto-$dPort"} = $timeInt;
  } elsif ( $line =~ m/^(.+)\s(\d+)\s(\d+\:\d+\:\d+)\s(\S+)\s.*labrea[0-9\[\]]*\: Initial Connect .*\: (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s(\d+)\s\-\>\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s(\d+).*$/ ) {
    # tcp initial connect
    $month = $1;
    $day = $2;
    $time = $3;
    $sensor = $4;
    $sIp = $5;
    $sPort = $6;
    $dIp = $7;
    $dPort = $8;
    $proto = "TCP";
    if ( $::Config{'debug'} >= 2 ) {
      print "DEBUG - Initial TCP Connection $sIp $sPort $dIp $dPort\n";
    }
    $timeInt = time2int( $::Config{'year'}, $month, $day, $time );
    if ( exists $::Traffic{"$sIp-$proto-$dPort"} ) {
      # XXX check to see how long it has been sinse last seen
      if ( $::Traffic{"$sIp-$proto-$dPort"} < ( $timeInt - $::Config{'threshold'} ) ) {
	print "INFO - First seen [$month $day $time] $proto $sIp $sPort $dIp $dPort\n";
      }
    } else {
      print "INFO - First seen [$month $day $time] $proto $sIp $sPort $dIp $dPort\n";
    }
    $::Traffic{"$sIp-$proto-$dPort"} = $timeInt;
  } elsif ( $line =~ m/^(.+)\s(\d+)\s(\d+\:\d+\:\d+)\s(\S+)\s.*labrea[0-9\[\]]*\: Persist Trapping .*\: (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s(\d+)\s\-\>\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s(\d+).*$/ ) {
    # tcp trapping
    $month = $1;
    $day = $2;
    $time = $3;
    $sensor = $4;
    $sIp = $5;
    $sPort = $6;
    $dIp = $7;
    $dPort = $8;
    $proto = "TCP";
  } elsif ( $line =~ m/^(.+)\s(\d+)\s(\d+\:\d+\:\d+)\s(\S+)\s.*labrea[0-9\[\]]*\: Persist Activity\: (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s(\d+)\s\-\>\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s(\d+).*$/ ) {
    # tcp continuing traffic
    $month = $1;
    $day = $2;
    $time = $3;
    $sensor = $4;
    $sIp = $5;
    $sPort = $6;
    $dIp = $7;
    $dPort = $8;
    $proto = "TCP";
  } elsif ( $line =~ m/^(.+)\s(\d+)\s(\d+\:\d+\:\d+)\s(\S+)\s.*labrea[0-9\[\]]*\: Inbound UDP Packet\: (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s(\d+)\s\-\>\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s(\d+).*$/ ) {
    # udp traffic
    $month = $1;
    $day = $2;
    $time = $3;
    $sensor = $4;
    $sIp = $5;
    $sPort = $6;
    $dIp = $7;
    $dPort = $8;
    $proto = "UDP";
    if ( $::Config{'debug'} >= 2 ) {
      print "DEBUG - UDP Traffic $sIp $sPort $dIp $dPort\n";
    }
    $timeInt = time2int( $::Config{'year'}, $month, $day, $time );
    if ( exists $::Traffic{"$sIp-$proto-$dPort"} ) {
      # XXX check to see how long it has been sinse last seen
      if ( $::Traffic{"$sIp-$proto-$dPort"} < ( $timeInt - $::Config{'threshold'} ) ) {
	print "INFO - First seen [$month $day $time] $proto $sIp $sPort $dIp $dPort\n";
      }
    } else {
      print "INFO - First seen [$month $day $time] $proto $sIp $sPort $dIp $dPort\n";
    }
    $::Traffic{"$sIp-$proto-$dPort"} = $timeInt;
  } else {
    # ignore the line
    if ( $::Config{'debug'} >= 6 ) {
      print "DEBUG - [$line]\n";
    }
    return $::FALSE;
  }

  if ( $::Config{'debug'} >= 3 ) {
    print "DEBUG - $month $day $time $sensor $proto $sIp $sPort $dIp $dPort\n";
  }

  return $::TRUE;
}

#
# date stuff
#
sub time2int {
  my ( $year, $month, $day, $time ) = @_;
  my @mStrings = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" );
  my $i;
  my $hour;
  my $min;
  my $sec;

  # convert year to offset from 1900
  if ( $year > 1900 ) {
    $year =- 1900;
  }

  # convert month to a number
  for ( $i = 0; $mStrings[$i] != ""; $i++ ) {
    if ( "$month" eq "$mStrings[$i]" ) {
      $month = $i;
    }
  }

  # split time
  ( $hour, $min, $sec ) = split( ":", $time );

  # convert to an int
  return timelocal( $sec, $min, $hour, $day, $month, $year );
}
