Jonathan Worthington :: jnthn.net

A Perl Port Scanner

After ailing to possess a port scanner when a friend requested I give his machine a lookover to check he wasn't wide open, I decided to write one. The code below is what I came up with after some head scratching. It works great under Linux, but will blow up in ActivePerl under Windows if you have a version earlier than 5.8.

It uses forking to speed up the process, however only the parent forks and the moment, and it keeps creating kids until the system runs out of resources. When that happens it reaps all the kids that have been created. This means it can be somewhat of a resource hog. This will probably be fixed in the future. I believe it can be made to go faster by allowing some kids to fork further children, but this needs to be controlled very carefully, and is a job for another night. Anyway, the code. Actually, a more ethical note first...

NOTE: Do not portscan machines you do not have permission to. This is for educational purposes as well as checking machines you own or have permission to check. Remember that just because you host a website or have an account on a machine doesn't mean you have permission to portscan it.

#!/usr/bin/perl # #################################################################### # FORKING PERL PORT SCANNER # By Jonathan Worthington # This script is made available under the sames terms as Perl itself. # #################################################################### use strict; use warnings; use IO::Socket::INET; #Auto-flush. $| = 1; #Get host. my $host = $ARGV[0]; #Parent thread has no parent. my $parent = 0; #We need a place to store child PIDs. my @children = (); #Port scan host. print "Scanning $host...\n"; my $port; FORK: for ($port=1; $port<=65535; $port++) { #Fork. my $oldpid = $$; my $pid = fork; #If fork failed... if (not defined $pid) { #If resource is not available... if ($! =~ /Resource temporarily unavailable/) { #Reap children. &DoReap; #Retry this port. $port --; } else { #Otherwise, show the error. die "Can't fork: $!\n"; } } elsif ($pid == 0) { #This is the child. Save parent. $parent = $oldpid; #Clearup kids table. @children = (); #We don't want this thread to fork any more. last FORK; } else { #This is the parent. Store child pid to wait on it later. push @children, $pid; } } #If this is a child (i.e. it has a parent)... if ($parent) { #Attempt to connect to $host on $port. my $socket; my $success = eval { $socket = IO::Socket::INET->new( PeerAddr => $host, PeerPort => $port, Proto => 'tcp' ) }; #If the port was opened, say it was and close it. if ($success) { print "Port $port: Open\n"; shutdown($socket, 2); } #Exit. exit 0; } else { #If we're not the kid, we're the parent. Do a reap. &DoReap; } #This sub is the reaper. sub DoReap { while (my $child = shift @children) { waitpid $child, 0; } }

All content Copyright (C) Jonathan Worthington 2003-2005 unless otherwise stated.