#!/usr/bin/perl5.00502


#
# 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.
#

require 'config/paths.pl';
require 'config/saint.cf';
require 'perl/socket.pl';	# work around socket.ph problems
$name = $ARGV[0];
if (! $name) { die "Usage: $0 network-address\n"; }
if ($ARGV[1] =~ "FW") {
  $firewall_flag=1;
}
#
# hostname?
if ($name !~ /[0-9]+\.[0-9]+\.[0-9]+/) {
	($name, $aliases, $type, $len, @ip) = gethostbyname($name);
	($a,$b,$c,$d) = unpack('C4',$ip[0]);
	$name = "$a.$b.$c";
	}

#
# IP addr?  If four octets, chop off the last one
if ($name =~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/) {
	($name) = ($name =~ /^([0-9]+\.[0-9]+\.[0-9]+)\.[0-9]+/);
	}

# 3 octets of an ip address:
if ($name =~ /^[0-9]+\.[0-9]+\.[0-9]+$/) {
	for $i (1..255) { $args .= "$name.$i "; }
	}
# range of IP addresses:
elsif ($name =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)-(\d+)\.(\d+)\.(\d+)\.(\d+)$/) {
	# don't allow ranges beyond one class B network
	die "IP address range too large\n" if (($1 != $5) || ($2 != $6));
	# don't allow reversed ranges
	die "Null IP address range\n"
		if (($3 > $7) || (($3 == $7) && ($4 > $8)));
	for $i ($3..$7) {
		for $j ($4..$8) { $args .= "$1.$2.$i.$j "; }
		}
	}
# range of subnets:
elsif ($name =~ /^(\d+)\.(\d+)\.(\d+)-(\d+)\.(\d+)\.(\d+)$/) {
	# don't allow ranges beyond one class B network
	die "IP address range too large\n" if (($1 != $4) || ($2 != $5));
	# don't allow reversed ranges
	die "Null IP address range\n" if ($3 > $6);
	for $i ($3..$6) {
		for $j (1..255) { $args .= "$1.$2.$i.$j "; }
		}
	}
else { die "Can't figure out what to scan ($name)\n"; }


# spawn off fping, look at results
if ($firewall_flag) {
	die "Can't execute $FWPING" unless open(FPING, "$FWPING $args |");
}
 else {
	die "Can't execute $FPING" unless open(FPING, "$FPING $args |");
}

while (<FPING>) {
	chop;
	($target, $result) = /(\S+)\s+(.*)$/;
	if ($_ =~ /is unreachable/) { next; }
	if ($_ =~ /is alive/) {
		($a,$b,$c,$d) = split(/\./, $target);
		@ip = ($a,$b,$c,$d);
		# Hack alert!! Some libcs dump when ahost has many addresses.
		if (fork() == 0) {
			($name) = gethostbyaddr(pack("C4", @ip), &AF_INET);
			if ($name) { print "$name\n"; }
			else { print "$target\n"; }
			exit;
			}
		else { wait; }
		}
	}

close(FPING);
exit;
