Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ndiff: improve python3 compatibility #1484

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 63 additions & 47 deletions ndiff/ndiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
# David Fifield
# based on a design by Michael Pattrick

from __future__ import print_function

import datetime
import difflib
import getopt
Expand All @@ -26,7 +28,17 @@
import xml.sax
import xml.sax.saxutils
import xml.dom.minidom
from StringIO import StringIO
try:
from StringIO import StringIO
except ImportError:
from io import StringIO

PY3 = sys.version_info[0] == 3

if PY3:
string_types = str
else:
string_types = basestring

verbose = False

Expand Down Expand Up @@ -246,16 +258,16 @@ def __init__(self, s):
self.s = s

def __eq__(self, other):
return self.__cmp__(other) == 0
return self.sort_key() == other.sort_key()

def __ne__(self, other):
return not self.__eq__(other)

def __hash__(self):
return hash(self.sort_key())

def __cmp__(self, other):
return cmp(self.sort_key(), other.sort_key())
def __lt__(self, other):
return self.sort_key() < other.sort_key()

def __str__(self):
return str(self.s)
Expand Down Expand Up @@ -322,22 +334,26 @@ def state_string(self):
if self.state is None:
return u"unknown"
else:
return unicode(self.state)
return str(self.state)

def spec_string(self):
return u"%d/%s" % self.spec

def __cmp__(self, other):
d = cmp(self.spec, other.spec)
if d != 0:
return d
return cmp((self.spec, self.service, self.script_results),
(other.spec, other.service, other.script_results))
def __hash__(self):
return hash((self.spec, self.state))

def __eq__(self, other):
return (self.spec, self.service, self.script_results) == (
other.spec, other.service, other.script_results)

def __lt__(self, other):
return (self.spec, self.service, self.script_results) < (
other.spec, other.service, other.script_results)

