summaryrefslogtreecommitdiffstats
path: root/zenmap/radialnet
diff options
context:
space:
mode:
Diffstat (limited to 'zenmap/radialnet')
-rw-r--r--zenmap/radialnet/CHANGELOG149
-rw-r--r--zenmap/radialnet/README10
-rw-r--r--zenmap/radialnet/__init__.py58
-rw-r--r--zenmap/radialnet/bestwidgets/__init__.py72
-rw-r--r--zenmap/radialnet/bestwidgets/boxes.py202
-rw-r--r--zenmap/radialnet/bestwidgets/buttons.py91
-rw-r--r--zenmap/radialnet/bestwidgets/comboboxes.py135
-rw-r--r--zenmap/radialnet/bestwidgets/expanders.py97
-rw-r--r--zenmap/radialnet/bestwidgets/frames.py96
-rw-r--r--zenmap/radialnet/bestwidgets/labels.py94
-rw-r--r--zenmap/radialnet/bestwidgets/textview.py209
-rw-r--r--zenmap/radialnet/bestwidgets/windows.py103
-rw-r--r--zenmap/radialnet/core/ArgvHandle.py98
-rw-r--r--zenmap/radialnet/core/Coordinate.py208
-rw-r--r--zenmap/radialnet/core/Graph.py247
-rw-r--r--zenmap/radialnet/core/Info.py63
-rw-r--r--zenmap/radialnet/core/Interpolation.py157
-rw-r--r--zenmap/radialnet/core/XMLHandler.py324
-rw-r--r--zenmap/radialnet/core/__init__.py56
-rw-r--r--zenmap/radialnet/gui/Application.py159
-rw-r--r--zenmap/radialnet/gui/ControlWidget.py1270
-rw-r--r--zenmap/radialnet/gui/Dialogs.py89
-rw-r--r--zenmap/radialnet/gui/HostsViewer.py231
-rw-r--r--zenmap/radialnet/gui/Image.py164
-rw-r--r--zenmap/radialnet/gui/LegendWindow.py242
-rw-r--r--zenmap/radialnet/gui/NodeNotebook.py742
-rw-r--r--zenmap/radialnet/gui/NodeWindow.py129
-rw-r--r--zenmap/radialnet/gui/RadialNet.py2020
-rw-r--r--zenmap/radialnet/gui/SaveDialog.py169
-rw-r--r--zenmap/radialnet/gui/Toolbar.py309
-rw-r--r--zenmap/radialnet/gui/__init__.py56
-rwxr-xr-xzenmap/radialnet/radialnet.pyw116
-rw-r--r--zenmap/radialnet/share/sample/nmap_example.xml480
-rw-r--r--zenmap/radialnet/util/__init__.py56
-rw-r--r--zenmap/radialnet/util/drawing.py67
-rw-r--r--zenmap/radialnet/util/geometry.py152
-rw-r--r--zenmap/radialnet/util/integration.py269
-rw-r--r--zenmap/radialnet/util/misc.py115
38 files changed, 9304 insertions, 0 deletions
diff --git a/zenmap/radialnet/CHANGELOG b/zenmap/radialnet/CHANGELOG
new file mode 100644
index 0000000..cb2c4af
--- /dev/null
+++ b/zenmap/radialnet/CHANGELOG
@@ -0,0 +1,149 @@
+RadialNet 0.44
+--------------
+
+o Fix bug caused by gtk.gdk.PixbufLoader that don't recognize image type in
+ Windows. Now gtk.gdk.pixbuf_new_from_file() is used (thanks to IndianZ).
+
+o Added new version of XMLTreeParser.py called XMLHandler.py.
+
+o Added new version of bestwidgets.
+
+o Change radialnet.py file name to radialnet.pyw to application starts with no
+ command prompt on Windows.
+
+
+RadialNet 0.43
+--------------
+
+o Clean __init__.py files.
+
+o Added vendor to address field when addrtype is MAC.
+
+o Added new version of bestwidgets.
+
+o Fixed bug caused by list key error on NodeNotebook.py.
+
+
+RadialNet 0.42
+--------------
+
+o Fixed bug caused when tcptssequence xml node has no values information.
+
+o Improve the coherence in animation.
+
+
+RadialNet 0.41
+--------------
+
+o Create a solution to fix the gtk issue that don't show text in BWTextEditor
+ class correctly (thanks to Luís Bastião).
+
+o Due to encoding problems the encoding of all file was changed to utf-8.
+
+o Created a new logo and About dialog. New logo was used as window icon too.
+
+o Added a Info module that contains the program information.
+
+o Added a Image superclass in gui/Icons.py file. Change gui/Icons.py filename to
+ gui/Image.py.
+
+o Added a Path class to hold directory base. This make possible RadialNet
+ runnable from any directory.
+
+
+RadialNet 0.4
+-------------
+
+o Incorporated a HostsViewer class that lists the hosts in a left list and show
+ its information in a NodeNotebook in the right side. It can be accessed in the
+ new Tools button on toolbar.
+
+o Added a complete OS Fingerprint and Sequences view with other general
+ informations in the NodeNotebook class.
+
+o Create a set of class to make more easy follow the Gnome HIG 2.0
+ specifications and the creation of complex composed widgets. This set is
+ called bestwidgets.
+
+o Create a class and file named NodeNotebook and move node noetebook pages to
+ it.
+
+o Added a Application class to solve user interface issues like hide and show of
+ widgets.
+
+o Improve command line options with ArgvHandle class. And add a file chooser
+ dialog to interface to open Nmap XML files from the GUI.
+
+o Enable visualization of host with no traceroute information. These nodes are
+ link to localhost and has black dashed connections. This make possible for
+ non-privileged users use the RadialNet.
+
+o Remove statusbar from node's window and put hostname in title.
+
+o Added a new notebook to node's window with all traceroute information.
+
+o By default latency numbers are hidden, ring gap is changed to 30 and fisheye
+ spread factor to 0.5.
+
+o Convert frames in control panel to expanders to grant to user see only he
+ need.
+
+o Move actions toolbar menu to right control panel. And a information button
+ was added. They are placed in a "Action" expander.
+
+o Include "Option" expander in "View" expander.
+
+
+RadialNet 0.31
+--------------
+
+o Improve the animation by coherent calculus of children positions. Using some
+ angle conditions we can perform a better animation.
+
+o Reduce excessive processing on some update methods that check values in
+ RadialNet class periodically.
+
+
+RadialNet 0.3
+-------------
+
+o New services viewer on pop-up windows. With NSE script output support with a
+ text viewer.
+
+o Clean the interface hiding some controllers if no needed using toggle buttons.
+
+o Added option to enable/disable slow-in/slow-out animation in options list.
+
+o Fixes wrong English messages in the program (thanks to DePriest, Jasey).
+
+
+RadialNet 0.2
+-------------
+
+o Improve icons apparency, size and position.
+
+o Added latency mean for each edge as text and width of edge. The edge's width
+ is normalized between 1 (min value) to 5 (max value) pixels.
+
+o When a node is a group we draw a black circle if localhost (127.0.0.1) is
+ grouped on it.
+
+o Change mouse button events to a toolbar. The options. We include buttons to
+ guide mouse actions. Three options are added, one to change the center of
+ visualization, another to group node's children, and more one to fill node's
+ draw region.
+
+o Added region fill feature. When clicking in a node with this option the
+ hierarchical region that a node use to draw its children is filled with a
+ chosen color (red, yellow or green).
+
+o Added different draw properties for unknown nodes and edges latencies. If a
+ node is unknown we draw a white node with blue stroke, and if the edge latency
+ is unknown we draw it dashed.
+
+o Decrease draw complexity. Reducing the number of draw method and making the
+ methods for draw node and edges more intelligent.
+
+o Fixed division by zero error when number of frames is less than 3. This
+ happens because the interpolation method need a number of pass great or equal
+ to 3 (thanks to Hiroshi).
diff --git a/zenmap/radialnet/README b/zenmap/radialnet/README
new file mode 100644
index 0000000..54d1a7a
--- /dev/null
+++ b/zenmap/radialnet/README
@@ -0,0 +1,10 @@
+To start the application you may just double-click the radialnet.py file or
+start it with the following command:
+
+ $ python radialnet.py
+
+To view command line list of arguments type '--help':
+
+ $ python radialnet.py --help
+
+Att, João Medeiros <ignotus21@gmail.com>.
diff --git a/zenmap/radialnet/__init__.py b/zenmap/radialnet/__init__.py
new file mode 100644
index 0000000..98e2c1e
--- /dev/null
+++ b/zenmap/radialnet/__init__.py
@@ -0,0 +1,58 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+__all__ = ["bestwidgets", "core", "gui", "util"]
diff --git a/zenmap/radialnet/bestwidgets/__init__.py b/zenmap/radialnet/bestwidgets/__init__.py
new file mode 100644
index 0000000..03877f0
--- /dev/null
+++ b/zenmap/radialnet/bestwidgets/__init__.py
@@ -0,0 +1,72 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk
+
+gtk_version_major, gtk_version_minor, gtk_version_release = gi.version_info
+
+#from boxes import BWHBox, BWVBox, BWTable, BWStatusbar, BWScrolledWindow
+#from buttons import BWStockButton, BWToggleStockButton
+#from comboboxes import BWChangeableComboBoxEntry
+#from expanders import BWExpander
+#from frames import BWFrame
+#from labels import BWLabel, BWSectionLabel
+#from textview import BWTextView, BWTextEditor
+#from windows import BWWindow, BWMainWindow, BWAlertDialog
diff --git a/zenmap/radialnet/bestwidgets/boxes.py b/zenmap/radialnet/bestwidgets/boxes.py
new file mode 100644
index 0000000..5b43a90
--- /dev/null
+++ b/zenmap/radialnet/bestwidgets/boxes.py
@@ -0,0 +1,202 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk
+
+__all__ = ('BWBox', 'BWHBox', 'BWVBox',
+ 'BWStatusbar', 'BWTable', 'BWScrolledWindow')
+
+
+class BWBox(Gtk.Box):
+ """
+ """
+ def bw_pack_start_expand_fill(self, widget, padding=0):
+ """
+ """
+ self.pack_start(widget, True, True, padding)
+
+ def bw_pack_start_expand_nofill(self, widget, padding=0):
+ """
+ """
+ self.pack_start(widget, True, False, padding)
+
+ def bw_pack_start_noexpand_nofill(self, widget, padding=0):
+ """
+ """
+ self.pack_start(widget, False, False, padding)
+
+ def bw_pack_end_expand_fill(self, widget, padding=0):
+ """
+ """
+ self.pack_end(widget, True, True, padding)
+
+ def bw_pack_end_expand_nofill(self, widget, padding=0):
+ """
+ """
+ self.pack_end(widget, True, False, padding)
+
+ def bw_pack_end_noexpand_nofill(self, widget, padding=0):
+ """
+ """
+ self.pack_end(widget, False, False, padding)
+
+
+class BWHBox(BWBox):
+ """
+ """
+ def __init__(self, homogeneous=False, spacing=12):
+ """
+ """
+ Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL,
+ homogeneous=homogeneous, spacing=spacing)
+
+
+class BWVBox(BWBox):
+ """
+ """
+ def __init__(self, homogeneous=False, spacing=12):
+ """
+ """
+ Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL,
+ homogeneous=homogeneous, spacing=spacing)
+
+
+class BWStatusbar(Gtk.Statusbar, BWBox):
+ """
+ """
+ def __init__(self, homogeneous=False, spacing=12):
+ """
+ """
+ Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL,
+ homogeneous=homogeneous, spacing=spacing)
+
+
+class BWTable(Gtk.Table, BWBox):
+ """
+ """
+ def __init__(self, rows=1, columns=1, homogeneous=False):
+ """
+ """
+ Gtk.Table.__init__(self, n_rows=rows, n_columns=columns,
+ homogeneous=homogeneous)
+ self.bw_set_spacing(12)
+
+ self.__rows = rows
+ self.__columns = columns
+
+ self.__last_point = (0, 0)
+
+ def bw_set_spacing(self, spacing):
+ """
+ """
+ self.set_row_spacings(spacing)
+ self.set_col_spacings(spacing)
+
+ def bw_resize(self, rows, columns):
+ """
+ """
+ self.__rows = rows
+ self.__columns = columns
+
+ self.resize(rows, columns)
+
+ def bw_attach_next(self,
+ child,
+ xoptions=Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL,
+ yoptions=Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL,
+ xpadding=0,
+ ypadding=0):
+ """
+ """
+ row, column = self.__last_point
+
+ if row != self.__rows:
+
+ self.attach(child,
+ column,
+ column + 1,
+ row,
+ row + 1,
+ xoptions,
+ yoptions,
+ xpadding,
+ ypadding)
+
+ if column + 1 == self.__columns:
+
+ column = 0
+ row += 1
+
+ else:
+ column += 1
+
+ self.__last_point = (row, column)
+
+
+class BWScrolledWindow(Gtk.ScrolledWindow):
+ """
+ """
+ def __init__(self):
+ """
+ """
+ Gtk.ScrolledWindow.__init__(self)
+ self.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
+ self.set_shadow_type(Gtk.ShadowType.NONE)
+ self.set_border_width(6)
diff --git a/zenmap/radialnet/bestwidgets/buttons.py b/zenmap/radialnet/bestwidgets/buttons.py
new file mode 100644
index 0000000..3d3239d
--- /dev/null
+++ b/zenmap/radialnet/bestwidgets/buttons.py
@@ -0,0 +1,91 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk
+
+
+class BWStockButton(Gtk.Button):
+ """
+ """
+ def __init__(self, stock, text=None, size=Gtk.IconSize.BUTTON):
+ """
+ """
+ Gtk.Button.__init__(self, label=text)
+
+ self.__size = size
+
+ self.__image = Gtk.Image()
+ self.__image.set_from_stock(stock, self.__size)
+ self.set_image(self.__image)
+
+
+class BWToggleStockButton(Gtk.ToggleButton):
+ """
+ """
+ def __init__(self, stock, text=None, size=Gtk.IconSize.BUTTON):
+ """
+ """
+ Gtk.ToggleButton.__init__(self, label=text)
+
+ self.__size = size
+
+ self.__image = Gtk.Image()
+ self.__image.set_from_stock(stock, self.__size)
+ self.set_image(self.__image)
diff --git a/zenmap/radialnet/bestwidgets/comboboxes.py b/zenmap/radialnet/bestwidgets/comboboxes.py
new file mode 100644
index 0000000..e10d7e6
--- /dev/null
+++ b/zenmap/radialnet/bestwidgets/comboboxes.py
@@ -0,0 +1,135 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk, GObject
+
+
+class BWChangeableComboBoxEntry(Gtk.ComboBoxText):
+ """
+ """
+ def __init__(self):
+ """
+ """
+ self.__liststore = Gtk.ListStore.new([str])
+
+ Gtk.ComboBoxText.__init__(self, model=self.__liststore, has_entry=True)
+
+ self.connect("changed", self.__changed)
+ self.get_child().connect("changed", self.__entry_changed)
+
+ self.__last_active = None
+
+ def __changed(self, widget):
+ """
+ """
+ if self.get_active() != -1:
+ self.__last_active = self.get_active()
+
+ def bw_get_length(self):
+ """
+ """
+ return len(self.__liststore)
+
+ def __entry_changed(self, widget):
+ """
+ """
+ if len(self.__liststore) > 0 and\
+ self.__last_active is not None and\
+ self.get_active() == -1:
+
+ iter = self.get_model().get_iter((self.__last_active,))
+ self.__liststore.set_value(iter, 0, widget.get_text().strip())
+
+ def bw_get_active(self):
+ """
+ """
+ if self.get_active() == -1:
+ return self.__last_active
+
+ return self.get_active()
+
+
+# testing widget
+if __name__ == "__main__":
+
+ def button_clicked(widget, combo):
+ """
+ """
+ combo.append_text('New')
+
+ window = Gtk.Window()
+ window.connect("destroy", lambda w: Gtk.main_quit())
+
+ box = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
+
+ combo = BWChangeableComboBoxEntry()
+ combo.append_text('New')
+ combo.set_active(0)
+
+ button = Gtk.Button.new_with_label('More')
+ button.connect("clicked", button_clicked, combo)
+
+ box.pack_start(button, False, False, 0)
+ box.pack_start(combo, True, True, 0)
+
+ window.add(box)
+ window.show_all()
+
+ Gtk.main()
diff --git a/zenmap/radialnet/bestwidgets/expanders.py b/zenmap/radialnet/bestwidgets/expanders.py
new file mode 100644
index 0000000..a89e08b
--- /dev/null
+++ b/zenmap/radialnet/bestwidgets/expanders.py
@@ -0,0 +1,97 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk
+from radialnet.bestwidgets.labels import BWSectionLabel
+
+
+class BWExpander(Gtk.Expander):
+ """
+ """
+ def __init__(self, label=''):
+ """
+ """
+ Gtk.Expander.__init__(self)
+
+ self.__label = BWSectionLabel(label)
+ self.set_label_widget(self.__label)
+
+ self.__alignment = Gtk.Alignment.new(0, 0, 1, 1)
+ self.__alignment.set_padding(12, 0, 24, 0)
+
+ self.add(self.__alignment)
+
+ def bw_set_label_text(self, text):
+ """
+ """
+ self.__label.bw_set_text(text)
+
+ def bw_add(self, widget):
+ """
+ """
+ if len(self.__alignment.get_children()) > 0:
+ self.__alignment.remove(self.__alignment.get_children()[0])
+
+ self.__alignment.add(widget)
+
+ def bw_no_padding(self):
+ """
+ """
+ self.__alignment.set_padding(0, 0, 0, 0)
diff --git a/zenmap/radialnet/bestwidgets/frames.py b/zenmap/radialnet/bestwidgets/frames.py
new file mode 100644
index 0000000..3a35021
--- /dev/null
+++ b/zenmap/radialnet/bestwidgets/frames.py
@@ -0,0 +1,96 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk, Gdk
+
+
+class BWFrame(Gtk.Frame):
+ """
+ """
+ def __init__(self, label=''):
+ """
+ """
+ Gtk.Frame.__init__(self)
+
+ self.set_border_width(3)
+ self.set_shadow_type(Gtk.ShadowType.NONE)
+
+ self.__alignment = Gtk.Alignment.new(0, 0, 1, 1)
+ self.__alignment.set_padding(12, 0, 24, 0)
+
+ self.add(self.__alignment)
+
+ self.bw_set_label(label)
+
+ def bw_set_label(self, label):
+ """
+ """
+ self.set_label("<b>" + label + "</b>")
+ self.get_label_widget().set_use_markup(True)
+
+ def bw_add(self, widget):
+ """
+ """
+ self.__alignment.add(widget)
+
+ def bw_remove(self, widget):
+ """
+ """
+ self.__alignment.remove(widget)
diff --git a/zenmap/radialnet/bestwidgets/labels.py b/zenmap/radialnet/bestwidgets/labels.py
new file mode 100644
index 0000000..c3ec94f
--- /dev/null
+++ b/zenmap/radialnet/bestwidgets/labels.py
@@ -0,0 +1,94 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk
+
+
+class BWLabel(Gtk.Label):
+ """
+ """
+ def __init__(self, text=''):
+ """
+ """
+ Gtk.Label.__init__(self)
+
+ self.set_text(text)
+ self.set_justify(Gtk.Justification.LEFT)
+ self.set_alignment(0, 0.50)
+ self.set_line_wrap(True)
+
+
+class BWSectionLabel(Gtk.Label):
+ """
+ """
+ def __init__(self, text=''):
+ """
+ """
+ Gtk.Label.__init__(self)
+
+ self.set_markup('<b>' + text + '</b>')
+ self.set_justify(Gtk.Justification.LEFT)
+ self.set_alignment(0, 0.50)
+ self.set_line_wrap(True)
+
+ def bw_set_text(self, text):
+ """
+ """
+ self.set_markup('<b>' + text + '</b>')
diff --git a/zenmap/radialnet/bestwidgets/textview.py b/zenmap/radialnet/bestwidgets/textview.py
new file mode 100644
index 0000000..9deda93
--- /dev/null
+++ b/zenmap/radialnet/bestwidgets/textview.py
@@ -0,0 +1,209 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk
+from radialnet.bestwidgets.boxes import *
+
+
+class BWTextView(BWScrolledWindow):
+ """
+ """
+ def __init__(self):
+ """
+ """
+ BWScrolledWindow.__init__(self)
+
+ self.__auto_scroll = False
+
+ self.__create_widgets()
+
+ def __create_widgets(self):
+ """
+ """
+ self.__textbuffer = Gtk.TextBuffer()
+ self.__textview = Gtk.TextView.new_with_buffer(self.__textbuffer)
+
+ self.add_with_viewport(self.__textview)
+
+ def bw_set_auto_scroll(self, value):
+ """
+ """
+ self.__auto_scroll = value
+
+ def bw_set_editable(self, editable):
+ """
+ """
+ self.__textview.set_editable(False)
+
+ def bw_modify_font(self, font):
+ """
+ """
+ self.__textview.modify_font(font)
+
+ def bw_set_text(self, text):
+ """
+ """
+ self.__textbuffer.set_text(text)
+
+ if self.__auto_scroll:
+ self.bw_set_scroll_down()
+
+ def bw_get_text(self):
+ """
+ """
+ return self.__textbuffer.get_text(self.__textbuffer.get_start_iter(),
+ self.__textbuffer.get_end_iter())
+
+ def bw_set_scroll_down(self):
+ """
+ """
+ self.get_vadjustment().set_value(self.get_vadjustment().upper)
+
+ def bw_get_textbuffer(self):
+ """
+ """
+ return self.__textbuffer
+
+
+class BWTextEditor(BWScrolledWindow):
+ """
+ """
+ def __init__(self):
+ """
+ """
+ BWScrolledWindow.__init__(self)
+ self.connect('draw', self.__draw)
+
+ self.__auto_scroll = False
+
+ self.__create_widgets()
+
+ def __create_widgets(self):
+ """
+ """
+ self.__hbox = BWHBox(spacing=6)
+
+ self.__textbuffer = Gtk.TextBuffer()
+ self.__textview = Gtk.TextView.new_with_buffer(self.__textbuffer)
+
+ self.__linebuffer = Gtk.TextBuffer()
+ self.__lineview = Gtk.TextView.new_with_buffer(self.__linebuffer)
+ self.__lineview.set_justification(Gtk.Justification.RIGHT)
+ self.__lineview.set_editable(False)
+ self.__lineview.set_sensitive(False)
+
+ self.__hbox.bw_pack_start_noexpand_nofill(self.__lineview)
+ self.__hbox.bw_pack_start_expand_fill(self.__textview)
+
+ self.add_with_viewport(self.__hbox)
+
+ def __draw(self, widget, event):
+ """
+ """
+ # code to fix a gtk issue that don't show text correctly
+ self.__hbox.check_resize()
+
+ def bw_set_auto_scroll(self, value):
+ """
+ """
+ self.__auto_scroll = value
+
+ def bw_set_editable(self, editable):
+ """
+ """
+ self.__textview.set_editable(False)
+
+ def bw_modify_font(self, font):
+ """
+ """
+ self.__textview.modify_font(font)
+ self.__lineview.modify_font(font)
+
+ def bw_set_text(self, text):
+ """
+ """
+ if text != "":
+
+ count = text.count('\n') + text.count('\r')
+
+ lines = range(1, count + 2)
+ lines = [str(i).strip() for i in lines]
+
+ self.__textbuffer.set_text(text)
+ self.__linebuffer.set_text('\n'.join(lines))
+
+ if self.__auto_scroll:
+ self.bw_set_scroll_down()
+
+ else:
+
+ self.__textbuffer.set_text("")
+ self.__linebuffer.set_text("")
+
+ def bw_get_text(self):
+ """
+ """
+ return self.__textbuffer.get_text(self.__textbuffer.get_start_iter(),
+ self.__textbuffer.get_end_iter())
+
+ def bw_set_scroll_down(self):
+ """
+ """
+ self.get_vadjustment().set_value(self.get_vadjustment().upper)
diff --git a/zenmap/radialnet/bestwidgets/windows.py b/zenmap/radialnet/bestwidgets/windows.py
new file mode 100644
index 0000000..fe61162
--- /dev/null
+++ b/zenmap/radialnet/bestwidgets/windows.py
@@ -0,0 +1,103 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk
+
+PRIMARY_TEXT_MARKUP = '<span weight="bold" size="larger">%s</span>'
+
+
+class BWAlertDialog(Gtk.MessageDialog):
+ """
+ """
+ def __init__(self, parent=None, flags=0, type=Gtk.MessageType.INFO,
+ buttons=Gtk.ButtonsType.OK,
+ primary_text=None,
+ secondary_text=None):
+
+ Gtk.MessageDialog.__init__(self, parent=parent, flags=flags,
+ message_type=type, buttons=buttons)
+
+ self.connect('response', self.__destroy)
+
+ self.set_resizable(False)
+
+ self.set_title(_("Alert"))
+ self.set_markup(PRIMARY_TEXT_MARKUP % primary_text)
+
+ if secondary_text:
+ self.format_secondary_text(secondary_text)
+
+ def __destroy(self, dialog, id):
+ """
+ """
+ self.destroy()
+
+
+class BWWindow(Gtk.Window):
+ """
+ """
+ def __init__(self, type=Gtk.WindowType.TOPLEVEL):
+ """
+ """
+ Gtk.Window.__init__(self, type=type)
+ self.set_border_width(5)
+
+
+BWMainWindow = Gtk.Window
diff --git a/zenmap/radialnet/core/ArgvHandle.py b/zenmap/radialnet/core/ArgvHandle.py
new file mode 100644
index 0000000..2e72d93
--- /dev/null
+++ b/zenmap/radialnet/core/ArgvHandle.py
@@ -0,0 +1,98 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import sys
+
+
+class ArgvHandle:
+ """
+ """
+ def __init__(self, argv):
+ """
+ """
+ self.__argv = argv
+
+ def get_option(self, option):
+ """
+ """
+ if option in self.__argv:
+
+ index = self.__argv.index(option)
+
+ if index + 1 < len(self.__argv):
+ return self.__argv[index + 1]
+
+ return None
+
+ def has_option(self, option):
+ """
+ """
+ return option in self.__argv
+
+ def get_last_value(self):
+ """
+ """
+ return self.__argv[-1]
+
+
+if __name__ == '__main__':
+
+ import sys
+
+ h = ArgvHandle(sys.argv)
+
+ print(h.get_last_value())
diff --git a/zenmap/radialnet/core/Coordinate.py b/zenmap/radialnet/core/Coordinate.py
new file mode 100644
index 0000000..5c82212
--- /dev/null
+++ b/zenmap/radialnet/core/Coordinate.py
@@ -0,0 +1,208 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import math
+
+
+class PolarCoordinate:
+ """
+ Class to implement a polar coordinate object
+ """
+
+ def __init__(self, r=0, t=0):
+ """
+ Constructor method of PolarCoordinate class
+ @type r: number
+ @param r: The radius of coordinate
+ @type t: number
+ @param t: The angle (theta) of coordinate in radians
+ """
+
+ self.__r = r
+ """Radius of polar coordinate"""
+ self.__t = t
+ """Angle (theta) of polar coordinate in radians"""
+
+ def get_theta(self):
+ """
+ """
+ return math.degrees(self.__t)
+
+ def get_radius(self):
+ """
+ """
+ return self.__r
+
+ def set_theta(self, t):
+ """
+ """
+ self.__t = math.radians(t)
+
+ def set_radius(self, r):
+ """
+ """
+ self.__r = r
+
+ def get_coordinate(self):
+ """
+ Set polar coordinate
+ @rtype: tuple
+ @return: Polar coordinates (r, t)
+ """
+ return (self.__r, math.degrees(self.__t))
+
+ def set_coordinate(self, r, t):
+ """
+ Set polar coordinate
+ @type r: number
+ @param r: The radius of coordinate
+ @type t: number
+ @param t: The angle (theta) of coordinate
+ """
+ self.__r = r
+ self.__t = math.radians(t)
+
+ def to_cartesian(self):
+ """
+ Convert polar in cartesian coordinate
+ @rtype: tuple
+ @return: cartesian coordinates (x, y)
+ """
+ x = self.__r * math.cos(self.__t)
+ y = self.__r * math.sin(self.__t)
+
+ return (x, y)
+
+
+class CartesianCoordinate:
+ """
+ Class to implement a cartesian coordinate object
+ """
+ def __init__(self, x=0, y=0):
+ """
+ Constructor method of CartesianCoordinate class
+ @type x: number
+ @param x: The x component of coordinate
+ @type y: number
+ @param y: The y component of coordinate
+ """
+ self.__x = x
+ """X component of cartesian coordinate"""
+ self.__y = y
+ """Y component of cartesian coordinate"""
+
+ def get_coordinate(self):
+ """
+ Get cartesian coordinate
+ @rtype: tuple
+ @return: Cartesian coordinates (x, y)
+ """
+ return (self.__x, self.__y)
+
+ def set_coordinate(self, x, y):
+ """
+ Set cartesian coordinate
+ @type x: number
+ @param x: The x component of coordinate
+ @type y: number
+ @param y: The y component of coordinate
+ """
+ self.__x = x
+ self.__y = y
+
+ def to_polar(self):
+ """
+ Convert cartesian in polar coordinate
+ @rtype: tuple
+ @return: polar coordinates (r, t)
+ """
+ r = math.sqrt(self.__x ** 2 + self.__y ** 2)
+
+ if self.__x > 0:
+
+ if self.__y >= 0:
+ t = math.atan(self.__y / self.__x)
+
+ else:
+ t = math.atan(self.__y / self.__x) + 2 * math.pi
+
+ elif self.__x < 0:
+ t = math.atan(self.__y / self.__x) + math.pi
+
+ elif self.__x == 0:
+
+ if self.__y == 0:
+ t = 0
+
+ if self.__y > 0:
+ t = math.pi / 2
+
+ else:
+ t = -math.pi / 2
+
+ return (r, t)
+
+
+if __name__ == "__main__":
+
+ # Testing application
+
+ polar = PolarCoordinate(1, math.pi)
+ cartesian = CartesianCoordinate(-1, 0)
+
+ print(polar.to_cartesian())
+ print(cartesian.to_polar())
diff --git a/zenmap/radialnet/core/Graph.py b/zenmap/radialnet/core/Graph.py
new file mode 100644
index 0000000..1e0e8b5
--- /dev/null
+++ b/zenmap/radialnet/core/Graph.py
@@ -0,0 +1,247 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+
+class Node(object):
+ """
+ Node class
+ """
+ def __init__(self):
+ """
+ Constructor method of Node class
+ @type : integer
+ @param : Node identifier
+ """
+ self.__data = None
+ """User-controlled data pointer"""
+ self.__edges = []
+ """List of edges to other nodes"""
+
+ def get_data(self):
+ return self.__data
+
+ def set_data(self, data):
+ self.__data = data
+
+ def get_edge(self, dest):
+ """
+ Return the edge connecting to dest, or None if none
+ """
+ for edge in self.__edges:
+ if dest in edge.get_nodes():
+ return edge
+ return None
+
+ def get_edges(self):
+ """
+ Return the list of edges
+ """
+ return self.__edges
+
+ def add_edge(self, edge):
+ self.__edges.append(edge)
+
+
+class Edge:
+ """
+ """
+ def __init__(self, nodes):
+ """
+ """
+ self.__weights = []
+ self.__nodes = nodes
+ self.__weights_mean = None
+
+ def get_nodes(self):
+ """
+ """
+ return self.__nodes
+
+ def get_weights(self):
+ """
+ """
+ return self.__weights
+
+ def set_weights(self, weights):
+ """
+ """
+ self.__weights = weights
+ self.__weights_mean = sum(self.__weights) / len(self.__weights)
+
+ def add_weight(self, weight):
+ """
+ """
+ self.__weights.append(weight)
+ self.__weights_mean = sum(self.__weights) / len(self.__weights)
+
+ def get_weights_mean(self):
+ """
+ """
+ return self.__weights_mean
+
+
+class Graph:
+ """
+ Network Graph class
+ """
+
+ def __init__(self):
+ """
+ Constructor method of Graph class
+ @type : list
+ @param : List of nodes
+ """
+ self.__main_node = None
+ self.__nodes = []
+ self.__max_edge_mean_value = None
+ self.__min_edge_mean_value = None
+
+ def set_nodes(self, nodes):
+ """
+ """
+ self.__nodes = nodes
+
+ def get_nodes(self):
+ """
+ """
+ return self.__nodes
+
+ def get_number_of_nodes(self):
+ """
+ Get the number of nodes in graph
+ @rtype: number
+ @return: The number of nodes in the graph
+ """
+ return len(self.__nodes)
+
+ def set_main_node(self, node):
+ """
+ Set the main node
+ @type : number
+ @param : The node
+ """
+ self.__main_node = node
+
+ def get_main_node(self):
+ """
+ Get the main node
+ @rtype: Node
+ @return: The main node
+ """
+ return self.__main_node
+
+ def set_connection(self, a, b, weight=None):
+ """
+ Set node connections
+ @type : list
+ @param : List of connections
+ """
+
+ # if is a new connection make it
+ edge = a.get_edge(b)
+ if edge is None:
+ edge = Edge((a, b))
+ a.add_edge(edge)
+ b.add_edge(edge)
+
+ # then add new weight value
+ if weight is not None:
+
+ edge.add_weight(weight)
+
+ mean_weight = edge.get_weights_mean()
+ if (self.__min_edge_mean_value is None or
+ mean_weight < self.__min_edge_mean_value):
+ self.__min_edge_mean_value = mean_weight
+ if (self.__max_edge_mean_value is None or
+ mean_weight > self.__max_edge_mean_value):
+ self.__max_edge_mean_value = mean_weight
+
+ def get_edges(self):
+ """
+ An iterator that yields all edges
+ """
+ for node in self.__nodes:
+ for edge in node.get_edges():
+ if edge.get_nodes()[0] == node:
+ yield edge
+
+ def get_node_connections(self, node):
+ """
+ """
+ connections = []
+
+ for edge in node.get_edges():
+
+ (a, b) = edge.get_nodes()
+
+ if a == node:
+ connections.append(b)
+ if b == node:
+ connections.append(a)
+
+ return connections
+
+ def get_max_edge_mean_weight(self):
+ """
+ """
+ return self.__max_edge_mean_value
+
+ def get_min_edge_mean_weight(self):
+ """
+ """
+ return self.__min_edge_mean_value
diff --git a/zenmap/radialnet/core/Info.py b/zenmap/radialnet/core/Info.py
new file mode 100644
index 0000000..d04b31a
--- /dev/null
+++ b/zenmap/radialnet/core/Info.py
@@ -0,0 +1,63 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+
+INFO = {'name': 'RadialNet',
+ 'version': '0.44',
+ 'website': 'http://www.dca.ufrn.br/~joaomedeiros/radialnet/',
+ 'authors': ['João Paulo de Souza Medeiros'],
+ 'copyright': 'Copyright (C) 2007, 2008 Insecure.Com LLC'}
diff --git a/zenmap/radialnet/core/Interpolation.py b/zenmap/radialnet/core/Interpolation.py
new file mode 100644
index 0000000..1d89498
--- /dev/null
+++ b/zenmap/radialnet/core/Interpolation.py
@@ -0,0 +1,157 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+
+class Linear2DInterpolator:
+ """
+ Implements a bidimensional linear interpolator.
+ """
+
+ def __init__(self):
+ """
+ Constructor method of Linear2DInterpolator class
+ """
+ self.__start_point = (0, 0)
+ """Initial point of interpolation"""
+ self.__final_point = (0, 0)
+ """Final point of interpolation"""
+ self.__interpolated_points = []
+ """Interpolated points vector"""
+
+ def set_start_point(self, a, b):
+ """
+ Set initial coordinate
+ Set final coordinate
+ @type a: number
+ @param a: The first component of final point
+ @type b: number
+ @param b: The second component of final point
+ """
+ self.__start_point = (a, b)
+
+ def set_final_point(self, a, b):
+ """
+ Set final coordinate
+ @type a: number
+ @param a: The first component of final point
+ @type b: number
+ @param b: The second component of final point
+ """
+ self.__final_point = (a, b)
+
+ def get_weighed_points(self, number_of_pass, pass_vector):
+ """
+ Return the vector of coordinates between the initial and final
+ coordinates with the specified size
+ @type number_of_pass: number
+ @param number_of_pass: The number of pass of interpolation
+ @rtype: list
+ @return: A list of tuples with interpolated points
+ """
+ (ai, bi) = self.__start_point
+ (af, bf) = self.__final_point
+
+ a_conversion_factor = float(af - ai) / sum(pass_vector)
+ b_conversion_factor = float(bf - bi) / sum(pass_vector)
+
+ a_pass = 0
+ b_pass = 0
+
+ self.__interpolated_points = list(range(number_of_pass))
+
+ for i in range(0, number_of_pass):
+
+ a_pass += pass_vector[i] * a_conversion_factor
+ b_pass += pass_vector[i] * b_conversion_factor
+ self.__interpolated_points[i] = (ai + a_pass, bi + b_pass)
+
+ return self.__interpolated_points
+
+ def get_points(self, number_of_pass):
+ """
+ Return the vector of coordinates between the initial and final
+ coordinates with the specified size
+ @type number_of_pass: number
+ @param number_of_pass: The number of pass of interpolation
+ @rtype: list
+ @return: A list of tuples with interpolated points
+ """
+ (ai, bi) = self.__start_point
+ (af, bf) = self.__final_point
+
+ a_pass = float(af - ai) / number_of_pass
+ b_pass = float(bf - bi) / number_of_pass
+
+ self.__interpolated_points = list(range(number_of_pass))
+
+ for i in range(1, number_of_pass + 1):
+ self.__interpolated_points[i - 1] = (ai + a_pass * i,
+ bi + b_pass * i)
+
+ return self.__interpolated_points
+
+
+if __name__ == "__main__":
+
+ # Testing application
+
+ i = Linear2DInterpolator()
+
+ i.set_start_point(0, 0)
+ i.set_final_point(1, 1)
+
+ print(len(i.get_points(10)), i.get_points(10))
diff --git a/zenmap/radialnet/core/XMLHandler.py b/zenmap/radialnet/core/XMLHandler.py
new file mode 100644
index 0000000..36eb2e1
--- /dev/null
+++ b/zenmap/radialnet/core/XMLHandler.py
@@ -0,0 +1,324 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+
+# Prevent loading PyXML
+import xml
+xml.__path__ = [x for x in xml.__path__ if "_xmlplus" not in x]
+
+import xml.sax
+import xml.sax.saxutils
+from xml.sax.xmlreader import AttributesImpl as Attributes
+
+
+class XMLNode:
+ """
+ """
+ def __init__(self, name):
+ """
+ """
+ self.__name = name
+ self.__text = ""
+ self.__attrs = dict()
+ self.__children = []
+
+ def set_text(self, text):
+ """
+ """
+ self.__text = text
+
+ def get_text(self):
+ """
+ """
+ return self.__text
+
+ def set_name(self, name):
+ """
+ """
+ self.__name = name
+
+ def get_name(self):
+ """
+ """
+ return self.__name
+
+ def add_attr(self, key, value):
+ """
+ """
+ self.__attrs[key] = value
+
+ def add_child(self, child):
+ """
+ """
+ self.__children.append(child)
+
+ def get_keys(self):
+ """
+ """
+ return self.__attrs.keys()
+
+ def get_attr(self, attr):
+ """
+ """
+ return self.__attrs.get(attr)
+
+ def get_attrs(self):
+ """
+ """
+ return self.__attrs
+
+ def get_children(self):
+ """
+ """
+ return self.__children
+
+ def query_children(self, name, attr, value, first=False, deep=False):
+ """
+ """
+ result = []
+
+ for child in self.__children:
+
+ if child.get_name() == name:
+
+ if attr in child.get_attrs():
+
+ c_value = child.get_attr(attr)
+
+ if c_value == value or c_value == str(value):
+ result.append(child)
+
+ if deep:
+
+ c_result = child.query_children(name, attr, value, first, deep)
+
+ if c_result is not None:
+
+ if first:
+ return c_result
+
+ else:
+ result.extend(c_result)
+
+ if first and len(result) > 0:
+ return result[0]
+
+ if first:
+ return None
+
+ return result
+
+ def search_children(self, name, first=False, deep=False):
+ """
+ """
+ result = []
+
+ for child in self.__children:
+
+ if child.get_name() == name:
+
+ result.append(child)
+
+ if first:
+ return result[0]
+
+ if deep:
+
+ c_result = child.search_children(name, first, deep)
+
+ if c_result is not None and c_result != []:
+
+ if first:
+ return c_result
+
+ else:
+ result.extend(c_result)
+
+ if first:
+ return None
+
+ return result
+
+
+class XMLWriter(xml.sax.saxutils.XMLGenerator):
+ """
+ """
+ def __init__(self, file, root=None, encoding="utf-8"):
+ """
+ """
+ xml.sax.saxutils.XMLGenerator.__init__(self, file, encoding)
+
+ self.__root = root
+
+ def set_root(self, root):
+ """
+ """
+ self.__root = root
+
+ def write(self):
+ """
+ """
+ self.startDocument()
+ self.write_xml_node([self.__root])
+ self.endDocument()
+
+ def write_xml_node(self, root):
+ """
+ """
+ for child in root:
+
+ self.startElement(child.get_name(), Attributes(child.get_attrs()))
+
+ if child.get_text() != "":
+ self.characters(child.get_text())
+
+ self.write_xml_node(child.get_children())
+
+ self.endElement(child.get_name())
+
+
+class XMLReader(xml.sax.ContentHandler):
+ """
+ """
+ def __init__(self, file=None):
+ """
+ """
+ xml.sax.ContentHandler.__init__(self)
+ self.__text = ""
+ self.__status = []
+
+ self.__file = file
+ self.__root = None
+
+ self.__parser = xml.sax.make_parser()
+ self.__parser.setContentHandler(self)
+
+ def set_file(self, file, root):
+ """
+ """
+ self.__file = file
+
+ def get_file(self):
+ """
+ """
+ return self.__file
+
+ def get_root(self):
+ """
+ """
+ return self.__root
+
+ def parse(self):
+ """
+ """
+ if self.__file is not None:
+ self.__parser.parse(self.__file)
+
+ def startDocument(self):
+ """
+ """
+ pass
+
+ def startElement(self, name, attrs):
+ """
+ """
+ # create new node
+ node = XMLNode(name)
+
+ # putting attributes and values in node
+ for attr in attrs.getNames():
+ node.add_attr(attr, attrs.get(attr).strip())
+
+ # who is my father?
+ if len(self.__status) > 0:
+ self.__status[-1].add_child(node)
+
+ if self.__root is None:
+ self.__root = node
+
+ self.__status.append(node)
+
+ def endElement(self, name):
+ """
+ """
+ self.__status[-1].set_text(self.__text.strip())
+
+ self.__text = ""
+ self.__status.pop()
+
+ def endDocument(self):
+ """
+ """
+ pass
+
+ def characters(self, text):
+ """
+ """
+ self.__text += text
+
+
+if __name__ == "__main__":
+
+ import sys
+
+ reader = XMLReader(sys.argv[1])
+ reader.parse()
+
+ root = reader.get_root()
+
+ writer = XMLWriter(open("test.xml", 'w'), root)
+ writer.write()
diff --git a/zenmap/radialnet/core/__init__.py b/zenmap/radialnet/core/__init__.py
new file mode 100644
index 0000000..c314dd7
--- /dev/null
+++ b/zenmap/radialnet/core/__init__.py
@@ -0,0 +1,56 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
diff --git a/zenmap/radialnet/gui/Application.py b/zenmap/radialnet/gui/Application.py
new file mode 100644
index 0000000..dbc6fe2
--- /dev/null
+++ b/zenmap/radialnet/gui/Application.py
@@ -0,0 +1,159 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk
+
+from radialnet.util.integration import make_graph_from_nmap_parser
+from radialnet.core.Info import INFO
+from radialnet.core.XMLHandler import XMLReader
+from radialnet.gui.ControlWidget import ControlWidget, ControlFisheye
+from radialnet.gui.Toolbar import Toolbar
+from radialnet.gui.Image import Pixmaps
+import radialnet.gui.RadialNet as RadialNet
+from radialnet.bestwidgets.windows import BWMainWindow, BWAlertDialog
+from radialnet.bestwidgets.boxes import BWHBox, BWVBox, BWStatusbar
+
+
+DIMENSION = (640, 480)
+
+
+class Application(BWMainWindow):
+ """
+ """
+ def __init__(self):
+ """
+ """
+ BWMainWindow.__init__(self)
+ self.set_default_size(DIMENSION[0], DIMENSION[1])
+
+ self.set_icon(Pixmaps().get_pixbuf('logo'))
+
+ self.__create_widgets()
+
+ def __create_widgets(self):
+ """
+ """
+ self.__hbox = BWHBox(spacing=0)
+ self.__vbox = BWVBox(spacing=0)
+
+ self.__radialnet = RadialNet.RadialNet(RadialNet.LAYOUT_WEIGHTED)
+ self.__control = ControlWidget(self.__radialnet)
+ self.__fisheye = ControlFisheye(self.__radialnet)
+ self.__toolbar = Toolbar(self.__radialnet,
+ self,
+ self.__control,
+ self.__fisheye)
+ self.__statusbar = BWStatusbar()
+
+ self.__hbox.bw_pack_start_expand_fill(self.__radialnet)
+ self.__hbox.bw_pack_start_noexpand_nofill(self.__control)
+
+ self.__vbox.bw_pack_start_noexpand_nofill(self.__toolbar)
+ self.__vbox.bw_pack_start_expand_fill(self.__hbox)
+ self.__vbox.bw_pack_start_noexpand_nofill(self.__fisheye)
+ self.__vbox.bw_pack_start_noexpand_nofill(self.__statusbar)
+
+ self.add(self.__vbox)
+ self.set_title(" ".join([INFO['name'], INFO['version']]))
+ self.set_position(Gtk.WindowPosition.CENTER)
+ self.show_all()
+ self.connect('destroy', Gtk.main_quit)
+
+ self.__radialnet.set_no_show_all(True)
+ self.__control.set_no_show_all(True)
+ self.__fisheye.set_no_show_all(True)
+
+ self.__radialnet.hide()
+ self.__control.hide()
+ self.__fisheye.hide()
+ self.__toolbar.disable_controls()
+
+ def parse_nmap_xml_file(self, file):
+ """
+ """
+ try:
+
+ self.__parser = XMLReader(file)
+ self.__parser.parse()
+
+ except Exception as e:
+
+ text = 'It is not possible open file %s: %s' % (file, e)
+
+ alert = BWAlertDialog(self,
+ primary_text='Error opening file.',
+ secondary_text=text)
+
+ alert.show_all()
+
+ return False
+
+ self.__radialnet.set_empty()
+ self.__radialnet.set_graph(make_graph_from_nmap_parser(self.__parser))
+ self.__radialnet.show()
+
+ self.__toolbar.enable_controls()
+
+ return True
+
+ def start(self):
+ """
+ """
+ Gtk.main()
diff --git a/zenmap/radialnet/gui/ControlWidget.py b/zenmap/radialnet/gui/ControlWidget.py
new file mode 100644
index 0000000..99be773
--- /dev/null
+++ b/zenmap/radialnet/gui/ControlWidget.py
@@ -0,0 +1,1270 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk, GLib, Gdk
+
+import math
+
+import radialnet.util.geometry as geometry
+
+from radialnet.bestwidgets.boxes import *
+from radialnet.core.Coordinate import PolarCoordinate
+import radialnet.gui.RadialNet as RadialNet
+from radialnet.bestwidgets.expanders import BWExpander
+
+
+OPTIONS = ['address',
+ 'hostname',
+ 'icon',
+ 'latency',
+ 'ring',
+ 'region',
+ 'slow in/out']
+
+REFRESH_RATE = 500
+
+
+class ControlWidget(BWVBox):
+ """
+ """
+ def __init__(self, radialnet):
+ """
+ """
+ BWVBox.__init__(self)
+ self.set_border_width(6)
+
+ self.radialnet = radialnet
+
+ self.__create_widgets()
+
+ def __create_widgets(self):
+ """
+ """
+ self.__action = ControlAction(self.radialnet)
+ self.__interpolation = ControlInterpolation(self.radialnet)
+ self.__layout = ControlLayout(self.radialnet)
+ self.__view = ControlView(self.radialnet)
+
+ self.bw_pack_start_noexpand_nofill(self.__action)
+ self.bw_pack_start_noexpand_nofill(self.__interpolation)
+ self.bw_pack_start_noexpand_nofill(self.__layout)
+ self.bw_pack_start_noexpand_nofill(self.__view)
+
+
+class ControlAction(BWExpander):
+ """
+ """
+ def __init__(self, radialnet):
+ """
+ """
+ BWExpander.__init__(self, _('Action'))
+ self.set_expanded(True)
+
+ self.radialnet = radialnet
+
+ self.__create_widgets()
+
+ def __create_widgets(self):
+ """
+ """
+ self.__tbox = BWTable(1, 4)
+ self.__tbox.bw_set_spacing(0)
+ self.__vbox = BWVBox()
+
+ self.__jump_to = Gtk.RadioToolButton(group=None,
+ stock_id=Gtk.STOCK_JUMP_TO)
+ self.__jump_to.set_tooltip_text('Change focus')
+ self.__jump_to.connect('toggled',
+ self.__change_pointer,
+ RadialNet.POINTER_JUMP_TO)
+
+ self.__info = Gtk.RadioToolButton(group=self.__jump_to,
+ stock_id=Gtk.STOCK_INFO)
+ self.__info.set_tooltip_text('Show information')
+ self.__info.connect('toggled',
+ self.__change_pointer,
+ RadialNet.POINTER_INFO)
+
+ self.__group = Gtk.RadioToolButton(group=self.__jump_to,
+ stock_id=Gtk.STOCK_ADD)
+ self.__group.set_tooltip_text('Group children')
+ self.__group.connect('toggled',
+ self.__change_pointer,
+ RadialNet.POINTER_GROUP)
+
+ self.__region = Gtk.RadioToolButton(group=self.__jump_to,
+ stock_id=Gtk.STOCK_SELECT_COLOR)
+ self.__region.set_tooltip_text('Fill region')
+ self.__region.connect('toggled',
+ self.__change_pointer,
+ RadialNet.POINTER_FILL)
+
+ self.__region_color = Gtk.ComboBoxText.new()
+ self.__region_color.append_text(_('Red'))
+ self.__region_color.append_text(_('Yellow'))
+ self.__region_color.append_text(_('Green'))
+ self.__region_color.connect('changed', self.__change_region)
+ self.__region_color.set_active(self.radialnet.get_region_color())
+
+ self.__tbox.bw_attach_next(self.__jump_to)
+ self.__tbox.bw_attach_next(self.__info)
+ self.__tbox.bw_attach_next(self.__group)
+ self.__tbox.bw_attach_next(self.__region)
+
+ self.__vbox.bw_pack_start_noexpand_nofill(self.__tbox)
+ self.__vbox.bw_pack_start_noexpand_nofill(self.__region_color)
+
+ self.bw_add(self.__vbox)
+
+ self.__jump_to.set_active(True)
+ self.__region_color.set_no_show_all(True)
+ self.__region_color.hide()
+
+ def __change_pointer(self, widget, pointer):
+ """
+ """
+ if pointer != self.radialnet.get_pointer_status():
+ self.radialnet.set_pointer_status(pointer)
+
+ if pointer == RadialNet.POINTER_FILL:
+ self.__region_color.show()
+ else:
+ self.__region_color.hide()
+
+ def __change_region(self, widget):
+ """
+ """
+ self.radialnet.set_region_color(self.__region_color.get_active())
+
+
+class ControlVariableWidget(Gtk.DrawingArea):
+ """
+ """
+ def __init__(self, name, value, update, increment=1):
+ """
+ """
+ Gtk.DrawingArea.__init__(self)
+
+ self.__variable_name = name
+ self.__value = value
+ self.__update = update
+ self.__increment_pass = increment
+
+ self.__radius = 6
+ self.__increment_time = 100
+
+ self.__pointer_position = 0
+ self.__active_increment = False
+
+ self.__last_value = self.__value()
+
+ self.connect('draw', self.draw)
+ self.connect('button_press_event', self.button_press)
+ self.connect('button_release_event', self.button_release)
+ self.connect('motion_notify_event', self.motion_notify)
+
+ self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK |
+ Gdk.EventMask.BUTTON_RELEASE_MASK |
+ Gdk.EventMask.POINTER_MOTION_HINT_MASK |
+ Gdk.EventMask.POINTER_MOTION_MASK)
+
+ GLib.timeout_add(REFRESH_RATE, self.verify_value)
+
+ def verify_value(self):
+ """
+ """
+ if self.__value() != self.__last_value:
+ self.__last_value = self.__value()
+
+ self.queue_draw()
+
+ return True
+
+ def button_press(self, widget, event):
+ """
+ """
+ self.__active_increment = False
+ pointer = self.get_pointer()
+
+ if self.__button_is_clicked(pointer) and event.button == 1:
+
+ event.window.set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))
+ self.__active_increment = True
+ self.__increment_value()
+
+ def button_release(self, widget, event):
+ """
+ """
+ event.window.set_cursor(Gdk.Cursor(Gdk.CursorType.LEFT_PTR))
+
+ self.__active_increment = False
+ self.__pointer_position = 0
+
+ self.queue_draw()
+
+ def motion_notify(self, widget, event):
+ """
+ Drawing callback
+ @type widget: GtkWidget
+ @param widget: Gtk widget superclass
+ @type event: GtkEvent
+ @param event: Gtk event of widget
+ @rtype: boolean
+ @return: Indicator of the event propagation
+ """
+ if self.__active_increment:
+
+ xc, yc = self.__center_of_widget
+ x, _ = self.get_pointer()
+
+ if x - self.__radius > 0 and x + self.__radius < 2 * xc:
+ self.__pointer_position = x - xc
+
+ self.queue_draw()
+
+ def draw(self, widget, context):
+ """
+ Drawing callback
+ @type widget: GtkWidget
+ @param widget: Gtk widget superclass
+ @type context: cairo.Context
+ @param context: cairo context class
+ @rtype: boolean
+ @return: Indicator of the event propagation
+ """
+ self.set_size_request(100, 30)
+
+ self.__draw(context)
+
+ return True
+
+ def __draw(self, context):
+ """
+ """
+ allocation = self.get_allocation()
+
+ self.__center_of_widget = (allocation.width // 2,
+ allocation.height // 2)
+
+ xc, yc = self.__center_of_widget
+
+ # draw line
+ context.set_line_width(1)
+ context.set_dash([1, 2])
+ context.move_to(self.__radius,
+ yc + self.__radius)
+ context.line_to(2 * xc - 5,
+ yc + self.__radius)
+ context.stroke()
+
+ # draw text
+ context.set_dash([1, 0])
+ context.set_font_size(10)
+
+ context.move_to(5, yc - self.__radius)
+ context.show_text(self.__variable_name)
+
+ width = context.text_extents(str(self.__value()))[2]
+ context.move_to(2 * xc - width - 5, yc - self.__radius)
+ context.show_text(str(self.__value()))
+
+ context.set_line_width(1)
+ context.stroke()
+
+ # draw node
+ context.arc(xc + self.__pointer_position,
+ yc + self.__radius,
+ self.__radius, 0, 2 * math.pi)
+ if self.__active_increment:
+ context.set_source_rgb(0.0, 0.0, 0.0)
+ else:
+ context.set_source_rgb(1.0, 1.0, 1.0)
+ context.fill_preserve()
+ context.set_source_rgb(0.0, 0.0, 0.0)
+ context.stroke()
+
+ def __button_is_clicked(self, pointer):
+ """
+ """
+ xc, yc = self.__center_of_widget
+ center = (xc, yc + self.__radius)
+
+ return geometry.is_in_circle(pointer, 6, center)
+
+ def __increment_value(self):
+ """
+ """
+ self.__update(self.__value() + self.__pointer_position / 4)
+
+ self.queue_draw()
+
+ if self.__active_increment:
+
+ GLib.timeout_add(self.__increment_time,
+ self.__increment_value)
+
+ def set_value_function(self, value):
+ """
+ """
+ self.__value = value
+
+ def set_update_function(self, update):
+ """
+ """
+ self.__update = update
+
+
+class ControlVariable(BWHBox):
+ """
+ """
+ def __init__(self, name, get_function, set_function, increment=1):
+ """
+ """
+ BWHBox.__init__(self, spacing=0)
+
+ self.__increment_pass = increment
+ self.__increment_time = 200
+ self.__increment = False
+
+ self.__name = name
+ self.__get_function = get_function
+ self.__set_function = set_function
+
+ self.__create_widgets()
+
+ def __create_widgets(self):
+ """
+ """
+ self.__control = ControlVariableWidget(self.__name,
+ self.__get_function,
+ self.__set_function,
+ self.__increment_pass)
+
+ self.__left_button = Gtk.Button()
+ self.__left_button.set_size_request(20, 20)
+ self.__left_arrow = Gtk.Image.new_from_icon_name("pan-start-symbolic",
+ Gtk.IconSize.BUTTON);
+ self.__left_button.add(self.__left_arrow)
+ self.__left_button.connect('pressed',
+ self.__pressed,
+ -self.__increment_pass)
+ self.__left_button.connect('released', self.__released)
+
+ self.__right_button = Gtk.Button()
+ self.__right_button.set_size_request(20, 20)
+ self.__right_arrow = Gtk.Image.new_from_icon_name("pan-end-symbolic",
+ Gtk.IconSize.BUTTON);
+ self.__right_button.add(self.__right_arrow)
+ self.__right_button.connect('pressed',
+ self.__pressed,
+ self.__increment_pass)
+ self.__right_button.connect('released', self.__released)
+
+ self.bw_pack_start_noexpand_nofill(self.__left_button)
+ self.bw_pack_start_expand_fill(self.__control)
+ self.bw_pack_start_noexpand_nofill(self.__right_button)
+
+ def __pressed(self, widget, increment):
+ """
+ """
+ self.__increment = True
+ self.__increment_function(increment)
+
+ def __increment_function(self, increment):
+ """
+ """
+ if self.__increment:
+
+ self.__set_function(self.__get_function() + increment)
+ self.__control.verify_value()
+
+ GLib.timeout_add(self.__increment_time,
+ self.__increment_function,
+ increment)
+
+ def __released(self, widget):
+ """
+ """
+ self.__increment = False
+
+
+class ControlFisheye(BWVBox):
+ """
+ """
+ def __init__(self, radialnet):
+ """
+ """
+ BWVBox.__init__(self)
+ self.set_border_width(6)
+
+ self.radialnet = radialnet
+ self.__ring_max_value = self.radialnet.get_number_of_rings()
+
+ self.__create_widgets()
+
+ def __create_widgets(self):
+ """
+ """
+ self.__params = BWHBox()
+
+ self.__fisheye_label = Gtk.Label.new(_('<b>Fisheye</b> on ring'))
+ self.__fisheye_label.set_use_markup(True)
+
+ self.__ring = Gtk.Adjustment.new(0, 0, self.__ring_max_value, 0.01, 0.01, 0)
+
+ self.__ring_spin = Gtk.SpinButton(adjustment=self.__ring)
+ self.__ring_spin.set_digits(2)
+
+ self.__ring_scale = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL, adjustment=self.__ring)
+ self.__ring_scale.set_size_request(100, -1)
+ self.__ring_scale.set_digits(2)
+ self.__ring_scale.set_value_pos(Gtk.PositionType.LEFT)
+ self.__ring_scale.set_draw_value(False)
+
+ self.__interest_label = Gtk.Label.new(_('with interest factor'))
+ self.__interest = Gtk.Adjustment.new(0, 0, 10, 0.01, 0, 0)
+ self.__interest_spin = Gtk.SpinButton(adjustment=self.__interest)
+ self.__interest_spin.set_digits(2)
+
+ self.__spread_label = Gtk.Label.new(_('and spread factor'))
+ self.__spread = Gtk.Adjustment.new(0, -1.0, 1.0, 0.01, 0.01, 0)
+ self.__spread_spin = Gtk.SpinButton(adjustment=self.__spread)
+ self.__spread_spin.set_digits(2)
+
+ self.__params.bw_pack_start_noexpand_nofill(self.__fisheye_label)
+ self.__params.bw_pack_start_noexpand_nofill(self.__ring_spin)
+ self.__params.bw_pack_start_expand_fill(self.__ring_scale)
+ self.__params.bw_pack_start_noexpand_nofill(self.__interest_label)
+ self.__params.bw_pack_start_noexpand_nofill(self.__interest_spin)
+ self.__params.bw_pack_start_noexpand_nofill(self.__spread_label)
+ self.__params.bw_pack_start_noexpand_nofill(self.__spread_spin)
+
+ self.bw_pack_start_noexpand_nofill(self.__params)
+
+ self.__ring.connect('value_changed', self.__change_ring)
+ self.__interest.connect('value_changed', self.__change_interest)
+ self.__spread.connect('value_changed', self.__change_spread)
+
+ GLib.timeout_add(REFRESH_RATE, self.__update_fisheye)
+
+ def __update_fisheye(self):
+ """
+ """
+ # adjust ring scale to radialnet number of nodes
+ ring_max_value = self.radialnet.get_number_of_rings() - 1
+
+ if ring_max_value != self.__ring_max_value:
+
+ value = self.__ring.get_value()
+
+ if value == 0 and ring_max_value != 0:
+ value = 1
+
+ elif value > ring_max_value:
+ value = ring_max_value
+
+ self.__ring.configure(value, 1, ring_max_value, 0.01, 0.01, 0)
+ self.__ring_max_value = ring_max_value
+
+ self.__ring_scale.queue_draw()
+
+ # check ring value
+ ring_value = self.radialnet.get_fisheye_ring()
+
+ if self.__ring.get_value() != ring_value:
+ self.__ring.set_value(ring_value)
+
+ # check interest value
+ interest_value = self.radialnet.get_fisheye_interest()
+
+ if self.__interest.get_value() != interest_value:
+ self.__interest.set_value(interest_value)
+
+ # check spread value
+ spread_value = self.radialnet.get_fisheye_spread()
+
+ if self.__spread.get_value() != spread_value:
+ self.__spread.set_value(spread_value)
+
+ return True
+
+ def active_fisheye(self):
+ """
+ """
+ self.radialnet.set_fisheye(True)
+ self.__change_ring()
+ self.__change_interest()
+
+ def deactive_fisheye(self):
+ """
+ """
+ self.radialnet.set_fisheye(False)
+
+ def __change_ring(self, widget=None):
+ """
+ """
+ if not self.radialnet.is_in_animation():
+ self.radialnet.set_fisheye_ring(self.__ring.get_value())
+ else:
+ self.__ring.set_value(self.radialnet.get_fisheye_ring())
+
+ def __change_interest(self, widget=None):
+ """
+ """
+ if not self.radialnet.is_in_animation():
+ self.radialnet.set_fisheye_interest(self.__interest.get_value())
+ else:
+ self.__interest.set_value(self.radialnet.get_fisheye_interest())
+
+ def __change_spread(self, widget=None):
+ """
+ """
+ if not self.radialnet.is_in_animation():
+ self.radialnet.set_fisheye_spread(self.__spread.get_value())
+ else:
+ self.__spread.set_value(self.radialnet.get_fisheye_spread())
+
+
+class ControlInterpolation(BWExpander):
+ """
+ """
+ def __init__(self, radialnet):
+ """
+ """
+ BWExpander.__init__(self, _('Interpolation'))
+
+ self.radialnet = radialnet
+
+ self.__create_widgets()
+
+ def __create_widgets(self):
+ """
+ """
+ self.__vbox = BWVBox()
+
+ self.__cartesian_radio = Gtk.RadioButton(group=None,
+ label=_('Cartesian'))
+ self.__polar_radio = Gtk.RadioButton(group=self.__cartesian_radio,
+ label=_('Polar'))
+ self.__cartesian_radio.connect('toggled',
+ self.__change_system,
+ RadialNet.INTERPOLATION_CARTESIAN)
+ self.__polar_radio.connect('toggled',
+ self.__change_system,
+ RadialNet.INTERPOLATION_POLAR)
+
+ self.__system_box = BWHBox()
+ self.__system_box.bw_pack_start_noexpand_nofill(self.__polar_radio)
+ self.__system_box.bw_pack_start_noexpand_nofill(self.__cartesian_radio)
+
+ self.__frames_box = BWHBox()
+ self.__frames_label = Gtk.Label.new(_('Frames'))
+ self.__frames_label.set_alignment(0.0, 0.5)
+ self.__frames = Gtk.Adjustment.new(
+ self.radialnet.get_number_of_frames(), 1, 1000, 1, 0, 0)
+ self.__frames.connect('value_changed', self.__change_frames)
+ self.__frames_spin = Gtk.SpinButton(adjustment=self.__frames)
+ self.__frames_box.bw_pack_start_expand_fill(self.__frames_label)
+ self.__frames_box.bw_pack_start_noexpand_nofill(self.__frames_spin)
+
+ self.__vbox.bw_pack_start_noexpand_nofill(self.__frames_box)
+ self.__vbox.bw_pack_start_noexpand_nofill(self.__system_box)
+
+ self.bw_add(self.__vbox)
+
+ GLib.timeout_add(REFRESH_RATE, self.__update_animation)
+
+ def __update_animation(self):
+ """
+ """
+ active = self.radialnet.get_interpolation()
+
+ if active == RadialNet.INTERPOLATION_CARTESIAN:
+ self.__cartesian_radio.set_active(True)
+
+ else:
+ self.__polar_radio.set_active(True)
+
+ return True
+
+ def __change_system(self, widget, value):
+ """
+ """
+ if not self.radialnet.set_interpolation(value):
+
+ active = self.radialnet.get_interpolation()
+
+ if active == RadialNet.INTERPOLATION_CARTESIAN:
+ self.__cartesian_radio.set_active(True)
+
+ else:
+ self.__polar_radio.set_active(True)
+
+ def __change_frames(self, widget):
+ """
+ """
+ if not self.radialnet.set_number_of_frames(self.__frames.get_value()):
+ self.__frames.set_value(self.radialnet.get_number_of_frames())
+
+
+class ControlLayout(BWExpander):
+ """
+ """
+ def __init__(self, radialnet):
+ """
+ """
+ BWExpander.__init__(self, _('Layout'))
+
+ self.radialnet = radialnet
+
+ self.__create_widgets()
+
+ def __create_widgets(self):
+ """
+ """
+ self.__hbox = BWHBox()
+
+ self.__layout = Gtk.ComboBoxText()
+ self.__layout.append_text(_('Symmetric'))
+ self.__layout.append_text(_('Weighted'))
+ self.__layout.set_active(self.radialnet.get_layout())
+ self.__layout.connect('changed', self.__change_layout)
+ self.__force = Gtk.ToolButton(stock_id=Gtk.STOCK_REFRESH)
+ self.__force.connect('clicked', self.__force_update)
+
+ self.__hbox.bw_pack_start_expand_fill(self.__layout)
+ self.__hbox.bw_pack_start_noexpand_nofill(self.__force)
+
+ self.bw_add(self.__hbox)
+
+ self.__check_layout()
+
+ def __check_layout(self):
+ """
+ """
+ if self.__layout.get_active() == RadialNet.LAYOUT_WEIGHTED:
+ self.__force.set_sensitive(True)
+
+ else:
+ self.__force.set_sensitive(False)
+
+ return True
+
+ def __force_update(self, widget):
+ """
+ """
+ self.__fisheye_ring = self.radialnet.get_fisheye_ring()
+ self.radialnet.update_layout()
+
+ def __change_layout(self, widget):
+ """
+ """
+ if not self.radialnet.set_layout(self.__layout.get_active()):
+ self.__layout.set_active(self.radialnet.get_layout())
+
+ else:
+ self.__check_layout()
+
+
+class ControlRingGap(BWVBox):
+ """
+ """
+ def __init__(self, radialnet):
+ """
+ """
+ BWVBox.__init__(self)
+
+ self.radialnet = radialnet
+
+ self.__create_widgets()
+
+ def __create_widgets(self):
+ """
+ """
+ self.__radius = ControlVariable(_('Ring gap'),
+ self.radialnet.get_ring_gap,
+ self.radialnet.set_ring_gap)
+
+ self.__label = Gtk.Label.new(_('Lower ring gap'))
+ self.__label.set_alignment(0.0, 0.5)
+ self.__adjustment = Gtk.Adjustment.new(
+ self.radialnet.get_min_ring_gap(), 0, 50, 1, 0, 0)
+ self.__spin = Gtk.SpinButton(adjustment=self.__adjustment)
+ self.__spin.connect('value_changed', self.__change_lower)
+
+ self.__lower_hbox = BWHBox()
+ self.__lower_hbox.bw_pack_start_expand_fill(self.__label)
+ self.__lower_hbox.bw_pack_start_noexpand_nofill(self.__spin)
+
+ self.bw_pack_start_noexpand_nofill(self.__radius)
+ self.bw_pack_start_noexpand_nofill(self.__lower_hbox)
+
+ def __change_lower(self, widget):
+ """
+ """
+ if not self.radialnet.set_min_ring_gap(self.__adjustment.get_value()):
+ self.__adjustment.set_value(self.radialnet.get_min_ring_gap())
+
+
+class ControlOptions(BWScrolledWindow):
+ """
+ """
+ def __init__(self, radialnet):
+ """
+ """
+ BWScrolledWindow.__init__(self)
+
+ self.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.ALWAYS)
+ self.set_shadow_type(Gtk.ShadowType.NONE)
+
+ self.radialnet = radialnet
+
+ self.enable_labels = True
+
+ self.__create_widgets()
+
+ def __create_widgets(self):
+ """
+ """
+ self.__liststore = Gtk.ListStore.new([bool, str])
+
+ self.__liststore.append([None, OPTIONS[0]])
+ self.__liststore.append([None, OPTIONS[1]])
+ self.__liststore.append([None, OPTIONS[2]])
+ self.__liststore.append([None, OPTIONS[3]])
+ self.__liststore.append([None, OPTIONS[4]])
+ self.__liststore.append([None, OPTIONS[5]])
+ self.__liststore.append([None, OPTIONS[6]])
+
+ self.__cell_toggle = Gtk.CellRendererToggle()
+ self.__cell_toggle.set_property('activatable', True)
+ self.__cell_toggle.connect('toggled',
+ self.__change_option,
+ self.__liststore)
+
+ self.__column_toggle = Gtk.TreeViewColumn(cell_renderer=self.__cell_toggle)
+ self.__column_toggle.add_attribute(self.__cell_toggle, 'active', 0)
+ self.__column_toggle.set_cell_data_func(self.__cell_toggle, self.__cell_toggle_data_method)
+
+ self.__cell_text = Gtk.CellRendererText()
+
+ self.__column_text = Gtk.TreeViewColumn(title=None,
+ cell_renderer=self.__cell_text,
+ text=1)
+ self.__column_text.set_cell_data_func(self.__cell_text, self.__cell_text_data_method)
+
+ self.__treeview = Gtk.TreeView.new_with_model(self.__liststore)
+ self.__treeview.set_enable_search(True)
+ self.__treeview.set_search_column(1)
+ self.__treeview.set_headers_visible(False)
+ self.__treeview.append_column(self.__column_toggle)
+ self.__treeview.append_column(self.__column_text)
+
+ self.add_with_viewport(self.__treeview)
+
+ GLib.timeout_add(REFRESH_RATE, self.__update_options)
+
+ def __cell_toggle_data_method(self, column, cell, model, it, data):
+ if not self.enable_labels and model.get_value(it, 1) == 'hostname':
+ cell.set_property('activatable', False)
+ else:
+ cell.set_property('activatable', True)
+
+ def __cell_text_data_method(self, column, cell, model, it, data):
+ if not self.enable_labels and model.get_value(it, 1) == 'hostname':
+ cell.set_property('strikethrough', True)
+ else:
+ cell.set_property('strikethrough', False)
+
+ def __update_options(self):
+ """
+ """
+ model = self.__liststore
+
+ model[OPTIONS.index('address')][0] = self.radialnet.get_show_address()
+ model[OPTIONS.index('hostname')][0] = self.enable_labels and \
+ self.radialnet.get_show_hostname()
+ model[OPTIONS.index('icon')][0] = self.radialnet.get_show_icon()
+ model[OPTIONS.index('latency')][0] = self.radialnet.get_show_latency()
+ model[OPTIONS.index('ring')][0] = self.radialnet.get_show_ring()
+ model[OPTIONS.index('region')][0] = self.radialnet.get_show_region()
+ model[OPTIONS.index('slow in/out')][0] = \
+ self.radialnet.get_slow_inout()
+
+ return True
+
+ def __change_option(self, cell, option, model):
+ """
+ """
+ option = int(option)
+ model[option][0] = not model[option][0]
+
+ if OPTIONS[option] == 'address':
+ self.radialnet.set_show_address(model[option][0])
+ if model[option][0]:
+ model[OPTIONS.index('hostname')][0] = self.radialnet.get_show_hostname()
+ else:
+ model[OPTIONS.index('hostname')][0] = False
+ self.enable_labels = model[option][0]
+
+ elif OPTIONS[option] == 'hostname':
+ self.radialnet.set_show_hostname(model[option][0])
+
+ elif OPTIONS[option] == 'icon':
+ self.radialnet.set_show_icon(model[option][0])
+
+ elif OPTIONS[option] == 'latency':
+ self.radialnet.set_show_latency(model[option][0])
+
+ elif OPTIONS[option] == 'ring':
+ self.radialnet.set_show_ring(model[option][0])
+
+ elif OPTIONS[option] == 'region':
+ self.radialnet.set_show_region(model[option][0])
+
+ elif OPTIONS[option] == 'slow in/out':
+ self.radialnet.set_slow_inout(model[option][0])
+
+
+class ControlView(BWExpander):
+ """
+ """
+ def __init__(self, radialnet):
+ """
+ """
+ BWExpander.__init__(self, _('View'))
+ self.set_expanded(True)
+
+ self.radialnet = radialnet
+
+ self.__create_widgets()
+
+ def __create_widgets(self):
+ """
+ """
+ self.__vbox = BWVBox(spacing=0)
+
+ self.__zoom = ControlVariable(_('Zoom'),
+ self.radialnet.get_zoom,
+ self.radialnet.set_zoom)
+
+ self.__ring_gap = ControlRingGap(self.radialnet)
+ self.__navigation = ControlNavigation(self.radialnet)
+
+ self.__options = ControlOptions(self.radialnet)
+ self.__options.set_border_width(0)
+
+ self.__vbox.bw_pack_start_expand_nofill(self.__options)
+ self.__vbox.bw_pack_start_noexpand_nofill(self.__navigation)
+ self.__vbox.bw_pack_start_noexpand_nofill(self.__zoom)
+ self.__vbox.bw_pack_start_noexpand_nofill(self.__ring_gap)
+
+ self.bw_add(self.__vbox)
+
+
+class ControlNavigation(Gtk.DrawingArea):
+ """
+ """
+ def __init__(self, radialnet):
+ """
+ """
+ Gtk.DrawingArea.__init__(self)
+
+ self.radialnet = radialnet
+
+ self.__rotate_node = PolarCoordinate()
+ self.__rotate_node.set_coordinate(40, 90)
+ self.__center_of_widget = (50, 50)
+ self.__moving = None
+ self.__centering = False
+ self.__rotating = False
+ self.__move_pass = 100
+
+ self.__move_position = (0, 0)
+ self.__move_addition = [(-1, 0),
+ (-1, -1),
+ (0, -1),
+ (1, -1),
+ (1, 0),
+ (1, 1),
+ (0, 1),
+ (-1, 1)]
+
+ self.__move_factor = 1
+ self.__move_factor_limit = 20
+
+ self.__rotate_radius = 6
+ self.__move_radius = 6
+
+ self.__rotate_clicked = False
+ self.__move_clicked = None
+
+ self.connect('draw', self.draw)
+ self.connect('button_press_event', self.button_press)
+ self.connect('button_release_event', self.button_release)
+ self.connect('motion_notify_event', self.motion_notify)
+ self.connect('enter_notify_event', self.enter_notify)
+ self.connect('leave_notify_event', self.leave_notify)
+ self.connect('key_press_event', self.key_press)
+ self.connect('key_release_event', self.key_release)
+
+ self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK |
+ Gdk.EventMask.BUTTON_RELEASE_MASK |
+ Gdk.EventMask.ENTER_NOTIFY_MASK |
+ Gdk.EventMask.LEAVE_NOTIFY_MASK |
+ Gdk.EventMask.KEY_PRESS_MASK |
+ Gdk.EventMask.KEY_RELEASE_MASK |
+ Gdk.EventMask.POINTER_MOTION_HINT_MASK |
+ Gdk.EventMask.POINTER_MOTION_MASK)
+
+ self.__rotate_node.set_coordinate(40, self.radialnet.get_rotation())
+
+ def key_press(self, widget, event):
+ """
+ """
+ # key = Gdk.keyval_name(event.keyval)
+
+ self.queue_draw()
+
+ return True
+
+ def key_release(self, widget, event):
+ """
+ """
+ # key = Gdk.keyval_name(event.keyval)
+
+ self.queue_draw()
+
+ return True
+
+ def enter_notify(self, widget, event):
+ """
+ """
+ return False
+
+ def leave_notify(self, widget, event):
+ """
+ """
+ self.queue_draw()
+
+ return False
+
+ def button_press(self, widget, event):
+ """
+ Drawing callback
+ @type widget: GtkWidget
+ @param widget: Gtk widget superclass
+ @type event: GtkEvent
+ @param event: Gtk event of widget
+ @rtype: boolean
+ @return: Indicator of the event propagation
+ """
+ pointer = self.get_pointer()
+
+ direction = False
+
+ if self.__rotate_is_clicked(pointer):
+
+ event.window.set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))
+ self.__rotating = True
+
+ direction = self.__move_is_clicked(pointer)
+
+ if direction is not None and self.__moving is None:
+
+ event.window.set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))
+ self.__moving = direction
+ self.__move_in_direction(direction)
+
+ if self.__center_is_clicked(pointer):
+
+ event.window.set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))
+ self.__centering = True
+ self.__move_position = (0, 0)
+ self.radialnet.set_translation(self.__move_position)
+
+ self.queue_draw()
+
+ return False
+
+ def button_release(self, widget, event):
+ """
+ Drawing callback
+ @type widget: GtkWidget
+ @param widget: Gtk widget superclass
+ @type event: GtkEvent
+ @param event: Gtk event of widget
+ @rtype: boolean
+ @return: Indicator of the event propagation
+ """
+ self.__moving = None # stop moving
+ self.__centering = False
+ self.__rotating = False # stop rotate
+ self.__move_factor = 1
+
+ event.window.set_cursor(Gdk.Cursor(Gdk.CursorType.LEFT_PTR))
+
+ self.queue_draw()
+
+ return False
+
+ def motion_notify(self, widget, event):
+ """
+ Drawing callback
+ @type widget: GtkWidget
+ @param widget: Gtk widget superclass
+ @type event: GtkEvent
+ @param event: Gtk event of widget
+ @rtype: boolean
+ @return: Indicator of the event propagation
+ """
+ xc, yc = self.__center_of_widget
+ x, y = self.get_pointer()
+
+ status = not self.radialnet.is_in_animation()
+ status = status and not self.radialnet.is_empty()
+
+ if self.__rotating and status:
+
+ r, t = self.__rotate_node.get_coordinate()
+ t = math.degrees(math.atan2(yc - y, x - xc))
+
+ if t < 0:
+ t = 360 + t
+
+ self.radialnet.set_rotation(t)
+ self.__rotate_node.set_coordinate(r, t)
+
+ self.queue_draw()
+
+ return False
+
+ def draw(self, widget, context):
+ """
+ Drawing callback
+ @type widget: GtkWidget
+ @param widget: Gtk widget superclass
+ @type context: cairo.Context
+ @param context: cairo context class
+ @rtype: boolean
+ @return: Indicator of the event propagation
+ """
+ self.set_size_request(120, 130)
+
+ self.__draw(context)
+
+ return False
+
+ def __draw_rotate_control(self, context):
+ """
+ """
+ xc, yc = self.__center_of_widget
+ r, t = self.__rotate_node.get_coordinate()
+ x, y = self.__rotate_node.to_cartesian()
+
+ # draw text
+ context.set_font_size(10)
+ context.move_to(xc - 49, yc - 48)
+ context.show_text(_("Navigation"))
+
+ width = context.text_extents(str(int(t)))[2]
+ context.move_to(xc + 49 - width - 2, yc - 48)
+ context.show_text(str(round(t, 1)))
+ context.set_line_width(1)
+ context.stroke()
+
+ # draw arc
+ context.set_dash([1, 2])
+ context.arc(xc, yc, 40, 0, 2 * math.pi)
+ context.set_source_rgb(0.0, 0.0, 0.0)
+ context.set_line_width(1)
+ context.stroke()
+
+ # draw node
+ context.set_dash([1, 0])
+ context.arc(xc + x, yc - y, self.__rotate_radius, 0, 2 * math.pi)
+
+ if self.__rotating:
+ context.set_source_rgb(0.0, 0.0, 0.0)
+ else:
+ context.set_source_rgb(1.0, 1.0, 1.0)
+
+ context.fill_preserve()
+ context.set_source_rgb(0.0, 0.0, 0.0)
+ context.set_line_width(1)
+ context.stroke()
+
+ return False
+
+ def __draw_move_control(self, context):
+ """
+ """
+ xc, yc = self.__center_of_widget
+ pc = PolarCoordinate()
+
+ context.set_dash([1, 1])
+ context.arc(xc, yc, 23, 0, 2 * math.pi)
+ context.set_source_rgb(0.0, 0.0, 0.0)
+ context.set_line_width(1)
+ context.stroke()
+
+ for i in range(8):
+
+ pc.set_coordinate(23, 45 * i)
+ x, y = pc.to_cartesian()
+
+ context.set_dash([1, 1])
+ context.move_to(xc, yc)
+ context.line_to(xc + x, yc - y)
+ context.stroke()
+
+ context.set_dash([1, 0])
+ context.arc(
+ xc + x, yc - y, self.__move_radius, 0, 2 * math.pi)
+
+ if i == self.__moving:
+ context.set_source_rgb(0.0, 0.0, 0.0)
+ else:
+ context.set_source_rgb(1.0, 1.0, 1.0)
+ context.fill_preserve()
+ context.set_source_rgb(0.0, 0.0, 0.0)
+ context.set_line_width(1)
+ context.stroke()
+
+ context.arc(xc, yc, 6, 0, 2 * math.pi)
+
+ if self.__centering:
+ context.set_source_rgb(0.0, 0.0, 0.0)
+ else:
+ context.set_source_rgb(1.0, 1.0, 1.0)
+ context.fill_preserve()
+ context.set_source_rgb(0.0, 0.0, 0.0)
+ context.set_line_width(1)
+ context.stroke()
+
+ return False
+
+ def __draw(self, context):
+ """
+ Drawing method
+ """
+ # Getting allocation reference
+ allocation = self.get_allocation()
+
+ self.__center_of_widget = (allocation.width // 2,
+ allocation.height // 2)
+
+ self.__draw_rotate_control(context)
+ self.__draw_move_control(context)
+
+ return False
+
+ def __move_in_direction(self, direction):
+ """
+ """
+ if self.__moving is not None:
+
+ bx, by = self.__move_position
+ ax, ay = self.__move_addition[direction]
+
+ self.__move_position = (bx + self.__move_factor * ax,
+ by + self.__move_factor * ay)
+ self.radialnet.set_translation(self.__move_position)
+
+ if self.__move_factor < self.__move_factor_limit:
+ self.__move_factor += 1
+
+ GLib.timeout_add(self.__move_pass,
+ self.__move_in_direction,
+ direction)
+
+ return False
+
+ def __rotate_is_clicked(self, pointer):
+ """
+ """
+ xn, yn = self.__rotate_node.to_cartesian()
+ xc, yc = self.__center_of_widget
+
+ center = (xc + xn, yc - yn)
+ return geometry.is_in_circle(pointer, self.__rotate_radius, center)
+
+ def __center_is_clicked(self, pointer):
+ """
+ """
+ return geometry.is_in_circle(pointer, self.__move_radius,
+ self.__center_of_widget)
+
+ def __move_is_clicked(self, pointer):
+ """
+ """
+ xc, yc = self.__center_of_widget
+ pc = PolarCoordinate()
+
+ for i in range(8):
+
+ pc.set_coordinate(23, 45 * i)
+ x, y = pc.to_cartesian()
+
+ center = (xc + x, yc - y)
+ if geometry.is_in_circle(pointer, self.__move_radius, center):
+ return i
+
+ return None
diff --git a/zenmap/radialnet/gui/Dialogs.py b/zenmap/radialnet/gui/Dialogs.py
new file mode 100644
index 0000000..01d97f2
--- /dev/null
+++ b/zenmap/radialnet/gui/Dialogs.py
@@ -0,0 +1,89 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk
+
+from radialnet.core.Info import INFO
+from radialnet.gui.Image import Pixmaps
+
+
+class AboutDialog(Gtk.AboutDialog):
+ """
+ """
+ def __init__(self):
+ """
+ """
+ Gtk.AboutDialog.__init__(self)
+
+ self.set_name(INFO['name'])
+ self.set_version(INFO['version'])
+ self.set_website(INFO['website'])
+ self.set_authors(INFO['authors'])
+ self.set_license(INFO['license'])
+ self.set_copyright(INFO['copyright'])
+
+ self.set_logo(Pixmaps().get_pixbuf('logo'))
+
+ self.connect('response', self.__destroy)
+
+ def __destroy(self, dialog, id):
+ """
+ """
+ self.destroy()
diff --git a/zenmap/radialnet/gui/HostsViewer.py b/zenmap/radialnet/gui/HostsViewer.py
new file mode 100644
index 0000000..feb0ae8
--- /dev/null
+++ b/zenmap/radialnet/gui/HostsViewer.py
@@ -0,0 +1,231 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk
+
+import re
+
+from radialnet.bestwidgets.windows import BWMainWindow
+
+from radialnet.gui.NodeNotebook import NodeNotebook
+from radialnet.util.misc import ipv4_compare
+
+
+HOSTS_COLORS = ['#d5ffd5', '#ffffd5', '#ffd5d5']
+
+HOSTS_HEADER = ['ID', '#', 'Hosts']
+
+DIMENSION = (700, 400)
+
+IP_RE = re.compile(r'^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$')
+
+
+class HostsViewer(BWMainWindow):
+ """
+ """
+ def __init__(self, nodes):
+ """
+ """
+ BWMainWindow.__init__(self)
+ self.set_title(_('Hosts Viewer'))
+ self.set_default_size(DIMENSION[0], DIMENSION[1])
+
+ self.__nodes = nodes
+ self.__default_view = Gtk.Label.new(_("No node selected"))
+ self.__view = self.__default_view
+
+ self.__create_widgets()
+
+ def __create_widgets(self):
+ """
+ """
+ self.__panel = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL)
+ self.__panel.set_border_width(6)
+
+ self.__list = HostsList(self, self.__nodes)
+
+ self.__panel.add1(self.__list)
+ self.__panel.add2(self.__view)
+ self.__panel.set_position(int(DIMENSION[0] / 5))
+
+ self.add(self.__panel)
+
+ def change_notebook(self, node):
+ """
+ """
+ if self.__view is not None:
+ self.__view.destroy()
+
+ if node is not None:
+ self.__view = NodeNotebook(node)
+ else:
+ self.__view = self.__default_view
+ self.__view.show_all()
+
+ self.__panel.add2(self.__view)
+
+
+class HostsList(Gtk.ScrolledWindow):
+ """
+ """
+ def __init__(self, parent, nodes):
+ """
+ """
+ super(HostsList, self).__init__()
+ self.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
+ self.set_shadow_type(Gtk.ShadowType.NONE)
+
+ self.__parent = parent
+ self.__nodes = nodes
+
+ self.__create_widgets()
+
+ def __create_widgets(self):
+ """
+ """
+ self.__cell = Gtk.CellRendererText()
+
+ self.__hosts_store = Gtk.ListStore.new([int, int, str, str, bool])
+
+ self.__hosts_treeview = Gtk.TreeView.new_with_model(self.__hosts_store)
+ self.__hosts_treeview.connect('cursor-changed', self.__cursor_callback)
+
+ for i in range(len(self.__nodes)):
+
+ node = self.__nodes[i]
+
+ ports = node.get_info('number_of_open_ports')
+ color = HOSTS_COLORS[node.get_info('vulnerability_score')]
+
+ host = node.get_info('hostname') or node.get_info('ip') or ""
+
+ self.__hosts_store.append([i,
+ ports,
+ host,
+ color,
+ True])
+
+ self.__hosts_column = list()
+
+ for i in range(0, len(HOSTS_HEADER)):
+
+ column = Gtk.TreeViewColumn(title=HOSTS_HEADER[i],
+ cell_renderer=self.__cell,
+ text=i)
+
+ self.__hosts_column.append(column)
+
+ self.__hosts_column[i].set_reorderable(True)
+ self.__hosts_column[i].set_resizable(True)
+ self.__hosts_column[i].set_attributes(self.__cell,
+ text=i,
+ background=3,
+ editable=4)
+
+ self.__hosts_treeview.append_column(self.__hosts_column[2])
+
+ self.__hosts_store.set_sort_func(2, self.__host_sort)
+
+ self.__hosts_column[2].set_sort_column_id(2)
+
+ self.add_with_viewport(self.__hosts_treeview)
+
+ if len(self.__hosts_treeview.get_model()) > 0:
+ self.__hosts_treeview.set_cursor((0,))
+ self.__cursor_callback(self.__hosts_treeview)
+
+ def __cursor_callback(self, widget):
+ """
+ """
+ path = widget.get_cursor()[0]
+ if path is None:
+ return
+
+ iter = self.__hosts_store.get_iter(path)
+
+ node = self.__nodes[self.__hosts_store.get_value(iter, 0)]
+
+ self.__parent.change_notebook(node)
+
+ def __host_sort(self, treemodel, iter1, iter2, *_):
+ """
+ """
+ value1 = treemodel.get_value(iter1, 2)
+ value2 = treemodel.get_value(iter2, 2)
+
+ value1_is_ip = IP_RE.match(value1)
+ value2_is_ip = IP_RE.match(value2)
+
+ if value1_is_ip and value2_is_ip:
+ return ipv4_compare(value1, value2)
+
+ if value1_is_ip:
+ return -1
+
+ if value2_is_ip:
+ return 1
+
+ if value1 < value2:
+ return -1
+
+ if value1 > value2:
+ return 1
+
+ return 0
diff --git a/zenmap/radialnet/gui/Image.py b/zenmap/radialnet/gui/Image.py
new file mode 100644
index 0000000..72020ca
--- /dev/null
+++ b/zenmap/radialnet/gui/Image.py
@@ -0,0 +1,164 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import GdkPixbuf
+
+import os
+import array
+
+from zenmapCore.Paths import Path
+
+
+FORMAT_RGBA = 4
+FORMAT_RGB = 3
+
+
+def get_pixels_for_cairo_image_surface(pixbuf):
+ """
+ This method return the image stride and a python array.ArrayType
+ containing the icon pixels of a gtk.gdk.Pixbuf that can be used by
+ cairo.ImageSurface.create_for_data() method.
+ """
+ data = array.array('B')
+ image_format = pixbuf.get_rowstride() // pixbuf.get_width()
+
+ i = 0
+ j = 0
+ while i < len(pixbuf.get_pixels()):
+
+ b, g, r = pixbuf.get_pixels()[i:i + FORMAT_RGB]
+
+ if image_format == FORMAT_RGBA:
+ a = pixbuf.get_pixels()[i + FORMAT_RGBA - 1]
+ elif image_format == FORMAT_RGB:
+ a = 255
+ else:
+ raise TypeError('unknown image format')
+
+ data[j:j + FORMAT_RGBA] = array.array('B', [r, g, b, a])
+
+ i += image_format
+ j += FORMAT_RGBA
+
+ return (FORMAT_RGBA * pixbuf.get_width(), data)
+
+
+class Image:
+ """
+ """
+ def __init__(self, path=None):
+ """
+ """
+ self.__path = path
+ self.__cache = dict()
+
+ def set_path(self, path):
+ """
+ """
+ self.__path = path
+
+ def get_pixbuf(self, icon, image_type='png'):
+ """
+ """
+ if self.__path is None:
+ return False
+
+ if icon + image_type not in self.__cache.keys():
+
+ file = self.get_icon(icon, image_type)
+ self.__cache[icon + image_type] = \
+ GdkPixbuf.Pixbuf.new_from_file(file)
+
+ return self.__cache[icon + image_type]
+
+ def get_icon(self, icon, image_type='png'):
+ """
+ """
+ if self.__path is None:
+ return False
+
+ return os.path.join(self.__path, icon + "." + image_type)
+
+
+class Pixmaps(Image):
+ """
+ """
+ def __init__(self):
+ """
+ """
+ Image.__init__(self, os.path.join(Path.pixmaps_dir, "radialnet"))
+
+
+class Icons(Image):
+ """
+ """
+ def __init__(self):
+ """
+ """
+ Image.__init__(self, os.path.join(Path.pixmaps_dir, "radialnet"))
+
+
+class Application(Image):
+ """
+ """
+ def __init__(self):
+ """
+ """
+ Image.__init__(self, os.path.join(Path.pixmaps_dir, "radialnet"))
diff --git a/zenmap/radialnet/gui/LegendWindow.py b/zenmap/radialnet/gui/LegendWindow.py
new file mode 100644
index 0000000..3401e43
--- /dev/null
+++ b/zenmap/radialnet/gui/LegendWindow.py
@@ -0,0 +1,242 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk, Gdk, Pango
+
+import math
+import cairo
+
+import zenmapCore.I18N # lgtm[py/unused-import]
+
+from radialnet.gui.Image import Pixmaps
+DIMENSION_NORMAL = (350, 450)
+
+
+def draw_pixmap(context, x, y, name, label):
+ # This is pretty hideous workaround
+ Gdk.cairo_set_source_pixbuf(context, Pixmaps().get_pixbuf(name), x, y)
+ #context.set_source_pixbuf()
+ context.paint()
+ context.move_to(x + 50, y + 10)
+ context.set_source_rgb(0, 0, 0)
+ context.show_text(label)
+
+
+def reset_font(context):
+ context.select_font_face(
+ "Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_SLANT_NORMAL)
+ context.set_font_size(11)
+
+
+def draw_heading(context, x, y, label):
+ context.select_font_face(
+ "Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
+ context.set_font_size(13)
+ context.move_to(x - 15, y)
+ context.set_source_rgb(0, 0, 0)
+ context.show_text(label)
+ reset_font(context)
+
+
+def draw_circle(context, x, y, size, color, label):
+ context.set_source_rgb(0, 0, 0)
+ context.move_to(x, y)
+ context.arc(x, y, size, 0, 2 * math.pi)
+ context.stroke_preserve()
+ context.set_source_rgb(*color)
+ context.fill()
+ context.set_source_rgb(0, 0, 0)
+ context.move_to(x + 50, y + 5)
+ context.show_text(label)
+
+
+def draw_square(context, x, y, size, color):
+ context.set_source_rgb(0, 0, 0)
+ context.rectangle(x, y - size / 2, size, size)
+ context.stroke_preserve()
+ context.set_source_rgb(*color)
+ context.fill()
+
+
+def draw_line(context, x, y, dash, color, label):
+ context.set_source_rgb(*color)
+ context.move_to(x - 20, y)
+ context.set_dash(dash)
+ context.line_to(x + 25, y)
+ context.stroke()
+ context.set_dash([])
+ context.set_source_rgb(0, 0, 0)
+ context.move_to(x + 50, y + 5)
+ context.show_text(label)
+
+
+class LegendWindow(Gtk.Window):
+ """
+ """
+ def __init__(self):
+ """
+ """
+ Gtk.Window.__init__(self, type=Gtk.WindowType.TOPLEVEL)
+ self.set_default_size(DIMENSION_NORMAL[0], DIMENSION_NORMAL[1])
+ self.__title_font = Pango.FontDescription("Monospace Bold")
+ self.set_title(_("Topology Legend"))
+
+ self.vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
+ self.add(self.vbox)
+
+ self.drawing_area = Gtk.DrawingArea()
+ self.vbox.pack_start(self.drawing_area, True, True, 0)
+ self.drawing_area.connect("draw", self.draw_event_handler)
+ self.more_uri = Gtk.LinkButton.new_with_label(
+ "https://nmap.org/book/zenmap-topology.html#zenmap-topology-legend",
+ _("View full legend online"))
+ self.vbox.pack_start(self.more_uri, False, False, 0)
+
+ def draw_event_handler(self, widget, graphic_context):
+ """
+ """
+ x, y = 45, 20
+ draw_heading(graphic_context, x, y, _("Hosts"))
+
+ # white circle
+ y += 20
+ draw_circle(graphic_context, x, y, 3, (1, 1, 1),
+ _("host was not port scanned"))
+ # green circle
+ y += 20
+ draw_circle(graphic_context, x, y, 4, (0, 1, 0),
+ _("host with fewer than 3 open ports"))
+ # yellow circle
+ y += 20
+ draw_circle(graphic_context, x, y, 5, (1, 1, 0),
+ _("host with 3 to 6 open ports"))
+ # red circle
+ y += 20
+ draw_circle(graphic_context, x, y, 6, (1, 0, 0),
+ _("host with more than 6 open ports"))
+
+ # green square
+ y += 20
+ rx = x - 20
+ draw_square(graphic_context, rx, y, 10, (0, 1, 0))
+ rx += 10 + 5
+ # yellow square
+ draw_square(graphic_context, rx, y, 12, (1, 1, 0))
+ rx += 12 + 5
+ # red square
+ draw_square(graphic_context, rx, y, 14, (1, 0, 0))
+
+ graphic_context.move_to(x + 50, y + 5)
+ graphic_context.set_source_rgb(0, 0, 0)
+ graphic_context.show_text(_("host is a router, switch, or WAP"))
+
+ # connections between hosts
+ y += 30
+ draw_heading(graphic_context, x, y, _("Traceroute connections"))
+
+ y += 20
+ graphic_context.move_to(x, y)
+ graphic_context.show_text(_("Thicker line means higher round-trip time"))
+ # primary traceroute (blue line)
+ y += 20
+ draw_line(graphic_context, x, y, [], (0, 0, 1),
+ _("primary traceroute connection"))
+ # Alternate route (orange line)
+ y += 20
+ draw_line(graphic_context, x, y, [], (1, 0.5, 0),
+ _("alternate path"))
+ # no traceroute
+ y += 20
+ draw_line(graphic_context, x, y, [4.0, 2.0], (0, 0, 0),
+ _("no traceroute information"))
+ # missing traceroute
+ y += 20
+ graphic_context.set_source_rgb(0.5, 0.7, 0.95)
+ graphic_context.move_to(x - 15, y)
+ graphic_context.arc(x - 25, y, 5, 0, 2 * math.pi)
+ graphic_context.stroke_preserve()
+ draw_line(graphic_context, x, y, [4.0, 2.0], (0.5, 0.7, 0.95),
+ _("missing traceroute hop"))
+
+ # special purpose hosts
+ y += 30
+ draw_heading(graphic_context, x, y, _("Additional host icons"))
+
+ # router image
+ y += 20
+ draw_pixmap(graphic_context, x, y, "router", _("router"))
+
+ # switch image
+ y += 20
+ draw_pixmap(graphic_context, x, y, "switch", _("switch"))
+
+ # wap image
+ y += 20
+ draw_pixmap(graphic_context, x, y, "wireless",
+ _("wireless access point"))
+
+ # firewall image
+ y += 20
+ draw_pixmap(graphic_context, x, y, "firewall", _("firewall"))
+
+ # host with filtered ports
+ y += 20
+ draw_pixmap(graphic_context, x, y, "padlock",
+ _("host with some filtered ports"))
diff --git a/zenmap/radialnet/gui/NodeNotebook.py b/zenmap/radialnet/gui/NodeNotebook.py
new file mode 100644
index 0000000..ea208ec
--- /dev/null
+++ b/zenmap/radialnet/gui/NodeNotebook.py
@@ -0,0 +1,742 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk, GObject, Pango
+
+from radialnet.bestwidgets.boxes import BWVBox, BWHBox, BWScrolledWindow, BWTable
+from radialnet.bestwidgets.expanders import BWExpander
+from radialnet.bestwidgets.labels import BWLabel, BWSectionLabel
+from radialnet.bestwidgets.textview import BWTextEditor
+import zenmapCore.I18N # lgtm[py/unused-import]
+
+
+PORTS_HEADER = [
+ _('Port'), _('Protocol'), _('State'), _('Service'), _('Method')]
+EXTRAPORTS_HEADER = [_('Count'), _('State'), _('Reasons')]
+
+SERVICE_COLORS = {'open': '#ffd5d5', # noqa
+ 'closed': '#d5ffd5', # noqa
+ 'filtered': '#ffffd5', # noqa
+ 'unfiltered': '#ffd5d5', # noqa
+ 'open|filtered': '#ffd5d5', # noqa
+ 'closed|filtered': '#d5ffd5'} # noqa
+UNKNOWN_SERVICE_COLOR = '#d5d5d5'
+
+TRACE_HEADER = ['TTL', 'RTT', 'IP', _('Hostname')]
+
+TRACE_TEXT = _(
+ "Traceroute on port <b>%s/%s</b> totalized <b>%d</b> known hops.")
+
+NO_TRACE_TEXT = _("No traceroute information available.")
+
+HOP_COLOR = {'known': '#ffffff', # noqa
+ 'unknown': '#cccccc'} # noqa
+
+SYSTEM_ADDRESS_TEXT = "[%s] %s"
+
+OSMATCH_HEADER = ['%', _('Name'), _('DB Line')]
+OSCLASS_HEADER = ['%', _('Vendor'), _('Type'), _('Family'), _('Version')]
+
+USED_PORTS_TEXT = "%d/%s %s"
+
+TCP_SEQ_NOTE = _("""\
+<b>*</b> TCP sequence <i>index</i> equal to %d and <i>difficulty</i> is "%s".\
+""")
+
+
+def get_service_color(state):
+ color = SERVICE_COLORS.get(state)
+ if color is None:
+ color = UNKNOWN_SERVICE_COLOR
+ return color
+
+
+class NodeNotebook(Gtk.Notebook):
+ """
+ """
+ def __init__(self, node):
+ """
+ """
+ Gtk.Notebook.__init__(self)
+ self.set_tab_pos(Gtk.PositionType.TOP)
+
+ self.__node = node
+
+ self.__create_widgets()
+
+ def __create_widgets(self):
+ """
+ """
+ # create body elements
+ self.__services_page = ServicesPage(self.__node)
+ self.__system_page = SystemPage(self.__node)
+ self.__trace_page = TraceroutePage(self.__node)
+
+ # packing notebook elements
+ self.append_page(self.__system_page, BWLabel(_('General')))
+ self.append_page(self.__services_page, BWLabel(_('Services')))
+ self.append_page(self.__trace_page, BWLabel(_('Traceroute')))
+
+
+class ServicesPage(Gtk.Notebook):
+ """
+ """
+ def __init__(self, node):
+ """
+ """
+ Gtk.Notebook.__init__(self)
+ self.set_border_width(6)
+ self.set_tab_pos(Gtk.PositionType.TOP)
+
+ self.__node = node
+ self.__font = Pango.FontDescription('Monospace')
+
+ self.__create_widgets()
+
+ def __create_widgets(self):
+ """
+ """
+ self.__cell = Gtk.CellRendererText()
+
+ # texteditor widgets
+ self.__texteditor = BWTextEditor()
+ self.__texteditor.bw_modify_font(self.__font)
+ self.__texteditor.bw_set_editable(False)
+ self.__texteditor.set_border_width(0)
+
+ self.__select_combobox = Gtk.ComboBoxText()
+ self.__select_combobox.connect('changed', self.__change_text_value)
+
+ self.__viewer = BWVBox(spacing=6)
+ self.__viewer.set_border_width(6)
+
+ self.__viewer.bw_pack_start_noexpand_nofill(self.__select_combobox)
+ self.__viewer.bw_pack_start_expand_fill(self.__texteditor)
+
+ self.__text = list()
+
+ # ports information
+ number_of_ports = len(self.__node.get_info('ports'))
+ self.__ports_label = BWLabel(_('Ports (%s)') % number_of_ports)
+
+ self.__ports_scroll = BWScrolledWindow()
+
+ self.__ports_store = Gtk.TreeStore.new([GObject.TYPE_INT,
+ GObject.TYPE_STRING,
+ GObject.TYPE_STRING,
+ GObject.TYPE_STRING,
+ GObject.TYPE_STRING,
+ GObject.TYPE_STRING,
+ GObject.TYPE_BOOLEAN])
+
+ self.__ports_treeview = Gtk.TreeView.new_with_model(self.__ports_store)
+
+ for port in self.__node.get_info('ports'):
+
+ color = get_service_color(port['state']['state'])
+
+ service_name = port['service'].get('name', _('<unknown>'))
+
+ service_method = port['service'].get('method', _('<none>'))
+
+ reference = self.__ports_store.append(None,
+ [port['id'],
+ port['protocol'],
+ port['state']['state'],
+ service_name,
+ service_method,
+ color,
+ True])
+
+ for key in port['state']:
+ self.__ports_store.append(reference,
+ [port['id'],
+ 'state',
+ key,
+ port['state'][key],
+ '',
+ 'white',
+ True])
+
+ for key in port['service']:
+
+ if key in ['servicefp']:
+
+ text = _('[%d] service: %s') % (port['id'], key)
+
+ self.__select_combobox.append_text(text)
+ self.__text.append(port['service'][key])
+
+ value = _('<special field>')
+
+ else:
+ value = port['service'][key]
+
+ self.__ports_store.append(reference,
+ [port['id'],
+ 'service',
+ key,
+ value,
+ '',
+ 'white',
+ True])
+
+ #for script in port['scripts']:
+ # text = _('[%d] script: %s') % (port['id'], script['id'])
+ # self.__select_combobox.append_text(text)
+ # self.__text.append(script['output'])
+ #
+ # self.__ports_store.append(reference,
+ # [port['id'],
+ # 'script',
+ # 'id',
+ # script['id'],
+ # _('<special field>'),
+ # 'white',
+ # True])
+
+ self.__ports_column = list()
+
+ for i in range(len(PORTS_HEADER)):
+
+ column = Gtk.TreeViewColumn(title=PORTS_HEADER[i],
+ cell_renderer=self.__cell,
+ text=i)
+
+ self.__ports_column.append(column)
+
+ self.__ports_column[i].set_reorderable(True)
+ self.__ports_column[i].set_resizable(True)
+ self.__ports_column[i].set_sort_column_id(i)
+ self.__ports_column[i].set_attributes(self.__cell,
+ text=i,
+ background=5,
+ editable=6)
+
+ self.__ports_treeview.append_column(self.__ports_column[i])
+
+ self.__ports_scroll.add_with_viewport(self.__ports_treeview)
+
+ # extraports information
+ number_of_xports = 0
+
+ self.__xports_scroll = BWScrolledWindow()
+
+ self.__xports_store = Gtk.TreeStore.new([GObject.TYPE_INT,
+ GObject.TYPE_STRING,
+ GObject.TYPE_STRING,
+ GObject.TYPE_STRING,
+ GObject.TYPE_BOOLEAN])
+
+ self.__xports_treeview = Gtk.TreeView.new_with_model(self.__xports_store)
+
+ for xports in self.__node.get_info('extraports'):
+
+ color = get_service_color(xports['state'])
+ number_of_xports += xports['count']
+
+ reference = self.__xports_store.append(
+ None, [xports['count'], xports['state'],
+ ", ".join(xports['reason']), color, True])
+
+ for xreason in xports['all_reason']:
+ self.__xports_store.append(reference,
+ [xreason['count'],
+ xports['state'],
+ xreason['reason'],
+ 'white',
+ True])
+
+ self.__xports_column = list()
+
+ for i in range(len(EXTRAPORTS_HEADER)):
+
+ column = Gtk.TreeViewColumn(title=EXTRAPORTS_HEADER[i],
+ cell_renderer=self.__cell,
+ text=i)
+
+ self.__xports_column.append(column)
+
+ self.__xports_column[i].set_reorderable(True)
+ self.__xports_column[i].set_resizable(True)
+ self.__xports_column[i].set_sort_column_id(i)
+ self.__xports_column[i].set_attributes(self.__cell,
+ text=i,
+ background=3,
+ editable=4)
+
+ self.__xports_treeview.append_column(self.__xports_column[i])
+
+ xports_label_text = _('Extraports (%s)') % number_of_xports
+ self.__xports_label = BWLabel(xports_label_text)
+
+ self.__xports_scroll.add_with_viewport(self.__xports_treeview)
+
+ self.append_page(self.__ports_scroll, self.__ports_label)
+ self.append_page(self.__xports_scroll, self.__xports_label)
+ self.append_page(self.__viewer, BWLabel(_('Special fields')))
+
+ if len(self.__text) > 0:
+ self.__select_combobox.set_active(0)
+
+ def __change_text_value(self, widget):
+ """
+ """
+ id = self.__select_combobox.get_active()
+
+ self.__texteditor.bw_set_text(self.__text[id])
+
+
+class SystemPage(BWScrolledWindow):
+ """
+ """
+ def __init__(self, node):
+ """
+ """
+ BWScrolledWindow.__init__(self)
+
+ self.__node = node
+ self.__font = Pango.FontDescription('Monospace')
+
+ self.__create_widgets()
+
+ def __create_widgets(self):
+ """
+ """
+ self.__vbox = BWVBox()
+ self.__vbox.set_border_width(6)
+
+ self.__cell = Gtk.CellRendererText()
+
+ self.__general_frame = BWExpander(_('General information'))
+ self.__sequences_frame = BWExpander(_('Sequences'))
+ self.__os_frame = BWExpander(_('Operating System'))
+
+ self.__sequences_frame.bw_add(Gtk.Label.new(_('No sequence information.')))
+ self.__os_frame.bw_add(Gtk.Label.new(_('No OS information.')))
+
+ # general information widgets
+ self.__general = BWTable(3, 2)
+
+ self.__address_label = BWSectionLabel(_('Address:'))
+ self.__address_list = Gtk.ComboBoxText.new_with_entry()
+ self.__address_list.get_child().set_editable(False)
+
+ for address in self.__node.get_info('addresses'):
+
+ params = address['type'], address['addr']
+ address_text = SYSTEM_ADDRESS_TEXT % params
+
+ if address['vendor'] is not None and address['vendor'] != '':
+ address_text += " (%s)" % address['vendor']
+
+ self.__address_list.append_text(address_text)
+
+ self.__address_list.set_active(0)
+
+ self.__general.bw_attach_next(self.__address_label,
+ yoptions=Gtk.AttachOptions.FILL,
+ xoptions=Gtk.AttachOptions.FILL)
+ self.__general.bw_attach_next(self.__address_list, yoptions=Gtk.AttachOptions.FILL)
+
+ if self.__node.get_info('hostnames') is not None:
+
+ self.__hostname_label = BWSectionLabel(_('Hostname:'))
+ self.__hostname_list = Gtk.ComboBoxText.new_with_entry()
+ self.__hostname_list.get_child().set_editable(False)
+
+ for hostname in self.__node.get_info('hostnames'):
+
+ params = hostname['type'], hostname['name']
+ self.__hostname_list.append_text(SYSTEM_ADDRESS_TEXT % params)
+
+ self.__hostname_list.set_active(0)
+
+ self.__general.bw_attach_next(self.__hostname_label,
+ yoptions=Gtk.AttachOptions.FILL,
+ xoptions=Gtk.AttachOptions.FILL)
+ self.__general.bw_attach_next(self.__hostname_list,
+ yoptions=Gtk.AttachOptions.FILL)
+
+ if self.__node.get_info('uptime') is not None:
+
+ self.__uptime_label = BWSectionLabel(_('Last boot:'))
+
+ seconds = self.__node.get_info('uptime')['seconds']
+ lastboot = self.__node.get_info('uptime')['lastboot']
+
+ text = _('%s (%s seconds).') % (lastboot, seconds)
+
+ self.__uptime_value = BWLabel(text)
+ self.__uptime_value.set_selectable(True)
+ self.__uptime_value.set_line_wrap(False)
+
+ self.__general.bw_attach_next(self.__uptime_label,
+ yoptions=Gtk.AttachOptions.FILL,
+ xoptions=Gtk.AttachOptions.FILL)
+ self.__general.bw_attach_next(self.__uptime_value,
+ yoptions=Gtk.AttachOptions.FILL)
+
+ self.__general_frame.bw_add(self.__general)
+ self.__general_frame.set_expanded(True)
+
+ sequences = self.__node.get_info('sequences')
+ if len(sequences) > 0:
+ self.__sequences_frame.bw_add(
+ self.__create_sequences_widget(sequences))
+
+ # operating system information widgets
+ self.__os = Gtk.Notebook()
+
+ os = self.__node.get_info('os')
+
+ if os is not None:
+
+ if 'matches' in os:
+
+ self.__match_scroll = BWScrolledWindow()
+
+ self.__match_store = Gtk.ListStore.new([str, str, int, bool])
+ self.__match_treeview = Gtk.TreeView.new_with_model(self.__match_store)
+
+ for os_match in os['matches']:
+
+ self.__match_store.append([os_match['accuracy'],
+ os_match['name'],
+ #os_match['db_line'],
+ 0, # unsupported
+ True])
+
+ self.__match_column = list()
+
+ for i in range(len(OSMATCH_HEADER)):
+
+ column = Gtk.TreeViewColumn(title=OSMATCH_HEADER[i],
+ cell_renderer=self.__cell,
+ text=i)
+
+ self.__match_column.append(column)
+
+ self.__match_column[i].set_reorderable(True)
+ self.__match_column[i].set_resizable(True)
+ self.__match_column[i].set_attributes(self.__cell,
+ text=i,
+ editable=3)
+
+ self.__match_column[i].set_sort_column_id(i)
+ self.__match_treeview.append_column(self.__match_column[i])
+
+ self.__match_scroll.add_with_viewport(self.__match_treeview)
+
+ self.__os.append_page(self.__match_scroll, BWLabel(_('Match')))
+
+ if 'classes' in os:
+
+ self.__class_scroll = BWScrolledWindow()
+
+ self.__class_store = Gtk.ListStore.new([str, str, str, str, str, bool])
+ self.__class_treeview = Gtk.TreeView.new_with_model(self.__class_store)
+
+ for os_class in os['classes']:
+
+ os_gen = os_class.get('os_gen', '')
+
+ self.__class_store.append([os_class['accuracy'],
+ os_class['vendor'],
+ os_class['type'],
+ os_class['os_family'],
+ os_gen,
+ True])
+
+ self.__class_column = list()
+
+ for i in range(len(OSCLASS_HEADER)):
+
+ column = Gtk.TreeViewColumn(title=OSCLASS_HEADER[i],
+ cell_renderer=self.__cell,
+ text=i)
+
+ self.__class_column.append(column)
+
+ self.__class_column[i].set_reorderable(True)
+ self.__class_column[i].set_resizable(True)
+ self.__class_column[i].set_attributes(self.__cell,
+ text=i,
+ editable=5)
+
+ self.__class_column[i].set_sort_column_id(i)
+ self.__class_treeview.append_column(self.__class_column[i])
+
+ self.__class_scroll.add_with_viewport(self.__class_treeview)
+
+ self.__os.append_page(self.__class_scroll, BWLabel(_('Class')))
+
+ self.__fp_viewer = BWTextEditor()
+ self.__fp_viewer.bw_modify_font(self.__font)
+ self.__fp_viewer.bw_set_editable(False)
+ self.__fp_viewer.bw_set_text(os['fingerprint'])
+
+ self.__fp_ports = BWHBox()
+ self.__fp_label = BWSectionLabel(_('Used ports:'))
+
+ self.__fp_ports_list = Gtk.ComboBoxText.new_with_entry()
+ self.__fp_ports_list.get_child().set_editable(False)
+
+ self.__fp_vbox = BWVBox()
+
+ if 'used_ports' in os:
+
+ used_ports = os['used_ports']
+
+ for port in used_ports:
+
+ params = port['id'], port['protocol'], port['state']
+ self.__fp_ports_list.append_text(USED_PORTS_TEXT % params)
+
+ self.__fp_ports_list.set_active(0)
+
+ self.__fp_ports.bw_pack_start_noexpand_nofill(self.__fp_label)
+ self.__fp_ports.bw_pack_start_expand_fill(self.__fp_ports_list)
+
+ self.__fp_vbox.bw_pack_start_noexpand_nofill(self.__fp_ports)
+
+ self.__os.append_page(self.__fp_viewer, BWLabel(_('Fingerprint')))
+ self.__fp_vbox.bw_pack_start_expand_fill(self.__os)
+
+ self.__os_frame.bw_add(self.__fp_vbox)
+ self.__os_frame.set_expanded(True)
+
+ self.__vbox.bw_pack_start_noexpand_nofill(self.__general_frame)
+ self.__vbox.bw_pack_start_expand_fill(self.__os_frame)
+ self.__vbox.bw_pack_start_noexpand_nofill(self.__sequences_frame)
+
+ self.add_with_viewport(self.__vbox)
+
+ def __create_sequences_widget(self, sequences):
+ """Return a widget representing various OS detection sequences. The
+ sequences argument is a dict with zero or more of the keys 'tcp',
+ 'ip_id', and 'tcp_ts'."""
+ # sequences information widgets
+ table = BWTable(5, 3)
+
+ table.attach(BWSectionLabel(_('Class')), 1, 2, 0, 1)
+ table.attach(BWSectionLabel(_('Values')), 2, 3, 0, 1)
+
+ table.attach(BWSectionLabel('TCP *'), 0, 1, 1, 2)
+ table.attach(BWSectionLabel('IP ID'), 0, 1, 2, 3)
+ table.attach(BWSectionLabel(_('TCP Timestamp')), 0, 1, 3, 4)
+
+ tcp = sequences.get('tcp')
+ if tcp is not None:
+ tcp_class = BWLabel(tcp['class'])
+ tcp_class.set_selectable(True)
+
+ table.attach(tcp_class, 1, 2, 1, 2)
+
+ tcp_values = Gtk.ComboBoxText.new_with_entry()
+
+ for value in tcp['values']:
+ tcp_values.append_text(value)
+
+ tcp_values.set_active(0)
+
+ table.attach(tcp_values, 2, 3, 1, 2)
+
+ tcp_note = BWLabel()
+ tcp_note.set_selectable(True)
+ tcp_note.set_line_wrap(False)
+ tcp_note.set_alignment(1.0, 0.5)
+ tcp_note.set_markup(
+ TCP_SEQ_NOTE % (tcp['index'], tcp['difficulty']))
+
+ table.attach(tcp_note, 0, 3, 4, 5)
+
+ ip_id = sequences.get('ip_id')
+ if ip_id is not None:
+ ip_id_class = BWLabel(ip_id['class'])
+ ip_id_class.set_selectable(True)
+
+ table.attach(ip_id_class, 1, 2, 2, 3)
+
+ ip_id_values = Gtk.ComboBoxText.new_with_entry()
+
+ for value in ip_id['values']:
+ ip_id_values.append_text(value)
+
+ ip_id_values.set_active(0)
+
+ table.attach(ip_id_values, 2, 3, 2, 3)
+
+ tcp_ts = sequences.get('tcp_ts')
+ if tcp_ts is not None:
+ tcp_ts_class = BWLabel(tcp_ts['class'])
+ tcp_ts_class.set_selectable(True)
+
+ table.attach(tcp_ts_class, 1, 2, 3, 4)
+
+ if tcp_ts['values'] is not None:
+
+ tcp_ts_values = Gtk.ComboBoxText.new_with_entry()
+
+ for value in tcp_ts['values']:
+ tcp_ts_values.append_text(value)
+
+ tcp_ts_values.set_active(0)
+
+ table.attach(tcp_ts_values, 2, 3, 3, 4)
+
+ return table
+
+
+class TraceroutePage(BWVBox):
+ """
+ """
+ def __init__(self, node):
+ """
+ """
+ BWVBox.__init__(self)
+ self.set_border_width(6)
+
+ self.__node = node
+
+ self.__create_widgets()
+
+ def __create_widgets(self):
+ """
+ """
+ trace = self.__node.get_info('trace')
+ hops = None
+ if trace is not None:
+ hops = trace.get("hops")
+ if hops is None or len(hops) == 0:
+
+ self.__trace_label = Gtk.Label.new(NO_TRACE_TEXT)
+ self.pack_start(self.__trace_label, True, True, 0)
+
+ else:
+
+ # add hops
+ hops = self.__node.get_info('trace')['hops']
+ ttls = [int(i['ttl']) for i in hops]
+
+ self.__cell = Gtk.CellRendererText()
+
+ self.__trace_scroll = BWScrolledWindow()
+ self.__trace_scroll.set_border_width(0)
+
+ self.__trace_store = Gtk.ListStore.new([int, str, str, str, str, bool])
+ self.__trace_treeview = Gtk.TreeView.new_with_model(self.__trace_store)
+
+ count = 0
+
+ for i in range(1, max(ttls) + 1):
+
+ if i in ttls:
+
+ hop = hops[count]
+ count += 1
+
+ self.__trace_store.append([hop['ttl'],
+ hop['rtt'],
+ hop['ip'],
+ hop['hostname'],
+ HOP_COLOR['known'],
+ True])
+
+ else:
+ self.__trace_store.append([i,
+ '',
+ _('<unknown>'),
+ '',
+ HOP_COLOR['unknown'],
+ True])
+
+ self.__trace_column = list()
+
+ for i in range(len(TRACE_HEADER)):
+
+ column = Gtk.TreeViewColumn(title=TRACE_HEADER[i],
+ cell_renderer=self.__cell,
+ text=i)
+
+ self.__trace_column.append(column)
+
+ self.__trace_column[i].set_reorderable(True)
+ self.__trace_column[i].set_resizable(True)
+ self.__trace_column[i].set_attributes(self.__cell,
+ text=i,
+ background=4,
+ editable=5)
+
+ self.__trace_treeview.append_column(self.__trace_column[i])
+
+ self.__trace_column[0].set_sort_column_id(0)
+
+ self.__trace_scroll.add_with_viewport(self.__trace_treeview)
+
+ self.__trace_info = (self.__node.get_info('trace')['port'],
+ self.__node.get_info('trace')['protocol'],
+ len(self.__node.get_info('trace')['hops']))
+
+ self.__trace_label = BWLabel(TRACE_TEXT % self.__trace_info)
+ self.__trace_label.set_use_markup(True)
+
+ self.bw_pack_start_expand_fill(self.__trace_scroll)
+ self.bw_pack_start_noexpand_nofill(self.__trace_label)
diff --git a/zenmap/radialnet/gui/NodeWindow.py b/zenmap/radialnet/gui/NodeWindow.py
new file mode 100644
index 0000000..7c38078
--- /dev/null
+++ b/zenmap/radialnet/gui/NodeWindow.py
@@ -0,0 +1,129 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk, Gdk, Pango
+
+import radialnet.util.drawing as drawing
+
+from radialnet.bestwidgets.windows import BWWindow
+from radialnet.bestwidgets.boxes import BWVBox, BWHBox
+from radialnet.bestwidgets.labels import BWSectionLabel
+from radialnet.gui.Image import Application
+from radialnet.gui.NodeNotebook import NodeNotebook
+
+
+DIMENSION_NORMAL = (600, 400)
+
+
+class NodeWindow(BWWindow):
+ """
+ """
+ def __init__(self, node, position):
+ """
+ """
+ BWWindow.__init__(self, Gtk.WindowType.TOPLEVEL)
+ self.move(position[0], position[1])
+ self.set_default_size(DIMENSION_NORMAL[0], DIMENSION_NORMAL[1])
+
+ self.__node = node
+
+ self.__title_font = Pango.FontDescription('Monospace Bold')
+
+ self.__icon = Application()
+ self.__create_widgets()
+
+ def __create_widgets(self):
+ """
+ """
+ self.__content = BWVBox()
+ self.__head = BWHBox(spacing=2)
+
+ self.__notebook = NodeNotebook(self.__node)
+
+ # create head elements
+
+ # icon with node's score color
+ self.__color_box = Gtk.EventBox()
+ self.__color_image = Gtk.Image()
+ self.__color_image.set_from_file(self.__icon.get_icon('border'))
+ self.__color_box.add(self.__color_image)
+ self.__color_box.set_size_request(15, 15)
+ r, g, b = drawing.cairo_to_gdk_color(
+ self.__node.get_draw_info('color'))
+ self.__color_box.modify_bg(Gtk.StateType.NORMAL, Gdk.Color(r, g, b))
+
+ # title with the node ip and hostname
+ self.__title = self.__node.get_host().get_hostname()
+
+ self.set_title(self.__title)
+
+ self.__title_label = BWSectionLabel(self.__title)
+ self.__title_label.modify_font(self.__title_font)
+
+ # packing head elements
+ self.__head.bw_pack_start_noexpand_nofill(self.__color_box)
+ self.__head.bw_pack_start_expand_fill(self.__title_label)
+
+ # packing all to content
+ self.__content.bw_pack_start_noexpand_nofill(self.__head)
+ self.__content.bw_pack_start_expand_fill(self.__notebook)
+
+ # add content to window
+ self.add(self.__content)
diff --git a/zenmap/radialnet/gui/RadialNet.py b/zenmap/radialnet/gui/RadialNet.py
new file mode 100644
index 0000000..1a395dd
--- /dev/null
+++ b/zenmap/radialnet/gui/RadialNet.py
@@ -0,0 +1,2020 @@
+# vim: set encoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk, GLib, Gdk
+
+import math
+import cairo
+
+from functools import reduce
+
+import radialnet.util.geometry as geometry
+import radialnet.util.misc as misc
+
+from radialnet.core.Coordinate import PolarCoordinate, CartesianCoordinate
+from radialnet.core.Interpolation import Linear2DInterpolator
+from radialnet.core.Graph import Node
+from radialnet.gui.NodeWindow import NodeWindow
+from radialnet.gui.Image import Icons, get_pixels_for_cairo_image_surface
+
+REGION_COLORS = [(1.0, 0.0, 0.0), (1.0, 1.0, 0.0), (0.0, 1.0, 0.0)]
+REGION_RED = 0
+REGION_YELLOW = 1
+REGION_GREEN = 2
+
+SQUARE_TYPES = ['router', 'switch', 'wap']
+
+ICON_DICT = {'router': 'router',
+ 'switch': 'switch',
+ 'wap': 'wireless',
+ 'firewall': 'firewall'}
+
+POINTER_JUMP_TO = 0
+POINTER_INFO = 1
+POINTER_GROUP = 2
+POINTER_FILL = 3
+
+LAYOUT_SYMMETRIC = 0
+LAYOUT_WEIGHTED = 1
+
+INTERPOLATION_CARTESIAN = 0
+INTERPOLATION_POLAR = 1
+
+FILE_TYPE_PDF = 1
+FILE_TYPE_PNG = 2
+FILE_TYPE_PS = 3
+FILE_TYPE_SVG = 4
+
+
+class RadialNet(Gtk.DrawingArea):
+ """
+ Radial network visualization widget
+ """
+ def __init__(self, layout=LAYOUT_SYMMETRIC):
+ """
+ Constructor method of RadialNet widget class
+ @type number_of_rings: number
+ @param number_of_rings: Number of rings in radial layout
+ """
+ self.__center_of_widget = (0, 0)
+ self.__graph = None
+
+ self.__number_of_rings = 0
+ self.__ring_gap = 30
+ self.__min_ring_gap = 10
+
+ self.__layout = layout
+ self.__interpolation = INTERPOLATION_POLAR
+ self.__interpolation_slow_in_out = True
+
+ self.__animating = False
+ self.__animation_rate = 1000 // 60 # 60Hz (human perception factor)
+ self.__number_of_frames = 60
+
+ self.__scale = 1.0
+ # rotated so that single-host traceroute doesn't have overlapping hosts
+ self.__rotate = 225
+ self.__translation = (0, 0)
+
+ self.__button1_press = False
+ self.__button2_press = False
+ self.__button3_press = False
+
+ self.__last_motion_point = None
+
+ self.__fisheye = False
+ self.__fisheye_ring = 0
+ self.__fisheye_spread = 0.5
+ self.__fisheye_interest = 2
+
+ self.__show_address = True
+ self.__show_hostname = True
+ self.__show_icon = True
+ self.__show_latency = False
+ self.__show_ring = True
+ self.__show_region = True
+ self.__region_color = REGION_RED
+
+ self.__node_views = dict()
+ self.__last_group_node = None
+
+ self.__pointer_status = POINTER_JUMP_TO
+
+ self.__sorted_nodes = list()
+
+ self.__icon = Icons()
+
+ super(RadialNet, self).__init__()
+
+ self.connect('draw', self.draw)
+ self.connect('button_press_event', self.button_press)
+ self.connect('button_release_event', self.button_release)
+ self.connect('motion_notify_event', self.motion_notify)
+ self.connect('enter_notify_event', self.enter_notify)
+ self.connect('leave_notify_event', self.leave_notify)
+ self.connect('key_press_event', self.key_press)
+ self.connect('key_release_event', self.key_release)
+ self.connect('scroll_event', self.scroll_event)
+
+ self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK |
+ Gdk.EventMask.BUTTON_RELEASE_MASK |
+ Gdk.EventMask.ENTER_NOTIFY_MASK |
+ Gdk.EventMask.LEAVE_NOTIFY_MASK |
+ Gdk.EventMask.KEY_PRESS_MASK |
+ Gdk.EventMask.KEY_RELEASE_MASK |
+ Gdk.EventMask.POINTER_MOTION_HINT_MASK |
+ Gdk.EventMask.POINTER_MOTION_MASK |
+ Gdk.EventMask.SCROLL_MASK)
+
+ self.set_can_focus(True)
+ self.grab_focus()
+
+ def graph_is_not_empty(function):
+ """
+ Decorator function to prevent the execution when graph not is set
+ @type function: function
+ @param function: Protected function
+ """
+ def check_graph_status(*args):
+ if args[0].__graph is None:
+ return False
+ return function(*args)
+
+ return check_graph_status
+
+ def not_is_in_animation(function):
+ """
+ Decorator function to prevent the execution when graph is animating
+ @type function: function
+ @param function: Protected function
+ """
+ def check_animation_status(*args):
+ if args[0].__animating:
+ return False
+ return function(*args)
+
+ return check_animation_status
+
+ def save_drawing_to_file(self, file, type=FILE_TYPE_PNG):
+ """
+ """
+ allocation = self.get_allocation()
+
+ if type == FILE_TYPE_PDF:
+ self.surface = cairo.PDFSurface(file,
+ allocation.width,
+ allocation.height)
+ elif type == FILE_TYPE_PNG:
+ self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32,
+ allocation.width,
+ allocation.height)
+ elif type == FILE_TYPE_PS:
+ self.surface = cairo.PSSurface(file,
+ allocation.width,
+ allocation.height)
+ elif type == FILE_TYPE_SVG:
+ self.surface = cairo.SVGSurface(file,
+ allocation.width,
+ allocation.height)
+ else:
+ raise TypeError('unknown surface type')
+
+ context = cairo.Context(self.surface)
+
+ context.rectangle(0, 0, allocation.width, allocation.height)
+ context.set_source_rgb(1.0, 1.0, 1.0)
+ context.fill()
+
+ self.__draw(context)
+
+ if type == FILE_TYPE_PNG:
+ self.surface.write_to_png(file)
+
+ self.surface.flush()
+ self.surface.finish()
+
+ return True
+
+ def get_slow_inout(self):
+ """
+ """
+ return self.__interpolation_slow_in_out
+
+ def set_slow_inout(self, value):
+ """
+ """
+ self.__interpolation_slow_in_out = value
+
+ def get_region_color(self):
+ """
+ """
+ return self.__region_color
+
+ def set_region_color(self, value):
+ """
+ """
+ self.__region_color = value
+
+ def get_show_region(self):
+ """
+ """
+ return self.__show_region
+
+ def set_show_region(self, value):
+ """
+ """
+ self.__show_region = value
+ self.queue_draw()
+
+ def get_pointer_status(self):
+ """
+ """
+ return self.__pointer_status
+
+ def set_pointer_status(self, pointer_status):
+ """
+ """
+ self.__pointer_status = pointer_status
+
+ def get_show_address(self):
+ """
+ """
+ return self.__show_address
+
+ def get_show_hostname(self):
+ """
+ """
+ return self.__show_hostname
+
+ def get_show_ring(self):
+ """
+ """
+ return self.__show_ring
+
+ def set_show_address(self, value):
+ """
+ """
+ self.__show_address = value
+ self.queue_draw()
+
+ def set_show_hostname(self, value):
+ """
+ """
+ self.__show_hostname = value
+ self.queue_draw()
+
+ def set_show_ring(self, value):
+ """
+ """
+ self.__show_ring = value
+ self.queue_draw()
+
+ def get_min_ring_gap(self):
+ """
+ """
+ return self.__min_ring_gap
+
+ @graph_is_not_empty
+ @not_is_in_animation
+ def set_min_ring_gap(self, value):
+ """
+ """
+ self.__min_ring_gap = int(value)
+
+ if self.__ring_gap < self.__min_ring_gap:
+ self.__ring_gap = self.__min_ring_gap
+
+ self.__update_nodes_positions()
+ self.queue_draw()
+
+ return True
+
+ def get_number_of_frames(self):
+ """
+ """
+ return self.__number_of_frames
+
+ @not_is_in_animation
+ def set_number_of_frames(self, number_of_frames):
+ """
+ """
+ if number_of_frames > 2:
+
+ self.__number_of_frames = int(number_of_frames)
+ return True
+
+ self.__number_of_frames = 3
+ return False
+
+ @not_is_in_animation
+ def update_layout(self):
+ """
+ """
+ if self.__graph is None:
+ return
+ self.__animating = True
+ self.__calc_interpolation(self.__graph.get_main_node())
+ self.__livens_up()
+
+ @not_is_in_animation
+ def set_layout(self, layout):
+ """
+ """
+ if self.__layout != layout:
+
+ self.__layout = layout
+
+ if self.__graph is not None:
+
+ self.__animating = True
+ self.__calc_interpolation(self.__graph.get_main_node())
+ self.__livens_up()
+
+ return True
+
+ return False
+
+ def get_layout(self):
+ """
+ """
+ return self.__layout
+
+ @not_is_in_animation
+ def set_interpolation(self, interpolation):
+ """
+ """
+ self.__interpolation = interpolation
+
+ return True
+
+ def get_interpolation(self):
+ """
+ """
+ return self.__interpolation
+
+ def get_number_of_rings(self):
+ """
+ """
+ return self.__number_of_rings
+
+ def get_fisheye_ring(self):
+ """
+ """
+ return self.__fisheye_ring
+
+ def get_fisheye_interest(self):
+ """
+ """
+ return self.__fisheye_interest
+
+ def get_fisheye_spread(self):
+ """
+ """
+ return self.__fisheye_spread
+
+ def get_fisheye(self):
+ """
+ """
+ return self.__fisheye
+
+ def set_fisheye(self, enable):
+ """
+ """
+ self.__fisheye = enable
+
+ self.__update_nodes_positions()
+ self.queue_draw()
+
+ def set_fisheye_ring(self, value):
+ """
+ """
+ self.__fisheye_ring = value
+ self.__check_fisheye_ring()
+
+ self.__update_nodes_positions()
+ self.queue_draw()
+
+ def set_fisheye_interest(self, value):
+ """
+ """
+ self.__fisheye_interest = value
+
+ self.__update_nodes_positions()
+ self.queue_draw()
+
+ def set_fisheye_spread(self, value):
+ """
+ """
+ self.__fisheye_spread = value
+
+ self.__update_nodes_positions()
+ self.queue_draw()
+
+ def get_show_icon(self):
+ """
+ """
+ return self.__show_icon
+
+ def set_show_icon(self, value):
+ """
+ """
+ self.__show_icon = value
+ self.queue_draw()
+
+ def get_show_latency(self):
+ """
+ """
+ return self.__show_latency
+
+ def set_show_latency(self, value):
+ """
+ """
+ self.__show_latency = value
+ self.queue_draw()
+
+ def get_scale(self):
+ """
+ """
+ return self.__scale
+
+ def get_zoom(self):
+ """
+ """
+ return int(round(self.__scale * 100))
+
+ def set_scale(self, scale):
+ """
+ """
+ if scale >= 0.01:
+
+ self.__scale = scale
+ self.queue_draw()
+
+ def set_zoom(self, zoom):
+ """
+ """
+ if float(zoom) >= 1:
+
+ self.set_scale(float(zoom) / 100.0)
+ self.queue_draw()
+
+ def get_ring_gap(self):
+ """
+ """
+ return self.__ring_gap
+
+ @not_is_in_animation
+ def set_ring_gap(self, ring_gap):
+ """
+ """
+ if ring_gap >= self.__min_ring_gap:
+
+ self.__ring_gap = ring_gap
+ self.__update_nodes_positions()
+ self.queue_draw()
+
+ def scroll_event(self, widget, event):
+ """
+ """
+ if event.direction == Gdk.ScrollDirection.UP:
+ self.set_scale(self.__scale + 0.01)
+
+ if event.direction == Gdk.ScrollDirection.DOWN:
+ self.set_scale(self.__scale - 0.01)
+
+ self.queue_draw()
+
+ @graph_is_not_empty
+ @not_is_in_animation
+ def key_press(self, widget, event):
+ """
+ """
+ key = Gdk.keyval_name(event.keyval)
+
+ if key == 'KP_Add':
+ self.set_ring_gap(self.__ring_gap + 1)
+
+ elif key == 'KP_Subtract':
+ self.set_ring_gap(self.__ring_gap - 1)
+
+ elif key == 'Page_Up':
+ self.set_scale(self.__scale + 0.01)
+
+ elif key == 'Page_Down':
+ self.set_scale(self.__scale - 0.01)
+
+ self.queue_draw()
+
+ return True
+
+ @graph_is_not_empty
+ def key_release(self, widget, event):
+ """
+ """
+ key = Gdk.keyval_name(event.keyval)
+
+ if key == 'c':
+ self.__translation = (0, 0)
+
+ elif key == 'r':
+ self.__show_ring = not self.__show_ring
+
+ elif key == 'a':
+ self.__show_address = not self.__show_address
+
+ elif key == 'h':
+ self.__show_hostname = not self.__show_hostname
+
+ elif key == 'i':
+ self.__show_icon = not self.__show_icon
+
+ elif key == 'l':
+ self.__show_latency = not self.__show_latency
+
+ self.queue_draw()
+
+ return True
+
+ @graph_is_not_empty
+ @not_is_in_animation
+ def enter_notify(self, widget, event):
+ """
+ """
+ self.grab_focus()
+ return False
+
+ @graph_is_not_empty
+ @not_is_in_animation
+ def leave_notify(self, widget, event):
+ """
+ """
+ for node in self.__graph.get_nodes():
+ node.set_draw_info({'over': False})
+
+ self.queue_draw()
+
+ return False
+
+ @graph_is_not_empty
+ def button_press(self, widget, event):
+ """
+ Drawing callback
+ @type widget: GtkWidget
+ @param widget: Gtk widget superclass
+ @type event: GtkEvent
+ @param event: Gtk event of widget
+ @rtype: boolean
+ @return: Indicator of the event propagation
+ """
+ result = self.__get_node_by_coordinate(self.get_pointer())
+
+ if event.button == 1:
+ self.__button1_press = True
+
+ # animate if node is pressed
+ if self.__pointer_status == POINTER_JUMP_TO and event.button == 1:
+
+ # prevent double animation
+ if self.__animating:
+ return False
+
+ if result is not None:
+
+ node, point = result
+ main_node = self.__graph.get_main_node()
+
+ if node != main_node:
+
+ if node.get_draw_info('group'):
+
+ node.set_draw_info({'group': False})
+ node.set_subtree_info({'grouped': False,
+ 'group_node': None})
+
+ self.__animating = True
+ self.__calc_interpolation(node)
+ self.__livens_up()
+
+ # group node if it's pressed
+ elif self.__pointer_status == POINTER_GROUP and event.button == 1:
+
+ # prevent group on animation
+ if self.__animating:
+ return False
+
+ if result is not None:
+
+ node, point = result
+ main_node = self.__graph.get_main_node()
+
+ if node != main_node:
+
+ if node.get_draw_info('group'):
+
+ node.set_draw_info({'group': False})
+ node.set_subtree_info({'grouped': False,
+ 'group_node': None})
+
+ else:
+
+ self.__last_group_node = node
+
+ node.set_draw_info({'group': True})
+ node.set_subtree_info({'grouped': True,
+ 'group_node': node})
+
+ self.__animating = True
+ self.__calc_interpolation(self.__graph.get_main_node())
+ self.__livens_up()
+
+ # setting to show node's region
+ elif self.__pointer_status == POINTER_FILL and event.button == 1:
+
+ if result is not None:
+
+ node, point = result
+
+ if node.get_draw_info('region') == self.__region_color:
+ node.set_draw_info({'region': None})
+
+ else:
+ node.set_draw_info({'region': self.__region_color})
+
+ self.queue_draw()
+
+ # show node details
+ elif event.button == 3 or self.__pointer_status == POINTER_INFO:
+
+ if event.button == 3:
+ self.__button3_press = True
+
+ if result is not None:
+
+ # first returned value is not meaningful and should be ignored
+ _, xw, yw = self.get_window().get_origin()
+ node, point = result
+ x, y = point
+
+ if node in self.__node_views.keys():
+
+ self.__node_views[node].present()
+
+ elif node.get_draw_info('scanned'):
+
+ view = NodeWindow(node, (int(xw + x), int(yw + y)))
+
+ def close_view(view, event, node):
+ view.destroy()
+ del self.__node_views[node]
+
+ view.connect("delete-event", close_view, node)
+ view.show_all()
+ self.__node_views[node] = view
+
+ return False
+
+ @graph_is_not_empty
+ def button_release(self, widget, event):
+ """
+ Drawing callback
+ @type widget: GtkWidget
+ @param widget: Gtk widget superclass
+ @type event: GtkEvent
+ @param event: Gtk event of widget
+ @rtype: boolean
+ @return: Indicator of the event propagation
+ """
+ if event.button == 1:
+ self.__button1_press = False
+
+ if event.button == 2:
+ self.__button2_press = False
+
+ if event.button == 3:
+ self.__button3_press = False
+
+ self.grab_focus()
+
+ return False
+
+ @graph_is_not_empty
+ def motion_notify(self, widget, event):
+ """
+ Drawing callback
+ @type widget: GtkWidget
+ @param widget: Gtk widget superclass
+ @type event: GtkEvent
+ @param event: Gtk event of widget
+ @rtype: boolean
+ @return: Indicator of the event propagation
+ """
+ pointer = self.get_pointer()
+
+ for node in self.__graph.get_nodes():
+ node.set_draw_info({'over': False})
+
+ result = self.__get_node_by_coordinate(self.get_pointer())
+
+ if result is not None:
+ result[0].set_draw_info({'over': True})
+
+ elif self.__button1_press and self.__last_motion_point is not None:
+
+ ax, ay = pointer
+ ox, oy = self.__last_motion_point
+ tx, ty = self.__translation
+
+ self.__translation = (tx + ax - ox, ty - ay + oy)
+
+ self.__last_motion_point = pointer
+
+ self.grab_focus()
+ self.queue_draw()
+
+ return False
+
+ def draw(self, widget, context):
+ """
+ Drawing callback
+ @type widget: GtkWidget
+ @param widget: Gtk widget superclass
+ @type context: cairo.Context
+ @param context: cairo context class
+ @rtype: boolean
+ @return: Indicator of the event propagation
+ """
+ context.set_source_rgb(1.0, 1.0, 1.0)
+ context.fill()
+
+ self.__draw(context)
+
+ return False
+
+ @graph_is_not_empty
+ def __draw(self, context):
+ """
+ Drawing method
+ """
+ # getting allocation reference
+ allocation = self.get_allocation()
+
+ self.__center_of_widget = (allocation.width // 2,
+ allocation.height // 2)
+
+ xc, yc = self.__center_of_widget
+
+ ax, ay = self.__translation
+
+ # xc = 320 yc = 240
+
+ # -1.5 | -0.5 ( 480, 360)
+ # -1.0 | 0.0 ( 320, 240)
+ # -0.5 | 0.5 ( 160, 120)
+ # 0.0 | 1.0 ( 0, 0)
+ # 0.5 | 1.5 (-160, -120)
+ # 1.0 | 2.0 (-320, -240)
+ # 1.5 | 2.5 (-480, -360)
+
+ # scaling and translate
+ factor = -(self.__scale - 1)
+
+ context.translate(xc * factor + ax, yc * factor - ay)
+
+ if self.__scale != 1.0:
+ context.scale(self.__scale, self.__scale)
+
+ # drawing over node's region
+ if self.__show_region and not self.__animating:
+
+ for node in self.__sorted_nodes:
+
+ not_grouped = not node.get_draw_info('grouped')
+
+ if node.get_draw_info('region') is not None and not_grouped:
+
+ xc, yc = self.__center_of_widget
+ r, g, b = REGION_COLORS[node.get_draw_info('region')]
+
+ start, final = node.get_draw_info('range')
+
+ i_radius = node.get_coordinate_radius()
+ f_radius = self.__calc_radius(self.__number_of_rings - 1)
+
+ is_fill_all = abs(final - start) == 360
+
+ final = math.radians(final + self.__rotate)
+ start = math.radians(start + self.__rotate)
+
+ context.move_to(xc, yc)
+ context.set_source_rgba(r, g, b, 0.1)
+ context.new_path()
+ context.arc(xc, yc, i_radius, -final, -start)
+ context.arc_negative(xc, yc, f_radius, -start, -final)
+ context.close_path()
+ context.fill()
+ context.stroke()
+
+ if not is_fill_all:
+
+ context.set_source_rgb(r, g, b)
+ context.set_line_width(1)
+
+ xa, ya = PolarCoordinate(
+ i_radius, final).to_cartesian()
+ xb, yb = PolarCoordinate(
+ f_radius, final).to_cartesian()
+
+ context.move_to(xc + xa, yc - ya)
+ context.line_to(xc + xb, yc - yb)
+ context.stroke()
+
+ xa, ya = PolarCoordinate(
+ i_radius, start).to_cartesian()
+ xb, yb = PolarCoordinate(
+ f_radius, start).to_cartesian()
+
+ context.move_to(xc + xa, yc - ya)
+ context.line_to(xc + xb, yc - yb)
+ context.stroke()
+
+ # drawing network rings
+ if self.__show_ring and not self.__animating:
+
+ for i in range(1, self.__number_of_rings):
+
+ radius = self.__calc_radius(i)
+
+ context.arc(xc, yc, radius, 0, 2 * math.pi)
+ context.set_source_rgb(0.8, 0.8, 0.8)
+ context.set_line_width(1)
+ context.stroke()
+
+ # drawing nodes and your connections
+ for edge in self.__graph.get_edges():
+
+ # check group constraints for edges
+ a, b = edge.get_nodes()
+
+ a_is_grouped = a.get_draw_info('grouped')
+ b_is_grouped = b.get_draw_info('grouped')
+
+ a_is_group = a.get_draw_info('group')
+ b_is_group = b.get_draw_info('group')
+
+ a_group = a.get_draw_info('group_node')
+ b_group = b.get_draw_info('group_node')
+
+ a_is_child = a in b.get_draw_info('children')
+ b_is_child = b in a.get_draw_info('children')
+
+ last_group = self.__last_group_node
+ groups = [a_group, b_group]
+
+ if last_group in groups and last_group is not None:
+ self.__draw_edge(context, edge)
+
+ elif not a_is_grouped or not b_is_grouped:
+
+ if not (a_is_group and b_is_child or
+ b_is_group and a_is_child):
+ self.__draw_edge(context, edge)
+
+ elif a_group != b_group:
+ self.__draw_edge(context, edge)
+
+ for node in reversed(self.__sorted_nodes):
+
+ # check group constraints for nodes
+ group = node.get_draw_info('group_node')
+ grouped = node.get_draw_info('grouped')
+
+ if group == self.__last_group_node or not grouped:
+ self.__draw_node(context, node)
+
+ def __draw_edge(self, context, edge):
+ """
+ Draw the connection between two nodes
+ @type : Edge
+ @param : The second node that will be connected
+ """
+ a, b = edge.get_nodes()
+
+ xa, ya = a.get_cartesian_coordinate()
+ xb, yb = b.get_cartesian_coordinate()
+ xc, yc = self.__center_of_widget
+
+ a_children = a.get_draw_info('children')
+ b_children = b.get_draw_info('children')
+
+ latency = edge.get_weights_mean()
+
+ # check if isn't an hierarchy connection
+ if a not in b_children and b not in a_children:
+ context.set_source_rgba(1.0, 0.6, 0.1, 0.8)
+
+ elif a.get_draw_info('no_route') or b.get_draw_info('no_route'):
+ context.set_source_rgba(0.0, 0.0, 0.0, 0.8)
+
+ else:
+ context.set_source_rgba(0.1, 0.5, 1.0, 0.8)
+
+ # calculating line thickness by latency
+ if latency is not None:
+
+ min = self.__graph.get_min_edge_mean_weight()
+ max = self.__graph.get_max_edge_mean_weight()
+
+ if max != min:
+ thickness = (latency - min) * 4 / (max - min) + 1
+
+ else:
+ thickness = 1
+
+ context.set_line_width(thickness)
+
+ else:
+
+ context.set_dash([2, 2])
+ context.set_line_width(1)
+
+ context.move_to(xc + xa, yc - ya)
+ context.line_to(xc + xb, yc - yb)
+ context.stroke()
+
+ context.set_dash([1, 0])
+
+ if not self.__animating and self.__show_latency:
+
+ if latency is not None:
+
+ context.set_font_size(8)
+ context.set_line_width(1)
+ context.move_to(xc + (xa + xb) / 2 + 1,
+ yc - (ya + yb) / 2 + 4)
+ context.show_text(str(round(latency, 2)))
+ context.stroke()
+
+ def __draw_node(self, context, node):
+ """
+ Draw nodes and your information
+ @type : NetNode
+ @param : The node to be drawn
+ """
+ x, y = node.get_cartesian_coordinate()
+ xc, yc = self.__center_of_widget
+ r, g, b = node.get_draw_info('color')
+ radius = node.get_draw_info('radius')
+
+ type = node.get_info('device_type')
+
+ x_gap = radius + 2
+ y_gap = 0
+
+ # draw group indication
+ if node.get_draw_info('group'):
+
+ x_gap += 5
+
+ if type in SQUARE_TYPES:
+ context.rectangle(xc + x - radius - 5,
+ yc - y - radius - 5,
+ 2 * radius + 10,
+ 2 * radius + 10)
+
+ else:
+ context.arc(xc + x, yc - y, radius + 5, 0, 2 * math.pi)
+
+ context.set_source_rgb(1.0, 1.0, 1.0)
+ context.fill_preserve()
+
+ if node.deep_search_child(self.__graph.get_main_node()):
+ context.set_source_rgb(0.0, 0.0, 0.0)
+
+ else:
+ context.set_source_rgb(0.1, 0.5, 1.0)
+
+ context.set_line_width(2)
+ context.stroke()
+
+ # draw over node
+ if node.get_draw_info('over'):
+
+ context.set_line_width(0)
+
+ if type in SQUARE_TYPES:
+ context.rectangle(xc + x - radius - 5,
+ yc - y - radius - 5,
+ 2 * radius + 10,
+ 2 * radius + 10)
+
+ else:
+ context.arc(xc + x, yc - y, radius + 5, 0, 2 * math.pi)
+
+ context.set_source_rgb(0.1, 0.5, 1.0)
+ context.fill_preserve()
+ context.stroke()
+
+ # draw node
+ if type in SQUARE_TYPES:
+ context.rectangle(xc + x - radius,
+ yc - y - radius,
+ 2 * radius,
+ 2 * radius)
+
+ else:
+ context.arc(xc + x, yc - y, radius, 0, 2 * math.pi)
+
+ # draw icons
+ if not self.__animating and self.__show_icon:
+
+ icons = list()
+
+ if type in ICON_DICT.keys():
+ icons.append(self.__icon.get_pixbuf(ICON_DICT[type]))
+
+ if node.get_info('filtered'):
+ icons.append(self.__icon.get_pixbuf('padlock'))
+
+ for icon in icons:
+
+ stride, data = get_pixels_for_cairo_image_surface(icon)
+
+ # Cairo documentation says that the correct way to obtain a
+ # legal stride value is using the function
+ # cairo.ImageSurface.format_stride_for_width().
+ # But this method is only available since cairo 1.6. So we are
+ # using the stride returned by
+ # get_pixels_for_cairo_image_surface() function.
+ surface = cairo.ImageSurface.create_for_data(data,
+ cairo.FORMAT_ARGB32,
+ icon.get_width(),
+ icon.get_height(),
+ stride)
+
+ context.set_source_surface(surface,
+ round(xc + x + x_gap),
+ round(yc - y + y_gap - 6))
+ context.paint()
+
+ x_gap += 13
+
+ # draw node text
+ context.set_source_rgb(r, g, b)
+ context.fill_preserve()
+
+ if node.get_draw_info('valid'):
+ context.set_source_rgb(0.0, 0.0, 0.0)
+
+ else:
+ context.set_source_rgb(0.1, 0.5, 1.0)
+
+ if not self.__animating and self.__show_address:
+
+ context.set_font_size(8)
+ context.move_to(round(xc + x + x_gap),
+ round(yc - y + y_gap + 4))
+
+ hostname = node.get_info('hostname')
+
+ if hostname is not None and self.__show_hostname:
+ context.show_text(hostname)
+
+ elif node.get_info('ip') is not None:
+ context.show_text(node.get_info('ip'))
+
+ context.set_line_width(1)
+ context.stroke()
+
+ def __check_fisheye_ring(self):
+ """
+ """
+ if self.__fisheye_ring >= self.__number_of_rings:
+ self.__fisheye_ring = self.__number_of_rings - 1
+
+ def __set_number_of_rings(self, value):
+ """
+ """
+ self.__number_of_rings = value
+ self.__check_fisheye_ring()
+
+ def __fisheye_function(self, ring):
+ """
+ """
+ distance = abs(self.__fisheye_ring - ring)
+ level_of_detail = self.__ring_gap * self.__fisheye_interest
+ spread_distance = distance - distance * self.__fisheye_spread
+
+ value = level_of_detail / (spread_distance + 1)
+
+ if value < self.__min_ring_gap:
+ value = self.__min_ring_gap
+
+ return value
+
+ @graph_is_not_empty
+ @not_is_in_animation
+ def __update_nodes_positions(self):
+ """
+ """
+ for node in self.__sorted_nodes:
+
+ if node.get_draw_info('grouped'):
+
+ # deep group check
+ group = node.get_draw_info('group_node')
+
+ while group.get_draw_info('group_node') is not None:
+ group = group.get_draw_info('group_node')
+
+ ring = group.get_draw_info('ring')
+ node.set_coordinate_radius(self.__calc_radius(ring))
+
+ else:
+ ring = node.get_draw_info('ring')
+ node.set_coordinate_radius(self.__calc_radius(ring))
+
+ @graph_is_not_empty
+ def __get_node_by_coordinate(self, point):
+ """
+ """
+ xc, yc = self.__center_of_widget
+
+ for node in self.__graph.get_nodes():
+
+ if node.get_draw_info('grouped'):
+ continue
+
+ ax, ay = self.__translation
+
+ xn, yn = node.get_cartesian_coordinate()
+ center = (xc + xn * self.__scale + ax, yc - yn * self.__scale - ay)
+ radius = node.get_draw_info('radius') * self.__scale
+
+ type = node.get_info('device_type')
+
+ if type in SQUARE_TYPES:
+ if geometry.is_in_square(point, radius, center):
+ return node, center
+
+ else:
+ if geometry.is_in_circle(point, radius, center):
+ return node, center
+
+ return None
+
+ def __calc_radius(self, ring):
+ """
+ """
+ if self.__fisheye:
+
+ radius = 0
+
+ while ring > 0:
+
+ radius += self.__fisheye_function(ring)
+ ring -= 1
+
+ else:
+ radius = ring * self.__ring_gap
+
+ return radius
+
+ @graph_is_not_empty
+ def __arrange_nodes(self):
+ """
+ """
+ new_nodes = set([self.__graph.get_main_node()])
+ old_nodes = set()
+
+ number_of_needed_rings = 1
+ ring = 0
+
+ # while new nodes were found
+ while len(new_nodes) > 0:
+
+ tmp_nodes = set()
+
+ # for each new nodes
+ for node in new_nodes:
+
+ old_nodes.add(node)
+
+ # set ring location
+ node.set_draw_info({'ring': ring})
+
+ # check group constraints
+ if (node.get_draw_info('group') or
+ node.get_draw_info('grouped')):
+ children = node.get_draw_info('children')
+
+ else:
+
+ # getting connections and fixing multiple fathers
+ children = set()
+ for child in self.__graph.get_node_connections(node):
+ if child in old_nodes or child in new_nodes:
+ continue
+ if child.get_draw_info('grouped'):
+ continue
+ children.add(child)
+
+ # setting father foreign
+ for child in children:
+ child.set_draw_info({'father': node})
+
+ node.set_draw_info(
+ {'children': misc.sort_children(children, node)})
+ tmp_nodes.update(children)
+
+ # check group influence in number of rings
+ for node in tmp_nodes:
+
+ if not node.get_draw_info('grouped'):
+
+ number_of_needed_rings += 1
+ break
+
+ # update new nodes set
+ new_nodes.update(tmp_nodes)
+ new_nodes.difference_update(old_nodes)
+
+ ring += 1
+
+ self.__set_number_of_rings(number_of_needed_rings)
+
+ def __weighted_layout(self):
+ """
+ """
+ # calculating the space needed by each node
+ self.__graph.get_main_node().set_draw_info({'range': (0, 360)})
+ new_nodes = set([self.__graph.get_main_node()])
+
+ self.__graph.get_main_node().calc_needed_space()
+
+ while len(new_nodes) > 0:
+
+ node = new_nodes.pop()
+
+ # add only no grouped nodes
+ children = set()
+ for child in node.get_draw_info('children'):
+
+ if not child.get_draw_info('grouped'):
+ children.add(child)
+ new_nodes.add(child)
+
+ if len(children) > 0:
+
+ min, max = node.get_draw_info('range')
+
+ node_total = max - min
+ children_need = node.get_draw_info('children_need')
+
+ for child in children:
+
+ child_need = child.get_draw_info('space_need')
+ child_total = node_total * child_need / children_need
+
+ theta = child_total / 2 + min + self.__rotate
+
+ child.set_coordinate_theta(theta)
+ child.set_draw_info({'range': (min, min + child_total)})
+
+ min += child_total
+
+ def __symmetric_layout(self):
+ """
+ """
+ self.__graph.get_main_node().set_draw_info({'range': (0, 360)})
+ new_nodes = set([self.__graph.get_main_node()])
+
+ while len(new_nodes) > 0:
+
+ node = new_nodes.pop()
+
+ # add only no grouped nodes
+ children = set()
+ for child in node.get_draw_info('children'):
+
+ if not child.get_draw_info('grouped'):
+ children.add(child)
+ new_nodes.add(child)
+
+ if len(children) > 0:
+
+ min, max = node.get_draw_info('range')
+ factor = float(max - min) / len(children)
+
+ for child in children:
+
+ theta = factor / 2 + min + self.__rotate
+
+ child.set_coordinate_theta(theta)
+ child.set_draw_info({'range': (min, min + factor)})
+
+ min += factor
+
+ @graph_is_not_empty
+ def __calc_layout(self, reference):
+ """
+ """
+ # selecting layout algorithm
+ if self.__layout == LAYOUT_SYMMETRIC:
+ self.__symmetric_layout()
+
+ elif self.__layout == LAYOUT_WEIGHTED:
+ self.__weighted_layout()
+
+ # rotating focus' children to keep orientation
+ if reference is not None:
+
+ father, angle = reference
+ theta = father.get_coordinate_theta()
+ factor = theta - angle
+
+ for node in self.__graph.get_nodes():
+
+ theta = node.get_coordinate_theta()
+ node.set_coordinate_theta(theta - factor)
+
+ a, b = node.get_draw_info('range')
+ node.set_draw_info({'range': (a - factor, b - factor)})
+
+ @graph_is_not_empty
+ def __calc_node_positions(self, reference=None):
+ """
+ """
+ # set nodes' hierarchy
+ self.__arrange_nodes()
+ self.calc_sorted_nodes()
+
+ # set nodes' coordinate radius
+ for node in self.__graph.get_nodes():
+
+ ring = node.get_draw_info('ring')
+ node.set_coordinate_radius(self.__calc_radius(ring))
+
+ # set nodes' coordinate theta
+ self.__calc_layout(reference)
+
+ def __calc_interpolation(self, focus):
+ """
+ """
+ old_main_node = self.__graph.get_main_node()
+ self.__graph.set_main_node(focus)
+
+ # getting initial coordinates
+ for node in self.__graph.get_nodes():
+
+ if self.__interpolation == INTERPOLATION_POLAR:
+ coordinate = node.get_polar_coordinate()
+
+ elif self.__interpolation == INTERPOLATION_CARTESIAN:
+ coordinate = node.get_cartesian_coordinate()
+
+ node.set_draw_info({'start_coordinate': coordinate})
+
+ father = focus.get_draw_info('father')
+
+ # calculate nodes positions (and father orientation)?
+ if father is not None:
+
+ xa, ya = father.get_cartesian_coordinate()
+ xb, yb = focus.get_cartesian_coordinate()
+
+ angle = math.atan2(yb - ya, xb - xa)
+ angle = math.degrees(angle)
+
+ self.__calc_node_positions((father, 180 + angle))
+
+ else:
+ self.__calc_node_positions()
+
+ # steps for slow-in/slow-out animation
+ steps = list(range(self.__number_of_frames))
+
+ for i in range(len(steps) // 2):
+ steps[self.__number_of_frames - 1 - i] = steps[i]
+
+ # normalize angles and calculate interpolated points
+ for node in self.__sorted_nodes:
+
+ l2di = Linear2DInterpolator()
+
+ # change grouped nodes coordinate
+ if node.get_draw_info('grouped'):
+
+ group_node = node.get_draw_info('group_node')
+ a, b = group_node.get_draw_info('final_coordinate')
+
+ if self.__interpolation == INTERPOLATION_POLAR:
+ node.set_polar_coordinate(a, b)
+
+ elif self.__interpolation == INTERPOLATION_CARTESIAN:
+ node.set_cartesian_coordinate(a, b)
+
+ # change interpolation method
+ if self.__interpolation == INTERPOLATION_POLAR:
+
+ coordinate = node.get_polar_coordinate()
+ node.set_draw_info({'final_coordinate': coordinate})
+
+ # adjusting polar coordinates
+ ri, ti = node.get_draw_info('start_coordinate')
+ rf, tf = node.get_draw_info('final_coordinate')
+
+ # normalization [0, 360]
+ ti = geometry.normalize_angle(ti)
+ tf = geometry.normalize_angle(tf)
+
+ # against longest path
+ ti, tf = geometry.calculate_short_path(ti, tf)
+
+ # main node goes direct to center (no arc)
+ if node == self.__graph.get_main_node():
+ tf = ti
+
+ # old main node goes direct to new position (no arc)
+ if node == old_main_node:
+ ti = tf
+
+ node.set_draw_info({'start_coordinate': (ri, ti)})
+ node.set_draw_info({'final_coordinate': (rf, tf)})
+
+ elif self.__interpolation == INTERPOLATION_CARTESIAN:
+
+ coordinate = node.get_cartesian_coordinate()
+ node.set_draw_info({'final_coordinate': coordinate})
+
+ # calculate interpolated points
+ ai, bi = node.get_draw_info('start_coordinate')
+ af, bf = node.get_draw_info('final_coordinate')
+
+ l2di.set_start_point(ai, bi)
+ l2di.set_final_point(af, bf)
+
+ if self.__interpolation_slow_in_out:
+ points = l2di.get_weighed_points(
+ self.__number_of_frames, steps)
+
+ else:
+ points = l2di.get_points(self.__number_of_frames)
+
+ node.set_draw_info({'interpolated_coordinate': points})
+
+ return True
+
+ def __livens_up(self, index=0):
+ """
+ """
+ if self.__graph is None:
+ # Bail out if the graph became empty during an animation.
+ self.__last_group_node = None
+ self.__animating = False
+ return False
+
+ # prepare interpolated points
+ if index == 0:
+
+ # prevent unnecessary animation
+ no_need_to_move = True
+
+ for node in self.__graph.get_nodes():
+
+ ai, bi = node.get_draw_info('start_coordinate')
+ af, bf = node.get_draw_info('final_coordinate')
+
+ start_c = round(ai), round(bi)
+ final_c = round(af), round(bf)
+
+ if start_c != final_c:
+ no_need_to_move = False
+
+ if no_need_to_move:
+
+ self.__animating = False
+ return False
+
+ # move all nodes for pass 'index'
+ for node in self.__graph.get_nodes():
+
+ a, b = node.get_draw_info('interpolated_coordinate')[index]
+
+ if self.__interpolation == INTERPOLATION_POLAR:
+ node.set_polar_coordinate(a, b)
+
+ elif self.__interpolation == INTERPOLATION_CARTESIAN:
+ node.set_cartesian_coordinate(a, b)
+
+ self.queue_draw()
+
+ # animation continue condition
+ if index < self.__number_of_frames - 1:
+ GLib.timeout_add(self.__animation_rate, # time to recall
+ self.__livens_up, # recursive call
+ index + 1) # next iteration
+ else:
+ self.__last_group_node = None
+ self.__animating = False
+
+ return False
+
+ @not_is_in_animation
+ def set_graph(self, graph):
+ """
+ Set graph to be displayed in layout
+ @type : Graph
+ @param : Set the graph used in visualization
+ """
+ if graph.get_number_of_nodes() > 0:
+
+ self.__graph = graph
+
+ self.__calc_node_positions()
+ self.queue_draw()
+
+ else:
+ self.__graph = None
+
+ def get_scanned_nodes(self):
+ """
+ """
+ nodes = list()
+ if self.__graph is None:
+ return nodes
+
+ for node in self.__graph.get_nodes():
+
+ if node.get_draw_info('scanned'):
+ nodes.append(node)
+
+ return nodes
+
+ def get_graph(self):
+ """
+ """
+ return self.__graph
+
+ def set_empty(self):
+ """
+ """
+ del(self.__graph)
+ self.__graph = None
+
+ self.queue_draw()
+
+ def get_rotation(self):
+ """
+ """
+ return self.__rotate
+
+ @graph_is_not_empty
+ def set_rotation(self, angle):
+ """
+ """
+ delta = angle - self.__rotate
+ self.__rotate = angle
+
+ for node in self.__graph.get_nodes():
+
+ theta = node.get_coordinate_theta()
+ node.set_coordinate_theta(theta + delta)
+
+ self.queue_draw()
+
+ def get_translation(self):
+ """
+ """
+ return self.__translation
+
+ @graph_is_not_empty
+ def set_translation(self, translation):
+ """
+ """
+ self.__translation = translation
+ self.queue_draw()
+
+ def is_empty(self):
+ """
+ """
+ return self.__graph is None
+
+ def is_in_animation(self):
+ """
+ """
+ return self.__animating
+
+ def calc_sorted_nodes(self):
+ """
+ """
+ self.__sorted_nodes = list(self.__graph.get_nodes())
+ self.__sorted_nodes.sort(key=lambda n: n.get_draw_info('ring'))
+
+
+class NetNode(Node):
+ """
+ Node class for radial network widget
+ """
+ def __init__(self):
+ """
+ """
+ self.__draw_info = dict()
+ """Hash with draw information"""
+ self.__coordinate = PolarCoordinate()
+
+ super(NetNode, self).__init__()
+
+ def get_host(self):
+ """
+ Set the HostInfo that this node represents
+ """
+ return self.get_data()
+
+ def set_host(self, host):
+ """
+ Set the HostInfo that this node represents
+ """
+ self.set_data(host)
+
+ def get_info(self, info):
+ """Return various information extracted from the host set with
+ set_host."""
+ host = self.get_data()
+ if host is not None:
+ if info == "number_of_open_ports":
+ return host.get_port_count_by_states(["open"])
+ elif info == "vulnerability_score":
+ num_open_ports = host.get_port_count_by_states(["open"])
+ if num_open_ports < 3:
+ return 0
+ elif num_open_ports < 7:
+ return 1
+ else:
+ return 2
+ elif info == "addresses":
+ addresses = []
+ if host.ip is not None:
+ addresses.append(host.ip)
+ if host.ipv6 is not None:
+ addresses.append(host.ipv6)
+ if host.mac is not None:
+ addresses.append(host.mac)
+ return addresses
+ elif info == "ip":
+ for addr in (host.ip, host.ipv6, host.mac):
+ if addr:
+ return addr.get("addr")
+ elif info == "hostnames":
+ hostnames = []
+ for hostname in host.hostnames:
+ copy = {}
+ copy["name"] = hostname.get("hostname", "")
+ copy["type"] = hostname.get("hostname_type", "")
+ hostnames.append(copy)
+ return hostnames
+ elif info == "hostname":
+ return host.get_hostname()
+ elif info == "uptime":
+ if host.uptime.get("seconds") or host.uptime.get("lastboot"):
+ return host.uptime
+ elif info == "device_type":
+ osmatch = host.get_best_osmatch()
+ if osmatch is None:
+ return None
+ osclasses = osmatch['osclasses']
+ if len(osclasses) == 0:
+ return None
+ types = ["router", "wap", "switch", "firewall"]
+ for type in types:
+ if type in osclasses[0].get("type", "").lower():
+ return type
+ elif info == "os":
+ os = {}
+
+ # osmatches
+ if len(host.osmatches) > 0 and \
+ host.osmatches[0]["accuracy"] != "" and \
+ host.osmatches[0]["name"] != "":
+ if os is None:
+ os = {}
+ os["matches"] = host.osmatches
+ os["matches"][0]["db_line"] = 0 # not supported
+
+ os_classes = []
+ for osclass in host.osmatches[0]["osclasses"]:
+ os_class = {}
+
+ os_class["type"] = osclass.get("type", "")
+ os_class["vendor"] = osclass.get("vendor", "")
+ os_class["accuracy"] = osclass.get("accuracy", "")
+ os_class["os_family"] = osclass.get("osfamily", "")
+ os_class["os_gen"] = osclass.get("osgen", "")
+
+ os_classes.append(os_class)
+ os["classes"] = os_classes
+
+ # ports_used
+ if len(host.ports_used) > 0:
+ if os is None:
+ os = {}
+ os_portsused = []
+
+ for portused in host.ports_used:
+ os_portused = {}
+
+ os_portused["state"] = portused.get("state", "")
+ os_portused["protocol"] = portused.get("proto", "")
+ os_portused["id"] = int(portused.get("portid", "0"))
+
+ os_portsused.append(os_portused)
+
+ os["used_ports"] = os_portsused
+
+ if len(os) > 0:
+ os["fingerprint"] = ""
+ return os
+ elif info == "sequences":
+ # getting sequences information
+ sequences = {}
+ # If all fields are empty, we don't put it into the sequences
+ # list
+ if reduce(lambda x, y: x + y,
+ host.tcpsequence.values(), "") != "":
+ tcp = {}
+ if host.tcpsequence.get("index", "") != "":
+ tcp["index"] = int(host.tcpsequence["index"])
+ else:
+ tcp["index"] = 0
+ tcp["class"] = "" # not supported
+ tcp["values"] = host.tcpsequence.get(
+ "values", "").split(",")
+ tcp["difficulty"] = host.tcpsequence.get("difficulty", "")
+ sequences["tcp"] = tcp
+ if reduce(lambda x, y: x + y,
+ host.ipidsequence.values(), "") != "":
+ ip_id = {}
+ ip_id["class"] = host.ipidsequence.get("class", "")
+ ip_id["values"] = host.ipidsequence.get(
+ "values", "").split(",")
+ sequences["ip_id"] = ip_id
+ if reduce(lambda x, y: x + y,
+ host.tcptssequence.values(), "") != "":
+ tcp_ts = {}
+ tcp_ts["class"] = host.tcptssequence.get("class", "")
+ tcp_ts["values"] = host.tcptssequence.get(
+ "values", "").split(",")
+ sequences["tcp_ts"] = tcp_ts
+ return sequences
+ elif info == "filtered":
+ if (len(host.extraports) > 0 and
+ host.extraports[0]["state"] == "filtered"):
+ return True
+ else:
+ for port in host.ports:
+ if port["port_state"] == "filtered":
+ return True
+ return False
+ elif info == "ports":
+ ports = list()
+ for host_port in host.ports:
+ port = dict()
+ state = dict()
+ service = dict()
+
+ port["id"] = int(host_port.get("portid", ""))
+ port["protocol"] = host_port.get("protocol", "")
+
+ state["state"] = host_port.get("port_state", "")
+ state["reason"] = "" # not supported
+ state["reason_ttl"] = "" # not supported
+ state["reason_ip"] = "" # not supported
+
+ service["name"] = host_port.get("service_name", "")
+ service["conf"] = host_port.get("service_conf", "")
+ service["method"] = host_port.get("service_method", "")
+ service["version"] = host_port.get("service_version", "")
+ service["product"] = host_port.get("service_product", "")
+ service["extrainfo"] = host_port.get(
+ "service_extrainfo", "")
+
+ port["state"] = state
+ port["scripts"] = None # not supported
+ port["service"] = service
+
+ ports.append(port)
+ return ports
+ elif info == "extraports":
+ # extraports
+ all_extraports = list()
+ for extraport in host.extraports:
+ extraports = dict()
+ extraports["count"] = int(extraport.get("count", ""))
+ extraports["state"] = extraport.get("state", "")
+ extraports["reason"] = list() # not supported
+ extraports["all_reason"] = list() # not supported
+
+ all_extraports.append(extraports)
+ return all_extraports
+ elif info == "trace":
+ # getting traceroute information
+ if len(host.trace) > 0:
+ trace = {}
+ hops = []
+
+ for host_hop in host.trace.get("hops", []):
+ hop = {}
+ hop["ip"] = host_hop.get("ipaddr", "")
+ hop["ttl"] = int(host_hop.get("ttl", ""))
+ hop["rtt"] = host_hop.get("rtt", "")
+ hop["hostname"] = host_hop.get("host", "")
+
+ hops.append(hop)
+
+ trace["hops"] = hops
+ trace["port"] = host.trace.get("port", "")
+ trace["protocol"] = host.trace.get("proto", "")
+
+ return trace
+ else: # host is None
+ pass
+
+ return None
+
+ def get_coordinate_theta(self):
+ """
+ """
+ return self.__coordinate.get_theta()
+
+ def get_coordinate_radius(self):
+ """
+ """
+ return self.__coordinate.get_radius()
+
+ def set_coordinate_theta(self, value):
+ """
+ """
+ self.__coordinate.set_theta(value)
+
+ def set_coordinate_radius(self, value):
+ """
+ """
+ self.__coordinate.set_radius(value)
+
+ def set_polar_coordinate(self, r, t):
+ """
+ Set polar coordinate
+ @type r: number
+ @param r: The radius of coordinate
+ @type t: number
+ @param t: The angle (theta) of coordinate in radians
+ """
+ self.__coordinate.set_coordinate(r, t)
+
+ def get_polar_coordinate(self):
+ """
+ Get cartesian coordinate
+ @rtype: tuple
+ @return: Cartesian coordinates (x, y)
+ """
+ return self.__coordinate.get_coordinate()
+
+ def set_cartesian_coordinate(self, x, y):
+ """
+ Set cartesian coordinate
+ """
+ cartesian = CartesianCoordinate(x, y)
+ r, t = cartesian.to_polar()
+
+ self.set_polar_coordinate(r, math.degrees(t))
+
+ def get_cartesian_coordinate(self):
+ """
+ Get cartesian coordinate
+ @rtype: tuple
+ @return: Cartesian coordinates (x, y)
+ """
+ return self.__coordinate.to_cartesian()
+
+ def get_draw_info(self, info=None):
+ """
+ Get draw information about node
+ @type : string
+ @param : Information name
+ @rtype: mixed
+ @return: The requested information
+ """
+ if info is None:
+ return self.__draw_info
+
+ return self.__draw_info.get(info)
+
+ def set_draw_info(self, info):
+ """
+ Set draw information
+ @type : dict
+ @param : Draw information dictionary
+ """
+ for key in info:
+ self.__draw_info[key] = info[key]
+
+ def deep_search_child(self, node):
+ """
+ """
+ for child in self.get_draw_info('children'):
+
+ if child == node:
+ return True
+
+ elif child.deep_search_child(node):
+ return True
+
+ return False
+
+ def set_subtree_info(self, info):
+ """
+ """
+ for child in self.get_draw_info('children'):
+
+ child.set_draw_info(info)
+
+ if not child.get_draw_info('group'):
+ child.set_subtree_info(info)
+
+ def calc_needed_space(self):
+ """
+ """
+ number_of_children = len(self.get_draw_info('children'))
+
+ sum_angle = 0
+ own_angle = 0
+
+ if number_of_children > 0 and not self.get_draw_info('group'):
+
+ for child in self.get_draw_info('children'):
+
+ child.calc_needed_space()
+ sum_angle += child.get_draw_info('space_need')
+
+ distance = self.get_coordinate_radius()
+ size = self.get_draw_info('radius') * 2
+ own_angle = geometry.angle_from_object(distance, size)
+
+ self.set_draw_info({'children_need': sum_angle})
+ self.set_draw_info({'space_need': max(sum_angle, own_angle)})
diff --git a/zenmap/radialnet/gui/SaveDialog.py b/zenmap/radialnet/gui/SaveDialog.py
new file mode 100644
index 0000000..60cfbbe
--- /dev/null
+++ b/zenmap/radialnet/gui/SaveDialog.py
@@ -0,0 +1,169 @@
+# vim: set encoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk
+
+import os.path
+import radialnet.gui.RadialNet as RadialNet
+import zenmapGUI.FileChoosers
+
+from zenmapGUI.higwidgets.higboxes import HIGHBox
+from zenmapGUI.higwidgets.higdialogs import HIGAlertDialog
+
+
+TYPES = ((_("By extension"), None, None),
+ ("PDF", RadialNet.FILE_TYPE_PDF, ".pdf"),
+ ("PNG", RadialNet.FILE_TYPE_PNG, ".png"),
+ ("PostScript", RadialNet.FILE_TYPE_PS, ".ps"),
+ ("SVG", RadialNet.FILE_TYPE_SVG, ".svg"))
+# Build a reverse index of extensions to file types, for the "By extension"
+# file type.
+EXTENSIONS = {}
+for type in TYPES:
+ if type[2] is not None:
+ EXTENSIONS[type[2]] = type[1]
+
+
+class SaveDialog(Gtk.FileChooserDialog):
+ def __init__(self):
+ """
+ """
+ super(SaveDialog, self).__init__(title=_("Save Topology"),
+ action=Gtk.FileChooserAction.SAVE,
+ buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
+ Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
+
+ types_store = Gtk.ListStore.new([str, object, str])
+ for type in TYPES:
+ types_store.append(type)
+
+ self.__combo = Gtk.ComboBox.new_with_model(types_store)
+ cell = Gtk.CellRendererText()
+ self.__combo.pack_start(cell, True)
+ self.__combo.add_attribute(cell, "text", 0)
+
+ self.__combo.connect("changed", self.__combo_changed_cb)
+ self.__combo.set_active(0)
+
+ self.connect("response", self.__response_cb)
+
+ hbox = HIGHBox()
+ label = Gtk.Label.new(_("Select File Type:"))
+ hbox.pack_end(self.__combo, False, True, 0)
+ hbox.pack_end(label, False, True, 0)
+
+ self.set_extra_widget(hbox)
+ self.set_do_overwrite_confirmation(True)
+
+ hbox.show_all()
+
+ def __combo_changed_cb(self, widget):
+ filename = self.get_filename() or ""
+ dir, basename = os.path.split(filename)
+ if dir != self.get_current_folder():
+ self.set_current_folder(dir)
+
+ # Find the recommended extension.
+ new_ext = self.__combo.get_model().get_value(
+ self.__combo.get_active_iter(), 2)
+ if new_ext is not None:
+ # Change the filename to use the recommended extension.
+ root, ext = os.path.splitext(basename)
+ if len(ext) == 0 and root.startswith("."):
+ root = ""
+ self.set_current_name(root + new_ext)
+
+ def __response_cb(self, widget, response_id):
+ """Intercept the "response" signal to check if someone used the "By
+ extension" file type with an unknown extension."""
+ if response_id == Gtk.ResponseType.OK and self.get_filetype() is None:
+ ext = self.__get_extension()
+ if ext == "":
+ filename = self.get_filename() or ""
+ dir, basename = os.path.split(filename)
+ alert = HIGAlertDialog(
+ message_format=_("No filename extension"),
+ secondary_text=_("""\
+The filename "%s" does not have an extension, \
+and no specific file type was chosen.
+Enter a known extension or select the file type from the list.""" % basename))
+
+ else:
+ alert = HIGAlertDialog(
+ message_format=_("Unknown filename extension"),
+ secondary_text=_("""\
+There is no file type known for the filename extension "%s".
+Enter a known extension or select the file type from the list.\
+""") % self.__get_extension())
+ alert.run()
+ alert.destroy()
+ # Go back to the dialog.
+ self.emit_stop_by_name("response")
+
+ def __get_extension(self):
+ return os.path.splitext(self.get_filename())[1]
+
+ def get_filetype(self):
+ filetype = self.__combo.get_model().get_value(
+ self.__combo.get_active_iter(), 1)
+ if filetype is None:
+ # Guess based on extension.
+ return EXTENSIONS.get(self.__get_extension())
+ return filetype
diff --git a/zenmap/radialnet/gui/Toolbar.py b/zenmap/radialnet/gui/Toolbar.py
new file mode 100644
index 0000000..23b7077
--- /dev/null
+++ b/zenmap/radialnet/gui/Toolbar.py
@@ -0,0 +1,309 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk
+
+from radialnet.bestwidgets.buttons import BWStockButton, BWToggleStockButton
+from radialnet.gui.SaveDialog import SaveDialog
+from radialnet.gui.Dialogs import AboutDialog
+from radialnet.gui.LegendWindow import LegendWindow
+from radialnet.gui.HostsViewer import HostsViewer
+from zenmapGUI.higwidgets.higdialogs import HIGAlertDialog
+
+
+SHOW = True
+HIDE = False
+
+REFRESH_RATE = 500
+
+
+class ToolsMenu(Gtk.Menu):
+ """
+ """
+ def __init__(self, radialnet):
+ """
+ """
+ Gtk.Menu.__init__(self)
+
+ self.radialnet = radialnet
+
+ self.__create_items()
+
+ def __create_items(self):
+ """
+ """
+ self.__hosts = Gtk.ImageMenuItem.new_with_label(_('Hosts viewer'))
+ self.__hosts.connect("activate", self.__hosts_viewer_callback)
+ self.__hosts_image = Gtk.Image()
+ self.__hosts_image.set_from_stock(Gtk.STOCK_INDEX, Gtk.IconSize.MENU)
+ self.__hosts.set_image(self.__hosts_image)
+
+ self.append(self.__hosts)
+
+ self.__hosts.show_all()
+
+ def __hosts_viewer_callback(self, widget):
+ """
+ """
+ window = HostsViewer(self.radialnet.get_scanned_nodes())
+ window.show_all()
+ window.set_keep_above(True)
+
+ def enable_dependents(self):
+ """
+ """
+ self.__hosts.set_sensitive(True)
+
+ def disable_dependents(self):
+ """
+ """
+ self.__hosts.set_sensitive(False)
+
+
+class Toolbar(Gtk.Box):
+ """
+ """
+ def __init__(self, radialnet, window, control, fisheye):
+ """
+ """
+ Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL)
+ #self.set_style(gtk.TOOLBAR_BOTH_HORIZ)
+ #self.set_tooltips(True)
+
+ self.radialnet = radialnet
+
+ self.__window = window
+ self.__control_widget = control
+ self.__fisheye_widget = fisheye
+
+ self.__control_widget.show_all()
+ self.__control_widget.set_no_show_all(True)
+ self.__control_widget.hide()
+
+ self.__fisheye_widget.show_all()
+ self.__fisheye_widget.set_no_show_all(True)
+ self.__fisheye_widget.hide()
+
+ self.__save_chooser = None
+
+ self.__create_widgets()
+
+ def __create_widgets(self):
+ """
+ """
+ # self.__tooltips = gtk.Tooltips()
+
+ #self.__tools_menu = ToolsMenu(self.radialnet)
+
+ #self.__tools_button = gtk.MenuToolButton(gtk.STOCK_PREFERENCES)
+ #self.__tools_button.set_label(_('Tools'))
+ #self.__tools_button.set_is_important(True)
+ #self.__tools_button.set_menu(self.__tools_menu)
+ #self.__tools_button.connect('clicked', self.__tools_callback)
+
+ self.__save_button = BWStockButton(Gtk.STOCK_SAVE, _("Save Graphic"))
+ self.__save_button.connect("clicked", self.__save_image_callback)
+
+ self.__hosts_button = BWStockButton(Gtk.STOCK_INDEX, _("Hosts Viewer"))
+ self.__hosts_button.connect("clicked", self.__hosts_viewer_callback)
+
+ self.__control = BWToggleStockButton(
+ Gtk.STOCK_PROPERTIES, _("Controls"))
+ self.__control.connect('clicked', self.__control_callback)
+ self.__control.set_active(False)
+
+ self.__fisheye = BWToggleStockButton(Gtk.STOCK_ZOOM_FIT, _("Fisheye"))
+ self.__fisheye.connect('clicked', self.__fisheye_callback)
+ self.__fisheye.set_active(False)
+
+ self.__legend_button = BWStockButton(Gtk.STOCK_INDEX, _("Legend"))
+ self.__legend_button.connect('clicked', self.__legend_callback)
+
+ #self.__fullscreen = gtk.ToggleToolButton(gtk.STOCK_FULLSCREEN)
+ #self.__fullscreen.set_label(_('Fullscreen'))
+ #self.__fullscreen.set_is_important(True)
+ #self.__fullscreen.connect('clicked', self.__fullscreen_callback)
+ #self.__fullscreen.set_tooltip(self.__tooltips, _('Toggle fullscreen'))
+
+ #self.__about = gtk.ToolButton(gtk.STOCK_ABOUT)
+ #self.__about.set_label(_('About'))
+ #self.__about.set_is_important(True)
+ #self.__about.connect('clicked', self.__about_callback)
+ #self.__about.set_tooltip(self.__tooltips, _('About RadialNet'))
+
+ self.__separator = Gtk.SeparatorToolItem()
+ self.__expander = Gtk.SeparatorToolItem()
+ self.__expander.set_expand(True)
+ self.__expander.set_draw(False)
+
+ #self.insert(self.__open, 0)
+ #self.insert(self.__separator, 1)
+ #self.insert(self.__tools_button, 2)
+ #self.insert(self.__expander, 3)
+ #self.insert(self.__control, 4)
+ #self.insert(self.__fisheye, 5)
+ #self.insert(self.__fullscreen, 6)
+ #self.insert(self.__about, 7)
+
+ #self.pack_start(self.__tools_button, False)
+ self.pack_start(self.__hosts_button, False, True, 0)
+ self.pack_start(self.__fisheye, False, True, 0)
+ self.pack_start(self.__control, False, True, 0)
+ self.pack_end(self.__save_button, False, True, 0)
+ self.pack_end(self.__legend_button, False, True, 0)
+
+ def disable_controls(self):
+ """
+ """
+ self.__control.set_sensitive(False)
+ self.__fisheye.set_sensitive(False)
+ self.__hosts_button.set_sensitive(False)
+ self.__legend_button.set_sensitive(False)
+ #self.__tools_menu.disable_dependents()
+
+ def enable_controls(self):
+ """
+ """
+ self.__control.set_sensitive(True)
+ self.__fisheye.set_sensitive(True)
+ self.__hosts_button.set_sensitive(True)
+ self.__legend_button.set_sensitive(True)
+ #self.__tools_menu.enable_dependents()
+
+ def __tools_callback(self, widget):
+ """
+ """
+ self.__tools_menu.popup(None, None, None, 1, 0)
+
+ def __hosts_viewer_callback(self, widget):
+ """
+ """
+ window = HostsViewer(self.radialnet.get_scanned_nodes())
+ window.show_all()
+ window.set_keep_above(True)
+
+ def __save_image_callback(self, widget):
+ """
+ """
+ if self.__save_chooser is None:
+ self.__save_chooser = SaveDialog()
+
+ response = self.__save_chooser.run()
+
+ if response == Gtk.ResponseType.OK:
+ filename = self.__save_chooser.get_filename()
+ filetype = self.__save_chooser.get_filetype()
+
+ try:
+ self.radialnet.save_drawing_to_file(filename, filetype)
+ except Exception as e:
+ alert = HIGAlertDialog(parent=self.__save_chooser,
+ type=Gtk.MessageType.ERROR,
+ message_format=_("Error saving snapshot"),
+ secondary_text=str(e))
+ alert.run()
+ alert.destroy()
+
+ self.__save_chooser.hide()
+
+ def __control_callback(self, widget=None):
+ """
+ """
+ if self.__control.get_active():
+ self.__control_widget.show()
+
+ else:
+ self.__control_widget.hide()
+
+ def __fisheye_callback(self, widget=None):
+ """
+ """
+ if not self.radialnet.is_in_animation():
+
+ if self.__fisheye.get_active():
+
+ self.__fisheye_widget.active_fisheye()
+ self.__fisheye_widget.show()
+
+ else:
+
+ self.__fisheye_widget.deactive_fisheye()
+ self.__fisheye_widget.hide()
+
+ def __legend_callback(self, widget):
+ """
+ """
+ self.__legend_window = LegendWindow()
+ self.__legend_window.show_all()
+
+ def __about_callback(self, widget):
+ """
+ """
+ self.__about_dialog = AboutDialog()
+ self.__about_dialog.show_all()
+
+ def __fullscreen_callback(self, widget=None):
+ """
+ """
+ if self.__fullscreen.get_active():
+ self.__window.fullscreen()
+
+ else:
+ self.__window.unfullscreen()
diff --git a/zenmap/radialnet/gui/__init__.py b/zenmap/radialnet/gui/__init__.py
new file mode 100644
index 0000000..c314dd7
--- /dev/null
+++ b/zenmap/radialnet/gui/__init__.py
@@ -0,0 +1,56 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
diff --git a/zenmap/radialnet/radialnet.pyw b/zenmap/radialnet/radialnet.pyw
new file mode 100755
index 0000000..ff15924
--- /dev/null
+++ b/zenmap/radialnet/radialnet.pyw
@@ -0,0 +1,116 @@
+#!/usr/bin/env python3
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import os
+import sys
+
+from core.ArgvHandle import ArgvHandle
+from gui.Application import Application
+
+
+USAGE = """\
+Description: Show RadialNet application from Nmap XML file. It's better if Nmap
+XML input file has traceroute information. If you don't know what is this, see
+Nmap documentation. You can pass the Nmap xml file by command line or open it
+later.
+
+Usage: radialnet.py [options]
+
+Options:
+
+ -h, --help Show this help text.
+ -f <file> Use <file> as Nmap XML input.
+
+Suggestions or bug reports can be send to ignotus21_at_gmail_dot_com.\
+"""
+
+# check for psyco
+try:
+
+ import psyco
+ psyco.full()
+
+except ImportError:
+ print 'Running without psyco (http://psyco.sourceforge.net/).'
+
+
+
+if __name__ == '__main__':
+
+ argvh = ArgvHandle(sys.argv)
+
+ if argvh.has_option('-h') or argvh.has_option('--help'):
+
+ print USAGE
+ sys.exit(0)
+
+ application = Application()
+
+ if argvh.has_option('-f'):
+
+ file = argvh.get_option('-f')
+
+ if file is None:
+
+ print USAGE
+ sys.exit(0)
+
+ else:
+ application.parse_nmap_xml_file(os.path.abspath(file))
+
+ application.start()
diff --git a/zenmap/radialnet/share/sample/nmap_example.xml b/zenmap/radialnet/share/sample/nmap_example.xml
new file mode 100644
index 0000000..5e689e8
--- /dev/null
+++ b/zenmap/radialnet/share/sample/nmap_example.xml
@@ -0,0 +1,480 @@
+<?xml version="1.0" ?>
+<?xml-stylesheet href="/opt/nmap/share/nmap/nmap.xsl" type="text/xsl"?>
+<!-- Nmap 4.53 scan initiated Sun Jan 27 21:10:02 2008 as: nmap -A -v -oX sample-03.xml freshmeat.net sourceforge.net nmap.org kernel.org openbsd.org netbsd.org google.com gmail.com -->
+<nmaprun scanner="nmap" args="nmap -A -v -oX sample-03.xml freshmeat.net sourceforge.net nmap.org kernel.org openbsd.org netbsd.org google.com gmail.com" start="1201479002" startstr="Sun Jan 27 21:10:02 2008" version="4.53" xmloutputversion="1.01">
+<scaninfo type="syn" protocol="tcp" numservices="1714" services="1-1027,1029-1033,1040,1043,1050,1058-1059,1067-1068,1076,1080,1083-1084,1103,1109-1110,1112,1127,1139,1155,1158,1178,1212,1214,1220,1222,1234,1241,1248,1270,1337,1346-1381,1383-1552,1600,1650-1652,1661-1672,1680,1720,1723,1755,1761-1764,1827,1900,1935,1984,1986-2028,2030,2032-2035,2038,2040-2049,2053,2064-2065,2067-2068,2105-2106,2108,2111-2112,2120-2121,2201,2232,2241,2301,2307,2401,2430-2433,2500-2501,2564,2600-2605,2627-2628,2638,2766,2784,2809,2903,2998,3000-3001,3005-3006,3025,3045,3049,3052,3064,3086,3128,3141,3264,3268-3269,3292,3299,3306,3333,3372,3389,3397-3399,3421,3455-3457,3462,3531,3632,3689,3900,3984-3986,3999-4000,4002,4008,4045,4125,4132-4133,4144,4199,4224,4321,4333,4343,4444,4480,4500,4557,4559,4660,4662,4672,4899,4987,4998,5000-5003,5009-5011,5050,5060,5100-5102,5145,5190-5193,5232,5236,5300-5305,5308,5400,5405,5432,5490,5500,5510,5520,5530,5540,5550,5555,5560,5631-5632,5679-5680,5713-5717,5800-5803,5900-5903,5977-5979,5997-6009,6017,6050,6101,6103,6105-6106,6110-6112,6141-6148,6222,6346-6347,6400-6401,6502,6543-6544,6547-6548,6558,6588,6662,6665-6670,6699-6701,6881,6969,7000-7010,7070,7100,7200-7201,7273,7326,7464,7597,7937-7938,8000,8007,8009,8021,8076,8080-8082,8118,8123,8443,8770,8888,8892,9040,9050-9051,9090,9100-9107,9111,9152,9535,9876,9991-9992,9999-10000,10005,10082-10083,11371,12000,12345-12346,13701-13702,13705-13706,13708-13718,13720-13722,13782-13783,14141,15126,15151,16080,16444,16959,17007,17300,18000,18181-18185,18187,19150,20005,22273,22289,22305,22321,22370,26208,27000-27010,27374,27665,31337,31416,32770-32780,32786-32787,38037,38292,43188,44334,44442-44443,47557,49400,50000,50002,54320,61439-61441,65301" />
+<verbose level="1" />
+<debugging level="0" />
+<taskbegin task="Ping Scan" time="1201479013" />
+<taskend task="Ping Scan" time="1201479014" extrainfo="8 total hosts" />
+<taskbegin task="Parallel DNS resolution of 8 hosts." time="1201479014" />
+<taskend task="Parallel DNS resolution of 8 hosts." time="1201479015" />
+<taskbegin task="System CNAME DNS resolution of 4 hosts." time="1201479015" />
+<taskend task="System CNAME DNS resolution of 4 hosts." time="1201479016" />
+<taskbegin task="SYN Stealth Scan" time="1201479016" />
+<taskprogress task="SYN Stealth Scan" time="1201479046" percent="3.22" remaining="903" etc="1201479949" />
+<taskprogress task="SYN Stealth Scan" time="1201479442" percent="56.66" remaining="325" etc="1201479767" />
+<taskprogress task="SYN Stealth Scan" time="1201479770" percent="77.02" remaining="225" etc="1201479995" />
+<taskprogress task="SYN Stealth Scan" time="1201479996" percent="81.95" remaining="215" etc="1201480212" />
+<taskprogress task="SYN Stealth Scan" time="1201480213" percent="86.79" remaining="182" etc="1201480395" />
+<taskprogress task="SYN Stealth Scan" time="1201480260" percent="87.84" remaining="172" etc="1201480433" />
+<taskprogress task="SYN Stealth Scan" time="1201480435" percent="91.65" remaining="129" etc="1201480564" />
+<taskprogress task="SYN Stealth Scan" time="1201480565" percent="94.43" remaining="91" etc="1201480656" />
+<taskprogress task="SYN Stealth Scan" time="1201480658" percent="96.35" remaining="62" etc="1201480720" />
+<taskprogress task="SYN Stealth Scan" time="1201480721" percent="97.76" remaining="39" etc="1201480760" />
+<taskend task="SYN Stealth Scan" time="1201480878" extrainfo="8570 total ports" />
+<taskbegin task="Service scan" time="1201480879" />
+<taskend task="Service scan" time="1201480984" extrainfo="20 services on 5 hosts" />
+<taskbegin task="Traceroute" time="1201481006" />
+<taskend task="Traceroute" time="1201481028" />
+<taskbegin task="Traceroute" time="1201481028" />
+<taskend task="Traceroute" time="1201481059" />
+<taskbegin task="Parallel DNS resolution of 85 hosts." time="1201481059" />
+<taskend task="Parallel DNS resolution of 85 hosts." time="1201481070" />
+<taskbegin task="System CNAME DNS resolution of 8 hosts." time="1201481070" />
+<taskend task="System CNAME DNS resolution of 8 hosts." time="1201481086" />
+<taskbegin task="SCRIPT ENGINE" time="1201481086" />
+<taskend task="SCRIPT ENGINE" time="1201481197" />
+<host><status state="up" reason="reset"/>
+<address addr="66.35.250.168" addrtype="ipv4" />
+<hostnames><hostname name="freshmeat.net" type="PTR" /></hostnames>
+<ports><extraports state="filtered" count="1712">
+<extrareasons reason="host-prohibiteds" count="1712"/>
+</extraports>
+<port protocol="tcp" portid="80"><state state="open" reason="syn-ack" reason_ttl="45"/><service name="http" product="Apache httpd" version="1.3.39" extrainfo="(Unix) PHP/4.4.7" method="probed" conf="10" /><script id="robots.txt" output="User-Agent: * /img/ /redir/ " /><script id="HTML title" output="freshmeat.net: Welcome to freshmeat.net" /></port>
+<port protocol="tcp" portid="443"><state state="closed" reason="reset" reason_ttl="46"/><service name="https" method="table" conf="3" /></port>
+</ports>
+<os><portused state="open" proto="tcp" portid="80" />
+<portused state="closed" proto="tcp" portid="443" />
+<osclass type="software router" vendor="MikroTik" osfamily="RouterOS" osgen="2.X" accuracy="94" />
+<osclass type="WAP" vendor="Linksys" osfamily="Linux" osgen="2.4.X" accuracy="94" />
+<osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="2.4.X" accuracy="94" />
+<osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="2.6.X" accuracy="94" />
+<osclass type="VoIP phone" vendor="WebVOIZE" osfamily="embedded" accuracy="94" />
+<osclass type="WAP" vendor="D-Link" osfamily="Linux" osgen="2.4.X" accuracy="91" />
+<osclass type="WAP" vendor="Inventel" osfamily="embedded" accuracy="91" />
+<osclass type="broadband router" vendor="USRobotics" osfamily="embedded" accuracy="91" />
+<osclass type="broadband router" vendor="Linux" osfamily="Linux" osgen="2.4.X" accuracy="91" />
+<osclass type="WAP" vendor="Linux" osfamily="Linux" osgen="2.4.X" accuracy="91" />
+<osclass type="media device" vendor="Linux" osfamily="Linux" osgen="2.4.X" accuracy="91" />
+<osclass type="VoIP gateway" vendor="Linux" osfamily="Linux" osgen="2.4.X" accuracy="91" />
+<osclass type="WAP" vendor="Netgear" osfamily="embedded" accuracy="91" />
+<osclass type="switch" vendor="QLogic" osfamily="embedded" accuracy="91" />
+<osclass type="PDA" vendor="Sharp" osfamily="Linux" osgen="2.4.X" accuracy="91" />
+<osclass type="WAP" vendor="FON" osfamily="Linux" osgen="2.6.X" accuracy="91" />
+<osclass type="WAP" vendor="FON" osfamily="Linux" osgen="2.4.X" accuracy="90" />
+<osclass type="WAP" vendor="Belkin" osfamily="embedded" accuracy="90" />
+<osclass type="WAP" vendor="Asus" osfamily="embedded" accuracy="90" />
+<osclass type="WAP" vendor="Netgear" osfamily="Linux" osgen="2.4.X" accuracy="90" />
+<osclass type="printer" vendor="Xerox" osfamily="embedded" accuracy="90" />
+<osclass type="security-misc" vendor="Aladdin" osfamily="Linux" osgen="2.4.X" accuracy="89" />
+<osclass type="VoIP gateway" vendor="Occam" osfamily="embedded" accuracy="89" />
+<osclass type="media device" vendor="Roku" osfamily="embedded" accuracy="89" />
+<osclass type="WAP" vendor="Siemens" osfamily="Linux" accuracy="89" />
+<osclass type="broadband router" vendor="3Com" osfamily="Linux" osgen="2.4.X" accuracy="89" />
+<osclass type="media device" vendor="Dream Multimedia" osfamily="Linux" osgen="2.6.X" accuracy="89" />
+<osclass type="storage-misc" vendor="Iomega" osfamily="Linux" osgen="2.6.X" accuracy="89" />
+<osmatch name="MicroTik RouterOS 2.9.46" accuracy="94" line="14788"/>
+<osmatch name="Linksys WRT54GS WAP (Linux kernel)" accuracy="94" line="8292"/>
+<osmatch name="Linux 2.4.18 - 2.4.32 (likely embedded)" accuracy="94" line="8499"/>
+<osmatch name="Linux 2.4.21 - 2.4.33" accuracy="94" line="8624"/>
+<osmatch name="Linux 2.4.27" accuracy="94" line="8675"/>
+<osmatch name="Linux 2.4.28 - 2.4.30" accuracy="94" line="8693"/>
+<osmatch name="Linux 2.6.5 - 2.6.18" accuracy="94" line="11411"/>
+<osmatch name="Linux 2.6.8" accuracy="94" line="11485"/>
+<osmatch name="WebVOIZE 120 IP phone" accuracy="94" line="18921"/>
+<osmatch name="Linux 2.4.2 (Red Hat 7.1)" accuracy="91" line="8533"/>
+<osfingerprint fingerprint="SCAN(V=4.53%D=1/27%OT=80%CT=443%CU=%PV=N%G=N%TM=479D25ED%P=i686-pc-linux-gnu)&#xa;SEQ(SP=F2%GCD=1%ISR=E9%TI=Z%TS=1C)&#xa;OPS(O1=M5B4ST11NW0%O2=M5B4ST11NW0%O3=M5B4NNT11NW0%O4=M5B4ST11NW0%O5=M5B4ST11NW0%O6=M5B4ST11)&#xa;WIN(W1=16A0%W2=16A0%W3=16A0%W4=16A0%W5=16A0%W6=16A0)&#xa;ECN(R=Y%DF=Y%TG=40%W=16D0%O=M5B4NNSNW0%CC=N%Q=)&#xa;T1(R=Y%DF=Y%TG=40%S=O%A=S+%F=AS%RD=0%Q=)&#xa;T2(R=N)&#xa;T3(R=Y%DF=Y%TG=40%W=16A0%S=O%A=S+%F=AS%O=M5B4ST11NW0%RD=0%Q=)&#xa;T4(R=Y%DF=Y%TG=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)&#xa;T5(R=Y%DF=Y%TG=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)&#xa;T6(R=Y%DF=Y%TG=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)&#xa;T7(R=Y%DF=Y%TG=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)&#xa;U1(R=N)&#xa;IE(R=N)&#xa;" />
+</os>
+<uptime seconds="206" lastboot="Sun Jan 27 21:43:11 2008" />
+<tcpsequence index="242" class="unknown class" difficulty="Good luck!" values="457B276,4584FC8,161C122C,161B185F,1605EA95,1614C498" />
+<ipidsequence class="All zeros" values="0,0,0,0,0,0" />
+<tcptssequence class="other" values="3FB03AA9,3FB03C75,45B26360,45B2636A,45B26374,45B2637E" />
+<trace port="80" proto="tcp">
+<hop ttl="1" rtt="1.83" ipaddr="192.168.254.254"/>
+<hop ttl="2" rtt="18.95" ipaddr="200.217.89.32"/>
+<hop ttl="3" rtt="18.33" ipaddr="200.217.30.250" host="gigabitethernet5-1.80-cto-rn-rotd-02.telemar.net.br"/>
+<hop ttl="4" rtt="45.05" ipaddr="200.97.65.250" host="pos15-1-nbv-pe-rotd-03.telemar.net.br"/>
+<hop ttl="5" rtt="43.49" ipaddr="200.223.131.13" host="pos6-0-nbv-pe-rotn-01.telemar.net.br"/>
+<hop ttl="6" rtt="91.27" ipaddr="200.223.131.205" host="so-0-2-0-0-arc-rj-rotn-01.telemar.net.br"/>
+<hop ttl="8" rtt="191.87" ipaddr="200.223.131.110" host="PO0-3.ARC-RJ-ROTN-01.telemar.net.br"/>
+<hop ttl="9" rtt="177.30" ipaddr="208.173.90.89" host="bpr2-so-5-2-0.miamimit.savvis.net"/>
+<hop ttl="10" rtt="181.50" ipaddr="208.172.97.169" host="cr2-pos-0-3-1-0.miami.savvis.net"/>
+<hop ttl="11" rtt="336.43" ipaddr="206.24.210.70" host="cr1-loopback.sfo.savvis.net"/>
+<hop ttl="12" rtt="245.32" ipaddr="204.70.200.229" host="er1-te-1-0-1.SanJose3Equinix.savvis.net"/>
+<hop ttl="13" rtt="238.47" ipaddr="204.70.200.210" host="hr1-te-2-0-0.santaclarasc4.savvis.net"/>
+<hop ttl="14" rtt="322.90" ipaddr="204.70.200.217" host="hr1-te-2-0-0.santaclarasc9.savvis.net"/>
+<hop ttl="15" rtt="330.96" ipaddr="204.70.203.146"/>
+<hop ttl="16" rtt="342.57" ipaddr="66.35.194.59" host="csr2-ve242.santaclarasc8.savvis.net"/>
+<hop ttl="17" rtt="248.22" ipaddr="66.35.210.202"/>
+<hop ttl="18" rtt="238.36" ipaddr="66.35.250.168" host="freshmeat.net"/>
+</trace>
+<times srtt="269788" rttvar="41141" to="434352" />
+</host>
+<host><status state="up" reason="reset"/>
+<address addr="66.35.250.203" addrtype="ipv4" />
+<hostnames><hostname name="sourceforge.net" type="PTR" /></hostnames>
+<ports><extraports state="filtered" count="1711">
+<extrareasons reason="host-prohibiteds" count="1711"/>
+</extraports>
+<port protocol="tcp" portid="80"><state state="open" reason="syn-ack" reason_ttl="44"/><service name="http" product="lighttpd" version="1.4.18" method="probed" conf="10" /><script id="HTML title" output="Site doesn&apos;t have a title." /><script id="robots.txt" output="User-agent: * &#xa;/forum /pm /search /softwaremap /top /tracker /users " /></port>
+<port protocol="tcp" portid="443"><state state="open" reason="syn-ack" reason_ttl="44"/><service name="http" product="lighttpd" version="1.4.18" tunnel="ssl" method="probed" conf="10" /><script id="SSLv2" output="server still supports SSLv2&#xa;&#x9;SSL2_DES_192_EDE3_CBC_WITH_MD5&#xa;&#x9;SSL2_RC2_CBC_128_CBC_WITH_MD5&#xa;&#x9;SSL2_RC4_128_WITH_MD5&#xa;&#x9;SSL2_RC4_64_WITH_MD5&#xa;&#x9;SSL2_DES_64_CBC_WITH_MD5&#xa;&#x9;SSL2_RC2_CBC_128_CBC_WITH_MD5&#xa;&#x9;SSL2_RC4_128_EXPORT40_WITH_MD5&#xa;" /><script id="HTML title" output="Site doesn&apos;t have a title." /></port>
+<port protocol="tcp" portid="563"><state state="closed" reason="reset" reason_ttl="45"/><service name="snews" method="table" conf="3" /></port>
+</ports>
+<os><portused state="open" proto="tcp" portid="80" />
+<portused state="closed" proto="tcp" portid="563" />
+<osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="2.6.X" accuracy="94" />
+<osclass type="WAP" vendor="FON" osfamily="Linux" osgen="2.4.X" accuracy="92" />
+<osclass type="WAP" vendor="Siemens" osfamily="Linux" accuracy="90" />
+<osclass type="broadband router" vendor="3Com" osfamily="Linux" osgen="2.4.X" accuracy="90" />
+<osclass type="media device" vendor="Dream Multimedia" osfamily="Linux" osgen="2.6.X" accuracy="90" />
+<osclass type="storage-misc" vendor="Iomega" osfamily="Linux" osgen="2.6.X" accuracy="90" />
+<osclass type="WAP" vendor="FON" osfamily="Linux" osgen="2.6.X" accuracy="90" />
+<osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="2.4.X" accuracy="90" />
+<osmatch name="Linux 2.6.17 - 2.6.18" accuracy="94" line="9533"/>
+<osmatch name="Linux 2.6.17 - 2.6.18 (x86)" accuracy="94" line="9630"/>
+<osmatch name="Linux 2.6.9 - 2.6.21" accuracy="94" line="11799"/>
+<osmatch name="Linux 2.6.9 - 2.6.20 (Fedora Core 5 or 6)" accuracy="94" line="11777"/>
+<osmatch name="FON La Fonera WAP (OpenWrt, Linux 2.4.32)" accuracy="92" line="4356"/>
+<osmatch name="Linux 2.6.18.8 (openSUSE 10.2)" accuracy="91" line="10440"/>
+<osmatch name="FON La Fonera WAP running OpenWrt w/Linux kernel 2.4.32" accuracy="91" line="4390"/>
+<osmatch name="Siemens Gigaset SE515dsl wireless broadband router" accuracy="90" line="17372"/>
+<osmatch name="3Com OfficeConnect" accuracy="90" line="250"/>
+<osmatch name="Linux 2.6.9 - 2.6.19" accuracy="90" line="4200"/>
+<osfingerprint fingerprint="SCAN(V=4.53%D=1/27%OT=80%CT=563%CU=%PV=N%G=N%TM=479D25ED%P=i686-pc-linux-gnu)&#xa;SEQ(SP=109%GCD=1%ISR=105%TI=Z%TS=1F)&#xa;SEQ(SP=109%GCD=1%ISR=108%TI=Z%II=I%TS=1F)&#xa;OPS(O1=M5B4ST11NW3%O2=M5B4ST11NW3%O3=M5B4NNT11NW3%O4=M5B4ST11NW3%O5=M5B4ST11NW3%O6=M5B4ST11)&#xa;WIN(W1=16A0%W2=16A0%W3=16A0%W4=16A0%W5=16A0%W6=16A0)&#xa;ECN(R=Y%DF=Y%TG=40%W=16D0%O=M5B4NNSNW3%CC=N%Q=)&#xa;T1(R=Y%DF=Y%TG=40%S=O%A=S+%F=AS%RD=0%Q=)&#xa;T2(R=N)&#xa;T3(R=Y%DF=Y%TG=40%W=16A0%S=O%A=S+%F=AS%O=M5B4ST11NW3%RD=0%Q=)&#xa;T4(R=Y%DF=Y%TG=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)&#xa;T5(R=Y%DF=Y%TG=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)&#xa;T6(R=Y%DF=Y%TG=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)&#xa;T7(R=Y%DF=Y%TG=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)&#xa;U1(R=N)&#xa;IE(R=Y%DFI=N%TG=40%TOSI=S%CD=S%SI=S%DLI=S)&#xa;" />
+</os>
+<uptime seconds="201" lastboot="Sun Jan 27 21:43:16 2008" />
+<tcpsequence index="265" class="unknown class" difficulty="Good luck!" values="E7AA462B,41E21423,41900EE4,B0406748,AF655A96" />
+<ipidsequence class="All zeros" values="0,0,0,0,0" />
+<tcptssequence class="other" values="45702AB,E5E6ADFA,E5E6AE64,FA2DC6F6,FA2DC756" />
+<trace port="80" proto="tcp">
+<hop ttl="1" rtt="--" ipaddr="192.168.254.254"/>
+<hop ttl="2" rtt="--" ipaddr="200.217.89.32"/>
+<hop ttl="3" rtt="--" ipaddr="200.217.30.250" host="gigabitethernet5-1.80-cto-rn-rotd-02.telemar.net.br"/>
+<hop ttl="4" rtt="--" ipaddr="200.97.65.250" host="pos15-1-nbv-pe-rotd-03.telemar.net.br"/>
+<hop ttl="5" rtt="44.53" ipaddr="200.223.131.13" host="pos6-0-nbv-pe-rotn-01.telemar.net.br"/>
+<hop ttl="6" rtt="96.74" ipaddr="200.223.131.2" host="pos2-0-bvg-pe-rotn-01.telemar.net.br"/>
+<hop ttl="7" rtt="243.94" ipaddr="200.223.131.18" host="pos9-0-asgs-ba-rotn-01.telemar.net.br"/>
+<hop ttl="8" rtt="92.72" ipaddr="200.223.131.74" host="so-0-2-2-0-bot-rj-rotn-01.telemar.net.br"/>
+<hop ttl="9" rtt="189.57" ipaddr="200.223.131.110" host="PO0-3.ARC-RJ-ROTN-01.telemar.net.br"/>
+<hop ttl="10" rtt="187.09" ipaddr="208.173.90.89" host="bpr2-so-5-2-0.miamimit.savvis.net"/>
+<hop ttl="11" rtt="185.44" ipaddr="208.172.97.169" host="cr2-pos-0-3-1-0.miami.savvis.net"/>
+<hop ttl="12" rtt="246.55" ipaddr="206.24.210.70" host="cr1-loopback.sfo.savvis.net"/>
+<hop ttl="13" rtt="246.08" ipaddr="204.70.200.229" host="er1-te-1-0-1.SanJose3Equinix.savvis.net"/>
+<hop ttl="14" rtt="242.86" ipaddr="204.70.200.210" host="hr1-te-2-0-0.santaclarasc4.savvis.net"/>
+<hop ttl="15" rtt="333.68" ipaddr="204.70.200.217" host="hr1-te-2-0-0.santaclarasc9.savvis.net"/>
+<hop ttl="16" rtt="245.30" ipaddr="204.70.203.146"/>
+<hop ttl="17" rtt="258.32" ipaddr="66.35.194.58" host="csr1-ve242.santaclarasc8.savvis.net"/>
+<hop ttl="18" rtt="249.12" ipaddr="66.35.212.174"/>
+<hop ttl="19" rtt="251.98" ipaddr="66.35.250.203" host="sourceforge.net"/>
+</trace>
+<times srtt="285380" rttvar="52198" to="494172" />
+</host>
+<host><status state="up" reason="reset"/>
+<address addr="64.13.134.48" addrtype="ipv4" />
+<hostnames />
+<ports><extraports state="filtered" count="1708">
+<extrareasons reason="no-responses" count="1708"/>
+</extraports>
+<port protocol="tcp" portid="22"><state state="open" reason="syn-ack" reason_ttl="48"/><service name="ssh" product="OpenSSH" version="4.3" extrainfo="protocol 2.0" method="probed" conf="10" /></port>
+<port protocol="tcp" portid="25"><state state="closed" reason="reset" reason_ttl="47"/><service name="smtp" method="table" conf="3" /></port>
+<port protocol="tcp" portid="53"><state state="open" reason="syn-ack" reason_ttl="47"/><service name="domain" method="probed" conf="10" /></port>
+<port protocol="tcp" portid="70"><state state="closed" reason="reset" reason_ttl="47"/><service name="gopher" method="table" conf="3" /></port>
+<port protocol="tcp" portid="80"><state state="open" reason="syn-ack" reason_ttl="47"/><service name="http" product="Apache httpd" version="2.2.2" extrainfo="(Fedora)" method="probed" conf="10" /><script id="HTML title" output="Nmap - Free Security Scanner For Network Exploration &amp; Securit..." /></port>
+<port protocol="tcp" portid="113"><state state="closed" reason="reset" reason_ttl="47"/><service name="auth" method="table" conf="3" /></port>
+</ports>
+<os><portused state="open" proto="tcp" portid="22" />
+<portused state="closed" proto="tcp" portid="25" />
+<osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="2.6.X" accuracy="100" />
+<osmatch name="Linux 2.6.17 - 2.6.21" accuracy="100" line="9847" />
+<osfingerprint fingerprint="SCAN(V=4.53%D=1/27%OT=22%CT=25%CU=%PV=N%G=N%TM=479D25ED%P=i686-pc-linux-gnu)&#xa;SEQ(SP=C4%GCD=1%ISR=D4%TI=Z%II=I%TS=A)&#xa;OPS(O1=M5B4ST11NW7%O2=M5B4ST11NW7%O3=M5B4NNT11NW7%O4=M5B4ST11NW7%O5=M5B4ST11NW7%O6=M5B4ST11)&#xa;WIN(W1=16A0%W2=16A0%W3=16A0%W4=16A0%W5=16A0%W6=16A0)&#xa;ECN(R=Y%DF=Y%TG=40%W=16D0%O=M5B4NNSNW7%CC=N%Q=)&#xa;T1(R=Y%DF=Y%TG=40%S=O%A=S+%F=AS%RD=0%Q=)&#xa;T2(R=N)&#xa;T3(R=Y%DF=Y%TG=40%W=16A0%S=O%A=S+%F=AS%O=M5B4ST11NW7%RD=0%Q=)&#xa;T4(R=Y%DF=Y%TG=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)&#xa;T5(R=Y%DF=Y%TG=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)&#xa;T6(R=Y%DF=Y%TG=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)&#xa;T7(R=Y%DF=Y%TG=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)&#xa;U1(R=N)&#xa;IE(R=Y%DFI=N%TG=40%TOSI=S%CD=S%SI=S%DLI=S)&#xa;" />
+</os>
+<uptime seconds="3678886" lastboot="Sun Dec 16 07:51:51 2007" />
+<tcpsequence index="196" class="unknown class" difficulty="Good luck!" values="3B4828E4,3C05079E,3B93665A,3C0C2FD5,3B4F7938,3BD91399" />
+<ipidsequence class="All zeros" values="0,0,0,0,0,0" />
+<tcptssequence class="1000HZ" values="DB441BF8,DB441C5D,DB441CC1,DB441D24,DB441D88,DB441DED" />
+<trace port="22" proto="tcp">
+<hop ttl="1" rtt="--" ipaddr="192.168.254.254"/>
+<hop ttl="2" rtt="--" ipaddr="200.217.89.32"/>
+<hop ttl="3" rtt="--" ipaddr="200.217.30.250" host="gigabitethernet5-1.80-cto-rn-rotd-02.telemar.net.br"/>
+<hop ttl="4" rtt="--" ipaddr="200.97.65.250" host="pos15-1-nbv-pe-rotd-03.telemar.net.br"/>
+<hop ttl="5" rtt="2887.23" ipaddr="200.223.131.13" host="pos6-0-nbv-pe-rotn-01.telemar.net.br"/>
+<hop ttl="7" rtt="109.87" ipaddr="200.223.131.134" host="so-0-2-1-0-bot-rj-rotn-01.telemar.net.br"/>
+<hop ttl="8" rtt="185.40" ipaddr="200.187.128.66"/>
+<hop ttl="10" rtt="186.95" ipaddr="64.125.13.69" host="ge-6-1-0.mpr1.iad10.us.above.net"/>
+<hop ttl="11" rtt="191.85" ipaddr="64.125.30.118" host="so-4-0-0.mpr1.iad2.us.above.net"/>
+<hop ttl="12" rtt="191.88" ipaddr="64.125.27.74" host="so-6-0-0.mpr1.iad5.us.above.net"/>
+<hop ttl="13" rtt="212.11" ipaddr="64.125.29.229" host="so-4-0-0.mpr1.iad1.us.above.net"/>
+<hop ttl="14" rtt="192.05" ipaddr="64.125.28.62" host="so-0-2-0.mpr1.lga5.us.above.net"/>
+<hop ttl="15" rtt="275.83" ipaddr="64.125.26.229" host="so-2-1-0.mpr1.sjc2.above.net"/>
+<hop ttl="16" rtt="268.86" ipaddr="64.125.28.142" host="so-4-2-0.mpr3.pao1.us.above.net"/>
+<hop ttl="17" rtt="299.43" ipaddr="208.185.168.173" host="metro0.sv.svcolo.com"/>
+<hop ttl="18" rtt="352.22" ipaddr="64.13.134.48"/>
+</trace>
+<times srtt="373460" rttvar="156723" to="1000352" />
+</host>
+<host><status state="up" reason="reset"/>
+<address addr="204.152.191.37" addrtype="ipv4" />
+<hostnames><hostname name="pub2.kernel.org" type="PTR" /></hostnames>
+<ports><extraports state="closed" count="1704">
+<extrareasons reason="resets" count="1704"/>
+</extraports>
+<port protocol="tcp" portid="21"><state state="open" reason="syn-ack" reason_ttl="50"/><service name="ftp" product="vsftpd or WU-FTPD" hostname="Welcome" method="probed" conf="10" /><script id="Anonymous FTP" output="FTP: Anonymous login allowed" /></port>
+<port protocol="tcp" portid="22"><state state="open" reason="syn-ack" reason_ttl="50"/><service name="ssh" product="OpenSSH" version="4.3" extrainfo="protocol 2.0" method="probed" conf="10" /></port>
+<port protocol="tcp" portid="79"><state state="open" reason="syn-ack" reason_ttl="50"/><service name="finger" servicefp="SF-Port79-TCP:V=4.53%I=7%D=1/27%Time=479D24B2%P=i686-pc-linux-gnu%r(NULL,1&#xa;SF:A3,&quot;The\x20latest\x20stable\x20version\x20of\x20the\x20Linux\x20kernel\&#xa;SF:x20is:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x202\.6\.24\nThe\x20late&#xa;SF:st\x20snapshot\x20for\x20the\x20stable\x20Linux\x20kernel\x20tree\x20is&#xa;SF::\x20\x20\x20\x202\.6\.24-git3\nThe\x20latest\x202\.4\x20version\x20of\&#xa;SF:x20the\x20Linux\x20kernel\x20is:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2&#xa;SF:0\x20\x20\x20\x202\.4\.36\nThe\x20latest\x202\.2\x20version\x20of\x20th&#xa;SF:e\x20Linux\x20kernel\x20is:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20&#xa;SF:\x20\x20\x202\.2\.26\nThe\x20latest\x20prepatch\x20for\x20the\x202\.2\x&#xa;SF:20Linux\x20kernel\x20tree\x20is:\x20\x20\x20\x20\x20\x20\x202\.2\.27-rc&#xa;SF:2\nThe\x20latest\x20-mm\x20patch\x20to\x20the\x20stable\x20Linux\x20ker&#xa;SF:nels\x20is:\x20\x20\x20\x20\x20\x20\x20\x202\.6\.24-rc8-mm1\n&quot;)%r(Gener&#xa;SF:icLines,1A3,&quot;The\x20latest\x20stable\x20version\x20of\x20the\x20Linux\x&#xa;SF:20kernel\x20is:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x202\.6\.24\nTh&#xa;SF:e\x20latest\x20snapshot\x20for\x20the\x20stable\x20Linux\x20kernel\x20t&#xa;SF:ree\x20is:\x20\x20\x20\x202\.6\.24-git3\nThe\x20latest\x202\.4\x20versi&#xa;SF:on\x20of\x20the\x20Linux\x20kernel\x20is:\x20\x20\x20\x20\x20\x20\x20\x&#xa;SF:20\x20\x20\x20\x20\x20\x202\.4\.36\nThe\x20latest\x202\.2\x20version\x2&#xa;SF:0of\x20the\x20Linux\x20kernel\x20is:\x20\x20\x20\x20\x20\x20\x20\x20\x2&#xa;SF:0\x20\x20\x20\x20\x202\.2\.26\nThe\x20latest\x20prepatch\x20for\x20the\&#xa;SF:x202\.2\x20Linux\x20kernel\x20tree\x20is:\x20\x20\x20\x20\x20\x20\x202\&#xa;SF:.2\.27-rc2\nThe\x20latest\x20-mm\x20patch\x20to\x20the\x20stable\x20Lin&#xa;SF:ux\x20kernels\x20is:\x20\x20\x20\x20\x20\x20\x20\x202\.6\.24-rc8-mm1\n&quot;&#xa;SF:)%r(GetRequest,1A3,&quot;The\x20latest\x20stable\x20version\x20of\x20the\x20&#xa;SF:Linux\x20kernel\x20is:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x202\.6\&#xa;SF:.24\nThe\x20latest\x20snapshot\x" method="table" conf="3" /></port>
+<port protocol="tcp" portid="80"><state state="open" reason="syn-ack" reason_ttl="50"/><service name="http" product="Apache httpd" version="2.2.2" extrainfo="(Fedora)" method="probed" conf="10" /><script id="robots.txt" output="&#xa;/cgi-bin/ /pub/mirrors/ /pub/scm/ &#xa;/mirrors/process-registration.cgi /lsb/ /linuxeda/ /os.org/ /debian/ &#xa;/debian-cd/ /lanana/ /li18nux/ /freestandards/ &#xa;/filehub/ /diff/ /git/ /hg/ " /><script id="HTML title" output="The Linux Kernel Archives" /></port>
+<port protocol="tcp" portid="199"><state state="open" reason="syn-ack" reason_ttl="50"/><service name="smux" product="Linux SNMP multiplexer" ostype="Linux" method="probed" conf="10" /></port>
+<port protocol="tcp" portid="443"><state state="open" reason="syn-ack" reason_ttl="50"/><service name="https" tunnel="ssl" method="table" conf="3" /><script id="HTML title" output="The Linux Kernel Archives" /></port>
+<port protocol="tcp" portid="873"><state state="open" reason="syn-ack" reason_ttl="53"/><service name="rsync" extrainfo="protocol version 29" method="probed" conf="10" /></port>
+<port protocol="tcp" portid="1720"><state state="filtered" reason="no-response" reason_ttl="0"/><service name="H.323/Q.931" method="table" conf="3" /></port>
+<port protocol="tcp" portid="5978"><state state="open" reason="syn-ack" reason_ttl="50"/><service name="ncd-diag-tcp" method="table" conf="3" /></port>
+<port protocol="tcp" portid="7000"><state state="open" reason="syn-ack" reason_ttl="53"/><service name="afs3-fileserver" method="table" conf="3" /></port>
+</ports>
+<os><portused state="open" proto="tcp" portid="21" />
+<portused state="closed" proto="tcp" portid="1" />
+<portused state="closed" proto="udp" portid="34527" />
+<osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="2.6.X" accuracy="91" />
+<osclass type="security-misc" vendor="Aladdin" osfamily="Linux" osgen="2.4.X" accuracy="88" />
+<osclass type="general purpose" vendor="Linux" osfamily="Linux" osgen="2.4.X" accuracy="87" />
+<osclass type="switch" vendor="QLogic" osfamily="embedded" accuracy="87" />
+<osclass type="PDA" vendor="Sharp" osfamily="Linux" osgen="2.4.X" accuracy="87" />
+<osclass type="VoIP gateway" vendor="Tandberg" osfamily="Linux" osgen="2.6.X" accuracy="87" />
+<osclass type="WAP" vendor="Siemens" osfamily="Linux" accuracy="87" />
+<osclass type="server appliance" vendor="Toshiba" osfamily="Linux" osgen="2.4.X" accuracy="87" />
+<osclass type="VoIP gateway" vendor="Occam" osfamily="embedded" accuracy="86" />
+<osclass type="WAP" vendor="FON" osfamily="Linux" osgen="2.4.X" accuracy="86" />
+<osclass type="media device" vendor="Dream Multimedia" osfamily="Linux" osgen="2.6.X" accuracy="85" />
+<osclass type="storage-misc" vendor="Iomega" osfamily="Linux" osgen="2.6.X" accuracy="85" />
+<osclass type="WAP" vendor="Inventel" osfamily="embedded" accuracy="85" />
+<osclass type="broadband router" vendor="USRobotics" osfamily="embedded" accuracy="85" />
+<osclass type="broadband router" vendor="Linux" osfamily="Linux" osgen="2.4.X" accuracy="85" />
+<osclass type="WAP" vendor="Linux" osfamily="Linux" osgen="2.4.X" accuracy="85" />
+<osclass type="media device" vendor="Linux" osfamily="Linux" osgen="2.4.X" accuracy="85" />
+<osclass type="VoIP gateway" vendor="Linux" osfamily="Linux" osgen="2.4.X" accuracy="85" />
+<osmatch name="Linux 2.6.5 - 2.6.9" accuracy="91" line="11429"/>
+<osmatch name="Linux 2.6.17 - 2.6.18" accuracy="91" line="9552"/>
+<osmatch name="Linux 2.6.22" accuracy="89" line="11205"/>
+<osmatch name="Linux 2.6.15.4" accuracy="89" line="9337"/>
+<osmatch name="Linux 2.6.9-022stab078.19-enterprise (CentOS 4.2 x86)" accuracy="89" line="11816"/>
+<osmatch name="Linux 2.6.17 - 2.6.18 (x86_64, SMP)" accuracy="89" line="9650"/>
+<osmatch name="Linux 2.6.17 - 2.6.21" accuracy="89" line="9847"/>
+<osmatch name="Linux 2.6.18 (Gentoo, x86)" accuracy="89" line="10263"/>
+<osmatch name="Linux 2.6.5-7.283-smp (SuSE Enterprise Server 9, x86)" accuracy="88" line="11465"/>
+<osmatch name="Aladdin eSafe security gateway (runs Linux 2.4.21)" accuracy="88" line="463"/>
+<osfingerprint fingerprint="SCAN(V=4.53%D=1/27%OT=21%CT=1%CU=34527%PV=N%DS=13%G=N%TM=479D25ED%P=i686-pc-linux-gnu)&#xa;SEQ(SP=CB%GCD=1%ISR=CF%TI=Z%TS=E)&#xa;SEQ(SP=CB%GCD=1%ISR=CF%TI=Z%II=I%TS=D)&#xa;OPS(O1=M5B4ST11NW7%O2=M5B4ST11NW7%O3=M5B4NNT11NW7%O4=M5B4ST11NW7%O5=M5B4ST11NW7%O6=M5B4ST11)&#xa;WIN(W1=16A0%W2=16A0%W3=16A0%W4=16A0%W5=16A0%W6=16A0)&#xa;ECN(R=Y%DF=Y%T=3F%W=16D0%O=M5B4NNSNW7%CC=N%Q=)&#xa;T1(R=Y%DF=Y%T=3F%S=O%A=S+%F=AS%RD=0%Q=)&#xa;T2(R=N)&#xa;T3(R=Y%DF=Y%T=3F%W=16A0%S=O%A=S+%F=AS%O=M5B4ST11NW7%RD=0%Q=)&#xa;T4(R=Y%DF=Y%T=3F%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)&#xa;T5(R=Y%DF=Y%T=3F%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)&#xa;T6(R=Y%DF=Y%T=3F%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)&#xa;T7(R=Y%DF=Y%T=3F%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)&#xa;U1(R=Y%DF=N%T=3F%TOS=0%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=ABF4%RUL=G%RUD=G)&#xa;IE(R=Y%DFI=N%T=3F%TOSI=Z%CD=S%SI=S%DLI=S)&#xa;IE(R=Y%DFI=N%T=42%TOSI=Z%CD=S%SI=S%DLI=S)&#xa;" />
+</os>
+<uptime seconds="404814" lastboot="Wed Jan 23 05:19:43 2008" />
+<distance value="13" />
+<tcpsequence index="203" class="unknown class" difficulty="Good luck!" values="51411AEE,509EEED0,508D446B,50BABE19,5110A098,506A461E" />
+<ipidsequence class="All zeros" values="0,0,0,0,0,0" />
+<tcptssequence class="other" values="A4550333,A455026B,A454F6B8,A454F71A,A454F77F,A454F7DB" />
+<trace port="21" proto="tcp">
+<hop ttl="1" rtt="2.66" ipaddr="192.168.254.254"/>
+<hop ttl="2" rtt="27.86" ipaddr="200.217.89.32"/>
+<hop ttl="3" rtt="46.94" ipaddr="200.217.30.210" host="gigabitethernet6-1.90-cto-rn-rotd-02.telemar.net.br"/>
+<hop ttl="4" rtt="53.15" ipaddr="200.97.65.250" host="pos15-1-nbv-pe-rotd-03.telemar.net.br"/>
+<hop ttl="5" rtt="47.43" ipaddr="200.223.131.13" host="pos6-0-nbv-pe-rotn-01.telemar.net.br"/>
+<hop ttl="8" rtt="181.54" ipaddr="200.223.131.246"/>
+<hop ttl="9" rtt="183.95" ipaddr="209.58.18.1" host="if-0-11.ihar1.N60-NewYork.teleglobe.net"/>
+<hop ttl="10" rtt="267.48" ipaddr="216.6.87.17" host="if-12-0.mcore4.NQT-NewYork.teleglobe.net"/>
+<hop ttl="11" rtt="347.36" ipaddr="216.6.86.13" host="if-4-0.mcore4.PDI-PaloAlto.teleglobe.net"/>
+<hop ttl="12" rtt="345.61" ipaddr="216.6.86.2" host="if-7-0.core3.PDI-PaloAlto.teleglobe.net"/>
+<hop ttl="13" rtt="337.96" ipaddr="207.45.196.66" host="ix-4-6.core3.PDI-PaloAlto.Teleglobe.net"/>
+<hop ttl="14" rtt="251.55" ipaddr="204.152.191.37" host="pub2.kernel.org"/>
+</trace>
+<times srtt="302981" rttvar="47378" to="492493" />
+</host>
+<host><status state="up" reason="echo-reply"/>
+<address addr="199.185.137.3" addrtype="ipv4" />
+<hostnames><hostname name="cvs.openbsd.org" type="PTR" /></hostnames>
+<ports><extraports state="filtered" count="1019">
+<extrareasons reason="host-unreaches" count="1013"/>
+<extrareasons reason="no-responses" count="6"/>
+</extraports>
+<extraports state="closed" count="690">
+<extrareasons reason="resets" count="690"/>
+</extraports>
+<port protocol="tcp" portid="21"><state state="open" reason="syn-ack" reason_ttl="48"/><service name="ftp" product="bsd-ftpd" hostname="cvs.openbsd.org" ostype="Linux" method="probed" conf="10" /></port>
+<port protocol="tcp" portid="25"><state state="open" reason="syn-ack" reason_ttl="46"/><service name="smtp" product="OpenBSD spamd" method="probed" conf="10" /><script id="SMTP" output="EHLO with errors or timeout. Enable -&#45;script-trace to see what is happening." /></port>
+<port protocol="tcp" portid="53"><state state="open" reason="syn-ack" reason_ttl="45"/><service name="domain" product="ISC BIND" version="9.X" method="probed" conf="10" /><script id="zone-transfer" output=" &#xa;openbsd.org. SOA zeus.theos.com. root.theos.com. &#xa;openbsd.org. NS ns.appli.se. &#xa;openbsd.org. NS ns&#x9;sigmasoft.com. &#xa;openbsd.org. NS cvs.openbsd.org. &#xa;openbsd.org. NS citi.umich.edu. &#xa;openbsd.org. NS zeus.theos.com. &#xa;openbsd.org. A 199.185.137.3 &#xa;openbsd.org. MX shear.ucar.edu. &#xa;openbsd.org. MX cvs.openbsd.org. &#xa;alpha.openbsd.org. A 199.185.137.77 &#xa;amd64.openbsd.org. A 199.185.137.80 &#xa;anoncvs.openbsd.org. CNAME &#xa;ftp.ar.openbsd.org. CNAME &#xa;ftp1.ar.openbsd.org. CNAME &#xa;www.ar.openbsd.org. CNAME &#xa;armish.openbsd.org. A 199.185.137.155 &#xa;ftp.as.openbsd.org. CNAME &#xa;ftp1.as.openbsd.org. CNAME &#xa;ftp2.as.openbsd.org. CNAME &#xa;ftp3.as.openbsd.org. CNAME &#xa;ftp4.as.openbsd.org. CNAME &#xa;anoncvs2.at.openbsd.org. A 128.130.59.60 &#xa;www.at.openbsd.org. CNAME &#xa;ftp.au.openbsd.org. CNAME &#xa;ftp1.au.openbsd.org. CNAME &#xa;ftp2.au.openbsd.org. CNAME &#xa;ftp3.au.openbsd.org. CNAME &#xa;www.au.openbsd.org. CNAME &#xa;cvsup.bg.openbsd.org. A 217.10.246.146 &#xa;ftp.bg.openbsd.org. A 217.10.246.146 &#xa;ftp1.bg.openbsd.org. A 217.10.246.146 &#xa;c.openbsd.org. CNAME &#xa;anoncvs.ca.openbsd.org. CNAME &#xa;anoncvs1.ca.openbsd.org. CNAME &#xa;ctm.ca.openbsd.org. CNAME &#xa;cvsup.ca.openbsd.org. A 206.51.28.249 &#xa;ftp.ca.openbsd.org. CNAME &#xa;ftp1.ca.openbsd.org. CNAME &#xa;www.ca.openbsd.org. A 129.128.5.191 &#xa;cats.openbsd.org. A 199.185.137.33 &#xa;ctm.openbsd.org. CNAME &#xa;cvs.openbsd.org. A 199.185.137.3 &#xa;cvsup.openbsd.org. CNAME &#xa;de.openbsd.org. NS a.ns.bsws.de. &#xa;de.openbsd.org. NS a.ns&#x9;pestilenz.org. &#xa;de.openbsd.org. NS b.ns.bsws.de. &#xa;de.openbsd.org. NS c.ns.bsws.de. &#xa;ftp.eu.openbsd.org. CNAME &#xa;ftp1.eu.openbsd.org. CNAME &#xa;ftp2.eu.openbsd.org. CNAME &#xa;ftp3.eu.openbsd.org. CNAME &#xa;ftp4.eu.openbsd.org. CNAME &#xa;ftp5.eu.openbsd.org. CNAME &#xa;ftp6.eu.openbsd.org. CNAME &#xa;ftp8.eu.openbsd.org. CNAME &#xa;ftp9.eu.openbsd.org. A 193.120.14.244 &#xa;cvsup.fr.openbsd.org. CNAME &#xa;ftp.fr.openbsd.org. CNAME &#xa;ftp1.fr.openbsd.org. CNAME &#xa;ftp2.fr.openbsd.org. CNAME &#xa;ftp3.fr.openbsd.org. CNAME &#xa;ftp.openbsd.org. CNAME &#xa;gw.openbsd.org. A 199.185.230.1 &#xa;hp3...openbsd.org. A 199.185.137.74 &#xa;hppa.openbsd.org. A 199.185.137.79 &#xa;https.openbsd.org. A 68.148.128.241 &#xa;cvsup.hu.openbsd.org. CNAME &#xa;i386.openbsd.org. A 199.185.137.8 &#xa;cvsup.id.openbsd.org. CNAME &#xa;ftp.ie.openbsd.org. A 193.120.14.244 &#xa;ftp19.ie.openbsd.org. A 193.120.14.244 &#xa;www.ie.openbsd.org. A 193.120.14.244 &#xa;anoncvs.jp.openbsd.org. CNAME &#xa;anoncvs1.jp.openbsd.org. CNAME &#xa;cvsup.jp.openbsd.org. CNAME &#xa;ftp.jp.openbsd.org. CNAME &#xa;ftp1.jp.openbsd.org. CNAME &#xa;ftp2.jp.openbsd.org. CNAME &#xa;www.jp.openbsd.org. CNAME &#xa;cvsup.kr.openbsd.org. CNAME &#xa;ftp.kr.openbsd.org. CNAME &#xa;ftp1.kr.openbsd.org. CNAME &#xa;landisk.openbsd.org. A 199.185.137.81 &#xa;landiskx.openbsd.org. A 199.185.137.157 &#xa;list.openbsd.org. CNAME &#xa;lists.openbsd.org. CNAME &#xa;localhost.openbsd.org. A 127.0.0.1 &#xa;loghost.openbsd.org. CNAME &#xa;macppc.openbsd.org. A 199.185.137.87 &#xa;mail.openbsd.org. CNAME &#xa;music.openbsd.org. A 199.185.137.230 &#xa;mvme68k.openbsd.org. A 199.185.137.42 &#xa;mvme88k.openbsd.org. A 199.185.137.88 &#xa;n.openbsd.org. A 199.185.136.200 &#xa;n.openbsd.org. MX cvs.openbsd.org. &#xa;anoncvs.nl.openbsd.org. CNAME &#xa;anoncvs1.nl.openbsd.org. CNAME &#xa;anoncvs.no.openbsd.org. CNAME &#xa;anoncvs1.no.openbsd.org. CNAME &#xa;cvsup.no.openbsd.org. CNAME &#xa;anoncvs.nyc.openbsd.org. CNAME &#xa;ftp.nyc.openbsd.org. CNAME &#xa;ops.openbsd.org. A 199.185.137.129 &#xa;pf.openbsd.org. A 199.185.136.128 &#xa;pf.openbsd.org. A 199.185.137.128 &#xa;pf.openbsd.org. A 199.185.231.128 &#xa;pf.openbsd.org. A 207.153.1.26 &#xa;anoncvs.pl.openbsd.org. CNAME &#xa;anoncvs1.pl.openbsd.org. CNAME &#xa;pmax.openbsd.org. A 199.185.137.52 &#xa;ports.openbsd.org. CNAME &#xa;alpha.ports.openbsd.org. A 199.185.231.77 &#xa;amd64.ports.openbsd.org. A 199.185.231.80 &#xa;armish.ports.openbsd.org. A 199.185.231.155 &#xa;cvs.ports.openbsd.org. CNAME &#xa;hppa.ports.openbsd.org. A 199.185.231.78 &#xa;i386.ports.openbsd.org. A 199.185.231.6 &#xa;landisk.ports.openbsd.org. A 199.185.231.81 &#xa;m68k.ports.openbsd.org. A 199.185.231.7 &#xa;macppc.ports.openbsd.org. A 199.185.231.8 &#xa;serial.ports.openbsd.org. A 199.185.231.111 &#xa;sgi.ports.openbsd.org. A 199.185.231.99 &#xa;sparc.ports.openbsd.org. A 199.185.231.5 &#xa;sparc-..ports.openbsd.org. CNAME &#xa;sparc-1.ports.openbsd.org. A 199.185.231.20 &#xa;sparc-2.ports.openbsd.org. A 199.185.231.21 &#xa;sparc-3.ports.openbsd.org. A 199.185.231.22 &#xa;sparc64.ports.openbsd.org. A 199.185.231.94 &#xa;sparc64-..ports.openbsd.org. CNAME &#xa;sparc64-1.ports.openbsd.org. A 199.185.231.95 &#xa;sparc64-2.ports.openbsd.org. A 199.185.231.96 &#xa;sparc64-3.ports.openbsd.org. A 199.185.231.97 &#xa;sparc64-4.ports.openbsd.org. A 199.185.231.98 &#xa;vax.ports.openbsd.org. A 199.185.231.79 &#xa;zaurus.ports.openbsd.org. A 199.185.231.50 &#xa;cvsup.pt.openbsd.org. CNAME &#xa;ftp.ro.openbsd.org. CNAME &#xa;ftp1.ro.openbsd.org. CNAME &#xa;anoncvs.se.openbsd.org. CNAME &#xa;anoncvs1.se.openbsd.org. CNAME &#xa;ctm.se.openbsd.org. CNAME &#xa;ftp.se.openbsd.org. CNAME &#xa;ftp1.se.openbsd.org. CNAME &#xa;serial.openbsd.org. A 199.185.137.111 &#xa;sgi.openbsd.org. A 199.185.137.99 &#xa;sparc.openbsd.org. A 199.185.137.92 &#xa;sparc64.openbsd.org. A 199.185.137.94 &#xa;test.openbsd.org. A 199.185.137.17 &#xa;test2.openbsd.org. A 199.185.137.18 &#xa;test3.openbsd.org. A 199.185.137.19 &#xa;test4.openbsd.org. A 199.185.137.20 &#xa;ftp.th.openbsd.org. CNAME &#xa;ftp1.th.openbsd.org. CNAME &#xa;www.tr.openbsd.org. CNAME &#xa;anoncvs.tw.openbsd.org. CNAME &#xa;anoncvs1.tw.openbsd.org. CNAME &#xa;cvsup.tw.openbsd.org. A 140.113.17.208 &#xa;ftp.tw.openbsd.org. CNAME &#xa;ftp1.tw.openbsd.org. CNAME &#xa;www.tw.openbsd.org. CNAME &#xa;u.openbsd.org. A 199.185.136.4 &#xa;u.openbsd.org. MX cvs.openbsd.org. &#xa;cvsup.uk.openbsd.org. A 194.242.139.171 &#xa;anoncvs.usa.openbsd.org. CNAME &#xa;anoncvs1.usa.openbsd.org. CNAME &#xa;anoncvs2.usa.openbsd.org. CNAME &#xa;anoncvs3.usa.openbsd.org. CNAME &#xa;anoncvs4.usa.openbsd.org. CNAME &#xa;anoncvs5.usa.openbsd.org. CNAME &#xa;anoncvs6.usa.openbsd.org. CNAME &#xa;cvsup.usa.openbsd.org. A 128.46.156.46 &#xa;ftp.usa.openbsd.org. CNAME &#xa;ftp1.usa.openbsd.org. CNAME &#xa;ftp2.usa.openbsd.org. CNAME &#xa;ftp3.usa.openbsd.org. CNAME &#xa;ftp5.usa.openbsd.org. CNAME &#xa;ftp6.usa.openbsd.org. CNAME &#xa;www.usa.openbsd.org. A 198.175.14.3 &#xa;v.openbsd.org. A 199.185.136.2 &#xa;v.openbsd.org. MX cvs.openbsd.org. &#xa;vax.openbsd.org. A 199.185.137.78 &#xa;w.openbsd.org. A 199.185.136.3 &#xa;w.openbsd.org. MX cvs.openbsd.org. &#xa;www.openbsd.org. A 129.128.5.191 &#xa;x.openbsd.org. A 199.185.136.5 &#xa;x.openbsd.org. MX cvs.openbsd.org. &#xa;zaurus.openbsd.org. A 199.185.137.50 &#xa;zeus.openbsd.org. CNAME &#xa;openbsd.org. SOA zeus.theos.com. root.theos.com. &#xa;" /></port>
+<port protocol="tcp" portid="80"><state state="open" reason="syn-ack" reason_ttl="48"/><service name="http" product="Apache httpd" method="probed" conf="10" /><script id="robots.txt" output="/cgi-bin/ /faq/new/ &#xa;/donations.html " /><script id="HTML title" output="OpenBSD" /></port>
+<port protocol="tcp" portid="7326"><state state="open" reason="syn-ack" reason_ttl="45"/><service name="icb" servicefp="SF-Port7326-TCP:V=4.53%I=7%D=1/27%Time=479D24BC%P=i686-pc-linux-gnu%r(NULL&#xa;SF:,43,&quot;Bj1\x01cvs\.openbsd\.org\x01Darkside\x20Server\x20version\x201\.3\&#xa;SF:.221\x20\+\x20pain,\x20v0\.100\0&quot;)%r(GenericLines,43,&quot;Bj1\x01cvs\.openb&#xa;SF:sd\.org\x01Darkside\x20Server\x20version\x201\.3\.221\x20\+\x20pain,\x2&#xa;SF:0v0\.100\0&quot;)%r(GetRequest,43,&quot;Bj1\x01cvs\.openbsd\.org\x01Darkside\x20S&#xa;SF:erver\x20version\x201\.3\.221\x20\+\x20pain,\x20v0\.100\0&quot;)%r(HTTPOptio&#xa;SF:ns,43,&quot;Bj1\x01cvs\.openbsd\.org\x01Darkside\x20Server\x20version\x201\.&#xa;SF:3\.221\x20\+\x20pain,\x20v0\.100\0&quot;)%r(RTSPRequest,43,&quot;Bj1\x01cvs\.open&#xa;SF:bsd\.org\x01Darkside\x20Server\x20version\x201\.3\.221\x20\+\x20pain,\x&#xa;SF:20v0\.100\0&quot;)%r(RPCCheck,43,&quot;Bj1\x01cvs\.openbsd\.org\x01Darkside\x20Se&#xa;SF:rver\x20version\x201\.3\.221\x20\+\x20pain,\x20v0\.100\0&quot;)%r(DNSVersion&#xa;SF:BindReq,43,&quot;Bj1\x01cvs\.openbsd\.org\x01Darkside\x20Server\x20version\x&#xa;SF:201\.3\.221\x20\+\x20pain,\x20v0\.100\0&quot;)%r(DNSStatusRequest,43,&quot;Bj1\x0&#xa;SF:1cvs\.openbsd\.org\x01Darkside\x20Server\x20version\x201\.3\.221\x20\+\&#xa;SF:x20pain,\x20v0\.100\0&quot;)%r(Help,43,&quot;Bj1\x01cvs\.openbsd\.org\x01Darkside&#xa;SF:\x20Server\x20version\x201\.3\.221\x20\+\x20pain,\x20v0\.100\0&quot;)%r(SSLS&#xa;SF:essionReq,43,&quot;Bj1\x01cvs\.openbsd\.org\x01Darkside\x20Server\x20version&#xa;SF:\x201\.3\.221\x20\+\x20pain,\x20v0\.100\0&quot;)%r(SMBProgNeg,43,&quot;Bj1\x01cvs&#xa;SF:\.openbsd\.org\x01Darkside\x20Server\x20version\x201\.3\.221\x20\+\x20p&#xa;SF:ain,\x20v0\.100\0&quot;)%r(X11Probe,43,&quot;Bj1\x01cvs\.openbsd\.org\x01Darkside&#xa;SF:\x20Server\x20version\x201\.3\.221\x20\+\x20pain,\x20v0\.100\0&quot;)%r(Four&#xa;SF:OhFourRequest,43,&quot;Bj1\x01cvs\.openbsd\.org\x01Darkside\x20Server\x20ver&#xa;SF:sion\x201\.3\.221\x20\+\x20pain,\x20v0\.100\0&quot;)%r(LPDString,6C,&quot;Bj1\x01&#xa;SF:cvs\.openbsd\.org\x01Darkside\x20Server\x20version\x201\.3\.221\x20\+\x&#xa;SF:2" method="table" conf="3" /></port>
+</ports>
+<os><portused state="open" proto="tcp" portid="21" />
+<portused state="closed" proto="tcp" portid="22" />
+<portused state="closed" proto="udp" portid="34307" />
+<osclass type="VoIP gateway" vendor="Netcomm" osfamily="embedded" accuracy="87" />
+<osmatch name="Netcomm V300 VoIP gateway" accuracy="87" line="15324"/>
+<osfingerprint fingerprint="SCAN(V=4.53%D=1/27%OT=21%CT=22%CU=34307%PV=N%DS=21%G=N%TM=479D25ED%P=i686-pc-linux-gnu)&#xa;SEQ(SP=103%GCD=1%ISR=10B%TI=RD%TS=21)&#xa;OPS(O1=M5B4NNSNW0NNT11%O2=M5B4NNSNW0NNT11%O3=M5B4NW0NNT11%O4=M5B4NNSNW0NNT11%O5=M5B4NNSNW0NNT11%O6=M5B4NNSNNT11)&#xa;WIN(W1=4000%W2=4000%W3=4000%W4=4000%W5=4000%W6=4000)&#xa;ECN(R=Y%DF=Y%T=42%W=4000%O=M5B4NNSNW0%CC=N%Q=)&#xa;ECN(R=N)&#xa;T1(R=Y%DF=Y%T=42%S=O%A=S+%F=AS%RD=0%Q=)&#xa;T2(R=N)&#xa;T3(R=N)&#xa;T4(R=N)&#xa;T5(R=Y%DF=Y%T=43%W=0%S=A%A=S+%F=AR%O=%RD=0%Q=)&#xa;T5(R=N)&#xa;T6(R=N)&#xa;T7(R=N)&#xa;U1(R=Y%DF=N%T=101%TOS=0%IPL=38%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=ACD0%RUL=G%RUD=G)&#xa;IE(R=Y%DFI=S%T=104%TOSI=S%CD=S%SI=S%DLI=S)&#xa;IE(R=Y%DFI=S%T=101%TOSI=S%CD=S%SI=S%DLI=S)&#xa;" />
+</os>
+<distance value="21" />
+<trace port="21" proto="tcp">
+<hop ttl="1" rtt="3.40" ipaddr="192.168.254.254"/>
+<hop ttl="2" rtt="33.50" ipaddr="200.217.89.32"/>
+<hop ttl="3" rtt="35.53" ipaddr="200.217.30.250" host="gigabitethernet5-1.80-cto-rn-rotd-02.telemar.net.br"/>
+<hop ttl="4" rtt="52.95" ipaddr="200.97.65.245" host="pos9-1-bvg-pe-rotd-02.telemar.net.br"/>
+<hop ttl="5" rtt="53.04" ipaddr="200.223.131.21" host="pos6-0-bvg-pe-rotn-01.telemar.net.br"/>
+<hop ttl="6" rtt="87.46" ipaddr="200.223.43.245"/>
+<hop ttl="7" rtt="208.03" ipaddr="200.223.131.138" host="PO12-0.ARC-RJ-ROTD-03.telemar.net.br"/>
+<hop ttl="8" rtt="199.96" ipaddr="144.228.186.117" host="sl-st22-mia-13-0-0.sprintlink.net"/>
+<hop ttl="9" rtt="198.19" ipaddr="144.232.2.205" host="sl-bb20-mia-10-0-0.sprintlink.net"/>
+<hop ttl="10" rtt="203.13" ipaddr="144.232.2.203" host="sl-bb22-mia-3-0-0.sprintlink.net"/>
+<hop ttl="11" rtt="198.98" ipaddr="144.232.18.216" host="sl-crs2-atl-0-0-0-1.sprintlink.net"/>
+<hop ttl="12" rtt="289.94" ipaddr="144.232.9.125" host="sl-bb20-nsh-15-0-0.sprintlink.net"/>
+<hop ttl="13" rtt="206.40" ipaddr="144.232.23.129"/>
+<hop ttl="14" rtt="214.70" ipaddr="144.232.9.126" host="sl-bb25-chi-12-0-0.sprintlink.net"/>
+<hop ttl="15" rtt="259.42" ipaddr="144.232.20.156" host="sl-bb21-sea-1-0.sprintlink.net"/>
+<hop ttl="16" rtt="260.75" ipaddr="144.232.6.134" host="sl-gw14-sea-9-0.sprintlink.net"/>
+<hop ttl="17" rtt="263.99" ipaddr="144.232.219.238" host="sl-callnet-49-0.sprintlink.net"/>
+<hop ttl="18" rtt="282.49" ipaddr="204.50.128.13" host="p3-0-S1.bb1.cal1.rogerstelecom.net"/>
+<hop ttl="19" rtt="273.89" ipaddr="204.50.251.141" host="g5-1-S1.tls2.cal1.rogerstelecom.net"/>
+<hop ttl="20" rtt="277.01" ipaddr="207.107.204.178" host="Z-s4-0-0-5-0-S1.tls2.cal1.rogerstelecom.net"/>
+<hop ttl="21" rtt="280.83" ipaddr="199.185.230.2" host="pf.openbsd.org"/>
+<hop ttl="22" rtt="280.60" ipaddr="199.185.137.3" host="cvs.openbsd.org"/>
+</trace>
+<times srtt="326085" rttvar="47769" to="517161" />
+</host>
+<taskbegin task="SYN Stealth Scan" time="1201481197" />
+<taskprogress task="SYN Stealth Scan" time="1201481228" percent="9.14" remaining="298" etc="1201481526" />
+<taskend task="SYN Stealth Scan" time="1201481433" extrainfo="5142 total ports" />
+<taskbegin task="Service scan" time="1201481433" />
+<taskend task="Service scan" time="1201481455" extrainfo="12 services on 3 hosts" />
+<taskbegin task="Traceroute" time="1201481472" />
+<taskend task="Traceroute" time="1201481512" />
+<taskbegin task="Traceroute" time="1201481512" />
+<taskend task="Traceroute" time="1201481523" />
+<taskbegin task="Parallel DNS resolution of 46 hosts." time="1201481523" />
+<taskend task="Parallel DNS resolution of 46 hosts." time="1201481536" />
+<taskbegin task="System CNAME DNS resolution of 1 host." time="1201481536" />
+<taskend task="System CNAME DNS resolution of 1 host." time="1201481536" />
+<taskbegin task="SCRIPT ENGINE" time="1201481536" />
+<taskend task="SCRIPT ENGINE" time="1201481569" />
+<host><status state="up" reason="reset"/>
+<address addr="204.152.190.12" addrtype="ipv4" />
+<hostnames><hostname name="www.netbsd.org" type="PTR" /></hostnames>
+<ports><extraports state="closed" count="1705">
+<extrareasons reason="resets" count="1705"/>
+</extraports>
+<port protocol="tcp" portid="22"><state state="open" reason="syn-ack" reason_ttl="46"/><service name="ssh" product="OpenSSH" version="4.4" extrainfo="NetBSD 20061114; protocol 2.0" ostype="NetBSD" method="probed" conf="10" /></port>
+<port protocol="tcp" portid="25"><state state="open" reason="syn-ack" reason_ttl="46"/><service name="smtp" product="Postfix smtpd" hostname=" narn.NetBSD.org" method="probed" conf="10" /><script id="SMTP" output="HELP with errors or timeout. Enable -&#45;script-trace to see what is happening." /></port>
+<port protocol="tcp" portid="80"><state state="open" reason="syn-ack" reason_ttl="46"/><service name="http" product="Apache httpd" version="2.0.61" extrainfo="(Unix) mod_ssl/2.0.61 DAV/2 mod_fastcgi/2.4.2 mod_apreq2-20051231/2.6.0" method="probed" conf="10" /><script id="HTML title" output="The NetBSD Project" /></port>
+<port protocol="tcp" portid="443"><state state="open" reason="syn-ack" reason_ttl="46"/><service name="http" product="Apache httpd" version="2.0.61" extrainfo="mod_ssl/2.0.61 DAV/2 mod_fastcgi/2.4.2 mod_apreq2-20051231/2.6.0" hostname="rt.NetBSD.org" method="probed" conf="10" /><script id="HTML title" output="400 Bad Request" /><script id="SSLv2" output="server still supports SSLv2&#xa;&#x9;SSL2_DES_192_EDE3_CBC_WITH_MD5&#xa;&#x9;SSL2_IDEA_128_CBC_WITH_MD5&#xa;&#x9;SSL2_RC2_CBC_128_CBC_WITH_MD5&#xa;&#x9;SSL2_RC4_128_WITH_MD5&#xa;&#x9;SSL2_DES_64_CBC_WITH_MD5&#xa;&#x9;SSL2_RC2_CBC_128_CBC_WITH_MD5&#xa;&#x9;SSL2_RC4_128_EXPORT40_WITH_MD5&#xa;" /></port>
+<port protocol="tcp" portid="871"><state state="open" reason="syn-ack" reason_ttl="46"/><service name="supfilesrv" method="table" conf="3" /></port>
+<port protocol="tcp" portid="874"><state state="open" reason="syn-ack" reason_ttl="46"/><service name="rsync" extrainfo="protocol version 29" method="probed" conf="10" /></port>
+<port protocol="tcp" portid="1720"><state state="filtered" reason="no-response" reason_ttl="0"/><service name="H.323/Q.931" method="table" conf="3" /></port>
+<port protocol="tcp" portid="1984"><state state="open" reason="syn-ack" reason_ttl="46"/><service name="tcpwrapped" method="probed" conf="8" /></port>
+<port protocol="tcp" portid="2022"><state state="open" reason="syn-ack" reason_ttl="46"/><service name="ssh" product="OpenSSH" version="4.4" extrainfo="NetBSD 20061114; protocol 2.0" ostype="NetBSD" method="probed" conf="10" /></port>
+</ports>
+<os><portused state="open" proto="tcp" portid="22" />
+<portused state="closed" proto="tcp" portid="1" />
+<portused state="closed" proto="udp" portid="32193" />
+<osclass type="WAP" vendor="Apple" osfamily="NetBSD" osgen="4.X" accuracy="90" />
+<osclass type="general purpose" vendor="NetBSD" osfamily="NetBSD" osgen="4.X" accuracy="90" />
+<osmatch name="Apple AirPort Extreme WAP (runs NetBSD)" accuracy="90" line="1382"/>
+<osmatch name="NetBSD 4.99.4 (x86)" accuracy="90" line="15307"/>
+<osfingerprint fingerprint="SCAN(V=4.53%D=1/27%OT=22%CT=1%CU=32193%PV=N%DS=14%G=N%TM=479D2761%P=i686-pc-linux-gnu)&#xa;SEQ(SP=D7%GCD=1%ISR=E1%TI=I%II=I%SS=O%TS=0)&#xa;SEQ(SP=DD%GCD=1%ISR=DE%TS=0)&#xa;OPS(O1=M5B4NW0NNT01SNN%O2=M5B4NW0NNT01SNN%O3=M5B4NW0NNT01%O4=M5B4NW0NNT01SNN%O5=M5B4NW0NNT01SNN%O6=M5B4NNT01SNN)&#xa;WIN(W1=8000%W2=8000%W3=8000%W4=8000%W5=8000%W6=8000)&#xa;ECN(R=Y%DF=Y%T=3C%W=8000%O=M5B4NW0SNN%CC=N%Q=)&#xa;T1(R=Y%DF=Y%T=3C%S=O%A=S+%F=AS%RD=0%Q=)&#xa;T2(R=N)&#xa;T3(R=Y%DF=Y%T=3C%W=8000%S=O%A=S+%F=AS%O=M5B4NW0NNT01SNN%RD=0%Q=)&#xa;T4(R=Y%DF=N%T=3C%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)&#xa;T5(R=Y%DF=N%T=3C%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)&#xa;T6(R=Y%DF=N%T=3C%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)&#xa;T7(R=Y%DF=N%T=3C%W=0%S=Z%A=S%F=AR%O=%RD=0%Q=)&#xa;U1(R=Y%DF=N%T=FB%TOS=0%IPL=38%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=B43D%RUL=G%RUD=G)&#xa;IE(R=Y%DFI=N%T=FB%TOSI=Z%CD=S%SI=S%DLI=S)&#xa;" />
+</os>
+<distance value="14" />
+<tcpsequence index="221" class="unknown class" difficulty="Good luck!" values="CED51C9E,D2626FBA,D39DAEE3,D423A220,D782D901,D8072C18" />
+<ipidsequence class="Busy server or unknown class" values="712C,713A,713C,7142,7145,7146" />
+<tcptssequence class="zero timestamp" values="0,0,0,0,0,0" />
+<trace port="22" proto="tcp">
+<hop ttl="1" rtt="1.07" ipaddr="192.168.254.254"/>
+<hop ttl="2" rtt="19.01" ipaddr="200.217.89.32"/>
+<hop ttl="3" rtt="23.34" ipaddr="200.217.30.210" host="gigabitethernet6-1.90-cto-rn-rotd-02.telemar.net.br"/>
+<hop ttl="4" rtt="44.72" ipaddr="200.97.65.245" host="pos9-1-bvg-pe-rotd-02.telemar.net.br"/>
+<hop ttl="5" rtt="45.01" ipaddr="200.223.131.21" host="pos6-0-bvg-pe-rotn-01.telemar.net.br"/>
+<hop ttl="6" rtt="87.41" ipaddr="200.223.43.245"/>
+<hop ttl="7" rtt="242.32" ipaddr="200.223.131.138" host="PO12-0.ARC-RJ-ROTD-03.telemar.net.br"/>
+<hop ttl="8" rtt="197.53" ipaddr="208.51.142.233" host="so-6-2-0.ar2.MIA1.gblx.net"/>
+<hop ttl="10" rtt="329.86" ipaddr="64.215.195.22"/>
+<hop ttl="13" rtt="250.62" ipaddr="149.20.65.32" host="int-1-2-0.r2.sfo2.isc.org"/>
+<hop ttl="14" rtt="246.57" ipaddr="149.20.65.1" host="int-0-2.r1.sql1.isc.org"/>
+<hop ttl="15" rtt="252.50" ipaddr="204.152.190.12" host="www.netbsd.org"/>
+</trace>
+<times srtt="266011" rttvar="14051" to="322215" />
+</host>
+<host><status state="up" reason="reset"/>
+<address addr="72.14.207.99" addrtype="ipv4" />
+<hostnames><hostname name="eh-in-f99.google.com" type="PTR" /></hostnames>
+<ports><extraports state="filtered" count="1710">
+<extrareasons reason="no-responses" count="1710"/>
+</extraports>
+<port protocol="tcp" portid="80"><state state="open" reason="syn-ack" reason_ttl="49"/><service name="http" product="Google httpd" version="1.3" extrainfo="GFE" ostype="Linux" method="probed" conf="10" /><script id="HTML title" output="302 Moved" /></port>
+<port protocol="tcp" portid="113"><state state="closed" reason="reset" reason_ttl="245"/><service name="auth" method="table" conf="3" /></port>
+<port protocol="tcp" portid="179"><state state="closed" reason="reset" reason_ttl="240"/><service name="bgp" method="table" conf="3" /></port>
+<port protocol="tcp" portid="443"><state state="open" reason="syn-ack" reason_ttl="49"/><service name="http" product="Google httpd" version="1.3" extrainfo="GFE" ostype="Linux" tunnel="ssl" method="probed" conf="10" /><script id="SSLv2" output="server still supports SSLv2&#xa;&#x9;SSL2_DES_192_EDE3_CBC_WITH_MD5&#xa;&#x9;SSL2_RC2_CBC_128_CBC_WITH_MD5&#xa;&#x9;SSL2_RC4_128_WITH_MD5&#xa;&#x9;SSL2_DES_64_CBC_WITH_MD5&#xa;&#x9;SSL2_RC2_CBC_128_CBC_WITH_MD5&#xa;&#x9;SSL2_RC4_128_EXPORT40_WITH_MD5&#xa;" /><script id="HTML title" output="302 Moved" /></port>
+</ports>
+<os><portused state="open" proto="tcp" portid="80" />
+<portused state="closed" proto="tcp" portid="113" />
+<osfingerprint fingerprint="SCAN(V=4.53%D=1/27%OT=80%CT=113%CU=%PV=N%DS=14%G=N%TM=479D2761%P=i686-pc-linux-gnu)&#xa;SEQ(SP=D4%GCD=1%ISR=D8%TI=RD%TS=21)&#xa;SEQ(SP=C4%GCD=1%ISR=C5%TI=RD%TS=20)&#xa;OPS(O1=M596ST11NW0%O2=M596ST11NW0%O3=M596NNT11NW0%O4=M596ST11NW0%O5=M596ST11NW0%O6=M596ST11)&#xa;WIN(W1=1628%W2=1628%W3=1628%W4=1628%W5=1628%W6=1628)&#xa;ECN(R=Y%DF=N%TG=40%W=1658%O=M596NNSNW0%CC=N%Q=)&#xa;T1(R=Y%DF=N%TG=40%S=O%A=S+%F=AS%RD=0%Q=)&#xa;T2(R=N)&#xa;T3(R=Y%DF=N%TG=40%W=1628%S=O%A=S+%F=AS%O=M596ST11NW0%RD=0%Q=)&#xa;T4(R=Y%DF=N%TG=FF%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)&#xa;T5(R=Y%DF=N%TG=FF%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)&#xa;T6(R=Y%DF=N%TG=FF%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)&#xa;T7(R=Y%DF=N%TG=FF%W=0%S=Z%A=S%F=AR%O=%RD=0%Q=)&#xa;T7(R=Y%DF=N%TG=FF%W=0%S=Z%A=S%F=AR%O=%RD=0%Q=R)&#xa;U1(R=N)&#xa;IE(R=N)&#xa;" />
+</os>
+<uptime seconds="105" lastboot="Sun Jan 27 21:51:04 2008" />
+<distance value="14" />
+<tcpsequence index="196" class="unknown class" difficulty="Good luck!" values="CBEEB360,CC4F1B1F,CD1DFB63,CD000A94,CCEE5704,CD2469F3" />
+<ipidsequence class="Randomized" values="5AA6,7CDB,65B,1CCC,65C5,65C" />
+<tcptssequence class="other" values="837BC3C0,2208A10,8375A88B,8372C162,837C9852,8375A8CA" />
+<trace port="80" proto="tcp">
+<hop ttl="1" rtt="1.08" ipaddr="192.168.254.254"/>
+<hop ttl="2" rtt="20.26" ipaddr="200.217.89.32"/>
+<hop ttl="3" rtt="28.64" ipaddr="200.217.30.210" host="gigabitethernet6-1.90-cto-rn-rotd-02.telemar.net.br"/>
+<hop ttl="4" rtt="47.95" ipaddr="200.97.65.245" host="pos9-1-bvg-pe-rotd-02.telemar.net.br"/>
+<hop ttl="5" rtt="47.54" ipaddr="200.223.131.21" host="pos6-0-bvg-pe-rotn-01.telemar.net.br"/>
+<hop ttl="6" rtt="87.51" ipaddr="200.223.43.245"/>
+<hop ttl="7" rtt="116.31" ipaddr="200.223.43.242"/>
+<hop ttl="8" rtt="99.86" ipaddr="74.125.51.29"/>
+<hop ttl="9" rtt="115.52" ipaddr="209.85.250.242"/>
+<hop ttl="10" rtt="236.86" ipaddr="209.85.249.199"/>
+<hop ttl="11" rtt="232.95" ipaddr="216.239.43.146"/>
+<hop ttl="12" rtt="252.37" ipaddr="66.249.94.92"/>
+<hop ttl="13" rtt="239.82" ipaddr="72.14.236.130"/>
+<hop ttl="14" rtt="254.89" ipaddr="72.14.207.99" host="eh-in-f99.google.com"/>
+</trace>
+<times srtt="116825" rttvar="14467" to="174693" />
+</host>
+<host><status state="up" reason="reset"/>
+<address addr="72.14.253.83" addrtype="ipv4" />
+<hostnames><hostname name="po-in-f83.google.com" type="PTR" /></hostnames>
+<ports><extraports state="filtered" count="1710">
+<extrareasons reason="no-responses" count="1710"/>
+</extraports>
+<port protocol="tcp" portid="80"><state state="open" reason="syn-ack" reason_ttl="46"/><service name="http" product="Google httpd" version="1.3" extrainfo="GFE" ostype="Linux" method="probed" conf="10" /><script id="HTML title" output="302 Moved" /><script id="robots.txt" output="/news?output=xhtml&amp; /search /groups /images &#xa;/catalogs /catalogues /news /nwshp /? /addurl/image? &#xa;/pagead/ /relpage/ /relcontent /sorry/ /imgres &#xa;/keyword/ /u/ /univ/ /cobrand /custom &#xa;/advanced_group_search /advanced_search /googlesite /preferences &#xa;/setprefs /swr /url /default /m? /m/search? /wml? &#xa;/wml/search? /xhtml? /xhtml/search? /xml? /imode? &#xa;/imode/search? /jsky? /jsky/search? /pda? /pda/search? &#xa;/sprint_xhtml /sprint_wml /pqa /palm /gwt/ /purchases /hws &#xa;/bsd? /linux? /mac? /microsoft? /unclesam? &#xa;/answers/search?q= /local? /local_url /froogle? /products? &#xa;/froogle_ /product_ /products_ /print /books /patents? &#xa;/scholar? /complete /sponsoredlinks /videosearch? &#xa;/videopreview? /videoprograminfo? /maps? /mapstt? /mapslt? &#xa;/translate? /ie? /sms/demo? /katrina? /blogsearch? &#xa;/blogsearch/ /blogsearch_feeds /advanced_blog_search &#xa;/reader/ /uds/ /chart? /transit? /mbd? /extern_js/ &#xa;/calendar/feeds/ /calendar/ical/ /cl2/feeds/ /cl2/ical/ &#xa;/coop/directory /coop/manage /trends? /trends/music? &#xa;/notebook/search? /music /browsersync /call /archivesearch? &#xa;/archivesearch/url /archivesearch/advanced_search &#xa;/base/search? /base/reportbadoffer /base/s2 &#xa;/urchin_test/ /movies? /codesearch? &#xa;/codesearch/feeds/search? /wapsearch? /safebrowsing /reviews/search? &#xa;/orkut/albums /jsapi /views? /c/ /cbk &#xa;/recharge/dashboard/car /recharge/dashboard/static/ /translate_c? &#xa;/s2 /transconsole/portal/ /gcc/ /aclk /cse? &#xa;/tbproxy/ " /></port>
+<port protocol="tcp" portid="113"><state state="closed" reason="reset" reason_ttl="246"/><service name="auth" method="table" conf="3" /></port>
+<port protocol="tcp" portid="179"><state state="closed" reason="reset" reason_ttl="237"/><service name="bgp" method="table" conf="3" /></port>
+<port protocol="tcp" portid="443"><state state="open" reason="syn-ack" reason_ttl="46"/><service name="https" tunnel="ssl" method="table" conf="3" /><script id="HTML title" output="302 Moved" /><script id="SSLv2" output="server still supports SSLv2&#xa;&#x9;SSL2_DES_192_EDE3_CBC_WITH_MD5&#xa;&#x9;SSL2_RC2_CBC_128_CBC_WITH_MD5&#xa;&#x9;SSL2_RC4_128_WITH_MD5&#xa;&#x9;SSL2_DES_64_CBC_WITH_MD5&#xa;&#x9;SSL2_RC2_CBC_128_CBC_WITH_MD5&#xa;&#x9;SSL2_RC4_128_EXPORT40_WITH_MD5&#xa;" /></port>
+</ports>
+<os><portused state="open" proto="tcp" portid="80" />
+<portused state="closed" proto="tcp" portid="113" />
+<osfingerprint fingerprint="SCAN(V=4.53%D=1/27%OT=80%CT=113%CU=%PV=N%DS=14%G=N%TM=479D2761%P=i686-pc-linux-gnu)&#xa;SEQ(SP=105%GCD=1%ISR=10A%TI=RD%TS=21)&#xa;SEQ(SP=105%GCD=1%ISR=106%TI=RD%TS=21)&#xa;OPS(O1=M596ST11NW6%O2=M596ST11NW6%O3=M596NNT11NW6%O4=M596ST11NW6%O5=M596ST11NW6%O6=M596ST11)&#xa;WIN(W1=1628%W2=1628%W3=1628%W4=1628%W5=1628%W6=1628)&#xa;ECN(R=Y%DF=N%TG=40%W=1658%O=M596NNSNW6%CC=N%Q=)&#xa;T1(R=Y%DF=N%TG=40%S=O%A=S+%F=AS%RD=0%Q=)&#xa;T2(R=N)&#xa;T3(R=Y%DF=N%TG=40%W=1628%S=O%A=S+%F=AS%O=M596ST11NW6%RD=0%Q=)&#xa;T4(R=Y%DF=N%TG=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)&#xa;T5(R=Y%DF=N%TG=FF%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)&#xa;T5(R=Y%DF=N%TG=FF%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=R)&#xa;T6(R=Y%DF=N%TG=FF%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)&#xa;T7(R=Y%DF=N%TG=FF%W=0%S=Z%A=S%F=AR%O=%RD=0%Q=)&#xa;U1(R=N)&#xa;IE(R=N)&#xa;" />
+</os>
+<uptime seconds="105" lastboot="Sun Jan 27 21:51:04 2008" />
+<distance value="14" />
+<tcpsequence index="261" class="unknown class" difficulty="Good luck!" values="6ACD6329,921C6817,E6012864,90104A22,90651704,E5CE7F5A" />
+<ipidsequence class="Randomized" values="D6CC,94A,2253,2AE5,A75E,2254" />
+<tcptssequence class="other" values="52F3E255,22566720,D6046C6E,D4792BF0,22539966,D6046E1B" />
+<trace port="80" proto="tcp">
+<hop ttl="1" rtt="1.60" ipaddr="192.168.254.254"/>
+<hop ttl="2" rtt="25.93" ipaddr="200.217.89.32"/>
+<hop ttl="3" rtt="33.57" ipaddr="200.217.30.250" host="gigabitethernet5-1.80-cto-rn-rotd-02.telemar.net.br"/>
+<hop ttl="4" rtt="47.18" ipaddr="200.97.65.245" host="pos9-1-bvg-pe-rotd-02.telemar.net.br"/>
+<hop ttl="5" rtt="47.91" ipaddr="200.223.131.21" host="pos6-0-bvg-pe-rotn-01.telemar.net.br"/>
+<hop ttl="6" rtt="87.59" ipaddr="200.223.43.245"/>
+<hop ttl="7" rtt="116.58" ipaddr="200.223.43.242"/>
+<hop ttl="8" rtt="117.01" ipaddr="74.125.51.5"/>
+<hop ttl="9" rtt="117.63" ipaddr="209.85.250.242"/>
+<hop ttl="10" rtt="427.06" ipaddr="209.85.249.199"/>
+<hop ttl="11" rtt="279.22" ipaddr="72.14.236.213"/>
+<hop ttl="12" rtt="305.96" ipaddr="209.85.130.22"/>
+<hop ttl="13" rtt="312.64" ipaddr="216.239.46.50"/>
+<hop ttl="14" rtt="291.35" ipaddr="209.85.250.144"/>
+<hop ttl="15" rtt="294.36" ipaddr="216.239.48.143"/>
+<hop ttl="16" rtt="306.89" ipaddr="209.85.251.153"/>
+<hop ttl="17" rtt="291.39" ipaddr="74.125.30.170"/>
+<hop ttl="18" rtt="293.11" ipaddr="74.125.30.42"/>
+<hop ttl="19" rtt="312.18" ipaddr="72.14.253.83" host="po-in-f83.google.com"/>
+</trace>
+<times srtt="244322" rttvar="98383" to="637854" />
+</host>
+<runstats><finished time="1201481569" timestr="Sun Jan 27 21:52:49 2008"/><hosts up="8" down="0" total="8" />
+<!-- Nmap done at Sun Jan 27 21:52:49 2008; 8 IP addresses (8 hosts up) scanned in 2567.750 seconds -->
+</runstats></nmaprun>
diff --git a/zenmap/radialnet/util/__init__.py b/zenmap/radialnet/util/__init__.py
new file mode 100644
index 0000000..c314dd7
--- /dev/null
+++ b/zenmap/radialnet/util/__init__.py
@@ -0,0 +1,56 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
diff --git a/zenmap/radialnet/util/drawing.py b/zenmap/radialnet/util/drawing.py
new file mode 100644
index 0000000..0cc3763
--- /dev/null
+++ b/zenmap/radialnet/util/drawing.py
@@ -0,0 +1,67 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+
+def cairo_to_gdk_color(color):
+ """
+ """
+ new_color = list(range(len(color)))
+
+ for i in range(len(color)):
+ new_color[i] = int(color[i] * 65535)
+
+ return new_color
diff --git a/zenmap/radialnet/util/geometry.py b/zenmap/radialnet/util/geometry.py
new file mode 100644
index 0000000..109443b
--- /dev/null
+++ b/zenmap/radialnet/util/geometry.py
@@ -0,0 +1,152 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+import math
+
+
+def is_in_square(point, half_side, center=(0, 0)):
+ """
+ """
+ x, y = point
+ a, b = center
+
+ if a + half_side >= x >= a - half_side:
+ if b + half_side >= y >= b - half_side:
+ return True
+
+ return False
+
+
+def is_in_circle(point, radius=1, center=(0, 0)):
+ """
+ """
+ x, y = point
+ a, b = center
+
+ if ((x - a) ** 2 + (y - b) ** 2) <= (radius ** 2):
+ return True
+
+ return False
+
+
+def atan_scale(point, scale_ceil):
+ """
+ """
+ new_point = float(10.0 * point / scale_ceil) - 5
+ return math.atan(abs(new_point))
+
+
+def normalize_angle(angle):
+ """
+ """
+ new_angle = 360.0 * (float(angle / 360) - int(angle / 360))
+
+ if new_angle < 0:
+ return 360 + new_angle
+
+ return new_angle
+
+
+def is_between_angles(a, b, c):
+ """
+ """
+ a = normalize_angle(a)
+ b = normalize_angle(b)
+ c = normalize_angle(c)
+
+ if a > b:
+
+ if c >= a and c <= 360 or c <= b:
+ return True
+
+ return False
+
+ else:
+
+ if c >= a and c <= b:
+ return True
+
+ return False
+
+
+def angle_distance(a, b):
+ """
+ """
+ distance = abs(normalize_angle(a) - normalize_angle(b))
+
+ if distance > 180:
+ return 360 - distance
+
+ return distance
+
+
+def calculate_short_path(iangle, fangle):
+ """
+ """
+ if iangle - fangle > 180:
+ fangle += 360
+
+ if iangle - fangle < -180:
+ fangle -= 360
+
+ return iangle, fangle
+
+
+def angle_from_object(distance, size):
+ """
+ """
+ return math.degrees(math.atan2(size / 2.0, distance))
diff --git a/zenmap/radialnet/util/integration.py b/zenmap/radialnet/util/integration.py
new file mode 100644
index 0000000..6efd9e8
--- /dev/null
+++ b/zenmap/radialnet/util/integration.py
@@ -0,0 +1,269 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+from radialnet.core.Graph import Graph
+from radialnet.gui.RadialNet import NetNode
+
+import math
+
+
+COLORS = [(0.0, 1.0, 0.0),
+ (1.0, 1.0, 0.0),
+ (1.0, 0.0, 0.0)]
+
+BASE_RADIUS = 5.5
+NONE_RADIUS = 4.5
+
+
+def set_node_info(node, host):
+ """
+ """
+ node.set_host(host)
+
+ radius = BASE_RADIUS + 2 * math.log(
+ node.get_info("number_of_open_ports") + 1)
+
+ node.set_draw_info({"color": COLORS[node.get_info("vulnerability_score")],
+ "radius": radius})
+
+
+class TracerouteHostInfo(object):
+ """This is a minimal implementation of HostInfo, sufficient to
+ represent the information in an intermediate traceroute hop."""
+ def __init__(self):
+ self.ip = None
+ self.ipv6 = None
+ self.mac = None
+ self.hostname = None
+ self.ports = []
+ self.extraports = []
+ self.osmatches = []
+
+ def get_hostname(self):
+ return self.hostname
+
+ def get_best_osmatch(self):
+ if not self.osmatches:
+ return None
+
+ def osmatch_key(osmatch):
+ try:
+ return -float(osmatch["accuracy"])
+ except ValueError:
+ return 0
+
+ return sorted(self.osmatches, key=osmatch_key)[0]
+
+ hostnames = property(lambda self: self.hostname and [self.hostname] or [])
+
+
+def find_hop_by_ttl(hops, ttl):
+ assert ttl >= 0, "ttl must be non-negative"
+ if ttl == 0: # Same machine (i.e. localhost)
+ return {"ipaddr": "127.0.0.1/8"}
+ for h in hops:
+ if ttl == int(h["ttl"]):
+ return h
+ return None
+
+
+def make_graph_from_hosts(hosts):
+ #hosts = parser.get_root().search_children('host', deep=True)
+ graph = Graph()
+ nodes = list()
+ node_cache = {}
+ ancestor_node_cache = {}
+ descendant_node_cache = {}
+
+ # Setting initial reference host
+ main_node = NetNode()
+ nodes.append(main_node)
+
+ localhost = TracerouteHostInfo()
+ localhost.ip = {"addr": "127.0.0.1/8", "type": "ipv4"}
+ localhost.hostname = "localhost"
+ main_node.set_host(localhost)
+ main_node.set_draw_info(
+ {"valid": True, "color": (0, 0, 0), "radius": NONE_RADIUS})
+
+ #Save endpoints for attaching scanned hosts to
+ endpoints = {}
+ # For each host in hosts just mount the graph
+ for host in hosts:
+ trace = host.trace
+ endpoints[host] = nodes[0]
+ hops = trace.get("hops")
+
+ # If host has traceroute information mount graph
+ if hops is not None and len(hops) > 0:
+ prev_node = nodes[0]
+ hops = trace.get("hops", [])
+ ttls = [int(hop["ttl"]) for hop in hops]
+
+ # Getting nodes of host by ttl
+ for ttl in range(1, max(ttls) + 1):
+ if ttl in ttls:
+ hop = find_hop_by_ttl(hops, ttl)
+ node = node_cache.get(hop["ipaddr"])
+ if node is None:
+ node = NetNode()
+ nodes.append(node)
+
+ hop_host = TracerouteHostInfo()
+ hop_host.ip = {
+ "addr": hop["ipaddr"],
+ "type": "",
+ "vendor": ""
+ }
+ node.set_draw_info({"valid": True})
+ node.set_draw_info({"color": (1, 1, 1),
+ "radius": NONE_RADIUS})
+
+ if hop["host"] != "":
+ hop_host.hostname = hop["host"]
+
+ node.set_host(hop_host)
+
+ node_cache[node.get_info("ip")] = node
+
+ rtt = hop["rtt"]
+ if rtt != "--":
+ graph.set_connection(node, prev_node, float(rtt))
+ else:
+ graph.set_connection(node, prev_node)
+ else:
+ # Add an "anonymous" node only if there isn't already a
+ # node equivalent to it (i.e. at same distance from the
+ # previous "real" node)
+
+ pre_hop = None
+ pre_hop_distance = 0
+ for i in range(1, ttl + 1):
+ pre_hop = find_hop_by_ttl(hops, ttl - i)
+ if pre_hop is not None:
+ pre_hop_distance = i
+ break
+
+ post_hop = None
+ post_hop_distance = 0
+ for i in range(1, max(ttls) - ttl):
+ post_hop = find_hop_by_ttl(hops, ttl + i)
+ if post_hop is not None:
+ post_hop_distance = i
+ break
+
+ assert pre_hop is not None, \
+ "pre_hop should have become localhost if nothing else" # noqa
+
+ ancestor_key = (pre_hop["ipaddr"], pre_hop_distance)
+ descendant_key = None
+ if post_hop is not None:
+ descendant_key = \
+ (post_hop["ipaddr"], post_hop_distance)
+
+ if ancestor_key in ancestor_node_cache:
+ node = ancestor_node_cache[ancestor_key]
+ elif (descendant_key is not None and
+ descendant_key in descendant_node_cache):
+ node = descendant_node_cache[descendant_key]
+ graph.set_connection(node, prev_node)
+ else:
+ node = NetNode()
+ nodes.append(node)
+
+ node.set_draw_info({"valid": False})
+ node.set_draw_info(
+ {"color": (1, 1, 1), "radius": NONE_RADIUS})
+
+ graph.set_connection(node, prev_node)
+
+ ancestor_node_cache[ancestor_key] = node
+ if descendant_key is not None:
+ descendant_node_cache[descendant_key] = node
+
+ prev_node = node
+ endpoints[host] = node
+
+ # For each fully scanned host
+ for host in hosts:
+ ip = host.ip
+ if ip is None:
+ ip = host.ipv6
+
+ node = node_cache.get(ip["addr"])
+ if node is None:
+ node = NetNode()
+ nodes.append(node)
+
+ node.set_draw_info({"no_route": True})
+
+ graph.set_connection(node, endpoints[host])
+
+ node.set_draw_info({"valid": True})
+ node.set_draw_info({"scanned": True})
+ set_node_info(node, host)
+ node_cache[node.get_info("ip")] = node
+
+ graph.set_nodes(nodes)
+ graph.set_main_node(main_node)
+
+ return graph
+
+
+def make_graph_from_nmap_parser(parser):
+ return make_graph_from_hosts(
+ parser.get_root().search_children('host', deep=True))
diff --git a/zenmap/radialnet/util/misc.py b/zenmap/radialnet/util/misc.py
new file mode 100644
index 0000000..10f24b9
--- /dev/null
+++ b/zenmap/radialnet/util/misc.py
@@ -0,0 +1,115 @@
+# vim: set fileencoding=utf-8 :
+
+# ***********************IMPORTANT NMAP LICENSE TERMS************************
+# *
+# * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+# * Project"). Nmap is also a registered trademark of the Nmap Project.
+# *
+# * This program is distributed under the terms of the Nmap Public Source
+# * License (NPSL). The exact license text applying to a particular Nmap
+# * release or source code control revision is contained in the LICENSE
+# * file distributed with that version of Nmap or source code control
+# * revision. More Nmap copyright/legal information is available from
+# * https://nmap.org/book/man-legal.html, and further information on the
+# * NPSL license itself can be found at https://nmap.org/npsl/ . This
+# * header summarizes some key points from the Nmap license, but is no
+# * substitute for the actual license text.
+# *
+# * Nmap is generally free for end users to download and use themselves,
+# * including commercial use. It is available from https://nmap.org.
+# *
+# * The Nmap license generally prohibits companies from using and
+# * redistributing Nmap in commercial products, but we sell a special Nmap
+# * OEM Edition with a more permissive license and special features for
+# * this purpose. See https://nmap.org/oem/
+# *
+# * If you have received a written Nmap license agreement or contract
+# * stating terms other than these (such as an Nmap OEM license), you may
+# * choose to use and redistribute Nmap under those terms instead.
+# *
+# * The official Nmap Windows builds include the Npcap software
+# * (https://npcap.com) for packet capture and transmission. It is under
+# * separate license terms which forbid redistribution without special
+# * permission. So the official Nmap Windows builds may not be redistributed
+# * without special permission (such as an Nmap OEM license).
+# *
+# * Source is provided to this software because we believe users have a
+# * right to know exactly what a program is going to do before they run it.
+# * This also allows you to audit the software for security holes.
+# *
+# * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+# * new features. You are highly encouraged to submit your changes as a Github PR
+# * or by email to the dev@nmap.org mailing list for possible incorporation into
+# * the main distribution. Unless you specify otherwise, it is understood that
+# * you are offering us very broad rights to use your submissions as described in
+# * the Nmap Public Source License Contributor Agreement. This is important
+# * because we fund the project by selling licenses with various terms, and also
+# * because the inability to relicense code has caused devastating problems for
+# * other Free Software projects (such as KDE and NASM).
+# *
+# * The free version of Nmap is distributed in the hope that it will be
+# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+# * indemnification and commercial support are all available through the
+# * Npcap OEM program--see https://nmap.org/oem/
+# *
+# ***************************************************************************/
+
+from radialnet.core.Coordinate import CartesianCoordinate
+from radialnet.util.geometry import normalize_angle
+import math
+
+
+def ipv4_compare(ip1, ip2):
+ """
+ """
+ ip1 = [int(i) for i in ip1.split('.')]
+ ip2 = [int(i) for i in ip2.split('.')]
+
+ for i in range(4):
+
+ if ip1[i] != ip2[i]:
+
+ if ip1[i] < ip2[i]:
+ return -1
+
+ else:
+ return 1
+
+ return 0
+
+
+def swap(list, a, b):
+ """
+ """
+ list[a], list[b] = list[b], list[a]
+
+
+def sort_children(children, father):
+ """
+ """
+ if len(children) < 2:
+ return children
+
+ # create angle reference
+ f_x, f_y = father.get_cartesian_coordinate()
+
+ for child in children:
+
+ c_x, c_y = child.get_cartesian_coordinate()
+ _, angle = CartesianCoordinate(c_x - f_x, c_y - f_y).to_polar()
+
+ child.set_draw_info({'angle_from_father': math.degrees(angle)})
+
+ return sort_children_by_angle(children)
+
+
+def sort_children_by_angle(children):
+ """
+ """
+
+ vector = list(children)
+ vector.sort(
+ key=lambda c: normalize_angle(
+ c.get_draw_info('angle_from_father')))
+ return vector