#!/usr/bin/perl
# multi-thread front end for whisker 			v1.3.0
# by rain forest puppy   <rfp@wiretrip.net>   http://www.wiretrip.net

# YOU MAY WANT TO MODIFY THESE


# how many whiskers to run in parallel
# (remember, you are running ($MAX_KIDS * 2) + 1 perl interpreters)
$MAX_KIDS=5;


# path to whisker...assume local directory
$path_to_whisker="./whisker.pl";


# END OF USER MODIFICATION SECTION
###################################################################

use POSIX; use Getopt::Std;

# save parameters for passing to whisker
foreach $arg (@ARGV) {
	$params.= "$arg ";}

# has to match whisker's so getopts won't bitch
getopts("fs:n:vdh:l:H:Vu:iI:A:S:EF:p:M:UL:a:W", \%args);

$|=1;
%kids=(); $kids=0;
@hosts_to_scan=();
$whisker_version="1.3.0";

print "-- whisker / v$whisker_version / rain.forest.puppy / ADM / wiretrip --\n\n";

# we take over the -l command
if (defined $args{l}){
	print "Error: you can't specify -l option with threaded frontend\n";
	exit;}

# we take over the -F command
if (defined $args{F}){
	print "Error: you can't specify -F option with threaded frontend\n";
	exit;}

###################################################################
# figure out our host list

$nmapfile       =$args{n} if defined($args{n});
$singlehost     =$args{h} if defined($args{h});
$hostsfile      =$args{H} if defined($args{H});

if (defined($args{H})){
        if(!(-e $hostsfile)){ die("How about specifing a file that exists?!?\n");}
        open(VIN,"<$hostsfile");
        while(<VIN>){ s/[^-a-zA-Z0-9\.]//g;
                push @hosts_to_scan, $_;}
        close(VIN);}

if (defined($args{h})){ push @hosts_to_scan, $singlehost;}

if (defined($args{n})){
# read in nmap on generate the list of hosts to scan
open(NMAP, "<$nmapfile"); #yes, this is scary.  Don't worry about it.
while(<NMAP>){ %udp=%tcp=(), $udp=$tcp=0, $Index=$OS=$IP=$Name=$Host="";
next if(m/^#/);
(($$3{$1}=$2) && $$3++) while(m#([0-9]+)/([a-z]+)/(udp|tcp)/\w*/\w*///[,]*#g);
$$1=$2 while(m#([^ \t\n:]+):\W*([^\t\n]+)#g);
(($Smurf=$1) && next) if (m#Status: Smurf\W*\(\W*([0-9]+)#);
($Host=~m#([\w\.]+)\W*\(([^ \t]*)\)#) && (($IP=$1) && ($Name=$2));

if($tcp{80} eq "open"){ push @hosts_to_scan, $IP;}
} # end the while(<nmap>) loop
close(NMAP);
} #end if defined $args{n}

####################################################################
# host processing stuff

# make the list unique
%uniq_hosts=();
foreach $host (@hosts_to_scan){
	$uniq_hosts{$host}=1;}
@hosts_to_scan=();
foreach $host (keys %uniq_hosts){
	push(@hosts_to_scan,$host);}

# removes any blank entries

@precheck=@hosts_to_scan;
@hosts_to_scan=();
foreach $check (@precheck){
	push(@hosts_to_scan, $check) if ($check!~/^[\r\n]*$/);}

$hostcount=@hosts_to_scan;

if($hostcount < 1){ print "= No hosts found to scan!\n\n"; exit;}

print "- MULTI-THREAD: Checking $hostcount hosts\n";

####################################################################
# main spawing loop

if($hostcount < $MAX_KIDS){
	$MAX_KIDS=$hostcount;}

for (1 .. $MAX_KIDS) {
	$check_host=pop @hosts_to_scan;
	$hostcount--;
	spawn($check_host);}

$SIG{CHLD}=\&deadkid;
$SIG{INT}=\&killfamily;

while($hostcount > 0) {
	sleep;
	for ($i = $kids; $i < $MAX_KIDS; $i++){
	$check_host=pop @hosts_to_scan;
	$hostcount--;
	spawn($check_host);}}

#print "- MULTI-THREAD: All done--waiting for kids to finish\n";

while($kids >0){
	sleep;}

print "- MULTI-THREAD: All done.\n";
exit; # assumed

####################################################################

sub spawn {
	my $target=shift;
	my $pid;
	my $sigset= POSIX::SigSet->new(SIGINT);
	sigprocmask(SIG_BLOCK, $sigset) or die ("Sigprocmask: $!\n");
	die ("fork: $!\n") unless defined ($pid=fork);

	if($pid){ # parent
		sigprocmask(SIG_UNBLOCK,$sigset) or
			die ("Sigprocmask-unset: $!\n");
		$kids{$pid}=1;
		$kids++;
		return;
	} else { # kid
		$SIG{INT} = 'DEFAULT';
		sigprocmask(SIG_UNBLOCK, $sigset) or 
			die ("Sigprocmask-unset: $!\n");
		system("$path_to_whisker $params -l log.$$ -F $target");
		open(IN,"<log.$$");
		print <IN>;
		close(IN);
		unlink "log.$$";
		exit;
	}}

sub killfamily {
	local($SIG{CHLD})='IGNORE';
	kill 'INT' => keys %kids;
	exit;}

sub deadkid {
	$SIG{CHLD} = \&deadkid;
	my $pid = wait;
	$kids--;
	delete $kids{$pid};}
