#!/usr/bin/perl -w
#
# Description: Port Scan Attack Detector (psad)
# - uses ipchains/iptables logs.  Uses input from /var/log/psad/fwdata generated by
# kmsgsd
#
# version 0.8.6
# Copyright (C) 1999-2001 Michael B. Rash (mbr@cipherdyne.com)
# Thanks to Manuel Caphina for greatly assisting in the original version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# TODO: 
#	- add a danger level field to psad_signatures so that signatures
#	  will automatically be assigned the danger level in this field.
#	- make use of other logging options available in iptables to detect
#	  more tcp signatures.  (E.g. --log-tcp-options, --log-ip-options,
#	  --log-tcp-sequence, etc.).
#	- put source and destination ip addresses back into psad_signatures.
#	- make line counts of fwdata file take high-rate scans into
#	  account.
#	- ipfilter support on *BSD platforms.
#	- icmp/udp...
#	- signal handler for configuration updating. 
#	- use umask for newly created files.
#	- make CHECK_INTERVAL configurable from the command line.
#	- man page
#
# Sample packet (rejected by ipchains)
# Dec 19 11:54:07 orthanc kernel: Packet log: input REJECT lo PROTO=1
# 10.0.0.4:3127.0.0.1:3 L=88 S=0xC0 I=49513 F=0x0000 T=255
#
# Sample packet (rejected by iptables... --log-prefix = "DENY")
# Mar 11 13:15:52 orthanc kernel: DENY IN=lo OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:08:00
# SRC=127.0.0.1 DST=127.0.0.1 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=0 DF PROTO=TCP SPT=44847 
# DPT=35 WINDOW=32304 RES=0x00 SYN URGP=0
#
# There may be a bug in iptables, since occasionally log entries are generated 
# by a long port scan like this (note there is no 'DPT' field: 
#   Mar 16 23:50:25 orthanc kernel: DENY IN=lo OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:08:00 
#   SRC=127.0.0.1 DST=127.0.0.1 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=0 DF PROTO=TCP SPT=39935 
#   DINDOW=32304 RES=0x00 SYN URGP=0
#
#
#==================== config =======================
# Initialize config variables.  Some can be overriden with command line args
my $PSAD_LOGFILE = "/var/log/psad/scanlog";
my $FW_DATA = "/var/log/psad/fwdata";
my $ERROR_LOG = "/var/log/psad/fwerrorlog";
my $EMAIL_ALERTFILE = "/var/log/psad/email_alert";
$PRINT_SCAN_HASH = "/var/log/psad/scan_hash.$$";  # global for sub print_scan
my $CHECK_INTERVAL = 15;
my $PORT_RANGE_SCAN_THRESHOLD = 1;
my $ENABLE_PERSISTENCE = "Y";	# if "Y", means that scans will never timeout
my $SCAN_TIMEOUT = 3600;	# this is used only if $ENABLE_PERSISTENCE = "N";
my $SHOW_ALL_SIGNATURES = "N";	# if "Y", means that all tcp signatures will be shown since the scan started instead of just the current ones.
my %DANGER_LEVELS;
$DANGER_LEVELS{'1'} = 10;
$DANGER_LEVELS{'2'} = 50;
$DANGER_LEVELS{'3'} = 1000;
$DANGER_LEVELS{'4'} = 5000;
$DANGER_LEVELS{'5'} = 10000;
my $ENABLE_EMAIL_ALERTS = "Y";
my $EMAIL_ALERT_DANGER_LEVEL = 1;  # Send email alert if danger level >= to this value
my $EMAIL_ADDRESS = "root\@localhost";  # Email the alert to this address
my $ALERT_ALL = "Y";	# if "Y", send email for all new bad packets instead of just when a danger level increases
my $LONG_FORMAT = "Y";	# if "Y", specifies long logging/emailing format
my $ENABLE_AUTO_IDS = "N"; 	# if "Y", enable automated IDS response (auto manages firewall rulesets). 
my $AUTO_IDS_DANGER_LEVEL = 5;  # Block all traffic from offending IP if danger level >= to this value 

# system binaries
my $ipchainsCmd = "/sbin/ipchains";
my $iptablesCmd = "/usr/local/bin/iptables";
my $kmsgsdCmd = "/usr/local/bin/kmsgsd";
my $wcCmd = "/usr/bin/wc";
my $tailCmd = "/usr/bin/tail";
my $mailCmd = "/bin/mail";
my $touchCmd = "/bin/touch";
my $ifconfigCmd = "/sbin/ifconfig";
my $grepCmd = "/bin/grep";
my $psCmd = "/bin/ps";
my $netstatCmd = "/bin/netstat";
my $unameCmd = "/bin/uname";
my $hostnameCmd = "/bin/hostname";
my $whoisCmd = "/usr/local/bin/whois.psad";
#================== end config ====================
#===================== main =======================

$USE_IPCHAINS = 0;      # these two are global variables
$USE_IPTABLES = 0;

my $Scan_href;  # contains ips, port ranges, tcp flags, and danger levels
my $daemon = 0;
my $output = 0;
my $errors = 0;
my $dnslookups = 0;
my $whoislookups = 0;
my $signatures = 0;
my $auto_ips = 0;
my $netstat_lookup = 0;
my $fwcheck = 0;
my $dnsstring;

use File::stat;
use Getopt::Long;
use Data::Dumper;
use Socket;

my %Cmds = (
        "ipchains"      => $ipchainsCmd,
        "iptables"      => $iptablesCmd,
	"kmsgsd"	=> $kmsgsdCmd,
	"wc"		=> $wcCmd,
	"tail"		=> $tailCmd,
	"mail"		=> $mailCmd,
	"touch"		=> $touchCmd,
	"ifconfig"	=> $ifconfigCmd,
	"grep"		=> $grepCmd,
	"ps"		=> $psCmd,
	"netstat"	=> $netstatCmd,
	"uname"		=> $unameCmd,
	"hostname"	=> $hostnameCmd,
	"whois"		=> $whoisCmd
);

%Cmds = check_commands(\%Cmds);

$whoislookups = 1 if ($Cmds{'whois'} !~ /psad/);  # don't use the normal whois binary.

