Skip to content

Commit 1fe026e

Browse files
committed
feature: added management of nmap tasks
1 parent 5bcda81 commit 1fe026e

4 files changed

Lines changed: 128 additions & 20 deletions

File tree

examples/nmap_task.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/env python
2+
3+
from libnmap.process import NmapProcess
4+
from time import sleep
5+
6+
7+
nmap_proc = NmapProcess(targets="scanme.nmap.org", options="-sV")
8+
nmap_proc.run_background()
9+
while nmap_proc.is_alive():
10+
nmaptask = nmap_proc.current_task
11+
if nmaptask:
12+
print "Task {0} ({1}): ETC: {2} DONE: {3}%".format(nmaptask.name,
13+
nmaptask.status,
14+
nmaptask.etc,
15+
nmaptask.progress)
16+
sleep(2)
17+
18+
print "rc: {0} output: {1}".format(nmap_proc.rc, nmap_proc.summary)
19+
print nmap_proc.stdout

libnmap/process.py

Lines changed: 107 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,30 @@
1717
]
1818

1919

20+
class NmapTask(object):
21+
"""
22+
NmapTask is a internal class used by process. Each time nmap
23+
starts a new task during the scan, a new class will be instanciated.
24+
Classes examples are: "Ping Scan", "NSE script", "DNS Resolve",..
25+
To each class an estimated time to complete is assigned and updated
26+
at least every second within the NmapProcess.
27+
A property NmapProcess.current_task points to the running task at
28+
time T and a dictionnary NmapProcess.tasks with "task name" as key
29+
is built during scan execution
30+
"""
31+
def __init__(self, name, starttime=0, extrainfo=''):
32+
self.name = name
33+
self.etc = 0
34+
self.progress = 0
35+
self.percent = 0
36+
self.remaining = 0
37+
self.status = 'started'
38+
self.starttime = starttime
39+
self.endtime = 0
40+
self.extrainfo = extrainfo
41+
self.updated = 0
42+
43+
2044
class NmapProcess(Thread):
2145
"""
2246
NmapProcess is a class which wraps around the nmap executable.
@@ -95,12 +119,12 @@ def _run_init(self):
95119
self.__starttime = 0
96120
self.__endtime = 0
97121
self.__version = ''
98-
self.__progress = 0
99-
self.__etc = 0
100122
self.__elapsed = ''
101123
self.__summary = ''
102124
self.__stdout = ''
103125
self.__stderr = ''
126+
self.__current_task = ''
127+
self.__nmap_tasks = {}
104128

105129
def _whereis(self, program):
106130
"""
@@ -286,12 +310,17 @@ def ioreader_routine(proc_stdout, io_queue, data_pushed, producing):
286310
self.__state = self.CANCELLED
287311
elif self.rc == 0:
288312
self.__state = self.DONE
289-
self.__progress = 100
313+
if self.current_task:
314+
self.__nmap_tasks[self.current_task.name].progress = 100
290315
else:
291316
self.__state = self.FAILED
292317
return self.rc
293318

294319
def run_background(self):
320+
"""
321+
run nmap scan in background as a thread.
322+
For privileged scans, consider NmapProcess.sudo_run_background()
323+
"""
295324
super(NmapProcess, self).start()
296325

