The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

# Copyright (C) 2010 Andrea Martire
# 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; either version 2 of the License, or (at your option) any later
# version.
#
# 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., 51
# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

use DateTime;
require "/etc/aLid/sendmail";
require "/etc/aLid/time";
require "/etc/aLid/sendlog";

qx{ touch /etc/aLid/run.info };
open INFO, ">>/etc/aLid/run.info";
print INFO "$$\n";
close INFO;

print "FinProtect\n";
$range = $ARGV[0];
print "Event Range: $range\n";
$evt_bound = $ARGV[1];
print "Event Bound: $evt_bound\n";
$red_bound = $ARGV[2];
print "Redemption Bound: $red_bound\n";
$fileBlocked = $ARGV[3];
print "Ip Blocked's File: $fileBlocked\n";
$mode = uc($ARGV[4]);
die "Error Option MODE: Insert mail, log, all or nothing\n" if ( $mode ne 'MAIL' && $mode ne 'LOG' && $mode ne 'ALL' && $mode ne 'NOTHING' );
print "Mode: $mode\n";
$drop = uc($ARGV[5]);
die "Error Option DROP: Insert drop or nodrop\n" if ($drop ne 'DROP' && $drop ne 'NODROP');
print "Drop: $drop\n";
if( $mode eq 'ALL' || $mode eq 'MAIL' ) {
	$freqEmail = $ARGV[6];
	print "Mail's Frequency: $freqEmail\n";
}

$fileEmails = "/etc/aLid/emails.conf";
$fileLog = "/etc/aLid/log";

# load blocked ip 
qx{ touch $fileBlocked };
@file_ip_blocked = qx{ cat $fileBlocked };

for( $i = 0; $i < @file_ip_blocked; $i++ ){
	#print "READ FROM FILE: $file_ip_blocked[$i]\n";
	if( $file_ip_blocked[$i] =~ /^(.*)\s(\d+)/ ) {
		qx{ iptables -D FORWARD -s $1 -j DROP };
		qx{ iptables -D FORWARD -d $1 -j DROP };
		if( $mode eq 'ALL' || $mode eq 'LOG' ) {
			&sendlog( $fileLog, "FinProtect Initialization: Redemption ip=$1" );
		}
	}
}

#$fileInput = "/var/log/iptables.log";
#open LOGFILE, "tail --sleep-interval=0 -f $fileInput|" || die "Error Opening the file: $fileInput\n";

$modified_blocked = 0;