$SIG{USR1} = \&print_scan;  # install signal handler for debugging the %Scan hash with Data::Dumper

usage_and_exit(1) unless (GetOptions (
        'help'          => \$help,		# display help
	'auto_ips=s'	=> \$auto_ips,		# enable automatic ip danger level assignment
	'output'	=> \$output,		# write scanlog messages to STDOUT
	'daemon'	=> \$daemon,		# do not run as a daemon
	'firewallcheck'	=> \$fwcheck,		# do not check firewall rules
	'config=s'	=> \$config,		# specify configuration file
	'namelookups'	=> \$dnslookups,	# dns lookup against scanning ip address
	'signatures=s'	=> \$signatures,	# scan signatures
	'localport'	=> \$netstat_lookup,	# listening on localport that has been scanned?
	'errors'	=> \$errors,		# do not write malformed packet messages to error log
	'whois'		=> \$whoislookups
));
usage_and_exit(0) if ($help);

unless ($fwcheck) {
	unless (check_firewall_rules(\%Cmds)) {
        	die "*** After setting up your firewall per the above note, execute \"/etc/rc.d/init.d/psad-init start\" to start psad\n";
	}
}

fork and exit unless ($daemon);

if ($config) {
	open CONF, "< $config";
	eval $_ while(<CONF>);
	close CONF;
}	
unless ($errors) {
	open ERR, ">> $ERROR_LOG";
	print ERR "============= psad: pid $$ ===============\n";
	close ERR;
}
`$Cmds{'touch'} $FW_DATA` unless (-e $FW_DATA);
my $Sigs_href = import_signatures($signatures) if $signatures;
my $Auto_ips_href;
if ($auto_ips) {  # support automatic ip danger level increases/decreases.
	$Auto_ips_href = import_auto_ips($auto_ips);
} else {
	$Auto_ips_href = 0;
}
# main loop
for (;;) {
	check_kmsgsd($EMAIL_ADDRESS, \%Cmds);
	my $auto_ips_mtime_start = stat($auto_ips)->mtime if $auto_ips;
	my $sigs_mtime_start = stat($signatures)->mtime if $signatures;
	my $fwdata_start_lines = (split /\s+/, `$Cmds{'wc'} -l $FW_DATA`)[1];
#	print "Current number of lines: $fwdata_start_lines\n";   # this will be useful for the TODO
	sleep $CHECK_INTERVAL;
	my $fwdata_end_lines = (split /\s+/, `$Cmds{'wc'} -l $FW_DATA`)[1];
	if ($signatures) {
		my $sigs_mtime_end = stat($signatures)->mtime;
		if ($sigs_mtime_start != $sigs_mtime_end) {  # the signatures were update... import the new signatures
			$Sigs_href = import_signatures($signatures);
		}
	}
	if ($auto_ips) {
		my $auto_ips_mtime_end = stat($auto_ips)->mtime;
		if ($auto_ips_mtime_start != $auto_ips_mtime_end) {  # the auto ips file was updated... import the new ip/danger level pairs.
			$Auto_ips_href = import_auto_ips($auto_ips);
			$Scan_href = reset_auto_tags($Scan_href);  # need to set $Scan_href->{$ip}->{'AUTO'} = "N" since $Auto_ips_href was updated.
		}
	}
	if ($fwdata_end_lines - $fwdata_start_lines > 0) {
		my $grabnum = $fwdata_end_lines - $fwdata_start_lines;
		my @process_lines = `$Cmds{'tail'} -$grabnum $FW_DATA`;
#		print "Number of lines to process: $#process_lines\n";  # this will be useful for the TODO
		$Scan_href = check_scan(\@process_lines, $Sigs_href, $signatures, $netstat_lookup, $errors, 
									$ERROR_LOG, $ENABLE_PERSISTENCE, $SCAN_TIMEOUT, \%Cmds);
		$Scan_href = assign_danger_level($Scan_href, $Auto_ips_href, $PORT_RANGE_SCAN_THRESHOLD, $ALERT_ALL, \%DANGER_LEVELS);
		$Scan_href = logr($Scan_href, $PSAD_LOGFILE, $output, $dnslookups, $whoislookups, $ENABLE_EMAIL_ALERTS,
					$EMAIL_ALERT_DANGER_LEVEL, $EMAIL_ALERTFILE, $EMAIL_ADDRESS, $LONG_FORMAT, $SHOW_ALL_SIGNATURES, \%Cmds);
		if ($ENABLE_AUTO_IDS eq "Y") {
			$Scan_href = auto_psad_response($Scan_href, $AUTO_IDS_DANGER_LEVEL, \%Cmds);
		}
	}
}

