#!/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;
#	eventProtect name exprReg range bound redemption freqEmail fileAllowed notify filter

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

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

$evt_name = $ARGV[0];
$reg_expr = $ARGV[1];
$range = $ARGV[2];
$evt_bound = $ARGV[3];
$red_bound = $ARGV[4];
$freqEmail = $ARGV[5];
$fileAllowed = $ARGV[6];
$mode = uc($ARGV[7]);
die "Error Option MODE: Insert mail, log, all or nothing\n" if ( $mode ne 'MAIL' && $mode ne 'LOG' && $mode ne 'ALL' && $mode ne 'NOTHING' );
$drop = uc($ARGV[8]);
die "Error Option DROP: Insert drop or nodrop\n" if ($drop ne 'DROP' && $drop ne 'NODROP');

$fileEmails = "/etc/aLid/emails";
$fileLog = "/etc/aLid/log";
$fileBlocked = "/etc/aLid/ip_blocked_$evt_name";

print "Event: $evt_name\n";
print "Regular Expression: $reg_expr\n";
print "Range (sec): $range\n";
print "Event Bound: $evt_bound\n";
print "Redemption Bound: $red_bound\n";
print "Mail Frequency: $freqEmail\n";
print "Ip Allowed's File: $fileAllowed\n";
print "Ip Blocked's File: $fileBlocked\n";
print "Email's file: $fileEmails\n";
print "LogFile: $fileLog\n";
print "Mode: $mode\n";
print "Drop: $drop\n";

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

for( $i = 0; $i < @file_ip_blocked; $i++ ){
	print "Reading 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, "$evt_name Initialization: Redemption ip=$1" );
		}
	}
}

qx{ rm $fileBlocked };
qx{ touch $fileBlocked };

# load allowed ip 
qx{ touch $fileAllowed };
@file_ip_allowed = qx{ cat $fileAllowed };

for( $i = 0; $i < @file_ip_allowed; $i++ ){
	print "READ FROM FILE: $file_ip_allowed[$i]\n";
	if( $file_ip_allowed[$i] =~ /^(.*)/ ) {
		$ip_allowed{$1} = 1;
		print "ALLOWED $1\n";
	}
}

#$fileInput = "/var/log/iptables.log";
#open LOGFILE, "tail -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 "Mese $1 giorno $2 ora $3 minuto $4 sec $5 anno $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 STDERR "DATA = $dt\n";
			$date_update = 1;
		}

		while (($ip, $ip_time) = each(%time_blocked)){
			#print STDERR "read IP = $ip, TIME = $ip_time, NOW = $utc_t \n";
			if( $utc_t - $ip_time >= $red_bound ) {
				$last_modify = $utc_t;
				delete ($ip_blocked{$ip});
				delete ($time_blocked{$ip});
				print STDERR "$evt_name Redemption $ip $utc_t - $ip_time >= $red_bound\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, "$evt_name Redemption ip=$ip" );
				}
			}
		}
	}

	if( $line =~ /$reg_expr/ ) {
		if ( $line =~ /^(\w+).*?(\d+)\s(\d\d)\:(\d\d)\:(\d\d)\s.*\sSRC=(\d+\.\d+\.\d+\.\d+)\s.*/ ){	
			
			if( $ip_allowed{$6} != 1 ) {
				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+)/ ) {
						#print "Mese $1 giorno $2 ora $3 minuto $4 sec $5 anno $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;
					}
				}
				#print STDERR "$utc_t - ";

				if($ip_blocked{$6} == 1) {
					$last_modify = $utc_t;
					$time_blocked{$6} = $utc_t;
					#print ".";
				}
				else {
					#print ",";
					$logMonth =$1; $logDay = $2; $hours = $3; $minutes = $4; $seconds = $5; $ipsource = $6;
					# 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"}++;
			
					# create consumptive structure
					%report = (); # delete old structure
					undef %report;
					# remove old data
					while (($chiave, $valore) = each(%dati))
					{
						print "($chiave - $valore)\n";
						if( $chiave =~ /^(\d+)\s/ ) {
							if( $current_time - $1 > $range ) {
								delete ($dati{$chiave});
							}
							elsif( $chiave =~ /(\d+\.\d+\.\d+\.\d+)$/ ) {
								$report{"$1"}+=$valore;
							}
						}
					}

					while (($k, $v) = each(%report)){
						#print  STDERR "IP = $k TRIES = $v BOUND $evt_bound\n";
						if($v > $evt_bound){
							print "$k SUPERA SOGLIA $evt_bound con $v\n";
							# delete data where ip is present
							while (($chiave, $valore) = each(%dati)){
								#print "chiave $chiave valore $valore - report $k $v\n";
								if( $chiave =~ /^\d+\s(\d+\.\d+\.\d+\.\d+)/ && $1 eq $k ) {
									#print "cancello $chiavif($ip_blocked{$6} == 1) {e $valore\n";
									delete $dati{$chiave};
								}
							}
							# if it isn't allowed
							if( $ip_allowed{$k} != 1 ){
								$last_modify = $utc_t;
								# add to bad ip
								$bad_ip_source = $k;
								$n_requests = $v;
								$time_requests = $utc_t;
								#print "New bad ip $k\n";
								print STDERR "BLOCK: $k $evt_name $utc_t\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, "$evt_name Detected ip=$k times=$n_requests" );
									}
								}
								$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 is a bad ip
		if( $mode eq 'MAIL' || $mode eq 'ALL' ){
			if( $mailPreparing == 0 ) {
				# initialize mail
				$mailPreparingTime = $utc_t;
				$mailPreparing = 1;
				$message = "Event: $evt_name\n\n";
				$currDate = &getTime();
				$message .= "Data Detection: $currDate UTC\n";
				$message .= "Matching Regular Expression: $reg_expr\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 "INVIO EMAIL a $email\n";
			print "Messaggio:\n$message\n";
			&sendmail( "eventdetective\@unical.it", $email, "Detected Event $evt_name", $message );
		}
		if ( $mode eq 'ALL' ) {
			&sendlog( $fileLog, "$evt_name sent an email notification" );
		}
		$mailPreparingTime = 0;
		$mailPreparing = 0;
		$message = "";
	}

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

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