% Tests for Scapy's p0f module.

~ p0f

+ Basic p0f module tests

= Module loading
load_module('p0f')

= Fetch database
~ netaccess

try:
    from urllib.request import urlopen
except ImportError:
    from urllib2 import urlopen

for i in range(10):
    try:
        open("p0f.fp", 'wb').write(urlopen('https://raw.githubusercontent.com/p0f/p0f/e8b924ae7fa099a3a5fe7def0ce3e397fd9a7137/p0f.fp').read())
        break
    except:
        raise

conf.p0f_base = "p0f.fp"
p0fdb.reload(conf.p0f_base)

+ Default tests

= Test TCP p0f, SYN - Windows
~ netaccess

pkt = IP(b'E\x00\x004Se@\x00\x80\x06\x93?\n\x00\x00\x14\n\x00\x00\x0c\xc3\x08\x01\xbb\xcf\xb4\xbb\\\x00\x00\x00\x00\x80\x02 \x00\xeb\x1b\x00\x00\x02\x04\x05\xb4\x01\x03\x03\x08\x01\x01\x04\x02')
assert p0f(pkt) == (('s', 'win', 'Windows', '7 or 8'), 0, False)

= Test TCP p0f, SYN - Linux
~ netaccess

pkt = IP(b"E\x10\x00<A0@\x00@\x06t\xdd\xc0\xa8\x01\x8c\xc0\xa8\x01\xc2\xdd\xb8\x00\x17\xda\xcf!\xd5\x00\x00\x00\x00\xa0\x02\x16\xd0q\x10\x00\x00\x02\x04\x05\xb4\x04\x02\x08\n\x00'`\xe5\x00\x00\x00\x00\x01\x03\x03\x07")
assert p0f(pkt) == (('s', 'unix', 'Linux', '2.6.x'), 0, False)

= Test TCP p0f, SYN - IPv6 FreeBSD
~ netaccess

pkt = IPv6(hlim=64) / TCP(seq=1, window=65535, options=[("MSS", 150), ("NOP", None), ("WScale", 6), ("SAckOK", ""), ("Timestamp", (12345, 0))])
assert p0f(pkt) == (('s', 'unix', 'FreeBSD', '9.x or newer'), 0, False)

= Test TCP p0f, SYN-ACK - Linux
~ netaccess

pkt = IP(b'E\x00\x00<\x00\x00@\x008\x06N;?t\xf3a\xc0\xa8\x01\x03\x00P\xe5\xc0\xa3\xc4\x80\x9f\xe5\x94=\xab\xa0\x12\x16\xa0N\x07\x00\x00\x02\x04\x05\xb4\x04\x02\x08\n\x8d\x9d\x9d\xfa\x00\x17\x95e\x01\x03\x03\x05')
assert p0f(pkt) == (('s', 'unix', 'Linux', '2.6.x'), 8, False)

= Test HTTP p0f, request - wget
~ netaccess

pkt = IP(b'E\x00\x00\xba\xcb]@\x00@\x06(d\xc0\xa8\x01\x8c\xae\x8f\xd5\xb8\xe1N\x00P\x8eP\x19\x02\xc7R\x9d\x89\x80\x18\x00.G)\x00\x00\x01\x01\x08\n\x00!\xd2_1\xc7\xbaHGET /images/layout/logo.png HTTP/1.0\r\nUser-Agent: Wget/1.12 (linux-gnu)\r\nAccept: */*\r\nHost: packetlife.net\r\nConnection: Keep-Alive\r\n\r\n')
assert p0f(pkt) == (('s', '!', 'wget', '', ('@unix', 'Windows')), False)

= Test HTTP p0f, response - nginx
~ netaccess

pkt = IP(b"E\x00\x05\xdc'\xde@\x00\xfb\x06\x0b\xc1\xae\x8f\xd5\xb8\xc0\xa8\x01\x8c\x00P\xe1N\xc7R\x9d\x89\x8eP\x19\x88\x80\x10\x00lS\xc4\x00\x00\x01\x01\x08\n1\xc7\xbaT\x00!\xd2_HTTP/1.1 200 OK\r\nServer: nginx/0.8.53\r\nDate: Tue, 01 Mar 2011 20:45:16 GMT\r\nContent-Type: image/png\r\nContent-Length: 21684\r\nLast-Modified: Fri, 21 Jan 2011 03:41:14 GMT\r\nConnection: keep-alive\r\nKeep-Alive: timeout=20\r\nExpires: Wed, 29 Feb 2012 20:45:16 GMT\r\nCache-Control: max-age=31536000\r\nCache-Control: public\r\nVary: Accept-Encoding\r\nAccept-Ranges: bytes\r\n\r\n")
assert p0f(pkt) == (('s', '!', 'nginx', '1.x', ('@unix',)), False)

= Test MTU p0f
~ netaccess

pkt = IP(b'E\x00\x004Se@\x00\x80\x06\x93?\n\x00\x00\x14\n\x00\x00\x0c\xc3\x08\x01\xbb\xcf\xb4\xbb\\\x00\x00\x00\x00\x80\x02 \x00\xeb\x1b\x00\x00\x02\x04\x05\xb4\x01\x03\x03\x08\x01\x01\x04\x02')
assert fingerprint_mtu(pkt) == "Ethernet or modem"


+ Tests for p0f_impersonate

= Check that the impersonated packet is properly detected by p0f
~ netaccess

pkt = p0f_impersonate(IP()/TCP(), osgenre="Linux", osdetails="3.11 and newer")
assert p0f(pkt) == (("s", "unix", "Linux", "3.11 and newer"), 0, False)

= Impersonate when window size must be multiple of some integer
sig = "*:64:0:1460:%8192,0:mss,nop,ws::0"
pkt = p0f_impersonate(IP()/TCP(), signature=sig)
assert pkt[TCP].window % 8192 == 0

= Impersonate when window size must be multiple of mss
sig = "*:64:0:1024:mss*4,0:mss::0"
pkt = p0f_impersonate(IP()/TCP(), signature=sig)
assert (pkt[TCP].window // 4) == 1024

= Impersonate when the following quirks are present: seq-,ack-,pushf+,urgf+
sig = "*:64:0:1460:8192,0:mss:seq-,ack-,pushf+,urgf+:0"
pkt = p0f_impersonate(IP()/TCP(seq=1, ack=1, flags="S"), signature=sig)
tcp = pkt[TCP]
assert pkt[TCP].seq == pkt[TCP].ack == 0
assert pkt[TCP].flags.A and pkt[TCP].flags.P and pkt[TCP].flags.U

= Use valid option values from original packet
sig = "*:64:0:*:8192,*:mss,ws,ts::0"
opts = [("MSS", 1400), ("WScale", 3), ("Timestamp", (97256, 0))]
pkt = p0f_impersonate(IP()/TCP(options=opts), signature=sig)
assert pkt[TCP].options == opts

= Discard invalid options values
sig = "*:64:0:1000:8192,5:mss,ws::0"
opts = [("MSS", 1400), ("WScale", 3)]
pkt = p0f_impersonate(IP()/TCP(options=opts), signature=sig)
assert pkt[TCP].options[0][1] == 1000
assert pkt[TCP].options[1][1] == 5

+ Clear temp files

= Remove fp files
def _rem(f):
    try:
        os.remove(f)
    except:
        pass

_rem("p0f.fp")
