use strict; use IO::Socket::INET; use IO::Select; use Time::HiRes qw(time); # timeout #perl scan.pl 82.94.215.151 -p 8080 # conn refused #perl scan.pl 127.0.0.1 -p 8080 # ok #perl scan.pl 82.94.215.151 -p 80 package RangeParser; ############################################################################## # handle parsing of ip/port ranges sub incmm { my ($p, $m)= @_; my $i=0 ; while ($i<@$p) { last if ($p->[$i]!=$m->[$i]); $p->[$i]= 0; $i++; } if ($i<@$p) { $p->[$i]++; return 1; } return undef; } sub expandrange { my $x= shift; if ($x =~ /^\d+$/) { return ($x); } elsif ($x =~ /^(\d+)-(\d+)$/) { return ($1 .. $2); } else { die "invalid list spec: $x\n"; } } sub expandlist { my @l; for my $r (split /,/, shift) { push @l, expandrange($r); } return @l; } sub expandip { my $ip= shift; return ($ip) if ($ip !~ /^[0-9,-]+\.[0-9,-]+\.[0-9,-]+\.[0-9,-]+$/) ; my @n; for my $x (split /\./, $ip) { push @n, [expandlist($x)]; } my @l; my @m= map { $#$_ } @n; my @p= map { 0 } @n; do { push @l, join(".", map { $n[$_][$p[$_]] } 0..$#p); } while (incmm(\@p, \@m)); return @l; } sub new { my ($class, $ports, @ips)= @_; my $self= bless { p=>[expandlist($ports)], h=>[map { expandip($_) } @ips], ip=>0, ih=>0, }, $class; return $self; } sub next { my ($self)= @_; my ($h, $p)= ($self->{h}[$self->{ih}], $self->{p}[$self->{ip}]); if (++$self->{ih} == @{$self->{h}}) { $self->{ih}= 0; $self->{ip}++ if ($self->{ip} < @{$self->{p}}); } printf("ret: %s:%d\n", $h, $p); return ($h, $p); } sub havemore { my ($self)= @_; return $self->{ip}<@{$self->{p}} && $self->{ih}<@{$self->{h}}; } package main; use strict; use Getopt::Long; ############################################################################## my %reg; sub newsock { my ($ip, $port)= @_; my $sock= IO::Socket::INET->new(PeerAddr=>$ip, PeerPort=>$port, Blocking=>0); $reg{$sock}= { k=>$sock, t=>time(), d=>"$ip:$port", rd=>\&handle_connectfailed, er=>\&handle_connectfailed, wr=>\&handle_connected, }; printf("created %s\n", $reg{$sock}{d}); return $sock; } sub handle_connected { my ($self)= @_; $self->{tm_c}= time(); printf("connected to %s %f\n", $self->{d}, $self->{tm_c}-$self->{t}); $self->{rd}=\&handle_read; $self->{wr}=\&handle_write; return 1; } sub handle_connectfailed { my ($self)= @_; printf("failed to %s : %d\n", $self->{d}, $self->{k}->sockopt(SO_ERROR)); return -1; } sub handle_read { my ($self)= @_; my $data; my $n= sysread($self->{k}, $data, 1600); printf("read from %s: %s\n", $self->{d}, unpack("H*", $data)) if length($data); if ($n==0) { delete $self->{rd}; printf("sock: EOF\n"); return 0; } return 1; } sub handle_write { my ($self)= @_; $self->{k}->print("GET /xxxxxxxxx HTTP/1.0\r\nHost: localhost\r\n\r\n"); printf("sock: wrote\n"); delete $self->{wr}; return 0; } my $ports; GetOptions("p=s" => \$ports); my $iter= RangeParser->new($ports, @ARGV); my $maxconn= 200; my $selw= IO::Select->new(); my $selr= IO::Select->new(); while ($iter->havemore() || $selr->count || $selw->count) { while ($selr->count<$maxconn && $selw->count<$maxconn && $iter->havemore()) { my $s= newsock($iter->next()); $selr->add($s); $selw->add($s); printf("added\n"); } my ($r, $w, $e)= IO::Select::select($selr, $selw, undef, 1); for my $s (@$r) { printf("r %s\n", $s); if (exists $reg{$s} && $reg{$s}{rd}) { my $r= $reg{$s}{rd}->($reg{$s}); $selr->remove($s) if ($r<=0); $selw->remove($s) if ($r<0); if (!$selr->exists($s) && !$selw->exists($s)) { delete $reg{$s}; printf("r -> deleted\n"); } } } for my $s (@$w) { printf("w %s\n", $s); if (exists $reg{$s} && $reg{$s}{wr}) { my $r= $reg{$s}{wr}->($reg{$s}); $selw->remove($s) if ($r<=0); $selr->remove($s) if ($r<0); if (!$selr->exists($s) && !$selw->exists($s)) { delete $reg{$s}; printf("w -> deleted\n"); } } } while (my ($k, $s) = each %reg) { #printf("t=%f, %s %s\n", time(), $k, join(", ", map { sprintf("%s:%s", $_, $s->{$_}) } keys %$s)); if (time() - $s->{t} > 5) { printf("timedout %s\n", $s->{d}); $selw->remove($s->{k}); $selr->remove($s->{k}); delete $reg{$s->{k}}; } } # for my $s (@$e) { # printf("e\n"); # if (!$reg{$s}{er}->($reg{$s})) { # $sel->remove($s); # delete $reg{$s}; # printf("e -> deleted\n"); # } # } printf("--- %d %d %d\n", $selr->count, $selw->count, scalar keys %reg); }