#!/usr/bin/perl

#########################################################
#Document Page: http://coolscript.org/index.php/psad2ipt
#########################################################

# Disclaimer:
# This program is distributed in the hope that it will be useful,
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
# OTHER DEALINGS IN THE SOFTWARE.   

#1.0.1 - First release - Mar2016
#1.0.2 - Add GeoDB - Apr2016
#1.0.3 - Add Blacklist support - Apr2016
#1.0.4 - Add Lockfile support = May2016

#Interesting:
#https://isc.sans.edu/api/topips/records/50
#https://dshield.org/api/#topips
#Ramdisk sample
#mount -t tmpfs -o size=200M none /usr/local/psad2ipt/geoload/ramdisk
#umount /usr/local/psad2ipt/geoload/ramdisk 

#Init:
use strict;
use POSIX;
use IPC::Run 'run';
use Getopt::Long;
use Proc::ProcessTable;
use XML::Simple;
use File::Basename;
use File::Path qw/make_path/;
use Net::Syslog;
use DBI;
use Data::Dumper;

my (%hConfigItems,%hConfigEpoch,%hConfigExpires,%hConfigPrefix,%hConfigItemMatch,$tmpfnd,$tmpRwrite,$RecCnt,$RuleCnt,$CurRuleCnt,%hWhiteList,%hBlackList,$timeoffset);
my (%hConfigHistoryItems,%hConfigHistoryExpires,%hConfigHistoryEpoch,$confLogPath,$confChain,$confOutsideInterface,$confWhiteListPath,$StartupInfo,$tmpPid,$confBlackListPath);
my ($confMaxEvents,$confMaxRules,$confLookBehind,$confLookBehindOrig,$confUseSyslog,$confSplunkServer,$confDBFile,$confMaxLockDays,$confMaxRecords,%dbDuplicate,$confDBGeo);
my ($suberr,%hRunningConfig,%hRunningConfigRAW,$tmpTime,$confBaseLock,$tmpAppearance,$tmpLockTime,$cntWhitelist,$cntBlacklist,$Conffile,$database,@sWhiteFiles,@sBlackFiles);
my ($confDBUseUseCountryList,$RecordCount,$RecordCountIPT,$pcnt);
my ($opt_A,$flag_v,$flag_C,$flag_l,$opt_D,$flag_F,$flag_h,$opt_c,$opt_i,$opt_E,$opt_e,$flag_V,$flag_k,%hCountryRule,$tmpcc); 

#Parameter Init
Getopt::Long::Configure('bundling');
GetOptions   
	("A=s" => \$opt_A,
	 "C" => \$flag_C,
	 "v" => \$flag_v,
	 "l" => \$flag_l,
	 "D=s" => \$opt_D,
	 "F" => \$flag_F,
	 "h" => \$flag_h,
	 "c=s" => \$opt_c,
	 "i=s" => \$opt_i,
	 "e" => \$opt_e,
	 "V" => \$flag_V,
	 "E=s" => \$opt_E,
	 "k" => \$flag_k,
 );

$tmpPid=$$; #Get PID for later usage
#Check switches
if ($flag_h) { &printHelp }  #Print help and exit
if ( (!$opt_A) and (!$flag_C) and (!$flag_l) and (!$opt_D) and (!$flag_F)and (!$flag_V) and (!$opt_E)   ) { &printHelp } #Print help and exit

if ($opt_c) {
 $Conffile=$opt_c;
} else {
 $Conffile='psad2ipt.xml';
} 

###############################
&ReadConfig;   #Read $Conffile
###############################

my $geodatabase ="$confDBGeo";   #1.0.2
my ($drivergeo,$dsngeo,$useridgeo,$passwordgeo,$dbhg);

if($confDBGeo) {  #Load GeoDB if configured - 1.0.2
 $geodatabase ="$confDBGeo";
 #SQLite Init
 $drivergeo   = "SQLite";
 $dsngeo = "DBI:$drivergeo:dbname=$geodatabase";
 $useridgeo = "";
 $passwordgeo = "";
 $dbhg = DBI->connect($dsngeo, $useridgeo, $passwordgeo, { RaiseError => 1 }) or die $DBI::errstr; 
}

if ((!$confDBFile) or (length($confDBFile) eq 1)) { #Use application dir if -c is not given
 $database = "psad2ipt.db";
} else {
 $database = $confDBFile;
}

#SQLite Init
my $driver   = "SQLite";
my $dsn = "DBI:$driver:dbname=$database";
my $userid = "";
my $password = "";

#Get DB handles
my $dbh = DBI->connect($dsn, $userid, $password, { RaiseError => 1 }) or die $DBI::errstr;
my $dbhn = DBI->connect($dsn, $userid, $password, { RaiseError => 1 }) or die $DBI::errstr;

#Convert var to epoch time
$confLookBehind = time - ($confLookBehind * 24 * 60 * 60); #Sec of days
$confMaxLockDays=$confMaxLockDays * 24 * 60 * 60;

my $iptables='/sbin/iptables';  
my $WriteLog=1;
$tmpAppearance=0;
#Process control, note that the script could run multi threaded too but prefer to leave as single process
my $pntMax=10;  #Process increment, this is the max nuber of seconds to wait until a previous process has finished
my $myProc='psad2ipt.pl';


my $timeforever=1999999999;  #time used for blacklist enries - 1.0.3

#Open our todays logfile handle
if ($confUseSyslog ne "1"){
 open (FH, ">>" .$confLogPath.'/'. &logshortdate . "-psad2ipt.log") or die "cannot write psad2ipt.log";
} 
if (&GetStartupInfo){
 syslog("** Startup using parameter: $StartupInfo **");
}

if ($confUseSyslog ne "1"){  #If not syslog ..
 if ($confLogPath){ #if the log path is given
  if (!-d $confLogPath) {make_path($confLogPath)}  #Autcreate directory if not exist
 } 
} 
if ($confWhiteListPath) { #if the white list path is given
 if (!-d $confWhiteListPath) {make_path($confWhiteListPath)}  #Autcreate directory if not exist
}
if ($confBlackListPath) { #if the white list path is given
 if (!-d $confBlackListPath) {make_path($confBlackListPath)}  #Autcreate directory if not exist
}


if ($confWhiteListPath) { #if the white list path is given
 opendir(DIR, $confWhiteListPath);
 @sWhiteFiles = grep(/\.*$/,readdir(DIR));
 closedir(DIR);
 #syslog("Whitelist Path: $confWhiteListPath");
 &ReadWhiteList;
 if (!$cntWhitelist){$cntWhitelist=0}
} 

