1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3 4# ***********************IMPORTANT NMAP LICENSE TERMS************************ 5# * * 6# * The Nmap Security Scanner is (C) 1996-2020 Insecure.Com LLC ("The Nmap * 7# * Project"). Nmap is also a registered trademark of the Nmap Project. * 8# * * 9# * This program is distributed under the terms of the Nmap Public Source * 10# * License (NPSL). The exact license text applying to a particular Nmap * 11# * release or source code control revision is contained in the LICENSE * 12# * file distributed with that version of Nmap or source code control * 13# * revision. More Nmap copyright/legal information is available from * 14# * https://nmap.org/book/man-legal.html, and further information on the * 15# * NPSL license itself can be found at https://nmap.org/npsl. This header * 16# * summarizes some key points from the Nmap license, but is no substitute * 17# * for the actual license text. * 18# * * 19# * Nmap is generally free for end users to download and use themselves, * 20# * including commercial use. It is available from https://nmap.org. * 21# * * 22# * The Nmap license generally prohibits companies from using and * 23# * redistributing Nmap in commercial products, but we sell a special Nmap * 24# * OEM Edition with a more permissive license and special features for * 25# * this purpose. See https://nmap.org/oem * 26# * * 27# * If you have received a written Nmap license agreement or contract * 28# * stating terms other than these (such as an Nmap OEM license), you may * 29# * choose to use and redistribute Nmap under those terms instead. * 30# * * 31# * The official Nmap Windows builds include the Npcap software * 32# * (https://npcap.org) for packet capture and transmission. It is under * 33# * separate license terms which forbid redistribution without special * 34# * permission. So the official Nmap Windows builds may not be * 35# * redistributed without special permission (such as an Nmap OEM * 36# * license). * 37# * * 38# * Source is provided to this software because we believe users have a * 39# * right to know exactly what a program is going to do before they run it. * 40# * This also allows you to audit the software for security holes. * 41# * * 42# * Source code also allows you to port Nmap to new platforms, fix bugs, * 43# * and add new features. You are highly encouraged to submit your * 44# * changes as a Github PR or by email to the dev@nmap.org mailing list * 45# * for possible incorporation into the main distribution. Unless you * 46# * specify otherwise, it is understood that you are offering us very * 47# * broad rights to use your submissions as described in the Nmap Public * 48# * Source License Contributor Agreement. This is important because we * 49# * fund the project by selling licenses with various terms, and also * 50# * because the inability to relicense code has caused devastating * 51# * problems for other Free Software projects (such as KDE and NASM). * 52# * * 53# * The free version of Nmap is distributed in the hope that it will be * 54# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * 55# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, * 56# * indemnification and commercial support are all available through the * 57# * Npcap OEM program--see https://nmap.org/oem. * 58# * * 59# ***************************************************************************/ 60 61import gtk 62 63from zenmapGUI.higwidgets.higboxes import HIGVBox 64from zenmapGUI.Icons import get_os_icon 65import zenmapCore.I18N # lgtm[py/unused-import] 66 67 68def treemodel_get_addrs_for_sort(model, iter): 69 host = model.get_value(iter, 0) 70 return host.get_addrs_for_sort() 71 72 73# Used to sort hosts by address. 74def cmp_treemodel_addr(model, iter_a, iter_b): 75 addrs_a = treemodel_get_addrs_for_sort(model, iter_a) 76 addrs_b = treemodel_get_addrs_for_sort(model, iter_b) 77 return cmp(addrs_a, addrs_b) 78 79 80class ScanHostsView(HIGVBox, object): 81 HOST_MODE, SERVICE_MODE = range(2) 82 83 def __init__(self, scan_interface): 84 HIGVBox.__init__(self) 85 86 self._scan_interface = scan_interface 87 self._create_widgets() 88 self._connect_widgets() 89 self._pack_widgets() 90 self._set_scrolled() 91 self._set_host_list() 92 self._set_service_list() 93 94 self._pack_expand_fill(self.main_vbox) 95 96 self.mode = None 97 98 # Default mode is host mode 99 self.host_mode(self.host_mode_button) 100 101 self.host_view.show_all() 102 self.service_view.show_all() 103 104 def _create_widgets(self): 105 # Mode buttons 106 self.host_mode_button = gtk.ToggleButton(_("Hosts")) 107 self.service_mode_button = gtk.ToggleButton(_("Services")) 108 self.buttons_box = gtk.HBox() 109 110 # Main window vbox 111 self.main_vbox = HIGVBox() 112 113 # Host list 114 self.host_list = gtk.ListStore(object, str, str) 115 self.host_list.set_sort_func(1000, cmp_treemodel_addr) 116 self.host_list.set_sort_column_id(1000, gtk.SORT_ASCENDING) 117 self.host_view = gtk.TreeView(self.host_list) 118 self.pic_column = gtk.TreeViewColumn(_('OS')) 119 self.host_column = gtk.TreeViewColumn(_('Host')) 120 self.os_cell = gtk.CellRendererPixbuf() 121 self.host_cell = gtk.CellRendererText() 122 123 # Service list 124 self.service_list = gtk.ListStore(str) 125 self.service_list.set_sort_column_id(0, gtk.SORT_ASCENDING) 126 self.service_view = gtk.TreeView(self.service_list) 127 self.service_column = gtk.TreeViewColumn(_('Service')) 128 self.service_cell = gtk.CellRendererText() 129 130 self.scrolled = gtk.ScrolledWindow() 131 132 def _pack_widgets(self): 133 self.main_vbox.set_spacing(0) 134 self.main_vbox.set_border_width(0) 135 self.main_vbox._pack_noexpand_nofill(self.buttons_box) 136 self.main_vbox._pack_expand_fill(self.scrolled) 137 138 self.host_mode_button.set_active(True) 139 140 self.buttons_box.set_border_width(5) 141 self.buttons_box.pack_start(self.host_mode_button) 142 self.buttons_box.pack_start(self.service_mode_button) 143 144 def _connect_widgets(self): 145 self.host_mode_button.connect("toggled", self.host_mode) 146 self.service_mode_button.connect("toggled", self.service_mode) 147 148 def host_mode(self, widget): 149 self._remove_scrolled_child() 150 if widget.get_active(): 151 self.mode = self.HOST_MODE 152 self.service_mode_button.set_active(False) 153 self.scrolled.add(self.host_view) 154 else: 155 self.service_mode_button.set_active(True) 156 157 def service_mode(self, widget): 158 self._remove_scrolled_child() 159 if widget.get_active(): 160 self.mode = self.SERVICE_MODE 161 self.host_mode_button.set_active(False) 162 self.scrolled.add(self.service_view) 163 else: 164 self.host_mode_button.set_active(True) 165 166 def _remove_scrolled_child(self): 167 try: 168 child = self.scrolled.get_child() 169 self.scrolled.remove(child) 170 except Exception: 171 pass 172 173 def _set_scrolled(self): 174 self.scrolled.set_border_width(5) 175 self.scrolled.set_size_request(150, -1) 176 self.scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) 177 178 def _set_service_list(self): 179 self.service_view.set_enable_search(True) 180 self.service_view.set_search_column(0) 181 182 selection = self.service_view.get_selection() 183 selection.set_mode(gtk.SELECTION_MULTIPLE) 184 self.service_view.append_column(self.service_column) 185 186 self.service_column.set_resizable(True) 187 self.service_column.set_sort_column_id(0) 188 self.service_column.set_reorderable(True) 189 self.service_column.pack_start(self.service_cell, True) 190 self.service_column.set_attributes(self.service_cell, text=0) 191 192 def _set_host_list(self): 193 self.host_view.set_enable_search(True) 194 self.host_view.set_search_column(1) 195 196 selection = self.host_view.get_selection() 197 selection.set_mode(gtk.SELECTION_MULTIPLE) 198 199 self.host_view.append_column(self.pic_column) 200 self.host_view.append_column(self.host_column) 201 202 self.host_column.set_resizable(True) 203 self.pic_column.set_resizable(True) 204 205 self.host_column.set_sort_column_id(1000) 206 self.pic_column.set_sort_column_id(1) 207 208 self.host_column.set_reorderable(True) 209 self.pic_column.set_reorderable(True) 210 211 self.pic_column.pack_start(self.os_cell, True) 212 self.host_column.pack_start(self.host_cell, True) 213 214 self.pic_column.set_min_width(35) 215 self.pic_column.set_attributes(self.os_cell, stock_id=1) 216 self.host_column.set_attributes(self.host_cell, text=2) 217 218 def mass_update(self, hosts): 219 """Update the internal ListStores to reflect the hosts and services 220 passed in. Hosts that have not changed are left alone.""" 221 hosts = set(hosts) 222 services = set() 223 for h in hosts: 224 services.update([s["service_name"] for s in h.services]) 225 226 # Disable sorting while elements are added. See the PyGTK FAQ 13.43, 227 # "Are there tips for improving performance when adding many rows to a 228 # Treeview?" 229 sort_column_id = self.host_list.get_sort_column_id() 230 self.host_list.set_default_sort_func(lambda *args: -1) 231 self.host_list.set_sort_column_id(-1, gtk.SORT_ASCENDING) 232 self.host_view.freeze_child_notify() 233 self.host_view.set_model(None) 234 235 it = self.host_list.get_iter_first() 236 # Remove any of our ListStore hosts that aren't in the list passed in. 237 while it: 238 host = self.host_list.get_value(it, 0) 239 if host in hosts: 240 hosts.remove(host) 241 self.host_list.set(it, 1, get_os_icon(host)) 242 it = self.host_list.iter_next(it) 243 else: 244 if not self.host_list.remove(it): 245 it = None 246 # Add any remaining hosts into our ListStore. 247 for host in hosts: 248 self.add_host(host) 249 250 # Reenable sorting. 251 if sort_column_id != (None, None): 252 self.host_list.set_sort_column_id(*sort_column_id) 253 self.host_view.set_model(self.host_list) 254 self.host_view.thaw_child_notify() 255 256 it = self.service_list.get_iter_first() 257 # Remove any of our ListStore services that aren't in the list passed 258 # in. 259 while it: 260 service_name = self.service_list.get_value(it, 0) 261 if service_name in services: 262 services.remove(service_name) 263 it = self.service_list.iter_next(it) 264 else: 265 if not self.service_list.remove(it): 266 it = None 267 # Add any remaining services into our ListStore. 268 for service_name in services: 269 self.add_service(service_name) 270 271 def add_host(self, host): 272 self.host_list.append([host, get_os_icon(host), host.get_hostname()]) 273 274 def add_service(self, service): 275 self.service_list.append([service]) 276 277if __name__ == "__main__": 278 w = gtk.Window() 279 h = ScanHostsView(None) 280 w.add(h) 281 w.show_all() 282 gtk.main() 283