1# vim: set encoding=utf-8 : 2 3# ***********************IMPORTANT NMAP LICENSE TERMS************************ 4# * * 5# * The Nmap Security Scanner is (C) 1996-2020 Insecure.Com LLC ("The Nmap * 6# * Project"). Nmap is also a registered trademark of the Nmap Project. * 7# * * 8# * This program is distributed under the terms of the Nmap Public Source * 9# * License (NPSL). The exact license text applying to a particular Nmap * 10# * release or source code control revision is contained in the LICENSE * 11# * file distributed with that version of Nmap or source code control * 12# * revision. More Nmap copyright/legal information is available from * 13# * https://nmap.org/book/man-legal.html, and further information on the * 14# * NPSL license itself can be found at https://nmap.org/npsl. This header * 15# * summarizes some key points from the Nmap license, but is no substitute * 16# * for the actual license text. * 17# * * 18# * Nmap is generally free for end users to download and use themselves, * 19# * including commercial use. It is available from https://nmap.org. * 20# * * 21# * The Nmap license generally prohibits companies from using and * 22# * redistributing Nmap in commercial products, but we sell a special Nmap * 23# * OEM Edition with a more permissive license and special features for * 24# * this purpose. See https://nmap.org/oem * 25# * * 26# * If you have received a written Nmap license agreement or contract * 27# * stating terms other than these (such as an Nmap OEM license), you may * 28# * choose to use and redistribute Nmap under those terms instead. * 29# * * 30# * The official Nmap Windows builds include the Npcap software * 31# * (https://npcap.org) for packet capture and transmission. It is under * 32# * separate license terms which forbid redistribution without special * 33# * permission. So the official Nmap Windows builds may not be * 34# * redistributed without special permission (such as an Nmap OEM * 35# * license). * 36# * * 37# * Source is provided to this software because we believe users have a * 38# * right to know exactly what a program is going to do before they run it. * 39# * This also allows you to audit the software for security holes. * 40# * * 41# * Source code also allows you to port Nmap to new platforms, fix bugs, * 42# * and add new features. You are highly encouraged to submit your * 43# * changes as a Github PR or by email to the dev@nmap.org mailing list * 44# * for possible incorporation into the main distribution. Unless you * 45# * specify otherwise, it is understood that you are offering us very * 46# * broad rights to use your submissions as described in the Nmap Public * 47# * Source License Contributor Agreement. This is important because we * 48# * fund the project by selling licenses with various terms, and also * 49# * because the inability to relicense code has caused devastating * 50# * problems for other Free Software projects (such as KDE and NASM). * 51# * * 52# * The free version of Nmap is distributed in the hope that it will be * 53# * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * 54# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, * 55# * indemnification and commercial support are all available through the * 56# * Npcap OEM program--see https://nmap.org/oem. * 57# * * 58# ***************************************************************************/ 59 60import gtk 61import math 62import cairo 63import gobject 64 65import radialnet.util.geometry as geometry 66import radialnet.util.misc as misc 67 68from radialnet.core.Coordinate import PolarCoordinate, CartesianCoordinate 69from radialnet.core.Interpolation import Linear2DInterpolator 70from radialnet.core.Graph import Node 71from radialnet.gui.NodeWindow import NodeWindow 72from radialnet.gui.Image import Icons, get_pixels_for_cairo_image_surface 73 74from zenmapCore.BasePaths import fs_enc 75 76REGION_COLORS = [(1.0, 0.0, 0.0), (1.0, 1.0, 0.0), (0.0, 1.0, 0.0)] 77REGION_RED = 0 78REGION_YELLOW = 1 79REGION_GREEN = 2 80 81SQUARE_TYPES = ['router', 'switch', 'wap'] 82 83ICON_DICT = {'router': 'router', 84 'switch': 'switch', 85 'wap': 'wireless', 86 'firewall': 'firewall'} 87 88POINTER_JUMP_TO = 0 89POINTER_INFO = 1 90POINTER_GROUP = 2 91POINTER_FILL = 3 92 93LAYOUT_SYMMETRIC = 0 94LAYOUT_WEIGHTED = 1 95 96INTERPOLATION_CARTESIAN = 0 97INTERPOLATION_POLAR = 1 98 99FILE_TYPE_PDF = 1 100FILE_TYPE_PNG = 2 101FILE_TYPE_PS = 3 102FILE_TYPE_SVG = 4 103 104 105class RadialNet(gtk.DrawingArea): 106 """ 107 Radial network visualization widget 108 """ 109 def __init__(self, layout=LAYOUT_SYMMETRIC): 110 """ 111 Constructor method of RadialNet widget class 112 @type number_of_rings: number 113 @param number_of_rings: Number of rings in radial layout 114 """ 115 self.__center_of_widget = (0, 0) 116 self.__graph = None 117 118 self.__number_of_rings = 0 119 self.__ring_gap = 30 120 self.__min_ring_gap = 10 121 122 self.__layout = layout 123 self.__interpolation = INTERPOLATION_POLAR 124 self.__interpolation_slow_in_out = True 125 126 self.__animating = False 127 self.__animation_rate = 1000 // 60 # 60Hz (human perception factor) 128 self.__number_of_frames = 60 129 130 self.__scale = 1.0 131 # rotated so that single-host traceroute doesn't have overlapping hosts 132 self.__rotate = 225 133 self.__translation = (0, 0) 134 135 self.__button1_press = False 136 self.__button2_press = False 137 self.__button3_press = False 138 139 self.__last_motion_point = None 140 141 self.__fisheye = False 142 self.__fisheye_ring = 0 143 self.__fisheye_spread = 0.5 144 self.__fisheye_interest = 2 145 146 self.__show_address = True 147 self.__show_hostname = True 148 self.__show_icon = True 149 self.__show_latency = False 150 self.__show_ring = True 151 self.__show_region = True 152 self.__region_color = REGION_RED 153 154 self.__node_views = dict() 155 self.__last_group_node = None 156 157 self.__pointer_status = POINTER_JUMP_TO 158 159 self.__sorted_nodes = list() 160 161 self.__icon = Icons() 162 163 super(RadialNet, self).__init__() 164 165 self.connect('expose_event', self.expose) 166 self.connect('button_press_event', self.button_press) 167 self.connect('button_release_event', self.button_release) 168 self.connect('motion_notify_event', self.motion_notify) 169 self.connect('enter_notify_event', self.enter_notify) 170 self.connect('leave_notify_event', self.leave_notify) 171 self.connect('key_press_event', self.key_press) 172 self.connect('key_release_event', self.key_release) 173 self.connect('scroll_event', self.scroll_event) 174 175 self.add_events(gtk.gdk.BUTTON_PRESS_MASK | 176 gtk.gdk.BUTTON_RELEASE_MASK | 177 gtk.gdk.ENTER_NOTIFY | 178 gtk.gdk.LEAVE_NOTIFY | 179 gtk.gdk.MOTION_NOTIFY | 180 gtk.gdk.NOTHING | 181 gtk.gdk.KEY_PRESS_MASK | 182 gtk.gdk.KEY_RELEASE_MASK | 183 gtk.gdk.POINTER_MOTION_HINT_MASK | 184 gtk.gdk.POINTER_MOTION_MASK | 185 gtk.gdk.SCROLL_MASK) 186 187 self.set_flags(gtk.CAN_FOCUS) 188 self.grab_focus() 189 190 def graph_is_not_empty(function): 191 """ 192 Decorator function to prevent the execution when graph not is set 193 @type function: function 194 @param function: Protected function 195 """ 196 def check_graph_status(*args): 197 if args[0].__graph is None: 198 return False 199 return function(*args) 200 201 return check_graph_status 202 203 def not_is_in_animation(function): 204 """ 205 Decorator function to prevent the execution when graph is animating 206 @type function: function 207 @param function: Protected function 208 """ 209 def check_animation_status(*args): 210 if args[0].__animating: 211 return False 212 return function(*args) 213 214 return check_animation_status 215 216 def save_drawing_to_file(self, file, type=FILE_TYPE_PNG): 217 """ 218 """ 219 allocation = self.get_allocation() 220 221 if type == FILE_TYPE_PDF: 222 self.surface = cairo.PDFSurface(file, 223 allocation.width, 224 allocation.height) 225 elif type == FILE_TYPE_PNG: 226 self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 227 allocation.width, 228 allocation.height) 229 elif type == FILE_TYPE_PS: 230 self.surface = cairo.PSSurface(file, 231 allocation.width, 232 allocation.height) 233 elif type == FILE_TYPE_SVG: 234 self.surface = cairo.SVGSurface(file, 235 allocation.width, 236 allocation.height) 237 else: 238 raise TypeError('unknown surface type') 239 240 context = cairo.Context(self.surface) 241 242 context.rectangle(0, 0, allocation.width, allocation.height) 243 context.set_source_rgb(1.0, 1.0, 1.0) 244 context.fill() 245 246 self.__draw(context) 247 248 if type == FILE_TYPE_PNG: 249 # write_to_png requires a str, not unicode, in py2cairo 1.8.10 and 250 # earlier. 251 self.surface.write_to_png(fs_enc(file)) 252 253 self.surface.flush() 254 self.surface.finish() 255 256 return True 257 258 def get_slow_inout(self): 259 """ 260 """ 261 return self.__interpolation_slow_in_out 262 263 def set_slow_inout(self, value): 264 """ 265 """ 266 self.__interpolation_slow_in_out = value 267 268 def get_region_color(self): 269 """ 270 """ 271 return self.__region_color 272 273 def set_region_color(self, value): 274 """ 275 """ 276 self.__region_color = value 277 278 def get_show_region(self): 279 """ 280 """ 281 return self.__show_region 282 283 def set_show_region(self, value): 284 """ 285 """ 286 self.__show_region = value 287 self.queue_draw() 288 289 def get_pointer_status(self): 290 """ 291 """ 292 return self.__pointer_status 293 294 def set_pointer_status(self, pointer_status): 295 """ 296 """ 297 self.__pointer_status = pointer_status 298 299 def get_show_address(self): 300 """ 301 """ 302 return self.__show_address 303 304 def get_show_hostname(self): 305 """ 306 """ 307 return self.__show_hostname 308 309 def get_show_ring(self): 310 """ 311 """ 312 return self.__show_ring 313 314 def set_show_address(self, value): 315 """ 316 """ 317 self.__show_address = value 318 self.queue_draw() 319 320 def set_show_hostname(self, value): 321 """ 322 """ 323 self.__show_hostname = value 324 self.queue_draw() 325 326 def set_show_ring(self, value): 327 """ 328 """ 329 self.__show_ring = value 330 self.queue_draw() 331 332 def get_min_ring_gap(self): 333 """ 334 """ 335 return self.__min_ring_gap 336 337 @graph_is_not_empty 338 @not_is_in_animation 339 def set_min_ring_gap(self, value): 340 """ 341 """ 342 self.__min_ring_gap = int(value) 343 344 if self.__ring_gap < self.__min_ring_gap: 345 self.__ring_gap = self.__min_ring_gap 346 347 self.__update_nodes_positions() 348 self.queue_draw() 349 350 return True 351 352 def get_number_of_frames(self): 353 """ 354 """ 355 return self.__number_of_frames 356 357 @not_is_in_animation 358 def set_number_of_frames(self, number_of_frames): 359 """ 360 """ 361 if number_of_frames > 2: 362 363 self.__number_of_frames = int(number_of_frames) 364 return True 365 366 self.__number_of_frames = 3 367 return False 368 369 @not_is_in_animation 370 def update_layout(self): 371 """ 372 """ 373 if self.__graph is None: 374 return 375 self.__animating = True 376 self.__calc_interpolation(self.__graph.get_main_node()) 377 self.__livens_up() 378 379 @not_is_in_animation 380 def set_layout(self, layout): 381 """ 382 """ 383 if self.__layout != layout: 384 385 self.__layout = layout 386 387 if self.__graph is not None: 388 389 self.__animating = True 390 self.__calc_interpolation(self.__graph.get_main_node()) 391 self.__livens_up() 392 393 return True 394 395 return False 396 397 def get_layout(self): 398 """ 399 """ 400 return self.__layout 401 402 @not_is_in_animation 403 def set_interpolation(self, interpolation): 404 """ 405 """ 406 self.__interpolation = interpolation 407 408 return True 409 410 def get_interpolation(self): 411 """ 412 """ 413 return self.__interpolation 414 415 def get_number_of_rings(self): 416 """ 417 """ 418 return self.__number_of_rings 419 420 def get_fisheye_ring(self): 421 """ 422 """ 423 return self.__fisheye_ring 424 425 def get_fisheye_interest(self): 426 """ 427 """ 428 return self.__fisheye_interest 429 430 def get_fisheye_spread(self): 431 """ 432 """ 433 return self.__fisheye_spread 434 435 def get_fisheye(self): 436 """ 437 """ 438 return self.__fisheye 439 440 def set_fisheye(self, enable): 441 """ 442 """ 443 self.__fisheye = enable 444 445 self.__update_nodes_positions() 446 self.queue_draw() 447 448 def set_fisheye_ring(self, value): 449 """ 450 """ 451 self.__fisheye_ring = value 452 self.__check_fisheye_ring() 453 454 self.__update_nodes_positions() 455 self.queue_draw() 456 457 def set_fisheye_interest(self, value): 458 """ 459 """ 460 self.__fisheye_interest = value 461 462 self.__update_nodes_positions() 463 self.queue_draw() 464 465 def set_fisheye_spread(self, value): 466 """ 467 """ 468 self.__fisheye_spread = value 469 470 self.__update_nodes_positions() 471 self.queue_draw() 472 473 def get_show_icon(self): 474 """ 475 """ 476 return self.__show_icon 477 478 def set_show_icon(self, value): 479 """ 480 """ 481 self.__show_icon = value 482 self.queue_draw() 483 484 def get_show_latency(self): 485 """ 486 """ 487 return self.__show_latency 488 489 def set_show_latency(self, value): 490 """ 491 """ 492 self.__show_latency = value 493 self.queue_draw() 494 495 def get_scale(self): 496 """ 497 """ 498 return self.__scale 499 500 def get_zoom(self): 501 """ 502 """ 503 return int(round(self.__scale * 100)) 504 505 def set_scale(self, scale): 506 """ 507 """ 508 if scale >= 0.01: 509 510 self.__scale = scale 511 self.queue_draw() 512 513 def set_zoom(self, zoom): 514 """ 515 """ 516 if float(zoom) >= 1: 517 518 self.set_scale(float(zoom) / 100.0) 519 self.queue_draw() 520 521 def get_ring_gap(self): 522 """ 523 """ 524 return self.__ring_gap 525 526 @not_is_in_animation 527 def set_ring_gap(self, ring_gap): 528 """ 529 """ 530 if ring_gap >= self.__min_ring_gap: 531 532 self.__ring_gap = ring_gap 533 self.__update_nodes_positions() 534 self.queue_draw() 535 536 def scroll_event(self, widget, event): 537 """ 538 """ 539 if event.direction == gtk.gdk.SCROLL_UP: 540 self.set_scale(self.__scale + 0.01) 541 542 if event.direction == gtk.gdk.SCROLL_DOWN: 543 self.set_scale(self.__scale - 0.01) 544 545 self.queue_draw() 546 547 @graph_is_not_empty 548 @not_is_in_animation 549 def key_press(self, widget, event): 550 """ 551 """ 552 key = gtk.gdk.keyval_name(event.keyval) 553 554 if key == 'KP_Add': 555 self.set_ring_gap(self.__ring_gap + 1) 556 557 elif key == 'KP_Subtract': 558 self.set_ring_gap(self.__ring_gap - 1) 559 560 elif key == 'Page_Up': 561 self.set_scale(self.__scale + 0.01) 562 563 elif key == 'Page_Down': 564 self.set_scale(self.__scale - 0.01) 565 566 self.queue_draw() 567 568 return True 569 570 @graph_is_not_empty 571 def key_release(self, widget, event): 572 """ 573 """ 574 key = gtk.gdk.keyval_name(event.keyval) 575 576 if key == 'c': 577 self.__translation = (0, 0) 578 579 elif key == 'r': 580 self.__show_ring = not self.__show_ring 581 582 elif key == 'a': 583 self.__show_address = not self.__show_address 584 585 elif key == 'h': 586 self.__show_hostname = not self.__show_hostname 587 588 elif key == 'i': 589 self.__show_icon = not self.__show_icon 590 591 elif key == 'l': 592 self.__show_latency = not self.__show_latency 593 594 self.queue_draw() 595 596 return True 597 598 @graph_is_not_empty 599 @not_is_in_animation 600 def enter_notify(self, widget, event): 601 """ 602 """ 603 self.grab_focus() 604 return False 605 606 @graph_is_not_empty 607 @not_is_in_animation 608 def leave_notify(self, widget, event): 609 """ 610 """ 611 for node in self.__graph.get_nodes(): 612 node.set_draw_info({'over': False}) 613 614 self.queue_draw() 615 616 return False 617 618 @graph_is_not_empty 619 def button_press(self, widget, event): 620 """ 621 Drawing callback 622 @type widget: GtkWidget 623 @param widget: Gtk widget superclass 624 @type event: GtkEvent 625 @param event: Gtk event of widget 626 @rtype: boolean 627 @return: Indicator of the event propagation 628 """ 629 result = self.__get_node_by_coordinate(self.get_pointer()) 630 631 if event.button == 1: 632 self.__button1_press = True 633 634 # animate if node is pressed 635 if self.__pointer_status == POINTER_JUMP_TO and event.button == 1: 636 637 # prevent double animation 638 if self.__animating: 639 return False 640 641 if result is not None: 642 643 node, point = result 644 main_node = self.__graph.get_main_node() 645 646 if node != main_node: 647 648 if node.get_draw_info('group'): 649 650 node.set_draw_info({'group': False}) 651 node.set_subtree_info({'grouped': False, 652 'group_node': None}) 653 654 self.__animating = True 655 self.__calc_interpolation(node) 656 self.__livens_up() 657 658 # group node if it's pressed 659 elif self.__pointer_status == POINTER_GROUP and event.button == 1: 660 661 # prevent group on animation 662 if self.__animating: 663 return False 664 665 if result is not None: 666 667 node, point = result 668 main_node = self.__graph.get_main_node() 669 670 if node != main_node: 671 672 if node.get_draw_info('group'): 673 674 node.set_draw_info({'group': False}) 675 node.set_subtree_info({'grouped': False, 676 'group_node': None}) 677 678 else: 679 680 self.__last_group_node = node 681 682 node.set_draw_info({'group': True}) 683 node.set_subtree_info({'grouped': True, 684 'group_node': node}) 685 686 self.__animating = True 687 self.__calc_interpolation(self.__graph.get_main_node()) 688 self.__livens_up() 689 690 # setting to show node's region 691 elif self.__pointer_status == POINTER_FILL and event.button == 1: 692 693 if result is not None: 694 695 node, point = result 696 697 if node.get_draw_info('region') == self.__region_color: 698 node.set_draw_info({'region': None}) 699 700 else: 701 node.set_draw_info({'region': self.__region_color}) 702 703 self.queue_draw() 704 705 # show node details 706 elif event.button == 3 or self.__pointer_status == POINTER_INFO: 707 708 if event.button == 3: 709 self.__button3_press = True 710 711 if result is not None: 712 713 xw, yw = self.window.get_origin() 714 node, point = result 715 x, y = point 716 717 if node in self.__node_views.keys(): 718 719 self.__node_views[node].present() 720 721 elif node.get_draw_info('scanned'): 722 723 view = NodeWindow(node, (int(xw + x), int(yw + y))) 724 725 def close_view(view, event, node): 726 view.destroy() 727 del self.__node_views[node] 728 729 view.connect("delete-event", close_view, node) 730 view.show_all() 731 self.__node_views[node] = view 732 733 return False 734 735 @graph_is_not_empty 736 def button_release(self, widget, event): 737 """ 738 Drawing callback 739 @type widget: GtkWidget 740 @param widget: Gtk widget superclass 741 @type event: GtkEvent 742 @param event: Gtk event of widget 743 @rtype: boolean 744 @return: Indicator of the event propagation 745 """ 746 if event.button == 1: 747 self.__button1_press = False 748 749 if event.button == 2: 750 self.__button2_press = False 751 752 if event.button == 3: 753 self.__button3_press = False 754 755 self.grab_focus() 756 757 return False 758 759 @graph_is_not_empty 760 def motion_notify(self, widget, event): 761 """ 762 Drawing callback 763 @type widget: GtkWidget 764 @param widget: Gtk widget superclass 765 @type event: GtkEvent 766 @param event: Gtk event of widget 767 @rtype: boolean 768 @return: Indicator of the event propagation 769 """ 770 pointer = self.get_pointer() 771 772 for node in self.__graph.get_nodes(): 773 node.set_draw_info({'over': False}) 774 775 result = self.__get_node_by_coordinate(self.get_pointer()) 776 777 if result is not None: 778 result[0].set_draw_info({'over': True}) 779 780 elif self.__button1_press and self.__last_motion_point is not None: 781 782 ax, ay = pointer 783 ox, oy = self.__last_motion_point 784 tx, ty = self.__translation 785 786 self.__translation = (tx + ax - ox, ty - ay + oy) 787 788 self.__last_motion_point = pointer 789 790 self.grab_focus() 791 self.queue_draw() 792 793 return False 794 795 def expose(self, widget, event): 796 """ 797 Drawing callback 798 @type widget: GtkWidget 799 @param widget: Gtk widget superclass 800 @type event: GtkEvent 801 @param event: Gtk event of widget 802 @rtype: boolean 803 @return: Indicator of the event propagation 804 """ 805 context = widget.window.cairo_create() 806 807 context.rectangle(*event.area) 808 context.set_source_rgb(1.0, 1.0, 1.0) 809 context.fill() 810 811 self.__draw(context) 812 813 return False 814 815 @graph_is_not_empty 816 def __draw(self, context): 817 """ 818 Drawing method 819 """ 820 # getting allocation reference 821 allocation = self.get_allocation() 822 823 self.__center_of_widget = (allocation.width / 2, 824 allocation.height / 2) 825 826 xc, yc = self.__center_of_widget 827 828 ax, ay = self.__translation 829 830 # xc = 320 yc = 240 831 832 # -1.5 | -0.5 ( 480, 360) 833 # -1.0 | 0.0 ( 320, 240) 834 # -0.5 | 0.5 ( 160, 120) 835 # 0.0 | 1.0 ( 0, 0) 836 # 0.5 | 1.5 (-160, -120) 837 # 1.0 | 2.0 (-320, -240) 838 # 1.5 | 2.5 (-480, -360) 839 840 # scaling and translate 841 factor = -(self.__scale - 1) 842 843 context.translate(xc * factor + ax, yc * factor - ay) 844 845 if self.__scale != 1.0: 846 context.scale(self.__scale, self.__scale) 847 848 # drawing over node's region 849 if self.__show_region and not self.__animating: 850 851 for node in self.__sorted_nodes: 852 853 not_grouped = not node.get_draw_info('grouped') 854 855 if node.get_draw_info('region') is not None and not_grouped: 856 857 xc, yc = self.__center_of_widget 858 r, g, b = REGION_COLORS[node.get_draw_info('region')] 859 860 start, final = node.get_draw_info('range') 861 862 i_radius = node.get_coordinate_radius() 863 f_radius = self.__calc_radius(self.__number_of_rings - 1) 864 865 is_fill_all = abs(final - start) == 360 866 867 final = math.radians(final + self.__rotate) 868 start = math.radians(start + self.__rotate) 869 870 context.move_to(xc, yc) 871 context.set_source_rgba(r, g, b, 0.1) 872 context.new_path() 873 context.arc(xc, yc, i_radius, -final, -start) 874 context.arc_negative(xc, yc, f_radius, -start, -final) 875 context.close_path() 876 context.fill() 877 context.stroke() 878 879 if not is_fill_all: 880 881 context.set_source_rgb(r, g, b) 882 context.set_line_width(1) 883 884 xa, ya = PolarCoordinate( 885 i_radius, final).to_cartesian() 886 xb, yb = PolarCoordinate( 887 f_radius, final).to_cartesian() 888 889 context.move_to(xc + xa, yc - ya) 890 context.line_to(xc + xb, yc - yb) 891 context.stroke() 892 893 xa, ya = PolarCoordinate( 894 i_radius, start).to_cartesian() 895 xb, yb = PolarCoordinate( 896 f_radius, start).to_cartesian() 897 898 context.move_to(xc + xa, yc - ya) 899 context.line_to(xc + xb, yc - yb) 900 context.stroke() 901 902 # drawing network rings 903 if self.__show_ring and not self.__animating: 904 905 for i in range(1, self.__number_of_rings): 906 907 radius = self.__calc_radius(i) 908 909 context.arc(xc, yc, radius, 0, 2 * math.pi) 910 context.set_source_rgb(0.8, 0.8, 0.8) 911 context.set_line_width(1) 912 context.stroke() 913 914 # drawing nodes and your connections 915 for edge in self.__graph.get_edges(): 916 917 # check group constraints for edges 918 a, b = edge.get_nodes() 919 920 a_is_grouped = a.get_draw_info('grouped') 921 b_is_grouped = b.get_draw_info('grouped') 922 923 a_is_group = a.get_draw_info('group') 924 b_is_group = b.get_draw_info('group') 925 926 a_group = a.get_draw_info('group_node') 927 b_group = b.get_draw_info('group_node') 928 929 a_is_child = a in b.get_draw_info('children') 930 b_is_child = b in a.get_draw_info('children') 931 932 last_group = self.__last_group_node 933 groups = [a_group, b_group] 934 935 if last_group in groups and last_group is not None: 936 self.__draw_edge(context, edge) 937 938 elif not a_is_grouped or not b_is_grouped: 939 940 if not (a_is_group and b_is_child or 941 b_is_group and a_is_child): 942 self.__draw_edge(context, edge) 943 944 elif a_group != b_group: 945 self.__draw_edge(context, edge) 946 947 for node in reversed(self.__sorted_nodes): 948 949 # check group constraints for nodes 950 group = node.get_draw_info('group_node') 951 grouped = node.get_draw_info('grouped') 952 953 if group == self.__last_group_node or not grouped: 954 self.__draw_node(context, node) 955 956 def __draw_edge(self, context, edge): 957 """ 958 Draw the connection between two nodes 959 @type : Edge 960 @param : The second node that will be connected 961 """ 962 a, b = edge.get_nodes() 963 964 xa, ya = a.get_cartesian_coordinate() 965 xb, yb = b.get_cartesian_coordinate() 966 xc, yc = self.__center_of_widget 967 968 a_children = a.get_draw_info('children') 969 b_children = b.get_draw_info('children') 970 971 latency = edge.get_weights_mean() 972 973 # check if isn't an hierarchy connection 974 if a not in b_children and b not in a_children: 975 context.set_source_rgba(1.0, 0.6, 0.1, 0.8) 976 977 elif a.get_draw_info('no_route') or b.get_draw_info('no_route'): 978 context.set_source_rgba(0.0, 0.0, 0.0, 0.8) 979 980 else: 981 context.set_source_rgba(0.1, 0.5, 1.0, 0.8) 982 983 # calculating line thickness by latency 984 if latency is not None: 985 986 min = self.__graph.get_min_edge_mean_weight() 987 max = self.__graph.get_max_edge_mean_weight() 988 989 if max != min: 990 thickness = (latency - min) * 4 / (max - min) + 1 991 992 else: 993 thickness = 1 994 995 context.set_line_width(thickness) 996 997 else: 998 999 context.set_dash([2, 2]) 1000 context.set_line_width(1) 1001 1002 context.move_to(xc + xa, yc - ya) 1003 context.line_to(xc + xb, yc - yb) 1004 context.stroke() 1005 1006 context.set_dash([1, 0]) 1007 1008 if not self.__animating and self.__show_latency: 1009 1010 if latency is not None: 1011 1012 context.set_font_size(8) 1013 context.set_line_width(1) 1014 context.move_to(xc + (xa + xb) / 2 + 1, 1015 yc - (ya + yb) / 2 + 4) 1016 context.show_text(str(round(latency, 2))) 1017 context.stroke() 1018 1019 def __draw_node(self, context, node): 1020 """ 1021 Draw nodes and your information 1022 @type : NetNode 1023 @param : The node to be drawn 1024 """ 1025 x, y = node.get_cartesian_coordinate() 1026 xc, yc = self.__center_of_widget 1027 r, g, b = node.get_draw_info('color') 1028 radius = node.get_draw_info('radius') 1029 1030 type = node.get_info('device_type') 1031 1032 x_gap = radius + 2 1033 y_gap = 0 1034 1035 # draw group indication 1036 if node.get_draw_info('group'): 1037 1038 x_gap += 5 1039 1040 if type in SQUARE_TYPES: 1041 context.rectangle(xc + x - radius - 5, 1042 yc - y - radius - 5, 1043 2 * radius + 10, 1044 2 * radius + 10) 1045 1046 else: 1047 context.arc(xc + x, yc - y, radius + 5, 0, 2 * math.pi) 1048 1049 context.set_source_rgb(1.0, 1.0, 1.0) 1050 context.fill_preserve() 1051 1052 if node.deep_search_child(self.__graph.get_main_node()): 1053 context.set_source_rgb(0.0, 0.0, 0.0) 1054 1055 else: 1056 context.set_source_rgb(0.1, 0.5, 1.0) 1057 1058 context.set_line_width(2) 1059 context.stroke() 1060 1061 # draw over node 1062 if node.get_draw_info('over'): 1063 1064 context.set_line_width(0) 1065 1066 if type in SQUARE_TYPES: 1067 context.rectangle(xc + x - radius - 5, 1068 yc - y - radius - 5, 1069 2 * radius + 10, 1070 2 * radius + 10) 1071 1072 else: 1073 context.arc(xc + x, yc - y, radius + 5, 0, 2 * math.pi) 1074 1075 context.set_source_rgb(0.1, 0.5, 1.0) 1076 context.fill_preserve() 1077 context.stroke() 1078 1079 # draw node 1080 if type in SQUARE_TYPES: 1081 context.rectangle(xc + x - radius, 1082 yc - y - radius, 1083 2 * radius, 1084 2 * radius) 1085 1086 else: 1087 context.arc(xc + x, yc - y, radius, 0, 2 * math.pi) 1088 1089 # draw icons 1090 if not self.__animating and self.__show_icon: 1091 1092 icons = list() 1093 1094 if type in ICON_DICT.keys(): 1095 icons.append(self.__icon.get_pixbuf(ICON_DICT[type])) 1096 1097 if node.get_info('filtered'): 1098 icons.append(self.__icon.get_pixbuf('padlock')) 1099 1100 for icon in icons: 1101 1102 stride, data = get_pixels_for_cairo_image_surface(icon) 1103 1104 # Cairo documentation says that the correct way to obtain a 1105 # legal stride value is using the function 1106 # cairo.ImageSurface.format_stride_for_width(). 1107 # But this method is only available since cairo 1.6. So we are 1108 # using the stride returned by 1109 # get_pixels_for_cairo_image_surface() function. 1110 surface = cairo.ImageSurface.create_for_data(data, 1111 cairo.FORMAT_ARGB32, 1112 icon.get_width(), 1113 icon.get_height(), 1114 stride) 1115 1116 context.set_source_surface(surface, 1117 round(xc + x + x_gap), 1118 round(yc - y + y_gap - 6)) 1119 context.paint() 1120 1121 x_gap += 13 1122 1123 # draw node text 1124 context.set_source_rgb(r, g, b) 1125 context.fill_preserve() 1126 1127 if node.get_draw_info('valid'): 1128 context.set_source_rgb(0.0, 0.0, 0.0) 1129 1130 else: 1131 context.set_source_rgb(0.1, 0.5, 1.0) 1132 1133 if not self.__animating and self.__show_address: 1134 1135 context.set_font_size(8) 1136 context.move_to(round(xc + x + x_gap), 1137 round(yc - y + y_gap + 4)) 1138 1139 hostname = node.get_info('hostname') 1140 1141 if hostname is not None and self.__show_hostname: 1142 context.show_text(hostname) 1143 1144 elif node.get_info('ip') is not None: 1145 context.show_text(node.get_info('ip')) 1146 1147 context.set_line_width(1) 1148 context.stroke() 1149 1150 def __check_fisheye_ring(self): 1151 """ 1152 """ 1153 if self.__fisheye_ring >= self.__number_of_rings: 1154 self.__fisheye_ring = self.__number_of_rings - 1 1155 1156 def __set_number_of_rings(self, value): 1157 """ 1158 """ 1159 self.__number_of_rings = value 1160 self.__check_fisheye_ring() 1161 1162 def __fisheye_function(self, ring): 1163 """ 1164 """ 1165 distance = abs(self.__fisheye_ring - ring) 1166 level_of_detail = self.__ring_gap * self.__fisheye_interest 1167 spread_distance = distance - distance * self.__fisheye_spread 1168 1169 value = level_of_detail / (spread_distance + 1) 1170 1171 if value < self.__min_ring_gap: 1172 value = self.__min_ring_gap 1173 1174 return value 1175 1176 @graph_is_not_empty 1177 @not_is_in_animation 1178 def __update_nodes_positions(self): 1179 """ 1180 """ 1181 for node in self.__sorted_nodes: 1182 1183 if node.get_draw_info('grouped'): 1184 1185 # deep group check 1186 group = node.get_draw_info('group_node') 1187 1188 while group.get_draw_info('group_node') is not None: 1189 group = group.get_draw_info('group_node') 1190 1191 ring = group.get_draw_info('ring') 1192 node.set_coordinate_radius(self.__calc_radius(ring)) 1193 1194 else: 1195 ring = node.get_draw_info('ring') 1196 node.set_coordinate_radius(self.__calc_radius(ring)) 1197 1198 @graph_is_not_empty 1199 def __get_node_by_coordinate(self, point): 1200 """ 1201 """ 1202 xc, yc = self.__center_of_widget 1203 1204 for node in self.__graph.get_nodes(): 1205 1206 if node.get_draw_info('grouped'): 1207 continue 1208 1209 ax, ay = self.__translation 1210 1211 xn, yn = node.get_cartesian_coordinate() 1212 center = (xc + xn * self.__scale + ax, yc - yn * self.__scale - ay) 1213 radius = node.get_draw_info('radius') * self.__scale 1214 1215 type = node.get_info('device_type') 1216 1217 if type in SQUARE_TYPES: 1218 if geometry.is_in_square(point, radius, center): 1219 return node, center 1220 1221 else: 1222 if geometry.is_in_circle(point, radius, center): 1223 return node, center 1224 1225 return None 1226 1227 def __calc_radius(self, ring): 1228 """ 1229 """ 1230 if self.__fisheye: 1231 1232 radius = 0 1233 1234 while ring > 0: 1235 1236 radius += self.__fisheye_function(ring) 1237 ring -= 1 1238 1239 else: 1240 radius = ring * self.__ring_gap 1241 1242 return radius 1243 1244 @graph_is_not_empty 1245 def __arrange_nodes(self): 1246 """ 1247 """ 1248 new_nodes = set([self.__graph.get_main_node()]) 1249 old_nodes = set() 1250 1251 number_of_needed_rings = 1 1252 ring = 0 1253 1254 # while new nodes were found 1255 while len(new_nodes) > 0: 1256 1257 tmp_nodes = set() 1258 1259 # for each new nodes 1260 for node in new_nodes: 1261 1262 old_nodes.add(node) 1263 1264 # set ring location 1265 node.set_draw_info({'ring': ring}) 1266 1267 # check group constraints 1268 if (node.get_draw_info('group') or 1269 node.get_draw_info('grouped')): 1270 children = node.get_draw_info('children') 1271 1272 else: 1273 1274 # getting connections and fixing multiple fathers 1275 children = set() 1276 for child in self.__graph.get_node_connections(node): 1277 if child in old_nodes or child in new_nodes: 1278 continue 1279 if child.get_draw_info('grouped'): 1280 continue 1281 children.add(child) 1282 1283 # setting father foreign 1284 for child in children: 1285 child.set_draw_info({'father': node}) 1286 1287 node.set_draw_info( 1288 {'children': misc.sort_children(children, node)}) 1289 tmp_nodes.update(children) 1290 1291 # check group influence in number of rings 1292 for node in tmp_nodes: 1293 1294 if not node.get_draw_info('grouped'): 1295 1296 number_of_needed_rings += 1 1297 break 1298 1299 # update new nodes set 1300 new_nodes.update(tmp_nodes) 1301 new_nodes.difference_update(old_nodes) 1302 1303 ring += 1 1304 1305 self.__set_number_of_rings(number_of_needed_rings) 1306 1307 def __weighted_layout(self): 1308 """ 1309 """ 1310 # calculating the space needed by each node 1311 self.__graph.get_main_node().set_draw_info({'range': (0, 360)}) 1312 new_nodes = set([self.__graph.get_main_node()]) 1313 1314 self.__graph.get_main_node().calc_needed_space() 1315 1316 while len(new_nodes) > 0: 1317 1318 node = new_nodes.pop() 1319 1320 # add only no grouped nodes 1321 children = set() 1322 for child in node.get_draw_info('children'): 1323 1324 if not child.get_draw_info('grouped'): 1325 children.add(child) 1326 new_nodes.add(child) 1327 1328 if len(children) > 0: 1329 1330 min, max = node.get_draw_info('range') 1331 1332 node_total = max - min 1333 children_need = node.get_draw_info('children_need') 1334 1335 for child in children: 1336 1337 child_need = child.get_draw_info('space_need') 1338 child_total = node_total * child_need / children_need 1339 1340 theta = child_total / 2 + min + self.__rotate 1341 1342 child.set_coordinate_theta(theta) 1343 child.set_draw_info({'range': (min, min + child_total)}) 1344 1345 min += child_total 1346 1347 def __symmetric_layout(self): 1348 """ 1349 """ 1350 self.__graph.get_main_node().set_draw_info({'range': (0, 360)}) 1351 new_nodes = set([self.__graph.get_main_node()]) 1352 1353 while len(new_nodes) > 0: 1354 1355 node = new_nodes.pop() 1356 1357 # add only no grouped nodes 1358 children = set() 1359 for child in node.get_draw_info('children'): 1360 1361 if not child.get_draw_info('grouped'): 1362 children.add(child) 1363 new_nodes.add(child) 1364 1365 if len(children) > 0: 1366 1367 min, max = node.get_draw_info('range') 1368 factor = float(max - min) / len(children) 1369 1370 for child in children: 1371 1372 theta = factor / 2 + min + self.__rotate 1373 1374 child.set_coordinate_theta(theta) 1375 child.set_draw_info({'range': (min, min + factor)}) 1376 1377 min += factor 1378 1379 @graph_is_not_empty 1380 def __calc_layout(self, reference): 1381 """ 1382 """ 1383 # selecting layout algorithm 1384 if self.__layout == LAYOUT_SYMMETRIC: 1385 self.__symmetric_layout() 1386 1387 elif self.__layout == LAYOUT_WEIGHTED: 1388 self.__weighted_layout() 1389 1390 # rotating focus' children to keep orientation 1391 if reference is not None: 1392 1393 father, angle = reference 1394 theta = father.get_coordinate_theta() 1395 factor = theta - angle 1396 1397 for node in self.__graph.get_nodes(): 1398 1399 theta = node.get_coordinate_theta() 1400 node.set_coordinate_theta(theta - factor) 1401 1402 a, b = node.get_draw_info('range') 1403 node.set_draw_info({'range': (a - factor, b - factor)}) 1404 1405 @graph_is_not_empty 1406 def __calc_node_positions(self, reference=None): 1407 """ 1408 """ 1409 # set nodes' hierarchy 1410 self.__arrange_nodes() 1411 self.calc_sorted_nodes() 1412 1413 # set nodes' coordinate radius 1414 for node in self.__graph.get_nodes(): 1415 1416 ring = node.get_draw_info('ring') 1417 node.set_coordinate_radius(self.__calc_radius(ring)) 1418 1419 # set nodes' coordinate theta 1420 self.__calc_layout(reference) 1421 1422 def __calc_interpolation(self, focus): 1423 """ 1424 """ 1425 old_main_node = self.__graph.get_main_node() 1426 self.__graph.set_main_node(focus) 1427 1428 # getting initial coordinates 1429 for node in self.__graph.get_nodes(): 1430 1431 if self.__interpolation == INTERPOLATION_POLAR: 1432 coordinate = node.get_polar_coordinate() 1433 1434 elif self.__interpolation == INTERPOLATION_CARTESIAN: 1435 coordinate = node.get_cartesian_coordinate() 1436 1437 node.set_draw_info({'start_coordinate': coordinate}) 1438 1439 father = focus.get_draw_info('father') 1440 1441 # calculate nodes positions (and father orientation)? 1442 if father is not None: 1443 1444 xa, ya = father.get_cartesian_coordinate() 1445 xb, yb = focus.get_cartesian_coordinate() 1446 1447 angle = math.atan2(yb - ya, xb - xa) 1448 angle = math.degrees(angle) 1449 1450 self.__calc_node_positions((father, 180 + angle)) 1451 1452 else: 1453 self.__calc_node_positions() 1454 1455 # steps for slow-in/slow-out animation 1456 steps = range(self.__number_of_frames) 1457 1458 for i in range(len(steps) / 2): 1459 steps[self.__number_of_frames - 1 - i] = steps[i] 1460 1461 # normalize angles and calculate interpolated points 1462 for node in self.__sorted_nodes: 1463 1464 l2di = Linear2DInterpolator() 1465 1466 # change grouped nodes coordinate 1467 if node.get_draw_info('grouped'): 1468 1469 group_node = node.get_draw_info('group_node') 1470 a, b = group_node.get_draw_info('final_coordinate') 1471 1472 if self.__interpolation == INTERPOLATION_POLAR: 1473 node.set_polar_coordinate(a, b) 1474 1475 elif self.__interpolation == INTERPOLATION_CARTESIAN: 1476 node.set_cartesian_coordinate(a, b) 1477 1478 # change interpolation method 1479 if self.__interpolation == INTERPOLATION_POLAR: 1480 1481 coordinate = node.get_polar_coordinate() 1482 node.set_draw_info({'final_coordinate': coordinate}) 1483 1484 # adjusting polar coordinates 1485 ri, ti = node.get_draw_info('start_coordinate') 1486 rf, tf = node.get_draw_info('final_coordinate') 1487 1488 # normalization [0, 360] 1489 ti = geometry.normalize_angle(ti) 1490 tf = geometry.normalize_angle(tf) 1491 1492 # against longest path 1493 ti, tf = geometry.calculate_short_path(ti, tf) 1494 1495 # main node goes direct to center (no arc) 1496 if node == self.__graph.get_main_node(): 1497 tf = ti 1498 1499 # old main node goes direct to new position (no arc) 1500 if node == old_main_node: 1501 ti = tf 1502 1503 node.set_draw_info({'start_coordinate': (ri, ti)}) 1504 node.set_draw_info({'final_coordinate': (rf, tf)}) 1505 1506 elif self.__interpolation == INTERPOLATION_CARTESIAN: 1507 1508 coordinate = node.get_cartesian_coordinate() 1509 node.set_draw_info({'final_coordinate': coordinate}) 1510 1511 # calculate interpolated points 1512 ai, bi = node.get_draw_info('start_coordinate') 1513 af, bf = node.get_draw_info('final_coordinate') 1514 1515 l2di.set_start_point(ai, bi) 1516 l2di.set_final_point(af, bf) 1517 1518 if self.__interpolation_slow_in_out: 1519 points = l2di.get_weighed_points( 1520 self.__number_of_frames, steps) 1521 1522 else: 1523 points = l2di.get_points(self.__number_of_frames) 1524 1525 node.set_draw_info({'interpolated_coordinate': points}) 1526 1527 return True 1528 1529 def __livens_up(self, index=0): 1530 """ 1531 """ 1532 if self.__graph is None: 1533 # Bail out if the graph became empty during an animation. 1534 self.__last_group_node = None 1535 self.__animating = False 1536 return False 1537 1538 # prepare interpolated points 1539 if index == 0: 1540 1541 # prevent unnecessary animation 1542 no_need_to_move = True 1543 1544 for node in self.__graph.get_nodes(): 1545 1546 ai, bi = node.get_draw_info('start_coordinate') 1547 af, bf = node.get_draw_info('final_coordinate') 1548 1549 start_c = round(ai), round(bi) 1550 final_c = round(af), round(bf) 1551 1552 if start_c != final_c: 1553 no_need_to_move = False 1554 1555 if no_need_to_move: 1556 1557 self.__animating = False 1558 return False 1559 1560 # move all nodes for pass 'index' 1561 for node in self.__graph.get_nodes(): 1562 1563 a, b = node.get_draw_info('interpolated_coordinate')[index] 1564 1565 if self.__interpolation == INTERPOLATION_POLAR: 1566 node.set_polar_coordinate(a, b) 1567 1568 elif self.__interpolation == INTERPOLATION_CARTESIAN: 1569 node.set_cartesian_coordinate(a, b) 1570 1571 self.queue_draw() 1572 1573 # animation continue condition 1574 if index < self.__number_of_frames - 1: 1575 gobject.timeout_add(self.__animation_rate, # time to recall 1576 self.__livens_up, # recursive call 1577 index + 1) # next iteration 1578 else: 1579 self.__last_group_node = None 1580 self.__animating = False 1581 1582 return False 1583 1584 @not_is_in_animation 1585 def set_graph(self, graph): 1586 """ 1587 Set graph to be displayed in layout 1588 @type : Graph 1589 @param : Set the graph used in visualization 1590 """ 1591 if graph.get_number_of_nodes() > 0: 1592 1593 self.__graph = graph 1594 1595 self.__calc_node_positions() 1596 self.queue_draw() 1597 1598 else: 1599 self.__graph = None 1600 1601 def get_scanned_nodes(self): 1602 """ 1603 """ 1604 nodes = list() 1605 if self.__graph is None: 1606 return nodes 1607 1608 for node in self.__graph.get_nodes(): 1609 1610 if node.get_draw_info('scanned'): 1611 nodes.append(node) 1612 1613 return nodes 1614 1615 def get_graph(self): 1616 """ 1617 """ 1618 return self.__graph 1619 1620 def set_empty(self): 1621 """ 1622 """ 1623 del(self.__graph) 1624 self.__graph = None 1625 1626 self.queue_draw() 1627 1628 def get_rotation(self): 1629 """ 1630 """ 1631 return self.__rotate 1632 1633 @graph_is_not_empty 1634 def set_rotation(self, angle): 1635 """ 1636 """ 1637 delta = angle - self.__rotate 1638 self.__rotate = angle 1639 1640 for node in self.__graph.get_nodes(): 1641 1642 theta = node.get_coordinate_theta() 1643 node.set_coordinate_theta(theta + delta) 1644 1645 self.queue_draw() 1646 1647 def get_translation(self): 1648 """ 1649 """ 1650 return self.__translation 1651 1652 @graph_is_not_empty 1653 def set_translation(self, translation): 1654 """ 1655 """ 1656 self.__translation = translation 1657 self.queue_draw() 1658 1659 def is_empty(self): 1660 """ 1661 """ 1662 return self.__graph is None 1663 1664 def is_in_animation(self): 1665 """ 1666 """ 1667 return self.__animating 1668 1669 def calc_sorted_nodes(self): 1670 """ 1671 """ 1672 self.__sorted_nodes = list(self.__graph.get_nodes()) 1673 self.__sorted_nodes.sort(key=lambda n: n.get_draw_info('ring')) 1674 1675 1676class NetNode(Node): 1677 """ 1678 Node class for radial network widget 1679 """ 1680 def __init__(self): 1681 """ 1682 """ 1683 self.__draw_info = dict() 1684 """Hash with draw information""" 1685 self.__coordinate = PolarCoordinate() 1686 1687 super(NetNode, self).__init__() 1688 1689 def get_host(self): 1690 """ 1691 Set the HostInfo that this node represents 1692 """ 1693 return self.get_data() 1694 1695 def set_host(self, host): 1696 """ 1697 Set the HostInfo that this node represents 1698 """ 1699 self.set_data(host) 1700 1701 def get_info(self, info): 1702 """Return various information extracted from the host set with 1703 set_host.""" 1704 host = self.get_data() 1705 if host is not None: 1706 if info == "number_of_open_ports": 1707 return host.get_port_count_by_states(["open"]) 1708 elif info == "vulnerability_score": 1709 num_open_ports = host.get_port_count_by_states(["open"]) 1710 if num_open_ports < 3: 1711 return 0 1712 elif num_open_ports < 7: 1713 return 1 1714 else: 1715 return 2 1716 elif info == "addresses": 1717 addresses = [] 1718 if host.ip is not None: 1719 addresses.append(host.ip) 1720 if host.ipv6 is not None: 1721 addresses.append(host.ipv6) 1722 if host.mac is not None: 1723 addresses.append(host.mac) 1724 return addresses 1725 elif info == "ip": 1726 for addr in (host.ip, host.ipv6, host.mac): 1727 if addr: 1728 return addr.get("addr") 1729 elif info == "hostnames": 1730 hostnames = [] 1731 for hostname in host.hostnames: 1732 copy = {} 1733 copy["name"] = hostname.get("hostname", "") 1734 copy["type"] = hostname.get("hostname_type", "") 1735 hostnames.append(copy) 1736 return hostnames 1737 elif info == "hostname": 1738 return host.get_hostname() 1739 elif info == "uptime": 1740 if host.uptime.get("seconds") or host.uptime.get("lastboot"): 1741 return host.uptime 1742 elif info == "device_type": 1743 osmatch = host.get_best_osmatch() 1744 if osmatch is None: 1745 return None 1746 osclasses = osmatch['osclasses'] 1747 if len(osclasses) == 0: 1748 return None 1749 types = ["router", "wap", "switch", "firewall"] 1750 for type in types: 1751 if type in osclasses[0].get("type", "").lower(): 1752 return type 1753 elif info == "os": 1754 os = {} 1755 1756 # osmatches 1757 if len(host.osmatches) > 0 and \ 1758 host.osmatches[0]["accuracy"] != "" and \ 1759 host.osmatches[0]["name"] != "": 1760 if os is None: 1761 os = {} 1762 os["matches"] = host.osmatches 1763 os["matches"][0]["db_line"] = 0 # not supported 1764 1765 os_classes = [] 1766 for osclass in host.osmatches[0]["osclasses"]: 1767 os_class = {} 1768 1769 os_class["type"] = osclass.get("type", "") 1770 os_class["vendor"] = osclass.get("vendor", "") 1771 os_class["accuracy"] = osclass.get("accuracy", "") 1772 os_class["os_family"] = osclass.get("osfamily", "") 1773 os_class["os_gen"] = osclass.get("osgen", "") 1774 1775 os_classes.append(os_class) 1776 os["classes"] = os_classes 1777 1778 # ports_used 1779 if len(host.ports_used) > 0: 1780 if os is None: 1781 os = {} 1782 os_portsused = [] 1783 1784 for portused in host.ports_used: 1785 os_portused = {} 1786 1787 os_portused["state"] = portused.get("state", "") 1788 os_portused["protocol"] = portused.get("proto", "") 1789 os_portused["id"] = int(portused.get("portid", "0")) 1790 1791 os_portsused.append(os_portused) 1792 1793 os["used_ports"] = os_portsused 1794 1795 if len(os) > 0: 1796 os["fingerprint"] = "" 1797 return os 1798 elif info == "sequences": 1799 # getting sequences information 1800 sequences = {} 1801 # If all fields are empty, we don't put it into the sequences 1802 # list 1803 if reduce(lambda x, y: x + y, 1804 host.tcpsequence.values(), "") != "": 1805 tcp = {} 1806 if host.tcpsequence.get("index", "") != "": 1807 tcp["index"] = int(host.tcpsequence["index"]) 1808 else: 1809 tcp["index"] = 0 1810 tcp["class"] = "" # not supported 1811 tcp["values"] = host.tcpsequence.get( 1812 "values", "").split(",") 1813 tcp["difficulty"] = host.tcpsequence.get("difficulty", "") 1814 sequences["tcp"] = tcp 1815 if reduce(lambda x, y: x + y, 1816 host.ipidsequence.values(), "") != "": 1817 ip_id = {} 1818 ip_id["class"] = host.ipidsequence.get("class", "") 1819 ip_id["values"] = host.ipidsequence.get( 1820 "values", "").split(",") 1821 sequences["ip_id"] = ip_id 1822 if reduce(lambda x, y: x + y, 1823 host.tcptssequence.values(), "") != "": 1824 tcp_ts = {} 1825 tcp_ts["class"] = host.tcptssequence.get("class", "") 1826 tcp_ts["values"] = host.tcptssequence.get( 1827 "values", "").split(",") 1828 sequences["tcp_ts"] = tcp_ts 1829 return sequences 1830 elif info == "filtered": 1831 if (len(host.extraports) > 0 and 1832 host.extraports[0]["state"] == "filtered"): 1833 return True 1834 else: 1835 for port in host.ports: 1836 if port["port_state"] == "filtered": 1837 return True 1838 return False 1839 elif info == "ports": 1840 ports = list() 1841 for host_port in host.ports: 1842 port = dict() 1843 state = dict() 1844 service = dict() 1845 1846 port["id"] = int(host_port.get("portid", "")) 1847 port["protocol"] = host_port.get("protocol", "") 1848 1849 state["state"] = host_port.get("port_state", "") 1850 state["reason"] = "" # not supported 1851 state["reason_ttl"] = "" # not supported 1852 state["reason_ip"] = "" # not supported 1853 1854 service["name"] = host_port.get("service_name", "") 1855 service["conf"] = host_port.get("service_conf", "") 1856 service["method"] = host_port.get("service_method", "") 1857 service["version"] = host_port.get("service_version", "") 1858 service["product"] = host_port.get("service_product", "") 1859 service["extrainfo"] = host_port.get( 1860 "service_extrainfo", "") 1861 1862 port["state"] = state 1863 port["scripts"] = None # not supported 1864 port["service"] = service 1865 1866 ports.append(port) 1867 return ports 1868 elif info == "extraports": 1869 # extraports 1870 all_extraports = list() 1871 for extraport in host.extraports: 1872 extraports = dict() 1873 extraports["count"] = int(extraport.get("count", "")) 1874 extraports["state"] = extraport.get("state", "") 1875 extraports["reason"] = list() # not supported 1876 extraports["all_reason"] = list() # not supported 1877 1878 all_extraports.append(extraports) 1879 return all_extraports 1880 elif info == "trace": 1881 # getting traceroute information 1882 if len(host.trace) > 0: 1883 trace = {} 1884 hops = [] 1885 1886 for host_hop in host.trace.get("hops", []): 1887 hop = {} 1888 hop["ip"] = host_hop.get("ipaddr", "") 1889 hop["ttl"] = int(host_hop.get("ttl", "")) 1890 hop["rtt"] = host_hop.get("rtt", "") 1891 hop["hostname"] = host_hop.get("host", "") 1892 1893 hops.append(hop) 1894 1895 trace["hops"] = hops 1896 trace["port"] = host.trace.get("port", "") 1897 trace["protocol"] = host.trace.get("proto", "") 1898 1899 return trace 1900 else: # host is None 1901 pass 1902 1903 return None 1904 1905 def get_coordinate_theta(self): 1906 """ 1907 """ 1908 return self.__coordinate.get_theta() 1909 1910 def get_coordinate_radius(self): 1911 """ 1912 """ 1913 return self.__coordinate.get_radius() 1914 1915 def set_coordinate_theta(self, value): 1916 """ 1917 """ 1918 self.__coordinate.set_theta(value) 1919 1920 def set_coordinate_radius(self, value): 1921 """ 1922 """ 1923 self.__coordinate.set_radius(value) 1924 1925 def set_polar_coordinate(self, r, t): 1926 """ 1927 Set polar coordinate 1928 @type r: number 1929 @param r: The radius of coordinate 1930 @type t: number 1931 @param t: The angle (theta) of coordinate in radians 1932 """ 1933 self.__coordinate.set_coordinate(r, t) 1934 1935 def get_polar_coordinate(self): 1936 """ 1937 Get cartesian coordinate 1938 @rtype: tuple 1939 @return: Cartesian coordinates (x, y) 1940 """ 1941 return self.__coordinate.get_coordinate() 1942 1943 def set_cartesian_coordinate(self, x, y): 1944 """ 1945 Set cartesian coordinate 1946 """ 1947 cartesian = CartesianCoordinate(x, y) 1948 r, t = cartesian.to_polar() 1949 1950 self.set_polar_coordinate(r, math.degrees(t)) 1951 1952 def get_cartesian_coordinate(self): 1953 """ 1954 Get cartesian coordinate 1955 @rtype: tuple 1956 @return: Cartesian coordinates (x, y) 1957 """ 1958 return self.__coordinate.to_cartesian() 1959 1960 def get_draw_info(self, info=None): 1961 """ 1962 Get draw information about node 1963 @type : string 1964 @param : Information name 1965 @rtype: mixed 1966 @return: The requested information 1967 """ 1968 if info is None: 1969 return self.__draw_info 1970 1971 return self.__draw_info.get(info) 1972 1973 def set_draw_info(self, info): 1974 """ 1975 Set draw information 1976 @type : dict 1977 @param : Draw information dictionary 1978 """ 1979 for key in info: 1980 self.__draw_info[key] = info[key] 1981 1982 def deep_search_child(self, node): 1983 """ 1984 """ 1985 for child in self.get_draw_info('children'): 1986 1987 if child == node: 1988 return True 1989 1990 elif child.deep_search_child(node): 1991 return True 1992 1993 return False 1994 1995 def set_subtree_info(self, info): 1996 """ 1997 """ 1998 for child in self.get_draw_info('children'): 1999 2000 child.set_draw_info(info) 2001 2002 if not child.get_draw_info('group'): 2003 child.set_subtree_info(info) 2004 2005 def calc_needed_space(self): 2006 """ 2007 """ 2008 number_of_children = len(self.get_draw_info('children')) 2009 2010 sum_angle = 0 2011 own_angle = 0 2012 2013 if number_of_children > 0 and not self.get_draw_info('group'): 2014 2015 for child in self.get_draw_info('children'): 2016 2017 child.calc_needed_space() 2018 sum_angle += child.get_draw_info('space_need') 2019 2020 distance = self.get_coordinate_radius() 2021 size = self.get_draw_info('radius') * 2 2022 own_angle = geometry.angle_from_object(distance, size) 2023 2024 self.set_draw_info({'children_need': sum_angle}) 2025 self.set_draw_info({'space_need': max(sum_angle, own_angle)}) 2026