if ($confBlackListPath) { #if the black list path is given - 1.0.3
 opendir(DIR, $confBlackListPath);
 @sBlackFiles = grep(/\.*$/,readdir(DIR));
 closedir(DIR);
 &ReadBlackList;
 if (!$cntBlacklist){$cntBlacklist=0}
 for my $pnt ( sort keys %hBlackList ){	
  if (!check_ip($pnt)){ #Check for valid ip address, delete if not
 	 delete $hBlackList{$pnt};
 	 $cntBlacklist--;
  }
 } 
} 

#############
#   BEGIN
#############
$tmpTime=time;
if(!check_chain($confChain)){
 syslog("Invalid Chain: $confChain"); 
 exit;
}
if ($confLookBehind < 1) {
 syslog("LookBehind must be equal or grater then 1, better 7 or higher"); 
 exit;
}
if ($confOutsideInterface !~ /^eth/){
 syslog("Invalid Interface: $confOutsideInterface"); 
 exit;
}

###############################
#  Get Running Configuration
###############################
if (($opt_A) or ($flag_C) or ($opt_D) or ($flag_F)){  
 if (!&getRunningConfig,0) { #Get my running config with iptables
  syslog ("ERROR $suberr");
  if ($suberr =~ /No chain\/target\/match by that name/){
   syslog ("Please consider to create the CHAIN first by: $iptables -N $confChain");
  }
  exit; #=>Important, exit from here since our runtime enumeration failed!
 }   #if (&getRunningConfig)  #Get my running config with iptables
 
 
 for my $pntr (sort {$a <=> $b} keys %hRunningConfig){   	
  if (($opt_A) and ($hRunningConfig{$pntr} eq $opt_A)){
   syslog ("Attention: $opt_A is already on IPT");
   print "Attention: $opt_A is already on IPT\n";
   exit;
  }
 }


 #check for single proc usage, do this from here ...!
 ProcControl($myProc); #1.0.1 back on


 if (!$RuleCnt){$RuleCnt=0}

 if ($RuleCnt > $confMaxRules) {
  syslog("Maximum Rules reached! $RuleCnt/$confMaxRules ");
  print"Maximum Rules reached! $RuleCnt/$confMaxRules \n";
  exit;
 } 
 
 #DB: Get amount of records
 my $stmt = "select count(*) as expr from PSAD2IPT;";
 my $sql = $dbh->prepare($stmt);  
 $sql->execute ;   
 $RecordCount =$sql->fetchrow_array; 
 if (!$RecordCount){$RecordCount=0}

 #DB: Get amount of records
 $stmt = "select count(*) as expr from PSAD2IPT where DATE_EXPIRE>0;";
 $sql = $dbh->prepare($stmt);  
 $sql->execute ;   
 $RecordCountIPT =$sql->fetchrow_array; 
 if (!$RecordCountIPT){$RecordCountIPT=0}
 
 
 if ($confBlackListPath) { #if the black list path is given - 1.0.3
  syslog("Info: DBRecords=$RecordCount DBIPT=$RecordCountIPT/IPT=$RuleCnt Whitelist=$cntWhitelist Blacklist=$cntBlacklist");
 } else {
 	syslog("Info: DBRecords=$RecordCount DBIPT=$RecordCountIPT/IPT=$RuleCnt Whitelist=$cntWhitelist");
 } 
 
 my $myCnt=1;
 
 



 ##############################
 #  Check Blacklist     #1.0.3
 ##############################

 my ($stmt,$sql,$sqln,$stmtn);
 if (($confBlackListPath) and ($cntBlacklist>0)) { 
  #print "Check Blacklist against IPT \n";
  for my $pnt ( sort keys %hBlackList ){	
   $tmpfnd=undef; 
   #print "check $pnt\n";
   for my $pntr (keys %hRunningConfig){ 
    #print "check $pnt -- $hRunningConfig{$pntr}\n";	
    if ($pnt==$hRunningConfig{$pntr}){
     #print "found $pnt\n";	
     $tmpfnd=1;
     my $tmpsplunkstr;
     $stmt = "select IP,BL,Date_Expire from PSAD2IPT where IP='$pnt' and Date_EXPIRE > '0';";
     $sql = $dbh->prepare($stmt);  
     $sql->execute ;  
     (my ($SQLIP,$SQLBL,$SQLDate_Expire)=$sql->fetchrow_array);
     if (!$SQLIP){   #*** Note that we never should come down here, thats epxperimental ! *** 
      $tmpcc=GetCountryByIP($pnt);   
      #print "DEBUG Not Found in SQL - ADD $pnt -- $tmpcc\n ";
      $RecordCountIPT++;  #Inc. DB Record Counter
      if ($confDBGeo){  #1.0.2
       $stmt = "INSERT INTO PSAD2IPT (Date_Created,Date_Expire,IP,Source,PID,CC,BL) VALUES ($tmpTime, $timeforever, '$pnt', 'PSAD2IPT-BL', $tmpPid,'$tmpcc',1);";
       $tmpsplunkstr="M=P2I A=ADD2IPT LCK=1 W=0 IP=$pnt LTIME=".($tmpTime)." SEEN=-1 IPT=".($RecordCountIPT)." DBEXP=".$RecordCountIPT." DB=".$RecordCount ." CC=".$tmpcc ." BL=1";
      } else {  #if ($confDBGeo)
       $stmt = "INSERT INTO PSAD2IPT (Date_Created,Date_Expire,IP,Source,PID,BL) VALUES ($tmpTime, $timeforever, '$pnt', 'PSAD2IPT-BL', $tmpPid,1);";	
       $tmpsplunkstr="M=P2I A=ADD2IPT LCK=1 W=0 IP=$pnt LTIME=".($tmpTime)." SEEN=-1 IPT=".($RecordCountIPT)." DBEXP=".$RecordCountIPT." DB=".$RecordCount ." BL=1";
      }         #if ($confDBGeo)
      if (checkWhiteList($pnt)){
       syslog("ignore blacklist record due to whitelist - $pnt - " . checkWhiteList($pnt) . " - no overwrite!");
       last;
      } else {  #if (checkWhiteList($pnt))
       sendMsgToSplunk($tmpsplunkstr);	
       syslog("Add blacklisted record to DB: $pnt");
       my $sql = $dbh->prepare($stmt);  
       $sql->execute ;  
      }  #if (checkWhiteList($pnt))
     }   #if (!$SQLIP)
    }    #$if ($pnt==$hRunningConfig{$pntr})
   }     #for my $pntr (keys %hRunningConfig)	
   if (!$tmpfnd){
    if (checkWhiteList($pnt)){
     syslog("ignore blacklist record due to whitelist - $pnt - " . checkWhiteList($pnt) . " - no overwrite!");
     last;
    } else { #if (checkWhiteList($pnt))
     #print "Not found at all = $pnt\n";
     $tmpcc=GetCountryByIP($pnt);   
     #print "DEBUG Not Found in SQL - ADD $pnt -- $tmpcc\n ";
     if ($confDBGeo){  #1.0.2
      $stmt = "INSERT INTO PSAD2IPT (Date_Created,Date_Expire,IP,Source,PID,CC,BL) VALUES ($tmpTime, $timeforever, '$pnt', 'PSAD2IPT-BL', $tmpPid,'$tmpcc',1);";
      sendMsgToSplunk("M=P2I A=ADD2IPT LCK=1 W=0 IP=$pnt LTIME=".($tmpTime)." SEEN=-1 IPT=".($RecordCountIPT)." DBEXP=".$RecordCountIPT." DB=".$RecordCount ." CC=".$tmpcc ." BL=1");
     } else {
      $stmt = "INSERT INTO PSAD2IPT (Date_Created,Date_Expire,IP,Source,PID,BL) VALUES ($tmpTime, $timeforever, '$pnt', 'PSAD2IPT-BL', $tmpPid,1);";	
      sendMsgToSplunk("M=P2I A=ADD2IPT LCK=1 W=0 IP=$pnt LTIME=".($tmpTime)." SEEN=-1 IPT=".($RecordCountIPT)." DBEXP=".$RecordCountIPT." DB=".$RecordCount ." BL=1");
     }
     syslog("Add blacklisted (x) record to DB: $pnt");
     $RecordCountIPT++;  #Inc. DB Record Counter
     my $sql = $dbh->prepare($stmt);  
     $sql->execute ;  
     #Add to splunk!
     #syslog("Deby: $stmt -- $RecordCountIPT");
     #print "$stmt\n";
     #if (addRuleByConfig($confChain,$pnt,$tmpTime,$timeforever,$tmpcc)) {
     #  syslog("IPT: Success adding $pnt");
     ##  my $tmpVal=scalar %hRunningConfig;
     #  my $tmpVal=(keys %hRunningConfig)-1
     #  $tmpVal++;
     #  $hRunningConfig{$tmpVal}=$pnt;
     #  syslog("IPT: Records: $tmpVal");
     # }  else {
     #  syslog("IPT: Error adding $pnt");
     # }
    }   #if (checkWhiteList($pnt))
   }    #if (!$tmpfnd)
  }     #for my $pnt ( sort keys %hBlackList ){	
 }      #if (($confBlackListPath) and ($cntBlacklist>0)) 
 if ($confBlackListPath) { 
  #Check blacklisted records against inventory
  $stmt = "select IP,BL,Date_Expire from PSAD2IPT where DATE_EXPIRE > '0' and BL=1;";
  $sql = $dbh->prepare($stmt);  
  $sql->execute ;  
  my %hTmpIP;
  while (my ($SQLIP,$SQLBL,$SQLDate_Expire)=$sql->fetchrow_array) {
 	 if ($SQLIP == $hBlackList{$SQLIP}){
 		#print "IP is on blacklist = $SQLIP\n";
 	 } else {
 	 	#print "IP blacklist has gone = $SQLIP => Delete!\n";
    $hTmpIP{$SQLIP}=$SQLIP;
 	 }
  }
  for my $pnt (keys %hTmpIP){  
   #print "Delete $pnt\n";
   $stmt = "update PSAD2IPT set Date_Expire='0' where IP='$pnt';";
   #syslog("deb z $stmt");
   $RecordCountIPT--;  #Dec. record for later usage
   syslog("Delete expired blacklist ip: $pnt");
   #sendMsgToSplunk("M=P2I A=DELETE LCK=0 W=0 IP=$pnt BL=1");  #Will get end later
   $sql = $dbh->prepare($stmt);  
   $sql->execute ;  
   #for my $pntr (keys %hRunningConfig){   #1.0.1 No sort - sort is not be better ...
   # if ($pnt == $hRunningConfig{$pntr}) {
   #  if (deleteRuleByChainAndNumber($confChain,$pntr)){ #Delete Rule!!!
   #   $RecordCountIPT--;
   #   $CurRuleCnt=$RuleCnt-1;
   #   sendMsgToSplunk("M=P2I A=EXPIRE W=0 LCK=0 IP=$hRunningConfig{$pntr} IPT=".$CurRuleCnt." DBEXP=".$RecordCountIPT." DB=".$RecordCount." BL=1");
   #   syslog("IPT: BL Rule has been deleted (LP) $pntr => $hRunningConfig{$pntr} BL");
   #   delete $hRunningConfig{$pntr};
   #  	if (!&getRunningConfig) { #Get my running config with iptables
   #    syslog ("ERROR X3 $suberr");
   #   }
   #  last; #YES!!!!
   #  }	
   # }
   #}
  }	
 }
######################## 
#exit;
}  #if (($opt_A) or ($flag_C) or ($opt_D) or ($flag_F))
#Base configuration load complete




