#!/usr/bin/perl -w
#
#  Generate report from output of 'tcpdump -etn ip'
#  in HTML format.
#
#  Usage Example
#       tcpdump -c 100000 -w test.dump
#       gzip test.dump
#       zcat test.dump.gz | tcpdump -tenr - ip | MakeReport test.html test.txt
#
#  Usage:   MakeReport [yyyy-mm-dd[-hh:mm] ]  HTML_REPORT 
#
#
#  Changes:
#
#  Version 0.1
#    x - Sort Incoming scans by remote/local host in ascending order
#    x - Sort Outgoing scans by local/remote host in ascending order
#    x - Sort Chat sessions by local host
#  Version 0.2
#    x - Mark 0 value for incoming data (as is done for outgoing)
#  Version 0.3
#      - Write both text and html reports
#      - Change command line syntax
#      - Print total number of connections
#  Version 0.4
#      - Correct Scan code error from Verion 0.3
#      - Exempt special hosts from outgoing scans
#  Version 0.5
#      - Change reports to
#          Busiest Connections (old)
#          Busiest Local  Hosts - Traffic
#          Busiest Local  Hosts - Contacts
#          Busiest Remote Hosts - Traffic
#          Busiest Remote Hosts - Contacts
#   Version 0.6
#      -   Put Busiest Connection report last
#      -   Put Incoming/Outgoing Busiest Host reports side by side
#      -   Put Incoming/Outgoing Scan         reports side by side
#   Version 0.7
#      -   Include Multi-cast packets.  Assign Multi-cast address
#          as opposite of other address, local or remote
#      -   Print Total traffic in summary as in DailyReport.
#   Version 0.8
#      -   Correct GetService(), was mistakenly return 0 when
#          port did not have a built-in name
#   Version 0.9
#      -   Add fields for number of local/remote hosts probed/responding
#   Version 0.10
#      -   Test for too many connections, reject Local or Remote host
#          with too many connections
#   Version 0.11
#      -   Reject packet lines which contain letter in ip address field
#          (Found in 1999-09-07-12:00 packets  'gre-proto' (what is this?)
#   Version 0.20
#      -   Use new ipread produced files, no need to collate
#          Use 40 top connections instead of 20
#          Long ago removed outgoing scan hosts exemption
#   Version 0.21
#      -   Reduced memory consumption by about 50%
#          by (1) removing HostPair{} and consolidating
#             (2) consolidating all array indexed i
#                 by Connection or Host ip
#             (3) Using packed key for Connections
#                 (uses 13bytes instead of 35+ bytes)
#
#  Wish List
#      - Only include Ports <= 1024 in scans.
#      - Print protocol/port info for scan reports
#


use Socket;  #  Use to get DNS names

#  Print debug messages
$DEBUG=0;

#
#  Control constants
#
#  Number of busiest connections
$NTOP_CONN = 40;

#  Number of lines in TOP repotes
$NTOP = 20;

# Indices for total, incoming, outgoing sums
$TOT=0;
$INC=1;
$OUT=2;
$SCAN=3;

# Limit for number of connections
# Change from 99999 to 99999 - 1999-09-18 jr
### $CONNECTION_LIMIT=9999;


@LocCol = ("#ffffff","#ddddff");
@RemCol = ("#ddffdd","#ffffff");
@ZerCol = ("#ffdddd","#ffffff");


%ProtName = (
1  => "icmp",
6  => "tcp",
17 => "udp"
);


#
#  Font size for table entries
#
$fnt="<font size=-1>";

#
#  Get arguements
#
&Usage if (scalar(@ARGV)==0);

if (@ARGV==1) {
	$date     = "";
	$HTMLName = $ARGV[0];
} else {
	$date     = $ARGV[0];
	$HTMLName = $ARGV[1];
}


open (HTML, ">".$HTMLName) || die "Cannot open HTML output file.\n";


#  Find Dir for config file
$Path = &FindConfig("", "ipaudit.cfg");