#================== end main ======================
sub check_scan() {  # keeps track of scanning ip's, increments packet counters, keep track of tcp flags for each
		    # scan (iptables only)
	my ($process_lines_aref, $sigs_href, $signatures, $netstat_lookup, $errors, $error_log, $enable_persistence, $scan_timeout, $Cmds_href) = @_;
	my @bad_packets;
	my ($srcip, $dstip, $proto, $srcport, $dstport, $flags);
	# if necessary check which firewall (ipchains vs. iptables)
	check_fw($process_lines_aref->[0]) unless ($USE_IPCHAINS || $USE_IPTABLES);
	READPKT: foreach my $l (@$process_lines_aref) {
		chomp $l;
		if ($USE_IPTABLES) {
			# sometimes the log entry is messed up by iptables so we right it to the error log
			if ($l =~ /SRC\=(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\sDST\=(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s.+?PROTO\=(\w{3,4})\sSPT\=(\d+)\sDPT\=(\d+)\s.+?RES\S+\s(.+?)\s+URGP/) {
				 ($srcip, $dstip, $proto, $srcport, $dstport, $flags) = ($1,$2,$3,$4,$5,$6);
			} else {
				push @bad_packets, $l;
				next READPKT;
			}
			# double check packet
			unless ($flags =~ /SYN|FIN|URG|PSH/ && $flags !~ /LEN|TOS|PREC|PROTO|WINDOW|RES/) {
				push @bad_packets, $l;
                                next READPKT;
                        }
		} elsif ($USE_IPCHAINS) {
			# could implement source port checking here
			if ($l =~ /PROTO\=(\d+)\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\:(\d+)\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\:(\d+)/) {
				($proto, $srcip, $srcport, $dstip, $dstport) = ($1,$2,$3,$4,$5);
				$flags = "NONE";
			} else {
				push @bad_packets, $l;
                                next READPKT;
                        }
		}
		if ($enable_persistence eq "N") {
			$currtime = time();
			if (defined $Scan{$srcip}{'START_TIME'}{'EPOCH_SECONDS'}) {
				my $tmp = $currtime - $Scan{$srcip}{'START_TIME'}{'EPOCH_SECONDS'};
#  debug				print "$currtime - $Scan{$srcip}{'START_TIME'}{'EPOCH_SECONDS'} = $tmp >= $scan_timeout\n";
				if (($currtime - $Scan{$srcip}{'START_TIME'}{'EPOCH_SECONDS'}) >= $scan_timeout) {
					undef $Scan{$srcip};
				}
			}
		} 
		# hash initialization
		$Scan{$srcip}{'LOGR'} = "Y";
		$Scan{$srcip}{'ABSNUM'} = 0 unless (defined $Scan{$srcip});
		$Scan{$srcip}{'DSTIP'} = $dstip unless (defined $Scan{$srcip}{'DSTIP'});
		$Scan{$srcip}{'FLAGS'}{$flags} = 0 unless (defined $Scan{$srcip}{'FLAGS'}{$flags});
		$Scan{$srcip}{'AUTO'} = "N" unless (defined $Scan{$srcip}{'AUTO'});
		$Scan{$srcip}{'CURRENT_DANGER_LEVEL'} = 0 unless (defined $Scan{$srcip}{'CURRENT_DANGER_LEVEL'});
		unless (defined $Scan{$srcip}{'START_PORT'}) {  # this is the absolute starting port since the first packet was detected
			$Scan{$srcip}{'START_PORT'} = 65535; # make sure the initial start port is not too low
			$Scan{$srcip}{'END_PORT'} = 0; # make sure the initial end port is not too high
		}
		my $epoch_seconds = time() if ($enable_persistence eq "N");
		my @time = split / /, scalar localtime;   ### Get the current time as a nice ASCII string.
		pop @time; shift @time;    ### Get rid of the day and the year to make the time consistent with syslog
		my $time = join ' ', @time;
		unless (defined $Scan{$srcip}{'CURRENT_INTERVAL'}{'START_PORT'}) {  # this is the current starting port since $CHECK_INTERVAL
			$Scan{$srcip}{'CURRENT_INTERVAL'}{'START_TIME'} = $time;
                        $Scan{$srcip}{'CURRENT_INTERVAL'}{'START_PORT'} = 65535; # make sure the initial start port is not too low
                        $Scan{$srcip}{'CURRENT_INTERVAL'}{'END_PORT'} = 0; # make sure the initial end port is not too high
                }
		unless (defined $Scan{$srcip}{'START_TIME'}{'READABLE'}) {
			$Scan{$srcip}{'START_TIME'}{'READABLE'} = $time;
			$Scan{$srcip}{'START_TIME'}{'EPOCH_SECONDS'} = $epoch_seconds if ($enable_persistence eq "N");
		}
		$Scan{$srcip}{'END_TIME'} = $time;
		$Scan{$srcip}{'END_TIME'}{'EPOCH_SECONDS'} = $epoch_seconds if ($enable_persistence eq "N");
		# increment hash values
		$Scan{$srcip}{'ABSNUM'}++;
		$Scan{$srcip}{'FLAGS'}{$flags}++;
		# see if this port lies outside our current range
		($Scan{$srcip}{'START_PORT'}, $Scan{$srcip}{'END_PORT'}) = 
				check_range($dstport, $Scan{$srcip}{'START_PORT'}, $Scan{$srcip}{'END_PORT'});
		($Scan{$srcip}{'CURRENT_INTERVAL'}{'START_PORT'}, $Scan{$srcip}{'CURRENT_INTERVAL'}{'END_PORT'}) =
                                check_range($dstport, $Scan{$srcip}{'CURRENT_INTERVAL'}{'START_PORT'}, $Scan{$srcip}{'CURRENT_INTERVAL'}{'END_PORT'});
		if ($signatures && $USE_IPTABLES) {  # might try use ipchains also, but then cannot use tcp flags except for -y -l rules
			foreach my $msg (keys %$sigs_href) {
				my $dstport_criteria = 0;
				my $srcport_criteria = 0;
				if (check_port($sigs_href, $msg, $dstport, "DSTPORT") 
						&& check_port($sigs_href, $msg, $srcport, "SRCPORT")
								&& check_flags($sigs_href, $msg, $flags)) {	# tripped a tcp signature!
					my $listening_port = "";
					unless ($netstat_lookup) {
						if (`$Cmds_href->{'netstat'} -an |$Cmds_href->{'grep'} \"LISTEN\\b\" |$Cmds_href->{'grep'} \"\\:$dstport\\b\"`) {
							$listening_port = "YOUR MACHINE IS LISTENING ON PORT: $dstport";
						} else {
							$listening_port = "There is no server listening on port $dstport";
						}
					}
# including sp almost always changes the hash key: 	my $alert_string = "$msg  sp=$srcport, dp=$dstport, flags=$flags.  $listening_port";
					my $alert_string = "$msg  dp=$dstport, flags=$flags.  $listening_port";
					if (defined $Scan{$srcip}{'SIGMATCH'}{$alert_string}) {
						$Scan{$srcip}{'SIGMATCH'}{$alert_string}++;
					} else {
						$Scan{$srcip}{'SIGMATCH'}{$alert_string} = 1;
					}
					unless (defined $Scan{$srcip}{'CURRENT_SIGMATCH_START_TIME'}) {
						$Scan{$srcip}{'CURRENT_SIGMATCH_START_TIME'} = $time;
					}
					if (defined $Scan{$srcip}{'CURRENT_SIGMATCH'}{$alert_string}) {
                                                $Scan{$srcip}{'CURRENT_SIGMATCH'}{$alert_string}++;
                                        } else {
                                                $Scan{$srcip}{'CURRENT_SIGMATCH'}{$alert_string} = 1;
                                        }
				}
			} 
		}
	}
	collect_errors(\@bad_packets, $error_log) unless $errors;
	return \%Scan;
}
sub check_flags() {
	my ($sigs_href, $msg, $flags_to_check) = @_;
	my $msgflags = $sigs_href->{$msg}->{'FLAGS'};
	return 1 if ($msgflags eq "S" && $flags_to_check eq "SYN");
	return 1 if ($msgflags eq "F" && $flags_to_check eq "FIN");
	return 1 if ($msgflags =~ /SF|FS/ && $flags_to_check eq "SYN FIN");
	return 1 if ($msgflags =~ /UPF|UFP|FPU|FUP|PFU|PUF/o && $flags_to_check eq "URG PSH FIN");
	return 1 if ($msgflags =~ /UPSF|UPFS|UFPS|UFSP|USPF|USFP|PUSF|PUFS|PSFU|PSUF|PFSU|PFUS|FPSU|FPUS|FUPS|FUSP|FSUP|FSPU|SPFU|SPUF|SUPF|SUFP|SFPU|SFUP/o && $flags_to_check eq "URG PSH SYN FIN");
	return 0;
}
sub check_port() {
	my ($sigs_href, $msg, $port_to_check, $portdirection) = @_;
        if (defined $sigs_href->{$msg}->{$portdirection} && $sigs_href->{$msg}->{$portdirection} eq "any") {
        	return 1;
        } elsif (defined $sigs_href->{$msg}->{$portdirection} && $port_to_check == $sigs_href->{$msg}->{$portdirection}) {
        	return 1;
        } elsif (defined $sigs_href->{$msg}->{$portdirection}->{'START'}) {
        	my $start = $sigs_href->{$msg}->{$portdirection}->{'START'};
        	my $end = $sigs_href->{$msg}->{$portdirection}->{'END'};
        	return 1 if ($port_to_check >= $start && $port_to_check <= $end);
        } elsif (defined $sigs_href->{$msg}->{$portdirection}->{'NOT'}) {
        	return 1 if ($port_to_check != $sigs_href->{$msg}->{$portdirection}->{'NOT'});
        } elsif (defined $sigs_href->{$msg}->{$portdirection}->{'NEGSTART'}) {
        	my $start = $sigs_href->{$msg}->{$portdirection}->{'NEGSTART'};
        	my $end = $sigs_href->{$msg}->{$portdirection}->{'NEGEND'};
        	return 1 if ($port_to_check < $start || $port_to_check > $end);
       }
	return 0;
}
sub check_kmsgsd() {
	my $email_address = shift;
	my $Cmds_href = shift;
	unless (`$Cmds_href->{'ps'} -auxw |$Cmds_href->{'grep'} kmsgsd |$Cmds_href->{'grep'} -v grep`) {
		system "$Cmds_href->{'kmsgsd'}";   # restart kmsgsd
		my $hostname = `$Cmds_href->{'hostname'}`;
        	chomp $hostname;
		system "$Cmds_href->{'mail'} $email_address -s \"psad: restarted kmsgsd on $hostname\" < /dev/null";
	}
	return;
}
sub import_signatures() {
	my $sigfile = shift;
	my ($proto, $srcport, $dstport, $msg, $flags);
	my %Sigs;
	my ($start, $end, $tmpport);
	open SIGS, "< $sigfile" or die "Could not open the signatures file $sigfile: $!\n";
	my @sigs = <SIGS>;
	close SIGS;
	foreach my $s (@sigs) {
		chomp $s;
		next if ($s =~ /^#/);
		if ($s =~ /^(\w{3,4})\s+(\S+)\s+\-\>\s+(\S+)\s+msg:(\".*?\")\;\s+flags:\s?(\w+)\;/) {
			($proto, $srcport, $dstport, $msg, $flags) = ($1,$2,$3,$4,$5);
			$Sigs{$msg}{'PROTO'} = $proto;
			$Sigs{$msg}{'FLAGS'} = $flags;
			if ($srcport =~ /\:/ && $srcport !~ /\!/) {
				($start, $end) = split /:/, $srcport;
				$start = 1 if ($start eq '');
				$end = 65535 if ($end eq '');
				$Sigs{$msg}{'SRCPORT'}{'START'} = $start;
				$Sigs{$msg}{'SRCPORT'}{'END'} = $end;
			} elsif ($srcport =~ /\!/ && $srcport !~ /\:/) {
				$tmpport = (split /\!/, $srcport)[1];
				$Sigs{$msg}{'SRCPORT'}{'NOT'} = $tmpport;
			} elsif ($srcport =~ /\:/ && $srcport =~ /\!/) {
				($start, $end) = split /:/, $srcport;
                                $start = 1 if ($start !~ /\d/);
				$end = 65535 if ($end !~ /\d/);
				$Sigs{$msg}{'SRCPORT'}{'NEGSTART'} = $start;
                                $Sigs{$msg}{'SRCPORT'}{'NEGEND'} = $end;
			} else {
				$Sigs{$msg}{'SRCPORT'} = $srcport;
			}
			if ($dstport =~ /\:/ && $dstport !~ /\!/) {
                                ($start, $end) = split /:/, $dstport;
                                $start = 1 if ($start eq '');
                                $end = 65535 if ($end eq '');
                                $Sigs{$msg}{'DSTPORT'}{'START'} = $start;
                                $Sigs{$msg}{'DSTPORT'}{'END'} = $end;
                        } elsif ($dstport =~ /\!/ && $dstport !~ /\:/) {
                                $tmpport = (split /\!/, $dstport)[1];
                                $Sigs{$msg}{'DSTPORT'}{'NOT'} = $tmpport;
                        } elsif ($dstport =~ /\:/ && $dstport =~ /\!/) {
                                ($start, $end) = split /:/, $dstport;
                                $start = 1 if ($start !~ /\d/);
                                $end = 65535 if ($end !~ /\d/);
                                $Sigs{$msg}{'DSTPORT'}{'NEGSTART'} = $start;
                                $Sigs{$msg}{'DSTPORT'}{'NEGEND'} = $end;
			} else {
                                $Sigs{$msg}{'DSTPORT'} = $dstport;
                        }
		}
	}
	return \%Sigs;	
}
sub import_auto_ips() {
	my $auto_ips_file = shift;
	open AUTO, "< $auto_ips_file";
	my @lines = <AUTO>;
	close AUTO;
	foreach my $l (@lines) {
		next if ($l =~ /^#/);
		if ($l =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+([0-5]|\-1)/) {
			$Auto_ips{$1} = $2;
		}
	}
	return \%Auto_ips;
}
sub reset_auto_tags() {
	my $Scan_href = shift;
	foreach my $ip (keys %$Scan_href) {
		$Scan_href->{$ip}->{'AUTO'} = "N";
	}
	return $Scan_href;
}
sub check_range() {
	my ($port, $start, $end) = @_;
	$start = $port if ($port < $start);
	$end = $port if ($port > $end);
	return $start, $end;
}
sub assign_danger_level() {
	my ($Scan_href, $Auto_ips_href, $port_range_scan_threshold, $alert_all, $danger_levels_href) = @_;
	$Scan_href = automatic_ip_danger_assignment($Scan_href, $Auto_ips_href) if ($Auto_ips_href); 
	foreach my $ip (keys %$Scan_href) {
		my $absnum = $Scan_href->{$ip}->{'ABSNUM'};
		my $range = $Scan_href->{$ip}->{'END_PORT'} - $Scan_href->{$ip}->{'START_PORT'};
		# If a scan signature packet has been detected but no other packets are detected, assign a danger
		# level of 1.  (This should really look at the danger level field for each signature to assign a real
		# danger level that is based on the type of signature... see the TODO)
		if ($Scan_href->{$ip}->{'CURRENT_DANGER_LEVEL'} == 0 && defined $Scan_href->{$ip}->{'SIGMATCH'}) {
			$Scan_href->{$ip}->{'CURRENT_DANGER_LEVEL'} = 1;
		}
		# if $port_range_scan_threshold is >= 1, then psad will not assign a danger level to repeated packets to the same tcp port
		if ($absnum >= $danger_levels_href->{'1'} && $absnum < $danger_levels_href->{'2'} && $range >= $port_range_scan_threshold) {
			if ($Scan_href->{$ip}->{'CURRENT_DANGER_LEVEL'} < 1 && $Scan_href->{$ip}->{'CURRENT_DANGER_LEVEL'} != -1) {
				$Scan_href->{$ip}->{'ALERTED'} = "N";
				$Scan_href->{$ip}->{'CURRENT_DANGER_LEVEL'} = 1;
			}
		}
		if ($absnum >= $danger_levels_href->{'2'} && $absnum < $danger_levels_href->{'3'} && $range >= $port_range_scan_threshold) {
			if ($Scan_href->{$ip}->{'CURRENT_DANGER_LEVEL'} < 2 && $Scan_href->{$ip}->{'CURRENT_DANGER_LEVEL'} != -1) {
				$Scan_href->{$ip}->{'ALERTED'} = "N";
				$Scan_href->{$ip}->{'CURRENT_DANGER_LEVEL'} = 2;
			}
		}
		if ($absnum >= $danger_levels_href->{'3'} && $absnum < $danger_levels_href->{'4'} && $range >= $port_range_scan_threshold) {
			if ($Scan_href->{$ip}->{'CURRENT_DANGER_LEVEL'} < 3 && $Scan_href->{$ip}->{'CURRENT_DANGER_LEVEL'} != -1) {
				$Scan_href->{$ip}->{'ALERTED'} = "N";
				$Scan_href->{$ip}->{'CURRENT_DANGER_LEVEL'} = 3;
			}
		}
		if ($absnum >= $danger_levels_href->{'4'} && $absnum < $danger_levels_href->{'5'} && $range >= $port_range_scan_threshold) {
			if ($Scan_href->{$ip}->{'CURRENT_DANGER_LEVEL'} < 4 && $Scan_href->{$ip}->{'CURRENT_DANGER_LEVEL'} != -1) {
				$Scan_href->{$ip}->{'ALERTED'} = "N";
				$Scan_href->{$ip}->{'CURRENT_DANGER_LEVEL'} = 4;
			}
		}
		if ($absnum >= $danger_levels_href->{'5'} && $range >= $port_range_scan_threshold) {
			if ($Scan_href->{$ip}->{'CURRENT_DANGER_LEVEL'} < 5 && $Scan_href->{$ip}->{'CURRENT_DANGER_LEVEL'} != -1) {
				$Scan_href->{$ip}->{'ALERTED'} = "N";
				$Scan_href->{$ip}->{'CURRENT_DANGER_LEVEL'} = 5;
			}
		}
		if ($alert_all eq "Y") {  # we will always send an alert email for any new "bad" packet if $alert_all eq "Y"
			$Scan_href->{$ip}->{'ALERTED'} = "N";
		}
	}
	return $Scan_href;
}
sub automatic_ip_danger_assignment() {
	my ($Scan_href, $Auto_ips_href) = @_;
	foreach my $scan_ip (keys %$Scan_href) {
		foreach my $auto_ip (keys %$Auto_ips_href) {
			if ($scan_ip eq $auto_ip && $Scan_href->{$scan_ip}->{'AUTO'} eq "N") {
				$Scan_href->{$scan_ip}->{'CURRENT_DANGER_LEVEL'} = $Auto_ips_href->{$auto_ip};
#				print "Assigning $scan_ip danger level: $Auto_ips_href->{$auto_ip}\n";
				$Scan_href->{$scan_ip}->{'ALERTED'} = "N";
				$Scan_href->{$scan_ip}->{'AUTO'} = "Y";
			}
		}
	}
	return $Scan_href;
}
sub collect_errors() {
	my ($bad_packets_aref, $error_log) = @_;
	open ERRORS, ">> $error_log";
	foreach my $l (@$bad_packets_aref) {
		print ERRORS "$l\n";
	}
	close ERRORS;
}
sub logr() {
	my ($Scan_href, $logfile, $output, $dnslookups, $whoislookups, $enable_email_alerts, $email_alert_danger_level,
					$email_alertfile, $email_address, $long_format, $show_all_signatures, $Cmds_href) = @_;
	my $mesg;
	my $flags_mesg;
	my $hndl;
	my $range;
	my $newrange;
	my $dnsstring = "";
	my @whois_info;
       	if ($output) {
                $hndl = "STDOUT";
       	} else {
		$hndl = "LOG"; 
		open $hndl, ">> $logfile";
       	}
	foreach my $ip (keys %$Scan_href) {
		my $current_danger_level = $Scan_href->{$ip}->{'CURRENT_DANGER_LEVEL'};
		if ($current_danger_level >= 1 && $Scan_href->{$ip}->{'LOGR'} eq "Y") {
			my $abs_start_range = $Scan_href->{$ip}->{'START_PORT'};
			my $abs_end_range = $Scan_href->{$ip}->{'END_PORT'};
			my $new_start_range = $Scan_href->{$ip}->{'CURRENT_INTERVAL'}->{'START_PORT'};
			my $new_end_range = $Scan_href->{$ip}->{'CURRENT_INTERVAL'}->{'END_PORT'};
			my $start_time = $Scan_href->{$ip}->{'START_TIME'}->{'READABLE'};
			my $new_start_time = $Scan_href->{$ip}->{'CURRENT_INTERVAL'}->{'START_TIME'};
			my $end_time = $Scan_href->{$ip}->{'END_TIME'};
			my $dstip = $Scan_href->{$ip}->{'DSTIP'};
                        my @time = split / /, scalar localtime;   ### Get the current time as a nice ASCII string.
                        pop @time; shift @time;    ### Get rid of the day and the year to make the time consistent with syslog
                        my $time = join ' ', @time;
                        unless ($dnslookups) {
                                my $ipaddr = gethostbyname($ip);
                                my $rdns = gethostbyaddr($ipaddr, AF_INET);
                                $rdns = "No reverse dns info available" unless $rdns;
                                $dnsstring = "$ip -> $rdns";
                        } 
			unless ($whoislookups) {
				@whois_info = `$Cmds_href->{'whois'} $ip`;
			}
			if ($abs_start_range == $abs_end_range) {
				$range = $abs_start_range;
			} else {
				$range = "$abs_start_range-$abs_end_range";
			}
			if ($new_start_range == $new_end_range) {
                                $newrange = $new_start_range;
                        } else {
                                $newrange = "$new_start_range-$new_end_range";
                        }
			undef $Scan_href->{$ip}->{'CURRENT_INTERVAL'};  # get rid of the current range to make room for a new one.
			# need to add current_interval stuff for recent scans to $mesg
			$mesg = "**** $time PORTSCAN  $ip -> $dstip [$range]  pkts=$Scan_href->{$ip}->{'ABSNUM'}, start=[$start_time], end=[$end_time], DangerLevel=[$Scan_href->{$ip}->{'CURRENT_DANGER_LEVEL'} out of 5], $dnsstring";
		        open EMAILALERT, "> $email_alertfile";
			if ($long_format eq "Y") {
				print $hndl "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
				print $hndl "$time:     Portscan Detected by psad (pid $$).\n";
				print $hndl "\n";
				print $hndl "Source:                   $ip\n";
				print $hndl "Destination:              $dstip\n";
				print $hndl "Complete port range:      [$range]  (since: $start_time)\n";
				print $hndl "Newly scanned ports:      [$newrange]  (since: $new_start_time)\n";
				print $hndl "Number of packets:        $Scan_href->{$ip}->{'ABSNUM'}\n";
				print $hndl "Start time:               $start_time\n";
				print $hndl "End time:                 $end_time\n";
				print $hndl "Danger level:             $Scan_href->{$ip}->{'CURRENT_DANGER_LEVEL'} out of 5\n";
				print $hndl "DNS info:                 $dnsstring\n" unless $dnslookups;
				print EMAILALERT "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
                                print EMAILALERT "$time:     Portscan Detected by psad (pid $$).\n";
				print EMAILALERT "\n";
                                print EMAILALERT "Source:                   $ip\n";
                                print EMAILALERT "Destination:              $dstip\n";
                                print EMAILALERT "Complete port range:      [$range]  (since:  $start_time)\n";
				print EMAILALERT "Newly scanned ports:      [$newrange]  (since:  $new_start_time)\n";
                                print EMAILALERT "Number of packets:        $Scan_href->{$ip}->{'ABSNUM'}\n";
                                print EMAILALERT "Start time:               $start_time\n";
                                print EMAILALERT "End time:                 $end_time\n";
                                print EMAILALERT "Danger level:             $Scan_href->{$ip}->{'CURRENT_DANGER_LEVEL'} out of 5\n";
                                print EMAILALERT "DNS info:                 $dnsstring\n" unless $dnslookups;
				unless ($whoislookups) {
					print $hndl "\n";
					print $hndl "Whois Information:\n";
					print EMAILALERT "\n";
					print EMAILALERT "Whois Information\n";
					print $hndl $_ foreach (@whois_info);
					print EMAILALERT $_ foreach (@whois_info);
					print $hndl "\n";
					print EMAILALERT "\n";
				}
				if ($USE_IPCHAINS) {
					print $hndl "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
					print EMAILALERT "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
				}
			} else {
				print $hndl "$mesg\n";
				print EMAILALERT "$mesg\n";
			}
#			print $hndl "\n";
			if ($USE_IPTABLES) {
				foreach my $flags (keys %{$Scan_href->{$ip}->{'FLAGS'}}) {
					my $nmapOpts = 0;
					$nmapOpts = "-sT or -sS" if ($flags eq "SYN");
					$nmapOpts = "-sF" if ($flags eq "FIN");
					$nmapOpts = "-sX" if ($flags eq "URG PSH FIN"); 
					if ($nmapOpts) {
						$flags_mesg = "TCP flags:                [$flags: $Scan_href->{$ip}->{'FLAGS'}->{$flags} packets]  Nmap: [$nmapOpts]";
						print $hndl "$flags_mesg\n";
						print EMAILALERT "$flags_mesg\n";
					} else {
						$flags_mesg = "TCP flags:                [$flags: $Scan_href->{$ip}->{'FLAGS'}->{$flags} packets]\n";
#						print $hndl "\n";
						print $hndl "$flags_mesg\n";
						print EMAILALERT "$flags_mesg\n";
					}
				}
#				print $hndl "\n";
				if (defined $Scan_href->{$ip}->{'CURRENT_SIGMATCH_START_TIME'}) {
					print $hndl "\n";
					print EMAILALERT "\n";
					print $hndl "---- TCP alert signatures found since [$Scan_href->{$ip}->{'CURRENT_SIGMATCH_START_TIME'}]\n";
					print EMAILALERT "---- TCP alert signatures found since [$Scan_href->{$ip}->{'CURRENT_SIGMATCH_START_TIME'}]\n";
					foreach my $sigmatch (keys %{$Scan_href->{$ip}->{'CURRENT_SIGMATCH'}}) {
                                                print $hndl "$sigmatch  Packets=$Scan_href->{$ip}->{'CURRENT_SIGMATCH'}->{$sigmatch}\n";
                                                print EMAILALERT "$sigmatch  Packets=$Scan_href->{$ip}->{'CURRENT_SIGMATCH'}->{$sigmatch}\n";
                                        }
					undef $Scan_href->{$ip}->{'CURRENT_SIGMATCH_START_TIME'};
					undef $Scan_href->{$ip}->{'CURRENT_SIGMATCH'};
				}
				if (defined $Scan_href->{$ip}->{'SIGMATCH'} && $show_all_signatures eq "Y") {
					print $hndl "\n";
                                        print EMAILALERT "\n";
					print $hndl "---- ALL TCP alert signatures found since [$start_time]\n";
                                        print EMAILALERT "---- ALL TCP alert signatures found since [$start_time]\n";
					foreach my $sigmatch (keys %{$Scan_href->{$ip}->{'SIGMATCH'}}) {
						print $hndl "$sigmatch  Packets=$Scan_href->{$ip}->{'SIGMATCH'}->{$sigmatch}\n";
						print EMAILALERT "$sigmatch  Packets=$Scan_href->{$ip}->{'SIGMATCH'}->{$sigmatch}\n";
					}
				}
			} else {
				$flags_mesg = 0;
			}
			print EMAILALERT "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
			close EMAILALERT;
			if ($Scan_href->{$ip}->{'ALERTED'} eq "N" && $current_danger_level >= $EMAIL_ALERT_DANGER_LEVEL) {
				$Scan_href = send_email_alert($Scan_href, $ip, $mesg, $flags_mesg, $email_alertfile, $email_address, $Cmds_href);
			}
			$Scan_href->{$ip}->{'LOGR'} = "N";
			print $hndl "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
		}
	}
	close $hndl unless $output;
	return $Scan_href;
}
sub auto_psad_response(){
        my ($Scan_href, $auto_psad_level, $Cmds_href) = @_;
	foreach my $ip (keys %$Scan_href) {
		my $current_danger_level = $Scan_href->{$ip}->{'CURRENT_DANGER_LEVEL'};
		### We only want to block the IP once
        	if ($current_danger_level >= $auto_psad_level && !(defined $Scan_href->{$ip}->{'BLOCKED'})) {
                	if ($USE_IPCHAINS) {
                        	my @chains = get_input_chains($Cmds_href->{'ipchains'});
                        	foreach my $inchain (@chains) {
                                	`$Cmds_href->{'ipchains'} -I $inchain 1 -s $ip -l -j DENY`;
                        	}
				$Scan_href->{$ip}->{'BLOCKED'} = "Y";
                	} elsif ($USE_IPTABLES) {
                        	my @chains = get_input_chains($Cmds_href->{'iptables'});
                        	foreach my $inchain (@chains) {
                                	`$Cmds_href->{'iptables'} -I $inchain 1 -s $ip -j DROP`;
                        	}
				$Scan_href->{$ip}->{'BLOCKED'} = "Y";
                	}
		}
        }
	return $Scan_href;
}
sub get_input_chains() {
	my $fwCmd = shift;
        my @rules;
        my @chains;
        @rules = `$fwCmd -L`;
        foreach my $r (@rules) {
                next unless ($r =~ /Chain/);
                my ($cname) = ($r =~ /Chain\s(\w+)/);
                next unless ($cname =~ /in/i);  # we don't have an input chain
                push @chains, $cname;
        }
        return @chains;
}
sub send_email_alert() {
        my ($Scan_href, $ip, $mesg, $flags_mesg, $email_alertfile, $email_address, $Cmds_href) = @_;
	my $hostname = `$Cmds_href->{'hostname'}`;
	chomp $hostname;
       	system("$Cmds_href->{'mail'} $email_address -s \"psad WARNING: $hostname has been scanned!\" < $email_alertfile");
	$Scan_href->{$ip}->{'ALERTED'} = "Y";
	return $Scan_href;
}
sub print_scan() {  # this should primarily be used for debugging
	open PSCAN, "> $PRINT_SCAN_HASH";
	print PSCAN Dumper $Scan_href;
	close PSCAN;
	return;
}
sub check_fw() {
	my $line = shift;
	if ($line !~ /MAC/) {  # ipchains log messages do not have a MAC address field
		$USE_IPCHAINS = 1;
	} else {
		$USE_IPTABLES = 1;
	}
}
sub check_firewall_rules() {
	my $Cmds_href = shift;
        my @localips;
        my $kernel = (split /\s/, `$Cmds_href->{'uname'} -a`)[2];
        my @localips_tmp = `$Cmds_href->{'ifconfig'} -a |$Cmds_href->{'grep'} inet`;
        push @localips, (split /:/, (split /\s+/, $_)[2])[1] foreach (@localips_tmp);
        my $iptables = 1 if ($kernel =~ /^2.3/ || $kernel =~ /^2.4/);
        my $ipchains = 1 if ($kernel =~ /^2.2/); # and also 2.0.x ?
        if ($iptables) {
# target     prot opt source               destination
# LOG        tcp  --  anywhere             anywhere           tcp flags:SYN,RST,ACK/SYN LOG level warning prefix `DENY '
# DROP       tcp  --  anywhere             anywhere           tcp flags:SYN,RST,ACK/SYN

# ACCEPT     tcp  --  0.0.0.0/0            64.44.21.15        tcp dpt:80 flags:0x0216/0x022
# LOG        tcp  --  0.0.0.0/0            0.0.0.0/0          tcp flags:0x0216/0x022 LOG flags 0 level 4 prefix `DENY '
# DROP       tcp  --  0.0.0.0/0            0.0.0.0/0          tcp flags:0x0216/0x022
                my @rules = `$Cmds_href->{'iptables'} -nL`;
                my $drop_rule = 0;
                FWPARSE: foreach my $rule (@rules) {
                        next FWPARSE if ($rule =~ /^Chain/ || $rule =~ /^target/);
                        if ($rule =~ /^(LOG)\s+(\w+)\s+\S+\s+\S+\s+(\S+)\s.+prefix\s\`(.+)\'/) {
                                ($target, $proto, $dst, $prefix) = ($1, $2, $3, $4);
                                if ($target eq "LOG" && $proto =~ /all|tcp/ && $prefix =~ /drop|reject|deny/i) { # only tcp supported right now...
                                # this needs work... see above _two_ rules.
                                        if (check_destination($dst, \@localips)) {
#                                                print STDOUT "*** Your firewall setup looks good.  Unauthorized tcp packets will be logged.\n";
                                                return 1;
                                        }
                                }
                        }
                }
                print STDERR "*** Your firewall does not include rules that will log dropped/rejected packets.  Psad will not work with such a firewall setup.\n";
                return 0;
        } elsif ($ipchains) {
# target     prot opt     source                destination           ports
# DENY       tcp  ----l-  anywhere             anywhere              any ->   telnet

#Chain input (policy ACCEPT):
#target     prot opt     source                destination           ports
#ACCEPT     tcp  ------  0.0.0.0/0            0.0.0.0/0             * ->   22
#DENY       tcp  ----l-  0.0.0.0/0            0.0.0.0/0             * ->   *
                my @rules = `$Cmds_href->{'ipchains'} -nL`;
                FWPARSE: foreach my $rule (@rules) {
                        chomp $rule;
                        next FWPARSE if ($rule =~ /^Chain/ || $rule =~ /^target/);
                        if ($rule =~ /^(\w+)\s+(\w+)\s+(\S+)\s+\S+\s+(\S+)\s+(\*)\s+\-\>\s+(\*)/) {
                                my ($target, $proto, $opt, $dst, $srcpt, $dstpt) = ($1, $2, $3, $4, $5, $6);
                                if ($target =~ /drop|reject|deny/i && $proto =~ /all|tcp/ && $opt =~ /....l./) {
                                        if (check_destination($dst, \@localips)) {
#                                                print STDOUT "*** Your firewall setup looks good.  Unauthorized tcp packets will be dropped and logged.\n";
                                                return 1;
                                        }
				}		
                        } elsif ($rule =~ /^(\w+)\s+(\w+)\s+(\S+)\s+\S+\s+(\S+)\s+(n\/a)/) {
                                my ($target, $proto, $opt, $dst, $srcpt, $dstpt) = ($1, $2, $3, $4, $5);
                                if ($target =~ /drop|reject|deny/i && $proto =~ /all|tcp/ && $opt =~ /....l./) {
                                        if (check_destination($dst, \@localips)) {
						return 1;
					}
				}
			}
                }
                print STDERR "*** Your firewall does not include rules that will log dropped/rejected packets.  Psad will not work with such a firewall setup.\n";
                return 0;
        } else {
                die "*** The linux kernel version you are currently running (v $kernel) does not seem to support ipchains or iptables.  psad will not run!\n";
        }
}
sub check_destination() {
        my ($dst, $localips_aref) = @_;
        return 1 if ($dst =~ /0\.0\.0\.0\/0/);
        foreach my $ip (@$localips_aref) {
                my ($oct) = ($ip =~ /^(\d{1,3}\.\d{1,3})/);
                return 1 if ($dst =~ /^$oct/);
        }
        return 0;
}
sub check_commands() {
        my $Cmds_href = shift;
        CMD: foreach my $cmd (keys %$Cmds_href) {
                unless (-e $Cmds_href->{$cmd}) {
                        $real_location = `which $cmd 2> /dev/null`;
                        chomp $real_location;
                        if ($real_location) {
                                $Cmds_href->{$cmd} = $real_location;
                        } else {
                                if ($cmd ne "ipchains" && $cmd ne "iptables") {
                                        die "Could not find $cmd anywhere!!!  Please edit the config section to include the path to $cmd.\n";
                                } elsif (defined $Cmds_href->{'uname'}) {
                                        my $kernel = (split /\s+/, `$Cmds_href->{'uname'} -a`)[2];
                                        if ($kernel =~ /^2.3/ || $kernel =~ /^2.4/) {
						if ($cmd eq "ipchains") {
							next CMD;
						} else {
                                                	die "*** You appear to be running kernel $kernel so you should be running iptables but iptables is not\nlocated at $Cmds_href->{'iptables'
}.  Please edit the config section to include the path to iptables.\n";
						}
                                        }
                                        if ($kernel =~ /^2.2/) { # and also 2.0.x ?
						if ($cmd eq "iptables") {
							next CMD;
						} else {
                                                	die "*** You appear to be running kernel $kernel so you should be running ipchains but ipchains is not\nlocated at $Cmds_href->{'ipchains'
}.  Please edit the config section to include the path to ipchains.\n";
						}
                                        }
                                }
                        }
                }
        }
        return %$Cmds_href;
}
sub usage_and_exit() {
        my $exitcode = shift;
        print <<_HELP_;

Usage: psad [-d] [-o] [-e] [-f] [-c <config file>] [-s <signature file>] [-a <auto danger level file>] [-h]

        -daemon			- do not run as a daemon.
	-error			- do not write errors to the error log.
	-firewallcheck		- disable firewall rules verification.
	-output			- print all messages to STDOUT (this 
				  does not include bad packet messages
				  that are printed to the error log).
	-config <config file>	- use config file instead of the values
				  contained within the psad script.
	-namelookups		- disable name resolution against
				  scanning ips.
	-signatures <sig file>	- import scan signatures.
	-auto_ips <ips file>	- import auto ips file for automatic 
				  ip danger level increases/decreses.
	-local_port_lookup	- disable local port lookups for scan
				  signatures.
        -h                      - prints this help message.

_HELP_
        exit $exitcode;
}
