mopmeat has asked for the wisdom of the Perl Monks concerning the following question:

Hey Folks, I'm having some trouble parsing the results generated by Nmap::Scanner and was hoping you guys could help out. Here's what I've got:

use Nmap::Scanner; use Data::Dumper; my $scanner = new Nmap::Scanner; $scanner->tcp_connect_scan(); $scanner->add_scan_port('1-1024'); $scanner->max_rtt_timeout(3); $scanner->add_target('127.0.0.1'); my $results = $scanner->scan(); print Dumper($results->{'ALLHOSTS'});

The dump suggests that the output is a hash (I think) with the key Nmap::Scanner::Address=Hash(0x1231234). The problem I'm having is reliably accessing the contents of that key since every time the program runs, I get a different hash address. The solution I have so far is to do something like:

for my $key (keys $results->{'ALLHOSTS'}){ print Dumper($results->{'ALLHOSTS'}{$key}->{'ports'}); }
This allows me to retrieve the values associated with the key (e.g. ports info), but it can't be the best way to do it. What would you suggest as an alternative? EDIT: Thanks for the help everyone. It gave me a better understanding of what I was trying to do and gave me a few new tools to look into. Much appreciated.

Replies are listed 'Best First'.
Re: Parsing output from Nmap::Scanner with varying hash address.
by stevieb (Canon) on Jul 10, 2015 at 23:12 UTC

    Now we're talking my language... networking and network penetration testing :)

    The return you get is definitely a reference to a hash, but it is a blessed() hash, meaning it is an object (aka instance of a class (package)).

    Per the SYNOPSIS of perldoc Nmap::Scanner, we see that: $results is an instance of Nmap::Scanner::Backend::Results.

    What you're doing to extract the parts of the return are typical for a hash, but if you can specify what exactly you're trying to do, it may help us sort out a different/better/cleaner way. For the most part, you want to use the object's methods to extract the data you need (if the class has the methods to do so) instead of opening the object up like a can of tuna. Not all classes have such methods available though (and if this one doesn't, I've found my module to work on) :)

    You might be interested to read Nmap::Scanner::Backend::Results documentation and the rest of the sub-modules for methods that may allow you to extract the info you need using the object's built-ins. (Nmap::Scanner module listing).

    Beyond that, again, state what it is you need out of the object.

    -stevieb

      For starters, I'd like to produce a simpler hash with a few key elements. Something like the following;

      ( 'smtp' => 'closed', 'http' => 'open', 'https' => 'open', )

      Right now I have the following and I'd like a more elegant/cleaner solution, if possible.

      my %shortlist; for my $key (keys $results->{'ALLHOSTS'}){ for my $key2 (keys $results->{'ALLHOSTS'}{$key}->{'ports'}->{' +tcp'}){ $shortlist{"$results->{'ALLHOSTS'}{$key}->{'ports'}->{'tcp +'}->{$key2}->{'service'}->{'name'}"} = $results->{'ALLHOSTS'}{$key}-> +{'ports'}->{'tcp'}->{$key2}->{'state'}; } }
        I do not know if this is cleaner or more elegant, but you could remove most of these -> dereferencing arrows (those between closing and opening curly braces), thereby making the syntax at least a bit shorter.

        Something like this should probably work (but is untested):

        my %shortlist; for my $key (keys $results->{'ALLHOSTS'}){ for my $key2 (keys $results->{'ALLHOSTS'}{$key}{'ports'}{'tcp'}){ $shortlist{"$results->{'ALLHOSTS'}{$key}{'ports'}{'tcp'}{$key2 +}{'service'}{'name'}"} = $results->{'ALLHOSTS'}{$key}{'ports'}{'tcp'} +{$key2}{'state'}; } }
Re: Parsing output from Nmap::Scanner with varying hash address.
by kcott (Archbishop) on Jul 11, 2015 at 22:44 UTC

    G'day mopmeat,

    Welcome to the Monastery.

    I'll just point out that 'keys HASHREF' is an experimental feature introduced in Perl v5.14 (see "perl5140delta: Syntactical Enhancements").

    It's listed in perlexperiment but the link to [perl #119437], which is autogenerated, is wrong. It should be: https://rt.perl.org/Public/Bug/Display.html?id=119437.

    The warnings: category is experimental::autoderef.

    So, to ensure a Perl version of sufficient vintage to handle this, and to stop all the warnings, your code should start something like this:

    #!/usr/bin/env perl use 5.014; use strict; use warnings; no warnings 'experimental::autoderef'; ...

    Having said all that, consider whether you really want to use experimental features. They are subject to change (including removal) and it can be a real pain if you have change code to accommodate this.

    I'd recommend sticking with 'keys %{$hashref}' unless you have some compelling reason not to.

    -- Ken

Re: Parsing output from Nmap::Scanner with varying hash address.
by tangent (Parson) on Jul 11, 2015 at 22:36 UTC
    As stevieb points out, the results you get back are objects, and they in turn contain other objects. The docs are a bit scattered, but the result objects are either a list with a get_next() method, or they represent a host, port, service etc. and have relevant access methods. From the code you posted I think this should achieve the same thing (untested):
    my $results = $scanner->scan(); my $host_list = $results->get_host_list; while ( my $host = $host_list->get_next ) { my $port_list = $host->get_tcp_port_list; my %report; while ( my $number = $port_list->get_next ) { my $port = $host->get_tcp_port( $number ); $report{ $port->service->name } = $port->state; } }

      There are a lot of great replies in this thread, but this is exactly what I was hoping for, I just didn't have the time on Friday evening to do the actual digging. This is great and I'm glad you posted this.

      ++ tangent. I had been briefly looking for such access methods in the documentation, but did not find them. I probably failed to look at the right place/level in the class hierarchy. Using accessors when the exists is of course a much better way to access the contents of objects.