if (open (INFILE, "$Path")) {
	while (<INFILE>) {
 		next if (/^\s*#/);
		chop;
		($Name,$Value) = split(/[= ]+/);
		$Conf{$Name} = $Value;
	}
	close (INFILE);
} else {
	print "Cannot open file ipaudit.cfg.\n";
}


#
#  Read service descriptions from /etc/services
#
if (open (INFILE,"/etc/services")) {
	while (<INFILE>)
		{
 		next if (/^\s*#/ || /^[\s*]$/);
		chop;
		($Service, $Port, $Protocol, undef) = split (/[\t\/ ]+/);
 		if ($Protocol eq "udp") {
 			$UDPService{$Port} = $Service;
 		} elsif ($Protocol eq "tcp") {
 			$TCPService{$Port} = $Service;
 		}
	}
	close (INFILE);
}


#
#  Reformat list of local nets
#
$Conf{'LOCAL_NET'} =~ s/(["'])(.*)\1/$2/;            #  Strip quote marks
@LocalNet = split /[^0-9\.]+/, $Conf{"LOCAL_NET"};
die "No local nets found, did you set up the Envinronment Variables?\n" if ($#LocalNet<0);
for (@LocalNet) {
	@Octet = split /\./;
	$_ = sprintf "%03d", $Octet[0];
	for ($i=1; $i<=$#Octet; $i++) {
		$_ = $_ . "." . sprintf "%03d", $Octet[$i];
	}
}

#
#  Read output from "tcpdump -ten ip"  - one line per packet header
#

$InternalComm = 0;
$ExternalComm = 0;
$UnknownComm = 0;
$Incoming = 0;
$Outgoing = 0;

#  Read connections info from STDIN and sum
#  Each input line may contain *partial* info on connection.
#  Need to read all lines to get all info
print "Reading data\n" if $DEBUG;
while (<STDIN>) {

	#  truncate \n
	chomp;

	#  Get fields
	($ip[0], $ip[1], $prot, $prt[0], $prt[1], $byt[0], $byt[1], $pkt[0], $pkt[1]) = split;

	#  Increment total number of packets (in and out)
	$NumPacket += $pkt[0] + $pkt[1];

	#  Total traffic for this connection
	$Totb = $byt[0] + $byt[1];

	# Test for Src/Dst  local/remote or remote/local
	$Location0  = &GetLocation ($ip[0]);
	$Location1  = &GetLocation ($ip[1]);
	$IsLocal0   = ($Location0 eq "L" || $Location1 eq "R");
	if ($Location0 eq $Location1 ) {
		if ($Location0 eq "L") {
			$InternalComm += $Totb;
		} elsif ($Location0 eq "R") {
			$ExternalComm += $Totb;
		} else {
			$UnknownComm  += $Totb;
		}
		next;
	}

	if ($IsLocal0) {
		$Inc=0;
		$Out=1;
	} else {
		$Inc=1;
		$Out=0;
	}
	$Loc =$ip[$Inc];
	$Rem =$ip[$Out];
	$Incb=$byt[$Inc];
	$Outb=$byt[$Out];

	$Outgoing += $Outb;
	$Incoming += $Incb;

	$BigKey      = &MakeBigKey($Loc,$Rem,$prot,$prt[$Inc],$prt[$Out]);

	#
	#  "Connections"
	#
	#  Store traffic (total, outgoing, incoming)
	#   keyed by combination of 
	#     local ip, remote ip, protocol, local port, remote port
	$LocRemSrvPrt{$BigKey}[$TOT] += $Totb;
	$LocRemSrvPrt{$BigKey}[$INC] += $Incb;
	$LocRemSrvPrt{$BigKey}[$OUT] += $Outb;

	#
	#  Store traffic by local or remote host
	#
	$LocalTraffic    {$Loc}[$TOT] += $Totb;
	$RemoteTraffic   {$Rem}[$TOT] += $Totb;
	$LocalTraffic    {$Loc}[$INC] += $Incb;
	$RemoteTraffic   {$Rem}[$INC] += $Incb;
	$LocalTraffic    {$Loc}[$OUT] += $Outb;
	$RemoteTraffic   {$Rem}[$OUT] += $Outb;

	}
	#  End of input loop

#  Total number of connections
$NConn  = scalar keys (%LocRemSrvPrt);


# Number of local / remote hosts
$NumLocalHosts  = scalar keys %LocalTraffic;
$NumRemoteHosts = scalar keys %RemoteTraffic;

#  Number of local/remote hosts probed/responding
while ( ($key,undef) = each %LocalTraffic) {
	$LocalProbed++  if $LocalTraffic{$key}[$INC];
	$LocalRespond++ if $LocalTraffic{$key}[$OUT];
}
while ( ($key,undef) = each %RemoteTraffic) {
	$RemoteProbed++  if $RemoteTraffic{$key}[$OUT];
	$RemoteRespond++ if $RemoteTraffic{$key}[$INC];
}

&PrintHTMLReport;
close(HTML);



#
#  END OF MAIN ROUTINE
#
exit;



########################################################################
########################################################################
#
#  HTML Report
#
sub PrintHTMLReport
{
########################################################################
#
#  REPORT:  Print header and summary
#
#
### &PrintHeader;  # Don't print header, header added to output later
&PrintSummary;



########################################################################
#
#  REPORT:  Busiest Host
#
#
#  List 'NTOP' Top Local/Remote hosts by amount of traffic
#
print "Starting Busiest Host\n" if $DEBUG;
print HTML "<table cellspacing=20><tr><td>\n";



&MakeTopList ($NTOP, \%LocalTraffic, 0, \%LocalSort);

&PrintTraffic 
   (
   "BUSIEST LOCAL MACHINES - TRAFFIC (bytes)", 
   \%LocalSort,
	\%LocalTraffic
   );



print HTML "</td><td>\n";

&MakeTopList ($NTOP, \%RemoteTraffic, 0, \%RemoteSort);

&PrintTraffic 
   (
   "BUSIEST REMOTE MACHINES - TRAFFIC (bytes)", 
   \%RemoteSort,
	\%RemoteTraffic
   );


print HTML "</td></tr></table>\n";


########################################################################
#
#  REPORT:  Possible Incoming/Outgoing Machine Probes (many connections on one machine)
#
#
#  List 'NTOP' Top Local/Remote pair by number of one-way connections
#
print "Starting Local Machine Probed\n" if $DEBUG;

#
#  Find all loc/rem host pairs with one-way (incoming) connections
#

#  Tablulte late of remote->local port scans
while ( ($key,undef) = each %LocRemSrvPrt) {
	if (0==$LocRemSrvPrt{$key}[$OUT]) {
		($Loc, $Rem) = SplitBigKey($key);
		$pairkey = MakeHostPairKey($Loc,$Rem);
		$TempPairProbe{$pairkey}++;
	}
}

#  Print table of remote->local port scans
&MakeTopList2 ($NTOP, \%TempPairProbe, \%TempPairList);
&PrintProbe ("INCOMING MACHINE PROBES", \%TempPairList);
print "After PrintProbe()\n" if $DEBUG;
undef %TempPairProbe;
undef %TempPairList;


print "Starting Remote Machine Probed\n" if $DEBUG;

#  Tabulate list of local->remote port scans
while ( ($key,undef) = each %LocRemSrvPrt) {
	if (0==$LocRemSrvPrt{$key}[$INC]) {
		($Loc, $Rem) = SplitBigKey($key);
		$pairkey = MakeHostPairKey($Loc,$Rem);
		$TempPairProbe{$pairkey}++;
	}
}

#  Print table of local->remote port scans
&MakeTopList2 ($NTOP, \%TempPairProbe, \%TempPairList);
print HTML "<br><br>\n";
&PrintProbe ("OUTGOING MACHINE PROBES", \%TempPairList);
undef %TempPairProbe;
undef %TempPairList;



########################################################################
#
#  REPORT:  Possible Incoming/Outgoing Scans
#
#
#  List 'NTOP' Top Local/Remote hosts by number of local hosts contacted
#

print "Starting Incoming Scan\n" if $DEBUG;
#
#  Find all loc/rem host pairs with one-way (incoming) connections
#
while ( ($key,undef) = each %LocRemSrvPrt) {
	($Loc, $Rem) = SplitBigKey($key);
	if (0==$LocRemSrvPrt{$key}[$OUT]) {
		$pairkey = MakeHostPairKey($Loc,$Rem);
		$LocalPair{$pairkey}++;
	}
}

#  Find number of local hosts each remote host has a one-way connection with
for (keys %LocalPair) {
	($Loc,$Rem) = &SplitHostPairKey($_);
	$RemoteTraffic{$Rem}[$SCAN]++;
}
undef %LocalPair;


print "Starting Remote Scan\n" if $DEBUG;

#
#  Find all loc/rem host pairs with one-way (incoming) connections
#
while ( ($key,undef) = each %LocRemSrvPrt) {
	($Loc, $Rem) = SplitBigKey($key);
	if (0==$LocRemSrvPrt{$key}[$INC]) {
		$pairkey = MakeHostPairKey($Loc,$Rem);
		$RemotePair{$pairkey}++;
	}
}
#  Find number of local hosts each remote host has a one-way connection with
for (keys %RemotePair) {
	($Loc,$Rem) = &SplitHostPairKey($_);
	$LocalTraffic{$Loc}[$SCAN]++;
}
undef %RemotePair;

###
### # Origianl scan code, sorted remote/local machines by number of connections, 
### # which confused machine scans with port scans on a single machine
###
### while ( ($key,undef) = each %LocRemSrvPrt) {
### 	($Loc,$Rem) = SplitBigKey($key);
### 	#  If incoming is 0, then count as scan from a local machine
### 	#  (outgoing scan)
### 	if (0==$LocRemSrvPrt{$key}[$INC]) {
### 		$LocalTraffic{$Loc}[$SCAN]++;
### 	#  If outgoing is 0, then count as scan from a remote machine
### 	#  (incoming scan)
### 	} elsif (0==$LocRemSrvPrt{$key}[$OUT]) {
### 		$RemoteTraffic{$Rem}[$SCAN]++;
### 	}
### }

#  Find maximum incoming scans
&MakeTopList ($NTOP, \%RemoteTraffic, $SCAN, \%ScanRemote);
	
#  Find maximum outgoing scans
&MakeTopList ($NTOP, \%LocalTraffic, $SCAN, \%ScanLocal);


print HTML "<table cellspacing=20><tr><td>\n";

&PrintScan ("POSSIBLE INCOMING-SCAN HOSTS", \%ScanRemote);

print HTML "</td><td>\n";

&PrintScan ("POSSIBLE OUTGOING-SCAN HOSTS", \%ScanLocal);

print HTML "</td></tr></table>\n";


########################################################################
#
#  REPORT:  Busiest Connections
#
#


#
#  Determine connection (Host Pair, Protocol and Port combination)
#  with heaviest traffice
#
&MakeTopList($NTOP_CONN, \%LocRemSrvPrt, 0, \%MaxList);

&PrintSubList 
	(
	"BUSIEST CONNECTIONS", 
	\%MaxList,
	\%LocRemSrvPrt
	);




###  ########################################################################
###  #
###  #  REPORT:  Possible Chat Sessions
###  #
###  #
###  $i=0;
###  foreach $key (@AllKey)
###  	{
###  	($Addr1, $Addr2, $Pro, $Prt1, $Prt2) = split(/:/,$key);
###  	if ($Prt1>=6660 && $Prt1<=6669 || $Prt2>=6660 && $Prt2<=6669)
###  		{
###  		$IsUsed{$key} = 1;
###  		$Chat[$i] = $key;
###  		$i++;
###  		}
###  	}
###  
###  @SortChat = sort @Chat;
###  
###  &PrintSubList 
###  	(
###  	"Possible Chat Sessions",
###  	\@SortChat,
###  	\%LocRemSrvPrt
###  	);
###  
###  
###  
###  ########################################################################
###  #
###  #  REPORT:  NFS mounts
###  #
###  #
###  #  Incoming/Outgoing NFS
###  $i=0;
###  foreach $key (@AllKey)
###  	{
###  	($Addr1, $Addr2, $Pro, $Prt1, $Prt2) = split(/:/,$key);
###  	if ($Prt1==111 || $Prt2==111)
###  		{
###  		$IsUsed{$key} = 1;
###  		$NFS[$i] = $key;
###  		$i++;
###  		}
###  	}
###  
###  &PrintSubList 
###  	(
###  	"NFS Mounts",
###  	\@NFS,
###  	\%LocRemSrvPrt
###  	);



}


#
#  Print start of HTML page
#
sub PrintHeader
{
print HTML "<HTML>\n";
print HTML "<HEAD><TITLE>$ARGV[0]</TITLE></HEAD>\n";
print HTML "<body bgcolor=white>\n";
print HTML "<center><font size=+1>$ARGV[0]</font></center><hr noshade>";
}


sub PrintSummary
{
print  HTML "<pre><b>\n";
printf HTML 
  "Connections:             [%14s]\n", &ic($NConn);
printf HTML 
  "LocalHosts:      Total   [%14s]   Probed  [%14s]   Respond [%14s]\n", 
   &ic($NumLocalHosts), &ic($LocalProbed), &ic($LocalRespond);
printf HTML 
  "RemoteHosts:     Total   [%14s]   Probed  [%14s]   Respond [%14s]\n", 
   &ic($NumRemoteHosts), &ic($RemoteProbed), &ic($RemoteRespond); 
printf HTML 
  "Packets:         Detected[%14s]\n", &ic($NumPacket);
printf HTML 
  "Traffic:         Total   [%14s]   Incoming[%14s]   Outgoing[%14s]\n",
  &ic($Incoming+$Outgoing), &ic($Incoming), &ic($Outgoing);
printf HTML
  "                 Internal[%14s]   External[%14s]   Unknown [%14s]",
  &ic($InternalComm), &ic($ExternalComm), &ic($UnknownComm);
print HTML "</b></pre>\n";

}



#
#  Print report of Incoming/Outgoing traffic with key of the form
#
#     LocalHost:RemoteHost:Protocol:LocPort:RemPort
#  
#   all fields are optional
#  
#
sub PrintSubList {

my ($Message, $Top, $Hash) = @_;

#  Print no data message
if (scalar keys %$Top < 1)
	{
	print HTML
	  "<br><b><tt><font color=#888888>No $Message Detected</font></tt></b><br>\n";
	return;
	}

print HTML "<table cellpadding=2 cellspacing=0 border=2>\n";
print HTML "<tr><th colspan=10><tt>$Message</tt></th></tr>\n";

&PrintTableHeading;


$i=0;
$PrevAddr1 = "";
$PrevAddr2 = "";
$iLoc   = 0;
$iRem   = 0;
for $key (sort { $$Top{$b} <=> $$Top{$a} } keys %$Top )
	{
	$i++;
	($Addr1, $Addr2, $Pro, $Prt1, $Prt2) = SplitBigKey($key);
	$Srv1 = GetService($Pro,$Prt1);
	$Srv2 = GetService($Pro,$Prt2);

	#  Set fields to space if empty
	$Addr1 = "&nbsp;" unless defined ($Addr1);
	$Addr2 = "&nbsp;" unless defined ($Addr2);
	$Pro   = "&nbsp;" unless defined ($Pro);
	$Srv1  = "&nbsp;" unless defined ($Srv1);
	$Srv2  = "&nbsp;" unless defined ($Srv2);

	if (defined($ProtName{$Pro})) {
		$Pro = $ProtName{$Pro};
	}

	#  Start row
	print HTML "<tr>\n";
	
	#  Print LOCAL ip address
	#     Toggle background color if this address is new
	$iLoc = 1-$iLoc if ($Addr1 ne $PrevAddr1);
	$PrevAddr1 = $Addr1;
	print HTML "<td bgcolor=$LocCol[$iLoc]>$fnt$Addr1</td>\n";

	#  Print REMOTE ip address
	#    Toggle background color if this address is new
	$iRem = 1-$iRem if ($Addr2 ne $PrevAddr2);
	$PrevAddr2 = $Addr2;
	print HTML "<td bgcolor=$RemCol[$iRem]>$fnt$Addr2</td>\n";

	#  Print LOCAL Name
	#     Toggle background color if this address is new
	print HTML "<td bgcolor=$LocCol[$iLoc]>$fnt", &GetDNS($Addr1), "</td>\n";

	#  Print REMOTE Name
	#    Toggle background color if this address is new
	print HTML "<td bgcolor=$RemCol[$iLoc]>$fnt", &GetDNS($Addr2), "</td>\n";

	#  Print incoming traffic
	#    Color background if zero bytes
	$traffic = &ic($$Hash{$key}[1]+$$Hash{$key}[2]);
	$Color = ( $traffic eq "0" ? $ZerCol[0] : $ZerCol[1] );
	print HTML "<td align=right bgcolor=$Color>$fnt$traffic</td>\n";

	#  Print incoming traffic
	#    Color background if zero bytes
	$traffic = &ic($$Hash{$key}[1]);
	$Color = ( $traffic eq "0" ? $ZerCol[0] : $ZerCol[1] );
	print HTML "<td align=right bgcolor=$Color>$fnt$traffic</td>\n";

	#  Print outgoing traffic
	#    Color background if zero bytes
	#
	$traffic = &ic($$Hash{$key}[2]);
	$Color = ( $traffic eq "0" ? $ZerCol[0] : $ZerCol[1] );
	print HTML "<td align=right bgcolor=$Color>$fnt$traffic</td>\n";

	#  Print protocol, local and remote port
	print HTML "<td>$fnt$Pro</td><td>$fnt$Srv1</td><td>$fnt$Srv2</td>\n";

	#  End row
	print HTML "</tr>\n";
	}


&PrintTableHeading;

#  End table
print HTML "</table>\n";
}



sub GetService {
	my ($Pro, $Prt) = @_;
	if ( ($Pro eq "udp" || $Pro==17)  && $UDPService{$Prt}) {
		$UDPService{$Prt};
	} elsif ( ($Pro eq "tcp" || $Pro==6)  && $TCPService{$Prt}) {
		$TCPService{$Prt};
	} else {
		$Prt;
	}
}
		


sub PrintTableHeading {
print HTML << "EOM";
<tr>
<th><tt>Local IP</tt></th>
<th><tt>Remote IP</tt></th>
<th><tt>Local Name</tt></th>
<th><tt>Remote Name</tt></th>
<th><tt>&nbsp;&nbsp;&nbsp;Total</tt></th>
<th><tt>Incoming</tt></th>
<th><tt>Outgoing</tt></th>
<th><tt>Protocol</tt></th>
<th><tt>Local Port</tt></th>
<th><tt>Remote Port</tt></th>
</tr>
EOM
}



sub PrintScan {
my ($Message, $Count) = @_;
my ($name);

print HTML << "EOM";
<table cellpadding=2 cellspacing=0 border=2>
<tr><th colspan=3><tt>$Message</tt></th></tr>
<tr>
<th><tt>IP Address</tt></th>
<th><tt>IP Name</tt></th>
<th><tt>Number of Contacts</tt></th>
</tr>
EOM

for (sort {$$Count{$b} <=> $$Count{$a}} keys %$Count) {
	$name = &GetDNS($_);
	print HTML "<tr><td>$fnt", &iplink($date,$_), "</td>\n";
	print HTML "<td>$fnt$name</td>\n";
	print HTML "<td>$fnt$$Count{$_}</td></tr>\n";
}
print HTML "</table>";
}


sub PrintTraffic {
	my ($Message, $KeyHash, $Hash) = @_;
	my ($name);


print HTML <<"EOM";
<table cellpadding=2 cellspacing=0 border=2>
<tr><th colspan=5><tt>$Message</tt></th></tr>
<tr>
<th><tt>IP Address</tt></th>
<th><tt>Name</tt></th>
<th><tt>Total Traffic</tt></th>
<th><tt>Incoming</tt></th>
<th><tt>Outgoing</tt></th>
</tr>
EOM


	for ( sort {$$KeyHash{$b} <=> $$KeyHash{$a}} keys %$KeyHash ) {

		$Total = &ic ($$Hash{$_}[0]);

		if (!defined($$Hash{$_}[1])) {
			$Incoming = 0;
		} else {
			$Incoming = &ic($$Hash{$_}[1]);
		}

		if (!defined($$Hash{$_}[2])) {
			$Outgoing = 0;
		} else {
			$Outgoing = &ic($$Hash{$_}[2]);
		}
			
		$name = &GetDNS($_);
		print HTML "<tr><td>$fnt ", &iplink($date,$_), "</td>";
		print HTML "<td>$fnt $name</td>";
		print HTML "<td align=right>$fnt$Total</td>";
		print HTML "<td align=right>$fnt$Incoming</td>";
		print HTML "<td align=right>$fnt$Outgoing</td></tr>\n";
		}
	print HTML "</table>";
	}


#
#  Convert positive whole number from nnnnnn to n,nnn,nnn etc.
#
sub ic
{
	my ($string) = @_;
	1 while $string=~s/(\d)(\d\d\d)(?!\d)/$1,$2/g;
	return $string;
}

### sub ic0 {
### 	my ($string) = @_;
### 	my ($i,$pre,$n);
### 
### 	return 0 if !defined($string);
### 
### 	$n=length $string;
### 	
### 	#  No commas needed 
### 	return $string if $n<4;
### 
### 	#  Get between 1-2 digits before first comma
### 	$i = (($n-1) % 3) + 1;
### 	$pre = substr($string,0,$i);
### 	for (; $i<$n; $i+=3)  {
### 		$pre .= "," . substr($string,$i,3);
### 	}
### 	$pre;
### }


#
#
#
sub GetLocation 
	{
	my ($IP) = @_;

	#  Test for local network
	for (@LocalNet)
		{
		return "L" if (substr($IP,0,length) eq $_);
		}

	#  Test for 0.0.0.0  or 255.255.255.255 or 224.x.x.x	
	#  These are "Unknown" addresses
	return "U" if ($IP eq 000.000.000.000);
	return "U" if ($IP eq 255.255.255.255);
	return "U" if ($IP =~ /^224\./);

	#  Anything else is remote address
	return "R";
	}


sub Usage {
print <<"EOM";

   Usage: $0 HTML_REPORT
   
   Reads traffic info (from ipaudit) on STDIN and writes an HTML report to <HTML_REPORT>.

   Input format is a text file of IP connections data with 9 columns of data.
   Lines with more or less columns, and lines beginning with # are ignored.  The
   data in the 9 columns are
       ip1 ip2 prot port1 port2 byt1 byt2 pkt1 pkt2
   where
     ip1, ip2     -   ip address of machines 1,2
     prot         -   protocol number (6->tcp, 17->ucp, etc).
     port1,port2  -   ports of machines 1,2  (only valid for for tcp and udp connections)
     byt1,byt2    -   number of bytes    recieved by machines 1,2
     pkt1,pkt2    -   number of packetes recieved by machines 1,2

EOM
exit;
}


sub MakeHostPairKey {
	my ($loc,$rem) = @_;
	return pack "cccccccc",
      split(/\./,$loc), split(/\./,$rem);
}


sub MakeBigKey {
	my ($loc,$rem,$prot,$lpt,$rpt) = @_;
	return pack "cccccccccnn",
      split(/\./,$loc), split(/\./,$rem), $prot, $lpt, $rpt;  
}

sub SplitHostPairKey {
	my ($pack) = @_;
	my ($key);
	$key = sprintf "%03d.%03d.%03d.%03d:%03d.%03d.%03d.%03d",
         unpack ("CCCCCCCC", $pack); 
	return split(/:/,$key);
}


sub SplitBigKey {
	my ($pack) = @_;
	my ($key);
	$key = sprintf "%03d.%03d.%03d.%03d:%03d.%03d.%03d.%03d:%d:%d:%d",
         unpack ("CCCCCCCCCnn", $pack); 
	return split(/:/,$key);
}


sub GetDNS {
	my ($name) = @_;
	$name = sprintf "%d.%d.%d.%d", split(/\./,$name);
	$name = inet_aton($name);
	$name = gethostbyaddr($name, AF_INET) if defined($name);
	return defined($name) ? $name : "&nbsp";
}



#
#  Read a hash of form $hash{$key}[$x] for given $x
#    where $hash{$key}[$x] is a number.
#    Return a new hash consisting only of $ntop elements.
#
sub MakeTopList {
	my ($ntop, $hash, $x, $sort) = @_;
	my ($i, $MaxValue, $MaxKey, $nhash);


#  Find maximum incoming scans
	$nhash = (scalar keys %$hash);
	$ntop  = ($nhash<$ntop) ? $nhash : $ntop;
   for ($i=0;$i<$ntop;$i++) {
   	$MaxValue = 0;
   	$MaxKey   = "";
		while ( ($key,undef) = each %$hash) {
   		next unless defined($$hash{$key}[$x]);
   		next if defined($$sort{$key});
   		next if $MaxValue > $$hash{$key}[$x];
   		$MaxKey = $key;
   		$MaxValue = $$hash{$key}[$x];
   	}
   	last if $MaxValue==0;
   	$$sort{$MaxKey} = $MaxValue;
   }
}


#
#  Read a hash of form $hash{$key}
#    where $hash{$key} is a number.
#    Return a new hash consisting only of $ntop elements.
#   Like MakeTopList() but doesn't use array index $x
#
sub MakeTopList2 {
	my ($ntop, $hash, $sort) = @_;
	my ($i, $MaxValue, $MaxKey, $nhash);


#  Find maximum incoming scans
	$nhash = (scalar keys %$hash);
	$ntop  = ($nhash<$ntop) ? $nhash : $ntop;
   for ($i=0;$i<$ntop;$i++) {
   	$MaxValue = 0;
   	$MaxKey   = "";
		while ( ($key,undef) = each %$hash) {
   		next unless defined($$hash{$key});
   		next if defined($$sort{$key});
   		next if $MaxValue > $$hash{$key};
   		$MaxKey = $key;
   		$MaxValue = $$hash{$key};
   	}
   	last if $MaxValue==0;
   	$$sort{$MaxKey} = $MaxValue;
   }
}



#
#  Print host-pair probe results
#
sub PrintProbe {
my ($title,$data) = @_;
my ($loc,$rem,$lname,$rname,$key);

print HTML <<"EOM";
<table cellpadding=2 cellspacing=0 border=2>
<tr><th align=center colspan=5><tt>$title</tt></th></tr>
<tr>
<th><tt>Local IP</tt></th>
<th><tt>Local Name</tt></th>
<th><tt>Remote IP</tt></th>
<th><tt>Remote Name</tt></th>
<th><tt>Connections</tt></th>
</tr>
EOM

for $key (sort {$$data{$b}<=>$$data{$a}} keys %$data) {
	($loc, $rem) = &SplitHostPairKey($key);
	$lname = &GetDNS($loc);
	$rname = &GetDNS($rem);
	print HTML "<tr>\n";
	print HTML "<td><tt>$fnt", &iplink($date,$loc), "</tt></td>\n";
	print HTML "<td><tt>$fnt$lname</tt></td>\n";
	print HTML "<td><tt>$fnt", &iplink($date,$rem), "</tt></td>\n";
	print HTML "<td><tt>$fnt$rname</tt></td>\n";
	print HTML "<td><tt>$fnt$$data{$key}</tt></td>\n";
	print HTML "</tr>\n";
}
print HTML "</table>\n";
}


sub iplink {
	my ($date,$ip) = @_;
	return $ip if $date eq "";
	return "<a href=$Conf{'CGI_BIN'}/SearchIpauditData?date=$date&ip=$ip&sort=0>$ip</a>";
}

#  Search upward from $Dir looking for $File
sub FindConfig {
        my ($Dir, $File) = @_;

        $Dir  = `pwd`         if $Dir  eq "";
        $File = "ipaudit.cfg" if $File eq "";

        chomp $Dir;

        while (! -f "$Dir/$File" && $Dir ne "") {
                $Dir=~s/\/[^\/]+$//;
        }

        die "Cannot find config file\n"
                if ! -f "$Dir/$File";
        return "$Dir/$File";
}


