#!/usr/bin/perl

########################################################
# ftpcheck v0.3                       [November 2, 1998] 
#          http://david.weekly.org/code
#
# by David Weekly <dew@cs.stanford.edu>
#
# Thanks to Shane Kerr for cleaning up the process code!
#
# This code is under the GPL. Use it freely. Enjoy.
#        Debug. Enhance. Email me the patches!
########################################################

use Socket;
use Net::FTP;

# how many simultaneous processes are we allowed to use?
my $MAX_PROCESSES=10;

# timeouts in seconds for creating a socket and connecting
my $MAX_SOCKET_TIME = 2;
my $MAX_CONNECT_TIME = 3;

my $HELP=q
{Usage: ftpcheck [-h | --help] host
};

my @hosts;

for $_ (@ARGV){
    if(/^--(.*)/){
	$_=$1;
	if(/help/){
	    print $HELP;
	    exit(0);
	}
    }
    elsif(/^-(.*)/){
	$_=$1;
	if(/^h/ or /^?/){
	    print $HELP;
	    exit(0);
	}
    }else{
	push @hosts,$_;
    }
}

if(!$hosts[0]){
    print $HELP;
    exit(-1);
}

my $host;
$|=1;
print "ftpcheck v0.3 by dave weekly <dew\@cs.stanford.edu>\n\n";

# go through all of the hosts, replacing subnets with all contained IPs.
for $host (@hosts){
    $_=shift(@hosts);

    # scan a class C
    if(/^([^.]+)\.([^.]+)\.([^.]+)$/){
	my $i;
	print "Expanding class C $_\n";
	for($i=1;$i<255;$i++){
	    my $thost="$_.$i";
	    push @hosts,$thost;
	}
    }
    else{
	push @hosts,$_;
    }
}

my @pids;
my $npids=0;

for $host (@hosts){
    my $pid;
    $pid=fork();
    if($pid>0){
	$npids++;
	if($npids>=$MAXPIDS){
	    for(1..($MAXPIDS/2)){
		$wait_ret=wait();
		if($wait_ret>0){
		    $npids--;
		}
	    }
	}
	next;
    }elsif(undef $pid){
	print "fork error\n";
	exit(0);
    }else{
	my($proto,$port,$sin,$ip);

	$0="(checking $host)";

	# kill thread on timeout
	local $SIG{'ALRM'} = sub { exit(0); };

	alarm $MAX_SOCKET_TIME;
	$proto=getprotobyname('tcp');
	$port=21;
	$ip=inet_aton($host);
	if(!$ip){
	    print "couldn't find host $host\n";
	    exit(0);
	}
	$sin=sockaddr_in($port,$ip);
	socket(Sock, PF_INET, SOCK_STREAM, $proto);

	alarm $MAX_CONNECT_TIME;
	if(!connect(Sock,$sin)){
	    exit(0);
	}
	my $iaddr=(unpack_sockaddr_in(getpeername(Sock)))[1];
	close(Sock);       
	
	# SOMETHING is listening on the FTP port...really ftp server?
	
	alarm 0;
	$hostname=gethostbyaddr($iaddr,AF_INET);

	$ftp = Net::FTP->new($host);
	if(!$ftp){
	    print "      <$host ($hostname) denied you>\n";
	    exit(0);
	}
	if(!$ftp->login("anonymous","just\@checking.com")){
	    print "   FTP on $host [$hostname]\n";
	    exit(0);
	}

	print "Anon FTP on $host [$hostname]\n";

	$ftp->quit;
	exit(0);
    }
}

print "done spawning, $npids children remain\n";

# wait for my children
for(1..$npids){
    my $wt=wait();
    if($wt==-1){
	print "hey $!\n";
	redo;
    }
}

print "Done\n";
