#!/usr/bin/python
''':'
exec python -u "$0" ${1+"$@"}
' '''

import sys
import re
import os

KnownPorts = {21:'FTP',	22:'SSH', 23:'Telnet', 25:'SMTP', 80:'HTTP', 443:'HTTPS'}

def help():
  print """stomper.py
  -h --help : help
  -file <filename> : nmap file for analysis
  -dir <directory location> : directory with nmap files for analysis
  -output <csv file> : output csv file. If not mentioned then the text will be printed to the shell
  -state <state(,state,state)> : only these states will appear in the output (e.g "-state open,closed")
  -stateonly : the output will only include the state of the port
  -report : output csv will be in the format that is required for an infra report appendix
  """
  sys.exit(1)

def parse(f):
  f = open(f).read()
  # ports is a list of all ports in the scan
  ports = set()
  # a list of text blocks, each block contains information about 1 host
  hosts = f.split('\n\n')
  if len(hosts) > 1:
    hosts = hosts[:-1]
  # final will be a list of dictionaries, each dictionary is a host
  final = []
  log = ''
  for host in hosts:
    try:
      # output will hold the information about a host before adding it to final
      output = {}
      try:
        info = re.findall(r'Nmap scan report for (.*)',host)[0].split()
      except:
        continue
      if len(info) > 1:
        # Hostname exists
        output['IP'] = re.findall(r'(\d{0,3}\.\d{0,3}\.\d{0,3}\.\d{0,3})',info[1])[0]
        output['HOSTNAME'] = info[0]
      else:
        # no Hostname
        output['IP'] = info[0]
        output['HOSTNAME'] = ''
      os = re.findall(' OS: (.*)',host)
      if os == []:
        os = re.findall('OS details: (.*)',host)
      if os == []:
        output['OS'] = ''
      else:
        output['OS'] = os[0].replace(","," ")
      # PORTS is a dictionary of all the ports that had some kind of respond on the host
      output['PORTS'] = {}
      for line in host.splitlines():
        if re.findall('[0-9]{1,5}\/[tcud]{2}p',line) != []:
          port_vars = line.split()
          #find out the port state
          state = port_vars[1]
          if states != [] and state in states:
            port = re.findall('([0-9]{1,5})\/[tcud]{2}p',port_vars[0])[0]
            ports.add(int(port))
            port_dict = {}
            # find out what flags existed in the command
            sections = re.findall('(PORT.*)',host)[0].split()
            for i in range(1,len(sections)-1):
              port_dict[sections[i]] = port_vars[i]
            # parse version
            if len(port_vars) > i+1:
              port_dict[sections[-1]] = ' '.join(port_vars[i+1:])
            output['PORTS'][port] = port_dict
          elif states == []:
            port = re.findall('([0-9]{1,5})\/[tcud]{2}p',port_vars[0])[0]
            ports.add(int(port))
            port_dict = {}
            # find out what flags existed in the command
            sections = re.findall('(PORT.*)',host)[0].split()
            for i in range(1,len(sections)-1):
              port_dict[sections[i]] = port_vars[i]
            # parse version
            if len(port_vars) > i+1:
              port_dict[sections[-1]] = ' '.join(port_vars[i+1:])
            output['PORTS'][port] = port_dict
      if output['PORTS'] == {}:
        output['ALIVE'] = '0'
      else:
        output['ALIVE'] = '1'
      final.append(output)
    except:
      log += str(info) + '\r\n'
  ports = list(ports)
  ports.sort()
  return final, ports, log

def parse_to_excel(final, ports):
  ports = map(lambda p: str(p), ports)
  txt = [','.join(['IP','Hostname','ALIVE?','OS']+ports)]
  for host in final:
    row = ','.join([host['IP'],host['HOSTNAME'],host['ALIVE'],host['OS'],''])
    ind = 0
    host_ports = host['PORTS'].keys()
    host_ports = map(lambda i: int(i),host_ports)
    host_ports.sort()
    host_ports = map(lambda i: str(i),host_ports)
    for port in host_ports:
      row += ','*(ports.index(port)-ind)
      info = host['PORTS'][port].keys()
      info.sort()
      for data in info:
        if stateonly == False:
          row += host['PORTS'][port][data]+'; '
        elif stateonly == True and data == 'STATE':
          row += host['PORTS'][port][data]
      ind = ports.index(port)
    txt.append(row)
  txt = '\n'.join(txt)
  return txt