while ( $line = <STDIN> ){
	$date_update = 0;
	if( keys( %ip_blocked ) > 0 ) {
		$now_time = qx{ date -u };
		#print "NOW TIME = $now_time\n";

		if( $now_time =~ /\w\w\w\s(\w\w\w)\s+(\d+)\s(\d+)\:(\d+)\:(\d+)\s\w+\s(\d+)/ ) {
			#print "Month $1 day $2 hour $3 minute $4 second $5 year $6\n";
			$utc_t = DateTime->new( year => $6, month => &month_index($1), day => $2, 
									hour => $3, minute=> $4, second => $5, time_zone => 'Europe/Rome' )->utc_rd_as_seconds();
			#print "DATA = $dt\n";
			$date_update = 1;
		}

		while (($ip, $ip_time) = each(%time_blocked)){
			#print "LETTO IP = $ip, TIME = $ip_time, NOW = $utc_t \n";
			# Se l'ip bloccato merita la redenzione non lo metto in lista
			if( $utc_t - $ip_time >= $red_bound ) {
				$last_modify = $utc_t;
				delete ($ip_blocked{$ip});
				delete ($time_blocked{$ip});
				print STDERR "REDEMPTION $ip FIN SCAN\n";
				qx{ iptables -D FORWARD -s $ip -j DROP };
				qx{ iptables -D FORWARD -d $ip -j DROP };
				if( $mode eq 'ALL' || $mode eq 'LOG' ) {
					&sendlog( $fileLog, "FinProtect Redemption ip=$ip" );
				}
			}
		}
	}

	if ( $line =~/RES=\dx\d\d\sFIN\sURGP=.*$/ ) {
		if( $line =~ /^(\w+)\s+(\d+)\s(\d+)\:(\d+)\:(\d+).*\sSRC=(\d+\.\d+\.\d+\.\d+)\sDST=(\d+\.\d+\.\d+\.\d+).*\sPROTO=TCP\s.*\sDPT=(\d+)\s/ ){	
			#print STDERR "s";

			if( $date_update == 0 ) {
				$now_time = qx{ date -u };
				#print "NOW TIME = $now_time\n";
				if( $now_time =~ /\w\w\w\s(\w\w\w)\s+(\d+)\s(\d+)\:(\d+)\:(\d+)\s\w+\s(\d+)/ ) {
					$utc_t = DateTime->new( year => $6, month => &month_index($1), day => $2, 
											hour => $3, minute=> $4, second => $5, time_zone => 'Europe/Rome' )->utc_rd_as_seconds();
					#print "DATA = $dt\n";
					$date_update = 1;
				}
			}

			if($ip_blocked{$6} == 1) {
				$last_modify = $utc_t;
				$time_blocked{$6} = $utc_t;
			}
			else {
				$logMonth =$1; $logDay = $2; $hours = $3; $minutes = $4; $seconds = $5; $ipsource = $6; $ipdest = $7; $dport = $8;
				# Calculate system's year
				$system_year = qx< date +%Y >;
				chomp($system_year);
				$current_time = DateTime->new( year => $system_year, month => &month_index($logMonth), day => $logDay, 
												hour => $hours, minute => $minutes, second => $seconds, 
												time_zone => 'Europe/Rome' )->utc_rd_as_seconds();
				#print "UTC LOG = $current_time \n";
				# add to data's list
				$dati{"$current_time $ipsource $ipdest $dport"}++;

				# calculate port requests and remove old data
				%tmp = (); # delete old structure
				undef %tmp;
				while (($chiave, $valore) = each(%dati))
				{
					if( $chiave =~ /^(\d+)\s/ ) {
						if( $current_time - $1 > $range ) {
							delete ($dati{$chiave});
						}
						elsif( $chiave =~ /^\d+\s(\d+\.\d+\.\d+\.\d+)\s.*\s(\d+)$/ ) {
							# every port is counted once
							$tmp{"$1 $2"}++;
						}
					}
				}

				# new structure with consumptive data
				%report = (); # delete old structure
				undef %report;
				while (($k, $v) = each(%tmp)){
					# for every ip it calcute sum of tries
					if( $k =~ /^(\d+\.\d+\.\d+\.\d+)\s/ ) {
						# every port is counted once
						$report{$1}++;
					}
				}

				while (($k, $v) = each(%report)){
					#print "IP = $k TRIES = $v\n";
					if($v > $evt_bound){
						# delete data where is present found ip
						while (($kData, $vData) = each(%dati)){
							#print "key $kData value $vData - report $k $v\n";
							if( $kData =~ /^\d+\s(\d+\.\d+\.\d+\.\d+)/ && $1 eq $k ) {
								delete $dati{$kData};
							}
						}
						$last_modify = $utc_t;
						# Save bad ip's informations
						$bad_ip_source = $k;
						$n_requests = $v;
						$time_requests = $utc_t;
						#print "New bad ip $k\n";
						print STDERR "BLOCK: $k FIN SCAN\n";
						if( $drop eq 'DROP' ) {
							qx{ iptables -I FORWARD -d $k -j DROP };
							qx{ iptables -I FORWARD -s $k -j DROP };
							if( $mode eq 'ALL' || $mode eq 'LOG' ) {
								&sendlog( $fileLog, "FinProtect Blocking ip=$k" );
							}
						}
						$ip_blocked{$k} = 1;
						# insert with detecting time and not with logging time
						$time_blocked{$k} = $utc_t;
					}
				}
			}	
		}
	}

	if( $bad_ip_source ne '' ) {
		# if there are bad ip
		if( $mode eq 'MAIL' || $mode eq 'ALL' ){
			if( $mailPreparing == 0 ) {
				# initialize mail
				$mailPreparingTime = $utc_t;
				$mailPreparing = 1;
				$message = "FinProtect\n\n";
				$currDate = &getTime();
				$message .= "Data Detection: $currDate UTC\n";
				$message .= "Blocking Bound: $evt_bound\n";
				$message .= "Redemption Bound: $red_bound\n";
				$message .= "Range (sec): $range\n\n";
			}
			$message .= "IP Source: $bad_ip_source\t";
			$message .= "Requests: $n_requests\t";
			$message .= "Event Date: $logDay $logMonth $hours:$minutes:$seconds UTC\n";
		}
	}
	# if there is a mail still to send
	if( $mailPreparing == 1 && ($utc_t - $mailPreparingTime) >= $freqEmail ) {
		# load mail addresses
		@emails = qx { cat $fileEmails };
		foreach $email( @emails ){
			#print "SEND EMAIL to $email\n";
			#print "Message:\n$message\n MODE $mode --------------------------------------------------";
			&sendmail( "tcpdetective\@unical.it", $email, "Detected TCP Port Scanning", $message );
		}
		if ( $mode eq 'ALL' ) {
			&sendlog( $fileLog, "FinProtect sent an email notification" );
		}
		$mailPreparingTime = 0;
		$mailPreparing = 0;
		$message = "";
	}

	# Refresh ip blocked's file
	if( $modified_blocked < $last_modify ) {
		$modified_blocked = $last_modify;
		open(IPBLOCK, ">$fileBlocked");
		while (($k, $v) = each(%ip_blocked)){
			print ">";
			print IPBLOCK "$k $time_blocked{$k}\n";
		}
		close(IPBLOCK);
	}

	# Reset data structure
	$bad_ip_source = ''; 
	$n_requests = 0; 
	$time_requests = 0; 
}