patternMinor
Simple socket communication port scanner
Viewed 0 times
simplecommunicationscannerportsocket
Problem
At the moment,
Going forward, the next option I would like to add is a pretty printer for the open ports (IE, if it returns 22, 80, 4200, 4201, 4202, then that should print 22, 80, 4200-4202) and potentially make this async.
prtscn takes a server, and a list of port ranges to test (of the form x,y-z,a-c,d,e etc). The -v option will show all ports being scanned, and the -r option will return the first open port in the list. If -r is passed, the list of port ranges is optional. In this case, if the port list is missing, -r will just return the first open port on the target OS.Going forward, the next option I would like to add is a pretty printer for the open ports (IE, if it returns 22, 80, 4200, 4201, 4202, then that should print 22, 80, 4200-4202) and potentially make this async.
#!/usr/bin/perl
# prtscn.pl - scan server for open
# TODO
# Make async
# if any ports are sequential, form a range (ie if 22, 4200, 4201, and 4202 are open, print 22, 4200-4202)
use strict;
use warnings;
use IO::Socket;
use Getopt::Long;
my $verbosity = undef;
my $use_first_port = undef;
GetOptions('v' => \$verbosity, 'u' => \$use_first_port);
sub scan {
my $server = shift;
my $start_port = shift;
my $end_port = shift;
my @open_ports = ();
while($start_port-1 new(PeerHost => $server, PeerPort => $start_port);
if($sock) {
push @open_ports, $start_port;
if($use_first_port) {
return @open_ports;
}
}
$start_port += 1;
}
return @open_ports;
}
my $server = shift || die "err: need server.\n";
my $range_list = shift || "1-65536";
my @ranges = split /,/, $range_list || die "err: need range list.\n";
my @open_ports = ();
foreach my $range (@ranges) {
my ($start, $end) = (undef, undef);
if($range =~ /-/) {
($start, $end) = split /-/, $range;
} else {
($start, $end) = ($range, $range);
}
push @open_ports, scan($server, $start, $end);
}
if(scalar @open_ports != 0) {
print "open port(s): ${\join ', ', sort @open_ports}\n";
} else {
print "no ports listed are open.\n";
}Solution
Layout
Place the
I prefer to only import the necessary functions form the modules (in the
You only use one function from
Variable declarations
There is no need to explicitly initialize variables to
It is not possible to hit the
Comments
Remove the unnecessary
Style
Add space between control keywords (
Use single quotes to avoid unnecessary variable interpolation for the
There is no need for
The
Use
User documentation and argument checking
Add usage documentation with the standard Pod::Usage module and plain old documentation (POD). Move argument parsing and checking to it's own sub. Check the return value of GetOptions for enhanced argument checking.
Your question text mentions the
Here is new code with the suggestions above:
Place the
scan sub at the end of the file instead of in the middle of the procedural code.use as little as possibleI prefer to only import the necessary functions form the modules (in the
use lines). Since you are not using anything from IO::Socket, you can pass it the empty list: qw().You only use one function from
Getopt::Long.Variable declarations
There is no need to explicitly initialize variables to
undef or the empty list in your code.It is not possible to hit the
die statement on the @ranges line.Comments
Remove the unnecessary
TODO comments.Style
for is simlper than foreach.Add space between control keywords (
if, while) and the opening paren (.Use single quotes to avoid unnecessary variable interpolation for the
$range_list default string.There is no need for
scalar.The
\join code is a little hard to understand.Use
++ instead of += 1.User documentation and argument checking
Add usage documentation with the standard Pod::Usage module and plain old documentation (POD). Move argument parsing and checking to it's own sub. Check the return value of GetOptions for enhanced argument checking.
Your question text mentions the
-r option, but your code uses -u instead.Here is new code with the suggestions above:
use strict;
use warnings;
use IO::Socket qw();
use Getopt::Long qw(GetOptions);
use Pod::Usage qw(pod2usage);
my $verbosity;
my $use_first_port;
my $server;
my $range_list;
parse_args();
my @ranges = split /,/, $range_list;
my @open_ports;
for my $range (@ranges) {
my ($start, $end);
if ($range =~ /-/) {
($start, $end) = split /-/, $range;
} else {
($start, $end) = ($range, $range);
}
push @open_ports, scan($server, $start, $end);
}
if (@open_ports) {
print 'open port(s): ', (join ', ', sort @open_ports), "\n";
} else {
print "no ports listed are open.\n";
}
exit;
sub scan {
my $server = shift;
my $start_port = shift;
my $end_port = shift;
my @open_ports;
while (($start_port - 1) new(PeerHost => $server, PeerPort => $start_port);
if ($sock) {
push @open_ports, $start_port;
if ($use_first_port) {
return @open_ports;
}
}
$start_port++;
}
return @open_ports;
}
sub parse_args {
my $help;
GetOptions(
'help' => \$help,
'v' => \$verbosity,
'u' => \$use_first_port,
) or pod2usage();
$help and pod2usage(-verbose => 2);
if (@ARGV) {
$server = shift @ARGV;
$range_list = (shift @ARGV) || '1-65536';
} else {
pod2usage('Error: server required.');
}
@ARGV and pod2usage("Error: unexpected args: @ARGV");
}
=head1 NAME
prtscn
=head1 SYNOPSIS
prtscn [options] server [range]
Options:
-help Verbose help
-v Verbose output for debug
-u Use first port
=head1 DESCRIPTION
Show ports for a server.
=head1 ARGUMENTS
=over 4
=item B
A server is required.
=item B
A range is optional. The default range is C
=back
=head1 OPTIONS
All options can be abbreviated.
=over 4
=item B
Show verbose output on stdout
prtscn -v 0.0.0.0
=item B
By default, show all ports in the range.
To show just the first port in the range, use the C option.
prtscn -u 0.0.0.0 111-555
=item B
Show verbose usage information.
=back
=cutCode Snippets
use strict;
use warnings;
use IO::Socket qw();
use Getopt::Long qw(GetOptions);
use Pod::Usage qw(pod2usage);
my $verbosity;
my $use_first_port;
my $server;
my $range_list;
parse_args();
my @ranges = split /,/, $range_list;
my @open_ports;
for my $range (@ranges) {
my ($start, $end);
if ($range =~ /-/) {
($start, $end) = split /-/, $range;
} else {
($start, $end) = ($range, $range);
}
push @open_ports, scan($server, $start, $end);
}
if (@open_ports) {
print 'open port(s): ', (join ', ', sort @open_ports), "\n";
} else {
print "no ports listed are open.\n";
}
exit;
sub scan {
my $server = shift;
my $start_port = shift;
my $end_port = shift;
my @open_ports;
while (($start_port - 1) < $end_port) {
if ($verbosity) {
print "scanning port $start_port.\n";
}
my $sock = IO::Socket::INET->new(PeerHost => $server, PeerPort => $start_port);
if ($sock) {
push @open_ports, $start_port;
if ($use_first_port) {
return @open_ports;
}
}
$start_port++;
}
return @open_ports;
}
sub parse_args {
my $help;
GetOptions(
'help' => \$help,
'v' => \$verbosity,
'u' => \$use_first_port,
) or pod2usage();
$help and pod2usage(-verbose => 2);
if (@ARGV) {
$server = shift @ARGV;
$range_list = (shift @ARGV) || '1-65536';
} else {
pod2usage('Error: server required.');
}
@ARGV and pod2usage("Error: unexpected args: @ARGV");
}
=head1 NAME
prtscn
=head1 SYNOPSIS
prtscn [options] server [range]
Options:
-help Verbose help
-v Verbose output for debug
-u Use first port
=head1 DESCRIPTION
Show ports for a server.
=head1 ARGUMENTS
=over 4
=item B<server>
A server is required.
=item B<range>
A range is optional. The default range is C<1-65536>
=back
=head1 OPTIONS
All options can be abbreviated.
=over 4
=item B<-v>
Show verbose output on stdout
prtscn -v 0.0.0.0
=item B<-u>
By default, show all ports in the range.
To show just the first port in the range, use the C<-u> option.
prtscn -u 0.0.0.0 111-555
=item B<-help>
Show verbose usage information.
=back
=cutContext
StackExchange Code Review Q#125677, answer score: 2
Revisions (0)
No revisions yet.