#! /usr/bin/env python2

# REQUIREMENTS:
# libnids (and libnet 1.0.2a, libpcap)
# python 2.2 or later
# pynids: http://pilcrow.madison.wi.us/pynids/

# shamelessly stolen from the pynids Example code ...
# passive HTTP/SSH/MUA client/server snarfer. all hail libnids.

# TODO for a more formal release
# - append client IP:service tuple

# standard imports
import os, pwd, string, sys

# local imports ...
import nids

NOTROOT = "nobody"   	# edit to taste
end_states = (nids.NIDS_CLOSE, nids.NIDS_TIMEOUT, nids.NIDS_RESET)
seen = []		# hosts we've seen by IP:port

def handleTcpStream(tcp):
    import string

    mail_from = ""
    mailer = ""

    if tcp.nids_state == nids.NIDS_JUST_EST:
        ((src, sport), (dst, dport)) = tcp.addr
        if dport in (80, 8000, 8080, 22, 2222, 2022, 25, 587):
            tcp.client.collect = 1
            tcp.server.collect = 1
    elif tcp.nids_state == nids.NIDS_DATA:
        # keep all of the stream's new data
        tcp.discard(0)
    elif tcp.nids_state in end_states:
        headers = string.split(tcp.client.data, "\n")	# grab server headers
        for header in headers:
            if tcp.addr[1][1] in (80, 8000, 8080):
                if "Server: " in header: 		# get Server header
                    servtype = string.replace(header, "Server: ", "")
                    if tcp.addr[1] not in seen:		# don't report twice
                        print "%16s:%4d: \t%s" %(tcp.addr[1][0], tcp.addr[1][1], servtype)
                        seen.append(tcp.addr[1])
                        break
            if tcp.addr[1][1] in (22, 2222, 2022):
                if "SSH-" in header:			# SSH server string
                    if tcp.addr[1] not in seen:         # don't report twice
                        print "%16s:%4d: \t%s" %(tcp.addr[1][0], tcp.addr[1][1], header)
                        seen.append(tcp.addr[1])
                        break
        headers = string.split(tcp.server.data, "\n")	# grab client data
        for header in headers:
            if tcp.addr[1][1] in (80, 8000, 8080):
                if "User-Agent: " in header: 		# get client's user-agent header
                    clienttype = string.replace(header, "User-Agent: ", "")
                    if tcp.addr[0][0] not in seen:		# don't report twice
                        print "%16s:%4s:\t%s" %(tcp.addr[0][0], "http", clienttype)
                        seen.append(tcp.addr[0][0])
                        break
            if tcp.addr[1][1] in (22, 2222, 2022):
                if "SSH-" in header:			# SSH client string
                    if tcp.addr[0][0] not in seen:         # don't report twice
                        print "%16s:%4s: \t%s" %(tcp.addr[0][0], "ssh", header)
                        seen.append(tcp.addr[0][0])
                        break
            if tcp.addr[1][1] in (25, 587):
                if "From: " in header:
                    mail_from = string.replace(header, "From: ", "")
                    continue 
                if "X-Mailer: " in header:
                    mailer = string.replace(header, "X-Mailer: ", "")
                    continue 
                if mail_from and mailer:
       	            print "%s\n%16s:%4s: \t%s" %(mail_from, "", "smtp", mailer)
                    break

def main():
    nids.param("scan_num_hosts", 0)  # disable portscan detection
    if not nids.init():
        print "error -", nids.errbuf()
        sys.exit(1)

    (uid, gid) = pwd.getpwnam(NOTROOT)[2:4]
    os.setgroups([gid,])
    os.setgid(gid)
    os.setuid(uid)
    if 0 in [os.getuid(), os.getgid()] + list(os.getgroups()):
        print "error - drop root, please!"
        sys.exit(1)

    nids.register_tcp(handleTcpStream)
    try:
        nids.run()  # loop forever
    except KeyboardInterrupt:
        for ip in seen:
            print "%s" % (ip[0])
        sys.exit(1)

if __name__ == '__main__':
    main()