def to_dom_fragment(self, document):
frag = document.createDocumentFragment()
elem = document.createElement(u"port")
elem.setAttribute(u"portid", unicode(self.spec[0]))
elem.setAttribute(u"portid", str(self.spec[0]))
elem.setAttribute(u"protocol", self.spec[1])
if self.state is not None:
state_elem = document.createElement(u"state")
Expand Down Expand Up @@ -474,14 +490,14 @@ def print_script_result_diffs_text(title, script_results_a, script_results_b,
for sr_diff in script_result_diffs:
sr_diff.append_to_port_table(table)
if len(table) > 0:
print >> f
print(file=f)
if len(script_results_b) == 0:
print >> f, u"-%s:" % title
print(u"-%s:" % title, file=f)
elif len(script_results_a) == 0:
print >> f, u"+%s:" % title
print(u"+%s:" % title, file=f)
else:
print >> f, u" %s:" % title
print >> f, table
print(u" %s:" % title, file=f)
print(table, file=f)


def script_result_diffs_to_dom_fragment(elem, script_results_a,
Expand Down Expand Up @@ -581,10 +597,10 @@ def output_beginning(self):
banner_a = format_banner(self.scan_a)
banner_b = format_banner(self.scan_b)
if banner_a != banner_b:
print >> self.f, u"-%s" % banner_a
print >> self.f, u"+%s" % banner_b
print(u"-%s" % banner_a, file=self.f)
print(u"+%s" % banner_b, file=self.f)
elif verbose:
print >> self.f, u" %s" % banner_a
print(u" %s" % banner_a, file=self.f)

def output_pre_scripts(self, pre_script_result_diffs):
print_script_result_diffs_text("Pre-scan script results",
Expand All @@ -597,7 +613,7 @@ def output_post_scripts(self, post_script_result_diffs):
post_script_result_diffs, self.f)

def output_host_diff(self, h_diff):
print >> self.f
print(file=self.f)
h_diff.print_text(self.f)

def output_ending(self):
Expand Down Expand Up @@ -747,30 +763,30 @@ def print_text(self, f=sys.stdout):
# Names and addresses.
if self.id_changed:
if host_a.state is not None:
print >> f, u"-%s:" % host_a.format_name()
print(u"-%s:" % host_a.format_name(), file=f)
if self.host_b.state is not None:
print >> f, u"+%s:" % host_b.format_name()
print(u"+%s:" % host_b.format_name(), file=f)
else:
print >> f, u" %s:" % host_a.format_name()
print(u" %s:" % host_a.format_name(), file=f)

# State.
if self.state_changed:
if host_a.state is not None:
print >> f, u"-Host is %s." % host_a.state
print(u"-Host is %s." % host_a.state, file=f)
if host_b.state is not None:
print >> f, u"+Host is %s." % host_b.state
print(u"+Host is %s." % host_b.state, file=f)
elif verbose:
print >> f, u" Host is %s." % host_b.state
print(u" Host is %s." % host_b.state, file=f)

# Extraports.
if self.extraports_changed:
if len(host_a.extraports) > 0:
print >> f, u"-Not shown: %s" % host_a.extraports_string()
print(u"-Not shown: %s" % host_a.extraports_string(), file=f)
if len(host_b.extraports) > 0:
print >> f, u"+Not shown: %s" % host_b.extraports_string()
print(u"+Not shown: %s" % host_b.extraports_string(), file=f)
elif verbose:
if len(host_a.extraports) > 0:
print >> f, u" Not shown: %s" % host_a.extraports_string()
print(u" Not shown: %s" % host_a.extraports_string(), file=f)

# Port table.
port_table = Table(u"** * * *")
Expand All @@ -787,29 +803,29 @@ def print_text(self, f=sys.stdout):
port_diff.append_to_port_table(port_table, host_a, host_b)

if len(port_table) > 1:
print >> f, port_table
print(port_table, file=f)

# OS changes.
if self.os_changed or verbose:
if len(host_a.os) > 0:
if len(host_b.os) > 0:
print >> f, u" OS details:"
print(u" OS details:", file=f)
else:
print >> f, u"-OS details:"
print(u"-OS details:", file=f)
elif len(host_b.os) > 0:
print >> f, u"+OS details:"
print(u"+OS details:", file=f)
# os_diffs is a list of 5-tuples returned by
# difflib.SequenceMatcher.
for op, i1, i2, j1, j2 in self.os_diffs:
if op == "replace" or op == "delete":
for i in range(i1, i2):
print >> f, "- %s" % host_a.os[i]
print("- %s" % host_a.os[i], file=f)
if op == "replace" or op == "insert":
for i in range(j1, j2):
print >> f, "+ %s" % host_b.os[i]
print("+ %s" % host_b.os[i], file=f)
if op == "equal":
for i in range(i1, i2):
print >> f, " %s" % host_a.os[i]
print(" %s" % host_a.os[i], file=f)

print_script_result_diffs_text("Host script results",
host_a.script_results, host_b.script_results,
Expand Down Expand Up @@ -1167,7 +1183,7 @@ def __str__(self):
for row in self.rows:
parts = [self.prefix]
i = 0
if isinstance(row, basestring):
if isinstance(row, string_types):
# A raw string.
lines.append(row)
else:
Expand All @@ -1182,7 +1198,7 @@ def __str__(self):

def warn(str):
"""Print a warning to stderr."""
print >> sys.stderr, str
print(str, file=sys.stderr)


class NmapContentHandler(xml.sax.handler.ContentHandler):
Expand Down Expand Up @@ -1263,7 +1279,7 @@ def _start_status(self, name, attrs):
state = attrs.get(u"state")
if state is None:
warn(u'%s element of host %s is missing the "state" attribute; '
'assuming \unknown\.' % (
r'assuming \unknown\.' % (
name, self.current_host.format_name()))
return
self.current_host.state = state
Expand Down Expand Up @@ -1441,7 +1457,7 @@ def frag_b(self, frag):


def usage():
print u"""\
print(u"""\
Usage: %s [option] FILE1 FILE2
Compare two Nmap XML files and display a list of their differences.
Differences include host state changes, port state changes, and changes to
Expand All @@ -1451,16 +1467,16 @@ def usage():
-v, --verbose also show hosts and ports that haven't changed.
--text display output in text format (default)
--xml display output in XML format\
""" % sys.argv[0]
""" % sys.argv[0])

EXIT_EQUAL = 0
EXIT_DIFFERENT = 1
EXIT_ERROR = 2


def usage_error(msg):
print >> sys.stderr, u"%s: %s" % (sys.argv[0], msg)
print >> sys.stderr, u"Try '%s -h' for help." % sys.argv[0]
print(u"%s: %s" % (sys.argv[0], msg), file=sys.stderr)
print(u"Try '%s -h' for help." % sys.argv[0], file=sys.stderr)
sys.exit(EXIT_ERROR)


Expand All @@ -1471,7 +1487,7 @@ def main():
try:
opts, input_filenames = getopt.gnu_getopt(
sys.argv[1:], "hv", ["help", "text", "verbose", "xml"])
except getopt.GetoptError, e:
except getopt.GetoptError as e:
usage_error(e.msg)
for o, a in opts:
if o == "-h" or o == "--help":
Expand Down Expand Up @@ -1502,8 +1518,8 @@ def main():
scan_a.load_from_file(filename_a)
scan_b = Scan()
scan_b.load_from_file(filename_b)
except IOError, e:
print >> sys.stderr, u"Can't open file: %s" % str(e)
except IOError as e:
print(u"Can't open file: %s" % str(e), file=sys.stderr)
sys.exit(EXIT_ERROR)

if output_format == "text":
Expand Down
24 changes: 17 additions & 7 deletions ndiff/ndifftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,17 @@
sys.dont_write_bytecode = dont_write_bytecode
del dont_write_bytecode

import StringIO
try:
from StringIO import StringIO
except ImportError:
from io import StringIO

PY3 = sys.version_info[0] == 3

if PY3:
string_types = str
else:
string_types = basestring


class scan_test(unittest.TestCase):
Expand Down Expand Up @@ -52,7 +62,7 @@ def test_extraports(self):
scan.load_from_file("test-scans/single.xml")
host = scan.hosts[0]
self.assertEqual(len(host.ports), 5)
self.assertEqual(host.extraports.items(), [("filtered", 95)])
self.assertEqual(list(host.extraports.items()), [("filtered", 95)])

def test_extraports_multi(self):
"""Test that the correct number of known ports is returned when there
Expand Down Expand Up @@ -128,7 +138,7 @@ def test_empty(self):

def test_format_name(self):
h = Host()
self.assertTrue(isinstance(h.format_name(), basestring))
self.assertTrue(isinstance(h.format_name(), string_types))
h.add_address(IPv4Address(u"127.0.0.1"))
self.assertTrue(u"127.0.0.1" in h.format_name())
h.add_address(IPv6Address("::1"))
Expand Down Expand Up @@ -197,8 +207,8 @@ def test_parse(self):
h = s.hosts[0]
self.assertEqual(len(h.ports), 5)
self.assertEqual(len(h.extraports), 1)
self.assertEqual(h.extraports.keys()[0], u"filtered")
self.assertEqual(h.extraports.values()[0], 95)
self.assertEqual(list(h.extraports.keys())[0], u"filtered")
self.assertEqual(list(h.extraports.values())[0], 95)
self.assertEqual(h.state, "up")


Expand Down Expand Up @@ -703,7 +713,7 @@ def setUp(self):
a.load_from_file("test-scans/empty.xml")
b = Scan()
b.load_from_file("test-scans/simple.xml")
f = StringIO.StringIO()
f = StringIO()
self.scan_diff = ScanDiffXML(a, b, f)
self.scan_diff.output()
self.xml = f.getvalue()
Expand All @@ -712,7 +722,7 @@ def setUp(self):
def test_well_formed(self):
try:
document = xml.dom.minidom.parseString(self.xml)
except Exception, e:
except Exception as e:
self.fail(u"Parsing XML diff output caused the exception: %s"
% str(e))

Expand Down
14 changes: 8 additions & 6 deletions ndiff/scripts/ndiff
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
# David Fifield
# based on a design by Michael Pattrick

from __future__ import print_function

import sys

# Check if the given directory, and all its parent directories, are owned and
Expand Down Expand Up @@ -67,15 +69,15 @@ if INSTALL_LIB is not None and is_secure_dir(INSTALL_LIB):

try:
import ndiff
except ImportError, e:
print >> sys.stderr, """\
except ImportError as e:
print("""\
Could not import the ndiff module: %s.
I checked in these directories:""" % repr(e.message)
I checked in these directories:""" % repr(e), file=sys.stderr)
for dir in sys.path:
print >> sys.stderr, " %s" % dir
print >> sys.stderr, """\
print(" %s" % dir, file=sys.stderr)
print("""\
If you installed Ndiff in another directory, you may have to add the
modules directory to the PYTHONPATH environment variable."""
modules directory to the PYTHONPATH environment variable.""", file=sys.stderr)
sys.exit(1)

import ndiff
Expand Down
Loading