####################
#  Check Database
####################
if ($flag_C) {  #Check for outdated records
 ###########################
 #DB: Delete expired records
 ###########################
 my $tmpActivity;
 my $stmt = "select count(*) as expr,IP from PSAD2IPT where Date_Expire <> 0 and  Date_Expire < $tmpTime group by IP order by expr desc;";
 #syslog("SQLITE Query: $stmt");
 my $sql = $dbh->prepare($stmt);  
 $sql->execute ;  
 my $myCnt;
 while (my ($SQLCNT,$SQLIP)=$sql->fetchrow_array){
  #syslog("Res: $SQLCNT-,-$SQLIP - $tmpTime");
  $myCnt++;
  if ($SQLCNT >= $confMaxEvents) {  #Allowance to block
   if ($myCnt > 1){
   	syslog("IPT: Reload RunningConfig since there was a change");
   	if (!&getRunningConfig) { #Get my running config with iptables
     syslog ("ERROR X1 $suberr");
    }
   }
   syslog("DB: Delete expired record: $SQLCNT,$SQLIP - $tmpTime");
   my $stmtn = "update PSAD2IPT set Date_Expire = 0 where IP='$SQLIP' and Date_Expire <> 0 and  Date_Expire < $tmpTime;";
   my $rv = $dbh->do($stmtn) or die $DBI::errstr;
   $tmpActivity=1;
   #Make me better!!!!
   #for my $pntr (sort {$b <=> $a} keys %hRunningConfig){   #Descending sort to keep line orders for deleting save!!
   for my $pntr (keys %hRunningConfig){   #1.0.1 No sort - sort is not be better ...
    if ($SQLIP == $hRunningConfig{$pntr}) {
     if (deleteRuleByChainAndNumber($confChain,$pntr)){ #Delete Rule!!!
      $RecordCountIPT--;
      #$RuleCnt--;
      $CurRuleCnt=$RuleCnt-1;
      sendMsgToSplunk("M=P2I A=EXPIRE W=0 LCK=0 IP=$hRunningConfig{$pntr} IPT=".$CurRuleCnt." DBEXP=".$RecordCountIPT." DB=".$RecordCount);
      syslog("IPT: Rule has been deleted (LP) $pntr => $hRunningConfig{$pntr}");
      delete $hRunningConfig{$pntr};
      last; #YES!!!!
     }	else {
      syslog("Error deleting rule XX $pntr - $hRunningConfig{$pntr} -> ErrStr: $suberr");
     }
    }
   }
  }
 }    #while (my ($SQLCNT,$SQLIP)=$sql->fetchrow_array)
 ###################################################################
 #Sync: Delete any running entry if it is not found in our inventory
 ###################################################################
 if ($RecordCountIPT ne $CurRuleCnt){  #double check IPT records
 	$tmpActivity=1;
  syslog("***Sync from DB to IPT*** $RecordCountIPT/$RuleCnt");
  for my $pntr (sort {$b <=> $a} keys %hRunningConfig){   #Descedning sort to keep line orders for deleting save!!
   my $stmt = "select count(*) as expr from PSAD2IPT where IP = '$hRunningConfig{$pntr}' and Date_Expire <> 0;";
   #syslog("SQLITE Query: $stmt");
   my $sql = $dbh->prepare($stmt);  
   $sql->execute ;  
   my $tmpRec =$sql->fetchrow_array;
   #syslog("Res: $tmpRec");
   if ($tmpRec == 0) { 
  	#syslog("Delete - $pntr -  $hRunningConfig{$pntr}");
  	if (check_ip($hRunningConfig{$pntr})) {  #1.0.1
  	 syslog("Delete - $pntr -  $hRunningConfig{$pntr}");
     if (deleteRuleByChainAndNumber($confChain,$pntr)){ #Delete Rule!!!
      #$RuleCnt--;
      $CurRuleCnt=$RuleCnt-1;
      sendMsgToSplunk("M=P2I A=DELETE W=0 LCK=0 IP=$hRunningConfig{$pntr} IPT=".$CurRuleCnt." DBEXP=".$RecordCountIPT." DB=".$RecordCount);
      syslog("IPT: Rule has been deleted $pntr => $hRunningConfig{$pntr}");
      delete $hRunningConfig{$pntr};
      #last; #Exit from here?
     } else {
      syslog("Error deleting rule YY $pntr - $hRunningConfig{$pntr} -> ErrStr: $suberr");
     }
    }
   }
  }
 }
 
 if ($RecordCountIPT ne $CurRuleCnt){  #1.0.1 Double check DB records
  my $stmt = "select count(*) as dbcount,IP,Date_Created,Date_Expire,CC from PSAD2IPT where  Date_Expire > 1 group by IP order by dbcount desc;";
  syslog("DB: $stmt");
  my $sql = $dbh->prepare($stmt);  
  $sql->execute ;  
  my ($tmpDateC,$tmpDateE);
  %dbDuplicate=undef;
  while (my ($SQLCNT,$SQLIP,$SQLDC,$SQLDE,$SQLCC)=$sql->fetchrow_array){
   #syslog("Inv: $SQLCNT $SQLIP");
   $tmpfnd=undef;
   for my $pntr (sort {$a <=> $b} keys %hRunningConfig){   
    if ($hRunningConfig{$pntr} eq $SQLIP){
     $tmpfnd=1;
     last;
    } 
   } 
   if (!$tmpfnd) {
    if ($SQLCNT >= $confMaxEvents) {  #Allowence to block - ???
     syslog("Sync from DB to IPT: $SQLIP");
     if (addRuleByConfig($confChain,$SQLIP,$SQLDC,$SQLDE,$SQLCC)) {
      syslog("IPT: Success adding $SQLIP");
      #my $tmpVal=scalar %hRunningConfig;   #1.0.3
      my $tmpVal=(keys %hRunningConfig)-1;  #hash starts with id 0 while ipt starts with 1, thats why we do minus 1  - 1.0.3
      $tmpVal++;
      $hRunningConfig{$tmpVal}=$SQLIP;
      syslog("IPT: Records: $tmpVal");
     }  else {
      syslog("IPT: Error adding $SQLIP");
     }
    }
   }
 
   if ($SQLCNT>1){ #??????? 1.0.3
  	syslog("WARNING: Duplicate IP - $SQLCNT/$SQLIP/$SQLDE ");
    $dbDuplicate{$SQLIP}=$SQLDE;
   }
  }  #while
 
  for my $pnt (keys %dbDuplicate){   
   if (check_ip($pnt)) {  
    #syslog("\tDelete $pnt $dbDuplicate{$pnt}");
    my $stmt = "delete from PSAD2IPT where IP='$pnt' and Date_Expire = '$dbDuplicate{$pnt}';";
    syslog("\tDB: $stmt");
    my $rv = $dbh->do($stmt) or die $DBI::errstr;
   } 
  }  #for my $pnt (keys %dbDuplicate)
 } else {  #if ($RecordCountIPT ne $CurRuleCnt)
 	if(!$tmpActivity){
 	 syslog("Nothing to do");
 	} 
 	
 }   #if ($RecordCountIPT ne $CurRuleCnt)  #1.0.1 Double check DB records
}    #if ($flag_C) 


