#!/usr/bin/env python
#
# Copyright (c) 2004 Niels Provos <provos@citi.umich.edu>
# All rights reserved.
#
import os
import sys
import getopt
import popen2
import regress
import time
import re
import tempfile

def get_ipaddr(count):
    octet1 = count % 254
    octet2 = count / 254

    return "192.18.%d.%d" % (octet2 + 1, octet1 + 1)


def nmap(count):

    ipaddr = get_ipaddr(count)
# We might want to use a temporary file?
#    (_, logfile) = tempfile.mkstemp()
# XXXX is this the way it should be done?
#   log = open(logfile, "w")
# Create the logfile in the current directory, not under /tmp to
# avoid race conditions
    logfile = 'nmap.log'
    log = open(logfile, "w")
    nmap = reg.find_cmd('nmap')
    if nmap == 0:
        print >>sys.stderr, "Could not find a suitable binary for nmap!"
	sys.exit(1)
    else:
        if debug:
            print >>sys.stderr, "Found nmap binary at "+nmap
            

    # Check if nmap exists, although it should because we found
    # it above, this is just a race condition check
    try:
        os.stat(nmap)
    except OSError:
        print >>sys.stderr, 'Cannot find nmap program "%s" ' % nmap
        sys.exit(1)
    # XXX - find out which 'lo' should we use
    loopif = reg.find_loopback()
    if not len(loopif):
        print >>sys.stderr, 'Could not extract the loopback interface ', error,
        sys.exit(1)
    # XXX - we should use --datadir to make sure that the fingerprint
    # file matches the one we used to generate the configuration
    # or otherwise it might use the system's and it will not work
    # So maybe we should get the fingerprint as an argument, copy
    # it over to our directory as 'nmap-os-fingerprints' and remove
    # it when existing the function
    (fr, _, fe) = popen2.popen3(nmap+' -P0 127.0.0.1 -e '+loopif+' -sS -O -p1,23 %s' % ipaddr)

    oses = ""
    output = ""
    for line in fr:
#        if re.match("^(SInfo|TSeq|T[0-9]|PU)", line):
#        if debug:
#            print >>sys.stderr, "Reading line %s" % line
        output += line
        res = re.match("OS (guesses|details): (.*)", line)
        if res:
            oses = res.group(2)
        elif re.match("^No exact OS", line):
            oses = None

    res = 0
    if oses:
        if oses == prints[count]:
            print "+",
            res = 1
        elif oses.find(prints[count]) != -1:
            print "-",
            res = 2
        else:
            print "?",
            print >>log, "Wanted: '%s' but got '%s':\n%s" % \
                  (count, oses, output)
            failures.append("%d:" % count + prints[count] + oses + ":\n" + output)
    else:
        print >>log, "Wanted: '"+prints[count]+"' but nmap did not find the OS: :\n", output
        failures.append("%d: No match for signature '%s'. Nmap result was:\n%s" % (count, prints[count], output))
        print "_",

    sys.stdout.flush()
    fr.close()
    fe.close()
    log.close()
    return res
    
def make_configuration(filename, fingerprints):
    try:
        output = open(filename, "w")
    except:
        print >>sys.stderr, "Could not write to file '%s'!" % output
        sys.exit(1)
    try:
        input = open(fingerprints, "r")
    except:
        print >>sys.stderr, "Cannot read fingerprint file '%s'!" % fingerprints
        sys.exit(1)

    print >>output, """create template
set template default tcp action reset
add template tcp port 23 open
"""
    count = 0
    r = re.compile('\s*$')
    m = re.compile("^Fingerprint ([^#]*)$")
    for line in input:
        line = r.sub("", line)
        res = m.match(line)
        if not res:
            continue

        fname = res.group(1)

        prints[count] = fname
        ipaddr = get_ipaddr(count)

        # Create template
        print >>output, 'bind %s template' % ipaddr
        print >>output, 'set %s personality "%s"' % (ipaddr, fname)

        count += 1

    output.close()
    input.close()

    return count

# Main
def usage():
    print "Usage: %s [-dg]" % sys.argv[0]

try:
    opts, args = getopt.getopt(sys.argv[1:],"dg", ["debug", "generate"])
except getopt.GetoptError:
    usage()
    sys.exit(2)

debug = 0
generate = 0
for o, a in opts:
    if o in ("-d", "--debug"):
	    debug = 1
    if o in ("-g", "--generate"):
	    generate = 1
    if o in ("-h", "--help"):
            usage()
            sys.exit(1)

failures = []
prints = {}

number = make_configuration("config.nmap", "../nmap.prints")
if debug:
    print >>sys.stderr, "Generated configuration file for %u hosts" % number

reg = regress.regress("Nmap fingerprints", "../honeyd", "config.nmap", debug)
reg.generate = generate
reg.start_honeyd(reg.config)

reg.fe.read()

success = 0
partial = 0
nothing = 0
for count in range(0, number):
    res = nmap(count)
    if res == 1:
        success += 1
    elif res == 2:
        partial += 1
    else:
        nothing += 1
    reg.fe.read()

reg.stop_honeyd()
reg.runtests = number
reg.oktests = success
print "\nSuccesses: %d, Partials: %d, Nothing: %d of %d" % (success, partial, nothing, number)
if debug:
    for line in failures:
        print line
reg.finish()
