1package Nmap::Scanner::Backend::XML;
2
3use strict;
4
5use vars qw(@ISA);
6@ISA = qw(Nmap::Scanner::Backend::Processor);
7
8use XML::SAX::ParserFactory;
9
10use Nmap::Scanner;
11use Nmap::Scanner::Backend::Results;
12use Nmap::Scanner::Backend::Processor;
13
14sub new {
15    my $class = shift;
16    my $you = $class->SUPER::new();
17    return bless $you, $class;
18}
19
20#  Process results from "-oX -"
21
22sub process {
23
24    my $self = shift;
25    my $pid = shift;
26    my $read = shift;
27    my $cmdline = shift;
28    my $error = shift;
29
30    #  Suppress warnings about reading unopened handle
31    $^W = 0;
32    my $err = join('', (<$error>));
33    $^W = 1;
34
35    if ($err ne '') {
36
37        close($read);
38        close($error);
39
40        warn <<EOF;
41<nmap-error>
42  <pid="$pid"/>
43  <cmdline="$cmdline"/>
44  <nmap-err>$err</nmap-msg>
45</nmap-error>
46EOF
47        exit 1;
48    }
49
50    my $handler = NmapHandler->new($self);
51    my $parser = XML::SAX::ParserFactory->parser(Handler => $handler);
52
53    eval { $parser->parse_file($read) };
54
55
56    if (defined($@) && ($@ ne '')) {
57
58        my $msg = join('', <$read>);
59
60        Nmap::Scanner::debug("bytes in input stream: " .  tell($read));
61        Nmap::Scanner::debug("bytes in error stream: " .  tell($error));
62
63        warn <<EOF;
64<nmap-error>
65  <pid="$pid"/>
66  <cmdline="$cmdline"/>
67  <perl-msg>$@</perl-msg>
68  <nmap-msg>$msg</nmap-msg>
69  <nmap-err>$err</nmap-msg>
70</nmap-error>
71EOF
72        close($read);
73        exit 1;
74
75    }
76
77    close($read);
78
79    return $handler->results();
80
81}
82
831;
84
85#
86#  SAX listener to process XML output from nmap.
87#
88
89package NmapHandler;
90
91    use strict;
92    use base qw(XML::SAX::Base);
93
94    use XML::SAX::Base;
95
96    use Nmap::Scanner::Host;
97    use Nmap::Scanner::Hostname;
98    use Nmap::Scanner::Port;
99    use Nmap::Scanner::Service;
100    use Nmap::Scanner::Address;
101    use Nmap::Scanner::Hosts;
102    use Nmap::Scanner::ExtraPorts;
103    use Nmap::Scanner::RunStats;
104    use Nmap::Scanner::RunStats::Finished;
105    use Nmap::Scanner::NmapRun;
106    use Nmap::Scanner::ScanInfo;
107    use Nmap::Scanner::Task;
108    use Nmap::Scanner::TaskProgress;
109    use Nmap::Scanner::Distance;
110    use Nmap::Scanner::Backend::Results;
111
112    use Nmap::Scanner::OS;
113    use Nmap::Scanner::OS::PortUsed;
114    use Nmap::Scanner::OS::Uptime;
115    use Nmap::Scanner::OS::Class;
116    use Nmap::Scanner::OS::Match;
117    use Nmap::Scanner::OS::TCPSequence;
118    use Nmap::Scanner::OS::TCPTSSequence;
119    use Nmap::Scanner::OS::IPIdSequence;
120    use Nmap::Scanner::OS::Fingerprint;
121
122    #  One function per element .. fun! ;)
123
124    my %HANDLERS = (
125        host          => \&host,
126        hosts         => \&hosts,
127        status        => \&hoststatus,
128        hostname      => \&hostname,
129        smurf         => \&smurf,
130        address       => \&hostaddress,
131        hostnames     => \&hostnames,
132        port          => \&port,
133        ports         => \&ports,
134        state         => \&state,
135        service       => \&service,
136        owner         => \&owner,
137        addport       => \&addport,
138        extraports    => \&extraports,
139        os            => \&os,
140        portused      => \&portused,
141        osclass       => \&osclass,
142        osfingerprint => \&osfingerprint,
143        osmatch       => \&osmatch,
144        uptime        => \&uptime,
145        tcpsequence   => \&tcpsequence,
146        tcptssequence => \&tcptssequence,
147        ipidsequence  => \&ipidsequence,
148        nmaprun       => \&nmaprun,
149        scaninfo      => \&scaninfo,
150        taskbegin     => \&taskbegin,
151        taskend       => \&taskend,
152        taskprogress  => \&taskprogress,
153        verbose       => \&verbose,
154        debugging     => \&debugging,
155        runstats      => \&runstats,
156        finished      => \&finished,
157        distance      => \&distance,
158    );
159
160    sub new {
161        my $class = shift;
162        my $backend = shift;
163        my $self = $class->SUPER::new();
164        $self->{NMAP_BACKEND} = $backend;
165        $self->{NMAP_PORT} = undef;
166        $self->{NMAP_HOST} = undef;
167        $self->{NMAP_RUNSTATS} = undef;
168        $self->{NMAP_NMAPRUN} = undef;
169        $self->{PORT_COUNT} = 0;
170        $self->{NMAP_OSGUESS} = undef;
171        $self->{NMAP_RESULTS} = Nmap::Scanner::Backend::Results->new();
172        $self->{NMAP_TASK} = undef;
173        $self->{NMAP_TASK_PROGRESS} = undef;
174        return bless $self, $class;
175    }
176
177    #  Controller for start element handlers
178
179    sub start_element {
180       my ($self, $el) = @_;
181       my $name  = $el->{Name};
182
183       if (exists $HANDLERS{$name}) {
184           Nmap::Scanner::debug("About to handle $name");
185           &{$HANDLERS{$name}}($self, $el->{Attributes});
186           Nmap::Scanner::debug("Handled $name");
187       } else {
188
189            my %attrs = %{$el->{Attributes}};
190
191            return unless Nmap::Scanner::debug("Received unhandled XML: $name");
192
193            for my $key (keys %attrs) {
194                Nmap::Scanner::debug("Unhandled[$name]: $key = $attrs{$key}");
195            }
196       }
197    }
198
199    #  Controller for end element handlers
200
201    sub end_element {
202
203        my ($self, $el) = @_;
204
205        if ($el->{Name} eq 'host') {
206            my $host = $self->{NMAP_HOST};
207            $self->{NMAP_HOST}->os($self->{NMAP_OSGUESS})
208                if $self->{NMAP_OSGUESS};
209            $self->{NMAP_RESULTS}->add_host($host);
210            $self->{NMAP_BACKEND}->notify_scan_complete($self->{NMAP_HOST});
211            undef $self->{NMAP_OSGUESS};
212        } elsif ($el->{Name} eq 'hosts') {
213            $self->{NMAP_NMAPRUN}->run_stats($self->{NMAP_RUNSTATS});
214        } elsif ($el->{Name} eq 'port') {
215            my $port = $self->{NMAP_PORT};
216            Nmap::Scanner::debug("Adding port: " . $port->portid());
217            $self->{NMAP_HOST}->add_port($port);
218            $self->{PORT_COUNT}++;
219            $self->{NMAP_BACKEND}->notify_port_found(
220                $self->{NMAP_HOST}, $self->{NMAP_PORT}
221            );
222            undef $self->{NMAP_PORT};
223        } elsif ($el->{Name} eq 'ports') {
224            unless ($self->{PORT_COUNT} > 0) {
225                $self->{NMAP_BACKEND}->notify_no_ports_open(
226                    $self->{NMAP_HOST}, $self->{NMAP_HOST}->extra_ports()
227                );
228            }
229            $self->{PORT_COUNT} = 0;
230        } elsif ($el->{Name} eq 'hostnames') {
231             $self->{NMAP_BACKEND}->notify_scan_started($self->{NMAP_HOST});
232        } elsif ($el->{Name} eq 'taskbegin') {
233            $self->{NMAP_BACKEND}->notify_task_started($self->{NMAP_TASK});
234        } elsif ($el->{Name} eq 'taskend') {
235            $self->{NMAP_BACKEND}->notify_task_ended($self->{NMAP_TASK});
236        } elsif ($el->{Name} eq 'taskprogress') {
237            $self->{NMAP_BACKEND}->notify_task_progress(
238                $self->{NMAP_TASK_PROGRESS});
239        } elsif ($el->{Name} eq 'nmaprun') {
240            $self->{NMAP_RESULTS}->nmap_run($self->{NMAP_NMAPRUN});
241        }
242    }
243
244    sub host {
245        my ($self, $ref) = @_;
246        $self->{NMAP_HOST} = Nmap::Scanner::Host->new();
247    }
248
249    sub hoststatus {
250        my ($self, $ref) = @_;
251        my $state = $ref->{'{}state'}->{Value};
252        $self->{NMAP_HOST}->status($state);
253    }
254
255    sub hostname {
256        my ($self, $ref) = @_;
257        my $host = $self->{NMAP_HOST};
258        my $hostname = Nmap::Scanner::Hostname->new();
259        $hostname->name($ref->{'{}name'}->{Value});
260        $hostname->type($ref->{'{}type'}->{Value});
261        $host->add_hostname($hostname);
262    }
263
264    sub hostnames {
265        my $self = shift;
266        #  Do nothing, as this is just an array of hostnames, held in Host object.
267    }
268
269    sub ports {
270        my $self = shift;
271        #  Do nothing, as this is just an array of ports, held in Host object.
272    }
273
274    sub smurf {
275        my ($self, $ref) = @_;
276        my $smurf = $ref->{'{}responses'}->{Value};
277        $self->{NMAP_HOST}->smurf($smurf);
278    }
279
280    sub hostaddress {
281        my ($self, $ref) = @_;
282        my $addr = Nmap::Scanner::Address->new();
283        $addr->addr($ref->{'{}addr'}->{Value});
284        $addr->addrtype($ref->{'{}addrtype'}->{Value});
285        $addr->vendor($ref->{'{}vendor'}->{Value})
286            if $ref->{'{}vendor'}->{Value};
287        $self->{NMAP_HOST}->add_address($addr);
288    }
289
290    sub port {
291        my ($self, $ref) = @_;
292        my $port = Nmap::Scanner::Port->new();
293        $port->protocol($ref->{'{}protocol'}->{Value});
294        $port->portid($ref->{'{}portid'}->{Value});
295        $self->{NMAP_PORT} = $port;
296    }
297
298    sub state {
299        my ($self, $ref) = @_;
300        $self->{NMAP_PORT}->state($ref->{'{}state'}->{Value});
301    }
302
303    sub owner {
304        my ($self, $ref) = @_;
305        my $owner = $ref->{'{}name'}->{Value};
306        $self->{NMAP_PORT}->owner($owner);
307    }
308
309    sub service {
310
311        my ($self, $ref) = @_;
312        my $port = $self->{NMAP_PORT};
313        my $svc = Nmap::Scanner::Service->new();
314        $svc->name($ref->{'{}name'}->{Value});
315        $svc->proto($ref->{'{}proto'}->{Value});
316        $svc->rpcnum($ref->{'{}rpcnum'}->{Value});
317        $svc->lowver($ref->{'{}lowver'}->{Value});
318        $svc->highver($ref->{'{}highver'}->{Value});
319        $svc->method($ref->{'{}method'}->{Value});
320        $svc->conf($ref->{'{}conf'}->{Value});
321        $svc->tunnel($ref->{'{}tunnel'}->{Value});
322        $svc->product($ref->{'{}product'}->{Value});
323        $svc->version($ref->{'{}version'}->{Value});
324        $svc->extrainfo($ref->{'{}extrainfo'}->{Value});
325        $port->service($svc);
326    }
327
328    sub addport {
329        my ($self, $ref) = @_;
330
331        my $port = Nmap::Scanner::Port->new();
332        $port->state($ref->{'{}state'}->{Value});
333        $port->protocol($ref->{'{}protocol'}->{Value});
334        $port->portid($ref->{'{}portid'}->{Value});
335        $port->owner($ref->{'{}owner'}->{Value})
336            if $ref->{'{}owner'};
337        $self->{NMAP_BACKEND}->notify_port_found(
338            $self->{NMAP_HOST}, $port
339        );
340    }
341
342    sub extraports {
343        my ($self, $ref) = @_;
344        my $extras = Nmap::Scanner::ExtraPorts->new();
345        $extras->state($ref->{'{}state'}->{Value});
346        $extras->count($ref->{'{}count'}->{Value});
347        $self->{NMAP_HOST}->extra_ports($extras);
348    }
349
350    sub os {
351        my ($self, $ref) = @_;
352        $self->{NMAP_OSGUESS} = Nmap::Scanner::OS->new();
353    }
354
355    sub osclass {
356        my ($self, $ref) = @_;
357        my $os = $self->{NMAP_OSGUESS};
358        my $class = Nmap::Scanner::OS::Class->new();
359        $class->type($ref->{'{}type'}->{Value});
360        $class->vendor($ref->{'{}vendor'}->{Value});
361        $class->osfamily($ref->{'{}osfamily'}->{Value});
362        $class->osgen($ref->{'{}osgen'}->{Value});
363        $class->accuracy($ref->{'{}accuracy'}->{Value});
364        $os->add_os_class($class);
365    }
366
367    sub osfingerprint {
368        my ($self, $ref) = @_;
369        my $os = $self->{NMAP_OSGUESS};
370        my $fingerprint = Nmap::Scanner::OS::Fingerprint->new();
371        $fingerprint->fingerprint($ref->{'{}fingerprint'}->{Value});
372        $os->osfingerprint($fingerprint);
373    }
374
375    sub osmatch {
376        my ($self, $ref) = @_;
377        my $os = $self->{NMAP_OSGUESS};
378        my $match = Nmap::Scanner::OS::Match->new();
379        $match->name($ref->{'{}name'}->{Value});
380        $match->accuracy($ref->{'{}accuracy'}->{Value});
381        $os->add_os_match($match);
382    }
383
384    sub portused {
385        my ($self, $ref) = @_;
386        my $os = $self->{NMAP_OSGUESS};
387        my $port = Nmap::Scanner::OS::PortUsed->new();
388        $port->state($ref->{'{}state'}->{Value});
389        $port->proto($ref->{'{}proto'}->{Value});
390        $port->portid($ref->{'{}portid'}->{Value});
391        $os->add_port_used($port);
392    }
393
394    sub uptime {
395        my ($self, $ref) = @_;
396        my $os = $self->{NMAP_OSGUESS};
397        my $u = Nmap::Scanner::OS::Uptime->new();
398        $u->seconds($ref->{'{}seconds'}->{Value});
399        $u->lastboot($ref->{'{}lastboot'}->{Value});
400        $os->uptime($u);
401    }
402
403    sub tcpsequence {
404        my ($self, $ref) = @_;
405        my $os = $self->{NMAP_OSGUESS};
406        my $t = Nmap::Scanner::OS::TCPSequence->new();
407        $t->index($ref->{'{}index'}->{Value});
408        $t->class($ref->{'{}class'}->{Value});
409        $t->difficulty($ref->{'{}difficulty'}->{Value});
410        $t->values($ref->{'{}values'}->{Value});
411        $os->tcpsequence($t);
412    }
413
414    sub tcptssequence {
415        my ($self, $ref) = @_;
416        my $os = $self->{NMAP_OSGUESS};
417        my $t = Nmap::Scanner::OS::TCPTSSequence->new();
418        $t->class($ref->{'{}class'}->{Value});
419        $t->values($ref->{'{}values'}->{Value});
420        $os->tcptssequence($t);
421    }
422
423    sub ipidsequence {
424        my ($self, $ref) = @_;
425        my $os = $self->{NMAP_OSGUESS};
426        my $t = Nmap::Scanner::OS::IPIdSequence->new();
427        $t->class($ref->{'{}class'}->{Value});
428        $t->values($ref->{'{}values'}->{Value});
429        $os->ipidsequence($t);
430    }
431
432    sub nmaprun {
433        my ($self, $ref) = @_;
434        my $run = Nmap::Scanner::NmapRun->new();
435        $run->scanner($ref->{'{}scanner'}->{Value});
436        $run->args($ref->{'{}args'}->{Value});
437        $run->start($ref->{'{}start'}->{Value});
438        $run->startstr($ref->{'{}startstr'}->{Value});
439        $run->version($ref->{'{}version'}->{Value});
440        $run->xmloutputversion($ref->{'{}xmloutputversion'}->{Value});
441        $self->{NMAP_NMAPRUN} = $run;
442    }
443
444    sub scaninfo {
445        my ($self, $ref) = @_;
446        my $info = Nmap::Scanner::ScanInfo->new();
447        $info->type($ref->{'{}type'}->{Value});
448        $info->protocol($ref->{'{}protocol'}->{Value});
449        $info->numservices($ref->{'{}numservices'}->{Value});
450        $info->services($ref->{'{}services'}->{Value});
451        $self->{NMAP_NMAPRUN}->add_scan_info($info);
452    }
453
454    sub taskbegin {
455        my ($self, $ref) = @_;
456        my $name = $ref->{'{}task'}->{'Value'};
457        my $time = $ref->{'{}time'}->{'Value'};
458        my $task = Nmap::Scanner::Task->new();
459        $task->name($name);
460        $task->begin_time($time);
461        $self->{NMAP_TASK} = $task;
462    }
463
464    sub taskprogress {
465
466        my ($self, $ref) = @_;
467
468        my $time = $ref->{'{}time'}->{'Value'};
469        my $percent = $ref->{'{}percent'}->{'Value'};
470        my $remaining = $ref->{'{}remaining'}->{'Value'};
471        my $etc = $ref->{'{}etc'}->{'Value'};
472        my $tp = Nmap::Scanner::TaskProgress->new();
473
474        $tp->task($self->{NMAP_TASK});
475        $tp->time($time);
476        $tp->percent($percent);
477        $tp->remaining($remaining);
478        $tp->etc($etc);
479
480        $self->{NMAP_TASK_PROGRESS} = $tp;
481
482    }
483
484    sub taskend {
485
486        my ($self, $ref) = @_;
487        my $name = $ref->{'{}task'}->{'Value'};
488        my $time = $ref->{'{}time'}->{'Value'};
489        my $task = $self->{NMAP_TASK};
490
491        $task->end_time($time);
492
493        $self->{NMAP_NMAPRUN}->add_task($task);
494
495    }
496
497    sub distance {
498        my ($self, $ref) = @_;
499        my $distance = Nmap::Scanner::Distance->new();
500        my $value = $ref->{'{}value'}->{Value};
501        $distance->value($value);
502        $self->{NMAP_HOST}->distance($distance);
503    }
504
505    sub verbose {
506        my ($self, $ref) = @_;
507        $self->{NMAP_NMAPRUN}->verbose($ref->{'{}level'}->{Value});
508    }
509
510    sub debugging {
511        my ($self, $ref) = @_;
512        $self->{NMAP_NMAPRUN}->debugging($ref->{'{}level'}->{Value});
513    }
514
515    sub runstats {
516        my ($self, $ref) = @_;
517        my $stats = Nmap::Scanner::RunStats->new();
518        $self->{NMAP_RUNSTATS} = $stats;
519    }
520
521    sub hosts {
522        my ($self, $ref) = @_;
523        my $hosts = Nmap::Scanner::Hosts->new();
524        $hosts->up($ref->{'{}up'}->{Value});
525        $hosts->down($ref->{'{}down'}->{Value});
526        $hosts->total($ref->{'{}total'}->{Value});
527        $self->{NMAP_RUNSTATS}->hosts($hosts);
528    }
529
530    sub finished {
531        my ($self, $ref) = @_;
532        my $f = Nmap::Scanner::RunStats::Finished->new();
533        $f->time($ref->{'{}time'}->{Value});
534        $f->timestr($ref->{'{}timestr'}->{Value});
535        $self->{NMAP_RUNSTATS}->finished($f);
536    }
537
538    sub results {
539        my $self = shift;
540        return $self->{NMAP_RESULTS};
541    }
542
5431;
544
545