############################
#   Add address and block
############################
my ($tmprcnt,$stskip,$geo_num);
if ($opt_A) {  #Feeding psad2ipt
 if ($CurRuleCnt < $confMaxRules) {
  if (check_ip($opt_A)) {  
   #Check the ip address for whitelisting
   if (checkWhiteList($opt_A)){
    syslog("This address is whitelisted: $opt_A");
    sendMsgToSplunk("M=P2I A=ADD2IPT LCK=0 W=1 IP=$opt_A LTIME=".($tmpLockTime * 60)." SEEN=$tmpAppearance IPT=".($RuleCnt)." DBEXP=".$RecordCountIPT." DB=".$RecordCount);
    exit;
   }
   if ( $confMaxRecords < $RecordCount){
   	syslog("Max records reached:  $confMaxRecords > $RecordCount => Abort");
   	print "Max records reached:  $confMaxRecords > $RecordCount => Abort\n";
   	exit;
   }
   $tmpcc=GetCountryByIP($opt_A);   
   
   $tmprcnt=1;
   #print "Add mode $opt_A == $RuleCnt / $RecCnt / $confMaxRules\n";	
   my $stmt = "select count(*) as expr from PSAD2IPT where IP = '$opt_A' and DATE_EXPIRE=0 AND DATE_CREATED > $confLookBehind"; #Get the amount of incidents
   #my $stmt = "select count(*) as expr from PSAD2IPT where IP = '$opt_A' and DATE_EXPIRE=0";
   #syslog("SQLITE Query: $stmt");
   my $sql = $dbh->prepare($stmt);  
   $sql->execute ;  
   my ($tmpDateC,$tmpDateE);
   while (my ($SQLEXPR)=$sql->fetchrow_array){
    $tmpAppearance=$SQLEXPR;
    if (($confDBGeo) and ($confDBUseUseCountryList)) {  #Overwrite by country 1.0.2
     for my $pnt ( sort keys %hCountryRule ) {
      #print "$pnt $hCountryRule{$pnt} $tmpcc\n";
      if ($pnt eq $tmpcc){
       #print "***MATCH COUNTRY IP*** $tmpcc=$hCountryRule{$pnt} \n";
       syslog ("***  Apply block time by country: $pnt = $hCountryRule{$pnt} Min.  ***");
       ########################################
       #*** Important change happens here **** 
       $confBaseLock=$hCountryRule{$pnt};
       ########################################
       last;
      }  #if ($pnt eq $tmpcc)	
     }   #for my $pnt ( sort keys %hCountryRule ) 
    }    #if (($confDBGeo) and ($confDBUseUseCountryList)) {  Overwrite by country 1.0.2
    
    #########################################################  
    #*** Important calclation happens here **** 
    $tmpLockTime = ( 2 ** ($tmpAppearance)) * $confBaseLock;
    #########################################################
    last;
   }     #while (my ($SQLEXPR)=$sql->fetchrow_array)
   syslog(" Seen: $tmpAppearance / Lock: $tmpLockTime Min.");
   ################################################################
   #*** Add the expire date *** Important change happens here ***
   $timeoffset=$tmpTime + ($tmpLockTime * 60); #Add and convert to minutes
   ################################################################
   #Check if ipt_A is already locked or about to be locked
   #my $stmt = "select count(*) as expr from PSAD2IPT where IP = '$opt_A' and Date_Expire > $tmpTime;";
   #my $stmt = "select count(*) as expr from PSAD2IPT where IP = '$opt_A' and Date_Expire > 0;";
   my $stmt = "select count(*) as expr from PSAD2IPT where IP = '$opt_A' and Date_Expire > $confLookBehind;";
   syslog("DB: $stmt");
   my $sql = $dbh->prepare($stmt);  
   $sql->execute ;  
   $tmprcnt=$sql->fetchrow_array;
   #print "$opt_A / $tmprcnt / $confMaxEvents\n";
   if ($tmprcnt <= $confMaxEvents) {  #Allow this address to get locked
    if ($timeoffset > ($tmpTime + $confMaxLockDays)) {
     my $tmpDateC=strftime("%d-%m-%Y %H:%M:%S", localtime($tmpTime + $confMaxLockDays));
     my $tmpDateE=strftime("%d-%m-%Y %H:%M:%S", localtime($timeoffset));
     $timeoffset = ($tmpTime + $confMaxLockDays);
     $tmpLockTime=($confMaxLockDays/60); #Overwrite due to limit, konvert to minutes
     syslog ("Limit expire time to $tmpDateC instead of $tmpDateE");
     print "Limit expire time to $tmpDateC instead of $tmpDateE - $timeoffset\n";
    }
    my $stmt;
    if ($confDBGeo){  #1.0.2
     $stmt = "INSERT INTO PSAD2IPT (Date_Created,Date_Expire,IP,Source,PID,CC,BL) VALUES ($tmpTime, $timeoffset, '$opt_A', 'PSAD2IPT', $tmpPid,'$tmpcc',0);";
    } else {
     $stmt = "INSERT INTO PSAD2IPT (Date_Created,Date_Expire,IP,Source,PID,CC,BL) VALUES ($tmpTime, $timeoffset, '$opt_A', 'PSAD2IPT', $tmpPid,'0',0);";	
    }
    #print "$stmt\n";
    my $rv = $dbh->do($stmt) or die $DBI::errstr;
    syslog("DB: $stmt");
    $RecordCountIPT++;
    if ($confDBGeo){  #1.0.2
     sendMsgToSplunk("M=P2I A=ADD2IPT LCK=1 W=0 IP=$opt_A LTIME=".($tmpLockTime)." SEEN=$tmpAppearance IPT=".($RuleCnt+1)." DBEXP=".$RecordCountIPT." DB=".$RecordCount ." CC=".$tmpcc);
    } else {
     sendMsgToSplunk("M=P2I A=ADD2IPT LCK=1 W=0 IP=$opt_A LTIME=".($tmpLockTime)." SEEN=$tmpAppearance IPT=".($RuleCnt+1)." DBEXP=".$RecordCountIPT." DB=".$RecordCount);
    } 
    if (addRuleByConfig($confChain,$opt_A)) {
     syslog("IPT: Success adding $opt_A");
    }  #if (addRuleByConfig($confChain,$opt_A)) 
   } else {
     syslog ("Attention: $opt_A is already on IPT");
     print "Attention: $opt_A is already listed in our database\n";
     exit;
   }    #if ($tmprcnt >= $confMaxEvents)   #Allow this address to get locked
  } else {
   print "Option -A requires a valid IP Address: $opt_A\n";	
   exit;
  }
 } else {
  syslog("Maximum Rules reached! $confMaxRules ");
 } #if ($RuleCnt < $confMaxRules) 
  exit;
}   #if ($opt_A) = Feeding psad2ipt


