Skip Menu |

This queue is for tickets about the Nmap-Parser CPAN distribution.

Report information
The Basics
Id: 49901
Status: new
Priority: 0/
Queue: Nmap-Parser

People
Owner: Nobody in particular
Requestors: TEQUETER [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Wishlist
Broken in: 1.19
Fixed in: (no value)



Subject: [patch] Support for Nmap's traceroute feature
Nmap provides a traceroute feature when started with --traceroute (or -A), but so far Nmap::Parser was not including it in its results. This patch provides support for it through additional Nmap::Parser::Host methods and a new Nmap::Parser::Host::TraceHop class. The documentation and tests have been updated accordingly. Thank you much for this useful module! Regards, -- Thomas Equeter Straton IT
Subject: nmap-parser-traceroute.patch
diff -ur Nmap-Parser-1.19/Parser.pm Nmap-Parser-1.20/Parser.pm --- Nmap-Parser-1.19/Parser.pm 2008-11-07 08:57:44.000000000 +0100 +++ Nmap-Parser-1.20/Parser.pm 2009-06-18 13:17:51.000000000 +0200 @@ -282,7 +282,8 @@ $D{$$}{HOSTS}{$id}{tcptssequence} = __host_tcptssequence_tag_hdlr($tag); $D{$$}{HOSTS}{$id}{distance} = __host_distance_tag_hdlr($tag); #returns simple value - + $D{$$}{HOSTS}{$id}{trace} = __host_trace_tag_hdlr($tag); + $D{$$}{HOSTS}{$id}{trace_error} = __host_trace_error_tag_hdlr($tag); } #CREATE HOST OBJECT FOR USER @@ -530,6 +531,54 @@ return $distance->{att}->{value}; } +sub __host_trace_tag_hdlr { + my $tag = shift; + my $trace_tag = $tag->first_child('trace'); + my $trace_hashref = { hops => [], }; + + if ( defined $trace_tag ) { + + my $proto = $trace_tag->{att}->{proto}; + $trace_hashref->{proto} = $proto if defined $proto; + + my $port = $trace_tag->{att}->{port}; + $trace_hashref->{port} = $port if defined $port; + + for my $hop_tag ( $trace_tag->children('hop') ) { + + # Copy the known hop attributes, they will go in + # Nmap::Parser::Host::TraceHop + my %hop_data; + $hop_data{$_} = $hop_tag->{att}->{$_} for qw( ttl rtt ipaddr host ); + delete $hop_data{rtt} if $hop_data{rtt} !~ /^[\d.]+$/; + + push @{ $trace_hashref->{hops} }, \%hop_data; + } + + } + + return $trace_hashref; +} + +sub __host_trace_error_tag_hdlr { + my $tag = shift; + my $trace_tag = $tag->first_child('trace'); + + if ( defined $trace_tag ) { + + my $error_tag = $trace_tag->first_child('error'); + if ( defined $error_tag ) { + + # If an error happens, always provide a true value even if + # it doesn't contains a useful string + my $errorstr = $error_tag->{att}->{errorstr} || 1; + return $errorstr; + } + } + + return; +} + #/*****************************************************************************/ # NMAP::PARSER::SESSION #/*****************************************************************************/ @@ -625,6 +674,19 @@ sub extraports_count { return $_[0]->{ports}{extraports}{count}; } sub distance { return $_[0]->{distance}; } +sub all_trace_hops { + + my $self = shift; + + return unless defined $self->{trace}->{hops}; + return map { Nmap::Parser::Host::TraceHop->new( $_ ) } + @{ $self->{trace}->{hops} }; +} + +sub trace_port { return $_[0]->{trace}->{port} } +sub trace_proto { return $_[0]->{trace}->{proto} } +sub trace_error { return $_[0]->{trace_error} } + sub _del_port { my $self = shift; my $proto = pop; #portid might be empty, so this goes first @@ -844,6 +906,39 @@ return $self->{ $type . '_' . $param }[$index]; } +#/*****************************************************************************/ +# NMAP::PARSER::HOST::TRACEHOP +#/*****************************************************************************/ + +package Nmap::Parser::Host::TraceHop; +use vars qw($AUTOLOAD); + +sub new { + my $class = shift; + $class = ref($class) || $class; + my $self = shift || {}; + bless( $self, $class ); + return $self; +} + +sub AUTOLOAD { + ( my $param = $AUTOLOAD ) =~ s{.*::}{}xms; + return if ( $param eq 'DESTROY' ); + no strict 'refs'; + $param = lc($param); + + # Supported accessors: + my %subs; + @subs{ qw( ttl rtt ipaddr host ) } = 1; + + if ( exists $subs{$param} ) { + + *$AUTOLOAD = sub { $_[0]->{$param} }; + goto &$AUTOLOAD; + } + else { die '[Nmap-Parser] method ->' . $param . "() not defined!\n"; } +} + 1; __END__ @@ -1161,6 +1256,30 @@ Return the distance (in hops) of the target machine from the machine that performed the scan. +=item B<trace_error()> + +Returns a true value (usually a meaningful error message) if the traceroute was +performed but could not reach the destination. In this case C<all_trace_hops()> +contains only the part of the path that could be determined. + +=item B<all_trace_hops()> + +Returns an array of Nmap::Parser::Host::TraceHop objects representing the path +to the target host. This array may be empty if Nmap did not perform the +traceroute for some reason (same network, for example). + +Some hops may be missing if Nmap could not figure out information about them. +In this case there is a gap between the C<ttl()> values of consecutive returned +hops. See also C<trace_error()>. + +=item B<trace_proto()> + +Returns the name of the protocol used to perform the traceroute. + +=item B<trace_port()> + +Returns the port used to perform the traceroute. + =item B<os_sig()> Returns an Nmap::Parser::Host::OS object that can be used to obtain all the @@ -1428,6 +1547,35 @@ =back +=head3 Nmap::Parser::Host::TraceHop + +This object represents a router on the IP path towards the destination or the +destination itself. This is similar to what the C<traceroute> command outputs. + +Nmap::Parser::Host::TraceHop objects are obtained through the +C<all_trace_hops()> and C<trace_hop()> Nmap::Parser::Host methods. + +=over 4 + +=item B<ttl()> + +The Time To Live is the network distance of this hop. + +=item B<rtt()> + +The Round Trip Time is roughly equivalent to the "ping" time towards this hop. +It is not always available (in which case it will be undef). + +=item B<ipaddr()> + +The known IP address of this hop. + +=item B<host()> + +The host name of this hop, if known. + +=back + =head1 EXAMPLES I think some of us best learn from examples. These are a couple of examples to help diff -ur Nmap-Parser-1.19/t/nmap_results.xml Nmap-Parser-1.20/t/nmap_results.xml --- Nmap-Parser-1.19/t/nmap_results.xml 2008-11-07 08:55:51.000000000 +0100 +++ Nmap-Parser-1.20/t/nmap_results.xml 2009-06-18 13:17:51.000000000 +0200 @@ -139,6 +139,11 @@ <service name="rndc" method="table" conf="3" servicefp="SF-Port21-TCP:V=3.48%D=11/5%Time=3FA9032C%r(NULL,32,&quot;220\x20Oracle\x20Internet\x20File\x20System\x20FTP\x20Server\x20ready\r\n&quot;)%r(GenericLines,53,&quot;220\x20Oracle\x20Internet\x20File\x20System\x20FTP\x20Server\x20ready\r\n200\x20Connection\x20closed,\x20good\x20bye\r\n&quot;)%r(Help,57,&quot;220\x20Oracle\x20Internet\x20File\x20System\x20FTP\x20Server\x20ready\r\n500\x20HELP:\x20command\x20not\x20understood\.\r\n&quot;);"/> </port> </ports> + <trace port="80" proto="tcp"> + <hop ttl="1" rtt="1.17" ipaddr="192.168.1.1"/> + <hop ttl="2" rtt="26.86" ipaddr="88.181.1.1"/> + <error errorstr="Error" /><!-- According to the DTD... --> + </trace> </host> <host> <status state="up"/> @@ -175,6 +180,12 @@ <ipidsequence class="Incremental" values="C4BE,C4C6,C4CC,C4CE,C4D4,C4DC"/> <tcptssequence class="none returned (unsupported)"/> <distance value="10"/> + <trace port="80" proto="tcp"> + <hop ttl="1" rtt="--" ipaddr="192.168.1.1"/> + <hop ttl="2" rtt="26.86" ipaddr="88.181.1.1"/> + <!-- Missing hop3 --> + <hop ttl="4" rtt="26.48" ipaddr="1.1.1.1" host="www.straton-it.fr"/> + </trace> </host> <runstats> <finished time="1057088900" timestr="Tue Jul 1 14:48:20 2003"/> diff -ur Nmap-Parser-1.19/t/parser.t Nmap-Parser-1.20/t/parser.t --- Nmap-Parser-1.19/t/parser.t 2008-11-07 08:55:51.000000000 +0100 +++ Nmap-Parser-1.20/t/parser.t 2009-06-18 13:17:51.000000000 +0200 @@ -5,7 +5,7 @@ use Nmap::Parser; use File::Spec; use Cwd; -use Test::More tests => 153; +use Test::More tests => 168; use constant HOST1 => '127.0.0.1'; use constant HOST2 => '127.0.0.2'; @@ -268,6 +268,9 @@ " 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' ); } sub host_2 { @@ -337,6 +340,11 @@ 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' ); + } sub host_4 { @@ -412,4 +420,21 @@ 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' ); + }