1From bbbf474b2ebdbdac4d557e3351210f3fe2175c33 Mon Sep 17 00:00:00 2001
2From: Mingli Yu <mingli.yu@windriver.com>
3Date: Fri, 14 Feb 2020 10:09:55 +0000
4Subject: [PATCH] Make ndiff support python3
5
6Backport a patch from debian to make ndiff support
7python3.
8
9Refer to https://sources.debian.org/data/main/n/nmap/7.80+dfsg1-2/debian/patches/0004-Python3-port-of-ndiff.patch
10
11Upstream-Status: Pending
12
13Signed-off-by: Mingli Yu <mingli.yu@windriver.com>
14---
15 Makefile.in                   |  12 +-
16 ndiff/ndiff.py                | 495 +++++++++++++++++-----------------
17 ndiff/ndifftest.py            |  94 +++----
18 ndiff/scripts/ndiff           |  14 +-
19 ndiff/setup.py                |  34 +--
20 ndiff/test-scans/anonymize.py |  18 +-
21 6 files changed, 333 insertions(+), 334 deletions(-)
22 mode change 100644 => 100755 ndiff/setup.py
23
24diff --git a/Makefile.in b/Makefile.in
25index eee8863..32f86ba 100644
26--- a/Makefile.in
27+++ b/Makefile.in
28@@ -35,6 +35,7 @@ ZENMAPDIR = @ZENMAPDIR@
29 NDIFFDIR = @NDIFFDIR@
30 NPINGDIR = @NPINGDIR@
31 PYTHON = @PYTHON@
32+PYTHON3 = /usr/bin/env python3
33 DEFS = @DEFS@ -DNMAP_PLATFORM=\"$(NMAP_PLATFORM)\" -DNMAPDATADIR=\"$(nmapdatadir)\"
34 # With GCC, add extra security checks to source code.
35 # http://gcc.gnu.org/ml/gcc-patches/2004-09/msg02055.html
36@@ -260,7 +261,7 @@ clean-zenmap:
37 	rm -f $(ZENMAPDIR)/zenmapCore/Name.pyc
38
39 clean-ndiff:
40-	-cd $(NDIFFDIR) && $(PYTHON) setup.py clean --all
41+	-cd $(NDIFFDIR) && $(PYTHON3) setup.py clean --all
42
43 clean-nping:
44 	-cd $(NPINGDIR) && $(MAKE) clean
45@@ -368,6 +369,7 @@ tests/check_dns: $(OBJS)
46 # this as the location of the interpreter whenever we're not doing a
47 # local installation.
48 DEFAULT_PYTHON_PATH = /usr/bin/env python
49+DEFAULT_PYTHON3_PATH = /usr/bin/env python3
50
51 build-zenmap: $(ZENMAPDIR)/setup.py $(ZENMAPDIR)/zenmapCore/Version.py
52 # When DESTDIR is defined, assume we're building an executable
53@@ -388,13 +390,13 @@ install-zenmap: $(ZENMAPDIR)/setup.py
54 	ln -sf zenmap $(DESTDIR)$(bindir)/xnmap
55
56 build-ndiff:
57-	cd $(NDIFFDIR) && $(PYTHON) setup.py build $(if $(DESTDIR),--executable "$(DEFAULT_PYTHON_PATH)")
58+	cd $(NDIFFDIR) && $(PYTHON3) setup.py build $(if $(DESTDIR),--executable "$(DEFAULT_PYTHON3_PATH)")
59
60 build-nping: $(NPINGDIR)/Makefile build-nbase build-nsock build-netutil $(NPINGDIR)/nping.h @DNET_BUILD@ @PCAP_BUILD@
61 	@cd $(NPINGDIR) && $(MAKE)
62
63 install-ndiff:
64-	cd $(NDIFFDIR) && $(PYTHON) setup.py install --prefix "$(prefix)" --install-lib="${PYTHON_SITEPACKAGES_DIR}" $(if $(DESTDIR),--root "$(DESTDIR)")
65+	cd $(NDIFFDIR) && $(PYTHON3) setup.py install --prefix "$(prefix)" --install-lib="${PYTHON_SITEPACKAGES_DIR}" $(if $(DESTDIR),--root "$(DESTDIR)")
66
67 NSE_FILES = scripts/script.db scripts/*.nse
68 NSE_LIB_LUA_FILES = nselib/*.lua nselib/*.luadoc
69@@ -443,7 +445,7 @@ uninstall-zenmap:
70 	rm -f $(DESTDIR)$(bindir)/xnmap
71
72 uninstall-ndiff:
73-	cd $(NDIFFDIR) && $(PYTHON) setup.py uninstall
74+	cd $(NDIFFDIR) && $(PYTHON3) setup.py uninstall
75
76 uninstall-ncat:
77 	@cd $(NCATDIR) && $(MAKE) uninstall
78@@ -458,7 +460,7 @@ check-ncat:
79 	@cd $(NCATDIR) && $(MAKE) check
80
81 check-ndiff:
82-	@cd $(NDIFFDIR) && $(PYTHON) ndifftest.py
83+	@cd $(NDIFFDIR) && $(PYTHON3) ndifftest.py
84
85 check-nsock:
86 	@cd $(NSOCKDIR)/src && $(MAKE) check
87diff --git a/ndiff/ndiff.py b/ndiff/ndiff.py
88index 043273f..abbd1c5 100755
89--- a/ndiff/ndiff.py
90+++ b/ndiff/ndiff.py
91@@ -1,4 +1,4 @@
92-#!/usr/bin/env python
93+#!/usr/bin/env python3
94
95 # Ndiff
96 #
97@@ -26,11 +26,11 @@ xml.__path__ = [x for x in xml.__path__ if "_xmlplus" not in x]
98 import xml.sax
99 import xml.sax.saxutils
100 import xml.dom.minidom
101-from StringIO import StringIO
102+from io import StringIO
103
104 verbose = False
105
106-NDIFF_XML_VERSION = u"1"
107+NDIFF_XML_VERSION = "1"
108
109
110 class OverrideEntityResolver(xml.sax.handler.EntityResolver):
111@@ -78,35 +78,35 @@ class Scan(object):
112     def write_nmaprun_open(self, writer):
113         attrs = {}
114         if self.scanner is not None:
115-            attrs[u"scanner"] = self.scanner
116+            attrs["scanner"] = self.scanner
117         if self.args is not None:
118-            attrs[u"args"] = self.args
119+            attrs["args"] = self.args
120         if self.start_date is not None:
121-            attrs[u"start"] = "%d" % time.mktime(self.start_date.timetuple())
122-            attrs[u"startstr"] = self.start_date.strftime(
123+            attrs["start"] = "%d" % time.mktime(self.start_date.timetuple())
124+            attrs["startstr"] = self.start_date.strftime(
125                     "%a %b %d %H:%M:%S %Y")
126         if self.version is not None:
127-            attrs[u"version"] = self.version
128-        writer.startElement(u"nmaprun", attrs)
129+            attrs["version"] = self.version
130+        writer.startElement("nmaprun", attrs)
131
132     def write_nmaprun_close(self, writer):
133-        writer.endElement(u"nmaprun")
134+        writer.endElement("nmaprun")
135
136     def nmaprun_to_dom_fragment(self, document):
137         frag = document.createDocumentFragment()
138-        elem = document.createElement(u"nmaprun")
139+        elem = document.createElement("nmaprun")
140         if self.scanner is not None:
141-            elem.setAttribute(u"scanner", self.scanner)
142+            elem.setAttribute("scanner", self.scanner)
143         if self.args is not None:
144-            elem.setAttribute(u"args", self.args)
145+            elem.setAttribute("args", self.args)
146         if self.start_date is not None:
147             elem.setAttribute(
148-                    u"start", "%d" % time.mktime(self.start_date.timetuple()))
149+                    "start", "%d" % time.mktime(self.start_date.timetuple()))
150             elem.setAttribute(
151-                    u"startstr",
152+                    "startstr",
153                     self.start_date.strftime("%a %b %d %H:%M:%S %Y"))
154         if self.version is not None:
155-            elem.setAttribute(u"version", self.version)
156+            elem.setAttribute("version", self.version)
157         frag.appendChild(elem)
158         return frag
159
160@@ -136,17 +136,17 @@ class Host(object):
161
162     def format_name(self):
163         """Return a human-readable identifier for this host."""
164-        address_s = u", ".join(a.s for a in sorted(self.addresses))
165-        hostname_s = u", ".join(sorted(self.hostnames))
166+        address_s = ", ".join(a.s for a in sorted(self.addresses))
167+        hostname_s = ", ".join(sorted(self.hostnames))
168         if len(hostname_s) > 0:
169             if len(address_s) > 0:
170-                return u"%s (%s)" % (hostname_s, address_s)
171+                return "%s (%s)" % (hostname_s, address_s)
172             else:
173                 return hostname_s
174         elif len(address_s) > 0:
175             return address_s
176         else:
177-            return u"<no name>"
178+            return "<no name>"
179
180     def add_port(self, port):
181         self.ports[port.spec] = port
182@@ -163,46 +163,46 @@ class Host(object):
183         return state is None or state in self.extraports
184
185     def extraports_string(self):
186-        list = [(count, state) for (state, count) in self.extraports.items()]
187+        locallist = [(count, state) for (state, count) in list(self.extraports.items())]
188         # Reverse-sort by count.
189-        list.sort(reverse=True)
190-        return u", ".join(
191-                [u"%d %s ports" % (count, state) for (count, state) in list])
192+        locallist.sort(reverse=True)
193+        return ", ".join(
194+                ["%d %s ports" % (count, state) for (count, state) in locallist])
195
196     def state_to_dom_fragment(self, document):
197         frag = document.createDocumentFragment()
198         if self.state is not None:
199-            elem = document.createElement(u"status")
200-            elem.setAttribute(u"state", self.state)
201+            elem = document.createElement("status")
202+            elem.setAttribute("state", self.state)
203             frag.appendChild(elem)
204         return frag
205
206     def hostname_to_dom_fragment(self, document, hostname):
207         frag = document.createDocumentFragment()
208-        elem = document.createElement(u"hostname")
209-        elem.setAttribute(u"name", hostname)
210+        elem = document.createElement("hostname")
211+        elem.setAttribute("name", hostname)
212         frag.appendChild(elem)
213         return frag
214
215     def extraports_to_dom_fragment(self, document):
216         frag = document.createDocumentFragment()
217-        for state, count in self.extraports.items():
218-            elem = document.createElement(u"extraports")
219-            elem.setAttribute(u"state", state)
220-            elem.setAttribute(u"count", unicode(count))
221+        for state, count in list(self.extraports.items()):
222+            elem = document.createElement("extraports")
223+            elem.setAttribute("state", state)
224+            elem.setAttribute("count", str(count))
225             frag.appendChild(elem)
226         return frag
227
228     def os_to_dom_fragment(self, document, os):
229         frag = document.createDocumentFragment()
230-        elem = document.createElement(u"osmatch")
231-        elem.setAttribute(u"name", os)
232+        elem = document.createElement("osmatch")
233+        elem.setAttribute("name", os)
234         frag.appendChild(elem)
235         return frag
236
237     def to_dom_fragment(self, document):
238         frag = document.createDocumentFragment()
239-        elem = document.createElement(u"host")
240+        elem = document.createElement("host")
241
242         if self.state is not None:
243             elem.appendChild(self.state_to_dom_fragment(document))
244@@ -211,13 +211,13 @@ class Host(object):
245             elem.appendChild(addr.to_dom_fragment(document))
246
247         if len(self.hostnames) > 0:
248-            hostnames_elem = document.createElement(u"hostnames")
249+            hostnames_elem = document.createElement("hostnames")
250             for hostname in self.hostnames:
251                 hostnames_elem.appendChild(
252                         self.hostname_to_dom_fragment(document, hostname))
253             elem.appendChild(hostnames_elem)
254
255-        ports_elem = document.createElement(u"ports")
256+        ports_elem = document.createElement("ports")
257         ports_elem.appendChild(self.extraports_to_dom_fragment(document))
258         for port in sorted(self.ports.values()):
259             if not self.is_extraports(port.state):
260@@ -226,13 +226,13 @@ class Host(object):
261             elem.appendChild(ports_elem)
262
263         if len(self.os) > 0:
264-            os_elem = document.createElement(u"os")
265+            os_elem = document.createElement("os")
266             for os in self.os:
267                 os_elem.appendChild(self.os_to_dom_fragment(document, os))
268             elem.appendChild(os_elem)
269
270         if len(self.script_results) > 0:
271-            hostscript_elem = document.createElement(u"hostscript")
272+            hostscript_elem = document.createElement("hostscript")
273             for sr in self.script_results:
274                 hostscript_elem.appendChild(sr.to_dom_fragment(document))
275             elem.appendChild(hostscript_elem)
276@@ -246,7 +246,7 @@ class Address(object):
277         self.s = s
278
279     def __eq__(self, other):
280-        return self.__cmp__(other) == 0
281+        return self.sort_key() == other.sort_key()
282
283     def __ne__(self, other):
284         return not self.__eq__(other)
285@@ -254,8 +254,8 @@ class Address(object):
286     def __hash__(self):
287         return hash(self.sort_key())
288
289-    def __cmp__(self, other):
290-        return cmp(self.sort_key(), other.sort_key())
291+    def __lt__(self, other):
292+        return self.sort_key() < other.sort_key()
293
294     def __str__(self):
295         return str(self.s)
296@@ -264,21 +264,21 @@ class Address(object):
297         return self.s
298
299     def new(type, s):
300-        if type == u"ipv4":
301+        if type == "ipv4":
302             return IPv4Address(s)
303-        elif type == u"ipv6":
304+        elif type == "ipv6":
305             return IPv6Address(s)
306-        elif type == u"mac":
307+        elif type == "mac":
308             return MACAddress(s)
309         else:
310-            raise ValueError(u"Unknown address type %s." % type)
311+            raise ValueError("Unknown address type %s." % type)
312     new = staticmethod(new)
313
314     def to_dom_fragment(self, document):
315         frag = document.createDocumentFragment()
316-        elem = document.createElement(u"address")
317-        elem.setAttribute(u"addr", self.s)
318-        elem.setAttribute(u"addrtype", self.type)
319+        elem = document.createElement("address")
320+        elem.setAttribute("addr", self.s)
321+        elem.setAttribute("addrtype", self.type)
322         frag.appendChild(elem)
323         return frag
324
325@@ -287,21 +287,21 @@ class Address(object):
326
327
328 class IPv4Address(Address):
329-    type = property(lambda self: u"ipv4")
330+    type = property(lambda self: "ipv4")
331
332     def sort_key(self):
333         return (0, self.s)
334
335
336 class IPv6Address(Address):
337-    type = property(lambda self: u"ipv6")
338+    type = property(lambda self: "ipv6")
339
340     def sort_key(self):
341         return (1, self.s)
342
343
344 class MACAddress(Address):
345-    type = property(lambda self: u"mac")
346+    type = property(lambda self: "mac")
347
348     def sort_key(self):
349         return (2, self.s)
350@@ -320,28 +320,25 @@ class Port(object):
351
352     def state_string(self):
353         if self.state is None:
354-            return u"unknown"
355+            return "unknown"
356         else:
357-            return unicode(self.state)
358+            return str(self.state)
359
360     def spec_string(self):
361-        return u"%d/%s" % self.spec
362+        return "%d/%s" % self.spec
363
364-    def __cmp__(self, other):
365-        d = cmp(self.spec, other.spec)
366-        if d != 0:
367-            return d
368-        return cmp((self.spec, self.service, self.script_results),
369-            (other.spec, other.service, other.script_results))
370+    def __lt__(self, other):
371+        return (self.spec, self.service, self.script_results) < (
372+            other.spec, other.service, other.script_results)
373
374     def to_dom_fragment(self, document):
375         frag = document.createDocumentFragment()
376-        elem = document.createElement(u"port")
377-        elem.setAttribute(u"portid", unicode(self.spec[0]))
378-        elem.setAttribute(u"protocol", self.spec[1])
379+        elem = document.createElement("port")
380+        elem.setAttribute("portid", str(self.spec[0]))
381+        elem.setAttribute("protocol", self.spec[1])
382         if self.state is not None:
383-            state_elem = document.createElement(u"state")
384-            state_elem.setAttribute(u"state", self.state)
385+            state_elem = document.createElement("state")
386+            state_elem.setAttribute("state", self.state)
387             elem.appendChild(state_elem)
388         elem.appendChild(self.service.to_dom_fragment(document))
389         for sr in self.script_results:
390@@ -385,7 +382,7 @@ class Service(object):
391         if len(parts) == 0:
392             return None
393         else:
394-            return u"/".join(parts)
395+            return "/".join(parts)
396
397     def version_string(self):
398         """Get a string like in the VERSION column of Nmap output."""
399@@ -395,17 +392,17 @@ class Service(object):
400         if self.version is not None:
401             parts.append(self.version)
402         if self.extrainfo is not None:
403-            parts.append(u"(%s)" % self.extrainfo)
404+            parts.append("(%s)" % self.extrainfo)
405
406         if len(parts) == 0:
407             return None
408         else:
409-            return u" ".join(parts)
410+            return " ".join(parts)
411
412     def to_dom_fragment(self, document):
413         frag = document.createDocumentFragment()
414-        elem = document.createElement(u"service")
415-        for attr in (u"name", u"product", u"version", u"extrainfo", u"tunnel"):
416+        elem = document.createElement("service")
417+        for attr in ("name", "product", "version", "extrainfo", "tunnel"):
418             v = getattr(self, attr)
419             if v is None:
420                 continue
421@@ -435,53 +432,53 @@ class ScriptResult(object):
422         result = []
423         lines = self.output.splitlines()
424         if len(lines) > 0:
425-            lines[0] = self.id + u": " + lines[0]
426+            lines[0] = self.id + ": " + lines[0]
427         for line in lines[:-1]:
428-            result.append(u"|  " + line)
429+            result.append("|  " + line)
430         if len(lines) > 0:
431-            result.append(u"|_ " + lines[-1])
432+            result.append("|_ " + lines[-1])
433         return result
434
435     def to_dom_fragment(self, document):
436         frag = document.createDocumentFragment()
437-        elem = document.createElement(u"script")
438-        elem.setAttribute(u"id", self.id)
439-        elem.setAttribute(u"output", self.output)
440+        elem = document.createElement("script")
441+        elem.setAttribute("id", self.id)
442+        elem.setAttribute("output", self.output)
443         frag.appendChild(elem)
444         return frag
445
446
447 def format_banner(scan):
448     """Format a startup banner more or less like Nmap does."""
449-    scanner = u"Nmap"
450-    if scan.scanner is not None and scan.scanner != u"nmap":
451+    scanner = "Nmap"
452+    if scan.scanner is not None and scan.scanner != "nmap":
453         scanner = scan.scanner
454     parts = [scanner]
455     if scan.version is not None:
456         parts.append(scan.version)
457-    parts.append(u"scan")
458+    parts.append("scan")
459     if scan.start_date is not None:
460-        parts.append(u"initiated %s" % scan.start_date.strftime(
461+        parts.append("initiated %s" % scan.start_date.strftime(
462             "%a %b %d %H:%M:%S %Y"))
463     if scan.args is not None:
464-        parts.append(u"as: %s" % scan.args)
465-    return u" ".join(parts)
466+        parts.append("as: %s" % scan.args)
467+    return " ".join(parts)
468
469
470 def print_script_result_diffs_text(title, script_results_a, script_results_b,
471         script_result_diffs, f=sys.stdout):
472-    table = Table(u"*")
473+    table = Table("*")
474     for sr_diff in script_result_diffs:
475         sr_diff.append_to_port_table(table)
476     if len(table) > 0:
477-        print >> f
478+        print(file=f)
479         if len(script_results_b) == 0:
480-            print >> f, u"-%s:" % title
481+            print("-%s:" % title, file=f)
482         elif len(script_results_a) == 0:
483-            print >> f, u"+%s:" % title
484+            print("+%s:" % title, file=f)
485         else:
486-            print >> f, u" %s:" % title
487-        print >> f, table
488+            print(" %s:" % title, file=f)
489+        print(table, file=f)
490
491
492 def script_result_diffs_to_dom_fragment(elem, script_results_a,
493@@ -489,13 +486,13 @@ def script_result_diffs_to_dom_fragment(elem, script_results_a,
494     if len(script_results_a) == 0 and len(script_results_b) == 0:
495         return document.createDocumentFragment()
496     elif len(script_results_b) == 0:
497-        a_elem = document.createElement(u"a")
498+        a_elem = document.createElement("a")
499         for sr in script_results_a:
500             elem.appendChild(sr.to_dom_fragment(document))
501         a_elem.appendChild(elem)
502         return a_elem
503     elif len(script_results_a) == 0:
504-        b_elem = document.createElement(u"b")
505+        b_elem = document.createElement("b")
506         for sr in script_results_b:
507             elem.appendChild(sr.to_dom_fragment(document))
508         b_elem.appendChild(elem)
509@@ -581,10 +578,10 @@ class ScanDiffText(ScanDiff):
510         banner_a = format_banner(self.scan_a)
511         banner_b = format_banner(self.scan_b)
512         if banner_a != banner_b:
513-            print >> self.f, u"-%s" % banner_a
514-            print >> self.f, u"+%s" % banner_b
515+            print("-%s" % banner_a, file=self.f)
516+            print("+%s" % banner_b, file=self.f)
517         elif verbose:
518-            print >> self.f, u" %s" % banner_a
519+            print(" %s" % banner_a, file=self.f)
520
521     def output_pre_scripts(self, pre_script_result_diffs):
522         print_script_result_diffs_text("Pre-scan script results",
523@@ -597,7 +594,7 @@ class ScanDiffText(ScanDiff):
524             post_script_result_diffs, self.f)
525
526     def output_host_diff(self, h_diff):
527-        print >> self.f
528+        print(file=self.f)
529         h_diff.print_text(self.f)
530
531     def output_ending(self):
532@@ -622,8 +619,8 @@ class ScanDiffXML(ScanDiff):
533
534     def output_beginning(self):
535         self.writer.startDocument()
536-        self.writer.startElement(u"nmapdiff", {u"version": NDIFF_XML_VERSION})
537-        self.writer.startElement(u"scandiff", {})
538+        self.writer.startElement("nmapdiff", {"version": NDIFF_XML_VERSION})
539+        self.writer.startElement("scandiff", {})
540
541         if self.nmaprun_differs():
542             self.writer.frag_a(
543@@ -636,7 +633,7 @@ class ScanDiffXML(ScanDiff):
544
545     def output_pre_scripts(self, pre_script_result_diffs):
546         if len(pre_script_result_diffs) > 0 or verbose:
547-            prescript_elem = self.document.createElement(u"prescript")
548+            prescript_elem = self.document.createElement("prescript")
549             frag = script_result_diffs_to_dom_fragment(
550                 prescript_elem, self.scan_a.pre_script_results,
551                 self.scan_b.pre_script_results, pre_script_result_diffs,
552@@ -646,7 +643,7 @@ class ScanDiffXML(ScanDiff):
553
554     def output_post_scripts(self, post_script_result_diffs):
555         if len(post_script_result_diffs) > 0 or verbose:
556-            postscript_elem = self.document.createElement(u"postscript")
557+            postscript_elem = self.document.createElement("postscript")
558             frag = script_result_diffs_to_dom_fragment(
559                 postscript_elem, self.scan_a.post_script_results,
560                 self.scan_b.post_script_results, post_script_result_diffs,
561@@ -660,8 +657,8 @@ class ScanDiffXML(ScanDiff):
562         frag.unlink()
563
564     def output_ending(self):
565-        self.writer.endElement(u"scandiff")
566-        self.writer.endElement(u"nmapdiff")
567+        self.writer.endElement("scandiff")
568+        self.writer.endElement("nmapdiff")
569         self.writer.endDocument()
570
571
572@@ -719,9 +716,9 @@ class HostDiff(object):
573         self.cost += os_cost
574
575         extraports_a = tuple((count, state)
576-                for (state, count) in self.host_a.extraports.items())
577+                for (state, count) in list(self.host_a.extraports.items()))
578         extraports_b = tuple((count, state)
579-                for (state, count) in self.host_b.extraports.items())
580+                for (state, count) in list(self.host_b.extraports.items()))
581         if extraports_a != extraports_b:
582             self.extraports_changed = True
583             self.cost += 1
584@@ -747,69 +744,69 @@ class HostDiff(object):
585         # Names and addresses.
586         if self.id_changed:
587             if host_a.state is not None:
588-                print >> f, u"-%s:" % host_a.format_name()
589+                print("-%s:" % host_a.format_name(), file=f)
590             if self.host_b.state is not None:
591-                print >> f, u"+%s:" % host_b.format_name()
592+                print("+%s:" % host_b.format_name(), file=f)
593         else:
594-            print >> f, u" %s:" % host_a.format_name()
595+            print(" %s:" % host_a.format_name(), file=f)
596
597         # State.
598         if self.state_changed:
599             if host_a.state is not None:
600-                print >> f, u"-Host is %s." % host_a.state
601+                print("-Host is %s." % host_a.state, file=f)
602             if host_b.state is not None:
603-                print >> f, u"+Host is %s." % host_b.state
604+                print("+Host is %s." % host_b.state, file=f)
605         elif verbose:
606-            print >> f, u" Host is %s." % host_b.state
607+            print(" Host is %s." % host_b.state, file=f)
608
609         # Extraports.
610         if self.extraports_changed:
611             if len(host_a.extraports) > 0:
612-                print >> f, u"-Not shown: %s" % host_a.extraports_string()
613+                print("-Not shown: %s" % host_a.extraports_string(), file=f)
614             if len(host_b.extraports) > 0:
615-                print >> f, u"+Not shown: %s" % host_b.extraports_string()
616+                print("+Not shown: %s" % host_b.extraports_string(), file=f)
617         elif verbose:
618             if len(host_a.extraports) > 0:
619-                print >> f, u" Not shown: %s" % host_a.extraports_string()
620+                print(" Not shown: %s" % host_a.extraports_string(), file=f)
621
622         # Port table.
623-        port_table = Table(u"** * * *")
624+        port_table = Table("** * * *")
625         if host_a.state is None:
626-            mark = u"+"
627+            mark = "+"
628         elif host_b.state is None:
629-            mark = u"-"
630+            mark = "-"
631         else:
632-            mark = u" "
633-        port_table.append((mark, u"PORT", u"STATE", u"SERVICE", u"VERSION"))
634+            mark = " "
635+        port_table.append((mark, "PORT", "STATE", "SERVICE", "VERSION"))
636
637         for port in self.ports:
638             port_diff = self.port_diffs[port]
639             port_diff.append_to_port_table(port_table, host_a, host_b)
640
641         if len(port_table) > 1:
642-            print >> f, port_table
643+            print(port_table, file=f)
644
645         # OS changes.
646         if self.os_changed or verbose:
647             if len(host_a.os) > 0:
648                 if len(host_b.os) > 0:
649-                    print >> f, u" OS details:"
650+                    print(" OS details:", file=f)
651                 else:
652-                    print >> f, u"-OS details:"
653+                    print("-OS details:", file=f)
654             elif len(host_b.os) > 0:
655-                print >> f, u"+OS details:"
656+                print("+OS details:", file=f)
657             # os_diffs is a list of 5-tuples returned by
658             # difflib.SequenceMatcher.
659             for op, i1, i2, j1, j2 in self.os_diffs:
660                 if op == "replace" or op == "delete":
661                     for i in range(i1, i2):
662-                        print >> f, "-  %s" % host_a.os[i]
663+                        print("-  %s" % host_a.os[i], file=f)
664                 if op == "replace" or op == "insert":
665                     for i in range(j1, j2):
666-                        print >> f, "+  %s" % host_b.os[i]
667+                        print("+  %s" % host_b.os[i], file=f)
668                 if op == "equal":
669                     for i in range(i1, i2):
670-                        print >> f, "   %s" % host_a.os[i]
671+                        print("   %s" % host_a.os[i], file=f)
672
673         print_script_result_diffs_text("Host script results",
674             host_a.script_results, host_b.script_results,
675@@ -820,32 +817,32 @@ class HostDiff(object):
676         host_b = self.host_b
677
678         frag = document.createDocumentFragment()
679-        hostdiff_elem = document.createElement(u"hostdiff")
680+        hostdiff_elem = document.createElement("hostdiff")
681         frag.appendChild(hostdiff_elem)
682
683         if host_a.state is None or host_b.state is None:
684             # The host is missing in one scan. Output the whole thing.
685             if host_a.state is not None:
686-                a_elem = document.createElement(u"a")
687+                a_elem = document.createElement("a")
688                 a_elem.appendChild(host_a.to_dom_fragment(document))
689                 hostdiff_elem.appendChild(a_elem)
690             elif host_b.state is not None:
691-                b_elem = document.createElement(u"b")
692+                b_elem = document.createElement("b")
693                 b_elem.appendChild(host_b.to_dom_fragment(document))
694                 hostdiff_elem.appendChild(b_elem)
695             return frag
696
697-        host_elem = document.createElement(u"host")
698+        host_elem = document.createElement("host")
699
700         # State.
701         if host_a.state == host_b.state:
702             if verbose:
703                 host_elem.appendChild(host_a.state_to_dom_fragment(document))
704         else:
705-            a_elem = document.createElement(u"a")
706+            a_elem = document.createElement("a")
707             a_elem.appendChild(host_a.state_to_dom_fragment(document))
708             host_elem.appendChild(a_elem)
709-            b_elem = document.createElement(u"b")
710+            b_elem = document.createElement("b")
711             b_elem.appendChild(host_b.state_to_dom_fragment(document))
712             host_elem.appendChild(b_elem)
713
714@@ -854,31 +851,31 @@ class HostDiff(object):
715         addrset_b = set(host_b.addresses)
716         for addr in sorted(addrset_a.intersection(addrset_b)):
717             host_elem.appendChild(addr.to_dom_fragment(document))
718-        a_elem = document.createElement(u"a")
719+        a_elem = document.createElement("a")
720         for addr in sorted(addrset_a - addrset_b):
721             a_elem.appendChild(addr.to_dom_fragment(document))
722         if a_elem.hasChildNodes():
723             host_elem.appendChild(a_elem)
724-        b_elem = document.createElement(u"b")
725+        b_elem = document.createElement("b")
726         for addr in sorted(addrset_b - addrset_a):
727             b_elem.appendChild(addr.to_dom_fragment(document))
728         if b_elem.hasChildNodes():
729             host_elem.appendChild(b_elem)
730
731         # Host names.
732-        hostnames_elem = document.createElement(u"hostnames")
733+        hostnames_elem = document.createElement("hostnames")
734         hostnameset_a = set(host_a.hostnames)
735         hostnameset_b = set(host_b.hostnames)
736         for hostname in sorted(hostnameset_a.intersection(hostnameset_b)):
737             hostnames_elem.appendChild(
738                     host_a.hostname_to_dom_fragment(document, hostname))
739-        a_elem = document.createElement(u"a")
740+        a_elem = document.createElement("a")
741         for hostname in sorted(hostnameset_a - hostnameset_b):
742             a_elem.appendChild(
743                     host_a.hostname_to_dom_fragment(document, hostname))
744         if a_elem.hasChildNodes():
745             hostnames_elem.appendChild(a_elem)
746-        b_elem = document.createElement(u"b")
747+        b_elem = document.createElement("b")
748         for hostname in sorted(hostnameset_b - hostnameset_a):
749             b_elem.appendChild(
750                     host_b.hostname_to_dom_fragment(document, hostname))
751@@ -887,15 +884,15 @@ class HostDiff(object):
752         if hostnames_elem.hasChildNodes():
753             host_elem.appendChild(hostnames_elem)
754
755-        ports_elem = document.createElement(u"ports")
756+        ports_elem = document.createElement("ports")
757         # Extraports.
758         if host_a.extraports == host_b.extraports:
759             ports_elem.appendChild(host_a.extraports_to_dom_fragment(document))
760         else:
761-            a_elem = document.createElement(u"a")
762+            a_elem = document.createElement("a")
763             a_elem.appendChild(host_a.extraports_to_dom_fragment(document))
764             ports_elem.appendChild(a_elem)
765-            b_elem = document.createElement(u"b")
766+            b_elem = document.createElement("b")
767             b_elem.appendChild(host_b.extraports_to_dom_fragment(document))
768             ports_elem.appendChild(b_elem)
769         # Port list.
770@@ -911,18 +908,18 @@ class HostDiff(object):
771
772         # OS changes.
773         if self.os_changed or verbose:
774-            os_elem = document.createElement(u"os")
775+            os_elem = document.createElement("os")
776             # os_diffs is a list of 5-tuples returned by
777             # difflib.SequenceMatcher.
778             for op, i1, i2, j1, j2 in self.os_diffs:
779                 if op == "replace" or op == "delete":
780-                    a_elem = document.createElement(u"a")
781+                    a_elem = document.createElement("a")
782                     for i in range(i1, i2):
783                         a_elem.appendChild(host_a.os_to_dom_fragment(
784                             document, host_a.os[i]))
785                     os_elem.appendChild(a_elem)
786                 if op == "replace" or op == "insert":
787-                    b_elem = document.createElement(u"b")
788+                    b_elem = document.createElement("b")
789                     for i in range(j1, j2):
790                         b_elem.appendChild(host_b.os_to_dom_fragment(
791                             document, host_b.os[i]))
792@@ -936,7 +933,7 @@ class HostDiff(object):
793
794         # Host script changes.
795         if len(self.script_result_diffs) > 0 or verbose:
796-            hostscript_elem = document.createElement(u"hostscript")
797+            hostscript_elem = document.createElement("hostscript")
798             host_elem.appendChild(script_result_diffs_to_dom_fragment(
799                 hostscript_elem, host_a.script_results,
800                 host_b.script_results, self.script_result_diffs,
801@@ -989,38 +986,38 @@ class PortDiff(object):
802             self.port_b.service.version_string()]
803         if a_columns == b_columns:
804             if verbose or self.script_result_diffs > 0:
805-                table.append([u" "] + a_columns)
806+                table.append([" "] + a_columns)
807         else:
808             if not host_a.is_extraports(self.port_a.state):
809-                table.append([u"-"] + a_columns)
810+                table.append(["-"] + a_columns)
811             if not host_b.is_extraports(self.port_b.state):
812-                table.append([u"+"] + b_columns)
813+                table.append(["+"] + b_columns)
814
815         for sr_diff in self.script_result_diffs:
816             sr_diff.append_to_port_table(table)
817
818     def to_dom_fragment(self, document):
819         frag = document.createDocumentFragment()
820-        portdiff_elem = document.createElement(u"portdiff")
821+        portdiff_elem = document.createElement("portdiff")
822         frag.appendChild(portdiff_elem)
823         if (self.port_a.spec == self.port_b.spec and
824                 self.port_a.state == self.port_b.state):
825-            port_elem = document.createElement(u"port")
826-            port_elem.setAttribute(u"portid", unicode(self.port_a.spec[0]))
827-            port_elem.setAttribute(u"protocol", self.port_a.spec[1])
828+            port_elem = document.createElement("port")
829+            port_elem.setAttribute("portid", str(self.port_a.spec[0]))
830+            port_elem.setAttribute("protocol", self.port_a.spec[1])
831             if self.port_a.state is not None:
832-                state_elem = document.createElement(u"state")
833-                state_elem.setAttribute(u"state", self.port_a.state)
834+                state_elem = document.createElement("state")
835+                state_elem.setAttribute("state", self.port_a.state)
836                 port_elem.appendChild(state_elem)
837             if self.port_a.service == self.port_b.service:
838                 port_elem.appendChild(
839                         self.port_a.service.to_dom_fragment(document))
840             else:
841-                a_elem = document.createElement(u"a")
842+                a_elem = document.createElement("a")
843                 a_elem.appendChild(
844                         self.port_a.service.to_dom_fragment(document))
845                 port_elem.appendChild(a_elem)
846-                b_elem = document.createElement(u"b")
847+                b_elem = document.createElement("b")
848                 b_elem.appendChild(
849                         self.port_b.service.to_dom_fragment(document))
850                 port_elem.appendChild(b_elem)
851@@ -1028,10 +1025,10 @@ class PortDiff(object):
852                 port_elem.appendChild(sr_diff.to_dom_fragment(document))
853             portdiff_elem.appendChild(port_elem)
854         else:
855-            a_elem = document.createElement(u"a")
856+            a_elem = document.createElement("a")
857             a_elem.appendChild(self.port_a.to_dom_fragment(document))
858             portdiff_elem.appendChild(a_elem)
859-            b_elem = document.createElement(u"b")
860+            b_elem = document.createElement("b")
861             b_elem.appendChild(self.port_b.to_dom_fragment(document))
862             portdiff_elem.appendChild(b_elem)
863
864@@ -1086,13 +1083,13 @@ class ScriptResultDiff(object):
865             for op, i1, i2, j1, j2 in diffs.get_opcodes():
866                 if op == "replace" or op == "delete":
867                     for k in range(i1, i2):
868-                        table.append_raw(u"-" + a_lines[k])
869+                        table.append_raw("-" + a_lines[k])
870                 if op == "replace" or op == "insert":
871                     for k in range(j1, j2):
872-                        table.append_raw(u"+" + b_lines[k])
873+                        table.append_raw("+" + b_lines[k])
874                 if op == "equal":
875                     for k in range(i1, i2):
876-                        table.append_raw(u" " + a_lines[k])
877+                        table.append_raw(" " + a_lines[k])
878
879     def to_dom_fragment(self, document):
880         frag = document.createDocumentFragment()
881@@ -1102,11 +1099,11 @@ class ScriptResultDiff(object):
882             frag.appendChild(self.sr_a.to_dom_fragment(document))
883         else:
884             if self.sr_a is not None:
885-                a_elem = document.createElement(u"a")
886+                a_elem = document.createElement("a")
887                 a_elem.appendChild(self.sr_a.to_dom_fragment(document))
888                 frag.appendChild(a_elem)
889             if self.sr_b is not None:
890-                b_elem = document.createElement(u"b")
891+                b_elem = document.createElement("b")
892                 b_elem.appendChild(self.sr_b.to_dom_fragment(document))
893                 frag.appendChild(b_elem)
894         return frag
895@@ -1120,7 +1117,7 @@ class Table(object):
896         copied to the output."""
897         self.widths = []
898         self.rows = []
899-        self.prefix = u""
900+        self.prefix = ""
901         self.padding = []
902         j = 0
903         while j < len(template) and template[j] != "*":
904@@ -1145,7 +1142,7 @@ class Table(object):
905
906         for i in range(len(row)):
907             if row[i] is None:
908-                s = u""
909+                s = ""
910             else:
911                 s = str(row[i])
912             if i == len(self.widths):
913@@ -1167,7 +1164,7 @@ class Table(object):
914         for row in self.rows:
915             parts = [self.prefix]
916             i = 0
917-            if isinstance(row, basestring):
918+            if isinstance(row, str):
919                 # A raw string.
920                 lines.append(row)
921             else:
922@@ -1176,13 +1173,13 @@ class Table(object):
923                     if i < len(self.padding):
924                         parts.append(self.padding[i])
925                     i += 1
926-                lines.append(u"".join(parts).rstrip())
927-        return u"\n".join(lines)
928+                lines.append("".join(parts).rstrip())
929+        return "\n".join(lines)
930
931
932 def warn(str):
933     """Print a warning to stderr."""
934-    print >> sys.stderr, str
935+    print(str, file=sys.stderr)
936
937
938 class NmapContentHandler(xml.sax.handler.ContentHandler):
939@@ -1200,22 +1197,22 @@ class NmapContentHandler(xml.sax.handler.ContentHandler):
940         self.current_port = None
941
942         self._start_elem_handlers = {
943-            u"nmaprun": self._start_nmaprun,
944-            u"host": self._start_host,
945-            u"status": self._start_status,
946-            u"address": self._start_address,
947-            u"hostname": self._start_hostname,
948-            u"extraports": self._start_extraports,
949-            u"port": self._start_port,
950-            u"state": self._start_state,
951-            u"service": self._start_service,
952-            u"script": self._start_script,
953-            u"osmatch": self._start_osmatch,
954-            u"finished": self._start_finished,
955+            "nmaprun": self._start_nmaprun,
956+            "host": self._start_host,
957+            "status": self._start_status,
958+            "address": self._start_address,
959+            "hostname": self._start_hostname,
960+            "extraports": self._start_extraports,
961+            "port": self._start_port,
962+            "state": self._start_state,
963+            "service": self._start_service,
964+            "script": self._start_script,
965+            "osmatch": self._start_osmatch,
966+            "finished": self._start_finished,
967         }
968         self._end_elem_handlers = {
969-            u'host': self._end_host,
970-            u'port': self._end_port,
971+            'host': self._end_host,
972+            'port': self._end_port,
973         }
974
975     def parent_element(self):
976@@ -1245,68 +1242,68 @@ class NmapContentHandler(xml.sax.handler.ContentHandler):
977     def _start_nmaprun(self, name, attrs):
978         assert self.parent_element() is None
979         if "start" in attrs:
980-            start_timestamp = int(attrs.get(u"start"))
981+            start_timestamp = int(attrs.get("start"))
982             self.scan.start_date = datetime.datetime.fromtimestamp(
983                     start_timestamp)
984-        self.scan.scanner = attrs.get(u"scanner")
985-        self.scan.args = attrs.get(u"args")
986-        self.scan.version = attrs.get(u"version")
987+        self.scan.scanner = attrs.get("scanner")
988+        self.scan.args = attrs.get("args")
989+        self.scan.version = attrs.get("version")
990
991     def _start_host(self, name, attrs):
992-        assert self.parent_element() == u"nmaprun"
993+        assert self.parent_element() == "nmaprun"
994         self.current_host = Host()
995         self.scan.hosts.append(self.current_host)
996
997     def _start_status(self, name, attrs):
998-        assert self.parent_element() == u"host"
999+        assert self.parent_element() == "host"
1000         assert self.current_host is not None
1001-        state = attrs.get(u"state")
1002+        state = attrs.get("state")
1003         if state is None:
1004             warn(u'%s element of host %s is missing the "state" attribute; '
1005-                    'assuming \unknown\.' % (
1006+                    r'assuming \unknown\.' % (
1007                         name, self.current_host.format_name()))
1008             return
1009         self.current_host.state = state
1010
1011     def _start_address(self, name, attrs):
1012-        assert self.parent_element() == u"host"
1013+        assert self.parent_element() == "host"
1014         assert self.current_host is not None
1015-        addr = attrs.get(u"addr")
1016+        addr = attrs.get("addr")
1017         if addr is None:
1018-            warn(u'%s element of host %s is missing the "addr" '
1019+            warn('%s element of host %s is missing the "addr" '
1020                     'attribute; skipping.' % (
1021                         name, self.current_host.format_name()))
1022             return
1023-        addrtype = attrs.get(u"addrtype", u"ipv4")
1024+        addrtype = attrs.get("addrtype", "ipv4")
1025         self.current_host.add_address(Address.new(addrtype, addr))
1026
1027     def _start_hostname(self, name, attrs):
1028-        assert self.parent_element() == u"hostnames"
1029+        assert self.parent_element() == "hostnames"
1030         assert self.current_host is not None
1031-        hostname = attrs.get(u"name")
1032+        hostname = attrs.get("name")
1033         if hostname is None:
1034-            warn(u'%s element of host %s is missing the "name" '
1035+            warn('%s element of host %s is missing the "name" '
1036                     'attribute; skipping.' % (
1037                         name, self.current_host.format_name()))
1038             return
1039         self.current_host.add_hostname(hostname)
1040
1041     def _start_extraports(self, name, attrs):
1042-        assert self.parent_element() == u"ports"
1043+        assert self.parent_element() == "ports"
1044         assert self.current_host is not None
1045-        state = attrs.get(u"state")
1046+        state = attrs.get("state")
1047         if state is None:
1048-            warn(u'%s element of host %s is missing the "state" '
1049+            warn('%s element of host %s is missing the "state" '
1050                     'attribute; assuming "unknown".' % (
1051                         name, self.current_host.format_name()))
1052             state = None
1053         if state in self.current_host.extraports:
1054-            warn(u'Duplicate extraports state "%s" in host %s.' % (
1055+            warn('Duplicate extraports state "%s" in host %s.' % (
1056                 state, self.current_host.format_name()))
1057
1058-        count = attrs.get(u"count")
1059+        count = attrs.get("count")
1060         if count is None:
1061-            warn(u'%s element of host %s is missing the "count" '
1062+            warn('%s element of host %s is missing the "count" '
1063                     'attribute; assuming 0.' % (
1064                         name, self.current_host.format_name()))
1065             count = 0
1066@@ -1314,99 +1311,99 @@ class NmapContentHandler(xml.sax.handler.ContentHandler):
1067             try:
1068                 count = int(count)
1069             except ValueError:
1070-                warn(u"Can't convert extraports count \"%s\" "
1071+                warn("Can't convert extraports count \"%s\" "
1072                         "to an integer in host %s; assuming 0." % (
1073-                            attrs[u"count"], self.current_host.format_name()))
1074+                            attrs["count"], self.current_host.format_name()))
1075                 count = 0
1076         self.current_host.extraports[state] = count
1077
1078     def _start_port(self, name, attrs):
1079-        assert self.parent_element() == u"ports"
1080+        assert self.parent_element() == "ports"
1081         assert self.current_host is not None
1082-        portid_str = attrs.get(u"portid")
1083+        portid_str = attrs.get("portid")
1084         if portid_str is None:
1085-            warn(u'%s element of host %s missing the "portid" '
1086+            warn('%s element of host %s missing the "portid" '
1087                     'attribute; skipping.' % (
1088                         name, self.current_host.format_name()))
1089             return
1090         try:
1091             portid = int(portid_str)
1092         except ValueError:
1093-            warn(u"Can't convert portid \"%s\" to an integer "
1094+            warn("Can't convert portid \"%s\" to an integer "
1095                     "in host %s; skipping port." % (
1096                         portid_str, self.current_host.format_name()))
1097             return
1098-        protocol = attrs.get(u"protocol")
1099+        protocol = attrs.get("protocol")
1100         if protocol is None:
1101-            warn(u'%s element of host %s missing the "protocol" '
1102+            warn('%s element of host %s missing the "protocol" '
1103                     'attribute; skipping.' % (
1104                         name, self.current_host.format_name()))
1105             return
1106         self.current_port = Port((portid, protocol))
1107
1108     def _start_state(self, name, attrs):
1109-        assert self.parent_element() == u"port"
1110+        assert self.parent_element() == "port"
1111         assert self.current_host is not None
1112         if self.current_port is None:
1113             return
1114         if "state" not in attrs:
1115-            warn(u'%s element of port %s is missing the "state" '
1116+            warn('%s element of port %s is missing the "state" '
1117                     'attribute; assuming "unknown".' % (
1118                         name, self.current_port.spec_string()))
1119             return
1120-        self.current_port.state = attrs[u"state"]
1121+        self.current_port.state = attrs["state"]
1122         self.current_host.add_port(self.current_port)
1123
1124     def _start_service(self, name, attrs):
1125-        assert self.parent_element() == u"port"
1126+        assert self.parent_element() == "port"
1127         assert self.current_host is not None
1128         if self.current_port is None:
1129             return
1130-        self.current_port.service.name = attrs.get(u"name")
1131-        self.current_port.service.product = attrs.get(u"product")
1132-        self.current_port.service.version = attrs.get(u"version")
1133-        self.current_port.service.extrainfo = attrs.get(u"extrainfo")
1134-        self.current_port.service.tunnel = attrs.get(u"tunnel")
1135+        self.current_port.service.name = attrs.get("name")
1136+        self.current_port.service.product = attrs.get("product")
1137+        self.current_port.service.version = attrs.get("version")
1138+        self.current_port.service.extrainfo = attrs.get("extrainfo")
1139+        self.current_port.service.tunnel = attrs.get("tunnel")
1140
1141     def _start_script(self, name, attrs):
1142         result = ScriptResult()
1143-        result.id = attrs.get(u"id")
1144+        result.id = attrs.get("id")
1145         if result.id is None:
1146-            warn(u'%s element missing the "id" attribute; skipping.' % name)
1147+            warn('%s element missing the "id" attribute; skipping.' % name)
1148             return
1149
1150-        result.output = attrs.get(u"output")
1151+        result.output = attrs.get("output")
1152         if result.output is None:
1153-            warn(u'%s element missing the "output" attribute; skipping.'
1154+            warn('%s element missing the "output" attribute; skipping.'
1155                     % name)
1156             return
1157-        if self.parent_element() == u"prescript":
1158+        if self.parent_element() == "prescript":
1159             self.scan.pre_script_results.append(result)
1160-        elif self.parent_element() == u"postscript":
1161+        elif self.parent_element() == "postscript":
1162             self.scan.post_script_results.append(result)
1163-        elif self.parent_element() == u"hostscript":
1164+        elif self.parent_element() == "hostscript":
1165             self.current_host.script_results.append(result)
1166-        elif self.parent_element() == u"port":
1167+        elif self.parent_element() == "port":
1168             self.current_port.script_results.append(result)
1169         else:
1170-            warn(u"%s element not inside prescript, postscript, hostscript, "
1171+            warn("%s element not inside prescript, postscript, hostscript, "
1172                     "or port element; ignoring." % name)
1173             return
1174
1175     def _start_osmatch(self, name, attrs):
1176-        assert self.parent_element() == u"os"
1177+        assert self.parent_element() == "os"
1178         assert self.current_host is not None
1179         if "name" not in attrs:
1180-            warn(u'%s element of host %s is missing the "name" '
1181+            warn('%s element of host %s is missing the "name" '
1182                     'attribute; skipping.' % (
1183                         name, self.current_host.format_name()))
1184             return
1185-        self.current_host.os.append(attrs[u"name"])
1186+        self.current_host.os.append(attrs["name"])
1187
1188     def _start_finished(self, name, attrs):
1189-        assert self.parent_element() == u"runstats"
1190+        assert self.parent_element() == "runstats"
1191         if "time" in attrs:
1192-            end_timestamp = int(attrs.get(u"time"))
1193+            end_timestamp = int(attrs.get("time"))
1194             self.scan.end_date = datetime.datetime.fromtimestamp(end_timestamp)
1195
1196     def _end_host(self, name):
1197@@ -1425,23 +1422,23 @@ class XMLWriter (xml.sax.saxutils.XMLGenerator):
1198
1199     def frag(self, frag):
1200         for node in frag.childNodes:
1201-            node.writexml(self.f, newl=u"\n")
1202+            node.writexml(self.f, newl="\n")
1203
1204     def frag_a(self, frag):
1205-        self.startElement(u"a", {})
1206+        self.startElement("a", {})
1207         for node in frag.childNodes:
1208-            node.writexml(self.f, newl=u"\n")
1209-        self.endElement(u"a")
1210+            node.writexml(self.f, newl="\n")
1211+        self.endElement("a")
1212
1213     def frag_b(self, frag):
1214-        self.startElement(u"b", {})
1215+        self.startElement("b", {})
1216         for node in frag.childNodes:
1217-            node.writexml(self.f, newl=u"\n")
1218-        self.endElement(u"b")
1219+            node.writexml(self.f, newl="\n")
1220+        self.endElement("b")
1221
1222
1223 def usage():
1224-    print u"""\
1225+    print("""\
1226 Usage: %s [option] FILE1 FILE2
1227 Compare two Nmap XML files and display a list of their differences.
1228 Differences include host state changes, port state changes, and changes to
1229@@ -1451,7 +1448,7 @@ service and OS detection.
1230   -v, --verbose  also show hosts and ports that haven't changed.
1231   --text         display output in text format (default)
1232   --xml          display output in XML format\
1233-""" % sys.argv[0]
1234+""" % sys.argv[0])
1235
1236 EXIT_EQUAL = 0
1237 EXIT_DIFFERENT = 1
1238@@ -1459,8 +1456,8 @@ EXIT_ERROR = 2
1239
1240
1241 def usage_error(msg):
1242-    print >> sys.stderr, u"%s: %s" % (sys.argv[0], msg)
1243-    print >> sys.stderr, u"Try '%s -h' for help." % sys.argv[0]
1244+    print("%s: %s" % (sys.argv[0], msg), file=sys.stderr)
1245+    print("Try '%s -h' for help." % sys.argv[0], file=sys.stderr)
1246     sys.exit(EXIT_ERROR)
1247
1248
1249@@ -1471,7 +1468,7 @@ def main():
1250     try:
1251         opts, input_filenames = getopt.gnu_getopt(
1252                 sys.argv[1:], "hv", ["help", "text", "verbose", "xml"])
1253-    except getopt.GetoptError, e:
1254+    except getopt.GetoptError as e:
1255         usage_error(e.msg)
1256     for o, a in opts:
1257         if o == "-h" or o == "--help":
1258@@ -1481,15 +1478,15 @@ def main():
1259             verbose = True
1260         elif o == "--text":
1261             if output_format is not None and output_format != "text":
1262-                usage_error(u"contradictory output format options.")
1263+                usage_error("contradictory output format options.")
1264             output_format = "text"
1265         elif o == "--xml":
1266             if output_format is not None and output_format != "xml":
1267-                usage_error(u"contradictory output format options.")
1268+                usage_error("contradictory output format options.")
1269             output_format = "xml"
1270
1271     if len(input_filenames) != 2:
1272-        usage_error(u"need exactly two input filenames.")
1273+        usage_error("need exactly two input filenames.")
1274
1275     if output_format is None:
1276         output_format = "text"
1277@@ -1502,8 +1499,8 @@ def main():
1278         scan_a.load_from_file(filename_a)
1279         scan_b = Scan()
1280         scan_b.load_from_file(filename_b)
1281-    except IOError, e:
1282-        print >> sys.stderr, u"Can't open file: %s" % str(e)
1283+    except IOError as e:
1284+        print("Can't open file: %s" % str(e), file=sys.stderr)
1285         sys.exit(EXIT_ERROR)
1286
1287     if output_format == "text":
1288diff --git a/ndiff/ndifftest.py b/ndiff/ndifftest.py
1289index 2fa4ae0..27fc525 100755
1290--- a/ndiff/ndifftest.py
1291+++ b/ndiff/ndifftest.py
1292@@ -1,4 +1,4 @@
1293-#!/usr/bin/env python
1294+#!/usr/bin/env python3
1295
1296 # Unit tests for Ndiff.
1297
1298@@ -22,7 +22,7 @@ for x in dir(ndiff):
1299 sys.dont_write_bytecode = dont_write_bytecode
1300 del dont_write_bytecode
1301
1302-import StringIO
1303+import io
1304
1305
1306 class scan_test(unittest.TestCase):
1307@@ -52,7 +52,7 @@ class scan_test(unittest.TestCase):
1308         scan.load_from_file("test-scans/single.xml")
1309         host = scan.hosts[0]
1310         self.assertEqual(len(host.ports), 5)
1311-        self.assertEqual(host.extraports.items(), [("filtered", 95)])
1312+        self.assertEqual(list(host.extraports.items()), [("filtered", 95)])
1313
1314     def test_extraports_multi(self):
1315         """Test that the correct number of known ports is returned when there
1316@@ -68,9 +68,9 @@ class scan_test(unittest.TestCase):
1317         """Test that nmaprun information is recorded."""
1318         scan = Scan()
1319         scan.load_from_file("test-scans/empty.xml")
1320-        self.assertEqual(scan.scanner, u"nmap")
1321-        self.assertEqual(scan.version, u"4.90RC2")
1322-        self.assertEqual(scan.args, u"nmap -oX empty.xml -p 1-100")
1323+        self.assertEqual(scan.scanner, "nmap")
1324+        self.assertEqual(scan.version, "4.90RC2")
1325+        self.assertEqual(scan.args, "nmap -oX empty.xml -p 1-100")
1326
1327     def test_addresses(self):
1328         """Test that addresses are recorded."""
1329@@ -84,7 +84,7 @@ class scan_test(unittest.TestCase):
1330         scan = Scan()
1331         scan.load_from_file("test-scans/simple.xml")
1332         host = scan.hosts[0]
1333-        self.assertEqual(host.hostnames, [u"scanme.nmap.org"])
1334+        self.assertEqual(host.hostnames, ["scanme.nmap.org"])
1335
1336     def test_os(self):
1337         """Test that OS information is recorded."""
1338@@ -99,7 +99,7 @@ class scan_test(unittest.TestCase):
1339         scan.load_from_file("test-scans/complex.xml")
1340         host = scan.hosts[0]
1341         self.assertTrue(len(host.script_results) > 0)
1342-        self.assertTrue(len(host.ports[(22, u"tcp")].script_results) > 0)
1343+        self.assertTrue(len(host.ports[(22, "tcp")].script_results) > 0)
1344
1345 # This test is commented out because Nmap XML doesn't store any information
1346 # about down hosts, not even the fact that they are down. Recovering the list
1347@@ -128,16 +128,16 @@ class host_test(unittest.TestCase):
1348
1349     def test_format_name(self):
1350         h = Host()
1351-        self.assertTrue(isinstance(h.format_name(), basestring))
1352-        h.add_address(IPv4Address(u"127.0.0.1"))
1353-        self.assertTrue(u"127.0.0.1" in h.format_name())
1354+        self.assertTrue(isinstance(h.format_name(), str))
1355+        h.add_address(IPv4Address("127.0.0.1"))
1356+        self.assertTrue("127.0.0.1" in h.format_name())
1357         h.add_address(IPv6Address("::1"))
1358-        self.assertTrue(u"127.0.0.1" in h.format_name())
1359-        self.assertTrue(u"::1" in h.format_name())
1360-        h.add_hostname(u"localhost")
1361-        self.assertTrue(u"127.0.0.1" in h.format_name())
1362-        self.assertTrue(u"::1" in h.format_name())
1363-        self.assertTrue(u"localhost" in h.format_name())
1364+        self.assertTrue("127.0.0.1" in h.format_name())
1365+        self.assertTrue("::1" in h.format_name())
1366+        h.add_hostname("localhost")
1367+        self.assertTrue("127.0.0.1" in h.format_name())
1368+        self.assertTrue("::1" in h.format_name())
1369+        self.assertTrue("localhost" in h.format_name())
1370
1371     def test_empty_get_port(self):
1372         h = Host()
1373@@ -197,8 +197,8 @@ class host_test(unittest.TestCase):
1374         h = s.hosts[0]
1375         self.assertEqual(len(h.ports), 5)
1376         self.assertEqual(len(h.extraports), 1)
1377-        self.assertEqual(h.extraports.keys()[0], u"filtered")
1378-        self.assertEqual(h.extraports.values()[0], 95)
1379+        self.assertEqual(list(h.extraports.keys())[0], "filtered")
1380+        self.assertEqual(list(h.extraports.values())[0], 95)
1381         self.assertEqual(h.state, "up")
1382
1383
1384@@ -241,13 +241,13 @@ class port_test(unittest.TestCase):
1385     """Test the Port class."""
1386     def test_spec_string(self):
1387         p = Port((10, "tcp"))
1388-        self.assertEqual(p.spec_string(), u"10/tcp")
1389+        self.assertEqual(p.spec_string(), "10/tcp")
1390         p = Port((100, "ip"))
1391-        self.assertEqual(p.spec_string(), u"100/ip")
1392+        self.assertEqual(p.spec_string(), "100/ip")
1393
1394     def test_state_string(self):
1395         p = Port((10, "tcp"))
1396-        self.assertEqual(p.state_string(), u"unknown")
1397+        self.assertEqual(p.state_string(), "unknown")
1398
1399
1400 class service_test(unittest.TestCase):
1401@@ -255,47 +255,47 @@ class service_test(unittest.TestCase):
1402     def test_compare(self):
1403         """Test that services with the same contents compare equal."""
1404         a = Service()
1405-        a.name = u"ftp"
1406-        a.product = u"FooBar FTP"
1407-        a.version = u"1.1.1"
1408-        a.tunnel = u"ssl"
1409+        a.name = "ftp"
1410+        a.product = "FooBar FTP"
1411+        a.version = "1.1.1"
1412+        a.tunnel = "ssl"
1413         self.assertEqual(a, a)
1414         b = Service()
1415-        b.name = u"ftp"
1416-        b.product = u"FooBar FTP"
1417-        b.version = u"1.1.1"
1418-        b.tunnel = u"ssl"
1419+        b.name = "ftp"
1420+        b.product = "FooBar FTP"
1421+        b.version = "1.1.1"
1422+        b.tunnel = "ssl"
1423         self.assertEqual(a, b)
1424-        b.name = u"http"
1425+        b.name = "http"
1426         self.assertNotEqual(a, b)
1427         c = Service()
1428         self.assertNotEqual(a, c)
1429
1430     def test_tunnel(self):
1431         serv = Service()
1432-        serv.name = u"http"
1433-        serv.tunnel = u"ssl"
1434-        self.assertEqual(serv.name_string(), u"ssl/http")
1435+        serv.name = "http"
1436+        serv.tunnel = "ssl"
1437+        self.assertEqual(serv.name_string(), "ssl/http")
1438
1439     def test_version_string(self):
1440         serv = Service()
1441-        serv.product = u"FooBar"
1442+        serv.product = "FooBar"
1443         self.assertTrue(len(serv.version_string()) > 0)
1444         serv = Service()
1445-        serv.version = u"1.2.3"
1446+        serv.version = "1.2.3"
1447         self.assertTrue(len(serv.version_string()) > 0)
1448         serv = Service()
1449-        serv.extrainfo = u"misconfigured"
1450+        serv.extrainfo = "misconfigured"
1451         self.assertTrue(len(serv.version_string()) > 0)
1452         serv = Service()
1453-        serv.product = u"FooBar"
1454-        serv.version = u"1.2.3"
1455+        serv.product = "FooBar"
1456+        serv.version = "1.2.3"
1457         # Must match Nmap output.
1458         self.assertEqual(serv.version_string(),
1459-                u"%s %s" % (serv.product, serv.version))
1460-        serv.extrainfo = u"misconfigured"
1461+                "%s %s" % (serv.product, serv.version))
1462+        serv.extrainfo = "misconfigured"
1463         self.assertEqual(serv.version_string(),
1464-                u"%s %s (%s)" % (serv.product, serv.version, serv.extrainfo))
1465+                "%s %s (%s)" % (serv.product, serv.version, serv.extrainfo))
1466
1467
1468 class ScanDiffSub(ScanDiff):
1469@@ -703,7 +703,7 @@ class scan_diff_xml_test(unittest.TestCase):
1470         a.load_from_file("test-scans/empty.xml")
1471         b = Scan()
1472         b.load_from_file("test-scans/simple.xml")
1473-        f = StringIO.StringIO()
1474+        f = io.StringIO()
1475         self.scan_diff = ScanDiffXML(a, b, f)
1476         self.scan_diff.output()
1477         self.xml = f.getvalue()
1478@@ -712,8 +712,8 @@ class scan_diff_xml_test(unittest.TestCase):
1479     def test_well_formed(self):
1480         try:
1481             document = xml.dom.minidom.parseString(self.xml)
1482-        except Exception, e:
1483-            self.fail(u"Parsing XML diff output caused the exception: %s"
1484+        except Exception as e:
1485+            self.fail("Parsing XML diff output caused the exception: %s"
1486                     % str(e))
1487
1488
1489@@ -739,8 +739,8 @@ def host_apply_diff(host, diff):
1490         host.os = diff.host_b.os[:]
1491
1492     if diff.extraports_changed:
1493-        for state in host.extraports.keys():
1494-            for port in host.ports.values():
1495+        for state in list(host.extraports.keys()):
1496+            for port in list(host.ports.values()):
1497                 if port.state == state:
1498                     del host.ports[port.spec]
1499         host.extraports = diff.host_b.extraports.copy()
1500diff --git a/ndiff/scripts/ndiff b/ndiff/scripts/ndiff
1501index 8517c07..4671e73 100755
1502--- a/ndiff/scripts/ndiff
1503+++ b/ndiff/scripts/ndiff
1504@@ -1,4 +1,4 @@
1505-#!/usr/bin/env python
1506+#!/usr/bin/env python3
1507
1508 # Ndiff
1509 #
1510@@ -67,15 +67,15 @@ if INSTALL_LIB is not None and is_secure_dir(INSTALL_LIB):
1511
1512 try:
1513     import ndiff
1514-except ImportError, e:
1515-    print >> sys.stderr, """\
1516+except ImportError as e:
1517+    print("""\
1518 Could not import the ndiff module: %s.
1519-I checked in these directories:""" % repr(e.message)
1520+I checked in these directories:""" % repr(e), file=sys.stderr)
1521     for dir in sys.path:
1522-        print >> sys.stderr, "    %s" % dir
1523-    print >> sys.stderr, """\
1524+        print("    %s" % dir, file=sys.stderr)
1525+    print("""\
1526 If you installed Ndiff in another directory, you may have to add the
1527-modules directory to the PYTHONPATH environment variable."""
1528+modules directory to the PYTHONPATH environment variable.""", file=sys.stderr)
1529     sys.exit(1)
1530
1531 import ndiff
1532diff --git a/ndiff/setup.py b/ndiff/setup.py
1533old mode 100644
1534new mode 100755
1535index b5e254c..c49bcf3
1536--- a/ndiff/setup.py
1537+++ b/ndiff/setup.py
1538@@ -94,7 +94,7 @@ class checked_install(distutils.command.install.install):
1539         self.saved_prefix = sys.prefix
1540         try:
1541             distutils.command.install.install.finalize_options(self)
1542-        except distutils.errors.DistutilsPlatformError, e:
1543+        except distutils.errors.DistutilsPlatformError as e:
1544             raise distutils.errors.DistutilsPlatformError(str(e) + """
1545 Installing your distribution's python-dev package may solve this problem.""")
1546
1547@@ -155,13 +155,13 @@ Installing your distribution's python-dev package may solve this problem.""")
1548 #!/usr/bin/env python
1549 import errno, os, os.path, sys
1550
1551-print 'Uninstall %(name)s'
1552+print('Uninstall %(name)s')
1553
1554 answer = raw_input('Are you sure that you want to uninstall '
1555     '%(name)s (yes/no) ')
1556
1557 if answer != 'yes' and answer != 'y':
1558-    print 'Not uninstalling.'
1559+    print('Not uninstalling.')
1560     sys.exit(0)
1561
1562 """ % {'name': APP_NAME}
1563@@ -177,8 +177,8 @@ if answer != 'yes' and answer != 'y':
1564                     # This should never happen (everything gets installed
1565                     # inside the root), but if it does, be safe and don't
1566                     # delete anything.
1567-                    uninstaller += ("print '%s was not installed inside "
1568-                        "the root %s; skipping.'\n" % (output, self.root))
1569+                    uninstaller += ("print('%s was not installed inside "
1570+                        "the root %s; skipping.')\n" % (output, self.root))
1571                     continue
1572                 output = path_strip_prefix(output, self.root)
1573                 assert os.path.isabs(output)
1574@@ -202,24 +202,24 @@ for path in INSTALLED_FILES:
1575         dirs.append(path)
1576 # Delete the files.
1577 for file in files:
1578-    print "Removing '%s'." % file
1579+    print("Removing '%s'." % file)
1580     try:
1581         os.remove(file)
1582-    except OSError, e:
1583-        print >> sys.stderr, '  Error: %s.' % str(e)
1584+    except OSError as e:
1585+        print('  Error: %s.' % str(e), file=sys.stderr)
1586 # Delete the directories. First reverse-sort the normalized paths by
1587 # length so that child directories are deleted before their parents.
1588 dirs = [os.path.normpath(dir) for dir in dirs]
1589 dirs.sort(key = len, reverse = True)
1590 for dir in dirs:
1591     try:
1592-        print "Removing the directory '%s'." % dir
1593+        print("Removing the directory '%s'." % dir)
1594         os.rmdir(dir)
1595-    except OSError, e:
1596+    except OSError as e:
1597         if e.errno == errno.ENOTEMPTY:
1598-            print "Directory '%s' not empty; not removing." % dir
1599+            print("Directory '%s' not empty; not removing." % dir)
1600         else:
1601-            print >> sys.stderr, str(e)
1602+            print(str(e), file=sys.stderr)
1603 """
1604
1605         uninstaller_file = open(uninstaller_filename, 'w')
1606@@ -227,7 +227,7 @@ for dir in dirs:
1607         uninstaller_file.close()
1608
1609         # Set exec bit for uninstaller
1610-        mode = ((os.stat(uninstaller_filename)[ST_MODE]) | 0555) & 07777
1611+        mode = ((os.stat(uninstaller_filename)[ST_MODE]) | 0o555) & 0o7777
1612         os.chmod(uninstaller_filename, mode)
1613
1614     def write_installed_files(self):
1615@@ -242,7 +242,7 @@ for dir in dirs:
1616         try:
1617             for output in self.get_installed_files():
1618                 assert "\n" not in output
1619-                print >> f, output
1620+                print(output, file=f)
1621         finally:
1622             f.close()
1623
1624@@ -266,7 +266,7 @@ class my_uninstall(distutils.cmd.Command):
1625         # Read the list of installed files.
1626         try:
1627             f = open(INSTALLED_FILES_NAME, "r")
1628-        except IOError, e:
1629+        except IOError as e:
1630             if e.errno == errno.ENOENT:
1631                 log.error("Couldn't open the installation record '%s'. "
1632                         "Have you installed yet?" % INSTALLED_FILES_NAME)
1633@@ -289,7 +289,7 @@ class my_uninstall(distutils.cmd.Command):
1634             try:
1635                 if not self.dry_run:
1636                     os.remove(file)
1637-            except OSError, e:
1638+            except OSError as e:
1639                 log.error(str(e))
1640         # Delete the directories. First reverse-sort the normalized paths by
1641         # length so that child directories are deleted before their parents.
1642@@ -300,7 +300,7 @@ class my_uninstall(distutils.cmd.Command):
1643                 log.info("Removing the directory '%s'." % dir)
1644                 if not self.dry_run:
1645                     os.rmdir(dir)
1646-            except OSError, e:
1647+            except OSError as e:
1648                 if e.errno == errno.ENOTEMPTY:
1649                     log.info("Directory '%s' not empty; not removing." % dir)
1650                 else:
1651diff --git a/ndiff/test-scans/anonymize.py b/ndiff/test-scans/anonymize.py
1652index 9ba612a..fd251fe 100755
1653--- a/ndiff/test-scans/anonymize.py
1654+++ b/ndiff/test-scans/anonymize.py
1655@@ -1,4 +1,4 @@
1656-#!/usr/bin/env python
1657+#!/usr/bin/env python3
1658
1659 # Anonymize an Nmap XML file, replacing host name and IP addresses with random
1660 # anonymous ones. Anonymized names will be consistent between runs of the
1661@@ -20,20 +20,20 @@ r = random.Random()
1662
1663
1664 def hash(s):
1665-    digest = hashlib.sha512(s).hexdigest()
1666+    digest = hashlib.sha512(s.encode()).hexdigest()
1667     return int(digest, 16)
1668
1669
1670 def anonymize_mac_address(addr):
1671     r.seed(hash(addr))
1672     nums = (0, 0, 0) + tuple(r.randrange(256) for i in range(3))
1673-    return u":".join(u"%02X" % x for x in nums)
1674+    return ":".join("%02X" % x for x in nums)
1675
1676
1677 def anonymize_ipv4_address(addr):
1678     r.seed(hash(addr))
1679     nums = (10,) + tuple(r.randrange(256) for i in range(3))
1680-    return u".".join(unicode(x) for x in nums)
1681+    return ".".join(str(x) for x in nums)
1682
1683
1684 def anonymize_ipv6_address(addr):
1685@@ -41,7 +41,7 @@ def anonymize_ipv6_address(addr):
1686     # RFC 4193.
1687     nums = (0xFD00 + r.randrange(256),)
1688     nums = nums + tuple(r.randrange(65536) for i in range(7))
1689-    return u":".join("%04X" % x for x in nums)
1690+    return ":".join("%04X" % x for x in nums)
1691
1692 # Maps to memoize address and host name conversions.
1693 hostname_map = {}
1694@@ -54,11 +54,11 @@ def anonymize_hostname(name):
1695     LETTERS = "acbdefghijklmnopqrstuvwxyz"
1696     r.seed(hash(name))
1697     length = r.randrange(5, 10)
1698-    prefix = u"".join(r.sample(LETTERS, length))
1699+    prefix = "".join(r.sample(LETTERS, length))
1700     num = r.randrange(1000)
1701-    hostname_map[name] = u"%s-%d.example.com" % (prefix, num)
1702+    hostname_map[name] = "%s-%d.example.com" % (prefix, num)
1703     if VERBOSE:
1704-        print >> sys.stderr, "Replace %s with %s" % (name, hostname_map[name])
1705+        print("Replace %s with %s" % (name, hostname_map[name]), file=sys.stderr)
1706     return hostname_map[name]
1707
1708 mac_re = re.compile(r'\b([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}\b')
1709@@ -78,7 +78,7 @@ def anonymize_address(addr):
1710     else:
1711         assert False
1712     if VERBOSE:
1713-        print >> sys.stderr, "Replace %s with %s" % (addr, address_map[addr])
1714+        print("Replace %s with %s" % (addr, address_map[addr]), file=sys.stderr)
1715     return address_map[addr]
1716
1717
1718--
17192.24.1
1720
1721