#!/usr/bin/python3 import json import sys from enum import Enum DEBUG = False full_output = False hosts = [] # Describe the port fields class Port: def __init__(self): self.port_number = 0 self.state = "" self.protocol = "" self.owner = "" self.service = "" self.sunRPCinfo = "" self.version = "" def __str__(self): return f"Port {self.port_number} - Version: {self.version}" def todict(self, full=False): if full: return { "port":self.port_number, "state":self.state, "protocol":self.protocol, "owner":self.owner, "service":self.service, "RPCInfo":self.sunRPCinfo, "version":self.version, } return { "port":self.port_number, "protocol":self.protocol, "service":self.service, "version":self.version, "state":self.state, } # Class to represent all the host attributes class Host: def __init__(self): self.ip = "" self.status = "" self.hostname = "" self.ports = [] # Array of Port() items def merge_ports(self, host): local_ports = [p.port_number for p in self.ports] for p in host.ports: if p.port_number not in local_ports: self.ports.append(p) def todict(self, full): return { "ip":self.ip, "hostname":self.hostname, "ports":[port.todict(full) for port in self.ports], "status":self.status } def open_gnmap(filename): with open(filename, "r") as gnmap: # split into lines and only take the ones that begin with "Host:" lines = gnmap.read().strip().split("\n") lines = [l for l in lines if not l.startswith("#") and l.startswith("Host:")] return lines # parse every single line def parse_line(line): global hosts # Create new Instance host = Host() # Split line into different fields fields = line.split("\t") # parse the different fields parse_fields(host, fields) # merge known hosts merge_hosts(host) # delete instance del host def parse_host_field(host, field): subfields = field.split(" (") host.ip = subfields[0] host.hostname =subfields[1].replace(")", "") if DEBUG: print(f"DEBUG: parse_host_field::ip={host.ip};hostname={host.hostname}") def parse_ports_field(host, field): portlist = [s.strip() for s in field.split("/,")] # loop through the ports and append them to the host for p in portlist: port = Port() portparts = p.split("/") port.port_number = portparts[0] port.state = portparts[1] port.protocol = portparts[2] port.owner = portparts[3] port.service = portparts[4] port.sunRPCinfo = portparts[5] port.version = portparts[6] host.ports.append(port) del port def parse_status_field(host, field): host.status = field return host # Jumptable for the different fields field_parser = { "Ports": parse_ports_field, "Status": parse_status_field, } # if no parser for that type of field is implemented def not_implemented(host, field_type): return host def merge_hosts(host): global hosts for ih in hosts: # hosts already present in list if ih.ip == host.ip: ih.merge_ports(host) break else: hosts.append(host) # parsed the different fields, each separeted by a space def parse_fields(host, fields): # create a Host instance # loop each field that is present for field in fields: field_parts = field.split(": ") field_type = field_parts[0] field_value = "".join(field_parts[1:]) if DEBUG: print(f"DEBUG: field_type={field_type}") if DEBUG: print(f"DEBUG: field_content={field_value}") # Host Field is the most important one if field_type == "Host": parse_host_field(host, field_value) else: field_parser.get(field_type, not_implemented)(host, field_value) return host def hosts_to_json(full_output): global hosts out = [] for host in hosts: out.append(host.todict(full_output)) return out # check usage if len(sys.argv) == 1: print(f"usage: {sys.argv[0]} ") exit(1) elif len(sys.argv) == 3 and sys.argv[2] == "--full": full_output = True # Open the gnmap file lines = open_gnmap(sys.argv[1]) # parse file for line in lines: parse_line(line) # dump as json print(json.dumps(hosts_to_json(full_output)))