From b89efa188810bf95a9d245e69e2961b5721c3b0f Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 21 Mar 2016 16:03:47 +0200 Subject: scapy python 2/3 --- .../scapy-2.3.1/python2/scapy/layers/__init__.py | 8 + .../scapy-2.3.1/python2/scapy/layers/all.py | 34 + .../scapy-2.3.1/python2/scapy/layers/bluetooth.py | 210 ++ .../scapy-2.3.1/python2/scapy/layers/dhcp.py | 373 +++ .../scapy-2.3.1/python2/scapy/layers/dhcp6.py | 1715 +++++++++++ .../scapy-2.3.1/python2/scapy/layers/dns.py | 690 +++++ .../scapy-2.3.1/python2/scapy/layers/dot11.py | 559 ++++ .../scapy-2.3.1/python2/scapy/layers/gprs.py | 21 + .../scapy-2.3.1/python2/scapy/layers/hsrp.py | 79 + .../scapy-2.3.1/python2/scapy/layers/inet.py | 1556 ++++++++++ .../scapy-2.3.1/python2/scapy/layers/inet6.py | 3011 ++++++++++++++++++++ .../scapy-2.3.1/python2/scapy/layers/ipsec.py | 981 +++++++ .../scapy-2.3.1/python2/scapy/layers/ir.py | 44 + .../scapy-2.3.1/python2/scapy/layers/isakmp.py | 348 +++ .../scapy-2.3.1/python2/scapy/layers/l2.py | 542 ++++ .../scapy-2.3.1/python2/scapy/layers/l2tp.py | 36 + .../scapy-2.3.1/python2/scapy/layers/llmnr.py | 65 + .../scapy-2.3.1/python2/scapy/layers/mgcp.py | 45 + .../scapy-2.3.1/python2/scapy/layers/mobileip.py | 47 + .../scapy-2.3.1/python2/scapy/layers/netbios.py | 222 ++ .../scapy-2.3.1/python2/scapy/layers/netflow.py | 48 + .../scapy-2.3.1/python2/scapy/layers/ntp.py | 77 + .../scapy-2.3.1/python2/scapy/layers/pflog.py | 59 + .../scapy-2.3.1/python2/scapy/layers/ppp.py | 347 +++ .../scapy-2.3.1/python2/scapy/layers/radius.py | 65 + .../scapy-2.3.1/python2/scapy/layers/rip.py | 74 + .../scapy-2.3.1/python2/scapy/layers/rtp.py | 40 + .../scapy-2.3.1/python2/scapy/layers/sctp.py | 437 +++ .../scapy-2.3.1/python2/scapy/layers/sebek.py | 109 + .../scapy-2.3.1/python2/scapy/layers/skinny.py | 161 ++ .../scapy-2.3.1/python2/scapy/layers/smb.py | 354 +++ .../scapy-2.3.1/python2/scapy/layers/snmp.py | 255 ++ .../scapy-2.3.1/python2/scapy/layers/tftp.py | 477 ++++ .../scapy-2.3.1/python2/scapy/layers/vrrp.py | 39 + .../scapy-2.3.1/python2/scapy/layers/x509.py | 108 + 35 files changed, 13236 insertions(+) create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/__init__.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/all.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/bluetooth.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/dhcp.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/dhcp6.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/dns.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/dot11.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/gprs.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/hsrp.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/inet.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/inet6.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/ipsec.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/ir.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/isakmp.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/l2.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/l2tp.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/llmnr.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/mgcp.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/mobileip.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/netbios.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/netflow.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/ntp.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/pflog.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/ppp.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/radius.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/rip.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/rtp.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/sctp.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/sebek.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/skinny.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/smb.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/snmp.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/tftp.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/vrrp.py create mode 100644 scripts/external_libs/scapy-2.3.1/python2/scapy/layers/x509.py (limited to 'scripts/external_libs/scapy-2.3.1/python2/scapy/layers') diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/__init__.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/__init__.py new file mode 100644 index 00000000..a3f2afb9 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/__init__.py @@ -0,0 +1,8 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +Layer package. +""" diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/all.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/all.py new file mode 100644 index 00000000..e92c22c5 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/all.py @@ -0,0 +1,34 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +All layers. Configurable with conf.load_layers. +""" + +from scapy.config import conf +from scapy.error import log_loading +import logging +log = logging.getLogger("scapy.loading") + +def _import_star(m): + mod = __import__(m, globals(), locals()) + for k,v in mod.__dict__.iteritems(): + globals()[k] = v + +# TBD-hhaim limit the number of layers +#conf.load_layers +#'inet6' +for _l in ['l2','inet','inet6']: + log_loading.debug("Loading layer %s" % _l) + #print "load ",_l + _import_star(_l) + #try: + # _import_star(_l) + #except Exception,e: + #log.warning("can't import layer %s: %s" % (_l,e)) + + + + diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/bluetooth.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/bluetooth.py new file mode 100644 index 00000000..662bad3b --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/bluetooth.py @@ -0,0 +1,210 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +Bluetooth layers, sockets and send/receive functions. +""" + +import socket,struct + +from scapy.config import conf +from scapy.packet import * +from scapy.fields import * +from scapy.supersocket import SuperSocket +from scapy.data import MTU + + +class HCI_Hdr(Packet): + name = "HCI header" + fields_desc = [ ByteEnumField("type",2,{1:"command",2:"ACLdata",3:"SCOdata",4:"event",5:"vendor"}),] + + def mysummary(self): + return self.sprintf("HCI %type%") + +class HCI_ACL_Hdr(Packet): + name = "HCI ACL header" + fields_desc = [ ByteField("handle",0), # Actually, handle is 12 bits and flags is 4. + ByteField("flags",0), # I wait to write a LEBitField + LEShortField("len",None), ] + def post_build(self, p, pay): + p += pay + if self.len is None: + l = len(p)-4 + p = p[:2]+chr(l&0xff)+chr((l>>8)&0xff)+p[4:] + return p + + +class L2CAP_Hdr(Packet): + name = "L2CAP header" + fields_desc = [ LEShortField("len",None), + LEShortEnumField("cid",0,{1:"control"}),] + + def post_build(self, p, pay): + p += pay + if self.len is None: + l = len(p)-4 + p = p[:2]+chr(l&0xff)+chr((l>>8)&0xff)+p[4:] + return p + + + +class L2CAP_CmdHdr(Packet): + name = "L2CAP command header" + fields_desc = [ + ByteEnumField("code",8,{1:"rej",2:"conn_req",3:"conn_resp", + 4:"conf_req",5:"conf_resp",6:"disconn_req", + 7:"disconn_resp",8:"echo_req",9:"echo_resp", + 10:"info_req",11:"info_resp"}), + ByteField("id",0), + LEShortField("len",None) ] + def post_build(self, p, pay): + p += pay + if self.len is None: + l = len(p)-4 + p = p[:2]+chr(l&0xff)+chr((l>>8)&0xff)+p[4:] + return p + def answers(self, other): + if other.id == self.id: + if self.code == 1: + return 1 + if other.code in [2,4,6,8,10] and self.code == other.code+1: + if other.code == 8: + return 1 + return self.payload.answers(other.payload) + return 0 + +class L2CAP_ConnReq(Packet): + name = "L2CAP Conn Req" + fields_desc = [ LEShortEnumField("psm",0,{1:"SDP",3:"RFCOMM",5:"telephony control"}), + LEShortField("scid",0), + ] + +class L2CAP_ConnResp(Packet): + name = "L2CAP Conn Resp" + fields_desc = [ LEShortField("dcid",0), + LEShortField("scid",0), + LEShortEnumField("result",0,["no_info","authen_pend","author_pend"]), + LEShortEnumField("status",0,["success","pend","bad_psm", + "cr_sec_block","cr_no_mem"]), + ] + def answers(self, other): + return self.scid == other.scid + +class L2CAP_CmdRej(Packet): + name = "L2CAP Command Rej" + fields_desc = [ LEShortField("reason",0), + ] + + +class L2CAP_ConfReq(Packet): + name = "L2CAP Conf Req" + fields_desc = [ LEShortField("dcid",0), + LEShortField("flags",0), + ] + +class L2CAP_ConfResp(Packet): + name = "L2CAP Conf Resp" + fields_desc = [ LEShortField("scid",0), + LEShortField("flags",0), + LEShortEnumField("result",0,["success","unaccept","reject","unknown"]), + ] + def answers(self, other): + return self.scid == other.scid + + +class L2CAP_DisconnReq(Packet): + name = "L2CAP Disconn Req" + fields_desc = [ LEShortField("dcid",0), + LEShortField("scid",0), ] + +class L2CAP_DisconnResp(Packet): + name = "L2CAP Disconn Resp" + fields_desc = [ LEShortField("dcid",0), + LEShortField("scid",0), ] + def answers(self, other): + return self.scid == other.scid + + + +class L2CAP_InfoReq(Packet): + name = "L2CAP Info Req" + fields_desc = [ LEShortEnumField("type",0,{1:"CL_MTU",2:"FEAT_MASK"}), + StrField("data","") + ] + + +class L2CAP_InfoResp(Packet): + name = "L2CAP Info Resp" + fields_desc = [ LEShortField("type",0), + LEShortEnumField("result",0,["success","not_supp"]), + StrField("data",""), ] + def answers(self, other): + return self.type == other.type + + + +bind_layers( HCI_Hdr, HCI_ACL_Hdr, type=2) +bind_layers( HCI_Hdr, conf.raw_layer, ) +bind_layers( HCI_ACL_Hdr, L2CAP_Hdr, ) +bind_layers( L2CAP_Hdr, L2CAP_CmdHdr, cid=1) +bind_layers( L2CAP_CmdHdr, L2CAP_CmdRej, code=1) +bind_layers( L2CAP_CmdHdr, L2CAP_ConnReq, code=2) +bind_layers( L2CAP_CmdHdr, L2CAP_ConnResp, code=3) +bind_layers( L2CAP_CmdHdr, L2CAP_ConfReq, code=4) +bind_layers( L2CAP_CmdHdr, L2CAP_ConfResp, code=5) +bind_layers( L2CAP_CmdHdr, L2CAP_DisconnReq, code=6) +bind_layers( L2CAP_CmdHdr, L2CAP_DisconnResp, code=7) +bind_layers( L2CAP_CmdHdr, L2CAP_InfoReq, code=10) +bind_layers( L2CAP_CmdHdr, L2CAP_InfoResp, code=11) + +class BluetoothL2CAPSocket(SuperSocket): + desc = "read/write packets on a connected L2CAP socket" + def __init__(self, peer): + s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, + socket.BTPROTO_L2CAP) + s.connect((peer,0)) + + self.ins = self.outs = s + + def recv(self, x=MTU): + return L2CAP_CmdHdr(self.ins.recv(x)) + + +class BluetoothHCISocket(SuperSocket): + desc = "read/write on a BlueTooth HCI socket" + def __init__(self, iface=0x10000, type=None): + s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI) + s.setsockopt(socket.SOL_HCI, socket.HCI_DATA_DIR,1) + s.setsockopt(socket.SOL_HCI, socket.HCI_TIME_STAMP,1) + s.setsockopt(socket.SOL_HCI, socket.HCI_FILTER, struct.pack("IIIh2x", 0xffffffffL,0xffffffffL,0xffffffffL,0)) #type mask, event mask, event mask, opcode + s.bind((iface,)) + self.ins = self.outs = s +# s.connect((peer,0)) + + + def recv(self, x): + return HCI_Hdr(self.ins.recv(x)) + +## Bluetooth + + +@conf.commands.register +def srbt(peer, pkts, inter=0.1, *args, **kargs): + """send and receive using a bluetooth socket""" + s = conf.BTsocket(peer=peer) + a,b = sndrcv(s,pkts,inter=inter,*args,**kargs) + s.close() + return a,b + +@conf.commands.register +def srbt1(peer, pkts, *args, **kargs): + """send and receive 1 packet using a bluetooth socket""" + a,b = srbt(peer, pkts, *args, **kargs) + if len(a) > 0: + return a[0][1] + + + +conf.BTsocket = BluetoothL2CAPSocket diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/dhcp.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/dhcp.py new file mode 100644 index 00000000..18f83f00 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/dhcp.py @@ -0,0 +1,373 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +DHCP (Dynamic Host Configuration Protocol) d BOOTP +""" + +import struct + +from scapy.packet import * +from scapy.fields import * +from scapy.ansmachine import * +from scapy.layers.inet import UDP,IP +from scapy.layers.l2 import Ether +from scapy.base_classes import Net +from scapy.volatile import RandField + +from scapy.arch import get_if_raw_hwaddr +from scapy.sendrecv import srp1 + +dhcpmagic="c\x82Sc" + + +class BOOTP(Packet): + name = "BOOTP" + fields_desc = [ ByteEnumField("op",1, {1:"BOOTREQUEST", 2:"BOOTREPLY"}), + ByteField("htype",1), + ByteField("hlen",6), + ByteField("hops",0), + IntField("xid",0), + ShortField("secs",0), + FlagsField("flags", 0, 16, "???????????????B"), + IPField("ciaddr","0.0.0.0"), + IPField("yiaddr","0.0.0.0"), + IPField("siaddr","0.0.0.0"), + IPField("giaddr","0.0.0.0"), + Field("chaddr","", "16s"), + Field("sname","","64s"), + Field("file","","128s"), + StrField("options","") ] + def guess_payload_class(self, payload): + if self.options[:len(dhcpmagic)] == dhcpmagic: + return DHCP + else: + return Packet.guess_payload_class(self, payload) + def extract_padding(self,s): + if self.options[:len(dhcpmagic)] == dhcpmagic: + # set BOOTP options to DHCP magic cookie and make rest a payload of DHCP options + payload = self.options[len(dhcpmagic):] + self.options = self.options[:len(dhcpmagic)] + return payload, None + else: + return "", None + def hashret(self): + return struct.pack("L", self.xid) + def answers(self, other): + if not isinstance(other, BOOTP): + return 0 + return self.xid == other.xid + + + +#DHCP_UNKNOWN, DHCP_IP, DHCP_IPLIST, DHCP_TYPE \ +#= range(4) +# + +DHCPTypes = { + 1: "discover", + 2: "offer", + 3: "request", + 4: "decline", + 5: "ack", + 6: "nak", + 7: "release", + 8: "inform", + 9: "force_renew", + 10:"lease_query", + 11:"lease_unassigned", + 12:"lease_unknown", + 13:"lease_active", + } + +DHCPOptions = { + 0: "pad", + 1: IPField("subnet_mask", "0.0.0.0"), + 2: "time_zone", + 3: IPField("router","0.0.0.0"), + 4: IPField("time_server","0.0.0.0"), + 5: IPField("IEN_name_server","0.0.0.0"), + 6: IPField("name_server","0.0.0.0"), + 7: IPField("log_server","0.0.0.0"), + 8: IPField("cookie_server","0.0.0.0"), + 9: IPField("lpr_server","0.0.0.0"), + 12: "hostname", + 14: "dump_path", + 15: "domain", + 17: "root_disk_path", + 22: "max_dgram_reass_size", + 23: "default_ttl", + 24: "pmtu_timeout", + 28: IPField("broadcast_address","0.0.0.0"), + 35: "arp_cache_timeout", + 36: "ether_or_dot3", + 37: "tcp_ttl", + 38: "tcp_keepalive_interval", + 39: "tcp_keepalive_garbage", + 40: "NIS_domain", + 41: IPField("NIS_server","0.0.0.0"), + 42: IPField("NTP_server","0.0.0.0"), + 43: "vendor_specific", + 44: IPField("NetBIOS_server","0.0.0.0"), + 45: IPField("NetBIOS_dist_server","0.0.0.0"), + 50: IPField("requested_addr","0.0.0.0"), + 51: IntField("lease_time", 43200), + 54: IPField("server_id","0.0.0.0"), + 55: "param_req_list", + 57: ShortField("max_dhcp_size", 1500), + 58: IntField("renewal_time", 21600), + 59: IntField("rebinding_time", 37800), + 60: "vendor_class_id", + 61: "client_id", + + 64: "NISplus_domain", + 65: IPField("NISplus_server","0.0.0.0"), + 69: IPField("SMTP_server","0.0.0.0"), + 70: IPField("POP3_server","0.0.0.0"), + 71: IPField("NNTP_server","0.0.0.0"), + 72: IPField("WWW_server","0.0.0.0"), + 73: IPField("Finger_server","0.0.0.0"), + 74: IPField("IRC_server","0.0.0.0"), + 75: IPField("StreetTalk_server","0.0.0.0"), + 76: "StreetTalk_Dir_Assistance", + 82: "relay_agent_Information", + 53: ByteEnumField("message-type", 1, DHCPTypes), + # 55: DHCPRequestListField("request-list"), + 255: "end" + } + +DHCPRevOptions = {} + +for k,v in DHCPOptions.iteritems(): + if type(v) is str: + n = v + v = None + else: + n = v.name + DHCPRevOptions[n] = (k,v) +del(n) +del(v) +del(k) + + + + +class RandDHCPOptions(RandField): + def __init__(self, size=None, rndstr=None): + if size is None: + size = RandNumExpo(0.05) + self.size = size + if rndstr is None: + rndstr = RandBin(RandNum(0,255)) + self.rndstr=rndstr + self._opts = DHCPOptions.values() + self._opts.remove("pad") + self._opts.remove("end") + def _fix(self): + op = [] + for k in range(self.size): + o = random.choice(self._opts) + if type(o) is str: + op.append((o,self.rndstr*1)) + else: + op.append((o.name, o.randval()._fix())) + return op + + +class DHCPOptionsField(StrField): + islist=1 + def i2repr(self,pkt,x): + s = [] + for v in x: + if type(v) is tuple and len(v) >= 2: + if DHCPRevOptions.has_key(v[0]) and isinstance(DHCPRevOptions[v[0]][1],Field): + f = DHCPRevOptions[v[0]][1] + vv = ",".join(f.i2repr(pkt,val) for val in v[1:]) + else: + vv = ",".join(repr(val) for val in v[1:]) + r = "%s=%s" % (v[0],vv) + s.append(r) + else: + s.append(sane(v)) + return "[%s]" % (" ".join(s)) + + def getfield(self, pkt, s): + return "", self.m2i(pkt, s) + def m2i(self, pkt, x): + opt = [] + while x: + o = ord(x[0]) + if o == 255: + opt.append("end") + x = x[1:] + continue + if o == 0: + opt.append("pad") + x = x[1:] + continue + if len(x) < 2 or len(x) < ord(x[1])+2: + opt.append(x) + break + elif DHCPOptions.has_key(o): + f = DHCPOptions[o] + + if isinstance(f, str): + olen = ord(x[1]) + opt.append( (f,x[2:olen+2]) ) + x = x[olen+2:] + else: + olen = ord(x[1]) + lval = [f.name] + try: + left = x[2:olen+2] + while left: + left, val = f.getfield(pkt,left) + lval.append(val) + except: + opt.append(x) + break + else: + otuple = tuple(lval) + opt.append(otuple) + x = x[olen+2:] + else: + olen = ord(x[1]) + opt.append((o, x[2:olen+2])) + x = x[olen+2:] + return opt + def i2m(self, pkt, x): + if type(x) is str: + return x + s = "" + for o in x: + if type(o) is tuple and len(o) >= 2: + name = o[0] + lval = o[1:] + + if isinstance(name, int): + onum, oval = name, "".join(lval) + elif DHCPRevOptions.has_key(name): + onum, f = DHCPRevOptions[name] + if f is not None: + lval = [f.addfield(pkt,"",f.any2i(pkt,val)) for val in lval] + oval = "".join(lval) + else: + warning("Unknown field option %s" % name) + continue + + s += chr(onum) + s += chr(len(oval)) + s += oval + + elif (type(o) is str and DHCPRevOptions.has_key(o) and + DHCPRevOptions[o][1] == None): + s += chr(DHCPRevOptions[o][0]) + elif type(o) is int: + s += chr(o)+"\0" + elif type(o) is str: + s += o + else: + warning("Malformed option %s" % o) + return s + + +class DHCP(Packet): + name = "DHCP options" + fields_desc = [ DHCPOptionsField("options","") ] + + +bind_layers( UDP, BOOTP, dport=67, sport=68) +bind_layers( UDP, BOOTP, dport=68, sport=67) +bind_bottom_up( UDP, BOOTP, dport=67, sport=67) +bind_layers( BOOTP, DHCP, options='c\x82Sc') + +def dhcp_request(iface=None,**kargs): + if conf.checkIPaddr != 0: + warning("conf.checkIPaddr is not 0, I may not be able to match the answer") + if iface is None: + iface = conf.iface + fam,hw = get_if_raw_hwaddr(iface) + return srp1(Ether(dst="ff:ff:ff:ff:ff:ff")/IP(src="0.0.0.0",dst="255.255.255.255")/UDP(sport=68,dport=67) + /BOOTP(chaddr=hw)/DHCP(options=[("message-type","discover"),"end"]),iface=iface,**kargs) + + +class BOOTP_am(AnsweringMachine): + function_name = "bootpd" + filter = "udp and port 68 and port 67" + send_function = staticmethod(sendp) + def parse_options(self, pool=Net("192.168.1.128/25"), network="192.168.1.0/24",gw="192.168.1.1", + domain="localnet", renewal_time=60, lease_time=1800): + if type(pool) is str: + poom = Net(pool) + self.domain = domain + netw,msk = (network.split("/")+["32"])[:2] + msk = itom(int(msk)) + self.netmask = ltoa(msk) + self.network = ltoa(atol(netw)&msk) + self.broadcast = ltoa( atol(self.network) | (0xffffffff&~msk) ) + self.gw = gw + if isinstance(pool,Gen): + pool = [k for k in pool if k not in [gw, self.network, self.broadcast]] + pool.reverse() + if len(pool) == 1: + pool, = pool + self.pool = pool + self.lease_time = lease_time + self.renewal_time = renewal_time + self.leases = {} + + def is_request(self, req): + if not req.haslayer(BOOTP): + return 0 + reqb = req.getlayer(BOOTP) + if reqb.op != 1: + return 0 + return 1 + + def print_reply(self, req, reply): + print "Reply %s to %s" % (reply.getlayer(IP).dst,reply.dst) + + def make_reply(self, req): + mac = req.src + if type(self.pool) is list: + if not self.leases.has_key(mac): + self.leases[mac] = self.pool.pop() + ip = self.leases[mac] + else: + ip = self.pool + + repb = req.getlayer(BOOTP).copy() + repb.op="BOOTREPLY" + repb.yiaddr = ip + repb.siaddr = self.gw + repb.ciaddr = self.gw + repb.giaddr = self.gw + del(repb.payload) + rep=Ether(dst=mac)/IP(dst=ip)/UDP(sport=req.dport,dport=req.sport)/repb + return rep + + +class DHCP_am(BOOTP_am): + function_name="dhcpd" + def make_reply(self, req): + resp = BOOTP_am.make_reply(self, req) + if DHCP in req: + dhcp_options = [(op[0],{1:2,3:5}.get(op[1],op[1])) + for op in req[DHCP].options + if type(op) is tuple and op[0] == "message-type"] + dhcp_options += [("server_id",self.gw), + ("domain", self.domain), + ("router", self.gw), + ("name_server", self.gw), + ("broadcast_address", self.broadcast), + ("subnet_mask", self.netmask), + ("renewal_time", self.renewal_time), + ("lease_time", self.lease_time), + "end" + ] + resp /= DHCP(options=dhcp_options) + return resp + + diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/dhcp6.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/dhcp6.py new file mode 100644 index 00000000..2bd215d0 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/dhcp6.py @@ -0,0 +1,1715 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +## Copyright (C) 2005 Guillaume Valadon +## Arnaud Ebalard + +""" +DHCPv6: Dynamic Host Configuration Protocol for IPv6. [RFC 3315] +""" + +import socket +from scapy.packet import * +from scapy.fields import * +from scapy.utils6 import * +from scapy.layers.inet6 import * +from scapy.ansmachine import AnsweringMachine + +############################################################################# +# Helpers ## +############################################################################# + +def get_cls(name, fallback_cls): + return globals().get(name, fallback_cls) + + +############################################################################# +############################################################################# +### DHCPv6 ### +############################################################################# +############################################################################# + +All_DHCP_Relay_Agents_and_Servers = "ff02::1:2" +All_DHCP_Servers = "ff05::1:3" # Site-Local scope : deprecated by 3879 + +dhcp6opts = { 1: "CLIENTID", + 2: "SERVERID", + 3: "IA_NA", + 4: "IA_TA", + 5: "IAADDR", + 6: "ORO", + 7: "PREFERENCE", + 8: "ELAPSED_TIME", + 9: "RELAY_MSG", + 11: "AUTH", + 12: "UNICAST", + 13: "STATUS_CODE", + 14: "RAPID_COMMIT", + 15: "USER_CLASS", + 16: "VENDOR_CLASS", + 17: "VENDOR_OPTS", + 18: "INTERFACE_ID", + 19: "RECONF_MSG", + 20: "RECONF_ACCEPT", + 21: "SIP Servers Domain Name List", #RFC3319 + 22: "SIP Servers IPv6 Address List", #RFC3319 + 23: "DNS Recursive Name Server Option", #RFC3646 + 24: "Domain Search List option", #RFC3646 + 25: "OPTION_IA_PD", #RFC3633 + 26: "OPTION_IAPREFIX", #RFC3633 + 27: "OPTION_NIS_SERVERS", #RFC3898 + 28: "OPTION_NISP_SERVERS", #RFC3898 + 29: "OPTION_NIS_DOMAIN_NAME", #RFC3898 + 30: "OPTION_NISP_DOMAIN_NAME", #RFC3898 + 31: "OPTION_SNTP_SERVERS", #RFC4075 + 32: "OPTION_INFORMATION_REFRESH_TIME", #RFC4242 + 33: "OPTION_BCMCS_SERVER_D", #RFC4280 + 34: "OPTION_BCMCS_SERVER_A", #RFC4280 + 36: "OPTION_GEOCONF_CIVIC", #RFC-ietf-geopriv-dhcp-civil-09.txt + 37: "OPTION_REMOTE_ID", #RFC4649 + 38: "OPTION_SUBSCRIBER_ID", #RFC4580 + 39: "OPTION_CLIENT_FQDN" } #RFC4704 + +dhcp6opts_by_code = { 1: "DHCP6OptClientId", + 2: "DHCP6OptServerId", + 3: "DHCP6OptIA_NA", + 4: "DHCP6OptIA_TA", + 5: "DHCP6OptIAAddress", + 6: "DHCP6OptOptReq", + 7: "DHCP6OptPref", + 8: "DHCP6OptElapsedTime", + 9: "DHCP6OptRelayMsg", + 11: "DHCP6OptAuth", + 12: "DHCP6OptServerUnicast", + 13: "DHCP6OptStatusCode", + 14: "DHCP6OptRapidCommit", + 15: "DHCP6OptUserClass", + 16: "DHCP6OptVendorClass", + 17: "DHCP6OptVendorSpecificInfo", + 18: "DHCP6OptIfaceId", + 19: "DHCP6OptReconfMsg", + 20: "DHCP6OptReconfAccept", + 21: "DHCP6OptSIPDomains", #RFC3319 + 22: "DHCP6OptSIPServers", #RFC3319 + 23: "DHCP6OptDNSServers", #RFC3646 + 24: "DHCP6OptDNSDomains", #RFC3646 + 25: "DHCP6OptIA_PD", #RFC3633 + 26: "DHCP6OptIAPrefix", #RFC3633 + 27: "DHCP6OptNISServers", #RFC3898 + 28: "DHCP6OptNISPServers", #RFC3898 + 29: "DHCP6OptNISDomain", #RFC3898 + 30: "DHCP6OptNISPDomain", #RFC3898 + 31: "DHCP6OptSNTPServers", #RFC4075 + 32: "DHCP6OptInfoRefreshTime", #RFC4242 + 33: "DHCP6OptBCMCSDomains", #RFC4280 + 34: "DHCP6OptBCMCSServers", #RFC4280 + #36: "DHCP6OptGeoConf", #RFC-ietf-geopriv-dhcp-civil-09.txt + 37: "DHCP6OptRemoteID", #RFC4649 + 38: "DHCP6OptSubscriberID", #RFC4580 + 39: "DHCP6OptClientFQDN", #RFC4704 + #40: "DHCP6OptPANAAgent", #RFC-ietf-dhc-paa-option-05.txt + #41: "DHCP6OptNewPOSIXTimeZone, #RFC4833 + #42: "DHCP6OptNewTZDBTimeZone, #RFC4833 + 43: "DHCP6OptRelayAgentERO" #RFC4994 + #44: "DHCP6OptLQQuery", #RFC5007 + #45: "DHCP6OptLQClientData", #RFC5007 + #46: "DHCP6OptLQClientTime", #RFC5007 + #47: "DHCP6OptLQRelayData", #RFC5007 + #48: "DHCP6OptLQClientLink", #RFC5007 +} + + +# sect 5.3 RFC 3315 : DHCP6 Messages types +dhcp6types = { 1:"SOLICIT", + 2:"ADVERTISE", + 3:"REQUEST", + 4:"CONFIRM", + 5:"RENEW", + 6:"REBIND", + 7:"REPLY", + 8:"RELEASE", + 9:"DECLINE", + 10:"RECONFIGURE", + 11:"INFORMATION-REQUEST", + 12:"RELAY-FORW", + 13:"RELAY-REPL" } + + +##################################################################### +### DHCPv6 DUID related stuff ### +##################################################################### + +duidtypes = { 1: "Link-layer address plus time", + 2: "Vendor-assigned unique ID based on Enterprise Number", + 3: "Link-layer Address" } + +# DUID hardware types - RFC 826 - Extracted from +# http://www.iana.org/assignments/arp-parameters on 31/10/06 +# We should add the length of every kind of address. +duidhwtypes = { 0: "NET/ROM pseudo", # Not referenced by IANA + 1: "Ethernet (10Mb)", + 2: "Experimental Ethernet (3Mb)", + 3: "Amateur Radio AX.25", + 4: "Proteon ProNET Token Ring", + 5: "Chaos", + 6: "IEEE 802 Networks", + 7: "ARCNET", + 8: "Hyperchannel", + 9: "Lanstar", + 10: "Autonet Short Address", + 11: "LocalTalk", + 12: "LocalNet (IBM PCNet or SYTEK LocalNET)", + 13: "Ultra link", + 14: "SMDS", + 15: "Frame Relay", + 16: "Asynchronous Transmission Mode (ATM)", + 17: "HDLC", + 18: "Fibre Channel", + 19: "Asynchronous Transmission Mode (ATM)", + 20: "Serial Line", + 21: "Asynchronous Transmission Mode (ATM)", + 22: "MIL-STD-188-220", + 23: "Metricom", + 24: "IEEE 1394.1995", + 25: "MAPOS", + 26: "Twinaxial", + 27: "EUI-64", + 28: "HIPARP", + 29: "IP and ARP over ISO 7816-3", + 30: "ARPSec", + 31: "IPsec tunnel", + 32: "InfiniBand (TM)", + 33: "TIA-102 Project 25 Common Air Interface (CAI)" } + +class UTCTimeField(IntField): + epoch = (2000, 1, 1, 0, 0, 0, 5, 1, 0) # required Epoch + def i2repr(self, pkt, x): + x = self.i2h(pkt, x) + from time import gmtime, strftime, mktime + delta = mktime(self.epoch) - mktime(gmtime(0)) + x = x + delta + t = strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime(x)) + return "%s (%d)" % (t, x) + +class _LLAddrField(MACField): + pass + +# XXX We only support Ethernet addresses at the moment. _LLAddrField +# will be modified when needed. Ask us. --arno +class DUID_LLT(Packet): # sect 9.2 RFC 3315 + name = "DUID - Link-layer address plus time" + fields_desc = [ ShortEnumField("type", 1, duidtypes), + XShortEnumField("hwtype", 1, duidhwtypes), + UTCTimeField("timeval", 0), # i.e. 01 Jan 2000 + _LLAddrField("lladdr", ETHER_ANY) ] + +# In fact, IANA enterprise-numbers file available at +# http//www.iana.org/asignments/enterprise-numbers) +# is simply huge (more than 2Mo and 600Ko in bz2). I'll +# add only most common vendors, and encountered values. +# -- arno +iana_enterprise_num = { 9: "ciscoSystems", + 35: "Nortel Networks", + 43: "3Com", + 311: "Microsoft", + 2636: "Juniper Networks, Inc.", + 4526: "Netgear", + 5771: "Cisco Systems, Inc.", + 5842: "Cisco Systems", + 16885: "Nortel Networks" } + +class DUID_EN(Packet): # sect 9.3 RFC 3315 + name = "DUID - Assigned by Vendor Based on Enterprise Number" + fields_desc = [ ShortEnumField("type", 2, duidtypes), + IntEnumField("enterprisenum", 311, iana_enterprise_num), + StrField("id","") ] + +class DUID_LL(Packet): # sect 9.4 RFC 3315 + name = "DUID - Based on Link-layer Address" + fields_desc = [ ShortEnumField("type", 3, duidtypes), + XShortEnumField("hwtype", 1, duidhwtypes), + _LLAddrField("lladdr", ETHER_ANY) ] + +duid_cls = { 1: "DUID_LLT", + 2: "DUID_EN", + 3: "DUID_LL"} + +##################################################################### +### DHCPv6 Options classes ### +##################################################################### + +class _DHCP6OptGuessPayload(Packet): + def guess_payload_class(self, payload): + cls = conf.raw_layer + if len(payload) > 2 : + opt = struct.unpack("!H", payload[:2])[0] + cls = get_cls(dhcp6opts_by_code.get(opt, "DHCP6OptUnknown"), DHCP6OptUnknown) + return cls + +class DHCP6OptUnknown(_DHCP6OptGuessPayload): # A generic DHCPv6 Option + name = "Unknown DHCPv6 OPtion" + fields_desc = [ ShortEnumField("optcode", 0, dhcp6opts), + FieldLenField("optlen", None, length_of="data", fmt="!H"), + StrLenField("data", "", + length_from = lambda pkt: pkt.optlen)] + +class _DUIDField(PacketField): + holds_packets=1 + def __init__(self, name, default, length_from=None): + StrField.__init__(self, name, default) + self.length_from = length_from + + def i2m(self, pkt, i): + return str(i) + + def m2i(self, pkt, x): + cls = conf.raw_layer + if len(x) > 4: + o = struct.unpack("!H", x[:2])[0] + cls = get_cls(duid_cls.get(o, conf.raw_layer), conf.raw_layer) + return cls(x) + + def getfield(self, pkt, s): + l = self.length_from(pkt) + return s[l:], self.m2i(pkt,s[:l]) + + +class DHCP6OptClientId(_DHCP6OptGuessPayload): # RFC sect 22.2 + name = "DHCP6 Client Identifier Option" + fields_desc = [ ShortEnumField("optcode", 1, dhcp6opts), + FieldLenField("optlen", None, length_of="duid", fmt="!H"), + _DUIDField("duid", "", + length_from = lambda pkt: pkt.optlen) ] + + +class DHCP6OptServerId(DHCP6OptClientId): # RFC sect 22.3 + name = "DHCP6 Server Identifier Option" + optcode = 2 + +# Should be encapsulated in the option field of IA_NA or IA_TA options +# Can only appear at that location. +# TODO : last field IAaddr-options is not defined in the reference document +class DHCP6OptIAAddress(_DHCP6OptGuessPayload): # RFC sect 22.6 + name = "DHCP6 IA Address Option (IA_TA or IA_NA suboption)" + fields_desc = [ ShortEnumField("optcode", 5, dhcp6opts), + FieldLenField("optlen", None, length_of="iaaddropts", + fmt="!H", adjust = lambda pkt,x: x+24), + IP6Field("addr", "::"), + IntField("preflft", 0), + IntField("validlft", 0), + XIntField("iaid", None), + StrLenField("iaaddropts", "", + length_from = lambda pkt: pkt.optlen - 24) ] + def guess_payload_class(self, payload): + return conf.padding_layer + +class _IANAOptField(PacketListField): + def i2len(self, pkt, z): + if z is None or z == []: + return 0 + return sum(map(lambda x: len(str(x)) ,z)) + + def getfield(self, pkt, s): + l = self.length_from(pkt) + lst = [] + remain, payl = s[:l], s[l:] + while len(remain)>0: + p = self.m2i(pkt,remain) + if conf.padding_layer in p: + pad = p[conf.padding_layer] + remain = pad.load + del(pad.underlayer.payload) + else: + remain = "" + lst.append(p) + return payl,lst + +class DHCP6OptIA_NA(_DHCP6OptGuessPayload): # RFC sect 22.4 + name = "DHCP6 Identity Association for Non-temporary Addresses Option" + fields_desc = [ ShortEnumField("optcode", 3, dhcp6opts), + FieldLenField("optlen", None, length_of="ianaopts", + fmt="!H", adjust = lambda pkt,x: x+12), + XIntField("iaid", None), + IntField("T1", None), + IntField("T2", None), + _IANAOptField("ianaopts", [], DHCP6OptIAAddress, + length_from = lambda pkt: pkt.optlen-12) ] + +class _IATAOptField(_IANAOptField): + pass + +class DHCP6OptIA_TA(_DHCP6OptGuessPayload): # RFC sect 22.5 + name = "DHCP6 Identity Association for Temporary Addresses Option" + fields_desc = [ ShortEnumField("optcode", 4, dhcp6opts), + FieldLenField("optlen", None, length_of="iataopts", + fmt="!H", adjust = lambda pkt,x: x+4), + XIntField("iaid", None), + _IATAOptField("iataopts", [], DHCP6OptIAAddress, + length_from = lambda pkt: pkt.optlen-4) ] + + +#### DHCPv6 Option Request Option ################################### + +class _OptReqListField(StrLenField): + islist = 1 + def i2h(self, pkt, x): + if x is None: + return [] + return x + + def i2len(self, pkt, x): + return 2*len(x) + + def any2i(self, pkt, x): + return x + + def i2repr(self, pkt, x): + s = [] + for y in self.i2h(pkt, x): + if dhcp6opts.has_key(y): + s.append(dhcp6opts[y]) + else: + s.append("%d" % y) + return "[%s]" % ", ".join(s) + + def m2i(self, pkt, x): + r = [] + while len(x) != 0: + if len(x)<2: + warning("Odd length for requested option field. Rejecting last byte") + return r + r.append(struct.unpack("!H", x[:2])[0]) + x = x[2:] + return r + + def i2m(self, pkt, x): + return "".join(map(lambda y: struct.pack("!H", y), x)) + +# A client may include an ORO in a solicit, Request, Renew, Rebind, +# Confirm or Information-request +class DHCP6OptOptReq(_DHCP6OptGuessPayload): # RFC sect 22.7 + name = "DHCP6 Option Request Option" + fields_desc = [ ShortEnumField("optcode", 6, dhcp6opts), + FieldLenField("optlen", None, length_of="reqopts", fmt="!H"), + _OptReqListField("reqopts", [23, 24], + length_from = lambda pkt: pkt.optlen) ] + + +#### DHCPv6 Preference Option ####################################### + +# emise par un serveur pour affecter le choix fait par le client. Dans +# les messages Advertise, a priori +class DHCP6OptPref(_DHCP6OptGuessPayload): # RFC sect 22.8 + name = "DHCP6 Preference Option" + fields_desc = [ ShortEnumField("optcode", 7, dhcp6opts), + ShortField("optlen", 1 ), + ByteField("prefval",255) ] + + +#### DHCPv6 Elapsed Time Option ##################################### + +class _ElapsedTimeField(ShortField): + def i2repr(self, pkt, x): + if x == 0xffff: + return "infinity (0xffff)" + return "%.2f sec" % (self.i2h(pkt, x)/100.) + +class DHCP6OptElapsedTime(_DHCP6OptGuessPayload):# RFC sect 22.9 + name = "DHCP6 Elapsed Time Option" + fields_desc = [ ShortEnumField("optcode", 8, dhcp6opts), + ShortField("optlen", 2), + _ElapsedTimeField("elapsedtime", 0) ] + + +#### DHCPv6 Relay Message Option #################################### + +# Relayed message is seen as a payload. +class DHCP6OptRelayMsg(_DHCP6OptGuessPayload):# RFC sect 22.10 + name = "DHCP6 Relay Message Option" + fields_desc = [ ShortEnumField("optcode", 9, dhcp6opts), + ShortField("optlen", None ) ] + def post_build(self, p, pay): + if self.optlen is None: + l = len(pay) + p = p[:2]+struct.pack("!H", l) + return p + pay + + +#### DHCPv6 Authentication Option ################################### + +# The following fields are set in an Authentication option for the +# Reconfigure Key Authentication Protocol: +# +# protocol 3 +# +# algorithm 1 +# +# RDM 0 +# +# The format of the Authentication information for the Reconfigure Key +# Authentication Protocol is: +# +# 0 1 2 3 +# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +# | Type | Value (128 bits) | +# +-+-+-+-+-+-+-+-+ | +# . . +# . . +# . +-+-+-+-+-+-+-+-+ +# | | +# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +# +# Type Type of data in Value field carried in this option: +# +# 1 Reconfigure Key value (used in Reply message). +# +# 2 HMAC-MD5 digest of the message (used in Reconfigure +# message). +# +# Value Data as defined by field. + + +# TODO : Decoding only at the moment +class DHCP6OptAuth(_DHCP6OptGuessPayload): # RFC sect 22.11 + name = "DHCP6 Option - Authentication" + fields_desc = [ ShortEnumField("optcode", 11, dhcp6opts), + FieldLenField("optlen", None, length_of="authinfo", + adjust = lambda pkt,x: x+11), + ByteField("proto", 3), # TODO : XXX + ByteField("alg", 1), # TODO : XXX + ByteField("rdm", 0), # TODO : XXX + StrFixedLenField("replay", "A"*8, 8), # TODO: XXX + StrLenField("authinfo", "", + length_from = lambda pkt: pkt.optlen - 11) ] + +#### DHCPv6 Server Unicast Option ################################### + +class _SrvAddrField(IP6Field): + def i2h(self, pkt, x): + if x is None: + return "::" + return x + + def i2m(self, pkt, x): + return inet_pton(socket.AF_INET6, self.i2h(pkt,x)) + +class DHCP6OptServerUnicast(_DHCP6OptGuessPayload):# RFC sect 22.12 + name = "DHCP6 Server Unicast Option" + fields_desc = [ ShortEnumField("optcode", 12, dhcp6opts), + ShortField("optlen", 16 ), + _SrvAddrField("srvaddr",None) ] + + +#### DHCPv6 Status Code Option ###################################### + +dhcp6statuscodes = { 0:"Success", # sect 24.4 + 1:"UnspecFail", + 2:"NoAddrsAvail", + 3:"NoBinding", + 4:"NotOnLink", + 5:"UseMulticast", + 6:"NoPrefixAvail"} # From RFC3633 + +class DHCP6OptStatusCode(_DHCP6OptGuessPayload):# RFC sect 22.13 + name = "DHCP6 Status Code Option" + fields_desc = [ ShortEnumField("optcode", 13, dhcp6opts), + FieldLenField("optlen", None, length_of="statusmsg", + fmt="!H", adjust = lambda pkt,x:x+2), + ShortEnumField("statuscode",None,dhcp6statuscodes), + StrLenField("statusmsg", "", + length_from = lambda pkt: pkt.optlen-2) ] + + +#### DHCPv6 Rapid Commit Option ##################################### + +class DHCP6OptRapidCommit(_DHCP6OptGuessPayload): # RFC sect 22.14 + name = "DHCP6 Rapid Commit Option" + fields_desc = [ ShortEnumField("optcode", 14, dhcp6opts), + ShortField("optlen", 0)] + + +#### DHCPv6 User Class Option ####################################### + +class _UserClassDataField(PacketListField): + def i2len(self, pkt, z): + if z is None or z == []: + return 0 + return sum(map(lambda x: len(str(x)) ,z)) + + def getfield(self, pkt, s): + l = self.length_from(pkt) + lst = [] + remain, payl = s[:l], s[l:] + while len(remain)>0: + p = self.m2i(pkt,remain) + if conf.padding_layer in p: + pad = p[conf.padding_layer] + remain = pad.load + del(pad.underlayer.payload) + else: + remain = "" + lst.append(p) + return payl,lst + + +class USER_CLASS_DATA(Packet): + name = "user class data" + fields_desc = [ FieldLenField("len", None, length_of="data"), + StrLenField("data", "", + length_from = lambda pkt: pkt.len) ] + def guess_payload_class(self, payload): + return conf.padding_layer + +class DHCP6OptUserClass(_DHCP6OptGuessPayload):# RFC sect 22.15 + name = "DHCP6 User Class Option" + fields_desc = [ ShortEnumField("optcode", 15, dhcp6opts), + FieldLenField("optlen", None, fmt="!H", + length_of="userclassdata"), + _UserClassDataField("userclassdata", [], USER_CLASS_DATA, + length_from = lambda pkt: pkt.optlen) ] + + +#### DHCPv6 Vendor Class Option ##################################### + +class _VendorClassDataField(_UserClassDataField): + pass + +class VENDOR_CLASS_DATA(USER_CLASS_DATA): + name = "vendor class data" + +class DHCP6OptVendorClass(_DHCP6OptGuessPayload):# RFC sect 22.16 + name = "DHCP6 Vendor Class Option" + fields_desc = [ ShortEnumField("optcode", 16, dhcp6opts), + FieldLenField("optlen", None, length_of="vcdata", fmt="!H", + adjust = lambda pkt,x: x+4), + IntEnumField("enterprisenum",None , iana_enterprise_num ), + _VendorClassDataField("vcdata", [], VENDOR_CLASS_DATA, + length_from = lambda pkt: pkt.optlen-4) ] + +#### DHCPv6 Vendor-Specific Information Option ###################### + +class VENDOR_SPECIFIC_OPTION(_DHCP6OptGuessPayload): + name = "vendor specific option data" + fields_desc = [ ShortField("optcode", None), + FieldLenField("optlen", None, length_of="optdata"), + StrLenField("optdata", "", + length_from = lambda pkt: pkt.optlen) ] + def guess_payload_class(self, payload): + return conf.padding_layer + +# The third one that will be used for nothing interesting +class DHCP6OptVendorSpecificInfo(_DHCP6OptGuessPayload):# RFC sect 22.17 + name = "DHCP6 Vendor-specific Information Option" + fields_desc = [ ShortEnumField("optcode", 17, dhcp6opts), + FieldLenField("optlen", None, length_of="vso", fmt="!H", + adjust = lambda pkt,x: x+4), + IntEnumField("enterprisenum",None , iana_enterprise_num), + _VendorClassDataField("vso", [], VENDOR_SPECIFIC_OPTION, + length_from = lambda pkt: pkt.optlen-4) ] + +#### DHCPv6 Interface-ID Option ##################################### + +# Repasser sur cette option a la fin. Elle a pas l'air d'etre des +# masses critique. +class DHCP6OptIfaceId(_DHCP6OptGuessPayload):# RFC sect 22.18 + name = "DHCP6 Interface-Id Option" + fields_desc = [ ShortEnumField("optcode", 18, dhcp6opts), + FieldLenField("optlen", None, fmt="!H", + length_of="ifaceid"), + StrLenField("ifaceid", "", + length_from = lambda pkt: pkt.optlen) ] + + +#### DHCPv6 Reconfigure Message Option ############################## + +# A server includes a Reconfigure Message option in a Reconfigure +# message to indicate to the client whether the client responds with a +# renew message or an Informatiion-request message. +class DHCP6OptReconfMsg(_DHCP6OptGuessPayload): # RFC sect 22.19 + name = "DHCP6 Reconfigure Message Option" + fields_desc = [ ShortEnumField("optcode", 19, dhcp6opts), + ShortField("optlen", 1 ), + ByteEnumField("msgtype", 11, { 5:"Renew Message", + 11:"Information Request"}) ] + + +#### DHCPv6 Reconfigure Accept Option ############################### + +# A client uses the Reconfigure Accept option to announce to the +# server whether the client is willing to accept Recoonfigure +# messages, and a server uses this option to tell the client whether +# or not to accept Reconfigure messages. The default behavior in the +# absence of this option, means unwillingness to accept reconfigure +# messages, or instruction not to accept Reconfigure messages, for the +# client and server messages, respectively. +class DHCP6OptReconfAccept(_DHCP6OptGuessPayload): # RFC sect 22.20 + name = "DHCP6 Reconfigure Accept Option" + fields_desc = [ ShortEnumField("optcode", 20, dhcp6opts), + ShortField("optlen", 0)] + +# As required in Sect 8. of RFC 3315, Domain Names must be encoded as +# described in section 3.1 of RFC 1035 +# XXX Label should be at most 63 octets in length : we do not enforce it +# Total length of domain should be 255 : we do not enforce it either +class DomainNameListField(StrLenField): + islist = 1 + + def i2len(self, pkt, x): + return len(self.i2m(pkt, x)) + + def m2i(self, pkt, x): + res = [] + while x: + cur = [] + while x and x[0] != '\x00': + l = ord(x[0]) + cur.append(x[1:l+1]) + x = x[l+1:] + res.append(".".join(cur)) + if x and x[0] == '\x00': + x = x[1:] + return res + + def i2m(self, pkt, x): + def conditionalTrailingDot(z): + if z and z[-1] == '\x00': + return z + return z+'\x00' + res = "" + tmp = map(lambda y: map((lambda z: chr(len(z))+z), y.split('.')), x) + return "".join(map(lambda x: conditionalTrailingDot("".join(x)), tmp)) + +class DHCP6OptSIPDomains(_DHCP6OptGuessPayload): #RFC3319 + name = "DHCP6 Option - SIP Servers Domain Name List" + fields_desc = [ ShortEnumField("optcode", 21, dhcp6opts), + FieldLenField("optlen", None, length_of="sipdomains"), + DomainNameListField("sipdomains", [], + length_from = lambda pkt: pkt.optlen) ] + +class DHCP6OptSIPServers(_DHCP6OptGuessPayload): #RFC3319 + name = "DHCP6 Option - SIP Servers IPv6 Address List" + fields_desc = [ ShortEnumField("optcode", 22, dhcp6opts), + FieldLenField("optlen", None, length_of="sipservers"), + IP6ListField("sipservers", [], + length_from = lambda pkt: pkt.optlen) ] + +class DHCP6OptDNSServers(_DHCP6OptGuessPayload): #RFC3646 + name = "DHCP6 Option - DNS Recursive Name Server" + fields_desc = [ ShortEnumField("optcode", 23, dhcp6opts), + FieldLenField("optlen", None, length_of="dnsservers"), + IP6ListField("dnsservers", [], + length_from = lambda pkt: pkt.optlen) ] + +class DHCP6OptDNSDomains(_DHCP6OptGuessPayload): #RFC3646 + name = "DHCP6 Option - Domain Search List option" + fields_desc = [ ShortEnumField("optcode", 24, dhcp6opts), + FieldLenField("optlen", None, length_of="dnsdomains"), + DomainNameListField("dnsdomains", [], + length_from = lambda pkt: pkt.optlen) ] + +# TODO: Implement iaprefopts correctly when provided with more +# information about it. +class DHCP6OptIAPrefix(_DHCP6OptGuessPayload): #RFC3633 + name = "DHCP6 Option - IA_PD Prefix option" + fields_desc = [ ShortEnumField("optcode", 26, dhcp6opts), + FieldLenField("optlen", None, length_of="iaprefopts", + adjust = lambda pkt,x: x+26), + IntField("preflft", 0), + IntField("validlft", 0), + ByteField("plen", 48), # TODO: Challenge that default value + IP6Field("prefix", "2001:db8::"), # At least, global and won't hurt + StrLenField("iaprefopts", "", + length_from = lambda pkt: pkt.optlen-26) ] + +class DHCP6OptIA_PD(_DHCP6OptGuessPayload): #RFC3633 + name = "DHCP6 Option - Identity Association for Prefix Delegation" + fields_desc = [ ShortEnumField("optcode", 25, dhcp6opts), + FieldLenField("optlen", None, length_of="iapdopt", + adjust = lambda pkt,x: x+12), + IntField("iaid", 0), + IntField("T1", 0), + IntField("T2", 0), + PacketListField("iapdopt", [], DHCP6OptIAPrefix, + length_from = lambda pkt: pkt.optlen-12) ] + +class DHCP6OptNISServers(_DHCP6OptGuessPayload): #RFC3898 + name = "DHCP6 Option - NIS Servers" + fields_desc = [ ShortEnumField("optcode", 27, dhcp6opts), + FieldLenField("optlen", None, length_of="nisservers"), + IP6ListField("nisservers", [], + length_from = lambda pkt: pkt.optlen) ] + +class DHCP6OptNISPServers(_DHCP6OptGuessPayload): #RFC3898 + name = "DHCP6 Option - NIS+ Servers" + fields_desc = [ ShortEnumField("optcode", 28, dhcp6opts), + FieldLenField("optlen", None, length_of="nispservers"), + IP6ListField("nispservers", [], + length_from = lambda pkt: pkt.optlen) ] + +class DomainNameField(StrLenField): + def getfield(self, pkt, s): + l = self.length_from(pkt) + return s[l:], self.m2i(pkt,s[:l]) + + def i2len(self, pkt, x): + return len(self.i2m(pkt, x)) + + def m2i(self, pkt, x): + cur = [] + while x: + l = ord(x[0]) + cur.append(x[1:1+l]) + x = x[l+1:] + ret_str = ".".join(cur) + return ret_str + + def i2m(self, pkt, x): + if not x: + return "" + tmp = "".join(map(lambda z: chr(len(z))+z, x.split('.'))) + return tmp + +class DHCP6OptNISDomain(_DHCP6OptGuessPayload): #RFC3898 + name = "DHCP6 Option - NIS Domain Name" + fields_desc = [ ShortEnumField("optcode", 29, dhcp6opts), + FieldLenField("optlen", None, length_of="nisdomain"), + DomainNameField("nisdomain", "", + length_from = lambda pkt: pkt.optlen) ] + +class DHCP6OptNISPDomain(_DHCP6OptGuessPayload): #RFC3898 + name = "DHCP6 Option - NIS+ Domain Name" + fields_desc = [ ShortEnumField("optcode", 30, dhcp6opts), + FieldLenField("optlen", None, length_of="nispdomain"), + DomainNameField("nispdomain", "", + length_from= lambda pkt: pkt.optlen) ] + +class DHCP6OptSNTPServers(_DHCP6OptGuessPayload): #RFC4075 + name = "DHCP6 option - SNTP Servers" + fields_desc = [ ShortEnumField("optcode", 31, dhcp6opts), + FieldLenField("optlen", None, length_of="sntpservers"), + IP6ListField("sntpservers", [], + length_from = lambda pkt: pkt.optlen) ] + +IRT_DEFAULT=86400 +IRT_MINIMUM=600 +class DHCP6OptInfoRefreshTime(_DHCP6OptGuessPayload): #RFC4242 + name = "DHCP6 Option - Information Refresh Time" + fields_desc = [ ShortEnumField("optcode", 32, dhcp6opts), + ShortField("optlen", 4), + IntField("reftime", IRT_DEFAULT)] # One day + +class DHCP6OptBCMCSDomains(_DHCP6OptGuessPayload): #RFC4280 + name = "DHCP6 Option - BCMCS Domain Name List" + fields_desc = [ ShortEnumField("optcode", 33, dhcp6opts), + FieldLenField("optlen", None, length_of="bcmcsdomains"), + DomainNameListField("bcmcsdomains", [], + length_from = lambda pkt: pkt.optlen) ] + +class DHCP6OptBCMCSServers(_DHCP6OptGuessPayload): #RFC4280 + name = "DHCP6 Option - BCMCS Addresses List" + fields_desc = [ ShortEnumField("optcode", 34, dhcp6opts), + FieldLenField("optlen", None, length_of="bcmcsservers"), + IP6ListField("bcmcsservers", [], + length_from= lambda pkt: pkt.optlen) ] + +# TODO : Does Nothing at the moment +class DHCP6OptGeoConf(_DHCP6OptGuessPayload): #RFC-ietf-geopriv-dhcp-civil-09.txt + name = "" + fields_desc = [ ShortEnumField("optcode", 36, dhcp6opts), + FieldLenField("optlen", None, length_of="optdata"), + StrLenField("optdata", "", + length_from = lambda pkt: pkt.optlen) ] + +# TODO: see if we encounter opaque values from vendor devices +class DHCP6OptRemoteID(_DHCP6OptGuessPayload): #RFC4649 + name = "DHCP6 Option - Relay Agent Remote-ID" + fields_desc = [ ShortEnumField("optcode", 37, dhcp6opts), + FieldLenField("optlen", None, length_of="remoteid", + adjust = lambda pkt,x: x+4), + IntEnumField("enterprisenum", None, iana_enterprise_num), + StrLenField("remoteid", "", + length_from = lambda pkt: pkt.optlen-4) ] + +# TODO : 'subscriberid' default value should be at least 1 byte long +class DHCP6OptSubscriberID(_DHCP6OptGuessPayload): #RFC4580 + name = "DHCP6 Option - Subscriber ID" + fields_desc = [ ShortEnumField("optcode", 38, dhcp6opts), + FieldLenField("optlen", None, length_of="subscriberid"), + StrLenField("subscriberid", "", + length_from = lambda pkt: pkt.optlen) ] + +# TODO : "The data in the Domain Name field MUST be encoded +# as described in Section 8 of [5]" +class DHCP6OptClientFQDN(_DHCP6OptGuessPayload): #RFC4704 + name = "DHCP6 Option - Client FQDN" + fields_desc = [ ShortEnumField("optcode", 39, dhcp6opts), + FieldLenField("optlen", None, length_of="fqdn", + adjust = lambda pkt,x: x+1), + BitField("res", 0, 5), + FlagsField("flags", 0, 3, "SON" ), + DomainNameField("fqdn", "", + length_from = lambda pkt: pkt.optlen-1) ] + +class DHCP6OptRelayAgentERO(_DHCP6OptGuessPayload): # RFC4994 + name = "DHCP6 Option - RelayRequest Option" + fields_desc = [ ShortEnumField("optcode", 43, dhcp6opts), + FieldLenField("optlen", None, length_of="reqopts", fmt="!H"), + _OptReqListField("reqopts", [23, 24], + length_from = lambda pkt: pkt.optlen) ] + +##################################################################### +### DHCPv6 messages ### +##################################################################### + +# Some state parameters of the protocols that should probably be +# useful to have in the configuration (and keep up-to-date) +DHCP6RelayAgentUnicastAddr="" +DHCP6RelayHopCount="" +DHCP6ServerUnicastAddr="" +DHCP6ClientUnicastAddr="" +DHCP6ClientIA_TA="" +DHCP6ClientIA_NA="" +DHCP6ClientIAID="" +T1="" # Voir 2462 +T2="" # Voir 2462 +DHCP6ServerDUID="" +DHCP6CurrentTransactionID="" # devrait etre utilise pour matcher une +# reponse et mis a jour en mode client par une valeur aleatoire pour +# laquelle on attend un retour de la part d'un serveur. +DHCP6PrefVal="" # la valeur de preference a utiliser dans +# les options preference + +# Emitted by : +# - server : ADVERTISE, REPLY, RECONFIGURE, RELAY-REPL (vers relay) +# - client : SOLICIT, REQUEST, CONFIRM, RENEW, REBIND, RELEASE, DECLINE, +# INFORMATION REQUEST +# - relay : RELAY-FORW (toward server) + +class _DHCP6GuessPayload(Packet): + def guess_payload_class(self, payload): + if len(payload) > 1 : + print ord(payload[0]) + return get_cls(dhcp6opts.get(ord(payload[0]),"DHCP6OptUnknown"), conf.raw_layer) + return conf.raw_layer + +##################################################################### +## DHCPv6 messages sent between Clients and Servers (types 1 to 11) +# Comme specifie en section 15.1 de la RFC 3315, les valeurs de +# transaction id sont selectionnees de maniere aleatoire par le client +# a chaque emission et doivent matcher dans les reponses faites par +# les clients +class DHCP6(_DHCP6OptGuessPayload): + name = "DHCPv6 Generic Message)" + fields_desc = [ ByteEnumField("msgtype",None,dhcp6types), + X3BytesField("trid",0x000000) ] + overload_fields = { UDP: {"sport": 546, "dport": 547} } + + def hashret(self): + return struct.pack("!I", self.trid)[1:4] + +##################################################################### +# Solicit Message : sect 17.1.1 RFC3315 +# - sent by client +# - must include a client identifier option +# - the client may include IA options for any IAs to which it wants the +# server to assign address +# - The client use IA_NA options to request the assignment of +# non-temporary addresses and uses IA_TA options to request the +# assignment of temporary addresses +# - The client should include an Option Request option to indicate the +# options the client is interested in receiving (eventually +# including hints) +# - The client includes a Reconfigure Accept option if is willing to +# accept Reconfigure messages from the server. +# Le cas du send and reply est assez particulier car suivant la +# presence d'une option rapid commit dans le solicit, l'attente +# s'arrete au premier message de reponse recu ou alors apres un +# timeout. De la meme maniere, si un message Advertise arrive avec une +# valeur de preference de 255, il arrete l'attente et envoie une +# Request. +# - The client announces its intention to use DHCP authentication by +# including an Authentication option in its solicit message. The +# server selects a key for the client based on the client's DUID. The +# client and server use that key to authenticate all DHCP messages +# exchanged during the session + +class DHCP6_Solicit(DHCP6): + name = "DHCPv6 Solicit Message" + msgtype = 1 + overload_fields = { UDP: {"sport": 546, "dport": 547} } + +##################################################################### +# Advertise Message +# - sent by server +# - Includes a server identifier option +# - Includes a client identifier option +# - the client identifier option must match the client's DUID +# - transaction ID must match + +class DHCP6_Advertise(DHCP6): + name = "DHCPv6 Advertise Message" + msgtype = 2 + overload_fields = { UDP: {"sport": 547, "dport": 546} } + + def answers(self, other): + return (isinstance(other,DHCP6_Solicit) and + other.msgtype == 1 and + self.trid == other.trid) + +##################################################################### +# Request Message +# - sent by clients +# - includes a server identifier option +# - the content of Server Identifier option must match server's DUID +# - includes a client identifier option +# - must include an ORO Option (even with hints) p40 +# - can includes a reconfigure Accept option indicating whether or +# not the client is willing to accept Reconfigure messages from +# the server (p40) +# - When the server receives a Request message via unicast from a +# client to which the server has not sent a unicast option, the server +# discards the Request message and responds with a Reply message +# containinig Status Code option with the value UseMulticast, a Server +# Identifier Option containing the server's DUID, the client +# Identifier option from the client message and no other option. + +class DHCP6_Request(DHCP6): + name = "DHCPv6 Request Message" + msgtype = 3 + +##################################################################### +# Confirm Message +# - sent by clients +# - must include a clien identifier option +# - When the server receives a Confirm Message, the server determines +# whether the addresses in the Confirm message are appropriate for the +# link to which the client is attached. cf p50 + +class DHCP6_Confirm(DHCP6): + name = "DHCPv6 Confirm Message" + msgtype = 4 + +##################################################################### +# Renew Message +# - sent by clients +# - must include a server identifier option +# - content of server identifier option must match the server's identifier +# - must include a client identifier option +# - the clients includes any IA assigned to the interface that may +# have moved to a new link, along with the addresses associated with +# those IAs in its confirm messages +# - When the server receives a Renew message that contains an IA +# option from a client, it locates the client's binding and verifies +# that the information in the IA from the client matches the +# information for that client. If the server cannot find a client +# entry for the IA the server returns the IA containing no addresses +# with a status code option est to NoBinding in the Reply message. cf +# p51 pour le reste. + +class DHCP6_Renew(DHCP6): + name = "DHCPv6 Renew Message" + msgtype = 5 + +##################################################################### +# Rebind Message +# - sent by clients +# - must include a client identifier option +# cf p52 + +class DHCP6_Rebind(DHCP6): + name = "DHCPv6 Rebind Message" + msgtype = 6 + +##################################################################### +# Reply Message +# - sent by servers +# - the message must include a server identifier option +# - transaction-id field must match the value of original message +# The server includes a Rapid Commit option in the Reply message to +# indicate that the reply is in response to a solicit message +# - if the client receives a reply message with a Status code option +# with the value UseMulticast, the client records the receipt of the +# message and sends subsequent messages to the server through the +# interface on which the message was received using multicast. The +# client resends the original message using multicast +# - When the client receives a NotOnLink status from the server in +# response to a Confirm message, the client performs DHCP server +# solicitation as described in section 17 and client-initiated +# configuration as descrribed in section 18 (RFC 3315) +# - when the client receives a NotOnLink status from the server in +# response to a Request, the client can either re-issue the Request +# without specifying any addresses or restart the DHCP server +# discovery process. +# - the server must include a server identifier option containing the +# server's DUID in the Reply message + +class DHCP6_Reply(DHCP6): + name = "DHCPv6 Reply Message" + msgtype = 7 + + overload_fields = { UDP: {"sport": 547, "dport": 546} } + + def answers(self, other): + + types = (DHCP6_InfoRequest, DHCP6_Confirm, DHCP6_Rebind, DHCP6_Decline, DHCP6_Request, DHCP6_Release, DHCP6_Renew) + + return (isinstance(other, types) and + self.trid == other.trid) + +##################################################################### +# Release Message +# - sent by clients +# - must include a server identifier option +# cf p53 + +class DHCP6_Release(DHCP6): + name = "DHCPv6 Release Message" + msgtype = 8 + +##################################################################### +# Decline Message +# - sent by clients +# - must include a client identifier option +# - Server identifier option must match server identifier +# - The addresses to be declined must be included in the IAs. Any +# addresses for the IAs the client wishes to continue to use should +# not be in added to the IAs. +# - cf p54 + +class DHCP6_Decline(DHCP6): + name = "DHCPv6 Decline Message" + msgtype = 9 + +##################################################################### +# Reconfigure Message +# - sent by servers +# - must be unicast to the client +# - must include a server identifier option +# - must include a client identifier option that contains the client DUID +# - must contain a Reconfigure Message Option and the message type +# must be a valid value +# - the server sets the transaction-id to 0 +# - The server must use DHCP Authentication in the Reconfigure +# message. Autant dire que ca va pas etre le type de message qu'on va +# voir le plus souvent. + +class DHCP6_Reconf(DHCP6): + name = "DHCPv6 Reconfigure Message" + msgtype = 10 + overload_fields = { UDP: { "sport": 547, "dport": 546 } } + + +##################################################################### +# Information-Request Message +# - sent by clients when needs configuration information but no +# addresses. +# - client should include a client identifier option to identify +# itself. If it doesn't the server is not able to return client +# specific options or the server can choose to not respond to the +# message at all. The client must include a client identifier option +# if the message will be authenticated. +# - client must include an ORO of option she's interested in receiving +# (can include hints) + +class DHCP6_InfoRequest(DHCP6): + name = "DHCPv6 Information Request Message" + msgtype = 11 + +##################################################################### +# sent between Relay Agents and Servers +# +# Normalement, doit inclure une option "Relay Message Option" +# peut en inclure d'autres. +# voir section 7.1 de la 3315 + +# Relay-Forward Message +# - sent by relay agents to servers +# If the relay agent relays messages to the All_DHCP_Servers multicast +# address or other multicast addresses, it sets the Hop Limit field to +# 32. + +class DHCP6_RelayForward(_DHCP6GuessPayload,Packet): + name = "DHCPv6 Relay Forward Message (Relay Agent/Server Message)" + fields_desc = [ ByteEnumField("msgtype", 12, dhcp6types), + ByteField("hopcount", None), + IP6Field("linkaddr", "::"), + IP6Field("peeraddr", "::") ] + def hashret(self): # we filter on peer address field + return inet_pton(socket.AF_INET6, self.peeraddr) + +##################################################################### +# sent between Relay Agents and Servers +# Normalement, doit inclure une option "Relay Message Option" +# peut en inclure d'autres. +# Les valeurs des champs hop-count, link-addr et peer-addr +# sont copiees du messsage Forward associe. POur le suivi de session. +# Pour le moment, comme decrit dans le commentaire, le hashret +# se limite au contenu du champ peer address. +# Voir section 7.2 de la 3315. + +# Relay-Reply Message +# - sent by servers to relay agents +# - if the solicit message was received in a Relay-Forward message, +# the server constructs a relay-reply message with the Advertise +# message in the payload of a relay-message. cf page 37/101. Envoie de +# ce message en unicast au relay-agent. utilisation de l'adresse ip +# presente en ip source du paquet recu + +class DHCP6_RelayReply(DHCP6_RelayForward): + name = "DHCPv6 Relay Reply Message (Relay Agent/Server Message)" + msgtype = 13 + def hashret(self): # We filter on peer address field. + return inet_pton(socket.AF_INET6, self.peeraddr) + def answers(self, other): + return (isinstance(other, DHCP6_RelayForward) and + self.hopcount == other.hopcount and + self.linkaddr == other.linkaddr and + self.peeraddr == other.peeraddr ) + + +dhcp6_cls_by_type = { 1: "DHCP6_Solicit", + 2: "DHCP6_Advertise", + 3: "DHCP6_Request", + 4: "DHCP6_Confirm", + 5: "DHCP6_Renew", + 6: "DHCP6_Rebind", + 7: "DHCP6_Reply", + 8: "DHCP6_Release", + 9: "DHCP6_Decline", + 10: "DHCP6_Reconf", + 11: "DHCP6_InfoRequest", + 12: "DHCP6_RelayForward", + 13: "DHCP6_RelayReply" } + +def _dhcp6_dispatcher(x, *args, **kargs): + cls = conf.raw_layer + if len(x) >= 2: + cls = get_cls(dhcp6_cls_by_type.get(ord(x[0]), "Raw"), conf.raw_layer) + return cls(x, *args, **kargs) + +bind_bottom_up(UDP, _dhcp6_dispatcher, { "dport": 547 } ) +bind_bottom_up(UDP, _dhcp6_dispatcher, { "dport": 546 } ) + + + +class DHCPv6_am(AnsweringMachine): + function_name = "dhcp6d" + filter = "udp and port 546 and port 547" + send_function = staticmethod(send) + def usage(self): + msg = """ +dhcp6d( dns="2001:500::1035", domain="localdomain, local", duid=None) + iface=conf.iface6, advpref=255, sntpservers=None, + sipdomains=None, sipservers=None, + nisdomain=None, nisservers=None, + nispdomain=None, nispservers=None, + bcmcsdomain=None, bcmcsservers=None) + + debug : When set, additional debugging information is printed. + + duid : some DUID class (DUID_LLT, DUID_LL or DUID_EN). If none + is provided a DUID_LLT is constructed based on the MAC + address of the sending interface and launch time of dhcp6d + answering machine. + + iface : the interface to listen/reply on if you do not want to use + conf.iface6. + + advpref : Value in [0,255] given to Advertise preference field. + By default, 255 is used. Be aware that this specific + value makes clients stops waiting for further Advertise + messages from other servers. + + dns : list of recursive DNS servers addresses (as a string or list). + By default, it is set empty and the associated DHCP6OptDNSServers + option is inactive. See RFC 3646 for details. + domain : a list of DNS search domain (as a string or list). By default, + it is empty and the associated DHCP6OptDomains option is inactive. + See RFC 3646 for details. + + sntpservers : a list of SNTP servers IPv6 addresses. By default, + it is empty and the associated DHCP6OptSNTPServers option + is inactive. + + sipdomains : a list of SIP domains. By default, it is empty and the + associated DHCP6OptSIPDomains option is inactive. See RFC 3319 + for details. + sipservers : a list of SIP servers IPv6 addresses. By default, it is + empty and the associated DHCP6OptSIPDomains option is inactive. + See RFC 3319 for details. + + nisdomain : a list of NIS domains. By default, it is empty and the + associated DHCP6OptNISDomains option is inactive. See RFC 3898 + for details. See RFC 3646 for details. + nisservers : a list of NIS servers IPv6 addresses. By default, it is + empty and the associated DHCP6OptNISServers option is inactive. + See RFC 3646 for details. + + nispdomain : a list of NIS+ domains. By default, it is empty and the + associated DHCP6OptNISPDomains option is inactive. See RFC 3898 + for details. + nispservers : a list of NIS+ servers IPv6 addresses. By default, it is + empty and the associated DHCP6OptNISServers option is inactive. + See RFC 3898 for details. + + bcmcsdomain : a list of BCMCS domains. By default, it is empty and the + associated DHCP6OptBCMCSDomains option is inactive. See RFC 4280 + for details. + bcmcsservers : a list of BCMCS servers IPv6 addresses. By default, it is + empty and the associated DHCP6OptBCMCSServers option is inactive. + See RFC 4280 for details. + + If you have a need for others, just ask ... or provide a patch.""" + print msg + + def parse_options(self, dns="2001:500::1035", domain="localdomain, local", + startip="2001:db8::1", endip="2001:db8::20", duid=None, + sntpservers=None, sipdomains=None, sipservers=None, + nisdomain=None, nisservers=None, nispdomain=None, + nispservers=None, bcmcsservers=None, bcmcsdomains=None, + iface=None, debug=0, advpref=255): + def norm_list(val, param_name): + if val is None: + return None + if type(val) is list: + return val + elif type(val) is str: + l = val.split(',') + return map(lambda x: x.strip(), l) + else: + print "Bad '%s' parameter provided." % param_name + self.usage() + return -1 + + if iface is None: + iface = conf.iface6 + + self.debug = debug + + # Dictionary of provided DHCPv6 options, keyed by option type + self.dhcpv6_options={} + + for o in [(dns, "dns", 23, lambda x: DHCP6OptDNSServers(dnsservers=x)), + (domain, "domain", 24, lambda x: DHCP6OptDNSDomains(dnsdomains=x)), + (sntpservers, "sntpservers", 31, lambda x: DHCP6OptSNTPServers(sntpservers=x)), + (sipservers, "sipservers", 22, lambda x: DHCP6OptSIPServers(sipservers=x)), + (sipdomains, "sipdomains", 21, lambda x: DHCP6OptSIPDomains(sipdomains=x)), + (nisservers, "nisservers", 27, lambda x: DHCP6OptNISServers(nisservers=x)), + (nisdomain, "nisdomain", 29, lambda x: DHCP6OptNISDomain(nisdomain=(x+[""])[0])), + (nispservers, "nispservers", 28, lambda x: DHCP6OptNISPServers(nispservers=x)), + (nispdomain, "nispdomain", 30, lambda x: DHCP6OptNISPDomain(nispdomain=(x+[""])[0])), + (bcmcsservers, "bcmcsservers", 33, lambda x: DHCP6OptBCMCSServers(bcmcsservers=x)), + (bcmcsdomains, "bcmcsdomains", 34, lambda x: DHCP6OptBCMCSDomains(bcmcsdomains=x))]: + + opt = norm_list(o[0], o[1]) + if opt == -1: # Usage() was triggered + return False + elif opt is None: # We won't return that option + pass + else: + self.dhcpv6_options[o[2]] = o[3](opt) + + if self.debug: + print "\n[+] List of active DHCPv6 options:" + opts = self.dhcpv6_options.keys() + opts.sort() + for i in opts: + print " %d: %s" % (i, repr(self.dhcpv6_options[i])) + + # Preference value used in Advertise. + self.advpref = advpref + + # IP Pool + self.startip = startip + self.endip = endip + # XXX TODO Check IPs are in same subnet + + #### + # The interface we are listening/replying on + self.iface = iface + + #### + # Generate a server DUID + if duid is not None: + self.duid = duid + else: + # Timeval + from time import gmtime, strftime, mktime + epoch = (2000, 1, 1, 0, 0, 0, 5, 1, 0) + delta = mktime(epoch) - mktime(gmtime(0)) + timeval = time.time() - delta + + # Mac Address + rawmac = get_if_raw_hwaddr(iface)[1] + mac = ":".join(map(lambda x: "%.02x" % ord(x), list(rawmac))) + + self.duid = DUID_LLT(timeval = timeval, lladdr = mac) + + if self.debug: + print "\n[+] Our server DUID:" + self.duid.show(label_lvl=" "*4) + + #### + # Find the source address we will use + l = filter(lambda x: x[2] == iface and in6_islladdr(x[0]), + in6_getifaddr()) + if not l: + warning("Unable to get a Link-Local address") + return + + self.src_addr = l[0][0] + + #### + # Our leases + self.leases = {} + + + if self.debug: + print "\n[+] Starting DHCPv6 service on %s:" % self.iface + + def is_request(self, p): + if not IPv6 in p: + return False + + src = p[IPv6].src + dst = p[IPv6].dst + + p = p[IPv6].payload + if not isinstance(p, UDP) or p.sport != 546 or p.dport != 547 : + return False + + p = p.payload + if not isinstance(p, DHCP6): + return False + + # Message we considered client messages : + # Solicit (1), Request (3), Confirm (4), Renew (5), Rebind (6) + # Decline (9), Release (8), Information-request (11), + if not (p.msgtype in [1, 3, 4, 5, 6, 8, 9, 11]): + return False + + # Message validation following section 15 of RFC 3315 + + if ((p.msgtype == 1) or # Solicit + (p.msgtype == 6) or # Rebind + (p.msgtype == 4)): # Confirm + if ((not DHCP6OptClientId in p) or + DHCP6OptServerId in p): + return False + + if (p.msgtype == 6 or # Rebind + p.msgtype == 4): # Confirm + # XXX We do not reply to Confirm or Rebind as we + # XXX do not support address assignment + return False + + elif (p.msgtype == 3 or # Request + p.msgtype == 5 or # Renew + p.msgtype == 8): # Release + + # Both options must be present + if ((not DHCP6OptServerId in p) or + (not DHCP6OptClientId in p)): + return False + # provided server DUID must match ours + duid = p[DHCP6OptServerId].duid + if (type(duid) != type(self.duid)): + return False + if str(duid) != str(self.duid): + return False + + if (p.msgtype == 5 or # Renew + p.msgtype == 8): # Release + # XXX We do not reply to Renew or Release as we + # XXX do not support address assignment + return False + + elif p.msgtype == 9: # Decline + # XXX We should check if we are tracking that client + if not self.debug: + return False + + bo = Color.bold + g = Color.green + bo + b = Color.blue + bo + n = Color.normal + r = Color.red + + vendor = in6_addrtovendor(src) + if (vendor and vendor != "UNKNOWN"): + vendor = " [" + b + vendor + n + "]" + else: + vendor = "" + src = bo + src + n + + it = p + addrs = [] + while it: + l = [] + if isinstance(it, DHCP6OptIA_NA): + l = it.ianaopts + elif isinstance(it, DHCP6OptIA_TA): + l = it.iataopts + + opsaddr = filter(lambda x: isinstance(x, DHCP6OptIAAddress),l) + a=map(lambda x: x.addr, opsaddr) + addrs += a + it = it.payload + + addrs = map(lambda x: bo + x + n, addrs) + if debug: + msg = r + "[DEBUG]" + n + " Received " + g + "Decline" + n + msg += " from " + bo + src + vendor + " for " + msg += ", ".join(addrs)+ n + print msg + + # See sect 18.1.7 + + # Sent by a client to warn us she has determined + # one or more addresses assigned to her is already + # used on the link. + # We should simply log that fact. No messaged should + # be sent in return. + + # - Message must include a Server identifier option + # - the content of the Server identifier option must + # match the server's identifier + # - the message must include a Client Identifier option + return False + + elif p.msgtype == 11: # Information-Request + if DHCP6OptServerId in p: + duid = p[DHCP6OptServerId].duid + if (type(duid) != type(self.duid)): + return False + if str(duid) != str(self.duid): + return False + if ((DHCP6OptIA_NA in p) or + (DHCP6OptIA_TA in p) or + (DHCP6OptIA_PD in p)): + return False + else: + return False + + return True + + def print_reply(self, req, reply): + def norm(s): + if s.startswith("DHCPv6 "): + s = s[7:] + if s.endswith(" Message"): + s = s[:-8] + return s + + if reply is None: + return + + bo = Color.bold + g = Color.green + bo + b = Color.blue + bo + n = Color.normal + reqtype = g + norm(req.getlayer(UDP).payload.name) + n + reqsrc = req.getlayer(IPv6).src + vendor = in6_addrtovendor(reqsrc) + if (vendor and vendor != "UNKNOWN"): + vendor = " [" + b + vendor + n + "]" + else: + vendor = "" + reqsrc = bo + reqsrc + n + reptype = g + norm(reply.getlayer(UDP).payload.name) + n + + print "Sent %s answering to %s from %s%s" % (reptype, reqtype, reqsrc, vendor) + + def make_reply(self, req): + req_mac_src = req.src + req_mac_dst = req.dst + + p = req[IPv6] + req_src = p.src + req_dst = p.dst + + p = p.payload.payload + + msgtype = p.msgtype + trid = p.trid + + if msgtype == 1: # SOLICIT (See Sect 17.1 and 17.2 of RFC 3315) + + # XXX We don't support address or prefix assignment + # XXX We also do not support relay function --arno + + client_duid = p[DHCP6OptClientId].duid + resp = IPv6(src=self.src_addr, dst=req_src) + resp /= UDP(sport=547, dport=546) + + if p.haslayer(DHCP6OptRapidCommit): + # construct a Reply packet + resp /= DHCP6_Reply(trid=trid) + resp /= DHCP6OptRapidCommit() # See 17.1.2 + resp /= DHCP6OptServerId(duid = self.duid) + resp /= DHCP6OptClientId(duid = client_duid) + + else: # No Rapid Commit in the packet. Reply with an Advertise + + if (p.haslayer(DHCP6OptIA_NA) or + p.haslayer(DHCP6OptIA_TA)): + # XXX We don't assign addresses at the moment + msg = "Scapy6 dhcp6d does not support address assignment" + resp /= DHCP6_Advertise(trid = trid) + resp /= DHCP6OptStatusCode(statuscode=2, statusmsg=msg) + resp /= DHCP6OptServerId(duid = self.duid) + resp /= DHCP6OptClientId(duid = client_duid) + + elif p.haslayer(DHCP6OptIA_PD): + # XXX We don't assign prefixes at the moment + msg = "Scapy6 dhcp6d does not support prefix assignment" + resp /= DHCP6_Advertise(trid = trid) + resp /= DHCP6OptStatusCode(statuscode=6, statusmsg=msg) + resp /= DHCP6OptServerId(duid = self.duid) + resp /= DHCP6OptClientId(duid = client_duid) + + else: # Usual case, no request for prefixes or addresse + resp /= DHCP6_Advertise(trid = trid) + resp /= DHCP6OptPref(prefval = self.advpref) + resp /= DHCP6OptServerId(duid = self.duid) + resp /= DHCP6OptClientId(duid = client_duid) + resp /= DHCP6OptReconfAccept() + + # See which options should be included + reqopts = [] + if p.haslayer(DHCP6OptOptReq): # add only asked ones + reqopts = p[DHCP6OptOptReq].reqopts + for o in self.dhcpv6_options.keys(): + if o in reqopts: + resp /= self.dhcpv6_options[o] + else: # advertise everything we have available + for o in self.dhcpv6_options.keys(): + resp /= self.dhcpv6_options[o] + + return resp + + elif msgtype == 3: #REQUEST (INFO-REQUEST is further below) + client_duid = p[DHCP6OptClientId].duid + resp = IPv6(src=self.src_addr, dst=req_src) + resp /= UDP(sport=547, dport=546) + resp /= DHCP6_Solicit(trid=trid) + resp /= DHCP6OptServerId(duid = self.duid) + resp /= DHCP6OptClientId(duid = client_duid) + + # See which options should be included + reqopts = [] + if p.haslayer(DHCP6OptOptReq): # add only asked ones + reqopts = p[DHCP6OptOptReq].reqopts + for o in self.dhcpv6_options.keys(): + if o in reqopts: + resp /= self.dhcpv6_options[o] + else: + # advertise everything we have available. + # Should not happen has clients MUST include + # and ORO in requests (sec 18.1.1) -- arno + for o in self.dhcpv6_options.keys(): + resp /= self.dhcpv6_options[o] + + return resp + + elif msgtype == 4: # CONFIRM + # see Sect 18.1.2 + + # Client want to check if addresses it was assigned + # are still appropriate + + # Server must discard any Confirm messages that + # do not include a Client Identifier option OR + # THAT DO INCLUDE a Server Identifier Option + + # XXX we must discard the SOLICIT if it is received with + # a unicast destination address + + pass + + elif msgtype == 5: # RENEW + # see Sect 18.1.3 + + # Clients want to extend lifetime of assigned addresses + # and update configuration parameters. This message is sent + # specifically to the server that provided her the info + + # - Received message must include a Server Identifier + # option. + # - the content of server identifier option must match + # the server's identifier. + # - the message must include a Client identifier option + + pass + + elif msgtype == 6: # REBIND + # see Sect 18.1.4 + + # Same purpose as the Renew message but sent to any + # available server after he received no response + # to its previous Renew message. + + + # - Message must include a Client Identifier Option + # - Message can't include a Server identifier option + + # XXX we must discard the SOLICIT if it is received with + # a unicast destination address + + pass + + elif msgtype == 8: # RELEASE + # See section 18.1.6 + + # Message is sent to the server to indicate that + # she will no longer use the addresses that was assigned + # We should parse the message and verify our dictionary + # to log that fact. + + + # - The message must include a server identifier option + # - The content of the Server Identifier option must + # match the server's identifier + # - the message must include a Client Identifier option + + pass + + elif msgtype == 9: # DECLINE + # See section 18.1.7 + pass + + elif msgtype == 11: # INFO-REQUEST + client_duid = None + if not p.haslayer(DHCP6OptClientId): + if self.debug: + warning("Received Info Request message without Client Id option") + else: + client_duid = p[DHCP6OptClientId].duid + + resp = IPv6(src=self.src_addr, dst=req_src) + resp /= UDP(sport=547, dport=546) + resp /= DHCP6_Reply(trid=trid) + resp /= DHCP6OptServerId(duid = self.duid) + + if client_duid: + resp /= DHCP6OptClientId(duid = client_duid) + + # Stack requested options if available + reqopts = [] + if p.haslayer(DHCP6OptOptReq): + reqopts = p[DHCP6OptOptReq].reqopts + for o in self.dhcpv6_options.keys(): + resp /= self.dhcpv6_options[o] + + return resp + + else: + # what else ? + pass + + # - We won't support reemission + # - We won't support relay role, nor relay forwarded messages + # at the beginning diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/dns.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/dns.py new file mode 100644 index 00000000..533db6c2 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/dns.py @@ -0,0 +1,690 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +DNS: Domain Name System. +""" + +import socket,struct + +from scapy.packet import * +from scapy.fields import * +from scapy.ansmachine import * +from scapy.layers.inet import IP, UDP + +class DNSStrField(StrField): + + def h2i(self, pkt, x): + if x == "": + return "." + return x + + def i2m(self, pkt, x): + if x == ".": + return "\x00" + + x = [k[:63] for k in x.split(".")] # Truncate chunks that cannot be encoded (more than 63 bytes..) + x = map(lambda y: chr(len(y))+y, x) + x = "".join(x) + if x[-1] != "\x00": + x += "\x00" + return x + + def getfield(self, pkt, s): + n = "" + + if ord(s[0]) == 0: + return s[1:], "." + + while 1: + l = ord(s[0]) + s = s[1:] + if not l: + break + if l & 0xc0: + raise Scapy_Exception("DNS message can't be compressed at this point!") + else: + n += s[:l]+"." + s = s[l:] + return s, n + + +class DNSRRCountField(ShortField): + holds_packets=1 + def __init__(self, name, default, rr): + ShortField.__init__(self, name, default) + self.rr = rr + def _countRR(self, pkt): + x = getattr(pkt,self.rr) + i = 0 + while isinstance(x, DNSRR) or isinstance(x, DNSQR) or isdnssecRR(x): + x = x.payload + i += 1 + return i + + def i2m(self, pkt, x): + if x is None: + x = self._countRR(pkt) + return x + def i2h(self, pkt, x): + if x is None: + x = self._countRR(pkt) + return x + + +def DNSgetstr(s,p): + name = "" + q = 0 + jpath = [p] + while 1: + if p >= len(s): + warning("DNS RR prematured end (ofs=%i, len=%i)"%(p,len(s))) + break + l = ord(s[p]) + p += 1 + if l & 0xc0: + if not q: + q = p+1 + if p >= len(s): + warning("DNS incomplete jump token at (ofs=%i)" % p) + break + p = ((l & 0x3f) << 8) + ord(s[p]) - 12 + if p in jpath: + warning("DNS decompression loop detected") + break + jpath.append(p) + continue + elif l > 0: + name += s[p:p+l]+"." + p += l + continue + break + if q: + p = q + return name,p + + +class DNSRRField(StrField): + holds_packets=1 + def __init__(self, name, countfld, passon=1): + StrField.__init__(self, name, None) + self.countfld = countfld + self.passon = passon + def i2m(self, pkt, x): + if x is None: + return "" + return str(x) + def decodeRR(self, name, s, p): + ret = s[p:p+10] + type,cls,ttl,rdlen = struct.unpack("!HHIH", ret) + p += 10 + rr = DNSRR("\x00"+ret+s[p:p+rdlen]) + if type in [2, 3, 4, 5]: + rr.rdata = DNSgetstr(s,p)[0] + del(rr.rdlen) + elif type in dnsRRdispatcher.keys(): + rr = dnsRRdispatcher[type]("\x00"+ret+s[p:p+rdlen]) + else: + del(rr.rdlen) + + p += rdlen + + rr.rrname = name + return rr,p + def getfield(self, pkt, s): + if type(s) is tuple : + s,p = s + else: + p = 0 + ret = None + c = getattr(pkt, self.countfld) + if c > len(s): + warning("wrong value: DNS.%s=%i" % (self.countfld,c)) + return s,"" + while c: + c -= 1 + name,p = DNSgetstr(s,p) + rr,p = self.decodeRR(name, s, p) + if ret is None: + ret = rr + else: + ret.add_payload(rr) + if self.passon: + return (s,p),ret + else: + return s[p:],ret + + +class DNSQRField(DNSRRField): + holds_packets=1 + def decodeRR(self, name, s, p): + ret = s[p:p+4] + p += 4 + rr = DNSQR("\x00"+ret) + rr.qname = name + return rr,p + + + +class RDataField(StrLenField): + def m2i(self, pkt, s): + family = None + if pkt.type == 1: # A + family = socket.AF_INET + elif pkt.type == 12: # PTR + s = DNSgetstr(s, 0)[0] + elif pkt.type == 16: # TXT + ret_s = "" + tmp_s = s + # RDATA contains a list of strings, each are prepended with + # a byte containing the size of the following string. + while tmp_s: + tmp_len = struct.unpack("!B", tmp_s[0])[0] + 1 + if tmp_len > len(tmp_s): + warning("DNS RR TXT prematured end of character-string (size=%i, remaining bytes=%i)" % (tmp_len, len(tmp_s))) + ret_s += tmp_s[1:tmp_len] + tmp_s = tmp_s[tmp_len:] + s = ret_s + elif pkt.type == 28: # AAAA + family = socket.AF_INET6 + if family is not None: + s = inet_ntop(family, s) + return s + def i2m(self, pkt, s): + if pkt.type == 1: # A + if s: + s = inet_aton(s) + elif pkt.type in [2,3,4,5]: # NS, MD, MF, CNAME + s = "".join(map(lambda x: chr(len(x))+x, s.split("."))) + if ord(s[-1]): + s += "\x00" + elif pkt.type == 16: # TXT + if s: + ret_s = "" + # The initial string must be splitted into a list of strings + # prepended with theirs sizes. + while len(s) >= 255: + ret_s += "\xff" + s[:255] + s = s[255:] + # The remaining string is less than 255 bytes long + if len(s): + ret_s += struct.pack("!B", len(s)) + s + s = ret_s + elif pkt.type == 28: # AAAA + if s: + s = inet_pton(socket.AF_INET6, s) + return s + +class RDLenField(Field): + def __init__(self, name): + Field.__init__(self, name, None, "H") + def i2m(self, pkt, x): + if x is None: + rdataf = pkt.get_field("rdata") + x = len(rdataf.i2m(pkt, pkt.rdata)) + return x + def i2h(self, pkt, x): + if x is None: + rdataf = pkt.get_field("rdata") + x = len(rdataf.i2m(pkt, pkt.rdata)) + return x + + +class DNS(Packet): + name = "DNS" + fields_desc = [ ShortField("id", 0), + BitField("qr", 0, 1), + BitEnumField("opcode", 0, 4, {0:"QUERY",1:"IQUERY",2:"STATUS"}), + BitField("aa", 0, 1), + BitField("tc", 0, 1), + BitField("rd", 0, 1), + BitField("ra", 0, 1), + BitField("z", 0, 1), + # AD and CD bits are defined in RFC 2535 + BitField("ad", 0, 1), # Authentic Data + BitField("cd", 0, 1), # Checking Disabled + BitEnumField("rcode", 0, 4, {0:"ok", 1:"format-error", 2:"server-failure", 3:"name-error", 4:"not-implemented", 5:"refused"}), + DNSRRCountField("qdcount", None, "qd"), + DNSRRCountField("ancount", None, "an"), + DNSRRCountField("nscount", None, "ns"), + DNSRRCountField("arcount", None, "ar"), + DNSQRField("qd", "qdcount"), + DNSRRField("an", "ancount"), + DNSRRField("ns", "nscount"), + DNSRRField("ar", "arcount",0) ] + def answers(self, other): + return (isinstance(other, DNS) + and self.id == other.id + and self.qr == 1 + and other.qr == 0) + + def mysummary(self): + type = ["Qry","Ans"][self.qr] + name = "" + if self.qr: + type = "Ans" + if self.ancount > 0 and isinstance(self.an, DNSRR): + name = ' "%s"' % self.an.rdata + else: + type = "Qry" + if self.qdcount > 0 and isinstance(self.qd, DNSQR): + name = ' "%s"' % self.qd.qname + return 'DNS %s%s ' % (type, name) + +dnstypes = { 0:"ANY", 255:"ALL", + 1:"A", 2:"NS", 3:"MD", 4:"MF", 5:"CNAME", 6:"SOA", 7: "MB", 8:"MG", + 9:"MR",10:"NULL",11:"WKS",12:"PTR",13:"HINFO",14:"MINFO",15:"MX",16:"TXT", + 17:"RP",18:"AFSDB",28:"AAAA", 33:"SRV",38:"A6",39:"DNAME", + 41:"OPT", 43:"DS", 46:"RRSIG", 47:"NSEC", 48:"DNSKEY", + 50: "NSEC3", 51: "NSEC3PARAM", 32769:"DLV" } + +dnsqtypes = {251:"IXFR",252:"AXFR",253:"MAILB",254:"MAILA",255:"ALL"} +dnsqtypes.update(dnstypes) +dnsclasses = {1: 'IN', 2: 'CS', 3: 'CH', 4: 'HS', 255: 'ANY'} + + +class DNSQR(Packet): + name = "DNS Question Record" + show_indent=0 + fields_desc = [ DNSStrField("qname",""), + ShortEnumField("qtype", 1, dnsqtypes), + ShortEnumField("qclass", 1, dnsclasses) ] + + + +# RFC 2671 - Extension Mechanisms for DNS (EDNS0) + +class EDNS0TLV(Packet): + name = "DNS EDNS0 TLV" + fields_desc = [ ShortEnumField("optcode", 0, { 0: "Reserved", 1: "LLQ", 2: "UL", 3: "NSID", 4: "Reserved", 5: "PING" }), + FieldLenField("optlen", None, "optdata", fmt="H"), + StrLenField("optdata", "", length_from=lambda pkt: pkt.optlen) ] + + def extract_padding(self, p): + return "", p + +class DNSRROPT(Packet): + name = "DNS OPT Resource Record" + fields_desc = [ DNSStrField("rrname",""), + ShortEnumField("type", 41, dnstypes), + ShortField("rclass", 4096), + ByteField("extrcode", 0), + ByteField("version", 0), + # version 0 means EDNS0 + BitEnumField("z", 32768, 16, { 32768: "D0" }), + # D0 means DNSSEC OK from RFC 3225 + FieldLenField("rdlen", None, length_of="rdata", fmt="H"), + PacketListField("rdata", [], EDNS0TLV, length_from=lambda pkt: pkt.rdlen) ] + +# RFC 4034 - Resource Records for the DNS Security Extensions + +# 09/2013 from http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml +dnssecalgotypes = { 0:"Reserved", 1:"RSA/MD5", 2:"Diffie-Hellman", 3:"DSA/SHA-1", + 4:"Reserved", 5:"RSA/SHA-1", 6:"DSA-NSEC3-SHA1", + 7:"RSASHA1-NSEC3-SHA1", 8:"RSA/SHA-256", 9:"Reserved", + 10:"RSA/SHA-512", 11:"Reserved", 12:"GOST R 34.10-2001", + 13:"ECDSA Curve P-256 with SHA-256", 14: "ECDSA Curve P-384 with SHA-384", + 252:"Reserved for Indirect Keys", 253:"Private algorithms - domain name", + 254:"Private algorithms - OID", 255:"Reserved" } + +# 09/2013 from http://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml +dnssecdigesttypes = { 0:"Reserved", 1:"SHA-1", 2:"SHA-256", 3:"GOST R 34.11-94", 4:"SHA-384" } + + +class TimeField(IntField): + + def any2i(self, pkt, x): + if type(x) == str: + import time, calendar + t = time.strptime(x, "%Y%m%d%H%M%S") + return int(calendar.timegm(t)) + return x + + def i2repr(self, pkt, x): + import time + x = self.i2h(pkt, x) + t = time.strftime("%Y%m%d%H%M%S", time.gmtime(x)) + return "%s (%d)" % (t ,x) + + +def bitmap2RRlist(bitmap): + """ + Decode the 'Type Bit Maps' field of the NSEC Resource Record into an + integer list. + """ + # RFC 4034, 4.1.2. The Type Bit Maps Field + + RRlist = [] + + while bitmap: + + if len(bitmap) < 2: + warning("bitmap too short (%i)" % len(bitmap)) + return + + window_block = ord(bitmap[0]) # window number + offset = 256*window_block # offset of the Ressource Record + bitmap_len = ord(bitmap[1]) # length of the bitmap in bytes + + if bitmap_len <= 0 or bitmap_len > 32: + warning("bitmap length is no valid (%i)" % bitmap_len) + return + + tmp_bitmap = bitmap[2:2+bitmap_len] + + # Let's compare each bit of tmp_bitmap and compute the real RR value + for b in xrange(len(tmp_bitmap)): + v = 128 + for i in xrange(8): + if ord(tmp_bitmap[b]) & v: + # each of the RR is encoded as a bit + RRlist += [ offset + b*8 + i ] + v = v >> 1 + + # Next block if any + bitmap = bitmap[2+bitmap_len:] + + return RRlist + + +def RRlist2bitmap(lst): + """ + Encode a list of integers representing Resource Records to a bitmap field + used in the NSEC Resource Record. + """ + # RFC 4034, 4.1.2. The Type Bit Maps Field + + import math + + bitmap = "" + lst = list(set(lst)) + lst.sort() + + lst = filter(lambda x: x <= 65535, lst) + lst = map(lambda x: abs(x), lst) + + # number of window blocks + max_window_blocks = int(math.ceil(lst[-1] / 256.)) + min_window_blocks = int(math.floor(lst[0] / 256.)) + if min_window_blocks == max_window_blocks: + max_window_blocks += 1 + + for wb in xrange(min_window_blocks, max_window_blocks+1): + # First, filter out RR not encoded in the current window block + # i.e. keep everything between 256*wb <= 256*(wb+1) + rrlist = filter(lambda x: 256*wb <= x and x < 256*(wb+1), lst) + rrlist.sort() + if rrlist == []: + continue + + # Compute the number of bytes used to store the bitmap + if rrlist[-1] == 0: # only one element in the list + bytes = 1 + else: + max = rrlist[-1] - 256*wb + bytes = int(math.ceil(max / 8)) + 1 # use at least 1 byte + if bytes > 32: # Don't encode more than 256 bits / values + bytes = 32 + + bitmap += struct.pack("B", wb) + bitmap += struct.pack("B", bytes) + + # Generate the bitmap + for tmp in xrange(bytes): + v = 0 + # Remove out of range Ressource Records + tmp_rrlist = filter(lambda x: 256*wb+8*tmp <= x and x < 256*wb+8*tmp+8, rrlist) + if not tmp_rrlist == []: + # 1. rescale to fit into 8 bits + tmp_rrlist = map(lambda x: (x-256*wb)-(tmp*8), tmp_rrlist) + # 2. x gives the bit position ; compute the corresponding value + tmp_rrlist = map(lambda x: 2**(7-x) , tmp_rrlist) + # 3. sum everything + v = reduce(lambda x,y: x+y, tmp_rrlist) + bitmap += struct.pack("B", v) + + return bitmap + + +class RRlistField(StrField): + def h2i(self, pkt, x): + if type(x) == list: + return RRlist2bitmap(x) + return x + + def i2repr(self, pkt, x): + x = self.i2h(pkt, x) + rrlist = bitmap2RRlist(x) + return [ dnstypes.get(rr, rr) for rr in rrlist ] + + +class _DNSRRdummy(Packet): + name = "Dummy class that implements post_build() for Ressource Records" + def post_build(self, pkt, pay): + if not self.rdlen == None: + return pkt + + lrrname = len(self.fields_desc[0].i2m("", self.getfieldval("rrname"))) + l = len(pkt) - lrrname - 10 + pkt = pkt[:lrrname+8] + struct.pack("!H", l) + pkt[lrrname+8+2:] + + return pkt + +class DNSRRSOA(_DNSRRdummy): + name = "DNS SOA Resource Record" + fields_desc = [ DNSStrField("rrname",""), + ShortEnumField("type", 6, dnstypes), + ShortEnumField("rclass", 1, dnsclasses), + IntField("ttl", 0), + ShortField("rdlen", None), + DNSStrField("mname", ""), + DNSStrField("rname", ""), + IntField("serial", 0), + IntField("refresh", 0), + IntField("retry", 0), + IntField("expire", 0), + IntField("minimum", 0) + ] + +class DNSRRRSIG(_DNSRRdummy): + name = "DNS RRSIG Resource Record" + fields_desc = [ DNSStrField("rrname",""), + ShortEnumField("type", 46, dnstypes), + ShortEnumField("rclass", 1, dnsclasses), + IntField("ttl", 0), + ShortField("rdlen", None), + ShortEnumField("typecovered", 1, dnstypes), + ByteEnumField("algorithm", 5, dnssecalgotypes), + ByteField("labels", 0), + IntField("originalttl", 0), + TimeField("expiration", 0), + TimeField("inception", 0), + ShortField("keytag", 0), + DNSStrField("signersname", ""), + StrField("signature", "") + ] + + +class DNSRRNSEC(_DNSRRdummy): + name = "DNS NSEC Resource Record" + fields_desc = [ DNSStrField("rrname",""), + ShortEnumField("type", 47, dnstypes), + ShortEnumField("rclass", 1, dnsclasses), + IntField("ttl", 0), + ShortField("rdlen", None), + DNSStrField("nextname", ""), + RRlistField("typebitmaps", "") + ] + + +class DNSRRDNSKEY(_DNSRRdummy): + name = "DNS DNSKEY Resource Record" + fields_desc = [ DNSStrField("rrname",""), + ShortEnumField("type", 48, dnstypes), + ShortEnumField("rclass", 1, dnsclasses), + IntField("ttl", 0), + ShortField("rdlen", None), + FlagsField("flags", 256, 16, "S???????Z???????"), + # S: Secure Entry Point + # Z: Zone Key + ByteField("protocol", 3), + ByteEnumField("algorithm", 5, dnssecalgotypes), + StrField("publickey", "") + ] + + +class DNSRRDS(_DNSRRdummy): + name = "DNS DS Resource Record" + fields_desc = [ DNSStrField("rrname",""), + ShortEnumField("type", 43, dnstypes), + ShortEnumField("rclass", 1, dnsclasses), + IntField("ttl", 0), + ShortField("rdlen", None), + ShortField("keytag", 0), + ByteEnumField("algorithm", 5, dnssecalgotypes), + ByteEnumField("digesttype", 5, dnssecdigesttypes), + StrField("digest", "") + ] + + +# RFC 5074 - DNSSEC Lookaside Validation (DLV) +class DNSRRDLV(DNSRRDS): + name = "DNS DLV Resource Record" + def __init__(self, *args, **kargs): + DNSRRDS.__init__(self, *args, **kargs) + if not kargs.get('type', 0): + self.type = 32769 + +# RFC 5155 - DNS Security (DNSSEC) Hashed Authenticated Denial of Existence +class DNSRRNSEC3(_DNSRRdummy): + name = "DNS NSEC3 Resource Record" + fields_desc = [ DNSStrField("rrname",""), + ShortEnumField("type", 50, dnstypes), + ShortEnumField("rclass", 1, dnsclasses), + IntField("ttl", 0), + ShortField("rdlen", None), + ByteField("hashalg", 0), + BitEnumField("flags", 0, 8, {1:"Opt-Out"}), + ShortField("iterations", 0), + FieldLenField("saltlength", 0, fmt="!B", length_of="salt"), + StrLenField("salt", "", length_from=lambda x: x.saltlength), + FieldLenField("hashlength", 0, fmt="!B", length_of="nexthashedownername"), + StrLenField("nexthashedownername", "", length_from=lambda x: x.hashlength), + RRlistField("typebitmaps", "") + ] + + +class DNSRRNSEC3PARAM(_DNSRRdummy): + name = "DNS NSEC3PARAM Resource Record" + fields_desc = [ DNSStrField("rrname",""), + ShortEnumField("type", 51, dnstypes), + ShortEnumField("rclass", 1, dnsclasses), + IntField("ttl", 0), + ShortField("rdlen", None), + ByteField("hashalg", 0), + ByteField("flags", 0), + ShortField("iterations", 0), + FieldLenField("saltlength", 0, fmt="!B", length_of="salt"), + StrLenField("salt", "", length_from=lambda pkt: pkt.saltlength) + ] + + +dnssecclasses = [ DNSRROPT, DNSRRRSIG, DNSRRDLV, DNSRRDNSKEY, DNSRRNSEC, DNSRRDS, DNSRRNSEC3, DNSRRNSEC3PARAM ] + +def isdnssecRR(obj): + list = [ isinstance (obj, cls) for cls in dnssecclasses ] + return reduce(lambda x,y: x or y, list) + +dnsRRdispatcher = { #6: DNSRRSOA, + 41: DNSRROPT, # RFC 1671 + 43: DNSRRDS, # RFC 4034 + 46: DNSRRRSIG, # RFC 4034 + 47: DNSRRNSEC, # RFC 4034 + 48: DNSRRDNSKEY, # RFC 4034 + 50: DNSRRNSEC3, # RFC 5155 + 51: DNSRRNSEC3PARAM, # RFC 5155 + 32769: DNSRRDLV # RFC 4431 + } + +class DNSRR(Packet): + name = "DNS Resource Record" + show_indent=0 + fields_desc = [ DNSStrField("rrname",""), + ShortEnumField("type", 1, dnstypes), + ShortEnumField("rclass", 1, dnsclasses), + IntField("ttl", 0), + RDLenField("rdlen"), + RDataField("rdata", "", length_from=lambda pkt:pkt.rdlen) ] + +bind_layers( UDP, DNS, dport=53) +bind_layers( UDP, DNS, sport=53) + + +@conf.commands.register +def dyndns_add(nameserver, name, rdata, type="A", ttl=10): + """Send a DNS add message to a nameserver for "name" to have a new "rdata" +dyndns_add(nameserver, name, rdata, type="A", ttl=10) -> result code (0=ok) + +example: dyndns_add("ns1.toto.com", "dyn.toto.com", "127.0.0.1") +RFC2136 +""" + zone = name[name.find(".")+1:] + r=sr1(IP(dst=nameserver)/UDP()/DNS(opcode=5, + qd=[DNSQR(qname=zone, qtype="SOA")], + ns=[DNSRR(rrname=name, type="A", + ttl=ttl, rdata=rdata)]), + verbose=0, timeout=5) + if r and r.haslayer(DNS): + return r.getlayer(DNS).rcode + else: + return -1 + + + + +@conf.commands.register +def dyndns_del(nameserver, name, type="ALL", ttl=10): + """Send a DNS delete message to a nameserver for "name" +dyndns_del(nameserver, name, type="ANY", ttl=10) -> result code (0=ok) + +example: dyndns_del("ns1.toto.com", "dyn.toto.com") +RFC2136 +""" + zone = name[name.find(".")+1:] + r=sr1(IP(dst=nameserver)/UDP()/DNS(opcode=5, + qd=[DNSQR(qname=zone, qtype="SOA")], + ns=[DNSRR(rrname=name, type=type, + rclass="ANY", ttl=0, rdata="")]), + verbose=0, timeout=5) + if r and r.haslayer(DNS): + return r.getlayer(DNS).rcode + else: + return -1 + + +class DNS_am(AnsweringMachine): + function_name="dns_spoof" + filter = "udp port 53" + + def parse_options(self, joker="192.168.1.1", match=None): + if match is None: + self.match = {} + else: + self.match = match + self.joker=joker + + def is_request(self, req): + return req.haslayer(DNS) and req.getlayer(DNS).qr == 0 + + def make_reply(self, req): + ip = req.getlayer(IP) + dns = req.getlayer(DNS) + resp = IP(dst=ip.src, src=ip.dst)/UDP(dport=ip.sport,sport=ip.dport) + rdata = self.match.get(dns.qd.qname, self.joker) + resp /= DNS(id=dns.id, qr=1, qd=dns.qd, + an=DNSRR(rrname=dns.qd.qname, ttl=10, rdata=rdata)) + return resp + + diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/dot11.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/dot11.py new file mode 100644 index 00000000..b340dd85 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/dot11.py @@ -0,0 +1,559 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +Wireless LAN according to IEEE 802.11. +""" + +import re,struct + +from scapy.packet import * +from scapy.fields import * +from scapy.plist import PacketList +from scapy.layers.l2 import * + + +try: + from Crypto.Cipher import ARC4 +except ImportError: + log_loading.info("Can't import python Crypto lib. Won't be able to decrypt WEP.") + + +### Fields + +class Dot11AddrMACField(MACField): + def is_applicable(self, pkt): + return 1 + def addfield(self, pkt, s, val): + if self.is_applicable(pkt): + return MACField.addfield(self, pkt, s, val) + else: + return s + def getfield(self, pkt, s): + if self.is_applicable(pkt): + return MACField.getfield(self, pkt, s) + else: + return s,None + +class Dot11Addr2MACField(Dot11AddrMACField): + def is_applicable(self, pkt): + if pkt.type == 1: + return pkt.subtype in [ 0xb, 0xa, 0xe, 0xf] # RTS, PS-Poll, CF-End, CF-End+CF-Ack + return 1 + +class Dot11Addr3MACField(Dot11AddrMACField): + def is_applicable(self, pkt): + if pkt.type in [0,2]: + return 1 + return 0 + +class Dot11Addr4MACField(Dot11AddrMACField): + def is_applicable(self, pkt): + if pkt.type == 2: + if pkt.FCfield & 0x3 == 0x3: # To-DS and From-DS are set + return 1 + return 0 + + +### Layers + + +class PrismHeader(Packet): + """ iwpriv wlan0 monitor 3 """ + name = "Prism header" + fields_desc = [ LEIntField("msgcode",68), + LEIntField("len",144), + StrFixedLenField("dev","",16), + LEIntField("hosttime_did",0), + LEShortField("hosttime_status",0), + LEShortField("hosttime_len",0), + LEIntField("hosttime",0), + LEIntField("mactime_did",0), + LEShortField("mactime_status",0), + LEShortField("mactime_len",0), + LEIntField("mactime",0), + LEIntField("channel_did",0), + LEShortField("channel_status",0), + LEShortField("channel_len",0), + LEIntField("channel",0), + LEIntField("rssi_did",0), + LEShortField("rssi_status",0), + LEShortField("rssi_len",0), + LEIntField("rssi",0), + LEIntField("sq_did",0), + LEShortField("sq_status",0), + LEShortField("sq_len",0), + LEIntField("sq",0), + LEIntField("signal_did",0), + LEShortField("signal_status",0), + LEShortField("signal_len",0), + LESignedIntField("signal",0), + LEIntField("noise_did",0), + LEShortField("noise_status",0), + LEShortField("noise_len",0), + LEIntField("noise",0), + LEIntField("rate_did",0), + LEShortField("rate_status",0), + LEShortField("rate_len",0), + LEIntField("rate",0), + LEIntField("istx_did",0), + LEShortField("istx_status",0), + LEShortField("istx_len",0), + LEIntField("istx",0), + LEIntField("frmlen_did",0), + LEShortField("frmlen_status",0), + LEShortField("frmlen_len",0), + LEIntField("frmlen",0), + ] + def answers(self, other): + if isinstance(other, PrismHeader): + return self.payload.answers(other.payload) + else: + return self.payload.answers(other) + +class RadioTap(Packet): + name = "RadioTap dummy" + fields_desc = [ ByteField('version', 0), + ByteField('pad', 0), + FieldLenField('len', None, 'notdecoded', ' %Dot11.addr1%") + def guess_payload_class(self, payload): + if self.type == 0x02 and (self.subtype >= 0x08 and self.subtype <=0xF and self.subtype != 0xD): + return Dot11QoS + elif self.FCfield & 0x40: + return Dot11WEP + else: + return Packet.guess_payload_class(self, payload) + def answers(self, other): + if isinstance(other,Dot11): + if self.type == 0: # management + if self.addr1.lower() != other.addr2.lower(): # check resp DA w/ req SA + return 0 + if (other.subtype,self.subtype) in [(0,1),(2,3),(4,5)]: + return 1 + if self.subtype == other.subtype == 11: # auth + return self.payload.answers(other.payload) + elif self.type == 1: # control + return 0 + elif self.type == 2: # data + return self.payload.answers(other.payload) + elif self.type == 3: # reserved + return 0 + return 0 + def unwep(self, key=None, warn=1): + if self.FCfield & 0x40 == 0: + if warn: + warning("No WEP to remove") + return + if isinstance(self.payload.payload, NoPayload): + if key or conf.wepkey: + self.payload.decrypt(key) + if isinstance(self.payload.payload, NoPayload): + if warn: + warning("Dot11 can't be decrypted. Check conf.wepkey.") + return + self.FCfield &= ~0x40 + self.payload=self.payload.payload + + +class Dot11QoS(Packet): + name = "802.11 QoS" + fields_desc = [ BitField("TID",None,4), + BitField("EOSP",None,1), + BitField("Ack Policy",None,2), + BitField("Reserved",None,1), + ByteField("TXOP",None) ] + def guess_payload_class(self, payload): + if isinstance(self.underlayer, Dot11): + if self.underlayer.FCfield & 0x40: + return Dot11WEP + return Packet.guess_payload_class(self, payload) + + +capability_list = [ "res8", "res9", "short-slot", "res11", + "res12", "DSSS-OFDM", "res14", "res15", + "ESS", "IBSS", "CFP", "CFP-req", + "privacy", "short-preamble", "PBCC", "agility"] + +reason_code = {0:"reserved",1:"unspec", 2:"auth-expired", + 3:"deauth-ST-leaving", + 4:"inactivity", 5:"AP-full", 6:"class2-from-nonauth", + 7:"class3-from-nonass", 8:"disas-ST-leaving", + 9:"ST-not-auth"} + +status_code = {0:"success", 1:"failure", 10:"cannot-support-all-cap", + 11:"inexist-asso", 12:"asso-denied", 13:"algo-unsupported", + 14:"bad-seq-num", 15:"challenge-failure", + 16:"timeout", 17:"AP-full",18:"rate-unsupported" } + +class Dot11Beacon(Packet): + name = "802.11 Beacon" + fields_desc = [ LELongField("timestamp", 0), + LEShortField("beacon_interval", 0x0064), + FlagsField("cap", 0, 16, capability_list) ] + + +class Dot11Elt(Packet): + name = "802.11 Information Element" + fields_desc = [ ByteEnumField("ID", 0, {0:"SSID", 1:"Rates", 2: "FHset", 3:"DSset", 4:"CFset", 5:"TIM", 6:"IBSSset", 16:"challenge", + 42:"ERPinfo", 46:"QoS Capability", 47:"ERPinfo", 48:"RSNinfo", 50:"ESRates",221:"vendor",68:"reserved"}), + FieldLenField("len", None, "info", "B"), + StrLenField("info", "", length_from=lambda x:x.len) ] + def mysummary(self): + if self.ID == 0: + return "SSID=%s"%repr(self.info),[Dot11] + else: + return "" + +class Dot11ATIM(Packet): + name = "802.11 ATIM" + +class Dot11Disas(Packet): + name = "802.11 Disassociation" + fields_desc = [ LEShortEnumField("reason", 1, reason_code) ] + +class Dot11AssoReq(Packet): + name = "802.11 Association Request" + fields_desc = [ FlagsField("cap", 0, 16, capability_list), + LEShortField("listen_interval", 0x00c8) ] + + +class Dot11AssoResp(Packet): + name = "802.11 Association Response" + fields_desc = [ FlagsField("cap", 0, 16, capability_list), + LEShortField("status", 0), + LEShortField("AID", 0) ] + +class Dot11ReassoReq(Packet): + name = "802.11 Reassociation Request" + fields_desc = [ FlagsField("cap", 0, 16, capability_list), + LEShortField("listen_interval", 0x00c8), + MACField("current_AP", ETHER_ANY) ] + + +class Dot11ReassoResp(Dot11AssoResp): + name = "802.11 Reassociation Response" + +class Dot11ProbeReq(Packet): + name = "802.11 Probe Request" + +class Dot11ProbeResp(Packet): + name = "802.11 Probe Response" + fields_desc = [ LELongField("timestamp", 0), + LEShortField("beacon_interval", 0x0064), + FlagsField("cap", 0, 16, capability_list) ] + +class Dot11Auth(Packet): + name = "802.11 Authentication" + fields_desc = [ LEShortEnumField("algo", 0, ["open", "sharedkey"]), + LEShortField("seqnum", 0), + LEShortEnumField("status", 0, status_code) ] + def answers(self, other): + if self.seqnum == other.seqnum+1: + return 1 + return 0 + +class Dot11Deauth(Packet): + name = "802.11 Deauthentication" + fields_desc = [ LEShortEnumField("reason", 1, reason_code) ] + + + +class Dot11WEP(Packet): + name = "802.11 WEP packet" + fields_desc = [ StrFixedLenField("iv", "\0\0\0", 3), + ByteField("keyid", 0), + StrField("wepdata",None,remain=4), + IntField("icv",None) ] + + def post_dissect(self, s): +# self.icv, = struct.unpack("!I",self.wepdata[-4:]) +# self.wepdata = self.wepdata[:-4] + self.decrypt() + + def build_payload(self): + if self.wepdata is None: + return Packet.build_payload(self) + return "" + + def post_build(self, p, pay): + if self.wepdata is None: + key = conf.wepkey + if key: + if self.icv is None: + pay += struct.pack(" %IP.dst%:%TCP.dport%") + + def send_reply(self, reply): + sendp(reply, iface=self.ifto, **self.optsend) + + def sniff(self): + sniff(iface=self.iffrom, **self.optsniff) + + + +plst=[] +def get_toDS(): + global plst + while 1: + p,=sniff(iface="eth1",count=1) + if not isinstance(p,Dot11): + continue + if p.FCfield & 1: + plst.append(p) + print "." + + +# if not ifto.endswith("ap"): +# print "iwpriv %s hostapd 1" % ifto +# os.system("iwpriv %s hostapd 1" % ifto) +# ifto += "ap" +# +# os.system("iwconfig %s mode monitor" % iffrom) +# + +def airpwn(iffrom, ifto, replace, pattern="", ignorepattern=""): + """Before using this, initialize "iffrom" and "ifto" interfaces: +iwconfig iffrom mode monitor +iwpriv orig_ifto hostapd 1 +ifconfig ifto up +note: if ifto=wlan0ap then orig_ifto=wlan0 +note: ifto and iffrom must be set on the same channel +ex: +ifconfig eth1 up +iwconfig eth1 mode monitor +iwconfig eth1 channel 11 +iwpriv wlan0 hostapd 1 +ifconfig wlan0ap up +iwconfig wlan0 channel 11 +iwconfig wlan0 essid dontexist +iwconfig wlan0 mode managed +""" + + ptrn = re.compile(pattern) + iptrn = re.compile(ignorepattern) + def do_airpwn(p, ifto=ifto, replace=replace, ptrn=ptrn, iptrn=iptrn): + if not isinstance(p,Dot11): + return + if not p.FCfield & 1: + return + if not p.haslayer(TCP): + return + ip = p.getlayer(IP) + tcp = p.getlayer(TCP) + pay = str(tcp.payload) +# print "got tcp" + if not ptrn.match(pay): + return +# print "match 1" + if iptrn.match(pay): + return +# print "match 2" + del(p.payload.payload.payload) + p.FCfield="from-DS" + p.addr1,p.addr2 = p.addr2,p.addr1 + q = p.copy() + p /= IP(src=ip.dst,dst=ip.src) + p /= TCP(sport=tcp.dport, dport=tcp.sport, + seq=tcp.ack, ack=tcp.seq+len(pay), + flags="PA") + q = p.copy() + p /= replace + q.ID += 1 + q.getlayer(TCP).flags="RA" + q.getlayer(TCP).seq+=len(replace) + + sendp([p,q], iface=ifto, verbose=0) +# print "send",repr(p) +# print "send",repr(q) + print p.sprintf("Sent %IP.src%:%IP.sport% > %IP.dst%:%TCP.dport%") + + sniff(iface=iffrom,prn=do_airpwn) + + + +conf.stats_dot11_protocols += [Dot11WEP, Dot11Beacon, ] + + + + + +class Dot11PacketList(PacketList): + def __init__(self, res=None, name="Dot11List", stats=None): + if stats is None: + stats = conf.stats_dot11_protocols + + PacketList.__init__(self, res, name, stats) + def toEthernet(self): + data = map(lambda x:x.getlayer(Dot11), filter(lambda x : x.haslayer(Dot11) and x.type == 2, self.res)) + r2 = [] + for p in data: + q = p.copy() + q.unwep() + r2.append(Ether()/q.payload.payload.payload) #Dot11/LLC/SNAP/IP + return PacketList(r2,name="Ether from %s"%self.listname) + + diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/gprs.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/gprs.py new file mode 100644 index 00000000..31a931fe --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/gprs.py @@ -0,0 +1,21 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +GPRS (General Packet Radio Service) for mobile data communication. +""" + +from scapy.fields import * +from scapy.packet import * +from scapy.layers.inet import IP + +class GPRS(Packet): + name = "GPRSdummy" + fields_desc = [ + StrStopField("dummy","","\x65\x00\x00",1) + ] + + +bind_layers( GPRS, IP, ) diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/hsrp.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/hsrp.py new file mode 100644 index 00000000..7193b97e --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/hsrp.py @@ -0,0 +1,79 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +############################################################################# +## ## +## hsrp.py --- HSRP protocol support for Scapy ## +## ## +## Copyright (C) 2010 Mathieu RENARD mathieu.renard(at)gmail.com ## +## ## +## This program is free software; you can redistribute it and/or modify it ## +## under the terms of the GNU General Public License version 2 as ## +## published by the Free Software Foundation; version 2. ## +## ## +## This program is distributed in the hope that it will be useful, but ## +## WITHOUT ANY WARRANTY; without even the implied warranty of ## +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## +## General Public License for more details. ## +## ## +############################################################################# +## HSRP Version 1 +## Ref. RFC 2281 +## HSRP Version 2 +## Ref. http://www.smartnetworks.jp/2006/02/hsrp_8_hsrp_version_2.html +## +## $Log: hsrp.py,v $ +## Revision 0.2 2011/05/01 15:23:34 mrenard +## Cleanup code + +""" +HSRP (Hot Standby Router Protocol): proprietary redundancy protocol for Cisco routers. +""" + +from scapy.fields import * +from scapy.packet import * +from scapy.layers.inet import UDP + + +class HSRP(Packet): + name = "HSRP" + fields_desc = [ + ByteField("version", 0), + ByteEnumField("opcode", 0, {0: "Hello", 1: "Coup", 2: "Resign", 3: "Advertise"}), + ByteEnumField("state", 16, {0: "Initial", 1: "Learn", 2: "Listen", 4: "Speak", 8: "Standby", 16: "Active"}), + ByteField("hellotime", 3), + ByteField("holdtime", 10), + ByteField("priority", 120), + ByteField("group", 1), + ByteField("reserved", 0), + StrFixedLenField("auth", "cisco" + "\00" * 3, 8), + IPField("virtualIP", "192.168.1.1")] + + def guess_payload_class(self, payload): + if self.underlayer.len > 28: + return HSRPmd5 + else: + return Packet.guess_payload_class(self, payload) + + +class HSRPmd5(Packet): + name = "HSRP MD5 Authentication" + fields_desc = [ + ByteEnumField("type", 4, {4: "MD5 authentication"}), + ByteField("len", None), + ByteEnumField("algo", 0, {1: "MD5"}), + ByteField("padding", 0x00), + XShortField("flags", 0x00), + IPField("sourceip", None), + XIntField("keyid", 0x00), + StrFixedLenField("authdigest", "\00" * 16, 16)] + + def post_build(self, p, pay): + if self.len is None and pay: + l = len(pay) + p = p[:1] + hex(l)[30:] + p[30:] + return p + +bind_layers(UDP, HSRP, dport=1985, sport=1985) diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/inet.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/inet.py new file mode 100644 index 00000000..34b5e7be --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/inet.py @@ -0,0 +1,1556 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +IPv4 (Internet Protocol v4). +""" + +import os,time,struct,re,socket,new +from select import select +from collections import defaultdict +from scapy.utils import checksum +from scapy.layers.l2 import * +from scapy.config import conf +from scapy.fields import * +from scapy.packet import * +from scapy.volatile import * +from scapy.sendrecv import sr,sr1,srp1 +from scapy.plist import PacketList,SndRcvList +from scapy.automaton import Automaton,ATMT + +import scapy.as_resolvers + + +#################### +## IP Tools class ## +#################### + +class IPTools: + """Add more powers to a class that have a "src" attribute.""" + def whois(self): + os.system("whois %s" % self.src) + def ottl(self): + t = [32,64,128,255]+[self.ttl] + t.sort() + return t[t.index(self.ttl)+1] + def hops(self): + return self.ottl()-self.ttl-1 + + +_ip_options_names = { 0: "end_of_list", + 1: "nop", + 2: "security", + 3: "loose_source_route", + 4: "timestamp", + 5: "extended_security", + 6: "commercial_security", + 7: "record_route", + 8: "stream_id", + 9: "strict_source_route", + 10: "experimental_measurement", + 11: "mtu_probe", + 12: "mtu_reply", + 13: "flow_control", + 14: "access_control", + 15: "encode", + 16: "imi_traffic_descriptor", + 17: "extended_IP", + 18: "traceroute", + 19: "address_extension", + 20: "router_alert", + 21: "selective_directed_broadcast_mode", + 23: "dynamic_packet_state", + 24: "upstream_multicast_packet", + 25: "quick_start", + 30: "rfc4727_experiment", + } + + +class _IPOption_HDR(Packet): + fields_desc = [ BitField("copy_flag",0, 1), + BitEnumField("optclass",0,2,{0:"control",2:"debug"}), + BitEnumField("option",0,5, _ip_options_names) ] + +class IPOption(Packet): + name = "IP Option" + fields_desc = [ _IPOption_HDR, + FieldLenField("length", None, fmt="B", # Only option 0 and 1 have no length and value + length_of="value", adjust=lambda pkt,l:l+2), + StrLenField("value", "",length_from=lambda pkt:pkt.length-2) ] + + def extract_padding(self, p): + return "",p + + registered_ip_options = {} + @classmethod + def register_variant(cls): + cls.registered_ip_options[cls.option.default] = cls + @classmethod + def dispatch_hook(cls, pkt=None, *args, **kargs): + if pkt: + opt = ord(pkt[0])&0x1f + if opt in cls.registered_ip_options: + return cls.registered_ip_options[opt] + return cls + +class IPOption_EOL(IPOption): + name = "IP Option End of Options List" + option = 0 + fields_desc = [ _IPOption_HDR ] + + +class IPOption_NOP(IPOption): + name = "IP Option No Operation" + option=1 + fields_desc = [ _IPOption_HDR ] + +class IPOption_Security(IPOption): + name = "IP Option Security" + copy_flag = 1 + option = 2 + fields_desc = [ _IPOption_HDR, + ByteField("length", 11), + ShortField("security",0), + ShortField("compartment",0), + ShortField("handling_restrictions",0), + StrFixedLenField("transmission_control_code","xxx",3), + ] + +class IPOption_LSRR(IPOption): + name = "IP Option Loose Source and Record Route" + copy_flag = 1 + option = 3 + fields_desc = [ _IPOption_HDR, + FieldLenField("length", None, fmt="B", + length_of="routers", adjust=lambda pkt,l:l+3), + ByteField("pointer",4), # 4 is first IP + FieldListField("routers",[],IPField("","0.0.0.0"), + length_from=lambda pkt:pkt.length-3) + ] + def get_current_router(self): + return self.routers[self.pointer/4-1] + +class IPOption_RR(IPOption_LSRR): + name = "IP Option Record Route" + option = 7 + +class IPOption_SSRR(IPOption_LSRR): + name = "IP Option Strict Source and Record Route" + option = 9 + +class IPOption_Stream_Id(IPOption): + name = "IP Option Stream ID" + option = 8 + fields_desc = [ _IPOption_HDR, + ByteField("length", 4), + ShortField("security",0), ] + +class IPOption_MTU_Probe(IPOption): + name = "IP Option MTU Probe" + option = 11 + fields_desc = [ _IPOption_HDR, + ByteField("length", 4), + ShortField("mtu",0), ] + +class IPOption_MTU_Reply(IPOption_MTU_Probe): + name = "IP Option MTU Reply" + option = 12 + +class IPOption_Traceroute(IPOption): + name = "IP Option Traceroute" + copy_flag = 1 + option = 18 + fields_desc = [ _IPOption_HDR, + ByteField("length", 12), + ShortField("id",0), + ShortField("outbound_hops",0), + ShortField("return_hops",0), + IPField("originator_ip","0.0.0.0") ] + +class IPOption_Address_Extension(IPOption): + name = "IP Option Address Extension" + copy_flag = 1 + option = 19 + fields_desc = [ _IPOption_HDR, + ByteField("length", 10), + IPField("src_ext","0.0.0.0"), + IPField("dst_ext","0.0.0.0") ] + +class IPOption_Router_Alert(IPOption): + name = "IP Option Router Alert" + copy_flag = 1 + option = 20 + fields_desc = [ _IPOption_HDR, + ByteField("length", 4), + ShortEnumField("alert",0, {0:"router_shall_examine_packet"}), ] + + +class IPOption_SDBM(IPOption): + name = "IP Option Selective Directed Broadcast Mode" + copy_flag = 1 + option = 21 + fields_desc = [ _IPOption_HDR, + FieldLenField("length", None, fmt="B", + length_of="addresses", adjust=lambda pkt,l:l+2), + FieldListField("addresses",[],IPField("","0.0.0.0"), + length_from=lambda pkt:pkt.length-2) + ] + + + +TCPOptions = ( + { 0 : ("EOL",None), + 1 : ("NOP",None), + 2 : ("MSS","!H"), + 3 : ("WScale","!B"), + 4 : ("SAckOK",None), + 5 : ("SAck","!"), + 8 : ("Timestamp","!II"), + 14 : ("AltChkSum","!BH"), + 15 : ("AltChkSumOpt",None), + 25 : ("Mood","!p") + }, + { "EOL":0, + "NOP":1, + "MSS":2, + "WScale":3, + "SAckOK":4, + "SAck":5, + "Timestamp":8, + "AltChkSum":14, + "AltChkSumOpt":15, + "Mood":25 + } ) + +class TCPOptionsField(StrField): + islist=1 + def getfield(self, pkt, s): + opsz = (pkt.dataofs-5)*4 + if opsz < 0: + warning("bad dataofs (%i). Assuming dataofs=5"%pkt.dataofs) + opsz = 0 + return s[opsz:],self.m2i(pkt,s[:opsz]) + def m2i(self, pkt, x): + opt = [] + while x: + onum = ord(x[0]) + if onum == 0: + opt.append(("EOL",None)) + x=x[1:] + break + if onum == 1: + opt.append(("NOP",None)) + x=x[1:] + continue + olen = ord(x[1]) + if olen < 2: + warning("Malformed TCP option (announced length is %i)" % olen) + olen = 2 + oval = x[2:olen] + if TCPOptions[0].has_key(onum): + oname, ofmt = TCPOptions[0][onum] + if onum == 5: #SAck + ofmt += "%iI" % (len(oval)/4) + if ofmt and struct.calcsize(ofmt) == len(oval): + oval = struct.unpack(ofmt, oval) + if len(oval) == 1: + oval = oval[0] + opt.append((oname, oval)) + else: + opt.append((onum, oval)) + x = x[olen:] + return opt + + def i2m(self, pkt, x): + opt = "" + for oname,oval in x: + if type(oname) is str: + if oname == "NOP": + opt += "\x01" + continue + elif oname == "EOL": + opt += "\x00" + continue + elif TCPOptions[1].has_key(oname): + onum = TCPOptions[1][oname] + ofmt = TCPOptions[0][onum][1] + if onum == 5: #SAck + ofmt += "%iI" % len(oval) + if ofmt is not None and (type(oval) is not str or "s" in ofmt): + if type(oval) is not tuple: + oval = (oval,) + oval = struct.pack(ofmt, *oval) + else: + warning("option [%s] unknown. Skipped."%oname) + continue + else: + onum = oname + if type(oval) is not str: + warning("option [%i] is not string."%onum) + continue + opt += chr(onum)+chr(2+len(oval))+oval + return opt+"\x00"*(3-((len(opt)+3)%4)) + def randval(self): + return [] # XXX + + +class ICMPTimeStampField(IntField): + re_hmsm = re.compile("([0-2]?[0-9])[Hh:](([0-5]?[0-9])([Mm:]([0-5]?[0-9])([sS:.]([0-9]{0,3}))?)?)?$") + def i2repr(self, pkt, val): + if val is None: + return "--" + else: + sec, milli = divmod(val, 1000) + min, sec = divmod(sec, 60) + hour, min = divmod(min, 60) + return "%d:%d:%d.%d" %(hour, min, sec, int(milli)) + def any2i(self, pkt, val): + if type(val) is str: + hmsms = self.re_hmsm.match(val) + if hmsms: + h,_,m,_,s,_,ms = hmsms = hmsms.groups() + ms = int(((ms or "")+"000")[:3]) + val = ((int(h)*60+int(m or 0))*60+int(s or 0))*1000+ms + else: + val = 0 + elif val is None: + val = int((time.time()%(24*60*60))*1000) + return val + + +class IP(Packet, IPTools): + name = "IP" + fields_desc = [ BitField("version" , 4 , 4), + BitField("ihl", None, 4), + XByteField("tos", 0), + ShortField("len", None), + ShortField("id", 1), + FlagsField("flags", 0, 3, ["MF","DF","evil"]), + BitField("frag", 0, 13), + ByteField("ttl", 64), + ByteEnumField("proto", 0, IP_PROTOS), + XShortField("chksum", None), + #IPField("src", "127.0.0.1"), + #Emph(SourceIPField("src","dst")), + Emph(IPField("src", "16.0.0.1")), + Emph(IPField("dst", "48.0.0.1")), + PacketListField("options", [], IPOption, length_from=lambda p:p.ihl*4-20) ] + def post_build(self, p, pay): + ihl = self.ihl + p += "\0"*((-len(p))%4) # pad IP options if needed + if ihl is None: + ihl = len(p)/4 + p = chr(((self.version&0xf)<<4) | ihl&0x0f)+p[1:] + if self.len is None: + l = len(p)+len(pay) + p = p[:2]+struct.pack("!H", l)+p[4:] + if self.chksum is None: + ck = checksum(p) + p = p[:10]+chr(ck>>8)+chr(ck&0xff)+p[12:] + return p+pay + + def extract_padding(self, s): + l = self.len - (self.ihl << 2) + return s[:l],s[l:] + + def send(self, s, slp=0): + for p in self: + try: + s.sendto(str(p), (p.dst,0)) + except socket.error, msg: + log_runtime.error(msg) + if slp: + time.sleep(slp) + def route(self): + dst = self.dst + if isinstance(dst,Gen): + dst = iter(dst).next() + return conf.route.route(dst) + def hashret(self): + if ( (self.proto == socket.IPPROTO_ICMP) + and (isinstance(self.payload, ICMP)) + and (self.payload.type in [3,4,5,11,12]) ): + return self.payload.payload.hashret() + else: + if conf.checkIPsrc and conf.checkIPaddr: + return strxor(inet_aton(self.src),inet_aton(self.dst))+struct.pack("B",self.proto)+self.payload.hashret() + else: + return struct.pack("B", self.proto)+self.payload.hashret() + def answers(self, other): + if not isinstance(other,IP): + return 0 + if conf.checkIPaddr and (self.dst != other.src): + return 0 + if ( (self.proto == socket.IPPROTO_ICMP) and + (isinstance(self.payload, ICMP)) and + (self.payload.type in [3,4,5,11,12]) ): + # ICMP error message + return self.payload.payload.answers(other) + + else: + if ( (conf.checkIPaddr and (self.src != other.dst)) or + (self.proto != other.proto) ): + return 0 + return self.payload.answers(other.payload) + def mysummary(self): + s = self.sprintf("%IP.src% > %IP.dst% %IP.proto%") + if self.frag: + s += " frag:%i" % self.frag + return s + + def fragment(self, fragsize=1480): + """Fragment IP datagrams""" + fragsize = (fragsize+7)/8*8 + lst = [] + fnb = 0 + fl = self + while fl.underlayer is not None: + fnb += 1 + fl = fl.underlayer + + for p in fl: + s = str(p[fnb].payload) + nb = (len(s)+fragsize-1)/fragsize + for i in range(nb): + q = p.copy() + del(q[fnb].payload) + del(q[fnb].chksum) + del(q[fnb].len) + if i == nb-1: + q[IP].flags &= ~1 + else: + q[IP].flags |= 1 + q[IP].frag = i*fragsize/8 + r = conf.raw_layer(load=s[i*fragsize:(i+1)*fragsize]) + r.overload_fields = p[IP].payload.overload_fields.copy() + q.add_payload(r) + lst.append(q) + return lst + + +class TCP(Packet): + name = "TCP" + fields_desc = [ ShortEnumField("sport", 20, TCP_SERVICES), + ShortEnumField("dport", 80, TCP_SERVICES), + IntField("seq", 0), + IntField("ack", 0), + BitField("dataofs", None, 4), + BitField("reserved", 0, 4), + FlagsField("flags", 0x2, 8, "FSRPAUEC"), + ShortField("window", 8192), + XShortField("chksum", None), + ShortField("urgptr", 0), + TCPOptionsField("options", {}) ] + def post_build(self, p, pay): + p += pay + dataofs = self.dataofs + if dataofs is None: + dataofs = 5+((len(self.get_field("options").i2m(self,self.options))+3)/4) + p = p[:12]+chr((dataofs << 4) | ord(p[12])&0x0f)+p[13:] + if self.chksum is None: + if isinstance(self.underlayer, IP): + if self.underlayer.len is not None: + ln = self.underlayer.len-20 + else: + ln = len(p) + psdhdr = struct.pack("!4s4sHH", + inet_aton(self.underlayer.src), + inet_aton(self.underlayer.dst), + self.underlayer.proto, + ln) + ck=checksum(psdhdr+p) + p = p[:16]+struct.pack("!H", ck)+p[18:] + elif conf.ipv6_enabled and isinstance(self.underlayer, scapy.layers.inet6.IPv6) or isinstance(self.underlayer, scapy.layers.inet6._IPv6ExtHdr): + ck = scapy.layers.inet6.in6_chksum(socket.IPPROTO_TCP, self.underlayer, p) + p = p[:16]+struct.pack("!H", ck)+p[18:] + else: + warning("No IP underlayer to compute checksum. Leaving null.") + return p + def hashret(self): + if conf.checkIPsrc: + return struct.pack("H",self.sport ^ self.dport)+self.payload.hashret() + else: + return self.payload.hashret() + def answers(self, other): + if not isinstance(other, TCP): + return 0 + if conf.checkIPsrc: + if not ((self.sport == other.dport) and + (self.dport == other.sport)): + return 0 + if (abs(other.seq-self.ack) > 2+len(other.payload)): + return 0 + return 1 + def mysummary(self): + if isinstance(self.underlayer, IP): + return self.underlayer.sprintf("TCP %IP.src%:%TCP.sport% > %IP.dst%:%TCP.dport% %TCP.flags%") + elif conf.ipv6_enabled and isinstance(self.underlayer, scapy.layers.inet6.IPv6): + return self.underlayer.sprintf("TCP %IPv6.src%:%TCP.sport% > %IPv6.dst%:%TCP.dport% %TCP.flags%") + else: + return self.sprintf("TCP %TCP.sport% > %TCP.dport% %TCP.flags%") + +class UDP(Packet): + name = "UDP" + fields_desc = [ ShortEnumField("sport", 53, UDP_SERVICES), + ShortEnumField("dport", 53, UDP_SERVICES), + ShortField("len", None), + XShortField("chksum", None), ] + def post_build(self, p, pay): + p += pay + l = self.len + if l is None: + l = len(p) + p = p[:4]+struct.pack("!H",l)+p[6:] + if self.chksum is None: + if isinstance(self.underlayer, IP): + if self.underlayer.len is not None: + ln = self.underlayer.len-20 + else: + ln = len(p) + psdhdr = struct.pack("!4s4sHH", + inet_aton(self.underlayer.src), + inet_aton(self.underlayer.dst), + self.underlayer.proto, + ln) + ck=checksum(psdhdr+p) + p = p[:6]+struct.pack("!H", ck)+p[8:] + elif isinstance(self.underlayer, scapy.layers.inet6.IPv6) or isinstance(self.underlayer, scapy.layers.inet6._IPv6ExtHdr): + ck = scapy.layers.inet6.in6_chksum(socket.IPPROTO_UDP, self.underlayer, p) + p = p[:6]+struct.pack("!H", ck)+p[8:] + else: + warning("No IP underlayer to compute checksum. Leaving null.") + return p + def extract_padding(self, s): + l = self.len - 8 + return s[:l],s[l:] + def hashret(self): + return self.payload.hashret() + def answers(self, other): + if not isinstance(other, UDP): + return 0 + if conf.checkIPsrc: + if self.dport != other.sport: + return 0 + return self.payload.answers(other.payload) + def mysummary(self): + if isinstance(self.underlayer, IP): + return self.underlayer.sprintf("UDP %IP.src%:%UDP.sport% > %IP.dst%:%UDP.dport%") + elif isinstance(self.underlayer, scapy.layers.inet6.IPv6): + return self.underlayer.sprintf("UDP %IPv6.src%:%UDP.sport% > %IPv6.dst%:%UDP.dport%") + else: + return self.sprintf("UDP %UDP.sport% > %UDP.dport%") + +icmptypes = { 0 : "echo-reply", + 3 : "dest-unreach", + 4 : "source-quench", + 5 : "redirect", + 8 : "echo-request", + 9 : "router-advertisement", + 10 : "router-solicitation", + 11 : "time-exceeded", + 12 : "parameter-problem", + 13 : "timestamp-request", + 14 : "timestamp-reply", + 15 : "information-request", + 16 : "information-response", + 17 : "address-mask-request", + 18 : "address-mask-reply" } + +icmpcodes = { 3 : { 0 : "network-unreachable", + 1 : "host-unreachable", + 2 : "protocol-unreachable", + 3 : "port-unreachable", + 4 : "fragmentation-needed", + 5 : "source-route-failed", + 6 : "network-unknown", + 7 : "host-unknown", + 9 : "network-prohibited", + 10 : "host-prohibited", + 11 : "TOS-network-unreachable", + 12 : "TOS-host-unreachable", + 13 : "communication-prohibited", + 14 : "host-precedence-violation", + 15 : "precedence-cutoff", }, + 5 : { 0 : "network-redirect", + 1 : "host-redirect", + 2 : "TOS-network-redirect", + 3 : "TOS-host-redirect", }, + 11 : { 0 : "ttl-zero-during-transit", + 1 : "ttl-zero-during-reassembly", }, + 12 : { 0 : "ip-header-bad", + 1 : "required-option-missing", }, } + + + + +class ICMP(Packet): + name = "ICMP" + fields_desc = [ ByteEnumField("type",8, icmptypes), + MultiEnumField("code",0, icmpcodes, depends_on=lambda pkt:pkt.type,fmt="B"), + XShortField("chksum", None), + ConditionalField(XShortField("id",0), lambda pkt:pkt.type in [0,8,13,14,15,16,17,18]), + ConditionalField(XShortField("seq",0), lambda pkt:pkt.type in [0,8,13,14,15,16,17,18]), + ConditionalField(ICMPTimeStampField("ts_ori", None), lambda pkt:pkt.type in [13,14]), + ConditionalField(ICMPTimeStampField("ts_rx", None), lambda pkt:pkt.type in [13,14]), + ConditionalField(ICMPTimeStampField("ts_tx", None), lambda pkt:pkt.type in [13,14]), + ConditionalField(IPField("gw","0.0.0.0"), lambda pkt:pkt.type==5), + ConditionalField(ByteField("ptr",0), lambda pkt:pkt.type==12), + ConditionalField(X3BytesField("reserved",0), lambda pkt:pkt.type==12), + ConditionalField(IPField("addr_mask","0.0.0.0"), lambda pkt:pkt.type in [17,18]), + ConditionalField(IntField("unused",0), lambda pkt:pkt.type not in [0,5,8,12,13,14,15,16,17,18]), + + ] + def post_build(self, p, pay): + p += pay + if self.chksum is None: + ck = checksum(p) + p = p[:2]+chr(ck>>8)+chr(ck&0xff)+p[4:] + return p + + def hashret(self): + if self.type in [0,8,13,14,15,16,17,18]: + return struct.pack("HH",self.id,self.seq)+self.payload.hashret() + return self.payload.hashret() + def answers(self, other): + if not isinstance(other,ICMP): + return 0 + if ( (other.type,self.type) in [(8,0),(13,14),(15,16),(17,18)] and + self.id == other.id and + self.seq == other.seq ): + return 1 + return 0 + + def guess_payload_class(self, payload): + if self.type in [3,4,5,11,12]: + return IPerror + else: + return None + def mysummary(self): + if isinstance(self.underlayer, IP): + return self.underlayer.sprintf("ICMP %IP.src% > %IP.dst% %ICMP.type% %ICMP.code%") + else: + return self.sprintf("ICMP %ICMP.type% %ICMP.code%") + + + + + +class IPerror(IP): + name = "IP in ICMP" + def answers(self, other): + if not isinstance(other, IP): + return 0 + if not ( ((conf.checkIPsrc == 0) or (self.dst == other.dst)) and + (self.src == other.src) and + ( ((conf.checkIPID == 0) + or (self.id == other.id) + or (conf.checkIPID == 1 and self.id == socket.htons(other.id)))) and + (self.proto == other.proto) ): + return 0 + return self.payload.answers(other.payload) + def mysummary(self): + return Packet.mysummary(self) + + +class TCPerror(TCP): + name = "TCP in ICMP" + def answers(self, other): + if not isinstance(other, TCP): + return 0 + if conf.checkIPsrc: + if not ((self.sport == other.sport) and + (self.dport == other.dport)): + return 0 + if conf.check_TCPerror_seqack: + if self.seq is not None: + if self.seq != other.seq: + return 0 + if self.ack is not None: + if self.ack != other.ack: + return 0 + return 1 + def mysummary(self): + return Packet.mysummary(self) + + +class UDPerror(UDP): + name = "UDP in ICMP" + def answers(self, other): + if not isinstance(other, UDP): + return 0 + if conf.checkIPsrc: + if not ((self.sport == other.sport) and + (self.dport == other.dport)): + return 0 + return 1 + def mysummary(self): + return Packet.mysummary(self) + + + +class ICMPerror(ICMP): + name = "ICMP in ICMP" + def answers(self, other): + if not isinstance(other,ICMP): + return 0 + if not ((self.type == other.type) and + (self.code == other.code)): + return 0 + if self.code in [0,8,13,14,17,18]: + if (self.id == other.id and + self.seq == other.seq): + return 1 + else: + return 0 + else: + return 1 + def mysummary(self): + return Packet.mysummary(self) + +bind_layers( Ether, IP, type=2048) +bind_layers( CookedLinux, IP, proto=2048) +bind_layers( GRE, IP, proto=2048) +bind_layers( SNAP, IP, code=2048) +bind_layers( IPerror, IPerror, frag=0, proto=4) +bind_layers( IPerror, ICMPerror, frag=0, proto=1) +bind_layers( IPerror, TCPerror, frag=0, proto=6) +bind_layers( IPerror, UDPerror, frag=0, proto=17) +bind_layers( IP, IP, frag=0, proto=4) +bind_layers( IP, ICMP, frag=0, proto=1) +bind_layers( IP, TCP, frag=0, proto=6) +bind_layers( IP, UDP, frag=0, proto=17) +bind_layers( IP, GRE, frag=0, proto=47) + +conf.l2types.register(101, IP) +conf.l2types.register_num2layer(12, IP) + +conf.l3types.register(ETH_P_IP, IP) +conf.l3types.register_num2layer(ETH_P_ALL, IP) + + +conf.neighbor.register_l3(Ether, IP, lambda l2,l3: getmacbyip(l3.dst)) +conf.neighbor.register_l3(Dot3, IP, lambda l2,l3: getmacbyip(l3.dst)) + + +################### +## Fragmentation ## +################### + +@conf.commands.register +def fragment(pkt, fragsize=1480): + """Fragment a big IP datagram""" + fragsize = (fragsize+7)/8*8 + lst = [] + for p in pkt: + s = str(p[IP].payload) + nb = (len(s)+fragsize-1)/fragsize + for i in range(nb): + q = p.copy() + del(q[IP].payload) + del(q[IP].chksum) + del(q[IP].len) + if i == nb-1: + q[IP].flags &= ~1 + else: + q[IP].flags |= 1 + q[IP].frag = i*fragsize/8 + r = conf.raw_layer(load=s[i*fragsize:(i+1)*fragsize]) + r.overload_fields = p[IP].payload.overload_fields.copy() + q.add_payload(r) + lst.append(q) + return lst + +def overlap_frag(p, overlap, fragsize=8, overlap_fragsize=None): + if overlap_fragsize is None: + overlap_fragsize = fragsize + q = p.copy() + del(q[IP].payload) + q[IP].add_payload(overlap) + + qfrag = fragment(q, overlap_fragsize) + qfrag[-1][IP].flags |= 1 + return qfrag+fragment(p, fragsize) + +@conf.commands.register +def defrag(plist): + """defrag(plist) -> ([not fragmented], [defragmented], + [ [bad fragments], [bad fragments], ... ])""" + frags = defaultdict(PacketList) + nofrag = PacketList() + for p in plist: + ip = p[IP] + if IP not in p: + nofrag.append(p) + continue + if ip.frag == 0 and ip.flags & 1 == 0: + nofrag.append(p) + continue + uniq = (ip.id,ip.src,ip.dst,ip.proto) + frags[uniq].append(p) + defrag = [] + missfrag = [] + for lst in frags.itervalues(): + lst.sort(key=lambda x: x.frag) + p = lst[0] + lastp = lst[-1] + if p.frag > 0 or lastp.flags & 1 != 0: # first or last fragment missing + missfrag.append(lst) + continue + p = p.copy() + if conf.padding_layer in p: + del(p[conf.padding_layer].underlayer.payload) + ip = p[IP] + if ip.len is None or ip.ihl is None: + clen = len(ip.payload) + else: + clen = ip.len - (ip.ihl<<2) + txt = conf.raw_layer() + for q in lst[1:]: + if clen != q.frag<<3: # Wrong fragmentation offset + if clen > q.frag<<3: + warning("Fragment overlap (%i > %i) %r || %r || %r" % (clen, q.frag<<3, p,txt,q)) + missfrag.append(lst) + break + if q[IP].len is None or q[IP].ihl is None: + clen += len(q[IP].payload) + else: + clen += q[IP].len - (q[IP].ihl<<2) + if conf.padding_layer in q: + del(q[conf.padding_layer].underlayer.payload) + txt.add_payload(q[IP].payload.copy()) + else: + ip.flags &= ~1 # !MF + del(ip.chksum) + del(ip.len) + p = p/txt + defrag.append(p) + defrag2=PacketList() + for p in defrag: + defrag2.append(p.__class__(str(p))) + return nofrag,defrag2,missfrag + +@conf.commands.register +def defragment(plist): + """defrag(plist) -> plist defragmented as much as possible """ + frags = defaultdict(lambda:[]) + final = [] + + pos = 0 + for p in plist: + p._defrag_pos = pos + pos += 1 + if IP in p: + ip = p[IP] + if ip.frag != 0 or ip.flags & 1: + ip = p[IP] + uniq = (ip.id,ip.src,ip.dst,ip.proto) + frags[uniq].append(p) + continue + final.append(p) + + defrag = [] + missfrag = [] + for lst in frags.itervalues(): + lst.sort(key=lambda x: x.frag) + p = lst[0] + lastp = lst[-1] + if p.frag > 0 or lastp.flags & 1 != 0: # first or last fragment missing + missfrag += lst + continue + p = p.copy() + if conf.padding_layer in p: + del(p[conf.padding_layer].underlayer.payload) + ip = p[IP] + if ip.len is None or ip.ihl is None: + clen = len(ip.payload) + else: + clen = ip.len - (ip.ihl<<2) + txt = conf.raw_layer() + for q in lst[1:]: + if clen != q.frag<<3: # Wrong fragmentation offset + if clen > q.frag<<3: + warning("Fragment overlap (%i > %i) %r || %r || %r" % (clen, q.frag<<3, p,txt,q)) + missfrag += lst + break + if q[IP].len is None or q[IP].ihl is None: + clen += len(q[IP].payload) + else: + clen += q[IP].len - (q[IP].ihl<<2) + if conf.padding_layer in q: + del(q[conf.padding_layer].underlayer.payload) + txt.add_payload(q[IP].payload.copy()) + else: + ip.flags &= ~1 # !MF + del(ip.chksum) + del(ip.len) + p = p/txt + p._defrag_pos = max(x._defrag_pos for x in lst) + defrag.append(p) + defrag2=[] + for p in defrag: + q = p.__class__(str(p)) + q._defrag_pos = p._defrag_pos + defrag2.append(q) + final += defrag2 + final += missfrag + final.sort(key=lambda x: x._defrag_pos) + for p in final: + del(p._defrag_pos) + + if hasattr(plist, "listname"): + name = "Defragmented %s" % plist.listname + else: + name = "Defragmented" + + return PacketList(final, name=name) + + + +### Add timeskew_graph() method to PacketList +def _packetlist_timeskew_graph(self, ip, **kargs): + """Tries to graph the timeskew between the timestamps and real time for a given ip""" + res = map(lambda x: self._elt2pkt(x), self.res) + b = filter(lambda x:x.haslayer(IP) and x.getlayer(IP).src == ip and x.haslayer(TCP), res) + c = [] + for p in b: + opts = p.getlayer(TCP).options + for o in opts: + if o[0] == "Timestamp": + c.append((p.time,o[1][0])) + if not c: + warning("No timestamps found in packet list") + return + d = map(lambda (x,y): (x%2000,((x-c[0][0])-((y-c[0][1])/1000.0))),c) + g = Gnuplot.Gnuplot() + g.plot(Gnuplot.Data(d,**kargs)) + return g + +PacketList.timeskew_graph = new.instancemethod(_packetlist_timeskew_graph, None, PacketList) + + +### Create a new packet list +class TracerouteResult(SndRcvList): + def __init__(self, res=None, name="Traceroute", stats=None): + PacketList.__init__(self, res, name, stats) + self.graphdef = None + self.graphASres = 0 + self.padding = 0 + self.hloc = None + self.nloc = None + + def show(self): + return self.make_table(lambda (s,r): (s.sprintf("%IP.dst%:{TCP:tcp%ir,TCP.dport%}{UDP:udp%ir,UDP.dport%}{ICMP:ICMP}"), + s.ttl, + r.sprintf("%-15s,IP.src% {TCP:%TCP.flags%}{ICMP:%ir,ICMP.type%}"))) + + + def get_trace(self): + trace = {} + for s,r in self.res: + if IP not in s: + continue + d = s[IP].dst + if d not in trace: + trace[d] = {} + trace[d][s[IP].ttl] = r[IP].src, ICMP not in r + for k in trace.values(): + m = filter(lambda x:k[x][1], k.keys()) + if not m: + continue + m = min(m) + for l in k.keys(): + if l > m: + del(k[l]) + return trace + + def trace3D(self): + """Give a 3D representation of the traceroute. + right button: rotate the scene + middle button: zoom + left button: move the scene + left button on a ball: toggle IP displaying + ctrl-left button on a ball: scan ports 21,22,23,25,80 and 443 and display the result""" + trace = self.get_trace() + import visual + + class IPsphere(visual.sphere): + def __init__(self, ip, **kargs): + visual.sphere.__init__(self, **kargs) + self.ip=ip + self.label=None + self.setlabel(self.ip) + def setlabel(self, txt,visible=None): + if self.label is not None: + if visible is None: + visible = self.label.visible + self.label.visible = 0 + elif visible is None: + visible=0 + self.label=visual.label(text=txt, pos=self.pos, space=self.radius, xoffset=10, yoffset=20, visible=visible) + def action(self): + self.label.visible ^= 1 + + visual.scene = visual.display() + visual.scene.exit = True + start = visual.box() + rings={} + tr3d = {} + for i in trace: + tr = trace[i] + tr3d[i] = [] + ttl = tr.keys() + for t in range(1,max(ttl)+1): + if t not in rings: + rings[t] = [] + if t in tr: + if tr[t] not in rings[t]: + rings[t].append(tr[t]) + tr3d[i].append(rings[t].index(tr[t])) + else: + rings[t].append(("unk",-1)) + tr3d[i].append(len(rings[t])-1) + for t in rings: + r = rings[t] + l = len(r) + for i in range(l): + if r[i][1] == -1: + col = (0.75,0.75,0.75) + elif r[i][1]: + col = visual.color.green + else: + col = visual.color.blue + + s = IPsphere(pos=((l-1)*visual.cos(2*i*visual.pi/l),(l-1)*visual.sin(2*i*visual.pi/l),2*t), + ip = r[i][0], + color = col) + for trlst in tr3d.values(): + if t <= len(trlst): + if trlst[t-1] == i: + trlst[t-1] = s + forecol = colgen(0.625, 0.4375, 0.25, 0.125) + for trlst in tr3d.values(): + col = forecol.next() + start = (0,0,0) + for ip in trlst: + visual.cylinder(pos=start,axis=ip.pos-start,color=col,radius=0.2) + start = ip.pos + + movcenter=None + while 1: + visual.rate(50) + if visual.scene.kb.keys: + k = visual.scene.kb.getkey() + if k == "esc" or k == "q": + break + if visual.scene.mouse.events: + ev = visual.scene.mouse.getevent() + if ev.press == "left": + o = ev.pick + if o: + if ev.ctrl: + if o.ip == "unk": + continue + savcolor = o.color + o.color = (1,0,0) + a,b=sr(IP(dst=o.ip)/TCP(dport=[21,22,23,25,80,443]),timeout=2) + o.color = savcolor + if len(a) == 0: + txt = "%s:\nno results" % o.ip + else: + txt = "%s:\n" % o.ip + for s,r in a: + txt += r.sprintf("{TCP:%IP.src%:%TCP.sport% %TCP.flags%}{TCPerror:%IPerror.dst%:%TCPerror.dport% %IP.src% %ir,ICMP.type%}\n") + o.setlabel(txt, visible=1) + else: + if hasattr(o, "action"): + o.action() + elif ev.drag == "left": + movcenter = ev.pos + elif ev.drop == "left": + movcenter = None + if movcenter: + visual.scene.center -= visual.scene.mouse.pos-movcenter + movcenter = visual.scene.mouse.pos + + + def world_trace(self): + from modules.geo import locate_ip + ips = {} + rt = {} + ports_done = {} + for s,r in self.res: + ips[r.src] = None + if s.haslayer(TCP) or s.haslayer(UDP): + trace_id = (s.src,s.dst,s.proto,s.dport) + elif s.haslayer(ICMP): + trace_id = (s.src,s.dst,s.proto,s.type) + else: + trace_id = (s.src,s.dst,s.proto,0) + trace = rt.get(trace_id,{}) + if not r.haslayer(ICMP) or r.type != 11: + if ports_done.has_key(trace_id): + continue + ports_done[trace_id] = None + trace[s.ttl] = r.src + rt[trace_id] = trace + + trt = {} + for trace_id in rt: + trace = rt[trace_id] + loctrace = [] + for i in range(max(trace.keys())): + ip = trace.get(i,None) + if ip is None: + continue + loc = locate_ip(ip) + if loc is None: + continue +# loctrace.append((ip,loc)) # no labels yet + loctrace.append(loc) + if loctrace: + trt[trace_id] = loctrace + + tr = map(lambda x: Gnuplot.Data(x,with_="lines"), trt.values()) + g = Gnuplot.Gnuplot() + world = Gnuplot.File(conf.gnuplot_world,with_="lines") + g.plot(world,*tr) + return g + + def make_graph(self,ASres=None,padding=0): + if ASres is None: + ASres = conf.AS_resolver + self.graphASres = ASres + self.graphpadding = padding + ips = {} + rt = {} + ports = {} + ports_done = {} + for s,r in self.res: + r = r.getlayer(IP) or (conf.ipv6_enabled and r[scapy.layers.inet6.IPv6]) or r + s = s.getlayer(IP) or (conf.ipv6_enabled and s[scapy.layers.inet6.IPv6]) or s + ips[r.src] = None + if TCP in s: + trace_id = (s.src,s.dst,6,s.dport) + elif UDP in s: + trace_id = (s.src,s.dst,17,s.dport) + elif ICMP in s: + trace_id = (s.src,s.dst,1,s.type) + else: + trace_id = (s.src,s.dst,s.proto,0) + trace = rt.get(trace_id,{}) + ttl = conf.ipv6_enabled and scapy.layers.inet6.IPv6 in s and s.hlim or s.ttl + if not (ICMP in r and r[ICMP].type == 11) and not (conf.ipv6_enabled and scapy.layers.inet6.IPv6 in r and scapy.layers.inet6.ICMPv6TimeExceeded in r): + if trace_id in ports_done: + continue + ports_done[trace_id] = None + p = ports.get(r.src,[]) + if TCP in r: + p.append(r.sprintf(" %TCP.sport% %TCP.flags%")) + trace[ttl] = r.sprintf('"%r,src%":T%ir,TCP.sport%') + elif UDP in r: + p.append(r.sprintf(" %UDP.sport%")) + trace[ttl] = r.sprintf('"%r,src%":U%ir,UDP.sport%') + elif ICMP in r: + p.append(r.sprintf(" ICMP %ICMP.type%")) + trace[ttl] = r.sprintf('"%r,src%":I%ir,ICMP.type%') + else: + p.append(r.sprintf("{IP: IP %proto%}{IPv6: IPv6 %nh%}")) + trace[ttl] = r.sprintf('"%r,src%":{IP:P%ir,proto%}{IPv6:P%ir,nh%}') + ports[r.src] = p + else: + trace[ttl] = r.sprintf('"%r,src%"') + rt[trace_id] = trace + + # Fill holes with unk%i nodes + unknown_label = incremental_label("unk%i") + blackholes = [] + bhip = {} + for rtk in rt: + trace = rt[rtk] + k = trace.keys() + for n in range(min(k), max(k)): + if not trace.has_key(n): + trace[n] = unknown_label.next() + if not ports_done.has_key(rtk): + if rtk[2] == 1: #ICMP + bh = "%s %i/icmp" % (rtk[1],rtk[3]) + elif rtk[2] == 6: #TCP + bh = "%s %i/tcp" % (rtk[1],rtk[3]) + elif rtk[2] == 17: #UDP + bh = '%s %i/udp' % (rtk[1],rtk[3]) + else: + bh = '%s %i/proto' % (rtk[1],rtk[2]) + ips[bh] = None + bhip[rtk[1]] = bh + bh = '"%s"' % bh + trace[max(k)+1] = bh + blackholes.append(bh) + + # Find AS numbers + ASN_query_list = dict.fromkeys(map(lambda x:x.rsplit(" ",1)[0],ips)).keys() + if ASres is None: + ASNlist = [] + else: + ASNlist = ASres.resolve(*ASN_query_list) + + ASNs = {} + ASDs = {} + for ip,asn,desc, in ASNlist: + if asn is None: + continue + iplist = ASNs.get(asn,[]) + if ip in bhip: + if ip in ports: + iplist.append(ip) + iplist.append(bhip[ip]) + else: + iplist.append(ip) + ASNs[asn] = iplist + ASDs[asn] = desc + + + backcolorlist=colgen("60","86","ba","ff") + forecolorlist=colgen("a0","70","40","20") + + s = "digraph trace {\n" + + s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n" + + s += "\n#ASN clustering\n" + for asn in ASNs: + s += '\tsubgraph cluster_%s {\n' % asn + col = backcolorlist.next() + s += '\t\tcolor="#%s%s%s";' % col + s += '\t\tnode [fillcolor="#%s%s%s",style=filled];' % col + s += '\t\tfontsize = 10;' + s += '\t\tlabel = "%s\\n[%s]"\n' % (asn,ASDs[asn]) + for ip in ASNs[asn]: + + s += '\t\t"%s";\n'%ip + s += "\t}\n" + + + + + s += "#endpoints\n" + for p in ports: + s += '\t"%s" [shape=record,color=black,fillcolor=green,style=filled,label="%s|%s"];\n' % (p,p,"|".join(ports[p])) + + s += "\n#Blackholes\n" + for bh in blackholes: + s += '\t%s [shape=octagon,color=black,fillcolor=red,style=filled];\n' % bh + + if padding: + s += "\n#Padding\n" + pad={} + for snd,rcv in self.res: + if rcv.src not in ports and rcv.haslayer(conf.padding_layer): + p = rcv.getlayer(conf.padding_layer).load + if p != "\x00"*len(p): + pad[rcv.src]=None + for rcv in pad: + s += '\t"%s" [shape=triangle,color=black,fillcolor=red,style=filled];\n' % rcv + + + + s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n" + + + for rtk in rt: + s += "#---[%s\n" % `rtk` + s += '\t\tedge [color="#%s%s%s"];\n' % forecolorlist.next() + trace = rt[rtk] + k = trace.keys() + for n in range(min(k), max(k)): + s += '\t%s ->\n' % trace[n] + s += '\t%s;\n' % trace[max(k)] + + s += "}\n"; + self.graphdef = s + + def graph(self, ASres=None, padding=0, **kargs): + """x.graph(ASres=conf.AS_resolver, other args): + ASres=None : no AS resolver => no clustering + ASres=AS_resolver() : default whois AS resolver (riswhois.ripe.net) + ASres=AS_resolver_cymru(): use whois.cymru.com whois database + ASres=AS_resolver(server="whois.ra.net") + type: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option + target: filename or redirect. Defaults pipe to Imagemagick's display program + prog: which graphviz program to use""" + if ASres is None: + ASres = conf.AS_resolver + if (self.graphdef is None or + self.graphASres != ASres or + self.graphpadding != padding): + self.make_graph(ASres,padding) + + return do_graph(self.graphdef, **kargs) + + + +@conf.commands.register +def traceroute(target, dport=80, minttl=1, maxttl=30, sport=RandShort(), l4 = None, filter=None, timeout=2, verbose=None, **kargs): + """Instant TCP traceroute +traceroute(target, [maxttl=30,] [dport=80,] [sport=80,] [verbose=conf.verb]) -> None +""" + if verbose is None: + verbose = conf.verb + if filter is None: + # we only consider ICMP error packets and TCP packets with at + # least the ACK flag set *and* either the SYN or the RST flag + # set + filter="(icmp and (icmp[0]=3 or icmp[0]=4 or icmp[0]=5 or icmp[0]=11 or icmp[0]=12)) or (tcp and (tcp[13] & 0x16 > 0x10))" + if l4 is None: + a,b = sr(IP(dst=target, id=RandShort(), ttl=(minttl,maxttl))/TCP(seq=RandInt(),sport=sport, dport=dport), + timeout=timeout, filter=filter, verbose=verbose, **kargs) + else: + # this should always work + filter="ip" + a,b = sr(IP(dst=target, id=RandShort(), ttl=(minttl,maxttl))/l4, + timeout=timeout, filter=filter, verbose=verbose, **kargs) + + a = TracerouteResult(a.res) + if verbose: + a.show() + return a,b + + + +############################# +## Simple TCP client stack ## +############################# + +class TCP_client(Automaton): + + def parse_args(self, ip, port, *args, **kargs): + self.dst = iter(Net(ip)).next() + self.dport = port + self.sport = random.randrange(0,2**16) + self.l4 = IP(dst=ip)/TCP(sport=self.sport, dport=self.dport, flags=0, + seq=random.randrange(0,2**32)) + self.src = self.l4.src + self.swin=self.l4[TCP].window + self.dwin=1 + self.rcvbuf="" + bpf = "host %s and host %s and port %i and port %i" % (self.src, + self.dst, + self.sport, + self.dport) + +# bpf=None + Automaton.parse_args(self, filter=bpf, **kargs) + + + def master_filter(self, pkt): + return (IP in pkt and + pkt[IP].src == self.dst and + pkt[IP].dst == self.src and + TCP in pkt and + pkt[TCP].sport == self.dport and + pkt[TCP].dport == self.sport and + self.l4[TCP].seq >= pkt[TCP].ack and # XXX: seq/ack 2^32 wrap up + ((self.l4[TCP].ack == 0) or (self.l4[TCP].ack <= pkt[TCP].seq <= self.l4[TCP].ack+self.swin)) ) + + + @ATMT.state(initial=1) + def START(self): + pass + + @ATMT.state() + def SYN_SENT(self): + pass + + @ATMT.state() + def ESTABLISHED(self): + pass + + @ATMT.state() + def LAST_ACK(self): + pass + + @ATMT.state(final=1) + def CLOSED(self): + pass + + + @ATMT.condition(START) + def connect(self): + raise self.SYN_SENT() + @ATMT.action(connect) + def send_syn(self): + self.l4[TCP].flags = "S" + self.send(self.l4) + self.l4[TCP].seq += 1 + + + @ATMT.receive_condition(SYN_SENT) + def synack_received(self, pkt): + if pkt[TCP].flags & 0x3f == 0x12: + raise self.ESTABLISHED().action_parameters(pkt) + @ATMT.action(synack_received) + def send_ack_of_synack(self, pkt): + self.l4[TCP].ack = pkt[TCP].seq+1 + self.l4[TCP].flags = "A" + self.send(self.l4) + + @ATMT.receive_condition(ESTABLISHED) + def incoming_data_received(self, pkt): + if not isinstance(pkt[TCP].payload, NoPayload) and not isinstance(pkt[TCP].payload, conf.padding_layer): + raise self.ESTABLISHED().action_parameters(pkt) + @ATMT.action(incoming_data_received) + def receive_data(self,pkt): + data = str(pkt[TCP].payload) + if data and self.l4[TCP].ack == pkt[TCP].seq: + self.l4[TCP].ack += len(data) + self.l4[TCP].flags = "A" + self.send(self.l4) + self.rcvbuf += data + if pkt[TCP].flags & 8 != 0: #PUSH + self.oi.tcp.send(self.rcvbuf) + self.rcvbuf = "" + + @ATMT.ioevent(ESTABLISHED,name="tcp", as_supersocket="tcplink") + def outgoing_data_received(self, fd): + raise self.ESTABLISHED().action_parameters(fd.recv()) + @ATMT.action(outgoing_data_received) + def send_data(self, d): + self.l4[TCP].flags = "PA" + self.send(self.l4/d) + self.l4[TCP].seq += len(d) + + + @ATMT.receive_condition(ESTABLISHED) + def reset_received(self, pkt): + if pkt[TCP].flags & 4 != 0: + raise self.CLOSED() + + @ATMT.receive_condition(ESTABLISHED) + def fin_received(self, pkt): + if pkt[TCP].flags & 0x1 == 1: + raise self.LAST_ACK().action_parameters(pkt) + @ATMT.action(fin_received) + def send_finack(self, pkt): + self.l4[TCP].flags = "FA" + self.l4[TCP].ack = pkt[TCP].seq+1 + self.send(self.l4) + self.l4[TCP].seq += 1 + + @ATMT.receive_condition(LAST_ACK) + def ack_of_fin_received(self, pkt): + if pkt[TCP].flags & 0x3f == 0x10: + raise self.CLOSED() + + + + +##################### +## Reporting stuff ## +##################### + +def report_ports(target, ports): + """portscan a target and output a LaTeX table +report_ports(target, ports) -> string""" + ans,unans = sr(IP(dst=target)/TCP(dport=ports),timeout=5) + rep = "\\begin{tabular}{|r|l|l|}\n\\hline\n" + for s,r in ans: + if not r.haslayer(ICMP): + if r.payload.flags == 0x12: + rep += r.sprintf("%TCP.sport% & open & SA \\\\\n") + rep += "\\hline\n" + for s,r in ans: + if r.haslayer(ICMP): + rep += r.sprintf("%TCPerror.dport% & closed & ICMP type %ICMP.type%/%ICMP.code% from %IP.src% \\\\\n") + elif r.payload.flags != 0x12: + rep += r.sprintf("%TCP.sport% & closed & TCP %TCP.flags% \\\\\n") + rep += "\\hline\n" + for i in unans: + rep += i.sprintf("%TCP.dport% & ? & unanswered \\\\\n") + rep += "\\hline\n\\end{tabular}\n" + return rep + + + +def IPID_count(lst, funcID=lambda x:x[1].id, funcpres=lambda x:x[1].summary()): + idlst = map(funcID, lst) + idlst.sort() + classes = [idlst[0]]+map(lambda x:x[1],filter(lambda (x,y): abs(x-y)>50, map(lambda x,y: (x,y),idlst[:-1], idlst[1:]))) + lst = map(lambda x:(funcID(x), funcpres(x)), lst) + lst.sort() + print "Probably %i classes:" % len(classes), classes + for id,pr in lst: + print "%5i" % id, pr + + +def fragleak(target,sport=123, dport=123, timeout=0.2, onlyasc=0): + load = "XXXXYYYYYYYYYY" +# getmacbyip(target) +# pkt = IP(dst=target, id=RandShort(), options="\x22"*40)/UDP()/load + pkt = IP(dst=target, id=RandShort(), options="\x00"*40, flags=1)/UDP(sport=sport, dport=sport)/load + s=conf.L3socket() + intr=0 + found={} + try: + while 1: + try: + if not intr: + s.send(pkt) + sin,sout,serr = select([s],[],[],timeout) + if not sin: + continue + ans=s.recv(1600) + if not isinstance(ans, IP): #TODO: IPv6 + continue + if not isinstance(ans.payload, ICMP): + continue + if not isinstance(ans.payload.payload, IPerror): + continue + if ans.payload.payload.dst != target: + continue + if ans.src != target: + print "leak from", ans.src, + + +# print repr(ans) + if not ans.haslayer(conf.padding_layer): + continue + + +# print repr(ans.payload.payload.payload.payload) + +# if not isinstance(ans.payload.payload.payload.payload, conf.raw_layer): +# continue +# leak = ans.payload.payload.payload.payload.load[len(load):] + leak = ans.getlayer(conf.padding_layer).load + if leak not in found: + found[leak]=None + linehexdump(leak, onlyasc=onlyasc) + except KeyboardInterrupt: + if intr: + raise + intr=1 + except KeyboardInterrupt: + pass + +def fragleak2(target, timeout=0.4, onlyasc=0): + found={} + try: + while 1: + p = sr1(IP(dst=target, options="\x00"*40, proto=200)/"XXXXYYYYYYYYYYYY",timeout=timeout,verbose=0) + if not p: + continue + if conf.padding_layer in p: + leak = p[conf.padding_layer].load + if leak not in found: + found[leak]=None + linehexdump(leak,onlyasc=onlyasc) + except: + pass + + +conf.stats_classic_protocols += [TCP,UDP,ICMP] +conf.stats_dot11_protocols += [TCP,UDP,ICMP] + +if conf.ipv6_enabled: + import scapy.layers.inet6 diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/inet6.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/inet6.py new file mode 100644 index 00000000..46cd85e5 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/inet6.py @@ -0,0 +1,3011 @@ +#! /usr/bin/env python +############################################################################# +## ## +## inet6.py --- IPv6 support for Scapy ## +## see http://natisbad.org/IPv6/ ## +## for more informations ## +## ## +## Copyright (C) 2005 Guillaume Valadon ## +## Arnaud Ebalard ## +## ## +## This program is free software; you can redistribute it and/or modify it ## +## under the terms of the GNU General Public License version 2 as ## +## published by the Free Software Foundation. ## +## ## +## This program is distributed in the hope that it will be useful, but ## +## WITHOUT ANY WARRANTY; without even the implied warranty of ## +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## +## General Public License for more details. ## +## ## +############################################################################# + +""" +IPv6 (Internet Protocol v6). +""" + + +import socket +if not socket.has_ipv6: + raise socket.error("can't use AF_INET6, IPv6 is disabled") +if not hasattr(socket, "IPPROTO_IPV6"): + # Workaround for http://bugs.python.org/issue6926 + socket.IPPROTO_IPV6 = 41 + +if not ('IPPROTO_IPIP ' in globals()): + IPPROTO_IPIP=4 + + +from scapy.config import conf +from scapy.layers.l2 import * +from scapy.layers.inet import * +from scapy.fields import * +from scapy.packet import * +from scapy.volatile import * +from scapy.sendrecv import sr,sr1,srp1 +from scapy.as_resolvers import AS_resolver_riswhois +from scapy.supersocket import SuperSocket,L3RawSocket +from scapy.arch import * +from scapy.utils6 import * + + +############################################################################# +# Helpers ## +############################################################################# + +def get_cls(name, fallback_cls): + return globals().get(name, fallback_cls) + + +########################## +## Neighbor cache stuff ## +########################## + +conf.netcache.new_cache("in6_neighbor", 120) + +def neighsol(addr, src, iface, timeout=1, chainCC=0): + """ + Sends an ICMPv6 Neighbor Solicitation message to get the MAC address + of the neighbor with specified IPv6 address addr. 'src' address is + used as source of the message. Message is sent on iface. By default, + timeout waiting for an answer is 1 second. + + If no answer is gathered, None is returned. Else, the answer is + returned (ethernet frame). + """ + + nsma = in6_getnsma(inet_pton(socket.AF_INET6, addr)) + d = inet_ntop(socket.AF_INET6, nsma) + dm = in6_getnsmac(nsma) + p = Ether(dst=dm)/IPv6(dst=d, src=src, hlim=255) + p /= ICMPv6ND_NS(tgt=addr) + p /= ICMPv6NDOptSrcLLAddr(lladdr=get_if_hwaddr(iface)) + res = srp1(p,type=ETH_P_IPV6, iface=iface, timeout=1, verbose=0, + chainCC=chainCC) + + return res + +def getmacbyip6(ip6, chainCC=0): + """ + Returns the mac address to be used for provided 'ip6' peer. + neighborCache.get() method is used on instantiated neighbor cache. + Resolution mechanism is described in associated doc string. + + (chainCC parameter value ends up being passed to sending function + used to perform the resolution, if needed) + """ + + if in6_ismaddr(ip6): # Multicast + mac = in6_getnsmac(inet_pton(socket.AF_INET6, ip6)) + return mac + + iff,a,nh = conf.route6.route(ip6, dev=conf.iface6) + + if iff == LOOPBACK_NAME: + return "ff:ff:ff:ff:ff:ff" + + if nh != '::': + ip6 = nh # Found next hop + + mac = conf.netcache.in6_neighbor.get(ip6) + if mac: + return mac + + res = neighsol(ip6, a, iff, chainCC=chainCC) + + if res is not None: + if ICMPv6NDOptDstLLAddr in res: + mac = res[ICMPv6NDOptDstLLAddr].lladdr + else: + mac = res.src + conf.netcache.in6_neighbor[ip6] = mac + return mac + + return None + + +############################################################################# +############################################################################# +### IPv6 addresses manipulation routines ### +############################################################################# +############################################################################# + +class Net6(Gen): # syntax ex. fec0::/126 + """Generate a list of IPv6s from a network address or a name""" + name = "ipv6" + ipaddress = re.compile(r"^([a-fA-F0-9:]+)(/[1]?[0-3]?[0-9])?$") + + def __init__(self, net): + self.repr = net + + tmp = net.split('/')+["128"] + if not self.ipaddress.match(net): + tmp[0]=socket.getaddrinfo(tmp[0], None, socket.AF_INET6)[0][-1][0] + + netmask = int(tmp[1]) + self.net = inet_pton(socket.AF_INET6, tmp[0]) + self.mask = in6_cidr2mask(netmask) + self.plen = netmask + + def __iter__(self): + def m8(i): + if i % 8 == 0: + return i + tuple = filter(lambda x: m8(x), xrange(8, 129)) + + a = in6_and(self.net, self.mask) + tmp = map(lambda x: x, struct.unpack('16B', a)) + + def parse_digit(a, netmask): + netmask = min(8,max(netmask,0)) + a = (int(a) & (0xffL<>(8-netmask)))+1) + return a + self.parsed = map(lambda x,y: parse_digit(x,y), tmp, map(lambda x,nm=self.plen: x-nm, tuple)) + + def rec(n, l): + if n and n % 2 == 0: + sep = ':' + else: + sep = '' + if n == 16: + return l + else: + ll = [] + for i in xrange(*self.parsed[n]): + for y in l: + ll += [y+sep+'%.2x'%i] + return rec(n+1, ll) + + return iter(rec(0, [''])) + + def __repr__(self): + return "" % self.repr + + + + + + +############################################################################# +############################################################################# +### IPv6 Class ### +############################################################################# +############################################################################# + +class IP6Field(Field): + def __init__(self, name, default): + Field.__init__(self, name, default, "16s") + def h2i(self, pkt, x): + if type(x) is str: + try: + x = in6_ptop(x) + except socket.error: + x = Net6(x) + elif type(x) is list: + x = map(Net6, x) + return x + def i2m(self, pkt, x): + return inet_pton(socket.AF_INET6, x) + def m2i(self, pkt, x): + return inet_ntop(socket.AF_INET6, x) + def any2i(self, pkt, x): + return self.h2i(pkt,x) + def i2repr(self, pkt, x): + if x is None: + return self.i2h(pkt,x) + elif not isinstance(x, Net6) and not type(x) is list: + if in6_isaddrTeredo(x): # print Teredo info + server, flag, maddr, mport = teredoAddrExtractInfo(x) + return "%s [Teredo srv: %s cli: %s:%s]" % (self.i2h(pkt, x), server, maddr,mport) + elif in6_isaddr6to4(x): # print encapsulated address + vaddr = in6_6to4ExtractAddr(x) + return "%s [6to4 GW: %s]" % (self.i2h(pkt, x), vaddr) + return self.i2h(pkt, x) # No specific information to return + def randval(self): + return RandIP6() + +class SourceIP6Field(IP6Field): + def __init__(self, name, dstname): + IP6Field.__init__(self, name, None) + self.dstname = dstname + def i2m(self, pkt, x): + if x is None: + dst=getattr(pkt,self.dstname) + iff,x,nh = conf.route6.route(dst) + return IP6Field.i2m(self, pkt, x) + def i2h(self, pkt, x): + if x is None: + dst=getattr(pkt,self.dstname) + if isinstance(dst,Gen): + r = map(conf.route6.route, dst) + r.sort() + if r[0] == r[-1]: + x=r[0][1] + else: + warning("More than one possible route for %s"%repr(dst)) + return None + else: + iff,x,nh = conf.route6.route(dst) + return IP6Field.i2h(self, pkt, x) + +ipv6nh = { 0:"Hop-by-Hop Option Header", + 4:"IP", + 6:"TCP", + 17:"UDP", + 41:"IPv6", + 43:"Routing Header", + 44:"Fragment Header", + 47:"GRE", + 50:"ESP Header", + 51:"AH Header", + 58:"ICMPv6", + 59:"No Next Header", + 60:"Destination Option Header", + 135:"Mobility Header"} + +ipv6nhcls = { 0: "IPv6ExtHdrHopByHop", + 4: "IP", + 6: "TCP", + 17: "UDP", + 43: "IPv6ExtHdrRouting", + 44: "IPv6ExtHdrFragment", + #50: "IPv6ExtHrESP", + #51: "IPv6ExtHdrAH", + 58: "ICMPv6Unknown", + 59: "Raw", + 60: "IPv6ExtHdrDestOpt" } + +class IP6ListField(StrField): + islist = 1 + def __init__(self, name, default, count_from=None, length_from=None): + if default is None: + default = [] + StrField.__init__(self, name, default) + self.count_from = count_from + self.length_from = length_from + + def i2len(self, pkt, i): + return 16*len(i) + + def i2count(self, pkt, i): + if type(i) is list: + return len(i) + return 0 + + def getfield(self, pkt, s): + c = l = None + if self.length_from is not None: + l = self.length_from(pkt) + elif self.count_from is not None: + c = self.count_from(pkt) + + lst = [] + ret = "" + remain = s + if l is not None: + remain,ret = s[:l],s[l:] + while remain: + if c is not None: + if c <= 0: + break + c -= 1 + addr = inet_ntop(socket.AF_INET6, remain[:16]) + lst.append(addr) + remain = remain[16:] + return remain+ret,lst + + def i2m(self, pkt, x): + s = '' + for y in x: + try: + y = inet_pton(socket.AF_INET6, y) + except: + y = socket.getaddrinfo(y, None, socket.AF_INET6)[0][-1][0] + y = inet_pton(socket.AF_INET6, y) + s += y + return s + + def i2repr(self,pkt,x): + s = [] + if x == None: + return "[]" + for y in x: + s.append('%s' % y) + return "[ %s ]" % (", ".join(s)) + +class _IPv6GuessPayload: + name = "Dummy class that implements guess_payload_class() for IPv6" + def default_payload_class(self,p): + if self.nh == 58: # ICMPv6 + t = ord(p[0]) + if len(p) > 2 and t == 139 or t == 140: # Node Info Query + return _niquery_guesser(p) + if len(p) >= icmp6typesminhdrlen.get(t, sys.maxint): # Other ICMPv6 messages + return get_cls(icmp6typescls.get(t,"Raw"), "Raw") + return Raw + elif self.nh == 135 and len(p) > 3: # Mobile IPv6 + return _mip6_mhtype2cls.get(ord(p[2]), MIP6MH_Generic) + else: + return get_cls(ipv6nhcls.get(self.nh,"Raw"), "Raw") + +class IPv6(_IPv6GuessPayload, Packet, IPTools): + name = "IPv6" + fields_desc = [ BitField("version" , 6 , 4), + BitField("tc", 0, 8), #TODO: IPv6, ByteField ? + BitField("fl", 0, 20), + ShortField("plen", None), + ByteEnumField("nh", 59, ipv6nh), + ByteField("hlim", 64), + IP6Field("src", "::2"), + #SourceIP6Field("src", "dst"), # dst is for src @ selection + IP6Field("dst", "::1") ] + + def route(self): + dst = self.dst + if isinstance(dst,Gen): + dst = iter(dst).next() + return conf.route6.route(dst) + + def mysummary(self): + return "%s > %s (%i)" % (self.src,self.dst, self.nh) + + def post_build(self, p, pay): + p += pay + if self.plen is None: + l = len(p) - 40 + p = p[:4]+struct.pack("!H", l)+p[6:] + return p + + def extract_padding(self, s): + l = self.plen + return s[:l], s[l:] + + def hashret(self): + if self.nh == 58 and isinstance(self.payload, _ICMPv6): + if self.payload.type < 128: + return self.payload.payload.hashret() + elif (self.payload.type in [133,134,135,136,144,145]): + return struct.pack("B", self.nh)+self.payload.hashret() + + nh = self.nh + sd = self.dst + ss = self.src + if self.nh == 43 and isinstance(self.payload, IPv6ExtHdrRouting): + # With routing header, the destination is the last + # address of the IPv6 list if segleft > 0 + nh = self.payload.nh + try: + sd = self.addresses[-1] + except IndexError: + sd = '::1' + # TODO: big bug with ICMPv6 error messages as the destination of IPerror6 + # could be anything from the original list ... + if 1: + sd = inet_pton(socket.AF_INET6, sd) + for a in self.addresses: + a = inet_pton(socket.AF_INET6, a) + sd = strxor(sd, a) + sd = inet_ntop(socket.AF_INET6, sd) + + if self.nh == 44 and isinstance(self.payload, IPv6ExtHdrFragment): + nh = self.payload.nh + + if self.nh == 0 and isinstance(self.payload, IPv6ExtHdrHopByHop): + nh = self.payload.nh + + if self.nh == 60 and isinstance(self.payload, IPv6ExtHdrDestOpt): + foundhao = None + for o in self.payload.options: + if isinstance(o, HAO): + foundhao = o + if foundhao: + nh = self.payload.nh # XXX what if another extension follows ? + ss = foundhao.hoa + + if conf.checkIPsrc and conf.checkIPaddr: + sd = inet_pton(socket.AF_INET6, sd) + ss = inet_pton(socket.AF_INET6, self.src) + return struct.pack("B",nh)+self.payload.hashret() + else: + return struct.pack("B", nh)+self.payload.hashret() + + def answers(self, other): + if not isinstance(other, IPv6): # self is reply, other is request + return False + if conf.checkIPaddr: + ss = inet_pton(socket.AF_INET6, self.src) + sd = inet_pton(socket.AF_INET6, self.dst) + os = inet_pton(socket.AF_INET6, other.src) + od = inet_pton(socket.AF_INET6, other.dst) + # request was sent to a multicast address (other.dst) + # Check reply destination addr matches request source addr (i.e + # sd == os) except when reply is multicasted too + # XXX test mcast scope matching ? + if in6_ismaddr(other.dst): + if in6_ismaddr(self.dst): + if ((od == sd) or + (in6_isaddrllallnodes(self.dst) and in6_isaddrllallservers(other.dst))): + return self.payload.answers(other.payload) + return False + if (os == sd): + return self.payload.answers(other.payload) + return False + elif (sd != os): # or ss != od): <- removed for ICMP errors + return False + if self.nh == 58 and isinstance(self.payload, _ICMPv6) and self.payload.type < 128: + # ICMPv6 Error message -> generated by IPv6 packet + # Note : at the moment, we jump the ICMPv6 specific class + # to call answers() method of erroneous packet (over + # initial packet). There can be cases where an ICMPv6 error + # class could implement a specific answers method that perform + # a specific task. Currently, don't see any use ... + return self.payload.payload.answers(other) + elif other.nh == 0 and isinstance(other.payload, IPv6ExtHdrHopByHop): + return self.payload.answers(other.payload.payload) + elif other.nh == 44 and isinstance(other.payload, IPv6ExtHdrFragment): + return self.payload.answers(other.payload.payload) + elif other.nh == 43 and isinstance(other.payload, IPv6ExtHdrRouting): + return self.payload.answers(other.payload.payload) # Buggy if self.payload is a IPv6ExtHdrRouting + elif other.nh == 60 and isinstance(other.payload, IPv6ExtHdrDestOpt): + return self.payload.payload.answers(other.payload.payload) + elif self.nh == 60 and isinstance(self.payload, IPv6ExtHdrDestOpt): # BU in reply to BRR, for instance + return self.payload.payload.answers(other.payload) + else: + if (self.nh != other.nh): + return False + return self.payload.answers(other.payload) + + +conf.neighbor.register_l3(Ether, IPv6, lambda l2,l3: getmacbyip6(l3.dst)) + + +class IPerror6(IPv6): + name = "IPv6 in ICMPv6" + def answers(self, other): + if not isinstance(other, IPv6): + return False + sd = inet_pton(socket.AF_INET6, self.dst) + ss = inet_pton(socket.AF_INET6, self.src) + od = inet_pton(socket.AF_INET6, other.dst) + os = inet_pton(socket.AF_INET6, other.src) + + # Make sure that the ICMPv6 error is related to the packet scapy sent + if isinstance(self.underlayer, _ICMPv6) and self.underlayer.type < 128: + + # find upper layer for self (possible citation) + selfup = self.payload + while selfup is not None and isinstance(selfup, _IPv6ExtHdr): + selfup = selfup.payload + + # find upper layer for other (initial packet). Also look for RH + otherup = other.payload + request_has_rh = False + while otherup is not None and isinstance(otherup, _IPv6ExtHdr): + if isinstance(otherup, IPv6ExtHdrRouting): + request_has_rh = True + otherup = otherup.payload + + if ((ss == os and sd == od) or # <- Basic case + (ss == os and request_has_rh)): # <- Request has a RH : + # don't check dst address + + # Let's deal with possible MSS Clamping + if (isinstance(selfup, TCP) and + isinstance(otherup, TCP) and + selfup.options != otherup.options): # seems clamped + + # Save fields modified by MSS clamping + old_otherup_opts = otherup.options + old_otherup_cksum = otherup.chksum + old_otherup_dataofs = otherup.dataofs + old_selfup_opts = selfup.options + old_selfup_cksum = selfup.chksum + old_selfup_dataofs = selfup.dataofs + + # Nullify them + otherup.options = [] + otherup.chksum = 0 + otherup.dataofs = 0 + selfup.options = [] + selfup.chksum = 0 + selfup.dataofs = 0 + + # Test it and save result + s1 = str(selfup) + s2 = str(otherup) + l = min(len(s1), len(s2)) + res = s1[:l] == s2[:l] + + # recall saved values + otherup.options = old_otherup_opts + otherup.chksum = old_otherup_cksum + otherup.dataofs = old_otherup_dataofs + selfup.options = old_selfup_opts + selfup.chksum = old_selfup_cksum + selfup.dataofs = old_selfup_dataofs + + return res + + s1 = str(selfup) + s2 = str(otherup) + l = min(len(s1), len(s2)) + return s1[:l] == s2[:l] + + return False + + def mysummary(self): + return Packet.mysummary(self) + + +############################################################################# +############################################################################# +### Upper Layer Checksum computation ### +############################################################################# +############################################################################# + +class PseudoIPv6(Packet): # IPv6 Pseudo-header for checksum computation + name = "Pseudo IPv6 Header" + fields_desc = [ IP6Field("src", "::"), + IP6Field("dst", "::"), + ShortField("uplen", None), + BitField("zero", 0, 24), + ByteField("nh", 0) ] + +def in6_chksum(nh, u, p): + """ + Performs IPv6 Upper Layer checksum computation. Provided parameters are: + + - 'nh' : value of upper layer protocol + - 'u' : upper layer instance (TCP, UDP, ICMPv6*, ). Instance must be + provided with all under layers (IPv6 and all extension headers, + for example) + - 'p' : the payload of the upper layer provided as a string + + Functions operate by filling a pseudo header class instance (PseudoIPv6) + with + - Next Header value + - the address of _final_ destination (if some Routing Header with non + segleft field is present in underlayer classes, last address is used.) + - the address of _real_ source (basically the source address of an + IPv6 class instance available in the underlayer or the source address + in HAO option if some Destination Option header found in underlayer + includes this option). + - the length is the length of provided payload string ('p') + """ + + ph6 = PseudoIPv6() + ph6.nh = nh + rthdr = 0 + hahdr = 0 + final_dest_addr_found = 0 + while u != None and not isinstance(u, IPv6): + if (isinstance(u, IPv6ExtHdrRouting) and + u.segleft != 0 and len(u.addresses) != 0 and + final_dest_addr_found == 0): + rthdr = u.addresses[-1] + final_dest_addr_found = 1 + elif (isinstance(u, IPv6ExtHdrDestOpt) and (len(u.options) == 1) and + isinstance(u.options[0], HAO)): + hahdr = u.options[0].hoa + u = u.underlayer + if u is None: + warning("No IPv6 underlayer to compute checksum. Leaving null.") + return 0 + if hahdr: + ph6.src = hahdr + else: + ph6.src = u.src + if rthdr: + ph6.dst = rthdr + else: + ph6.dst = u.dst + ph6.uplen = len(p) + ph6s = str(ph6) + return checksum(ph6s+p) + + +############################################################################# +############################################################################# +### Extension Headers ### +############################################################################# +############################################################################# + + +# Inherited by all extension header classes +class _IPv6ExtHdr(_IPv6GuessPayload, Packet): + name = 'Abstract IPV6 Option Header' + aliastypes = [IPv6, IPerror6] # TODO ... + + +#################### IPv6 options for Extension Headers ##################### + +_hbhopts = { 0x00: "Pad1", + 0x01: "PadN", + 0x04: "Tunnel Encapsulation Limit", + 0x05: "Router Alert", + 0x06: "Quick-Start", + 0xc2: "Jumbo Payload", + 0xc9: "Home Address Option" } + +class _OTypeField(ByteEnumField): + """ + Modified BytEnumField that displays information regarding the IPv6 option + based on its option type value (What should be done by nodes that process + the option if they do not understand it ...) + + It is used by Jumbo, Pad1, PadN, RouterAlert, HAO options + """ + pol = {0x00: "00: skip", + 0x40: "01: discard", + 0x80: "10: discard+ICMP", + 0xC0: "11: discard+ICMP not mcast"} + + enroutechange = {0x00: "0: Don't change en-route", + 0x20: "1: May change en-route" } + + def i2repr(self, pkt, x): + s = self.i2s.get(x, repr(x)) + polstr = self.pol[(x & 0xC0)] + enroutechangestr = self.enroutechange[(x & 0x20)] + return "%s [%s, %s]" % (s, polstr, enroutechangestr) + +class HBHOptUnknown(Packet): # IPv6 Hop-By-Hop Option + name = "Scapy6 Unknown Option" + fields_desc = [_OTypeField("otype", 0x01, _hbhopts), + FieldLenField("optlen", None, length_of="optdata", fmt="B"), + StrLenField("optdata", "", + length_from = lambda pkt: pkt.optlen) ] + def alignment_delta(self, curpos): # By default, no alignment requirement + """ + As specified in section 4.2 of RFC 2460, every options has + an alignment requirement ususally expressed xn+y, meaning + the Option Type must appear at an integer multiple of x octest + from the start of the header, plus y octet. + + That function is provided the current position from the + start of the header and returns required padding length. + """ + return 0 + +class Pad1(Packet): # IPv6 Hop-By-Hop Option + name = "Pad1" + fields_desc = [ _OTypeField("otype", 0x00, _hbhopts) ] + def alignment_delta(self, curpos): # No alignment requirement + return 0 + +class PadN(Packet): # IPv6 Hop-By-Hop Option + name = "PadN" + fields_desc = [_OTypeField("otype", 0x01, _hbhopts), + FieldLenField("optlen", None, length_of="optdata", fmt="B"), + StrLenField("optdata", "", + length_from = lambda pkt: pkt.optlen)] + def alignment_delta(self, curpos): # No alignment requirement + return 0 + +class RouterAlert(Packet): # RFC 2711 - IPv6 Hop-By-Hop Option + name = "Router Alert" + fields_desc = [_OTypeField("otype", 0x05, _hbhopts), + ByteField("optlen", 2), + ShortEnumField("value", None, + { 0: "Datagram contains a MLD message", + 1: "Datagram contains RSVP message", + 2: "Datagram contains an Active Network message" }) ] + # TODO : Check IANA has not defined new values for value field of RouterAlertOption + # TODO : now that we have that option, we should do something in MLD class that need it + def alignment_delta(self, curpos): # alignment requirement : 2n+0 + x = 2 ; y = 0 + delta = x*((curpos - y + x - 1)/x) + y - curpos + return delta + +class Jumbo(Packet): # IPv6 Hop-By-Hop Option + name = "Jumbo Payload" + fields_desc = [_OTypeField("otype", 0xC2, _hbhopts), + ByteField("optlen", 4), + IntField("jumboplen", None) ] + def alignment_delta(self, curpos): # alignment requirement : 4n+2 + x = 4 ; y = 2 + delta = x*((curpos - y + x - 1)/x) + y - curpos + return delta + +class HAO(Packet): # IPv6 Destination Options Header Option + name = "Home Address Option" + fields_desc = [_OTypeField("otype", 0xC9, _hbhopts), + ByteField("optlen", 16), + IP6Field("hoa", "::") ] + def alignment_delta(self, curpos): # alignment requirement : 8n+6 + x = 8 ; y = 6 + delta = x*((curpos - y + x - 1)/x) + y - curpos + return delta + +_hbhoptcls = { 0x00: Pad1, + 0x01: PadN, + 0x05: RouterAlert, + 0xC2: Jumbo, + 0xC9: HAO } + + +######################## Hop-by-Hop Extension Header ######################## + +class _HopByHopOptionsField(PacketListField): + islist = 1 + holds_packet = 1 + def __init__(self, name, default, cls, curpos, count_from=None, length_from=None): + self.curpos = curpos + PacketListField.__init__(self, name, default, cls, count_from=count_from, length_from=length_from) + + def i2len(self, pkt, i): + l = len(self.i2m(pkt, i)) + return l + + def i2count(self, pkt, i): + if type(i) is list: + return len(i) + return 0 + + def getfield(self, pkt, s): + c = l = None + if self.length_from is not None: + l = self.length_from(pkt) + elif self.count_from is not None: + c = self.count_from(pkt) + + opt = [] + ret = "" + x = s + if l is not None: + x,ret = s[:l],s[l:] + while x: + if c is not None: + if c <= 0: + break + c -= 1 + o = ord(x[0]) # Option type + cls = self.cls + if _hbhoptcls.has_key(o): + cls = _hbhoptcls[o] + try: + op = cls(x) + except: + op = self.cls(x) + opt.append(op) + if isinstance(op.payload, conf.raw_layer): + x = op.payload.load + del(op.payload) + else: + x = "" + return x+ret,opt + + def i2m(self, pkt, x): + autopad = None + try: + autopad = getattr(pkt, "autopad") # Hack : 'autopad' phantom field + except: + autopad = 1 + + if not autopad: + return "".join(map(str, x)) + + curpos = self.curpos + s = "" + for p in x: + d = p.alignment_delta(curpos) + curpos += d + if d == 1: + s += str(Pad1()) + elif d != 0: + s += str(PadN(optdata='\x00'*(d-2))) + pstr = str(p) + curpos += len(pstr) + s += pstr + + # Let's make the class including our option field + # a multiple of 8 octets long + d = curpos % 8 + if d == 0: + return s + d = 8 - d + if d == 1: + s += str(Pad1()) + elif d != 0: + s += str(PadN(optdata='\x00'*(d-2))) + + return s + + def addfield(self, pkt, s, val): + return s+self.i2m(pkt, val) + +class _PhantomAutoPadField(ByteField): + def addfield(self, pkt, s, val): + return s + + def getfield(self, pkt, s): + return s, 1 + + def i2repr(self, pkt, x): + if x: + return "On" + return "Off" + + +class IPv6ExtHdrHopByHop(_IPv6ExtHdr): + name = "IPv6 Extension Header - Hop-by-Hop Options Header" + fields_desc = [ ByteEnumField("nh", 59, ipv6nh), + FieldLenField("len", None, length_of="options", fmt="B", + adjust = lambda pkt,x: (x+2+7)/8 - 1), + _PhantomAutoPadField("autopad", 1), # autopad activated by default + _HopByHopOptionsField("options", [], HBHOptUnknown, 2, + length_from = lambda pkt: (8*(pkt.len+1))-2) ] + overload_fields = {IPv6: { "nh": 0 }} + + +######################## Destination Option Header ########################## + +class IPv6ExtHdrDestOpt(_IPv6ExtHdr): + name = "IPv6 Extension Header - Destination Options Header" + fields_desc = [ ByteEnumField("nh", 59, ipv6nh), + FieldLenField("len", None, length_of="options", fmt="B", + adjust = lambda pkt,x: (x+2+7)/8 - 1), + _PhantomAutoPadField("autopad", 1), # autopad activated by default + _HopByHopOptionsField("options", [], HBHOptUnknown, 2, + length_from = lambda pkt: (8*(pkt.len+1))-2) ] + overload_fields = {IPv6: { "nh": 60 }} + + +############################# Routing Header ################################ + +class IPv6ExtHdrRouting(_IPv6ExtHdr): + name = "IPv6 Option Header Routing" + fields_desc = [ ByteEnumField("nh", 59, ipv6nh), + FieldLenField("len", None, count_of="addresses", fmt="B", + adjust = lambda pkt,x:2*x), # in 8 bytes blocks + ByteField("type", 0), + ByteField("segleft", None), + BitField("reserved", 0, 32), # There is meaning in this field ... + IP6ListField("addresses", [], + length_from = lambda pkt: 8*pkt.len)] + overload_fields = {IPv6: { "nh": 43 }} + + def post_build(self, pkt, pay): + if self.segleft is None: + pkt = pkt[:3]+struct.pack("B", len(self.addresses))+pkt[4:] + return _IPv6ExtHdr.post_build(self, pkt, pay) + +########################### Fragmentation Header ############################ + +class IPv6ExtHdrFragment(_IPv6ExtHdr): + name = "IPv6 Extension Header - Fragmentation header" + fields_desc = [ ByteEnumField("nh", 59, ipv6nh), + BitField("res1", 0, 8), + BitField("offset", 0, 13), + BitField("res2", 0, 2), + BitField("m", 0, 1), + IntField("id", None) ] + overload_fields = {IPv6: { "nh": 44 }} + + +def defragment6(pktlist): + """ + Performs defragmentation of a list of IPv6 packets. Packets are reordered. + Crap is dropped. What lacks is completed by 'X' characters. + """ + + l = filter(lambda x: IPv6ExtHdrFragment in x, pktlist) # remove non fragments + if not l: + return [] + + id = l[0][IPv6ExtHdrFragment].id + + llen = len(l) + l = filter(lambda x: x[IPv6ExtHdrFragment].id == id, l) + if len(l) != llen: + warning("defragment6: some fragmented packets have been removed from list") + llen = len(l) + + # reorder fragments + i = 0 + res = [] + while l: + min_pos = 0 + min_offset = l[0][IPv6ExtHdrFragment].offset + for p in l: + cur_offset = p[IPv6ExtHdrFragment].offset + if cur_offset < min_offset: + min_pos = 0 + min_offset = cur_offset + res.append(l[min_pos]) + del(l[min_pos]) + + # regenerate the fragmentable part + fragmentable = "" + for p in res: + q=p[IPv6ExtHdrFragment] + offset = 8*q.offset + if offset != len(fragmentable): + warning("Expected an offset of %d. Found %d. Padding with XXXX" % (len(fragmentable), offset)) + fragmentable += "X"*(offset - len(fragmentable)) + fragmentable += str(q.payload) + + # Regenerate the unfragmentable part. + q = res[0] + nh = q[IPv6ExtHdrFragment].nh + q[IPv6ExtHdrFragment].underlayer.nh = nh + q[IPv6ExtHdrFragment].underlayer.payload = None + q /= conf.raw_layer(load=fragmentable) + + return IPv6(str(q)) + + +def fragment6(pkt, fragSize): + """ + Performs fragmentation of an IPv6 packet. Provided packet ('pkt') must already + contain an IPv6ExtHdrFragment() class. 'fragSize' argument is the expected + maximum size of fragments (MTU). The list of packets is returned. + + If packet does not contain an IPv6ExtHdrFragment class, it is returned in + result list. + """ + + pkt = pkt.copy() + + if not IPv6ExtHdrFragment in pkt: + # TODO : automatically add a fragment before upper Layer + # at the moment, we do nothing and return initial packet + # as single element of a list + return [pkt] + + # If the payload is bigger than 65535, a Jumbo payload must be used, as + # an IPv6 packet can't be bigger than 65535 bytes. + if len(str(pkt[IPv6ExtHdrFragment])) > 65535: + warning("An IPv6 packet can'be bigger than 65535, please use a Jumbo payload.") + return [] + + s = str(pkt) # for instantiation to get upper layer checksum right + + if len(s) <= fragSize: + return [pkt] + + # Fragmentable part : fake IPv6 for Fragmentable part length computation + fragPart = pkt[IPv6ExtHdrFragment].payload + tmp = str(IPv6(src="::1", dst="::1")/fragPart) + fragPartLen = len(tmp) - 40 # basic IPv6 header length + fragPartStr = s[-fragPartLen:] + + # Grab Next Header for use in Fragment Header + nh = IPv6(tmp[:40]).nh + + # Keep fragment header + fragHeader = pkt[IPv6ExtHdrFragment] + fragHeader.payload = None # detach payload + + # Unfragmentable Part + unfragPartLen = len(s) - fragPartLen - 8 + unfragPart = pkt + pkt[IPv6ExtHdrFragment].underlayer.payload = None # detach payload + + # Cut the fragmentable part to fit fragSize. Inner fragments have + # a length that is an integer multiple of 8 octets. last Frag MTU + # can be anything below MTU + lastFragSize = fragSize - unfragPartLen - 8 + innerFragSize = lastFragSize - (lastFragSize % 8) + + if lastFragSize <= 0 or innerFragSize == 0: + warning("Provided fragment size value is too low. " + + "Should be more than %d" % (unfragPartLen + 8)) + return [unfragPart/fragHeader/fragPart] + + remain = fragPartStr + res = [] + fragOffset = 0 # offset, incremeted during creation + fragId = random.randint(0,0xffffffff) # random id ... + if fragHeader.id is not None: # ... except id provided by user + fragId = fragHeader.id + fragHeader.m = 1 + fragHeader.id = fragId + fragHeader.nh = nh + + # Main loop : cut, fit to FRAGSIZEs, fragOffset, Id ... + while True: + if (len(remain) > lastFragSize): + tmp = remain[:innerFragSize] + remain = remain[innerFragSize:] + fragHeader.offset = fragOffset # update offset + fragOffset += (innerFragSize / 8) # compute new one + if IPv6 in unfragPart: + unfragPart[IPv6].plen = None + tempo = unfragPart/fragHeader/conf.raw_layer(load=tmp) + res.append(tempo) + else: + fragHeader.offset = fragOffset # update offSet + fragHeader.m = 0 + if IPv6 in unfragPart: + unfragPart[IPv6].plen = None + tempo = unfragPart/fragHeader/conf.raw_layer(load=remain) + res.append(tempo) + break + return res + + +############################### AH Header ################################### + +# class _AHFieldLenField(FieldLenField): +# def getfield(self, pkt, s): +# l = getattr(pkt, self.fld) +# l = (l*8)-self.shift +# i = self.m2i(pkt, s[:l]) +# return s[l:],i + +# class _AHICVStrLenField(StrLenField): +# def i2len(self, pkt, x): + + + +# class IPv6ExtHdrAH(_IPv6ExtHdr): +# name = "IPv6 Extension Header - AH" +# fields_desc = [ ByteEnumField("nh", 59, ipv6nh), +# _AHFieldLenField("len", None, "icv"), +# ShortField("res", 0), +# IntField("spi", 0), +# IntField("sn", 0), +# _AHICVStrLenField("icv", None, "len", shift=2) ] +# overload_fields = {IPv6: { "nh": 51 }} + +# def post_build(self, pkt, pay): +# if self.len is None: +# pkt = pkt[0]+struct.pack("!B", 2*len(self.addresses))+pkt[2:] +# if self.segleft is None: +# pkt = pkt[:3]+struct.pack("!B", len(self.addresses))+pkt[4:] +# return _IPv6ExtHdr.post_build(self, pkt, pay) + + +############################### ESP Header ################################## + +# class IPv6ExtHdrESP(_IPv6extHdr): +# name = "IPv6 Extension Header - ESP" +# fields_desc = [ IntField("spi", 0), +# IntField("sn", 0), +# # there is things to extract from IKE work +# ] +# overloads_fields = {IPv6: { "nh": 50 }} + + + +############################################################################# +############################################################################# +### ICMPv6* Classes ### +############################################################################# +############################################################################# + +icmp6typescls = { 1: "ICMPv6DestUnreach", + 2: "ICMPv6PacketTooBig", + 3: "ICMPv6TimeExceeded", + 4: "ICMPv6ParamProblem", + 128: "ICMPv6EchoRequest", + 129: "ICMPv6EchoReply", + 130: "ICMPv6MLQuery", + 131: "ICMPv6MLReport", + 132: "ICMPv6MLDone", + 133: "ICMPv6ND_RS", + 134: "ICMPv6ND_RA", + 135: "ICMPv6ND_NS", + 136: "ICMPv6ND_NA", + 137: "ICMPv6ND_Redirect", + #138: Do Me - RFC 2894 - Seems painful + 139: "ICMPv6NIQuery", + 140: "ICMPv6NIReply", + 141: "ICMPv6ND_INDSol", + 142: "ICMPv6ND_INDAdv", + #143: Do Me - RFC 3810 + 144: "ICMPv6HAADRequest", + 145: "ICMPv6HAADReply", + 146: "ICMPv6MPSol", + 147: "ICMPv6MPAdv", + #148: Do Me - SEND related - RFC 3971 + #149: Do Me - SEND related - RFC 3971 + 151: "ICMPv6MRD_Advertisement", + 152: "ICMPv6MRD_Solicitation", + 153: "ICMPv6MRD_Termination", + } + +icmp6typesminhdrlen = { 1: 8, + 2: 8, + 3: 8, + 4: 8, + 128: 8, + 129: 8, + 130: 24, + 131: 24, + 132: 24, + 133: 8, + 134: 16, + 135: 24, + 136: 24, + 137: 40, + #139: + #140 + 141: 8, + 142: 8, + 144: 8, + 145: 8, + 146: 8, + 147: 8, + 151: 8, + 152: 4, + 153: 4 + } + +icmp6types = { 1 : "Destination unreachable", + 2 : "Packet too big", + 3 : "Time exceeded", + 4 : "Parameter problem", + 100 : "Private Experimentation", + 101 : "Private Experimentation", + 128 : "Echo Request", + 129 : "Echo Reply", + 130 : "MLD Query", + 131 : "MLD Report", + 132 : "MLD Done", + 133 : "Router Solicitation", + 134 : "Router Advertisement", + 135 : "Neighbor Solicitation", + 136 : "Neighbor Advertisement", + 137 : "Redirect Message", + 138 : "Router Renumbering", + 139 : "ICMP Node Information Query", + 140 : "ICMP Node Information Response", + 141 : "Inverse Neighbor Discovery Solicitation Message", + 142 : "Inverse Neighbor Discovery Advertisement Message", + 143 : "Version 2 Multicast Listener Report", + 144 : "Home Agent Address Discovery Request Message", + 145 : "Home Agent Address Discovery Reply Message", + 146 : "Mobile Prefix Solicitation", + 147 : "Mobile Prefix Advertisement", + 148 : "Certification Path Solicitation", + 149 : "Certification Path Advertisement", + 151 : "Multicast Router Advertisement", + 152 : "Multicast Router Solicitation", + 153 : "Multicast Router Termination", + 200 : "Private Experimentation", + 201 : "Private Experimentation" } + + +class _ICMPv6(Packet): + name = "ICMPv6 dummy class" + overload_fields = {IPv6: {"nh": 58}} + def post_build(self, p, pay): + p += pay + if self.cksum == None: + chksum = in6_chksum(58, self.underlayer, p) + p = p[:2]+struct.pack("!H", chksum)+p[4:] + return p + + def hashret(self): + return self.payload.hashret() + + def answers(self, other): + # isinstance(self.underlayer, _IPv6ExtHdr) may introduce a bug ... + if (isinstance(self.underlayer, IPerror6) or + isinstance(self.underlayer, _IPv6ExtHdr) and + isinstance(other, _ICMPv6)): + if not ((self.type == other.type) and + (self.code == other.code)): + return 0 + return 1 + return 0 + + +class _ICMPv6Error(_ICMPv6): + name = "ICMPv6 errors dummy class" + def guess_payload_class(self,p): + return IPerror6 + +class ICMPv6Unknown(_ICMPv6): + name = "Scapy6 ICMPv6 fallback class" + fields_desc = [ ByteEnumField("type",1, icmp6types), + ByteField("code",0), + XShortField("cksum", None), + StrField("msgbody", "")] + + +################################## RFC 2460 ################################# + +class ICMPv6DestUnreach(_ICMPv6Error): + name = "ICMPv6 Destination Unreachable" + fields_desc = [ ByteEnumField("type",1, icmp6types), + ByteEnumField("code",0, { 0: "No route to destination", + 1: "Communication with destination administratively prohibited", + 2: "Beyond scope of source address", + 3: "Address unreachable", + 4: "Port unreachable" }), + XShortField("cksum", None), + XIntField("unused",0x00000000)] + +class ICMPv6PacketTooBig(_ICMPv6Error): + name = "ICMPv6 Packet Too Big" + fields_desc = [ ByteEnumField("type",2, icmp6types), + ByteField("code",0), + XShortField("cksum", None), + IntField("mtu",1280)] + +class ICMPv6TimeExceeded(_ICMPv6Error): + name = "ICMPv6 Time Exceeded" + fields_desc = [ ByteEnumField("type",3, icmp6types), + ByteEnumField("code",0, { 0: "hop limit exceeded in transit", + 1: "fragment reassembly time exceeded"}), + XShortField("cksum", None), + XIntField("unused",0x00000000)] + +# The default pointer value is set to the next header field of +# the encapsulated IPv6 packet +class ICMPv6ParamProblem(_ICMPv6Error): + name = "ICMPv6 Parameter Problem" + fields_desc = [ ByteEnumField("type",4, icmp6types), + ByteEnumField("code",0, {0: "erroneous header field encountered", + 1: "unrecognized Next Header type encountered", + 2: "unrecognized IPv6 option encountered"}), + XShortField("cksum", None), + IntField("ptr",6)] + +class ICMPv6EchoRequest(_ICMPv6): + name = "ICMPv6 Echo Request" + fields_desc = [ ByteEnumField("type", 128, icmp6types), + ByteField("code", 0), + XShortField("cksum", None), + XShortField("id",0), + XShortField("seq",0), + StrField("data", "")] + def mysummary(self): + return self.sprintf("%name% (id: %id% seq: %seq%)") + def hashret(self): + return struct.pack("HH",self.id,self.seq)+self.payload.hashret() + + +class ICMPv6EchoReply(ICMPv6EchoRequest): + name = "ICMPv6 Echo Reply" + type = 129 + def answers(self, other): + # We could match data content between request and reply. + return (isinstance(other, ICMPv6EchoRequest) and + self.id == other.id and self.seq == other.seq and + self.data == other.data) + + +############ ICMPv6 Multicast Listener Discovery (RFC3810) ################## + +# tous les messages MLD sont emis avec une adresse source lien-locale +# -> Y veiller dans le post_build si aucune n'est specifiee +# La valeur de Hop-Limit doit etre de 1 +# "and an IPv6 Router Alert option in a Hop-by-Hop Options +# header. (The router alert option is necessary to cause routers to +# examine MLD messages sent to multicast addresses in which the router +# itself has no interest" +class _ICMPv6ML(_ICMPv6): + fields_desc = [ ByteEnumField("type", 130, icmp6types), + ByteField("code", 0), + XShortField("cksum", None), + ShortField("mrd", 0), + ShortField("reserved", 0), + IP6Field("mladdr","::")] + +# general queries are sent to the link-scope all-nodes multicast +# address ff02::1, with a multicast address field of 0 and a MRD of +# [Query Response Interval] +# Default value for mladdr is set to 0 for a General Query, and +# overloaded by the user for a Multicast Address specific query +# TODO : See what we can do to automatically include a Router Alert +# Option in a Destination Option Header. +class ICMPv6MLQuery(_ICMPv6ML): # RFC 2710 + name = "MLD - Multicast Listener Query" + type = 130 + mrd = 10000 + mladdr = "::" # 10s for mrd + overload_fields = {IPv6: { "dst": "ff02::1", "hlim": 1, "nh": 58 }} + def hashret(self): + if self.mladdr != "::": + return struct.pack("HH",self.mladdr)+self.payload.hashret() + else: + return self.payload.hashret() + + +# TODO : See what we can do to automatically include a Router Alert +# Option in a Destination Option Header. +class ICMPv6MLReport(_ICMPv6ML): # RFC 2710 + name = "MLD - Multicast Listener Report" + type = 131 + overload_fields = {IPv6: {"hlim": 1, "nh": 58}} + # implementer le hashret et le answers + +# When a node ceases to listen to a multicast address on an interface, +# it SHOULD send a single Done message to the link-scope all-routers +# multicast address (FF02::2), carrying in its multicast address field +# the address to which it is ceasing to listen +# TODO : See what we can do to automatically include a Router Alert +# Option in a Destination Option Header. +class ICMPv6MLDone(_ICMPv6ML): # RFC 2710 + name = "MLD - Multicast Listener Done" + type = 132 + overload_fields = {IPv6: { "dst": "ff02::2", "hlim": 1, "nh": 58}} + + +########## ICMPv6 MRD - Multicast Router Discovery (RFC 4286) ############### + +# TODO: +# - 04/09/06 troglocan : find a way to automatically add a router alert +# option for all MRD packets. This could be done in a specific +# way when IPv6 is the under layer with some specific keyword +# like 'exthdr'. This would allow to keep compatibility with +# providing IPv6 fields to be overloaded in fields_desc. +# +# At the moment, if user inserts an IPv6 Router alert option +# none of the IPv6 default values of IPv6 layer will be set. + +class ICMPv6MRD_Advertisement(_ICMPv6): + name = "ICMPv6 Multicast Router Discovery Advertisement" + fields_desc = [ByteEnumField("type", 151, icmp6types), + ByteField("advinter", 20), + XShortField("cksum", None), + ShortField("queryint", 0), + ShortField("robustness", 0)] + overload_fields = {IPv6: { "nh": 58, "hlim": 1, "dst": "ff02::2"}} + # IPv6 Router Alert requires manual inclusion + def extract_padding(self, s): + return s[:8], s[8:] + +class ICMPv6MRD_Solicitation(_ICMPv6): + name = "ICMPv6 Multicast Router Discovery Solicitation" + fields_desc = [ByteEnumField("type", 152, icmp6types), + ByteField("res", 0), + XShortField("cksum", None) ] + overload_fields = {IPv6: { "nh": 58, "hlim": 1, "dst": "ff02::2"}} + # IPv6 Router Alert requires manual inclusion + def extract_padding(self, s): + return s[:4], s[4:] + +class ICMPv6MRD_Termination(_ICMPv6): + name = "ICMPv6 Multicast Router Discovery Termination" + fields_desc = [ByteEnumField("type", 153, icmp6types), + ByteField("res", 0), + XShortField("cksum", None) ] + overload_fields = {IPv6: { "nh": 58, "hlim": 1, "dst": "ff02::6A"}} + # IPv6 Router Alert requires manual inclusion + def extract_padding(self, s): + return s[:4], s[4:] + + +################### ICMPv6 Neighbor Discovery (RFC 2461) #################### + +icmp6ndopts = { 1: "Source Link-Layer Address", + 2: "Target Link-Layer Address", + 3: "Prefix Information", + 4: "Redirected Header", + 5: "MTU", + 6: "NBMA Shortcut Limit Option", # RFC2491 + 7: "Advertisement Interval Option", + 8: "Home Agent Information Option", + 9: "Source Address List", + 10: "Target Address List", + 11: "CGA Option", # RFC 3971 + 12: "RSA Signature Option", # RFC 3971 + 13: "Timestamp Option", # RFC 3971 + 14: "Nonce option", # RFC 3971 + 15: "Trust Anchor Option", # RFC 3971 + 16: "Certificate Option", # RFC 3971 + 17: "IP Address Option", # RFC 4068 + 18: "New Router Prefix Information Option", # RFC 4068 + 19: "Link-layer Address Option", # RFC 4068 + 20: "Neighbor Advertisement Acknowledgement Option", + 21: "CARD Request Option", # RFC 4065/4066/4067 + 22: "CARD Reply Option", # RFC 4065/4066/4067 + 23: "MAP Option", # RFC 4140 + 24: "Route Information Option", # RFC 4191 + 25: "Recusive DNS Server Option", + 26: "IPv6 Router Advertisement Flags Option" + } + +icmp6ndoptscls = { 1: "ICMPv6NDOptSrcLLAddr", + 2: "ICMPv6NDOptDstLLAddr", + 3: "ICMPv6NDOptPrefixInfo", + 4: "ICMPv6NDOptRedirectedHdr", + 5: "ICMPv6NDOptMTU", + 6: "ICMPv6NDOptShortcutLimit", + 7: "ICMPv6NDOptAdvInterval", + 8: "ICMPv6NDOptHAInfo", + 9: "ICMPv6NDOptSrcAddrList", + 10: "ICMPv6NDOptTgtAddrList", + #11: Do Me, + #12: Do Me, + #13: Do Me, + #14: Do Me, + #15: Do Me, + #16: Do Me, + 17: "ICMPv6NDOptIPAddr", + 18: "ICMPv6NDOptNewRtrPrefix", + 19: "ICMPv6NDOptLLA", + #18: Do Me, + #19: Do Me, + #20: Do Me, + #21: Do Me, + #22: Do Me, + 23: "ICMPv6NDOptMAP", + 24: "ICMPv6NDOptRouteInfo", + 25: "ICMPv6NDOptRDNSS", + 26: "ICMPv6NDOptEFA" + } + +class _ICMPv6NDGuessPayload: + name = "Dummy ND class that implements guess_payload_class()" + def guess_payload_class(self,p): + if len(p) > 1: + return get_cls(icmp6ndoptscls.get(ord(p[0]),"Raw"), "Raw") # s/Raw/ICMPv6NDOptUnknown/g ? + + +# Beginning of ICMPv6 Neighbor Discovery Options. + +class ICMPv6NDOptUnknown(_ICMPv6NDGuessPayload, Packet): + name = "ICMPv6 Neighbor Discovery Option - Scapy Unimplemented" + fields_desc = [ ByteField("type",None), + FieldLenField("len",None,length_of="data",fmt="B", + adjust = lambda pkt,x: x+2), + StrLenField("data","", + length_from = lambda pkt: pkt.len-2) ] + +# NOTE: len includes type and len field. Expressed in unit of 8 bytes +# TODO: Revoir le coup du ETHER_ANY +class ICMPv6NDOptSrcLLAddr(_ICMPv6NDGuessPayload, Packet): + name = "ICMPv6 Neighbor Discovery Option - Source Link-Layer Address" + fields_desc = [ ByteField("type", 1), + ByteField("len", 1), + MACField("lladdr", ETHER_ANY) ] + def mysummary(self): + return self.sprintf("%name% %lladdr%") + +class ICMPv6NDOptDstLLAddr(ICMPv6NDOptSrcLLAddr): + name = "ICMPv6 Neighbor Discovery Option - Destination Link-Layer Address" + type = 2 + +class ICMPv6NDOptPrefixInfo(_ICMPv6NDGuessPayload, Packet): + name = "ICMPv6 Neighbor Discovery Option - Prefix Information" + fields_desc = [ ByteField("type",3), + ByteField("len",4), + ByteField("prefixlen",None), + BitField("L",1,1), + BitField("A",1,1), + BitField("R",0,1), + BitField("res1",0,5), + XIntField("validlifetime",0xffffffffL), + XIntField("preferredlifetime",0xffffffffL), + XIntField("res2",0x00000000), + IP6Field("prefix","::") ] + def mysummary(self): + return self.sprintf("%name% %prefix%") + +# TODO: We should also limit the size of included packet to something +# like (initiallen - 40 - 2) +class TruncPktLenField(PacketLenField): + + def __init__(self, name, default, cls, cur_shift, length_from=None, shift=0): + PacketLenField.__init__(self, name, default, cls, length_from=length_from) + self.cur_shift = cur_shift + + def getfield(self, pkt, s): + l = self.length_from(pkt) + i = self.m2i(pkt, s[:l]) + return s[l:],i + + def m2i(self, pkt, m): + s = None + try: # It can happen we have sth shorter than 40 bytes + s = self.cls(m) + except: + return conf.raw_layer(m) + return s + + def i2m(self, pkt, x): + s = str(x) + l = len(s) + r = (l + self.cur_shift) % 8 + l = l - r + return s[:l] + + def i2len(self, pkt, i): + return len(self.i2m(pkt, i)) + + +# Faire un post_build pour le recalcul de la taille (en multiple de 8 octets) +class ICMPv6NDOptRedirectedHdr(_ICMPv6NDGuessPayload, Packet): + name = "ICMPv6 Neighbor Discovery Option - Redirected Header" + fields_desc = [ ByteField("type",4), + FieldLenField("len", None, length_of="pkt", fmt="B", + adjust = lambda pkt,x:(x+8)/8), + StrFixedLenField("res", "\x00"*6, 6), + TruncPktLenField("pkt", "", IPv6, 8, + length_from = lambda pkt: 8*pkt.len-8) ] + +# See which value should be used for default MTU instead of 1280 +class ICMPv6NDOptMTU(_ICMPv6NDGuessPayload, Packet): + name = "ICMPv6 Neighbor Discovery Option - MTU" + fields_desc = [ ByteField("type",5), + ByteField("len",1), + XShortField("res",0), + IntField("mtu",1280)] + +class ICMPv6NDOptShortcutLimit(_ICMPv6NDGuessPayload, Packet): # RFC 2491 + name = "ICMPv6 Neighbor Discovery Option - NBMA Shortcut Limit" + fields_desc = [ ByteField("type", 6), + ByteField("len", 1), + ByteField("shortcutlim", 40), # XXX + ByteField("res1", 0), + IntField("res2", 0) ] + +class ICMPv6NDOptAdvInterval(_ICMPv6NDGuessPayload, Packet): + name = "ICMPv6 Neighbor Discovery - Interval Advertisement" + fields_desc = [ ByteField("type",7), + ByteField("len",1), + ShortField("res", 0), + IntField("advint", 0) ] + def mysummary(self): + return self.sprintf("%name% %advint% milliseconds") + +class ICMPv6NDOptHAInfo(_ICMPv6NDGuessPayload, Packet): + name = "ICMPv6 Neighbor Discovery - Home Agent Information" + fields_desc = [ ByteField("type",8), + ByteField("len",1), + ShortField("res", 0), + ShortField("pref", 0), + ShortField("lifetime", 1)] + def mysummary(self): + return self.sprintf("%name% %pref% %lifetime% seconds") + +# type 9 : See ICMPv6NDOptSrcAddrList class below in IND (RFC 3122) support + +# type 10 : See ICMPv6NDOptTgtAddrList class below in IND (RFC 3122) support + +class ICMPv6NDOptIPAddr(_ICMPv6NDGuessPayload, Packet): # RFC 4068 + name = "ICMPv6 Neighbor Discovery - IP Address Option (FH for MIPv6)" + fields_desc = [ ByteField("type",17), + ByteField("len", 3), + ByteEnumField("optcode", 1, {1: "Old Care-Of Address", + 2: "New Care-Of Address", + 3: "NAR's IP address" }), + ByteField("plen", 64), + IntField("res", 0), + IP6Field("addr", "::") ] + +class ICMPv6NDOptNewRtrPrefix(_ICMPv6NDGuessPayload, Packet): # RFC 4068 + name = "ICMPv6 Neighbor Discovery - New Router Prefix Information Option (FH for MIPv6)" + fields_desc = [ ByteField("type",18), + ByteField("len", 3), + ByteField("optcode", 0), + ByteField("plen", 64), + IntField("res", 0), + IP6Field("prefix", "::") ] + +_rfc4068_lla_optcode = {0: "Wildcard requesting resolution for all nearby AP", + 1: "LLA for the new AP", + 2: "LLA of the MN", + 3: "LLA of the NAR", + 4: "LLA of the src of TrSolPr or PrRtAdv msg", + 5: "AP identified by LLA belongs to current iface of router", + 6: "No preifx info available for AP identified by the LLA", + 7: "No fast handovers support for AP identified by the LLA" } + +class ICMPv6NDOptLLA(_ICMPv6NDGuessPayload, Packet): # RFC 4068 + name = "ICMPv6 Neighbor Discovery - Link-Layer Address (LLA) Option (FH for MIPv6)" + fields_desc = [ ByteField("type", 19), + ByteField("len", 1), + ByteEnumField("optcode", 0, _rfc4068_lla_optcode), + MACField("lla", ETHER_ANY) ] # We only support ethernet + +class ICMPv6NDOptMAP(_ICMPv6NDGuessPayload, Packet): # RFC 4140 + name = "ICMPv6 Neighbor Discovery - MAP Option" + fields_desc = [ ByteField("type", 23), + ByteField("len", 3), + BitField("dist", 1, 4), + BitField("pref", 15, 4), # highest availability + BitField("R", 1, 1), + BitField("res", 0, 7), + IntField("validlifetime", 0xffffffff), + IP6Field("addr", "::") ] + + +class IP6PrefixField(IP6Field): + def __init__(self, name, default): + IP6Field.__init__(self, name, default) + self.length_from = lambda pkt: 8*(pkt.len - 1) + + def addfield(self, pkt, s, val): + return s + self.i2m(pkt, val) + + def getfield(self, pkt, s): + l = self.length_from(pkt) + p = s[:l] + if l < 16: + p += '\x00'*(16-l) + return s[l:], self.m2i(pkt,p) + + def i2len(self, pkt, x): + return len(self.i2m(pkt, x)) + + def i2m(self, pkt, x): + l = pkt.len + + if x is None: + x = "::" + if l is None: + l = 1 + x = inet_pton(socket.AF_INET6, x) + + if l is None: + return x + if l in [0, 1]: + return "" + if l in [2, 3]: + return x[:8*(l-1)] + + return x + '\x00'*8*(l-3) + +class ICMPv6NDOptRouteInfo(_ICMPv6NDGuessPayload, Packet): # RFC 4191 + name = "ICMPv6 Neighbor Discovery Option - Route Information Option" + fields_desc = [ ByteField("type",24), + FieldLenField("len", None, length_of="prefix", fmt="B", + adjust = lambda pkt,x: x/8 + 1), + ByteField("plen", None), + BitField("res1",0,3), + BitField("prf",0,2), + BitField("res2",0,3), + IntField("rtlifetime", 0xffffffff), + IP6PrefixField("prefix", None) ] + +class ICMPv6NDOptRDNSS(_ICMPv6NDGuessPayload, Packet): # RFC 5006 + name = "ICMPv6 Neighbor Discovery Option - Recursive DNS Server Option" + fields_desc = [ ByteField("type", 25), + FieldLenField("len", None, count_of="dns", fmt="B", + adjust = lambda pkt,x: 2*x+1), + ShortField("res", None), + IntField("lifetime", 0xffffffff), + IP6ListField("dns", [], + length_from = lambda pkt: 8*(pkt.len-1)) ] + +class ICMPv6NDOptEFA(_ICMPv6NDGuessPayload, Packet): # RFC 5175 (prev. 5075) + name = "ICMPv6 Neighbor Discovery Option - Expanded Flags Option" + fields_desc = [ ByteField("type", 26), + ByteField("len", 1), + BitField("res", 0, 48) ] + +# End of ICMPv6 Neighbor Discovery Options. + +class ICMPv6ND_RS(_ICMPv6NDGuessPayload, _ICMPv6): + name = "ICMPv6 Neighbor Discovery - Router Solicitation" + fields_desc = [ ByteEnumField("type", 133, icmp6types), + ByteField("code",0), + XShortField("cksum", None), + IntField("res",0) ] + overload_fields = {IPv6: { "nh": 58, "dst": "ff02::2", "hlim": 255 }} + +class ICMPv6ND_RA(_ICMPv6NDGuessPayload, _ICMPv6): + name = "ICMPv6 Neighbor Discovery - Router Advertisement" + fields_desc = [ ByteEnumField("type", 134, icmp6types), + ByteField("code",0), + XShortField("cksum", None), + ByteField("chlim",0), + BitField("M",0,1), + BitField("O",0,1), + BitField("H",0,1), + BitEnumField("prf",1,2, { 0: "Medium (default)", + 1: "High", + 2: "Reserved", + 3: "Low" } ), # RFC 4191 + BitField("P",0,1), + BitField("res",0,2), + ShortField("routerlifetime",1800), + IntField("reachabletime",0), + IntField("retranstimer",0) ] + overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }} + + def answers(self, other): + return isinstance(other, ICMPv6ND_RS) + +class ICMPv6ND_NS(_ICMPv6NDGuessPayload, _ICMPv6, Packet): + name = "ICMPv6 Neighbor Discovery - Neighbor Solicitation" + fields_desc = [ ByteEnumField("type",135, icmp6types), + ByteField("code",0), + XShortField("cksum", None), + IntField("res", 0), + IP6Field("tgt","::") ] + overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }} + + def mysummary(self): + return self.sprintf("%name% (tgt: %tgt%)") + + def hashret(self): + return self.tgt+self.payload.hashret() + +class ICMPv6ND_NA(_ICMPv6NDGuessPayload, _ICMPv6, Packet): + name = "ICMPv6 Neighbor Discovery - Neighbor Advertisement" + fields_desc = [ ByteEnumField("type",136, icmp6types), + ByteField("code",0), + XShortField("cksum", None), + BitField("R",1,1), + BitField("S",0,1), + BitField("O",1,1), + XBitField("res",0,29), + IP6Field("tgt","::") ] + overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }} + + def mysummary(self): + return self.sprintf("%name% (tgt: %tgt%)") + + def hashret(self): + return self.tgt+self.payload.hashret() + + def answers(self, other): + return isinstance(other, ICMPv6ND_NS) and self.tgt == other.tgt + +# associated possible options : target link-layer option, Redirected header +class ICMPv6ND_Redirect(_ICMPv6NDGuessPayload, _ICMPv6, Packet): + name = "ICMPv6 Neighbor Discovery - Redirect" + fields_desc = [ ByteEnumField("type",137, icmp6types), + ByteField("code",0), + XShortField("cksum", None), + XIntField("res",0), + IP6Field("tgt","::"), + IP6Field("dst","::") ] + overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }} + + + +################ ICMPv6 Inverse Neighbor Discovery (RFC 3122) ############### + +class ICMPv6NDOptSrcAddrList(_ICMPv6NDGuessPayload, Packet): + name = "ICMPv6 Inverse Neighbor Discovery Option - Source Address List" + fields_desc = [ ByteField("type",9), + FieldLenField("len", None, count_of="addrlist", fmt="B", + adjust = lambda pkt,x: 2*x+1), + StrFixedLenField("res", "\x00"*6, 6), + IP6ListField("addrlist", [], + length_from = lambda pkt: 8*(pkt.len-1)) ] + +class ICMPv6NDOptTgtAddrList(ICMPv6NDOptSrcAddrList): + name = "ICMPv6 Inverse Neighbor Discovery Option - Target Address List" + type = 10 + + +# RFC3122 +# Options requises : source lladdr et target lladdr +# Autres options valides : source address list, MTU +# - Comme precise dans le document, il serait bien de prendre l'adresse L2 +# demandee dans l'option requise target lladdr et l'utiliser au niveau +# de l'adresse destination ethernet si aucune adresse n'est precisee +# - ca semble pas forcement pratique si l'utilisateur doit preciser toutes +# les options. +# Ether() must use the target lladdr as destination +class ICMPv6ND_INDSol(_ICMPv6NDGuessPayload, _ICMPv6): + name = "ICMPv6 Inverse Neighbor Discovery Solicitation" + fields_desc = [ ByteEnumField("type",141, icmp6types), + ByteField("code",0), + XShortField("cksum",None), + XIntField("reserved",0) ] + overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }} + +# Options requises : target lladdr, target address list +# Autres options valides : MTU +class ICMPv6ND_INDAdv(_ICMPv6NDGuessPayload, _ICMPv6): + name = "ICMPv6 Inverse Neighbor Discovery Advertisement" + fields_desc = [ ByteEnumField("type",142, icmp6types), + ByteField("code",0), + XShortField("cksum",None), + XIntField("reserved",0) ] + overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }} + + +############################################################################### +# ICMPv6 Node Information Queries (RFC 4620) +############################################################################### + +# [ ] Add automatic destination address computation using computeNIGroupAddr +# in IPv6 class (Scapy6 modification when integrated) if : +# - it is not provided +# - upper layer is ICMPv6NIQueryName() with a valid value +# [ ] Try to be liberal in what we accept as internal values for _explicit_ +# DNS elements provided by users. Any string should be considered +# valid and kept like it has been provided. At the moment, i2repr() will +# crash on many inputs +# [ ] Do the documentation +# [ ] Add regression tests +# [ ] Perform test against real machines (NOOP reply is proof of implementation). +# [ ] Check if there are differences between different stacks. Among *BSD, +# with others. +# [ ] Deal with flags in a consistent way. +# [ ] Implement compression in names2dnsrepr() and decompresiion in +# dnsrepr2names(). Should be deactivable. + +icmp6_niqtypes = { 0: "NOOP", + 2: "Node Name", + 3: "IPv6 Address", + 4: "IPv4 Address" } + + +class _ICMPv6NIHashret: + def hashret(self): + return self.nonce + +class _ICMPv6NIAnswers: + def answers(self, other): + return self.nonce == other.nonce + +# Buggy; always returns the same value during a session +class NonceField(StrFixedLenField): + def __init__(self, name, default=None): + StrFixedLenField.__init__(self, name, default, 8) + if default is None: + self.default = self.randval() + +# Compute the NI group Address. Can take a FQDN as input parameter +def computeNIGroupAddr(name): + import md5 + name = name.lower().split(".")[0] + record = chr(len(name))+name + h = md5.new(record) + h = h.digest() + addr = "ff02::2:%2x%2x:%2x%2x" % struct.unpack("BBBB", h[:4]) + return addr + + +# Here is the deal. First, that protocol is a piece of shit. Then, we +# provide 4 classes for the different kinds of Requests (one for every +# valid qtype: NOOP, Node Name, IPv6@, IPv4@). They all share the same +# data field class that is made to be smart by guessing the specifc +# type of value provided : +# +# - IPv6 if acceptable for inet_pton(AF_INET6, ): code is set to 0, +# if not overriden by user +# - IPv4 if acceptable for inet_pton(AF_INET, ): code is set to 2, +# if not overriden +# - Name in the other cases: code is set to 0, if not overriden by user +# +# Internal storage, is not only the value, but the a pair providing +# the type and the value (1 is IPv6@, 1 is Name or string, 2 is IPv4@) +# +# Note : I merged getfield() and m2i(). m2i() should not be called +# directly anyway. Same remark for addfield() and i2m() +# +# -- arno + +# "The type of information present in the Data field of a query is +# declared by the ICMP Code, whereas the type of information in a +# Reply is determined by the Qtype" + +def names2dnsrepr(x): + """ + Take as input a list of DNS names or a single DNS name + and encode it in DNS format (with possible compression) + If a string that is already a DNS name in DNS format + is passed, it is returned unmodified. Result is a string. + !!! At the moment, compression is not implemented !!! + """ + + if type(x) is str: + if x and x[-1] == '\x00': # stupid heuristic + return x + x = [x] + + res = [] + for n in x: + termin = "\x00" + if n.count('.') == 0: # single-component gets one more + termin += '\x00' + n = "".join(map(lambda y: chr(len(y))+y, n.split("."))) + termin + res.append(n) + return "".join(res) + + +def dnsrepr2names(x): + """ + Take as input a DNS encoded string (possibly compressed) + and returns a list of DNS names contained in it. + If provided string is already in printable format + (does not end with a null character, a one element list + is returned). Result is a list. + """ + res = [] + cur = "" + while x: + l = ord(x[0]) + x = x[1:] + if l == 0: + if cur and cur[-1] == '.': + cur = cur[:-1] + res.append(cur) + cur = "" + if x and ord(x[0]) == 0: # single component + x = x[1:] + continue + if l & 0xc0: # XXX TODO : work on that -- arno + raise Exception("DNS message can't be compressed at this point!") + else: + cur += x[:l]+"." + x = x[l:] + return res + + +class NIQueryDataField(StrField): + def __init__(self, name, default): + StrField.__init__(self, name, default) + + def i2h(self, pkt, x): + if x is None: + return x + t,val = x + if t == 1: + val = dnsrepr2names(val)[0] + return val + + def h2i(self, pkt, x): + if x is tuple and type(x[0]) is int: + return x + + val = None + try: # Try IPv6 + inet_pton(socket.AF_INET6, x) + val = (0, x) + except: + try: # Try IPv4 + inet_pton(socket.AF_INET, x) + val = (2, x) + except: # Try DNS + if x is None: + x = "" + x = names2dnsrepr(x) + val = (1, x) + return val + + def i2repr(self, pkt, x): + t,val = x + if t == 1: # DNS Name + # we don't use dnsrepr2names() to deal with + # possible weird data extracted info + res = [] + weird = None + while val: + l = ord(val[0]) + val = val[1:] + if l == 0: + if (len(res) > 1 and val): # fqdn with data behind + weird = val + elif len(val) > 1: # single label with data behind + weird = val[1:] + break + res.append(val[:l]+".") + val = val[l:] + tmp = "".join(res) + if tmp and tmp[-1] == '.': + tmp = tmp[:-1] + return tmp + return repr(val) + + def getfield(self, pkt, s): + qtype = getattr(pkt, "qtype") + if qtype == 0: # NOOP + return s, (0, "") + else: + code = getattr(pkt, "code") + if code == 0: # IPv6 Addr + return s[16:], (0, inet_ntop(socket.AF_INET6, s[:16])) + elif code == 2: # IPv4 Addr + return s[4:], (2, inet_ntop(socket.AF_INET, s[:4])) + else: # Name or Unknown + return "", (1, s) + + def addfield(self, pkt, s, val): + if ((type(val) is tuple and val[1] is None) or + val is None): + val = (1, "") + t = val[0] + if t == 1: + return s + val[1] + elif t == 0: + return s + inet_pton(socket.AF_INET6, val[1]) + else: + return s + inet_pton(socket.AF_INET, val[1]) + +class NIQueryCodeField(ByteEnumField): + def i2m(self, pkt, x): + if x is None: + d = pkt.getfieldval("data") + if d is None: + return 1 + elif d[0] == 0: # IPv6 address + return 0 + elif d[0] == 1: # Name + return 1 + elif d[0] == 2: # IPv4 address + return 2 + else: + return 1 + return x + + +_niquery_code = {0: "IPv6 Query", 1: "Name Query", 2: "IPv4 Query"} + +#_niquery_flags = { 2: "All unicast addresses", 4: "IPv4 addresses", +# 8: "Link-local addresses", 16: "Site-local addresses", +# 32: "Global addresses" } + +# "This NI type has no defined flags and never has a Data Field". Used +# to know if the destination is up and implements NI protocol. +class ICMPv6NIQueryNOOP(_ICMPv6NIHashret, _ICMPv6): + name = "ICMPv6 Node Information Query - NOOP Query" + fields_desc = [ ByteEnumField("type", 139, icmp6types), + NIQueryCodeField("code", None, _niquery_code), + XShortField("cksum", None), + ShortEnumField("qtype", 0, icmp6_niqtypes), + BitField("unused", 0, 10), + FlagsField("flags", 0, 6, "TACLSG"), + NonceField("nonce", None), + NIQueryDataField("data", None) ] + +class ICMPv6NIQueryName(ICMPv6NIQueryNOOP): + name = "ICMPv6 Node Information Query - IPv6 Name Query" + qtype = 2 + +# We ask for the IPv6 address of the peer +class ICMPv6NIQueryIPv6(ICMPv6NIQueryNOOP): + name = "ICMPv6 Node Information Query - IPv6 Address Query" + qtype = 3 + flags = 0x3E + +class ICMPv6NIQueryIPv4(ICMPv6NIQueryNOOP): + name = "ICMPv6 Node Information Query - IPv4 Address Query" + qtype = 4 + +_nireply_code = { 0: "Successful Reply", + 1: "Response Refusal", + 3: "Unknown query type" } + +_nireply_flags = { 1: "Reply set incomplete", + 2: "All unicast addresses", + 4: "IPv4 addresses", + 8: "Link-local addresses", + 16: "Site-local addresses", + 32: "Global addresses" } + +# Internal repr is one of those : +# (0, "some string") : unknow qtype value are mapped to that one +# (3, [ (ttl, ip6), ... ]) +# (4, [ (ttl, ip4), ... ]) +# (2, [ttl, dns_names]) : dns_names is one string that contains +# all the DNS names. Internally it is kept ready to be sent +# (undissected). i2repr() decode it for user. This is to +# make build after dissection bijective. +# +# I also merged getfield() and m2i(), and addfield() and i2m(). +class NIReplyDataField(StrField): + + def i2h(self, pkt, x): + if x is None: + return x + t,val = x + if t == 2: + ttl, dnsnames = val + val = [ttl] + dnsrepr2names(dnsnames) + return val + + def h2i(self, pkt, x): + qtype = 0 # We will decode it as string if not + # overridden through 'qtype' in pkt + + # No user hint, let's use 'qtype' value for that purpose + if type(x) is not tuple: + if pkt is not None: + qtype = getattr(pkt, "qtype") + else: + qtype = x[0] + x = x[1] + + # From that point on, x is the value (second element of the tuple) + + if qtype == 2: # DNS name + if type(x) is str: # listify the string + x = [x] + if type(x) is list and x and type(x[0]) is not int: # ttl was omitted : use 0 + x = [0] + x + ttl = x[0] + names = x[1:] + return (2, [ttl, names2dnsrepr(names)]) + + elif qtype in [3, 4]: # IPv4 or IPv6 addr + if type(x) is str: + x = [x] # User directly provided an IP, instead of list + + # List elements are not tuples, user probably + # omitted ttl value : we will use 0 instead + def addttl(x): + if type(x) is str: + return (0, x) + return x + + return (qtype, map(addttl, x)) + + return (qtype, x) + + + def addfield(self, pkt, s, val): + t,tmp = val + if tmp is None: + tmp = "" + if t == 2: + ttl,dnsstr = tmp + return s+ struct.pack("!I", ttl) + dnsstr + elif t == 3: + return s + "".join(map(lambda (x,y): struct.pack("!I", x)+inet_pton(socket.AF_INET6, y), tmp)) + elif t == 4: + return s + "".join(map(lambda (x,y): struct.pack("!I", x)+inet_pton(socket.AF_INET, y), tmp)) + else: + return s + tmp + + def getfield(self, pkt, s): + code = getattr(pkt, "code") + if code != 0: + return s, (0, "") + + qtype = getattr(pkt, "qtype") + if qtype == 0: # NOOP + return s, (0, "") + + elif qtype == 2: + if len(s) < 4: + return s, (0, "") + ttl = struct.unpack("!I", s[:4])[0] + return "", (2, [ttl, s[4:]]) + + elif qtype == 3: # IPv6 addresses with TTLs + # XXX TODO : get the real length + res = [] + while len(s) >= 20: # 4 + 16 + ttl = struct.unpack("!I", s[:4])[0] + ip = inet_ntop(socket.AF_INET6, s[4:20]) + res.append((ttl, ip)) + s = s[20:] + return s, (3, res) + + elif qtype == 4: # IPv4 addresses with TTLs + # XXX TODO : get the real length + res = [] + while len(s) >= 8: # 4 + 4 + ttl = struct.unpack("!I", s[:4])[0] + ip = inet_ntop(socket.AF_INET, s[4:8]) + res.append((ttl, ip)) + s = s[8:] + return s, (4, res) + else: + # XXX TODO : implement me and deal with real length + return "", (0, s) + + def i2repr(self, pkt, x): + if x is None: + return "[]" + + if type(x) is tuple and len(x) == 2: + t, val = x + if t == 2: # DNS names + ttl,l = val + l = dnsrepr2names(l) + return "ttl:%d %s" % (ttl, ", ".join(l)) + elif t == 3 or t == 4: + return "[ %s ]" % (", ".join(map(lambda (x,y): "(%d, %s)" % (x, y), val))) + return repr(val) + return repr(x) # XXX should not happen + +# By default, sent responses have code set to 0 (successful) +class ICMPv6NIReplyNOOP(_ICMPv6NIAnswers, _ICMPv6NIHashret, _ICMPv6): + name = "ICMPv6 Node Information Reply - NOOP Reply" + fields_desc = [ ByteEnumField("type", 140, icmp6types), + ByteEnumField("code", 0, _nireply_code), + XShortField("cksum", None), + ShortEnumField("qtype", 0, icmp6_niqtypes), + BitField("unused", 0, 10), + FlagsField("flags", 0, 6, "TACLSG"), + NonceField("nonce", None), + NIReplyDataField("data", None)] + +class ICMPv6NIReplyName(ICMPv6NIReplyNOOP): + name = "ICMPv6 Node Information Reply - Node Names" + qtype = 2 + +class ICMPv6NIReplyIPv6(ICMPv6NIReplyNOOP): + name = "ICMPv6 Node Information Reply - IPv6 addresses" + qtype = 3 + +class ICMPv6NIReplyIPv4(ICMPv6NIReplyNOOP): + name = "ICMPv6 Node Information Reply - IPv4 addresses" + qtype = 4 + +class ICMPv6NIReplyRefuse(ICMPv6NIReplyNOOP): + name = "ICMPv6 Node Information Reply - Responder refuses to supply answer" + code = 1 + +class ICMPv6NIReplyUnknown(ICMPv6NIReplyNOOP): + name = "ICMPv6 Node Information Reply - Qtype unknown to the responder" + code = 2 + + +def _niquery_guesser(p): + cls = conf.raw_layer + type = ord(p[0]) + if type == 139: # Node Info Query specific stuff + if len(p) > 6: + qtype, = struct.unpack("!H", p[4:6]) + cls = { 0: ICMPv6NIQueryNOOP, + 2: ICMPv6NIQueryName, + 3: ICMPv6NIQueryIPv6, + 4: ICMPv6NIQueryIPv4 }.get(qtype, conf.raw_layer) + elif type == 140: # Node Info Reply specific stuff + code = ord(p[1]) + if code == 0: + if len(p) > 6: + qtype, = struct.unpack("!H", p[4:6]) + cls = { 2: ICMPv6NIReplyName, + 3: ICMPv6NIReplyIPv6, + 4: ICMPv6NIReplyIPv4 }.get(qtype, ICMPv6NIReplyNOOP) + elif code == 1: + cls = ICMPv6NIReplyRefuse + elif code == 2: + cls = ICMPv6NIReplyUnknown + return cls + + +############################################################################# +############################################################################# +### Mobile IPv6 (RFC 3775) and Nemo (RFC 3963) ### +############################################################################# +############################################################################# + +# Mobile IPv6 ICMPv6 related classes + +class ICMPv6HAADRequest(_ICMPv6): + name = 'ICMPv6 Home Agent Address Discovery Request' + fields_desc = [ ByteEnumField("type", 144, icmp6types), + ByteField("code", 0), + XShortField("cksum", None), + XShortField("id", None), + BitEnumField("R", 1, 1, {1: 'MR'}), + XBitField("res", 0, 15) ] + def hashret(self): + return struct.pack("!H",self.id)+self.payload.hashret() + +class ICMPv6HAADReply(_ICMPv6): + name = 'ICMPv6 Home Agent Address Discovery Reply' + fields_desc = [ ByteEnumField("type", 145, icmp6types), + ByteField("code", 0), + XShortField("cksum", None), + XShortField("id", None), + BitEnumField("R", 1, 1, {1: 'MR'}), + XBitField("res", 0, 15), + IP6ListField('addresses', None) ] + def hashret(self): + return struct.pack("!H",self.id)+self.payload.hashret() + + def answers(self, other): + if not isinstance(other, ICMPv6HAADRequest): + return 0 + return self.id == other.id + +class ICMPv6MPSol(_ICMPv6): + name = 'ICMPv6 Mobile Prefix Solicitation' + fields_desc = [ ByteEnumField("type", 146, icmp6types), + ByteField("code", 0), + XShortField("cksum", None), + XShortField("id", None), + XShortField("res", 0) ] + def _hashret(self): + return struct.pack("!H",self.id) + +class ICMPv6MPAdv(_ICMPv6NDGuessPayload, _ICMPv6): + name = 'ICMPv6 Mobile Prefix Advertisement' + fields_desc = [ ByteEnumField("type", 147, icmp6types), + ByteField("code", 0), + XShortField("cksum", None), + XShortField("id", None), + BitEnumField("flags", 2, 2, {2: 'M', 1:'O'}), + XBitField("res", 0, 14) ] + def hashret(self): + return struct.pack("!H",self.id) + + def answers(self, other): + return isinstance(other, ICMPv6MPSol) + +# Mobile IPv6 Options classes + + +_mobopttypes = { 2: "Binding Refresh Advice", + 3: "Alternate Care-of Address", + 4: "Nonce Indices", + 5: "Binding Authorization Data", + 6: "Mobile Network Prefix (RFC3963)", + 7: "Link-Layer Address (RFC4068)", + 8: "Mobile Node Identifier (RFC4283)", + 9: "Mobility Message Authentication (RFC4285)", + 10: "Replay Protection (RFC4285)", + 11: "CGA Parameters Request (RFC4866)", + 12: "CGA Parameters (RFC4866)", + 13: "Signature (RFC4866)", + 14: "Home Keygen Token (RFC4866)", + 15: "Care-of Test Init (RFC4866)", + 16: "Care-of Test (RFC4866)" } + + +class _MIP6OptAlign: + """ Mobile IPv6 options have alignment requirements of the form x*n+y. + This class is inherited by all MIPv6 options to help in computing the + required Padding for that option, i.e. the need for a Pad1 or PadN + option before it. They only need to provide x and y as class + parameters. (x=0 and y=0 are used when no alignment is required)""" + def alignment_delta(self, curpos): + x = self.x ; y = self.y + if x == 0 and y ==0: + return 0 + delta = x*((curpos - y + x - 1)/x) + y - curpos + return delta + + +class MIP6OptBRAdvice(_MIP6OptAlign, Packet): + name = 'Mobile IPv6 Option - Binding Refresh Advice' + fields_desc = [ ByteEnumField('otype', 2, _mobopttypes), + ByteField('olen', 2), + ShortField('rinter', 0) ] + x = 2 ; y = 0# alignment requirement: 2n + +class MIP6OptAltCoA(_MIP6OptAlign, Packet): + name = 'MIPv6 Option - Alternate Care-of Address' + fields_desc = [ ByteEnumField('otype', 3, _mobopttypes), + ByteField('olen', 16), + IP6Field("acoa", "::") ] + x = 8 ; y = 6 # alignment requirement: 8n+6 + +class MIP6OptNonceIndices(_MIP6OptAlign, Packet): + name = 'MIPv6 Option - Nonce Indices' + fields_desc = [ ByteEnumField('otype', 4, _mobopttypes), + ByteField('olen', 16), + ShortField('hni', 0), + ShortField('coni', 0) ] + x = 2 ; y = 0 # alignment requirement: 2n + +class MIP6OptBindingAuthData(_MIP6OptAlign, Packet): + name = 'MIPv6 Option - Binding Authorization Data' + fields_desc = [ ByteEnumField('otype', 5, _mobopttypes), + ByteField('olen', 16), + BitField('authenticator', 0, 96) ] + x = 8 ; y = 2 # alignment requirement: 8n+2 + +class MIP6OptMobNetPrefix(_MIP6OptAlign, Packet): # NEMO - RFC 3963 + name = 'NEMO Option - Mobile Network Prefix' + fields_desc = [ ByteEnumField("otype", 6, _mobopttypes), + ByteField("olen", 18), + ByteField("reserved", 0), + ByteField("plen", 64), + IP6Field("prefix", "::") ] + x = 8 ; y = 4 # alignment requirement: 8n+4 + +class MIP6OptLLAddr(_MIP6OptAlign, Packet): # Sect 6.4.4 of RFC 4068 + name = "MIPv6 Option - Link-Layer Address (MH-LLA)" + fields_desc = [ ByteEnumField("otype", 7, _mobopttypes), + ByteField("olen", 7), + ByteEnumField("ocode", 2, _rfc4068_lla_optcode), + ByteField("pad", 0), + MACField("lla", ETHER_ANY) ] # Only support ethernet + x = 0 ; y = 0 # alignment requirement: none + +class MIP6OptMNID(_MIP6OptAlign, Packet): # RFC 4283 + name = "MIPv6 Option - Mobile Node Identifier" + fields_desc = [ ByteEnumField("otype", 8, _mobopttypes), + FieldLenField("olen", None, length_of="id", fmt="B", + adjust = lambda pkt,x: x+1), + ByteEnumField("subtype", 1, {1: "NAI"}), + StrLenField("id", "", + length_from = lambda pkt: pkt.olen-1) ] + x = 0 ; y = 0 # alignment requirement: none + +# We only support decoding and basic build. Automatic HMAC computation is +# too much work for our current needs. It is left to the user (I mean ... +# you). --arno +class MIP6OptMsgAuth(_MIP6OptAlign, Packet): # RFC 4285 (Sect. 5) + name = "MIPv6 Option - Mobility Message Authentication" + fields_desc = [ ByteEnumField("otype", 9, _mobopttypes), + FieldLenField("olen", None, length_of="authdata", fmt="B", + adjust = lambda pkt,x: x+5), + ByteEnumField("subtype", 1, {1: "MN-HA authentication mobility option", + 2: "MN-AAA authentication mobility option"}), + IntField("mspi", None), + StrLenField("authdata", "A"*12, + length_from = lambda pkt: pkt.olen-5) ] + x = 4 ; y = 1 # alignment requirement: 4n+1 + +# Extracted from RFC 1305 (NTP) : +# NTP timestamps are represented as a 64-bit unsigned fixed-point number, +# in seconds relative to 0h on 1 January 1900. The integer part is in the +# first 32 bits and the fraction part in the last 32 bits. +class NTPTimestampField(LongField): + epoch = (1900, 1, 1, 0, 0, 0, 5, 1, 0) + def i2repr(self, pkt, x): + if x < ((50*31536000)<<32): + return "Some date a few decades ago (%d)" % x + + # delta from epoch (= (1900, 1, 1, 0, 0, 0, 5, 1, 0)) to + # January 1st 1970 : + delta = -2209075761 + i = int(x >> 32) + j = float(x & 0xffffffff) * 2.0**-32 + res = i + j + delta + from time import strftime + t = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(res)) + + return "%s (%d)" % (t, x) + +class MIP6OptReplayProtection(_MIP6OptAlign, Packet): # RFC 4285 (Sect. 6) + name = "MIPv6 option - Replay Protection" + fields_desc = [ ByteEnumField("otype", 10, _mobopttypes), + ByteField("olen", 8), + NTPTimestampField("timestamp", 0) ] + x = 8 ; y = 2 # alignment requirement: 8n+2 + +class MIP6OptCGAParamsReq(_MIP6OptAlign, Packet): # RFC 4866 (Sect. 5.6) + name = "MIPv6 option - CGA Parameters Request" + fields_desc = [ ByteEnumField("otype", 11, _mobopttypes), + ByteField("olen", 0) ] + x = 0 ; y = 0 # alignment requirement: none + +# XXX TODO: deal with CGA param fragmentation and build of defragmented +# XXX version. Passing of a big CGAParam structure should be +# XXX simplified. Make it hold packets, by the way --arno +class MIP6OptCGAParams(_MIP6OptAlign, Packet): # RFC 4866 (Sect. 5.1) + name = "MIPv6 option - CGA Parameters" + fields_desc = [ ByteEnumField("otype", 12, _mobopttypes), + FieldLenField("olen", None, length_of="cgaparams", fmt="B"), + StrLenField("cgaparams", "", + length_from = lambda pkt: pkt.olen) ] + x = 0 ; y = 0 # alignment requirement: none + +class MIP6OptSignature(_MIP6OptAlign, Packet): # RFC 4866 (Sect. 5.2) + name = "MIPv6 option - Signature" + fields_desc = [ ByteEnumField("otype", 13, _mobopttypes), + FieldLenField("olen", None, length_of="sig", fmt="B"), + StrLenField("sig", "", + length_from = lambda pkt: pkt.olen) ] + x = 0 ; y = 0 # alignment requirement: none + +class MIP6OptHomeKeygenToken(_MIP6OptAlign, Packet): # RFC 4866 (Sect. 5.3) + name = "MIPv6 option - Home Keygen Token" + fields_desc = [ ByteEnumField("otype", 14, _mobopttypes), + FieldLenField("olen", None, length_of="hkt", fmt="B"), + StrLenField("hkt", "", + length_from = lambda pkt: pkt.olen) ] + x = 0 ; y = 0 # alignment requirement: none + +class MIP6OptCareOfTestInit(_MIP6OptAlign, Packet): # RFC 4866 (Sect. 5.4) + name = "MIPv6 option - Care-of Test Init" + fields_desc = [ ByteEnumField("otype", 15, _mobopttypes), + ByteField("olen", 0) ] + x = 0 ; y = 0 # alignment requirement: none + +class MIP6OptCareOfTest(_MIP6OptAlign, Packet): # RFC 4866 (Sect. 5.5) + name = "MIPv6 option - Care-of Test" + fields_desc = [ ByteEnumField("otype", 16, _mobopttypes), + FieldLenField("olen", None, length_of="cokt", fmt="B"), + StrLenField("cokt", '\x00'*8, + length_from = lambda pkt: pkt.olen) ] + x = 0 ; y = 0 # alignment requirement: none + +class MIP6OptUnknown(_MIP6OptAlign, Packet): + name = 'Scapy6 - Unknown Mobility Option' + fields_desc = [ ByteEnumField("otype", 6, _mobopttypes), + FieldLenField("olen", None, length_of="odata", fmt="B"), + StrLenField("odata", "", + length_from = lambda pkt: pkt.olen) ] + x = 0 ; y = 0 # alignment requirement: none + +moboptcls = { 0: Pad1, + 1: PadN, + 2: MIP6OptBRAdvice, + 3: MIP6OptAltCoA, + 4: MIP6OptNonceIndices, + 5: MIP6OptBindingAuthData, + 6: MIP6OptMobNetPrefix, + 7: MIP6OptLLAddr, + 8: MIP6OptMNID, + 9: MIP6OptMsgAuth, + 10: MIP6OptReplayProtection, + 11: MIP6OptCGAParamsReq, + 12: MIP6OptCGAParams, + 13: MIP6OptSignature, + 14: MIP6OptHomeKeygenToken, + 15: MIP6OptCareOfTestInit, + 16: MIP6OptCareOfTest } + + +# Main Mobile IPv6 Classes + +mhtypes = { 0: 'BRR', + 1: 'HoTI', + 2: 'CoTI', + 3: 'HoT', + 4: 'CoT', + 5: 'BU', + 6: 'BA', + 7: 'BE', + 8: 'Fast BU', + 9: 'Fast BA', + 10: 'Fast NA' } + +# From http://www.iana.org/assignments/mobility-parameters +bastatus = { 0: 'Binding Update accepted', + 1: 'Accepted but prefix discovery necessary', + 128: 'Reason unspecified', + 129: 'Administratively prohibited', + 130: 'Insufficient resources', + 131: 'Home registration not supported', + 132: 'Not home subnet', + 133: 'Not home agent for this mobile node', + 134: 'Duplicate Address Detection failed', + 135: 'Sequence number out of window', + 136: 'Expired home nonce index', + 137: 'Expired care-of nonce index', + 138: 'Expired nonces', + 139: 'Registration type change disallowed', + 140: 'Mobile Router Operation not permitted', + 141: 'Invalid Prefix', + 142: 'Not Authorized for Prefix', + 143: 'Forwarding Setup failed (prefixes missing)', + 144: 'MIPV6-ID-MISMATCH', + 145: 'MIPV6-MESG-ID-REQD', + 146: 'MIPV6-AUTH-FAIL', + 147: 'Permanent home keygen token unavailable', + 148: 'CGA and signature verification failed', + 149: 'Permanent home keygen token exists', + 150: 'Non-null home nonce index expected' } + + +class _MobilityHeader(Packet): + name = 'Dummy IPv6 Mobility Header' + overload_fields = { IPv6: { "nh": 135 }} + + def post_build(self, p, pay): + p += pay + l = self.len + if self.len is None: + l = (len(p)-8)/8 + p = p[0] + struct.pack("B", l) + p[2:] + if self.cksum is None: + cksum = in6_chksum(135, self.underlayer, p) + else: + cksum = self.cksum + p = p[:4]+struct.pack("!H", cksum)+p[6:] + return p + + +class MIP6MH_Generic(_MobilityHeader): # Mainly for decoding of unknown msg + name = "IPv6 Mobility Header - Generic Message" + fields_desc = [ ByteEnumField("nh", 59, ipv6nh), + ByteField("len", None), + ByteEnumField("mhtype", None, mhtypes), + ByteField("res", None), + XShortField("cksum", None), + StrLenField("msg", "\x00"*2, + length_from = lambda pkt: 8*pkt.len-6) ] + + + +# TODO: make a generic _OptionsField +class _MobilityOptionsField(PacketListField): + islist = 1 + holds_packet = 1 + + def __init__(self, name, default, cls, curpos, count_from=None, length_from=None): + self.curpos = curpos + PacketListField.__init__(self, name, default, cls, count_from=count_from, length_from=length_from) + + def getfield(self, pkt, s): + l = self.length_from(pkt) + return s[l:],self.m2i(pkt, s[:l]) + + def i2len(self, pkt, i): + return len(self.i2m(pkt, i)) + + def m2i(self, pkt, x): + opt = [] + while x: + o = ord(x[0]) # Option type + cls = self.cls + if moboptcls.has_key(o): + cls = moboptcls[o] + try: + op = cls(x) + except: + op = self.cls(x) + opt.append(op) + if isinstance(op.payload, conf.raw_layer): + x = op.payload.load + del(op.payload) + else: + x = "" + return opt + + def i2m(self, pkt, x): + autopad = None + try: + autopad = getattr(pkt, "autopad") # Hack : 'autopad' phantom field + except: + autopad = 1 + + if not autopad: + return "".join(map(str, x)) + + curpos = self.curpos + s = "" + for p in x: + d = p.alignment_delta(curpos) + curpos += d + if d == 1: + s += str(Pad1()) + elif d != 0: + s += str(PadN(optdata='\x00'*(d-2))) + pstr = str(p) + curpos += len(pstr) + s += pstr + + # Let's make the class including our option field + # a multiple of 8 octets long + d = curpos % 8 + if d == 0: + return s + d = 8 - d + if d == 1: + s += str(Pad1()) + elif d != 0: + s += str(PadN(optdata='\x00'*(d-2))) + + return s + + def addfield(self, pkt, s, val): + return s+self.i2m(pkt, val) + +class MIP6MH_BRR(_MobilityHeader): + name = "IPv6 Mobility Header - Binding Refresh Request" + fields_desc = [ ByteEnumField("nh", 59, ipv6nh), + ByteField("len", None), + ByteEnumField("mhtype", 0, mhtypes), + ByteField("res", None), + XShortField("cksum", None), + ShortField("res2", None), + _PhantomAutoPadField("autopad", 1), # autopad activated by default + _MobilityOptionsField("options", [], MIP6OptUnknown, 8, + length_from = lambda pkt: 8*pkt.len) ] + overload_fields = { IPv6: { "nh": 135 } } + def hashret(self): + # Hack: BRR, BU and BA have the same hashret that returns the same + # value "\x00\x08\x09" (concatenation of mhtypes). This is + # because we need match BA with BU and BU with BRR. --arno + return "\x00\x08\x09" + +class MIP6MH_HoTI(_MobilityHeader): + name = "IPv6 Mobility Header - Home Test Init" + fields_desc = [ ByteEnumField("nh", 59, ipv6nh), + ByteField("len", None), + ByteEnumField("mhtype", 1, mhtypes), + ByteField("res", None), + XShortField("cksum", None), + StrFixedLenField("reserved", "\x00"*2, 2), + StrFixedLenField("cookie", "\x00"*8, 8), + _PhantomAutoPadField("autopad", 1), # autopad activated by default + _MobilityOptionsField("options", [], MIP6OptUnknown, 16, + length_from = lambda pkt: 8*(pkt.len-1)) ] + overload_fields = { IPv6: { "nh": 135 } } + def hashret(self): + return self.cookie + +class MIP6MH_CoTI(MIP6MH_HoTI): + name = "IPv6 Mobility Header - Care-of Test Init" + mhtype = 2 + def hashret(self): + return self.cookie + +class MIP6MH_HoT(_MobilityHeader): + name = "IPv6 Mobility Header - Home Test" + fields_desc = [ ByteEnumField("nh", 59, ipv6nh), + ByteField("len", None), + ByteEnumField("mhtype", 3, mhtypes), + ByteField("res", None), + XShortField("cksum", None), + ShortField("index", None), + StrFixedLenField("cookie", "\x00"*8, 8), + StrFixedLenField("token", "\x00"*8, 8), + _PhantomAutoPadField("autopad", 1), # autopad activated by default + _MobilityOptionsField("options", [], MIP6OptUnknown, 24, + length_from = lambda pkt: 8*(pkt.len-2)) ] + overload_fields = { IPv6: { "nh": 135 } } + def hashret(self): + return self.cookie + def answers(self): + if (isinstance(other, MIP6MH_HoTI) and + self.cookie == other.cookie): + return 1 + return 0 + +class MIP6MH_CoT(MIP6MH_HoT): + name = "IPv6 Mobility Header - Care-of Test" + mhtype = 4 + def hashret(self): + return self.cookie + + def answers(self): + if (isinstance(other, MIP6MH_CoTI) and + self.cookie == other.cookie): + return 1 + return 0 + +class LifetimeField(ShortField): + def i2repr(self, pkt, x): + return "%d sec" % (4*x) + +class MIP6MH_BU(_MobilityHeader): + name = "IPv6 Mobility Header - Binding Update" + fields_desc = [ ByteEnumField("nh", 59, ipv6nh), + ByteField("len", None), # unit == 8 bytes (excluding the first 8 bytes) + ByteEnumField("mhtype", 5, mhtypes), + ByteField("res", None), + XShortField("cksum", None), + XShortField("seq", None), # TODO: ShortNonceField + FlagsField("flags", "KHA", 7, "PRMKLHA"), + XBitField("reserved", 0, 9), + LifetimeField("mhtime", 3), # unit == 4 seconds + _PhantomAutoPadField("autopad", 1), # autopad activated by default + _MobilityOptionsField("options", [], MIP6OptUnknown, 12, + length_from = lambda pkt: 8*pkt.len - 4) ] + overload_fields = { IPv6: { "nh": 135 } } + + def hashret(self): # Hack: see comment in MIP6MH_BRR.hashret() + return "\x00\x08\x09" + + def answers(self, other): + if isinstance(other, MIP6MH_BRR): + return 1 + return 0 + +class MIP6MH_BA(_MobilityHeader): + name = "IPv6 Mobility Header - Binding ACK" + fields_desc = [ ByteEnumField("nh", 59, ipv6nh), + ByteField("len", None), # unit == 8 bytes (excluding the first 8 bytes) + ByteEnumField("mhtype", 6, mhtypes), + ByteField("res", None), + XShortField("cksum", None), + ByteEnumField("status", 0, bastatus), + FlagsField("flags", "K", 3, "PRK"), + XBitField("res2", None, 5), + XShortField("seq", None), # TODO: ShortNonceField + XShortField("mhtime", 0), # unit == 4 seconds + _PhantomAutoPadField("autopad", 1), # autopad activated by default + _MobilityOptionsField("options", [], MIP6OptUnknown, 12, + length_from = lambda pkt: 8*pkt.len-4) ] + overload_fields = { IPv6: { "nh": 135 }} + + def hashret(self): # Hack: see comment in MIP6MH_BRR.hashret() + return "\x00\x08\x09" + + def answers(self, other): + if (isinstance(other, MIP6MH_BU) and + other.mhtype == 5 and + self.mhtype == 6 and + other.flags & 0x1 and # Ack request flags is set + self.seq == other.seq): + return 1 + return 0 + +_bestatus = { 1: 'Unknown binding for Home Address destination option', + 2: 'Unrecognized MH Type value' } + +# TODO: match Binding Error to its stimulus +class MIP6MH_BE(_MobilityHeader): + name = "IPv6 Mobility Header - Binding Error" + fields_desc = [ ByteEnumField("nh", 59, ipv6nh), + ByteField("len", None), # unit == 8 bytes (excluding the first 8 bytes) + ByteEnumField("mhtype", 7, mhtypes), + ByteField("res", 0), + XShortField("cksum", None), + ByteEnumField("status", 0, _bestatus), + ByteField("reserved", 0), + IP6Field("ha", "::"), + _MobilityOptionsField("options", [], MIP6OptUnknown, 24, + length_from = lambda pkt: 8*(pkt.len-2)) ] + overload_fields = { IPv6: { "nh": 135 }} + +_mip6_mhtype2cls = { 0: MIP6MH_BRR, + 1: MIP6MH_HoTI, + 2: MIP6MH_CoTI, + 3: MIP6MH_HoT, + 4: MIP6MH_CoT, + 5: MIP6MH_BU, + 6: MIP6MH_BA, + 7: MIP6MH_BE } + + +############################################################################# +############################################################################# +### Traceroute6 ### +############################################################################# +############################################################################# + +class AS_resolver6(AS_resolver_riswhois): + def _resolve_one(self, ip): + """ + overloaded version to provide a Whois resolution on the + embedded IPv4 address if the address is 6to4 or Teredo. + Otherwise, the native IPv6 address is passed. + """ + + if in6_isaddr6to4(ip): # for 6to4, use embedded @ + tmp = inet_pton(socket.AF_INET6, ip) + addr = inet_ntop(socket.AF_INET, tmp[2:6]) + elif in6_isaddrTeredo(ip): # for Teredo, use mapped address + addr = teredoAddrExtractInfo(ip)[2] + else: + addr = ip + + _, asn, desc = AS_resolver_riswhois._resolve_one(self, addr) + + return ip,asn,desc + +class TracerouteResult6(TracerouteResult): + def show(self): + return self.make_table(lambda (s,r): (s.sprintf("%-42s,IPv6.dst%:{TCP:tcp%TCP.dport%}{UDP:udp%UDP.dport%}{ICMPv6EchoRequest:IER}"), # TODO: ICMPv6 ! + s.hlim, + r.sprintf("%-42s,IPv6.src% {TCP:%TCP.flags%}"+ + "{ICMPv6DestUnreach:%ir,type%}{ICMPv6PacketTooBig:%ir,type%}"+ + "{ICMPv6TimeExceeded:%ir,type%}{ICMPv6ParamProblem:%ir,type%}"+ + "{ICMPv6EchoReply:%ir,type%}"))) + + def get_trace(self): + trace = {} + + for s,r in self.res: + if IPv6 not in s: + continue + d = s[IPv6].dst + if d not in trace: + trace[d] = {} + + t = not (ICMPv6TimeExceeded in r or + ICMPv6DestUnreach in r or + ICMPv6PacketTooBig in r or + ICMPv6ParamProblem in r) + + trace[d][s[IPv6].hlim] = r[IPv6].src, t + + for k in trace.values(): + m = filter(lambda x: k[x][1], k.keys()) + if not m: + continue + m = min(m) + for l in k.keys(): + if l > m: + del(k[l]) + + return trace + + def graph(self, ASres=AS_resolver6(), **kargs): + TracerouteResult.graph(self, ASres=ASres, **kargs) + +def traceroute6(target, dport=80, minttl=1, maxttl=30, sport=RandShort(), + l4 = None, timeout=2, verbose=None, **kargs): + """ + Instant TCP traceroute using IPv6 : + traceroute6(target, [maxttl=30], [dport=80], [sport=80]) -> None + """ + if verbose is None: + verbose = conf.verb + + if l4 is None: + a,b = sr(IPv6(dst=target, hlim=(minttl,maxttl))/TCP(seq=RandInt(),sport=sport, dport=dport), + timeout=timeout, filter="icmp6 or tcp", verbose=verbose, **kargs) + else: + a,b = sr(IPv6(dst=target, hlim=(minttl,maxttl))/l4, + timeout=timeout, verbose=verbose, **kargs) + + a = TracerouteResult6(a.res) + + if verbose: + a.display() + + return a,b + +############################################################################# +############################################################################# +### Sockets ### +############################################################################# +############################################################################# + +class L3RawSocket6(L3RawSocket): + def __init__(self, type = ETH_P_IPV6, filter=None, iface=None, promisc=None, nofilter=0): + L3RawSocket.__init__(self, type, filter, iface, promisc) + # NOTE: if fragmentation is needed, it will be done by the kernel (RFC 2292) + self.outs = socket.socket(socket.AF_INET6, socket.SOCK_RAW, socket.IPPROTO_RAW) + self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) + +def IPv6inIP(dst='203.178.135.36', src=None): + _IPv6inIP.dst = dst + _IPv6inIP.src = src + if not conf.L3socket == _IPv6inIP: + _IPv6inIP.cls = conf.L3socket + else: + del(conf.L3socket) + return _IPv6inIP + +class _IPv6inIP(SuperSocket): + dst = '127.0.0.1' + src = None + cls = None + + def __init__(self, family=socket.AF_INET6, type=socket.SOCK_STREAM, proto=0, **args): + SuperSocket.__init__(self, family, type, proto) + self.worker = self.cls(**args) + + def set(self, dst, src=None): + _IPv6inIP.src = src + _IPv6inIP.dst = dst + + def nonblock_recv(self): + p = self.worker.nonblock_recv() + return self._recv(p) + + def recv(self, x): + p = self.worker.recv(x) + return self._recv(p, x) + + def _recv(self, p, x=MTU): + if p is None: + return p + elif isinstance(p, IP): + # TODO: verify checksum + if p.src == self.dst and p.proto == socket.IPPROTO_IPV6: + if isinstance(p.payload, IPv6): + return p.payload + return p + + def send(self, x): + return self.worker.send(IP(dst=self.dst, src=self.src, proto=socket.IPPROTO_IPV6)/x) + + +############################################################################# +############################################################################# +### Layers binding ### +############################################################################# +############################################################################# + +conf.l3types.register(ETH_P_IPV6, IPv6) +conf.l2types.register(31, IPv6) + +bind_layers(Ether, IPv6, type = 0x86dd ) +bind_layers(CookedLinux, IPv6, proto = 0x86dd ) +bind_layers(IPerror6, TCPerror, nh = socket.IPPROTO_TCP ) +bind_layers(IPerror6, UDPerror, nh = socket.IPPROTO_UDP ) +bind_layers(IPv6, TCP, nh = socket.IPPROTO_TCP ) +bind_layers(IPv6, UDP, nh = socket.IPPROTO_UDP ) +bind_layers(IP, IPv6, proto = socket.IPPROTO_IPV6 ) +bind_layers(IPv6, IPv6, nh = socket.IPPROTO_IPV6 ) +bind_layers(IPv6, IP, nh = IPPROTO_IPIP ) diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/ipsec.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/ipsec.py new file mode 100644 index 00000000..692a6e18 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/ipsec.py @@ -0,0 +1,981 @@ +############################################################################# +## ipsec.py --- IPSec support for Scapy ## +## ## +## Copyright (C) 2014 6WIND ## +## ## +## This program is free software; you can redistribute it and/or modify it ## +## under the terms of the GNU General Public License version 2 as ## +## published by the Free Software Foundation. ## +## ## +## This program is distributed in the hope that it will be useful, but ## +## WITHOUT ANY WARRANTY; without even the implied warranty of ## +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## +## General Public License for more details. ## +############################################################################# +""" +IPSec layer +=========== + +Example of use: + +>>> sa = SecurityAssociation(ESP, spi=0xdeadbeef, crypt_algo='AES-CBC', +... crypt_key='sixteenbytes key') +>>> p = IP(src='1.1.1.1', dst='2.2.2.2') +>>> p /= TCP(sport=45012, dport=80) +>>> p /= Raw('testdata') +>>> p = IP(str(p)) +>>> p +>> +>>> +>>> e = sa.encrypt(p) +>>> e +> +>>> +>>> d = sa.decrypt(e) +>>> d +>> +>>> +>>> d == p +True +""" + +import socket +import fractions + +from scapy.data import IP_PROTOS + +from scapy.fields import ByteEnumField, ByteField, StrField, XIntField, IntField, \ + ShortField, PacketField + +from scapy.packet import Packet, bind_layers, Raw + +from scapy.layers.inet import IP, UDP +from scapy.layers.inet6 import IPv6, IPv6ExtHdrHopByHop, IPv6ExtHdrDestOpt, \ + IPv6ExtHdrRouting + + +#------------------------------------------------------------------------------ +class AH(Packet): + """ + Authentication Header + + See https://tools.ietf.org/rfc/rfc4302.txt + """ + + name = 'AH' + + fields_desc = [ + ByteEnumField('nh', None, IP_PROTOS), + ByteField('payloadlen', None), + ShortField('reserved', None), + XIntField('spi', 0x0), + IntField('seq', 0), + StrField('icv', None), + StrField('padding', None), + ] + + overload_fields = { + IP: {'proto': socket.IPPROTO_AH}, + IPv6: {'nh': socket.IPPROTO_AH}, + IPv6ExtHdrHopByHop: {'nh': socket.IPPROTO_AH}, + IPv6ExtHdrDestOpt: {'nh': socket.IPPROTO_AH}, + IPv6ExtHdrRouting: {'nh': socket.IPPROTO_AH}, + } + +bind_layers(IP, AH, proto=socket.IPPROTO_AH) +bind_layers(IPv6, AH, nh=socket.IPPROTO_AH) + +#------------------------------------------------------------------------------ +class ESP(Packet): + """ + Encapsulated Security Payload + + See https://tools.ietf.org/rfc/rfc4303.txt + """ + name = 'ESP' + + fields_desc = [ + XIntField('spi', 0x0), + IntField('seq', 0), + StrField('data', None), + ] + + overload_fields = { + IP: {'proto': socket.IPPROTO_ESP}, + IPv6: {'nh': socket.IPPROTO_ESP}, + IPv6ExtHdrHopByHop: {'nh': socket.IPPROTO_ESP}, + IPv6ExtHdrDestOpt: {'nh': socket.IPPROTO_ESP}, + IPv6ExtHdrRouting: {'nh': socket.IPPROTO_ESP}, + } + +bind_layers(IP, ESP, proto=socket.IPPROTO_ESP) +bind_layers(IPv6, ESP, nh=socket.IPPROTO_ESP) +bind_layers(UDP, ESP, dport=4500) # NAT-Traversal encapsulation +bind_layers(UDP, ESP, sport=4500) # NAT-Traversal encapsulation + +#------------------------------------------------------------------------------ +class _ESPPlain(Packet): + """ + Internal class to represent unencrypted ESP packets. + """ + name = 'ESP' + + fields_desc = [ + XIntField('spi', 0x0), + IntField('seq', 0), + + StrField('iv', ''), + PacketField('data', '', Raw), + StrField('padding', ''), + + ByteField('padlen', 0), + ByteEnumField('nh', 0, IP_PROTOS), + StrField('icv', ''), + ] + + def data_for_encryption(self): + return str(self.data) + self.padding + chr(self.padlen) + chr(self.nh) + +#------------------------------------------------------------------------------ +try: + from Crypto.Cipher import AES + from Crypto.Cipher import DES + from Crypto.Cipher import DES3 + from Crypto.Cipher import CAST + from Crypto.Cipher import Blowfish + from Crypto.Util import Counter + from Crypto import Random +except ImportError: + # no error if pycrypto is not available but encryption won't be supported + AES = None + DES = None + DES3 = None + CAST = None + Blowfish = None + Random = None + +#------------------------------------------------------------------------------ +def _lcm(a, b): + """ + Least Common Multiple between 2 integers. + """ + if a == 0 or b == 0: + return 0 + else: + return abs(a * b) // fractions.gcd(a, b) + +class CryptAlgo(object): + """ + IPSec encryption algorithm + """ + + def __init__(self, name, cipher, mode, block_size=None, iv_size=None, key_size=None): + """ + @param name: the name of this encryption algorithm + @param cipher: a Cipher module + @param mode: the mode used with the cipher module + @param block_size: the length a block for this algo. Defaults to the + `block_size` of the cipher. + @param iv_size: the length of the initialization vector of this algo. + Defaults to the `block_size` of the cipher. + @param key_size: an integer or list/tuple of integers. If specified, + force the secret keys length to one of the values. + Defaults to the `key_size` of the cipher. + """ + self.name = name + self.cipher = cipher + self.mode = mode + + if block_size is not None: + self.block_size = block_size + elif cipher is not None: + self.block_size = cipher.block_size + else: + self.block_size = 1 + + if iv_size is None: + self.iv_size = self.block_size + else: + self.iv_size = iv_size + + if key_size is not None: + self.key_size = key_size + elif cipher is not None: + self.key_size = cipher.key_size + else: + self.key_size = None + + def check_key(self, key): + """ + Check that the key length is valid. + + @param key: a byte string + """ + if self.key_size and not (len(key) == self.key_size or len(key) in self.key_size): + raise TypeError('invalid key size %s, must be %s' % + (len(key), self.key_size)) + + def generate_iv(self): + """ + Generate a random initialization vector. If pycrypto is not available, + return a buffer of the correct length filled with only '\x00'. + """ + if Random: + return Random.get_random_bytes(self.iv_size) + else: + return chr(0) * self.iv_size + + def new_cipher(self, key, iv): + """ + @param key: the secret key, a byte string + @param iv: the initialization vector, a byte string + @return: an initialized cipher object for this algo + """ + if (hasattr(self.cipher, 'MODE_CTR') and self.mode == self.cipher.MODE_CTR + or hasattr(self.cipher, 'MODE_GCM') and self.mode == self.cipher.MODE_GCM): + # in counter mode, the "iv" must be incremented for each block + # it is calculated like this: + # +---------+------------------+---------+ + # | nonce | IV | counter | + # +---------+------------------+---------+ + # m bytes n bytes 4 bytes + # <--------------------------------------> + # block_size + nonce_size = self.cipher.block_size - self.iv_size - 4 + + # instead of asking for an extra parameter, we extract the last + # nonce_size bytes of the key and use them as the nonce. + # +----------------------------+---------+ + # | cipher key | nonce | + # +----------------------------+---------+ + # <---------> + # nonce_size + cipher_key, nonce = key[:-nonce_size], key[-nonce_size:] + + return self.cipher.new(cipher_key, self.mode, + counter=Counter.new(4 * 8, prefix=nonce + iv)) + else: + return self.cipher.new(key, self.mode, iv) + + def pad(self, esp): + """ + Add the correct amount of padding so that the data to encrypt is + exactly a multiple of the algorithm's block size. + + Also, make sure that the total ESP packet length is a multiple of 4 or + 8 bytes with IP or IPv6 respectively. + + @param esp: an unencrypted _ESPPlain packet + """ + # 2 extra bytes for padlen and nh + data_len = len(esp.data) + 2 + + # according to the RFC4303, section 2.4. Padding (for Encryption) + # the size of the ESP payload must be a multiple of 32 bits + align = _lcm(self.block_size, 4) + + # pad for block size + esp.padlen = -data_len % align + + # padding must be an array of bytes starting from 1 to padlen + esp.padding = '' + for b in range(1, esp.padlen + 1): + esp.padding += chr(b) + + # If the following test fails, it means that this algo does not comply + # with the RFC + payload_len = len(esp.iv) + len(esp.data) + len(esp.padding) + 2 + if payload_len % 4 != 0: + raise ValueError('The size of the ESP data is not aligned to 32 bits after padding.') + + return esp + + def encrypt(self, esp, key): + """ + Encrypt an ESP packet + + @param esp: an unencrypted _ESPPlain packet with valid padding + @param key: the secret key used for encryption + + @return: a valid ESP packet encrypted with this algorithm + """ + data = esp.data_for_encryption() + + if self.cipher: + self.check_key(key) + cipher = self.new_cipher(key, esp.iv) + data = cipher.encrypt(data) + + return ESP(spi=esp.spi, seq=esp.seq, data=esp.iv + data) + + def decrypt(self, esp, key, icv_size=0): + """ + Decrypt an ESP packet + + @param esp: an encrypted ESP packet + @param key: the secret key used for encryption + @param icv_size: the length of the icv used for integrity check + + @return: a valid ESP packet encrypted with this algorithm + """ + self.check_key(key) + + iv = esp.data[:self.iv_size] + data = esp.data[self.iv_size:len(esp.data) - icv_size] + icv = esp.data[len(esp.data) - icv_size:] + + if self.cipher: + cipher = self.new_cipher(key, iv) + data = cipher.decrypt(data) + + # extract padlen and nh + padlen = ord(data[-2]) + nh = ord(data[-1]) + + # then use padlen to determine data and padding + data = data[:len(data) - padlen - 2] + padding = data[len(data) - padlen - 2: len(data) - 2] + + return _ESPPlain(spi=esp.spi, + seq=esp.seq, + iv=iv, + data=data, + padding=padding, + padlen=padlen, + nh=nh, + icv=icv) + +#------------------------------------------------------------------------------ +# The names of the encryption algorithms are the same than in scapy.contrib.ikev2 +# see http://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml + +CRYPT_ALGOS = { + 'NULL': CryptAlgo('NULL', cipher=None, mode=None, iv_size=0), +} + +if AES: + CRYPT_ALGOS['AES-CBC'] = CryptAlgo('AES-CBC', + cipher=AES, + mode=AES.MODE_CBC) + # specific case for counter mode: + # the last 4 bytes of the key are used to carry the nonce of the counter + CRYPT_ALGOS['AES-CTR'] = CryptAlgo('AES-CTR', + cipher=AES, + mode=AES.MODE_CTR, + block_size=1, + iv_size=8, + key_size=(16 + 4, 24 + 4, 32 + 4)) +if DES: + CRYPT_ALGOS['DES'] = CryptAlgo('DES', + cipher=DES, + mode=DES.MODE_CBC) +if Blowfish: + CRYPT_ALGOS['Blowfish'] = CryptAlgo('Blowfish', + cipher=Blowfish, + mode=Blowfish.MODE_CBC) +if DES3: + CRYPT_ALGOS['3DES'] = CryptAlgo('3DES', + cipher=DES3, + mode=DES3.MODE_CBC) +if CAST: + CRYPT_ALGOS['CAST'] = CryptAlgo('CAST', + cipher=CAST, + mode=CAST.MODE_CBC) + +#------------------------------------------------------------------------------ +try: + from Crypto.Hash import HMAC + from Crypto.Hash import SHA + from Crypto.Hash import MD5 + from Crypto.Hash import SHA256 + from Crypto.Hash import SHA384 + from Crypto.Hash import SHA512 +except ImportError: + # no error if pycrypto is not available but authentication won't be supported + HMAC = None + SHA = None + MD5 = None + SHA256 = None + SHA384 = None +try: + from Crypto.Hash import XCBCMAC +except ImportError: + XCBCMAC = None + +#------------------------------------------------------------------------------ +class IPSecIntegrityError(Exception): + """ + Error risen when the integrity check fails. + """ + pass + +class AuthAlgo(object): + """ + IPSec integrity algorithm + """ + + def __init__(self, name, mac, digestmod, icv_size, key_size=None): + """ + @param name: the name of this integrity algorithm + @param mac: a Message Authentication Code module + @param digestmod: a Hash or Cipher module + @param icv_size: the length of the integrity check value of this algo + @param key_size: an integer or list/tuple of integers. If specified, + force the secret keys length to one of the values. + Defaults to the `key_size` of the cipher. + """ + self.name = name + self.mac = mac + self.digestmod = digestmod + self.icv_size = icv_size + self.key_size = key_size + + def check_key(self, key): + """ + Check that the key length is valid. + + @param key: a byte string + """ + if self.key_size and len(key) not in self.key_size: + raise TypeError('invalid key size %s, must be one of %s' % + (len(key), self.key_size)) + + def new_mac(self, key): + """ + @param key: a byte string + @return: an initialized mac object for this algo + """ + if self.mac is XCBCMAC: + # specific case here, ciphermod instead of digestmod + return self.mac.new(key, ciphermod=self.digestmod) + else: + return self.mac.new(key, digestmod=self.digestmod) + + def sign(self, pkt, key): + """ + Sign an IPSec (ESP or AH) packet with this algo. + + @param pkt: a packet that contains a valid encrypted ESP or AH layer + @param key: the authentication key, a byte string + + @return: the signed packet + """ + if not self.mac: + return pkt + + self.check_key(key) + + mac = self.new_mac(key) + + if pkt.haslayer(ESP): + mac.update(str(pkt[ESP])) + pkt[ESP].data += mac.digest()[:self.icv_size] + + elif pkt.haslayer(AH): + clone = zero_mutable_fields(pkt.copy(), sending=True) + mac.update(str(clone)) + pkt[AH].icv = mac.digest()[:self.icv_size] + + return pkt + + def verify(self, pkt, key): + """ + Check that the integrity check value (icv) of a packet is valid. + + @param pkt: a packet that contains a valid encrypted ESP or AH layer + @param key: the authentication key, a byte string + + @raise IPSecIntegrityError: if the integrity check fails + """ + if not self.mac or self.icv_size == 0: + return + + self.check_key(key) + + mac = self.new_mac(key) + + pkt_icv = 'not found' + computed_icv = 'not computed' + + if isinstance(pkt, ESP): + pkt_icv = pkt.data[len(pkt.data) - self.icv_size:] + + pkt = pkt.copy() + pkt.data = pkt.data[:len(pkt.data) - self.icv_size] + mac.update(str(pkt)) + computed_icv = mac.digest()[:self.icv_size] + + elif pkt.haslayer(AH): + pkt_icv = pkt[AH].icv[:self.icv_size] + + clone = zero_mutable_fields(pkt.copy(), sending=False) + mac.update(str(clone)) + computed_icv = mac.digest()[:self.icv_size] + + if pkt_icv != computed_icv: + raise IPSecIntegrityError('pkt_icv=%r, computed_icv=%r' % + (pkt_icv, computed_icv)) + +#------------------------------------------------------------------------------ +# The names of the integrity algorithms are the same than in scapy.contrib.ikev2 +# see http://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml + +AUTH_ALGOS = { + 'NULL': AuthAlgo('NULL', mac=None, digestmod=None, icv_size=0), +} + +if HMAC: + if SHA: + AUTH_ALGOS['HMAC-SHA1-96'] = AuthAlgo('HMAC-SHA1-96', + mac=HMAC, + digestmod=SHA, + icv_size=12) + if SHA256: + AUTH_ALGOS['SHA2-256-128'] = AuthAlgo('SHA2-256-128', + mac=HMAC, + digestmod=SHA256, + icv_size=16) + if SHA384: + AUTH_ALGOS['SHA2-384-192'] = AuthAlgo('SHA2-384-192', + mac=HMAC, + digestmod=SHA384, + icv_size=24) + if SHA512: + AUTH_ALGOS['SHA2-512-256'] = AuthAlgo('SHA2-512-256', + mac=HMAC, + digestmod=SHA512, + icv_size=32) + if MD5: + AUTH_ALGOS['HMAC-MD5-96'] = AuthAlgo('HMAC-MD5-96', + mac=HMAC, + digestmod=MD5, + icv_size=12) +if AES and XCBCMAC: + AUTH_ALGOS['AES-XCBC-96'] = AuthAlgo('AES-XCBC-96', + mac=XCBCMAC, + digestmod=AES, + icv_size=12, + key_size=(16,)) + +#------------------------------------------------------------------------------ + + +#------------------------------------------------------------------------------ +def split_for_transport(orig_pkt, transport_proto): + """ + Split an IP(v6) packet in the correct location to insert an ESP or AH + header. + + @param orig_pkt: the packet to split. Must be an IP or IPv6 packet + @param transport_proto: the IPSec protocol number that will be inserted + at the split position. + @return: a tuple (header, nh, payload) where nh is the protocol number of + payload. + """ + header = orig_pkt.copy() + next_hdr = header.payload + nh = None + + if header.version == 4: + nh = header.proto + header.proto = transport_proto + header.remove_payload() + del header.chksum + del header.len + + return header, nh, next_hdr + else: + found_rt_hdr = False + prev = header + + # Since the RFC 4302 is vague about where the ESP/AH headers should be + # inserted in IPv6, I chose to follow the linux implementation. + while isinstance(next_hdr, (IPv6ExtHdrHopByHop, IPv6ExtHdrRouting, IPv6ExtHdrDestOpt)): + if isinstance(next_hdr, IPv6ExtHdrHopByHop): + pass + if isinstance(next_hdr, IPv6ExtHdrRouting): + found_rt_hdr = True + elif isinstance(next_hdr, IPv6ExtHdrDestOpt) and found_rt_hdr: + break + + prev = next_hdr + next_hdr = next_hdr.payload + + nh = prev.nh + prev.nh = transport_proto + prev.remove_payload() + del header.plen + + return header, nh, next_hdr + +#------------------------------------------------------------------------------ +# see RFC 4302 - Appendix A. Mutability of IP Options/Extension Headers +IMMUTABLE_IPV4_OPTIONS = ( + 0, # End Of List + 1, # No OPeration + 2, # Security + 5, # Extended Security + 6, # Commercial Security + 20, # Router Alert + 21, # Sender Directed Multi-Destination Delivery +) +def zero_mutable_fields(pkt, sending=False): + """ + When using AH, all "mutable" fields must be "zeroed" before calculating + the ICV. See RFC 4302, Section 3.3.3.1. Handling Mutable Fields. + + @param pkt: an IP(v6) packet containing an AH layer. + NOTE: The packet will be modified + @param sending: if true, ipv6 routing headers will not be reordered + """ + + if pkt.haslayer(AH): + pkt[AH].icv = chr(0) * len(pkt[AH].icv) + else: + raise TypeError('no AH layer found') + + if pkt.version == 4: + # the tos field has been replaced by DSCP and ECN + # Routers may rewrite the DS field as needed to provide a + # desired local or end-to-end service + pkt.tos = 0 + # an intermediate router might set the DF bit, even if the source + # did not select it. + pkt.flags = 0 + # changed en route as a normal course of processing by routers + pkt.ttl = 0 + # will change if any of these other fields change + pkt.chksum = 0 + + immutable_opts = [] + for opt in pkt.options: + if opt.option in IMMUTABLE_IPV4_OPTIONS: + immutable_opts.append(opt) + else: + immutable_opts.append(Raw(chr(0) * len(opt))) + pkt.options = immutable_opts + + else: + # holds DSCP and ECN + pkt.tc = 0 + # The flow label described in AHv1 was mutable, and in RFC 2460 [DH98] + # was potentially mutable. To retain compatibility with existing AH + # implementations, the flow label is not included in the ICV in AHv2. + pkt.fl = 0 + # same as ttl + pkt.hlim = 0 + + next_hdr = pkt.payload + + while isinstance(next_hdr, (IPv6ExtHdrHopByHop, IPv6ExtHdrRouting, IPv6ExtHdrDestOpt)): + if isinstance(next_hdr, (IPv6ExtHdrHopByHop, IPv6ExtHdrDestOpt)): + for opt in next_hdr.options: + if opt.otype & 0x20: + # option data can change en-route and must be zeroed + opt.optdata = chr(0) * opt.optlen + elif isinstance(next_hdr, IPv6ExtHdrRouting) and sending: + # The sender must order the field so that it appears as it + # will at the receiver, prior to performing the ICV computation. + next_hdr.segleft = 0 + if next_hdr.addresses: + final = next_hdr.addresses.pop() + next_hdr.addresses.insert(0, pkt.dst) + pkt.dst = final + else: + break + + next_hdr = next_hdr.payload + + return pkt + +#------------------------------------------------------------------------------ +class SecurityAssociation(object): + """ + This class is responsible of "encryption" and "decryption" of IPSec packets. + """ + + SUPPORTED_PROTOS = (IP, IPv6) + + def __init__(self, proto, spi, seq_num=1, crypt_algo=None, crypt_key=None, + auth_algo=None, auth_key=None, tunnel_header=None, nat_t_header=None): + """ + @param proto: the IPSec proto to use (ESP or AH) + @param spi: the Security Parameters Index of this SA + @param seq_num: the initial value for the sequence number on encrypted + packets + @param crypt_algo: the encryption algorithm name (only used with ESP) + @param crypt_key: the encryption key (only used with ESP) + @param auth_algo: the integrity algorithm name + @param auth_key: the integrity key + @param tunnel_header: an instance of a IP(v6) header that will be used + to encapsulate the encrypted packets. + @param nat_t_header: an instance of a UDP header that will be used + for NAT-Traversal. + """ + + if proto not in (ESP, AH, ESP.name, AH.name): + raise ValueError("proto must be either ESP or AH") + if isinstance(proto, basestring): + self.proto = eval(proto) + else: + self.proto = proto + + self.spi = spi + self.seq_num = seq_num + + if crypt_algo: + if crypt_algo not in CRYPT_ALGOS: + raise TypeError('unsupported encryption algo %r, try %r' % + (crypt_algo, CRYPT_ALGOS.keys())) + self.crypt_algo = CRYPT_ALGOS[crypt_algo] + self.crypt_algo.check_key(crypt_key) + self.crypt_key = crypt_key + else: + self.crypt_algo = CRYPT_ALGOS['NULL'] + self.crypt_key = None + + if auth_algo: + if auth_algo not in AUTH_ALGOS: + raise TypeError('unsupported integrity algo %r, try %r' % + (auth_algo, AUTH_ALGOS.keys())) + self.auth_algo = AUTH_ALGOS[auth_algo] + self.auth_algo.check_key(auth_key) + self.auth_key = auth_key + else: + self.auth_algo = AUTH_ALGOS['NULL'] + self.auth_key = None + + if tunnel_header and not isinstance(tunnel_header, (IP, IPv6)): + raise TypeError('tunnel_header must be %s or %s' % (IP.name, IPv6.name)) + self.tunnel_header = tunnel_header + + if nat_t_header: + if proto is not ESP: + raise TypeError('nat_t_header is only allowed with ESP') + if not isinstance(nat_t_header, UDP): + raise TypeError('nat_t_header must be %s' % UDP.name) + self.nat_t_header = nat_t_header + + def check_spi(self, pkt): + if pkt.spi != self.spi: + raise TypeError('packet spi=0x%x does not match the SA spi=0x%x' % + (pkt.spi, self.spi)) + + def _encrypt_esp(self, pkt, seq_num=None, iv=None): + + if iv is None: + iv = self.crypt_algo.generate_iv() + else: + if len(iv) != self.crypt_algo.iv_size: + raise TypeError('iv length must be %s' % self.crypt_algo.iv_size) + + esp = _ESPPlain(spi=self.spi, seq=seq_num or self.seq_num, iv=iv) + + if self.tunnel_header: + tunnel = self.tunnel_header.copy() + + if tunnel.version == 4: + del tunnel.proto + del tunnel.len + del tunnel.chksum + else: + del tunnel.nh + del tunnel.plen + + pkt = tunnel.__class__(str(tunnel / pkt)) + + ip_header, nh, payload = split_for_transport(pkt, socket.IPPROTO_ESP) + esp.data = payload + esp.nh = nh + + esp = self.crypt_algo.pad(esp) + esp = self.crypt_algo.encrypt(esp, self.crypt_key) + + self.auth_algo.sign(esp, self.auth_key) + + if self.nat_t_header: + nat_t_header = self.nat_t_header.copy() + nat_t_header.chksum = 0 + del nat_t_header.len + if ip_header.version == 4: + del ip_header.proto + else: + del ip_header.nh + ip_header /= nat_t_header + + if ip_header.version == 4: + ip_header.len = len(ip_header) + len(esp) + del ip_header.chksum + ip_header = ip_header.__class__(str(ip_header)) + else: + ip_header.plen = len(ip_header.payload) + len(esp) + + # sequence number must always change, unless specified by the user + if seq_num is None: + self.seq_num += 1 + + return ip_header / esp + + def _encrypt_ah(self, pkt, seq_num=None): + + ah = AH(spi=self.spi, seq=seq_num or self.seq_num, + icv=chr(0) * self.auth_algo.icv_size) + + if self.tunnel_header: + tunnel = self.tunnel_header.copy() + + if tunnel.version == 4: + del tunnel.proto + del tunnel.len + del tunnel.chksum + else: + del tunnel.nh + del tunnel.plen + + pkt = tunnel.__class__(str(tunnel / pkt)) + + ip_header, nh, payload = split_for_transport(pkt, socket.IPPROTO_AH) + ah.nh = nh + + if ip_header.version == 6 and len(ah) % 8 != 0: + # For IPv6, the total length of the header must be a multiple of + # 8-octet units. + ah.padding = chr(0) * (-len(ah) % 8) + elif len(ah) % 4 != 0: + # For IPv4, the total length of the header must be a multiple of + # 4-octet units. + ah.padding = chr(0) * (-len(ah) % 4) + + # RFC 4302 - Section 2.2. Payload Length + # This 8-bit field specifies the length of AH in 32-bit words (4-byte + # units), minus "2". + ah.payloadlen = len(ah) / 4 - 2 + + if ip_header.version == 4: + ip_header.len = len(ip_header) + len(ah) + len(payload) + del ip_header.chksum + ip_header = ip_header.__class__(str(ip_header)) + else: + ip_header.plen = len(ip_header.payload) + len(ah) + len(payload) + + signed_pkt = self.auth_algo.sign(ip_header / ah / payload, self.auth_key) + + # sequence number must always change, unless specified by the user + if seq_num is None: + self.seq_num += 1 + + return signed_pkt + + def encrypt(self, pkt, seq_num=None, iv=None): + """ + Encrypt (and encapsulate) an IP(v6) packet with ESP or AH according + to this SecurityAssociation. + + @param pkt: the packet to encrypt + @param seq_num: if specified, use this sequence number instead of the + generated one + @param iv: if specified, use this initialization vector for + encryption instead of a random one. + + @return: the encrypted/encapsulated packet + """ + if not isinstance(pkt, self.SUPPORTED_PROTOS): + raise TypeError('cannot encrypt %s, supported protos are %s' + % (pkt.__class__, self.SUPPORTED_PROTOS)) + if self.proto is ESP: + return self._encrypt_esp(pkt, seq_num=seq_num, iv=iv) + else: + return self._encrypt_ah(pkt, seq_num=seq_num) + + def _decrypt_esp(self, pkt, verify=True): + + encrypted = pkt[ESP] + + if verify: + self.check_spi(pkt) + self.auth_algo.verify(encrypted, self.auth_key) + + esp = self.crypt_algo.decrypt(encrypted, self.crypt_key, + self.auth_algo.icv_size) + + if self.tunnel_header: + # drop the tunnel header and return the payload untouched + + pkt.remove_payload() + if pkt.version == 4: + pkt.proto = esp.nh + else: + pkt.nh = esp.nh + cls = pkt.guess_payload_class(esp.data) + + return cls(esp.data) + else: + ip_header = pkt + + if ip_header.version == 4: + ip_header.proto = esp.nh + del ip_header.chksum + ip_header.remove_payload() + ip_header.len = len(ip_header) + len(esp.data) + # recompute checksum + ip_header = ip_header.__class__(str(ip_header)) + else: + encrypted.underlayer.nh = esp.nh + encrypted.underlayer.remove_payload() + ip_header.plen = len(ip_header.payload) + len(esp.data) + + cls = ip_header.guess_payload_class(esp.data) + + # reassemble the ip_header with the ESP payload + return ip_header / cls(esp.data) + + def _decrypt_ah(self, pkt, verify=True): + + if verify: + self.check_spi(pkt) + self.auth_algo.verify(pkt, self.auth_key) + + ah = pkt[AH] + payload = ah.payload + payload.remove_underlayer(None) # useless argument... + + if self.tunnel_header: + return payload + else: + ip_header = pkt + + if ip_header.version == 4: + ip_header.proto = ah.nh + del ip_header.chksum + ip_header.remove_payload() + ip_header.len = len(ip_header) + len(payload) + # recompute checksum + ip_header = ip_header.__class__(str(ip_header)) + else: + ah.underlayer.nh = ah.nh + ah.underlayer.remove_payload() + ip_header.plen = len(ip_header.payload) + len(payload) + + # reassemble the ip_header with the AH payload + return ip_header / payload + + def decrypt(self, pkt, verify=True): + """ + Decrypt (and decapsulate) an IP(v6) packet containing ESP or AH. + + @param pkt: the packet to decrypt + @param verify: if False, do not perform the integrity check + + @return: the decrypted/decapsulated packet + @raise IPSecIntegrityError: if the integrity check fails + """ + if not isinstance(pkt, self.SUPPORTED_PROTOS): + raise TypeError('cannot decrypt %s, supported protos are %s' + % (pkt.__class__, self.SUPPORTED_PROTOS)) + + if self.proto is ESP and pkt.haslayer(ESP): + return self._decrypt_esp(pkt, verify=verify) + elif self.proto is AH and pkt.haslayer(AH): + return self._decrypt_ah(pkt, verify=verify) + else: + raise TypeError('%s has no %s layer' % (pkt, self.proto.name)) diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/ir.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/ir.py new file mode 100644 index 00000000..fc738c55 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/ir.py @@ -0,0 +1,44 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +IrDA infrared data communication. +""" + +from scapy.packet import * +from scapy.fields import * +from scapy.layers.l2 import CookedLinux + + + +# IR + +class IrLAPHead(Packet): + name = "IrDA Link Access Protocol Header" + fields_desc = [ XBitField("Address", 0x7f, 7), + BitEnumField("Type", 1, 1, {"Response":0, + "Command":1})] + +class IrLAPCommand(Packet): + name = "IrDA Link Access Protocol Command" + fields_desc = [ XByteField("Control", 0), + XByteField("Format identifier", 0), + XIntField("Source address", 0), + XIntField("Destination address", 0xffffffffL), + XByteField("Discovery flags", 0x1), + ByteEnumField("Slot number", 255, {"final":255}), + XByteField("Version", 0)] + + +class IrLMP(Packet): + name = "IrDA Link Management Protocol" + fields_desc = [ XShortField("Service hints", 0), + XByteField("Character set", 0), + StrField("Device name", "") ] + + +bind_layers( CookedLinux, IrLAPHead, proto=23) +bind_layers( IrLAPHead, IrLAPCommand, Type=1) +bind_layers( IrLAPCommand, IrLMP, ) diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/isakmp.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/isakmp.py new file mode 100644 index 00000000..9c54bed4 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/isakmp.py @@ -0,0 +1,348 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +ISAKMP (Internet Security Association and Key Management Protocol). +""" + +import struct +from scapy.packet import * +from scapy.fields import * +from scapy.ansmachine import * +from scapy.layers.inet import IP,UDP +from scapy.sendrecv import sr + + +# see http://www.iana.org/assignments/ipsec-registry for details +ISAKMPAttributeTypes= { "Encryption": (1, { "DES-CBC" : 1, + "IDEA-CBC" : 2, + "Blowfish-CBC" : 3, + "RC5-R16-B64-CBC" : 4, + "3DES-CBC" : 5, + "CAST-CBC" : 6, + "AES-CBC" : 7, + "CAMELLIA-CBC" : 8, }, 0), + "Hash": (2, { "MD5": 1, + "SHA": 2, + "Tiger": 3, + "SHA2-256": 4, + "SHA2-384": 5, + "SHA2-512": 6,}, 0), + "Authentication":(3, { "PSK": 1, + "DSS": 2, + "RSA Sig": 3, + "RSA Encryption": 4, + "RSA Encryption Revised": 5, + "ElGamal Encryption": 6, + "ElGamal Encryption Revised": 7, + "ECDSA Sig": 8, + "HybridInitRSA": 64221, + "HybridRespRSA": 64222, + "HybridInitDSS": 64223, + "HybridRespDSS": 64224, + "XAUTHInitPreShared": 65001, + "XAUTHRespPreShared": 65002, + "XAUTHInitDSS": 65003, + "XAUTHRespDSS": 65004, + "XAUTHInitRSA": 65005, + "XAUTHRespRSA": 65006, + "XAUTHInitRSAEncryption": 65007, + "XAUTHRespRSAEncryption": 65008, + "XAUTHInitRSARevisedEncryption": 65009, + "XAUTHRespRSARevisedEncryptio": 65010, }, 0), + "GroupDesc": (4, { "768MODPgr" : 1, + "1024MODPgr" : 2, + "EC2Ngr155" : 3, + "EC2Ngr185" : 4, + "1536MODPgr" : 5, + "2048MODPgr" : 14, + "3072MODPgr" : 15, + "4096MODPgr" : 16, + "6144MODPgr" : 17, + "8192MODPgr" : 18, }, 0), + "GroupType": (5, {"MODP": 1, + "ECP": 2, + "EC2N": 3}, 0), + "GroupPrime": (6, {}, 1), + "GroupGenerator1":(7, {}, 1), + "GroupGenerator2":(8, {}, 1), + "GroupCurveA": (9, {}, 1), + "GroupCurveB": (10, {}, 1), + "LifeType": (11, {"Seconds": 1, + "Kilobytes": 2, }, 0), + "LifeDuration": (12, {}, 1), + "PRF": (13, {}, 0), + "KeyLength": (14, {}, 0), + "FieldSize": (15, {}, 0), + "GroupOrder": (16, {}, 1), + } + +# the name 'ISAKMPTransformTypes' is actually a misnomer (since the table +# holds info for all ISAKMP Attribute types, not just transforms, but we'll +# keep it for backwards compatibility... for now at least +ISAKMPTransformTypes = ISAKMPAttributeTypes + +ISAKMPTransformNum = {} +for n in ISAKMPTransformTypes: + val = ISAKMPTransformTypes[n] + tmp = {} + for e in val[1]: + tmp[val[1][e]] = e + ISAKMPTransformNum[val[0]] = (n,tmp, val[2]) +del(n) +del(e) +del(tmp) +del(val) + + +class ISAKMPTransformSetField(StrLenField): + islist=1 + def type2num(self, (typ,val)): + type_val,enc_dict,tlv = ISAKMPTransformTypes.get(typ, (typ,{},0)) + val = enc_dict.get(val, val) + s = "" + if (val & ~0xffff): + if not tlv: + warning("%r should not be TLV but is too big => using TLV encoding" % typ) + n = 0 + while val: + s = chr(val&0xff)+s + val >>= 8 + n += 1 + val = n + else: + type_val |= 0x8000 + return struct.pack("!HH",type_val, val)+s + def num2type(self, typ, enc): + val = ISAKMPTransformNum.get(typ,(typ,{})) + enc = val[1].get(enc,enc) + return (val[0],enc) + def i2m(self, pkt, i): + if i is None: + return "" + i = map(self.type2num, i) + return "".join(i) + def m2i(self, pkt, m): + # I try to ensure that we don't read off the end of our packet based + # on bad length fields we're provided in the packet. There are still + # conditions where struct.unpack() may not get enough packet data, but + # worst case that should result in broken attributes (which would + # be expected). (wam) + lst = [] + while len(m) >= 4: + trans_type, = struct.unpack("!H", m[:2]) + is_tlv = not (trans_type & 0x8000) + if is_tlv: + # We should probably check to make sure the attribute type we + # are looking at is allowed to have a TLV format and issue a + # warning if we're given an TLV on a basic attribute. + value_len, = struct.unpack("!H", m[2:4]) + if value_len+4 > len(m): + warning("Bad length for ISAKMP tranform type=%#6x" % trans_type) + value = m[4:4+value_len] + value = reduce(lambda x,y: (x<<8L)|y, struct.unpack("!%s" % ("B"*len(value),), value),0) + else: + trans_type &= 0x7fff + value_len=0 + value, = struct.unpack("!H", m[2:4]) + m=m[4+value_len:] + lst.append(self.num2type(trans_type, value)) + if len(m) > 0: + warning("Extra bytes after ISAKMP transform dissection [%r]" % m) + return lst + + +ISAKMP_payload_type = ["None","SA","Proposal","Transform","KE","ID","CERT","CR","Hash", + "SIG","Nonce","Notification","Delete","VendorID"] + +ISAKMP_exchange_type = ["None","base","identity prot.", + "auth only", "aggressive", "info"] + + +class ISAKMP_class(Packet): + def guess_payload_class(self, payload): + np = self.next_payload + if np == 0: + return conf.raw_layer + elif np < len(ISAKMP_payload_type): + pt = ISAKMP_payload_type[np] + return globals().get("ISAKMP_payload_%s" % pt, ISAKMP_payload) + else: + return ISAKMP_payload + + +class ISAKMP(ISAKMP_class): # rfc2408 + name = "ISAKMP" + fields_desc = [ + StrFixedLenField("init_cookie","",8), + StrFixedLenField("resp_cookie","",8), + ByteEnumField("next_payload",0,ISAKMP_payload_type), + XByteField("version",0x10), + ByteEnumField("exch_type",0,ISAKMP_exchange_type), + FlagsField("flags",0, 8, ["encryption","commit","auth_only","res3","res4","res5","res6","res7"]), # XXX use a Flag field + IntField("id",0), + IntField("length",None) + ] + + def guess_payload_class(self, payload): + if self.flags & 1: + return conf.raw_layer + return ISAKMP_class.guess_payload_class(self, payload) + + def answers(self, other): + if isinstance(other, ISAKMP): + if other.init_cookie == self.init_cookie: + return 1 + return 0 + def post_build(self, p, pay): + p += pay + if self.length is None: + p = p[:24]+struct.pack("!I",len(p))+p[28:] + return p + + + + +class ISAKMP_payload_Transform(ISAKMP_class): + name = "IKE Transform" + fields_desc = [ + ByteEnumField("next_payload",None,ISAKMP_payload_type), + ByteField("res",0), +# ShortField("len",None), + ShortField("length",None), + ByteField("num",None), + ByteEnumField("id",1,{1:"KEY_IKE"}), + ShortField("res2",0), + ISAKMPTransformSetField("transforms",None,length_from=lambda x:x.length-8) +# XIntField("enc",0x80010005L), +# XIntField("hash",0x80020002L), +# XIntField("auth",0x80030001L), +# XIntField("group",0x80040002L), +# XIntField("life_type",0x800b0001L), +# XIntField("durationh",0x000c0004L), +# XIntField("durationl",0x00007080L), + ] + def post_build(self, p, pay): + if self.length is None: + l = len(p) + p = p[:2]+chr((l>>8)&0xff)+chr(l&0xff)+p[4:] + p += pay + return p + + + + +class ISAKMP_payload_Proposal(ISAKMP_class): + name = "IKE proposal" +# ISAKMP_payload_type = 0 + fields_desc = [ + ByteEnumField("next_payload",None,ISAKMP_payload_type), + ByteField("res",0), + FieldLenField("length",None,"trans","H", adjust=lambda pkt,x:x+8), + ByteField("proposal",1), + ByteEnumField("proto",1,{1:"ISAKMP"}), + FieldLenField("SPIsize",None,"SPI","B"), + ByteField("trans_nb",None), + StrLenField("SPI","",length_from=lambda x:x.SPIsize), + PacketLenField("trans",conf.raw_layer(),ISAKMP_payload_Transform,length_from=lambda x:x.length-8), + ] + + +class ISAKMP_payload(ISAKMP_class): + name = "ISAKMP payload" + fields_desc = [ + ByteEnumField("next_payload",None,ISAKMP_payload_type), + ByteField("res",0), + FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), + StrLenField("load","",length_from=lambda x:x.length-4), + ] + + +class ISAKMP_payload_VendorID(ISAKMP_class): + name = "ISAKMP Vendor ID" + overload_fields = { ISAKMP: { "next_payload":13 }} + fields_desc = [ + ByteEnumField("next_payload",None,ISAKMP_payload_type), + ByteField("res",0), + FieldLenField("length",None,"vendorID","H", adjust=lambda pkt,x:x+4), + StrLenField("vendorID","",length_from=lambda x:x.length-4), + ] + +class ISAKMP_payload_SA(ISAKMP_class): + name = "ISAKMP SA" + overload_fields = { ISAKMP: { "next_payload":1 }} + fields_desc = [ + ByteEnumField("next_payload",None,ISAKMP_payload_type), + ByteField("res",0), + FieldLenField("length",None,"prop","H", adjust=lambda pkt,x:x+12), + IntEnumField("DOI",1,{1:"IPSEC"}), + IntEnumField("situation",1,{1:"identity"}), + PacketLenField("prop",conf.raw_layer(),ISAKMP_payload_Proposal,length_from=lambda x:x.length-12), + ] + +class ISAKMP_payload_Nonce(ISAKMP_class): + name = "ISAKMP Nonce" + overload_fields = { ISAKMP: { "next_payload":10 }} + fields_desc = [ + ByteEnumField("next_payload",None,ISAKMP_payload_type), + ByteField("res",0), + FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), + StrLenField("load","",length_from=lambda x:x.length-4), + ] + +class ISAKMP_payload_KE(ISAKMP_class): + name = "ISAKMP Key Exchange" + overload_fields = { ISAKMP: { "next_payload":4 }} + fields_desc = [ + ByteEnumField("next_payload",None,ISAKMP_payload_type), + ByteField("res",0), + FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), + StrLenField("load","",length_from=lambda x:x.length-4), + ] + +class ISAKMP_payload_ID(ISAKMP_class): + name = "ISAKMP Identification" + overload_fields = { ISAKMP: { "next_payload":5 }} + fields_desc = [ + ByteEnumField("next_payload",None,ISAKMP_payload_type), + ByteField("res",0), + FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+8), + ByteEnumField("IDtype",1,{1:"IPv4_addr", 11:"Key"}), + ByteEnumField("ProtoID",0,{0:"Unused"}), + ShortEnumField("Port",0,{0:"Unused"}), +# IPField("IdentData","127.0.0.1"), + StrLenField("load","",length_from=lambda x:x.length-8), + ] + + + +class ISAKMP_payload_Hash(ISAKMP_class): + name = "ISAKMP Hash" + overload_fields = { ISAKMP: { "next_payload":8 }} + fields_desc = [ + ByteEnumField("next_payload",None,ISAKMP_payload_type), + ByteField("res",0), + FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+4), + StrLenField("load","",length_from=lambda x:x.length-4), + ] + + + +ISAKMP_payload_type_overload = {} +for i in range(len(ISAKMP_payload_type)): + name = "ISAKMP_payload_%s" % ISAKMP_payload_type[i] + if name in globals(): + ISAKMP_payload_type_overload[globals()[name]] = {"next_payload":i} + +del(i) +del(name) +ISAKMP_class.overload_fields = ISAKMP_payload_type_overload.copy() + + +bind_layers( UDP, ISAKMP, dport=500, sport=500) +def ikescan(ip): + return sr(IP(dst=ip)/UDP()/ISAKMP(init_cookie=RandString(8), + exch_type=2)/ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal())) + diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/l2.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/l2.py new file mode 100644 index 00000000..3f80ed7d --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/l2.py @@ -0,0 +1,542 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +Classes and functions for layer 2 protocols. +""" + +import os,struct,time +from scapy.base_classes import Net +from scapy.config import conf +from scapy.packet import * +from scapy.ansmachine import * +from scapy.plist import SndRcvList +from scapy.fields import * +from scapy.sendrecv import srp,srp1 +from scapy.arch import get_if_hwaddr + + + + +################# +## Tools ## +################# + + +class Neighbor: + def __init__(self): + self.resolvers = {} + + def register_l3(self, l2, l3, resolve_method): + self.resolvers[l2,l3]=resolve_method + + def resolve(self, l2inst, l3inst): + k = l2inst.__class__,l3inst.__class__ + if k in self.resolvers: + return self.resolvers[k](l2inst,l3inst) + + def __repr__(self): + return "\n".join("%-15s -> %-15s" % (l2.__name__, l3.__name__) for l2,l3 in self.resolvers) + +conf.neighbor = Neighbor() + +conf.netcache.new_cache("arp_cache", 120) # cache entries expire after 120s + + +@conf.commands.register +def getmacbyip(ip, chainCC=0): + """Return MAC address corresponding to a given IP address""" + if isinstance(ip,Net): + ip = iter(ip).next() + ip = inet_ntoa(inet_aton(ip)) + tmp = map(ord, inet_aton(ip)) + if (tmp[0] & 0xf0) == 0xe0: # mcast @ + return "01:00:5e:%.2x:%.2x:%.2x" % (tmp[1]&0x7f,tmp[2],tmp[3]) + iff,a,gw = conf.route.route(ip) + if ( (iff == "lo") or (ip == conf.route.get_if_bcast(iff)) ): + return "ff:ff:ff:ff:ff:ff" + if gw != "0.0.0.0": + ip = gw + + mac = conf.netcache.arp_cache.get(ip) + if mac: + return mac + + res = srp1(Ether(dst=ETHER_BROADCAST)/ARP(op="who-has", pdst=ip), + type=ETH_P_ARP, + iface = iff, + timeout=2, + verbose=0, + chainCC=chainCC, + nofilter=1) + if res is not None: + mac = res.payload.hwsrc + conf.netcache.arp_cache[ip] = mac + return mac + return None + + + +### Fields + +class DestMACField(MACField): + def __init__(self, name): + MACField.__init__(self, name, None) + def i2h(self, pkt, x): + if x is None: + x = conf.neighbor.resolve(pkt,pkt.payload) + if x is None: + x = "ff:ff:ff:ff:ff:ff" + warning("Mac address to reach destination not found. Using broadcast.") + return MACField.i2h(self, pkt, x) + def i2m(self, pkt, x): + return MACField.i2m(self, pkt, self.i2h(pkt, x)) + +class SourceMACField(MACField): + def __init__(self, name): + MACField.__init__(self, name, None) + def i2h(self, pkt, x): + if x is None: + iff,a,gw = pkt.payload.route() + if iff: + try: + x = get_if_hwaddr(iff) + except: + pass + if x is None: + x = "00:00:00:00:00:00" + return MACField.i2h(self, pkt, x) + def i2m(self, pkt, x): + return MACField.i2m(self, pkt, self.i2h(pkt, x)) + +class ARPSourceMACField(MACField): + def __init__(self, name): + MACField.__init__(self, name, None) + def i2h(self, pkt, x): + if x is None: + iff,a,gw = pkt.route() + if iff: + try: + x = get_if_hwaddr(iff) + except: + pass + if x is None: + x = "00:00:00:00:00:00" + return MACField.i2h(self, pkt, x) + def i2m(self, pkt, x): + return MACField.i2m(self, pkt, self.i2h(pkt, x)) + + + +### Layers + + +class Ether(Packet): + name = "Ethernet" + fields_desc = [ MACField("dst","00:00:00:01:00:00"), + MACField("src","00:00:00:02:00:00"), + XShortEnumField("type", 0x9000, ETHER_TYPES) ] + def hashret(self): + return struct.pack("H",self.type)+self.payload.hashret() + def answers(self, other): + if isinstance(other,Ether): + if self.type == other.type: + return self.payload.answers(other.payload) + return 0 + def mysummary(self): + return self.sprintf("%src% > %dst% (%type%)") + @classmethod + def dispatch_hook(cls, _pkt=None, *args, **kargs): + if _pkt and len(_pkt) >= 14: + if struct.unpack("!H", _pkt[12:14])[0] <= 1500: + return Dot3 + return cls + + +class Dot3(Packet): + name = "802.3" + fields_desc = [ DestMACField("dst"), + MACField("src", ETHER_ANY), + LenField("len", None, "H") ] + def extract_padding(self,s): + l = self.len + return s[:l],s[l:] + def answers(self, other): + if isinstance(other,Dot3): + return self.payload.answers(other.payload) + return 0 + def mysummary(self): + return "802.3 %s > %s" % (self.src, self.dst) + @classmethod + def dispatch_hook(cls, _pkt=None, *args, **kargs): + if _pkt and len(_pkt) >= 14: + if struct.unpack("!H", _pkt[12:14])[0] > 1500: + return Ether + return cls + + +class LLC(Packet): + name = "LLC" + fields_desc = [ XByteField("dsap", 0x00), + XByteField("ssap", 0x00), + ByteField("ctrl", 0) ] + +conf.neighbor.register_l3(Ether, LLC, lambda l2,l3: conf.neighbor.resolve(l2,l3.payload)) +conf.neighbor.register_l3(Dot3, LLC, lambda l2,l3: conf.neighbor.resolve(l2,l3.payload)) + + +class CookedLinux(Packet): + name = "cooked linux" + fields_desc = [ ShortEnumField("pkttype",0, {0: "unicast", + 4:"sent-by-us"}), #XXX incomplete + XShortField("lladdrtype",512), + ShortField("lladdrlen",0), + StrFixedLenField("src","",8), + XShortEnumField("proto",0x800,ETHER_TYPES) ] + + + +class SNAP(Packet): + name = "SNAP" + fields_desc = [ X3BytesField("OUI",0x000000), + XShortEnumField("code", 0x000, ETHER_TYPES) ] + +conf.neighbor.register_l3(Dot3, SNAP, lambda l2,l3: conf.neighbor.resolve(l2,l3.payload)) + + +class Dot1Q(Packet): + name = "802.1Q" + aliastypes = [ Ether ] + fields_desc = [ BitField("prio", 0, 3), + BitField("id", 0, 1), + BitField("vlan", 1, 12), + XShortEnumField("type", 0x0000, ETHER_TYPES) ] + def answers(self, other): + if isinstance(other,Dot1Q): + if ( (self.type == other.type) and + (self.vlan == other.vlan) ): + return self.payload.answers(other.payload) + else: + return self.payload.answers(other) + return 0 + def default_payload_class(self, pay): + if self.type <= 1500: + return LLC + return conf.raw_layer + def extract_padding(self,s): + if self.type <= 1500: + return s[:self.type],s[self.type:] + return s,None + def mysummary(self): + if isinstance(self.underlayer, Ether): + return self.underlayer.sprintf("802.1q %Ether.src% > %Ether.dst% (%Dot1Q.type%) vlan %Dot1Q.vlan%") + else: + return self.sprintf("802.1q (%Dot1Q.type%) vlan %Dot1Q.vlan%") + + +conf.neighbor.register_l3(Ether, Dot1Q, lambda l2,l3: conf.neighbor.resolve(l2,l3.payload)) + +class STP(Packet): + name = "Spanning Tree Protocol" + fields_desc = [ ShortField("proto", 0), + ByteField("version", 0), + ByteField("bpdutype", 0), + ByteField("bpduflags", 0), + ShortField("rootid", 0), + MACField("rootmac", ETHER_ANY), + IntField("pathcost", 0), + ShortField("bridgeid", 0), + MACField("bridgemac", ETHER_ANY), + ShortField("portid", 0), + BCDFloatField("age", 1), + BCDFloatField("maxage", 20), + BCDFloatField("hellotime", 2), + BCDFloatField("fwddelay", 15) ] + + +class EAPOL(Packet): + name = "EAPOL" + fields_desc = [ ByteField("version", 1), + ByteEnumField("type", 0, ["EAP_PACKET", "START", "LOGOFF", "KEY", "ASF"]), + LenField("len", None, "H") ] + + EAP_PACKET= 0 + START = 1 + LOGOFF = 2 + KEY = 3 + ASF = 4 + def extract_padding(self, s): + l = self.len + return s[:l],s[l:] + def hashret(self): + return chr(self.type)+self.payload.hashret() + def answers(self, other): + if isinstance(other,EAPOL): + if ( (self.type == self.EAP_PACKET) and + (other.type == self.EAP_PACKET) ): + return self.payload.answers(other.payload) + return 0 + def mysummary(self): + return self.sprintf("EAPOL %EAPOL.type%") + + +class EAP(Packet): + name = "EAP" + fields_desc = [ ByteEnumField("code", 4, {1:"REQUEST",2:"RESPONSE",3:"SUCCESS",4:"FAILURE"}), + ByteField("id", 0), + ShortField("len",None), + ConditionalField(ByteEnumField("type",0, {1:"ID",4:"MD5"}), lambda pkt:pkt.code not in [EAP.SUCCESS, EAP.FAILURE]) + + ] + + REQUEST = 1 + RESPONSE = 2 + SUCCESS = 3 + FAILURE = 4 + TYPE_ID = 1 + TYPE_MD5 = 4 + def answers(self, other): + if isinstance(other,EAP): + if self.code == self.REQUEST: + return 0 + elif self.code == self.RESPONSE: + if ( (other.code == self.REQUEST) and + (other.type == self.type) ): + return 1 + elif other.code == self.RESPONSE: + return 1 + return 0 + + def post_build(self, p, pay): + if self.len is None: + l = len(p)+len(pay) + p = p[:2]+chr((l>>8)&0xff)+chr(l&0xff)+p[4:] + return p+pay + + +class ARP(Packet): + name = "ARP" + fields_desc = [ XShortField("hwtype", 0x0001), + XShortEnumField("ptype", 0x0800, ETHER_TYPES), + ByteField("hwlen", 6), + ByteField("plen", 4), + ShortEnumField("op", 1, {"who-has":1, "is-at":2, "RARP-req":3, "RARP-rep":4, "Dyn-RARP-req":5, "Dyn-RAR-rep":6, "Dyn-RARP-err":7, "InARP-req":8, "InARP-rep":9}), + ARPSourceMACField("hwsrc"), + SourceIPField("psrc","pdst"), + MACField("hwdst", ETHER_ANY), + IPField("pdst", "0.0.0.0") ] + who_has = 1 + is_at = 2 + def answers(self, other): + if isinstance(other,ARP): + if ( (self.op == self.is_at) and + (other.op == self.who_has) and + (self.psrc == other.pdst) ): + return 1 + return 0 + def route(self): + dst = self.pdst + if isinstance(dst,Gen): + dst = iter(dst).next() + return conf.route.route(dst) + def extract_padding(self, s): + return "",s + def mysummary(self): + if self.op == self.is_at: + return self.sprintf("ARP is at %hwsrc% says %psrc%") + elif self.op == self.who_has: + return self.sprintf("ARP who has %pdst% says %psrc%") + else: + return self.sprintf("ARP %op% %psrc% > %pdst%") + +conf.neighbor.register_l3(Ether, ARP, lambda l2,l3: getmacbyip(l3.pdst)) + +class GRErouting(Packet): + name = "GRE routing informations" + fields_desc = [ ShortField("address_family",0), + ByteField("SRE_offset", 0), + FieldLenField("SRE_len", None, "routing_info", "B"), + StrLenField("routing_info", "", "SRE_len"), + ] + + +class GRE(Packet): + name = "GRE" + fields_desc = [ BitField("chksum_present",0,1), + BitField("routing_present",0,1), + BitField("key_present",0,1), + BitField("seqnum_present",0,1), + BitField("strict_route_source",0,1), + BitField("recursion_control",0,3), + BitField("flags",0,5), + BitField("version",0,3), + XShortEnumField("proto", 0x0000, ETHER_TYPES), + ConditionalField(XShortField("chksum",None), lambda pkt:pkt.chksum_present==1 or pkt.routing_present==1), + ConditionalField(XShortField("offset",None), lambda pkt:pkt.chksum_present==1 or pkt.routing_present==1), + ConditionalField(XIntField("key",None), lambda pkt:pkt.key_present==1), + ConditionalField(XIntField("seqence_number",None), lambda pkt:pkt.seqnum_present==1), + ] + def post_build(self, p, pay): + p += pay + if self.chksum_present and self.chksum is None: + c = checksum(p) + p = p[:4]+chr((c>>8)&0xff)+chr(c&0xff)+p[6:] + return p + + + + +bind_layers( Dot3, LLC, ) +bind_layers( Ether, LLC, type=122) +bind_layers( Ether, Dot1Q, type=33024) +bind_layers( Ether, Ether, type=1) +bind_layers( Ether, ARP, type=2054) +bind_layers( Ether, EAPOL, type=34958) +bind_layers( Ether, EAPOL, dst='01:80:c2:00:00:03', type=34958) +bind_layers( CookedLinux, LLC, proto=122) +bind_layers( CookedLinux, Dot1Q, proto=33024) +bind_layers( CookedLinux, Ether, proto=1) +bind_layers( CookedLinux, ARP, proto=2054) +bind_layers( CookedLinux, EAPOL, proto=34958) +bind_layers( GRE, LLC, proto=122) +bind_layers( GRE, Dot1Q, proto=33024) +bind_layers( GRE, Ether, proto=1) +bind_layers( GRE, ARP, proto=2054) +bind_layers( GRE, EAPOL, proto=34958) +bind_layers( GRE, GRErouting, { "routing_present" : 1 } ) +bind_layers( GRErouting, conf.raw_layer,{ "address_family" : 0, "SRE_len" : 0 }) +bind_layers( GRErouting, GRErouting, { } ) +bind_layers( EAPOL, EAP, type=0) +bind_layers( LLC, STP, dsap=66, ssap=66, ctrl=3) +bind_layers( LLC, SNAP, dsap=170, ssap=170, ctrl=3) +bind_layers( SNAP, Dot1Q, code=33024) +bind_layers( SNAP, Ether, code=1) +bind_layers( SNAP, ARP, code=2054) +bind_layers( SNAP, EAPOL, code=34958) +bind_layers( SNAP, STP, code=267) + +conf.l2types.register(ARPHDR_ETHER, Ether) +conf.l2types.register_num2layer(ARPHDR_METRICOM, Ether) +conf.l2types.register_num2layer(ARPHDR_LOOPBACK, Ether) +conf.l2types.register_layer2num(ARPHDR_ETHER, Dot3) +conf.l2types.register(144, CookedLinux) # called LINUX_IRDA, similar to CookedLinux +conf.l2types.register(113, CookedLinux) + +conf.l3types.register(ETH_P_ARP, ARP) + + + + +### Technics + + + +@conf.commands.register +def arpcachepoison(target, victim, interval=60): + """Poison target's cache with (your MAC,victim's IP) couple +arpcachepoison(target, victim, [interval=60]) -> None +""" + tmac = getmacbyip(target) + p = Ether(dst=tmac)/ARP(op="who-has", psrc=victim, pdst=target) + try: + while 1: + sendp(p, iface_hint=target) + if conf.verb > 1: + os.write(1,".") + time.sleep(interval) + except KeyboardInterrupt: + pass + + +class ARPingResult(SndRcvList): + def __init__(self, res=None, name="ARPing", stats=None): + SndRcvList.__init__(self, res, name, stats) + + def show(self): + for s,r in self.res: + print r.sprintf("%19s,Ether.src% %ARP.psrc%") + + + +@conf.commands.register +def arping(net, timeout=2, cache=0, verbose=None, **kargs): + """Send ARP who-has requests to determine which hosts are up +arping(net, [cache=0,] [iface=conf.iface,] [verbose=conf.verb]) -> None +Set cache=True if you want arping to modify internal ARP-Cache""" + if verbose is None: + verbose = conf.verb + ans,unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=net), verbose=verbose, + filter="arp and arp[7] = 2", timeout=timeout, iface_hint=net, **kargs) + ans = ARPingResult(ans.res) + + if cache and ans is not None: + for pair in ans: + conf.netcache.arp_cache[pair[1].psrc] = (pair[1].hwsrc, time.time()) + if verbose: + ans.show() + return ans,unans + +@conf.commands.register +def is_promisc(ip, fake_bcast="ff:ff:00:00:00:00",**kargs): + """Try to guess if target is in Promisc mode. The target is provided by its ip.""" + + responses = srp1(Ether(dst=fake_bcast) / ARP(op="who-has", pdst=ip),type=ETH_P_ARP, iface_hint=ip, timeout=1, verbose=0,**kargs) + + return responses is not None + +@conf.commands.register +def promiscping(net, timeout=2, fake_bcast="ff:ff:ff:ff:ff:fe", **kargs): + """Send ARP who-has requests to determine which hosts are in promiscuous mode + promiscping(net, iface=conf.iface)""" + ans,unans = srp(Ether(dst=fake_bcast)/ARP(pdst=net), + filter="arp and arp[7] = 2", timeout=timeout, iface_hint=net, **kargs) + ans = ARPingResult(ans.res, name="PROMISCPing") + + ans.display() + return ans,unans + + +class ARP_am(AnsweringMachine): + function_name="farpd" + filter = "arp" + send_function = staticmethod(sendp) + + def parse_options(self, IP_addr=None, iface=None, ARP_addr=None): + self.IP_addr=IP_addr + self.iface=iface + self.ARP_addr=ARP_addr + + def is_request(self, req): + return (req.haslayer(ARP) and + req.getlayer(ARP).op == 1 and + (self.IP_addr == None or self.IP_addr == req.getlayer(ARP).pdst)) + + def make_reply(self, req): + ether = req.getlayer(Ether) + arp = req.getlayer(ARP) + iff,a,gw = conf.route.route(arp.psrc) + if self.iface != None: + iff = iface + ARP_addr = self.ARP_addr + IP_addr = arp.pdst + resp = Ether(dst=ether.src, + src=ARP_addr)/ARP(op="is-at", + hwsrc=ARP_addr, + psrc=IP_addr, + hwdst=arp.hwsrc, + pdst=arp.pdst) + return resp + + def sniff(self): + sniff(iface=self.iface, **self.optsniff) + +@conf.commands.register +def etherleak(target, **kargs): + """Exploit Etherleak flaw""" + return srpflood(Ether()/ARP(pdst=target), + prn=lambda (s,r): conf.padding_layer in r and hexstr(r[conf.padding_layer].load), + filter="arp", **kargs) + + diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/l2tp.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/l2tp.py new file mode 100644 index 00000000..0b56db21 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/l2tp.py @@ -0,0 +1,36 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +L2TP (Layer 2 Tunneling Protocol) for VPNs. + +[RFC 2661] +""" + +import struct + +from scapy.packet import * +from scapy.fields import * +from scapy.layers.inet import UDP +from scapy.layers.ppp import PPP + +class L2TP(Packet): + fields_desc = [ ShortEnumField("pkt_type",2,{2:"data"}), + ShortField("len", None), + ShortField("tunnel_id", 0), + ShortField("session_id", 0), + ShortField("ns", 0), + ShortField("nr", 0), + ShortField("offset", 0) ] + + def post_build(self, pkt, pay): + if self.len is None: + l = len(pkt)+len(pay) + pkt = pkt[:2]+struct.pack("!H", l)+pkt[4:] + return pkt+pay + + +bind_layers( UDP, L2TP, sport=1701, dport=1701) +bind_layers( L2TP, PPP, ) diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/llmnr.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/llmnr.py new file mode 100644 index 00000000..65ecad41 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/llmnr.py @@ -0,0 +1,65 @@ +from scapy.fields import * +from scapy.packet import * +from scapy.layers.inet import UDP +from scapy.layers.dns import DNSQRField, DNSRRField, DNSRRCountField + +""" +LLMNR (Link Local Multicast Node Resolution). + +[RFC 4795] +""" + +############################################################################# +### LLMNR (RFC4795) ### +############################################################################# +# LLMNR is based on the DNS packet format (RFC1035 Section 4) +# RFC also envisions LLMNR over TCP. Like vista, we don't support it -- arno + +_LLMNR_IPv6_mcast_Addr = "FF02:0:0:0:0:0:1:3" +_LLMNR_IPv4_mcast_addr = "224.0.0.252" + +class LLMNRQuery(Packet): + name = "Link Local Multicast Node Resolution - Query" + fields_desc = [ ShortField("id", 0), + BitField("qr", 0, 1), + BitEnumField("opcode", 0, 4, { 0:"QUERY" }), + BitField("c", 0, 1), + BitField("tc", 0, 2), + BitField("z", 0, 4), + BitEnumField("rcode", 0, 4, { 0:"ok" }), + DNSRRCountField("qdcount", None, "qd"), + DNSRRCountField("ancount", None, "an"), + DNSRRCountField("nscount", None, "ns"), + DNSRRCountField("arcount", None, "ar"), + DNSQRField("qd", "qdcount"), + DNSRRField("an", "ancount"), + DNSRRField("ns", "nscount"), + DNSRRField("ar", "arcount",0)] + overload_fields = {UDP: {"sport": 5355, "dport": 5355 }} + def hashret(self): + return struct.pack("!H", self.id) + +class LLMNRResponse(LLMNRQuery): + name = "Link Local Multicast Node Resolution - Response" + qr = 1 + def answers(self, other): + return (isinstance(other, LLMNRQuery) and + self.id == other.id and + self.qr == 1 and + other.qr == 0) + +def _llmnr_dispatcher(x, *args, **kargs): + cls = conf.raw_layer + if len(x) >= 3: + if (ord(x[4]) & 0x80): # Response + cls = LLMNRResponse + else: # Query + cls = LLMNRQuery + return cls(x, *args, **kargs) + +bind_bottom_up(UDP, _llmnr_dispatcher, { "dport": 5355 }) +bind_bottom_up(UDP, _llmnr_dispatcher, { "sport": 5355 }) + +# LLMNRQuery(id=RandShort(), qd=DNSQR(qname="vista."))) + + diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/mgcp.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/mgcp.py new file mode 100644 index 00000000..5d8a064e --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/mgcp.py @@ -0,0 +1,45 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +MGCP (Media Gateway Control Protocol) + +[RFC 2805] +""" + +from scapy.packet import * +from scapy.fields import * +from scapy.layers.inet import UDP + +class MGCP(Packet): + name = "MGCP" + longname = "Media Gateway Control Protocol" + fields_desc = [ StrStopField("verb","AUEP"," ", -1), + StrFixedLenField("sep1"," ",1), + StrStopField("transaction_id","1234567"," ", -1), + StrFixedLenField("sep2"," ",1), + StrStopField("endpoint","dummy@dummy.net"," ", -1), + StrFixedLenField("sep3"," ",1), + StrStopField("version","MGCP 1.0 NCS 1.0","\x0a", -1), + StrFixedLenField("sep4","\x0a",1), + ] + + +#class MGCP(Packet): +# name = "MGCP" +# longname = "Media Gateway Control Protocol" +# fields_desc = [ ByteEnumField("type",0, ["request","response","others"]), +# ByteField("code0",0), +# ByteField("code1",0), +# ByteField("code2",0), +# ByteField("code3",0), +# ByteField("code4",0), +# IntField("trasid",0), +# IntField("req_time",0), +# ByteField("is_duplicate",0), +# ByteField("req_available",0) ] +# +bind_layers( UDP, MGCP, dport=2727) +bind_layers( UDP, MGCP, sport=2727) diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/mobileip.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/mobileip.py new file mode 100644 index 00000000..bbaa8ce7 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/mobileip.py @@ -0,0 +1,47 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +Mobile IP. +""" + +from scapy.fields import * +from scapy.packet import * +from scapy.layers.inet import IP,UDP + + +class MobileIP(Packet): + name = "Mobile IP (RFC3344)" + fields_desc = [ ByteEnumField("type", 1, {1:"RRQ", 3:"RRP"}) ] + +class MobileIPRRQ(Packet): + name = "Mobile IP Registration Request (RFC3344)" + fields_desc = [ XByteField("flags", 0), + ShortField("lifetime", 180), + IPField("homeaddr", "0.0.0.0"), + IPField("haaddr", "0.0.0.0"), + IPField("coaddr", "0.0.0.0"), + LongField("id", 0), ] + +class MobileIPRRP(Packet): + name = "Mobile IP Registration Reply (RFC3344)" + fields_desc = [ ByteField("code", 0), + ShortField("lifetime", 180), + IPField("homeaddr", "0.0.0.0"), + IPField("haaddr", "0.0.0.0"), + LongField("id", 0), ] + +class MobileIPTunnelData(Packet): + name = "Mobile IP Tunnel Data Message (RFC3519)" + fields_desc = [ ByteField("nexthdr", 4), + ShortField("res", 0) ] + + +bind_layers( UDP, MobileIP, sport=434) +bind_layers( UDP, MobileIP, dport=434) +bind_layers( MobileIP, MobileIPRRQ, type=1) +bind_layers( MobileIP, MobileIPRRP, type=3) +bind_layers( MobileIP, MobileIPTunnelData, type=4) +bind_layers( MobileIPTunnelData, IP, nexthdr=4) diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/netbios.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/netbios.py new file mode 100644 index 00000000..605e06f1 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/netbios.py @@ -0,0 +1,222 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +NetBIOS over TCP/IP + +[RFC 1001/1002] +""" + +import struct +from scapy.packet import * +from scapy.fields import * +from scapy.layers.inet import UDP,TCP +from scapy.layers.l2 import SourceMACField + +class NetBIOS_DS(Packet): + name = "NetBIOS datagram service" + fields_desc = [ + ByteEnumField("type",17, {17:"direct_group"}), + ByteField("flags",0), + XShortField("id",0), + IPField("src","127.0.0.1"), + ShortField("sport",138), + ShortField("len",None), + ShortField("ofs",0), + NetBIOSNameField("srcname",""), + NetBIOSNameField("dstname",""), + ] + def post_build(self, p, pay): + p += pay + if self.len is None: + l = len(p)-14 + p = p[:10]+struct.pack("!H", l)+p[12:] + return p + +# ShortField("length",0), +# ShortField("Delimitor",0), +# ByteField("command",0), +# ByteField("data1",0), +# ShortField("data2",0), +# ShortField("XMIt",0), +# ShortField("RSPCor",0), +# StrFixedLenField("dest","",16), +# StrFixedLenField("source","",16), +# +# ] +# + +#NetBIOS + + +# Name Query Request +# Node Status Request +class NBNSQueryRequest(Packet): + name="NBNS query request" + fields_desc = [ShortField("NAME_TRN_ID",0), + ShortField("FLAGS", 0x0110), + ShortField("QDCOUNT",1), + ShortField("ANCOUNT",0), + ShortField("NSCOUNT",0), + ShortField("ARCOUNT",0), + NetBIOSNameField("QUESTION_NAME","windows"), + ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), + ByteField("NULL",0), + ShortEnumField("QUESTION_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), + ShortEnumField("QUESTION_CLASS",1,{1:"INTERNET"})] + +# Name Registration Request +# Name Refresh Request +# Name Release Request or Demand +class NBNSRequest(Packet): + name="NBNS request" + fields_desc = [ShortField("NAME_TRN_ID",0), + ShortField("FLAGS", 0x2910), + ShortField("QDCOUNT",1), + ShortField("ANCOUNT",0), + ShortField("NSCOUNT",0), + ShortField("ARCOUNT",1), + NetBIOSNameField("QUESTION_NAME","windows"), + ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), + ByteField("NULL",0), + ShortEnumField("QUESTION_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), + ShortEnumField("QUESTION_CLASS",1,{1:"INTERNET"}), + ShortEnumField("RR_NAME",0xC00C,{0xC00C:"Label String Pointer to QUESTION_NAME"}), + ShortEnumField("RR_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), + ShortEnumField("RR_CLASS",1,{1:"INTERNET"}), + IntField("TTL", 0), + ShortField("RDLENGTH", 6), + BitEnumField("G",0,1,{0:"Unique name",1:"Group name"}), + BitEnumField("OWNER_NODE_TYPE",00,2,{00:"B node",01:"P node",02:"M node",03:"H node"}), + BitEnumField("UNUSED",0,13,{0:"Unused"}), + IPField("NB_ADDRESS", "127.0.0.1")] + +# Name Query Response +# Name Registration Response +class NBNSQueryResponse(Packet): + name="NBNS query response" + fields_desc = [ShortField("NAME_TRN_ID",0), + ShortField("FLAGS", 0x8500), + ShortField("QDCOUNT",0), + ShortField("ANCOUNT",1), + ShortField("NSCOUNT",0), + ShortField("ARCOUNT",0), + NetBIOSNameField("RR_NAME","windows"), + ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), + ByteField("NULL",0), + ShortEnumField("QUESTION_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), + ShortEnumField("QUESTION_CLASS",1,{1:"INTERNET"}), + IntField("TTL", 0x493e0), + ShortField("RDLENGTH", 6), + ShortField("NB_FLAGS", 0), + IPField("NB_ADDRESS", "127.0.0.1")] + +# Name Query Response (negative) +# Name Release Response +class NBNSQueryResponseNegative(Packet): + name="NBNS query response (negative)" + fields_desc = [ShortField("NAME_TRN_ID",0), + ShortField("FLAGS", 0x8506), + ShortField("QDCOUNT",0), + ShortField("ANCOUNT",1), + ShortField("NSCOUNT",0), + ShortField("ARCOUNT",0), + NetBIOSNameField("RR_NAME","windows"), + ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), + ByteField("NULL",0), + ShortEnumField("RR_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), + ShortEnumField("RR_CLASS",1,{1:"INTERNET"}), + IntField("TTL",0), + ShortField("RDLENGTH",6), + BitEnumField("G",0,1,{0:"Unique name",1:"Group name"}), + BitEnumField("OWNER_NODE_TYPE",00,2,{00:"B node",01:"P node",02:"M node",03:"H node"}), + BitEnumField("UNUSED",0,13,{0:"Unused"}), + IPField("NB_ADDRESS", "127.0.0.1")] + +# Node Status Response +class NBNSNodeStatusResponse(Packet): + name="NBNS Node Status Response" + fields_desc = [ShortField("NAME_TRN_ID",0), + ShortField("FLAGS", 0x8500), + ShortField("QDCOUNT",0), + ShortField("ANCOUNT",1), + ShortField("NSCOUNT",0), + ShortField("ARCOUNT",0), + NetBIOSNameField("RR_NAME","windows"), + ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), + ByteField("NULL",0), + ShortEnumField("RR_TYPE",0x21, {0x20:"NB",0x21:"NBSTAT"}), + ShortEnumField("RR_CLASS",1,{1:"INTERNET"}), + IntField("TTL",0), + ShortField("RDLENGTH",83), + ByteField("NUM_NAMES",1)] + +# Service for Node Status Response +class NBNSNodeStatusResponseService(Packet): + name="NBNS Node Status Response Service" + fields_desc = [StrFixedLenField("NETBIOS_NAME","WINDOWS ",15), + ByteEnumField("SUFFIX",0,{0:"workstation",0x03:"messenger service",0x20:"file server service",0x1b:"domain master browser",0x1c:"domain controller", 0x1e:"browser election service"}), + ByteField("NAME_FLAGS",0x4), + ByteEnumField("UNUSED",0,{0:"unused"})] + +# End of Node Status Response packet +class NBNSNodeStatusResponseEnd(Packet): + name="NBNS Node Status Response" + fields_desc = [SourceMACField("MAC_ADDRESS"), + BitField("STATISTICS",0,57*8)] + +# Wait for Acknowledgement Response +class NBNSWackResponse(Packet): + name="NBNS Wait for Acknowledgement Response" + fields_desc = [ShortField("NAME_TRN_ID",0), + ShortField("FLAGS", 0xBC07), + ShortField("QDCOUNT",0), + ShortField("ANCOUNT",1), + ShortField("NSCOUNT",0), + ShortField("ARCOUNT",0), + NetBIOSNameField("RR_NAME","windows"), + ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), + ByteField("NULL",0), + ShortEnumField("RR_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), + ShortEnumField("RR_CLASS",1,{1:"INTERNET"}), + IntField("TTL", 2), + ShortField("RDLENGTH",2), + BitField("RDATA",10512,16)] #10512=0010100100010000 + +class NBTDatagram(Packet): + name="NBT Datagram Packet" + fields_desc= [ByteField("Type", 0x10), + ByteField("Flags", 0x02), + ShortField("ID", 0), + IPField("SourceIP", "127.0.0.1"), + ShortField("SourcePort", 138), + ShortField("Length", 272), + ShortField("Offset", 0), + NetBIOSNameField("SourceName","windows"), + ShortEnumField("SUFFIX1",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), + ByteField("NULL",0), + NetBIOSNameField("DestinationName","windows"), + ShortEnumField("SUFFIX2",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), + ByteField("NULL",0)] + + +class NBTSession(Packet): + name="NBT Session Packet" + fields_desc= [ByteEnumField("TYPE",0,{0x00:"Session Message",0x81:"Session Request",0x82:"Positive Session Response",0x83:"Negative Session Response",0x84:"Retarget Session Response",0x85:"Session Keepalive"}), + BitField("RESERVED",0x00,7), + BitField("LENGTH",0,17)] + +bind_layers( UDP, NBNSQueryRequest, dport=137) +bind_layers( UDP, NBNSRequest, dport=137) +bind_layers( UDP, NBNSQueryResponse, sport=137) +bind_layers( UDP, NBNSQueryResponseNegative, sport=137) +bind_layers( UDP, NBNSNodeStatusResponse, sport=137) +bind_layers( NBNSNodeStatusResponse, NBNSNodeStatusResponseService, ) +bind_layers( NBNSNodeStatusResponse, NBNSNodeStatusResponseService, ) +bind_layers( NBNSNodeStatusResponseService, NBNSNodeStatusResponseService, ) +bind_layers( NBNSNodeStatusResponseService, NBNSNodeStatusResponseEnd, ) +bind_layers( UDP, NBNSWackResponse, sport=137) +bind_layers( UDP, NBTDatagram, dport=138) +bind_layers( TCP, NBTSession, dport=139) diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/netflow.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/netflow.py new file mode 100644 index 00000000..44567737 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/netflow.py @@ -0,0 +1,48 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +Cisco NetFlow protocol v1 +""" + + +from scapy.fields import * +from scapy.packet import * + +# Cisco Netflow Protocol version 1 +class NetflowHeader(Packet): + name = "Netflow Header" + fields_desc = [ ShortField("version", 1) ] + +class NetflowHeaderV1(Packet): + name = "Netflow Header V1" + fields_desc = [ ShortField("count", 0), + IntField("sysUptime", 0), + IntField("unixSecs", 0), + IntField("unixNanoSeconds", 0) ] + + +class NetflowRecordV1(Packet): + name = "Netflow Record" + fields_desc = [ IPField("ipsrc", "0.0.0.0"), + IPField("ipdst", "0.0.0.0"), + IPField("nexthop", "0.0.0.0"), + ShortField("inputIfIndex", 0), + ShortField("outpuIfIndex", 0), + IntField("dpkts", 0), + IntField("dbytes", 0), + IntField("starttime", 0), + IntField("endtime", 0), + ShortField("srcport", 0), + ShortField("dstport", 0), + ShortField("padding", 0), + ByteField("proto", 0), + ByteField("tos", 0), + IntField("padding1", 0), + IntField("padding2", 0) ] + + +bind_layers( NetflowHeader, NetflowHeaderV1, version=1) +bind_layers( NetflowHeaderV1, NetflowRecordV1, ) diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/ntp.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/ntp.py new file mode 100644 index 00000000..6d11966c --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/ntp.py @@ -0,0 +1,77 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +NTP (Network Time Protocol). +""" + +import time +from scapy.packet import * +from scapy.fields import * +from scapy.layers.inet import UDP + + +# seconds between 01-01-1900 and 01-01-1970 +_NTP_BASETIME = 2208988800 + +class TimeStampField(FixedPointField): + def __init__(self, name, default): + FixedPointField.__init__(self, name, default, 64, 32) + + def i2repr(self, pkt, val): + if val is None: + return "--" + val = self.i2h(pkt,val) + if val < _NTP_BASETIME: + return val + return time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(val-_NTP_BASETIME)) + + def any2i(self, pkt, val): + if type(val) is str: + return int(time.mktime(time.strptime(val))) + _NTP_BASETIME + 3600 # XXX + return FixedPointField.any2i(self,pkt,val) + + def i2m(self, pkt, val): + if val is None: + val = FixedPointField.any2i(self, pkt, time.time()+_NTP_BASETIME) + return FixedPointField.i2m(self, pkt, val) + + + +class NTP(Packet): + # RFC 1769 + name = "NTP" + fields_desc = [ + BitEnumField('leap', 0, 2, + { 0: 'nowarning', + 1: 'longminute', + 2: 'shortminute', + 3: 'notsync'}), + BitField('version', 3, 3), + BitEnumField('mode', 3, 3, + { 0: 'reserved', + 1: 'sym_active', + 2: 'sym_passive', + 3: 'client', + 4: 'server', + 5: 'broadcast', + 6: 'control', + 7: 'private'}), + BitField('stratum', 2, 8), + BitField('poll', 0xa, 8), ### XXX : it's a signed int + BitField('precision', 0, 8), ### XXX : it's a signed int + FixedPointField('delay', 0, size=32, frac_bits=16), + FixedPointField('dispersion', 0, size=32, frac_bits=16), + IPField('id', "127.0.0.1"), + TimeStampField('ref', 0), + TimeStampField('orig', None), # None means current time + TimeStampField('recv', 0), + TimeStampField('sent', None) + ] + def mysummary(self): + return self.sprintf("NTP v%ir,NTP.version%, %NTP.mode%") + + +bind_layers( UDP, NTP, dport=123, sport=123) diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/pflog.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/pflog.py new file mode 100644 index 00000000..a8fc9fe0 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/pflog.py @@ -0,0 +1,59 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +PFLog: OpenBSD PF packet filter logging. +""" + +from scapy.packet import * +from scapy.fields import * +from scapy.layers.inet import IP +if conf.ipv6_enabled: + from scapy.layers.inet6 import IPv6 +from scapy.config import conf + +class PFLog(Packet): + name = "PFLog" + # from OpenBSD src/sys/net/pfvar.h and src/sys/net/if_pflog.h + fields_desc = [ ByteField("hdrlen", 0), + ByteEnumField("addrfamily", 2, {socket.AF_INET: "IPv4", + socket.AF_INET6: "IPv6"}), + ByteEnumField("action", 1, {0: "pass", 1: "drop", + 2: "scrub", 3: "no-scrub", + 4: "nat", 5: "no-nat", + 6: "binat", 7: "no-binat", + 8: "rdr", 9: "no-rdr", + 10: "syn-proxy-drop" }), + ByteEnumField("reason", 0, {0: "match", 1: "bad-offset", + 2: "fragment", 3: "short", + 4: "normalize", 5: "memory", + 6: "bad-timestamp", + 7: "congestion", + 8: "ip-options", + 9: "proto-cksum", + 10: "state-mismatch", + 11: "state-insert", + 12: "state-limit", + 13: "src-limit", + 14: "syn-proxy" }), + StrFixedLenField("iface", "", 16), + StrFixedLenField("ruleset", "", 16), + SignedIntField("rulenumber", 0), + SignedIntField("subrulenumber", 0), + SignedIntField("uid", 0), + IntField("pid", 0), + SignedIntField("ruleuid", 0), + IntField("rulepid", 0), + ByteEnumField("direction", 255, {0: "inout", 1: "in", + 2:"out", 255: "unknown"}), + StrFixedLenField("pad", "\x00\x00\x00", 3 ) ] + def mysummary(self): + return self.sprintf("%PFLog.addrfamily% %PFLog.action% on %PFLog.iface% by rule %PFLog.rulenumber%") + +bind_layers(PFLog, IP, addrfamily=socket.AF_INET) +if conf.ipv6_enabled: + bind_layers(PFLog, IPv6, addrfamily=socket.AF_INET6) + +conf.l2types.register(117, PFLog) diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/ppp.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/ppp.py new file mode 100644 index 00000000..50c68465 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/ppp.py @@ -0,0 +1,347 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +PPP (Point to Point Protocol) + +[RFC 1661] +""" + +import struct +from scapy.packet import * +from scapy.layers.l2 import * +from scapy.layers.inet import * +from scapy.fields import * + +class PPPoE(Packet): + name = "PPP over Ethernet" + fields_desc = [ BitField("version", 1, 4), + BitField("type", 1, 4), + ByteEnumField("code", 0, {0:"Session"}), + XShortField("sessionid", 0x0), + ShortField("len", None) ] + + def post_build(self, p, pay): + p += pay + if self.len is None: + l = len(p)-6 + p = p[:4]+struct.pack("!H", l)+p[6:] + return p + +class PPPoED(PPPoE): + name = "PPP over Ethernet Discovery" + fields_desc = [ BitField("version", 1, 4), + BitField("type", 1, 4), + ByteEnumField("code", 0x09, {0x09:"PADI",0x07:"PADO",0x19:"PADR",0x65:"PADS",0xa7:"PADT"}), + XShortField("sessionid", 0x0), + ShortField("len", None) ] + + +_PPP_proto = { 0x0001: "Padding Protocol", + 0x0003: "ROHC small-CID [RFC3095]", + 0x0005: "ROHC large-CID [RFC3095]", + 0x0021: "Internet Protocol version 4", + 0x0023: "OSI Network Layer", + 0x0025: "Xerox NS IDP", + 0x0027: "DECnet Phase IV", + 0x0029: "Appletalk", + 0x002b: "Novell IPX", + 0x002d: "Van Jacobson Compressed TCP/IP", + 0x002f: "Van Jacobson Uncompressed TCP/IP", + 0x0031: "Bridging PDU", + 0x0033: "Stream Protocol (ST-II)", + 0x0035: "Banyan Vines", + 0x0037: "reserved (until 1993) [Typo in RFC1172]", + 0x0039: "AppleTalk EDDP", + 0x003b: "AppleTalk SmartBuffered", + 0x003d: "Multi-Link [RFC1717]", + 0x003f: "NETBIOS Framing", + 0x0041: "Cisco Systems", + 0x0043: "Ascom Timeplex", + 0x0045: "Fujitsu Link Backup and Load Balancing (LBLB)", + 0x0047: "DCA Remote Lan", + 0x0049: "Serial Data Transport Protocol (PPP-SDTP)", + 0x004b: "SNA over 802.2", + 0x004d: "SNA", + 0x004f: "IPv6 Header Compression", + 0x0051: "KNX Bridging Data [ianp]", + 0x0053: "Encryption [Meyer]", + 0x0055: "Individual Link Encryption [Meyer]", + 0x0057: "Internet Protocol version 6 [Hinden]", + 0x0059: "PPP Muxing [RFC3153]", + 0x005b: "Vendor-Specific Network Protocol (VSNP) [RFC3772]", + 0x0061: "RTP IPHC Full Header [RFC3544]", + 0x0063: "RTP IPHC Compressed TCP [RFC3544]", + 0x0065: "RTP IPHC Compressed Non TCP [RFC3544]", + 0x0067: "RTP IPHC Compressed UDP 8 [RFC3544]", + 0x0069: "RTP IPHC Compressed RTP 8 [RFC3544]", + 0x006f: "Stampede Bridging", + 0x0071: "Reserved [Fox]", + 0x0073: "MP+ Protocol [Smith]", + 0x007d: "reserved (Control Escape) [RFC1661]", + 0x007f: "reserved (compression inefficient [RFC1662]", + 0x0081: "Reserved Until 20-Oct-2000 [IANA]", + 0x0083: "Reserved Until 20-Oct-2000 [IANA]", + 0x00c1: "NTCITS IPI [Ungar]", + 0x00cf: "reserved (PPP NLID)", + 0x00fb: "single link compression in multilink [RFC1962]", + 0x00fd: "compressed datagram [RFC1962]", + 0x00ff: "reserved (compression inefficient)", + 0x0201: "802.1d Hello Packets", + 0x0203: "IBM Source Routing BPDU", + 0x0205: "DEC LANBridge100 Spanning Tree", + 0x0207: "Cisco Discovery Protocol [Sastry]", + 0x0209: "Netcs Twin Routing [Korfmacher]", + 0x020b: "STP - Scheduled Transfer Protocol [Segal]", + 0x020d: "EDP - Extreme Discovery Protocol [Grosser]", + 0x0211: "Optical Supervisory Channel Protocol (OSCP)[Prasad]", + 0x0213: "Optical Supervisory Channel Protocol (OSCP)[Prasad]", + 0x0231: "Luxcom", + 0x0233: "Sigma Network Systems", + 0x0235: "Apple Client Server Protocol [Ridenour]", + 0x0281: "MPLS Unicast [RFC3032] ", + 0x0283: "MPLS Multicast [RFC3032]", + 0x0285: "IEEE p1284.4 standard - data packets [Batchelder]", + 0x0287: "ETSI TETRA Network Protocol Type 1 [Nieminen]", + 0x0289: "Multichannel Flow Treatment Protocol [McCann]", + 0x2063: "RTP IPHC Compressed TCP No Delta [RFC3544]", + 0x2065: "RTP IPHC Context State [RFC3544]", + 0x2067: "RTP IPHC Compressed UDP 16 [RFC3544]", + 0x2069: "RTP IPHC Compressed RTP 16 [RFC3544]", + 0x4001: "Cray Communications Control Protocol [Stage]", + 0x4003: "CDPD Mobile Network Registration Protocol [Quick]", + 0x4005: "Expand accelerator protocol [Rachmani]", + 0x4007: "ODSICP NCP [Arvind]", + 0x4009: "DOCSIS DLL [Gaedtke]", + 0x400B: "Cetacean Network Detection Protocol [Siller]", + 0x4021: "Stacker LZS [Simpson]", + 0x4023: "RefTek Protocol [Banfill]", + 0x4025: "Fibre Channel [Rajagopal]", + 0x4027: "EMIT Protocols [Eastham]", + 0x405b: "Vendor-Specific Protocol (VSP) [RFC3772]", + 0x8021: "Internet Protocol Control Protocol", + 0x8023: "OSI Network Layer Control Protocol", + 0x8025: "Xerox NS IDP Control Protocol", + 0x8027: "DECnet Phase IV Control Protocol", + 0x8029: "Appletalk Control Protocol", + 0x802b: "Novell IPX Control Protocol", + 0x802d: "reserved", + 0x802f: "reserved", + 0x8031: "Bridging NCP", + 0x8033: "Stream Protocol Control Protocol", + 0x8035: "Banyan Vines Control Protocol", + 0x8037: "reserved (until 1993)", + 0x8039: "reserved", + 0x803b: "reserved", + 0x803d: "Multi-Link Control Protocol", + 0x803f: "NETBIOS Framing Control Protocol", + 0x8041: "Cisco Systems Control Protocol", + 0x8043: "Ascom Timeplex", + 0x8045: "Fujitsu LBLB Control Protocol", + 0x8047: "DCA Remote Lan Network Control Protocol (RLNCP)", + 0x8049: "Serial Data Control Protocol (PPP-SDCP)", + 0x804b: "SNA over 802.2 Control Protocol", + 0x804d: "SNA Control Protocol", + 0x804f: "IP6 Header Compression Control Protocol", + 0x8051: "KNX Bridging Control Protocol [ianp]", + 0x8053: "Encryption Control Protocol [Meyer]", + 0x8055: "Individual Link Encryption Control Protocol [Meyer]", + 0x8057: "IPv6 Control Protovol [Hinden]", + 0x8059: "PPP Muxing Control Protocol [RFC3153]", + 0x805b: "Vendor-Specific Network Control Protocol (VSNCP) [RFC3772]", + 0x806f: "Stampede Bridging Control Protocol", + 0x8073: "MP+ Control Protocol [Smith]", + 0x8071: "Reserved [Fox]", + 0x807d: "Not Used - reserved [RFC1661]", + 0x8081: "Reserved Until 20-Oct-2000 [IANA]", + 0x8083: "Reserved Until 20-Oct-2000 [IANA]", + 0x80c1: "NTCITS IPI Control Protocol [Ungar]", + 0x80cf: "Not Used - reserved [RFC1661]", + 0x80fb: "single link compression in multilink control [RFC1962]", + 0x80fd: "Compression Control Protocol [RFC1962]", + 0x80ff: "Not Used - reserved [RFC1661]", + 0x8207: "Cisco Discovery Protocol Control [Sastry]", + 0x8209: "Netcs Twin Routing [Korfmacher]", + 0x820b: "STP - Control Protocol [Segal]", + 0x820d: "EDPCP - Extreme Discovery Protocol Ctrl Prtcl [Grosser]", + 0x8235: "Apple Client Server Protocol Control [Ridenour]", + 0x8281: "MPLSCP [RFC3032]", + 0x8285: "IEEE p1284.4 standard - Protocol Control [Batchelder]", + 0x8287: "ETSI TETRA TNP1 Control Protocol [Nieminen]", + 0x8289: "Multichannel Flow Treatment Protocol [McCann]", + 0xc021: "Link Control Protocol", + 0xc023: "Password Authentication Protocol", + 0xc025: "Link Quality Report", + 0xc027: "Shiva Password Authentication Protocol", + 0xc029: "CallBack Control Protocol (CBCP)", + 0xc02b: "BACP Bandwidth Allocation Control Protocol [RFC2125]", + 0xc02d: "BAP [RFC2125]", + 0xc05b: "Vendor-Specific Authentication Protocol (VSAP) [RFC3772]", + 0xc081: "Container Control Protocol [KEN]", + 0xc223: "Challenge Handshake Authentication Protocol", + 0xc225: "RSA Authentication Protocol [Narayana]", + 0xc227: "Extensible Authentication Protocol [RFC2284]", + 0xc229: "Mitsubishi Security Info Exch Ptcl (SIEP) [Seno]", + 0xc26f: "Stampede Bridging Authorization Protocol", + 0xc281: "Proprietary Authentication Protocol [KEN]", + 0xc283: "Proprietary Authentication Protocol [Tackabury]", + 0xc481: "Proprietary Node ID Authentication Protocol [KEN]"} + + +class HDLC(Packet): + fields_desc = [ XByteField("address",0xff), + XByteField("control",0x03) ] + +class PPP(Packet): + name = "PPP Link Layer" + fields_desc = [ ShortEnumField("proto", 0x0021, _PPP_proto) ] + @classmethod + def dispatch_hook(cls, _pkt=None, *args, **kargs): + if _pkt and _pkt[0] == '\xff': + cls = HDLC + return cls + +_PPP_conftypes = { 1:"Configure-Request", + 2:"Configure-Ack", + 3:"Configure-Nak", + 4:"Configure-Reject", + 5:"Terminate-Request", + 6:"Terminate-Ack", + 7:"Code-Reject", + 8:"Protocol-Reject", + 9:"Echo-Request", + 10:"Echo-Reply", + 11:"Discard-Request", + 14:"Reset-Request", + 15:"Reset-Ack", + } + + +### PPP IPCP stuff (RFC 1332) + +# All IPCP options are defined below (names and associated classes) +_PPP_ipcpopttypes = { 1:"IP-Addresses (Deprecated)", + 2:"IP-Compression-Protocol", + 3:"IP-Address", + 4:"Mobile-IPv4", # not implemented, present for completeness + 129:"Primary-DNS-Address", + 130:"Primary-NBNS-Address", + 131:"Secondary-DNS-Address", + 132:"Secondary-NBNS-Address"} + + +class PPP_IPCP_Option(Packet): + name = "PPP IPCP Option" + fields_desc = [ ByteEnumField("type" , None , _PPP_ipcpopttypes), + FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), + StrLenField("data", "", length_from=lambda p:max(0,p.len-2)) ] + def extract_padding(self, pay): + return "",pay + + registered_options = {} + @classmethod + def register_variant(cls): + cls.registered_options[cls.type.default] = cls + @classmethod + def dispatch_hook(cls, _pkt=None, *args, **kargs): + if _pkt: + o = ord(_pkt[0]) + return cls.registered_options.get(o, cls) + return cls + + +class PPP_IPCP_Option_IPAddress(PPP_IPCP_Option): + name = "PPP IPCP Option: IP Address" + fields_desc = [ ByteEnumField("type" , 3 , _PPP_ipcpopttypes), + FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), + IPField("data","0.0.0.0"), + ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] + +class PPP_IPCP_Option_DNS1(PPP_IPCP_Option): + name = "PPP IPCP Option: DNS1 Address" + fields_desc = [ ByteEnumField("type" , 129 , _PPP_ipcpopttypes), + FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), + IPField("data","0.0.0.0"), + ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] + +class PPP_IPCP_Option_DNS2(PPP_IPCP_Option): + name = "PPP IPCP Option: DNS2 Address" + fields_desc = [ ByteEnumField("type" , 131 , _PPP_ipcpopttypes), + FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), + IPField("data","0.0.0.0"), + ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] + +class PPP_IPCP_Option_NBNS1(PPP_IPCP_Option): + name = "PPP IPCP Option: NBNS1 Address" + fields_desc = [ ByteEnumField("type" , 130 , _PPP_ipcpopttypes), + FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), + IPField("data","0.0.0.0"), + ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] + +class PPP_IPCP_Option_NBNS2(PPP_IPCP_Option): + name = "PPP IPCP Option: NBNS2 Address" + fields_desc = [ ByteEnumField("type" , 132 , _PPP_ipcpopttypes), + FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), + IPField("data","0.0.0.0"), + ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] + + +class PPP_IPCP(Packet): + fields_desc = [ ByteEnumField("code" , 1, _PPP_conftypes), + XByteField("id", 0 ), + FieldLenField("len" , None, fmt="H", length_of="options", adjust=lambda p,x:x+4 ), + PacketListField("options", [], PPP_IPCP_Option, length_from=lambda p:p.len-4,) ] + + +### ECP + +_PPP_ecpopttypes = { 0:"OUI", + 1:"DESE", } + +class PPP_ECP_Option(Packet): + name = "PPP ECP Option" + fields_desc = [ ByteEnumField("type" , None , _PPP_ecpopttypes), + FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), + StrLenField("data", "", length_from=lambda p:max(0,p.len-2)) ] + def extract_padding(self, pay): + return "",pay + + registered_options = {} + @classmethod + def register_variant(cls): + cls.registered_options[cls.type.default] = cls + @classmethod + def dispatch_hook(cls, _pkt=None, *args, **kargs): + if _pkt: + o = ord(_pkt[0]) + return cls.registered_options.get(o, cls) + return cls + +class PPP_ECP_Option_OUI(PPP_ECP_Option): + fields_desc = [ ByteEnumField("type" , 0 , _PPP_ecpopttypes), + FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+6), + StrFixedLenField("oui","",3), + ByteField("subtype",0), + StrLenField("data", "", length_from=lambda p:p.len-6) ] + + + +class PPP_ECP(Packet): + fields_desc = [ ByteEnumField("code" , 1, _PPP_conftypes), + XByteField("id", 0 ), + FieldLenField("len" , None, fmt="H", length_of="options", adjust=lambda p,x:x+4 ), + PacketListField("options", [], PPP_ECP_Option, length_from=lambda p:p.len-4,) ] + +bind_layers( Ether, PPPoED, type=0x8863) +bind_layers( Ether, PPPoE, type=0x8864) +bind_layers( CookedLinux, PPPoED, proto=0x8863) +bind_layers( CookedLinux, PPPoE, proto=0x8864) +bind_layers( PPPoE, PPP, code=0) +bind_layers( HDLC, PPP, ) +bind_layers( PPP, IP, proto=33) +bind_layers( PPP, PPP_IPCP, proto=0x8021) +bind_layers( PPP, PPP_ECP, proto=0x8053) +bind_layers( Ether, PPP_IPCP, type=0x8021) +bind_layers( Ether, PPP_ECP, type=0x8053) diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/radius.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/radius.py new file mode 100644 index 00000000..13239603 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/radius.py @@ -0,0 +1,65 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +RADIUS (Remote Authentication Dial In User Service) +""" + +import struct +from scapy.packet import * +from scapy.fields import * + +class Radius(Packet): + name = "Radius" + fields_desc = [ ByteEnumField("code", 1, {1: "Access-Request", + 2: "Access-Accept", + 3: "Access-Reject", + 4: "Accounting-Request", + 5: "Accounting-Accept", + 6: "Accounting-Status", + 7: "Password-Request", + 8: "Password-Ack", + 9: "Password-Reject", + 10: "Accounting-Message", + 11: "Access-Challenge", + 12: "Status-Server", + 13: "Status-Client", + 21: "Resource-Free-Request", + 22: "Resource-Free-Response", + 23: "Resource-Query-Request", + 24: "Resource-Query-Response", + 25: "Alternate-Resource-Reclaim-Request", + 26: "NAS-Reboot-Request", + 27: "NAS-Reboot-Response", + 29: "Next-Passcode", + 30: "New-Pin", + 31: "Terminate-Session", + 32: "Password-Expired", + 33: "Event-Request", + 34: "Event-Response", + 40: "Disconnect-Request", + 41: "Disconnect-ACK", + 42: "Disconnect-NAK", + 43: "CoA-Request", + 44: "CoA-ACK", + 45: "CoA-NAK", + 50: "IP-Address-Allocate", + 51: "IP-Address-Release", + 253: "Experimental-use", + 254: "Reserved", + 255: "Reserved"} ), + ByteField("id", 0), + ShortField("len", None), + StrFixedLenField("authenticator","",16) ] + def post_build(self, p, pay): + p += pay + l = self.len + if l is None: + l = len(p) + p = p[:2]+struct.pack("!H",l)+p[4:] + return p + + + diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/rip.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/rip.py new file mode 100644 index 00000000..1507fe5c --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/rip.py @@ -0,0 +1,74 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +RIP (Routing Information Protocol). +""" + +from scapy.packet import * +from scapy.fields import * +from scapy.layers.inet import UDP + +class RIP(Packet): + name = "RIP header" + fields_desc = [ + ByteEnumField("cmd", 1, {1:"req", 2:"resp", 3:"traceOn", 4:"traceOff", + 5:"sun", 6:"trigReq", 7:"trigResp", 8:"trigAck", + 9:"updateReq", 10:"updateResp", 11:"updateAck"}), + ByteField("version", 1), + ShortField("null", 0), + ] + + def guess_payload_class(self, payload): + if payload[:2] == "\xff\xff": + return RIPAuth + else: + return Packet.guess_payload_class(self, payload) + +class RIPEntry(RIP): + name = "RIP entry" + fields_desc = [ + ShortEnumField("AF", 2, {2:"IP"}), + ShortField("RouteTag", 0), + IPField("addr", "0.0.0.0"), + IPField("mask", "0.0.0.0"), + IPField("nextHop", "0.0.0.0"), + IntEnumField("metric", 1, {16:"Unreach"}), + ] + +class RIPAuth(Packet): + name = "RIP authentication" + fields_desc = [ + ShortEnumField("AF", 0xffff, {0xffff:"Auth"}), + ShortEnumField("authtype", 2, {1:"md5authdata", 2:"simple", 3:"md5"}), + ConditionalField(StrFixedLenField("password", None, 16), + lambda pkt: pkt.authtype == 2), + ConditionalField(ShortField("digestoffset", 0), + lambda pkt: pkt.authtype == 3), + ConditionalField(ByteField("keyid", 0), + lambda pkt: pkt.authtype == 3), + ConditionalField(ByteField("authdatalen", 0), + lambda pkt: pkt.authtype == 3), + ConditionalField(IntField("seqnum", 0), + lambda pkt: pkt.authtype == 3), + ConditionalField(StrFixedLenField("zeropad", None, 8), + lambda pkt: pkt.authtype == 3), + ConditionalField(StrLenField("authdata", None, + length_from=lambda pkt: pkt.md5datalen), + lambda pkt: pkt.authtype == 1) + ] + + def pre_dissect(self, s): + if s[2:4] == "\x00\x01": + self.md5datalen = len(s) - 4 + + return s + + +bind_layers( UDP, RIP, sport=520) +bind_layers( UDP, RIP, dport=520) +bind_layers( RIP, RIPEntry, ) +bind_layers( RIPEntry, RIPEntry, ) +bind_layers( RIPAuth, RIPEntry, ) diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/rtp.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/rtp.py new file mode 100644 index 00000000..629dccdd --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/rtp.py @@ -0,0 +1,40 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +RTP (Real-time Transport Protocol). +""" + +from scapy.packet import * +from scapy.fields import * + +_rtp_payload_types = { + # http://www.iana.org/assignments/rtp-parameters + 0: 'G.711 PCMU', 3: 'GSM', + 4: 'G723', 5: 'DVI4', + 6: 'DVI4', 7: 'LPC', + 8: 'PCMA', 9: 'G722', + 10: 'L16', 11: 'L16', + 12: 'QCELP', 13: 'CN', + 14: 'MPA', 15: 'G728', + 16: 'DVI4', 17: 'DVI4', + 18: 'G729', 25: 'CelB', + 26: 'JPEG', 28: 'nv', + 31: 'H261', 32: 'MPV', + 33: 'MP2T', 34: 'H263' } + +class RTP(Packet): + name="RTP" + fields_desc = [ BitField('version', 2, 2), + BitField('padding', 0, 1), + BitField('extension', 0, 1), + BitFieldLenField('numsync', None, 4, count_of='sync'), + BitField('marker', 0, 1), + BitEnumField('payload', 0, 7, _rtp_payload_types), + ShortField('sequence', 0), + IntField('timestamp', 0), + IntField('sourcesync', 0), + FieldListField('sync', [], IntField("id",0), count_from=lambda pkt:pkt.numsync) ] + diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/sctp.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/sctp.py new file mode 100644 index 00000000..632becb1 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/sctp.py @@ -0,0 +1,437 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## Copyright (C) 6WIND +## This program is published under a GPLv2 license + +""" +SCTP (Stream Control Transmission Protocol). +""" + +import struct + +from scapy.packet import * +from scapy.fields import * +from scapy.layers.inet import IP +from scapy.layers.inet6 import IP6Field + +IPPROTO_SCTP=132 + +# crc32-c (Castagnoli) (crc32c_poly=0x1EDC6F41) +crc32c_table = [ + 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, + 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, + 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, + 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, + 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, + 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, + 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, + 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, + 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, + 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, + 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, + 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, + 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, + 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, + 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, + 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, + 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, + 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, + 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, + 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, + 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, + 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, + 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, + 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, + 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, + 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, + 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, + 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, + 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, + 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, + 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, + 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, + 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, + 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, + 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, + 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, + 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, + 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, + 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, + 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, + 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, + 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, + 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, + 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, + 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, + 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, + 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, + 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, + 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, + 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, + 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, + 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, + 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, + 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, + 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, + 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, + 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, + 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, + 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, + 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, + 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, + 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, + 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, + 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351, + ] + +def crc32c(buf): + crc = 0xffffffff + for c in buf: + crc = (crc>>8) ^ crc32c_table[(crc^(ord(c))) & 0xFF] + crc = (~crc) & 0xffffffff + # reverse endianness + return struct.unpack(">I",struct.pack("> 16) & 0xffff + print s1,s2 + + for c in buf: + print ord(c) + s1 = (s1 + ord(c)) % BASE + s2 = (s2 + s1) % BASE + print s1,s2 + return (s2 << 16) + s1 + +def sctp_checksum(buf): + return update_adler32(1, buf) +""" + +sctpchunktypescls = { + 0 : "SCTPChunkData", + 1 : "SCTPChunkInit", + 2 : "SCTPChunkInitAck", + 3 : "SCTPChunkSACK", + 4 : "SCTPChunkHeartbeatReq", + 5 : "SCTPChunkHeartbeatAck", + 6 : "SCTPChunkAbort", + 7 : "SCTPChunkShutdown", + 8 : "SCTPChunkShutdownAck", + 9 : "SCTPChunkError", + 10 : "SCTPChunkCookieEcho", + 11 : "SCTPChunkCookieAck", + 14 : "SCTPChunkShutdownComplete", + } + +sctpchunktypes = { + 0 : "data", + 1 : "init", + 2 : "init-ack", + 3 : "sack", + 4 : "heartbeat-req", + 5 : "heartbeat-ack", + 6 : "abort", + 7 : "shutdown", + 8 : "shutdown-ack", + 9 : "error", + 10 : "cookie-echo", + 11 : "cookie-ack", + 14 : "shutdown-complete", + } + +sctpchunkparamtypescls = { + 1 : "SCTPChunkParamHearbeatInfo", + 5 : "SCTPChunkParamIPv4Addr", + 6 : "SCTPChunkParamIPv6Addr", + 7 : "SCTPChunkParamStateCookie", + 8 : "SCTPChunkParamUnrocognizedParam", + 9 : "SCTPChunkParamCookiePreservative", + 11 : "SCTPChunkParamHostname", + 12 : "SCTPChunkParamSupportedAddrTypes", + 32768 : "SCTPChunkParamECNCapable", + 49152 : "SCTPChunkParamFwdTSN", + 49158 : "SCTPChunkParamAdaptationLayer", + } + +sctpchunkparamtypes = { + 1 : "heartbeat-info", + 5 : "IPv4", + 6 : "IPv6", + 7 : "state-cookie", + 8 : "unrecognized-param", + 9 : "cookie-preservative", + 11 : "hostname", + 12 : "addrtypes", + 32768 : "ecn-capable", + 49152 : "fwd-tsn-supported", + 49158 : "adaptation-layer", + } + +############## SCTP header + +# Dummy class to guess payload type (variable parameters) +class _SCTPChunkGuessPayload: + def default_payload_class(self,p): + if len(p) < 4: + return conf.padding_layer + else: + t = ord(p[0]) + return globals().get(sctpchunktypescls.get(t, "Raw"), conf.raw_layer) + + +class SCTP(_SCTPChunkGuessPayload, Packet): + fields_desc = [ ShortField("sport", None), + ShortField("dport", None), + XIntField("tag", None), + XIntField("chksum", None), ] + def answers(self, other): + if not isinstance(other, SCTP): + return 0 + if conf.checkIPsrc: + if not ((self.sport == other.dport) and + (self.dport == other.sport)): + return 0 + return 1 + def post_build(self, p, pay): + p += pay + if self.chksum is None: + crc = crc32c(str(p)) + p = p[:8]+struct.pack(">I", crc)+p[12:] + return p + +############## SCTP Chunk variable params + +class ChunkParamField(PacketListField): + islist = 1 + holds_packets=1 + def __init__(self, name, default, count_from=None, length_from=None): + PacketListField.__init__(self, name, default, conf.raw_layer, count_from=count_from, length_from=length_from) + def m2i(self, p, m): + cls = conf.raw_layer + if len(m) >= 4: + t = ord(m[0]) * 256 + ord(m[1]) + cls = globals().get(sctpchunkparamtypescls.get(t, "Raw"), conf.raw_layer) + return cls(m) + +# dummy class to avoid Raw() after Chunk params +class _SCTPChunkParam: + def extract_padding(self, s): + return "",s[:] + +class SCTPChunkParamHearbeatInfo(_SCTPChunkParam, Packet): + fields_desc = [ ShortEnumField("type", 1, sctpchunkparamtypes), + FieldLenField("len", None, length_of="data", + adjust = lambda pkt,x:x+4), + PadField(StrLenField("data", "", + length_from=lambda pkt: pkt.len-4), + 4, padwith="\x00"),] + +class SCTPChunkParamIPv4Addr(_SCTPChunkParam, Packet): + fields_desc = [ ShortEnumField("type", 5, sctpchunkparamtypes), + ShortField("len", 8), + IPField("addr","127.0.0.1"), ] + +class SCTPChunkParamIPv6Addr(_SCTPChunkParam, Packet): + fields_desc = [ ShortEnumField("type", 6, sctpchunkparamtypes), + ShortField("len", 20), + IP6Field("addr","::1"), ] + +class SCTPChunkParamStateCookie(_SCTPChunkParam, Packet): + fields_desc = [ ShortEnumField("type", 7, sctpchunkparamtypes), + FieldLenField("len", None, length_of="cookie", + adjust = lambda pkt,x:x+4), + PadField(StrLenField("cookie", "", + length_from=lambda pkt: pkt.len-4), + 4, padwith="\x00"),] + +class SCTPChunkParamUnrocognizedParam(_SCTPChunkParam, Packet): + fields_desc = [ ShortEnumField("type", 8, sctpchunkparamtypes), + FieldLenField("len", None, length_of="param", + adjust = lambda pkt,x:x+4), + PadField(StrLenField("param", "", + length_from=lambda pkt: pkt.len-4), + 4, padwith="\x00"),] + +class SCTPChunkParamCookiePreservative(_SCTPChunkParam, Packet): + fields_desc = [ ShortEnumField("type", 9, sctpchunkparamtypes), + ShortField("len", 8), + XIntField("sug_cookie_inc", None), ] + +class SCTPChunkParamHostname(_SCTPChunkParam, Packet): + fields_desc = [ ShortEnumField("type", 11, sctpchunkparamtypes), + FieldLenField("len", None, length_of="hostname", + adjust = lambda pkt,x:x+4), + PadField(StrLenField("hostname", "", + length_from=lambda pkt: pkt.len-4), + 4, padwith="\x00"), ] + +class SCTPChunkParamSupportedAddrTypes(_SCTPChunkParam, Packet): + fields_desc = [ ShortEnumField("type", 12, sctpchunkparamtypes), + FieldLenField("len", None, length_of="addr_type_list", + adjust = lambda pkt,x:x+4), + PadField(FieldListField("addr_type_list", [ "IPv4" ], + ShortEnumField("addr_type", 5, sctpchunkparamtypes), + length_from=lambda pkt: pkt.len-4), + 4, padwith="\x00"), ] + +class SCTPChunkParamECNCapable(_SCTPChunkParam, Packet): + fields_desc = [ ShortEnumField("type", 32768, sctpchunkparamtypes), + ShortField("len", 4), ] + +class SCTPChunkParamFwdTSN(_SCTPChunkParam, Packet): + fields_desc = [ ShortEnumField("type", 49152, sctpchunkparamtypes), + ShortField("len", 4), ] + +class SCTPChunkParamAdaptationLayer(_SCTPChunkParam, Packet): + fields_desc = [ ShortEnumField("type", 49158, sctpchunkparamtypes), + ShortField("len", 8), + XIntField("indication", None), ] + +############## SCTP Chunks + +class SCTPChunkData(_SCTPChunkGuessPayload, Packet): + fields_desc = [ ByteEnumField("type", 0, sctpchunktypes), + BitField("reserved", None, 4), + BitField("delay_sack", 0, 1), + BitField("unordered", 0, 1), + BitField("beginning", 0, 1), + BitField("ending", 0, 1), + FieldLenField("len", None, length_of="data", adjust = lambda pkt,x:x+16), + XIntField("tsn", None), + XShortField("stream_id", None), + XShortField("stream_seq", None), + XIntField("proto_id", None), + PadField(StrLenField("data", None, length_from=lambda pkt: pkt.len-16), + 4, padwith="\x00"), + ] + +class SCTPChunkInit(_SCTPChunkGuessPayload, Packet): + fields_desc = [ ByteEnumField("type", 1, sctpchunktypes), + XByteField("flags", None), + FieldLenField("len", None, length_of="params", adjust = lambda pkt,x:x+20), + XIntField("init_tag", None), + IntField("a_rwnd", None), + ShortField("n_out_streams", None), + ShortField("n_in_streams", None), + XIntField("init_tsn", None), + ChunkParamField("params", None, length_from=lambda pkt:pkt.len-20), + ] + +class SCTPChunkInitAck(_SCTPChunkGuessPayload, Packet): + fields_desc = [ ByteEnumField("type", 2, sctpchunktypes), + XByteField("flags", None), + FieldLenField("len", None, length_of="params", adjust = lambda pkt,x:x+20), + XIntField("init_tag", None), + IntField("a_rwnd", None), + ShortField("n_out_streams", None), + ShortField("n_in_streams", None), + XIntField("init_tsn", None), + ChunkParamField("params", None, length_from=lambda pkt:pkt.len-20), + ] + +class GapAckField(Field): + def __init__(self, name, default): + Field.__init__(self, name, default, "4s") + def i2m(self, pkt, x): + if x is None: + return "\0\0\0\0" + sta, end = map(int, x.split(":")) + args = tuple([">HH", sta, end]) + return struct.pack(*args) + def m2i(self, pkt, x): + return "%d:%d"%(struct.unpack(">HH", x)) + def any2i(self, pkt, x): + if type(x) is tuple and len(x) == 2: + return "%d:%d"%(x) + return x + +class SCTPChunkSACK(_SCTPChunkGuessPayload, Packet): + fields_desc = [ ByteEnumField("type", 3, sctpchunktypes), + XByteField("flags", None), + ShortField("len", None), + XIntField("cumul_tsn_ack", None), + IntField("a_rwnd", None), + FieldLenField("n_gap_ack", None, count_of="gap_ack_list"), + FieldLenField("n_dup_tsn", None, count_of="dup_tsn_list"), + FieldListField("gap_ack_list", [ ], GapAckField("gap_ack", None), count_from=lambda pkt:pkt.n_gap_ack), + FieldListField("dup_tsn_list", [ ], XIntField("dup_tsn", None), count_from=lambda pkt:pkt.n_dup_tsn), + ] + + def post_build(self, p, pay): + if self.len is None: + p = p[:2] + struct.pack(">H", len(p)) + p[4:] + return p+pay + + +class SCTPChunkHeartbeatReq(_SCTPChunkGuessPayload, Packet): + fields_desc = [ ByteEnumField("type", 4, sctpchunktypes), + XByteField("flags", None), + FieldLenField("len", None, length_of="params", adjust = lambda pkt,x:x+4), + ChunkParamField("params", None, length_from=lambda pkt:pkt.len-4), + ] + +class SCTPChunkHeartbeatAck(_SCTPChunkGuessPayload, Packet): + fields_desc = [ ByteEnumField("type", 5, sctpchunktypes), + XByteField("flags", None), + FieldLenField("len", None, length_of="params", adjust = lambda pkt,x:x+4), + ChunkParamField("params", None, length_from=lambda pkt:pkt.len-4), + ] + +class SCTPChunkAbort(_SCTPChunkGuessPayload, Packet): + fields_desc = [ ByteEnumField("type", 6, sctpchunktypes), + BitField("reserved", None, 7), + BitField("TCB", 0, 1), + FieldLenField("len", None, length_of="error_causes", adjust = lambda pkt,x:x+4), + PadField(StrLenField("error_causes", "", length_from=lambda pkt: pkt.len-4), + 4, padwith="\x00"), + ] + +class SCTPChunkShutdown(_SCTPChunkGuessPayload, Packet): + fields_desc = [ ByteEnumField("type", 7, sctpchunktypes), + XByteField("flags", None), + ShortField("len", 8), + XIntField("cumul_tsn_ack", None), + ] + +class SCTPChunkShutdownAck(_SCTPChunkGuessPayload, Packet): + fields_desc = [ ByteEnumField("type", 8, sctpchunktypes), + XByteField("flags", None), + ShortField("len", 4), + ] + +class SCTPChunkError(_SCTPChunkGuessPayload, Packet): + fields_desc = [ ByteEnumField("type", 9, sctpchunktypes), + XByteField("flags", None), + FieldLenField("len", None, length_of="error_causes", adjust = lambda pkt,x:x+4), + PadField(StrLenField("error_causes", "", length_from=lambda pkt: pkt.len-4), + 4, padwith="\x00"), + ] + +class SCTPChunkCookieEcho(_SCTPChunkGuessPayload, Packet): + fields_desc = [ ByteEnumField("type", 10, sctpchunktypes), + XByteField("flags", None), + FieldLenField("len", None, length_of="cookie", adjust = lambda pkt,x:x+4), + PadField(StrLenField("cookie", "", length_from=lambda pkt: pkt.len-4), + 4, padwith="\x00"), + ] + +class SCTPChunkCookieAck(_SCTPChunkGuessPayload, Packet): + fields_desc = [ ByteEnumField("type", 11, sctpchunktypes), + XByteField("flags", None), + ShortField("len", 4), + ] + +class SCTPChunkShutdownComplete(_SCTPChunkGuessPayload, Packet): + fields_desc = [ ByteEnumField("type", 12, sctpchunktypes), + BitField("reserved", None, 7), + BitField("TCB", 0, 1), + ShortField("len", 4), + ] + +bind_layers( IP, SCTP, proto=IPPROTO_SCTP) + diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/sebek.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/sebek.py new file mode 100644 index 00000000..c54e6728 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/sebek.py @@ -0,0 +1,109 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +Sebek: Linux kernel module for data collection on honeypots. +""" + +from scapy.fields import * +from scapy.packet import * +from scapy.layers.inet import UDP + + +### SEBEK + + +class SebekHead(Packet): + name = "Sebek header" + fields_desc = [ XIntField("magic", 0xd0d0d0), + ShortField("version", 1), + ShortEnumField("type", 0, {"read":0, "write":1, + "socket":2, "open":3}), + IntField("counter", 0), + IntField("time_sec", 0), + IntField("time_usec", 0) ] + def mysummary(self): + return self.sprintf("Sebek Header v%SebekHead.version% %SebekHead.type%") + +# we need this because Sebek headers differ between v1 and v3, and +# between v3 type socket and v3 others + +class SebekV1(Packet): + name = "Sebek v1" + fields_desc = [ IntField("pid", 0), + IntField("uid", 0), + IntField("fd", 0), + StrFixedLenField("command", "", 12), + FieldLenField("data_length", None, "data",fmt="I"), + StrLenField("data", "", length_from=lambda x:x.data_length) ] + def mysummary(self): + if isinstance(self.underlayer, SebekHead): + return self.underlayer.sprintf("Sebek v1 %SebekHead.type% (%SebekV1.command%)") + else: + return self.sprintf("Sebek v1 (%SebekV1.command%)") + +class SebekV3(Packet): + name = "Sebek v3" + fields_desc = [ IntField("parent_pid", 0), + IntField("pid", 0), + IntField("uid", 0), + IntField("fd", 0), + IntField("inode", 0), + StrFixedLenField("command", "", 12), + FieldLenField("data_length", None, "data",fmt="I"), + StrLenField("data", "", length_from=lambda x:x.data_length) ] + def mysummary(self): + if isinstance(self.underlayer, SebekHead): + return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV3.command%)") + else: + return self.sprintf("Sebek v3 (%SebekV3.command%)") + +class SebekV2(SebekV3): + def mysummary(self): + if isinstance(self.underlayer, SebekHead): + return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV2.command%)") + else: + return self.sprintf("Sebek v2 (%SebekV2.command%)") + +class SebekV3Sock(Packet): + name = "Sebek v2 socket" + fields_desc = [ IntField("parent_pid", 0), + IntField("pid", 0), + IntField("uid", 0), + IntField("fd", 0), + IntField("inode", 0), + StrFixedLenField("command", "", 12), + IntField("data_length", 15), + IPField("dip", "127.0.0.1"), + ShortField("dport", 0), + IPField("sip", "127.0.0.1"), + ShortField("sport", 0), + ShortEnumField("call", 0, { "bind":2, + "connect":3, "listen":4, + "accept":5, "sendmsg":16, + "recvmsg":17, "sendto":11, + "recvfrom":12}), + ByteEnumField("proto", 0, IP_PROTOS) ] + def mysummary(self): + if isinstance(self.underlayer, SebekHead): + return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV3Sock.command%)") + else: + return self.sprintf("Sebek v3 socket (%SebekV3Sock.command%)") + +class SebekV2Sock(SebekV3Sock): + def mysummary(self): + if isinstance(self.underlayer, SebekHead): + return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV2Sock.command%)") + else: + return self.sprintf("Sebek v2 socket (%SebekV2Sock.command%)") + +bind_layers( UDP, SebekHead, sport=1101) +bind_layers( UDP, SebekHead, dport=1101) +bind_layers( UDP, SebekHead, dport=1101, sport=1101) +bind_layers( SebekHead, SebekV1, version=1) +bind_layers( SebekHead, SebekV2Sock, version=2, type=2) +bind_layers( SebekHead, SebekV2, version=2) +bind_layers( SebekHead, SebekV3Sock, version=3, type=2) +bind_layers( SebekHead, SebekV3, version=3) diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/skinny.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/skinny.py new file mode 100644 index 00000000..9fb6ac06 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/skinny.py @@ -0,0 +1,161 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +Cisco Skinny protocol. +""" + +from scapy.packet import * +from scapy.fields import * +from scapy.layers.inet import TCP + +# shamelessly ripped from Ethereal dissector +skinny_messages = { +# Station -> Callmanager + 0x0000: "KeepAliveMessage", + 0x0001: "RegisterMessage", + 0x0002: "IpPortMessage", + 0x0003: "KeypadButtonMessage", + 0x0004: "EnblocCallMessage", + 0x0005: "StimulusMessage", + 0x0006: "OffHookMessage", + 0x0007: "OnHookMessage", + 0x0008: "HookFlashMessage", + 0x0009: "ForwardStatReqMessage", + 0x000A: "SpeedDialStatReqMessage", + 0x000B: "LineStatReqMessage", + 0x000C: "ConfigStatReqMessage", + 0x000D: "TimeDateReqMessage", + 0x000E: "ButtonTemplateReqMessage", + 0x000F: "VersionReqMessage", + 0x0010: "CapabilitiesResMessage", + 0x0011: "MediaPortListMessage", + 0x0012: "ServerReqMessage", + 0x0020: "AlarmMessage", + 0x0021: "MulticastMediaReceptionAck", + 0x0022: "OpenReceiveChannelAck", + 0x0023: "ConnectionStatisticsRes", + 0x0024: "OffHookWithCgpnMessage", + 0x0025: "SoftKeySetReqMessage", + 0x0026: "SoftKeyEventMessage", + 0x0027: "UnregisterMessage", + 0x0028: "SoftKeyTemplateReqMessage", + 0x0029: "RegisterTokenReq", + 0x002A: "MediaTransmissionFailure", + 0x002B: "HeadsetStatusMessage", + 0x002C: "MediaResourceNotification", + 0x002D: "RegisterAvailableLinesMessage", + 0x002E: "DeviceToUserDataMessage", + 0x002F: "DeviceToUserDataResponseMessage", + 0x0030: "UpdateCapabilitiesMessage", + 0x0031: "OpenMultiMediaReceiveChannelAckMessage", + 0x0032: "ClearConferenceMessage", + 0x0033: "ServiceURLStatReqMessage", + 0x0034: "FeatureStatReqMessage", + 0x0035: "CreateConferenceResMessage", + 0x0036: "DeleteConferenceResMessage", + 0x0037: "ModifyConferenceResMessage", + 0x0038: "AddParticipantResMessage", + 0x0039: "AuditConferenceResMessage", + 0x0040: "AuditParticipantResMessage", + 0x0041: "DeviceToUserDataVersion1Message", +# Callmanager -> Station */ + 0x0081: "RegisterAckMessage", + 0x0082: "StartToneMessage", + 0x0083: "StopToneMessage", + 0x0085: "SetRingerMessage", + 0x0086: "SetLampMessage", + 0x0087: "SetHkFDetectMessage", + 0x0088: "SetSpeakerModeMessage", + 0x0089: "SetMicroModeMessage", + 0x008A: "StartMediaTransmission", + 0x008B: "StopMediaTransmission", + 0x008C: "StartMediaReception", + 0x008D: "StopMediaReception", + 0x008F: "CallInfoMessage", + 0x0090: "ForwardStatMessage", + 0x0091: "SpeedDialStatMessage", + 0x0092: "LineStatMessage", + 0x0093: "ConfigStatMessage", + 0x0094: "DefineTimeDate", + 0x0095: "StartSessionTransmission", + 0x0096: "StopSessionTransmission", + 0x0097: "ButtonTemplateMessage", + 0x0098: "VersionMessage", + 0x0099: "DisplayTextMessage", + 0x009A: "ClearDisplay", + 0x009B: "CapabilitiesReqMessage", + 0x009C: "EnunciatorCommandMessage", + 0x009D: "RegisterRejectMessage", + 0x009E: "ServerResMessage", + 0x009F: "Reset", + 0x0100: "KeepAliveAckMessage", + 0x0101: "StartMulticastMediaReception", + 0x0102: "StartMulticastMediaTransmission", + 0x0103: "StopMulticastMediaReception", + 0x0104: "StopMulticastMediaTransmission", + 0x0105: "OpenReceiveChannel", + 0x0106: "CloseReceiveChannel", + 0x0107: "ConnectionStatisticsReq", + 0x0108: "SoftKeyTemplateResMessage", + 0x0109: "SoftKeySetResMessage", + 0x0110: "SelectSoftKeysMessage", + 0x0111: "CallStateMessage", + 0x0112: "DisplayPromptStatusMessage", + 0x0113: "ClearPromptStatusMessage", + 0x0114: "DisplayNotifyMessage", + 0x0115: "ClearNotifyMessage", + 0x0116: "ActivateCallPlaneMessage", + 0x0117: "DeactivateCallPlaneMessage", + 0x0118: "UnregisterAckMessage", + 0x0119: "BackSpaceReqMessage", + 0x011A: "RegisterTokenAck", + 0x011B: "RegisterTokenReject", + 0x0042: "DeviceToUserDataResponseVersion1Message", + 0x011C: "StartMediaFailureDetection", + 0x011D: "DialedNumberMessage", + 0x011E: "UserToDeviceDataMessage", + 0x011F: "FeatureStatMessage", + 0x0120: "DisplayPriNotifyMessage", + 0x0121: "ClearPriNotifyMessage", + 0x0122: "StartAnnouncementMessage", + 0x0123: "StopAnnouncementMessage", + 0x0124: "AnnouncementFinishMessage", + 0x0127: "NotifyDtmfToneMessage", + 0x0128: "SendDtmfToneMessage", + 0x0129: "SubscribeDtmfPayloadReqMessage", + 0x012A: "SubscribeDtmfPayloadResMessage", + 0x012B: "SubscribeDtmfPayloadErrMessage", + 0x012C: "UnSubscribeDtmfPayloadReqMessage", + 0x012D: "UnSubscribeDtmfPayloadResMessage", + 0x012E: "UnSubscribeDtmfPayloadErrMessage", + 0x012F: "ServiceURLStatMessage", + 0x0130: "CallSelectStatMessage", + 0x0131: "OpenMultiMediaChannelMessage", + 0x0132: "StartMultiMediaTransmission", + 0x0133: "StopMultiMediaTransmission", + 0x0134: "MiscellaneousCommandMessage", + 0x0135: "FlowControlCommandMessage", + 0x0136: "CloseMultiMediaReceiveChannel", + 0x0137: "CreateConferenceReqMessage", + 0x0138: "DeleteConferenceReqMessage", + 0x0139: "ModifyConferenceReqMessage", + 0x013A: "AddParticipantReqMessage", + 0x013B: "DropParticipantReqMessage", + 0x013C: "AuditConferenceReqMessage", + 0x013D: "AuditParticipantReqMessage", + 0x013F: "UserToDeviceDataVersion1Message", + } + + + +class Skinny(Packet): + name="Skinny" + fields_desc = [ LEIntField("len",0), + LEIntField("res",0), + LEIntEnumField("msg",0,skinny_messages) ] + +bind_layers( TCP, Skinny, dport=2000) +bind_layers( TCP, Skinny, sport=2000) diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/smb.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/smb.py new file mode 100644 index 00000000..73ebe5b1 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/smb.py @@ -0,0 +1,354 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +SMB (Server Message Block), also known as CIFS. +""" + +from scapy.packet import * +from scapy.fields import * +from scapy.layers.netbios import NBTSession + + +# SMB NetLogon Response Header +class SMBNetlogon_Protocol_Response_Header(Packet): + name="SMBNetlogon Protocol Response Header" + fields_desc = [StrFixedLenField("Start","\xffSMB",4), + ByteEnumField("Command",0x25,{0x25:"Trans"}), + ByteField("Error_Class",0x02), + ByteField("Reserved",0), + LEShortField("Error_code",4), + ByteField("Flags",0), + LEShortField("Flags2",0x0000), + LEShortField("PIDHigh",0x0000), + LELongField("Signature",0x0), + LEShortField("Unused",0x0), + LEShortField("TID",0), + LEShortField("PID",0), + LEShortField("UID",0), + LEShortField("MID",0), + ByteField("WordCount",17), + LEShortField("TotalParamCount",0), + LEShortField("TotalDataCount",112), + LEShortField("MaxParamCount",0), + LEShortField("MaxDataCount",0), + ByteField("MaxSetupCount",0), + ByteField("unused2",0), + LEShortField("Flags3",0), + ByteField("TimeOut1",0xe8), + ByteField("TimeOut2",0x03), + LEShortField("unused3",0), + LEShortField("unused4",0), + LEShortField("ParamCount2",0), + LEShortField("ParamOffset",0), + LEShortField("DataCount",112), + LEShortField("DataOffset",92), + ByteField("SetupCount", 3), + ByteField("unused5", 0)] + +# SMB MailSlot Protocol +class SMBMailSlot(Packet): + name = "SMB Mail Slot Protocol" + fields_desc = [LEShortField("opcode", 1), + LEShortField("priority", 1), + LEShortField("class", 2), + LEShortField("size", 135), + StrNullField("name","\MAILSLOT\NET\GETDC660")] + +# SMB NetLogon Protocol Response Tail SAM +class SMBNetlogon_Protocol_Response_Tail_SAM(Packet): + name = "SMB Netlogon Protocol Response Tail SAM" + fields_desc = [ByteEnumField("Command", 0x17, {0x12:"SAM logon request", 0x17:"SAM Active directory Response"}), + ByteField("unused", 0), + ShortField("Data1", 0), + ShortField("Data2", 0xfd01), + ShortField("Data3", 0), + ShortField("Data4", 0xacde), + ShortField("Data5", 0x0fe5), + ShortField("Data6", 0xd10a), + ShortField("Data7", 0x374c), + ShortField("Data8", 0x83e2), + ShortField("Data9", 0x7dd9), + ShortField("Data10", 0x3a16), + ShortField("Data11", 0x73ff), + ByteField("Data12", 0x04), + StrFixedLenField("Data13", "rmff", 4), + ByteField("Data14", 0x0), + ShortField("Data16", 0xc018), + ByteField("Data18", 0x0a), + StrFixedLenField("Data20", "rmff-win2k", 10), + ByteField("Data21", 0xc0), + ShortField("Data22", 0x18c0), + ShortField("Data23", 0x180a), + StrFixedLenField("Data24", "RMFF-WIN2K", 10), + ShortField("Data25", 0), + ByteField("Data26", 0x17), + StrFixedLenField("Data27", "Default-First-Site-Name", 23), + ShortField("Data28", 0x00c0), + ShortField("Data29", 0x3c10), + ShortField("Data30", 0x00c0), + ShortField("Data31", 0x0200), + ShortField("Data32", 0x0), + ShortField("Data33", 0xac14), + ShortField("Data34", 0x0064), + ShortField("Data35", 0x0), + ShortField("Data36", 0x0), + ShortField("Data37", 0x0), + ShortField("Data38", 0x0), + ShortField("Data39", 0x0d00), + ShortField("Data40", 0x0), + ShortField("Data41", 0xffff)] + +# SMB NetLogon Protocol Response Tail LM2.0 +class SMBNetlogon_Protocol_Response_Tail_LM20(Packet): + name = "SMB Netlogon Protocol Response Tail LM20" + fields_desc = [ByteEnumField("Command",0x06,{0x06:"LM 2.0 Response to logon request"}), + ByteField("unused", 0), + StrFixedLenField("DblSlash", "\\\\", 2), + StrNullField("ServerName","WIN"), + LEShortField("LM20Token", 0xffff)] + +# SMBNegociate Protocol Request Header +class SMBNegociate_Protocol_Request_Header(Packet): + name="SMBNegociate Protocol Request Header" + fields_desc = [StrFixedLenField("Start","\xffSMB",4), + ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}), + ByteField("Error_Class",0), + ByteField("Reserved",0), + LEShortField("Error_code",0), + ByteField("Flags",0x18), + LEShortField("Flags2",0x0000), + LEShortField("PIDHigh",0x0000), + LELongField("Signature",0x0), + LEShortField("Unused",0x0), + LEShortField("TID",0), + LEShortField("PID",1), + LEShortField("UID",0), + LEShortField("MID",2), + ByteField("WordCount",0), + LEShortField("ByteCount",12)] + +# SMB Negociate Protocol Request Tail +class SMBNegociate_Protocol_Request_Tail(Packet): + name="SMB Negociate Protocol Request Tail" + fields_desc=[ByteField("BufferFormat",0x02), + StrNullField("BufferData","NT LM 0.12")] + +# SMBNegociate Protocol Response Advanced Security +class SMBNegociate_Protocol_Response_Advanced_Security(Packet): + name="SMBNegociate Protocol Response Advanced Security" + fields_desc = [StrFixedLenField("Start","\xffSMB",4), + ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}), + ByteField("Error_Class",0), + ByteField("Reserved",0), + LEShortField("Error_Code",0), + ByteField("Flags",0x98), + LEShortField("Flags2",0x0000), + LEShortField("PIDHigh",0x0000), + LELongField("Signature",0x0), + LEShortField("Unused",0x0), + LEShortField("TID",0), + LEShortField("PID",1), + LEShortField("UID",0), + LEShortField("MID",2), + ByteField("WordCount",17), + LEShortField("DialectIndex",7), + ByteField("SecurityMode",0x03), + LEShortField("MaxMpxCount",50), + LEShortField("MaxNumberVC",1), + LEIntField("MaxBufferSize",16144), + LEIntField("MaxRawSize",65536), + LEIntField("SessionKey",0x0000), + LEShortField("ServerCapabilities",0xf3f9), + BitField("UnixExtensions",0,1), + BitField("Reserved2",0,7), + BitField("ExtendedSecurity",1,1), + BitField("CompBulk",0,2), + BitField("Reserved3",0,5), +# There have been 127490112000000000 tenths of micro-seconds between 1st january 1601 and 1st january 2005. 127490112000000000=0x1C4EF94D6228000, so ServerTimeHigh=0xD6228000 and ServerTimeLow=0x1C4EF94. + LEIntField("ServerTimeHigh",0xD6228000L), + LEIntField("ServerTimeLow",0x1C4EF94), + LEShortField("ServerTimeZone",0x3c), + ByteField("EncryptionKeyLength",0), + LEFieldLenField("ByteCount", None, "SecurityBlob", adjust=lambda pkt,x:x-16), + BitField("GUID",0,128), + StrLenField("SecurityBlob", "", length_from=lambda x:x.ByteCount+16)] + +# SMBNegociate Protocol Response No Security +# When using no security, with EncryptionKeyLength=8, you must have an EncryptionKey before the DomainName +class SMBNegociate_Protocol_Response_No_Security(Packet): + name="SMBNegociate Protocol Response No Security" + fields_desc = [StrFixedLenField("Start","\xffSMB",4), + ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}), + ByteField("Error_Class",0), + ByteField("Reserved",0), + LEShortField("Error_Code",0), + ByteField("Flags",0x98), + LEShortField("Flags2",0x0000), + LEShortField("PIDHigh",0x0000), + LELongField("Signature",0x0), + LEShortField("Unused",0x0), + LEShortField("TID",0), + LEShortField("PID",1), + LEShortField("UID",0), + LEShortField("MID",2), + ByteField("WordCount",17), + LEShortField("DialectIndex",7), + ByteField("SecurityMode",0x03), + LEShortField("MaxMpxCount",50), + LEShortField("MaxNumberVC",1), + LEIntField("MaxBufferSize",16144), + LEIntField("MaxRawSize",65536), + LEIntField("SessionKey",0x0000), + LEShortField("ServerCapabilities",0xf3f9), + BitField("UnixExtensions",0,1), + BitField("Reserved2",0,7), + BitField("ExtendedSecurity",0,1), + FlagsField("CompBulk",0,2,"CB"), + BitField("Reserved3",0,5), + # There have been 127490112000000000 tenths of micro-seconds between 1st january 1601 and 1st january 2005. 127490112000000000=0x1C4EF94D6228000, so ServerTimeHigh=0xD6228000 and ServerTimeLow=0x1C4EF94. + LEIntField("ServerTimeHigh",0xD6228000L), + LEIntField("ServerTimeLow",0x1C4EF94), + LEShortField("ServerTimeZone",0x3c), + ByteField("EncryptionKeyLength",8), + LEShortField("ByteCount",24), + BitField("EncryptionKey",0,64), + StrNullField("DomainName","WORKGROUP"), + StrNullField("ServerName","RMFF1")] + +# SMBNegociate Protocol Response No Security No Key +class SMBNegociate_Protocol_Response_No_Security_No_Key(Packet): + namez="SMBNegociate Protocol Response No Security No Key" + fields_desc = [StrFixedLenField("Start","\xffSMB",4), + ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}), + ByteField("Error_Class",0), + ByteField("Reserved",0), + LEShortField("Error_Code",0), + ByteField("Flags",0x98), + LEShortField("Flags2",0x0000), + LEShortField("PIDHigh",0x0000), + LELongField("Signature",0x0), + LEShortField("Unused",0x0), + LEShortField("TID",0), + LEShortField("PID",1), + LEShortField("UID",0), + LEShortField("MID",2), + ByteField("WordCount",17), + LEShortField("DialectIndex",7), + ByteField("SecurityMode",0x03), + LEShortField("MaxMpxCount",50), + LEShortField("MaxNumberVC",1), + LEIntField("MaxBufferSize",16144), + LEIntField("MaxRawSize",65536), + LEIntField("SessionKey",0x0000), + LEShortField("ServerCapabilities",0xf3f9), + BitField("UnixExtensions",0,1), + BitField("Reserved2",0,7), + BitField("ExtendedSecurity",0,1), + FlagsField("CompBulk",0,2,"CB"), + BitField("Reserved3",0,5), + # There have been 127490112000000000 tenths of micro-seconds between 1st january 1601 and 1st january 2005. 127490112000000000=0x1C4EF94D6228000, so ServerTimeHigh=0xD6228000 and ServerTimeLow=0x1C4EF94. + LEIntField("ServerTimeHigh",0xD6228000L), + LEIntField("ServerTimeLow",0x1C4EF94), + LEShortField("ServerTimeZone",0x3c), + ByteField("EncryptionKeyLength",0), + LEShortField("ByteCount",16), + StrNullField("DomainName","WORKGROUP"), + StrNullField("ServerName","RMFF1")] + +# Session Setup AndX Request +class SMBSession_Setup_AndX_Request(Packet): + name="Session Setup AndX Request" + fields_desc=[StrFixedLenField("Start","\xffSMB",4), + ByteEnumField("Command",0x73,{0x73:"SMB_COM_SESSION_SETUP_ANDX"}), + ByteField("Error_Class",0), + ByteField("Reserved",0), + LEShortField("Error_Code",0), + ByteField("Flags",0x18), + LEShortField("Flags2",0x0001), + LEShortField("PIDHigh",0x0000), + LELongField("Signature",0x0), + LEShortField("Unused",0x0), + LEShortField("TID",0), + LEShortField("PID",1), + LEShortField("UID",0), + LEShortField("MID",2), + ByteField("WordCount",13), + ByteEnumField("AndXCommand",0x75,{0x75:"SMB_COM_TREE_CONNECT_ANDX"}), + ByteField("Reserved2",0), + LEShortField("AndXOffset",96), + LEShortField("MaxBufferS",2920), + LEShortField("MaxMPXCount",50), + LEShortField("VCNumber",0), + LEIntField("SessionKey",0), + LEFieldLenField("ANSIPasswordLength",None,"ANSIPassword"), + LEShortField("UnicodePasswordLength",0), + LEIntField("Reserved3",0), + LEShortField("ServerCapabilities",0x05), + BitField("UnixExtensions",0,1), + BitField("Reserved4",0,7), + BitField("ExtendedSecurity",0,1), + BitField("CompBulk",0,2), + BitField("Reserved5",0,5), + LEShortField("ByteCount",35), + StrLenField("ANSIPassword", "Pass",length_from=lambda x:x.ANSIPasswordLength), + StrNullField("Account","GUEST"), + StrNullField("PrimaryDomain", ""), + StrNullField("NativeOS","Windows 4.0"), + StrNullField("NativeLanManager","Windows 4.0"), + ByteField("WordCount2",4), + ByteEnumField("AndXCommand2",0xFF,{0xFF:"SMB_COM_NONE"}), + ByteField("Reserved6",0), + LEShortField("AndXOffset2",0), + LEShortField("Flags3",0x2), + LEShortField("PasswordLength",0x1), + LEShortField("ByteCount2",18), + ByteField("Password",0), + StrNullField("Path","\\\\WIN2K\\IPC$"), + StrNullField("Service","IPC")] + +# Session Setup AndX Response +class SMBSession_Setup_AndX_Response(Packet): + name="Session Setup AndX Response" + fields_desc=[StrFixedLenField("Start","\xffSMB",4), + ByteEnumField("Command",0x73,{0x73:"SMB_COM_SESSION_SETUP_ANDX"}), + ByteField("Error_Class",0), + ByteField("Reserved",0), + LEShortField("Error_Code",0), + ByteField("Flags",0x90), + LEShortField("Flags2",0x1001), + LEShortField("PIDHigh",0x0000), + LELongField("Signature",0x0), + LEShortField("Unused",0x0), + LEShortField("TID",0), + LEShortField("PID",1), + LEShortField("UID",0), + LEShortField("MID",2), + ByteField("WordCount",3), + ByteEnumField("AndXCommand",0x75,{0x75:"SMB_COM_TREE_CONNECT_ANDX"}), + ByteField("Reserved2",0), + LEShortField("AndXOffset",66), + LEShortField("Action",0), + LEShortField("ByteCount",25), + StrNullField("NativeOS","Windows 4.0"), + StrNullField("NativeLanManager","Windows 4.0"), + StrNullField("PrimaryDomain",""), + ByteField("WordCount2",3), + ByteEnumField("AndXCommand2",0xFF,{0xFF:"SMB_COM_NONE"}), + ByteField("Reserved3",0), + LEShortField("AndXOffset2",80), + LEShortField("OptionalSupport",0x01), + LEShortField("ByteCount2",5), + StrNullField("Service","IPC"), + StrNullField("NativeFileSystem","")] + +bind_layers( NBTSession, SMBNegociate_Protocol_Request_Header, ) +bind_layers( NBTSession, SMBNegociate_Protocol_Response_Advanced_Security, ExtendedSecurity=1) +bind_layers( NBTSession, SMBNegociate_Protocol_Response_No_Security, ExtendedSecurity=0, EncryptionKeyLength=8) +bind_layers( NBTSession, SMBNegociate_Protocol_Response_No_Security_No_Key, ExtendedSecurity=0, EncryptionKeyLength=0) +bind_layers( NBTSession, SMBSession_Setup_AndX_Request, ) +bind_layers( NBTSession, SMBSession_Setup_AndX_Response, ) +bind_layers( SMBNegociate_Protocol_Request_Header, SMBNegociate_Protocol_Request_Tail, ) +bind_layers( SMBNegociate_Protocol_Request_Tail, SMBNegociate_Protocol_Request_Tail, ) diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/snmp.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/snmp.py new file mode 100644 index 00000000..2c588250 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/snmp.py @@ -0,0 +1,255 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +SNMP (Simple Network Management Protocol). +""" + +from scapy.asn1packet import * +from scapy.asn1fields import * +from scapy.layers.inet import UDP + +########## +## SNMP ## +########## + +######[ ASN1 class ]###### + +class ASN1_Class_SNMP(ASN1_Class_UNIVERSAL): + name="SNMP" + PDU_GET = 0xa0 + PDU_NEXT = 0xa1 + PDU_RESPONSE = 0xa2 + PDU_SET = 0xa3 + PDU_TRAPv1 = 0xa4 + PDU_BULK = 0xa5 + PDU_INFORM = 0xa6 + PDU_TRAPv2 = 0xa7 + + +class ASN1_SNMP_PDU_GET(ASN1_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_GET + +class ASN1_SNMP_PDU_NEXT(ASN1_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_NEXT + +class ASN1_SNMP_PDU_RESPONSE(ASN1_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_RESPONSE + +class ASN1_SNMP_PDU_SET(ASN1_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_SET + +class ASN1_SNMP_PDU_TRAPv1(ASN1_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_TRAPv1 + +class ASN1_SNMP_PDU_BULK(ASN1_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_BULK + +class ASN1_SNMP_PDU_INFORM(ASN1_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_INFORM + +class ASN1_SNMP_PDU_TRAPv2(ASN1_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_TRAPv2 + + +######[ BER codecs ]####### + +class BERcodec_SNMP_PDU_GET(BERcodec_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_GET + +class BERcodec_SNMP_PDU_NEXT(BERcodec_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_NEXT + +class BERcodec_SNMP_PDU_RESPONSE(BERcodec_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_RESPONSE + +class BERcodec_SNMP_PDU_SET(BERcodec_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_SET + +class BERcodec_SNMP_PDU_TRAPv1(BERcodec_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_TRAPv1 + +class BERcodec_SNMP_PDU_BULK(BERcodec_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_BULK + +class BERcodec_SNMP_PDU_INFORM(BERcodec_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_INFORM + +class BERcodec_SNMP_PDU_TRAPv2(BERcodec_SEQUENCE): + tag = ASN1_Class_SNMP.PDU_TRAPv2 + + + +######[ ASN1 fields ]###### + +class ASN1F_SNMP_PDU_GET(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_SNMP.PDU_GET + +class ASN1F_SNMP_PDU_NEXT(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_SNMP.PDU_NEXT + +class ASN1F_SNMP_PDU_RESPONSE(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_SNMP.PDU_RESPONSE + +class ASN1F_SNMP_PDU_SET(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_SNMP.PDU_SET + +class ASN1F_SNMP_PDU_TRAPv1(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_SNMP.PDU_TRAPv1 + +class ASN1F_SNMP_PDU_BULK(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_SNMP.PDU_BULK + +class ASN1F_SNMP_PDU_INFORM(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_SNMP.PDU_INFORM + +class ASN1F_SNMP_PDU_TRAPv2(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_SNMP.PDU_TRAPv2 + + + +######[ SNMP Packet ]###### + +SNMP_error = { 0: "no_error", + 1: "too_big", + 2: "no_such_name", + 3: "bad_value", + 4: "read_only", + 5: "generic_error", + 6: "no_access", + 7: "wrong_type", + 8: "wrong_length", + 9: "wrong_encoding", + 10: "wrong_value", + 11: "no_creation", + 12: "inconsistent_value", + 13: "ressource_unavailable", + 14: "commit_failed", + 15: "undo_failed", + 16: "authorization_error", + 17: "not_writable", + 18: "inconsistent_name", + } + +SNMP_trap_types = { 0: "cold_start", + 1: "warm_start", + 2: "link_down", + 3: "link_up", + 4: "auth_failure", + 5: "egp_neigh_loss", + 6: "enterprise_specific", + } + +class SNMPvarbind(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("oid","1.3"), + ASN1F_field("value",ASN1_NULL(0)) + ) + + +class SNMPget(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SNMP_PDU_GET( ASN1F_INTEGER("id",0), + ASN1F_enum_INTEGER("error",0, SNMP_error), + ASN1F_INTEGER("error_index",0), + ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) + ) + +class SNMPnext(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SNMP_PDU_NEXT( ASN1F_INTEGER("id",0), + ASN1F_enum_INTEGER("error",0, SNMP_error), + ASN1F_INTEGER("error_index",0), + ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) + ) + +class SNMPresponse(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SNMP_PDU_RESPONSE( ASN1F_INTEGER("id",0), + ASN1F_enum_INTEGER("error",0, SNMP_error), + ASN1F_INTEGER("error_index",0), + ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) + ) + +class SNMPset(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SNMP_PDU_SET( ASN1F_INTEGER("id",0), + ASN1F_enum_INTEGER("error",0, SNMP_error), + ASN1F_INTEGER("error_index",0), + ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) + ) + +class SNMPtrapv1(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SNMP_PDU_TRAPv1( ASN1F_OID("enterprise", "1.3"), + ASN1F_IPADDRESS("agent_addr","0.0.0.0"), + ASN1F_enum_INTEGER("generic_trap", 0, SNMP_trap_types), + ASN1F_INTEGER("specific_trap", 0), + ASN1F_TIME_TICKS("time_stamp", IntAutoTime()), + ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) + ) + +class SNMPbulk(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SNMP_PDU_BULK( ASN1F_INTEGER("id",0), + ASN1F_INTEGER("non_repeaters",0), + ASN1F_INTEGER("max_repetitions",0), + ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) + ) + +class SNMPinform(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SNMP_PDU_INFORM( ASN1F_INTEGER("id",0), + ASN1F_enum_INTEGER("error",0, SNMP_error), + ASN1F_INTEGER("error_index",0), + ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) + ) + +class SNMPtrapv2(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SNMP_PDU_TRAPv2( ASN1F_INTEGER("id",0), + ASN1F_enum_INTEGER("error",0, SNMP_error), + ASN1F_INTEGER("error_index",0), + ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) + ) + + +class SNMP(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SEQUENCE( + ASN1F_enum_INTEGER("version", 1, {0:"v1", 1:"v2c", 2:"v2", 3:"v3"}), + ASN1F_STRING("community","public"), + ASN1F_CHOICE("PDU", SNMPget(), + SNMPget, SNMPnext, SNMPresponse, SNMPset, + SNMPtrapv1, SNMPbulk, SNMPinform, SNMPtrapv2) + ) + def answers(self, other): + return ( isinstance(self.PDU, SNMPresponse) and + ( isinstance(other.PDU, SNMPget) or + isinstance(other.PDU, SNMPnext) or + isinstance(other.PDU, SNMPset) ) and + self.PDU.id == other.PDU.id ) + +bind_layers( UDP, SNMP, sport=161) +bind_layers( UDP, SNMP, dport=161) +bind_layers( UDP, SNMP, sport=162) +bind_layers( UDP, SNMP, dport=162) + +def snmpwalk(dst, oid="1", community="public"): + try: + while 1: + r = sr1(IP(dst=dst)/UDP(sport=RandShort())/SNMP(community=community, PDU=SNMPnext(varbindlist=[SNMPvarbind(oid=oid)])),timeout=2, chainCC=1, verbose=0, retry=2) + if ICMP in r: + print repr(r) + break + if r is None: + print "No answers" + break + print "%-40s: %r" % (r[SNMPvarbind].oid.val,r[SNMPvarbind].value) + oid = r[SNMPvarbind].oid + + except KeyboardInterrupt: + pass + diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/tftp.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/tftp.py new file mode 100644 index 00000000..1535e99c --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/tftp.py @@ -0,0 +1,477 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +TFTP (Trivial File Transfer Protocol). +""" + +import os,random +from scapy.packet import * +from scapy.fields import * +from scapy.automaton import * +from scapy.layers.inet import UDP + + + +TFTP_operations = { 1:"RRQ",2:"WRQ",3:"DATA",4:"ACK",5:"ERROR",6:"OACK" } + + +class TFTP(Packet): + name = "TFTP opcode" + fields_desc = [ ShortEnumField("op", 1, TFTP_operations), ] + + + +class TFTP_RRQ(Packet): + name = "TFTP Read Request" + fields_desc = [ StrNullField("filename", ""), + StrNullField("mode", "octet") ] + def answers(self, other): + return 0 + def mysummary(self): + return self.sprintf("RRQ %filename%"),[UDP] + + +class TFTP_WRQ(Packet): + name = "TFTP Write Request" + fields_desc = [ StrNullField("filename", ""), + StrNullField("mode", "octet") ] + def answers(self, other): + return 0 + def mysummary(self): + return self.sprintf("WRQ %filename%"),[UDP] + +class TFTP_DATA(Packet): + name = "TFTP Data" + fields_desc = [ ShortField("block", 0) ] + def answers(self, other): + return self.block == 1 and isinstance(other, TFTP_RRQ) + def mysummary(self): + return self.sprintf("DATA %block%"),[UDP] + +class TFTP_Option(Packet): + fields_desc = [ StrNullField("oname",""), + StrNullField("value","") ] + def extract_padding(self, pkt): + return "",pkt + +class TFTP_Options(Packet): + fields_desc = [ PacketListField("options", [], TFTP_Option, length_from=lambda x:None) ] + + +class TFTP_ACK(Packet): + name = "TFTP Ack" + fields_desc = [ ShortField("block", 0) ] + def answers(self, other): + if isinstance(other, TFTP_DATA): + return self.block == other.block + elif isinstance(other, TFTP_RRQ) or isinstance(other, TFTP_WRQ) or isinstance(other, TFTP_OACK): + return self.block == 0 + return 0 + def mysummary(self): + return self.sprintf("ACK %block%"),[UDP] + +TFTP_Error_Codes = { 0: "Not defined", + 1: "File not found", + 2: "Access violation", + 3: "Disk full or allocation exceeded", + 4: "Illegal TFTP operation", + 5: "Unknown transfer ID", + 6: "File already exists", + 7: "No such user", + 8: "Terminate transfer due to option negotiation", + } + +class TFTP_ERROR(Packet): + name = "TFTP Error" + fields_desc = [ ShortEnumField("errorcode", 0, TFTP_Error_Codes), + StrNullField("errormsg", "")] + def answers(self, other): + return (isinstance(other, TFTP_DATA) or + isinstance(other, TFTP_RRQ) or + isinstance(other, TFTP_WRQ) or + isinstance(other, TFTP_ACK)) + def mysummary(self): + return self.sprintf("ERROR %errorcode%: %errormsg%"),[UDP] + + +class TFTP_OACK(Packet): + name = "TFTP Option Ack" + fields_desc = [ ] + def answers(self, other): + return isinstance(other, TFTP_WRQ) or isinstance(other, TFTP_RRQ) + + +bind_layers(UDP, TFTP, dport=69) +bind_layers(TFTP, TFTP_RRQ, op=1) +bind_layers(TFTP, TFTP_WRQ, op=2) +bind_layers(TFTP, TFTP_DATA, op=3) +bind_layers(TFTP, TFTP_ACK, op=4) +bind_layers(TFTP, TFTP_ERROR, op=5) +bind_layers(TFTP, TFTP_OACK, op=6) +bind_layers(TFTP_RRQ, TFTP_Options) +bind_layers(TFTP_WRQ, TFTP_Options) +bind_layers(TFTP_OACK, TFTP_Options) + + +class TFTP_read(Automaton): + def parse_args(self, filename, server, sport = None, port=69, **kargs): + Automaton.parse_args(self, **kargs) + self.filename = filename + self.server = server + self.port = port + self.sport = sport + + + def master_filter(self, pkt): + return ( IP in pkt and pkt[IP].src == self.server and UDP in pkt + and pkt[UDP].dport == self.my_tid + and (self.server_tid is None or pkt[UDP].sport == self.server_tid) ) + + # BEGIN + @ATMT.state(initial=1) + def BEGIN(self): + self.blocksize=512 + self.my_tid = self.sport or RandShort()._fix() + bind_bottom_up(UDP, TFTP, dport=self.my_tid) + self.server_tid = None + self.res = "" + + self.l3 = IP(dst=self.server)/UDP(sport=self.my_tid, dport=self.port)/TFTP() + self.last_packet = self.l3/TFTP_RRQ(filename=self.filename, mode="octet") + self.send(self.last_packet) + self.awaiting=1 + + raise self.WAITING() + + # WAITING + @ATMT.state() + def WAITING(self): + pass + + + @ATMT.receive_condition(WAITING) + def receive_data(self, pkt): + if TFTP_DATA in pkt and pkt[TFTP_DATA].block == self.awaiting: + if self.server_tid is None: + self.server_tid = pkt[UDP].sport + self.l3[UDP].dport = self.server_tid + raise self.RECEIVING(pkt) + + @ATMT.receive_condition(WAITING, prio=1) + def receive_error(self, pkt): + if TFTP_ERROR in pkt: + raise self.ERROR(pkt) + + + @ATMT.timeout(WAITING, 3) + def timeout_waiting(self): + raise self.WAITING() + @ATMT.action(timeout_waiting) + def retransmit_last_packet(self): + self.send(self.last_packet) + + @ATMT.action(receive_data) +# @ATMT.action(receive_error) + def send_ack(self): + self.last_packet = self.l3 / TFTP_ACK(block = self.awaiting) + self.send(self.last_packet) + + + # RECEIVED + @ATMT.state() + def RECEIVING(self, pkt): + if conf.raw_layer in pkt: + recvd = pkt[conf.raw_layer].load + else: + recvd = "" + self.res += recvd + self.awaiting += 1 + if len(recvd) == self.blocksize: + raise self.WAITING() + raise self.END() + + # ERROR + @ATMT.state(error=1) + def ERROR(self,pkt): + split_bottom_up(UDP, TFTP, dport=self.my_tid) + return pkt[TFTP_ERROR].summary() + + #END + @ATMT.state(final=1) + def END(self): + split_bottom_up(UDP, TFTP, dport=self.my_tid) + return self.res + + + + +class TFTP_write(Automaton): + def parse_args(self, filename, data, server, sport=None, port=69,**kargs): + Automaton.parse_args(self, **kargs) + self.filename = filename + self.server = server + self.port = port + self.sport = sport + self.blocksize = 512 + self.origdata = data + + def master_filter(self, pkt): + return ( IP in pkt and pkt[IP].src == self.server and UDP in pkt + and pkt[UDP].dport == self.my_tid + and (self.server_tid is None or pkt[UDP].sport == self.server_tid) ) + + + # BEGIN + @ATMT.state(initial=1) + def BEGIN(self): + self.data = [ self.origdata[i*self.blocksize:(i+1)*self.blocksize] + for i in range( len(self.origdata)/self.blocksize+1) ] + self.my_tid = self.sport or RandShort()._fix() + bind_bottom_up(UDP, TFTP, dport=self.my_tid) + self.server_tid = None + + self.l3 = IP(dst=self.server)/UDP(sport=self.my_tid, dport=self.port)/TFTP() + self.last_packet = self.l3/TFTP_WRQ(filename=self.filename, mode="octet") + self.send(self.last_packet) + self.res = "" + self.awaiting=0 + + raise self.WAITING_ACK() + + # WAITING_ACK + @ATMT.state() + def WAITING_ACK(self): + pass + + @ATMT.receive_condition(WAITING_ACK) + def received_ack(self,pkt): + if TFTP_ACK in pkt and pkt[TFTP_ACK].block == self.awaiting: + if self.server_tid is None: + self.server_tid = pkt[UDP].sport + self.l3[UDP].dport = self.server_tid + raise self.SEND_DATA() + + @ATMT.receive_condition(WAITING_ACK) + def received_error(self, pkt): + if TFTP_ERROR in pkt: + raise self.ERROR(pkt) + + @ATMT.timeout(WAITING_ACK, 3) + def timeout_waiting(self): + raise self.WAITING_ACK() + @ATMT.action(timeout_waiting) + def retransmit_last_packet(self): + self.send(self.last_packet) + + # SEND_DATA + @ATMT.state() + def SEND_DATA(self): + self.awaiting += 1 + self.last_packet = self.l3/TFTP_DATA(block=self.awaiting)/self.data.pop(0) + self.send(self.last_packet) + if self.data: + raise self.WAITING_ACK() + raise self.END() + + + # ERROR + @ATMT.state(error=1) + def ERROR(self,pkt): + split_bottom_up(UDP, TFTP, dport=self.my_tid) + return pkt[TFTP_ERROR].summary() + + # END + @ATMT.state(final=1) + def END(self): + split_bottom_up(UDP, TFTP, dport=self.my_tid) + + +class TFTP_WRQ_server(Automaton): + + def parse_args(self, ip=None, sport=None, *args, **kargs): + Automaton.parse_args(self, *args, **kargs) + self.ip = ip + self.sport = sport + + def master_filter(self, pkt): + return TFTP in pkt and (not self.ip or pkt[IP].dst == self.ip) + + @ATMT.state(initial=1) + def BEGIN(self): + self.blksize=512 + self.blk=1 + self.filedata="" + self.my_tid = self.sport or random.randint(10000,65500) + bind_bottom_up(UDP, TFTP, dport=self.my_tid) + + @ATMT.receive_condition(BEGIN) + def receive_WRQ(self,pkt): + if TFTP_WRQ in pkt: + raise self.WAIT_DATA().action_parameters(pkt) + + @ATMT.action(receive_WRQ) + def ack_WRQ(self, pkt): + ip = pkt[IP] + self.ip = ip.dst + self.dst = ip.src + self.filename = pkt[TFTP_WRQ].filename + options = pkt[TFTP_Options] + self.l3 = IP(src=ip.dst, dst=ip.src)/UDP(sport=self.my_tid, dport=pkt.sport)/TFTP() + if options is None: + self.last_packet = self.l3/TFTP_ACK(block=0) + self.send(self.last_packet) + else: + opt = [x for x in options.options if x.oname.upper() == "BLKSIZE"] + if opt: + self.blksize = int(opt[0].value) + self.debug(2,"Negotiated new blksize at %i" % self.blksize) + self.last_packet = self.l3/TFTP_OACK()/TFTP_Options(options=opt) + self.send(self.last_packet) + + @ATMT.state() + def WAIT_DATA(self): + pass + + @ATMT.timeout(WAIT_DATA, 1) + def resend_ack(self): + self.send(self.last_packet) + raise self.WAIT_DATA() + + @ATMT.receive_condition(WAIT_DATA) + def receive_data(self, pkt): + if TFTP_DATA in pkt: + data = pkt[TFTP_DATA] + if data.block == self.blk: + raise self.DATA(data) + + @ATMT.action(receive_data) + def ack_data(self): + self.last_packet = self.l3/TFTP_ACK(block = self.blk) + self.send(self.last_packet) + + @ATMT.state() + def DATA(self, data): + self.filedata += data.load + if len(data.load) < self.blksize: + raise self.END() + self.blk += 1 + raise self.WAIT_DATA() + + @ATMT.state(final=1) + def END(self): + return self.filename,self.filedata + split_bottom_up(UDP, TFTP, dport=self.my_tid) + + +class TFTP_RRQ_server(Automaton): + def parse_args(self, store=None, joker=None, dir=None, ip=None, sport=None, serve_one=False, **kargs): + Automaton.parse_args(self,**kargs) + if store is None: + store = {} + if dir is not None: + self.dir = os.path.join(os.path.abspath(dir),"") + else: + self.dir = None + self.store = store + self.joker = joker + self.ip = ip + self.sport = sport + self.serve_one = serve_one + self.my_tid = self.sport or random.randint(10000,65500) + bind_bottom_up(UDP, TFTP, dport=self.my_tid) + + def master_filter(self, pkt): + return TFTP in pkt and (not self.ip or pkt[IP].dst == self.ip) + + @ATMT.state(initial=1) + def WAIT_RRQ(self): + self.blksize=512 + self.blk=0 + + @ATMT.receive_condition(WAIT_RRQ) + def receive_rrq(self, pkt): + if TFTP_RRQ in pkt: + raise self.RECEIVED_RRQ(pkt) + + + @ATMT.state() + def RECEIVED_RRQ(self, pkt): + ip = pkt[IP] + options = pkt[TFTP_Options] + self.l3 = IP(src=ip.dst, dst=ip.src)/UDP(sport=self.my_tid, dport=ip.sport)/TFTP() + self.filename = pkt[TFTP_RRQ].filename + self.blk=1 + self.data = None + if self.filename in self.store: + self.data = self.store[self.filename] + elif self.dir is not None: + fn = os.path.abspath(os.path.join(self.dir, self.filename)) + if fn.startswith(self.dir): # Check we're still in the server's directory + try: + self.data=open(fn).read() + except IOError: + pass + if self.data is None: + self.data = self.joker + + if options: + opt = [x for x in options.options if x.oname.upper() == "BLKSIZE"] + if opt: + self.blksize = int(opt[0].value) + self.debug(2,"Negotiated new blksize at %i" % self.blksize) + self.last_packet = self.l3/TFTP_OACK()/TFTP_Options(options=opt) + self.send(self.last_packet) + + + + + @ATMT.condition(RECEIVED_RRQ) + def file_in_store(self): + if self.data is not None: + self.blknb = len(self.data)/self.blksize+1 + raise self.SEND_FILE() + + @ATMT.condition(RECEIVED_RRQ) + def file_not_found(self): + if self.data is None: + raise self.WAIT_RRQ() + @ATMT.action(file_not_found) + def send_error(self): + self.send(self.l3/TFTP_ERROR(errorcode=1, errormsg=TFTP_Error_Codes[1])) + + @ATMT.state() + def SEND_FILE(self): + self.send(self.l3/TFTP_DATA(block=self.blk)/self.data[(self.blk-1)*self.blksize:self.blk*self.blksize]) + + @ATMT.timeout(SEND_FILE, 3) + def timeout_waiting_ack(self): + raise self.SEND_FILE() + + @ATMT.receive_condition(SEND_FILE) + def received_ack(self, pkt): + if TFTP_ACK in pkt and pkt[TFTP_ACK].block == self.blk: + raise self.RECEIVED_ACK() + @ATMT.state() + def RECEIVED_ACK(self): + self.blk += 1 + + @ATMT.condition(RECEIVED_ACK) + def no_more_data(self): + if self.blk > self.blknb: + if self.serve_one: + raise self.END() + raise self.WAIT_RRQ() + @ATMT.condition(RECEIVED_ACK, prio=2) + def data_remaining(self): + raise self.SEND_FILE() + + @ATMT.state(final=1) + def END(self): + split_bottom_up(UDP, TFTP, dport=self.my_tid) + + + + diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/vrrp.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/vrrp.py new file mode 100644 index 00000000..f874b352 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/vrrp.py @@ -0,0 +1,39 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## Copyright (C) 6WIND +## This program is published under a GPLv2 license + +""" +VRRP (Virtual Router Redundancy Protocol). +""" + +from scapy.packet import * +from scapy.fields import * +from scapy.layers.inet import IP + +IPPROTO_VRRP=112 + +# RFC 3768 - Virtual Router Redundancy Protocol (VRRP) +class VRRP(Packet): + fields_desc = [ + BitField("version" , 2, 4), + BitField("type" , 1, 4), + ByteField("vrid", 1), + ByteField("priority", 100), + FieldLenField("ipcount", None, count_of="addrlist", fmt="B"), + ByteField("authtype", 0), + ByteField("adv", 1), + XShortField("chksum", None), + FieldListField("addrlist", [], IPField("", "0.0.0.0"), + count_from = lambda pkt: pkt.ipcount), + IntField("auth1", 0), + IntField("auth2", 0) ] + + def post_build(self, p, pay): + if self.chksum is None: + ck = checksum(p) + p = p[:6]+chr(ck>>8)+chr(ck&0xff)+p[8:] + return p + +bind_layers( IP, VRRP, proto=IPPROTO_VRRP) diff --git a/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/x509.py b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/x509.py new file mode 100644 index 00000000..18aaa5e3 --- /dev/null +++ b/scripts/external_libs/scapy-2.3.1/python2/scapy/layers/x509.py @@ -0,0 +1,108 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Philippe Biondi +## This program is published under a GPLv2 license + +""" +X.509 certificates. +""" + +from scapy.asn1packet import * +from scapy.asn1fields import * + +########## +## X509 ## +########## + +######[ ASN1 class ]###### + +class ASN1_Class_X509(ASN1_Class_UNIVERSAL): + name="X509" + CONT0 = 0xa0 + CONT1 = 0xa1 + CONT2 = 0xa2 + CONT3 = 0xa3 + +class ASN1_X509_CONT0(ASN1_SEQUENCE): + tag = ASN1_Class_X509.CONT0 + +class ASN1_X509_CONT1(ASN1_SEQUENCE): + tag = ASN1_Class_X509.CONT1 + +class ASN1_X509_CONT2(ASN1_SEQUENCE): + tag = ASN1_Class_X509.CONT2 + +class ASN1_X509_CONT3(ASN1_SEQUENCE): + tag = ASN1_Class_X509.CONT3 + +######[ BER codecs ]####### + +class BERcodec_X509_CONT0(BERcodec_SEQUENCE): + tag = ASN1_Class_X509.CONT0 + +class BERcodec_X509_CONT1(BERcodec_SEQUENCE): + tag = ASN1_Class_X509.CONT1 + +class BERcodec_X509_CONT2(BERcodec_SEQUENCE): + tag = ASN1_Class_X509.CONT2 + +class BERcodec_X509_CONT3(BERcodec_SEQUENCE): + tag = ASN1_Class_X509.CONT3 + +######[ ASN1 fields ]###### + +class ASN1F_X509_CONT0(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_X509.CONT0 + +class ASN1F_X509_CONT1(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_X509.CONT1 + +class ASN1F_X509_CONT2(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_X509.CONT2 + +class ASN1F_X509_CONT3(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_X509.CONT3 + +######[ X509 packets ]###### + +class X509RDN(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SET( + ASN1F_SEQUENCE( ASN1F_OID("oid","2.5.4.6"), + ASN1F_PRINTABLE_STRING("value","") + ) + ) + +class X509v3Ext(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_field("val",ASN1_NULL(0)) + + +class X509Cert(ASN1_Packet): + ASN1_codec = ASN1_Codecs.BER + ASN1_root = ASN1F_SEQUENCE( + ASN1F_SEQUENCE( + ASN1F_optionnal(ASN1F_X509_CONT0(ASN1F_INTEGER("version",3))), + ASN1F_INTEGER("sn",1), + ASN1F_SEQUENCE(ASN1F_OID("sign_algo","1.2.840.113549.1.1.5"), + ASN1F_field("sa_value",ASN1_NULL(0))), + ASN1F_SEQUENCE_OF("issuer",[],X509RDN), + ASN1F_SEQUENCE(ASN1F_UTC_TIME("not_before",ZuluTime(-600)), # ten minutes ago + ASN1F_UTC_TIME("not_after",ZuluTime(+86400))), # for 24h + ASN1F_SEQUENCE_OF("subject",[],X509RDN), + ASN1F_SEQUENCE( + ASN1F_SEQUENCE(ASN1F_OID("pubkey_algo","1.2.840.113549.1.1.1"), + ASN1F_field("pk_value",ASN1_NULL(0))), + ASN1F_BIT_STRING("pubkey","") + ), + ASN1F_optionnal(ASN1F_X509_CONT3(ASN1F_SEQUENCE_OF("x509v3ext",[],X509v3Ext))), + + ), + ASN1F_SEQUENCE(ASN1F_OID("sign_algo2","1.2.840.113549.1.1.5"), + ASN1F_field("sa2_value",ASN1_NULL(0))), + ASN1F_BIT_STRING("signature","") + ) + + + + -- cgit 1.2.3-korg