#################
# List database
#################

if ($flag_l){  
 my $stmt; 
 if ($opt_i) {
  if (check_ip($opt_i)) {  #1.0.3 - ADD BL Field
   $stmt = "select DATE_CREATED,DATE_EXPIRE,IP,CC,BL from PSAD2IPT where IP='$opt_i';";
   if ($opt_e) {  #Show Locked Addresses
    $stmt = "select DATE_CREATED,DATE_EXPIRE,IP,CC,BL from PSAD2IPT where IP='$opt_i' and Date_Expire > 0;";
   }	
  }
 } else {
  $stmt = "select DATE_CREATED,DATE_EXPIRE,IP,CC,BL from PSAD2IPT;";
  if ($opt_e) {   #Show Locked Addresses
   $stmt = "select DATE_CREATED,DATE_EXPIRE,IP,CC,BL from PSAD2IPT where Date_Expire > 0;";
  }
 }
 #syslog("SQLITE Query: $stmt");
 my $sql = $dbh->prepare($stmt);  
 $sql->execute ;  
 my ($tmpDateC,$tmpDateE);
 my $sout=sprintf("%-16s","IP");
 if ($confDBGeo){  #1.0.2
  print "$sout\tCountry\tCreated\t\t\tExpires\n";
 } else {
 	print "$sout\tCreated\t\t\tExpires\n";
 } 
 
 while (my ($SQLDATE_CREATED,$SQLDATE_EXPIRE,$SQLIP,$SQLCC,$SQLBL)=$sql->fetchrow_array){
  $tmpDateC=strftime("%d-%m-%Y %H:%M:%S", localtime($SQLDATE_CREATED));
  if ($SQLDATE_EXPIRE > 0 ){
   if ($SQLBL == 1){
    $tmpDateE="Blacklisted";
   } else {	
    $tmpDateE=strftime("%d-%m-%Y %H:%M:%S", localtime($SQLDATE_EXPIRE));
   } 
   #$tmpCC=
  } else {
   $tmpDateE=0;
  } 
  my $sout=sprintf("%-16s",$SQLIP);
  #aloa
  if ($confDBGeo){
   print "$sout\t$SQLCC\t$tmpDateC\t$tmpDateE\n";
  } else {
   print "$sout\t$tmpDateC\t$tmpDateE\n";
  } 
 }
 exit;
}