def parse_to_report(final):
  delimiter = ','
  lineDelimiter = '\n'
  openPortChar = 'X'
  otherPortsDelimiter = ', '
  IPHeader = 'IP Address'
  otherHeaderText = 'Other'
  
  txt = IPHeader + delimiter
  # Create title
  knownPorts = KnownPorts.keys()
  knownPorts.sort()
  for portNum in knownPorts:
    txt += KnownPorts[portNum] + ' (' + str(portNum) + ')' + delimiter

  txt += otherHeaderText + lineDelimiter

  # parse each host
  for host in final:
    hostline = host['IP'] + delimiter
    # Parse all Known Ports
    for portNum in knownPorts:
      try:
        if host['PORTS'][str(portNum)]['STATE'] == 'open':
          # The port exist
          hostline += openPortChar + delimiter
        else:
          # The port is not open
          hostline += delimiter
      except:
        # The port does not exist
        hostline += delimiter
    # Parse all other ports
    hostPorts = host['PORTS'].keys()
    hostPorts = map(int, hostPorts)
    hostPorts.sort()
    if hostPorts:
      otherPorts = []
      for portNum in hostPorts:
        if portNum not in knownPorts:
          otherPorts.append(str(portNum))
      hostline += '"' + otherPortsDelimiter.join(otherPorts) + '"'

    hostline += lineDelimiter
    txt += hostline
  
  return txt

  
# Main starts here
if (len(sys.argv)<2) or '-h' in sys.argv or '--help' in sys.argv:
  help()
  
if '-report' in sys.argv:
  REPORT = True
else:
  REPORT = False

if '-stateonly' in sys.argv:
  stateonly = True
else:
  stateonly = False

if '-state' in sys.argv:
  state = sys.argv[sys.argv.index('-state') + 1]
  states = state.split(",")
else:
  states = []

if '-output' in sys.argv:
  OUTPUT = True
  ind = sys.argv.index('-output') + 1
  outputfile = sys.argv[ind]
  if outputfile.find('"') != -1:
    while len(re.findall('"',outputfile)) < 2:
      ind += 1
      outputfile += sys.argv[ind]
else:
  OUTPUT = False
  
if '-dir' in sys.argv:
  ind = sys.argv.index('-dir') + 1
  directory = sys.argv[ind]
  if directory.find('"') != -1:
    while len(re.findall('"',directory)) < 2:
      ind += 1
      directory += sys.argv[ind]
  all_files = os.listdir(directory)
  nmaps = []
  for i in all_files:
    if i[-5:] == '.nmap':
      nmaps.append(directory+'\\'+i)
  all_final = []
  all_ports = []
  all_log = ''
  for i in nmaps:
    final,ports,log = parse(i)
    all_final += final
    all_ports += ports
    all_log += log
  all_ports = list(set(all_ports))
  all_ports.sort()
  if all_log:
    file(directory+'.log','w').write(all_log)
  if REPORT == True:
    txt = parse_to_report(all_final)
  else:
    txt = parse_to_excel(all_final,all_ports)

if '-file' in sys.argv:
  ind = sys.argv.index('-file') + 1
  filename = sys.argv[ind]
  if filename.find('"') != -1:
    while len(re.findall('"',filename)) < 2:
      ind += 1
      filename += sys.argv[ind]
  final,ports,log = parse(filename)
  if log:
    file(filename+'.log','w').write(log)
  if REPORT == True:
    txt = parse_to_report(final)
  else:
    txt = parse_to_excel(final,ports)

if OUTPUT == True:
  file(outputfile,'w').write(txt)
else:
  print txt














