Skip to content

Commit

Permalink
-- minor fix for version 0.4.5
Browse files Browse the repository at this point in the history
 - corrected missing incomplete parameter on parse_fromfile and parse_fromstring
 - added support to run scan in background with sudo support added NmapProcess.sudo_run_background()
 - fixed issue with run() blocking when an error triggered during the scan
  • Loading branch information
savon-noir committed Apr 6, 2014
1 parent 12e9ad3 commit 3c639ea
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 8 deletions.
7 changes: 7 additions & 0 deletions CHANGES.txt
@@ -1,3 +1,10 @@
v0.4.6, 06/04/2014 -- minor fix
- corrected missing incomplete parameter on parse_fromfile
and parse_fromstring
- added support to run scan in background with sudo support
added NmapProcess.sudo_run_background()
- fixed issue with run() blocking when an error triggered
during the scan
v0.4.5, 06/04/2014 -- minor fixes and botox injections
- Added "incomplete" argument in NmapReport.parse()
in order to enable parsing of incomplete or interrupted
Expand Down
4 changes: 2 additions & 2 deletions README.rst
Expand Up @@ -14,8 +14,8 @@ libnmap is a python library enabling python developpers to manipulate nmap proce
libnmap is what you were looking for if you need to implement the following:

- automate or schedule nmap scans on a regular basis
- manipulate nmap scans results to do reporting for instance
- compare and diff nmap scans to generate graphs for instance
- manipulate nmap scans results to do reporting
- compare and diff nmap scans to generate graphs
- batch process scan reports
- ...

Expand Down
23 changes: 18 additions & 5 deletions libnmap/parser.py
Expand Up @@ -82,8 +82,9 @@ def _parse_xml(cls, nmap_data=None, incomplete=False):
raise NmapParserException("wrong nmap_data type given as "
"argument: cannot parse data")

if incomplete:
if incomplete is True:
nmap_data += "</nmaprun>"

try:
root = ET.fromstring(nmap_data)
except:
Expand Down Expand Up @@ -137,7 +138,7 @@ def _parse_xml_report(cls, root=None):
return NmapReport(nmap_scan)

@classmethod
def parse_fromstring(cls, nmap_data, data_type="XML"):
def parse_fromstring(cls, nmap_data, data_type="XML", incomplete=False):
"""
Call generic cls.parse() method and ensure that a string is
passed on as argument. If not, an exception is raised.
Expand All @@ -150,16 +151,23 @@ def parse_fromstring(cls, nmap_data, data_type="XML"):
:param data_type: Specifies the type of data passed on as argument.
:param incomplete: enable you to parse interrupted nmap scans
and/or incomplete nmap xml blocks by adding a </nmaprun> at
the end of the scan.
:type incomplete: boolean
:return: NmapObject
"""

if not isinstance(nmap_data, str):
raise NmapParserException("bad argument type for "
"xarse_fromstring(): should be a string")
return cls.parse(nmap_data, data_type)
return cls.parse(nmap_data, data_type, incomplete)

@classmethod
def parse_fromfile(cls, nmap_report_path, data_type="XML"):
def parse_fromfile(cls, nmap_report_path,
data_type="XML",
incomplete=False):
"""
Call generic cls.parse() method and ensure that a correct file
path is given as argument. If not, an exception is raised.
Expand All @@ -173,13 +181,18 @@ def parse_fromfile(cls, nmap_report_path, data_type="XML"):
:param data_type: Specifies the type of serialization in the file.
:param incomplete: enable you to parse interrupted nmap scans
and/or incomplete nmap xml blocks by adding a </nmaprun> at
the end of the scan.
:type incomplete: boolean
:return: NmapObject
"""

try:
with open(nmap_report_path, 'r') as fileobj:
fdata = fileobj.read()
rval = cls.parse(fdata, data_type)
rval = cls.parse(fdata, data_type, incomplete)
except IOError:
raise
return rval
Expand Down
32 changes: 31 additions & 1 deletion libnmap/process.py
Expand Up @@ -183,6 +183,34 @@ def sudo_run(self, run_as='root'):

return rc

def sudo_run_background(self, run_as='root'):
"""
Public method enabling the library's user to run in background a
nmap scan with priviledges via sudo.
The sudo configuration should be set manually on the local system
otherwise sudo will prompt for a password.
This method alters the command line by prefixing the sudo command to
nmap and will then call self.run()
:param run_as: user name to which the lib needs to sudo to run the scan
:return: return code from nmap execution
"""
sudo_user = run_as.split().pop()
try:
pwd.getpwnam(sudo_user).pw_uid
except KeyError:
raise

sudo_path = self._whereis("sudo")
if sudo_path is None:
raise EnvironmentError(2, "sudo is not installed or "
"could not be found in system path: "
"cannot run nmap with sudo")

self.__sudo_run = "{0} -u {1}".format(sudo_path, sudo_user)
super(NmapProcess, self).start()

