Skip to content

Commit d65a2b1

Browse files
committed
Merge pull request savon-noir#20 from savon-noir/fixv0.4.5
Fix for version 0.4.5
2 parents 12e9ad3 + 2522c5c commit d65a2b1

10 files changed

Lines changed: 223 additions & 8 deletions

File tree

CHANGES.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
v0.4.6, 06/04/2014 -- minor fix
2+
- corrected missing incomplete parameter on parse_fromfile
3+
and parse_fromstring
4+
- added support to run scan in background with sudo support
5+
added NmapProcess.sudo_run_background()
6+
- fixed issue with run() blocking when an error triggered
7+
during the scan
18
v0.4.5, 06/04/2014 -- minor fixes and botox injections
29
- Added "incomplete" argument in NmapReport.parse()
310
in order to enable parsing of incomplete or interrupted

README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ libnmap is a python library enabling python developpers to manipulate nmap proce
1414
libnmap is what you were looking for if you need to implement the following:
1515

1616
- automate or schedule nmap scans on a regular basis
17-
- manipulate nmap scans results to do reporting for instance
18-
- compare and diff nmap scans to generate graphs for instance
17+
- manipulate nmap scans results to do reporting
18+
- compare and diff nmap scans to generate graphs
1919
- batch process scan reports
2020
- ...
2121

libnmap/parser.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,9 @@ def _parse_xml(cls, nmap_data=None, incomplete=False):
8282
raise NmapParserException("wrong nmap_data type given as "
8383
"argument: cannot parse data")
8484

85-
if incomplete:
85+
if incomplete is True:
8686
nmap_data += "</nmaprun>"
87+
8788
try:
8889
root = ET.fromstring(nmap_data)
8990
except:
@@ -137,7 +138,7 @@ def _parse_xml_report(cls, root=None):
137138
return NmapReport(nmap_scan)
138139

139140
@classmethod
140-
def parse_fromstring(cls, nmap_data, data_type="XML"):
141+
def parse_fromstring(cls, nmap_data, data_type="XML", incomplete=False):
141142
"""
142143
Call generic cls.parse() method and ensure that a string is
143144
passed on as argument. If not, an exception is raised.
@@ -150,16 +151,23 @@ def parse_fromstring(cls, nmap_data, data_type="XML"):
150151
151152
:param data_type: Specifies the type of data passed on as argument.
152153
154+
:param incomplete: enable you to parse interrupted nmap scans
155+
and/or incomplete nmap xml blocks by adding a </nmaprun> at
156+
the end of the scan.
157+
:type incomplete: boolean
158+
153159
:return: NmapObject
154160
"""
155161

156162
if not isinstance(nmap_data, str):
157163
raise NmapParserException("bad argument type for "
158164
"xarse_fromstring(): should be a string")
159-
return cls.parse(nmap_data, data_type)
165+
return cls.parse(nmap_data, data_type, incomplete)
160166

161167
@classmethod
162-
def parse_fromfile(cls, nmap_report_path, data_type="XML"):
168+
def parse_fromfile(cls, nmap_report_path,
169+
data_type="XML",
170+
incomplete=False):
163171
"""
164172
Call generic cls.parse() method and ensure that a correct file
165173
path is given as argument. If not, an exception is raised.
@@ -173,13 +181,18 @@ def parse_fromfile(cls, nmap_report_path, data_type="XML"):
173181
174182
:param data_type: Specifies the type of serialization in the file.
175183
184+
:param incomplete: enable you to parse interrupted nmap scans
185+
and/or incomplete nmap xml blocks by adding a </nmaprun> at
186+
the end of the scan.
187+
:type incomplete: boolean
188+
176189
:return: NmapObject
177190
"""
178191

179192
try:
180193
with open(nmap_report_path, 'r') as fileobj:
181194
fdata = fileobj.read()
182-
rval = cls.parse(fdata, data_type)
195+
rval = cls.parse(fdata, data_type, incomplete)
183196
except IOError:
184197
raise
185198
return rval