if ($opt_E) {
 my $stmt;
 if ($opt_E > $confLookBehindOrig) {
  $stmt = "delete from PSAD2IPT where Date_Created < '" . ($tmpTime - ($opt_E*60*60*24)). "';";  
  if ($flag_k){
   $stmt = "delete from PSAD2IPT where Date_Created < '" . ($tmpTime - ($opt_E*60*60*24)). "' and DATE_EXPIRE=0;";  
  }
  my $rv = $dbh->do($stmt) or die $DBI::errstr;
  syslog("DB: $stmt");
 } else {
  syslog("Number of days must be higher then LookBehind -> $opt_E vs $confLookBehindOrig");
  print "Number of days must be higher then LookBehind -> $opt_E vs $confLookBehindOrig\n";
 }
}

################
# Flush database
################
my $stmt;
if ($flag_F){  
 $stmt = "delete from PSAD2IPT;";
 if ($flag_k){
 	$stmt = "delete from PSAD2IPT where DATE_EXPIRE=0;";
 }
 my $rv = $dbh->do($stmt) or die $DBI::errstr;
 syslog("Flush database -> $stmt");
 for my $pntr (sort {$b <=> $a} keys %hRunningConfig){   #Descedning sort to keep line orders for deleting save!!
  if (deleteRuleByChainAndNumber($confChain,$pntr)){ #Delete Rule!!!
   syslog("IPT: Rule has been deleted (flush) $pntr => $hRunningConfig{$pntr}");
   delete $hRunningConfig{$pntr};
  } else {
   syslog("Error deleting rule $pntr - $hRunningConfig{$pntr} -> ErrStr: $suberr");
  }
 }
 exit;
}

#####################
# Delete Record by IP
#####################
if ($opt_D){  
 if (check_ip($opt_D)) {  
  my $stmt = "delete from PSAD2IPT where IP='$opt_D';";
  my $rv = $dbh->do($stmt) or die $DBI::errstr;
  syslog("DB: Delete $opt_D");
  for my $pntr (sort {$b <=> $a} keys %hRunningConfig){   #Descedning sort to keep line orders for deleting save!!
   #print "DEB: $pntr eq $opt_D \n";
   if ($hRunningConfig{$pntr} eq $opt_D){  #Delete rule if it exists
    if (deleteRuleByChainAndNumber($confChain,$pntr)){ #Delete Rule!!!
     syslog("IPT: Rule has been deleted (del) $pntr => $hRunningConfig{$pntr}");
     delete $hRunningConfig{$pntr};
    }	else {
     syslog("Error deleting rule $pntr - $hRunningConfig{$pntr} -> ErrStr: $suberr");
    }
   }
  }
 } else {
  print "Option -D requires a valid IP Address: $opt_D\n";	
 }
 exit;
}


if ($flag_V) {
 my $stmt = "VACUUM psad2ipt;";
 my $rv = $dbh->do($stmt) or die $DBI::errstr;
 syslog("DB: $stmt\n");
}

exit;


#############
#    END
#############


sub addRuleByConfig () {
 #$_[0] = Chain
 #$_[1] = Number
 #$_[2] = Optional Date_Created
 #$_[3] = Optional Date_Expired
 #$_[4] = Optional CountryCode
 my ($tldate,$stmpcomment,$tldaten);

if ( (!$_[2]) and (!$_[3]) ) {
 $tldate = strftime("%Y-%m-%d %H:%M:%S", localtime(time));
 
 $stmpcomment="P2I add at $tldate";
 if ($timeoffset) {
  $tldaten = strftime("%Y-%m-%d %H:%M:%S", localtime($timeoffset));
  $stmpcomment.=" to $tldaten";
 }
} else {
 $tldate = strftime("%Y-%m-%d %H:%M:%S", localtime($_[2]));
 $stmpcomment="P2I sync at $tldate";
 
 if ($_[3]) {
  $tldaten = strftime("%Y-%m-%d %H:%M:%S", localtime($_[3]));
  $stmpcomment.=" to $tldaten ";
 }
 if ($_[4]) { #1.0.2
  $stmpcomment.=" $_[4] "; 
 } 
}
 #$stmpcomment.="==$_[2]=$_[3]==";
 my ($stdout);
 if (!check_ip($_[1])){return undef;}
 syslog ( "IPT: Apply: " . $iptables . "-I " . $_[0] . " -i " . $confOutsideInterface ." -s " .$_[1] . " -m " . " state" . " --state" . " NEW" . " -j" . " DROP"  . " -m " . " comment" . " --comment " .$stmpcomment );
 run ([ $iptables , "-I", $_[0], "-i", $confOutsideInterface,"-s",$_[1],"-m","state","--state","NEW","-j","DROP","-m","comment","--comment",$stmpcomment  ], ">", \$stdout, '2>', \$suberr) or return undef;  
 return 1;
}

