#!/usr/bin/perl
#
#  Usage:  ReportServer
#
use Socket;   # for gethostbyaddr()

#  Map service (port) number to service name
%ServiceName = (
21 => "ftp",
22 => "ssh", 
23 => "telnet", 
513 => "rlogin", 
6667 => "irc"
);

%ServiceProtocol = (
21 => 6,
22 => 6,
23 => 6,
513 => 6,
6667 => 6
);


#  Map service name to number
for (keys %ServiceName) {
	$ServiceNumber{$ServiceName{$_}} = $_;
}

# Make unique ORmask for each service
$mask=1;
for (keys %ServiceName) {
	$mask{$_} = $mask;
	$mask <<= 1;
}

#  Constants
$MIN_TRAFFIC=2000;  # Minimum number of bytes exchanged per connection

if ($ARGV[0] eq '-h' ) {
   print "Usage: ReportServer '<filelist>\n";
   exit 
}


#  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";
}


#  Reformat list of local nets

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


#  Read files
%Found=();
while (<STDIN>) {
   chomp;
   @F = split(/\s+/);
   #  Determine if machines are local or remote
   $Location1 = &GetLocation($F[0]);
   $Location2 = &GetLocation($F[1]);
   #  Reject connections between two local or two remote machines
   next if ($Location1 eq $Location2);
   #  Reject if unknown location
   next if ($Location1 eq "U" || $Location2 eq "U");
   #  Make first machine the local one
   if ("L" eq $Location2) {
      ($F[0],$F[1],$F[3],$F[4]) = ($F[1],$F[0], $F[4],$F[3]);
   }

   #  Test for interesting service
   next unless defined($ServiceName{$F[3]});

   #  Test for correct protocol for this service
   next unless $F[2]==$ServiceProtocol{$F[3]};

   #  Test for significant traffic
   next if ( $F[5] < $MIN_TRAFFIC);

   #  Store bit value if:  telnet, rlogin, irc
   $Found{$F[3]} = 1;
   $tally{$F[0]}{$F[1]} |= $mask{$F[3]};


   #  Get local, remote ip names
   if ($locip2name{$F[0]} eq "") {
      $locname = &GetIPName($F[0]);
      #  Don't use '-' as key because then
                #  different IPs will map to same key
      $locname = $F[0] if $locname eq "-";
      $locip2name{$F[0]}    = $locname;
      $locname2ip{$locname} = $F[0];
      }
   if (! defined($remip2name{$F[1]})) {
      $remname = &GetIPName($F[1]);
      $remip2name{$F[1]}    = $remname;
      }
   }


# -------------------------------------------------------------------
# count number of local machines that each remote machines contacted
# -------------------------------------------------------------------

for $a (keys %tally) {
   for $b (keys %{$tally{$a}}) {
      $RemCount{$b}++ if defined($tally{$a}{$b});
      }
   }


# ---------------------------------------------------------
# print service reports sorted by service name 
# ---------------------------------------------------------



#
#  Print links/messages at top of page
#
print "<br><br>&nbsp; &nbsp; ";
$i=0;
for (sort keys %ServiceNumber) {
	print " &nbsp; | &nbsp; " unless $i==0;
	$i=1;
	if ($Found{$ServiceNumber{$_}}) {
		print "<a href=#$_>$_</a>";	
	} else {
		print "No $_";
	}
}
print "<br>\n";

#
#  Print report for each service
#
for (sort keys %ServiceNumber) {
	next unless $Found{$ServiceNumber{$_}};
	$mask  = $mask{$ServiceNumber{$_}};
	@col = ( "#ddffdd", "#ddddff");
	$icol=1;
	#  Print anchor for rlogin
	$sname = uc($_);
	print "<a name=$_>\n";
	print "<hr noshade>\n";
	print "<center>\n";
	print "<font size=+2>\n";
	print "<b>$sname</b> access to Local Subnets<br>\n";
	print "</center>\n";
	print "<table border=2 cellpadding=5>\n";
	print "<tr><th>Local Name</th><th>Remote Name</th><th>Local IP</th><th>Remote IP</th></tr>\n";
	$previp="";
	for (sort keys %locname2ip) {
	   $locip = $locname2ip{$_};
	   for $remip (keys %{$tally{$locip}}) {
	      #  Is this telnet access
	      next unless  ($tally{$locip}{$remip} & $mask);
	      #  Make line bold if Remote computer has more than 1 local telnet host
	      ($b0,$b1) = ($RemCount{$remip}>1) ? ("<b>","</b>") : ("","");
	      #  Toggle color
	      $icol = 1-$icol if ($previp ne $locip);
	      $previp = $locip;
	      print "<tr>\n";
	      print "<td bgcolor=$col[$icol]><tt>$b0$_$b1</tt></td>\n";
	      print "<td bgcolor=$col[$icol]><tt>\n";
	      print "$b0($RemCount{$remip})\n";
	      print "&nbsp; $remip2name{$remip}$b1</tt></td>\n"; 
	      print "<td><tt>$b0$locip$b1</tt></td>\n"; 
	      print "<td><tt>$b0$remip$b1</tt></td>\n"; 
	      print "</tr>\n";
	      }
	   }
	print "</table>\n";
}


#  Don't use report format below
exit;



sub GetIPName
{
my ($ip) = @_;
my (@ipdigit);

@ipdigit = ($ip=~/(\d+)\.(\d+)\.(\d+)\.(\d+)/);
$ip = sprintf ("%d.%d.%d.%d", @ipdigit);
#  Get DNS name
$name = gethostbyaddr(inet_aton($ip), AF_INET);
$name = "-" if ($name  eq "");
return $name;
}


sub GetLocation {
	local ($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";
}


#  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";
}
