#!/usr/bin/perl5.8.4
#
# Scan a subnet for live hosts
#
# If given IP address or hostname, scan everything on that subnet.
# If given partial IP address (e.g. 140.174.97), do the same.
#
# Method used:
#
#  Use fping to scan the net; any hits send to gethostbyaddr to
# get the hostname.  This will print out a list of hostname and/or
# IP addresses that are alive, one per line.
#
# Modified by Advanced Research Corporation to include subnet mask
# Modified by Advanced Research Corporation to include subnet range
#

require 'config/paths.pl';
require 'config/sara.cf';
require 'perl/socket.pl';	# work around socket.ph problems
require 'perl/misc.pl';
@mask= (0, 128, 192, 224, 240, 248, 252, 254, 255);
%all_targets = "";
#
# Test to see if this a range argument (IP - IP)
#
$range  = 0;
$range0 = 0;
$range1 = 255;
($first_ip, $last_ip) = split("-", $ARGV[0]);
if ($last_ip =~ /^\d+\.\d+\.\d+\.\d+$/) {
  if ($first_ip =~ /^\d+\.\d+\.\d+\.\d+$/) {
    $attack_proximate_subnets=1;
    ($a,$b,$c,$d) = split('\.', $first_ip);
    ($A,$B,$C,$D) = split('\.', $last_ip);
    if ($a == $A) {
      if ($b == $B) {
        if ($c == $C) {
          if ($d <= $D) {
            if ($D <= 255) {
              $range  = 1;
              $range0 = $d;
              $range1 = $D;
            } else {
              die "Illegal IP range; last IP incorrect";
            }
          } else {
            die "Illegal IP range; last IP incorrect";
          }
        } else {
          die "Illegal IP range; range spans more than a Class C subnet";
        }
      } else {
        die "Illegal IP range; range spans more than a Class C subnet";
      }
    } else {
      die "Illegal IP range; range spans more than a Class C subnet";
    }
#  } else {
#    die "Illegal IP range; first IP not correct ($first_ip";
  }
#} else {
#  die "Illegal IP range; second IP not correct ($last_ip)" if $last_ip;
}
#
# Test to see if this is a supernet/subnet definition
#  
 
($name, $subnet) = split("/", $ARGV[0]);

if ($subnet && $range == 1) {
  die "Cannot mix range and subnet definitions";
}

if (! $subnet){
   $cmult=0;
   $cmask=255; 
   $dmult=255; 
   $dmask=0; 
}
elsif ($subnet >= 16 && $subnet <= 24) {
   $cmult=(2 ** (24-$subnet))-1;
   $cmask=$mask[$subnet-16];
   $dmult=255;
   $dmask=0;
}
elsif ($subnet > 24 && $subnet <=32) {
   $cmult=0;
   $cmask=255;
   $dmult=(2 ** (32-$subnet))-1;
   $dmask=$mask[$subnet-24];
}
else
{
   die "SARA can not process sub Class B networks\n";
}
if (! $name) { die "Usage: $0 network-address\n"; }
 foreach (keys %ENV) {
   $firewall_flag=$ENV{SARA_FW}; 
}
#
# hostname?
if ($name !~ /[0-9]+\.[0-9]+\.[0-9]+/) {
	($name, $aliases, $type, $len, @ip) = gethostbyname($name);
	($a,$b,$c,$d) = unpack('C4',$ip[0]);
	$c = ($c & $cmask);
	$d = ($d & $dmask);
	$name = "$a.$b.$c.$d";
	}
else {
	($a, $b, $c, $d)  = split('\.',"$name");
	$c = ($c & $cmask);
	$d = ($d & $dmask);
	$name = "$a.$b.$c.$d";
}
for $_c ($c..($c+$cmult)) {
     $name = "$a.$b.$_c.$d";
     if ($name =~ /^[0-9]+\.[0-9]+\.[0-9]+/) {
	$args = "";
	for $_d ($d..$d+$dmult) { 
         if ($_d >= $range0) {
           if ($_d <= $range1) {
              $ip     = "$a.$b.$_c.$_d";
              $args  .= "$a.$b.$_c.$_d "; 
              $mac = &ip2mac($ip);
              if ( $mac =~ /[0-9A-F]+\:/i) {
                ($aa,$bb,$cc,$dd) = split(/\./, $ip);
                @ip = ($aa,$bb,$cc,$dd);
                ($name) = gethostbyaddr(pack("C4", @ip), &_AF_INET);
                $all_targets{$ip}{'NAME'}  = $name;
                $all_targets{$ip}{'MAC'}   = $mac;
              }
           }
         }
        }
      }
      else { die "Can't figure out what to scan ($name)\n"; }
# spawn off fping, look at results
	die "Can't execute $FPING" unless open(FPING, "$FPING $args |");

while (<FPING>) {
	chop;
	($target, $result) = /(\S+)\s+(.*)$/;
	if ($_ =~ /is unreachable/) { next; }
	if ($_ =~ /is alive/) {
		($aa,$bb,$cc,$dd) = split(/\./, $target);
		@ip = ($aa,$bb,$cc,$dd);
		# Hack alert!! Some libcs dump when ahost has many addresses.
		($name) = gethostbyaddr(pack("C4", @ip), &_AF_INET);
		$all_targets{$target}{'NAME'}  = $name;
                $all_targets{$target}{'FPING'} = $result;
	}
}

close(FPING);
die "Can't execute $FWPING" unless open(FPING, "$FWPING $args |");
while (<FPING>) {
        chop;
        ($target, $result) = /(\S+)\s+(.*)$/;
        if ($_ =~ /is unreachable/) { next; }
        if ($_ =~ /is alive/) {
                $all_targets{$target}{'FWPING'} = $result;
		if (! $all_targets{$target}{'NAME'}) {
                  ($aa,$bb,$cc,$dd) = split(/\./, $target);
                   @ip = ($aa,$bb,$cc,$dd);
                  ($name) = gethostbyaddr(pack("C4", @ip), &_AF_INET);
                  $all_targets{$target}{'NAME'}  = $name;
                }
        }
}
close(FPING);
}
for $ip (keys %all_targets) {
  if ($ip =~ /^[0-9]+\.[0-9]+\.[0-9]+/) {
    $name   = $all_targets{$ip}{'NAME'};
    $name   = $ip if !$name;
    $fping  = $all_targets{$ip}{'FPING'};
    $fwping = $all_targets{$ip}{'FWPING'};
    $mac    = $all_targets{$ip}{'MAC'};
    $status ="";
    $status = "alive" if ($fping && $fwping);
    $status = "fw-0" if (!$fping && $fwping);
    $status = "fw-1" if ($fping && !$fwping);
    $status = "fw-2" if ($fwping =~ /firewall/);
    $status = "fw-3" if (!$status && $mac =~ /[0-9A-F]+\:/i);
    print "$name|$ip|$mac|$status\n";
#  print "   $all_targets{$ip}{'FPING'}\n";
#  print "   $all_targets{$ip}{'FWPING'}\n\n";
  }
}
exit;
