#!/usr/bin/perl
###############################################################################
# Copyright 2006-2012, Way to the Web Limited
# URL: http://www.configserver.com
# Email: sales@waytotheweb.com
###############################################################################
# start main
use strict;
use lib '/etc/csf';
use Crypt::CBC;
use Digest::MD5;
use Fcntl qw(:DEFAULT :flock);
use File::Basename;
use File::Find;
use File::Path;
use Geo::IP::PurePerl;
use IO::Socket::INET;
use IPC::Open3;
use LWP::UserAgent;
use Net::CIDR::Lite;
use Net::SMTP;
use POSIX qw(:sys_wait_h sysconf strftime setsid);
use Socket;
use Time::HiRes qw(gettimeofday);

our (%logins, $pidfile, $pid, @lffd, @lfino, @lfbuf, %db, %messengerports,
     $count, %config, %logfiles, $childpid, $childcnt, %logintimeout, $cidr,
	 %loginproto, $cttimeout, %ips, %ifaces, $scriptline, @cidrs, %pskip,
	 %scripts, $scripttimeout, %blockedips, $pttimeout, %skip, $csftimeout,
	 $dshieldtimeout, $spamhaustimeout, $dirwatchtimeout, @suspicious,
	 %skipfile, %sfile, %nofiles, @matchfile, $toomanymatches, $pidino,
	 %dirwatchfile, $dirwatchfiletimeout, %skipuser, $globaltimeout,
	 %skipscript, %ports, $smtptimeout, $dyndnstimeout, @lfsize, $hostshort,
	 $loadtimeout, %relayip, $integritytimeout, $tar, %relays, $relaytimeout,
	 $bogontimeout, $exploittimeout, $pstimeout, %portscans, %psports,
	 %cpconfig, $hostname, $clock_ticks, %suignore, $ptchildpid, $tz,
	 $attimeout, %accounttracking, %newaccounttracking, $queuetimeout,
	 $ipcountry, %psips, $cctimeout, @rdns, %cpanelalert, %ignoreips,
	 %rtignore, $gdyndnstimeout, $masterpid, %adb, $accept, $version, %adf,
	 $ipscidr, $ipv6reg, $ipv4reg, %cpanelalertusers, @gcidrs, %gignoreips,
	 $gcidr, $ccltimeout, %apache404, $apache404timeout, $ethdevin,
	 $ethdevout, %apache403, $apache403timeout, %logscannerfiles,
	 @logignore, $loginterval, $ipscidr6, $cidr6, $gcidr6, $io_socket_ssl,
	 $sys_syslog, $systemstatstimeout, $syslogchecktimeout, $syslogcheckcode,
	 @cccidrs, %ccignoreips, $cccidr, $cccidr6, $tortimeout);

$pidfile = "/var/run/lfd.pid";

if (-e "/etc/csf/csf.disable") {
	print "csf and lfd have been disabled\n";
	exit;
}
if (-e "/etc/csf/csf.error") {
	print "\nError: You have an unresolved error when starting csf. You need to restart csf successfully before starting lfd\n";
	exit;
}