libnmap/process.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,34 @@ def sudo_run(self, run_as='root'):
183183

184184
return rc
185185

186+
def sudo_run_background(self, run_as='root'):
187+
"""
188+
Public method enabling the library's user to run in background a
189+
nmap scan with priviledges via sudo.
190+
The sudo configuration should be set manually on the local system
191+
otherwise sudo will prompt for a password.
192+
This method alters the command line by prefixing the sudo command to
193+
nmap and will then call self.run()
194+
195+
:param run_as: user name to which the lib needs to sudo to run the scan
196+
197+
:return: return code from nmap execution
198+
"""
199+
sudo_user = run_as.split().pop()
200+
try:
201+
pwd.getpwnam(sudo_user).pw_uid
202+
except KeyError:
203+
raise
204+
205+
sudo_path = self._whereis("sudo")
206+
if sudo_path is None:
207+
raise EnvironmentError(2, "sudo is not installed or "
208+
"could not be found in system path: "
209+
"cannot run nmap with sudo")
210+
211+
self.__sudo_run = "{0} -u {1}".format(sudo_path, sudo_user)
212+
super(NmapProcess, self).start()
213+
186214
def run(self):
187215
"""
188216
Public method which is usually called right after the constructor
@@ -259,6 +287,8 @@ def __wait(self):
259287
not self.__ioerr_queue.empty()):
260288
if self.__process_killed.isSet():
261289
break
290+
if not self.__ioerr_queue.empty():
291+
self.__stderr += self.__ioerr_queue.get_nowait()
262292
try:
263293
thread_stream = self.__io_queue.get_nowait()
264294
except Empty:
@@ -281,7 +311,7 @@ def __wait(self):
281311
else:
282312
self.__state = self.FAILED
283313
try:
284-
self.__stderr = self.__ioerr_queue.get(timeout=1)
314+
self.__stderr += self.__ioerr_queue.get(timeout=1)
285315
self.__ioerr_queue.task_done()
286316
except Empty:
287317
pass
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env python
2+
3+
from libnmap.process import NmapProcess
4+
5+
def make_nmproc_obj(targets, options):
6+
return NmapProcess(targets=targets, options=options)
7+
8+
def start_all(nmprocs):
9+
for nmp in nmprocs:
10+
print("Starting scan for host {0}".format(nmp.targets))
11+
nmp.run()
12+
13+
def summarize(nmprocs):
14+
for nmp in nmprocs:
15+
print "rc: {0} output: {1}".format(nmp.rc, nmp.summary)
16+
17+
nm_targets = ["localhost", "localhost", "localhost"]
18+
nm_opts = "-sT"
19+
20+
nm_procs = [make_nmproc_obj(t, nm_opts) for t in nm_targets]
21+
start_all(nm_procs)
22+
23+
summarize(nm_procs)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/usr/bin/env python
2+
3+
from libnmap.process import NmapProcess
4+
from time import sleep
5+
6+
def make_nmproc_obj(targets, options):
7+
return NmapProcess(targets=targets, options=options)
8+
9+
def start_all_bg(nmprocs):
10+
for nmp in nmprocs: nmp.run_background()
11+
12+
def any_running(nmprocs):
13+
return any([nmp.is_alive() for nmp in nmprocs])
14+
15+
def summarize(nmprocs):
16+
for nmp in nmprocs:
17+
print "rc: {0} output: {1}".format(nmp.rc, nmp.summary)
18+
19+
nm_targets = ["localhost", "localhost", "localhost"]
20+
nm_opts = "-sT"
21+
22+
nm_procs = [make_nmproc_obj(t, nm_opts) for t in nm_targets]
23+
start_all_bg(nm_procs)
24+
25+
while any_running(nm_procs):
26+
print "Nmap Scan running..."
27+
sleep(2)
28+
29+
summarize(nm_procs)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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="-sT")
8+
nmap_proc.run_background()
9+
while nmap_proc.is_alive():
10+
print "Nmap Scan running: ETC: {0} DONE: {1}%".format(nmap_proc.etc,
11+
nmap_proc.progress)
12+
sleep(2)
13+
14+
print "rc: {0} output: {1}".format(nmap_proc.rc, nmap_proc.summary)
15+
print nmap_proc.stdout
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/usr/bin/env python
2+
from libnmap.process import NmapProcess
3+
from libnmap.parser import NmapParser, NmapParserException
4+
5+
6+
# start a new nmap scan on localhost with some specific options
7+
def do_scan(targets, options):
8+
nm = NmapProcess(targets, options)
9+
rc = nm.run()
10+
if rc != 0:
11+
print "nmap scan failed: %s" % (nm.stderr)
12+
13+
try:
14+
parsed = NmapParser.parse(nm.stdout)
15+
except NmapParserException as e:
16+
print "Exception raised while parsing scan: %s" % (e.msg)
17+
18+
return parsed
19+
20+
21+
# print scan results from a nmap report
22+
def print_scan(nmap_report):
23+
print "Starting Nmap {0} ( http://nmap.org ) at {1}".format(
24+
nmap_report._nmaprun['version'],
25+
nmap_report._nmaprun['startstr'])
26+
27+
for host in nmap_report.hosts:
28+
if len(host.hostnames):
29+
tmp_host = host.hostnames.pop()
30+
else:
31+
tmp_host = host.address
32+
33+
print "Nmap scan report for {0} ({1})".format(
34+
tmp_host,
35+
host.address)
36+
print "Host is {0}.".format(host.status)
37+
print " PORT STATE SERVICE"
38+
39+
for serv in host.services:
40+
pserv = "{0:>5s}/{1:3s} {2:12s} {3}".format(
41+
str(serv.port),
42+
serv.protocol,
43+
serv.state,
44+
serv.service)
45+
if len(serv.banner):
46+
pserv += " ({0})".format(serv.banner)
47+
print pserv
48+
print nmap_report.summary
49+
50+
51+
if __name__ == "__main__":
52+
report = do_scan("127.0.0.1", "-sV")
53+
print_scan(report)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env python
2+
3+
from libnmap.process import NmapProcess
4+
from time import sleep
5+
6+
def make_nmproc_obj(targets, options):
7+
return NmapProcess(targets=targets, options=options)
8+
9+
def start_all_bg(nmprocs):
10+
for nmp in nmprocs: nmp.run_background()
11+
12+
def any_running(nmprocs):
13+
return any([nmp.is_alive() for nmp in nmprocs])
14+
15+
def summarize(nmprocs):
16+
for nmp in nmprocs:
17+
print "rc: {0} output: {1}".format(nmp.rc, len(nmp.stdout))
18+
19+
nb_targets = 1000
20+
nm_target = "localhost"
21+
nm_opts = "-sP"
22+
23+
nm_targets = [nm_target for i in range(nb_targets)]
24+
nm_procs = [make_nmproc_obj(t, nm_opts) for t in nm_targets]
25+
start_all_bg(nm_procs)
26+
27+
while any_running(nm_procs):
28+
sleep(5)
29+
30+
summarize(nm_procs)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/usr/bin/env python
2+
from libnmap.process import NmapProcess
3+
from libnmap.parser import NmapParser, NmapParserException
4+
5+
nm = NmapProcess('127.0.0.1', '-sP')
6+
rc = nm.run()
7+
if rc != 0:
8+
print "nmap scan failed: %s" % (nm.stderr)
9+
10+
try:
11+
report = NmapParser.parse(nm.stdout)
12+
except NmapParserException as e:
13+
print "Exception raised while parsing scan: %s" % (e.msg)
14+
15+
print len(nm.stdout)

0 commit comments

Comments
 (0)