297326
def is_running(self):
@@ -357,12 +386,42 @@ def __process_event(self, eventdata):
357386
edomdoc = pulldom.parseString(eventdata)
358387
for xlmnt, xmlnode in edomdoc:
359388
if xlmnt is not None and xlmnt == pulldom.START_ELEMENT:
360-
if (xmlnode.nodeName == 'taskprogress' and
389+
if (xmlnode.nodeName == 'taskbegin' and
390+
xmlnode.attributes.keys()):
391+
xt = xmlnode.attributes
392+
taskname = xt['task'].value
393+
starttime = xt['time'].value
394+
xinfo=''
395+
if xt.has_key('extrainfo'):
396+
xinfo = xt['extrainfo'].value
397+
newtask = NmapTask(taskname, starttime, xinfo)
398+
self.__nmap_tasks[newtask.name] = newtask
399+
self.__current_task = newtask.name
400+
rval = True
401+
elif (xmlnode.nodeName == 'taskend' and
361402
xmlnode.attributes.keys()):
362-
percent_done = xmlnode.attributes['percent'].value
363-
etc_done = xmlnode.attributes['etc'].value
364-
self.__progress = percent_done
365-
self.__etc = etc_done
403+
xt = xmlnode.attributes
404+
tname = xt['task'].value
405+
xinfo = ''
406+
self.__nmap_tasks[tname].endtime = xt['time'].value
407+
if xt.has_key('extrainfo'):
408+
xinfo = xt['extrainfo'].value
409+
self.__nmap_tasks[tname].extrainfo = xinfo
410+
self.__nmap_tasks[tname].status = "ended"
411+
rval = True
412+
elif (xmlnode.nodeName == 'taskprogress' and
413+
xmlnode.attributes.keys()):
414+
xt = xmlnode.attributes
415+
tname = xt['task'].value
416+
percent = xt['percent'].value
417+
etc = xt['etc'].value
418+
remaining = xt['remaining'].value
419+
updated = xt['time'].value
420+
self.__nmap_tasks[tname].percent = percent
421+
self.__nmap_tasks[tname].progress = percent
422+
self.__nmap_tasks[tname].etc = etc
423+
self.__nmap_tasks[tname].remaining = remaining
424+
self.__nmap_tasks[tname].updated = updated
366425
rval = True
367426
elif (xmlnode.nodeName == 'nmaprun' and
368427
xmlnode.attributes.keys()):
@@ -465,13 +524,13 @@ def summary(self):
465524
return self.__summary
466525

467526
@property
468-
def etc(self):
527+
def tasks(self):
469528
"""
470-
Accessor for estimated time to completion
529+
Accessor returning for the list of tasks ran during nmap scan
471530
472-
:return: estimated time to completion
531+
:return: dict of NmapTask object
473532
"""
474-
return self.__etc
533+
return self.__nmap_tasks
475534

476535
@property
477536
def version(self):
@@ -483,14 +542,41 @@ def version(self):
483542
"""
484543
return self.__version
485544

545+
@property
546+
def current_task(self):
547+
"""
548+
Accessor for the current NmapTask beeing run
549+
550+
:return: NmapTask or None if no task started yet
551+
"""
552+
rval = None
553+
if len(self.__current_task):
554+
rval = self.tasks[self.__current_task]
555+
return rval
556+
557+
@property
558+
def etc(self):
559+
"""
560+
Accessor for estimated time to completion
561+
562+
:return: estimated time to completion
563+
"""
564+
rval = 0
565+
if self.current_task:
566+
rval = self.current_task.etc
567+
return rval
568+
486569
@property
487570
def progress(self):
488571
"""
489572
Accessor for progress status in percentage
490573
491574
:return: percentage of job processed.
492575
"""
493-
return self.__progress
576+
rval = 0
577+
if self.current_task:
578+
rval = self.current_task.progress
579+
return rval
494580

495581
@property
496582
def rc(self):
@@ -524,13 +610,16 @@ def stderr(self):
524610

525611
def main():
526612
def mycallback(nmapscan=None):
527-
if nmapscan.is_running():
528-
print("Progress: {0}% - ETC: {1}").format(nmapscan.progress,
529-
nmapscan.etc)
530-
nm = NmapProcess("localhost", options="-sV",
613+
if nmapscan.is_running() and nmapscan.current_task:
614+
ntask = nmapscan.current_task
615+
print "Task {0} ({1}): ETC: {2} DONE: {3}%".format(ntask.name,
616+
ntask.status,
617+
ntask.etc,
618+
ntask.progress)
619+
nm = NmapProcess("scanme.nmap.org",
620+
options="-A",
531621
event_callback=mycallback)
532622
rc = nm.run()
533-
534623
if rc == 0:
535624
print("Scan started at {0} nmap version: {1}").format(nm.starttime,
536625
nm.version)

libnmap/test/process-stressbox/multi_nmap_process_background.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def any_running(nmprocs):
1414

1515
def summarize(nmprocs):
1616
for nmp in nmprocs:
17-
print "rc: {0} output: {1}".format(nmp.rc, nmp.summary)
17+
print "rc: {0} output: {1} stdout len: {2}".format(nmp.rc, nmp.summary, len(nmp.stdout))
1818

1919
nm_targets = ["localhost", "localhost", "localhost"]
2020
nm_opts = "-sT"

libnmap/test/process-stressbox/stressback.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def summarize(nmprocs):
1616
for nmp in nmprocs:
1717
print "rc: {0} output: {1}".format(nmp.rc, len(nmp.stdout))
1818

19-
nb_targets = 1000
19+
nb_targets = 10
2020
nm_target = "localhost"
2121
nm_opts = "-sP"
2222

0 commit comments

Comments
 (0)