my %configsetting;
open (IN, "</etc/csf/csf.conf") or &cleanup(__LINE__,"*Error* Enable to open /etc/csf/csf.conf: $!");
flock (IN, LOCK_SH) or &cleanup(__LINE__,"*Error* Enable to lock /etc/csf/csf.conf: $!");
my @config = <IN>;
close (IN);
chomp @config;
foreach my $line (@config) {
	if ($line =~ /^\#/) {next}
	if ($line !~ /=/) {next}
	my ($name,$value) = split (/=/,$line);
	$name =~ s/\s//g;
	if ($value =~ /\"(.*)\"/) {
		$value = $1;
	} else {
		&cleanup(__LINE__,"*Error* Invalid configuration line");
	}
	if ($configsetting{$name}) {&cleanup(__LINE__,"*Error* Setting $name is repeated in /etc/csf/csf.conf - you must remove the duplicates and then restart csf and lfd.")}
	$config{$name} = $value;
	$configsetting{$name} = 1;
}
unless ($config{LF_DAEMON}) {&cleanup(__LINE__,"*Error* LF_DAEMON not enabled in /etc/csf/csf.conf")}
if ($config{TESTING}) {&cleanup(__LINE__,"*Error* lfd will not run with TESTING enabled in /etc/csf/csf.conf")}

if ($config{UI}) {
	eval ('use IO::Socket::SSL;');
	unless ($@) {$io_socket_ssl = 1}
}
if ($config{SYSLOG} or $config{SYSLOG_CHECK}) {
	eval ('use Sys::Syslog;');
	unless ($@) {$sys_syslog = 1}
}

require "/etc/csf/regex.pm";
if (-e "/etc/csf/regex.custom.pm") {require "/etc/csf/regex.custom.pm"}

$SIG{CHLD} = 'IGNORE';

if ($pid = fork)  {
	exit 0;
} elsif (defined($pid)) {
	$pid = $$;
} else {
	die "*Error* Unable to fork: $!";
}

chdir("/etc/csf");

close(STDIN);
close(STDOUT);
close(STDERR);
open STDIN, "/dev/null";
open STDOUT, ">/dev/null";
open STDERR, ">/dev/null";
setsid();

my $oldfh = select STDERR;
local $| = 1;
select $oldfh;

if (-e "/proc/sys/kernel/hostname") {
	open (IN, "</proc/sys/kernel/hostname");
	$hostname = <IN>;
	chomp $hostname;
	close (IN);
} else {
	$hostname = "unknown";
}
$hostshort = (split(/\./,$hostname))[0];
$clock_ticks = sysconf( &POSIX::_SC_CLK_TCK ) || 100;
$tz = strftime("%z", localtime);

sysopen (PIDFILE, $pidfile, O_RDWR | O_CREAT) or &childcleanup(__LINE__,"*Error* unable to create lfd PID file [$pidfile] $!");
flock (PIDFILE, LOCK_EX | LOCK_NB) or &childcleanup(__LINE__,"*Error* attempt to start lfd when it is already running");
seek (PIDFILE, 0, 0);
truncate (PIDFILE, 0);
print PIDFILE "$pid\n";
$pidino = (stat($pidfile))[1];
$masterpid = $pid;

$0 = "lfd - starting";

$SIG{INT} = \&cleanup;
$SIG{TERM} = \&cleanup;
$SIG{HUP} = \&cleanup;
$SIG{__DIE__} = sub {&cleanup(@_);};
$SIG{CHLD} = 'IGNORE';
$SIG{PIPE} = 'IGNORE';

if (-e "/etc/csf/csf.tempconf") {unlink ("/etc/csf/csf.tempconf")}
if (-e "/proc/vz/veinfo") {$config{VPS} = 1}
$ipscidr = Net::CIDR::Lite->new;
$ipscidr6 = Net::CIDR::Lite->new;
$cidr = Net::CIDR::Lite->new;
$cidr6 = Net::CIDR::Lite->new;
$gcidr = Net::CIDR::Lite->new;
$gcidr6 = Net::CIDR::Lite->new;
$cccidr = Net::CIDR::Lite->new;
$cccidr6 = Net::CIDR::Lite->new;
eval {$ipscidr6->add("::1/128")};
eval {$ipscidr->add("127.0.0.0/8")};
$ipv4reg = $ipv6reg = qr/(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/;
if ($config{IPV6}) {$ipv6reg = qr/((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?/}

unless ($config{GENERIC}) {
	open (IN, "</etc/wwwacct.conf") or &cleanup(__LINE__,"Unable to open /etc/wwwacct.conf: $!");
	my @cpconfig = <IN>;
	close (IN);
	chomp @cpconfig;
	foreach my $line (@cpconfig) {
		if ($line =~ /^\#/) {next}
		my ($name,$value) = split (/ /,$line,2);
		$cpconfig{$name} = $value;
	}
}

if (-e "/etc/csf/lfd.enable") {unlink "/etc/csf/lfd.enable"}
if (-e "/etc/csf/lfd.start") {unlink "/etc/csf/lfd.start"}
if (-e "/etc/csf/lfd.restart") {unlink "/etc/csf/lfd.restart"}

&getethdev;

if ($config{OLD_REAPER}) {$SIG{CHLD} = sub {$childcnt++}}

if (-e "/etc/csf/csf.ignore") {
	open (IN, "</etc/csf/csf.ignore") or &cleanup(__LINE__,$!);
	flock (IN, LOCK_SH);
	my @ignore = <IN>;
	close (IN);
	chomp @ignore;
	foreach my $line (@ignore) {
		if ($line =~ /^(\#|\n|\r|\s)/ or $line eq "") {next}
		my ($first,undef) = split(/\s/,$line);
		my ($ip,$iscidr) = split(/\//,$first);
		if (&checkip($first)) {
			if ($iscidr) {push @cidrs,$first} else {$ignoreips{$ip} = 1}
		}
		elsif ($ip ne "127.0.0.1") {&logfile("Invalid entry in csf.ignore: [$first]")}
	}
	foreach my $entry (@cidrs) {
		if (&checkip($entry) == 6) {
			eval {$cidr6->add($entry)};
		} else {
			eval {$cidr->add($entry)};
		}
		if ($@) {&logfile("Invalid entry in csf.ignore: $entry")}
	}
}
if (-e "/etc/csf/csf.rignore") {
	open (IN, "</etc/csf/csf.rignore") or &cleanup(__LINE__,$!);
	flock (IN, LOCK_SH);
	my @rignore = <IN>;
	close (IN);
	chomp @rignore;
	foreach my $line (@rignore) {
		if ($line =~ /^\.|\w/) {
			my ($host,undef) = split (/\s/,$line);
			if ($host) {push @rdns,$host}
		}
	}
}
if (-e "/etc/csf/csf.gignore") {unlink "/etc/csf/csf.gignore"}
if ($config{IGNORE_ALLOW} and -e "/etc/csf/csf.allow") {
	open (IN, "</etc/csf/csf.allow") or &cleanup(__LINE__,$!);
	flock (IN, LOCK_SH);
	my @ignore = <IN>;
	close (IN);
	chomp @ignore;
	foreach my $line (@ignore) {
		if ($line =~ /^Include\s*(.*)$/) {
			my $incfile = $1;
			open (INCFILE, "<",$incfile) or &error(__LINE__,"Could not open $incfile: $!");
			flock (INCFILE, LOCK_SH) or &error(__LINE__,"Could not lock $incfile: $!");
			my @incallow = <INCFILE>;
			close (INCFILE) or &error(__LINE__,"Could not close $incfile: $!");
			chomp @incallow;
			push @ignore,@incallow;
		}
	}
	foreach my $line (@ignore) {
		if ($line =~ /^(\#|\n|\r|\s)/ or $line eq "") {next}
		my ($first,undef) = split(/\s/,$line);
		my ($ip,$iscidr) = split(/\//,$first);
		if (&checkip($first)) {
			if ($iscidr) {push @cidrs,$first} else {$ignoreips{$ip} = 1}
		}
	}
	foreach my $entry (@cidrs) {
		if (&checkip($entry) == 6) {
			eval {$cidr6->add($entry)};
		} else {
			eval {$cidr->add($entry)};
		}
		if ($@) {&logfile("Invalid CIDR in csf.allow: $entry")}
	}
}

if ($config{LF_HTACCESS} or $config{LF_APACHE_404} or $config{LF_APACHE_403} or $config{LF_QOS}) {$logfiles{$config{HTACCESS_LOG}} = 1}
if ($config{LF_MODSEC} or $config{LF_CXS}) {$logfiles{$config{MODSEC_LOG}} = 1}
if ($config{LF_SUHOSIN}) {$logfiles{$config{SUHOSIN_LOG}} = 1}
if ($config{LF_SMTPAUTH}) {$logfiles{$config{SMTPAUTH_LOG}} = 1}
if ($config{LF_POP3D} or $config{LT_POP3D}) {$logfiles{$config{POP3D_LOG}} = 1}
if ($config{LF_IMAPD} or $config{LT_IMAPD}) {$logfiles{$config{IMAPD_LOG}} = 1}
if ($config{LF_CPANEL}) {$logfiles{$config{CPANEL_LOG}} = 1}
if ($config{LF_SSHD} or $config{LF_SSH_EMAIL_ALERT} or $config{LF_CONSOLE_EMAIL_ALERT}) {$logfiles{$config{SSHD_LOG}} = 1}
if ($config{LF_FTPD}) {$logfiles{$config{FTPD_LOG}} = 1}
if ($config{LF_CPANEL_ALERT}) {$logfiles{$config{CPANEL_ACCESSLOG}} = 1}
if ($config{SYSLOG_CHECK} and $sys_syslog) {$logfiles{$config{SYSLOG_LOG}} = 1}

if ($config{PS_INTERVAL} or $config{ST_ENABLE}) {$logfiles{$config{IPTABLES_LOG}} = 1}
if ($config{LF_SU_EMAIL_ALERT}) {$logfiles{$config{SU_LOG}} = 1}
if ($config{LF_SCRIPT_ALERT}) {$logfiles{$config{SCRIPT_LOG}} = 1}
if ($config{RT_RELAY_ALERT} or $config{RT_AUTHRELAY_ALERT} or $config{RT_POPRELAY_ALERT}) {$logfiles{$config{SMTPRELAY_LOG}} = 1}

if ($config{LT_IMAPD}) {$loginproto{imapd} = $config{LT_IMAPD}}
if ($config{LT_POP3D}) {$loginproto{pop3d} = $config{LT_POP3D}}

for (my $x = 1;$x < 10;$x++) {$logfiles{$config{"CUSTOM$x\_LOG"}} = 1}

if ($config{SYSLOG} or $config{SYSLOG_CHECK}) {
	unless ($sys_syslog) {
		&logfile("*Error* Cannot log to SYSLOG - Perl module Sys::Syslog required");
	}
}

open (IN, "</etc/csf/version.txt") or &cleanup(__LINE__,"Unable to open version.txt: $!");
$version = <IN>;
close (IN);
chomp $version;
my $generic = " (cPanel)";
if ($config{GENERIC}) {$generic = " (generic)"}
if ($config{DIRECTADMIN}) {$generic = " (DirectAdmin)"}
&logfile("daemon started on $hostname - csf v$version$generic");
if ($config{DEBUG} >= 1) {&logfile("Clock Ticks: $clock_ticks")}
if ($config{DEBUG} >= 1) {&logfile("debug: **** DEBUG LEVEL $config{DEBUG} ENABLED ****")}

unless (-e $config{SENDMAIL}) {
	&logfile("*WARNING* Unable to send email reports - [$config{SENDMAIL}] not found");
}

if ($config{LOGSCANNER}) {
	open (IN, "</etc/csf/csf.logfiles") or &cleanup(__LINE__,$!);
	flock (IN, LOCK_SH);
	my @files = <IN>;
	close (IN);
	chomp @files;
	foreach my $file (@files) {
		if ($file =~ /^\#/) {next}
		if ($file eq "") {next}
		if (-e $file) {
			$logfiles{$file} = 1;
			$logscannerfiles{$file} = 1;
		}
	}
	open (IN, "</etc/csf/csf.logignore") or &cleanup(__LINE__,$!);
	flock (IN, LOCK_SH);
	my @ignoredata = <IN>;
	close (IN);
	chomp @ignoredata;
	foreach my $line (@ignoredata) {
		if ($line =~ /^\#/) {next}
		if ($line eq "") {next}
		if (&testregex($line)) {push @logignore, $line}
		else {&logfile("*Error* Invalid regex [$line] in csf.logignore")}
	}
	&logfile("Log Scanner...");
}

unless (-d "/var/spool/exim") {$config{LF_QUEUE_ALERT} = 0}

if ($config{DROP_IP_LOGGING} and $config{PS_INTERVAL}) {
	&logfile("Cannot use PS_INTERVAL with DROP_IP_LOGGING enabled. DROP_IP_LOGGING disabled");
	$config{DROP_IP_LOGGING} = 0;
}

$accept = "ACCEPT";
if ($config{WATCH_MODE}) {
	$accept = "LOGACCEPT";
	$config{DROP_NOLOG} = "";
	$config{DROP_LOGGING} = "1";
	$config{DROP_IP_LOGGING} = "1";
	$config{DROP_PF_LOGGING} = "1";
	$config{PS_INTERVAL} = "0";
	$config{DROP_ONLYRES} = "0";
	&logfile("WATCH_MODE enabled...");
}

if (-e "/etc/csf/csf.restart") {
	unlink "/etc/csf/csf.restart";
	&csfrestart;
}

if ($config{LF_CSF}) {
	&logfile("CSF Tracking...");
	&csfcheck;
	$csftimeout = 0;
}

if ($config{IPV6}) {
	&logfile("IPv6 Enabled...");
}

if ($config{PT_LOAD}) {
	&logfile("LOAD Tracking...");
	&loadcheck;
	$loadtimeout = 0;
}

if ($config{MESSENGER}) {
	my $pcnt = 0;
	foreach my $port (split(/\,/,$config{MESSENGER_HTML_IN})) {
		$messengerports{$port} = 1;
		$pcnt++;
	}
	if ($pcnt > 15) {
		&logfile("MESSENGER_HTML_IN contains more than 15 ports - disabling Messenger Service");
		$config{MESSENGER} = 0;
	} else {
		$pcnt = 0;
		foreach my $port (split(/\,/,$config{MESSENGER_TEXT_IN})) {
			$messengerports{$port} = 1;
			$pcnt++;
		}
		if ($pcnt > 15) {
			&logfile("MESSENGER_TEXT_IN contains more than 15 ports - disabling Messenger Service");
			$config{MESSENGER} = 0;
		} else {
			&logfile("Messenger HTML Service starting...");
			&messenger($config{MESSENGER_HTML},$config{MESSENGER_USER},"HTML");
			&logfile("Messenger TEXT Service starting...");
			&messenger($config{MESSENGER_TEXT},$config{MESSENGER_USER},"TEXT");
		}
	}
}

if ($config{UI}) {
	unless ($io_socket_ssl) {
		&logfile("*Error* Cannot run csf UI - Perl module IO::Socket::SSL required");
	} else {
		if ($config{UI_USER} eq "" or $config{UI_USER} eq "username") {&logfile("*Error* Cannot run csf Integrated UI - UI_USER must set")}
		elsif ($config{UI_PASS} eq "" or $config{UI_PASS} eq "password") {&logfile("*Error* Cannot run Integrated csf UI - UI_PASS must set")}
		else {
			&logfile("csf Integrated UI running up on port $config{UI_PORT}...");
			&ui;
		}
	}
}

if ($config{CLUSTER_RECVFROM}) {
	&logfile("Cluster Service starting...");
	if (length $config{CLUSTER_KEY} < 8) {
		&logfile("Failed: Cluster Service - CLUSTER_KEY too short");
	} else {
		if (length $config{CLUSTER_KEY} < 20) {&logfile("Cluster Service - CLUSTER_KEY should really be longer than 20 characters")}
		&lfdserver;
	}
}

if ($config{DYNDNS}) {
	&logfile("DynDNS Tracking...");
	&dyndns;
	$dyndnstimeout = 0;
	if ($config{DYNDNS} < 60) {
		&logfile("DYNDNS refresh increased to 300 to prevent looping (csf.conf setting: $config{DYNDNS})");
		$config{DYNDNS} = 300;
	}
}

if ($config{LF_GLOBAL}) {
	if ($config{GLOBAL_IGNORE}) {&logfile("Global Ignore Tracking...")}
	if ($config{GLOBAL_ALLOW}) {&logfile("Global Allow Tracking...")}
	if ($config{GLOBAL_DENY}) {&logfile("Global Deny Tracking...")}
	if ($config{GLOBAL_DYNDNS}) {&logfile("Global DynDNS Tracking...")}
	&global;
	$globaltimeout = 0;
	if ($config{LF_GLOBAL} < 60) {
		&logfile("LF_GLOBAL refresh increased to 300 to prevent looping (csf.conf setting: $config{LF_GLOBAL})");
		$config{LF_GLOBAL} = 300;
	}
	if ($config{GLOBAL_DYNDNS_INTERVAL} < 60) {
		&logfile("GLOBAL_DYNDNS_INTERVAL refresh increased to 300 to prevent looping (csf.conf setting: $config{GLOBAL_DYNDNS_INTERVAL})");
		$config{GLOBAL_DYNDNS_INTERVAL} = 300;
	}
}

if ($config{LF_DSHIELD}) {
	&logfile("DSHIELD Tracking...");
	if ($config{LF_DSHIELD} < 3600) {
		&logfile("LF_DSHIELD refresh increased to 3600 to prevent blacklisting (csf.conf setting: $config{LF_DSHIELD})");
		$config{LF_DSHIELD} = 3600;
	}
	&dshield;
	$dshieldtimeout = 0;
}
if ($config{LF_SPAMHAUS}) {
	&logfile("SPAMHAUS Tracking...");
	if ($config{LF_SPAMHAUS} < 3600) {
		&logfile("LF_SPAMHAUS refresh increased to 3600 to prevent blacklisting (csf.conf setting: $config{LF_SPAMHAUS})");
		$config{LF_SPAMHAUS} = 3600;
	}
	&spamhaus;
	$spamhaustimeout = 0;
}
if ($config{LF_TOR}) {
	&logfile("TOR Tracking...");
	if ($config{LF_TOR} < 3600) {
		&logfile("LF_TOR refresh increased to 3600 to prevent blacklisting (csf.conf setting: $config{LF_TOR})");
		$config{LF_TOR} = 3600;
	}
	&tor;
	$tortimeout = 0;
}
if ($config{LF_BOGON}) {
	&logfile("BOGON Tracking...");
	if ($config{LF_BOGON} < 3600) {
		&logfile("BOGON refresh increased to 3600 to prevent blacklisting (csf.conf setting: $config{LF_BOGON})");
		$config{LF_BOGON} = 3600;
	}
	&bogon;
	$bogontimeout = 0;
}

if ($config{CC_DENY} or $config{CC_ALLOW} or $config{CC_ALLOW_FILTER} or $config{CC_IGNORE}) {
	&logfile("Country Code Filters...");
	&countrycode;
	$cctimeout = 0;
	if ($config{CC_IGNORE}) {
		if (-e "/etc/csf/csf.ccignore") {unlink "/etc/csf/csf.ccignore"}
		foreach my $cc (split(/\,/,$config{CC_IGNORE})) {
			$cc = lc $cc;
			if ((-e "/etc/csf/zone/$cc.zone") and !(-z  "/etc/csf/zone/$cc.zone")) {
				if ($config{DEBUG} >= 1) {&logfile("debug: Repopulating CC_IGNORE [$cc]")}
				open (IN, "</etc/csf/zone/$cc.zone");
				flock (IN, LOCK_SH);
				while (my $line = <IN>) {
					chomp $line;
					if ($line =~ /^(\#|\n|\r|\s)/ or $line eq "") {next}
					my ($ip,undef) = split(/\s/,$line);
					my (undef,$iscidr) = split(/\//,$ip);
					my $v = &checkip($ip);
					if ($v) {
						if ($iscidr) {
							push @cccidrs,$ip;
							undef $@;
							if ($v == 6) {
								eval {$cccidr6->add($ip)};
							} else {
								eval {$cccidr->add($ip)};
							}
							if ($@) {&logfile("Invalid entry in CC_IGNORE [$cc]: $ip")}
						} else {$ccignoreips{$ip} = 1}
					}
					elsif ($ip ne "127.0.0.1") {&logfile("Invalid entry in CC_IGNORE [$cc]: [$ip]")}
				}
				close (IN);
			}
		}
	}
}

if ($config{CC_LOOKUPS}) {
	&logfile("Country Code Lookups...");
	&countrycodelookups;
	$ccltimeout = 0;
	if ($config{CC_LOOKUPS} == 2) {
		if (-e "/etc/csf/Geo/GeoLiteCity.dat") {$ipcountry = Geo::IP::PurePerl->open("/etc/csf/Geo/GeoLiteCity.dat")}
	} else {
		if (-e "/etc/csf/Geo/GeoIP.dat") {$ipcountry = Geo::IP::PurePerl->open("/etc/csf/Geo/GeoIP.dat")}
	}
}

if ($config{LF_INTEGRITY}) {
	&logfile("System Integrity Tracking...");
	&integrity;
	$integritytimeout = 0;
	if ($config{LF_INTEGRITY} < 120) {
		&logfile("LF_INTEGRITY refresh increased to 300 to prevent looping (csf.conf setting: $config{LF_INTEGRITY})");
		$config{LF_INTEGRITY} = 300;
	}
}

if ($config{LF_EXPLOIT}) {
	if (-e "/etc/csf/csf.tempexploit") {unlink ("/etc/csf/csf.tempexploit")}
	if (-e "/etc/csf/csf.suignore") {
		open (IN, "</etc/csf/csf.suignore") or &cleanup(__LINE__,$!);
		flock (IN, LOCK_SH);
		my @suignore = <IN>;
		close (IN);
		chomp @suignore;
		foreach my $line (@suignore) {
			if ($line =~ /^\#/) {next}
			if ($line eq "") {next}
			$suignore{$line} = 1;
		}
	}
	&logfile("Exploit Tracking...");
	&exploit;
	$exploittimeout = 0;
	if ($config{LF_EXPLOIT} < 60) {
		&logfile("LF_EXPLOIT refresh increased to 60 to prevent looping (csf.conf setting: $config{LF_EXPLOIT})");
		$config{LF_EXPLOIT} = 60;
	}
}

if ($config{LF_DIRWATCH}) {
	if (-e "/etc/csf/csf.fignore") {
		open (IN, "</etc/csf/csf.fignore") or &cleanup(__LINE__,$!);
		flock (IN, LOCK_SH);
		my @fignore = <IN>;
		close (IN);
		chomp @fignore;
		foreach my $line (@fignore) {
			if ($line =~ /^\#/) {next}
			if ($line eq "") {next}
			if ($line =~ /\*|\\/) {
				if (&testregex($line)) {push @matchfile, $line}
				else {&logfile("*Error* Invalid regex [$line] in csf.fignore")}
			}
			elsif ($line =~ /^user:(.*)/) {
				$skipuser{$1} = 1;
			}
			else {
				$skipfile{$line} = 1;
			}
		}
	}
	if (-e "/etc/csf/csf.tempfiles") {unlink ("/etc/csf/csf.tempfiles")}
	if (-e "/etc/csf/csf.dwdisable") {unlink ("/etc/csf/csf.dwdisable")}
	&logfile("Directory Watching...");
	$dirwatchtimeout = 0;
}

if ($config{LF_DIRWATCH_FILE}) {
	if (-e "/etc/csf/csf.dirwatch") {
		&logfile("Directory File Watching...");
		open (IN, "</etc/csf/csf.dirwatch") or &cleanup(__LINE__,$!);
		flock (IN, LOCK_SH);
		my @dirwatch = <IN>;
		close (IN);
		chomp @dirwatch;
		foreach my $line (@dirwatch) {
			if ($line =~ /^\#/) {next}
			if ($line eq "") {next}
			if (-e $line) {
				$dirwatchfile{$line} = 1;
			} else {
				&logfile("Directory File Watching [$line] not found - ignoring");
			}
		}
		&dirwatchfile;
		$dirwatchfiletimeout = 0;
	}
}

if ($config{LF_SCRIPT_ALERT}) {
	&logfile("Email Script Tracking...");
	if (-e "/etc/csf/csf.signore") {
		open (IN, "</etc/csf/csf.signore") or &cleanup(__LINE__,$!);
		flock (IN, LOCK_SH);
		my @signore = <IN>;
		close (IN);
		chomp @signore;
		foreach my $line (@signore) {
			if ($line =~ /^\#/) {next}
			if ($line eq "") {next}
			$skipscript{$line} = 1;
		}
	}
}

if ($config{LF_QUEUE_ALERT}) {
	&logfile("Email Queue Tracking...");
	&queuecheck;
	$queuetimeout = 0;
	if ($config{LF_QUEUE_INTERVAL} < 30) {
		&logfile("LF_QUEUE_INTERVAL refresh increased to 300 to prevent looping (csf.conf setting: $config{LF_QUEUE_INTERVAL})");
		$config{LF_QUEUE_INTERVAL} = 300;
	}
}

if ($config{RT_RELAY_ALERT} or $config{RT_AUTHRELAY_ALERT} or $config{RT_POPRELAY_ALERT} or $config{RT_LOCALRELAY_ALERT} or $config{RT_LOCALHOSTRELAY_ALERT}) {
	&logfile("Email Relay Tracking...");
	if ($config{RT_LOCALRELAY_ALERT}) {
		if (-e "/etc/csf/csf.mignore") {
			open (IN, "</etc/csf/csf.mignore") or &cleanup(__LINE__,$!);
			flock (IN, LOCK_SH);
			my @mignore = <IN>;
			close (IN);
			chomp @mignore;
			foreach my $line (@mignore) {
				if ($line =~ /^\#/) {next}
				if ($line eq "") {next}
				$rtignore{$line} = 1;
			}
		}
	}
}

if ($config{LF_PERMBLOCK}) {
	&logfile("Temp to Perm Block Tracking...");
}

if ($config{LF_NETBLOCK}) {
	&logfile("Netblock Tracking...");
}

if ($config{ST_SYSTEM}) {
	&logfile("System Statistics...");
	my $time = time;
	sysopen (SYSSTAT,"/etc/csf/stats/system", O_RDWR | O_CREAT);
	flock (SYSSTAT, LOCK_EX);
	my @sysstatdata = <SYSSTAT>;
	chomp @sysstatdata;
	seek (SYSSTAT, 0, 0);
	truncate (SYSSTAT, 0);
	foreach my $line (@sysstatdata) {
		my ($thistime,undef) = split(/\,/,$line);
		if ($time - $thistime > (86400 * $config{ST_SYSTEM_MAXDAYS})) {next}
		print SYSSTAT $line."\n";
	}
	close (SYSSTAT);
	&systemstats;
}
if ($config{PS_INTERVAL}) {
	&logfile("Port Scan Tracking...");
	if ($config{PS_INTERVAL} < 60) {
		&logfile("PS_INTERVAL refresh increased to 60 to prevent looping (csf.conf setting: $config{PS_INTERVAL})");
		$config{PS_INTERVAL} = 60;
	}
	$pstimeout = 0;
	foreach my $ports (split(/\,/,$config{PS_PORTS})) {
		if ($ports =~ /\:/) {
			my ($start,$end) = split(/\:/,$ports);
			for (my $x = $start; $x <= $end ;$x++) {
				$psports{$x} = 1;
			}

		} else {
			$psports{$ports} = 1;
		}
	}
}

if ($config{CT_LIMIT}) {
	if ($config{CT_STATES}) {
		&logfile("Connection Tracking ($config{CT_STATES})...");
	} else {
		&logfile("Connection Tracking...");
	}
	&connectiontracking;
	$cttimeout = 0;
	if ($config{CT_INTERVAL} < 10) {
		&logfile("PT_INTERVAL refresh increased to 30 to prevent looping (csf.conf setting: $config{PT_INTERVAL})");
		$config{CT_INTERVAL} = 30;
	}
}

if ($config{PT_LIMIT}) {
	if (-e "/etc/csf/csf.pignore") {
		open (IN, "</etc/csf/csf.pignore") or &cleanup(__LINE__,$!);
		flock (IN, LOCK_SH);
		my @pignore = <IN>;
		close (IN);
		chomp @pignore;
		foreach my $line (@pignore) {
			if ($line =~ /^\#/) {next}
			if ($line eq "") {next}
			my ($item,$rule) = split(/:/,$line,2);
			$rule =~ s/\r|\n//g;
			$item =~ s/\s//g;
			$item = lc $item;
			if ($item =~ /^(cmd|exe|user)$/) {
				$skip{$item}{$rule} = 1;
			}
			elsif ($item =~ /^(pcmd|pexe|puser)$/) {
				if (&testregex($rule)) {$pskip{$item}{$rule} = 1}
				else {&logfile("*Error* Invalid regex [$line] in csf.pignore")}
			}
		}
	}
	if (-e "/etc/csf/csf.temppids") {unlink ("/etc/csf/csf.temppids")}
	if (-e "/etc/csf/csf.tempusers") {unlink ("/etc/csf/csf.tempusers")}
	&logfile("Process Tracking...");
	&processtracking;
	$pttimeout = 0;
	if ($config{PT_INTERVAL} < 10) {
		&logfile("PT_INTERVAL refresh increased to 60 to prevent looping (csf.conf setting: $config{PT_INTERVAL})");
		$config{PT_INTERVAL} = 60;
	}
}

if ($config{AT_ALERT}) {
	if ($config{AT_ALERT} == 3) {
		my ($user,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell) = getpwnam("root");
		$accounttracking{$user}{account} = 1;
		$accounttracking{$user}{passwd} = $passwd;
		$accounttracking{$user}{uid} = $uid;
		$accounttracking{$user}{gid} = $gid;
		$accounttracking{$user}{dir} = $dir;
		$accounttracking{$user}{shell} = $shell;
	} else {
		while (my ($user,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell) = getpwent()) {
			if (($config{AT_ALERT} eq "2") and ($uid ne "0")) {next}
			$accounttracking{$user}{account} = 1;
			$accounttracking{$user}{passwd} = $passwd;
			$accounttracking{$user}{uid} = $uid;
			$accounttracking{$user}{gid} = $gid;
			$accounttracking{$user}{dir} = $dir;
			$accounttracking{$user}{shell} = $shell;
		}
		endpwent();
	}
	&logfile("Account Tracking...");
	$attimeout = 0;
	if ($config{AT_INTERVAL} < 10) {
		&logfile("AT_INTERVAL refresh increased to 60 to prevent looping (csf.conf setting: $config{PT_INTERVAL})");
		$config{AT_INTERVAL} = 60;
	}
}

if ($config{LF_SSH_EMAIL_ALERT}) {
	&logfile("SSH Tracking...");
}
if ($config{LF_SU_EMAIL_ALERT}) {
	&logfile("SU Tracking...");
}
if ($config{LF_CONSOLE_EMAIL_ALERT}) {
	&logfile("Console Tracking...");
}

if ($config{LF_CPANEL_ALERT}) {
	$config{LF_CPANEL_ALERT_USERS} =~ s/\s//g;
	foreach my $user (split(/\,/,$config{LF_CPANEL_ALERT_USERS})) {
		$cpanelalertusers{$user} = 1;
	}
	&logfile("WHM Tracking...");
}

if ($config{PORTKNOCKING} and $config{PORTKNOCKING_ALERT}) {
	&logfile("Port Knocking Tracking...");
}

if ($config{LF_SELECT} and !$config{LF_TRIGGER}) {
	$ports{pop3d} = "110,995";
	$ports{imapd} = "143,993";
	$ports{htpasswd} = "80,443";
	$ports{mod_security} = "80,443";
	$ports{mod_qos} = "80,443";
	$ports{cxs} = "80,443";
	$ports{bind} = "53";
	$ports{suhosin} = "80,443";
	$ports{cpanel} = "2077,2078,2082,2083,2086,2087,2095,2096";
	$ports{ftpd} = "20,21";
	$ports{smtpauth} = "25,465,587";

	opendir (DIR, "/etc/chkserv.d");
	while (my $file = readdir (DIR)) {
		if ($file =~ /exim-(\d+)/) {$ports{smtpauth} .= ",$1"}
	}
	closedir (DIR);

	open (IN, "</etc/ssh/sshd_config") or &cleanup(__LINE__,"Enable to open /etc/ssh/sshd_config: $!");
	flock (IN, LOCK_SH) or &cleanup(__LINE__,"Enable to lock /etc/ssh/sshd_config: $!");
	my @sshconfig = <IN>;
	close (IN);
	chomp @sshconfig;
	foreach my $line (@sshconfig) {
		if ($line =~ /^Port\s+(\d+)/) {
			my $port = $1;
			if ($ports{sshd}) {
				$ports{sshd} .= ",$port";
			} else {
				$ports{sshd} = $port;
			}
		}
	}
	unless ($ports{sshd}) {$ports{sshd} = "22"}
}

if ($config{LF_INTERVAL} < 60) {
	&logfile("LF_INTERVAL refresh increased to 300 to prevent looping (csf.conf setting: $config{LF_INTERVAL})");
	$config{LT_INTERVAL} = 300;
}

if ($config{LF_PARSE} < 5 or $config{LF_PARSE} > 20) {
	&logfile("LF_PARSE refresh reset to 5 to prevent looping (csf.conf setting: $config{LF_PARSE})");
	$config{LF_PARSE} = 5;
}

$scriptline = "";
my $lastline = "";
$scripttimeout = 0;
my $duration = 0;
my $maintimer = 0;
while (1)  {
	$0 = "lfd - processing";
	$maintimer = time;

	seek (PIDFILE, 0, 0);
	my @piddata = <PIDFILE>;
	chomp @piddata;
	if (($pid ne $piddata[0]) or ($pidino ne (stat($pidfile))[1])) {
		&cleanup(__LINE__,"*Error* pid mismatch or missing");
	}

	if (-e "/etc/csf/csf.error") {
		&cleanup(__LINE__,"*Error* You have an unresolved error when starting csf. You need to restart csf successfully before restarting lfd. *lfd stopped*");
	}
	my $perms = sprintf "%04o", (stat("/etc/csf"))[2] & 07777;
	if ($perms != "0600") {
		chmod (0600,"/etc/csf");
		&logfile("*Permissions* on /etc/csf reset to 0600 [currently: $perms]");
	}

	if (-e "/etc/csf/lfd.restart") {
		unlink "/etc/csf/lfd.restart";
		$SIG{INT} = 'IGNORE';
		$SIG{TERM} = 'IGNORE';
		$SIG{CHLD} = 'IGNORE';
		$0 = "lfd - stopping";

		&logfile("daemon restart requested");

		close(PIDFILE);
		unlink $pidfile;

		local $SIG{HUP} = 'IGNORE';
		kill HUP => -$$;
		exec("/etc/init.d/lfd start");
		exit 0;
	}

	if (-e "/etc/csf/csf.restart") {
		unlink "/etc/csf/csf.restart";
		&csfrestart;
	}

	if ($config{LF_CSF}) {
		$csftimeout+=$duration;
		if ($csftimeout >= 300) {
			$csftimeout = 0;
			&csfcheck;
		}
	}

	if ($config{SYSLOG_CHECK} and $sys_syslog) {
		$syslogchecktimeout+=$duration;
		if ($syslogchecktimeout >= $config{SYSLOG_CHECK}) {
			$syslogchecktimeout = 0;
			if ($syslogcheckcode eq "") {
				my @chars = ('0'..'9','a'..'z','A'..'Z');
				$syslogcheckcode = join '', map {$chars[rand(@chars)]} (1..(15 + int(rand(15))));
				eval {
					local $SIG{__DIE__} = undef;
					openlog('lfd', 'ndelay,pid', 'user');
					syslog('info', "SYSLOG check [$syslogcheckcode]");
					closelog();
				}
			} else {
				&syslogcheck;
				$syslogcheckcode = "";
			}
		}
	}

	if ($config{ST_SYSTEM}) {
		$systemstatstimeout+=$duration;
		if ($systemstatstimeout >= 60) {
			$systemstatstimeout = 0;
			&systemstats;
		}
	}

	if ($config{LT_POP3D}) {
		$logintimeout{pop3d}+=$duration;
		if ($logintimeout{pop3d} >= 3600) {
			delete $logintimeout{pop3d};
			delete $logins{pop3d};
		}
	}
	if ($config{LT_IMAPD}) {
		$logintimeout{imapd}+=$duration;
		if ($logintimeout{imapd} >= 3600) {
			delete $logintimeout{imapd};
			delete $logins{imapd};
		}
	}
	if ($config{PS_INTERVAL}) {
		$pstimeout+=$duration;
		if ($pstimeout >= $config{PS_INTERVAL}) {
			$pstimeout = 0;
			undef %portscans;
		}
	}
	if ($config{LF_SCRIPT_ALERT}) {
		$scripttimeout+=$duration;
		if ($scripttimeout >= 3600) {
			$scripttimeout = 0;
			undef %scripts;
		}
	}
	if ($config{LF_APACHE_404}) {
		$apache404timeout+=$duration;
		if ($apache404timeout >= $config{LF_INTERVAL}) {
			$apache404timeout = 0;
			undef %apache404;
		}
	}
	if ($config{LF_APACHE_403}) {
		$apache403timeout+=$duration;
		if ($apache403timeout >= $config{LF_INTERVAL}) {
			$apache403timeout = 0;
			undef %apache403;
		}
	}
	if ($config{RT_RELAY_ALERT} or $config{RT_AUTHRELAY_ALERT} or $config{RT_POPRELAY_ALERT} or $config{RT_LOCALRELAY_ALERT} or $config{RT_LOCALHOSTRELAY_ALERT}) {
		$relaytimeout+=$duration;
		if ($relaytimeout >= 3600) {
			$relaytimeout = 0;
			undef %relays;
		}
	}

	if (-e "/etc/csf/csf.tempconf") {
		open (IN, "</etc/csf/csf.tempconf");
		my @config = <IN>;
		close (IN);
		chomp @config;

		foreach my $line (@config) {
			if ($line =~ /^\#/) {next}
			if ($line !~ /=/) {next}
			my ($name,$value) = split (/=/,$line);
			$name =~ s/\s//g;
			if ($value =~ /\"(.*)\"/) {
				$value = $1;
			} else {
				&cleanup(__LINE__,"*Error* Invalid configuration line in csf.tempconf");
			}
			$config{$name} = $value;
		}
	}

	if ($config{GLOBAL_IGNORE} and -e "/etc/csf/csf.gignore") {
		open (IN, "</etc/csf/csf.gignore");
		flock (IN, LOCK_SH);
		my @ignore = <IN>;
		close (IN);
		unlink "/etc/csf/csf.gignore";
		chomp @ignore;
		undef @gcidrs;
		undef %gignoreips;
		undef $gcidr;
		undef $gcidr6;
		$gcidr = Net::CIDR::Lite->new;
		$gcidr6 = Net::CIDR::Lite->new;
		foreach my $line (@ignore) {
			if ($line =~ /^(\#|\n|\r|\s)/ or $line eq "") {next}
			my ($ip,undef) = split(/\s/,$line);
			my (undef,$iscidr) = split(/\//,$ip);
			my $v = &checkip($ip);
			if ($v) {
				if ($iscidr) {
					push @gcidrs,$ip;
					undef $@;
					if ($v == 6) {
						eval {$gcidr6->add($ip)};
					} else {
						eval {$gcidr->add($ip)};
					}
					if ($@) {&logfile("Invalid entry in GLOBAL_IGNORE: $ip")}
				} else {$gignoreips{$ip} = 1}
			}
			elsif ($ip ne "127.0.0.1") {&logfile("Invalid entry in GLOBAL_IGNORE: [$ip]")}
		}
	}

	if ($config{CC_IGNORE} and -e "/etc/csf/csf.ccignore") {
		unlink "/etc/csf/csf.ccignore";
		undef @cccidrs;
		undef %ccignoreips;
		undef $cccidr;
		undef $cccidr6;
		$cccidr = Net::CIDR::Lite->new;
		$cccidr6 = Net::CIDR::Lite->new;
		foreach my $cc (split(/\,/,$config{CC_IGNORE})) {
			$cc = lc $cc;
			if ((-e "/etc/csf/zone/$cc.zone") and !(-z  "/etc/csf/zone/$cc.zone")) {
				if ($config{DEBUG} >= 1) {&logfile("debug: Repopulating CC_IGNORE [$cc]")}
				open (IN, "</etc/csf/zone/$cc.zone");
				flock (IN, LOCK_SH);
				while (my $line = <IN>) {
					chomp $line;
					if ($line =~ /^(\#|\n|\r|\s)/ or $line eq "") {next}
					my ($ip,undef) = split(/\s/,$line);
					my (undef,$iscidr) = split(/\//,$ip);
					my $v = &checkip($ip);
					if ($v) {
						if ($iscidr) {
							push @cccidrs,$ip;
							undef $@;
							if ($v == 6) {
								eval {$cccidr6->add($ip)};
							} else {
								eval {$cccidr->add($ip)};
							}
							if ($@) {&logfile("Invalid entry in CC_IGNORE [$cc]: $ip")}
						} else {$ccignoreips{$ip} = 1}
					}
					elsif ($ip ne "127.0.0.1") {&logfile("Invalid entry in CC_IGNORE [$cc]: [$ip]")}
				}
				close (IN);
			}
		}
	}

	if (-e "/etc/csf/csf.cclookup") {
		unlink "/etc/csf/csf.cclookup";
		if ($config{CC_LOOKUPS} == 2) {
			if (-e "/etc/csf/Geo/GeoLiteCity.dat") {
				undef $ipcountry;
				$ipcountry = Geo::IP::PurePerl->open("/etc/csf/Geo/GeoLiteCity.dat");
				&logfile("CCL: Using new GeoLite Country database");
			}
		} else {
			if (-e "/etc/csf/Geo/GeoIP.dat") {
				undef $ipcountry;
				$ipcountry = Geo::IP::PurePerl->open("/etc/csf/Geo/GeoIP.dat");
				&logfile("CCL: Using new GeoLite Country database");
			}
		}
	}

	$count = 0;
	$0 = "lfd - scanning log files";
	undef %relayip;
	if ($config{RELAYHOSTS}) {
		open (IN, "</etc/relayhosts");
		flock (IN, LOCK_SH);
		my @relayhosts = <IN>;
		close (IN);
		chomp @relayhosts;

		foreach my $ip (@relayhosts) {
			if (&checkip($ip)) {$relayip{$ip} = 1}
		}
	}
	if ($config{DYNDNS} and $config{DYNDNS_IGNORE}) {
		open (IN, "</etc/csf/csf.tempdyn");
		flock (IN, LOCK_SH);
		my @relayhosts = <IN>;
		close (IN);
		chomp @relayhosts;

		foreach my $ip (@relayhosts) {
			if (&checkip($ip)) {$relayip{$ip} = 1}
		}
	}
	if ($config{GLOBAL_DYNDNS} and $config{GLOBAL_DYNDNS_IGNORE}) {
		open (IN, "</etc/csf/csf.tempgdyn");
		flock (IN, LOCK_SH);
		my @relayhosts = <IN>;
		close (IN);
		chomp @relayhosts;

		foreach my $ip (@relayhosts) {
			if (&checkip($ip)) {$relayip{$ip} = 1}
		}
	}
	foreach my $lgfile (keys %logfiles) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start",$lgfile,$timer)}
		my $totlines = 0;
		my @data;
		while (my $line = &getlogfile($lgfile,$count,$totlines))  {
			if ($line eq "reopen") {
				undef @data;
				last;
			} else {
				$totlines ++;
				push @data, $line;
			}
		}
		if ($config{DEBUG} >= 2) {&logfile("debug: Parsing $lgfile ($totlines lines)")}
		foreach my $line (@data) {
			if (($lastline ne "") and ($line =~ /^\S+\s+\d+\s+\S+ \S+ last message repeated (\d+) times/)) {
				my $hits = $1;
				if ($hits > 100) {$hits = 100}
				for (my $x = 0;$x <$hits ;$x++) {
					&dochecks($lastline,$lgfile);
				}
			} else {
				&dochecks($line,$lgfile);
				$lastline = $line;
			}
		}
		$lastline = "";
		$count++;
		undef %psips;
		undef %blockedips;
		if ($config{DEBUG} >= 3) {$timer = &timer("stop",$lgfile,$timer)}
	}

	$0 = "lfd - processing";
	if ($config{CT_LIMIT}) {
		$cttimeout+=$duration;
		if ($cttimeout >= $config{CT_INTERVAL}) {
			$cttimeout = 0;
			&connectiontracking;
		}
	}

	if ($config{DYNDNS}) {
		$dyndnstimeout+=$duration;
		if ($dyndnstimeout >= $config{DYNDNS}) {
			$dyndnstimeout = 0;
			&dyndns;
		}
	}

	if ($config{GLOBAL_DYNDNS}) {
		$gdyndnstimeout+=$duration;
		if ($gdyndnstimeout >= $config{GLOBAL_DYNDNS_INTERVAL}) {
			$gdyndnstimeout = 0;
			&globaldyndns;
		}
	}

	if ($config{LF_GLOBAL}) {
		$globaltimeout+=$duration;
		if ($globaltimeout >= $config{LF_GLOBAL}) {
			$globaltimeout = 0;
			&global;
		}
	}

	if ($config{LF_DSHIELD}) {
		$dshieldtimeout+=$duration;
		if ($dshieldtimeout >= 3600) {
			$dshieldtimeout = 0;
			&dshield;
		}
	}

	if ($config{LF_SPAMHAUS}) {
		$spamhaustimeout+=$duration;
		if ($spamhaustimeout >= 3600) {
			$spamhaustimeout = 0;
			&spamhaus;
		}
	}

	if ($config{LF_TOR}) {
		$tortimeout+=$duration;
		if ($tortimeout >= 3600) {
			$tortimeout = 0;
			&tor;
		}
	}

	if ($config{LF_BOGON}) {
		$bogontimeout+=$duration;
		if ($bogontimeout >= 3600) {
			$bogontimeout = 0;
			&bogon;
		}
	}

	if ($config{CC_DENY} or $config{CC_ALLOW} or $config{CC_ALLOW_FILTER} or $config{CC_IGNORE}) {
		$cctimeout+=$duration;
		if ($cctimeout >= 3600) {
			$cctimeout = 0;
			&countrycode;
		}
	}

	if ($config{CC_LOOKUPS}) {
		$ccltimeout+=$duration;
		if ($ccltimeout >= 3600) {
			$ccltimeout = 0;
			&countrycodelookups;
		}
	}

	if ($config{LOGSCANNER}) {
		my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
		if ($loginterval eq "") {
			if ($config{LOGSCANNER_INTERVAL} eq "hourly") {$loginterval = $hour}
			if ($config{LOGSCANNER_INTERVAL} eq "daily") {$loginterval = $mday}
		}
		if (-e "/etc/csf/csf.logrun") {
			unlink "/etc/csf/csf.logrun";
			&logscanner($hour);
		}
		elsif ($config{LOGSCANNER_INTERVAL} eq "hourly" and $loginterval ne $hour) {
			$loginterval = $hour;
			&logscanner($hour);
		}
		elsif ($config{LOGSCANNER_INTERVAL} eq "daily" and $loginterval ne $mday) {
			$loginterval = $mday;
			&logscanner($hour);
		}
	}

	if ($config{LF_INTEGRITY}) {
		$integritytimeout+=$duration;
		if ($integritytimeout >= $config{LF_INTEGRITY}) {
			$integritytimeout = 0;
			&integrity;
		}
	}

	if ($config{LF_QUEUE_ALERT}) {
		$queuetimeout+=$duration;
		if ($queuetimeout >= $config{LF_QUEUE_INTERVAL}) {
			$queuetimeout = 0;
			&queuecheck;
		}
	}

	if ($config{LF_EXPLOIT}) {
		$exploittimeout+=$duration;
		if ($exploittimeout >= $config{LF_EXPLOIT}) {
			$exploittimeout = 0;
			&exploit;
		}
	}

	if ($config{LF_DIRWATCH}) {
		$dirwatchtimeout+=$duration;
		if (not -e "/etc/csf/csf.dwdisable") {
			if ($dirwatchtimeout >= $config{LF_DIRWATCH}) {
				$dirwatchtimeout = 0;
				&dirwatch;
			}
		}
	}

	if ($config{LF_DIRWATCH_FILE}) {
		$dirwatchfiletimeout+=$duration;
		if ($dirwatchfiletimeout >= $config{LF_DIRWATCH_FILE}) {
			&dirwatchfile;
			$dirwatchfiletimeout = 0;
		}
	}

	if ($config{PT_LOAD}) {
		$loadtimeout+=$duration;
		if ($loadtimeout >= $config{PT_LOAD}) {
			$loadtimeout = 0;
			&loadcheck;
		}
	}

	if ($config{PT_LIMIT}) {
		$pttimeout+=$duration;
		if ($pttimeout >= $config{PT_INTERVAL}) {
			$pttimeout = 0;
			&processtracking;
		}
	}

	if ($config{AT_ALERT}) {
		$attimeout+=$duration;
		if ($attimeout >= $config{AT_INTERVAL}) {
			$attimeout = 0;
			undef %newaccounttracking;
			if ($config{AT_ALERT} == 3) {
				my ($user,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell) = getpwnam("root");
				$newaccounttracking{$user}{account} = 1;
				$newaccounttracking{$user}{passwd} = $passwd;
				$newaccounttracking{$user}{uid} = $uid;
				$newaccounttracking{$user}{gid} = $gid;
				$newaccounttracking{$user}{dir} = $dir;
				$newaccounttracking{$user}{shell} = $shell;
			} else {
				while (my ($user,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell) = getpwent()) {
					if (($config{AT_ALERT} eq "2") and ($uid ne "0")) {next}
					$newaccounttracking{$user}{account} = 1;
					$newaccounttracking{$user}{passwd} = $passwd;
					$newaccounttracking{$user}{uid} = $uid;
					$newaccounttracking{$user}{gid} = $gid;
					$newaccounttracking{$user}{dir} = $dir;
					$newaccounttracking{$user}{shell} = $shell;
				}
				endpwent();
			}
			&accounttracking;
			%accounttracking = %newaccounttracking;
		}
	}

	&ipunblock;

	if ($config{OLD_REAPER} and $childcnt) {&reaper}

	$0 = "lfd - sleeping";
	sleep ($config{LF_PARSE});

	$duration = time - $maintimer;
	if (($config{DEBUG} >= 1) and ($duration > ($config{LF_PARSE} * 10))) {
		&logfile ("debug: *Performance* log parsing taking $duration seconds");
	}
	if ($config{DEBUG} >= 2) {&logfile("debug: Tick: $duration [$config{LF_PARSE}]")}
}

exit;

# end main
###############################################################################
# start dochecks
sub dochecks {
	my $line = shift;
	my $lgfile = shift;
	my $timenow = time;
	my $logscanner_skip = 0;

	my ($reason, $ip, $app, $customtrigger, $customports, $customperm) = &processline ($line,$lgfile);

	my ($gip,$account) = split (/\|/,$ip,2);
	unless ($account =~ /^[a-zA-Z0-9\-\_\.\@\%\+]+$/) {
		if ($account and $config{DEBUG} >= 1) {&logfile("debug: (processline) Account name [$account] is invalid")}
		$account = "";
	}
	$ip = $gip;
	if (($ip) and ($ip !~ /^127\./)) {
		if (&ignoreip($ip)) {
			&logfile("$reason $ip - ignored");
		} else {
			if ($blockedips{$ip}{block} or ($blockedips{$ip}{apps} =~ /\b$app\b/)) {
				if ($config{DEBUG} >= 1) {&logfile("debug: $ip already blocked")}
			} else {
				$db{$ip}{count}++;
				$db{$ip}{text} .= "$line\n";
				$db{$ip}{apps} .= $app." ";
				$db{$ip}{appscount}{$app}++;
				$db{$ip}{mytime} .= "$timenow,";
				$db{$ip}{appstime}{$app} .= "$timenow,";

				my $hits;
				my $trigger;
				my $setting;
				my @times;
				if ($customtrigger) {
					$trigger = "LF_CUSTOMTRIGGER";
					$config{$trigger} = $customtrigger;
					$config{"$trigger\_PERM"} = $customperm;
					$ports{$app} = $customports;
				}
				if ($config{LF_TRIGGER}) {
					@times = split(/\,/,$db{$ip}{mytime});
					$trigger = "LF_TRIGGER";
				} else {
					@times = split(/\,/,$db{$ip}{appstime}{$app});
					if ($app eq "sshd") {$trigger = "LF_SSHD"}
					elsif ($app eq "pop3d") {$trigger = "LF_POP3D"}
					elsif ($app eq "imapd") {$trigger = "LF_IMAPD"}
					elsif ($app eq "ftpd") {$trigger = "LF_FTPD"}
					elsif ($app eq "smtpauth") {$trigger = "LF_SMTPAUTH"}
					elsif ($app eq "htpasswd") {$trigger = "LF_HTACCESS"}
					elsif ($app eq "mod_security") {$trigger = "LF_MODSEC"}
					elsif ($app eq "bind") {$trigger = "LF_BIND"}
					elsif ($app eq "suhosin") {$trigger = "LF_SUHOSIN"}
					elsif ($app eq "cpanel") {$trigger = "LF_CPANEL"}
					elsif ($app eq "whm") {$trigger = "LF_CPANEL"}
					elsif ($app eq "webmail") {$trigger = "LF_CPANEL"}
					elsif ($app eq "mod_qos") {$trigger = "LF_QOS"}
					elsif ($app eq "cxs") {$trigger = "LF_CXS"}
				}

				my $newtimes;
				my $newcnt = 0;
				foreach my $time (@times) {
					if ($timenow - $time <= $config{LF_INTERVAL}) {
						$newtimes .= "$time,";
						$newcnt++;
					}
				}
				if ($config{LF_TRIGGER}) {
					$db{$ip}{count} = $newcnt;
					$db{$ip}{mytime} = $newtimes;
					$hits = $db{$ip}{count};
				} else {
					$db{$ip}{appscount}{$app} = $newcnt;
					$db{$ip}{appstime}{$app} = $newtimes;
					$hits = $db{$ip}{appscount}{$app};
				}

				if ($config{DEBUG} >= 1) {&logfile("debug: $reason $ip - $hits failure(s) in the last $config{LF_INTERVAL} secs")}

				if ($hits >= $config{$trigger}) {
					my @text = split(/\n/,$db{$ip}{text});
					$db{$ip}{text} = "";
					for (-$hits..-1) {$db{$ip}{text} .= "$text[$_]\n"}
					$0 = "lfd - blocking $ip";
					&block($ip,$hits,$app,$config{"$trigger\_PERM"},$trigger,$reason);
					if ($config{LF_SELECT} and !$config{LF_TRIGGER}) {
						$db{$ip}{appscount}{$app} = 0;
						$db{$ip}{appstime}{$app} = "";
					} else {
						delete $db{$ip};
					}
					$0 = "lfd - scanning $lgfile";
				}

				if ($config{LF_DISTATTACK} and $account) {
					$adb{$app}{$account}{$timenow}{ip} .= "$ip,";
					$adb{$app}{$account}{$timenow}{text} .= "$line\n";
					my @accountips;
					my $text;
					foreach my $key (keys %{$adb{$app}{$account}}) {
						if ($timenow - $key <= $config{LF_INTERVAL}) {
							push @accountips, (split(/\,/,$adb{$app}{$account}{$key}{ip}));
							$text .= $adb{$app}{$account}{$key}{text};
						} else {
							delete $adb{$app}{$account}{$key};
						}
					}
					my %seen;
					my @uniqueips = grep { ! $seen{ $_ }++ } @accountips;
					if ($config{DEBUG} >= 1) {&logfile("debug: $reason ".@uniqueips." ip(s) to account [$account] ".@accountips." in the last $config{LF_INTERVAL} secs")}
					if ((@accountips >= $config{$trigger}) and (@uniqueips >= $config{LF_DISTATTACK_UNIQ})) {
						delete $adb{$app}{$account};
						&blockaccount(\@uniqueips,$account,$#accountips+1,$app,$text,$config{"$trigger\_PERM"});
					}
				}
			}
		}
	}

	if ($config{LF_DISTFTP} and ($lgfile eq $config{FTPD_LOG})) {
		my ($ip, $account) = &processdistftpline ($line);
		unless ($account =~ /^[a-zA-Z0-9\-\_\.\@\%\+]+$/) {
			if ($account and $config{DEBUG} >= 1) {&logfile("debug: (processdistftpline) Account name [$account] is invalid")}
			$account = "";
		}
		if ($ip and $account and ($ip !~ /^127\./)) {
			if (&ignoreip($ip)) {
				&logfile("Distributed FTP $ip - ignored");
			} else {
				$adf{$account}{$timenow}{ip} .= "$ip,";
				$adf{$account}{$timenow}{text} .= "$line\n";
				my @accountips;
				my $text;
				foreach my $key (keys %{$adf{$account}}) {
					if ($timenow - $key <= $config{LF_INTERVAL}) {
						push @accountips, (split(/\,/,$adf{$account}{$key}{ip}));
						$text .= $adf{$account}{$key}{text};
					} else {
						delete $adf{$account}{$key};
					}
				}
				my %seen;
				my @uniqueips = grep { ! $seen{ $_ }++ } @accountips;
				if ($config{DEBUG} >= 1) {&logfile("debug: FTP logins from ".@uniqueips." ip(s) to account [$account] ".@accountips." in the last $config{LF_INTERVAL} secs")}
				if ((@accountips >= $config{LF_DISTFTP}) and (@uniqueips >= $config{LF_DISTFTP_UNIQ})) {
					delete $adf{$account};
					&blockdistftp(\@uniqueips,$account,$#accountips+1,$text,$config{LF_DISTFTP_PERM});
				}
			}
		}
	}

	if ($config{LF_DISTSMTP} and ($lgfile eq $config{SMTPAUTH_LOG})) {
		my ($ip, $account) = &processdistsmtpline ($line);
		unless ($account =~ /^[a-zA-Z0-9\-\_\.\@\%\+]+$/) {
			if ($account and $config{DEBUG} >= 1) {&logfile("debug: (processdistsmtpline) Account name [$account] is invalid")}
			$account = "";
		}
		if ($ip and $account and ($ip !~ /^127\./)) {
			if (&ignoreip($ip)) {
				&logfile("Distributed SMTP $ip - ignored");
			} else {
				$adf{$account}{$timenow}{ip} .= "$ip,";
				$adf{$account}{$timenow}{text} .= "$line\n";
				my @accountips;
				my $text;
				foreach my $key (keys %{$adf{$account}}) {
					if ($timenow - $key <= $config{LF_INTERVAL}) {
						push @accountips, (split(/\,/,$adf{$account}{$key}{ip}));
						$text .= $adf{$account}{$key}{text};
					} else {
						delete $adf{$account}{$key};
					}
				}
				my %seen;
				my @uniqueips = grep { ! $seen{ $_ }++ } @accountips;
				if ($config{DEBUG} >= 1) {&logfile("debug: SMTP logins from ".@uniqueips." ip(s) to account [$account] ".@accountips." in the last $config{LF_INTERVAL} secs")}
				if ((@accountips >= $config{LF_DISTSMTP}) and (@uniqueips >= $config{LF_DISTSMTP_UNIQ})) {
					delete $adf{$account};
					&blockdistsmtp(\@uniqueips,$account,$#accountips+1,$text,$config{LF_DISTSMTP_PERM});
				}
			}
		}
	}

	if ($config{LF_APACHE_404} and ($lgfile eq $config{HTACCESS_LOG})) {
		my ($ip) = &loginline404($line);
		if ($ip and !&ignoreip($ip,1)) {
			$apache404{$ip}{count}++;
			$apache404{$ip}{text} .= "$line\n";
			if ($apache404{$ip}{count} > $config{LF_APACHE_404}) {
				&disable404($ip,$apache404{$ip}{text});
				delete $apache404{$ip};
			}
		}
	}

	if ($config{LF_APACHE_403} and ($lgfile eq $config{HTACCESS_LOG})) {
		my ($ip) = &loginline403($line);
		if ($ip and !&ignoreip($ip,1)) {
			$apache403{$ip}{count}++;
			$apache403{$ip}{text} .= "$line\n";
			if ($apache403{$ip}{count} > $config{LF_APACHE_403}) {
				&disable403($ip,$apache403{$ip}{text});
				delete $apache403{$ip};
			}
		}
	}

	if (($config{LT_POP3D} or $config{LT_IMAPD}) and (($lgfile eq $config{POP3D_LOG}) or ($lgfile eq $config{IMAPD_LOG}))) {
		my ($app, $account, $ip) = &processloginline ($line);
		unless ($account =~ /^[a-zA-Z0-9\-\_\.\@\%\+]+$/) {
			if ($account and $config{DEBUG} >= 1) {&logfile("debug: (processloginline) Account name [$account] is invalid")}
			$account = "";
		}
		if ($account and $loginproto{$app} and !&ignoreip($ip,1)) {
			$logins{$app}{$account}{$ip}++;
			if ($logins{$app}{$account}{$ip} > $loginproto{$app}) {
				$0 = "lfd - disabling $app logins for $account";
				&logindisable($app,$ip,$logins{$app}{$account}{$ip},$account);
				delete $logins{$app}{$account}{$ip};
				$0 = "lfd - scanning $lgfile";
			}
		}
	}

	if ($config{LF_SSH_EMAIL_ALERT} and (($lgfile eq "/var/log/messages") or ($lgfile eq "/var/log/secure") or ($lgfile eq "/var/log/auth.log") or ($lgfile eq $config{SSHD_LOG}))) {
		my ($account, $ip, $method) = &processsshline ($line);
		unless ($account =~ /^[a-zA-Z0-9\-\_\.\@\%\+]+$/) {
			if ($account and $config{DEBUG} >= 1) {&logfile("debug: (processsshline) Account name [$account] is invalid")}
			$account = "";
		}
		if ($account and $ip and !&ignoreip($ip)) {
			&sshalert($account, $ip, $method);
		}
		elsif (&ignoreip($ip)) {&logfile("*SSH login* from $ip into the $account account using $method authentication - ignored")}
	}

	if ($config{LF_SU_EMAIL_ALERT} and (($lgfile eq "/var/log/messages") or ($lgfile eq "/var/log/secure") or ($lgfile eq "/var/log/auth.log") or ($lgfile eq $config{SU_LOG}))) {
		my ($to, $from, $status) = &processsuline ($line);
		if (($to and $from) and ($from ne "root") and ($from ne 'root(uid=0)') and ($from ne '(uid=0)')) {
			&sualert($to, $from, $status);
		}
	}

	if ($config{LF_CONSOLE_EMAIL_ALERT} and (($lgfile eq "/var/log/messages") or ($lgfile eq "/var/log/secure") or ($lgfile eq "/var/log/auth.log") or ($lgfile eq $config{SU_LOG}))) {
		my ($status) = &processconsoleline ($line);
		if ($status) {
			&consolealert($line);
		}
	}

	if ($config{LF_CPANEL_ALERT} and ($lgfile eq $config{CPANEL_ACCESSLOG})) {
		my ($ip,$user) = &processcpanelline ($line);
		unless ($user =~ /^[a-zA-Z0-9\-\_\.\@\%\+]+$/) {
			if ($user and $config{DEBUG} >= 1) {&logfile("debug: (processcpanelline) Account name [$user] is invalid")}
			$user = "";
		}
		if ($ip and !&ignoreip($ip) and $user and ($cpanelalertusers{$user} or $cpanelalertusers{all})) {
			if ($cpanelalert{$ip}{$user} and (time - $cpanelalert{$ip}{$user} < 3600)) {
				$cpanelalert{$ip}{$user} = time;
			} else {
				$cpanelalert{$ip}{$user} = time;
				&cpanelalert($ip,$user);
			}
		}
	}

	if ($config{LF_SCRIPT_ALERT} and ($lgfile eq $config{SCRIPT_LOG})) {
		if ($scriptline ne "") {
			$scripts{$scriptline}{cnt}++;
			if ($scripts{$scriptline}{cnt} <= 10) {
				$scripts{$scriptline}{mails} .= "$line\n";
			}
			if ($scripts{$scriptline}{cnt} > $config{LF_SCRIPT_LIMIT}) {
				&scriptalert($scriptline,$scripts{$scriptline}{cnt},$scripts{$scriptline}{mails});
				delete $scripts{$scriptline};
			}
			$scriptline = "";
		}
		if (my $path = &scriptlinecheck($line)) {$scriptline = $path}
	}

	if ($config{PS_INTERVAL} and ($lgfile eq $config{IPTABLES_LOG})) {
		my ($ip, $port) = &pslinecheck($line);
		if ($port and $ip and !&ignoreip($ip)) {
			if ($psports{$port}) {
				$portscans{$ip}{count}++;
				$portscans{$ip}{blocks} .= "$line\n";
				if ($portscans{$ip}{count} > $config{PS_LIMIT}) {
					if ($psips{$ip}) {
						if ($config{DEBUG} >= 1) {&logfile("debug: *Port Scan* detected from $ip - already blocked")}
						delete $portscans{$ip};
					} else {
						&portscans($ip,$portscans{$ip}{count},$portscans{$ip}{blocks});
						$psips{$ip} = 1;
						delete $portscans{$ip};
					}
				}
			}
		}
		elsif (($config{DEBUG} >= 1) and $port and $ip and &ignoreip($ip)) {
			&logfile("debug: PS count from $ip - ignored");
		}
	}

	if ($config{ST_ENABLE} and ($lgfile eq $config{IPTABLES_LOG})) {
		if (&statscheck($line)) {
			&stats($line,"iptables");
		}
	}

	if ($config{SYSLOG_CHECK} and $sys_syslog and $syslogcheckcode and ($lgfile eq $config{SYSLOG_LOG})) {
		if (&syslogcheckline($line)) {
			if ($config{DEBUG} >= 2) {&logfile("debug: SYSLOG_CHECK match [$syslogcheckcode]")}
			$syslogcheckcode = "";
			$logscanner_skip = 1;
		}
	}

	if ($config{PORTKNOCKING} and $config{PORTKNOCKING_ALERT} and ($lgfile eq $config{IPTABLES_LOG})) {
		my ($ip, $port) = &portknockingcheck($line);
		if ($port and $ip and !&ignoreip($ip)) {
			&portknocking($ip, $port);
		}
	}

	if ($config{LOGSCANNER} and $logscannerfiles{$lgfile} and !$logscanner_skip) {
		my $hit = 1;
		foreach my $regex (@logignore) {
			if ($line =~ /$regex/) {
				$hit = 0;
				last;
			}
		}
		if ($hit) {
			unless (-e "/etc/csf/csf.logmax") {
				sysopen(LOGTEMP,"/etc/csf/csf.logtemp", O_RDWR | O_CREAT, 0600);
				flock (LOGTEMP, LOCK_EX);
				close (LOGTEMP);
				my @data = <LOGTEMP>;
				if (@data > $config{LOGSCANNER_LINES}) {
					open (OUT,">","/etc/csf/csf.logmax");
					close (OUT);
				} else {
					sysopen(LOGTEMP,"/etc/csf/csf.logtemp", O_WRONLY | O_APPEND | O_CREAT, 0600);
					flock (LOGTEMP, LOCK_EX);
					print LOGTEMP "$lgfile|$line\n";
					close (LOGTEMP);
				}
			}
		}
	}

	if ((($config{RT_RELAY_ALERT} or $config{RT_AUTHRELAY_ALERT} or $config{RT_POPRELAY_ALERT} or $config{RT_LOCALRELAY_ALERT} or $config{RT_LOCALHOSTRELAY_ALERT})) and ($lgfile eq $config{SMTPRELAY_LOG})) {
		my ($ip,$check) = &relaycheck($line);
		if ($ip) {
			if ($check eq "RELAY" and !$relays{$ip}{check}) {
				open (IN, "</etc/relayhosts");
				flock (IN, LOCK_SH);
				my @relayhosts = <IN>;
				close (IN);
				chomp @relayhosts;
				if (grep {$_ =~ /^$ip$/} @relayhosts) {$check = "POPRELAY"}
				open (IN, "</etc/alwaysrelay");
				flock (IN, LOCK_SH);
				@relayhosts = <IN>;
				close (IN);
				chomp @relayhosts;
				if (grep {$_ =~ /^$ip$/} @relayhosts) {$check = "POPRELAY"}
			}
			if ($ips{$ip} or $ipscidr->find($ip) or $ipscidr6->find($ip)) {$check = "LOCALHOSTRELAY"}
			if ($config{ST_SYSTEM}) {
				sysopen (EMAIL, "/etc/csf/stats/email", O_RDWR | O_CREAT);
				flock (EMAIL, LOCK_EX);
				my $stats = <EMAIL>;
				chomp $stats;
				my ($sent,$recv) = split(/\:/,$stats);
				if ($check eq "RELAY") {$recv++} else {$sent++}
				seek (EMAIL, 0, 0);
				truncate (EMAIL, 0);
				print EMAIL "$sent:$recv";
				close (EMAIL);
			}
		}
		if ($ip and ($ip ne "mailnull") and ($ip ne "root") and (!$rtignore{$ip})) {
			my $tline = $line;
			$tline =~ s/".*"/""/g;
			my $start = 0;
			my $cnt = 0;
			foreach my $item (split(/\s+/,$tline)) {
					if ($item eq "for") {$start = 1 ; next}
					if ($start and ($item =~ /\@/)) {$cnt++} else {$start = 0}
			}
			if ($cnt > 0) {
				$relays{$ip}{cnt}+=$cnt;
			} else {
				$relays{$ip}{cnt}++;
			}
			if ($config{DEBUG} >= 2) {&logfile("debug: RT\_$check\_LIMIT detected from $ip, count = $relays{$ip}{cnt}")}

			unless ($relays{$ip}{check}) {$relays{$ip}{check} = $check}

			my $mailcnt = 0;
			foreach my $mail (split(/\n/,$relays{$ip}{mails})) {$mailcnt++}
			if ($mailcnt < 10) {
				$relays{$ip}{mails} .= "$line\n";
			}

			if (($relays{$ip}{cnt} > $config{"RT\_$check\_LIMIT"}) and ($config{"RT\_$check\_ALERT"})) {
				if (($check eq "LOCALHOSTRELAY") or (!&ignoreip($ip))) {
					&relayalert($ip,$relays{$ip}{cnt},$relays{$ip}{check},$relays{$ip}{mails});
					delete $relays{$ip};
				}
			}
		}
	}
}
# end dochecks
###############################################################################
# start getlogfile
sub getlogfile {
	my $logfile = shift;
	my $lfn = shift;
	my $totlines = shift;
    my $junk;
    my $ino;
	my $size;
    my $line;
	my $count;

    if (!defined($lffd[$lfn]))  {
		if (&openlogfile($logfile,$lfn)) {return undef}
    }

    ($junk, $ino, $junk, $junk, $junk, $junk, $junk, $size, $junk) = stat($logfile);

    if ($ino != $lfino[$lfn])  {
		&logfile("$logfile rotated. Reopening log file");
		if (&openlogfile($logfile,$lfn)) {return undef}
	    return "reopen";
    }

	if ($size < $lfsize[$lfn])  {
		&logfile("$logfile has been reset. Reopening log file");
		if (&openlogfile($logfile,$lfn)) {return undef}
	    return "reopen";
    }

	$line = readline($lffd[$lfn]);

	if ($totlines > ($config{LF_PARSE} * 1000)) {
		my $text = "*Error* Log line flooding/looping in $logfile. Reopening log file";
		&logfile("$text");
		if ($config{LOGFLOOD_ALERT}) {
			open (IN, "</etc/csf/logfloodalert.txt");
			my @alert = <IN>;
			close (IN);
			chomp @alert;

			my @message;
			foreach my $line (@alert) {
				$line =~ s/\[text\]/$text/ig;
				push @message, $line;
			}
			&sendmail(@message);
		}
		if (&openlogfile($logfile,$lfn)) {return undef}
	    return "reopen";
	}
	
	chomp $line;
    if ($line)  {
		$lfsize[$lfn] = $size;
		return $line;
    }

    return undef;
}
# end getlogfile
###############################################################################
# start openlogfile
sub openlogfile {
	my $logfile = shift;
	my $lfn = shift;
    my $junk;

    if (defined($lffd[$lfn]))  {
		close($lffd[$lfn]);
		delete($lffd[$lfn]);
	}

	sysopen ($lffd[$lfn], $logfile, O_RDONLY | O_NONBLOCK);
	if (!defined($lffd[$lfn]))  {
		&logfile("*Error* Cannot open $logfile");
		return 1;
	}
	if (seek($lffd[$lfn], 0, 2) == -1)  {
		&logfile("*Error* Cannot seek to end of $logfile");
		return 1;
	}

	&logfile("Watching $logfile...");
	($junk, $lfino[$lfn], $junk, $junk, $junk, $junk, $junk, $lfsize[$lfn], $junk) = stat($lffd[$lfn]);

	return 0;
}
# end openlogfile
###############################################################################
# start block
sub block {
	my $ip = shift;
	my $ipcount = shift;
	my $app = shift;
	my $temp = shift;
	my $active = shift;
	my $reason = shift;
	my $ipc = $ipcount;

	my $text = $db{$ip}{text};
	my $apps = $db{$ip}{apps};
	unless ($config{LF_TRIGGER}) {$apps = $app}

	$blockedips{$ip}{block} = 1;
	$blockedips{$ip}{apps} .= "$app\,";

	unless ($config{OLD_REAPER}) {unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","block",$timer)}
		my %logapps;
		my $apptext;
		foreach my $app (split(/ /,$apps)) {$logapps{$app} = 1}
		foreach my $key (keys %logapps) {
			if ($apptext eq "") {$apptext = $key} else {$apptext .= ",$key"}
		}
		my $perm = 1;
		if ($temp > 1) {$perm = 0}

		$0 = "lfd - (child) blocking $ip";

		my $tip = &iplookup($ip);
		my $failtext = "Login failure/trigger from";
		if (keys %logapps == 1) {$failtext = $reason}
		if ($config{LF_SELECT} and !$config{LF_TRIGGER}) {
			&ipblock($perm,"($apptext) $failtext $tip: $ipcount in the last $config{LF_INTERVAL} secs",$ip,$ports{$app},"in",$temp,0,$text,$active);
		} else {
			&ipblock($perm,"($apptext) $failtext $tip: $ipcount in the last $config{LF_INTERVAL} secs",$ip,"","inout",$temp,0,$text,$active);
		}

		$0 = "lfd - (child) logging $ip";

		if ($config{LF_EMAIL_ALERT}) {
			$0 = "lfd - (child) sending alert email for $ip";

			open (IN, "</etc/csf/alert.txt");
			my @alert = <IN>;
			close (IN);
			chomp @alert;

			my $block = "Temporary Block";
			if ($perm) {$block = "Permanent Block"}

			my $allowip = &allowip($ip);
			if ($allowip == 1) {$block .= " (IP match in csf.allow, block may not work)"}
			if ($allowip == 2) {$block .= " (IP match in GLOBAL_ALLOW, block may not work)"}

			my @message;
			foreach my $line (@alert) {
				$line =~ s/\[ip\]/$tip/ig;
				$line =~ s/\[ipcount\]/$ipcount \($apptext\)/ig;
				$line =~ s/\[iptick\]/$config{LF_INTERVAL}/ig;
				$line =~ s/\[block\]/$block/ig;
				$line =~ s/\[text\]/$text/ig;
				push @message, $line;
			}
			&sendmail(@message);

			if ($config{DEBUG} >= 1) {&logfile("debug: alert email sent for $ip")}
		}

		if ($config{X_ARF}) {
			$0 = "lfd - (child) sending X-ARF email for $ip";

			open (IN, "</etc/csf/x-arf.txt");
			my @alert = <IN>;
			close (IN);
			chomp @alert;

			my @message;
			my $rfc3339 = strftime('%Y-%m-%dT%H:%M:%S%z',localtime);
			my $boundary = time;
			my $reportedfrom = "root\@$hostname";
			if ($config{X_ARF_TO}) {$config{LF_ALERT_TO} = $config{X_ARF_TO}}
			if ($config{X_ARF_FROM}) {$config{LF_ALERT_FROM} = $config{X_ARF_FROM}; $reportedfrom = $config{X_ARF_FROM}}
			my $iptype = "IPv".&checkip($ip);
			foreach my $line (@alert) {
				$line =~ s/\[ip\]/$ip/ig;
				$line =~ s/\[iptype\]/$iptype/ig;
				$line =~ s/\[tip\]/$tip/ig;
				$line =~ s/\[ipcount\]/$ipc/ig;
				$line =~ s/\[iptick\]/$config{LF_INTERVAL}/ig;
				$line =~ s/\[service\]/$app/ig;
				$line =~ s/\[csfversion\]/$version/ig;
				$line =~ s/\[reportedfrom\]/$reportedfrom/ig;
				$line =~ s/\[reportedid\]/$boundary\@$hostname/ig;
				$line =~ s/\[boundary\]/$boundary/ig;
				$line =~ s/\[text\]/$text/ig;
				$line =~ s/\[RFC3339\]/$rfc3339/ig;
				push @message, $line;
			}
			&sendmail(@message);

			if ($config{DEBUG} >= 1) {&logfile("debug: X-ARF email sent for $ip")}
		}

		if ($config{DEBUG} >= 3) {$timer = &timer("stop","block",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end block
###############################################################################
# start blockaccount
sub blockaccount {
	my $ipa = shift;
	my @ips = @$ipa;
	my $account = shift;
	my $ipcount = shift;
	my $app = shift;
	my $text = shift;
	my $temp = shift;

	foreach my $ip (@ips) {
		$blockedips{$ip}{block} = 1;
		$blockedips{$ip}{apps} .= "$app\,";
	}

	unless ($config{OLD_REAPER}) {unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","blockaccount",$timer)}
		my $perm = 1;
		if ($temp > 1) {$perm = 0}

		$text .= "\nIP Addresses Blocked:\n\n";
		foreach my $ip (@ips) {
			$0 = "lfd - (child) blocking $ip";
			my $tip = &iplookup($ip);
			if ($config{LF_SELECT} and !$config{LF_TRIGGER}) {
				&ipblock($perm,"$tip, $ipcount distributed $app attacks on account [$account] in the last $config{LF_INTERVAL} secs",$ip,$ports{$app},"in",$temp,0,$text,"LF_DISTATTACK");
			} else {
				&ipblock($perm,"$tip, $ipcount distributed $app attacks on account [$account] in the last $config{LF_INTERVAL} secs",$ip,"","inout",$temp,0,$text,"LF_DISTATTACK");
			}
			$text .= "$tip\n";
		}

		if ($config{LF_EMAIL_ALERT}) {
			open (IN, "</etc/csf/alert.txt");
			my @alert = <IN>;
			close (IN);
			chomp @alert;

			my $block = "Temporary Block";
			if ($perm) {$block = "Permanent Block"}

			my @message;
			foreach my $line (@alert) {
				$line =~ s/\[ip\]/distributed $app attack on account [$account]/ig;
				$line =~ s/\[ipcount\]/$ipcount/ig;
				$line =~ s/\[iptick\]/$config{LF_INTERVAL}/ig;
				$line =~ s/\[block\]/$block/ig;
				$line =~ s/\[text\]/$text/ig;
				push @message, $line;
			}
			&sendmail(@message);
		}

		if ($config{DEBUG} >= 3) {$timer = &timer("stop","blockaccount",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end blockaccount
###############################################################################
# start blockdistftp
sub blockdistftp {
	my $ipa = shift;
	my @ips = @$ipa;
	my $account = shift;
	my $ipcount = shift;
	my $text = shift;
	my $temp = shift;

	unless ($config{OLD_REAPER}) {unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","blockdistftp",$timer)}
		my $perm = 1;
		if ($temp > 1) {$perm = 0}

		$text .= "\nIP Addresses Blocked:\n\n";
		foreach my $ip (@ips) {
			$0 = "lfd - (child) blocking $ip";
			my $tip = &iplookup($ip);
			&ipblock($perm,"$tip, $ipcount distributed FTP Logins on account [$account] in the last $config{LF_INTERVAL} secs",$ip,"","inout",$temp,0,$text,"LF_DISTFTP");
			$text .= "$tip\n";
		}

		if ($config{LF_EMAIL_ALERT}) {
			open (IN, "</etc/csf/alert.txt");
			my @alert = <IN>;
			close (IN);
			chomp @alert;

			my $block = "Temporary Block";
			if ($perm) {$block = "Permanent Block"}

			my @message;
			foreach my $line (@alert) {
				$line =~ s/\[ip\]/distributed FTP Logins on account [$account]/ig;
				$line =~ s/\[ipcount\]/$ipcount/ig;
				$line =~ s/\[iptick\]/$config{LF_INTERVAL}/ig;
				$line =~ s/\[block\]/$block/ig;
				$line =~ s/\[text\]/$text/ig;
				push @message, $line;
			}
			&sendmail(@message);
		}

		if ($config{DEBUG} >= 3) {$timer = &timer("stop","blockdistftp",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end blockdistftp
###############################################################################
# start blockdistsmtp
sub blockdistsmtp {
	my $ipa = shift;
	my @ips = @$ipa;
	my $account = shift;
	my $ipcount = shift;
	my $text = shift;
	my $temp = shift;

	unless ($config{OLD_REAPER}) {unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","blockdistsmtp",$timer)}
		my $perm = 1;
		if ($temp > 1) {$perm = 0}

		$text .= "\nIP Addresses Blocked:\n\n";
		foreach my $ip (@ips) {
			$0 = "lfd - (child) blocking $ip";
			my $tip = &iplookup($ip);
			&ipblock($perm,"$tip, $ipcount distributed SMTP Logins on account [$account] in the last $config{LF_INTERVAL} secs",$ip,"","inout",$temp,0,$text,"LF_DISTSMTP");
			$text .= "$tip\n";
		}

		if ($config{LF_EMAIL_ALERT}) {
			open (IN, "</etc/csf/alert.txt");
			my @alert = <IN>;
			close (IN);
			chomp @alert;

			my $block = "Temporary Block";
			if ($perm) {$block = "Permanent Block"}

			my @message;
			foreach my $line (@alert) {
				$line =~ s/\[ip\]/distributed SMTP Logins on account [$account]/ig;
				$line =~ s/\[ipcount\]/$ipcount/ig;
				$line =~ s/\[iptick\]/$config{LF_INTERVAL}/ig;
				$line =~ s/\[block\]/$block/ig;
				$line =~ s/\[text\]/$text/ig;
				push @message, $line;
			}
			&sendmail(@message);
		}

		if ($config{DEBUG} >= 3) {$timer = &timer("stop","blockdistsmtp",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end blockdistsmtp
###############################################################################
# start disable404
sub disable404 {
	my $ip = shift;
	my $text = shift;

	unless ($config{OLD_REAPER}) {unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","disable404",$timer)}

		my $tip = &iplookup($ip);
		my $perm = 1;
		if ($config{LF_APACHE_404_PERM} > 1) {$perm = 0}
		&ipblock($perm,"$tip, more than $config{LF_APACHE_404} Apache 404 hits in the last $config{LF_INTERVAL} secs",$ip,$ports{mod_security},"in",$config{LF_APACHE_404_PERM},0,"","LF_APACHE_404");

		if ($config{LT_EMAIL_ALERT}) {
			$0 = "lfd - (child) sending alert email for $ip";

			open (IN, "</etc/csf/alert.txt");
			my @alert = <IN>;
			close (IN);
			chomp @alert;

			my $block = "Temporary Block";
			if ($perm) {$block = "Permanent Block"}
			my @message;
			foreach my $line (@alert) {
				$line =~ s/\[ip\]/$tip/ig;
				$line =~ s/\[ipcount\]/$config{LF_APACHE_404}/ig;
				$line =~ s/\[iptick\]/$config{LF_INTERVAL}/ig;
				$line =~ s/\[block\]/$block/ig;
				$line =~ s/\[text\]/$text/ig;
				push @message, $line;
			}
			&sendmail(@message);
		}

		if ($config{DEBUG} >= 3) {$timer = &timer("stop","disable404",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end disable404
###############################################################################
# start disable403
sub disable403 {
	my $ip = shift;
	my $text = shift;

	unless ($config{OLD_REAPER}) {unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","disable403",$timer)}

		my $tip = &iplookup($ip);
		my $perm = 1;
		if ($config{LF_APACHE_403_PERM} > 1) {$perm = 0}
		&ipblock($perm,"$tip, more than $config{LF_APACHE_403} Apache 403 hits in the last $config{LF_INTERVAL} secs",$ip,$ports{mod_security},"in",$config{LF_APACHE_403_PERM},0,"","LF_APACHE_403");

		if ($config{LT_EMAIL_ALERT}) {
			$0 = "lfd - (child) sending alert email for $ip";

			open (IN, "</etc/csf/alert.txt");
			my @alert = <IN>;
			close (IN);
			chomp @alert;

			my $block = "Temporary Block";
			if ($perm) {$block = "Permanent Block"}
			my @message;
			foreach my $line (@alert) {
				$line =~ s/\[ip\]/$tip/ig;
				$line =~ s/\[ipcount\]/$config{LF_APACHE_403}/ig;
				$line =~ s/\[iptick\]/$config{LF_INTERVAL}/ig;
				$line =~ s/\[block\]/$block/ig;
				$line =~ s/\[text\]/$text/ig;
				push @message, $line;
			}
			&sendmail(@message);
		}

		if ($config{DEBUG} >= 3) {$timer = &timer("stop","disable403",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end disable403
###############################################################################
# start logindisable
sub logindisable {
	my $app = shift;
	my $ip = shift;
	my $logins = shift;
	my $account = shift;
	my $trigger = "LT_".uc($app);

	my $port = "110";
	my $sport = "995";
	if ($app eq "imapd") {$port = "143"; $sport = "993"}

	unless ($config{OLD_REAPER}) {unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","logindisable",$timer)}
		my $flush = (3600-$logintimeout{$app});

		my $tip = &iplookup($ip);
		if ($config{LT_SKIPPERMBLOCK}) {$config{LF_PERMBLOCK} = 0}
		&ipblock(0,"$app - $logins logins in $logintimeout{$app} secs from $tip for $account exceeds $loginproto{$app}/hour",$ip,"$port,$sport","in",$flush,0,"",$trigger);

		if ($config{LT_EMAIL_ALERT}) {
			$0 = "lfd - (child) sending alert email for $account";

			open (IN, "</etc/csf/tracking.txt");
			my @alert = <IN>;
			close (IN);
			chomp @alert;

			my @message;
			foreach my $line (@alert) {
				$line =~ s/\[ip\]/$tip/ig;
				$line =~ s/\[app\]/$app/ig;
				$line =~ s/\[logins\]/$logins/ig;
				$line =~ s/\[account\]/$account/ig;
				$line =~ s/\[timeout\]/$logintimeout{$app}/ig;
				$line =~ s/\[flush\]/$flush/ig;
				$line =~ s/\[rate\]/$loginproto{$app}/ig;
				push @message, $line;
			}
			&sendmail(@message);

			&logfile("tracking email sent for $account");
		}

		if ($config{DEBUG} >= 3) {$timer = &timer("stop","logindisable",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end logindisable
###############################################################################
# start portscans
sub portscans {
	my $ip = shift;
	my $count = shift;
	my $blocks = shift;

	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","portscans",$timer)}

		my $skip = 0;
		open (IN, "</etc/csf/csf.deny");
		flock (IN, LOCK_SH);
		my @deny = <IN>;
		close (IN);
		chomp @deny;
		foreach my $line (@deny) {
			if ($line =~ /^Include\s*(.*)$/) {
				my $incfile = $1;
				open (INCFILE, "<",$incfile) or &error(__LINE__,"Could not open $incfile: $!");
				flock (INCFILE, LOCK_SH) or &error(__LINE__,"Could not lock $incfile: $!");
				my @incdeny = <INCFILE>;
				close (INCFILE) or &error(__LINE__,"Could not close $incfile: $!");
				chomp @incdeny;
				push @deny,@incdeny;
			}
		}
		if (grep {$_ =~ /^$ip\b/i} @deny) {
			$skip = 1;
		}
		open (IN, "</etc/csf/csf.tempban");
		flock (IN, LOCK_SH);
		@deny = <IN>;
		close (IN);
		chomp @deny;
		if (my @hits = grep {$_ =~ /\|$ip\|\|/i} @deny) {
			$skip = 1;
		}

		if ($skip) {
			if ($config{DEBUG} >= 1) {&logfile("debug: *Port Scan* detected from $ip. $count hits in the last $pstimeout seconds - already blocked")}
		} else {
			my $tip = &iplookup($ip);
			&ipblock($config{PS_PERMANENT},"*Port Scan* detected from $tip. $count hits in the last $pstimeout seconds",$ip,"","in",$config{PS_BLOCK_TIME},0,$blocks,"PS_LIMIT");

			if ($config{PS_EMAIL_ALERT}) {
				$0 = "lfd - (child) sending alert email for $ip";

				open (IN, "</etc/csf/portscan.txt");
				my @alert = <IN>;
				close (IN);
				chomp @alert;


				my $block = "Temporary Block";
				if ($config{PS_PERMANENT}) {$block = "Permanent Block"}

				my $allowip = &allowip($ip);
				if ($allowip == 1) {$block .= " (IP match in csf.allow, block may not work)"}
				if ($allowip == 2) {$block .= " (IP match in GLOBAL_ALLOW, block may not work)"}

				my @message;
				foreach my $line (@alert) {
					$line =~ s/\[ip\]/$tip/ig;
					$line =~ s/\[count\]/$count/ig;
					$line =~ s/\[blocks\]/$blocks/ig;
					$line =~ s/\[temp\]/$block/ig;
					push @message, $line;
				}
				&sendmail(@message);
				if ($config{DEBUG} >= 1) {&logfile("debug: alert email sent for $ip")}

				if ($config{X_ARF}) {
					$0 = "lfd - (child) sending X-ARF email for $ip";

					open (IN, "</etc/csf/x-arf.txt");
					my @alert = <IN>;
					close (IN);
					chomp @alert;

					my @message;
					my $rfc3339 = strftime('%Y-%m-%dT%H:%M:%S%z',localtime);
					my $boundary = time;
					my $reportedfrom = "root\@$hostname";
					if ($config{X_ARF_TO}) {$config{LF_ALERT_TO} = $config{X_ARF_TO}}
					if ($config{X_ARF_FROM}) {$config{LF_ALERT_FROM} = $config{X_ARF_FROM}; $reportedfrom = $config{X_ARF_FROM}}
					my $iptype = "IPv".&checkip($ip);
					foreach my $line (@alert) {
						$line =~ s/\[ip\]/$ip/ig;
						$line =~ s/\[iptype\]/$iptype/ig;
						$line =~ s/\[tip\]/$tip/ig;
						$line =~ s/\[ipcount\]/$count/ig;
						$line =~ s/\[iptick\]/$config{LF_INTERVAL}/ig;
						$line =~ s/\[service\]/firewall/ig;
						$line =~ s/\[csfversion\]/$version/ig;
						$line =~ s/\[reportedfrom\]/$reportedfrom/ig;
						$line =~ s/\[reportedid\]/$boundary\@$hostname/ig;
						$line =~ s/\[boundary\]/$boundary/ig;
						$line =~ s/\[text\]/$blocks/ig;
						$line =~ s/\[RFC3339\]/$rfc3339/ig;
						push @message, $line;
					}
					&sendmail(@message);

					if ($config{DEBUG} >= 1) {&logfile("debug: X-ARF email sent for $ip")}
				}
			}
		}

		if ($config{DEBUG} >= 3) {$timer = &timer("stop","portscans",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end portscans
###############################################################################
# start csfrestart
sub csfrestart {
	my $timer = time;
	if ($config{DEBUG} >= 3) {$timer = &timer("start","csfrestart",$timer)}
	$0 = "lfd - (re)starting csf...";

	&logfile("csf (re)start requested - running *csf startup*...");
	&syscommand(__LINE__,"/etc/csf/csf.pl -sf");
	&logfile("csf (re)start completed");

	if ($config{DEBUG} >= 3) {$timer = &timer("stop","csfrestart",$timer)}
	$0 = "lfd - processing";
}
# end csfrestart
###############################################################################
# start csfcheck
sub csfcheck {
	my $timer = time;
	if ($config{DEBUG} >= 3) {$timer = &timer("start","csfcheck",$timer)}

	my ($childin, $childout);
	my $cmdpid = open3($childin, $childout, $childout, "$config{IPTABLES} -L LOCALINPUT -n");
	my @ipdata = <$childout>;
	waitpid ($cmdpid, 0);
	chomp @ipdata;

	if ($ipdata[0] !~ /^Chain LOCALINPUT/) {
		$0 = "lfd - starting csf...";
		&logfile("iptables appears to have been flushed - running *csf startup*...");
		&syscommand(__LINE__,"/etc/csf/csf.pl -sf");
		&logfile("csf startup completed");
		$0 = "lfd - processing";
	}

	if ($config{DEBUG} >= 3) {$timer = &timer("stop","csfcheck",$timer)}
}
# end csfcheck
###############################################################################
# start loadcheck
sub loadcheck {
	if (-e "/etc/csf/csf.load") {
		open (IN, "</etc/csf/csf.load");
		flock (IN, LOCK_SH);
		my $start = <IN>;
		close (IN);
		chomp $start;
		if (time - $start < $config{PT_LOAD_SKIP}) {
			return;
		} else {
			unlink ("/etc/csf/csf.load");
		}
	}
	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","loadcheck",$timer)}
		$0 = "lfd - (child) checking load...";

		my $lockstr = "PT_LOAD";
		sysopen (THISLOCK, "/etc/csf/lock/$lockstr.lock", O_RDWR | O_CREAT) or &childcleanup("*Error* Unable to open /etc/csf/lock/$lockstr.lock");
		flock (THISLOCK, LOCK_EX | LOCK_NB) or &childcleanup("*Lock Error* [$lockstr] still active - section skipped");

		open (IN, "</proc/loadavg");
		my $loadavg = <IN>;
		close (IN);
		chomp $loadavg;
		my @load = split(/\s+/,$loadavg);

		my $reportload = $load[1];
		if ($config{PT_LOAD_AVG} == 1) {$reportload = $load[0]}
		elsif ($config{PT_LOAD_AVG} == 15) {$reportload = $load[2]}
		else {$config{PT_LOAD_AVG} = 5}

		if ($reportload >= $config{PT_LOAD_LEVEL}) {
			&logfile("*LOAD* $config{PT_LOAD_AVG} minute load average is $reportload, threshold is $config{PT_LOAD_LEVEL} - email sent");
			sysopen (LOAD, "/etc/csf/csf.load", O_WRONLY | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot write to file: $!");
			flock (LOAD, LOCK_EX);
			seek (LOAD, 0, 0);
			truncate (LOAD, 0);
			print LOAD time;
			close (LOAD);

			if ($config{PT_LOAD_ACTION} and -e "$config{PT_LOAD_ACTION}" and -x "$config{PT_LOAD_ACTION}") {
				unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
				unless (defined ($ptchildpid = fork)) {
					&cleanup(__LINE__,"*Error* cannot fork: $!");
				} 
				unless ($ptchildpid) {
					system($config{PT_LOAD_ACTION});
					exit;
				}
			}

			my @proclist;
			eval {
				local $SIG{__DIE__} = undef;
				local $SIG{'ALRM'} = sub {die};
				alarm(15);
				my ($childin, $childout);
				my $cmdpid = open3($childin, $childout, $childout, "$config{PS} axuf");
				@proclist = <$childout>;
				waitpid ($cmdpid, 0);
				alarm(0);
			};
			alarm(0);
			if ($@) {push @proclist, "Unable to obtain process output within 15 seconds - Timed out"}

			my @vmstat;
			eval {
				local $SIG{__DIE__} = undef;
				local $SIG{'ALRM'} = sub {die};
				alarm(10);
				my ($childin, $childout);
				my $cmdpid = open3($childin, $childout, $childout, "$config{VMSTAT}");
				@vmstat = <$childout>;
				waitpid ($cmdpid, 0);
				alarm(0);
			};
			alarm(0);
			if ($@) {push @vmstat, "Unable to obtain vmstat output within 10 seconds - Timed out"}

			my $url = $config{PT_APACHESTATUS};
			my ($status, $apache) = &urlget($url);
			if ($status) {$apache = "Unable to retrieve Apache Server Status [$url] - $apache"}

			open (IN, "</etc/csf/loadalert.txt");
			my @alert = <IN>;
			close (IN);
			chomp @alert;

			my $boundary = "csf".time;
			my @message;
			foreach my $line (@alert) {
				$line =~ s/\[loadavg1\]/$load[0]/ig;
				$line =~ s/\[loadavg5\]/$load[1]/ig;
				$line =~ s/\[loadavg15\]/$load[2]/ig;
				$line =~ s/\[loadavg\]/$config{PT_LOAD_AVG}/ig;
				$line =~ s/\[reportload\]/$reportload/ig;
				$line =~ s/\[totprocs\]/$load[3]/ig;
				$line =~ s/\[processlist\]/@proclist/ig;
				$line =~ s/\[vmstat\]/@vmstat/ig;
				$line =~ s/\[apache\]/$apache/ig;
				$line =~ s/\[boundary\]/$boundary/ig;
				push @message, $line;
			}
			&sendmail(@message);
		}

		close (THISLOCK);
		if ($config{DEBUG} >= 3) {$timer = &timer("stop","loadcheck",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end loadcheck
###############################################################################
# start queuecheck
sub queuecheck {
	if (-e "/etc/csf/csf.queue") {
		open (IN, "</etc/csf/csf.queue");
		flock (IN, LOCK_SH);
		my $start = <IN>;
		close (IN);
		chomp $start;
		if (time - $start < $config{LF_FLUSH}) {
			return;
		} else {
			unlink ("/etc/csf/csf.queue");
		}
	}
	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","queuecheck",$timer)}
		$0 = "lfd - (child) checking mail queue...";

		my $lockstr = "LF_QUEUE_INTERVAL";
		sysopen (THISLOCK, "/etc/csf/lock/$lockstr.lock", O_RDWR | O_CREAT) or &childcleanup("*Error* Unable to open /etc/csf/lock/$lockstr.lock");
		flock (THISLOCK, LOCK_EX | LOCK_NB) or &childcleanup("*Lock Error* [$lockstr] still active - section skipped");

		my $queue;
		my $msqueue;
		my $timeout = "";
		eval {
			local $SIG{__DIE__} = undef;
			local $SIG{'ALRM'} = sub {die};
			alarm(30);
			my ($childin, $childout);
			my $cmdpid = open3($childin, $childout, $childout, "/usr/sbin/exim -bpc");
			$queue = <$childout>;
			waitpid ($cmdpid, 0);
			alarm(0);
		};
		alarm(0);
		if ($@) {$timeout = "Unable to obtain exim queue length within 30 seconds - Timed out"}
		chomp $queue;

		if (-e "/etc/exim_outgoing.conf") {
			$msqueue = $queue;
			eval {
				local $SIG{__DIE__} = undef;
				local $SIG{'ALRM'} = sub {die};
				alarm(30);
				my ($childin, $childout);
				my $cmdpid = open3($childin, $childout, $childout, "/usr/sbin/exim -C /etc/exim_outgoing.conf -bpc");
				$queue = <$childout>;
				waitpid ($cmdpid, 0);
				alarm(0);
			};
			alarm(0);
			if ($@) {$timeout = "Unable to obtain exim_outgoing.conf queue length within 30 seconds - Timed out"}
			chomp $queue;
		}

		if (($queue > $config{LF_QUEUE_ALERT}) or ($msqueue > $config{LF_QUEUE_ALERT}) or ($timeout ne "")) {
			my $report = "The exim delivery queue size is $queue";
			if ($msqueue) {$report .= ", the MailScanner pending queue size is $msqueue"}
			if ($timeout) {$report = $timeout}
			&logfile("*Email Queue* $report");

			sysopen (QUEUE, "/etc/csf/csf.queue", O_WRONLY | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot write to file: $!");
			flock (QUEUE, LOCK_EX);
			seek (QUEUE, 0, 0);
			truncate (QUEUE, 0);
			print QUEUE time;
			close (QUEUE);

			open (IN, "</etc/csf/queuealert.txt");
			my @alert = <IN>;
			close (IN);
			chomp @alert;

			my @message;
			foreach my $line (@alert) {
				$line =~ s/\[text\]/$report/ig;
				push @message, $line;
			}
			&sendmail(@message);
		}

		close (THISLOCK);
		if ($config{DEBUG} >= 3) {$timer = &timer("stop","queuecheck",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end queuecheck
###############################################################################
# start connectiontracking
sub connectiontracking {

	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","connectiontracking",$timer)}
		$0 = "lfd - (child) connection tracking...";

		my $lockstr = "CT_INTERVAL";
		sysopen (THISLOCK, "/etc/csf/lock/$lockstr.lock", O_RDWR | O_CREAT) or &childcleanup("*Error* Unable to open /etc/csf/lock/$lockstr.lock");
		flock (THISLOCK, LOCK_EX | LOCK_NB) or &childcleanup("*Lock Error* [$lockstr] still active - section skipped");

		my @connections;
		my %ipcnt;
		my %iptext;
		my $alarm = int($config{CT_INTERVAL}/10) + 10;
		my $start = time;
		my $tfail = 0;
		my %states;
		if ($config{CT_STATES}) {
			foreach my $state (split(/\,/,$config{CT_STATES})) {
				$states{$state} = 1;
			}
		}
		my %countports;
		if ($config{CT_PORTS}) {
			foreach my $port (split(/\,/,$config{CT_PORTS})) {
				$countports{$port} = 1;
			}
		}

		my %net;
		my %tcpstates = ("01" => "ESTABLISHED",
						 "02" => "SYN_SENT",
						 "03" => "SYN_RECV",
						 "04" => "FIN_WAIT1",
						 "05" => "FIN_WAIT2",
						 "06" => "TIME_WAIT",
						 "07" => "CLOSE",
						 "08" => "CLOSE_WAIT",
						 "09" => "LAST_ACK",
						 "0A" => "LISTEN",
						 "0B" => "CLOSING");
		foreach my $proto ("tcp","udp","tcp6","udp6") {
			open (IN, "</proc/net/$proto");
			while (<IN>) {
				my @rec = split();
				if ($rec[9] =~ /uid/) {next}
				my (undef,$sport) = split(/:/,$rec[2]);
				$sport = hex($sport);

				my (undef,$dport) = split(/:/,$rec[1]);
				$dport = hex($dport);

				my $dip = &converthex2ip($rec[1]);
				my $sip = &converthex2ip($rec[2]);

				my $state = $tcpstates{$rec[3]};

				if ($config{DEBUG} >= 4) {logfile("debug: CT $proto: $sip:$sport -> $dip:$dport state:[$state]")}

				if ($config{CT_SKIP_TIME_WAIT} and ($state eq "TIME_WAIT")) {next}
				if ($config{CT_STATES} and ($states{$state} != 1)) {next}
				if ($config{CT_PORTS} and ($countports{$dport} != 1)) {next}
				if ($state eq "LISTEN") {next}
				if ($sip =~ /^127\./) {next}
				if ($sip =~ /^0\./) {next}
				$ipcnt{$sip}++;
				$iptext{$sip} .= "$proto: $sip:$sport -> $dip:$dport ($state)\n";
				if ($config{DEBUG} >= 4) {logfile("debug: CT $proto: $sip:$sport -> $dip:$dport state:[$state] count:[$ipcnt{$sip}]")}
			}
			close (IN);
		}

		open (IN, "</etc/csf/csf.deny");
		flock (IN, LOCK_SH);
		my @csfdeny = <IN>;
		close (IN);
		chomp @csfdeny;
		foreach my $line (@csfdeny) {
			if ($line =~ /^Include\s*(.*)$/) {
				my $incfile = $1;
				open (INCFILE, "<",$incfile) or &error(__LINE__,"Could not open $incfile: $!");
				flock (INCFILE, LOCK_SH) or &error(__LINE__,"Could not lock $incfile: $!");
				my @incdeny = <INCFILE>;
				close (INCFILE) or &error(__LINE__,"Could not close $incfile: $!");
				chomp @incdeny;
				push @csfdeny,@incdeny;
			}
		}

		open (IN, "</etc/csf/csf.tempban");
		flock (IN, LOCK_SH);
		my @csftmpdeny = <IN>;
		close (IN);
		chomp @csftmpdeny;

		foreach my $ip (keys %ipcnt) {
			if (($ipcnt{$ip} > $config{CT_LIMIT}) and !&ignoreip($ip)) {
				if ((grep {$_ =~ /^$ip\b/} @csfdeny) or (grep {$_ =~ /\|$ip\|\|/} @csftmpdeny)) {
					if ($config{DEBUG} >= 1) {&logfile("debug: (CT) IP $ip found to have $ipcnt{$ip} connections - IP already blocked")}
				} else {
					my $tip = &iplookup($ip);
					&ipblock($config{CT_PERMANENT},"(CT) IP $tip found to have $ipcnt{$ip} connections",$ip,"","inout",$config{CT_BLOCK_TIME},0,$iptext{$ip},"CT_LIMIT");

					if ($config{CT_EMAIL_ALERT}) {
						$0 = "lfd - (child) (CT) sending alert email for $ip";

						open (IN, "</etc/csf/connectiontracking.txt");
						my @alert = <IN>;
						close (IN);
						chomp @alert;

						my $block = "Temporary Block";
						if ($config{CT_PERMANENT}) {$block = "Permanent Block"}

						my $allowip = &allowip($ip);
						if ($allowip == 1) {$block .= " (IP match in csf.allow, block may not work)"}
						if ($allowip == 2) {$block .= " (IP match in GLOBAL_ALLOW, block may not work)"}

						my @message;
						foreach my $line (@alert) {
							$line =~ s/\[ip\]/$tip/ig;
							$line =~ s/\[ipcount\]/$ipcnt{$ip}/ig;
							$line =~ s/\[iptext\]/$iptext{$ip}/ig;
							$line =~ s/\[temp\]/$block/ig;
							push @message, $line;
						}
						&sendmail(@message);

						if ($config{X_ARF}) {
							$0 = "lfd - (child) sending X-ARF email for $ip";

							open (IN, "</etc/csf/x-arf.txt");
							my @alert = <IN>;
							close (IN);
							chomp @alert;

							my @message;
							my $rfc3339 = strftime('%Y-%m-%dT%H:%M:%S%z',localtime);
							my $boundary = time;
							my $reportedfrom = "root\@$hostname";
							if ($config{X_ARF_TO}) {$config{LF_ALERT_TO} = $config{X_ARF_TO}}
							if ($config{X_ARF_FROM}) {$config{LF_ALERT_FROM} = $config{X_ARF_FROM}; $reportedfrom = $config{X_ARF_FROM}}
							my $iptype = "IPv".&checkip($ip);
							foreach my $line (@alert) {
								$line =~ s/\[ip\]/$ip/ig;
								$line =~ s/\[iptype\]/$iptype/ig;
								$line =~ s/\[tip\]/$tip/ig;
								$line =~ s/\[ipcount\]/$ipcnt{$ip}/ig;
								$line =~ s/\[iptick\]/$config{LF_INTERVAL}/ig;
								$line =~ s/\[service\]/port-flood/ig;
								$line =~ s/\[csfversion\]/$version/ig;
								$line =~ s/\[reportedfrom\]/$reportedfrom/ig;
								$line =~ s/\[reportedid\]/$boundary\@$hostname/ig;
								$line =~ s/\[boundary\]/$boundary/ig;
								$line =~ s/\[text\]/$iptext{$ip}/ig;
								$line =~ s/\[RFC3339\]/$rfc3339/ig;
								push @message, $line;
							}
							&sendmail(@message);

							if ($config{DEBUG} >= 1) {&logfile("debug: X-ARF email sent for $ip")}
						}

						if ($config{DEBUG} >= 1) {&logfile("debug: alert email sent for $ip")}
					}
				}
			}
		}
		if ($tfail) {
			$config{CT_INTERVAL} = $config{CT_INTERVAL} * 1.5;
			sysopen (TEMPCONF, "/etc/csf/csf.tempconf", O_WRONLY | O_APPEND | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot append out file: $!");
			flock (TEMPCONF, LOCK_EX);
			print TEMPCONF "CT_INTERVAL = \"$config{CT_INTERVAL}\"\n";
			close (TEMPCONF);
			&logfile("CT_INTERVAL taking $alarm seconds, temporarily throttled to run every $config{CT_INTERVAL} seconds");
		}

		close (THISLOCK);
		if ($config{DEBUG} >= 3) {$timer = &timer("stop","connectiontracking",$timer)}
		$0 = "lfd - (child) closing";
		exit;
	}
}
# end connectiontracking
###############################################################################
# start accounttracking
sub accounttracking {
	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","accounttracking",$timer)}
		$0 = "lfd - (child) account tracking...";

		my $lockstr = "AT_INTERVAL";
		sysopen (THISLOCK, "/etc/csf/lock/$lockstr.lock", O_RDWR | O_CREAT) or &childcleanup("*Error* Unable to open /etc/csf/lock/$lockstr.lock");
		flock (THISLOCK, LOCK_EX | LOCK_NB) or &childcleanup("*Lock Error* [$lockstr] still active - section skipped");

		my $report = "";
		foreach my $user (keys %newaccounttracking) {
			if (($config{AT_ALERT} eq "2") and ($newaccounttracking{$user}{uid} ne "0")) {next}
			if ($accounttracking{$user}{account} != 1) {
				if ($config{AT_NEW}) {
					$report .= "New account [$user] has been created with uid:[$newaccounttracking{$user}{uid}] gid:[$newaccounttracking{$user}{gid}] login:[$newaccounttracking{$user}{dir}] shell:[$newaccounttracking{$user}{shell}]\n";
				}
			} else {
				if ($config{AT_PASSWD} and ($newaccounttracking{$user}{passwd} ne $accounttracking{$user}{passwd})) {
					$report .= "Account [$user] password has changed\n";
				}
				if ($config{AT_UID} and ($newaccounttracking{$user}{uid} ne $accounttracking{$user}{uid})) {
					$report .= "Account [$user] uid has changed from [$accounttracking{$user}{uid}] to [$newaccounttracking{$user}{uid}]\n";
				}
				if ($config{AT_GID} and ($newaccounttracking{$user}{gid} ne $accounttracking{$user}{gid})) {
					$report .= "Account [$user] gid has changed from [$accounttracking{$user}{gid}] to [$newaccounttracking{$user}{gid}]\n";
				}
				if ($config{AT_DIR} and ($newaccounttracking{$user}{dir} ne $accounttracking{$user}{dir})) {
					$report .= "Account [$user] login directory has changed from [$accounttracking{$user}{dir}] to [$newaccounttracking{$user}{dir}]\n";
				}
				if ($config{AT_SHELL} and ($newaccounttracking{$user}{shell} ne $accounttracking{$user}{shell})) {
					$report .= "Account [$user] login shell has changed from [$accounttracking{$user}{shell}] to [$newaccounttracking{$user}{shell}]\n";
				}
			}
		}
		foreach my $user (keys %accounttracking) {
			if (($config{AT_ALERT} eq "2") and ($accounttracking{$user}{uid} ne "0")) {next}
			if ($config{AT_OLD} and ($newaccounttracking{$user}{account} != 1)) {
				$report .= "Existing account [$user] has been removed. Old settings uid:[$accounttracking{$user}{uid}] gid:[$accounttracking{$user}{gid}] login:[$accounttracking{$user}{dir}] shell:[$accounttracking{$user}{shell}]\n";
			}
		}
		if ($report ne "") {
			&logfile("*Account Modification* Email sent");

			open (IN, "</etc/csf/accounttracking.txt");
			my @alert = <IN>;
			close (IN);
			chomp @alert;

			my @message;
			foreach my $line (@alert) {
				$line =~ s/\[report\]/$report/ig;
				push @message, $line;
			}
			&sendmail(@message);
		}			

		close (THISLOCK);
		if ($config{DEBUG} >= 3) {$timer = &timer("stop","accounttracking",$timer)}
		$0 = "lfd - (child) closing";
		exit;
	}
}
# end accounttracking
###############################################################################
# start syslogcheck
sub syslogcheck {
	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","syslogcheck",$timer)}
		$0 = "lfd - (child) SYSLOG check...";

		my $lockstr = "SYSLOG_CHECK";
		sysopen (THISLOCK, "/etc/csf/lock/$lockstr.lock", O_RDWR | O_CREAT) or &childcleanup("*Error* Unable to open /etc/csf/lock/$lockstr.lock");
		flock (THISLOCK, LOCK_EX | LOCK_NB) or &childcleanup("*Lock Error* [$lockstr] still active - section skipped");

		&logfile("*SYSLOG CHECK* Failed to detect check line [$syslogcheckcode] sent to SYSLOG");

		open (IN, "</etc/csf/syslogalert.txt");
		my @alert = <IN>;
		close (IN);
		chomp @alert;

		my @message;
		foreach my $line (@alert) {
			$line =~ s/\[code\]/$syslogcheckcode/ig;
			$line =~ s/\[log\]/$config{SYSLOG_LOG}/ig;
			push @message, $line;
		}
		&sendmail(@message);

		close (THISLOCK);
		if ($config{DEBUG} >= 3) {$timer = &timer("stop","syslogcheck",$timer)}
		$0 = "lfd - (child) closing";
		exit;
	}
}
# end syslogcheck
###############################################################################
# start processtracking
sub processtracking {
	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","processtracking",$timer)}
		$0 = "lfd - (child) process tracking...";

		my $lockstr = "PT_INTERVAL";
		sysopen (THISLOCK, "/etc/csf/lock/$lockstr.lock", O_RDWR | O_CREAT) or &childcleanup("*Error* Unable to open /etc/csf/lock/$lockstr.lock");
		flock (THISLOCK, LOCK_EX | LOCK_NB) or &childcleanup("*Lock Error* [$lockstr] still active - section skipped");

		my %users;
		my %net;

		unless ($config{GENERIC}) {
			opendir (DIR, "/var/cpanel/users");
			while (my $user = readdir (DIR)) {
				if ($user =~ /^\./) {next}
				$users{$user} = 1;
			}
			closedir (DIR);
			$users{nobody} = 1;
		}

		foreach my $proto ("udp","tcp","udp6","tcp6") {
			open (IN, "</proc/net/$proto");
			while (<IN>) {
				my @rec = split();
				if ($rec[9] =~ /uid/) {next}

				my (undef,$sport) = split(/:/,$rec[1]);
				$sport = hex($sport);

				my (undef,$dport) = split(/:/,$rec[2]);
				$dport = hex($dport);

				my $sip = &converthex2ip($rec[1]);
				my $dip = &converthex2ip($rec[2]);

				if ($sip eq '0.0.0.1') {next}

				$net{$rec[9]}{proto} = $proto;
				$net{$rec[9]}{sport} = $sport;
				$net{$rec[9]}{sip} = $sip;
				$net{$rec[9]}{dport} = $dport;
				$net{$rec[9]}{dip} = $dip;
				if ($config{DEBUG} >= 4) {logfile("debug: PT $proto: $sip:$sport -> $dip:$dport")}
			}
			close (IN);
		}

		open (IN,"</proc/uptime");
		my @up = <IN>;
		close (IN);
		chomp @up;
		my ($upsecs,undef) = split (/\s/,$up[0]);

		my %pids;
		if (! -z "/etc/csf/csf.temppids") {
			open (IN, "</etc/csf/csf.temppids");
			flock (IN, LOCK_SH);
			my @data = <IN>;
			close (IN);
			chomp @data;

			foreach my $line (@data) {
				my ($itemttl,$item) = split(/:/,$line);
				if (time - $itemttl < $config{LF_FLUSH}) {
					$pids{$item} = 1;
				}
			}
		}
		my %ignoreusers;
		if (! -z "/etc/csf/csf.tempusers") {
			open (IN, "</etc/csf/csf.tempusers");
			flock (IN, LOCK_SH);
			my @data = <IN>;
			close (IN);
			chomp @data;

			foreach my $line (@data) {
				my ($itemttl,$item) = split(/:/,$line);
				if (time - $itemttl < $config{LF_FLUSH}) {
					$ignoreusers{$item} = 1;
				}
			}
		}

		my %totproc;
		my %procres;
		opendir (PROCDIR, "/proc");
		while (my $pid = readdir(PROCDIR)) {
			if ($pid !~ /^\d+$/) {next}
			open (IN,"</proc/$pid/status") or next;
			my @status = <IN>;
			close (IN);
			chomp @status;
			my $user;
			my $uid;
			my $vmsize = 0;
			foreach my $line (@status) {
				if ($line =~ /^Uid:(.*)/) {
					my $uidline = $1;
					my @uids;
					foreach my $bit (split(/\s/,$uidline)) {
						if ($bit =~ /^(\d*)$/) {push @uids, $1}
					}
					$uid = $uids[-1];
					$user = getpwuid($uid);
				}
				if ($line =~ /^VmSize:\s+(\d+) kB$/) {$vmsize = $1}
			}

			if ($users{$user} or $config{GENERIC} or $config{PT_ALL_USERS}) {
				if ($user eq "root") {next}
				if ($pids{$pid}) {next}
				if ($skip{user}{$user}) {next}
				my $pmatch = 0;
				foreach my $item (keys %{$pskip{puser}}) {
					if ($user =~ /^$item$/) {
						$pmatch = 1;
						last;
					}
				}
				if ($pmatch) {next}

				my %printable = ( ( map { chr($_), unpack('H2', chr($_)) } (0..255) ), "\\"=>'\\', "\r"=>'r', "\n"=>'n', "\t"=>'t', "\""=>'"' );

				my $exe = readlink("/proc/$pid/exe");
				my $cwd = readlink("/proc/$pid/cwd");
				$exe =~ s/([\r\n\t\"\\\x00-\x1f\x7F-\xFF])/\\$printable{$1}/sg;
				$cwd =~ s/([\r\n\t\"\\\x00-\x1f\x7F-\xFF])/\\$printable{$1}/sg;
				if ($exe eq "") {next}

				if ($config{DEBUG} >= 4) {logfile("debug: PT exe = $exe")}
				my $exet = $exe;
				my $deleted = 0;
				if ($exe =~ /\(deleted\)/) {
					$deleted = 1;
					if ($config{PT_DELETED}) {
						$exet .= "\n\nThe file system shows this process is running an executable file that has been deleted. This typically happens when the original file has been replaced by a new file when the application is updated. To prevent this being reported again, restart the process that runs this excecutable file. See csf.conf and the PT_DELETED text for more information about the security implications of processes running deleted executable files.";
					} else {next}
				}

				if ($skip{exe}{$exe}) {next}

				$pmatch = 0;
				foreach my $item (keys %{$pskip{pexe}}) {
					if ($exe =~ /^$item$/) {
						$pmatch = 1;
						last;
					}
				}
				if ($pmatch) {next}

				open (IN,"</proc/$pid/cmdline");
				my @cmdline = <IN>;
				close (IN);
				chomp @cmdline;
				$cmdline[0] =~ s/\0$//g;
				$cmdline[0] =~ s/\0/ /g;
				$cmdline[0] =~ s/([\r\n\t\"\\\x00-\x1f\x7F-\xFF])/\\$printable{$1}/sg;
				if ($skip{cmd}{$cmdline[0]}) {next}
				$pmatch = 0;
				foreach my $item (keys %{$pskip{pcmd}}) {
					if ($cmdline[0] =~ /^$item$/) {
						$pmatch = 1;
						last;
					}
				}
				if ($pmatch) {next}

				if (($config{MESSENGER} and $user eq $config{MESSENGER_USER}) and ($cmdline[0] =~ /^lfd (HTML|TEXT) messenger/)) {next}

				open (IN,"</proc/$pid/stat") or next;
				my @pstatline = <IN>;
				close (IN);
				chomp @pstatline;
				my @pstat = split(/\s/,$pstatline[0]);

				if ($config{PT_SKIP_HTTP}) {
					my $pgrp = $pstat[4];
					my $pgrpexe = readlink("/proc/$pgrp/exe");
					if (($pid ne $pgrp) and ($pgrpexe eq "/usr/local/apache/bin/httpd")) {next}
				}

				my $jiffsecs = $pstat[21] / $clock_ticks;
				my $uptime = int($upsecs - $jiffsecs);

				if ($user ne "nobody") {
					unless ($deleted) {
						$totproc{$user}{count}++;
						if ($totproc{$user}{pids} eq "") {
							$totproc{$user}{pids} = $pid;
						} else {
							$totproc{$user}{pids} .= ",$pid";
						}
						$totproc{$user}{text} .= "User:$user PID:$pid Run Time:$uptime(secs) Memory:$vmsize(kb) exe:$exe cmd:$cmdline[0]\n";
						$procres{$pid}{vmsize} = $vmsize;
						$procres{$pid}{uptime} = $uptime;
						$procres{$pid}{user} = $user;
						$procres{$pid}{exe} = $exe;
						$procres{$pid}{exet} = $exet;
						$procres{$pid}{cmd} = $cmdline[0];
					}
				}

				if ($uptime > $config{PT_LIMIT}) {
					my $suspect = 0;

					my @fd;
					opendir (DIR, "/proc/$pid/fd") or next;
					while (my $file = readdir (DIR)) {
						if ($file =~ /^\./) {next}
						push (@fd, readlink("/proc/$pid/fd/$file"));
					}
					closedir (DIR);

					my $files;
					my $sockets;
					foreach my $file (@fd) {
						if ($file =~ /^socket:\[?([0-9]+)\]?$/) {
							my $ino = $1;
							if ($net{$ino}) {
								$sockets .= "$net{$ino}{proto}: $net{$ino}{sip}:$net{$ino}{sport} -> $net{$ino}{dip}:$net{$ino}{dport}\n";
								if ($suspect != 2) {$suspect = 1}
								if ($config{PT_SKIP_HTTP} and $net{$ino}{sport} =~ /^(80|443)$/) {$suspect = 2}
							}
						}
						if ($file =~ /^socket|pipe/) {next}
						$files .= $file."\n";
					}
					if ($suspect == 2) {$suspect = 0}

					if ($suspect or $deleted) {
						my $sexe = readlink("/proc/$pid/exe");
						if ($sexe eq "") {next}

						&logfile("*Suspicious Process* PID:$pid User:$user Uptime:$uptime secs EXE:$exe CMD:$cmdline[0]");

						sysopen (TEMPPIDS, "/etc/csf/csf.temppids", O_WRONLY | O_APPEND | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot append out file: $!");
						flock (TEMPPIDS, LOCK_EX);
						print TEMPPIDS time.":$pid\n";
						close (TEMPPIDS);

						$0 = "lfd - (child) (PT) sending alert email for process $pid";

						open (IN,"</proc/$pid/maps");
						my @maps = <IN>;
						close (IN);
						chomp @maps;
						my $maps;
						foreach my $line (@maps) {$maps .= $line."\n"}

						open (IN, "</etc/csf/processtracking.txt");
						my @alert = <IN>;
						close (IN);
						chomp @alert;

						my @message;
						foreach my $line (@alert) {
							$line =~ s/\[pid\]/$pid/ig;
							$line =~ s/\[user\]/$user/ig;
							$line =~ s/\[uptime\]/$uptime/ig;
							$line =~ s/\[sockets\]/$sockets/ig;
							$line =~ s/\[files\]/$files/ig;
							$line =~ s/\[maps\]/$maps/ig;
							$line =~ s/\[exe\]/$exet/ig;
							$line =~ s/\[cmdline\]/$cmdline[0]/ig;
							push @message, $line;
						}
						&sendmail(@message);

						if ($deleted and $config{PT_DELETED_ACTION} and -e "$config{PT_DELETED_ACTION}" and -x "$config{PT_DELETED_ACTION}") {
							unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
							unless (defined ($ptchildpid = fork)) {
								&childcleanup(__LINE__,"*Error* cannot fork: $!");
							} 
							unless ($ptchildpid) {
								system($config{PT_DELETED_ACTION},$exe,$pid,$user);
								&logfile("Executed PT_DELETED_ACTION for PID:$pid");
								exit;
							}
						}

					}
				}
			}
		}
		if ($config{PT_USERPROC}) {
			$0 = "lfd - (child) (PT) checking user processes";
			foreach my $user (keys %totproc) {
				if ($ignoreusers{$user}) {next}
				if ($totproc{$user}{count} > $config{PT_USERPROC}) {
					my $kill = "Not killed";
					if ($config{PT_USERKILL}) {
						foreach my $pid (split(/\,/,$totproc{$user}{pids})) {
							kill (9, $pid);
						}
						$kill = "Killed";
					} else {
						sysopen (TEMPUSERS, "/etc/csf/csf.tempusers", O_WRONLY | O_APPEND | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot append out file: $!");
						flock (TEMPUSERS, LOCK_EX);
						print TEMPUSERS time.":$user\n";
						close (TEMPUSERS);
					}

					&logfile("*Excessive Processes* User:$user Kill:$config{PT_USERKILL} Process Count:$totproc{$user}{count}");

					if (!$config{PT_USERKILL} or ($config{PT_USERKILL} and $config{PT_USERKILL_ALERT})) {
						open (IN, "</etc/csf/usertracking.txt");
						my @alert = <IN>;
						close (IN);
						chomp @alert;

						my @message;
						foreach my $line (@alert) {
							$line =~ s/\[user\]/$user/ig;
							$line =~ s/\[count\]/$totproc{$user}{count} \($kill\)/ig;
							$line =~ s/\[text\]/$totproc{$user}{text}/ig;
							$line =~ s/\[kill\]/$kill/ig;
							push @message, $line;
						}
						&sendmail(@message);
					}
					if ($config{PT_USER_ACTION} and -e "$config{PT_USER_ACTION}" and -x "$config{PT_USER_ACTION}") {
						unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
						unless (defined ($ptchildpid = fork)) {
							&childcleanup(__LINE__,"*Error* cannot fork: $!");
						} 
						unless ($ptchildpid) {
							system($config{PT_USER_ACTION},$totproc{$user}{pids});
							exit;
						}
					}
				}
			}
		}

		if ($config{PT_USERMEM} or $config{PT_USERTIME}) {
			foreach my $pid (keys %procres) {
				my $report = 0;
				my $resource;
				my $level;
				if ($config{PT_USERMEM} and ($procres{$pid}{vmsize} > ($config{PT_USERMEM} * 1024))) {
					$report = 1;
					$resource = "Virtual Memory Size";
					my $memsize = int($procres{$pid}{vmsize} / 1024);
					$level = "$memsize > $config{PT_USERMEM} (MB)";
					&logfile("*User Processing* PID:$pid Kill:$config{PT_USERKILL} User:$procres{$pid}{user} VM:$memsize(MB) EXE:$procres{$pid}{exe} CMD:$procres{$pid}{cmd}");
				}
				if ($config{PT_USERTIME} and ($procres{$pid}{uptime} > $config{PT_USERTIME})) {
					$report = 1;
					$resource = "Process Time";
					$level = "$procres{$pid}{uptime} > $config{PT_USERTIME} (seconds)";
					&logfile("*User Processing* PID:$pid Kill:$config{PT_USERKILL} User:$procres{$pid}{user} Time:$procres{$pid}{uptime} EXE:$procres{$pid}{exe} CMD:$procres{$pid}{cmd}");
				}
				if ($report) {
					my $kill = "No";
					if ($config{PT_USERKILL}) {
						kill (9, $pid);
						$kill = "Yes";
					} else {
						sysopen (TEMPPIDS, "/etc/csf/csf.temppids", O_WRONLY | O_APPEND | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot append out file: $!");
						flock (TEMPPIDS, LOCK_EX);
						print TEMPPIDS time.":$pid\n";
						close (TEMPPIDS);
					}

					if (!$config{PT_USERKILL} or ($config{PT_USERKILL} and $config{PT_USERKILL_ALERT})) {
						open (IN, "</etc/csf/resalert.txt");
						my @alert = <IN>;
						close (IN);
						chomp @alert;

						my @message;
						foreach my $line (@alert) {
							$line =~ s/\[user\]/$procres{$pid}{user}/ig;
							$line =~ s/\[cmd\]/$procres{$pid}{cmd}/ig;
							$line =~ s/\[exe\]/$procres{$pid}{exet}/ig;
							$line =~ s/\[resource\]/$resource/ig;
							$line =~ s/\[level\]/$level/ig;
							$line =~ s/\[kill\]/$kill/ig;
							$line =~ s/\[pid\]/$pid/ig;
							push @message, $line;
						}
						&sendmail(@message);
					}

					if ($config{PT_USER_ACTION} and -e "$config{PT_USER_ACTION}" and -x "$config{PT_USER_ACTION}") {
						unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
						unless (defined ($ptchildpid = fork)) {
							&childcleanup(__LINE__,"*Error* cannot fork: $!");
						} 
						unless ($ptchildpid) {
							system($config{PT_USER_ACTION},$pid);
							exit;
						}
					}
				}
			}
		}

		close (THISLOCK);
		if ($config{DEBUG} >= 3) {$timer = &timer("stop","processtracking",$timer)}
		$0 = "lfd - (child) closing";
		exit;
	}
}
# end processtracking
###############################################################################
# start sshalert
sub sshalert {
	my $account = shift;
	my $ip = shift;
	my $method = shift;

	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","sshalert",$timer)}
		&logfile("*SSH login* from $ip into the $account account using $method authentication");

		$0 = "lfd - (child) sending SSH login alert email for $ip";

		open (IN, "</etc/csf/sshalert.txt");
		my @alert = <IN>;
		close (IN);
		chomp @alert;

		my $tip = &iplookup($ip);
		my @message;
		foreach my $line (@alert) {
			$line =~ s/\[ip\]/$tip/ig;
			$line =~ s/\[account\]/$account/ig;
			$line =~ s/\[method\]/$method/ig;
			push @message, $line;
		}
		&sendmail(@message);

		if ($config{DEBUG} >= 3) {$timer = &timer("stop","sshalert",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end sshalert
###############################################################################
# start sualert
sub sualert {
	my $suto = shift;
	my $sufrom = shift;
	my $status = shift;

	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","sualert",$timer)}
		&logfile("*SU login* from account $sufrom to account $suto: $status");

		$0 = "lfd - (child) sending SU login alert email from $sufrom to $suto";

		open (IN, "</etc/csf/sualert.txt");
		my @alert = <IN>;
		close (IN);
		chomp @alert;

		my @message;
		foreach my $line (@alert) {
			$line =~ s/\[to\]/$suto/ig;
			$line =~ s/\[from\]/$sufrom/ig;
			$line =~ s/\[status\]/$status/ig;
			push @message, $line;
		}
		&sendmail(@message);

		if ($config{DEBUG} >= 3) {$timer = &timer("stop","sualert",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end sshalert
###############################################################################
# start consolealert
sub consolealert {
	my $logline = shift;
	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","consolealert",$timer)}
		&logfile("*CONSOLE login* to root");

		$0 = "lfd - (child) sending console login alert email";

		open (IN, "</etc/csf/consolealert.txt");
		my @alert = <IN>;
		close (IN);
		chomp @alert;

		my @message;
		foreach my $line (@alert) {
			$line =~ s/\[line\]/$logline/ig;
			push @message, $line;
		}
		&sendmail(@message);

		if ($config{DEBUG} >= 3) {$timer = &timer("stop","consolealert",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end consolealert
###############################################################################
# start cpanelalert
sub cpanelalert {
	my $ip = shift;
	my $user = shift;

	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","cpanelalert",$timer)}
		&logfile("*WHM/cPanel $user access* from $ip");

		$0 = "lfd - (child) sending WHM/cPanel access alert email for $ip";

		open (IN, "</etc/csf/cpanelalert.txt");
		my @alert = <IN>;
		close (IN);
		chomp @alert;

		my $tip = &iplookup($ip);
		my @message;
		foreach my $line (@alert) {
			$line =~ s/\[ip\]/$tip/ig;
			$line =~ s/\[user\]/$user/ig;
			push @message, $line;
		}
		&sendmail(@message);

		if ($config{LF_CPANEL_ALERT_ACTION} and -e "$config{LF_CPANEL_ALERT_ACTION}" and -x "$config{LF_CPANEL_ALERT_ACTION}") {
			$0 = "lfd - (child) running LF_CPANEL_ALERT_ACTION";
			system($config{LF_CPANEL_ALERT_ACTION},$ip,$user,$tip);
		}

		if ($config{DEBUG} >= 3) {$timer = &timer("stop","cpanelalert",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end cpanelalert
###############################################################################
# start scriptalert
sub scriptalert {
	my $path = shift;
	my $count = shift;
	my $mails = shift;
	my $text;
	my $files;

	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","scriptalert",$timer)}
		if ($skipscript{$path}) {
			&logfile("*Script Alert* - A script in $path has sent an email $count times within the last hour - ignored");
			exit;
		}
		&logfile("*Script Alert* - A script in $path has sent an email $count times within the last hour");

		$0 = "lfd - (child) identifying possible email scripts in $path";

		opendir (DIR, "$path");
		while (my $file = readdir (DIR)) {
			my $ext = (split(/\./,$file))[-1];
			if ($ext =~ /cgi|pl|php/) {
				open (IN, "<$path/$file");
				my @data = <IN>;
				close (IN);
				chomp @data;
				foreach my $line (@data) {
					if ($line =~ /mail\s*\(/) {$files .= "$path/$file\n"; last;}
					if ($line =~ /sendmail/) {$files .= "$path/$file\n"; last;}
					if ($line =~ /exim/) {$files .= "$path/$file\n"; last;}
				}
			}
		}
		closedir (DIR);

		if ($config{LF_SCRIPT_PERM}) {
			if (-l "$path") {
				&logfile("$path is a symlink - *not* disabled by LF_SCRIPT_PERM");
				$files .= "\nDirectory $path is a symlink - *not* disabled\n";
			} else {
				my $perms = sprintf "%04o", (stat("$path"))[2] & 00777;
				$files .= "\nDirectory $path has been disabled with 000 permissions.\n\nTo restore the permissions use:\nchattr -i $path\nchmod $perms $path\n";
				chmod (0000,$path);
				system("$config{CHATTR}","+i","$path");
				&logfile("$path has been disabled");
			}
		}

		$0 = "lfd - (child) sending script alert for $path";

		open (IN, "</etc/csf/scriptalert.txt");
		my @alert = <IN>;
		close (IN);
		chomp @alert;

		my @message;
		foreach my $line (@alert) {
			$line =~ s/\[path\]/$path/ig;
			$line =~ s/\[count\]/$count/ig;
			$line =~ s/\[emails\]/$mails/ig;
			$line =~ s/\[scripts\]/$files/ig;
			push @message, $line;
		}
		&sendmail(@message);

		if ($config{DEBUG} >= 3) {$timer = &timer("stop","scriptalert",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end scriptalert
###############################################################################
# start relayalert
sub relayalert {
	my $ip = shift;
	my $cnt = shift;
	my $check = shift;
	my $mails = shift;

	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","relayalert",$timer)}

		&logfile("*Exceeded $check limit* from $ip ($cnt in the last hour)");

		$0 = "lfd - (child) reporting exceeded $check limit";

		my $tip = $ip;
		my $type = "$check, Local Account";
		if ($ip =~ /^127\./) {
			$type = "$check, localhost";
		}
		elsif ($ip =~ /\d+\.\d+\.\d+\.\d+/) {
			$tip = &iplookup($ip);
			$type = "$check, Remote IP";
		}

		if ($config{"RT\_$check\_BLOCK"}) {
			if (($ip =~ /\d+\.\d+\.\d+\.\d+/) and !&ignoreip($ip)) {
				my $perm = 0;
				if ($config{"RT\_$check\_BLOCK"} == 1) {$perm = 1}
				&ipblock($perm,"$tip $check limit exceeded",$ip,$ports{smtpauth},"in",$config{"RT\_$check\_BLOCK"},0,$mails,"RT\_$check\_LIMIT");
			}
		}

		open (IN, "</etc/csf/relayalert.txt");
		my @alert = <IN>;
		close (IN);
		chomp @alert;

		my $block = "No";
		if ($config{"RT\_$check\_BLOCK"} == 1) {$block = "Permanent Block"}
		if ($config{"RT\_$check\_BLOCK"} > 1) {$block = "Temporary Block"}

		my $allowip = &allowip($ip);
		if ($allowip == 1 and $block ne "No") {$block .= " (IP match in csf.allow, block may not work)"}
		if ($allowip == 2 and $block ne "No") {$block .= " (IP match in GLOBAL_ALLOW, block may not work)"}

		my @message;
		foreach my $line (@alert) {
			$line =~ s/\[ip\]/$tip/ig;
			$line =~ s/\[block\]/$block/ig;
			$line =~ s/\[check\]/$check/ig;
			$line =~ s/\[type\]/$type/ig;
			$line =~ s/\[count\]/$cnt/ig;
			$line =~ s/\[emails\]/$mails/ig;
			push @message, $line;
		}
		&sendmail(@message);

		if ($config{RT_ACTION} and -e "$config{RT_ACTION}" and -x "$config{RT_ACTION}") {
			$0 = "lfd - (child) running RT_ACTION";
			system($config{RT_ACTION},$ip,$check,$block,$cnt,$mails);
		}

		if ($config{DEBUG} >= 3) {$timer = &timer("stop","relayalert",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end relayalert
###############################################################################
# start portknocking
sub portknocking {
	my $ip = shift;
	my $port = shift;

	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","portknocking",$timer)}
		&logfile("*Port Knocking* port $port opened by $ip");

		$0 = "lfd - (child) sending Port Knocking alert email for $ip";

		open (IN, "</etc/csf/portknocking.txt");
		my @alert = <IN>;
		close (IN);
		chomp @alert;

		my $tip = &iplookup($ip);
		my @message;
		foreach my $line (@alert) {
			$line =~ s/\[ip\]/$tip/ig;
			$line =~ s/\[port\]/$port/ig;
			push @message, $line;
		}
		&sendmail(@message);

		if ($config{DEBUG} >= 3) {$timer = &timer("stop","portknocking",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end portknocking
###############################################################################
# start dshield
sub dshield {
	my $getlist = 0;
	if (-e "/etc/csf/csf.dshield") {
		my $mtime = (stat("/etc/csf/csf.dshield"))[9];
		my $listtime = (time - $mtime);
		if ($listtime >= $config{LF_DSHIELD}) {$getlist = 1}
	} else {$getlist = 1}

	if ($getlist) {
		unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
		unless (defined ($childpid = fork)) {
			&cleanup(__LINE__,"*Error* cannot fork: $!");
		} 
		unless ($childpid) {
			my $timer = time;
			if ($config{DEBUG} >= 3) {$timer = &timer("start","dshield",$timer)}
			$0 = "lfd - retrieving dshield blocklist";

			my $lockstr = "LF_DSHIELD";
			sysopen (THISLOCK, "/etc/csf/lock/$lockstr.lock", O_RDWR | O_CREAT) or &childcleanup("*Error* Unable to open /etc/csf/lock/$lockstr.lock");
			flock (THISLOCK, LOCK_EX | LOCK_NB) or &childcleanup("*Lock Error* [$lockstr] still active - section skipped");

			$0 = "lfd - retrieving dshield blocklist (waiting for list lock)";
			&listlock("lock");
			$0 = "lfd - retrieving dshield blocklist";

			my ($status, $text) = &urlget($config{LF_DSHIELD_URL});
			if ($status) {
				&logfile("DSHIELD: Unable to retrieve dshield block list - $text");
				exit;
			}

			if (&csflock) {&lockfail("LF_DSHIELD")}
			&logfile("DSHIELD - retrieved and blocking IP address ranges");
			my $drop = $config{DROP};
			if ($config{DROP_IP_LOGGING}) {$drop = "BLOCKDROP"}

			if ($config{SAFECHAINUPDATE}) {
				&syscommand(__LINE__,"$config{IPTABLES} -N NEWDSHIELD");
			} else {
				&syscommand(__LINE__,"$config{IPTABLES} -F DSHIELD");
			}
			sysopen (DSHIELD, "/etc/csf/csf.dshield", O_WRONLY | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot open out file: $!");
			flock (DSHIELD, LOCK_EX);
			seek (DSHIELD, 0, 0);
			truncate (DSHIELD, 0);
			foreach my $line (split (/\n/,$text)) {
				if ($line =~ /^\#/) {next}
				if ($line =~ /^([\d\.]+)\s+([\d\.]+)\s+(\d+)\s+/) {
					my $iprange = $1;
					my $dcidr = $3;
					if ($iprange and $dcidr) {
						print DSHIELD "$iprange/$dcidr\n";
						if ($config{SAFECHAINUPDATE}) {
							&syscommand(__LINE__,"$config{IPTABLES} -I NEWDSHIELD -s $iprange/$dcidr -j $drop");
						} else {
							&syscommand(__LINE__,"$config{IPTABLES} -I DSHIELD -s $iprange/$dcidr -j $drop");
						}
					}
				}
			}
			close (DSHIELD);
			if ($config{SAFECHAINUPDATE}) {
				&syscommand(__LINE__,"$config{IPTABLES} -A LOCALINPUT $ethdevin -j NEWDSHIELD");
				&syscommand(__LINE__,"$config{IPTABLES} -D LOCALINPUT $ethdevin -j DSHIELD");
				&syscommand(__LINE__,"$config{IPTABLES} -F DSHIELD");
				&syscommand(__LINE__,"$config{IPTABLES} -X DSHIELD");
				&syscommand(__LINE__,"$config{IPTABLES} -E NEWDSHIELD DSHIELD");
			}

			&listlock("unlock");
			close (THISLOCK);
			if ($config{DEBUG} >= 3) {$timer = &timer("stop","dshield",$timer)}
			$0 = "lfd - child closing";
			exit;
		}
	}
}
# end dshield
###############################################################################
# start spamhaus
sub spamhaus {
	my $getlist = 0;
	if (-e "/etc/csf/csf.spamhaus") {
		my $mtime = (stat("/etc/csf/csf.spamhaus"))[9];
		my $listtime = (time - $mtime);
		if ($listtime >= $config{LF_SPAMHAUS}) {$getlist = 1}
	} else {$getlist = 1}

	if ($getlist) {
		unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
		unless (defined ($childpid = fork)) {
			&cleanup(__LINE__,"*Error* cannot fork: $!");
		} 
		unless ($childpid) {
			my $timer = time;
			if ($config{DEBUG} >= 3) {$timer = &timer("start","spamhaus",$timer)}
			$0 = "lfd - retrieving spamhaus blocklist";

			my $lockstr = "LF_SPAMHAUS";
			sysopen (THISLOCK, "/etc/csf/lock/$lockstr.lock", O_RDWR | O_CREAT) or &childcleanup("*Error* Unable to open /etc/csf/lock/$lockstr.lock");
			flock (THISLOCK, LOCK_EX | LOCK_NB) or &childcleanup("*Lock Error* [$lockstr] still active - section skipped");

			$0 = "lfd - retrieving spamhaus blocklist (waiting for list lock)";
			&listlock("lock");
			$0 = "lfd - retrieving spamhaus blocklist";

			my ($status, $text) = &urlget($config{LF_SPAMHAUS_URL});
			if ($status) {
				&logfile("SPAMHAUS: Unable to retrieve spamhaus block list - $text");
				exit;
			}

			if ($config{LF_SPAMHAUS_EXTENDED}) {
				my ($status, $etext) = &urlget($config{LF_SPAMHAUS_EXTENDED_URL});
				if ($status) {
					&logfile("SPAMHAUS: Unable to retrieve spamhaus extended block list - $etext");
				} else {
					$text .= $etext;
					if ($config{LF_SPAMHAUS_EXTENDED}) {&logfile("SPAMHAUS - retrieved and blocking extended IP address ranges")}
				}
			}

			if (&csflock) {&lockfail("LF_SPAMHAUS")}
			&logfile("SPAMHAUS - retrieved and blocking IP address ranges");
			my $drop = $config{DROP};
			if ($config{DROP_IP_LOGGING}) {$drop = "BLOCKDROP"}

			if ($config{SAFECHAINUPDATE}) {
				&syscommand(__LINE__,"$config{IPTABLES} -N NEWSPAMHAUS");
			} else {
				&syscommand(__LINE__,"$config{IPTABLES} -F SPAMHAUS");
			}
			sysopen (SPAMHAUS, "/etc/csf/csf.spamhaus", O_WRONLY | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot open out file: $!");
			flock (SPAMHAUS, LOCK_EX);
			seek (SPAMHAUS, 0, 0);
			truncate (SPAMHAUS, 0);
			foreach my $line (split (/\n/,$text)) {
				if ($line =~ /^\#/) {next}
				if ($line =~ /^([\d\.\/]+)\s+/) {
					my $iprange = $1;
					if ($iprange) {
						print SPAMHAUS "$iprange\n";
						if ($config{SAFECHAINUPDATE}) {
							&syscommand(__LINE__,"$config{IPTABLES} -I NEWSPAMHAUS -s $iprange -j $drop");
						} else {
							&syscommand(__LINE__,"$config{IPTABLES} -I SPAMHAUS -s $iprange -j $drop");
						}
					}
				}
			}
			close (SPAMHAUS);
			if ($config{SAFECHAINUPDATE}) {
				&syscommand(__LINE__,"$config{IPTABLES} -A LOCALINPUT $ethdevin -j NEWSPAMHAUS");
				&syscommand(__LINE__,"$config{IPTABLES} -D LOCALINPUT $ethdevin -j SPAMHAUS");
				&syscommand(__LINE__,"$config{IPTABLES} -F SPAMHAUS");
				&syscommand(__LINE__,"$config{IPTABLES} -X SPAMHAUS");
				&syscommand(__LINE__,"$config{IPTABLES} -E NEWSPAMHAUS SPAMHAUS");
			}

			&listlock("unlock");
			close (THISLOCK);
			if ($config{DEBUG} >= 3) {$timer = &timer("stop","spamhaus",$timer)}
			$0 = "lfd - child closing";
			exit;
		}
	}
}
# end spamhaus
###############################################################################
# start tor
sub tor {
	my $getlist = 0;
	if (-e "/etc/csf/csf.tor") {
		my $mtime = (stat("/etc/csf/csf.tor"))[9];
		my $listtime = (time - $mtime);
		if ($listtime >= $config{LF_TOR}) {$getlist = 1}
	} else {$getlist = 1}

	if ($getlist) {
		unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
		unless (defined ($childpid = fork)) {
			&cleanup(__LINE__,"*Error* cannot fork: $!");
		} 
		unless ($childpid) {
			my $timer = time;
			if ($config{DEBUG} >= 3) {$timer = &timer("start","tor",$timer)}
			$0 = "lfd - retrieving tor blocklist";

			my $lockstr = "LF_TOR";
			sysopen (THISLOCK, "/etc/csf/lock/$lockstr.lock", O_RDWR | O_CREAT) or &childcleanup("*Error* Unable to open /etc/csf/lock/$lockstr.lock");
			flock (THISLOCK, LOCK_EX | LOCK_NB) or &childcleanup("*Lock Error* [$lockstr] still active - section skipped");

			$0 = "lfd - retrieving tor blocklist (waiting for list lock)";
			&listlock("lock");
			$0 = "lfd - retrieving tor blocklist";

			my ($status, $text) = &urlget($config{LF_TOR_URL});
			if ($status) {
				&logfile("TOR: Unable to retrieve tor block list - $text");
				exit;
			}

			if (&csflock) {&lockfail("LF_TOR")}
			&logfile("TOR - retrieved and blocking IP address ranges");
			my $drop = $config{DROP};
			if ($config{DROP_IP_LOGGING}) {$drop = "BLOCKDROP"}

			if ($config{SAFECHAINUPDATE}) {
				&syscommand(__LINE__,"$config{IPTABLES} -N NEWTOR");
			} else {
				&syscommand(__LINE__,"$config{IPTABLES} -F TOR");
			}
			sysopen (TOR, "/etc/csf/csf.tor", O_WRONLY | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot open out file: $!");
			flock (TOR, LOCK_EX);
			seek (TOR, 0, 0);
			truncate (TOR, 0);
			foreach my $line (split (/\n/,$text)) {
				if ($line =~ /^\#/) {next}
				if (($line =~ /^([\d\.\/]+)\s+/) or ($line =~ /^ExitAddress\s([\d\.\/]+)\s/)) {
					my $iprange = $1;
					if ($iprange) {
						print TOR "$iprange\n";
						if ($config{SAFECHAINUPDATE}) {
							&syscommand(__LINE__,"$config{IPTABLES} -I NEWTOR -s $iprange -j $drop");
						} else {
							&syscommand(__LINE__,"$config{IPTABLES} -I TOR -s $iprange -j $drop");
						}
					}
				}
			}
			close (TOR);
			if ($config{SAFECHAINUPDATE}) {
				&syscommand(__LINE__,"$config{IPTABLES} -A LOCALINPUT $ethdevin -j NEWTOR");
				&syscommand(__LINE__,"$config{IPTABLES} -D LOCALINPUT $ethdevin -j TOR");
				&syscommand(__LINE__,"$config{IPTABLES} -F TOR");
				&syscommand(__LINE__,"$config{IPTABLES} -X TOR");
				&syscommand(__LINE__,"$config{IPTABLES} -E NEWTOR TOR");
			}

			&listlock("unlock");
			close (THISLOCK);
			if ($config{DEBUG} >= 3) {$timer = &timer("stop","tor",$timer)}
			$0 = "lfd - child closing";
			exit;
		}
	}
}
# end tor
###############################################################################
# start bogon
sub bogon {
	my $getlist = 0;
	if (-e "/etc/csf/csf.bogon") {
		my $mtime = (stat("/etc/csf/csf.bogon"))[9];
		my $listtime = (time - $mtime);
		if ($listtime >= $config{LF_BOGON}) {$getlist = 1}
	} else {$getlist = 1}

	if ($getlist) {
		unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
		unless (defined ($childpid = fork)) {
			&cleanup(__LINE__,"*Error* cannot fork: $!");
		} 
		unless ($childpid) {
			my $timer = time;
			if ($config{DEBUG} >= 3) {$timer = &timer("start","bogon",$timer)}
			$0 = "lfd - retrieving bogon blocklist";

			my $lockstr = "LF_BOGON";
			sysopen (THISLOCK, "/etc/csf/lock/$lockstr.lock", O_RDWR | O_CREAT) or &childcleanup("*Error* Unable to open /etc/csf/lock/$lockstr.lock");
			flock (THISLOCK, LOCK_EX | LOCK_NB) or &childcleanup("*Lock Error* [$lockstr] still active - section skipped");

			$0 = "lfd - retrieving bogon blocklist (waiting for list lock)";
			&listlock("lock");
			$0 = "lfd - retrieving bogon blocklist";

			my ($status, $text) = &urlget($config{LF_BOGON_URL});
			if ($status) {
				&logfile("BOGON: Unable to retrieve bogon block list - $text");
				exit;
			}

			if (&csflock) {&lockfail("LF_BOGON")}
			&logfile("BOGON - retrieved and blocking IP address ranges");
			my $drop = $config{DROP};
			if ($config{DROP_IP_LOGGING}) {$drop = "BLOCKDROP"}

			if ($config{SAFECHAINUPDATE}) {
				&syscommand(__LINE__,"$config{IPTABLES} -N NEWBOGON");
			} else {
				&syscommand(__LINE__,"$config{IPTABLES} -F BOGON");
			}
			sysopen (BOGON, "/etc/csf/csf.bogon", O_WRONLY | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot open out file: $!");
			flock (BOGON, LOCK_EX);
			seek (BOGON, 0, 0);
			truncate (BOGON, 0);
			foreach my $line (split (/\n/,$text)) {
				if ($line =~ /^\#/) {next}
				if ($line =~ /^([\d\.\/]+)$/) {
					my $iprange = $1;
					if ($iprange) {
						print BOGON "$iprange\n";
						if ($config{SAFECHAINUPDATE}) {
							&syscommand(__LINE__,"$config{IPTABLES} -I NEWBOGON -s $iprange -j $drop");
						} else {
							&syscommand(__LINE__,"$config{IPTABLES} -I BOGON -s $iprange -j $drop");
						}
					}
				}
			}
			close (BOGON);
			$config{LF_BOGON_SKIP} =~ s/\s//g;
			if ($config{LF_BOGON_SKIP} ne "") {
				foreach my $device (split(/\,/,$config{LF_BOGON_SKIP})) {
					if ($config{SAFECHAINUPDATE}) {
						&syscommand(__LINE__,"$config{IPTABLES} -I NEWBOGON -i $device -j RETURN");
					} else {
						&syscommand(__LINE__,"$config{IPTABLES} -I BOGON -i $device -j RETURN");
					}
				}
			}
			if ($config{SAFECHAINUPDATE}) {
				&syscommand(__LINE__,"$config{IPTABLES} -A LOCALINPUT $ethdevin -j NEWBOGON");
				&syscommand(__LINE__,"$config{IPTABLES} -D LOCALINPUT $ethdevin -j BOGON");
				&syscommand(__LINE__,"$config{IPTABLES} -F BOGON");
				&syscommand(__LINE__,"$config{IPTABLES} -X BOGON");
				&syscommand(__LINE__,"$config{IPTABLES} -E NEWBOGON BOGON");
			}

			&listlock("unlock");
			close (THISLOCK);
			if ($config{DEBUG} >= 3) {$timer = &timer("stop","bogon",$timer)}
			$0 = "lfd - child closing";
			exit;
		}
	}
}
# end bogon
###############################################################################
# start countrycode
sub countrycode {
	my $force = shift;

	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","countrycode",$timer)}
		$0 = "lfd - retrieving countrycode lists";

		my $lockstr = "COUNTRYCODE";
		sysopen (THISLOCK, "/etc/csf/lock/$lockstr.lock", O_RDWR | O_CREAT) or &childcleanup("*Error* Unable to open /etc/csf/lock/$lockstr.lock");
		flock (THISLOCK, LOCK_EX | LOCK_NB) or &childcleanup("*Lock Error* [$lockstr] still active - section skipped");

		$0 = "lfd - retrieving countrycode lists (waiting for list lock)";
		&listlock("lock");
		$0 = "lfd - retrieving countrycode lists";

		my $drop = $config{DROP};
		if ($config{DROP_IP_LOGGING}) {$drop = "CCDROP"}

		my $redo_deny = 0;
		my $redo_allow = 0;
		my $redo_allow_filter = 0;
		my $redo_ignore = 0;
		$config{CC_DENY} =~ s/\s//g;
		$config{CC_ALLOW} =~ s/\s//g;
		$config{CC_ALLOW_FILTER} =~ s/\s//g;
		$config{CC_IGNORE} =~ s/\s//g;

		my $getgeo = 0;
		my %cclist;
		unless (-e "/etc/csf/zone/GeoIPCountryWhois.csv") {$getgeo = 1}
		if (-z "/etc/csf/zone/GeoIPCountryWhois.csv") {$getgeo = 1}
		if (-e "/etc/csf/zone/GeoIPCountryCSV.zip") {
			my $mtime = (stat("/etc/csf/zone/GeoIPCountryCSV.zip"))[9];
			my $days = int((time - $mtime) / 86400);
			if ($days >= $config{CC_INTERVAL}) {$getgeo = 1}
		} else {$getgeo = 1}
		if ($getgeo) {
			&logfile("CC: Retrieving GeoLite CSV Country database [http://geolite.maxmind.com/download/geoip/database/GeoIPCountryCSV.zip]");
			my ($status, $text) = &urlget("http://geolite.maxmind.com/download/geoip/database/GeoIPCountryCSV.zip","/etc/csf/zone/GeoIPCountryCSV.zip");
			if ($status) {
				&logfile("CC Error: Unable to retrieve GeoLite CSV Country database [http://geolite.maxmind.com/download/geoip/database/GeoIPCountryCSV.zip] - $text");
			} else {
				my @data;
				eval {
					local $SIG{__DIE__} = undef;
					local $SIG{'ALRM'} = sub {die};
					alarm(180);
					my ($childin, $childout);
					my $cmdpid = open3($childin, $childout, $childout, "$config{UNZIP} -ud /etc/csf/zone/ /etc/csf/zone/GeoIPCountryCSV.zip");
					@data = <$childout>;
					waitpid ($cmdpid, 0);
					alarm(0);
				};
				alarm(0);
				if ($@) {
					&logfile("CC Error: Unable to unzip GeoLite CSV Country database /etc/csf/zone/GeoIPCountryCSV.zip - timeout");
				}
				if (-z "/etc/csf/zone/GeoIPCountryWhois.csv" or !(-e "/etc/csf/zone/GeoIPCountryWhois.csv")) {
					&logfile("CC Error: GeoIPCountryWhois.csv empty or missing");
				}
				foreach my $cc (split(/\,/,"$config{CC_DENY},$config{CC_ALLOW},$config{CC_ALLOW_FILTER},$config{CC_IGNORE}")) {
					if ($cc) {
						$cc = lc $cc;
						$cclist{$cc} = 1;
					}
				}
			}
		}

		foreach my $cc (split(/\,/,"$config{CC_DENY},$config{CC_ALLOW},$config{CC_ALLOW_FILTER},$config{CC_IGNORE}")) {
			if ($cc) {
				$cc = lc $cc;
				if (-e "/etc/csf/zone/$cc.zone") {
					my $mtime = (stat("/etc/csf/zone/$cc.zone"))[9];
					my $days = int((time - $mtime) / 86400);
					if ($days >= $config{CC_INTERVAL}) {$getgeo = 1; $cclist{$cc} = 1}
				} else {$getgeo = 1;  $cclist{$cc} = 1}
				if (-z "/etc/csf/zone/$cc.zone") {$getgeo = 1;  $cclist{$cc} = 1}

				if ($cclist{$cc}) {
					if ($config{CC_DENY} =~ /\b$cc\b/i) {$redo_deny = 1}
					if ($config{CC_ALLOW} =~ /\b$cc\b/i) {$redo_allow = 1}
					if ($config{CC_ALLOW_FILTER} =~ /\b$cc\b/i) {$redo_allow_filter = 1}
					if ($config{CC_IGNORE} =~ /\b$cc\b/i) {$redo_ignore = 1}
				}
			}
		}

		if ($getgeo) {
			&logfile("CC: Processing GeoLite CSV Country database");
			my %dcidr;
			foreach my $cc (keys %cclist) {
				$dcidr{$cc} = Net::CIDR::Lite->new;
			}
			open (IN, "</etc/csf/zone/GeoIPCountryWhois.csv");
			while (my $record = <IN>) {
				chomp $record;
				$record =~ s/\"//g;
				my ($start,$end,undef,undef,$ccmatch,undef) = split (/\,/,$record);
				foreach my $cc (keys %cclist) {
					if (uc $cc eq $ccmatch) {
						$dcidr{$cc}->add_range("$start-$end");
					}
				}
			}
			close (IN);
			foreach my $cc (keys %cclist) {
				&logfile("CC: Extracting zone from GeoLite CSV Country database for [".uc($cc)."]");
				my @cidr_list = $dcidr{$cc}->list;
				if (@cidr_list eq 0) {
					&logfile("CC: No entries found for [".uc($cc)."] in /etc/csf/zone/GeoIPCountryWhois.csv");
				} else {
					open (CIDROUT, ">/etc/csf/zone/$cc.zone");
					foreach my $item (@cidr_list) {print CIDROUT "$item\n"}
					close (CIDROUT);
				}
			}
		}
		
		if ($force) {
			$redo_deny = 1;
			$redo_allow = 1;
			$redo_allow_filter = 1;
			$redo_ignore = 1;
		}

		if ($config{CC_DENY} and $redo_deny) {
			if (&csflock) {&lockfail("CC_DENY")}
			if ($config{SAFECHAINUPDATE}) {
				&syscommand(__LINE__,"$config{IPTABLES} -N NEWCC_DENY");
			} else {
				&syscommand(__LINE__,"$config{IPTABLES} -F CC_DENY");
			}
			if ($config{DEBUG} >= 1) {&logfile("debug: Repopulating CC_DENY")}
			foreach my $cc (split(/\,/,$config{CC_DENY})) {
				$cc = lc $cc;
				if (-e "/etc/csf/zone/$cc.zone") {
					open (IN, "</etc/csf/zone/$cc.zone");
					flock (IN, LOCK_SH);
					while (my $line = <IN>) {
						chomp $line;
						my ($ip,undef) = split (/\s/,$line,2);
						if (&checkip($ip)) {
							if ($config{CC_DROP_CIDR} > 0 and $config{CC_DROP_CIDR} < 33) {
								my ($drop_ip,$drop_cidr) = split(/\//,$ip);
								if ($drop_cidr eq "") {$drop_cidr = "32"}
								if ($drop_cidr > $config{CC_DROP_CIDR}) {next}
							}
							if ($config{SAFECHAINUPDATE}) {
								&syscommand(__LINE__,"$config{IPTABLES} -I NEWCC_DENY -s $ip -j $drop");
							} else {
								&syscommand(__LINE__,"$config{IPTABLES} -I CC_DENY -s $ip -j $drop");
							}
						}
					}
					close (IN);
				}
			}
			if ($config{SAFECHAINUPDATE}) {
				&syscommand(__LINE__,"$config{IPTABLES} -A LOCALINPUT $ethdevin -j NEWCC_DENY");
				&syscommand(__LINE__,"$config{IPTABLES} -D LOCALINPUT $ethdevin -j CC_DENY");
				&syscommand(__LINE__,"$config{IPTABLES} -F CC_DENY");
				&syscommand(__LINE__,"$config{IPTABLES} -X CC_DENY");
				&syscommand(__LINE__,"$config{IPTABLES} -E NEWCC_DENY CC_DENY");
			}
		}

		if ($config{CC_ALLOW} and $redo_allow) {
			if (&csflock) {&lockfail("CC_ALLOW")}
			if ($config{SAFECHAINUPDATE}) {
				&syscommand(__LINE__,"$config{IPTABLES} -N NEWCC_ALLOW");
			} else {
				&syscommand(__LINE__,"$config{IPTABLES} -F CC_ALLOW");
			}
			if ($config{DEBUG} >= 1) {&logfile("debug: Repopulating CC_ALLOW")}
			foreach my $cc (split(/\,/,$config{CC_ALLOW})) {
				$cc = lc $cc;
				if (-e "/etc/csf/zone/$cc.zone") {
					open (IN, "</etc/csf/zone/$cc.zone");
					flock (IN, LOCK_SH);
					while (my $line = <IN>) {
						chomp $line;
						my ($ip,undef) = split (/\s/,$line,2);
						if (&checkip($ip)) {
							if ($config{CC_DROP_CIDR} > 0 and $config{CC_DROP_CIDR} < 33) {
								my ($drop_ip,$drop_cidr) = split(/\//,$ip);
								if ($drop_cidr eq "") {$drop_cidr = "32"}
								if ($drop_cidr > $config{CC_DROP_CIDR}) {next}
							}
							if ($config{SAFECHAINUPDATE}) {
								&syscommand(__LINE__,"$config{IPTABLES} -I NEWCC_ALLOW -s $ip -j $accept");
							} else {
								&syscommand(__LINE__,"$config{IPTABLES} -I CC_ALLOW -s $ip -j $accept");
							}
						}
					}
					close (IN);
				}
			}
			if ($config{SAFECHAINUPDATE}) {
				&syscommand(__LINE__,"$config{IPTABLES} -I LOCALINPUT $ethdevin -j NEWCC_ALLOW");
				&syscommand(__LINE__,"$config{IPTABLES} -D LOCALINPUT $ethdevin -j CC_ALLOW");
				&syscommand(__LINE__,"$config{IPTABLES} -F CC_ALLOW");
				&syscommand(__LINE__,"$config{IPTABLES} -X CC_ALLOW");
				&syscommand(__LINE__,"$config{IPTABLES} -E NEWCC_ALLOW CC_ALLOW");
			}
		}

		if ($config{CC_ALLOW_FILTER} and $redo_allow_filter) {
			my $cnt = 0;
			if (&csflock) {&lockfail("CC_ALLOW_FILTER")}
			if ($config{SAFECHAINUPDATE}) {
				&syscommand(__LINE__,"$config{IPTABLES} -N NCC_ALLOWF");
			} else {
				&syscommand(__LINE__,"$config{IPTABLES} -F CC_ALLOWF");
			}
			if ($config{DEBUG} >= 1) {&logfile("debug: Repopulating CC_ALLOWF")}
			foreach my $cc (split(/\,/,$config{CC_ALLOW_FILTER})) {
				$cc = lc $cc;
				if (-e "/etc/csf/zone/$cc.zone") {
					open (IN, "</etc/csf/zone/$cc.zone");
					flock (IN, LOCK_SH);
					while (my $line = <IN>) {
						chomp $line;
						my ($ip,undef) = split (/\s/,$line,2);
						if (&checkip($ip)) {
							if ($config{CC_DROP_CIDR} > 0 and $config{CC_DROP_CIDR} < 33) {
								my ($drop_ip,$drop_cidr) = split(/\//,$ip);
								if ($drop_cidr eq "") {$drop_cidr = "32"}
								if ($drop_cidr > $config{CC_DROP_CIDR}) {next}
							}
							$cnt++;
							if ($config{SAFECHAINUPDATE}) {
								&syscommand(__LINE__,"$config{IPTABLES} -I NCC_ALLOWF -s $ip -j RETURN");
							} else {
								&syscommand(__LINE__,"$config{IPTABLES} -I CC_ALLOWF -s $ip -j RETURN");
							}
						}
					}
					close (IN);
				}
			}
			if ($config{SAFECHAINUPDATE}) {
				if ($cnt > 0) {&syscommand(__LINE__,"$config{IPTABLES} -A NCC_ALLOWF -j $drop")}
				&syscommand(__LINE__,"$config{IPTABLES} -A LOCALINPUT $ethdevin -j NCC_ALLOWF");
				&syscommand(__LINE__,"$config{IPTABLES} -D LOCALINPUT $ethdevin -j CC_ALLOWF");
				&syscommand(__LINE__,"$config{IPTABLES} -F CC_ALLOWF");
				&syscommand(__LINE__,"$config{IPTABLES} -X CC_ALLOWF");
				&syscommand(__LINE__,"$config{IPTABLES} -E NCC_ALLOWF CC_ALLOWF");
			} else {
				if ($cnt > 0) {&syscommand(__LINE__,"$config{IPTABLES} -A CC_ALLOWF -j $drop")}
			}
		}

		if ($config{CC_IGNORE} and $redo_ignore) {
			open (OUT, ">", "/etc/csf/csf.ccignore");
			close (OUT);
		}

		&listlock("unlock");
		close (THISLOCK);
		if ($config{DEBUG} >= 3) {$timer = &timer("stop","countrycode",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end countrycode
###############################################################################
# start countrycodelookups
sub countrycodelookups {
	my $force = shift;

	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","countrycodelookups",$timer)}
		$0 = "lfd - retrieving countrycode lookups";

		my $lockstr = "CC_LOOKUPS";
		sysopen (THISLOCK, "/etc/csf/lock/$lockstr.lock", O_RDWR | O_CREAT) or &childcleanup("*Error* Unable to open /etc/csf/lock/$lockstr.lock");
		flock (THISLOCK, LOCK_EX | LOCK_NB) or &childcleanup("*Lock Error* [$lockstr] still active - section skipped");

		my $getgeo = 0;
		my $geofile = "/etc/csf/Geo/GeoIP.dat";
		if ($config{CC_LOOKUPS} == 2) {$geofile = "/etc/csf/Geo/GeoLiteCity.dat"}
		if (-e $geofile) {
			if (-z $geofile) {$getgeo = 1}
			my $mtime = (stat($geofile))[9];
			my $days = int((time - $mtime) / 86400);
			if ($days >= $config{CC_INTERVAL}) {$getgeo = 1}
		} else {$getgeo = 1}
		if ($getgeo) {
			my $status;
			my $text;
			if ($config{CC_LOOKUPS} == 2) {
				&logfile("CCL: Retrieving GeoLite Country database [http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz]");
				($status, $text) = &urlget("http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz","/etc/csf/Geo/GeoLiteCity.dat.gz");
			} else {
				&logfile("CCL: Retrieving GeoLite Country database [http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz]");
				($status, $text) = &urlget("http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz","/etc/csf/Geo/GeoIP.dat.gz");
			}
			if ($status) {
				if ($config{CC_LOOKUPS} == 2) {
					&logfile("CCL Error: Unable to retrieve GeoLite Country database [http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz] - $text");
				} else {
					&logfile("CCL Error: Unable to retrieve GeoLite Country database [http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz] - $text");
				}
			} else {
				my @data;
				eval {
					local $SIG{__DIE__} = undef;
					local $SIG{'ALRM'} = sub {die};
					alarm(180);
					my ($childin, $childout, $cmdpid);
					if ($config{CC_LOOKUPS} == 2) {
						$cmdpid = open3($childin, $childout, $childout, "$config{GUNZIP} -f /etc/csf/Geo/GeoLiteCity.dat.gz");
					} else {
						$cmdpid = open3($childin, $childout, $childout, "$config{GUNZIP} -f /etc/csf/Geo/GeoIP.dat.gz");
					}
					@data = <$childout>;
					waitpid ($cmdpid, 0);
					alarm(0);
				};
				alarm(0);
				if ($@) {
					if ($config{CC_LOOKUPS} == 2) {
						&logfile("CCL Error: Unable to unzip GeoLite Country database /etc/csf/Geo/GeoLiteCity.dat.gz - timeout");
					} else {
						&logfile("CCL Error: Unable to unzip GeoLite Country database /etc/csf/Geo/GeoIP.dat.gz - timeout");
					}
				}
				if (!(-e $geofile) or -z $geofile) {
					&logfile("CCL Error: $geofile empty or missing");
				} else {
					my $now = time;
					utime ($now,$now,$geofile);
					&logfile("CCL: Retrieved GeoLite IP database");
					open (OUT, ">", "/etc/csf/csf.cclookup");
					close (OUT);
				}
			}
		}

		close (THISLOCK);
		if ($config{DEBUG} >= 3) {$timer = &timer("stop","countrycodelookups",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end countrycodelookups
###############################################################################
# start global
sub global {
	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","global",$timer)}
		$0 = "lfd - retrieving global lists";

		my $lockstr = "LF_GLOBAL";
		sysopen (THISLOCK, "/etc/csf/lock/$lockstr.lock", O_RDWR | O_CREAT) or &childcleanup("*Error* Unable to open /etc/csf/lock/$lockstr.lock");
		flock (THISLOCK, LOCK_EX | LOCK_NB) or &childcleanup("*Lock Error* [$lockstr] still active - section skipped");

		$0 = "lfd - retrieving global lists (waiting for list lock)";
		&listlock("lock");
		$0 = "lfd - retrieving global lists";

		if ($config{GLOBAL_ALLOW}) {
			my ($status, $text) = &urlget($config{GLOBAL_ALLOW});
			if ($status) {
				&logfile("Unable to retrieve global allow list - $text");
			} else {
				if (&csflock) {&lockfail("GLOBAL_ALLOW")}
				&logfile("Global Allow - retrieved and allowing IP address ranges");

				if ($config{SAFECHAINUPDATE}) {
					&syscommand(__LINE__,"$config{IPTABLES} -N NEWGALLOWIN");
					&syscommand(__LINE__,"$config{IPTABLES} -N NEWGALLOWOUT");
					if ($config{IPV6}) {
						&syscommand(__LINE__,"$config{IP6TABLES} -N NEWGALLOWIN");
						&syscommand(__LINE__,"$config{IP6TABLES} -N NEWGALLOWOUT");
					}
				} else {
					&syscommand(__LINE__,"$config{IPTABLES} -F GALLOWIN");
					&syscommand(__LINE__,"$config{IPTABLES} -F GALLOWOUT");
					if ($config{IPV6}) {
						&syscommand(__LINE__,"$config{IP6TABLES} -F GALLOWIN");
						&syscommand(__LINE__,"$config{IP6TABLES} -F GALLOWOUT");
					}
				}
				sysopen (GALLOW, "/etc/csf/csf.gallow", O_WRONLY | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot open out file: $!");
				flock (GALLOW, LOCK_EX);
				seek (GALLOW, 0, 0);
				truncate (GALLOW, 0);
				foreach my $line (split (/\n/,$text)) {
					if ($line =~ /^\#/) {next}
					my ($ip,$comment) = split (/\s/,$line,2);
					print GALLOW "$ip\n";
					if ($config{SAFECHAINUPDATE}) {
						&linefilter($ip, "allow","NEWGALLOW");
					} else {
						&linefilter($ip, "allow","GALLOW");
					}
				}
				close (GALLOW);
				if ($config{SAFECHAINUPDATE}) {
					&syscommand(__LINE__,"$config{IPTABLES} -I LOCALINPUT $ethdevin -j NEWGALLOWIN");
					&syscommand(__LINE__,"$config{IPTABLES} -I LOCALOUTPUT $ethdevout -j NEWGALLOWOUT");
					&syscommand(__LINE__,"$config{IPTABLES} -D LOCALINPUT $ethdevin -j GALLOWIN");
					&syscommand(__LINE__,"$config{IPTABLES} -D LOCALOUTPUT $ethdevout -j GALLOWOUT");
					&syscommand(__LINE__,"$config{IPTABLES} -F GALLOWIN");
					&syscommand(__LINE__,"$config{IPTABLES} -F GALLOWOUT");
					&syscommand(__LINE__,"$config{IPTABLES} -X GALLOWIN");
					&syscommand(__LINE__,"$config{IPTABLES} -X GALLOWOUT");
					&syscommand(__LINE__,"$config{IPTABLES} -E NEWGALLOWIN GALLOWIN");
					&syscommand(__LINE__,"$config{IPTABLES} -E NEWGALLOWOUT GALLOWOUT");
					if ($config{IPV6}) {
						&syscommand(__LINE__,"$config{IP6TABLES} -I LOCALINPUT $ethdevin -j NEWGALLOWIN");
						&syscommand(__LINE__,"$config{IP6TABLES} -I LOCALOUTPUT $ethdevout -j NEWGALLOWOUT");
						&syscommand(__LINE__,"$config{IP6TABLES} -D LOCALINPUT $ethdevin -j GALLOWIN");
						&syscommand(__LINE__,"$config{IP6TABLES} -D LOCALOUTPUT $ethdevout -j GALLOWOUT");
						&syscommand(__LINE__,"$config{IP6TABLES} -F GALLOWIN");
						&syscommand(__LINE__,"$config{IP6TABLES} -F GALLOWOUT");
						&syscommand(__LINE__,"$config{IP6TABLES} -X GALLOWIN");
						&syscommand(__LINE__,"$config{IP6TABLES} -X GALLOWOUT");
						&syscommand(__LINE__,"$config{IP6TABLES} -E NEWGALLOWIN GALLOWIN");
						&syscommand(__LINE__,"$config{IP6TABLES} -E NEWGALLOWOUT GALLOWOUT");
					}
				}
			}
		}

		if ($config{GLOBAL_DENY}) {
			my ($status, $text) = &urlget($config{GLOBAL_DENY});
			if ($status) {
				&logfile("Unable to retrieve global deny list - $text");
			} else {
				if (&csflock) {&lockfail("GLOBAL_DENY")}
				&logfile("Global Deny - retrieved and blocking IP address ranges");
				my $drop = $config{DROP};
				if ($config{DROP_IP_LOGGING}) {$drop = "BLOCKDROP"}

				if ($config{SAFECHAINUPDATE}) {
					&syscommand(__LINE__,"$config{IPTABLES} -N NEWGDENYIN");
					&syscommand(__LINE__,"$config{IPTABLES} -N NEWGDENYOUT");
					if ($config{IPV6}) {
						&syscommand(__LINE__,"$config{IP6TABLES} -N NEWGDENYIN");
						&syscommand(__LINE__,"$config{IP6TABLES} -N NEWGDENYOUT");
					}
				} else {
					&syscommand(__LINE__,"$config{IPTABLES} -F GDENYIN");
					&syscommand(__LINE__,"$config{IPTABLES} -F GDENYOUT");
					if ($config{IPV6}) {
						&syscommand(__LINE__,"$config{IP6TABLES} -F GDENYIN");
						&syscommand(__LINE__,"$config{IP6TABLES} -F GDENYOUT");
					}
				}
				sysopen (GDENY, "/etc/csf/csf.gdeny", O_WRONLY | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot open out file: $!");
				flock (GDENY, LOCK_EX);
				seek (GDENY, 0, 0);
				truncate (GDENY, 0);
				foreach my $line (split (/\n/,$text)) {
					if ($line =~ /^\#/) {next}
					my ($ip,$comment) = split (/\s/,$line,2);
					print GDENY "$ip\n";
					if ($config{SAFECHAINUPDATE}) {
						&linefilter($ip, "deny","NEWGDENY");
					} else {
						&linefilter($ip, "deny","GDENY");
					}
				}
				close (GDENY);
				if ($config{SAFECHAINUPDATE}) {
					&syscommand(__LINE__,"$config{IPTABLES} -A LOCALINPUT $ethdevin -j NEWGDENYIN");
					&syscommand(__LINE__,"$config{IPTABLES} -A LOCALOUTPUT $ethdevout -j NEWGDENYOUT");
					&syscommand(__LINE__,"$config{IPTABLES} -D LOCALINPUT $ethdevin -j GDENYIN");
					&syscommand(__LINE__,"$config{IPTABLES} -D LOCALOUTPUT $ethdevout -j GDENYOUT");
					&syscommand(__LINE__,"$config{IPTABLES} -F GDENYIN");
					&syscommand(__LINE__,"$config{IPTABLES} -F GDENYOUT");
					&syscommand(__LINE__,"$config{IPTABLES} -X GDENYIN");
					&syscommand(__LINE__,"$config{IPTABLES} -X GDENYOUT");
					&syscommand(__LINE__,"$config{IPTABLES} -E NEWGDENYIN GDENYIN");
					&syscommand(__LINE__,"$config{IPTABLES} -E NEWGDENYOUT GDENYOUT");
					if ($config{IPV6}) {
						&syscommand(__LINE__,"$config{IP6TABLES} -A LOCALINPUT $ethdevin -j NEWGDENYIN");
						&syscommand(__LINE__,"$config{IP6TABLES} -A LOCALOUTPUT $ethdevout -j NEWGDENYOUT");
						&syscommand(__LINE__,"$config{IP6TABLES} -D LOCALINPUT $ethdevin -j GDENYIN");
						&syscommand(__LINE__,"$config{IP6TABLES} -D LOCALOUTPUT $ethdevout -j GDENYOUT");
						&syscommand(__LINE__,"$config{IP6TABLES} -F GDENYIN");
						&syscommand(__LINE__,"$config{IP6TABLES} -F GDENYOUT");
						&syscommand(__LINE__,"$config{IP6TABLES} -X GDENYIN");
						&syscommand(__LINE__,"$config{IP6TABLES} -X GDENYOUT");
						&syscommand(__LINE__,"$config{IP6TABLES} -E NEWGDENYIN GDENYIN");
						&syscommand(__LINE__,"$config{IP6TABLES} -E NEWGDENYOUT GDENYOUT");
					}
				}
			}
		}

		if ($config{GLOBAL_IGNORE}) {
			my ($status, $text) = &urlget($config{GLOBAL_IGNORE});
			if ($status) {
				&logfile("Unable to retrieve global ignore list - $text");
			} else {
				&logfile("Global Ignore - retrieved and ignoring");

				sysopen (GIGNORE, "/etc/csf/csf.gignore", O_WRONLY | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot open out file: $!");
				flock (GIGNORE, LOCK_EX);
				seek (GIGNORE, 0, 0);
				truncate (GIGNORE, 0);
				foreach my $line (split (/\n/,$text)) {
					if ($line =~ /^\#/) {next}
					my ($ip,$comment) = split (/\s/,$line,2);
					print GIGNORE "$ip\n";
				}
				close (GIGNORE);
			}
		}

		if ($config{GLOBAL_DYNDNS}) {
			my ($status, $text) = &urlget($config{GLOBAL_DYNDNS});
			if ($status) {
				&logfile("Unable to retrieve global dyndns list - $text");
			} else {
				&logfile("Global DynDNS - retrieved and allowing IP addresses");

				sysopen (GDYNDNS, "/etc/csf/csf.gdyndns", O_WRONLY | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot open out file: $!");
				flock (GDYNDNS, LOCK_EX);
				seek (GDYNDNS, 0, 0);
				truncate (GDYNDNS, 0);
				foreach my $line (split (/\n/,$text)) {
					if ($line =~ /^\#/) {next}
					my ($ip,$comment) = split (/\s/,$line,2);
					print GDYNDNS "$ip\n";
				}
				close (GDYNDNS);
				&globaldyndns;
			}
		}

		&listlock("unlock");
		close (THISLOCK);
		if ($config{DEBUG} >= 3) {$timer = &timer("stop","global",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end global
###############################################################################
# start dyndns
sub dyndns {
	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","dyndns",$timer)}
		$0 = "lfd - resolving dyndns IP addresses";

		my $lockstr = "DYNDNS";
		sysopen (THISLOCK, "/etc/csf/lock/$lockstr.lock", O_RDWR | O_CREAT) or &childcleanup("*Error* Unable to open /etc/csf/lock/$lockstr.lock");
		flock (THISLOCK, LOCK_EX | LOCK_NB) or &childcleanup("*Lock Error* [$lockstr] still active - section skipped");

		$0 = "lfd - resolving dyndns IP addresses (waiting for list lock)";
		&listlock("lock");
		$0 = "lfd - resolving dyndns IP addresses";

		open (IN, "</etc/csf/csf.dyndns");
		my @dyndns = <IN>;
		close (IN);
		chomp @dyndns;

		if (&csflock) {&lockfail("DYNDNS")}
		&logfile("DynDNS - update IP addresses");
		if ($config{SAFECHAINUPDATE}) {
			&syscommand(__LINE__,"$config{IPTABLES} -N NEWALLOWDYNIN");
			&syscommand(__LINE__,"$config{IPTABLES} -N NEWALLOWDYNOUT");
		} else {
			&syscommand(__LINE__,"$config{IPTABLES} -F ALLOWDYNIN");
			&syscommand(__LINE__,"$config{IPTABLES} -F ALLOWDYNOUT");
		}
		sysopen (TEMPDYN, "/etc/csf/csf.tempdyn", O_WRONLY | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot open out file: $!");
		flock (TEMPDYN, LOCK_EX);
		seek (TEMPDYN, 0, 0);
		truncate (TEMPDYN, 0);
		foreach my $line (@dyndns) {
			if ($line =~ /^\#/) {next}
			if ($line eq "") {next}
			if ($line =~ /^\n/) {next}
			if ($line =~ /^\r/) {next}
			if ($line =~ /^\s/) {next}
			my $ip;
			my $adport;
			my ($fqdn,undef) = split(/\s/,$line,2);
			if ($fqdn =~ /^(.*(s|d)=)(.*)$/) {$adport = $1; $fqdn = $3}
			my $error = "";
			eval {
				local $SIG{__DIE__} = undef;
				local $SIG{'ALRM'} = sub {die};
				alarm(10);
				$ip = gethostbyname($fqdn);
				if ($?) {$error = "Unable to resolve"}
				$ip = inet_ntoa($ip);
				alarm(0);
			};
			alarm(0);
			if ($@ and !$error) {$error = "Lookup timeout"}
			if ($ip) {
				if ($adport) {$ip = $adport.$ip}
				if ($config{SAFECHAINUPDATE}) {
					&linefilter($ip, "allow","NEWALLOWDYN");
				} else {
					&linefilter($ip, "allow","ALLOWDYN");
				}
				print TEMPDYN "$ip\n";
			}
			elsif ($error) {
				&logfile ("DynDNS: Lookup for [$fqdn] failed - $error");
			}
		}
		close (TEMPDYN);
		if ($config{SAFECHAINUPDATE}) {
			&syscommand(__LINE__,"$config{IPTABLES} -I LOCALINPUT $ethdevin -j NEWALLOWDYNIN");
			&syscommand(__LINE__,"$config{IPTABLES} -I LOCALOUTPUT $ethdevout -j NEWALLOWDYNOUT");
			&syscommand(__LINE__,"$config{IPTABLES} -D LOCALINPUT $ethdevin -j ALLOWDYNIN");
			&syscommand(__LINE__,"$config{IPTABLES} -D LOCALOUTPUT $ethdevout -j ALLOWDYNOUT");
			&syscommand(__LINE__,"$config{IPTABLES} -F ALLOWDYNIN");
			&syscommand(__LINE__,"$config{IPTABLES} -F ALLOWDYNOUT");
			&syscommand(__LINE__,"$config{IPTABLES} -X ALLOWDYNIN");
			&syscommand(__LINE__,"$config{IPTABLES} -X ALLOWDYNOUT");
			&syscommand(__LINE__,"$config{IPTABLES} -E NEWALLOWDYNIN ALLOWDYNIN");
			&syscommand(__LINE__,"$config{IPTABLES} -E NEWALLOWDYNOUT ALLOWDYNOUT");
		}

		&listlock("unlock");
		close (THISLOCK);
		if ($config{DEBUG} >= 3) {$timer = &timer("stop","dyndns",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end dyndns
###############################################################################
# start globaldyndns
sub globaldyndns {
	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","globaldyndns",$timer)}
		$0 = "lfd - resolving global dyndns IP addresses";

		my $lockstr = "GLOBAL_DYNDNS_INTERVAL";
		sysopen (THISLOCK, "/etc/csf/lock/$lockstr.lock", O_RDWR | O_CREAT) or &childcleanup("*Error* Unable to open /etc/csf/lock/$lockstr.lock");
		flock (THISLOCK, LOCK_EX | LOCK_NB) or &childcleanup("*Lock Error* [$lockstr] still active - section skipped");

		$0 = "lfd - resolving global dyndns IP addresses (waiting for list lock)";
		&listlock("lock");
		$0 = "lfd - resolving global dyndns IP addresses";

		open (IN, "</etc/csf/csf.gdyndns");
		my @dyndns = <IN>;
		close (IN);
		chomp @dyndns;

		if (&csflock) {&lockfail("GLOBAL_DYNDNS_INTERVAL")}
		&logfile("Global DynDNS - update IP addresses");
		if ($config{SAFECHAINUPDATE}) {
			&syscommand(__LINE__,"$config{IPTABLES} -N NEWGDYNIN");
			&syscommand(__LINE__,"$config{IPTABLES} -N NEWGDYNOUT");
		} else {
			&syscommand(__LINE__,"$config{IPTABLES} -F GDYNIN");
			&syscommand(__LINE__,"$config{IPTABLES} -F GDYNOUT");
		}
		sysopen (TEMPDYN, "/etc/csf/csf.tempgdyn", O_WRONLY | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot open out file: $!");
		flock (TEMPDYN, LOCK_EX);
		seek (TEMPDYN, 0, 0);
		truncate (TEMPDYN, 0);
		foreach my $line (@dyndns) {
			if ($line =~ /^\#/) {next}
			if ($line eq "") {next}
			if ($line =~ /^\n/) {next}
			if ($line =~ /^\r/) {next}
			if ($line =~ /^\s/) {next}
			my $ip;
			my $adport;
			my ($fqdn,undef) = split(/\s/,$line,2);
			if ($fqdn =~ /(.*:(s|d)=)(.*)$/) {$adport = $1; $fqdn = $3}
			my $error = "";
			eval {
				local $SIG{__DIE__} = undef;
				local $SIG{'ALRM'} = sub {die};
				alarm(10);
				$ip = gethostbyname($fqdn);
				if ($?) {$error = "Unable to resolve"}
				$ip = inet_ntoa($ip);
				alarm(0);
			};
			alarm(0);
			if ($@ and !$error) {$error = "Lookup timeout"}
			if ($ip) {
				if ($adport) {$ip = $adport.$ip}
				if ($config{SAFECHAINUPDATE}) {
					&linefilter($ip, "allow","NEWGDYN");
				} else {
					&linefilter($ip, "allow","GDYN");
				}
				print TEMPDYN "$ip\n";
			}
			elsif ($error) {
				&logfile ("Global DynDNS: Lookup for [$fqdn] failed - $error");
			}
		}
		close (TEMPDYN);
		if ($config{SAFECHAINUPDATE}) {
			&syscommand(__LINE__,"$config{IPTABLES} -I LOCALINPUT $ethdevin -j NEWGDYNIN");
			&syscommand(__LINE__,"$config{IPTABLES} -I LOCALOUTPUT $ethdevout -j NEWGDYNOUT");
			&syscommand(__LINE__,"$config{IPTABLES} -D LOCALINPUT $ethdevin -j GDYNIN");
			&syscommand(__LINE__,"$config{IPTABLES} -D LOCALOUTPUT $ethdevout -j GDYNOUT");
			&syscommand(__LINE__,"$config{IPTABLES} -F GDYNIN");
			&syscommand(__LINE__,"$config{IPTABLES} -F GDYNOUT");
			&syscommand(__LINE__,"$config{IPTABLES} -X GDYNIN");
			&syscommand(__LINE__,"$config{IPTABLES} -X GDYNOUT");
			&syscommand(__LINE__,"$config{IPTABLES} -E NEWGDYNIN GDYNIN");
			&syscommand(__LINE__,"$config{IPTABLES} -E NEWGDYNOUT GDYNOUT");
		}

		&listlock("unlock");
		close (THISLOCK);
		if ($config{DEBUG} >= 3) {$timer = &timer("stop","globaldyndns",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end globaldyndns
###############################################################################
# start listlock
sub listlock {
	my $state = shift;
	if ($state eq "lock") {
		sysopen (LISTLOCK, "/etc/csf/lock/list.lock", O_RDWR | O_CREAT) or &childcleanup("*Error* Unable to open /etc/csf/lock/list.lock");
		flock (LISTLOCK, LOCK_EX) or &childcleanup("*Lock Error* [listlock] unable to lock");
	} else {
		close (LISTLOCK);
	}
}
# end listlock
###############################################################################
# start dirwatch
sub dirwatch {
	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","dirwatch",$timer)}
		$0 = "lfd - checking directories";

		my $lockstr = "LF_DIRWATCH";
		sysopen (THISLOCK, "/etc/csf/lock/$lockstr.lock", O_RDWR | O_CREAT) or &childcleanup("*Error* Unable to open /etc/csf/lock/$lockstr.lock");
		flock (THISLOCK, LOCK_EX | LOCK_NB) or &childcleanup("*Lock Error* [$lockstr] still active - section skipped");

		my $alarm = int($config{LF_DIRWATCH}/10) + 10;
		my $start = time;
		my $tfail = 0;
		undef %nofiles;
		if (! -z "/etc/csf/csf.tempfiles") {
			open (IN, "</etc/csf/csf.tempfiles");
			flock (IN, LOCK_SH);
			my @data = <IN>;
			close (IN);
			chomp @data;

			foreach my $line (@data) {
				my ($itemttl,$item) = split(/:/,$line);
				if (time - $itemttl < $config{LF_FLUSH}) {
					$nofiles{$item} = 1;
				}
			}
		}

		undef @suspicious;
		my @dirs = ('/tmp','/dev/shm','/usr/local/apache/proxy','/etc/cron.d','/etc/cron.daily','/etc/cron.hourly','/etc/cron.weekly');
		my $tmpino = (stat("/tmp"))[1];
		my $ino = (lstat("/var/tmp"))[1];
		if ($ino ne $tmpino) {push @dirs, '/var/tmp'}
		$ino = (lstat("/usr/tmp"))[1];
		if ($ino ne $tmpino) {push @dirs, '/usr/tmp'}
		eval {
			local $SIG{__DIE__} = undef;
			local $SIG{'ALRM'} = sub {die};
			alarm($alarm);
			find(\&dirfiles, @dirs);
			alarm(0);
		};
		alarm(0);
		if ($@) {
			&logfile("Directory Watching terminated after $alarm seconds");
			$tfail = 1;
		} else {
			if (@suspicious) {
				$0 = "lfd - reporting directory watch results";
				open (IN, "</etc/csf/filealert.txt");
				my @alert = <IN>;
				close (IN);
				chomp @alert;

				my $matches = 0;

				foreach my $file (@suspicious) {
					if ($nofiles{$file}) {next}
					unless (-e $file) {next}
					$nofiles{$file} = 1;

					my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat($file);
					if (-l $file) {($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = lstat($file)}

					if ($file !~/\/core\./) {
						if ($uid eq "0") {next}
					}

					my $tuid = getpwuid($uid);
					my $tgid = getgrgid($gid);
					if ($file !~ /\/core\./) {
						if (($uid eq "postgres") and ($gid eq "postgres")) {next}
						if ($skipuser{$uid}) {next}
					}

					$matches++;
					if ($matches > 10) {
						&logfile("Too many hits for *LF_DIRWATCH* - Directory Watching disabled");
						sysopen (DWDISABLE, "/etc/csf/csf.dwdisable", O_WRONLY | O_APPEND | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot append out file: $!");
						flock (DWDISABLE, LOCK_EX);
						print DWDISABLE "disabled\n";
						close (DWDISABLE);

						my @newalert = @alert;
						my @message;
						foreach my $line (@newalert) {
							$line =~ s/\[file\]//ig;
							$line =~ s/\[reason\]//ig;
							$line =~ s/\[owner\]//ig;
							$line =~ s/\[action\]/Too many hits for \*LF_DIRWATCH\* - Directory Watching disabled/ig;
							push @message, $line;
						}
						&sendmail(@message);
						
						exit;
					}

					my $owner = "$tuid:$tgid ($uid:$gid)";
					my $line = "*Suspicious File* $file [$owner] - $sfile{$file}{reason}";
					my $action = "No action taken";

					if ($config{LF_DIRWATCH_DISABLE}) {
						if (-l $file) {
							unlink ($file);
							$action = "Symlink removed";
							$line .= " - symlink removed";
						} else {
							system("$config{TAR}","-rf","/etc/csf/suspicious.tar","$file");
							$) = $gid;
							$> = $uid;
							if (($) == $gid) and ($> == $uid)) {rmtree("$file", 0, 0)}
							$) = 0;
							$> = 0;
							$line .= " - removed";
							$action = "Moved into /etc/csf/suspicious.tar";
						}
						delete $nofiles{$file};
					}
					&logfile($line);
					$0 = "lfd - (child) suspicious file alert for $file";

					my @newalert = @alert;
					my @message;
					foreach my $line (@newalert) {
						$line =~ s/\[file\]/$file/ig;
						$line =~ s/\[reason\]/$sfile{$file}{reason}/ig;
						$line =~ s/\[owner\]/$owner/ig;
						$line =~ s/\[action\]/$action/ig;
						push @message, $line;
					}
					&sendmail(@message);

					if (! $config{LF_DIRWATCH_DISABLE}) {
						sysopen (TEMPFILES, "/etc/csf/csf.tempfiles", O_WRONLY | O_APPEND | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot append out file: $!");
						flock (TEMPFILES, LOCK_EX);
						print TEMPFILES time.":$file\n";
						close (TEMPFILES);
					}
				}
			}
		}

		if ($tfail) {
			$config{LF_DIRWATCH} = $config{LF_DIRWATCH} * 3;
			sysopen (TEMPCONF, "/etc/csf/csf.tempconf", O_WRONLY | O_APPEND | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot append out file: $!");
			flock (TEMPCONF, LOCK_EX);
			print TEMPCONF "LF_DIRWATCH = \"$config{LF_DIRWATCH}\"\n";
			close (TEMPCONF);
			&logfile("LF_DIRWATCH taking $alarm seconds, temporarily throttled to run every $config{LF_DIRWATCH} seconds");
		}

		close (THISLOCK);
		if ($config{DEBUG} >= 3) {$timer = &timer("stop","dirwatch",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end dirwatch
###############################################################################
# start dirfiles
sub dirfiles {
	if ($skipfile{$File::Find::name}) {return}
	if ($nofiles{$File::Find::name}) {return}
	if ((-d $File::Find::name) and ($_ =~ /^(\.|\.\.)$/)) {return}

	my $skip = 0;
	foreach my $match (@matchfile) {
		if ($File::Find::name =~ /$match/) {
			$skip = 1;
			last;
		}
	}
	if ($skip) {return}

	if (-l $File::Find::name) {
		push @suspicious, $File::Find::name;
		$sfile{$File::Find::name}{reason} = "Suspicious symlink (->".readlink($File::Find::name).")";
		return;
	}
	elsif ((-d $File::Find::name) and ($_ =~ /^(\.|\s)/)) {
		push @suspicious, $File::Find::name;
		$sfile{$File::Find::name}{reason} = "Suspicious directory";
		return;
	}
	elsif (-f $File::Find::name) {
		if ($File::Find::name =~ /^\/etc\/cron/) {
			if ($_ =~ /^core\./) {
				push @suspicious, $File::Find::name;
				$sfile{$File::Find::name}{reason} = "Core dump found - possible root exploit attack";
				return;
			} else {return}
		}
		if ($File::Find::name =~ /[\;\|\`\\]/) {
			push @suspicious, $File::Find::name;
			$sfile{$File::Find::name}{reason} = "Suspicious file name";
			return;
		}
		if ($File::Find::name =~ /^\-/) {
			push @suspicious, $File::Find::name;
			$sfile{$File::Find::name}{reason} = "Suspicious file name";
			return;
		}
		if ($_ =~ /\.(pl|cgi|ph.*|py|sh|bash)$/) {
			push @suspicious, $File::Find::name;
			$sfile{$File::Find::name}{reason} = "Script, file extension";
			return;
		}
		if ($_ =~ /^(udp\.pl|r0nin|dc\.pl|bind|bindz|inetd|z|httpd|sshd|ssh|cron|crond|su)$/) {
			push @suspicious, $File::Find::name;
			$sfile{$File::Find::name}{reason} = "Known exploit";
			return;
		}

		open (FILETYPE, "<", $File::Find::name);
		read (FILETYPE, my $filedata, 1024);
		close (FILETYPE);
		if ($filedata =~ m[^\177ELF]) {
			push @suspicious, $File::Find::name;
			$sfile{$File::Find::name}{reason} = "Linux Binary";
			return;
		}
		if ($filedata =~ m[^\#\!]) {
			push @suspicious, $File::Find::name;
			$sfile{$File::Find::name}{reason} = "Script, starts with \#\!";
			return;
		}
	}
}
# end dirfiles
###############################################################################
# start dirwatchfile
sub dirwatchfile {
	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","dirwatchfile",$timer)}

		my $lockstr = "LF_DIRWATCH_FILE";
		sysopen (THISLOCK, "/etc/csf/lock/$lockstr.lock", O_RDWR | O_CREAT) or &childcleanup("*Error* Unable to open /etc/csf/lock/$lockstr.lock");
		flock (THISLOCK, LOCK_EX | LOCK_NB) or &childcleanup("*Lock Error* [$lockstr] still active - section skipped");

		$0 = "lfd - checking files and directories";

		undef %nofiles;
		if (-e "/etc/csf/csf.tempwatch") {
			open (IN, "</etc/csf/csf.tempwatch");
			flock (IN, LOCK_EX);
			my @data = <IN>;
			close (IN);
			chomp @data;

			foreach my $line (@data) {
				my ($file,$md5sum) = split(/:/,$line);
				if ($dirwatchfile{$file}) {$dirwatchfile{$file} = $md5sum}
			}
		}

		open (IN, "</etc/csf/watchalert.txt");
		my @alert = <IN>;
		close (IN);
		chomp @alert;
		foreach my $file (keys %dirwatchfile) {
			unless (-e $file) {
				&logfile("Directory *File Watching* [$file] does not exist");
				next;
			}
			my @data;
			eval {
				local $SIG{__DIE__} = undef;
				local $SIG{'ALRM'} = sub {die};
				alarm(10);
				my ($childin, $childout);
				my $cmdpid = open3($childin, $childout, $childout, "$config{LS} --full-time -lARt $file");
				@data = <$childout>;
				waitpid ($cmdpid, 0);
				alarm(0);
			};
			alarm(0);
			if ($@) {
				&logfile("Directory File Watching terminated after 10 seconds for $file: ".$@);
				exit;
			}
			chomp @data;
			my $md5current = Digest::MD5->new;
			my $output;
			foreach my $line (@data) {
				$md5current->add($line);
				$output .= $line."\n";
			}
			my $md5sum = $md5current->b64digest;

			if ($dirwatchfile{$file} ne "1") {
				if ($md5sum ne $dirwatchfile{$file}) {
					$0 = "lfd - (child) suspicious file alert for $file";
					&logfile("Directory *File Watching* has detected a change in $file");
					$dirwatchfile{$file} = $md5sum;

					my @newalert = @alert;
					my @message;
					foreach my $line (@newalert) {
						$line =~ s/\[file\]/$file/ig;
						$line =~ s/\[output\]/$output/ig;
						push @message, $line;
					}
					&sendmail(@message);
				}
			} else {
				$dirwatchfile{$file} = $md5sum;
			}
		}
		
		sysopen (TEMPWATCH, "/etc/csf/csf.tempwatch", O_WRONLY | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot write out file: $!");
		flock (TEMPWATCH, LOCK_EX);
		seek (TEMPWATCH, 0, 0);
		truncate (TEMPWATCH, 0);
		foreach my $file (keys %dirwatchfile) {
			print TEMPWATCH "$file:$dirwatchfile{$file}\n";
		}
		close (TEMPWATCH);

		close (THISLOCK);
		if ($config{DEBUG} >= 3) {$timer = &timer("stop","dirwatchfile",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end dirwatchfile
###############################################################################
# start integrity
sub integrity {
	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","integrity",$timer)}

		my $lockstr = "LF_INTEGRITY";
		sysopen (THISLOCK, "/etc/csf/lock/$lockstr.lock", O_RDWR | O_CREAT) or &childcleanup("*Error* Unable to open /etc/csf/lock/$lockstr.lock");
		flock (THISLOCK, LOCK_EX | LOCK_NB) or &childcleanup("*Lock Error* [$lockstr] still active - section skipped");

		$0 = "lfd - checking system integrity";

		my $integrity = '/usr/bin/* /usr/sbin/* /bin/* /sbin/* /usr/local/bin/* /usr/local/sbin/* /etc/init.d/* /etc/xinetd.d/* /etc/rc.local';
		my $alarm = int($config{LF_INTEGRITY}/10) + 10;
		my $start = time;
		my $tfail = 0;

		my $action;
		if (-z "/etc/csf/csf.tempint") {$action = "start"}
		unless (-e "/etc/csf/csf.tempint") {$action = "start"}
		if ($action eq "start") {
			eval {
				local $SIG{__DIE__} = undef;
				local $SIG{'ALRM'} = sub {die};
				alarm($alarm);
				my ($childin, $childout);
				my $cmdpid = open3($childin, $childout, $childout, "$config{MD5SUM} $integrity > /etc/csf/csf.tempint");
				waitpid ($cmdpid, 0);
				alarm(0);
			};
			alarm(0);
			if ($@) {
				&logfile("System Integrity start terminated after $alarm seconds");
				$tfail = 1;
			}
		} else {
			my @data;
			eval {
				local $SIG{__DIE__} = undef;
				local $SIG{'ALRM'} = sub {die};
				alarm($alarm);
				my ($childin, $childout);
				my $cmdpid = open3($childin, $childout, $childout, "$config{MD5SUM} --check /etc/csf/csf.tempint");
				@data = <$childout>;
				waitpid ($cmdpid, 0);
				alarm(0);
			};
			alarm(0);
			if ($@) {
				&logfile("System Integrity check terminated after $alarm seconds");
				$tfail = 1;
			} else {
				chomp @data;
				my $report;
				my $files;
				foreach my $line (@data) {
					my ($file,$text) = split(/:/,$line);
					if ($text =~ /FAILED/) {
						$report .= "$line\n";
						$files .= " $file";
					}
				}

				if ($report) {
					&logfile("*System Integrity* has detected modified file(s):$files");
					$0 = "lfd - (child) system integrity alert";

					open (IN, "</etc/csf/integrityalert.txt");
					my @alert = <IN>;
					close (IN);
					chomp @alert;

					my @message;
					foreach my $line (@alert) {
						$line =~ s/\r//;
						$line =~ s/\[text\]/$report/ig;
						push @message, $line;
					}
					&sendmail(@message);
					unlink "/etc/csf/csf.tempint";

					eval {
						local $SIG{__DIE__} = undef;
						local $SIG{'ALRM'} = sub {die};
						alarm($alarm);
						my ($childin, $childout);
						my $cmdpid = open3($childin, $childout, $childout, "$config{MD5SUM} $integrity > /etc/csf/csf.tempint");
						waitpid ($cmdpid, 0);
						alarm(0);
					};
					alarm(0);
					if ($@) {
						&logfile("System Integrity start terminated after $alarm seconds");
						$tfail = 1;
					}
				}
			}
		}

		if ($tfail) {
			$config{LF_INTEGRITY} = $config{LF_INTEGRITY} * 1.5;
			sysopen (TEMPCONF, "/etc/csf/csf.tempconf", O_WRONLY | O_APPEND | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot append out file: $!");
			flock (TEMPCONF, LOCK_EX);
			print TEMPCONF "LF_INTEGRITY = \"$config{LF_INTEGRITY}\"\n";
			close (TEMPCONF);
			&logfile("LF_INTEGRITY taking $alarm seconds, temporarily throttled to run every $config{LF_INTEGRITY} seconds");
		}

		close (THISLOCK);
		if ($config{DEBUG} >= 3) {$timer = &timer("stop","integrity",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end integrity
###############################################################################
# start logscanner
sub logscanner {
	my $hour = shift;
	if (length $hour == 1) {$hour = "0$hour:00"} else {$hour = "$hour:00"}
	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","logscanner",$timer)}

		my $lockstr = "LOGSCANNER";
		sysopen (THISLOCK, "/etc/csf/lock/$lockstr.lock", O_RDWR | O_CREAT) or &childcleanup("*Error* Unable to open /etc/csf/lock/$lockstr.lock");
		flock (THISLOCK, LOCK_EX | LOCK_NB) or &childcleanup("*Lock Error* [$lockstr] still active - section skipped");

		$0 = "lfd - log scanner";

		unless (-z "/etc/csf/csf.logtemp" and $config{LOGSCANNER_EMPTY} == 0) {
			my $text;
			my %loglines;
			my $total = 0;
			my $max = 0;

			sysopen(LOGTEMP,"/etc/csf/csf.logtemp", O_RDWR | O_CREAT, 0600);
			flock (LOGTEMP, LOCK_EX);
			my @data = <LOGTEMP>;
			seek (LOGTEMP, 0, 0);
			truncate (LOGTEMP, 0);
			if (-e "/etc/csf/csf.logmax") {
				unlink "/etc/csf/csf.logmax";
				$max = 1;
			}
			close (LOGTEMP);
			chomp @data;

			if ($config{LOGSCANNER_STYLE} == "1") {
				foreach my $line (@data) {
					my ($logfile,$logline) = split(/\|/,$line);
					$loglines{$logfile} .= "$logline\n";
					$total++;
				}
				foreach my $logfile (keys %loglines) {
					$text .= "$logfile:\n$loglines{$logfile}\n";
				}
			} else {
				foreach my $line (@data) {
					my ($logfile,$logline) = split(/\|/,$line);
					$text .= "$logline\n";
					$total++;
				}
			}

			if ($max) {$text .= "\n...Report truncated as it exceeded $config{LOGSCANNER_LINES} of log lines...\n"}

			if ($text eq "") {$text = "...No log lines to report..."}

			open (IN, "</etc/csf/logalert.txt");
			my @alert = <IN>;
			close (IN);
			chomp @alert;

			my @message;
			foreach my $line (@alert) {
				$line =~ s/\r//;
				$line =~ s/\[text\]/$text/ig;
				$line =~ s/\[lines\]/$total/ig;
				$line =~ s/\[hour\]/$hour/ig;
				push @message, $line;
			}
			&sendmail(@message);

			if ($config{DEBUG} >= 2) {&logfile("Log Scanner report sent")}
		}

		close (THISLOCK);
		if ($config{DEBUG} >= 3) {$timer = &timer("stop","logscanner",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end logscanner
###############################################################################
# start exploit
sub exploit {
	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","exploit",$timer)}

		my $lockstr = "LF_EXPLOIT";
		sysopen (THISLOCK, "/etc/csf/lock/$lockstr.lock", O_RDWR | O_CREAT) or &childcleanup("*Error* Unable to open /etc/csf/lock/$lockstr.lock");
		flock (THISLOCK, LOCK_EX | LOCK_NB) or &childcleanup("*Lock Error* [$lockstr] still active - section skipped");

		$0 = "lfd - checking system exploit";

		my %exploit_tests;
		foreach my $test (split(/\,/,$config{LF_EXPLOIT_CHECK})) {$exploit_tests{$test} = 1}
		my $report = "";

		if ($exploit_tests{SUPERUSER}) {
			while (my ($name,undef,$uid) = getpwent()) {
				if (($uid == 0) and ($name ne "root") and ($suignore{$name} != 1)) {
					$report .= "Possible root compromise: User account $name is a superuser (UID 0)\n";
					&logfile("*System Exploit* has detected a possible root compromise ($name = UID 0)");
				}
			}
			endpwent();
		}

		if ($report) {
			$0 = "lfd - (child) system exploit alert";
			sysopen (TEMPEXPLOIT, "/etc/csf/csf.tempexploit", O_WRONLY | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot open out file: $!");
			flock (TEMPEXPLOIT, LOCK_EX);
			print TEMPEXPLOIT time;
			close (TEMPEXPLOIT);

			open (IN, "</etc/csf/exploitalert.txt");
			my @alert = <IN>;
			close (IN);
			chomp @alert;

			my @message;
			foreach my $line (@alert) {
				$line =~ s/\[text\]/$report/ig;
				push @message, $line;
			}
			&sendmail(@message);
			unlink "/etc/csf/csf.tempexp";
		}

		close (THISLOCK);
		if ($config{DEBUG} >= 3) {$timer = &timer("stop","exploit",$timer)}
		$0 = "lfd - child closing";
		exit;
	}
}
# end exploit
###############################################################################
# start getethdev
sub getethdev {
	unless (-e $config{IFCONFIG}) {&cleanup(__LINE__,"*Error* $config{IFCONFIG} (ifconfig binary location) -v does not exist!")}
	my ($childin, $childout);
	my $cmdpid = open3($childin, $childout, $childout, $config{IFCONFIG});
	my @ifconfig = <$childout>;
	waitpid ($cmdpid, 0);
	chomp @ifconfig;
	my $iface;

	($config{ETH_DEVICE},undef) = split (/:/,$config{ETH_DEVICE},2);

	foreach my $line (@ifconfig) {
		if ($line =~ /^(\w+)/ ) {
			$ifaces{$1} = 1;
		}
		if ($line =~ /inet \w*:\s*(\S+)/) {
			my $ip = $1;
			if (&checkip($ip)) {$ips{$ip} = 1}
		}
		if ($config{IPV6} and $line =~ /inet6 \w*:\s*(\S+)/) {
			my $ip = $1;
			if (&checkip($ip)) {eval {$ipscidr6->add($ip)}}
		}
	}
	if ($config{ETH_DEVICE} eq "") {
		$ethdevin = "! -i lo";
		$ethdevout = "! -o lo";
		return;
	} else {
		$ethdevin = "-i $config{ETH_DEVICE}";
		$ethdevout = "-o $config{ETH_DEVICE}";
	}

	if ($config{ETH_DEVICE} =~ /\+$/) {return}

	unless ($ifaces{$config{ETH_DEVICE}}) {&cleanup(__LINE__,"*Error* Main ethernet device [$config{ETH_DEVICE}] not listed in ifconfig")}
}
# end getethdev
###############################################################################
# start iplookup
sub iplookup {
	my $ip = shift;
	my $host = "-";

	if ($config{LF_LOOKUPS}) {
		my $iptype = &checkip($ip);
		if ($iptype == 4) {
			eval {
				local $SIG{__DIE__} = undef;
				local $SIG{'ALRM'} = sub {die};
				alarm(10);
				my $ipaddr = inet_aton($ip);
				$host = gethostbyaddr($ipaddr, AF_INET);
				alarm(0);
			};
			alarm(0);
		}
		elsif ($iptype == 6) {
			eval {
				local $SIG{__DIE__} = undef;
				local $SIG{'ALRM'} = sub {die};
				alarm(10);
				eval('use Socket6;');
				my $ipaddr = inet_pton(AF_INET6, $ip);
				$host = gethostbyaddr($ipaddr, AF_INET6);
				alarm(0);
			};
			alarm(0);
		}
		if ($host eq "") {$host = "-"}
	}

	if ($config{CC_LOOKUPS} and defined $ipcountry) {
		if ($config{CC_LOOKUPS} == 2) {
			my ($cc,undef,$country,$region,$city,undef) = $ipcountry->get_city_record($ip);
			unless ($cc) {$cc = "-"}
			unless ($country) {$country = "-"}
			unless ($region) {$region = "-"}
			unless ($city) {$city = "-"}
			return "$ip ($cc/$country/$region/$city/$host)";
		} else {
			my $cc = $ipcountry->country_code_by_addr($ip);
			my $country = $ipcountry->country_name_by_addr($ip);
			unless ($cc) {$cc = "-"}
			unless ($country) {$country = "-"}
			return "$ip ($cc/$country/$host)";
		}
	}

	if ($config{LF_LOOKUPS}) {
		if ($host eq "-") {$host = "Unknown"}
		return "$ip ($host)";
	} else {
		return $ip;
	}
}
# end iplookup
###############################################################################
# start logfile
sub logfile {
	my $line = shift;
	my @ts = split(/\s+/,scalar localtime);
	if ($ts[2] < 10) {$ts[2] = " ".$ts[2]}
	sysopen (LOGFILE,"/var/log/lfd.log", O_WRONLY | O_APPEND | O_CREAT);
	flock (LOGFILE, LOCK_EX);
	print LOGFILE "$ts[1] $ts[2] $ts[3] $hostshort lfd[$$]: $line\n";
	close (LOGFILE);

	if ($config{SYSLOG} and $sys_syslog) {
		eval {
			local $SIG{__DIE__} = undef;
			openlog('lfd', 'ndelay,pid', 'user');
			syslog('info', $line);
			closelog();
		}
	}
}
# end logfile
###############################################################################
# start converthex2ip
sub converthex2ip {
	my $addr=shift @_;
	my $pattern = "([0-9A-Z]{2})([0-9A-Z]{2})([0-9A-Z]{2})([0-9A-Z]{2})" .":([0-9A-Z]{2})([0-9A-Z]{2})";

	if ($addr =~ m/$pattern$/) {
		return  hex($4).".".hex($3).".".hex($2).".".hex($1);
	} else {
		return undef;
	}
}
# end converthex2ip
###############################################################################
# start cleanup
sub cleanup {
	$SIG{INT} = 'IGNORE';
	$SIG{TERM} = 'IGNORE';
	$SIG{HUP} = 'IGNORE';
	my $line = shift;
	my $message = shift;

	if (($message eq "") and $line) {
		$message = $line;
		$line = "";
	}

	$0 = "lfd - stopping";

	if ($message) {
		if ($line ne "") {$message .= ", at line $line"}
		&logfile("$message");
	}
	&logfile("daemon stopped");

	close(PIDFILE);
	unlink $pidfile;

	kill (9, -$$);

    exit 0;
}
# end cleanup
###############################################################################
# start childcleanup
sub childcleanup {
	$SIG{INT} = 'IGNORE';
	$SIG{TERM} = 'IGNORE';
	$SIG{HUP} = 'IGNORE';
	my $line = shift;
	my $message = shift;

	if (($message eq "") and $line) {
		$message = $line;
		$line = "";
	}

	$0 = "child - aborting";

	if ($message) {
		if ($line ne "") {$message .= ", at line $line"}
		&logfile("$message");
	}
    exit;
}
# end childcleanup
###############################################################################
# start reaper
sub reaper {
	my $zombie;
	my %kid_status;
	my $timer = time;
	if ($config{DEBUG} >= 3) {$timer = &timer("start","reaper",$timer)}
	eval {
		local $SIG{__DIE__} = undef;
		local $SIG{'ALRM'} = sub {die};
		alarm(5);
		while (($zombie = waitpid (-1, WNOHANG)) != -1) {
			$kid_status{$zombie} = $?;
			sleep 1;
		}
		$childcnt = 0;
		alarm(0);
	};
	alarm(0);
	if ($@) {sleep 10}
	if ($config{DEBUG} >= 3) {$timer = &timer("stop","reaper",$timer)}
}
# end reaper
###############################################################################
# start ignoreip
sub ignoreip {
	my $ip = shift;
	my $skip = shift;

	if ($ip eq "") {return 0}

	if ($ips{$ip} or $ipscidr->find($ip) or $ipscidr6->find($ip)) {return 1}

	if ($ignoreips{$ip}) {return 1}

	if ($gignoreips{$ip}) {return 1}

	if ($ccignoreips{$ip}) {return 1}

	if ($relayip{$ip} and !$skip) {return 1}

	if (@cidrs) {
		if ($cidr->find($ip)) {return 1}
		if ($cidr6->find($ip)) {return 1}
	}

	if (@gcidrs) {
		if ($gcidr->find($ip)) {return 1}
		if ($gcidr6->find($ip)) {return 1}
	}

	if (@cccidrs) {
		if ($cccidr->find($ip)) {return 1}
		if ($cccidr6->find($ip)) {return 1}
	}

	if (@rdns and !$skip) {
		my $matchdomain;
		my $matchip;

		eval {
			local $SIG{__DIE__} = undef;
			local $SIG{'ALRM'} = sub {die};
			alarm(10);
			$matchip = inet_aton($ip);
			$matchdomain = gethostbyaddr($matchip, AF_INET);
			$matchip = gethostbyname($matchdomain);
			$matchip = inet_ntoa($matchip);
			alarm(0);
		};
		alarm(0);

		if ($config{DEBUG} >= 2) {&logfile("debug: (ignoreip) [$ip]:[$matchip] [$matchdomain]")}

		if ($ip eq $matchip) {
			foreach my $host (@rdns) {
				if (($host =~ /\./) and ($matchdomain =~ /$host$/)) {
					if ($config{DEBUG} >= 1) {&logfile("debug: (ignoreip) [$ip]:[$matchip] [$host]:[$matchdomain]")}
					return 1;
				}
				if ($matchdomain eq $host) {
					if ($config{DEBUG} >= 1) {&logfile("debug: (ignoreip) [$ip]:[$matchip] [$host]:[$matchdomain]")}
					return 1;
				}
			}
		}
	}

	return 0;
}
# end ignoreip
###############################################################################
# start linefilter
sub linefilter {
	my $line = shift;
	my $ad = shift;
	my $chain = shift;
	my $delete = shift;
	my $pktin = "$accept";
	my $pktout = "$accept";
	my $inadd = "-I";
	my $verbose = "";
	if ($ad eq "deny") {
		$inadd = "-A";
		$pktin = $config{DROP};
		$pktout = $config{DROP};
		if ($config{DROP_IP_LOGGING}) {$pktin = "LOGDROPIN"}
		if ($config{DROP_IP_LOGGING}) {$pktout = "LOGDROPOUT"}
	}
	my $chainin = $chain."IN";
	my $chainout = $chain."OUT";

	$line =~ s/\n|\r//g;
	$line = lc $line;
	if ($line =~ /^\#/) {return}
	if ($line =~ /^Include/) {return}
	if ($line eq "") {return}

	my $checkip = checkip($line);

	if ($checkip) {
		my $iptables = $config{IPTABLES};
		my $ipv4 = 1;
		if ($checkip == 6) {
			$iptables = $config{IP6TABLES};
			$ipv4 = 0;
		}
		if ($chain) {
			&syscommand(__LINE__,"$iptables $verbose -A $chainin $ethdevin -s $line -j $pktin");
			&syscommand(__LINE__,"$iptables $verbose -A $chainout $ethdevout -d $line -j $pktout");
		} else {
			if ($delete) {
				&syscommand(__LINE__,"$iptables $verbose -D LOCALINPUT $ethdevin -s $line -j $pktin");
				&syscommand(__LINE__,"$iptables $verbose -D LOCALOUTPUT $ethdevout -d $line -j $pktout");
				if (($ad eq "deny") and ($ipv4 and $config{MESSENGER} and $config{MESSENGER_PERM})) {&domessenger($line,"D")}
			} else {
				&syscommand(__LINE__,"$iptables $verbose $inadd LOCALINPUT $ethdevin -s $line -j $pktin");
				&syscommand(__LINE__,"$iptables $verbose $inadd LOCALOUTPUT $ethdevout -d $line -j $pktout");
				if (($ad eq "deny") and ($ipv4 and $config{MESSENGER} and $config{MESSENGER_PERM})) {&domessenger($line,"A")}
			}
		}
	}
	elsif ($line =~ /\:|\|/) {
		if ($line !~ /\|/) {$line =~ s/\:/\|/g}
		my $sip;
		my $dip;
		my $sport;
		my $dport;
		my $protocol = "-p tcp";
		my $inout;
		my $from = 0;
		my $uid;
		my $gid;
		my $iptype;

		my @ll = split(/\|/,$line);
		if ($ll[0] eq "tcp") {
			$protocol = "-p tcp";
			$from = 1;
		}
		elsif ($ll[0] eq "udp") {
			$protocol = "-p udp";
			$from = 1;
		}
		elsif ($ll[0] eq "icmp") {
			$protocol = "-p icmp";
			$from = 1;
		}
		for (my $x = $from;$x < 2;$x++) {
			if (($ll[$x] eq "out")) {
				$inout = "out";
				$from = $x + 1;
				last;
			}
			elsif (($ll[$x] eq "in")) {
				$inout = "in";
				$from = $x + 1;
				last;
			}
		}
		for (my $x = $from;$x < 3;$x++) {
			if (($ll[$x] =~ /d=(.*)/)) {
				$dport = "--dport $1";
				$dport =~ s/_/:/g;
				if ($protocol eq "-p icmp") {$dport = "--icmp-type $1"}
				$from = $x + 1;
				last;
			}
			elsif (($ll[$x] =~ /s=(.*)/)) {
				$sport = "--sport $1";
				$sport =~ s/_/:/g;
				if ($protocol eq "-p icmp") {$sport = "--icmp-type $1"}
				$from = $x + 1;
				last;
			}
		}
		for (my $x = $from;$x < 4;$x++) {
			if (($ll[$x] =~ /d=(.*)/)) {
				my $ip = $1;
				my $status = &checkip($ip);
				if ($status) {
					$iptype = $status;
					$dip = "-d $1";
				}
				last;
			}
			elsif (($ll[$x] =~ /s=(.*)/)) {
				my $ip = $1;
				my $status = &checkip($ip);
				if ($status) {
					$iptype = $status;
					$sip = "-s $1";
				}
				last;
			}
		}
		for (my $x = $from;$x < 5;$x++) {
			if (($ll[$x] =~ /u=(.*)/)) {
				$uid = "--uid-owner $1";
				last;
			}
			elsif (($ll[$x] =~ /g=(.*)/)) {
				$gid = "--gid-owner $1";
				last;
			}
		}
		if (($sip or $dip) and ($dport or $sport)) {
			my $iptables = $config{IPTABLES};
			my $ipv4 = 1;
			if ($iptype == 6) {
				$iptables = $config{IP6TABLES};
				$ipv4 = 0;
			}
			if (($inout eq "") or ($inout eq "in")) {
				my $bport = $dport;
				$bport =~ s/--dport //o;
				my $bip = $sip;
				$bip =~ s/-s //o;
				if ($chain) {
					&syscommand(__LINE__,"$iptables $verbose -A $chainin $ethdevin $protocol $dip $sip $dport $sport -j $pktin");
				} else {
					if ($delete) {
						&syscommand(__LINE__,"$iptables $verbose -D LOCALINPUT $ethdevin $protocol $dip $sip $dport $sport -j $pktin");
						if ($messengerports{$bport} and ($ad eq "deny") and ($ipv4 and $config{MESSENGER} and $config{MESSENGER_PERM})) {&domessenger($bip,"D","$bport")}
					} else {
						&syscommand(__LINE__,"$iptables $verbose $inadd LOCALINPUT $ethdevin $protocol $dip $sip $dport $sport -j $pktin");
						if ($messengerports{$bport} and ($ad eq "deny") and ($ipv4 and $config{MESSENGER} and $config{MESSENGER_PERM})) {&domessenger($bip,"A","$bport")}
					}
				}
			}
			if ($inout eq "out") {
				if ($chain) {
					&syscommand(__LINE__,"$iptables $verbose -A $chainout $ethdevout $protocol $dip $sip $dport $sport -j $pktout");
				} else {
					if ($delete) {
						&syscommand(__LINE__,"$iptables $verbose -D LOCALOUTPUT $ethdevout $protocol $dip $sip $dport $sport -j $pktout");
					} else {
						&syscommand(__LINE__,"$iptables $verbose $inadd LOCALOUTPUT $ethdevout $protocol $dip $sip $dport $sport -j $pktout");
					}
				}
			}
		}
	}
}
# end linefilter
###############################################################################
# start syscommand
sub syscommand {
	my $line = shift;
	my $command = shift;
	my $status = 0;

	if (-e "/etc/csf/csf.error") {
		&cleanup(__LINE__,"*Error* csf reported an error. *lfd stopped*");
		exit;
	}

	if ($config{VPS}) {$status = &checkvps}
	if ($status) {
		&logfile($status);
	} else {
		if ($config{DEBUG} >= 2) {&logfile("debug[$line]: Command:$command")}
		if (&csflock) {
			&logfile("csf is currently restarting - command [$command] skipped on line $line");
			unless ($masterpid == $$) {exit}
		}
		my ($childin, $childout);
		my $cmdpid = open3($childin, $childout, $childout, $command);
		my @output = <$childout>;
		waitpid ($cmdpid, 0);
		chomp @output;
		if ($output[0] =~ /^deny failed\:/) {&logfile("*Error*: csf output: $output[0]")}
		if ($output[0] =~ /^Error\:/) {&logfile("*Error*: csf output: $output[0]")}

		if ($output[0] =~ /^iptables: Unknown error 4294967295/) {
			my $cnt = 0;
			my $repeat = 10;
			while ($cnt < $repeat) {
				Time::HiRes::sleep(0.5);
				if ($config{DEBUG} >= 1) {&logfile("debug[$line]: Retry (".($cnt+1).") [$command] due to [$output[0]]")}
				my ($childin, $childout);
				my $cmdpid = open3($childin, $childout, $childout, $command);
				my @output = <$childout>;
				waitpid ($cmdpid, 0);
				chomp @output;
				$cnt++;
				if ($output[0] =~ /^iptables: Unknown error 4294967295/ and $cnt == $repeat) {&logfile("*Error* processing command for line [$line] ($repeat times): [$output[0]]");}
				unless ($output[0] =~ /^iptables: Unknown error 4294967295/) {$cnt = $repeat}
			}
		}
		elsif ($config{DEBUG} >= 1 and $output[0] =~ /^iptables|ip6tables|Bad/) {
				&logfile("*Error* processing command for line [$line]: [$output[0]]");
		}
	}
}
# end syscommand
###############################################################################
# start checkip
sub checkip {
	my $line = shift;
	my $ret = 0;
	my ($ip,$cidr) = split(/\//,$line);
	if ($cidr ne "") {
		unless ($cidr =~ /^\d+$/) {return 0}
	}
	if ($ip =~ /^$ipv4reg$/) {
		$ret = 4;
		if ($cidr) {
			unless ($cidr >= 1 && $cidr <= 32) {return 0}
		}
		if ($ip eq "127.0.0.1") {return 0}
	}
	if ($config{IPV6} and $ip =~ /^$ipv6reg$/) {
		$ret = 6;
		if ($cidr) {
			unless ($cidr >= 1 && $cidr <= 128) {return 0}
		}
		$ip =~ s/://g;
		$ip =~ s/^0*//g;
		if ($ip == 1) {return 0}
	}
	return $ret;
}
# end checkip
###############################################################################
# start timer
sub timer {
	my $status = shift;
	my $check = shift;
	my $start = shift;

	if ($status eq "start") {
		&logfile("debug: TIMER start: $check");
		return gettimeofday();
	} else {
		if ($start == 0) {return}
		my $diff = sprintf '%.6f', (gettimeofday() - $start);
		&logfile("debug: TIMER stop:  $check ($diff secs)");
	}
}
# end timer
###############################################################################
# start csflock
sub csflock {
	my $ret = 0;
	sysopen (CSFLOCKFILE, "/etc/csf/csf.lock", O_RDWR | O_CREAT) or &childcleanup("*Error* Unable to open csf lock file");
	flock (CSFLOCKFILE, LOCK_SH | LOCK_NB) or $ret = 1;
	close (CSFLOCKFILE);

	return $ret;
}
# end csflock
###############################################################################
# start lockfail
sub lockfail {
	my $section = shift;
	&logfile("csf is currently restarting - section [$section] skipped");
	exit;
}
# end lockfail
###############################################################################
# start ipblock
sub ipblock {
	my $perm = shift;
	my $message = shift;
	my $ip = shift;
	my $port  = shift;
	my $inout = shift;
	my $timeout = shift;
	my $cluster = shift;
	my $logs = shift;
	my $active = shift;
	unless ($active) {$active = "other"}

	if ($message =~ / exceeds /) {$cluster = 1}

	my @report;
	$report[0] = $ip;
	if ($port) {$report[1] = "$port"} else {$report[1] = "*"}
	if ($perm) {$report[2] = "1"} else {$report[2] = "0"}
	if ($inout) {$report[3] = "$inout"} else {$report[3] = "inout"}
	if ($timeout) {$report[4] = "$timeout"} else {$report[4] = "0"}
	if ($message) {$report[5] = "$message"} else {$report[5] = " "}
	if ($logs) {$report[6] = "$logs"} else {$report[6] = " "}
	if ($active) {$report[7] = "$active"}

	my $ipv = &checkip($ip);
	unless ($ipv) {return}

	$0 = "lfd - (child) blocking $ip";

	my $blocked = 0;
	if ($config{LF_PERMBLOCK} or $config{LF_NETBLOCK}) {
		my $ips;
		my $ipmatch;
		my $text;
		my $nips;
		my $ntext;
		my $nipmatch;
		my $ipblock;
		my @newdata;
		my %skipip;
		my %skipnip;
		my $block_interval = $config{LF_PERMBLOCK_INTERVAL};
		if ($config{LF_NETBLOCK_INTERVAL} > $config{LF_PERMBLOCK_INTERVAL}) {$block_interval = $config{LF_NETBLOCK_INTERVAL}}
		if ($config{LF_NETBLOCK_CLASS} eq "A") {
			if ($ip =~ /^(\d+)/) {$ipblock = "$1\.0\.0\.0/8"}
		}
		elsif ($config{LF_NETBLOCK_CLASS} eq "B") {
			if ($ip =~ /^(\d+\.\d+)/) {$ipblock = "$1\.0\.0/16"}
		}
		elsif ($config{LF_NETBLOCK_CLASS} eq "C") {
			if ($ip =~ /^(\d+\.\d+\.\d+)/) {$ipblock = "$1\.0/24"}
		}

		sysopen (TEMPIP, "/etc/csf/csf.tempip", O_RDWR | O_CREAT);
		flock (TEMPIP, LOCK_EX);
		my @data = <TEMPIP>;
		chomp @data;
		foreach my $line (@data) {
			my ($oip,$operm,$otime) = split(/\|/,$line);
			if (time - $otime < $block_interval) {
				push @newdata,$line;
			}
			if ($config{LF_PERMBLOCK}) {
				if (($ip eq $oip) and ($operm != 1) and (time - $otime < $config{LF_PERMBLOCK_INTERVAL})) {
					$ips++;
					$text .= localtime($otime)." $ip\n";
					$skipip{$line} = 1;
				}
				if ($operm and ($ip eq $oip)) {
					$ipmatch = 1;
				}
			}
			if ($config{LF_NETBLOCK} and ($ipv == 4)) {
				if (time - $otime < $config{LF_NETBLOCK_INTERVAL}) {
					my $block = "";
					if ($config{LF_NETBLOCK_CLASS} eq "A") {
						if ($oip =~ /^(\d+)/) {$block = "$1\.0\.0\.0/8"}
					}
					elsif ($config{LF_NETBLOCK_CLASS} eq "B") {
						if ($oip =~ /^(\d+\.\d+)/) {$block = "$1\.0\.0/16"}
					}
					elsif ($config{LF_NETBLOCK_CLASS} eq "C") {
						if ($oip =~ /^(\d+\.\d+\.\d+)/) {$block = "$1\.0/24"}
					}
					if ($ipblock eq $block) {
						unless ($oip eq $ip) {
							$nips++;
							$ntext .= localtime($otime)." $oip\n";
							$skipnip{$line} = 1;
						}
					}
					if ($block eq $oip) {$nipmatch = 1}
				}
			}
		}
		if ($ipmatch) {
			$ips = 0;
			undef %skipip;
			if ($config{DEBUG} >= 1) {&logfile("debug: $message - already PERM blocked")}
		} else {
			$ips++;
			$text .= localtime(time)." $ip\n";
		}
		if ($nipmatch) {
			$nips = 0;
			undef %skipnip;
			if ($config{DEBUG} >= 1) {&logfile("debug: $message - already NET blocked")}
		} else {
			$nips++;
			$ntext .= localtime(time)." $ip\n";
		}

		if ($nips > $config{LF_NETBLOCK_COUNT} and ($ipv == 4)) {
			my $status = 0;
			if ($config{VPS}) {$status = &checkvps}
			if ($status) {
				&logfile($status);
			} else {
				$message = "(NETBLOCK) $ipblock has had more than $config{LF_NETBLOCK_COUNT} blocks in the last $config{LF_NETBLOCK_INTERVAL} secs";
				&syscommand(__LINE__,"/etc/csf/csf.pl -d $ipblock 'lfd: $message'");
				&logfile("$message - *Blocked in csf* [$active]");
				if ($config{CLUSTER_BLOCK} and $config{CLUSTER_SENDTO} and !$cluster) {&lfdclient(1,"",$ipblock,"","inout","0")}
				if ($config{BLOCK_REPORT}) {&block_report($ipblock,"*","1","inout","0",$message,"","LF_NETBLOCK_COUNT")}
				if ($config{ST_ENABLE}) {&stats_report($ipblock,"*","1","inout","0",$message,"","LF_NETBLOCK_COUNT")}
			}

			if ($config{LF_NETBLOCK_ALERT}) {
				$0 = "lfd - (child) sending alert email for $ipblock";

				open (IN, "</etc/csf/netblock.txt");
				my @alert = <IN>;
				close (IN);
				chomp @alert;

				my @message;
				foreach my $line (@alert) {
					$line =~ s/\[block\]/$ipblock/ig;
					$line =~ s/\[count\]/$nips/ig;
					$line =~ s/\[class\]/$config{LF_NETBLOCK_CLASS}/ig;
					$line =~ s/\[ips\]/$ntext/ig;
					push @message, $line;
				}
				&sendmail(@message);
			}

			$ip = $ipblock;
			$perm = 1;
			$blocked = 1;
		}
		elsif ($ips > $config{LF_PERMBLOCK_COUNT}) {
			my $status = 0;
			if ($config{VPS}) {$status = &checkvps}
			if ($status) {
				&logfile($status);
			} else {
				$message = "(PERMBLOCK) $ip has had more than $config{LF_PERMBLOCK_COUNT} temp blocks in the last $config{LF_PERMBLOCK_INTERVAL} secs";
				&syscommand(__LINE__,"/etc/csf/csf.pl -tr $ip");
				&syscommand(__LINE__,"/etc/csf/csf.pl -d $ip 'lfd: $message'");
				&logfile("$message - *Blocked in csf* [$active]");
				if ($config{CLUSTER_BLOCK} and $config{CLUSTER_SENDTO} and !$cluster) {&lfdclient(1,"",$ip,"","inout","0")}
				if ($config{BLOCK_REPORT}) {&block_report($ip,"*","1","inout","0",$message,"","LF_PERMBLOCK_COUNT")}
				if ($config{ST_ENABLE}) {&stats_report($ip,"*","1","inout","0",$message,"","LF_PERMBLOCK_COUNT")}
			}

			if ($config{LF_PERMBLOCK_ALERT}) {
				$0 = "lfd - (child) sending alert email for $ip";

				open (IN, "</etc/csf/permblock.txt");
				my @alert = <IN>;
				close (IN);
				chomp @alert;

				my $tip = &iplookup($ip);
				my @message;
				foreach my $line (@alert) {
					$line =~ s/\[ip\]/$tip/ig;
					$line =~ s/\[count\]/$ips/ig;
					$line =~ s/\[blocks\]/$text/ig;
					push @message, $line;
				}
				&sendmail(@message);
			}
			$perm = 1;
			$blocked = 1;
		}
		seek (TEMPIP, 0, 0);
		truncate (TEMPIP, 0);
		foreach my $line (@newdata) {
			if (($ips > $config{LF_PERMBLOCK_COUNT}) and ($skipip{$line})) {next}
			if (($nips > $config{LF_NETBLOCK_COUNT}) and ($skipnip{$line})) {next}
			print TEMPIP "$line\n";
		}
		print TEMPIP "$ip|$perm|".time."\n";
		close (TEMPIP);
	}
	unless ($blocked) {
		if ($perm) {
			if ($port) {
				foreach my $dport (split(/\,/,$port)) {
					my $status = 0;
					if ($config{VPS}) {$status = &checkvps}
					if ($status) {
						&logfile($status);
					} else {
						&syscommand(__LINE__,"/etc/csf/csf.pl -d \"tcp|in|d=$dport|s=$ip 'lfd: $message'\"");
						&logfile("$message - *Blocked in csf* port=$dport [$active]");
					}
				}
			} else {
				my $status = 0;
				if ($config{VPS}) {$status = &checkvps}
				if ($status) {
					&logfile($status);
				} else {
					&syscommand(__LINE__,"/etc/csf/csf.pl -d $ip 'lfd: $message'");
					&logfile("$message - *Blocked in csf* [$active]");
				}
			}
			if ($config{CLUSTER_BLOCK} and $config{CLUSTER_SENDTO} and !$cluster) {&lfdclient($perm,"",$ip,"$port","$inout","0")}
			if ($config{BLOCK_REPORT}) {&block_report(@report)}
			if ($config{ST_ENABLE}) {&stats_report(@report)}
		} else {
			open (IN, "</etc/csf/csf.tempban");
			flock (IN, LOCK_SH);
			my @deny = <IN>;
			close (IN);
			chomp @deny;
			unless (grep {$_ =~ /\b$ip\|$port\|\b/} @deny) {
				my $dropin = $config{DROP};
				my $dropout = $config{DROP};
				if ($config{DROP_IP_LOGGING}) {
					$dropin = "LOGDROPIN";
					$dropout = "LOGDROPOUT";
				}
				if ($timeout < 2) {$timeout = 3600}
				my $iptype = &checkip($ip);

				if ($inout =~ /in/) {
					if ($port) {
						foreach my $dport (split(/\,/,$port)) {
							if ($iptype == 6) {
								&syscommand(__LINE__,"$config{IP6TABLES} -A LOCALINPUT $ethdevin -p tcp --dport $dport -s $ip -j $dropin");
							} else {
								&syscommand(__LINE__,"$config{IPTABLES} -A LOCALINPUT $ethdevin -p tcp --dport $dport -s $ip -j $dropin");
								if ($messengerports{$dport} and $config{MESSENGER} and $config{MESSENGER_TEMP}) {&domessenger($ip,"A",$dport)}
							}
						}
					} else {
						if ($iptype == 6) {
							&syscommand(__LINE__,"$config{IP6TABLES} -A LOCALINPUT $ethdevin -s $ip -j $dropin");
						} else {
							&syscommand(__LINE__,"$config{IPTABLES} -A LOCALINPUT $ethdevin -s $ip -j $dropin");
							if ($config{MESSENGER} and $config{MESSENGER_TEMP}) {&domessenger($ip,"A")}
						}
					}
				}
				if ($inout =~ /out/) {
					if ($iptype == 6) {
						&syscommand(__LINE__,"$config{IP6TABLES} -A LOCALOUTPUT $ethdevout -d $ip -j $dropout");
					} else {
						&syscommand(__LINE__,"$config{IPTABLES} -A LOCALOUTPUT $ethdevout -d $ip -j $dropout");
					}
				}

				sysopen (TEMPBAN, "/etc/csf/csf.tempban", O_WRONLY | O_APPEND | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot append out file: $!");
				flock (TEMPBAN, LOCK_EX);
				print TEMPBAN time."|$ip|$port|$inout|$timeout|lfd - $message\n";
				close (TEMPBAN);

				if ($message) {&logfile("$message - *Blocked in csf* for $timeout secs [$active]")}
				if ($config{CLUSTER_BLOCK} and $config{CLUSTER_SENDTO} and !$cluster) {&lfdclient($perm,"",$ip,"$port","$inout","0")}
				if ($config{BLOCK_REPORT}) {&block_report(@report)}
				if ($config{ST_ENABLE}) {&stats_report(@report)}
			}
		}
	}
}
# end ipblock
###############################################################################
# start ipunblock
sub ipunblock {
	if (! -z "/etc/csf/csf.tempban") {
		unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
		unless (defined ($childpid = fork)) {
			&cleanup(__LINE__,"*Error* cannot fork: $!");
		} 
		unless ($childpid) {
			$0 = "lfd - processing temporary bans";
			my $timer = time;
			if ($config{DEBUG} >= 3) {$timer = &timer("start","ipunblock",$timer)}
			sysopen (TEMPBAN, "/etc/csf/csf.tempban", O_RDWR | O_CREAT) or &childcleanup(__LINE__,"Unable to open /etc/csf/csf.tempban: $!");
			unless (flock (TEMPBAN, LOCK_EX | LOCK_NB)) {
				if ($config{DEBUG} >= 3) {&logfile("debug: Unable to lock csf.tempban in ipunblock")}
			} else {
				my @data = <TEMPBAN>;
				chomp @data;

				my $cnt = @data;
				my @newdata;
				foreach my $line (@data) {
					my $unblock = 0;
					my $logmess = "";
					if ($config{DENY_TEMP_IP_LIMIT} and ($cnt > $config{DENY_TEMP_IP_LIMIT})) {
						$unblock = 1;
						$logmess = "(too many temporary bans in list)";
					}
					my ($time,$ip,$port,$inout,$timeout,$message) = split(/\|/,$line);
					my $iptype = &checkip($ip);
					if ((((time - $time) >= $timeout) and $ip) or $unblock) {
						my $dropin = $config{DROP};
						my $dropout = $config{DROP};
						if ($config{DROP_IP_LOGGING}) {$dropin = "LOGDROPIN"}
						if ($config{DROP_IP_LOGGING}) {$dropout = "LOGDROPOUT"}

						if ($inout =~ /in/) {
							if ($port) {
								foreach my $dport (split(/\,/,$port)) {
									if ($iptype == 6) {
										&syscommand(__LINE__,"$config{IP6TABLES} -D LOCALINPUT $ethdevin -p tcp --dport $dport -s $ip -j $dropin");
									} else {
										&syscommand(__LINE__,"$config{IPTABLES} -D LOCALINPUT $ethdevin -p tcp --dport $dport -s $ip -j $dropin");
										if ($messengerports{$dport} and $config{MESSENGER} and $config{MESSENGER_TEMP}) {&domessenger($ip,"D",$dport)}
									}
									&logfile("Incoming IP $ip:$dport temporary block removed");
								}
							} else {
								if ($iptype == 6) {
									&syscommand(__LINE__,"$config{IP6TABLES} -D LOCALINPUT $ethdevin -s $ip -j $dropin");
								} else {
									&syscommand(__LINE__,"$config{IPTABLES} -D LOCALINPUT $ethdevin -s $ip -j $dropin");
									if ($config{MESSENGER} and $config{MESSENGER_TEMP}) {&domessenger($ip,"D")}
								}
								&logfile("Incoming IP $ip temporary block removed");
							}
						}
						if ($inout =~ /out/) {
							if ($iptype == 6) {
								&syscommand(__LINE__,"$config{IP6TABLES} -D LOCALOUTPUT $ethdevout -d $ip -j $dropout");
							} else {
								&syscommand(__LINE__,"$config{IPTABLES} -D LOCALOUTPUT $ethdevout -d $ip -j $dropout");
							}
							&logfile("Outgoing IP $ip temporary block removed");
						}
					} else {
						push @newdata, $line;
					}
					$cnt--;
				}
				eval {
					local $SIG{__DIE__} = undef;
					local $SIG{'ALRM'} = sub {die};
					alarm(60);
					local $SIG{INT} = 'IGNORE';
					local $SIG{TERM} = 'IGNORE';
					local $SIG{HUP} = 'IGNORE';
					seek (TEMPBAN, 0, 0);
					truncate (TEMPBAN, 0);
					foreach my $line (@newdata) {print TEMPBAN "$line\n"}
				};
				alarm(0);
			}
			close (TEMPBAN);
			if ($config{DEBUG} >= 3) {$timer = &timer("stop","ipunblock",$timer)}
			$0 = "lfd - (child) closing";
			exit;
		}
	}
	if (! -z "/etc/csf/csf.tempallow") {
		unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
		unless (defined ($childpid = fork)) {
			&cleanup(__LINE__,"*Error* cannot fork: $!");
		} 
		unless ($childpid) {
			$0 = "lfd - processing temporary allows";
			my $timer = time;
			if ($config{DEBUG} >= 3) {$timer = &timer("start","ipunblock",$timer)}
			sysopen (TEMPALLOW, "/etc/csf/csf.tempallow", O_RDWR | O_CREAT) or &childcleanup(__LINE__,"Enable to open /etc/csf/csf.tempallow: $!");
			unless (flock (TEMPALLOW, LOCK_EX | LOCK_NB)) {
				if ($config{DEBUG} >= 3) {&logfile("debug: Unable to lock csf.tempallow in ipunblock")}
			} else {
				my @data = <TEMPALLOW>;
				chomp @data;

				my $cnt = @data;
				my @newdata;
				foreach my $line (@data) {
					my ($time,$ip,$port,$inout,$timeout,$message) = split(/\|/,$line);
					my $iptype = &checkip($ip);
					if ((((time - $time) >= $timeout) and $ip)) {
						if ($inout =~ /in/) {
							if ($port) {
								foreach my $dport (split(/\,/,$port)) {
									if ($iptype == 6) {
										&syscommand(__LINE__,"$config{IP6TABLES} -D LOCALINPUT $ethdevin -p tcp --dport $dport -s $ip -j $accept");
									} else {
										&syscommand(__LINE__,"$config{IPTABLES} -D LOCALINPUT $ethdevin -p tcp --dport $dport -s $ip -j $accept");
									}
									&logfile("Incoming IP $ip:$dport temporary allow removed");
								}
							} else {
								if ($iptype == 6) {
									&syscommand(__LINE__,"$config{IP6TABLES} -D LOCALINPUT $ethdevin -s $ip -j $accept");
								} else {
									&syscommand(__LINE__,"$config{IPTABLES} -D LOCALINPUT $ethdevin -s $ip -j $accept");
								}
								&logfile("Incoming IP $ip temporary allow removed");
							}
						}
						if ($inout =~ /out/) {
							if ($iptype == 6) {
								&syscommand(__LINE__,"$config{IP6TABLES} -D LOCALOUTPUT $ethdevout -d $ip -j $accept");
							} else {
								&syscommand(__LINE__,"$config{IPTABLES} -D LOCALOUTPUT $ethdevout -d $ip -j $accept");
							}
							&logfile("Outgoing IP $ip temporary allow removed");
						}
					} else {
						push @newdata, $line;
					}
					$cnt--;
				}
				eval {
					local $SIG{__DIE__} = undef;
					local $SIG{'ALRM'} = sub {die};
					alarm(60);
					local $SIG{INT} = 'IGNORE';
					local $SIG{TERM} = 'IGNORE';
					local $SIG{HUP} = 'IGNORE';
					seek (TEMPALLOW, 0, 0);
					truncate (TEMPALLOW, 0);
					foreach my $line (@newdata) {print TEMPALLOW "$line\n"}
				};
				alarm(0);
			}
			close (TEMPALLOW);
			if ($config{DEBUG} >= 3) {$timer = &timer("stop","ipunblock",$timer)}
			$0 = "lfd - (child) closing";
			exit;
		}
	}
}
# end ipunblock
###############################################################################
# start block_report
sub block_report {
	my @report = @_;
	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","block_report",$timer)}
		$0 = "lfd - (child) Block Report...";

		eval {
			local $SIG{__DIE__} = undef;
			local $SIG{'ALRM'} = sub {die};
			alarm(10);
			system("$config{BLOCK_REPORT}",@report);
			alarm(0);
		};
		alarm(0);
		if ($@) {
			&logfile("BLOCK_REPORT timed out after 10 seconds");
		} else {
			if ($config{DEBUG} >= 3) {&logfile("debug: BLOCK_REPORT [$config{BLOCK_REPORT}] for ['$report[0]' '$report[1]' '$report[2]' '$report[3]' '$report[4]' '$report[5]']")}
		}

		if ($config{DEBUG} >= 3) {$timer = &timer("stop","block_report",$timer)}
		$0 = "lfd - (child) closing";
		exit;
	}
}
# end block_report
###############################################################################
# start stats_report
sub stats_report {
	my @report = @_;
	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","stats_report",$timer)}
		$0 = "lfd - (child) Stats Report...";

		#[0-23] hour, [24-54] day, [55-57] month
		sysopen (STATS,"/etc/csf/stats/lfdmain", O_RDWR | O_CREAT);
		flock (STATS, LOCK_EX);
		my @stats = <STATS>;
		chomp @stats;

		my $perm = $report[2];
		my $trigger = $report[7];
		my $time = time;
		my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time);
		my @line;
		my %triggers;
		my $permdate;
		my $permcount;
		my $tempdate;
		my $tempcount;
		my $loop;

		@line = split(/\,/,$stats[$hour]);
		$permdate = $line[0];
		$permcount = $line[1];
		$tempdate = $line[2];
		$tempcount = $line[3];
		for ($loop = 4; $loop < @line; $loop+=2) {
			if ($time - $line[$loop] > (24 * 60 * 60)) {next}
			my ($triggerstat,$triggercount) = split(/\:/,$line[$loop+1]);
			$triggers{$triggerstat}{date} = $line[$loop];
			$triggers{$triggerstat}{count} = $triggercount;
		}
		$triggers{$trigger}{date} = $time;
		$triggers{$trigger}{count}++;
		if ($time - $permdate > (24 * 60 * 60)) {$permdate = 0; $permcount = 0}
		if ($time - $tempdate > (24 * 60 * 60)) {$tempdate = 0; $tempcount = 0}
		if ($perm) {$permdate = $time; $permcount++} else {$tempdate = $time; $tempcount++}
		$stats[$hour] = "$permdate,$permcount,$tempdate,$tempcount";
		foreach my $key (keys %triggers) {$stats[$hour] .= ",$triggers{$key}{date},$key:$triggers{$key}{count}"}
		
		@line = split(/\,/,$stats[$mday+24]);
		undef %triggers;
		$permdate = $line[0];
		$permcount = $line[1];
		$tempdate = $line[2];
		$tempcount = $line[3];
		for ($loop = 4; $loop < @line; $loop+=2) {
			if ($time - $line[$loop] > (29 * 24 * 60 * 60)) {next}
			my ($triggerstat,$triggercount) = split(/\:/,$line[$loop+1]);
			$triggers{$triggerstat}{date} = $line[$loop];
			$triggers{$triggerstat}{count} = $triggercount;
		}
		$triggers{$trigger}{date} = $time;
		$triggers{$trigger}{count}++;
		if ($time - $permdate > (29 * 24 * 60 * 60)) {$permdate = 0; $permcount = 0}
		if ($time - $tempdate > (29 * 24 * 60 * 60)) {$tempdate = 0; $tempcount = 0}
		if ($perm) {$permdate = $time; $permcount++} else {$tempdate = $time; $tempcount++}
		$stats[$mday+24] = "$permdate,$permcount,$tempdate,$tempcount";
		foreach my $key (keys %triggers) {$stats[$mday+24] .= ",$triggers{$key}{date},$key:$triggers{$key}{count}"}

		@line = split(/\,/,$stats[$mon+55]);
		undef %triggers;
		$permdate = $line[0];
		$permcount = $line[1];
		$tempdate = $line[2];
		$tempcount = $line[3];
		for ($loop = 4; $loop < @line; $loop+=2) {
			if ($time - $line[$loop] > (364 * 24 * 60 * 60)) {next}
			my ($triggerstat,$triggercount) = split(/\:/,$line[$loop+1]);
			$triggers{$triggerstat}{date} = $line[$loop];
			$triggers{$triggerstat}{count} = $triggercount;
		}
		$triggers{$trigger}{date} = $time;
		$triggers{$trigger}{count}++;
		if ($time - $permdate > (364 * 24 * 60 * 60)) {$permdate = 0; $permcount = 0}
		if ($time - $tempdate > (364 * 24 * 60 * 60)) {$tempdate = 0; $tempcount = 0}
		if ($perm) {$permdate = $time; $permcount++} else {$tempdate = $time; $tempcount++}
		$stats[$mon+55] = "$permdate,$permcount,$tempdate,$tempcount";
		foreach my $key (keys %triggers) {$stats[$mon+55] .= ",$triggers{$key}{date},$key:$triggers{$key}{count}"}

		if ($config{CC_LOOKUPS}) {
			my $cc = "**";
			my %ccs;
			@line = split(/\,/,$stats[68]);
			if ($report[5] =~ /\s\((\w\w)\//) {$cc = $1}
			for (my $x = 0; $x < @line; $x+=2) {$ccs{$line[$x]} = $line[$x+1]}
			$ccs{$cc}++;
			$stats[68] = "";
			foreach my $key (keys %ccs) {$stats[68] .= "$key,$ccs{$key},"}
		}

		seek (STATS, 0, 0);
		truncate (STATS, 0);
		foreach my $line (@stats) {
			print STATS "$line\n";
		}
		close (STATS);

		if ($config{DEBUG} >= 3) {$timer = &timer("stop","stats_report",$timer)}
		$0 = "lfd - (child) closing";
		exit;
	}
}
# end stats_report
###############################################################################
# start checkvps
sub checkvps {
	if (-e "/proc/user_beancounters" and !(-e "/proc/vz/version")) {
		open (INVPS, "</proc/user_beancounters");
		my @data = <INVPS>;
		close (INVPS);
		chomp @data;

		foreach my $line (@data) {
			if ($line =~ /^\s*numiptent\s+(\d*)\s+(\d*)\s+(\d*)\s+(\d*)/) {
				if ($1 > $4 - 10) {return "The VPS iptables rule limit (numiptent) is too low ($1/$4) - *IP not blocked*"}
			}
		}
	}
	return 0;
}
# end checkvps
###############################################################################
# start messenger
sub messenger {
	my $port = shift;
	my $user = shift;
	my $type = shift;

	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		$0 = "lfd $type messenger";

		my $server = IO::Socket::INET->new(LocalPort => $port, Type => SOCK_STREAM, Reuse => 1, Listen => $config{MESSENGER_CHILDREN}) or &childcleanup(__LINE__,"*Error* cannot open server on port $port: $!");

		open (IN, "<", "/etc/csf/messenger/index.".lc($type));
		my @message = <IN>;
		close (IN);
		chomp @message;

		my %images;
		if ($type eq "HTML") {
			opendir (DIR, "/etc/csf/messenger");
			foreach my $file (readdir(DIR)) {
				if ($file =~ /\.(gif|png|jpg)$/) {
					open (IN, "</etc/csf/messenger/$file");
					my @data = <IN>;
					close (IN);
					chomp @data;
					foreach my $line (@data) {
						$images{$file} .= "$line\n";
					}
				}
			}
			closedir (DIR);
		}

		if ($user ne "") {
			my (undef,undef,$uid,$gid) = getpwnam($user);
			if (($uid > 0) and ($gid > 0)) {
				$) = $gid;
				$> = $uid;
				chdir("/");
				if (($) != $gid) or ($> != $uid)) {
					&logfile("MESSENGER_USER unable to drop privileges - stopping $type Messenger");
					exit;
				}
			} else {
				&logfile("MESSENGER_USER invalid - stopping $type Messenger");
				exit;
			}
		} else {
			&logfile("MESSENGER_USER not set - stopping $type Messenger");
			exit;
		}

		my $loopcheck;
		while ($loopcheck < 1000) {
			$loopcheck++;
			while (my ($client, $c_addr) = $server->accept()) {
				$SIG{CHLD} = 'IGNORE';
				my $pid = fork;
				if ($pid == 0) {
					eval {
						local $SIG{__DIE__} = undef;
						local $SIG{'ALRM'} = sub {die};
						alarm(10);
						close $server;

						my($cport,$iaddr) = sockaddr_in($c_addr);
						my $peeraddress = inet_ntoa($iaddr);

						binmode $client;
						$|  = 1;
						my $firstline;

						if ($type eq "HTML") {
							sysread ($client,$firstline,1024);
							chomp $firstline;
						}
						if (($type eq "HTML") and ($firstline =~ /^GET\s+(\S*\/)?(\S*\.(gif|png|jpg))\s+/i)) {
							my $type = $3;
							if ($type eq "jpg") {$type = "jpeg"}
							print $client "HTTP/1.0 200 OK\r\n";
							print $client "Content-type: image/$type\r\n";
							print $client "\r\n";
							print $client $images{$2};
						} else {
							if ($type eq "HTML") {
								print $client "HTTP/1.0 200 OK\r\n";
								print $client "Content-type: text/html\r\n";
								print $client "\r\n";
							}
							foreach my $line (@message) {
								if ($line =~ /\[IPADDRESS\]/) {$line =~ s/\[IPADDRESS\]/$peeraddress/}
								if ($line =~ /\[HOSTNAME\]/) {$line =~ s/\[HOSTNAME\]/$hostname/}
								print $client "$line\r\n";
							}
						}
						shutdown ($client,2);
						close ($client);
						alarm(0);
						exit;
					};
					alarm(0);
					exit;
				}
			}
			exit;
		}
	}
}
# end messenger
###############################################################################
# start domessenger
sub domessenger {
	my $ip = shift;
	my $delete = shift;
	my $ports = shift;
	if ($ports eq "") {$ports = "$config{MESSENGER_HTML_IN},$config{MESSENGER_TEXT_IN}"}

	my $del = "-A";
	if ($delete eq "D") {$del = "-D"}

	my %textin;
	my %htmlin;
	foreach my $port (split(/\,/,$config{MESSENGER_HTML_IN})) {$htmlin{$port} = 1}
	foreach my $port (split(/\,/,$config{MESSENGER_TEXT_IN})) {$textin{$port} = 1}

	my $textports;
	my $htmlports;
	foreach my $port (split(/\,/,$ports)) {
		if ($htmlin{$port}) {
			if ($htmlports eq "") {$htmlports = "$port"} else {$htmlports .= ",$port"}
		}
		if ($textin{$port}) {
			if ($textports eq "") {$textports = "$port"} else {$textports .= ",$port"}
		}
	}
	if ($htmlports ne "") {
		&syscommand(__LINE__,"$config{IPTABLES} -t nat $del PREROUTING $ethdevin -p tcp -s $ip -m multiport --dports $htmlports -j REDIRECT --to-ports $config{MESSENGER_HTML}");
	}
	if ($textports ne "") {
		&syscommand(__LINE__,"$config{IPTABLES} -t nat $del PREROUTING $ethdevin -p tcp -s $ip -m multiport --dports $textports -j REDIRECT --to-ports $config{MESSENGER_TEXT}");
	}
}
# end domessenger
###############################################################################
# start ui
sub ui {
	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		$0 = "lfd UI";

		open (IN, "</etc/csf/uialert.txt");
		my @alert = <IN>;
		close (IN);
		chomp @alert;

		my $server = IO::Socket::SSL->new(
			LocalPort => $config{UI_PORT},
			Type => SOCK_STREAM,
			Reuse => 1,
			Listen => $config{UI_CHILDREN},
			SSL_server => 1,
			SSL_use_cert => 1,
			SSL_cipher_list => $config{UI_CIPHER},
			SSL_key_file => '/etc/csf/ui/server.key',
			SSL_cert_file => '/etc/csf/ui/server.crt',
#			SSL_ca_path => '/etc/ssl/certs',
#			SSL_verify_mode => 0x01,
		) or &childcleanup(__LINE__,"UI: *Error* cannot open server on port $config{UI_PORT}: ".IO::Socket::SSL->errstr);

		my $loopcheck;
		while ($loopcheck < 1000) {
			$loopcheck++;
			while (my $client = $server->accept()) {
				$SIG{CHLD} = 'IGNORE';
				my $pid = fork;
				if ($pid == 0) {
					eval {
						local $SIG{__DIE__} = undef;
						local $SIG{'ALRM'} = sub {die};
						alarm(30);
						close $server;

						our %FORM;
						our $myv;
						our $script;
						our $images;
						our $fileinc;

						my $input;
						my $session;
						my $file;
						my $cookie;
						my %header;
						my %fails;
						my $application = "csf";
						my $buffer;
						my $clientcnt;
						my $request;
						my @chars = ('0'..'9','a'..'z','A'..'Z');
						my $valid = "login";
						my $maxheader = 64;
						my $maxbody = 64 * 1024 * 1024;
						my $maxline = 1024 * 1024;
						my $peeraddress = $client->peerhost;

						if ($peeraddress =~ /^::ffff:(\d+\.\d+\.\d+\.\d+)$/) {$peeraddress = $1}
						if ($peeraddress eq "") {
							close ($client);
							alarm(0);
							exit;
						}
						$ENV{REMOTE_ADDR} = $peeraddress;

						if ($ips{$peeraddress}) {
							&logfile("UI: Login attempt from local IP address denied [$peeraddress]");
							if ($config{UI_ALERT} >= 4) {
								my @message;
								my $tip = &iplookup($peeraddress);
								foreach my $line (@alert) {
									$line =~ s/\[ip\]/$tip/ig;
									$line =~ s/\[alert\]/Login attempt from local IP/ig;
									$line =~ s/\[text\]/Login attempt from local IP address $tip - denied/ig;
									push @message, $line;
								}
								&sendmail(@message);
							}
							close ($client);
							alarm(0);
							exit;
						}

						if ($config{"UI_BAN"}) {
							open(UIBAN,"<","/etc/csf/ui/ui.ban");
							flock (UIBAN, LOCK_SH);
							my @records = <UIBAN>;
							chomp @records;
							close (UIBAN);
							foreach my $record (@records) {
								if ($record =~ /^(\#|\s|\r|\n)/) {next}
								my ($rip,undef) = split(/\s/,$record);
								if ($rip eq $peeraddress) {
									&logfile("UI: Access attempt from a banned IP address in /etc/csf/ui/ui.ban - denied [$peeraddress]");
									if ($config{UI_ALERT} >= 4) {
										my @message;
										my $tip = &iplookup($peeraddress);
										foreach my $line (@alert) {
											$line =~ s/\[ip\]/$tip/ig;
											$line =~ s/\[alert\]/Access attempt from banned IP/ig;
											$line =~ s/\[text\]/Access attempt from a banned IP $tip in \/etc\/csf\/ui\/ui\.ban - denied/ig;
											push @message, $line;
										}
										&sendmail(@message);
									}
									close ($client);
									alarm(0);
									exit;
								}
							}
						}

						if ($config{"UI_ALLOW"}) {
							my $allow = 0;
							sysopen(UIALLOW,"/etc/csf/ui/ui.allow", O_RDWR | O_CREAT);
							flock (UIALLOW, LOCK_SH);
							my @records = <UIALLOW>;
							chomp @records;
							close (UIALLOW);
							foreach my $record (@records) {
								if ($record =~ /^(\#|\s|\r|\n)/) {next}
								my ($rip,undef) = split(/\s/,$record);
								if ($rip eq $peeraddress) {
									$allow = 1;
									last;
								}
								my (undef,$cidr) = split(/\//,$rip);
								if ($cidr) {
									my $uicidr = Net::CIDR::Lite->new;
									eval {$uicidr->add($rip)};
									if ($uicidr->find($peeraddress)) {
										$allow = 1;
										last;
									}
								}
							}
							unless ($allow) {
								&logfile("UI: Access attempt from an IP not in /etc/csf/ui/ui.allow - denied [$peeraddress]");
								if ($config{UI_ALERT} >= 4) {
									my @message;
									my $tip = &iplookup($peeraddress);
									foreach my $line (@alert) {
										$line =~ s/\[ip\]/$tip/ig;
										$line =~ s/\[alert\]/Login attempt/ig;
										$line =~ s/\[text\]/Access attempt from an IP $tip not in \/etc\/csf\/ui\/ui\.allow - denied/ig;
										push @message, $line;
									}
									&sendmail(@message);
								}
								close ($client);
								alarm(0);
								exit;
							}
						}

						select $client;
						$|  = 1;

						$clientcnt = 0;
						while ($request !~ /\n$/) {
							my $char;
							$client->read($char,1);
							$request .= $char;
							$clientcnt++;
							if ($clientcnt > $maxline) {
								&ui_413;
								close ($client);
								alarm(0);
								exit;
							}
						}
						$request =~ s/\r\n$//;
						if ($request =~ /^(GET|POST)\s(\S+)\sHTTP/) {
							($file,undef) = split(/\?/,$2);
							if ($file =~ /^\/(\w+)(\/.*)/) {
								$session = $1;
								$file = $2;
							}
						} else {
							close ($client);
							alarm(0);
							exit;
						}
						my $linecnt;
						while (1) {
							my $line;
							$clientcnt = 0;
							while ($line !~ /\n$/) {
								my $char;
								$client->read($char,1);
								$line .= $char;
								$clientcnt++;
								if ($clientcnt > $maxline) {
									&ui_413;
									close ($client);
									alarm(0);
									exit;
								}
							}
							if ($line =~ /^\r\n$/) {last}
							$line =~ s/\r\n$//;
							my ($field,$value) = split(/\:\s/,$line);
							$header{$field} = $value;
							if ($config{DEBUG} >= 2) {&logfile("UI debug: header [$field] [$value]")}
							$linecnt++;
							if ($linecnt > $maxheader) {
								&ui_413;
								close ($client);
								alarm(0);
								exit;
							}
						}
						if ($header{'Content-Length'} > 0) {
							if ($header{'Content-Length'} > $maxbody) {
								&ui_413;
								close ($client);
								alarm(0);
								exit;
							} else {
								if ($header{'Content-Type'} =~ /multipart\/form-data/) {
									$client->read($fileinc,$header{'Content-Length'});
								} else {
									$client->read($buffer,$header{'Content-Length'});
								}
							}
						}
						if ($request =~ /^GET\s(\S+)\sHTTP/) {if ($1 =~ /\?([^\?]*)$/) {$buffer = $1}}
						if ($config{DEBUG} >= 2) {&logfile("UI debug: request [$request] buffer [$buffer]")}
						my @pairs = split(/&/,$buffer);
						foreach my $pair (@pairs) {
							my ($name, $value) = split(/=/, $pair);
							$value =~ tr/+/ /;
							$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
							$FORM{$name} = $value;
						}
						if ($header{Cookie} =~ /csfsession=(\w+)/) {$cookie = $1}

						if (($session ne "" and $cookie ne "") or defined $FORM{csflogin}) {
							sysopen(SESSION,"/etc/csf/ui/ui.session", O_RDWR | O_CREAT) or &childcleanup(__LINE__,"UI: unable to open csf.session: $!");
							flock (SESSION, LOCK_EX);
							my @records = <SESSION>;
							chomp @records;
							seek (SESSION, 0, 0);
							truncate (SESSION, 0);

							my $md5current = Digest::MD5->new;
							$md5current->add($header{'User-Agent'});
							my $md5sum = $md5current->b64digest;
							foreach my $record (@records) {
								my ($rtype,$rstart,$rtime,$rsession,$rcookie,$rip,$rhead,$rapp) = split(/\|/,$record,8);
								if ($rtype eq "login" and $rip eq $peeraddress and $rsession eq $session) {
									if ((time - $rtime) > $config{UI_TIMEOUT}) {
										$valid = "login";
										$record = "";
										($rstart,$rtime,$rsession,$rcookie,$rip,$rhead) = "";
										&logfile("UI: *Invalid session* $peeraddress [timeout]");
									}
									elsif ($rcookie eq $cookie) {
										if ($rhead eq $md5sum) {
											if ($FORM{csfaction} eq "csflogout") {
												$valid = "login";
												$record = "";
												&logfile("UI: Successful logout from $peeraddress");
											} else {
												$valid = "session";
												$application = $rapp;
												$rtime = time;
												$record = "$rtype|$rstart|$rtime|$rsession|$rcookie|$rip|$rhead|$rapp";
											}
										} else {
											$valid = "fail";
											$record = "";
											&logfile("UI: *Invalid session* $peeraddress [session-header]");
										}
									} else {
										$valid = "fail";
										$record = "";
										&logfile("UI: *Invalid session* $peeraddress [session-cookie]");
									}
								} else {
									if ($rtype eq "login") {
										if ((time - $rtime) > $config{UI_TIMEOUT}) {
											$record = "";
											($rstart,$rtime,$rsession,$rcookie,$rip,$rhead) = "";
										}
									}
									elsif ($rtype eq "fail") {
										if ((time - $rstart) > 86400) {
											$record = "";
										} else {
											$fails{$rip}++;
										}
									}
								}
								if ($record ne "") {print SESSION "$record\n"}
							}
							close (SESSION);
						} else {
							$valid = "login";
						}
						if (defined $FORM{csflogin} and $valid ne "fail") {
							if ($FORM{csflogin} eq $config{UI_USER} and $FORM{csfpassword} eq $config{UI_PASS}) {
								$valid = "yes";
							} else {
								$valid = "fail";
							}
						}

						if ($valid eq "fail") {
							$fails{$peeraddress}++;
							if ($fails{$peeraddress} > $config{UI_RETRY}) {
								if ($config{UI_BAN}) {
									sysopen(SESSIONBAN,"/etc/csf/ui/ui.ban", O_WRONLY | O_APPEND | O_CREAT) or &childcleanup(__LINE__,"UI: unable to open csf.session: $!");
									flock (SESSIONBAN, LOCK_EX);
									print SESSIONBAN "$peeraddress - Banned for too many login failures ".localtime()."\n";
									close (SESSIONBAN);
									&logfile("UI: *Invalid login* attempts from $peeraddress [$fails{$peeraddress}/$config{UI_RETRY}] - Banned in /etc/csf/ui/ui.ban");
								} else {
									&logfile("UI: *Invalid login* attempts from $peeraddress [$fails{$peeraddress}/$config{UI_RETRY}] - Not Banned");
								}
								sysopen(SESSION,"/etc/csf/ui/ui.session", O_RDWR | O_CREAT) or &childcleanup(__LINE__,"UI: unable to open csf.session: $!");
								flock (SESSION, LOCK_EX);
								my @records = <SESSION>;
								chomp @records;
								seek (SESSION, 0, 0);
								truncate (SESSION, 0);
								foreach my $record (@records) {
									my ($rtype,$rstart,$rtime,$rsession,$rcookie,$rip,$rhead,$rapp) = split(/\|/,$record,8);
									if ($rip eq $peeraddress) {next}
									print SESSION "$record\n"
								}
								close (SESSION);
								if ($config{UI_BLOCK}) {
									my $perm = 0;
									if ($config{UI_BLOCK} == 1) {$perm = 1}
									my $tip = &iplookup($peeraddress);
									&ipblock("1","UI: Invalid login attempts from $tip",$peeraddress,"","in",$config{UI_BLOCK},0,"UI: *Invalid login* attempts from $peeraddress [$fails{$peeraddress}/$config{UI_RETRY}] - Banned","UI_RETRY");
								}
								if ($config{UI_ALERT} >= 1) {
									my @message;
									my $tip = &iplookup($peeraddress);
									my $text;
									if ($config{UI_BAN}) {$text .= "Banned in ui.ban"}
									if ($config{UI_BLOCK}) {
										if ($text ne "") {$text .= ", "}
										$text .= "Blocked in csf";
									}
									foreach my $line (@alert) {
										$line =~ s/\[ip\]/$tip/ig;
										$line =~ s/\[alert\]/Login failure \[$fails{$peeraddress}\/$config{UI_RETRY}]/ig;
										$line =~ s/\[text\]/Login failure from IP address $tip \[$fails{$peeraddress}\/$config{UI_RETRY}] - $text/ig;
										push @message, $line;
									}
									&sendmail(@message);
								}
								&ui_403;
								close ($client);
								alarm(0);
								exit;
							} else {
								my $time = time;
								sysopen(SESSION,"/etc/csf/ui/ui.session", O_WRONLY | O_APPEND | O_CREAT) or &childcleanup(__LINE__,"UI: unable to open csf.session: $!");
								flock (SESSION, LOCK_EX);
								print SESSION "fail|$time||||$peeraddress||\n";
								close (SESSION);
								$valid = "login";
								&logfile("UI: *Invalid login* attempt from $peeraddress [$fails{$peeraddress}/$config{UI_RETRY}]");
								if ($config{UI_ALERT} >= 2) {
									my @message;
									my $tip = &iplookup($peeraddress);
									foreach my $line (@alert) {
										$line =~ s/\[ip\]/$tip/ig;
										$line =~ s/\[alert\]/Login failure \[$fails{$peeraddress}\/$config{UI_RETRY}]/ig;
										$line =~ s/\[text\]/Login failure from IP address $tip \[$fails{$peeraddress}\/$config{UI_RETRY}] - denied/ig;
										push @message, $line;
									}
									&sendmail(@message);
								}
							}
						}
						if ($valid eq "yes") {
							srand;
							$session = join '', map {$chars[rand(@chars)]} (1..(15 + int(rand(15))));
							$cookie = join '', map {$chars[rand(@chars)]} (1..(15 + int(rand(15))));

							my $md5current = Digest::MD5->new;
							$md5current->add($header{'User-Agent'});
							my $md5sum = $md5current->b64digest;
							my $time = time;
							sysopen(SESSION,"/etc/csf/ui/ui.session", O_RDWR | O_CREAT) or &childcleanup(__LINE__,"UI: unable to open csf.session: $!");
							flock (SESSION, LOCK_EX);
							my @records = <SESSION>;
							chomp @records;
							seek (SESSION, 0, 0);
							truncate (SESSION, 0);
							foreach my $record (@records) {
								my ($rtype,$rstart,$rtime,$rsession,$rcookie,$rip,$rhead) = split(/\|/,$record,8);
								if ($rtype eq "fail" and $rip eq $peeraddress) {next}
								print SESSION "$record\n"
							}
							print SESSION "login|$time|$time|$session|$cookie|$peeraddress|$md5sum|$application\n";
							close (SESSION);

							print "HTTP/1.0 301 Moved Permanently\r\n";
							print "Location: /$session/\r\n";
							print "Set-Cookie: csfsession=$cookie; secure\r\n";
							print "\r\n";

							&logfile("UI: Successful login from $peeraddress");
							if ($config{UI_ALERT} >= 3) {
								my @message;
								my $tip = &iplookup($peeraddress);
								foreach my $line (@alert) {
									$line =~ s/\[ip\]/$tip/ig;
									$line =~ s/\[alert\]/Login success/ig;
									$line =~ s/\[text\]/Login success from IP address $tip/ig;
									push @message, $line;
								}
								&sendmail(@message);
							}
						}
						if ($valid eq "login") {
							print "HTTP/1.0 200 OK\r\n";
							print "Content-type: text/html\r\n";
							print "\r\n";
							print "<HTML>\n<TITLE>ConfigServer Security & Firewall</TITLE>\n<BODY style='font-family:Arial, Helvetica, sans-serif;' onload='document.getElementById(\"user\").focus()'>\n";
							if ($valid eq "failed") {print "<div align='center'><h2>Login Failed</h2></div>\n"}
							print "<form action='/' method='post'><div align='center'>\n";
							print "<table align='center' border='0' cellspacing='0' cellpadding='4' bgcolor='#FFFFFF' style='border:1px solid #990000'>\n";
							print "<tr bgcolor='#F4F4EA'><td>Username:</td><td><input id='user' name='csflogin' type='text' size='15'></td></tr>\n";
							print "<tr bgcolor='#F4F4EA'><td>Password:</td><td><input name='csfpassword' type='password' size='15'></td></tr>\n";
							print "<tr bgcolor='#FFFFFF'><td colspan='2' align='center'><input type='submit' value='Enter'></td></tr>\n";
							print "</table></div></form>\n";
							print "\n</BODY>\n</HTML>\n";
						}
						if ($valid eq "session") {
							if (defined $FORM{csfapp} and ($FORM{csfapp} ne $application)) {
								my $newapp = $application;
								if ($FORM{csfapp} eq "csf") {$newapp = "csf"}
								elsif ($FORM{csfapp} eq "cxs" and $config{UI_CXS}) {$newapp = "cxs"}
								elsif ($FORM{csfapp} eq "cse" and $config{UI_CSE}) {$newapp = "cse"}
								if ($newapp ne $application) {
									sysopen(SESSION,"/etc/csf/ui/ui.session", O_RDWR | O_CREAT) or &childcleanup(__LINE__,"UI: unable to open csf.session: $!");
									flock (SESSION, LOCK_EX);
									my @records = <SESSION>;
									chomp @records;
									seek (SESSION, 0, 0);
									truncate (SESSION, 0);
									foreach my $record (@records) {
										my ($rtype,$rstart,$rtime,$rsession,$rcookie,$rip,$rhead,$rapp) = split(/\|/,$record,8);
										if ($rip eq $peeraddress and $rsession eq $session) {
											$record = "$rtype|$rstart|$rtime|$rsession|$rcookie|$rip|$rhead|$newapp";
											$application = $newapp;
										}
										print SESSION "$record\n"
									}
									close (SESSION);
								}
							}
							if ($file eq "/") {
								print "HTTP/1.0 200 OK\r\n";
								if ($application eq "csf") {
									print "Content-type: text/html\r\n";
									print "\r\n";
									print "<HTML>\n<TITLE>ConfigServer Security & Firewall</TITLE>\n";
									print "<style>\n";
									print "td {font-family:Arial, Helvetica, sans-serif;font-size:small}\n";
									print "body {font-family:Arial, Helvetica, sans-serif;font-size:small}\n";
									print "pre {font-size: medium}\n";
									print "</style>\n<BODY>\n";
									open (IN, "</etc/csf/version.txt") or die $!;
									$myv = <IN>;
									close (IN);
									chomp $myv;
									$script = "/$session/";
									$images = "/$session/images";
									$config{THIS_UI} = 1;
									print "<table border='0' cellpadding='4' cellspacing='0' width='95%'><tr>\n";
									print "<td valign='top' width='100%' nowrap style='font-size: medium'><img src='$images/csf_small.png' align='absmiddle' /> <b>ConfigServer Security & Firewall - csf v$myv</b>&nbsp;&nbsp;&nbsp;&nbsp;</td>\n";
									if ($config{UI_CXS} or $config{UI_CSE}) {
										print "<td valign='top' nowrap style='font-size: medium'><form action='$script' method='post'><select name='csfapp'><option selected>csf</option>";
										if ($config{UI_CXS}) {print "<option>cxs</option>"}
										if ($config{UI_CSE}) {print "<option>cse</option>"}
										print "</select> <input type='submit' value='Switch'></form>&nbsp;&nbsp;&nbsp;&nbsp;</td>\n";
									}
									print "<td valign='top' nowrap style='font-size: medium'>[<a href='/$session/?csfaction=csflogout'>csf Logout</a>]&nbsp;&nbsp;&nbsp;&nbsp;</td>\n";
									print "</tr></table>\n";
									do "/etc/csf/csfui.pl";
								}
								elsif ($application eq "cxs" and $config{UI_CXS}) {
									print "Content-type: text/html\r\n";
									print "\r\n";
									print "<HTML>\n<TITLE>ConfigServer eXploit Scanner</TITLE>\n";
									print "<style>\n";
									print "td {font-family:Arial, Helvetica, sans-serif;font-size:small}\n";
									print "body {font-family:Arial, Helvetica, sans-serif;font-size:small}\n";
									print "pre {font-size: medium}\n";
									print "</style>\n<BODY>\n";
									my ($childin, $childout);
									my $pid = open3($childin, $childout, $childout, "/usr/sbin/cxs --version");
									my @data = <$childout>;
									waitpid ($pid, 0);
									chomp @data;
									if ($data[0] =~ /v(.*)$/) {$myv = $1}
									$script = "/$session/";
									$images = "/$session/images";
									$config{THIS_UI} = 1;
									print "<table border='0' cellpadding='4' cellspacing='0' width='95%'><tr>\n";
									print "<td valign='top' width='100%' nowrap style='font-size: medium'><img src='$images/cxs_small.png' align='absmiddle' /> <b>ConfigServer eXploit Scanner - cxs v$myv</b>&nbsp;&nbsp;&nbsp;&nbsp;</td>\n";
									if ($config{UI_CXS} or $config{UI_CSE}) {
										print "<td valign='top' nowrap style='font-size: medium'><form action='$script' method='post'><select name='csfapp'><option>csf</option>";
										if ($config{UI_CXS}) {print "<option selected>cxs</option>"}
										if ($config{UI_CSE}) {print "<option>cse</option>"}
										print "</select> <input type='submit' value='Switch'></form>&nbsp;&nbsp;&nbsp;&nbsp;</td>\n";
									}
									print "<td valign='top' nowrap style='font-size: medium'>[<a href='/$session/?csfaction=csflogout'>cxs Logout</a>]</td>\n";
									print "</tr></table>\n";
									do "/etc/cxs/cxsui.pl";
								}
								elsif ($application eq "cse" and $config{UI_CSE}) {
									$script = "/$session/";
									$images = "/$session/images";
									$config{THIS_UI} = 1;
									do "/etc/csf/cseui.pl";
								}
							}
							elsif ($file =~ /^\/images\/([\w\-]+\.(gif|png|jpg))/i) {
								my $type = $2;
								if ($type eq "jpg") {$type = "jpeg"}
								print "HTTP/1.0 200 OK\r\n";
								print "Content-type: image/$type\r\n";
								print "\r\n";
								open (IMAGE, "<", "/etc/csf/ui/images/$1");
								while (<IMAGE>) {print $_}
								close (IMAGE);
							} else {
								&ui_403;
							}
						}
						close ($client);
						alarm(0);
						exit;
					};
					alarm(0);
					exit;
				}
			}
		}
		exit;
	}
}
# end ui
###############################################################################
# ui_403
sub ui_403 {
	print "HTTP/1.0 403 Forbidden\r\n";
	print "Content-type: text/html\r\n";
	print "\r\n";
	print "<html>\n<head>\n<title>403 Forbidden</title>\n</head>\n<body>\n";
	print "<h1>403 Forbidden</h1>\n";
	print "You don't have permission to access this resource\n";
	print "</body>\n</html>\n";
}
# end ui_403
###############################################################################
# ui_413
sub ui_413 {
	print "HTTP/1.0 413 Request Entity Too Large\r\n";
	print "Content-type: text/html\r\n";
	print "\r\n";
	print "<html>\n<head>\n<title>413 Request Entity Too Large</title>\n</head>\n<body>\n";
	print "<h1>413 Request Entity Too Large</h1>\n";
	print "The Request Data is too large for this server to handle";
	print "</body>\n</html>\n";
}
# end ui_413
###############################################################################
# start lfdserver
sub lfdserver {
	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $cipher = Crypt::CBC->new( -key => $config{CLUSTER_KEY}, -cipher => 'Blowfish_PP');
		my %cmembers;
		foreach my $cip (split(/\,/,$config{CLUSTER_RECVFROM})) {$cmembers{$cip} = 1}
		if ($config{CLUSTER_MASTER}) {$cmembers{$config{CLUSTER_MASTER}} = 1}

		$0 = "lfd Cluster Server";

		my $server = IO::Socket::INET->new(
			LocalPort => $config{CLUSTER_PORT},
			Type => SOCK_STREAM,
			Reuse => 1,
			Listen => $config{CLUSTER_CHILDREN},
		) or &childcleanup(__LINE__,"*Error* cannot open server on port $config{CLUSTER_PORT}: $!");

		my $loopcheck;
		while ($loopcheck < 1000) {
			$loopcheck++;
			while (my ($client, $c_addr) = $server->accept()) {
				$SIG{CHLD} = 'IGNORE';
				my $pid = fork;
				if ($pid == 0) {
					eval {
						local $SIG{__DIE__} = undef;
						local $SIG{'ALRM'} = sub {die};
						alarm(10);
						close $server;

						my ($cport,$iaddr) = sockaddr_in($c_addr);
						my $peeraddress = inet_ntoa($iaddr);
						my $pip = &iplookup($peeraddress);

						if ($cmembers{$peeraddress}) {
							binmode $client;
							$|  = 1;
							my $line;
							while (<$client>) {$line .= $_}
							chomp $line;

							shutdown ($client,2);
							close ($client);
							alarm(0);

							my $decrypted = $cipher->decrypt($line);
							if ($config{DEBUG} >= 2) {&logfile("debug: Cluster member $peeraddress said [$decrypted]")}
							my ($command,$ip,$perm,$ports,$inout,$timeout) = split(/\s/,$decrypted);
							if ($perm eq "") {$perm = 1}
							if ($ports eq "*") {$ports = ""}
							my $tip;
							if (&checkip($ip)) {$tip = &iplookup($ip)}
							if ($command eq "D") {
								&ipblock($perm,"Cluster member $pip said, DENY $tip",$ip,$ports,$inout,$timeout,1,"","LF_CLUSTER");
							}
							elsif ($command eq "A" and &checkip($ip)) {
								&logfile("Cluster member $pip said, ALLOW $tip");
								&syscommand(__LINE__,"/etc/csf/csf.pl -a $ip 'Cluster member $pip said, ALLOW $tip'");
							}
							elsif ($command eq "R" and &checkip($ip)) {
								&logfile("Cluster member $pip said, REMOVE $tip");
								&syscommand(__LINE__,"/etc/csf/csf.pl -dr $ip");
								&syscommand(__LINE__,"/etc/csf/csf.pl -tr $ip");
							}
							elsif ($command eq "PING") {
								&logfile("Cluster member $pip said PING!");
							}
							elsif ($command eq "C") {
								my (undef,$name,$value) = split(/\s/,$decrypted,3);
								if ($config{CLUSTER_MASTER} and ($config{CLUSTER_MASTER} eq $peeraddress)) {
									$value =~ s/\"|\=//g;
									$value =~ s/(^\s*)|(\s*$)//g;
									if ($config{CLUSTER_CONFIG}) {
										&logfile("Cluster member $pip said set [$name = \"$value\"]");
										&updateconfig($name,$value);
									} else {
										&logfile("Cluster member $pip said set [$name = \"$value\"], however CLUSTER_CONFIG disabled");
									}
								} else {
									&logfile("*Cluster* member $pip said set [$name = \"$value\"], however it is not the CLUSTER_MASTER");
								}
							}
							elsif ($command eq "FILE") {
								my (undef,$filename) = split(/\s/,$decrypted);
								my ($file, $filedir) = fileparse($filename);
								if ($config{CLUSTER_MASTER} and ($config{CLUSTER_MASTER} eq $peeraddress)) {
									if ($config{CLUSTER_CONFIG}) {
										my (undef,$content) = split(/\n/,$decrypted,2);
										&logfile("Cluster member $pip said store file [$file]");
										open (FH, ">", "/etc/csf/$file");
										binmode (FH);
										print FH $content;
										close (FH);
									} else {
										&logfile("*Cluster* member $pip said store file [$file], however CLUSTER_CONFIG disabled");
									}
								} else {
									&logfile("*Cluster* member $pip said store file [$file], however it is not the CLUSTER_MASTER");
								}
							}
							elsif ($command eq "RESTART") {
								if ($config{CLUSTER_MASTER} and ($config{CLUSTER_MASTER} eq $peeraddress)) {
									if ($config{CLUSTER_CONFIG}) {
										&logfile("Cluster member $pip said restart csf and lfd");
										&logfile("Cluster - csf restarting...");
										&syscommand(__LINE__,"/etc/csf/csf.pl -sf");
										&logfile("Cluster - lfd restarting...");
										open (LFDOUT, ">", "/etc/csf/lfd.restart");
										close (LFDOUT);
									} else {
										&logfile("*Cluster* member $pip said restart csf and lfd, however CLUSTER_CONFIG disabled");
									}
								} else {
									&logfile("*Cluster* member $pip said restart csf and lfd, however it is not the CLUSTER_MASTER");
								}
							}
							else {
								&logfile("*WARNING* Cluster member $pip talking nonsense");
							}
						} else {
							shutdown ($client,2);
							close ($client);
							alarm(0);
							&logfile("*WARNING* $pip attempted to connect to the Cluster but not a member!");
						}
						exit;
					};
					alarm(0);
					exit;
				}
			}
			exit;
		}
	}
}
# end lfdserver
###############################################################################
# start lfdclient
sub lfdclient {
	my $perm = shift;
	my $message = shift;
	my $ip = shift;
	my $port  = shift;
	my $inout = shift;
	my $timeout = shift;
	if ($port eq "") {$port = "*"}

	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","lfdclient",$timer)}
		$0 = "lfd - Cluster client";

		my $cipher = Crypt::CBC->new( -key => $config{CLUSTER_KEY}, -cipher => 'Blowfish_PP');
		my $text = "D $ip $perm $port $inout $timeout";
		my $encrypted = $cipher->encrypt($text);;

		foreach my $cip (split(/\,/,$config{CLUSTER_SENDTO})) {
			if ($ips{$cip} or $ipscidr->find($cip) or $ipscidr6->find($cip) or ($cip eq $config{CLUSTER_NAT})) {next}
			my $localaddr = "0.0.0.0";
			if ($config{CLUSTER_LOCALADDR}) {$localaddr = $config{CLUSTER_LOCALADDR}}
			my $tip = &iplookup($cip);
			my $sock;
			eval {$sock = IO::Socket::INET->new(PeerAddr => $cip, PeerPort => $config{CLUSTER_PORT}, LocalAddr => $localaddr, Timeout => '10');};
			unless (defined $sock) {
				&logfile("Cluster: Failed to connect to $tip");
			} else {
				my $status = send($sock,$encrypted,0);
				unless ($status) {
					&logfile("Cluster: Failed for $tip - $status");
				} else {
					&logfile("Cluster: DENY $ip sent to $tip");
				}
				shutdown($sock,2);
			}
		}
		if ($config{DEBUG} >= 3) {$timer = &timer("stop","lfdclient",$timer)}
		$0 = "lfd - (child) closing";
		exit;
	}
}
# end lfdclient
###############################################################################
# start updateconfig
sub updateconfig {
	my $chname = shift;
	my $chvalue = shift;

	sysopen (OUT, "/etc/csf/csf.conf", O_RDWR | O_CREAT);
	flock (OUT, LOCK_EX);
	my @confdata = <OUT>;
	chomp @confdata;
	seek (OUT, 0, 0);
	truncate (OUT, 0);
	for (my $x = 0; $x < @confdata;$x++) {
		if (($confdata[$x] !~ /^\#/) and ($confdata[$x] =~ /=/)) {
			my ($name,$value) = split (/=/,$confdata[$x]);
			$name =~ s/\s*//g;
			if ($name eq $chname) {
				print OUT "$name = \"$chvalue\"\n";
			} else {
				print OUT "$confdata[$x]\n";
			}
		} else {
			print OUT "$confdata[$x]\n";
		}
	}
	close (OUT);
}
# end updateconfig
###############################################################################
# start stats
sub stats {
	my $line = shift;
	my $type = shift;

	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","stats",$timer)}
		$0 = "lfd - (child) Statistics...";

		if ($type eq "iptables") {
			my ($in,$out,$src,$dst,$text);
			if ($line =~ /IN=(\S+)/) {$in = $1}
			if ($line =~ /OUT=(\S+)/) {$out = $1}
			if ($line =~ /SRC=(\S+)/) {$src = $1}
			if ($line =~ /DST=(\S+)/) {$dst = $1}

			if ($config{ST_LOOKUP}) {
				if ($in and $src) {$text = &iplookup($src)}
				elsif ($out and $dst) {$text = &iplookup($dst)}
			}

			sysopen (IPTABLES, "/etc/csf/stats/iptables_log", O_RDWR | O_CREAT);
			flock (IPTABLES, LOCK_EX);
			my @iptables = <IPTABLES>;
			chomp @iptables;
			push @iptables, "$text|$line";

			seek (IPTABLES, 0, 0);
			truncate (IPTABLES, 0);
			my $to = @iptables;
			my $from = 0;
			if ($to > $config{ST_IPTABLES}) {$from = $to - $config{ST_IPTABLES}}
			for (my $x = $from; $x < $to ;$x++) {
				print IPTABLES "$iptables[$x]\n";
			}
			close (IPTABLES);

			if ($config{DEBUG} >= 2) {&logfile("debug: STATS added iptables [$src -> $dst] log line")}
		}

		if ($config{DEBUG} >= 3) {$timer = &timer("stop","stats",$timer)}
		$0 = "lfd - (child) closing";
		exit;
	}
}
# end stats
###############################################################################
# start systemstats
sub systemstats {
	unless ($config{OLD_REAPER}) {$SIG{CHLD} = 'IGNORE';}
	unless (defined ($childpid = fork)) {
		&cleanup(__LINE__,"*Error* cannot fork: $!");
	} 
	unless ($childpid) {
		my $timer = time;
		if ($config{DEBUG} >= 3) {$timer = &timer("start","systemstats",$timer)}
		$0 = "lfd - (child) System Statistics...";
		local $SIG{__DIE__} = undef;
		
		my $time = time;
		my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time);
		my $cputotal;
		my $cpuidle;
		my $cpuiowait;
		my $memtotal;
		my $memfree;
		my $memswaptotal;
		my $memswapfree;
		my $netin;
		my $netout;
		my $diskread;
		my $diskwrite;
		my $mailin;
		my $mailout;
		my $cputemp;
		my $mysqlin;
		my $mysqlout;
		my $mysqlq;
		my $mysqlsq;
		my $mysqlcn;
		my $mysqlth;
		my $apachecpu;
		my $apacheacc;
		my $apachebwork;
		my $apacheiwork;

		open (IN, "<", "/proc/stat");
		my $line = <IN>;
		close (IN);
		chomp $line;
		my @cpu = split(/\s+/,$line);
		shift @cpu;
		foreach (@cpu) {$cputotal += $_}
		$cpuidle = $cpu[3];
		$cpuiowait = $cpu[4];

		open (IN, "<", "/proc/meminfo");
		my @memdata = <IN>;
		close (IN);
		chomp @memdata;
		foreach my $line (@memdata) {
			if ($line =~ /^MemTotal:\s+(\d+)\s+/) {$memtotal = $1}
			if ($line =~ /^MemFree:\s+(\d+)\s+/) {$memfree = $1}
			if ($line =~ /^SwapTotal:\s+(\d+)\s+/) {$memswaptotal = $1}
			if ($line =~ /^SwapFree:\s+(\d+)\s+/) {$memswapfree = $1}
		}

		open (IN, "<", "/proc/loadavg");
		my $loadavg = <IN>;
		close (IN);
		chomp $loadavg;
		my @load = split(/\s+/,$loadavg);

		opendir (DIR, "/sys/class/net");
		while (my $dir = readdir(DIR)) {
			if ($dir eq "." or $dir eq ".." or $dir eq "lo") {next}
			open (IN, "<", "/sys/class/net/$dir/operstate");
			my $state = <IN>;
			close (IN);
			chomp $state;
			if ($state ne "down") {
				open (IN, "<", "/sys/class/net/$dir/statistics/rx_bytes");
				my $datain = <IN>;
				close (IN);
				chomp $datain;
				$netin += $datain;
				open (IN, "<", "/sys/class/net/$dir/statistics/tx_bytes");
				my $dataout = <IN>;
				close (IN);
				chomp $dataout;
				$netout += $dataout;
			}
		}
		closedir (DIR);

		if (-e "/proc/diskstats") {
			open (IN, "<", "/proc/diskstats");
			my @diskdata = <IN>;
			close (IN);
			chomp @diskdata;
			foreach my $line (@diskdata) {
				my @item = split(/\s+/,$line);
				if ($item[3] =~ /^[[:alpha:]]+$/) {
					$diskread += $item[4];
					$diskwrite += $item[8];
				}
			}
			if ($diskread < 1) {
				foreach my $line (@diskdata) {
					my @item = split(/\s+/,$line);
					if ($item[3] =~ /^[[:alpha:]]+\d+$/) {
						$diskread += $item[4];
						$diskwrite += $item[8];
					}
				}
			}
		}

		if (-e "/sys/devices/platform/coretemp.0/temp1_input") {
			opendir (DIR, "/sys/devices/platform");
			while (my $dir = readdir(DIR)) {
				unless ($dir =~ /^coretemp/) {next}
				open (IN, "<", "/sys/devices/platform/$dir/temp1_input");
				my $temp = <IN>;
				close (IN);
				chomp $temp;
				if ($temp > $cputemp) {$cputemp = $temp}
			}
			closedir (DIR);
			$cputemp = sprintf("%.2f",$cputemp/1000)
		}

		sysopen (EMAIL, "/etc/csf/stats/email", O_RDWR | O_CREAT);
		flock (EMAIL, LOCK_EX);
		my $stats = <EMAIL>;
		chomp $stats;
		($mailout,$mailin) = split(/\:/,$stats);
		seek (EMAIL, 0, 0);
		truncate (EMAIL, 0);
		print EMAIL "0:0";
		close (EMAIL);

		if ($config{ST_MYSQL}) {
			eval ('use DBI');
			if ($@) {
				sysopen (TEMPCONF, "/etc/csf/csf.tempconf", O_WRONLY | O_APPEND | O_CREAT) or &childcleanup(__LINE__,"*Error* Cannot append out file: $!");
				flock (TEMPCONF, LOCK_EX);
				print TEMPCONF "ST_MYSQL = \"0\"\n";
				close (TEMPCONF);
				&logfile("STATS: DBI Perl Module missing - ST_MYSQL has been temporarily disabled. You should disable ST_MYSQL and restart lfd if you do not use this feature");
			} else {
				eval {
					local $SIG{__DIE__} = undef;
					local $SIG{'ALRM'} = sub {die};
					alarm(15);
					my $dbuser = $config{ST_MYSQL_USER};
					my $dbpass = $config{ST_MYSQL_PASS};
					my $dbhost = $config{ST_MYSQL_HOST};
					if ($dbpass eq "" and $dbuser eq "root") {
						open(DBS, "<", "/root/.my.cnf");
						while (<DBS>) {
							chomp;
							if (/^pass=(\S+)/) {
								$dbpass = $1;
								$dbpass =~ s/^\"|\"$//g;
							}
							if (/^host=(\S+)/) {
								$dbhost = $1;
								$dbhost =~ s/^\"|\"$//g;
							}
						}
					}
					my $status;
					my $dbh = DBI->connect("DBI:mysql:hostname=".$dbhost,$dbuser,$dbpass,{PrintError=>0}) or $status = $DBI::errstr;
					if ($status) {
						&logfile("STATS: Unable to connect to MySQL - $status. You should disable ST_MYSQL and restart lfd if you do not use this feature");
					} else {
						my $sth = $dbh->prepare('SHOW /*!50002 GLOBAL */ STATUS');
						$sth->execute();
						while(my ($key, $val) = $sth->fetchrow_array()) {
							if ($key eq "Bytes_received") {$mysqlin = $val}
							if ($key eq "Bytes_sent") {$mysqlout = $val}
							if ($key eq "Queries") {$mysqlq = $val}
							if ($key eq "Slow_queries") {$mysqlsq = $val}
							if ($key eq "Connections") {$mysqlcn = $val}
							if ($key eq "Threads_connected") {$mysqlth = $val}
						}
					}
					alarm(0);
				};
				alarm(0);
				if ($@) {&logfile("STATS: Timeout retrieving MySQL Status")}
			}
		}

		if ($config{ST_APACHE}) {
			eval {
				local $SIG{__DIE__} = undef;
				local $SIG{'ALRM'} = sub {die};
				alarm(15);
				my $url = $config{PT_APACHESTATUS}."?auto";
				my ($status, $apache) = &urlget($url);
				if ($status) {
					&logfile("STATS: Unable to retrieve Apache Server Status [$url] - $apache");
				} else {
					foreach my $line (split(/\n/,$apache)) {
						my ($item,$val) = split(/:\s*/,$line);
						if ($item eq "CPULoad") {$apachecpu = $val}
						if ($item eq "Total Accesses") {$apacheacc = $val}
						if ($item eq "BusyWorkers") {$apachebwork = $val}
						if ($item eq "IdleWorkers") {$apacheiwork = $val}
					}
				}
				alarm(0);
			};
			alarm(0);
			if ($@) {&logfile("STATS: Timeout retrieving Apache Server Status")}
		}

		sysopen (SYSSTAT,"/etc/csf/stats/system", O_WRONLY | O_APPEND | O_CREAT);
		flock (SYSSTAT, LOCK_EX);
		print SYSSTAT "$time,$cputotal,$cpuidle,$cpuiowait,$memtotal,$memfree,$memswaptotal,$memswapfree,$load[0],$load[1],$load[2],$netin,$netout,$diskread,$diskwrite,$mailin,$mailout,$cputemp,$mysqlin,$mysqlout,$mysqlq,$mysqlsq,$mysqlcn,$mysqlth,$apachecpu,$apacheacc,$apachebwork,$apacheiwork\n";
		close (SYSSTAT);

		if ($config{DEBUG} >= 3) {$timer = &timer("stop","systemstats",$timer)}
		$0 = "lfd - (child) closing";
		exit;
	}
}
# end systemstats
###############################################################################
# start allowip
sub allowip {
	my $ipmatch = shift;

	open (IN, "</etc/csf/csf.allow");
	flock (IN, LOCK_SH);
	my @allow = <IN>;
	close (IN);
	chomp @allow;
	foreach my $line (@allow) {
		if ($line =~ /^Include\s*(.*)$/) {
			my $incfile = $1;
			open (INCFILE, "<",$incfile) or &error(__LINE__,"Could not open $incfile: $!");
			flock (INCFILE, LOCK_SH) or &error(__LINE__,"Could not lock $incfile: $!");
			my @incallow = <INCFILE>;
			close (INCFILE) or &error(__LINE__,"Could not close $incfile: $!");
			chomp @incallow;
			push @allow,@incallow;
		}
	}
	foreach my $line (@allow) {
		if ($line eq "") {next}
		if ($line =~ /^\s*\#|Include/) {next}
		my ($ipd,$commentd) = split (/\s/,$line,2);
		if ($ipd eq $ipmatch) {
			return 1;
		}
		elsif ($ipd =~ /(\S+\/\d+)/) {
			my $cidrhit = $1;
			if (&checkip($cidrhit)) {
				my $dcidr = Net::CIDR::Lite->new;
				eval {$dcidr->add($cidrhit)};
				if ($@) {&logfile("Invalid CIDR in csf.allow: $cidrhit")}
				if ($dcidr->find($ipmatch)) {
					return 1;
				}
			}
		}
	}

	if ($config{GLOBAL_ALLOW} and -e "/etc/csf/csf.gallow") {
		open (IN, "</etc/csf/csf.gallow");
		flock (IN, LOCK_SH);
		my @allow = <IN>;
		close (IN);
		chomp @allow;
		foreach my $line (@allow) {
			if ($line eq "") {next}
			if ($line =~ /^\s*\#/) {next}
			my ($ipd,$commentd) = split (/\s/,$line,2);
			if ($ipd eq $ipmatch) {
				return 2;
			}
			elsif ($ipd =~ /(\S+\/\d+)/) {
				my $cidrhit = $1;
				if (&checkip($cidrhit)) {
					my $dcidr = Net::CIDR::Lite->new;
					eval {$dcidr->add($cidrhit)};
					if ($@) {&logfile("Invalid CIDR in csf.gallow: $cidrhit")}
					if ($dcidr->find($ipmatch)) {
						return 2;
					}
				}
			}
		}
	}
}
# end allowip
###############################################################################
# start sendmail
sub sendmail {
	my @message = @_;
	my $time = localtime(time);
	my $from = $config{LF_ALERT_FROM};
	my $to = $config{LF_ALERT_TO};
	my $data;

	if ($from =~ /([\w\.\=\-\_]+\@[\w\.\-\_]+)/) {$from = $1}
	if ($from eq "") {$from = "root"}
	if ($to =~ /([\w\.\=\-\_]+\@[\w\.\-\_]+)/) {$to = $1}
	if ($to eq "") {$to = "root"}

	my $header = 1;
	foreach my $line (@message) {
		$line =~ s/\r//;
		if ($line eq "") {$header = 0}
		$line =~ s/\[time\]/$time $tz/ig;
		$line =~ s/\[hostname\]/$hostname/ig;
		if ($header) {
			if ($line =~ /^To:(.*)$/i) {
				if ($config{LF_ALERT_TO} ne "") {
					$line =~ s/^To:.*$/To: $config{LF_ALERT_TO}/i;
				} else {
					$to = $1;
				}
			}
			if ($line =~ /^From:(.*)$/i) {
				if ($config{LF_ALERT_FROM} ne "") {
					$line =~ s/^From:.*$/From: $config{LF_ALERT_FROM}/i;
				} else {
					$from = $1;
				}
			}
		}
		$data .= $line."\n";
	}

	if ($config{LF_ALERT_SMTP} ne "") {
		if ($from !~ /\@/) {$from .= '@'.$hostname}
		if ($to !~ /\@/) {$to .= '@'.$hostname}
		my $smtp = Net::SMTP->new($config{LF_ALERT_SMTP}, Timeout => 10) or &logfile("Unable to send SMTP alert via [$config{LF_ALERT_SMTP}]: $!");
		if (defined $smtp) {
			$smtp->mail($from);
			$smtp->to($to);
			$smtp->data();
			$smtp->datasend($data);
			$smtp->dataend();
			$smtp->quit();
		}
	} else {
		open (MAIL, "|$config{SENDMAIL} -f $from -t") or &logfile("Unable to send SENDMAIL alert via [$config{SENDMAIL}]: $!");;
		print MAIL $data;
		close (MAIL);
	}
}
# end sendmail
###############################################################################
# start testregex
sub testregex {
	my $match = shift;
	eval {
		local $SIG{__DIE__} = undef;
		my $test =~ /$match/;
	};
	if ($@) {return 0}
	return 1;
}
# end testregex
###############################################################################

###############################################################################
# start urlget
sub urlget {
	my $url = shift;
	my $file = shift;
	my $status = 0;
	my $timeout = 1200;

	use LWP::UserAgent;
	my $ua = LWP::UserAgent->new;
	$ua->agent("csf/$version");
	$ua->timeout(300);
	my $req = HTTP::Request->new(GET => $url);
	my $res;
	my $text;

	($status, $text) = eval {
		local $SIG{__DIE__} = undef;
		local $SIG{'ALRM'} = sub {die "Download timeout after $timeout seconds"};
		alarm($timeout);
		if ($file) {
			$|=1;
			my $expected_length;
			my $bytes_received = 0;
			my $per = 0;
			my $oldper = 0;
			open (OUT, ">$file\.tmp") or return (1, "Unable to open $file\.tmp: $!");
			binmode (OUT);
			print "...0\%\n";
			$res = $ua->request($req,
				sub {
				my($chunk, $res) = @_;
				$bytes_received += length($chunk);
				unless (defined $expected_length) {$expected_length = $res->content_length || 0}
				if ($expected_length) {
					my $per = int(100 * $bytes_received / $expected_length);
					if ((int($per / 5) == $per / 5) and ($per != $oldper)) {
						print "...$per\%\n";
						$oldper = $per;
					}
				} else {
					print ".";
				}
				print OUT $chunk;
			});
			close (OUT);
			print "\n";
		} else {
			$res = $ua->request($req);
		}
		alarm(0);
		if ($res->is_success) {
			if ($file) {
				rename ("$file\.tmp","$file") or return (1, "Unable to rename $file\.tmp to $file: $!");
				return (0, $file);
			} else {
				return (0, $res->content);
			}
		} else {
			return (1, "Unable to download: ".$res->message);
		}
	};
	alarm(0);
	if ($@) {return (1, $@)}
	return ($status,$text);
}
# end urlget
###############################################################################