def run(self):
"""
Public method which is usually called right after the constructor
Expand Down Expand Up @@ -259,6 +287,8 @@ def __wait(self):
not self.__ioerr_queue.empty()):
if self.__process_killed.isSet():
break
if not self.__ioerr_queue.empty():
self.__stderr += self.__ioerr_queue.get_nowait()
try:
thread_stream = self.__io_queue.get_nowait()
except Empty:
Expand All @@ -281,7 +311,7 @@ def __wait(self):
else:
self.__state = self.FAILED
try:
self.__stderr = self.__ioerr_queue.get(timeout=1)
self.__stderr += self.__ioerr_queue.get(timeout=1)
self.__ioerr_queue.task_done()
except Empty:
pass
Expand Down
24 changes: 24 additions & 0 deletions libnmap/test/process-stressbox/multi_nmap_process.py
@@ -0,0 +1,24 @@
#!/usr/bin/env python

from libnmap.process import NmapProcess
from time import sleep

def make_nmproc_obj(targets, options):
return NmapProcess(targets=targets, options=options)

def start_all(nmprocs):
for nmp in nmprocs:
print("Starting scan for host {0}".format(nmp.targets))
nmp.run()

def summarize(nmprocs):
for nmp in nmprocs:
print "rc: {0} output: {1}".format(nmp.rc, nmp.summary)

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

nm_procs = [make_nmproc_obj(t, nm_opts) for t in nm_targets]
start_all(nm_procs)

summarize(nm_procs)
29 changes: 29 additions & 0 deletions libnmap/test/process-stressbox/multi_nmap_process_background.py
@@ -0,0 +1,29 @@
#!/usr/bin/env python

from libnmap.process import NmapProcess
from time import sleep

def make_nmproc_obj(targets, options):
return NmapProcess(targets=targets, options=options)

def start_all_bg(nmprocs):
for nmp in nmprocs: nmp.run_background()

def any_running(nmprocs):
return any([nmp.is_alive() for nmp in nmprocs])

def summarize(nmprocs):
for nmp in nmprocs:
print "rc: {0} output: {1}".format(nmp.rc, nmp.summary)

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

nm_procs = [make_nmproc_obj(t, nm_opts) for t in nm_targets]
start_all_bg(nm_procs)

while any_running(nm_procs):
print "Nmap Scan running..."
sleep(2)

summarize(nm_procs)
15 changes: 15 additions & 0 deletions libnmap/test/process-stressbox/proc_async.py
@@ -0,0 +1,15 @@
#!/usr/bin/env python

from libnmap.process import NmapProcess
from time import sleep


nmap_proc = NmapProcess(targets="scanme.nmap.org", options="-sT")
nmap_proc.run_background()
while nmap_proc.is_alive():
print "Nmap Scan running: ETC: {0} DONE: {1}%".format(nmap_proc.etc,
nmap_proc.progress)
sleep(2)

print "rc: {0} output: {1}".format(nmap_proc.rc, nmap_proc.summary)
print nmap_proc.stdout
53 changes: 53 additions & 0 deletions libnmap/test/process-stressbox/proc_nmap_like.py
@@ -0,0 +1,53 @@
#!/usr/bin/env python
from libnmap.process import NmapProcess
from libnmap.parser import NmapParser, NmapParserException


# start a new nmap scan on localhost with some specific options
def do_scan(targets, options):
nm = NmapProcess(targets, options)
rc = nm.run()
if rc != 0:
print "nmap scan failed: %s" % (nm.stderr)

try:
parsed = NmapParser.parse(nm.stdout)
except NmapParserException as e:
print "Exception raised while parsing scan: %s" % (e.msg)

return parsed


# print scan results from a nmap report
def print_scan(nmap_report):
print "Starting Nmap {0} ( http://nmap.org ) at {1}".format(
nmap_report._nmaprun['version'],
nmap_report._nmaprun['startstr'])

for host in nmap_report.hosts:
if len(host.hostnames):
tmp_host = host.hostnames.pop()
else:
tmp_host = host.address

print "Nmap scan report for {0} ({1})".format(
tmp_host,
host.address)
print "Host is {0}.".format(host.status)
print " PORT STATE SERVICE"

for serv in host.services:
pserv = "{0:>5s}/{1:3s} {2:12s} {3}".format(
str(serv.port),
serv.protocol,
serv.state,
serv.service)
if len(serv.banner):
pserv += " ({0})".format(serv.banner)
print pserv
print nmap_report.summary


if __name__ == "__main__":
report = do_scan("127.0.0.1", "-sV")
print_scan(report)
30 changes: 30 additions & 0 deletions libnmap/test/process-stressbox/stressback.py
@@ -0,0 +1,30 @@
#!/usr/bin/env python

from libnmap.process import NmapProcess
from time import sleep

def make_nmproc_obj(targets, options):
return NmapProcess(targets=targets, options=options)

def start_all_bg(nmprocs):
for nmp in nmprocs: nmp.run_background()

def any_running(nmprocs):
return any([nmp.is_alive() for nmp in nmprocs])

def summarize(nmprocs):
for nmp in nmprocs:
print "rc: {0} output: {1}".format(nmp.rc, len(nmp.stdout))

nb_targets = 1000
nm_target = "localhost"
nm_opts = "-sP"

nm_targets = [nm_target for i in range(nb_targets)]
nm_procs = [make_nmproc_obj(t, nm_opts) for t in nm_targets]
start_all_bg(nm_procs)

while any_running(nm_procs):
sleep(5)

summarize(nm_procs)
15 changes: 15 additions & 0 deletions libnmap/test/process-stressbox/stresstest.py
@@ -0,0 +1,15 @@
#!/usr/bin/env python
from libnmap.process import NmapProcess
from libnmap.parser import NmapParser, NmapParserException

nm = NmapProcess('127.0.0.1', '-sP')
rc = nm.run()
if rc != 0:
print "nmap scan failed: %s" % (nm.stderr)

try:
report = NmapParser.parse(nm.stdout)
except NmapParserException as e:
print "Exception raised while parsing scan: %s" % (e.msg)

print len(nm.stdout)

0 comments on commit 3c639ea

Please sign in to comment.