sub getRunningConfig () {
 #$_[0] = 1 - don't print INV 
 #Enumerate chain $confChain, return boolean status
 #Sample output:
 #Chain P2F (1 references)
 #num  target     prot opt source               destination
 #1    DROP       all  --  7.7.7.4              0.0.0.0/0
 #2    DROP       all  --  7.7.7.3              0.0.0.0/0
 my ($scnt,$stdout);
 $suberr=undef; #reset error string
 %hRunningConfig=undef;
 %hRunningConfigRAW=undef;
 $RuleCnt=undef;
 $CurRuleCnt=undef;
 run ([ $iptables , "-nL", $confChain, "--line-numbers" ], ">", \$stdout, '2>', \$suberr) or return undef;  
 my @scRec = split(/\n/,$stdout); #build scalar by spliting \n (CR)
 #Expected outut once:
 #num  target     prot opt source               destination
 if (($stdout!~/num/) or ($stdout!~/target/) or ($stdout!~/prot/) or ($stdout!~/opt/) or ($stdout!~/destination/) ) {
  return undef; #otherwise report error ...
 }
 foreach (@scRec){ #Enum rules within chain $confChain
  $scnt++;
  #print "$scnt/$RuleCnt => $_\n";
  #1    DROP       all  --  7.7.7.4              0.0.0.0/0
  if (/^(\d+)(\s+)DROP(\s+)all(\s+)\-\-(\s+)(\d+)\.(\d+)\.(\d+)\.(\d+)/){
   #print "\t Nbr $1 $2 $3 $4  ->$6-$7-$8-$9<-\n";
   $hRunningConfig{$1}="$6.$7.$8.$9";
   $RuleCnt++;
   #syslog("DEB $RuleCnt => $1 $_");
   if ($_[0] eq 1){
    syslog("\tIPT-INV =>$RuleCnt,$1,$hRunningConfig{$1}");
   } 
  # } else {
  #	print "$RuleCnt => $_\n";
  }
 }  #foreach (@scRec) #Enum rules within chain $confChain
 #syslog("\tTotCount: $RuleCnt");
 
 $CurRuleCnt=$RuleCnt; #1.0.1 #$RuleCnt is needed to mirror the actual counter needed to do an exact ipt delete, $CurRuleCnt count up or down.
 
 my $chkFlag;
 for my $pntr (keys %hRunningConfig){    #Sanity - Double check IPT for duplicate addresses, delete if exist  1.0.1
 	if ($hRunningConfigRAW{$hRunningConfig{$pntr}}){
 	 syslog("WARNING: This IP record does already exist=$hRunningConfig{$pntr} / $hRunningConfigRAW{$hRunningConfig{$pntr}} / $pntr / $confChain");
   if (deleteRuleByChainAndNumber($confChain,$pntr)){ #Delete Rule!!!
    syslog("IPT: Rule has been deleted (iptload) $pntr => $hRunningConfig{$pntr}");
    $chkFlag=1;
    last;
   } else {
    syslog("Error deleting rule $pntr - $hRunningConfig{$pntr} -> ErrStr: $suberr");
   }
 	} else {
 	 #syslog("ADD $hRunningConfigRAW{$hRunningConfig{$pntr}} $pntr");
 	 $hRunningConfigRAW{$hRunningConfig{$pntr}}=$pntr;
  } 
 }
 if ($chkFlag) { &getRunningConfig };  #1.0.1 - call back ourself if duplicate records were found
 
 return 1; 
}   #sub  




sub syslog {
  if ($WriteLog) {
    if ($confUseSyslog ne "1"){
     print FH &logdate ." " . $$ . " $_[0]\n";
    } else {
     my $s=new Net::Syslog(Facility=>'local4',Priority=>'debug',SyslogPort=>514,SyslogHost=>'127.0.0.1');
     $s->send($_[0],Priority=>'info');
    }
    if ($flag_v){
     print "$_[0]\n";
    } 
  }
}

sub logdate {
 my $tldate = strftime("%d-%m-%Y %H:%M:%S", localtime(time));
 return $tldate;
}

sub logshortdate {
 my $tldate = strftime("%Y-%m-%d", localtime(time));
 return $tldate;
}

sub ProcControl {
 #Process control, note that the script could run multi threaded too but prefer to leave as single process
 my $sumProc;
 while (1) {
  $sumProc=checkRunningProcess($_[0]);
  if ($sumProc > 1) {
   $pcnt++;
   syslog ("startup control (>=$sumProc):  $pcnt/$pntMax, please standby");    
   print "startup control (>=$sumProc):  $pcnt/$pntMax, please standby\n";    
   if ($pcnt > $pntMax) {
    syslog("startup give up!");      
    print "startup give up\n";
    exit;
   };
  } else {
   last;
  }
  sleep 1;
 }
 if (!$sumProc) {
  #syslog("OK");
 } else {
  if ($sumProc > 1) {
   syslog("WARNING: Amount of myProc = $sumProc");
  } 
 }
 $pcnt=0;    #1.0.4
 while (1) { #1.0.4
  if (-e 'psad2ipt.lck'){
 	 syslog("WARNING: Lockfile exist (psad2ipt.lck) - $pcnt / $pntMax");
   $pcnt++;
   if ($pcnt > $pntMax) {
    syslog("startup give up!");      
    print "startup give up\n";
    exit;
   };
   sleep 1;
  } else {  
   last;
  } #if (-e 'psad2ipt.lck')
 }  #while   #1.0.4
}   #Sub ProcControl


sub checkRunningProcess {
 my ($t,$p,$tcnt);
 $t = new Proc::ProcessTable;
 foreach $p (@{$t->table}){   
  if ($p->fname =~ /$_[0]/) {
   $tcnt++;
  }
  #print $p->fname . "\n";
 }
 if ($tcnt) {
  return $tcnt;
 } else {
  return undef;
 }
}  #checkRunningProcess


sub check_ip () {
 #Check if $_[0] is a valid ip address to allow us to block
 my $tmatch;
 if ($_[0] =~ /^127\.0/){return undef;}
 if ($_[0] =~ /^10\./){return undef;}
 if ($_[0] =~ /^172\.22/){return undef;}
 if ($_[0] =~ /^192\.168/){return undef;}
 if ($_[0] =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/){
  $tmatch=1;
  #print "=> $1 $2 $3 $4\n";
  if (length($1)>3){return undef;}
  if (length($2)>3){return undef;}
  if (length($3)>3){return undef;}
  if (length($4)>3){return undef;}
  if ($1<0){return undef;}
  if ($2<0){return undef;}
  if ($3<0){return undef;}
  if ($4<0){return undef;}
  if ($1>255){return undef;}
  if ($2>255){return undef;}
  if ($3>255){return undef;}
  if ($4>255){return undef;}
  if ($1 == 0){return undef;}
  if ($4 == 0){return undef;}
 }
 if(!$tmatch){return undef;}
 return 1;
}  #sub check_ip ()

sub check_chain () {
 if (uc($_[0]) eq "INPUT"){return undef;}
 if (uc($_[0]) eq "OUTPUT"){return undef;}
 if (uc($_[0]) eq "FORWARD"){return undef;}
 if (length($_[0]) <= 1){return undef;}
 return 1;
}  #sub check_chain


sub ReadConfig {
 my $xml = new XML::Simple (KeyAttr=>[]); # create XML object
 my $data = $xml->XMLin($Conffile);
 $confUseSyslog=$data->{System}->{UseSyslog};
 $confLogPath=$data->{System}->{LogPath};
 $confChain=$data->{System}->{Chain};
 $confOutsideInterface=$data->{System}->{OutsideInterface};
 $confMaxEvents=$data->{System}->{MaxEvents};
 $confMaxRules=$data->{System}->{MaxRules};
 $confWhiteListPath=$data->{System}->{WhiteListPath};
 $confBlackListPath=$data->{System}->{BlackListPath}; #1.0.3
 $confBaseLock=$data->{System}->{BaseLock};
 $confLookBehind=$data->{System}->{LookBehind};
 $confLookBehindOrig=$confLookBehind;
 $confSplunkServer=$data->{System}->{SplunkServer};
 $confDBFile=$data->{System}->{DBFile};
 $confMaxLockDays=$data->{System}->{MaxLockDays};
 $confMaxRecords=$data->{System}->{MaxRecords};
 $confDBGeo=$data->{System}->{DBGeo}; #1.0.2
 $confDBUseUseCountryList=$data->{System}->{UseCountryListDBGeo}; #1.0.2
 if (($confDBGeo eq "") or (length($confDBGeo) == 0) or ($confDBGeo eq "0")){
  $confDBGeo=undef;
 }
 
 if ($confDBGeo) { #1.0.2
 	if ($confDBUseUseCountryList) { 
   my $data = eval {XMLin($Conffile,ForceArray => 1)};
   for( @{$data->{Country}}){
    $hCountryRule{$_->{Code}}=$_->{BaseLock};
   }
  } 
 }
}


