#!/usr/bin/perl
use strict;
use blib;
use Nmap::Parser;
use File::Spec;
use Cwd;
use Test::More tests => 177;
use constant HOST1 => '127.0.0.1';
use constant HOST2 => '127.0.0.2';
use constant HOST3 => '127.0.0.3';
use constant HOST4 => '127.0.0.4';
my @UP_HOSTS = ( HOST1, HOST3, HOST4 );
my @DOWN_HOSTS = (HOST2);
use constant TOTAL_HOSTS => 4;
use constant TEST_FILE => 'nmap_results.xml';
use vars qw($host $np $session $svc $os $FH);
$FH = File::Spec->catfile( cwd(), 't', TEST_FILE );
$FH = File::Spec->catfile( cwd(), TEST_FILE ) unless ( -e $FH );
my $np = new Nmap::Parser;
parser_test();
session_test();
host_1();
host_2();
host_3();
host_4();
sub parser_test {
ok( $np->parsefile($FH), "Parsing nmap data: $FH" );
#TESTING GET_IPS()
is_deeply(
[ $np->get_ips() ],
[ HOST1, HOST2, HOST3, HOST4 ],
'Testing get_ips for correct number of hosts'
);
is_deeply(
[ $np->get_ips('up') ],
[ HOST1, HOST3, HOST4 ],
'Testing get_ips for correct hosts with status = up'
);
is_deeply( [ $np->get_ips('down') ],
[HOST2], 'Testing get_ips for correct hosts for with status = down' );
#TESTING IPV4_SORT
my @hosts = ( HOST3, HOST1, HOST4, HOST2 );
is_deeply(
[ $np->addr_sort(@hosts) ],
[ HOST1, HOST2, HOST3, HOST4 ],
'Testing addr_sort'
);
#TESTING ALL_HOSTS()
my $total_hosts = 0;
for my $h ( $np->all_hosts() ) {
isa_ok( $h, 'Nmap::Parser::Host' );
isnt( $h->status, undef,
'Testing host object ' . $h->addr . ' in all_hosts()' );
$total_hosts++;
}
is( $total_hosts, TOTAL_HOSTS,
"Testing correct number of hosts with all_hosts()" );
#TESTING ALL_HOSTS(UP)
my $total_uphosts = 0;
for my $h ( $np->all_hosts('up') ) {
isnt( $h->addr, HOST2,
'Testing host ' . $h->addr . ' in all_hosts(up)' );
$total_uphosts++;
}
is( $total_uphosts, scalar(@UP_HOSTS),
"Testing correct number of UP hosts with all_hosts(up)" );
#TESTING ALL_HOSTS(DOWN)
my $total_downhosts = 0;
for my $h ( $np->all_hosts('down') ) {
is( $h->addr, HOST2, 'Testing host ' . $h->addr . ' in all_hosts(up)' );
$total_downhosts++;
}
is( $total_downhosts, scalar(@DOWN_HOSTS),
"Testing correct number of DOWN hosts with all_hosts(down)" );
}
sub session_test {
isa_ok( $session = $np->get_session(), 'Nmap::Parser::Session' );
is(
$session->numservices(),
1023 + 1023,
'Session: total number of services'
);
is( $session->numservices('connect'),
1023, 'Session: numservices type = connect' );
is( $session->numservices('udp'), 1023, 'Session: numservices type = udp' );
is( $session->start_time(), 1057088883, 'Session: start_time' );
is(
$session->start_str(),
'Tue Jul 1 14:48:03 2003',
'Session: start_str'
);
is( $session->finish_time(), 1057088900, 'Session: finish_time' );
is( $session->time_str(), 'Tue Jul 1 14:48:20 2003', 'Session: time_str' );
is( $session->nmap_version(), '3.80', 'Session: nmap_version' );
is( $session->xml_version(), '1.04', 'Session: xml_version' );
is(
$session->scan_args(),
'nmap -v -v -v -oX test.xml -O -sTUR -p 1-1023 127.0.0.[1-4]',
'Session: scan_args'
);
is( $session->scan_type_proto(), undef, 'Session: scan_type_proto()' );
is( $session->scan_type_proto('connect'),
'tcp', 'Session: scan_type_proto(connect)' );
is( $session->scan_type_proto('udp'),
'udp', 'Session: scan_type_proto(udp)' );
is_deeply(
[ $session->prescripts ],
[ 'broadcast-dropbox-listener' ],
'Session has prescripts' );
like( $session->prescripts('broadcast-dropbox-listener')->{output},
qr/\ndisplayname .* 10422330$/s,
'Prescript output correct' );
is_deeply(
[ $session->postscripts ],
[ 'ssh-hostkey' ],
'Session has postscripts' );
like( $session->postscripts('ssh-hostkey')->{output},
qr/Possible .* 192.168.1.124$/s,
'Postscript output correct' );
}
sub host_1 {
isa_ok( $host = $np->get_host(HOST1), 'Nmap::Parser::Host',
'GET ' . HOST1 );
is( $host->status, 'up', 'Host1: status' );
is( $host->addr, HOST1, 'Host1: addr' );
is( $host->addrtype, 'ipv4', 'Host1: addrtype' );
is( $host->ipv4_addr, HOST1, 'Host1: ipv4_addr' );
is( $host->mac_addr, '00:09:5B:3F:7D:5E', 'Host1: mac_addr' );
is( $host->mac_vendor, 'Netgear', 'Host1: mac_vendor' );
is( $host->hostname, 'host1', 'Host1: hostname()' );
is( $host->hostname(0), $host->hostname, 'Host1: hostname(0)' );
is( $host->hostname(1), 'host1_2', 'Host1: hostname(1)' );
is_deeply(
[ $host->all_hostnames() ],
[ 'host1', 'host1_2' ],
'Host1: all_hostnames'
);
#Testing Port Information
is( $host->extraports_state(), 'closed', 'Host1: extraports_state' );
is( $host->extraports_count(), 2038, 'Host1: extraports_count' );
is( $host->tcp_port_count(), 8, 'Host1: tcp_port_count' );
is( $host->udp_port_count(), 2, 'Host1: udp_port_count' );
is_deeply(
[ $host->tcp_ports() ],
[qw(22 25 80 111 443 555 631 4903)],
'Host1: tcp_ports()'
);
is_deeply(
[ $host->tcp_ports('open') ],
[qw(80 111 443 555 631)],
'Host1: tcp_ports(open)'
);
is_deeply( [ $host->tcp_ports('closed') ],
[qw(4903)], 'Host1: tcp_ports(closed)' );
is_deeply( [ $host->tcp_ports('filtered') ],
[qw(22 25 555)], 'Host1: tcp_ports(filtered)' );
is_deeply( [ $host->tcp_ports('open|filtered') ],
[qw(555)], 'Host1: tcp_ports(open|filtered)' );
is_deeply( [ $host->udp_ports() ], [qw(111 937)], 'Host1: udp_ports()' );
is_deeply( [ $host->udp_ports('open') ],
[qw(111)], 'Host1: udp_ports(open)' );
is_deeply( [ $host->udp_ports('filtered') ],
[qw(937)], 'Host1: udp_ports(filtered)' );
is_deeply( [ $host->udp_ports('closed') ],
[qw()], 'Host1: udp_ports(closed)' );
is( $host->tcp_ports('open'),
$host->tcp_open_ports(), 'Host1: tcp_open_ports' );
is(
$host->tcp_ports('filtered'),
$host->tcp_filtered_ports(),
'Host1: tcp_filtered_ports'
);
is(
$host->tcp_ports('closed'),
$host->tcp_closed_ports(),
'Host1: tcp_closed_ports'
);
is( $host->udp_ports('open'),
$host->udp_open_ports(), 'Host1: udp_open_ports' );
is(
$host->udp_ports('filtered'),
$host->udp_filtered_ports(),
'Host1: udp_filtered_ports'
);
is(
$host->udp_ports('closed'),
$host->udp_closed_ports(),
'Host1: udp_closed_ports'
);
$host->tcp_del_ports('80');
is_deeply( [ $host->tcp_ports('open') ],
[qw(111 443 555 631)],
'Host1: tcp_del_ports(80) (should not be open)' );
$host->tcp_del_ports( 111, 443 );
is_deeply( [ $host->tcp_ports('open') ],
[qw(555 631)], 'Host1: tcp_del_ports(111,443) (should not be open)' );
is_deeply( [ $host->tcp_ports() ],
[qw(22 25 555 631 4903)],
'Host1: tcp_ports() after deleting 80,111,443' );
is( $host->uptime_seconds(), '1973', 'Host1: uptime_seconds' );
is(
$host->uptime_lastboot(),
'Tue Jul 1 14:15:27 2003',
'Host1: uptime_lastboot'
);
is( $host->tcpsequence_index(), 4336320, 'Host1: tcpsequence_index' );
is(
$host->tcpsequence_class(),
'random positive increments',
'Host1: tcpsequence_class'
);
is(
$host->tcpsequence_values(),
'B742FEAF,B673A3F0,B6B42D41,B6C710A1,B6F23FC4,B72FA3A8',
'Host1: tcpsequence_values'
);
is( $host->ipidsequence_class(), 'All zeros', 'Host1: ipidsequence_class' );
is( $host->ipidsequence_values(),
'0,0,0,0,0,0', 'Host1: ipidsequence_values' );
is( $host->tcptssequence_class(), '100HZ', 'Host1: tcptssequence_class' );
is(
$host->tcptssequence_values(),
'30299,302A5,302B1,302BD,302C9,302D5',
'Host1: tcptssequence_values'
);
is( $host->distance(), 1, 'Host1: distance = 1' );
isa_ok( $np->del_host(HOST1), 'Nmap::Parser::Host', 'DEL ' . HOST1 );
is( $np->get_host(HOST1), undef, 'Testing deletion of ' . HOST1 );
#TESTING SERVICE OBJECT FOR HOST 1
my $svc;
isa_ok( $svc = $host->tcp_service(22),
'Nmap::Parser::Host::Service', 'TCP port 22' );
is( $svc->name, 'ssh', 'Service: name' );
is( $svc->method, 'table', 'Service: method' );
is( $svc->confidence, 3, 'Service: confidence' );
isa_ok( $svc = $host->udp_service(111),
'Nmap::Parser::Host::Service', 'UDP port 111' );
is( $svc->name, 'rpcbind', 'Service: name' );
is( $svc->proto, 'rpc', 'Service: proto' );
is( $svc->rpcnum, '100000', 'Service: rpcnum' );
#TESTING OS OBJECT FOR HOST 1
my $os;
my $fingerprint =
" SEQ(SP=C5%GCD=1%ISR=C7%TI=Z%II=I%TS=8) ECN(R=Y%DF=Y%T=40%W=16D0%O=M5B4NNSNW2%CC=N%Q=) T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=) T2(R=N) T3(R=Y%DF=Y%T=40%W=16A0%S=O%A=S+%F=AS%O=M5B4ST11NW2%RD=0%Q=) T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=) T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=) U1(R=Y%DF=N%T=40%TOS=C0%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUL=G%RUD=G) IE(R=Y%DFI=N%T=40%TOSI=S%CD=S%SI=S%DLI=S) ";
isa_ok( $os = $host->os_sig(), 'Nmap::Parser::Host::OS', 'os_sig()' );
is( $os->os_fingerprint(), $fingerprint, 'HOST1: os_fingerprint()' );
#TESTING NON-EXISTENT TRACE FOR HOST1
ok( !$host->all_trace_hops(), 'Host1 has no trace information' );
#TESTING NON-EXISTENT HOSTSCRIPT FOR HOST1
ok( !$host->hostscripts(), 'Host1 has no hostscript information' );
}
sub host_2 {
isa_ok( $host = $np->get_host(HOST2), 'Nmap::Parser::Host',
'GET ' . HOST2 );
is( $host->addr, HOST2, 'Host2: addr' );
is( $host->status, 'down', 'Host2: status = down' );
isa_ok( $np->del_host(HOST2), 'Nmap::Parser::Host', 'DEL ' . HOST2 );
is( $np->get_host(HOST2), undef, 'Testing deletion of ' . HOST2 );
}
sub host_3 {
isa_ok( $host = $np->get_host(HOST3), 'Nmap::Parser::Host',
'GET ' . HOST3 );
#TESTING SERVICE OBJECTS
my $svc;
isa_ok(
$svc = $host->tcp_service(22),
'Nmap::Parser::Host::Service',
'tcp_service(22) for ' . HOST3
);
is( $svc->owner, 'root', 'TCP Service: owner' );
is( $svc->name, 'ssh', 'TCP Service: name' );
is( $svc->product, 'OpenSSH', 'TCP Service: product' );
is( $svc->version, '3.4p1', 'TCP Service: version' );
is( $svc->extrainfo, 'protocol 1.99', 'TCP Service: extrainfo' );
is_deeply( [ $svc->scripts() ], [ 'ssh-hostkey' ], 'Port has scripts' );
{
my $output = $svc->scripts('ssh-hostkey')->{output};
like( $output, qr/^1024 .* \(RSA\)$/s, 'Script output ok');
}
isa_ok(
$svc = $host->udp_service(80),
'Nmap::Parser::Host::Service',
'udp_service(780) for ' . HOST3
);
is( $svc->owner, 'www-data', 'UDP Service: owner' );
is( $svc->name, 'http', 'UDP Service: name' );
is( $svc->product, 'Apache httpd', 'UDP Service: product' );
is( $svc->version, '1.3.26', 'UDP Service: version' );
is( $svc->extrainfo, '(Unix) Debian GNU/Linux', 'UDP Service: extrainfo' );
my $servicefp =
'SF-Port25-TCP:V=3.48%D=11/5%Time=3FA9032C%r(NULL,57,"220\x20s0002\.berger\.com\x20ESMTP\x20Oracle\x20Email\x20Server\x20SMTP\x20Inbound\x20Server\t9\.0\.4\.0\.0\x20\t\x20\x20Ready\r\n")%r(Help,17D,"220\x20s0002\.berger\.com\x20ESMTP\x20Oracle\x20Email\x20Server\x20SMTP\x20Inbound\x20Server\t9\.0\.4\.0\.0\x20\t\x20\x20Ready\r\n214-2\.3\.0\x20This\x20is\x20Oracle\x20eMail\x20SMTP\x20Server\n214-2\.3\.0\x20\x20\x20\x20\x20\x20\x20\x20\x20HELO\x20\x20\x20\x20EHLO\x20\x20\x20\x20MAIL\x20\x20\x20\x20RCPT\x20\x20\x20\x20DATA\n214-2\.3\.0\x20\x20\x20\x20\x20\x20\x20\x20\x20RSET\x20\x20\x20\x20NOOP\x20\x20\x20\x20QUIT\x20\x20\x20\x20HELP\x20\x20\x20\x20DSN\n214-2\.3\.0\x20For\x20more\x20info\x20use\x20\"HELP\x20<topic>\"\.\n214-2\.3\.0\x20For\x20local\x20information\x20send\x20email\x20to\x20Postmaster\x20at\x20your\x20site\.\n214\x202\.3\.0\x20End\x20of\x20HELP\x20info\n");';
isa_ok(
$svc = $host->tcp_service(25),
'Nmap::Parser::Host::Service',
'tcp_service(25) for ' . HOST3
);
is( $svc->fingerprint(), $servicefp,
'Verifying correct servicefp parsing on tcp_service(25) for ' . HOST3 );
$servicefp =
'SF-Port21-TCP:V=3.48%D=11/5%Time=3FA9032C%r(NULL,32,"220\x20Oracle\x20Internet\x20File\x20System\x20FTP\x20Server\x20ready\r\n")%r(GenericLines,53,"220\x20Oracle\x20Internet\x20File\x20System\x20FTP\x20Server\x20ready\r\n200\x20Connection\x20closed,\x20good\x20bye\r\n")%r(Help,57,"220\x20Oracle\x20Internet\x20File\x20System\x20FTP\x20Server\x20ready\r\n500\x20HELP:\x20command\x20not\x20understood\.\r\n");';
isa_ok(
$svc = $host->tcp_service(953),
'Nmap::Parser::Host::Service',
'tcp_service(953) for ' . HOST3
);
is( $svc->fingerprint(), $servicefp,
'Verifying correct servicefp parsing on tcp_service(953) for '
. HOST3 );
isa_ok( $svc = $host->tcp_service(500),
'Nmap::Parser::Host::Service',
'Verifying good reference returned on unscanned port service (500)' );
isa_ok( $np->del_host(HOST3), 'Nmap::Parser::Host', 'DEL ' . HOST3 );
is( $np->get_host(HOST3), undef, 'Testing deletion of ' . HOST3 );
#TESTING TRACE FOR HOST3
my $hops_count = $host->all_trace_hops();
is( $hops_count, 2, 'Host3 has trace information' );
is( $host->trace_error(), 'Error', 'Host3 trace is in error' );
#TESTING HOSTSCRIPT FOR HOST3
is_deeply(
[ $host->hostscripts() ],
[ 'nbstat' ],
'Host3 has one hostscript' );
{
my $output = $host->hostscripts('nbstat')->{output};
is( substr($output,0,16), "\n NetBIOS name:",
'Host3 hostscript correct' );
}
}
sub host_4 {
isa_ok( $host = $np->get_host(HOST4), 'Nmap::Parser::Host',
'GET ' . HOST4 );
my $os;
#TESTING OS OBJECTS
isa_ok(
$os = $host->os_sig,
'Nmap::Parser::Host::OS',
'os_sig for ' . HOST4
);
is( $os->portused_open, 22, 'OS: portused open' );
is( $os->portused_closed, 1, 'OS: portused closed' );
is( $os->name_count, 2, 'OS: name count' );
is( $os->name, 'Linux Kernel 2.4.0 - 2.5.20', 'OS: name()' );
is( $os->name(0), $os->name, 'OS: name(0)' );
is( $os->name(1), 'Solaris 9', 'OS: name(1)' );
is_deeply(
[ $os->all_names ],
[ 'Linux Kernel 2.4.0 - 2.5.20', 'Solaris 9' ],
'OS: all_names'
);
is( $os->name_accuracy(), 100, 'OS: name_accuracy()' );
is( $os->name_accuracy(0), $os->name_accuracy(), 'OS: name_accuracy(0)' );
is( $os->name_accuracy(1), 99, 'OS: name_accuracy(1)' );
my $count = 11;
is( $os->class_count(), $count, 'OS: class_count MAX = ' . $count );
is( $os->osfamily(), 'AOS', 'OS: osfamily()' );
is( $os->osfamily(0), $os->osfamily(), 'OS: osfamily(0)' );
is( $os->osfamily($count), 'Linux', 'OS: osfamily(MAX)' );
is( $os->osfamily(15), $os->osfamily($count),
'OS: osfamily(15) = osfamily(MAX)' );
is( $os->vendor(), 'Redback', 'OS: vendor()' );
is( $os->vendor(0), $os->vendor(), 'OS: vendor(0)' );
is( $os->vendor($count), 'Linux', 'OS: vendor(MAX)' );
is( $os->vendor(15), $os->vendor($count), 'OS: vendor(15) = vendor(MAX)' );
is( $os->osgen(), undef, 'OS: osgen()' );
is( $os->osgen(0), $os->osgen(), 'OS: osgen(0)' );
is( $os->osgen($count), '2.4.x', 'OS: osgen(MAX)' );
is( $os->osgen(15), $os->osgen($count), 'OS: osgen(15) = osgen(MAX)' );
is( $os->type(), 'router', 'OS: type()' );
is( $os->type(0), $os->type(), 'OS: type(0)' );
is( $os->type($count), 'general purpose', 'OS: type(MAX)' );
is( $os->type(15), $os->type($count), 'OS: type(15) = type(MAX)' );
is( $os->class_accuracy(), 97, 'OS: class_accuracy()' );
is( $os->class_accuracy(0), $os->class_accuracy(),
'OS: class_accuracy(0)' );
is( $os->class_accuracy($count), 50, 'OS: class_accuracy(MAX)' );
is(
$os->class_accuracy(15),
$os->class_accuracy($count),
'OS: class_accuracy(15) = type(MAX)'
);
my $fingerprint =
" SCAN(V=4.20%D=6/11%OT=22%CT=%CU=%PV=N%DS=1%G=N%M=001321%TM=466DE2F1%P=i686-pc-windows-windows) T5(Resp=Y%DF=Y%W=0%ACK=S++%Flags=AR%Ops=) T6(Resp=Y%DF=Y%W=0%ACK=O%Flags=R%Ops=) T7(Resp=Y%DF=Y%W=0%ACK=S++%Flags=AR%Ops=) PU(Resp=Y%DF=N%TOS=C0%IPLEN=164%RIPTL=148%RID=E%RIPCK=E%UCK=E%ULEN=134%DAT=E) ";
isa_ok( $os = $host->os_sig(), 'Nmap::Parser::Host::OS', 'os_sig()' );
is( $os->os_fingerprint(), $fingerprint, 'HOST4: os_fingerprint()' );
is( $host->tcptssequence_values, undef,
'HOST4: tcptssequence_values = undef' );
is( $host->distance(), 10, 'Host4: distance = 10' );
isa_ok( $np->del_host(HOST4), 'Nmap::Parser::Host', 'DEL ' . HOST4 );
is( $np->get_host(HOST4), undef, 'Testing deletion of ' . HOST4 );
#TESTING TRACE FOR HOST4
my @hops = $host->all_trace_hops();
ok( !$host->trace_error(), 'Host4 trace is not in error' );
is( $host->trace_port(), 80, 'Host4 trace port information' );
is( $host->trace_proto(), 'tcp', 'Host4 trace proto information' );
is( ( scalar @hops ), 3, 'Host4 trace size' );
is( $hops[0]->ttl(), 1, 'Host4 hop1 TTL' );
ok( !$hops[0]->rtt(), 'Host4 hop1 has no RTT' );
is( $hops[0]->ipaddr(), '192.168.1.1', 'Host4 hop1 IP address' );
ok( !$hops[0]->host(), 'Host4 hop1 has no hostname' );
is( $hops[2]->ttl(), 4, 'Host4 hop4 TTL' );
is( $hops[2]->rtt(), 26.48, 'Host4 hop4 RTT' );
is( $hops[2]->ipaddr(), '1.1.1.1', 'Host4 hop4 IP address' );
is( $hops[2]->host(), 'www.straton-it.fr', 'Host4 hop4 hostname' );
}