diff --git a/zenmap/zenmapCore/NetworkInventory.py b/zenmap/zenmapCore/NetworkInventory.py index dc28662..90bd9e5 100644 --- a/zenmap/zenmapCore/NetworkInventory.py +++ b/zenmap/zenmapCore/NetworkInventory.py @@ -455,14 +455,29 @@ class FilteredNetworkInventory(NetworkInventory): def get_host_count(self): return len(self.network_inventory.hosts) def match_keyword(self, host, keyword): - return self.match_os(host, keyword) or\ + found = True + if keyword != "" and keyword[0] == "!": + keyword = keyword[1:] + found = False + + if self.match_os(host, keyword) or\ self.match_target(host, keyword) or\ - self.match_service(host, keyword) + self.match_service(host, keyword): + return found + else: + return not found def match_target(self, host, name): return HostSearch.match_target(host, name) def match_in_route(self, host, hop): + found = True + if hop != "" and hop[0] == "!": + hop = hop[1:] + found = False hops = host.get_trace().get('hops', []) - return hop in hops + if hop in hops: + return found + else: + return not found def match_hostname(self, host, hostname): return HostSearch.match_hostname(host, hostname) def match_service(self, host, service): diff --git a/zenmap/zenmapCore/SearchResult.py b/zenmap/zenmapCore/SearchResult.py index 75c023f..b41a58a 100644 --- a/zenmap/zenmapCore/SearchResult.py +++ b/zenmap/zenmapCore/SearchResult.py @@ -106,31 +106,47 @@ from zenmapCore.UmitLogging import log class HostSearch(object): @staticmethod def match_target(host, name): - addrs = [] + found = True + if name != "" and name[0] == "!": + name = name[1:] + found = False + name = name.lower() + mac = host.get_mac() ip = host.get_ip() ipv6 = host.get_ipv6() if mac and mac.has_key('addr'): - if name in mac['addr'].lower(): return True + if name in mac['addr'].lower(): return found if ip and ip.has_key('addr'): - if name in ip['addr'].lower(): return True + if name in ip['addr'].lower(): return found if ipv6 and ipv6.has_key('addr'): - if name in ipv6['addr'].lower(): return True + if name in ipv6['addr'].lower(): return found if HostSearch.match_hostname(host, name): - return True - return False + return found + return not found @staticmethod def match_hostname(host, hostname): + found = True + if hostname != "" and hostname[0] == "!": + hostname = hostname[1:] + found = False + hostname = hostname.lower() + hostnames = host.get_hostnames() for hn in hostnames: if hostname in hn['hostname'].lower(): - return True + return found else: - return False + return not found @staticmethod def match_service(host, service): + found = True + if service != "" and service[0] == "!": + service = service[1:] + found = False + service = service.lower() for port in host.get_ports(): # We concatenate all useful fields and add them to the list if port['port_state'] not in ['open','open|filtered']: @@ -140,11 +156,15 @@ class HostSearch(object): port.get("service_version", "") + " " + \ port.get("service_extrainfo", "") if service in version.lower(): - return True + return found else: - return False + return not found @staticmethod def match_os(host, os): + found = True + if os != "" and os[0] == "!": + os = os[1:] + found = False os = os.lower() os_str = "" @@ -158,19 +178,22 @@ class HostSearch(object): osclass['type'].lower() if os in os_str: - return True - return False + return found + return not found @staticmethod def match_port(host_ports, port, port_state): + found = True + if port != "" and port[0] == "!": + port = port[1:] + found = False # Check if the port is parsable, if not return False silently if re.match("^\d+$", port) == None: return False for hp in host_ports: if hp['portid'] == port and hp['port_state'] == port_state: - return True - else: - return False + return found + return not found class SearchResult(object): def __init__(self): @@ -212,32 +235,54 @@ class SearchResult(object): pass def basic_match(self, keyword, property): + found = True + if keyword != "" and keyword[0] == "!": + keyword = keyword[1:] + found = False if keyword == "*" or keyword == "": - return True + return found - return keyword.lower() in str(self.parsed_scan.__getattribute__(property)).lower() + if keyword.lower() in str(self.parsed_scan.__getattribute__(property)).lower(): + return found + else: + return not found def match_keyword(self, keyword): log.debug("Match keyword: %s" % keyword) + found = True + if keyword != "" and keyword[0] == "!": + keyword = keyword[1:] + found = False - return self.basic_match(keyword, "nmap_output") or \ + if self.basic_match(keyword, "nmap_output") or \ self.match_profile(keyword) or \ - self.match_target(keyword) + self.match_target(keyword): + return found + else: + return not found def match_profile(self, profile): log.debug("Match profile: %s" % profile) log.debug("Comparing: %s == %s ??" % (str(self.parsed_scan.profile_name).lower(), "*%s*" % profile.lower())) + found = True + if profile != "" and profile[0] == "!": + profile = profile[1:] + found = False if profile == "*" or profile == "" or \ profile.lower() in str(self.parsed_scan.profile_name).lower(): - return True - return False + return found + return not found def match_option(self, option): log.debug("Match option: %s" % option) + found = True + if option != "" and option[0] == "!": + option = option[1:] + found = False if option == "*" or option == "": - return True + return found # NOTE: Option matching treats "_" and "-" the same, just like the optcmp # function in utils.cc . Also, option matching is case-sensitive. @@ -256,10 +301,16 @@ class SearchResult(object): if val is None: val = ops["-" + optname] if val is None: - return False - return str(val) == optval or str(val) == optval + return not found + if str(val) == optval or str(val) == optval: + return found + else: + return not found else: - return ops["--" + option] is not None or ops["-" + option] is not None + if ops["--" + option] is not None or ops["-" + option] is not None: + return found + else: + return not found def match_date(self, date_arg, operator="date"): # The parsed scan's get_date() returns a time.struct_time, so we @@ -305,15 +356,20 @@ class SearchResult(object): def match_target(self, target): log.debug("Match target: %s" % target) + found = True + if target != "" and target[0] == "!": + target = target[1:] + found = False + for spec in self.parsed_scan.get_targets(): if target in spec: - return True + return found else: # We search the (rDNS) hostnames list for host in self.parsed_scan.get_hosts(): if HostSearch.match_target(host, target): - return True - return False + return found + return not found def match_os(self, os): # If you have lots of big scans in your DB (with a lot of hosts scanned), @@ -321,15 +377,22 @@ class SearchResult(object): # search just greps through the nmap output, while this function iterates # through all parsed OS-related values for every host in every scan! hosts = self.parsed_scan.get_hosts() - os = os.lower() + found = True + if os != "" and os[0] == "!": + os = os[1:] + found = False for host in hosts: if HostSearch.match_os(host, os): - return True - return False + return found + return not found def match_scanned(self, ports): + found = True + if ports != "" and ports[0] == "!": + ports = ports[1:] + found = False if ports == "": - return True + return found # Transform a comma-delimited string containing ports into a list ports = filter(lambda not_empty: not_empty, ports.split(",")) @@ -358,14 +421,18 @@ class SearchResult(object): elif port == service: break else: - return False + return not found else: # The ports loop finished for all ports, which means the search was successful. - return True + return found def match_port(self, ports, port_state): log.debug("Match port:%s" % ports) + found = True + if ports != "" and ports[0] == "!": + ports = ports[1:] + found = False # Transform a comma-delimited string containing ports into a list ports = filter(lambda not_empty: not_empty, ports.split(",")) @@ -374,9 +441,9 @@ class SearchResult(object): if not HostSearch.match_port(host.get_ports(), port, port_state): break else: - return True + return found else: - return False + return not found def match_open(self, port): return self.match_port(port, "open") @@ -397,19 +464,26 @@ class SearchResult(object): return self.match_port(port, "closed|filtered") def match_service(self, sversion): + found = True + if sversion != "" and sversion[0] == "!": + sversion = sversion[1:] + found = False if sversion == "" or sversion == "*": - return True + return found - versions = [] for host in self.parsed_scan.get_hosts(): if HostSearch.match_service(host, sversion): - return True + return found else: - return False + return not found def match_in_route(self, host): + found = True + if host != "" and host[0] == "!": + host = host[1:] + found = False if host == "" or host == "*": - return True + return found # Since the parser doesn't parse traceroute output, we need to cheat and look # the host up in the Nmap output, in the Traceroute section of the scan. @@ -426,9 +500,9 @@ class SearchResult(object): for tr in traceroutes: if host.lower() in tr.lower(): - return True + return found else: - return False + return not found def match_dir(self, dir): # The dir: operator is handled by the SearchParser class, we ignore it here.