sub ReadWhiteList () {
 #Read WhiteList Files
 foreach (@sWhiteFiles){
  if ((!/^\.$/) and (!/^\.\.$/) and (/\.conf$/)) {
   #print "$_ \n";
   #open WH, 	$confWhiteListPath
   open (WH, "<" .$confWhiteListPath.'/'. $_) or die "cannot open $_"; 		
   while (<WH>){
   chomp;
   s/#.*//;             #No Comments #
   s/'.*//;             #No Comments '
   s/^\s+//;            #No leading spaces
   s/\s+$//;            #No trailing spaces
   s/;.*//;             #No Comments ; 
   #print "$_\n";
   #Keep this order.
   if (/^(\d+)\.(\d+)\.(\d+)\.(\d+)/) {
    $hWhiteList{"$1\.$2\.$3\.$4"}="$1\.$2\.$3\.$4";
    $cntWhitelist++;
   } else {
    if (/^(\d+)\.(\d+)\.(\d+)/) {
     $hWhiteList{"$1\.$2\.$3"}="$1\.$2\.$3";
     $cntWhitelist++;
    } else {
     if (/^(\d+)\.(\d+)/) {
      $hWhiteList{"$1\.$2"}="$1\.$2";
      $cntWhitelist++;
     } else {
      if (/^(\d+)\./) {
       $hWhiteList{"$1\."}="$1\.";
       $cntWhitelist++;
      }
     }
    }
   } 
  }
 }  #while (<WH>) 
  close WH;
 }
}

sub ReadBlackList () {
 #Read WhiteList Files
 my $tmpip;
 foreach (@sBlackFiles){
  if ((!/^\.$/) and (!/^\.\.$/) and (/\.conf$/)) {
   open (BH, "<" .$confBlackListPath.'/'. $_) or die "cannot open $_"; 		
   while (<BH>){
   chomp;
   s/#.*//;             #No Comments #
   s/'.*//;             #No Comments '
   s/^\s+//;            #No leading spaces
   s/\s+$//;            #No trailing spaces
   s/;.*//;             #No Comments ; 
   #print "$_\n";
   #Keep this order.
   if (/^(\d+)\.(\d+)\.(\d+)\.(\d+)/) {
   	$tmpip="$1\.$2\.$3\.$4";
    $hBlackList{$tmpip}=$tmpip;
    #print "Add $tmpip\n";
    $cntBlacklist++;
   } 
  }
 }  #while (<WH>) 
  close BH;
 }
}

sub checkBlackList{
 for my $pnt ( sort keys %hBlackList ){	
  if ($_[0] =~ /$pnt/){
   return 1;
  }
 }
 return undef;
}


sub checkWhiteList{
 for my $pnt ( sort keys %hWhiteList ){	
  #syslog("$_[0] - $pnt");
  if ($_[0] =~ /$pnt/){
   return 1;
  }
 }
 return undef;
}


sub printHelp () {
 print "Usage: psad2ipt.pl \n";
 print "\t-c <path/name> Optional path to configuration file (psad2ipt.xml), used for crontab\n";
 print "\t-l List DB records \n";
 print "\t\t-i <ip address> list with -l \n";
 print "\t\t-e Show only locked records which are loaded on ipt\n";
 print "\t-v Verbose output\n";
 print "\t-A <ip address> Add IP address to ipt and psad2ipt \n";
 print "\t-C Check database for expired records then syncronize with ipt and vice versa\n";
 print "\t-D <ip address> Delete IP address from ipt and psad2ipt\n";
 print "\t-E <number> Delete expired records older then the given number of days \n";
 print "\t\t-k keep current locked ip addresses \n";
 print "\t-F Flush database and rules \n";
 print "\t\t-k keep current locked ip addresses \n";
 print "\t-V Vacuum/Shrink the database\n";
 exit;
}


sub sendMsgToSplunk () {
  #ToDo: Add error handling ...
  syslog("Splunk: $_[0]");
  if ($confSplunkServer ne "0"){
   my $s=new Net::Syslog(Facility=>'local4',Priority=>'debug',SyslogPort=>514,SyslogHost=>$confSplunkServer);
   $s->send($_[0],Priority=>'info');
  } 
}

sub GetStartupInfo () {
 
 if ($opt_A){
 	$StartupInfo="-A $opt_A ";
 	return 1;
 }
 if ($flag_C){
 	$StartupInfo="-C ";
 	return 1;
 }
 if ($opt_D){
 	$StartupInfo="-D $opt_D ";
 	return 1;
 }
 if ($flag_F){
 	$StartupInfo="-F ";
 	return 1;
 }
 if ($flag_V){
 	$StartupInfo="-V ";
 }
 if ($opt_E){
 	$StartupInfo.="-E $opt_E ";
 	if ($flag_k){
 		$StartupInfo="-k " . $StartupInfo;
 	}	
 }
 return undef;
}


sub deleteRuleByChainAndNumber () {
 #$_[0] = Chain
 #$_[1] = Number
 if ($_[1] > 0){
  my ($stdout);
  syslog("IPT: Apply $iptables-D $_[0] $_[1]");
  run ([ $iptables , "-D", $_[0], $_[1] ], ">", \$stdout, '2>', \$suberr) or return undef;  
  return 1;
 } 
 return undef;
}

sub ip_to_number($){ # IP => Number
 my $ip = shift;
 my (@octets, $ip_num);
  $ip =~ s/\n//g;
	@octets = split /\./, $ip;
	$ip_num = 0;
	foreach (@octets) {
	    $ip_num <<= 8;
	    $ip_num |= $_;
	}
 return $ip_num;
} 


sub GetCountryByIP {  #1.0.3
 my $tmpccx;
 if ($confDBGeo){  #1.0.2
  $geo_num=&ip_to_number("$_[0]");
  #print "$geo_num\n";
  my $sqlgeo = $dbhg->prepare("SELECT country_code FROM ip2location_db1 where ip_from<=$geo_num and ip_to>=$geo_num;" ); #Get Country by IP
  $sqlgeo->execute ; 
 (($tmpccx)=$sqlgeo->fetchrow_array); 
 } 
 return $tmpccx;   
}