From 1831e9bbc03484b078dbd604dfec0e025cea8eca Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Mon, 21 Mar 2016 12:55:16 +0200 Subject: [PATCH] scapy 3-0.18 with trex client diff --- .../scapy-python3-0.18/scapy/__init__.py | 15 + .../external_libs/scapy-python3-0.18/scapy/abc.py | 1 + .../external_libs/scapy-python3-0.18/scapy/all.py | 49 + .../scapy-python3-0.18/scapy/ansmachine.py | 130 + .../scapy-python3-0.18/scapy/arch/__init__.py | 108 + .../scapy-python3-0.18/scapy/arch/bsd.py | 12 + .../scapy-python3-0.18/scapy/arch/cdnet.py | 229 + .../scapy-python3-0.18/scapy/arch/linux.py | 530 + .../scapy-python3-0.18/scapy/arch/pcapdnet.py | 565 + .../scapy-python3-0.18/scapy/arch/solaris.py | 16 + .../scapy-python3-0.18/scapy/arch/unix.py | 168 + .../scapy/arch/windows/__init__.py | 501 + .../scapy-python3-0.18/scapy/arch/winpcapy.py | 739 ++ .../scapy-python3-0.18/scapy/as_resolvers.py | 115 + .../scapy-python3-0.18/scapy/asn1/__init__.py | 12 + .../scapy-python3-0.18/scapy/asn1/asn1.py | 321 + .../scapy-python3-0.18/scapy/asn1/ber.py | 370 + .../scapy-python3-0.18/scapy/asn1/mib.py | 149 + .../scapy-python3-0.18/scapy/asn1fields.py | 341 + .../scapy-python3-0.18/scapy/asn1packet.py | 26 + .../scapy-python3-0.18/scapy/automaton.py | 753 ++ .../scapy-python3-0.18/scapy/autorun.py | 142 + .../scapy-python3-0.18/scapy/base_classes.py | 237 + .../scapy-python3-0.18/scapy/config.py | 394 + .../scapy-python3-0.18/scapy/contrib/__init__.py | 8 + .../scapy-python3-0.18/scapy/contrib/avs.py | 57 + .../scapy-python3-0.18/scapy/contrib/bgp.py | 168 + .../scapy-python3-0.18/scapy/contrib/carp.py | 65 + .../scapy-python3-0.18/scapy/contrib/cdp.py | 306 + .../scapy-python3-0.18/scapy/contrib/chdlc.py | 42 + .../scapy-python3-0.18/scapy/contrib/dtp.py | 115 + .../scapy-python3-0.18/scapy/contrib/eigrp.py | 488 + .../scapy-python3-0.18/scapy/contrib/etherip.py | 19 + .../scapy-python3-0.18/scapy/contrib/gsm_um.py | 13119 +++++++++++++++++++ .../scapy-python3-0.18/scapy/contrib/gtp.py | 546 + .../scapy-python3-0.18/scapy/contrib/igmp.py | 171 + .../scapy-python3-0.18/scapy/contrib/igmpv3.py | 270 + .../scapy-python3-0.18/scapy/contrib/ikev2.py | 362 + .../scapy-python3-0.18/scapy/contrib/ldp.py | 475 + .../scapy-python3-0.18/scapy/contrib/mpls.py | 17 + .../scapy-python3-0.18/scapy/contrib/ospf.py | 833 ++ .../scapy-python3-0.18/scapy/contrib/ppi.py | 86 + .../scapy-python3-0.18/scapy/contrib/ppi_cace.py | 87 + .../scapy-python3-0.18/scapy/contrib/ppi_geotag.py | 464 + .../scapy-python3-0.18/scapy/contrib/ripng.py | 41 + .../scapy-python3-0.18/scapy/contrib/rsvp.py | 188 + .../scapy-python3-0.18/scapy/contrib/skinny.py | 499 + .../scapy/contrib/ubberlogger.py | 101 + .../scapy-python3-0.18/scapy/contrib/vqp.py | 58 + .../scapy-python3-0.18/scapy/contrib/vtp.py | 171 + .../scapy-python3-0.18/scapy/contrib/wpa_eapol.py | 35 + .../scapy-python3-0.18/scapy/crypto/__init__.py | 17 + .../scapy-python3-0.18/scapy/crypto/cert.py | 2486 ++++ .../scapy-python3-0.18/scapy/dadict.py | 91 + .../external_libs/scapy-python3-0.18/scapy/data.py | 215 + .../scapy-python3-0.18/scapy/error.py | 60 + .../scapy-python3-0.18/scapy/fields.py | 935 ++ .../scapy-python3-0.18/scapy/layers/__init__.py | 8 + .../scapy-python3-0.18/scapy/layers/all.py | 45 + .../scapy-python3-0.18/scapy/layers/bluetooth.py | 213 + .../scapy-python3-0.18/scapy/layers/dhcp.py | 381 + .../scapy-python3-0.18/scapy/layers/dhcp6.py | 1718 +++ .../scapy-python3-0.18/scapy/layers/dns.py | 712 + .../scapy-python3-0.18/scapy/layers/dot11.py | 560 + .../scapy-python3-0.18/scapy/layers/gprs.py | 21 + .../scapy-python3-0.18/scapy/layers/hsrp.py | 79 + .../scapy-python3-0.18/scapy/layers/inet.py | 1569 +++ .../scapy-python3-0.18/scapy/layers/inet6.py | 3047 +++++ .../scapy-python3-0.18/scapy/layers/ipsec.py | 995 ++ .../scapy-python3-0.18/scapy/layers/ir.py | 44 + .../scapy-python3-0.18/scapy/layers/isakmp.py | 355 + .../scapy-python3-0.18/scapy/layers/l2.py | 543 + .../scapy-python3-0.18/scapy/layers/l2tp.py | 36 + .../scapy-python3-0.18/scapy/layers/llmnr.py | 65 + .../scapy-python3-0.18/scapy/layers/mgcp.py | 45 + .../scapy-python3-0.18/scapy/layers/mobileip.py | 47 + .../scapy-python3-0.18/scapy/layers/netbios.py | 222 + .../scapy-python3-0.18/scapy/layers/netflow.py | 48 + .../scapy-python3-0.18/scapy/layers/ntp.py | 77 + .../scapy-python3-0.18/scapy/layers/pflog.py | 59 + .../scapy-python3-0.18/scapy/layers/ppp.py | 349 + .../scapy-python3-0.18/scapy/layers/radius.py | 65 + .../scapy-python3-0.18/scapy/layers/rip.py | 74 + .../scapy-python3-0.18/scapy/layers/rtp.py | 40 + .../scapy-python3-0.18/scapy/layers/sctp.py | 439 + .../scapy-python3-0.18/scapy/layers/sebek.py | 109 + .../scapy-python3-0.18/scapy/layers/skinny.py | 161 + .../scapy-python3-0.18/scapy/layers/smb.py | 354 + .../scapy-python3-0.18/scapy/layers/snmp.py | 255 + .../scapy-python3-0.18/scapy/layers/tftp.py | 477 + .../scapy-python3-0.18/scapy/layers/vrrp.py | 39 + .../scapy-python3-0.18/scapy/layers/x509.py | 108 + .../external_libs/scapy-python3-0.18/scapy/main.py | 380 + .../scapy-python3-0.18/scapy/modules/__init__.py | 8 + .../scapy-python3-0.18/scapy/modules/geoip.py | 77 + .../scapy-python3-0.18/scapy/modules/nmap.py | 215 + .../scapy-python3-0.18/scapy/modules/p0f.py | 549 + .../scapy-python3-0.18/scapy/modules/queso.py | 113 + .../scapy-python3-0.18/scapy/modules/voip.py | 149 + .../scapy-python3-0.18/scapy/packet.py | 1360 ++ .../scapy-python3-0.18/scapy/pipetool.py | 566 + .../scapy-python3-0.18/scapy/plist.py | 517 + .../scapy-python3-0.18/scapy/pton_ntop.py | 90 + .../scapy-python3-0.18/scapy/route.py | 175 + .../scapy-python3-0.18/scapy/route6.py | 288 + .../scapy-python3-0.18/scapy/scapypipes.py | 123 + .../scapy-python3-0.18/scapy/sendrecv.py | 678 + .../scapy-python3-0.18/scapy/supersocket.py | 141 + .../scapy-python3-0.18/scapy/themes.py | 277 + .../scapy-python3-0.18/scapy/tools/UTscapy.py | 677 + .../scapy-python3-0.18/scapy/tools/__init__.py | 8 + .../scapy-python3-0.18/scapy/tools/check_asdis.py | 103 + .../scapy-python3-0.18/scapy/utils.py | 1054 ++ .../scapy-python3-0.18/scapy/utils6.py | 823 ++ .../scapy-python3-0.18/scapy/volatile.py | 685 + scripts/external_libs/scapy3-0.18-origin.rar | Bin 0 -> 367282 bytes 116 files changed, 50933 insertions(+) create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/__init__.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/abc.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/all.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/ansmachine.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/arch/__init__.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/arch/bsd.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/arch/cdnet.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/arch/linux.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/arch/pcapdnet.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/arch/solaris.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/arch/unix.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/arch/windows/__init__.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/arch/winpcapy.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/as_resolvers.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/asn1/__init__.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/asn1/asn1.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/asn1/ber.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/asn1/mib.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/asn1fields.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/asn1packet.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/automaton.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/autorun.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/base_classes.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/config.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/__init__.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/avs.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/bgp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/carp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/cdp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/chdlc.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/dtp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/eigrp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/etherip.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/gsm_um.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/gtp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/igmp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/igmpv3.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/ikev2.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/ldp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/mpls.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/ospf.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/ppi.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/ppi_cace.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/ppi_geotag.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/ripng.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/rsvp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/skinny.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/ubberlogger.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/vqp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/vtp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/contrib/wpa_eapol.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/crypto/__init__.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/crypto/cert.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/dadict.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/data.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/error.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/fields.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/__init__.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/all.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/bluetooth.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/dhcp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/dhcp6.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/dns.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/dot11.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/gprs.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/hsrp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/inet.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/inet6.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/ipsec.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/ir.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/isakmp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/l2.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/l2tp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/llmnr.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/mgcp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/mobileip.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/netbios.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/netflow.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/ntp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/pflog.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/ppp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/radius.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/rip.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/rtp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/sctp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/sebek.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/skinny.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/smb.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/snmp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/tftp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/vrrp.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/layers/x509.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/main.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/modules/__init__.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/modules/geoip.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/modules/nmap.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/modules/p0f.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/modules/queso.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/modules/voip.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/packet.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/pipetool.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/plist.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/pton_ntop.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/route.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/route6.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/scapypipes.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/sendrecv.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/supersocket.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/themes.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/tools/UTscapy.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/tools/__init__.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/tools/check_asdis.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/utils.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/utils6.py create mode 100644 scripts/external_libs/scapy-python3-0.18/scapy/volatile.py create mode 100644 scripts/external_libs/scapy3-0.18-origin.rar diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/__init__.py b/scripts/external_libs/scapy-python3-0.18/scapy/__init__.py new file mode 100644 index 00000000..443b3675 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/__init__.py @@ -0,0 +1,15 @@ +## 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 + +""" +Scapy: create, send, sniff, dissect and manipulate network packets. + +Usable either from an interactive console or as a Python library. +http://www.secdev.org/projects/scapy +""" + +if __name__ == "__main__": + from scapy.main import interact + interact() diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/abc.py b/scripts/external_libs/scapy-python3-0.18/scapy/abc.py new file mode 100644 index 00000000..3d5f06e2 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/abc.py @@ -0,0 +1 @@ +from config import conf diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/all.py b/scripts/external_libs/scapy-python3-0.18/scapy/all.py new file mode 100644 index 00000000..46030c66 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/all.py @@ -0,0 +1,49 @@ +## 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 + +""" +Aggregate top level objects from all Scapy modules. +""" + +from .config import * +from .base_classes import * +from .dadict import * +from .data import * +from .error import * +#from .themes import * +from .arch import * + +from .plist import * +from .fields import * +from .packet import * +#from .asn1fields import * +#from .asn1packet import * + +from .utils import * +#from .route import * +#if conf.ipv6_enabled: +# from .utils6 import * +# from .route6 import * +#from .sendrecv import * +#from .supersocket import * +#from .volatile import * +#from .as_resolvers import * + +#from .ansmachine import * +#from .automaton import * +#from .autorun import * + +from .main import * + +from .layers.all import * + +#from .asn1.asn1 import * +#from .asn1.ber import * +#from .asn1.mib import * + +#from .crypto import * + +#from .pipetool import * +#from .scapypipes import * diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/ansmachine.py b/scripts/external_libs/scapy-python3-0.18/scapy/ansmachine.py new file mode 100644 index 00000000..b087ac44 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/ansmachine.py @@ -0,0 +1,130 @@ +## 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 + +""" +Answering machines. +""" + +######################## +## Answering machines ## +######################## + +from .sendrecv import send,sendp,sniff +from .config import conf +from .error import log_interactive + +class ReferenceAM(type): + def __new__(cls, name, bases, dct): + o = super(ReferenceAM, cls).__new__(cls, name, bases, dct) + if o.function_name: + globals()[o.function_name] = lambda o=o,*args,**kargs: o(*args,**kargs)() + return o + + +class AnsweringMachine(object): + __metaclass__ = ReferenceAM + function_name = "" + filter = None + sniff_options = { "store":0 } + sniff_options_list = [ "store", "iface", "count", "promisc", "filter", "type", "prn", "stop_filter" ] + send_options = { "verbose":0 } + send_options_list = ["iface", "inter", "loop", "verbose"] + send_function = staticmethod(send) + + + def __init__(self, **kargs): + self.mode = 0 + if self.filter: + kargs.setdefault("filter",self.filter) + kargs.setdefault("prn", self.reply) + self.optam1 = {} + self.optam2 = {} + self.optam0 = {} + doptsend,doptsniff = self.parse_all_options(1, kargs) + self.defoptsend = self.send_options.copy() + self.defoptsend.update(doptsend) + self.defoptsniff = self.sniff_options.copy() + self.defoptsniff.update(doptsniff) + self.optsend,self.optsniff = [{},{}] + + def __getattr__(self, attr): + for d in [self.optam2, self.optam1]: + if attr in d: + return d[attr] + raise AttributeError(attr) + + def __setattr__(self, attr, val): + mode = self.__dict__.get("mode",0) + if mode == 0: + self.__dict__[attr] = val + else: + [self.optam1, self.optam2][mode-1][attr] = val + + def parse_options(self): + pass + + def parse_all_options(self, mode, kargs): + sniffopt = {} + sendopt = {} + for k in list(kargs): + if k in self.sniff_options_list: + sniffopt[k] = kargs[k] + if k in self.send_options_list: + sendopt[k] = kargs[k] + if k in self.sniff_options_list+self.send_options_list: + del(kargs[k]) + if mode != 2 or kargs: + if mode == 1: + self.optam0 = kargs + elif mode == 2 and kargs: + k = self.optam0.copy() + k.update(kargs) + self.parse_options(**k) + kargs = k + omode = self.__dict__.get("mode",0) + self.__dict__["mode"] = mode + self.parse_options(**kargs) + self.__dict__["mode"] = omode + return sendopt,sniffopt + + def is_request(self, req): + return 1 + + def make_reply(self, req): + return req + + def send_reply(self, reply): + self.send_function(reply, **self.optsend) + + def print_reply(self, req, reply): + print("%s ==> %s" % (req.summary(),reply.summary())) + + def reply(self, pkt): + if not self.is_request(pkt): + return + reply = self.make_reply(pkt) + self.send_reply(reply) + if conf.verb >= 0: + self.print_reply(pkt, reply) + + def run(self, *args, **kargs): + log_interactive.warning("run() method deprecated. The intance is now callable") + self(*args,**kargs) + + def __call__(self, *args, **kargs): + optsend,optsniff = self.parse_all_options(2,kargs) + self.optsend=self.defoptsend.copy() + self.optsend.update(optsend) + self.optsniff=self.defoptsniff.copy() + self.optsniff.update(optsniff) + + try: + self.sniff() + except KeyboardInterrupt: + print("Interrupted by user") + + def sniff(self): + sniff(**self.optsniff) + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/arch/__init__.py b/scripts/external_libs/scapy-python3-0.18/scapy/arch/__init__.py new file mode 100644 index 00000000..0066e049 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/arch/__init__.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 + +""" +Operating system specific functionality. +""" + + +import sys,os,socket +from scapy.error import * +import scapy.config + +try: + import matplotlib.pyplot as plt + MATPLOTLIB = True + if scapy.config.conf.interactive: + plt.ion() +except ImportError: + log_loading.info("Can't import matplotlib. Not critical, but won't be able to plot.") + MATPLOTLIB = False + +try: + import networkx as nx + NETWORKX = True +except ImportError: + log_loading.info("Can't import networkx. Not criticial, but won't be able to draw network graphs.") + NETWORKX = False + +try: + import pyx + PYX=1 +except ImportError: + log_loading.info("Can't import PyX. Won't be able to use psdump() or pdfdump().") + PYX=0 + + +def str2mac(s): + #return ("%02x:"*6)[:-1] % tuple(map(ord, s)) + return ("%02x:"*6)[:-1] % tuple(s) + + + +def get_if_addr(iff): + return socket.inet_ntoa(get_if_raw_addr(iff)) + +def get_if_hwaddr(iff): + mac = get_if_raw_hwaddr(iff) + return str2mac(mac) + + +LINUX=sys.platform.startswith("linux") +OPENBSD=sys.platform.startswith("openbsd") +FREEBSD=sys.platform.startswith("freebsd") +NETBSD = sys.platform.startswith("netbsd") +DARWIN=sys.platform.startswith("darwin") +SOLARIS=sys.platform.startswith("sunos") +WINDOWS=sys.platform.startswith("win32") + +X86_64 = not WINDOWS and (os.uname()[4] == 'x86_64') + +#if WINDOWS: +# log_loading.warning("Windows support for scapy3k is currently in testing. Sniffing/sending/receiving packets should be working with WinPcap driver and Powershell. Create issues at https://github.com/phaethon/scapy") + +# Next step is to import following architecture specific functions: +# def get_if_raw_hwaddr(iff) +# def get_if_raw_addr(iff): +# def get_if_list(): +# def get_working_if(): +# def attach_filter(s, filter): +# def set_promisc(s,iff,val=1): +# def read_routes(): +# def get_if(iff,cmd): +# def get_if_index(iff): + + + +if LINUX: + from .linux import * + if scapy.config.conf.use_winpcapy or scapy.config.conf.use_netifaces: + from pcapdnet import * +elif OPENBSD or FREEBSD or NETBSD or DARWIN: + from .bsd import * +elif SOLARIS: + from .solaris import * +elif WINDOWS: + pass; + #from .windows import * + +LOOPBACK_NAME="a" + +if scapy.config.conf.iface is None: + scapy.config.conf.iface = LOOPBACK_NAME + +def get_if_raw_addr6(iff): + """ + Returns the main global unicast address associated with provided + interface, in network format. If no global address is found, None + is returned. + """ + #r = filter(lambda x: x[2] == iff and x[1] == IPV6_ADDR_GLOBAL, in6_getifaddr()) + r = [ x for x in in6_getifaddr() if x[2] == iff and x[1] == IPV6_ADDR_GLOBAL] + if len(r) == 0: + return None + else: + r = r[0][0] + return inet_pton(socket.AF_INET6, r) diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/arch/bsd.py b/scripts/external_libs/scapy-python3-0.18/scapy/arch/bsd.py new file mode 100644 index 00000000..c4220308 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/arch/bsd.py @@ -0,0 +1,12 @@ +## 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 + +""" +Support for BSD-like operating systems such as FreeBSD, OpenBSD and Mac OS X. +""" + +LOOPBACK_NAME="lo0" + +from .unix import * diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/arch/cdnet.py b/scripts/external_libs/scapy-python3-0.18/scapy/arch/cdnet.py new file mode 100644 index 00000000..98ebf084 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/arch/cdnet.py @@ -0,0 +1,229 @@ +from ctypes import * +from ctypes.util import find_library +import sys + +WIN=False + +if sys.platform.startswith('win'): + WIN=True + +if WIN: + SOCKET = c_uint + _lib=CDLL('dnet') +else: + SOCKET = c_int + _lib_name = find_library('dnet') + if not _lib_name: + raise OSError("Cannot find libdnet.so") + _lib=CDLL(_lib_name) + +ETH_ADDR_LEN = 6 +INTF_NAME_LEN = 16 +INTF_NAME_COUNT = 20 +INTF_ALIAS_COUNT = 20 +IP6_ADDR_LEN = 16 + +ADDR_TYPE_NONE = 0 +ADDR_TYPE_ETH = 1 +ADDR_TYPE_IP = 2 +ADDR_TYPE_IP6 = 3 + +INTF_TYPE_OTHER = 1 +INTF_TYPE_ETH = 6 +INTF_TYPE_TOKENRING = 9 +INTF_TYPE_FDDI = 15 +INTF_TYPE_PPP = 23 +INTF_TYPE_LOOPBACK = 24 +INTF_TYPE_SLIP = 28 +INTF_TYPE_TUN = 53 + + +uint8_t = c_ubyte +uint16_t = c_ushort +uint32_t = c_uint +ssize_t = c_long +dnet_ip_addr_t = uint32_t + +dnet_intf_name = c_char * INTF_NAME_LEN + +class dnet_intf_list(Structure): + pass + +dnet_intf_list._fields_ = [ ('length', c_int), + ('interfaces', dnet_intf_name * 20) ] + +class dnet_eth_addr(Structure): + pass + +dnet_eth_addr._fields_ = [ ('data', uint8_t * ETH_ADDR_LEN) ] +dnet_eth_addr_t = dnet_eth_addr + +class dnet_ip6_addr(Structure): + pass + +dnet_ip6_addr._fields_ = [ ('data', uint8_t * IP6_ADDR_LEN) ] +dnet_ip6_addr_t = dnet_ip6_addr + +class dnet_addr_u(Union): + pass + +dnet_addr_u._fields_ = [ ('eth', dnet_eth_addr_t), + ('ip', dnet_ip_addr_t), + ('ip6', dnet_ip6_addr_t), + ('data8', uint8_t * 16), + ('data16', uint16_t * 8), + ('data32', uint32_t * 4) ] + +class dnet_addr(Structure): + pass +dnet_addr._anonymous_ = ('__addr_u', ) +dnet_addr._fields_ = [ ('addr_type', uint16_t), + ('addr_bits', uint16_t), + ('__addr_u', dnet_addr_u) ] + +class dnet_intf_entry(Structure): + pass + +dnet_intf_entry._fields_ = [ ('intf_len', c_uint), + ('intf_name', c_char * INTF_NAME_LEN), + ('intf_type', c_ushort), + ('intf_flags', c_ushort), + ('intf_mtu', c_uint), + ('intf_addr', dnet_addr), + ('intf_dst_addr', dnet_addr), + ('intf_link_addr', dnet_addr), + ('intf_alias_num', c_uint), + ('intf_alias_addrs', dnet_addr * INTF_ALIAS_COUNT) ] + + +eth_t = c_void_p +intf_t = c_void_p +ip_t = c_void_p +dnet_intf_handler = CFUNCTYPE(c_int, POINTER(dnet_intf_entry), POINTER(c_void_p)) + +dnet_eth_open = _lib.eth_open +dnet_eth_open.restype = POINTER(eth_t) +dnet_eth_open.argtypes = [ POINTER(c_char) ] + +dnet_eth_get = _lib.eth_get +dnet_eth_get.restype = c_int +dnet_eth_get.argtypes = [ POINTER(eth_t), POINTER(dnet_eth_addr_t) ] + +dnet_eth_set = _lib.eth_set +dnet_eth_set.restype = c_int +dnet_eth_set.argtypes = [ POINTER(eth_t), POINTER(dnet_eth_addr_t) ] + +dnet_eth_send = _lib.eth_send +dnet_eth_send.restype = ssize_t +dnet_eth_send.argtypes = [ POINTER(eth_t), c_void_p, c_size_t ] + +dnet_eth_close = _lib.eth_close +dnet_eth_close.restype = POINTER(eth_t) +dnet_eth_close.argtypes = [ POINTER(eth_t) ] + +dnet_intf_open = _lib.intf_open +dnet_intf_open.restype = POINTER(intf_t) +dnet_intf_open.argtypes = [ ] + +dnet_intf_get = _lib.intf_get +dnet_intf_get.restype = c_int +dnet_intf_get.argtypes = [ POINTER(intf_t), POINTER(dnet_intf_entry) ] + +dnet_intf_get_src = _lib.intf_get_src +dnet_intf_get_src.restype = c_int +dnet_intf_get_src.argtypes = [ POINTER(intf_t), POINTER(dnet_intf_entry), POINTER(dnet_addr) ] + +dnet_intf_get_dst = _lib.intf_get_dst +dnet_intf_get_dst.restype = c_int +dnet_intf_get_dst.argtypes = [ POINTER(intf_t), POINTER(dnet_intf_entry), POINTER(dnet_addr) ] + +dnet_intf_set = _lib.intf_set +dnet_intf_set.restype = c_int +dnet_intf_set.argtypes = [ POINTER(intf_t), POINTER(dnet_intf_entry) ] + +dnet_intf_loop = _lib.intf_loop +dnet_intf_loop.restype = POINTER(intf_t) +dnet_intf_loop.argtypes = [ POINTER(intf_t), dnet_intf_handler, c_void_p ] + +dnet_intf_close = _lib.intf_close +dnet_intf_close.restype = POINTER(intf_t) +dnet_intf_close.argtypes = [ POINTER(intf_t) ] + +dnet_ip_open = _lib.ip_open +dnet_ip_open.restype = POINTER(ip_t) +dnet_ip_open.argtypes = [ ] + +dnet_ip_add_option = _lib.ip_add_option +dnet_ip_add_option.restype = ssize_t +dnet_ip_add_option.argtypes = [ POINTER(c_void_p), c_size_t, c_int, POINTER(c_void_p), c_size_t ] + +dnet_ip_checksum = _lib.ip_checksum +dnet_ip_checksum.restype = None +dnet_ip_checksum.argtypes = [ POINTER(c_void_p), c_size_t ] + +dnet_ip_send = _lib.ip_send +dnet_ip_send.restype = ssize_t +dnet_ip_send.argtypes = [ POINTER(ip_t), c_void_p, c_size_t ] + +dnet_ip_close = _lib.ip_close +dnet_ip_close.restype = POINTER(ip_t) +dnet_ip_close.argtypes = [ POINTER(ip_t) ] + +class dnet_eth: + def __init__(self, iface): + self.iface_b = create_string_buffer(iface.encode('ascii')) + self.eth = dnet_eth_open(self.iface_b) + def send(self, sx): + dnet_eth_send(self.eth, sx, len(sx)) + def close(self): + return dnet_eth_close(self.eth) + +class dnet_ip: + def __init__(self): + self.ip = dnet_ip_open() + def send(self, sx): + dnet_ip_send(self.ip, sx, len(sx)) + def close(self): + return dnet_ip_close(self.ip) + +def dnet_intf_name_loop(entry, intf_list): + l = cast(intf_list, POINTER(dnet_intf_list)) + if l.contents.length >= INTF_NAME_COUNT: + return -1 + for i in enumerate(entry.contents.intf_name): + l.contents.interfaces[l.contents.length][i[0]] = i[1] + l.contents.length += 1 + return 0 + +class dnet_intf: + def __init__(self): + self.intf = dnet_intf_open() + intf_list = dnet_intf_list() + intf_list.length = 0 + dnet_intf_loop(self.intf, dnet_intf_handler(dnet_intf_name_loop), pointer(intf_list)) + self.names = [] + for i in range(INTF_NAME_COUNT): + if i >= intf_list.length: + break + self.names.append(intf_list.interfaces[i].value.decode('ascii').strip('\0')) + + def close(self): + return dnet_intf_close(self.intf) + + def get(self, iface): + ret = {} + entry = dnet_intf_entry() + entry.intf_name = iface.encode('ascii') + entry.intf_len = sizeof(entry) + r = dnet_intf_get(self.intf, byref(entry)) + if r < 0: + return {} + ret['addr6'] = [] + for i in range(entry.intf_alias_num): + if entry.intf_alias_addrs[i].addr_type == ADDR_TYPE_IP6: + ret['addr6'].append(bytes(entry.intf_alias_addrs[i].data8[:16])) + ret['type'] = entry.intf_type + ret['addr'] = bytes(entry.intf_addr.data8[:4]) + ret['link_addr'] = bytes(entry.intf_link_addr.data8[:6]) + return ret + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/arch/linux.py b/scripts/external_libs/scapy-python3-0.18/scapy/arch/linux.py new file mode 100644 index 00000000..3eab16c6 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/arch/linux.py @@ -0,0 +1,530 @@ +## 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 + +""" +Linux specific functions. +""" + +import sys,os,struct,socket,time,ctypes +from select import select +from fcntl import ioctl +import scapy.utils +import scapy.utils6 +from scapy.config import conf +from scapy.data import * +from scapy.supersocket import SuperSocket +import scapy.arch +from scapy.error import warning, Scapy_Exception + + + +# From bits/ioctls.h +SIOCGIFHWADDR = 0x8927 # Get hardware address +SIOCGIFADDR = 0x8915 # get PA address +SIOCGIFNETMASK = 0x891b # get network PA mask +SIOCGIFNAME = 0x8910 # get iface name +SIOCSIFLINK = 0x8911 # set iface channel +SIOCGIFCONF = 0x8912 # get iface list +SIOCGIFFLAGS = 0x8913 # get flags +SIOCSIFFLAGS = 0x8914 # set flags +SIOCGIFINDEX = 0x8933 # name -> if_index mapping +SIOCGIFCOUNT = 0x8938 # get number of devices +SIOCGSTAMP = 0x8906 # get packet timestamp (as a timeval) + +# From if.h +IFF_UP = 0x1 # Interface is up. +IFF_BROADCAST = 0x2 # Broadcast address valid. +IFF_DEBUG = 0x4 # Turn on debugging. +IFF_LOOPBACK = 0x8 # Is a loopback net. +IFF_POINTOPOINT = 0x10 # Interface is point-to-point link. +IFF_NOTRAILERS = 0x20 # Avoid use of trailers. +IFF_RUNNING = 0x40 # Resources allocated. +IFF_NOARP = 0x80 # No address resolution protocol. +IFF_PROMISC = 0x100 # Receive all packets. + +# From netpacket/packet.h +PACKET_ADD_MEMBERSHIP = 1 +PACKET_DROP_MEMBERSHIP = 2 +PACKET_RECV_OUTPUT = 3 +PACKET_RX_RING = 5 +PACKET_STATISTICS = 6 +PACKET_MR_MULTICAST = 0 +PACKET_MR_PROMISC = 1 +PACKET_MR_ALLMULTI = 2 + +# From bits/socket.h +SOL_PACKET = 263 +# From asm/socket.h +SO_ATTACH_FILTER = 26 +SOL_SOCKET = 1 + +# From net/route.h +RTF_UP = 0x0001 # Route usable +RTF_REJECT = 0x0200 + +# From pcap/pcap.h +PCAP_ERRBUF_SIZE=256 + + + +LOOPBACK_NAME="lo" + +with os.popen("tcpdump -V 2> /dev/null") as _f: + if _f.close() >> 8 == 0x7f: + log_loading.warning("Failed to execute tcpdump. Check it is installed and in the PATH") + TCPDUMP=0 + else: + TCPDUMP=1 +del(_f) + + +def get_if_raw_hwaddr(iff): + return struct.unpack("16xh6s8x",get_if(iff,SIOCGIFHWADDR))[1] + +def get_if_raw_addr(iff): + try: + return get_if(iff, SIOCGIFADDR)[20:24] + except IOError: + return b"\0\0\0\0" + + +def get_if_list(): + try: + f=open("/proc/net/dev","r") + except IOError: + warning("Can't open /proc/net/dev !") + return [] + lst = [] + f.readline() + f.readline() + for l in f: + lst.append(l.split(":")[0].strip()) + f.close() + return lst + +def get_working_if(): + for i in get_if_list(): + if i == LOOPBACK_NAME: + continue + ifflags = struct.unpack("16xH14x",get_if(i,SIOCGIFFLAGS))[0] + if ifflags & IFF_UP: + return i + return LOOPBACK_NAME +def attach_filter(s, filter): + # XXX We generate the filter on the interface conf.iface + # because tcpdump open the "any" interface and ppp interfaces + # in cooked mode. As we use them in raw mode, the filter will not + # work... one solution could be to use "any" interface and translate + # the filter from cooked mode to raw mode + # mode + if not TCPDUMP: + return + try: + f = os.popen("%s -i %s -ddd -s 1600 '%s'" % (conf.prog.tcpdump,conf.iface,filter)) + except OSError as msg: + log_interactive.warning("Failed to execute tcpdump: (%s)") + return + lines = f.readlines() + if f.close(): + raise Scapy_Exception("Filter parse error") + nb = int(lines[0]) + bpf = b"" + for l in lines[1:]: + bpf += struct.pack("HBBI",*[int(x) for x in l.split()]) + + # XXX. Argl! We need to give the kernel a pointer on the BPF, + # python object header seems to be 20 bytes. 36 bytes for x86 64bits arch. + bpf_buf = ctypes.create_string_buffer(bpf) + class BpfProgram(ctypes.Structure): + _fields_ = [ ("bf_len", ctypes.c_int), ("bf_insn", ctypes.POINTER(type(bpf_buf))) ] + #if scapy.arch.X86_64: + # bpfh = struct.pack("HL", nb, id(bpf)+36) + #else: + # bpfh = struct.pack("HI", nb, id(bpf)+20) + bpfh = BpfProgram(nb, ctypes.pointer(bpf_buf)) + s.setsockopt(SOL_SOCKET, SO_ATTACH_FILTER, bpfh) + +def set_promisc(s,iff,val=1): + mreq = struct.pack("IHH8s", get_if_index(iff), PACKET_MR_PROMISC, 0, b"") + if val: + cmd = PACKET_ADD_MEMBERSHIP + else: + cmd = PACKET_DROP_MEMBERSHIP + s.setsockopt(SOL_PACKET, cmd, mreq) + + + +def read_routes(): + try: + f=open("/proc/net/route","rb") + except IOError: + warning("Can't open /proc/net/route !") + return [] + routes = [] + s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + ifreq = ioctl(s, SIOCGIFADDR,struct.pack("16s16x",LOOPBACK_NAME.encode('utf-8'))) + addrfamily = struct.unpack("h",ifreq[16:18])[0] + if addrfamily == socket.AF_INET: + ifreq2 = ioctl(s, SIOCGIFNETMASK,struct.pack("16s16x",LOOPBACK_NAME.encode('utf-8'))) + msk = socket.ntohl(struct.unpack("I",ifreq2[20:24])[0]) + dst = socket.ntohl(struct.unpack("I",ifreq[20:24])[0]) & msk + ifaddr = scapy.utils.inet_ntoa(ifreq[20:24]) + routes.append((dst, msk, "0.0.0.0", LOOPBACK_NAME, ifaddr)) + else: + warning("Interface lo: unkown address family (%i)"% addrfamily) + + for l in f.readlines()[1:]: + iff,dst,gw,flags,x,x,x,msk,x,x,x = l.split() + flags = int(flags,16) + if flags & RTF_UP == 0: + continue + if flags & RTF_REJECT: + continue + try: + ifreq = ioctl(s, SIOCGIFADDR,struct.pack("16s16x",iff)) + except IOError: # interface is present in routing tables but does not have any assigned IP + ifaddr="0.0.0.0" + else: + addrfamily = struct.unpack("h",ifreq[16:18])[0] + if addrfamily == socket.AF_INET: + ifaddr = scapy.utils.inet_ntoa(ifreq[20:24]) + else: + warning("Interface %s: unkown address family (%i)"%(iff, addrfamily)) + continue + routes.append((socket.htonl(int(dst,16))&0xffffffff, + socket.htonl(int(msk,16))&0xffffffff, + scapy.utils.inet_ntoa(struct.pack("I",int(gw,16))), + iff.decode('utf-8'), ifaddr)) + + f.close() + return routes + +############ +### IPv6 ### +############ + +def in6_getifaddr(): + """ + Returns a list of 3-tuples of the form (addr, scope, iface) where + 'addr' is the address of scope 'scope' associated to the interface + 'ifcace'. + + This is the list of all addresses of all interfaces available on + the system. + """ + ret = [] + try: + f = open("/proc/net/if_inet6","rb") + except IOError as err: + return ret + l = f.readlines() + for i in l: + # addr, index, plen, scope, flags, ifname + tmp = i.split() + addr = struct.unpack('4s4s4s4s4s4s4s4s', tmp[0]) + addr = scapy.utils6.in6_ptop(b':'.join(addr).decode('ascii')) + ret.append((addr, int(tmp[3], 16), tmp[5].decode('ascii'))) # (addr, scope, iface) + f.close() + return ret + +def read_routes6(): + try: + f = open("/proc/net/ipv6_route","r") + except IOError as err: + return [] + # 1. destination network + # 2. destination prefix length + # 3. source network displayed + # 4. source prefix length + # 5. next hop + # 6. metric + # 7. reference counter (?!?) + # 8. use counter (?!?) + # 9. flags + # 10. device name + routes = [] + def proc2r(p): + ret = struct.unpack('4s4s4s4s4s4s4s4s', p.encode('ascii')) + ret = b':'.join(ret) + return scapy.utils6.in6_ptop(ret.decode('ascii')) + + lifaddr = in6_getifaddr() + for l in f.readlines(): + d,dp,s,sp,nh,m,rc,us,fl,dev = l.split() + fl = int(fl, 16) + + if fl & RTF_UP == 0: + continue + if fl & RTF_REJECT: + continue + + d = proc2r(d) ; dp = int(dp, 16) + s = proc2r(s) ; sp = int(sp, 16) + nh = proc2r(nh) + + cset = [] # candidate set (possible source addresses) + if dev == LOOPBACK_NAME: + if d == '::': + continue + cset = ['::1'] + else: + #devaddrs = filter(lambda x: x[2] == dev, lifaddr) + devaddrs = [ x for x in lifaddr if x[2] == dev ] + cset = scapy.utils6.construct_source_candidate_set(d, dp, devaddrs, LOOPBACK_NAME) + + if len(cset) != 0: + routes.append((d, dp, nh, dev, cset)) + f.close() + return routes + + + + +def get_if(iff,cmd): + s=socket.socket() + ifreq = ioctl(s, cmd, struct.pack("16s16x",bytes(iff,'utf-8'))) + s.close() + return ifreq + + +def get_if_index(iff): + return int(struct.unpack("I",get_if(iff, SIOCGIFINDEX)[16:20])[0]) + +if os.uname()[4] == 'x86_64': + def get_last_packet_timestamp(sock): + ts = ioctl(sock, SIOCGSTAMP, "1234567890123456") + s,us = struct.unpack("QQ",ts) + return s+us/1000000.0 +else: + def get_last_packet_timestamp(sock): + ts = ioctl(sock, SIOCGSTAMP, "12345678") + s,us = struct.unpack("II",ts) + return s+us/1000000.0 + + +def _flush_fd(fd): + if type(fd) is not int: + fd = fd.fileno() + while 1: + r,w,e = select([fd],[],[],0) + if r: + os.read(fd,MTU) + else: + break + + + + + +class L3PacketSocket(SuperSocket): + desc = "read/write packets at layer 3 using Linux PF_PACKET sockets" + def __init__(self, type = ETH_P_ALL, filter=None, promisc=None, iface=None, nofilter=0): + self.type = type + self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) + self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0) + if iface: + self.ins.bind((iface, type)) + if not nofilter: + if conf.except_filter: + if filter: + filter = "(%s) and not (%s)" % (filter, conf.except_filter) + else: + filter = "not (%s)" % conf.except_filter + if filter is not None: + attach_filter(self.ins, filter) + _flush_fd(self.ins) + self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30) + self.outs = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) + self.outs.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2**30) + if promisc is None: + promisc = conf.promisc + self.promisc = promisc + if self.promisc: + if iface is None: + self.iff = get_if_list() + else: + if iface.__class__ is list: + self.iff = iface + else: + self.iff = [iface] + for i in self.iff: + set_promisc(self.ins, i) + def close(self): + if self.closed: + return + self.closed=1 + if self.promisc: + for i in self.iff: + set_promisc(self.ins, i, 0) + SuperSocket.close(self) + def recv(self, x=MTU): + pkt, sa_ll = self.ins.recvfrom(x) + if sa_ll[2] == socket.PACKET_OUTGOING: + return None + if sa_ll[3] in conf.l2types: + cls = conf.l2types[sa_ll[3]] + lvl = 2 + elif sa_ll[1] in conf.l3types: + cls = conf.l3types[sa_ll[1]] + lvl = 3 + else: + cls = conf.default_l2 + warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using %s" % (sa_ll[0],sa_ll[1],sa_ll[3],cls.name)) + lvl = 2 + + try: + pkt = cls(pkt) + except KeyboardInterrupt: + raise + except: + if conf.debug_dissector: + raise + pkt = conf.raw_layer(pkt) + if lvl == 2: + pkt = pkt.payload + + if pkt is not None: + pkt.time = get_last_packet_timestamp(self.ins) + return pkt + + def send(self, x): + iff,a,gw = x.route() + if iff is None: + iff = conf.iface + sdto = (iff, self.type) + self.outs.bind(sdto) + sn = self.outs.getsockname() + ll = lambda x:x + if type(x) in conf.l3types: + sdto = (iff, conf.l3types[type(x)]) + if sn[3] in conf.l2types: + ll = lambda x:conf.l2types[sn[3]]()/x + try: + sx = bytes(ll(x)) + x.sent_time = time.time() + self.outs.sendto(sx, sdto) + except OSError as msg: + x.sent_time = time.time() # bad approximation + if conf.auto_fragment and msg.errno == 90: + for p in x.fragment(): + self.outs.sendto(bytes(ll(p)), sdto) + else: + raise + + + +class L2Socket(SuperSocket): + desc = "read/write packets at layer 2 using Linux PF_PACKET sockets" + def __init__(self, iface = None, type = ETH_P_ALL, filter=None, nofilter=0): + if iface is None: + iface = conf.iface + self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) + self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0) + if not nofilter: + if conf.except_filter: + if filter: + filter = "(%s) and not (%s)" % (filter, conf.except_filter) + else: + filter = "not (%s)" % conf.except_filter + if filter is not None: + attach_filter(self.ins, filter) + self.ins.bind((iface, type)) + _flush_fd(self.ins) + self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30) + self.outs = self.ins + self.outs.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2**30) + sa_ll = self.outs.getsockname() + if sa_ll[3] in conf.l2types: + self.LL = conf.l2types[sa_ll[3]] + elif sa_ll[1] in conf.l3types: + self.LL = conf.l3types[sa_ll[1]] + else: + self.LL = conf.default_l2 + warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using %s" % (sa_ll[0],sa_ll[1],sa_ll[3],self.LL.name)) + + def recv(self, x=MTU): + pkt, sa_ll = self.ins.recvfrom(x) + if sa_ll[2] == socket.PACKET_OUTGOING: + return None + try: + q = self.LL(pkt) + except KeyboardInterrupt: + raise + except: + if conf.debug_dissector: + raise + q = conf.raw_layer(pkt) + q.time = get_last_packet_timestamp(self.ins) + return q + + +class L2ListenSocket(SuperSocket): + desc = "read packets at layer 2 using Linux PF_PACKET sockets" + def __init__(self, iface = None, type = ETH_P_ALL, promisc=None, filter=None, nofilter=0): + self.type = type + self.outs = None + self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) + self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0) + if iface is not None: + self.ins.bind((iface, type)) + if not nofilter: + if conf.except_filter: + if filter: + filter = "(%s) and not (%s)" % (filter, conf.except_filter) + else: + filter = "not (%s)" % conf.except_filter + if filter is not None: + attach_filter(self.ins, filter) + if promisc is None: + promisc = conf.sniff_promisc + self.promisc = promisc + if iface is None: + self.iff = get_if_list() + else: + if iface.__class__ is list: + self.iff = iface + else: + self.iff = [iface] + if self.promisc: + for i in self.iff: + set_promisc(self.ins, i) + _flush_fd(self.ins) + self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30) + def close(self): + if self.promisc: + for i in self.iff: + set_promisc(self.ins, i, 0) + SuperSocket.close(self) + + def recv(self, x=MTU): + pkt, sa_ll = self.ins.recvfrom(x) + if sa_ll[3] in conf.l2types : + cls = conf.l2types[sa_ll[3]] + elif sa_ll[1] in conf.l3types: + cls = conf.l3types[sa_ll[1]] + else: + cls = conf.default_l2 + warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using %s" % (sa_ll[0],sa_ll[1],sa_ll[3],cls.name)) + + try: + pkt = cls(pkt) + except KeyboardInterrupt: + raise + except: + if conf.debug_dissector: + raise + pkt = conf.raw_layer(pkt) + pkt.time = get_last_packet_timestamp(self.ins) + return pkt + + def send(self, x): + raise Scapy_Exception("Can't send anything with L2ListenSocket") + + +conf.L3socket = L3PacketSocket +conf.L2socket = L2Socket +conf.L2listen = L2ListenSocket + +conf.iface = get_working_if() diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/arch/pcapdnet.py b/scripts/external_libs/scapy-python3-0.18/scapy/arch/pcapdnet.py new file mode 100644 index 00000000..a2e8aa59 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/arch/pcapdnet.py @@ -0,0 +1,565 @@ +## 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 + +""" +Packet sending and receiving with libdnet and libpcap/WinPcap. +""" + +import time,struct,sys,socket +if not sys.platform.startswith("win"): + from fcntl import ioctl +from scapy.data import * +from scapy.config import conf +from scapy.utils import warning +from scapy.supersocket import SuperSocket +from scapy.error import Scapy_Exception +import scapy.arch + +if conf.use_dnet: + try: + from .cdnet import * + except OSError as e: + if conf.interactive: + log_loading.error("Unable to import libdnet library: %s" % e) + conf.use_dnet = False + else: + raise + +if conf.use_winpcapy: + try: + from .winpcapy import * + def winpcapy_get_if_list(): + err = create_string_buffer(PCAP_ERRBUF_SIZE) + devs = POINTER(pcap_if_t)() + ret = [] + if pcap_findalldevs(byref(devs), err) < 0: + return ret + try: + p = devs + while p: + ret.append(p.contents.name.decode('ascii')) + p = p.contents.next + return ret + finally: + pcap_freealldevs(devs) + + except OSError as e: + if conf.interactive: + log_loading.error("Unable to import libpcap library: %s" % e) + conf.use_winpcapy = False + else: + raise + + # From BSD net/bpf.h + #BIOCIMMEDIATE=0x80044270 + BIOCIMMEDIATE=-2147204496 + + class PcapTimeoutElapsed(Scapy_Exception): + pass + +if conf.use_netifaces: + try: + import netifaces + except ImportError as e: + log_loading.warning("Could not load module netifaces: %s" % e) + conf.use_netifaces = False + +if conf.use_netifaces: + def get_if_raw_hwaddr(iff): + if iff == scapy.arch.LOOPBACK_NAME: + return (772, '\x00'*6) + try: + s = netifaces.ifaddresses(iff)[netifaces.AF_LINK][0]['addr'] + return struct.pack('BBBBBB', *[ int(i, 16) for i in s.split(':') ]) + except: + raise Scapy_Exception("Error in attempting to get hw address for interface [%s]" % iff) + return l + def get_if_raw_addr(ifname): + try: + s = netifaces.ifaddresses(ifname)[netifaces.AF_INET][0]['addr'] + return socket.inet_aton(s) + except Exception as e: + return None + def get_if_list(): + #return [ i[1] for i in socket.if_nameindex() ] + return netifaces.interfaces() + def in6_getifaddr(): + """ + Returns a list of 3-tuples of the form (addr, scope, iface) where + 'addr' is the address of scope 'scope' associated to the interface + 'ifcace'. + + This is the list of all addresses of all interfaces available on + the system. + """ + + ret = [] + interfaces = get_if_list() + for i in interfaces: + addrs = netifaces.ifaddresses(i) + if netifaces.AF_INET6 not in addrs: + continue + for a in addrs[netifaces.AF_INET6]: + addr = a['addr'].split('%')[0] + scope = scapy.utils6.in6_getscope(addr) + ret.append((addr, scope, i)) + return ret +elif conf.use_winpcapy: + def get_if_raw_hwaddr(iff): + err = create_string_buffer(PCAP_ERRBUF_SIZE) + devs = POINTER(pcap_if_t)() + ret = b"\0\0\0\0\0\0" + if pcap_findalldevs(byref(devs), err) < 0: + return ret + try: + p = devs + while p: + if p.contents.name.endswith(iff.encode('ascii')): + a = p.contents.addresses + while a: + if hasattr(socket, 'AF_LINK') and a.contents.addr.contents.sa_family == socket.AF_LINK: + ap = a.contents.addr + val = cast(ap, POINTER(sockaddr_dl)) + ret = bytes(val.contents.sdl_data[ val.contents.sdl_nlen : val.contents.sdl_nlen + val.contents.sdl_alen ]) + a = a.contents.next + break + p = p.contents.next + return ret + finally: + pcap_freealldevs(devs) + def get_if_raw_addr(iff): + err = create_string_buffer(PCAP_ERRBUF_SIZE) + devs = POINTER(pcap_if_t)() + ret = b"\0\0\0\0" + if pcap_findalldevs(byref(devs), err) < 0: + return ret + try: + p = devs + while p: + if p.contents.name.endswith(iff.encode('ascii')): + a = p.contents.addresses + while a: + if a.contents.addr.contents.sa_family == socket.AF_INET: + ap = a.contents.addr + val = cast(ap, POINTER(sockaddr_in)) + ret = bytes(val.contents.sin_addr[:4]) + a = a.contents.next + break + p = p.contents.next + return ret + finally: + pcap_freealldevs(devs) + get_if_list = winpcapy_get_if_list + def in6_getifaddr(): + err = create_string_buffer(PCAP_ERRBUF_SIZE) + devs = POINTER(pcap_if_t)() + ret = [] + if pcap_findalldevs(byref(devs), err) < 0: + return ret + try: + p = devs + ret = [] + while p: + a = p.contents.addresses + while a: + if a.contents.addr.contents.sa_family == socket.AF_INET6: + ap = a.contents.addr + val = cast(ap, POINTER(sockaddr_in6)) + addr = socket.inet_ntop(socket.AF_INET6, bytes(val.contents.sin6_addr[:])) + scope = scapy.utils6.in6_getscope(addr) + ret.append((addr, scope, p.contents.name.decode('ascii'))) + a = a.contents.next + p = p.contents.next + return ret + finally: + pcap_freealldevs(devs) + +elif conf.use_dnet: + intf = dnet_intf() + def get_if_raw_hwaddr(iff): + return intf.get(iff)['link_addr'] + def get_if_raw_addr(iff): + return intf.get(iff)['addr'] + def get_if_list(): + return intf.names + def in6_getifaddr(): + ret = [] + for i in get_if_list(): + for a in intf.get(i)['addr6']: + addr = socket.inet_ntop(socket.AF_INET6, a) + scope = scapy.utils6.in6_getscope(addr) + ret.append((addr, scope, i)) + return ret + +else: + log_loading.warning("No known method to get ip and hw address for interfaces") + def get_if_raw_hwaddr(iff): + "dummy" + return b"\0\0\0\0\0\0" + def get_if_raw_addr(iff): + "dummy" + return b"\0\0\0\0" + def get_if_list(): + "dummy" + return [] + def in6_getifaddr(): + return [] + +if conf.use_winpcapy: + from ctypes import POINTER, byref, create_string_buffer + class _PcapWrapper_pypcap: + def __init__(self, device, snaplen, promisc, to_ms): + self.errbuf = create_string_buffer(PCAP_ERRBUF_SIZE) + self.iface = create_string_buffer(device.encode('ascii')) + self.pcap = pcap_open_live(self.iface, snaplen, promisc, to_ms, self.errbuf) + self.header = POINTER(pcap_pkthdr)() + self.pkt_data = POINTER(c_ubyte)() + self.bpf_program = bpf_program() + def next(self): + c = pcap_next_ex(self.pcap, byref(self.header), byref(self.pkt_data)) + if not c > 0: + return + ts = self.header.contents.ts.tv_sec + #pkt = "".join([ chr(i) for i in self.pkt_data[:self.header.contents.len] ]) + pkt = bytes(self.pkt_data[:self.header.contents.len]) + return ts, pkt + def datalink(self): + return pcap_datalink(self.pcap) + def fileno(self): + if sys.platform.startswith("win"): + error("Cannot get selectable PCAP fd on Windows") + return 0 + return pcap_get_selectable_fd(self.pcap) + def setfilter(self, f): + filter_exp = create_string_buffer(f.encode('ascii')) + if pcap_compile(self.pcap, byref(self.bpf_program), filter_exp, 0, -1) == -1: + error("Could not compile filter expression %s" % f) + return False + else: + if pcap_setfilter(self.pcap, byref(self.bpf_program)) == -1: + error("Could not install filter %s" % f) + return False + return True + def setnonblock(self, i): + pcap_setnonblock(self.pcap, i, self.errbuf) + def send(self, x): + pcap_sendpacket(self.pcap, x, len(x)) + def close(self): + pcap_close(self.pcap) + open_pcap = lambda *args,**kargs: _PcapWrapper_pypcap(*args,**kargs) + class PcapTimeoutElapsed(Scapy_Exception): + pass + + class L2pcapListenSocket(SuperSocket): + desc = "read packets at layer 2 using libpcap" + def __init__(self, iface = None, type = ETH_P_ALL, promisc=None, filter=None): + self.type = type + self.outs = None + self.iface = iface + if iface is None: + iface = conf.iface + if promisc is None: + promisc = conf.sniff_promisc + self.promisc = promisc + self.ins = open_pcap(iface, 1600, self.promisc, 100) + try: + ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1)) + except: + pass + if type == ETH_P_ALL: # Do not apply any filter if Ethernet type is given + if conf.except_filter: + if filter: + filter = "(%s) and not (%s)" % (filter, conf.except_filter) + else: + filter = "not (%s)" % conf.except_filter + if filter: + self.ins.setfilter(filter) + + def close(self): + self.ins.close() + + def recv(self, x=MTU): + ll = self.ins.datalink() + if ll in conf.l2types: + cls = conf.l2types[ll] + else: + cls = conf.default_l2 + warning("Unable to guess datalink type (interface=%s linktype=%i). Using %s" % (self.iface, ll, cls.name)) + + pkt = None + while pkt is None: + pkt = self.ins.next() + if pkt is not None: + ts,pkt = pkt + if scapy.arch.WINDOWS and pkt is None: + raise PcapTimeoutElapsed + + try: + pkt = cls(pkt) + except KeyboardInterrupt: + raise + except: + if conf.debug_dissector: + raise + pkt = conf.raw_layer(pkt) + pkt.time = ts + return pkt + + def send(self, x): + raise Scapy_Exception("Can't send anything with L2pcapListenSocket") + + + conf.L2listen = L2pcapListenSocket + class L2pcapSocket(SuperSocket): + desc = "read/write packets at layer 2 using only libpcap" + def __init__(self, iface = None, type = ETH_P_ALL, filter=None, nofilter=0): + if iface is None: + iface = conf.iface + self.iface = iface + self.ins = open_pcap(iface, 1600, 0, 100) + try: + ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1)) + except: + pass + if nofilter: + if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap + filter = "ether proto %i" % type + else: + filter = None + else: + if conf.except_filter: + if filter: + filter = "(%s) and not (%s)" % (filter, conf.except_filter) + else: + filter = "not (%s)" % conf.except_filter + if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap + if filter: + filter = "(ether proto %i) and (%s)" % (type,filter) + else: + filter = "ether proto %i" % type + if filter: + self.ins.setfilter(filter) + def send(self, x): + sx = bytes(x) + if hasattr(x, "sent_time"): + x.sent_time = time.time() + return self.ins.send(sx) + + def recv(self,x=MTU): + ll = self.ins.datalink() + if ll in conf.l2types: + cls = conf.l2types[ll] + else: + cls = conf.default_l2 + warning("Unable to guess datalink type (interface=%s linktype=%i). Using %s" % (self.iface, ll, cls.name)) + + pkt = self.ins.next() + if pkt is not None: + ts,pkt = pkt + if pkt is None: + return + + try: + pkt = cls(pkt) + except KeyboardInterrupt: + raise + except: + if conf.debug_dissector: + raise + pkt = conf.raw_layer(pkt) + pkt.time = ts + return pkt + + def nonblock_recv(self): + self.ins.setnonblock(1) + p = self.recv(MTU) + self.ins.setnonblock(0) + return p + + def close(self): + if hasattr(self, "ins"): + self.ins.close() + if hasattr(self, "outs"): + self.outs.close() + + class L3pcapSocket(L2pcapSocket): + #def __init__(self, iface = None, type = ETH_P_ALL, filter=None, nofilter=0): + # L2pcapSocket.__init__(self, iface, type, filter, nofilter) + def recv(self, x = MTU): + r = L2pcapSocket.recv(self, x) + if r: + return r.payload + else: + return + def send(self, x): + cls = conf.l2types[1] + sx = bytes(cls()/x) + if hasattr(x, "sent_time"): + x.sent_time = time.time() + return self.ins.send(sx) + conf.L2socket=L2pcapSocket + conf.L3socket=L3pcapSocket + +if conf.use_winpcapy and conf.use_dnet: + class L3dnetSocket(SuperSocket): + desc = "read/write packets at layer 3 using libdnet and libpcap" + def __init__(self, type = ETH_P_ALL, filter=None, promisc=None, iface=None, nofilter=0): + self.iflist = {} + self.intf = dnet_intf() + if iface is None: + iface = conf.iface + self.iface = iface + self.ins = open_pcap(iface, 1600, 0, 100) + try: + ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1)) + except: + pass + if nofilter: + if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap + filter = "ether proto %i" % type + else: + filter = None + else: + if conf.except_filter: + if filter: + filter = "(%s) and not (%s)" % (filter, conf.except_filter) + else: + filter = "not (%s)" % conf.except_filter + if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap + if filter: + filter = "(ether proto %i) and (%s)" % (type,filter) + else: + filter = "ether proto %i" % type + if filter: + self.ins.setfilter(filter) + def send(self, x): + iff,a,gw = x.route() + if iff is None: + iff = conf.iface + ifs,cls = self.iflist.get(iff,(None,None)) + if ifs is None: + iftype = self.intf.get(iff)["type"] + if iftype == INTF_TYPE_ETH: + try: + cls = conf.l2types[1] + except KeyError: + warning("Unable to find Ethernet class. Using nothing") + ifs = dnet_eth(iff) + else: + ifs = dnet_ip() + self.iflist[iff] = ifs,cls + if cls is None: + #sx = str(x) + sx = bytes(x) + else: + sx = bytes(cls()/x) + x.sent_time = time.time() + ifs.send(sx) + def recv(self,x=MTU): + ll = self.ins.datalink() + if ll in conf.l2types: + cls = conf.l2types[ll] + else: + cls = conf.default_l2 + warning("Unable to guess datalink type (interface=%s linktype=%i). Using %s" % (self.iface, ll, cls.name)) + + pkt = self.ins.next() + if pkt is not None: + ts,pkt = pkt + if pkt is None: + return + + try: + pkt = cls(pkt) + except KeyboardInterrupt: + raise + except: + if conf.debug_dissector: + raise + pkt = conf.raw_layer(pkt) + pkt.time = ts + return pkt.payload + + def nonblock_recv(self): + self.ins.setnonblock(1) + p = self.recv() + self.ins.setnonblock(0) + return p + + def close(self): + if hasattr(self, "ins"): + self.ins.close() + if hasattr(self, "outs"): + self.outs.close() + + class L2dnetSocket(SuperSocket): + desc = "read/write packets at layer 2 using libdnet and libpcap" + def __init__(self, iface = None, type = ETH_P_ALL, filter=None, nofilter=0): + if iface is None: + iface = conf.iface + self.iface = iface + self.ins = open_pcap(iface, 1600, 0, 100) + try: + ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1)) + except: + pass + if nofilter: + if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap + filter = "ether proto %i" % type + else: + filter = None + else: + if conf.except_filter: + if filter: + filter = "(%s) and not (%s)" % (filter, conf.except_filter) + else: + filter = "not (%s)" % conf.except_filter + if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap + if filter: + filter = "(ether proto %i) and (%s)" % (type,filter) + else: + filter = "ether proto %i" % type + if filter: + self.ins.setfilter(filter) + self.outs = dnet_eth(iface) + def recv(self,x=MTU): + ll = self.ins.datalink() + if ll in conf.l2types: + cls = conf.l2types[ll] + else: + cls = conf.default_l2 + warning("Unable to guess datalink type (interface=%s linktype=%i). Using %s" % (self.iface, ll, cls.name)) + + pkt = self.ins.next() + if pkt is not None: + ts,pkt = pkt + if pkt is None: + return + + try: + pkt = cls(pkt) + except KeyboardInterrupt: + raise + except: + if conf.debug_dissector: + raise + pkt = conf.raw_layer(pkt) + pkt.time = ts + return pkt + + def nonblock_recv(self): + self.ins.setnonblock(1) + p = self.recv(MTU) + self.ins.setnonblock(0) + return p + + def close(self): + if hasattr(self, "ins"): + self.ins.close() + if hasattr(self, "outs"): + self.outs.close() + + conf.L3socket=L3dnetSocket + conf.L2socket=L2dnetSocket diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/arch/solaris.py b/scripts/external_libs/scapy-python3-0.18/scapy/arch/solaris.py new file mode 100644 index 00000000..3117076a --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/arch/solaris.py @@ -0,0 +1,16 @@ +## 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 + +""" +Customization for the Solaris operation system. +""" + +# IPPROTO_GRE is missing on Solaris +import socket +socket.IPPROTO_GRE = 47 + +LOOPBACK_NAME="lo0" + +from unix import * diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/arch/unix.py b/scripts/external_libs/scapy-python3-0.18/scapy/arch/unix.py new file mode 100644 index 00000000..43e694b5 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/arch/unix.py @@ -0,0 +1,168 @@ +## 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 + +""" +Common customizations for all Unix-like operating systems other than Linux +""" + +import sys,os,struct,socket,time +from subprocess import check_output +from fcntl import ioctl +from scapy.error import warning +import scapy.config +import scapy.utils +import scapy.utils6 +import scapy.arch + +scapy.config.conf.use_winpcapy = True +scapy.config.conf.use_netifaces = True +scapy.config.conf.use_dnet = True +from .pcapdnet import * + + + +################## +## Routes stuff ## +################## + + +def read_routes(): + if scapy.arch.SOLARIS: + f=check_output(["netstat", "-rvn"], universal_newlines = True) # -f inet + elif scapy.arch.FREEBSD: + f=check_output(["netstat", "-rnW"], universal_newlines = True) # -W to handle long interface names + else: + f=check_output(["netstat", "-rn"], universal_newlines = True) # -f inet + ok = False + routes = [] + pending_if = [] + for l in f.split('\n'): + l = l.strip() + if l.find("----") >= 0: # a separation line + continue + if not ok: + if_index = [ l.split().index(i) for i in ['Iface', 'Netif', 'Interface', 'Device'] if i in l.split()] + if if_index: + ok = True + if_index = if_index[0] + continue + if not l: + break + if scapy.arch.SOLARIS: + lspl = l.split() + if len(lspl) == 10: + dest,mask,gw,netif,mxfrg,rtt,ref,flg = lspl[:8] + else: # missing interface + dest,mask,gw,mxfrg,rtt,ref,flg = lspl[:7] + netif=None + else: + rt = l.split() + dest,gw,flg = rt[:3] + netif = rt[if_index] + if flg.find("Lc") >= 0: + continue + if dest == "default": + dest = 0 + netmask = 0 + else: + if scapy.arch.SOLARIS: + netmask = scapy.utils.atol(mask) + elif "/" in dest: + dest,netmask = dest.split("/") + netmask = scapy.utils.itom(int(netmask)) + else: + netmask = scapy.utils.itom((dest.count(".") + 1) * 8) + dest += ".0"*(3-dest.count(".")) + dest = scapy.utils.atol(dest) + if not "G" in flg: + gw = '0.0.0.0' + if netif is not None: + ifaddr = scapy.arch.get_if_addr(netif) + routes.append((dest,netmask,gw,netif,ifaddr)) + else: + pending_if.append((dest,netmask,gw)) + + # On Solaris, netstat does not provide output interfaces for some routes + # We need to parse completely the routing table to route their gw and + # know their output interface + for dest,netmask,gw in pending_if: + gw_l = scapy.utils.atol(gw) + max_rtmask,gw_if,gw_if_addr, = 0,None,None + for rtdst,rtmask,_,rtif,rtaddr in routes[:]: + if gw_l & rtmask == rtdst: + if rtmask >= max_rtmask: + max_rtmask = rtmask + gw_if = rtif + gw_if_addr = rtaddr + if gw_if: + routes.append((dest,netmask,gw,gw_if,gw_if_addr)) + else: + warning("Did not find output interface to reach gateway %s" % gw) + + return routes + +############ +### IPv6 ### +############ + +def read_routes6(): + f = os.popen("netstat -rn -f inet6") + ok = False + mtu_present = False + prio_present = False + routes = [] + lifaddr = in6_getifaddr() + for l in f.readlines(): + if not l: + break + l = l.strip() + if not ok: + if l.find("Destination") >= 0: + ok = 1 + mtu_present = l.find("Mtu") >= 0 + prio_present = l.find("Prio") >= 0 + continue + # gv 12/12/06: under debugging + if scapy.arch.NETBSD or scapy.arch.OPENBSD: + lspl = l.split() + d,nh,fl = lspl[:3] + dev = lspl[5+mtu_present+prio_present] + expire = None + else: # FREEBSD or DARWIN + d,nh,fl,dev = l.split()[:4] + if [ x for x in lifaddr if x[2] == dev] == []: + continue + if 'L' in fl: # drop MAC addresses + continue + + if 'link' in nh: + nh = '::' + + cset = [] # candidate set (possible source addresses) + dp = 128 + if d == 'default': + d = '::' + dp = 0 + if '/' in d: + d,dp = d.split("/") + dp = int(dp) + if '%' in d: + d,dev = d.split('%') + if '%' in nh: + nh,dev = nh.split('%') + if scapy.arch.LOOPBACK_NAME in dev: + if d == '::' and dp == 96: #Do not use ::/96 deprecated IPV4 mapping address + continue + cset = ['::1'] + nh = '::' + else: + devaddrs = [ x for x in lifaddr if x[2] == dev ] + cset = scapy.utils6.construct_source_candidate_set(d, dp, devaddrs, scapy.arch.LOOPBACK_NAME) + + if len(cset) != 0: + routes.append((d, dp, nh, dev, cset)) + + f.close() + return routes diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/arch/windows/__init__.py b/scripts/external_libs/scapy-python3-0.18/scapy/arch/windows/__init__.py new file mode 100644 index 00000000..3bd1ade3 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/arch/windows/__init__.py @@ -0,0 +1,501 @@ +## 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 + +""" +Customizations needed to support Microsoft Windows. +""" + +import os,re,sys,socket,time, itertools +import subprocess as sp +from glob import glob +from scapy.config import conf,ConfClass +from scapy.error import Scapy_Exception,log_loading,log_runtime +from scapy.utils import atol, itom, inet_aton, inet_ntoa, PcapReader +from scapy.base_classes import Gen, Net, SetGen +import scapy.plist as plist +from scapy.sendrecv import debug, srp1 +from scapy.layers.l2 import Ether, ARP +from scapy.data import MTU, ETHER_BROADCAST, ETH_P_ARP + +conf.use_winpcapy = True +from scapy.arch import pcapdnet +from scapy.arch.pcapdnet import * + +LOOPBACK_NAME="lo0" +WINDOWS = True + + +def _where(filename, dirs=[], env="PATH"): + """Find file in current dir or system path""" + if not isinstance(dirs, list): + dirs = [dirs] + if glob(filename): + return filename + paths = [os.curdir] + os.environ[env].split(os.path.pathsep) + dirs + for path in paths: + for match in glob(os.path.join(path, filename)): + if match: + return os.path.normpath(match) + raise IOError("File not found: %s" % filename) + +def win_find_exe(filename, installsubdir=None, env="ProgramFiles"): + """Find executable in current dir, system path or given ProgramFiles subdir""" + for fn in [filename, filename+".exe"]: + try: + if installsubdir is None: + path = _where(fn) + else: + path = _where(fn, dirs=[os.path.join(os.environ[env], installsubdir)]) + except IOError: + path = filename + else: + break + return path + + +class WinProgPath(ConfClass): + _default = "" + # We try some magic to find the appropriate executables + pdfreader = win_find_exe("AcroRd32") + psreader = win_find_exe("gsview32.exe", "Ghostgum/gsview") + dot = win_find_exe("dot", "ATT/Graphviz/bin") + tcpdump = win_find_exe("windump") + tcpreplay = win_find_exe("tcpreplay") + display = _default + hexedit = win_find_exe("hexer") + wireshark = win_find_exe("wireshark", "wireshark") + +conf.prog = WinProgPath() + +class PcapNameNotFoundError(Scapy_Exception): + pass + +def get_windows_if_list(): + ps = sp.Popen(['powershell', 'Get-NetAdapter', '|', 'select Name, InterfaceIndex, InterfaceDescription, InterfaceGuid, MacAddress', '|', 'fl'], stdout = sp.PIPE) + stdout, stdin = ps.communicate(timeout = 10) + current_interface = None + interface_list = [] + for i in stdout.split(b'\r\n'): + if not i.strip(): + continue + if i.find(b':')<0: + continue + name, value = [ j.strip() for j in i.split(b':') ] + if name == b'Name': + if current_interface: + interface_list.append(current_interface) + current_interface = {} + current_interface['name'] = value.decode('ascii') + elif name == b'InterfaceIndex': + current_interface['win_index'] = int(value) + elif name == b'InterfaceDescription': + current_interface['description'] = value.decode('ascii') + elif name == b'InterfaceGuid': + current_interface['guid'] = value.decode('ascii') + elif name == b'MacAddress': + current_interface['mac'] = ':'.join([ j.decode('ascii') for j in value.split(b'-')]) + if current_interface: + interface_list.append(current_interface) + return interface_list + +class NetworkInterface(object): + """A network interface of your local host""" + + def __init__(self, data=None): + self.name = None + self.ip = None + self.mac = None + self.pcap_name = None + self.description = None + self.data = data + if data is not None: + self.update(data) + + def update(self, data): + """Update info about network interface according to given dnet dictionary""" + self.name = data["name"] + self.description = data['description'] + self.win_index = data['win_index'] + # Other attributes are optional + self._update_pcapdata() + try: + self.ip = socket.inet_ntoa(get_if_raw_addr(data['guid'])) + except (KeyError, AttributeError, NameError): + pass + try: + self.mac = data['mac'] + except KeyError: + pass + + def _update_pcapdata(self): + for i in winpcapy_get_if_list(): + if i.endswith(self.data['guid']): + self.pcap_name = i + return + + raise PcapNameNotFoundError + + def __repr__(self): + return "<%s: %s %s %s pcap_name=%s description=%s>" % (self.__class__.__name__, + self.name, self.ip, self.mac, self.pcap_name, self.description) + +from collections import UserDict + +class NetworkInterfaceDict(UserDict): + """Store information about network interfaces and convert between names""" + def load_from_powershell(self): + for i in get_windows_if_list(): + try: + interface = NetworkInterface(i) + self.data[interface.name] = interface + except (KeyError, PcapNameNotFoundError): + pass + if len(self.data) == 0: + log_loading.warning("No match between your pcap and windows network interfaces found. " + "You probably won't be able to send packets. " + "Deactivating unneeded interfaces and restarting Scapy might help." + "Check your winpcap and powershell installation, and access rights.") + + def pcap_name(self, devname): + """Return pcap device name for given Windows device name.""" + + try: + pcap_name = self.data[devname].pcap_name + except KeyError: + raise ValueError("Unknown network interface %r" % devname) + else: + return pcap_name + + def devname(self, pcap_name): + """Return Windows device name for given pcap device name.""" + + for devname, iface in self.items(): + if iface.pcap_name == pcap_name: + return iface.name + raise ValueError("Unknown pypcap network interface %r" % pcap_name) + + def devname_from_index(self, if_index): + """Return interface name from interface index""" + for devname, iface in self.items(): + if iface.win_index == if_index: + return iface.name + raise ValueError("Unknown network interface index %r" % if_index) + + def show(self, resolve_mac=True): + """Print list of available network interfaces in human readable form""" + + print("%s %s %s %s" % ("INDEX".ljust(5), "IFACE".ljust(35), "IP".ljust(15), "MAC")) + for iface_name in sorted(self.data.keys()): + dev = self.data[iface_name] + mac = dev.mac + if resolve_mac: + mac = conf.manufdb._resolve_MAC(mac) + print("%s %s %s %s" % (str(dev.win_index).ljust(5), str(dev.name).ljust(35), str(dev.ip).ljust(15), mac) ) + +ifaces = NetworkInterfaceDict() +ifaces.load_from_powershell() + +def pcap_name(devname): + """Return pypcap device name for given libdnet/Scapy device name""" + try: + pcap_name = ifaces.pcap_name(devname) + except ValueError: + # pcap.pcap() will choose a sensible default for sniffing if iface=None + pcap_name = None + return pcap_name + +def devname(pcap_name): + """Return libdnet/Scapy device name for given pypcap device name""" + return ifaces.devname(pcap_name) + +def devname_from_index(if_index): + """Return Windows adapter name for given Windows interface index""" + return ifaces.devname_from_index(if_index) + +def show_interfaces(resolve_mac=True): + """Print list of available network interfaces""" + return ifaces.show(resolve_mac) + +_orig_open_pcap = pcapdnet.open_pcap +pcapdnet.open_pcap = lambda iface,*args,**kargs: _orig_open_pcap(pcap_name(iface),*args,**kargs) + +_orig_get_if_raw_hwaddr = pcapdnet.get_if_raw_hwaddr +pcapdnet.get_if_raw_hwaddr = lambda iface,*args,**kargs: [ int(i, 16) for i in ifaces[iface].mac.split(':') ] +get_if_raw_hwaddr = pcapdnet.get_if_raw_hwaddr + +def read_routes(): + routes = [] + if_index = '(\d+)' + dest = '(\d+\.\d+\.\d+\.\d+)/(\d+)' + next_hop = '(\d+\.\d+\.\d+\.\d+)' + metric_pattern = "(\d+)" + delim = "\s+" # The columns are separated by whitespace + netstat_line = delim.join([if_index, dest, next_hop, metric_pattern]) + pattern = re.compile(netstat_line) + ps = sp.Popen(['powershell', 'Get-NetRoute', '-AddressFamily IPV4', '|', 'select ifIndex, DestinationPrefix, NextHop, RouteMetric'], stdout = sp.PIPE) + stdout, stdin = ps.communicate(timeout = 10) + for l in stdout.split(b'\r\n'): + match = re.search(pattern,l.decode('utf-8')) + if match: + try: + iface = devname_from_index(int(match.group(1))) + addr = ifaces[iface].ip + except: + continue + dest = atol(match.group(2)) + mask = itom(int(match.group(3))) + gw = match.group(4) + # try: + # intf = pcapdnet.dnet.intf().get_dst(pcapdnet.dnet.addr(type=2, addrtxt=dest)) + # except OSError: + # log_loading.warning("Building Scapy's routing table: Couldn't get outgoing interface for destination %s" % dest) + # continue + routes.append((dest, mask, gw, iface, addr)) + return routes + +def read_routes6(): + return [] + +try: + __IPYTHON__ +except NameError: + try: + import readline + console = readline.GetOutputFile() + except (ImportError, AttributeError): + log_loading.info("Could not get readline console. Will not interpret ANSI color codes.") + else: + conf.readfunc = readline.rl.readline + orig_stdout = sys.stdout + sys.stdout = console + +def sndrcv(pks, pkt, timeout = 2, inter = 0, verbose=None, chainCC=0, retry=0, multi=0): + if not isinstance(pkt, Gen): + pkt = SetGen(pkt) + + if verbose is None: + verbose = conf.verb + debug.recv = plist.PacketList([],"Unanswered") + debug.sent = plist.PacketList([],"Sent") + debug.match = plist.SndRcvList([]) + nbrecv=0 + ans = [] + # do it here to fix random fields, so that parent and child have the same + all_stimuli = tobesent = [p for p in pkt] + notans = len(tobesent) + + hsent={} + for i in tobesent: + h = i.hashret() + if h in hsent: + hsent[h].append(i) + else: + hsent[h] = [i] + if retry < 0: + retry = -retry + autostop=retry + else: + autostop=0 + + + while retry >= 0: + found=0 + + if timeout < 0: + timeout = None + + pid=1 + try: + if WINDOWS or pid == 0: + try: + try: + i = 0 + if verbose: + print("Begin emission:") + for p in tobesent: + pks.send(p) + i += 1 + time.sleep(inter) + if verbose: + print("Finished to send %i packets." % i) + except SystemExit: + pass + except KeyboardInterrupt: + pass + except: + log_runtime.exception("--- Error sending packets") + log_runtime.info("--- Error sending packets") + finally: + try: + sent_times = [p.sent_time for p in all_stimuli if p.sent_time] + except: + pass + if WINDOWS or pid > 0: + # Timeout starts after last packet is sent (as in Unix version) + if timeout: + stoptime = time.time()+timeout + else: + stoptime = 0 + remaintime = None + # inmask = [pks.ins.fd] + try: + try: + while 1: + if stoptime: + remaintime = stoptime-time.time() + if remaintime <= 0: + break + r = pks.recv(MTU) + if r is None: + continue + ok = 0 + h = r.hashret() + if h in hsent: + hlst = hsent[h] + for i in range(len(hlst)): + if r.answers(hlst[i]): + ans.append((hlst[i],r)) + if verbose > 1: + os.write(1, b"*") + ok = 1 + if not multi: + del(hlst[i]) + notans -= 1; + else: + if not hasattr(hlst[i], '_answered'): + notans -= 1; + hlst[i]._answered = 1; + break + if notans == 0 and not multi: + break + if not ok: + if verbose > 1: + os.write(1, b".") + nbrecv += 1 + if conf.debug_match: + debug.recv.append(r) + except KeyboardInterrupt: + if chainCC: + raise + finally: + if WINDOWS: + for p,t in zip(all_stimuli, sent_times): + p.sent_time = t + finally: + pass + + # remain = reduce(list.__add__, hsent.values(), []) + remain = list(itertools.chain(*[ i for i in hsent.values() ])) + + if multi: + #remain = filter(lambda p: not hasattr(p, '_answered'), remain); + remain = [ p for p in remain if not hasattr(p, '_answered')] + + if autostop and len(remain) > 0 and len(remain) != len(tobesent): + retry = autostop + + tobesent = remain + if len(tobesent) == 0: + break + retry -= 1 + + if conf.debug_match: + debug.sent=plist.PacketList(remain[:],"Sent") + debug.match=plist.SndRcvList(ans[:]) + + #clean the ans list to delete the field _answered + if (multi): + for s,r in ans: + if hasattr(s, '_answered'): + del(s._answered) + + if verbose: + print("\nReceived %i packets, got %i answers, remaining %i packets" % (nbrecv+len(ans), len(ans), notans)) + return plist.SndRcvList(ans),plist.PacketList(remain,"Unanswered") + + +import scapy.sendrecv +scapy.sendrecv.sndrcv = sndrcv + +def sniff(count=0, store=1, offline=None, prn = None, lfilter=None, L2socket=None, timeout=None, *arg, **karg): + """Sniff packets +sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2ListenSocket args) -> list of packets +Select interface to sniff by setting conf.iface. Use show_interfaces() to see interface names. + count: number of packets to capture. 0 means infinity + store: wether to store sniffed packets or discard them + prn: function to apply to each packet. If something is returned, + it is displayed. Ex: + ex: prn = lambda x: x.summary() +lfilter: python function applied to each packet to determine + if further action may be done + ex: lfilter = lambda x: x.haslayer(Padding) +offline: pcap file to read packets from, instead of sniffing them +timeout: stop sniffing after a given time (default: None) +L2socket: use the provided L2socket + """ + c = 0 + + if offline is None: + log_runtime.info('Sniffing on %s' % conf.iface) + if L2socket is None: + L2socket = conf.L2listen + s = L2socket(type=ETH_P_ALL, *arg, **karg) + else: + s = PcapReader(offline) + + lst = [] + if timeout is not None: + stoptime = time.time()+timeout + remain = None + while 1: + try: + if timeout is not None: + remain = stoptime-time.time() + if remain <= 0: + break + + try: + p = s.recv(MTU) + except PcapTimeoutElapsed: + continue + if p is None: + break + if lfilter and not lfilter(p): + continue + if store: + lst.append(p) + c += 1 + if prn: + r = prn(p) + if r is not None: + print(r) + if count > 0 and c >= count: + break + except KeyboardInterrupt: + break + s.close() + return plist.PacketList(lst,"Sniffed") + +import scapy.sendrecv +scapy.sendrecv.sniff = sniff + +# def get_if_list(): +# print('windows if_list') +# return sorted(ifaces.keys()) + +def get_working_if(): + try: + if 'Ethernet' in ifaces and ifaces['Ethernet'].ip != '0.0.0.0': + return 'Ethernet' + elif 'Wi-Fi' in ifaces and ifaces['Wi-Fi'].ip != '0.0.0.0': + return 'Wi-Fi' + elif len(ifaces) > 0: + return ifaces[list(ifaces.keys())[0]] + else: + return LOOPBACK_NAME + except: + return LOOPBACK_NAME + +conf.iface = get_working_if() diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/arch/winpcapy.py b/scripts/external_libs/scapy-python3-0.18/scapy/arch/winpcapy.py new file mode 100644 index 00000000..fc452a02 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/arch/winpcapy.py @@ -0,0 +1,739 @@ +#------------------------------------------------------------------------------- +# Name: winpcapy.py +# +# Author: Massimo Ciani +# +# Created: 01/09/2009 +# Copyright: (c) Massimo Ciani 2009 +# +#------------------------------------------------------------------------------- + + +from ctypes import * +from ctypes.util import find_library +import sys + +WIN32=False +HAVE_REMOTE=False + + +if sys.platform.startswith('win'): + WIN32=True + HAVE_REMOTE=True + +if WIN32: + SOCKET = c_uint + _lib=CDLL('wpcap.dll') +else: + SOCKET = c_int + _lib_name = find_library('pcap') + if not _lib_name: + raise OSError("Cannot fine libpcap.so library") + _lib=CDLL(_lib_name) + + + +## +## misc +## +u_short = c_ushort +bpf_int32 = c_int +u_int = c_int +bpf_u_int32 = u_int +pcap = c_void_p +pcap_dumper = c_void_p +u_char = c_ubyte +FILE = c_void_p +STRING = c_char_p + +class bpf_insn(Structure): + _fields_=[("code",c_ushort), + ("jt",c_ubyte), + ("jf",c_ubyte), + ("k",bpf_u_int32)] + +class bpf_program(Structure): + pass +bpf_program._fields_ = [('bf_len', u_int), + ('bf_insns', POINTER(bpf_insn))] + +class bpf_version(Structure): + _fields_=[("bv_major",c_ushort), + ("bv_minor",c_ushort)] + + +class timeval(Structure): + pass +timeval._fields_ = [('tv_sec', c_long), + ('tv_usec', c_long)] + +## sockaddr is used by pcap_addr. +## For exapmle if sa_family==socket.AF_INET then we need cast +## with sockaddr_in +if WIN32: + class sockaddr(Structure): + _fields_ = [("sa_family", c_ushort), + ("sa_data",c_ubyte * 14)] + + class sockaddr_in(Structure): + _fields_ = [("sin_family", c_ushort), + ("sin_port", c_uint16), + ("sin_addr", 4 * c_ubyte)] + + class sockaddr_in6(Structure): + _fields_ = [("sin6_family", c_ushort), + ("sin6_port", c_uint16), + ("sin6_flowinfo", c_uint32), + ("sin6_addr", 16 * c_ubyte), + ("sin6_scope", c_uint32)] +else: + class sockaddr(Structure): + _fields_ = [("sa_len", c_ubyte), + ("sa_family",c_ubyte), + ("sa_data",c_ubyte * 14)] + + class sockaddr_in(Structure): + _fields_ = [("sin_len", c_ubyte), + ("sin_family", c_ubyte), + ("sin_port", c_uint16), + ("sin_addr", 4 * c_ubyte), + ("sin_zero", 8 * c_char)] + + class sockaddr_in6(Structure): + _fields_ = [("sin6_len", c_ubyte), + ("sin6_family", c_ubyte), + ("sin6_port", c_uint16), + ("sin6_flowinfo", c_uint32), + ("sin6_addr", 16 * c_ubyte), + ("sin6_scope", c_uint32)] + + class sockaddr_dl(Structure): + _fields_ = [("sdl_len", c_ubyte), + ("sdl_family", c_ubyte), + ("sdl_index", c_ushort), + ("sdl_type", c_ubyte), + ("sdl_nlen", c_ubyte), + ("sdl_alen", c_ubyte), + ("sdl_slen", c_ubyte), + ("sdl_data", 46 * c_ubyte)] +## +## END misc +## + +## +## Data Structures +## + +## struct pcap_file_header +## Header of a libpcap dump file. +class pcap_file_header(Structure): + _fields_ = [('magic', bpf_u_int32), + ('version_major', u_short), + ('version_minor', u_short), + ('thiszone', bpf_int32), + ('sigfigs', bpf_u_int32), + ('snaplen', bpf_u_int32), + ('linktype', bpf_u_int32)] + +## struct pcap_pkthdr +## Header of a packet in the dump file. +class pcap_pkthdr(Structure): + _fields_ = [('ts', timeval), + ('caplen', bpf_u_int32), + ('len', bpf_u_int32)] + +## struct pcap_stat +## Structure that keeps statistical values on an interface. +class pcap_stat(Structure): + pass +### _fields_ list in Structure is final. +### We need a temp list +_tmpList=[] +_tmpList.append(("ps_recv",c_uint)) +_tmpList.append(("ps_drop",c_uint)) +_tmpList.append(("ps_ifdrop",c_uint)) +if HAVE_REMOTE: + _tmpList.append(("ps_capt",c_uint)) + _tmpList.append(("ps_sent",c_uint)) + _tmpList.append(("ps_netdrop",c_uint)) +pcap_stat._fields_=_tmpList + +## struct pcap_addr +## Representation of an interface address, used by pcap_findalldevs(). +class pcap_addr(Structure): + pass +pcap_addr._fields_ = [('next', POINTER(pcap_addr)), + ('addr', POINTER(sockaddr)), + ('netmask', POINTER(sockaddr)), + ('broadaddr', POINTER(sockaddr)), + ('dstaddr', POINTER(sockaddr))] + +## struct pcap_if +## Item in a list of interfaces, used by pcap_findalldevs(). +class pcap_if(Structure): + pass +pcap_if._fields_ = [('next', POINTER(pcap_if)), + ('name', STRING), + ('description', STRING), + ('addresses', POINTER(pcap_addr)), + ('flags', bpf_u_int32)] + +## +## END Data Structures +## + +## +## Defines +## + +##define PCAP_VERSION_MAJOR 2 +# Major libpcap dump file version. +PCAP_VERSION_MAJOR = 2 +##define PCAP_VERSION_MINOR 4 +# Minor libpcap dump file version. +PCAP_VERSION_MINOR = 4 +##define PCAP_ERRBUF_SIZE 256 +# Size to use when allocating the buffer that contains the libpcap errors. +PCAP_ERRBUF_SIZE = 256 +##define PCAP_IF_LOOPBACK 0x00000001 +# interface is loopback +PCAP_IF_LOOPBACK = 1 +##define MODE_CAPT 0 +# Capture mode, to be used when calling pcap_setmode(). +MODE_CAPT = 0 +##define MODE_STAT 1 +# Statistical mode, to be used when calling pcap_setmode(). +MODE_STAT = 1 + +## +## END Defines +## + +## +## Typedefs +## + +#typedef int bpf_int32 (already defined) +# 32-bit integer +#typedef u_int bpf_u_int32 (already defined) +# 32-bit unsigned integer +#typedef struct pcap pcap_t +# Descriptor of an open capture instance. This structure is opaque to the user, that handles its content through the functions provided by wpcap.dll. +pcap_t = pcap +#typedef struct pcap_dumper pcap_dumper_t +# libpcap savefile descriptor. +pcap_dumper_t = pcap_dumper +#typedef struct pcap_if pcap_if_t +# Item in a list of interfaces, see pcap_if. +pcap_if_t = pcap_if +#typedef struct pcap_addr pcap_addr_t +# Representation of an interface address, see pcap_addr. +pcap_addr_t = pcap_addr + +## +## END Typedefs +## + + + + + +# values for enumeration 'pcap_direction_t' +#pcap_direction_t = c_int # enum + +## +## Unix-compatible Functions +## These functions are part of the libpcap library, and therefore work both on Windows and on Linux. +## + +#typedef void(* pcap_handler )(u_char *user, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data) +# Prototype of the callback function that receives the packets. +## This one is defined from programmer +pcap_handler=CFUNCTYPE(None,POINTER(c_ubyte),POINTER(pcap_pkthdr),POINTER(c_ubyte)) + +#pcap_t * pcap_open_live (const char *device, int snaplen, int promisc, int to_ms, char *ebuf) +# Open a live capture from the network. +pcap_open_live = _lib.pcap_open_live +pcap_open_live.restype = POINTER(pcap_t) +pcap_open_live.argtypes = [STRING, c_int, c_int, c_int, STRING] + +#pcap_t * pcap_open_dead (int linktype, int snaplen) +# Create a pcap_t structure without starting a capture. +pcap_open_dead = _lib.pcap_open_dead +pcap_open_dead.restype = POINTER(pcap_t) +pcap_open_dead.argtypes = [c_int, c_int] + +#pcap_t * pcap_open_offline (const char *fname, char *errbuf) +# Open a savefile in the tcpdump/libpcap format to read packets. +pcap_open_offline = _lib.pcap_open_offline +pcap_open_offline.restype = POINTER(pcap_t) +pcap_open_offline.argtypes = [STRING, STRING] + +#pcap_dumper_t * pcap_dump_open (pcap_t *p, const char *fname) +# Open a file to write packets. +pcap_dump_open = _lib.pcap_dump_open +pcap_dump_open.restype = POINTER(pcap_dumper_t) +pcap_dump_open.argtypes = [POINTER(pcap_t), STRING] + +#int pcap_setnonblock (pcap_t *p, int nonblock, char *errbuf) +# Switch between blocking and nonblocking mode. +pcap_setnonblock = _lib.pcap_setnonblock +pcap_setnonblock.restype = c_int +pcap_setnonblock.argtypes = [POINTER(pcap_t), c_int, STRING] + +#int pcap_getnonblock (pcap_t *p, char *errbuf) +# Get the "non-blocking" state of an interface. +pcap_getnonblock = _lib.pcap_getnonblock +pcap_getnonblock.restype = c_int +pcap_getnonblock.argtypes = [POINTER(pcap_t), STRING] + +#int pcap_findalldevs (pcap_if_t **alldevsp, char *errbuf) +# Construct a list of network devices that can be opened with pcap_open_live(). +pcap_findalldevs = _lib.pcap_findalldevs +pcap_findalldevs.restype = c_int +pcap_findalldevs.argtypes = [POINTER(POINTER(pcap_if_t)), STRING] + +#void pcap_freealldevs (pcap_if_t *alldevsp) +# Free an interface list returned by pcap_findalldevs(). +pcap_freealldevs = _lib.pcap_freealldevs +pcap_freealldevs.restype = None +pcap_freealldevs.argtypes = [POINTER(pcap_if_t)] + +#char * pcap_lookupdev (char *errbuf) +# Return the first valid device in the system. +pcap_lookupdev = _lib.pcap_lookupdev +pcap_lookupdev.restype = STRING +pcap_lookupdev.argtypes = [STRING] + +#int pcap_lookupnet (const char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, char *errbuf) +# Return the subnet and netmask of an interface. +pcap_lookupnet = _lib.pcap_lookupnet +pcap_lookupnet.restype = c_int +pcap_lookupnet.argtypes = [STRING, POINTER(bpf_u_int32), POINTER(bpf_u_int32), STRING] + +#int pcap_dispatch (pcap_t *p, int cnt, pcap_handler callback, u_char *user) +# Collect a group of packets. +pcap_dispatch = _lib.pcap_dispatch +pcap_dispatch.restype = c_int +pcap_dispatch.argtypes = [POINTER(pcap_t), c_int, pcap_handler, POINTER(u_char)] + +#int pcap_loop (pcap_t *p, int cnt, pcap_handler callback, u_char *user) +# Collect a group of packets. +pcap_loop = _lib.pcap_loop +pcap_loop.restype = c_int +pcap_loop.argtypes = [POINTER(pcap_t), c_int, pcap_handler, POINTER(u_char)] + +#u_char * pcap_next (pcap_t *p, struct pcap_pkthdr *h) +# Return the next available packet. +pcap_next = _lib.pcap_next +pcap_next.restype = POINTER(u_char) +pcap_next.argtypes = [POINTER(pcap_t), POINTER(pcap_pkthdr)] + +#int pcap_next_ex (pcap_t *p, struct pcap_pkthdr **pkt_header, const u_char **pkt_data) +# Read a packet from an interface or from an offline capture. +pcap_next_ex = _lib.pcap_next_ex +pcap_next_ex.restype = c_int +pcap_next_ex.argtypes = [POINTER(pcap_t), POINTER(POINTER(pcap_pkthdr)), POINTER(POINTER(u_char))] + +#void pcap_breakloop (pcap_t *) +# set a flag that will force pcap_dispatch() or pcap_loop() to return rather than looping. +pcap_breakloop = _lib.pcap_breakloop +pcap_breakloop.restype = None +pcap_breakloop.argtypes = [POINTER(pcap_t)] + +#int pcap_sendpacket (pcap_t *p, u_char *buf, int size) +# Send a raw packet. +pcap_sendpacket = _lib.pcap_sendpacket +pcap_sendpacket.restype = c_int +#pcap_sendpacket.argtypes = [POINTER(pcap_t), POINTER(u_char), c_int] +pcap_sendpacket.argtypes = [POINTER(pcap_t), c_void_p, c_int] + +#void pcap_dump (u_char *user, const struct pcap_pkthdr *h, const u_char *sp) +# Save a packet to disk. +pcap_dump = _lib.pcap_dump +pcap_dump.restype = None +pcap_dump.argtypes = [POINTER(pcap_dumper_t), POINTER(pcap_pkthdr), POINTER(u_char)] + +#long pcap_dump_ftell (pcap_dumper_t *) +# Return the file position for a "savefile". +pcap_dump_ftell = _lib.pcap_dump_ftell +pcap_dump_ftell.restype = c_long +pcap_dump_ftell.argtypes = [POINTER(pcap_dumper_t)] + +#int pcap_compile (pcap_t *p, struct bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask) +# Compile a packet filter, converting an high level filtering expression (see Filtering expression syntax) in a program that can be interpreted by the kernel-level filtering engine. +pcap_compile = _lib.pcap_compile +pcap_compile.restype = c_int +pcap_compile.argtypes = [POINTER(pcap_t), POINTER(bpf_program), STRING, c_int, bpf_u_int32] + +#int pcap_compile_nopcap (int snaplen_arg, int linktype_arg, struct bpf_program *program, char *buf, int optimize, bpf_u_int32 mask) +# Compile a packet filter without the need of opening an adapter. This function converts an high level filtering expression (see Filtering expression syntax) in a program that can be interpreted by the kernel-level filtering engine. +pcap_compile_nopcap = _lib.pcap_compile_nopcap +pcap_compile_nopcap.restype = c_int +pcap_compile_nopcap.argtypes = [c_int, c_int, POINTER(bpf_program), STRING, c_int, bpf_u_int32] + +#int pcap_setfilter (pcap_t *p, struct bpf_program *fp) +# Associate a filter to a capture. +pcap_setfilter = _lib.pcap_setfilter +pcap_setfilter.restype = c_int +pcap_setfilter.argtypes = [POINTER(pcap_t), POINTER(bpf_program)] + +#void pcap_freecode (struct bpf_program *fp) +# Free a filter. +pcap_freecode = _lib.pcap_freecode +pcap_freecode.restype = None +pcap_freecode.argtypes = [POINTER(bpf_program)] + +#int pcap_datalink (pcap_t *p) +# Return the link layer of an adapter. +pcap_datalink = _lib.pcap_datalink +pcap_datalink.restype = c_int +pcap_datalink.argtypes = [POINTER(pcap_t)] + +#int pcap_list_datalinks (pcap_t *p, int **dlt_buf) +# list datalinks +pcap_list_datalinks = _lib.pcap_list_datalinks +pcap_list_datalinks.restype = c_int +#pcap_list_datalinks.argtypes = [POINTER(pcap_t), POINTER(POINTER(c_int))] + +#int pcap_set_datalink (pcap_t *p, int dlt) +# Set the current data link type of the pcap descriptor to the type specified by dlt. -1 is returned on failure. +pcap_set_datalink = _lib.pcap_set_datalink +pcap_set_datalink.restype = c_int +pcap_set_datalink.argtypes = [POINTER(pcap_t), c_int] + +#int pcap_datalink_name_to_val (const char *name) +# Translates a data link type name, which is a DLT_ name with the DLT_ removed, to the corresponding data link type value. The translation is case-insensitive. -1 is returned on failure. +pcap_datalink_name_to_val = _lib.pcap_datalink_name_to_val +pcap_datalink_name_to_val.restype = c_int +pcap_datalink_name_to_val.argtypes = [STRING] + +#const char * pcap_datalink_val_to_name (int dlt) +# Translates a data link type value to the corresponding data link type name. NULL is returned on failure. +pcap_datalink_val_to_name = _lib.pcap_datalink_val_to_name +pcap_datalink_val_to_name.restype = STRING +pcap_datalink_val_to_name.argtypes = [c_int] + +#const char * pcap_datalink_val_to_description (int dlt) +# Translates a data link type value to a short description of that data link type. NULL is returned on failure. +pcap_datalink_val_to_description = _lib.pcap_datalink_val_to_description +pcap_datalink_val_to_description.restype = STRING +pcap_datalink_val_to_description.argtypes = [c_int] + +#int pcap_snapshot (pcap_t *p) +# Return the dimension of the packet portion (in bytes) that is delivered to the application. +pcap_snapshot = _lib.pcap_snapshot +pcap_snapshot.restype = c_int +pcap_snapshot.argtypes = [POINTER(pcap_t)] + +#int pcap_is_swapped (pcap_t *p) +# returns true if the current savefile uses a different byte order than the current system. +pcap_is_swapped = _lib.pcap_is_swapped +pcap_is_swapped.restype = c_int +pcap_is_swapped.argtypes = [POINTER(pcap_t)] + +#int pcap_major_version (pcap_t *p) +# return the major version number of the pcap library used to write the savefile. +pcap_major_version = _lib.pcap_major_version +pcap_major_version.restype = c_int +pcap_major_version.argtypes = [POINTER(pcap_t)] + +#int pcap_minor_version (pcap_t *p) +# return the minor version number of the pcap library used to write the savefile. +pcap_minor_version = _lib.pcap_minor_version +pcap_minor_version.restype = c_int +pcap_minor_version.argtypes = [POINTER(pcap_t)] + +#FILE * pcap_file (pcap_t *p) +# Return the standard stream of an offline capture. +pcap_file=_lib.pcap_file +pcap_file.restype = FILE +pcap_file.argtypes = [POINTER(pcap_t)] + +#int pcap_stats (pcap_t *p, struct pcap_stat *ps) +# Return statistics on current capture. +pcap_stats = _lib.pcap_stats +pcap_stats.restype = c_int +pcap_stats.argtypes = [POINTER(pcap_t), POINTER(pcap_stat)] + +#void pcap_perror (pcap_t *p, char *prefix) +# print the text of the last pcap library error on stderr, prefixed by prefix. +pcap_perror = _lib.pcap_perror +pcap_perror.restype = None +pcap_perror.argtypes = [POINTER(pcap_t), STRING] + +#char * pcap_geterr (pcap_t *p) +# return the error text pertaining to the last pcap library error. +pcap_geterr = _lib.pcap_geterr +pcap_geterr.restype = STRING +pcap_geterr.argtypes = [POINTER(pcap_t)] + +#char * pcap_strerror (int error) +# Provided in case strerror() isn't available. +pcap_strerror = _lib.pcap_strerror +pcap_strerror.restype = STRING +pcap_strerror.argtypes = [c_int] + +#const char * pcap_lib_version (void) +# Returns a pointer to a string giving information about the version of the libpcap library being used; note that it contains more information than just a version number. +pcap_lib_version = _lib.pcap_lib_version +pcap_lib_version.restype = STRING +pcap_lib_version.argtypes = [] + +#void pcap_close (pcap_t *p) +# close the files associated with p and deallocates resources. +pcap_close = _lib.pcap_close +pcap_close.restype = None +pcap_close.argtypes = [POINTER(pcap_t)] + +#FILE * pcap_dump_file (pcap_dumper_t *p) +# return the standard I/O stream of the 'savefile' opened by pcap_dump_open(). +pcap_dump_file=_lib.pcap_dump_file +pcap_dump_file.restype=FILE +pcap_dump_file.argtypes= [POINTER(pcap_dumper_t)] + +#int pcap_dump_flush (pcap_dumper_t *p) +# Flushes the output buffer to the ``savefile,'' so that any packets written with pcap_dump() but not yet written to the ``savefile'' will be written. -1 is returned on error, 0 on success. +pcap_dump_flush = _lib.pcap_dump_flush +pcap_dump_flush.restype = c_int +pcap_dump_flush.argtypes = [POINTER(pcap_dumper_t)] + +#void pcap_dump_close (pcap_dumper_t *p) +# Closes a savefile. +pcap_dump_close = _lib.pcap_dump_close +pcap_dump_close.restype = None +pcap_dump_close.argtypes = [POINTER(pcap_dumper_t)] + +if not WIN32: + + pcap_get_selectable_fd = _lib.pcap_get_selectable_fd + pcap_get_selectable_fd.restype = c_int + pcap_get_selectable_fd.argtypes = [POINTER(pcap_t)] + +########################################### +## Windows-specific Extensions +## The functions in this section extend libpcap to offer advanced functionalities +## (like remote packet capture, packet buffer size variation or high-precision packet injection). +## Howerver, at the moment they can be used only in Windows. +########################################### +if WIN32: + HANDLE = c_void_p + + ############## + ## Identifiers related to the new source syntax + ############## + #define PCAP_SRC_FILE 2 + #define PCAP_SRC_IFLOCAL 3 + #define PCAP_SRC_IFREMOTE 4 + #Internal representation of the type of source in use (file, remote/local interface). + PCAP_SRC_FILE = 2 + PCAP_SRC_IFLOCAL = 3 + PCAP_SRC_IFREMOTE = 4 + + ############## + ## Strings related to the new source syntax + ############## + #define PCAP_SRC_FILE_STRING "file://" + #define PCAP_SRC_IF_STRING "rpcap://" + #String that will be used to determine the type of source in use (file, remote/local interface). + PCAP_SRC_FILE_STRING="file://" + PCAP_SRC_IF_STRING="rpcap://" + + ############## + ## Flags defined in the pcap_open() function + ############## + # define PCAP_OPENFLAG_PROMISCUOUS 1 + # Defines if the adapter has to go in promiscuous mode. + PCAP_OPENFLAG_PROMISCUOUS=1 + # define PCAP_OPENFLAG_DATATX_UDP 2 + # Defines if the data trasfer (in case of a remote capture) has to be done with UDP protocol. + PCAP_OPENFLAG_DATATX_UDP=2 + # define PCAP_OPENFLAG_NOCAPTURE_RPCAP 4 + PCAP_OPENFLAG_NOCAPTURE_RPCAP=4 + # Defines if the remote probe will capture its own generated traffic. + # define PCAP_OPENFLAG_NOCAPTURE_LOCAL 8 + PCAP_OPENFLAG_NOCAPTURE_LOCAL = 8 + # define PCAP_OPENFLAG_MAX_RESPONSIVENESS 16 + # This flag configures the adapter for maximum responsiveness. + PCAP_OPENFLAG_MAX_RESPONSIVENESS=16 + + ############## + ## Sampling methods defined in the pcap_setsampling() function + ############## + # define PCAP_SAMP_NOSAMP 0 + # No sampling has to be done on the current capture. + PCAP_SAMP_NOSAMP=0 + # define PCAP_SAMP_1_EVERY_N 1 + # It defines that only 1 out of N packets must be returned to the user. + PCAP_SAMP_1_EVERY_N=1 + #define PCAP_SAMP_FIRST_AFTER_N_MS 2 + # It defines that we have to return 1 packet every N milliseconds. + PCAP_SAMP_FIRST_AFTER_N_MS=2 + + ############## + ## Authentication methods supported by the RPCAP protocol + ############## + # define RPCAP_RMTAUTH_NULL 0 + # It defines the NULL authentication. + RPCAP_RMTAUTH_NULL=0 + # define RPCAP_RMTAUTH_PWD 1 + # It defines the username/password authentication. + RPCAP_RMTAUTH_PWD=1 + + + ############## + ## Remote struct and defines + ############## + # define PCAP_BUF_SIZE 1024 + # Defines the maximum buffer size in which address, port, interface names are kept. + PCAP_BUF_SIZE = 1024 + # define RPCAP_HOSTLIST_SIZE 1024 + # Maximum lenght of an host name (needed for the RPCAP active mode). + RPCAP_HOSTLIST_SIZE = 1024 + + class pcap_send_queue(Structure): + _fields_=[("maxlen",c_uint), + ("len",c_uint), + ("buffer",c_char_p)] + + ## struct pcap_rmtauth + ## This structure keeps the information needed to autheticate the user on a remote machine + class pcap_rmtauth(Structure): + _fields_=[("type",c_int), + ("username",c_char_p), + ("password",c_char_p)] + + ## struct pcap_samp + ## This structure defines the information related to sampling + class pcap_samp(Structure): + _fields_=[("method",c_int), + ("value",c_int)] + + #PAirpcapHandle pcap_get_airpcap_handle (pcap_t *p) + # Returns the AirPcap handler associated with an adapter. This handler can be used to change the wireless-related settings of the CACE Technologies AirPcap wireless capture adapters. + + #bool pcap_offline_filter (struct bpf_program *prog, const struct pcap_pkthdr *header, const u_char *pkt_data) + # Returns if a given filter applies to an offline packet. + pcap_offline_filter = _lib.pcap_offline_filter + pcap_offline_filter.restype = c_bool + pcap_offline_filter.argtypes = [POINTER(bpf_program),POINTER(pcap_pkthdr),POINTER(u_char)] + + #int pcap_live_dump (pcap_t *p, char *filename, int maxsize, int maxpacks) + # Save a capture to file. + pcap_live_dump = _lib.pcap_live_dump + pcap_live_dump.restype = c_int + pcap_live_dump.argtypes = [POINTER(pcap_t), POINTER(c_char), c_int,c_int] + + #int pcap_live_dump_ended (pcap_t *p, int sync) + # Return the status of the kernel dump process, i.e. tells if one of the limits defined with pcap_live_dump() has been reached. + pcap_live_dump_ended = _lib.pcap_live_dump_ended + pcap_live_dump_ended.restype = c_int + pcap_live_dump_ended.argtypes = [POINTER(pcap_t), c_int] + + #struct pcap_stat * pcap_stats_ex (pcap_t *p, int *pcap_stat_size) + # Return statistics on current capture. + pcap_stats_ex = _lib.pcap_stats_ex + pcap_stats_ex.restype = POINTER(pcap_stat) + pcap_stats_ex.argtypes = [POINTER(pcap_t), POINTER(c_int)] + + #int pcap_setbuff (pcap_t *p, int dim) + # Set the size of the kernel buffer associated with an adapter. + pcap_setbuff = _lib.pcap_setbuff + pcap_setbuff.restype = c_int + pcap_setbuff.argtypes = [POINTER(pcap_t), c_int] + + #int pcap_setmode (pcap_t *p, int mode) + # Set the working mode of the interface p to mode. + pcap_setmode = _lib.pcap_setmode + pcap_setmode.restype = c_int + pcap_setmode.argtypes = [POINTER(pcap_t), c_int] + + #int pcap_setmintocopy (pcap_t *p, int size) + # Set the minumum amount of data received by the kernel in a single call. + pcap_setmintocopy = _lib.pcap_setmintocopy + pcap_setmintocopy.restype = c_int + pcap_setmintocopy.argtype = [POINTER(pcap_t), c_int] + + #HANDLE pcap_getevent (pcap_t *p) + # Return the handle of the event associated with the interface p. + pcap_getevent = _lib.pcap_getevent + pcap_getevent.restype = HANDLE + pcap_getevent.argtypes = [POINTER(pcap_t)] + + #pcap_send_queue * pcap_sendqueue_alloc (u_int memsize) + # Allocate a send queue. + pcap_sendqueue_alloc = _lib.pcap_sendqueue_alloc + pcap_sendqueue_alloc.restype = POINTER(pcap_send_queue) + pcap_sendqueue_alloc.argtypes = [c_uint] + + #void pcap_sendqueue_destroy (pcap_send_queue *queue) + # Destroy a send queue. + pcap_sendqueue_destroy = _lib.pcap_sendqueue_destroy + pcap_sendqueue_destroy.restype = None + pcap_sendqueue_destroy.argtypes = [POINTER(pcap_send_queue)] + + #int pcap_sendqueue_queue (pcap_send_queue *queue, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data) + # Add a packet to a send queue. + pcap_sendqueue_queue = _lib.pcap_sendqueue_queue + pcap_sendqueue_queue.restype = c_int + pcap_sendqueue_queue.argtypes = [POINTER(pcap_send_queue), POINTER(pcap_pkthdr), POINTER(u_char)] + + #u_int pcap_sendqueue_transmit (pcap_t *p, pcap_send_queue *queue, int sync) + # Send a queue of raw packets to the network. + pcap_sendqueue_transmit = _lib.pcap_sendqueue_transmit + pcap_sendqueue_transmit.retype = u_int + pcap_sendqueue_transmit.argtypes = [POINTER(pcap_t), POINTER(pcap_send_queue), c_int] + + #int pcap_findalldevs_ex (char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf) + # Create a list of network devices that can be opened with pcap_open(). + pcap_findalldevs_ex = _lib.pcap_findalldevs_ex + pcap_findalldevs_ex.retype = c_int + pcap_findalldevs_ex.argtypes = [STRING, POINTER(pcap_rmtauth), POINTER(POINTER(pcap_if_t)), STRING] + + #int pcap_createsrcstr (char *source, int type, const char *host, const char *port, const char *name, char *errbuf) + # Accept a set of strings (host name, port, ...), and it returns the complete source string according to the new format (e.g. 'rpcap://1.2.3.4/eth0'). + pcap_createsrcstr = _lib.pcap_createsrcstr + pcap_createsrcstr.restype = c_int + pcap_createsrcstr.argtypes = [STRING, c_int, STRING, STRING, STRING, STRING] + + #int pcap_parsesrcstr (const char *source, int *type, char *host, char *port, char *name, char *errbuf) + # Parse the source string and returns the pieces in which the source can be split. + pcap_parsesrcstr = _lib.pcap_parsesrcstr + pcap_parsesrcstr.retype = c_int + pcap_parsesrcstr.argtypes = [STRING, POINTER(c_int), STRING, STRING, STRING, STRING] + + #pcap_t * pcap_open (const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf) + # Open a generic source in order to capture / send (WinPcap only) traffic. + pcap_open = _lib.pcap_open + pcap_open.restype = POINTER(pcap_t) + pcap_open.argtypes = [STRING, c_int, c_int, c_int, POINTER(pcap_rmtauth), STRING] + + #struct pcap_samp * pcap_setsampling (pcap_t *p) + # Define a sampling method for packet capture. + pcap_setsampling = _lib.pcap_setsampling + pcap_setsampling.restype = POINTER(pcap_samp) + pcap_setsampling.argtypes = [POINTER(pcap_t)] + + #SOCKET pcap_remoteact_accept (const char *address, const char *port, const char *hostlist, char *connectinghost, struct pcap_rmtauth *auth, char *errbuf) + # Block until a network connection is accepted (active mode only). + pcap_remoteact_accept = _lib.pcap_remoteact_accept + pcap_remoteact_accept.restype = SOCKET + pcap_remoteact_accept.argtypes = [STRING, STRING, STRING, STRING, POINTER(pcap_rmtauth), STRING] + + #int pcap_remoteact_close (const char *host, char *errbuf) + # Drop an active connection (active mode only). + pcap_remoteact_close = _lib.pcap_remoteact_close + pcap_remoteact_close.restypes = c_int + pcap_remoteact_close.argtypes = [STRING, STRING] + + #void pcap_remoteact_cleanup () + # Clean the socket that is currently used in waiting active connections. + pcap_remoteact_cleanup = _lib.pcap_remoteact_cleanup + pcap_remoteact_cleanup.restypes = None + pcap_remoteact_cleanup.argtypes = [] + + #int pcap_remoteact_list (char *hostlist, char sep, int size, char *errbuf) + # Return the hostname of the host that have an active connection with us (active mode only). + pcap_remoteact_list = _lib.pcap_remoteact_list + pcap_remoteact_list.restype = c_int + pcap_remoteact_list.argtypes = [STRING, c_char, c_int, STRING] diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/as_resolvers.py b/scripts/external_libs/scapy-python3-0.18/scapy/as_resolvers.py new file mode 100644 index 00000000..f04322b8 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/as_resolvers.py @@ -0,0 +1,115 @@ +## 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 + +""" +Resolve Autonomous Systems (AS). +""" + + +import socket +from .config import conf + +class AS_resolver: + server = None + options = "-k" + def __init__(self, server=None, port=43, options=None): + if server is not None: + self.server = server + self.port = port + if options is not None: + self.options = options + + def _start(self): + self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.s.connect((self.server,self.port)) + if self.options: + self.s.send(self.options+b"\n") + self.s.recv(8192) + def _stop(self): + self.s.close() + + def _parse_whois(self, txt): + asn,desc = None,b"" + for l in txt.splitlines(): + if not asn and l.startswith(b"origin:"): + asn = l[7:].strip().decode('utf-8') + if l.startswith(b"descr:"): + if desc: + desc += br"\n" + desc += l[6:].strip() + if asn is not None and desc.strip(): + desc = desc.strip().decode('utf-8') + break + return asn, desc + + def _resolve_one(self, ip): + self.s.send(b"".join([ip.encode('ascii')])+b"\n") + x = b"" + while not (b"%" in x or b"source" in x): + x += self.s.recv(8192) + asn, desc = self._parse_whois(x) + return ip,asn,desc + def resolve(self, *ips): + self._start() + ret = [] + for ip in ips: + ip,asn,desc = self._resolve_one(ip) + if asn is not None: + ret.append((ip,asn,desc)) + self._stop() + return ret + +class AS_resolver_riswhois(AS_resolver): + server = "riswhois.ripe.net" + options = b"-k -M -1" + + +class AS_resolver_radb(AS_resolver): + server = "whois.ra.net" + options = b"-k -M" + + +class AS_resolver_cymru(AS_resolver): + server = "whois.cymru.com" + options = None + def resolve(self, *ips): + ASNlist = [] + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((self.server,self.port)) + s.send(b"begin\r\n"+b"\r\n".join([ i.encode('ascii') for i in ips])+b"\r\nend\r\n") + r = b"" + while 1: + l = s.recv(8192) + if l == b"": + break + r += l + s.close() + for l in r.splitlines()[1:]: + if b"|" not in l: + continue + asn,ip,desc = [ i.decode('ascii') for i in map(bytes.strip, l.split(b"|")) ] + if asn == "NA": + continue + asn = int(asn) + ASNlist.append((ip,asn,desc)) + return ASNlist + +class AS_resolver_multi(AS_resolver): + resolvers_list = ( AS_resolver_cymru(),AS_resolver_riswhois(),AS_resolver_radb() ) + def __init__(self, *reslist): + if reslist: + self.resolvers_list = reslist + def resolve(self, *ips): + todo = ips + ret = [] + for ASres in self.resolvers_list: + res = ASres.resolve(*todo) + resolved = [ ip for ip,asn,desc in res ] + todo = [ ip for ip in todo if ip not in resolved ] + ret += res + return ret + + +conf.AS_resolver = AS_resolver_multi() diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/asn1/__init__.py b/scripts/external_libs/scapy-python3-0.18/scapy/asn1/__init__.py new file mode 100644 index 00000000..4827a588 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/asn1/__init__.py @@ -0,0 +1,12 @@ +## 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 + +""" +Package holding ASN.1 related modules. +""" + +# We do not import mib.py because it is more bound to scapy and +# less prone to be used in a standalone fashion +__all__ = ["asn1","ber"] diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/asn1/asn1.py b/scripts/external_libs/scapy-python3-0.18/scapy/asn1/asn1.py new file mode 100644 index 00000000..c94d0b12 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/asn1/asn1.py @@ -0,0 +1,321 @@ +## 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 + +""" +ASN.1 (Abstract Syntax Notation One) +""" + +import random +from scapy.config import conf +from scapy.error import Scapy_Exception,warning +from scapy.volatile import RandField +from scapy.utils import Enum_metaclass, EnumElement + +class RandASN1Object(RandField): + def __init__(self, objlist=None): + if objlist is None: + objlist = [ x._asn1_obj for x in + [ x for x in ASN1_Class_UNIVERSAL.__rdict__.values() if hasattr(x,"_asn1_obj") ]] +# objlist = map(lambda x:x._asn1_obj, +# [ x for x in ASN1_Class_UNIVERSAL.__rdict__.values() if hasattr(x,"_asn1_obj") ]) + self.objlist = objlist + self.chars = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + def _fix(self, n=0): + o = random.choice(self.objlist) + if issubclass(o, ASN1_INTEGER): + return o(int(random.gauss(0,1000))) + elif issubclass(o, ASN1_IPADDRESS): + z = RandIP()._fix() + return o(z) + elif issubclass(o, ASN1_STRING): + z = int(random.expovariate(0.05)+1) + return o(bytes([random.choice(self.chars) for i in range(z)])) + elif issubclass(o, ASN1_SEQUENCE) and (n < 10): + z = int(random.expovariate(0.08)+1) +# return o(map(lambda x:x._fix(n+1), [self.__class__(objlist=self.objlist)]*z)) + return o([ x._fix(n+1) for x in [self.__class__(objlist=self.objlist)]*z]) + return ASN1_INTEGER(int(random.gauss(0,1000))) + + +############## +#### ASN1 #### +############## + +class ASN1_Error(Scapy_Exception): + pass + +class ASN1_Encoding_Error(ASN1_Error): + pass + +class ASN1_Decoding_Error(ASN1_Error): + pass + +class ASN1_BadTag_Decoding_Error(ASN1_Decoding_Error): + pass + + + +class ASN1Codec(EnumElement): + def register_stem(cls, stem): + cls._stem = stem + def dec(cls, s, context=None): + return cls._stem.dec(s, context=context) + def safedec(cls, s, context=None): + return cls._stem.safedec(s, context=context) + def get_stem(cls): + return cls.stem + + +class ASN1_Codecs_metaclass(Enum_metaclass): + element_class = ASN1Codec + +class ASN1_Codecs(metaclass = ASN1_Codecs_metaclass): + #__metaclass__ = ASN1_Codecs_metaclass + BER = 1 + DER = 2 + PER = 3 + CER = 4 + LWER = 5 + BACnet = 6 + OER = 7 + SER = 8 + XER = 9 + +class ASN1Tag(EnumElement): + def __init__(self, key, value, context=None, codec=None): + EnumElement.__init__(self, key, value) + self._context = context + if codec == None: + codec = {} + self._codec = codec + def clone(self): # /!\ not a real deep copy. self.codec is shared + return self.__class__(self._key, self._value, self._context, self._codec) + def register_asn1_object(self, asn1obj): + self._asn1_obj = asn1obj + def asn1_object(self, val): + if hasattr(self,"_asn1_obj"): + return self._asn1_obj(val) + raise ASN1_Error("%r does not have any assigned ASN1 object" % self) + def register(self, codecnum, codec): + self._codec[codecnum] = codec + def get_codec(self, codec): + try: + c = self._codec[codec] + except KeyError as msg: + raise ASN1_Error("Codec %r not found for tag %r" % (codec, self)) + return c + +class ASN1_Class_metaclass(Enum_metaclass): + element_class = ASN1Tag + def __new__(cls, name, bases, dct): # XXX factorise a bit with Enum_metaclass.__new__() + for b in bases: + for k,v in b.__dict__.items(): + if k not in dct and isinstance(v,ASN1Tag): + dct[k] = v.clone() + + rdict = {} + for k,v in dct.items(): + if type(v) is int: + v = ASN1Tag(k,v) + dct[k] = v + rdict[v] = v + elif isinstance(v, ASN1Tag): + rdict[v] = v + dct["__rdict__"] = rdict + + cls = type.__new__(cls, name, bases, dct) + for v in cls.__dict__.values(): + if isinstance(v, ASN1Tag): + v.context = cls # overwrite ASN1Tag contexts, even cloned ones + return cls + + +class ASN1_Class(metaclass = ASN1_Class_metaclass): + pass + +class ASN1_Class_UNIVERSAL(ASN1_Class): + name = "UNIVERSAL" + ERROR = -3 + RAW = -2 + NONE = -1 + ANY = 0 + BOOLEAN = 1 + INTEGER = 2 + BIT_STRING = 3 + STRING = 4 + NULL = 5 + OID = 6 + OBJECT_DESCRIPTOR = 7 + EXTERNAL = 8 + REAL = 9 + ENUMERATED = 10 + EMBEDDED_PDF = 11 + UTF8_STRING = 12 + RELATIVE_OID = 13 + SEQUENCE = 0x30#XXX 16 ?? + SET = 0x31 #XXX 17 ?? + NUMERIC_STRING = 18 + PRINTABLE_STRING = 19 + T61_STRING = 20 + VIDEOTEX_STRING = 21 + IA5_STRING = 22 + UTC_TIME = 23 + GENERALIZED_TIME = 24 + GRAPHIC_STRING = 25 + ISO646_STRING = 26 + GENERAL_STRING = 27 + UNIVERSAL_STRING = 28 + CHAR_STRING = 29 + BMP_STRING = 30 + IPADDRESS = 0x40 + COUNTER32 = 0x41 + GAUGE32 = 0x42 + TIME_TICKS = 0x43 + SEP = 0x80 + +class ASN1_Object_metaclass(type): + def __new__(cls, name, bases, dct): + c = super(ASN1_Object_metaclass, cls).__new__(cls, name, bases, dct) + try: + c.tag.register_asn1_object(c) + except: + warning("Error registering %r for %r" % (c.tag, c.codec)) + return c + + +class ASN1_Object(metaclass = ASN1_Object_metaclass): + tag = ASN1_Class_UNIVERSAL.ANY + def __init__(self, val): + self.val = val + def enc(self, codec): + return self.tag.get_codec(codec).enc(self.val) + def __repr__(self): + return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), self.val) + def __str__(self): + raise Exception("Should not get here") + #return self.enc(conf.ASN1_default_codec) + def __bytes__(self): + return self.enc(conf.ASN1_default_codec) + def strshow(self, lvl=0): + return (" "*lvl)+repr(self)+"\n" + def show(self, lvl=0): + print(self.strshow(lvl)) + def __eq__(self, other): + return self.val == other + def __hash__(self): + return self.val + def __cmp__(self, other): + return cmp(self.val, other) + +class ASN1_DECODING_ERROR(ASN1_Object): + tag = ASN1_Class_UNIVERSAL.ERROR + def __init__(self, val, exc=None): + ASN1_Object.__init__(self, val) + self.exc = exc + def __repr__(self): + return "<%s[%r]{{%s}}>" % (self.__dict__.get("name", self.__class__.__name__), + self.val, self.exc.args[0]) + def enc(self, codec): + if isinstance(self.val, ASN1_Object): + return self.val.enc(codec) + return self.val + +class ASN1_force(ASN1_Object): + tag = ASN1_Class_UNIVERSAL.RAW + def enc(self, codec): + if isinstance(self.val, ASN1_Object): + return self.val.enc(codec) + return self.val + +class ASN1_BADTAG(ASN1_force): + pass + +class ASN1_INTEGER(ASN1_Object): + tag = ASN1_Class_UNIVERSAL.INTEGER + +class ASN1_STRING(ASN1_Object): + tag = ASN1_Class_UNIVERSAL.STRING + def __init__(self, val): + if type(val) is str: + self.val = val.encode('ascii') + elif type(val) is bytes: + self.val = val + else: + raise Exception("Unknown value type for ASN1_STRING") + +class ASN1_BIT_STRING(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.BIT_STRING + +class ASN1_PRINTABLE_STRING(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING + +class ASN1_T61_STRING(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.T61_STRING + +class ASN1_IA5_STRING(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.IA5_STRING + +class ASN1_NUMERIC_STRING(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.NUMERIC_STRING + +class ASN1_VIDEOTEX_STRING(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING + +class ASN1_IPADDRESS(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.IPADDRESS + +class ASN1_UTC_TIME(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.UTC_TIME + +class ASN1_GENERALIZED_TIME(ASN1_STRING): + tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME + +class ASN1_TIME_TICKS(ASN1_INTEGER): + tag = ASN1_Class_UNIVERSAL.TIME_TICKS + +class ASN1_BOOLEAN(ASN1_INTEGER): + tag = ASN1_Class_UNIVERSAL.BOOLEAN + +class ASN1_ENUMERATED(ASN1_INTEGER): + tag = ASN1_Class_UNIVERSAL.ENUMERATED + +class ASN1_NULL(ASN1_INTEGER): + tag = ASN1_Class_UNIVERSAL.NULL + +class ASN1_SEP(ASN1_NULL): + tag = ASN1_Class_UNIVERSAL.SEP + +class ASN1_GAUGE32(ASN1_INTEGER): + tag = ASN1_Class_UNIVERSAL.GAUGE32 + +class ASN1_COUNTER32(ASN1_INTEGER): + tag = ASN1_Class_UNIVERSAL.COUNTER32 + +class ASN1_SEQUENCE(ASN1_Object): + tag = ASN1_Class_UNIVERSAL.SEQUENCE + def strshow(self, lvl=0): + s = (" "*lvl)+("# %s:" % self.__class__.__name__)+"\n" + for o in self.val: + s += o.strshow(lvl=lvl+1) + return s + +class ASN1_SET(ASN1_SEQUENCE): + tag = ASN1_Class_UNIVERSAL.SET + +class ASN1_OID(ASN1_Object): + tag = ASN1_Class_UNIVERSAL.OID + def __init__(self, val): + if type(val) is str: + val = val.encode('ascii') + val = conf.mib._oid(val) + ASN1_Object.__init__(self, val) + def __repr__(self): + return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), conf.mib._oidname(self.val)) + def __oidname__(self): + return '%s'%conf.mib._oidname(self.val) + + + +conf.ASN1_default_codec = ASN1_Codecs.BER diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/asn1/ber.py b/scripts/external_libs/scapy-python3-0.18/scapy/asn1/ber.py new file mode 100644 index 00000000..48cb1c2d --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/asn1/ber.py @@ -0,0 +1,370 @@ +## 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 + +""" +Basic Encoding Rules (BER) for ASN.1 +""" + +from scapy.error import warning +from scapy.utils import inet_aton,inet_ntoa +from scapy.asn1.asn1 import ASN1_Decoding_Error,ASN1_Encoding_Error,ASN1_BadTag_Decoding_Error,ASN1_Codecs,ASN1_Class_UNIVERSAL,ASN1_Error,ASN1_DECODING_ERROR,ASN1_BADTAG + +################## +## BER encoding ## +################## + + + +#####[ BER tools ]##### + + +class BER_Exception(Exception): + pass + +class BER_Encoding_Error(ASN1_Encoding_Error): + def __init__(self, msg, encoded=None, remaining=None): + Exception.__init__(self, msg) + self.remaining = remaining + self.encoded = encoded + def __str__(self): + s = Exception.__str__(self) + if isinstance(self.encoded, BERcodec_Object): + s+="\n### Already encoded ###\n%s" % self.encoded.strshow() + else: + s+="\n### Already encoded ###\n%r" % self.encoded + s+="\n### Remaining ###\n%r" % self.remaining + return s + +class BER_Decoding_Error(ASN1_Decoding_Error): + def __init__(self, msg, decoded=None, remaining=None): + Exception.__init__(self, msg) + self.remaining = remaining + self.decoded = decoded + def __str__(self): + s = Exception.__str__(self) + if isinstance(self.decoded, BERcodec_Object): + s+="\n### Already decoded ###\n%s" % self.decoded.strshow() + else: + s+="\n### Already decoded ###\n%r" % self.decoded + s+="\n### Remaining ###\n%r" % self.remaining + return s + +class BER_BadTag_Decoding_Error(BER_Decoding_Error, ASN1_BadTag_Decoding_Error): + pass + +def BER_len_enc(l, size=0): + if l <= 127 and size==0: + return bytes([l]) + s = b"" + while l or size>0: + s = bytes([l&0xff])+s + l >>= 8 + size -= 1 + if len(s) > 127: + raise BER_Exception("BER_len_enc: Length too long (%i) to be encoded [%r]" % (len(s),s)) + return bytes([len(s)|0x80])+s +def BER_len_dec(s): + l = (s[0]) + if not l & 0x80: + return l,s[1:] + l &= 0x7f + if len(s) <= l: + raise BER_Decoding_Error("BER_len_dec: Got %i bytes while expecting %i" % (len(s)-1, l),remaining=s) + ll = 0 + for c in s[1:l+1]: + ll <<= 8 + ll |= (c) + return ll,s[l+1:] + +def BER_num_enc(l, size=1): + x=[] + while l or size>0: + x.insert(0, l & 0x7f) + if len(x) > 1: + x[0] |= 0x80 + l >>= 7 + size -= 1 + return bytes([(k) for k in x]) +def BER_num_dec(s): + x = 0 + for i in range(len(s)): + c = (s[i]) + x <<= 7 + x |= c&0x7f + if not c&0x80: + break + if c&0x80: + raise BER_Decoding_Error("BER_num_dec: unfinished number description", remaining=s) + return x, s[i+1:] + +#####[ BER classes ]##### + +class BERcodec_metaclass(type): + def __new__(cls, name, bases, dct): + c = super(BERcodec_metaclass, cls).__new__(cls, name, bases, dct) + try: + c.tag.register(c.codec, c) + except: + warning("Error registering %r for %r" % (c.tag, c.codec)) + return c + + +class BERcodec_Object( metaclass = BERcodec_metaclass): + codec = ASN1_Codecs.BER + tag = ASN1_Class_UNIVERSAL.ANY + + @classmethod + def asn1_object(cls, val): + return cls.tag.asn1_object(val) + + @classmethod + def check_string(cls, s): + if not s: + raise BER_Decoding_Error("%s: Got empty object while expecting tag %r" % + (cls.__name__,cls.tag), remaining=s) + @classmethod + def check_type(cls, s): + cls.check_string(s) + if hash(cls.tag) != (s[0]): + raise BER_BadTag_Decoding_Error("%s: Got tag [%i/%#x] while expecting %r" % + (cls.__name__, (s[0]), (s[0]),cls.tag), remaining=s) + return s[1:] + @classmethod + def check_type_get_len(cls, s): + s2 = cls.check_type(s) + if not s2: + raise BER_Decoding_Error("%s: No bytes while expecting a length" % + cls.__name__, remaining=s) + return BER_len_dec(s2) + @classmethod + def check_type_check_len(cls, s): + l,s3 = cls.check_type_get_len(s) + if len(s3) < l: + raise BER_Decoding_Error("%s: Got %i bytes while expecting %i" % + (cls.__name__, len(s3), l), remaining=s) + return l,s3[:l],s3[l:] + + @classmethod + def do_dec(cls, s, context=None, safe=False): + if context is None: + context = cls.tag.context + cls.check_string(s) + p = (s[0]) + if p not in context: + t = s + if len(t) > 18: + t = t[:15]+"..." + raise BER_Decoding_Error("Unknown prefix [%02x] for [%r]" % (p,t), remaining=s) + codec = context[p].get_codec(ASN1_Codecs.BER) + return codec.dec(s,context,safe) + + @classmethod + def dec(cls, s, context=None, safe=False): + if not safe: + return cls.do_dec(s, context, safe) + try: + return cls.do_dec(s, context, safe) + except BER_BadTag_Decoding_Error as e: + o,remain = BERcodec_Object.dec(e.remaining, context, safe) + return ASN1_BADTAG(o),remain + except BER_Decoding_Error as e: + return ASN1_DECODING_ERROR(s, exc=e),"" + except ASN1_Error as e: + return ASN1_DECODING_ERROR(s, exc=e),"" + + @classmethod + def safedec(cls, s, context=None): + return cls.dec(s, context, safe=True) + + + @classmethod + def enc(cls, s): + if type(s) is str: + return BERcodec_STRING.enc(s) + #TODO3 wild guess + elif type(s) is bytes: + return BERcodec_STRING.enc(s) + else: + return BERcodec_INTEGER.enc(hash(s)) + + + +ASN1_Codecs.BER.register_stem(BERcodec_Object) + + +class BERcodec_INTEGER(BERcodec_Object): + tag = ASN1_Class_UNIVERSAL.INTEGER + @classmethod + def enc(cls, i): + s = [] + while 1: + s.append(i&0xff) + if -127 <= i < 0: + break + if 128 <= i <= 255: + s.append(0) + i >>= 8 + if not i: + break + #s = map(chr, s) + s = bytes(s) + BER_len_enc(len(s)) + bytes([hash(cls.tag)]) + #s.reverse() + #return b"".join(s) + return s[::-1] + @classmethod + def do_dec(cls, s, context=None, safe=False): + l,s,t = cls.check_type_check_len(s) + x = 0 + if s: + if (s[0])&0x80: # negative int + x = -1 + for c in s: + x <<= 8 + x |= (c) + return cls.asn1_object(x),t + + +class BERcodec_BOOLEAN(BERcodec_INTEGER): + tag = ASN1_Class_UNIVERSAL.BOOLEAN + +class BERcodec_ENUMERATED(BERcodec_INTEGER): + tag = ASN1_Class_UNIVERSAL.ENUMERATED + +class BERcodec_NULL(BERcodec_INTEGER): + tag = ASN1_Class_UNIVERSAL.NULL + @classmethod + def enc(cls, i): + if i == 0: + return bytes([hash(cls.tag)])+b"\0" + else: + return BERcodec_INTEGER.enc(i) + +class BERcodec_SEP(BERcodec_NULL): + tag = ASN1_Class_UNIVERSAL.SEP + +class BERcodec_STRING(BERcodec_Object): + tag = ASN1_Class_UNIVERSAL.STRING + @classmethod + def enc(cls,s): + if type(s) is str: + s = s.encode('ascii') + return bytes([hash(cls.tag)])+BER_len_enc(len(s))+s + @classmethod + def do_dec(cls, s, context=None, safe=False): + l,s,t = cls.check_type_check_len(s) + return cls.tag.asn1_object(s),t + +class BERcodec_BIT_STRING(BERcodec_STRING): + tag = ASN1_Class_UNIVERSAL.BIT_STRING + +class BERcodec_PRINTABLE_STRING(BERcodec_STRING): + tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING + +class BERcodec_T61_STRING (BERcodec_STRING): + tag = ASN1_Class_UNIVERSAL.T61_STRING + +class BERcodec_IA5_STRING(BERcodec_STRING): + tag = ASN1_Class_UNIVERSAL.IA5_STRING + +class BERcodec_NUMERIC_STRING(BERcodec_STRING): + tag = ASN1_Class_UNIVERSAL.NUMERIC_STRING + +class BERcodec_VIDEOTEX_STRING(BERcodec_STRING): + tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING + +class BERcodec_IPADDRESS(BERcodec_STRING): + tag = ASN1_Class_UNIVERSAL.IPADDRESS + + @classmethod + def enc(cls, ipaddr_ascii): + try: + s = inet_aton(ipaddr_ascii) + except Exception: + raise BER_Encoding_Error("IPv4 address could not be encoded") + return bytes([hash(cls.tag)])+BER_len_enc(len(s))+s + + @classmethod + def do_dec(cls, s, context=None, safe=False): + l,s,t = cls.check_type_check_len(s) + try: + ipaddr_ascii = inet_ntoa(s) + except Exception: + raise BER_Decoding_Error("IP address could not be decoded", decoded=obj) + return cls.asn1_object(ipaddr_ascii), t + +class BERcodec_UTC_TIME(BERcodec_STRING): + tag = ASN1_Class_UNIVERSAL.UTC_TIME + +class BERcodec_GENERALIZED_TIME(BERcodec_STRING): + tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME + +class BERcodec_TIME_TICKS(BERcodec_INTEGER): + tag = ASN1_Class_UNIVERSAL.TIME_TICKS + +class BERcodec_GAUGE32(BERcodec_INTEGER): + tag = ASN1_Class_UNIVERSAL.GAUGE32 + +class BERcodec_COUNTER32(BERcodec_INTEGER): + tag = ASN1_Class_UNIVERSAL.COUNTER32 + +class BERcodec_SEQUENCE(BERcodec_Object): + tag = ASN1_Class_UNIVERSAL.SEQUENCE + @classmethod + def enc(cls, l): + #if type(l) is not str: + if type(l) is not bytes: + l = b"".join(map(lambda x: x.enc(cls.codec), l)) + return bytes([hash(cls.tag)])+BER_len_enc(len(l))+l + @classmethod + def do_dec(cls, s, context=None, safe=False): + if context is None: + context = cls.tag.context + l,st = cls.check_type_get_len(s) # we may have len(s) < l + s,t = st[:l],st[l:] + obj = [] + while s: + try: + o,s = BERcodec_Object.dec(s, context, safe) + except BER_Decoding_Error as err: + err.remaining += t + if err.decoded is not None: + obj.append(err.decoded) + err.decoded = obj + raise + obj.append(o) + if len(st) < l: + raise BER_Decoding_Error("Not enough bytes to decode sequence", decoded=obj) + return cls.asn1_object(obj),t + +class BERcodec_SET(BERcodec_SEQUENCE): + tag = ASN1_Class_UNIVERSAL.SET + + +class BERcodec_OID(BERcodec_Object): + tag = ASN1_Class_UNIVERSAL.OID + + @classmethod + def enc(cls, oid): + if type(oid) is str: + oid = oid.encode('ascii') + lst = [int(x) for x in oid.strip(b".").split(b".")] + if len(lst) >= 2: + lst[1] += 40*lst[0] + del(lst[0]) + s = b"".join([BER_num_enc(k) for k in lst]) + return bytes([hash(cls.tag)])+BER_len_enc(len(s))+s + @classmethod + def do_dec(cls, s, context=None, safe=False): + l,s,t = cls.check_type_check_len(s) + lst = [] + while s: + l,s = BER_num_dec(s) + lst.append(l) + if (len(lst) > 0): + lst.insert(0,lst[0]//40) + lst[1] %= 40 + return cls.asn1_object(b".".join([str(k).encode('ascii') for k in lst])), t + + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/asn1/mib.py b/scripts/external_libs/scapy-python3-0.18/scapy/asn1/mib.py new file mode 100644 index 00000000..8f3df25c --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/asn1/mib.py @@ -0,0 +1,149 @@ +## 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 + +""" +Management Information Base (MIB) parsing +""" + +import re +from glob import glob +from scapy.dadict import DADict,fixname +from scapy.config import conf +from scapy.utils import do_graph + +################# +## MIB parsing ## +################# + +_mib_re_integer = re.compile(b"^[0-9]+$") +_mib_re_both = re.compile(b"^([a-zA-Z_][a-zA-Z0-9_-]*)\(([0-9]+)\)$") +_mib_re_oiddecl = re.compile(b"$\s*([a-zA-Z0-9_-]+)\s+OBJECT([^:\{\}]|\{[^:]+\})+::=\s*\{([^\}]+)\}",re.M) +_mib_re_strings = re.compile(b'"[^"]*"') +_mib_re_comments = re.compile(b'--.*(\r|\n)') + +class MIBDict(DADict): + def _findroot(self, x): + if x.startswith(b"."): + x = x[1:] + if not x.endswith(b"."): + x += b"." + max=0 + root=b"." + for k in self.keys(): + if x.startswith(self[k]+b"."): + if max < len(self[k]): + max = len(self[k]) + root = k + return root, x[max:-1] + def _oidname(self, x): + root,remainder = self._findroot(x) + return root+remainder + def _oid(self, x): + if type(x) is str: + x = x.encode('ascii') + xl = x.strip(b".").split(b".") + p = len(xl)-1 + while p >= 0 and _mib_re_integer.match(xl[p]): + p -= 1 + if p != 0 or xl[p] not in self: + return x + xl[p] = self[xl[p]] + return b".".join(xl[p:]) + def _make_graph(self, other_keys=[], **kargs): + nodes = [(k,self[k]) for k in self.keys()] + oids = [self[k] for k in self.keys()] + for k in other_keys: + if k not in oids: + nodes.append(self.oidname(k),k) + s = 'digraph "mib" {\n\trankdir=LR;\n\n' + for k,o in nodes: + s += '\t"%s" [ label="%s" ];\n' % (o,k) + s += "\n" + for k,o in nodes: + parent,remainder = self._findroot(o[:-1]) + remainder = remainder[1:]+o[-1] + if parent != ".": + parent = self[parent] + s += '\t"%s" -> "%s" [label="%s"];\n' % (parent, o,remainder) + s += "}\n" + do_graph(s, **kargs) + def __len__(self): + return len(self.keys()) + + +def mib_register(ident, value, the_mib, unresolved): + if ident in the_mib or ident in unresolved: + return ident in the_mib + resval = [] + not_resolved = 0 + for v in value: + if _mib_re_integer.match(v): + resval.append(v) + else: + v = fixname(v) + if v not in the_mib: + not_resolved = 1 + if v in the_mib: + v = the_mib[v] + elif v in unresolved: + v = unresolved[v] + if type(v) is list: + resval += v + else: + resval.append(v) + if not_resolved: + unresolved[ident] = resval + return False + else: + the_mib[ident] = resval + keys = unresolved.keys() + i = 0 + while i < len(keys): + k = keys[i] + if mib_register(k,unresolved[k], the_mib, {}): + del(unresolved[k]) + del(keys[i]) + i = 0 + else: + i += 1 + + return True + + +def load_mib(filenames): + the_mib = {'iso': ['1']} + unresolved = {} + for k in conf.mib.keys(): + mib_register(k, conf.mib[k].split("."), the_mib, unresolved) + + if type(filenames) is str: + filenames = [filenames] + for fnames in filenames: + for fname in glob(fnames): + f = open(fname) + text = f.read() + cleantext = " ".join(_mib_re_strings.split(" ".join(_mib_re_comments.split(text)))) + for m in _mib_re_oiddecl.finditer(cleantext): + gr = m.groups() + ident,oid = gr[0],gr[-1] + ident=fixname(ident) + oid = oid.split() + for i in range(len(oid)): + m = _mib_re_both.match(oid[i]) + if m: + oid[i] = m.groups()[1] + mib_register(ident, oid, the_mib, unresolved) + + newmib = MIBDict(_name="MIB") + for k,o in the_mib.items(): + newmib[k]=".".join(o) + for k,o in unresolved.items(): + newmib[k]=".".join(o) + + conf.mib=newmib + + + +conf.mib = MIBDict(_name="MIB") diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/asn1fields.py b/scripts/external_libs/scapy-python3-0.18/scapy/asn1fields.py new file mode 100644 index 00000000..e7165a83 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/asn1fields.py @@ -0,0 +1,341 @@ +## 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 that implement ASN.1 data structures. +""" + +import itertools +from scapy.asn1.asn1 import * +from scapy.asn1.ber import * +from scapy.volatile import * +from scapy.base_classes import BasePacket + + +##################### +#### ASN1 Fields #### +##################### + +class ASN1F_badsequence(Exception): + pass + +class ASN1F_element: + pass + +class ASN1F_optionnal(ASN1F_element): + def __init__(self, field): + self._field=field + def __getattr__(self, attr): + return getattr(self._field,attr) + def dissect(self,pkt,s): + try: + return self._field.dissect(pkt,s) + except ASN1F_badsequence: + self._field.set_val(pkt,None) + return s + except BER_Decoding_Error: + self._field.set_val(pkt,None) + return s + def build(self, pkt): + if self._field.is_empty(pkt): + return b"" + return self._field.build(pkt) + +class ASN1F_field(ASN1F_element): + holds_packets=0 + islist=0 + + ASN1_tag = ASN1_Class_UNIVERSAL.ANY + context=ASN1_Class_UNIVERSAL + + def __init__(self, name, default, context=None): + if context is not None: + self.context = context + self.name = name + self.default = default + + def i2repr(self, pkt, x): + return repr(x) + def i2h(self, pkt, x): + return x + def any2i(self, pkt, x): + return x + def m2i(self, pkt, x): + return self.ASN1_tag.get_codec(pkt.ASN1_codec).safedec(x, context=self.context) + def i2m(self, pkt, x): + if x is None: + x = 0 + if isinstance(x, ASN1_Object): + if ( self.ASN1_tag == ASN1_Class_UNIVERSAL.ANY + or x.tag == ASN1_Class_UNIVERSAL.RAW + or x.tag == ASN1_Class_UNIVERSAL.ERROR + or self.ASN1_tag == x.tag ): + return x.enc(pkt.ASN1_codec) + else: + raise ASN1_Error("Encoding Error: got %r instead of an %r for field [%s]" % (x, self.ASN1_tag, self.name)) + return self.ASN1_tag.get_codec(pkt.ASN1_codec).enc(x) + + def do_copy(self, x): + if hasattr(x, "copy"): + return x.copy() + if type(x) is list: + x = x[:] + for i in range(len(x)): + if isinstance(x[i], BasePacket): + x[i] = x[i].copy() + return x + + def build(self, pkt): + return self.i2m(pkt, getattr(pkt, self.name)) + + def set_val(self, pkt, val): + setattr(pkt, self.name, val) + def is_empty(self, pkt): + return getattr(pkt,self.name) is None + + def dissect(self, pkt, s): + v,s = self.m2i(pkt, s) + self.set_val(pkt, v) + return s + + def get_fields_list(self): + return [self] + + def __hash__(self): + return hash(self.name) + def __str__(self): + return self.name + def __eq__(self, other): + return self.name == other + def __repr__(self): + return self.name + def randval(self): + return RandInt() + + +class ASN1F_INTEGER(ASN1F_field): + ASN1_tag= ASN1_Class_UNIVERSAL.INTEGER + def randval(self): + return RandNum(-2**64, 2**64-1) + +class ASN1F_BOOLEAN(ASN1F_field): + ASN1_tag= ASN1_Class_UNIVERSAL.BOOLEAN + def randval(self): + return RandChoice(True,False) + +class ASN1F_NULL(ASN1F_INTEGER): + ASN1_tag= ASN1_Class_UNIVERSAL.NULL + +class ASN1F_SEP(ASN1F_NULL): + ASN1_tag= ASN1_Class_UNIVERSAL.SEP + +class ASN1F_enum_INTEGER(ASN1F_INTEGER): + def __init__(self, name, default, enum): + ASN1F_INTEGER.__init__(self, name, default) + i2s = self.i2s = {} + s2i = self.s2i = {} + if type(enum) is list: + keys = range(len(enum)) + else: + keys = enum.keys() + #if filter(lambda x: type(x) is str, keys): + if list(filter(lambda x: type(x) is str, keys)): + i2s,s2i = s2i,i2s + for k in keys: + i2s[k] = enum[k] + s2i[enum[k]] = k + def any2i_one(self, pkt, x): + if type(x) is str: + x = self.s2i[x] + return x + def i2repr_one(self, pkt, x): + return self.i2s.get(x, repr(x)) + + def any2i(self, pkt, x): + if type(x) is list: + return map(lambda z,pkt=pkt:self.any2i_one(pkt,z), x) + else: + return self.any2i_one(pkt,x) + def i2repr(self, pkt, x): + if type(x) is list: + return map(lambda z,pkt=pkt:self.i2repr_one(pkt,z), x) + else: + return self.i2repr_one(pkt,x) + +class ASN1F_ENUMERATED(ASN1F_enum_INTEGER): + ASN1_tag = ASN1_Class_UNIVERSAL.ENUMERATED + +class ASN1F_STRING(ASN1F_field): + ASN1_tag = ASN1_Class_UNIVERSAL.STRING + def randval(self): + return RandString(RandNum(0, 1000)) + +class ASN1F_PRINTABLE_STRING(ASN1F_STRING): + ASN1_tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING + +class ASN1F_BIT_STRING(ASN1F_STRING): + ASN1_tag = ASN1_Class_UNIVERSAL.BIT_STRING + +class ASN1F_IPADDRESS(ASN1F_STRING): + ASN1_tag = ASN1_Class_UNIVERSAL.IPADDRESS + +class ASN1F_TIME_TICKS(ASN1F_INTEGER): + ASN1_tag = ASN1_Class_UNIVERSAL.TIME_TICKS + +class ASN1F_UTC_TIME(ASN1F_STRING): + ASN1_tag = ASN1_Class_UNIVERSAL.UTC_TIME + +class ASN1F_GENERALIZED_TIME(ASN1F_STRING): + ASN1_tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME + +class ASN1F_OID(ASN1F_field): + ASN1_tag = ASN1_Class_UNIVERSAL.OID + def randval(self): + return RandOID() + +class ASN1F_SEQUENCE(ASN1F_field): + ASN1_tag = ASN1_Class_UNIVERSAL.SEQUENCE + def __init__(self, *seq, **kargs): + if "ASN1_tag" in kargs: + self.ASN1_tag = kargs["ASN1_tag"] + self.seq = seq + def __repr__(self): + return "<%s%r>" % (self.__class__.__name__,self.seq,) + def set_val(self, pkt, val): + for f in self.seq: + f.set_val(pkt,val) + def is_empty(self, pkt): + for f in self.seq: + if not f.is_empty(pkt): + return False + return True + def get_fields_list(self): + #return reduce(lambda x,y: x+y.get_fields_list(), self.seq, []) + return list(itertools.chain(*[ i.get_fields_list() for i in self.seq ])) + def build(self, pkt): + #s = reduce(lambda x,y: x+y.build(pkt), self.seq, b"") + s = b"" + for i in self.seq: + s += i.build(pkt) + return self.i2m(pkt, s) + def dissect(self, pkt, s): + codec = self.ASN1_tag.get_codec(pkt.ASN1_codec) + try: + i,s,remain = codec.check_type_check_len(s) + for obj in self.seq: + s = obj.dissect(pkt,s) + if s: + warning("Too many bytes to decode sequence: [%r]" % s) # XXX not reversible! + return remain + except ASN1_Error as e: + raise ASN1F_badsequence(e) + +class ASN1F_SET(ASN1F_SEQUENCE): + ASN1_tag = ASN1_Class_UNIVERSAL.SET + +class ASN1F_SEQUENCE_OF(ASN1F_SEQUENCE): + holds_packets = 1 + islist = 1 + def __init__(self, name, default, asn1pkt, ASN1_tag=0x30): + self.asn1pkt = asn1pkt + self.tag = bytes([ASN1_tag]) + self.name = name + self.default = default + def i2repr(self, pkt, i): + if i is None: + return [] + return i + def get_fields_list(self): + return [self] + def set_val(self, pkt, val): + ASN1F_field.set_val(self, pkt, val) + def is_empty(self, pkt): + return ASN1F_field.is_empty(self, pkt) + def build(self, pkt): + val = getattr(pkt, self.name) + if isinstance(val, ASN1_Object) and val.tag == ASN1_Class_UNIVERSAL.RAW: + s = val + elif val is None: + s = b"" + else: + #print(val) + #s = b"".join(map(str, val )) + s = b"".join([ bytes(i) for i in val ]) + return self.i2m(pkt, s) + def dissect(self, pkt, s): + codec = self.ASN1_tag.get_codec(pkt.ASN1_codec) + i,s1,remain = codec.check_type_check_len(s) + lst = [] + while s1: + try: + p = self.asn1pkt(s1) + except ASN1F_badsequence as e: + lst.append(conf.raw_layer(s1)) + break + lst.append(p) + if conf.raw_layer in p: + s1 = p[conf.raw_layer].load + del(p[conf.raw_layer].underlayer.payload) + else: + break + self.set_val(pkt, lst) + return remain + def randval(self): + return fuzz(self.asn1pkt()) + def __repr__(self): + return "<%s %s>" % (self.__class__.__name__,self.name) + +class ASN1F_PACKET(ASN1F_field): + holds_packets = 1 + def __init__(self, name, default, cls): + ASN1F_field.__init__(self, name, default) + self.cls = cls + def i2m(self, pkt, x): + if x is None: + x = b"" + return bytes(x) + def extract_packet(self, cls, x): + try: + c = cls(x) + except ASN1F_badsequence: + c = conf.raw_layer(x) + cpad = c.getlayer(conf.padding_layer) + x = b"" + if cpad is not None: + x = cpad.load + del(cpad.underlayer.payload) + return c,x + def m2i(self, pkt, x): + return self.extract_packet(self.cls, x) + + +class ASN1F_CHOICE(ASN1F_PACKET): + ASN1_tag = ASN1_Class_UNIVERSAL.NONE + def __init__(self, name, default, *args): + self.name=name + self.choice = {} + for p in args: + self.choice[p.ASN1_root.ASN1_tag] = p +# self.context=context + self.default=default + def m2i(self, pkt, x): + if len(x) == 0: + return conf.raw_layer(),b"" + raise ASN1_Error("ASN1F_CHOICE: got empty string") + #if ord(x[0]) not in self.choice: + if (x[0]) not in self.choice: + return conf.raw_layer(x),b"" # XXX return RawASN1 packet ? Raise error + #raise ASN1_Error("Decoding Error: choice [%i] not found in %r" % (ord(x[0]), self.choice.keys())) + raise ASN1_Error("Decoding Error: choice [%i] not found in %r" % ((x[0]), self.choice.keys())) + + #z = ASN1F_PACKET.extract_packet(self, self.choice[ord(x[0])], x) + z = ASN1F_PACKET.extract_packet(self, self.choice[(x[0])], x) + return z + def randval(self): + return RandChoice(*map(lambda x:fuzz(x()), self.choice.values())) + + +# This import must come in last to avoid problems with cyclic dependencies +import scapy.packet diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/asn1packet.py b/scripts/external_libs/scapy-python3-0.18/scapy/asn1packet.py new file mode 100644 index 00000000..049e2b75 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/asn1packet.py @@ -0,0 +1,26 @@ +## 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 + +""" +Packet holding data in Abstract Syntax Notation (ASN.1). +""" + +from scapy.packet import * + +class ASN1_Packet(Packet): + ASN1_root = None + ASN1_codec = None + def init_fields(self): + flist = self.ASN1_root.get_fields_list() + self.do_init_fields(flist) + self.fields_desc = flist + def self_build(self): + if self.raw_packet_cache is not None: + return self.raw_packet_cache + return self.ASN1_root.build(self) + def do_dissect(self, x): + return self.ASN1_root.dissect(self, x) + + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/automaton.py b/scripts/external_libs/scapy-python3-0.18/scapy/automaton.py new file mode 100644 index 00000000..cd32b5c0 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/automaton.py @@ -0,0 +1,753 @@ +## 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 + +""" +Automata with states, transitions and actions. +""" + +from __future__ import with_statement +import types,itertools,time,os,sys,socket,functools +from select import select +from collections import deque +import _thread +from .config import conf +from .utils import do_graph +from .error import log_interactive +from .plist import PacketList +from .data import MTU +from .supersocket import SuperSocket + +class ObjectPipe: + def __init__(self): + self.rd,self.wr = os.pipe() + self.queue = deque() + def fileno(self): + return self.rd + def send(self, obj): + self.queue.append(obj) + os.write(self.wr,b"X") + def recv(self, n=0): + os.read(self.rd,1) + return self.queue.popleft() + + +class Message: + def __init__(self, **args): + self.__dict__.update(args) + def __repr__(self): + return "" % " ".join("%s=%r"%(k,v) + for (k,v) in self.__dict__.items() + if not k.startswith("_")) + +# Currently does not seem necessary +# class _meta_instance_state(type): +# def __init__(cls, name, bases, dct): +# def special_gen(special_method): +# def special_wrapper(self): +# print("Calling %s" % special_method) +# return getattr(getattr(self, "im_func"), special_method) +# return special_wrapper + +# type.__init__(cls, name, bases, dct) +# for i in ["__int__", "__repr__", "__str__", "__index__", "__add__", "__radd__", "__bytes__"]: +# setattr(cls, i, property(special_gen(i))) + +class _instance_state(): + def __init__(self, instance): + self.im_self = instance.__self__ + self.im_func = instance.__func__ + self.im_class = instance.__self__.__class__ + def __getattr__(self, attr): + return getattr(self.im_func, attr) + + def __call__(self, *args, **kargs): + return self.im_func(self.im_self, *args, **kargs) + def breaks(self): + return self.im_self.add_breakpoints(self.im_func) + def intercepts(self): + return self.im_self.add_interception_points(self.im_func) + def unbreaks(self): + return self.im_self.remove_breakpoints(self.im_func) + def unintercepts(self): + return self.im_self.remove_interception_points(self.im_func) + + +############## +## Automata ## +############## + +class ATMT: + STATE = "State" + ACTION = "Action" + CONDITION = "Condition" + RECV = "Receive condition" + TIMEOUT = "Timeout condition" + IOEVENT = "I/O event" + + class NewStateRequested(Exception): + def __init__(self, state_func, automaton, *args, **kargs): + self.func = state_func + self.state = state_func.atmt_state + self.initial = state_func.atmt_initial + self.error = state_func.atmt_error + self.final = state_func.atmt_final + Exception.__init__(self, "Request state [%s]" % self.state) + self.automaton = automaton + self.args = args + self.kargs = kargs + self.action_parameters() # init action parameters + def action_parameters(self, *args, **kargs): + self.action_args = args + self.action_kargs = kargs + return self + def run(self): + return self.func(self.automaton, *self.args, **self.kargs) + def __repr__(self): + return "NewStateRequested(%s)" % self.state + + @staticmethod + def state(initial=0,final=0,error=0): + def deco(f,initial=initial, final=final): + f.atmt_type = ATMT.STATE + f.atmt_state = f.__name__ + f.atmt_initial = initial + f.atmt_final = final + f.atmt_error = error +# @functools.wraps(f) This is possible alternative to assigning __qualname__; it would save __doc__, too + def state_wrapper(self, *args, **kargs): + return ATMT.NewStateRequested(f, self, *args, **kargs) + + state_wrapper.__qualname__ = "%s_wrapper" % f.__name__ + state_wrapper.atmt_type = ATMT.STATE + state_wrapper.atmt_state = f.__name__ + state_wrapper.atmt_initial = initial + state_wrapper.atmt_final = final + state_wrapper.atmt_error = error + state_wrapper.atmt_origfunc = f + return state_wrapper + return deco + @staticmethod + def action(cond, prio=0): + def deco(f,cond=cond): + if not hasattr(f,"atmt_type"): + f.atmt_cond = {} + f.atmt_type = ATMT.ACTION + f.atmt_cond[cond.atmt_condname] = prio + return f + return deco + @staticmethod + def condition(state, prio=0): + def deco(f, state=state): + f.atmt_type = ATMT.CONDITION + f.atmt_state = state.atmt_state + f.atmt_condname = f.__name__ + f.atmt_prio = prio + return f + return deco + @staticmethod + def receive_condition(state, prio=0): + def deco(f, state=state): + f.atmt_type = ATMT.RECV + f.atmt_state = state.atmt_state + f.atmt_condname = f.__name__ + f.atmt_prio = prio + return f + return deco + @staticmethod + def ioevent(state, name, prio=0, as_supersocket=None): + def deco(f, state=state): + f.atmt_type = ATMT.IOEVENT + f.atmt_state = state.atmt_state + f.atmt_condname = f.__name__ + f.atmt_ioname = name + f.atmt_prio = prio + f.atmt_as_supersocket = as_supersocket + return f + return deco + @staticmethod + def timeout(state, timeout): + def deco(f, state=state, timeout=timeout): + f.atmt_type = ATMT.TIMEOUT + f.atmt_state = state.atmt_state + f.atmt_timeout = timeout + f.atmt_condname = f.__name__ + return f + return deco + +class _ATMT_Command: + RUN = "RUN" + NEXT = "NEXT" + FREEZE = "FREEZE" + STOP = "STOP" + END = "END" + EXCEPTION = "EXCEPTION" + SINGLESTEP = "SINGLESTEP" + BREAKPOINT = "BREAKPOINT" + INTERCEPT = "INTERCEPT" + ACCEPT = "ACCEPT" + REPLACE = "REPLACE" + REJECT = "REJECT" + +class _ATMT_supersocket(SuperSocket): + def __init__(self, name, ioevent, automaton, proto, args, kargs): + self.name = name + self.ioevent = ioevent + self.proto = proto + self.spa,self.spb = socket.socketpair(socket.AF_UNIX, socket.SOCK_DGRAM) + kargs["external_fd"] = {ioevent:self.spb} + self.atmt = automaton(*args, **kargs) + self.atmt.runbg() + def fileno(self): + return self.spa.fileno() + def send(self, s): + if type(s) is str: + s = s.encode() + elif type(s) is not bytes: + s = bytes(s) + return self.spa.send(s) + def recv(self, n=MTU): + r = self.spa.recv(n) + if self.proto is not None: + r = self.proto(r) + return r + def close(self): + pass + +class _ATMT_to_supersocket: + def __init__(self, name, ioevent, automaton): + self.name = name + self.ioevent = ioevent + self.automaton = automaton + def __call__(self, proto, *args, **kargs): + return _ATMT_supersocket(self.name, self.ioevent, self.automaton, proto, args, kargs) + +class Automaton_metaclass(type): + def __new__(cls, name, bases, dct): + cls = super(Automaton_metaclass, cls).__new__(cls, name, bases, dct) + cls.states={} + cls.state = None + cls.recv_conditions={} + cls.conditions={} + cls.ioevents={} + cls.timeout={} + cls.actions={} + cls.initial_states=[] + cls.ionames = [] + cls.iosupersockets = [] + + members = {} + classes = [cls] + while classes: + c = classes.pop(0) # order is important to avoid breaking method overloading + classes += list(c.__bases__) + for k,v in c.__dict__.items(): + if k not in members: + members[k] = v + + decorated = [v for v in members.values() + if type(v) is types.FunctionType and hasattr(v, "atmt_type")] + + for m in decorated: + if m.atmt_type == ATMT.STATE: + s = m.atmt_state + cls.states[s] = m + cls.recv_conditions[s]=[] + cls.ioevents[s]=[] + cls.conditions[s]=[] + cls.timeout[s]=[] + if m.atmt_initial: + cls.initial_states.append(m) + elif m.atmt_type in [ATMT.CONDITION, ATMT.RECV, ATMT.TIMEOUT, ATMT.IOEVENT]: + cls.actions[m.atmt_condname] = [] + + for m in decorated: + if m.atmt_type == ATMT.CONDITION: + cls.conditions[m.atmt_state].append(m) + elif m.atmt_type == ATMT.RECV: + cls.recv_conditions[m.atmt_state].append(m) + elif m.atmt_type == ATMT.IOEVENT: + cls.ioevents[m.atmt_state].append(m) + cls.ionames.append(m.atmt_ioname) + if m.atmt_as_supersocket is not None: + cls.iosupersockets.append(m) + elif m.atmt_type == ATMT.TIMEOUT: + cls.timeout[m.atmt_state].append((m.atmt_timeout, m)) + elif m.atmt_type == ATMT.ACTION: + for c in m.atmt_cond: + cls.actions[c].append(m) + + + for v in cls.timeout.values(): + #v.sort(lambda (t1,f1),(t2,f2): cmp(t1,t2)) + v.sort(key = lambda x: x[0]) + v.append((None, None)) + for v in itertools.chain(cls.conditions.values(), + cls.recv_conditions.values(), + cls.ioevents.values()): + v.sort(key = lambda x: x.atmt_prio) + for condname,actlst in cls.actions.items(): + actlst.sort(key = lambda x: x.atmt_cond[condname]) + + for ioev in cls.iosupersockets: + setattr(cls, ioev.atmt_as_supersocket, _ATMT_to_supersocket(ioev.atmt_as_supersocket, ioev.atmt_ioname, cls)) + + return cls + + def graph(self, **kargs): + s = 'digraph "%s" {\n' % self.__class__.__name__ + + se = "" # Keep initial nodes at the begining for better rendering + for st in self.states.values(): + if st.atmt_initial: + se = ('\t"%s" [ style=filled, fillcolor=blue, shape=box, root=true];\n' % st.atmt_state)+se + elif st.atmt_final: + se += '\t"%s" [ style=filled, fillcolor=green, shape=octagon ];\n' % st.atmt_state + elif st.atmt_error: + se += '\t"%s" [ style=filled, fillcolor=red, shape=octagon ];\n' % st.atmt_state + s += se + + for st in self.states.values(): + for n in st.atmt_origfunc.__code__.co_names+st.atmt_origfunc.__code__.co_consts: + if n in self.states: + s += '\t"%s" -> "%s" [ color=green ];\n' % (st.atmt_state,n) + + + for c,k,v in ([("purple",k,v) for k,v in self.conditions.items()]+ + [("red",k,v) for k,v in self.recv_conditions.items()]+ + [("orange",k,v) for k,v in self.ioevents.items()]): + for f in v: + for n in f.__code__.co_names+f.__code__.co_consts: + if n in self.states: + l = f.atmt_condname + for x in self.actions[f.atmt_condname]: + l += "\\l>[%s]" % x.__name__ + s += '\t"%s" -> "%s" [label="%s", color=%s];\n' % (k,n,l,c) + for k,v in self.timeout.items(): + for t,f in v: + if f is None: + continue + for n in f.__code__.co_names+f.__code__.co_consts: + if n in self.states: + l = "%s/%.1fs" % (f.atmt_condname,t) + for x in self.actions[f.atmt_condname]: + l += "\\l>[%s]" % x.__name__ + s += '\t"%s" -> "%s" [label="%s",color=blue];\n' % (k,n,l) + s += "}\n" + return do_graph(s, **kargs) + + + +class Automaton(metaclass = Automaton_metaclass): + + ## Methods to overload + def parse_args(self, debug=0, store=1, **kargs): + self.debug_level=debug + self.socket_kargs = kargs + self.store_packets = store + + def master_filter(self, pkt): + return True + + def my_send(self, pkt): + self.send_sock.send(pkt) + + + ## Utility classes and exceptions + class _IO_fdwrapper: + def __init__(self,rd,wr): + if rd is not None and type(rd) is not int: + rd = rd.fileno() + if wr is not None and type(wr) is not int: + wr = wr.fileno() + self.rd = rd + self.wr = wr + def fileno(self): + return self.rd + def read(self, n=65535): + return os.read(self.rd, n) + def write(self, msg): + return os.write(self.wr,msg) + def recv(self, n=65535): + return self.read(n) + def send(self, msg): + return self.write(msg) + + class _IO_mixer: + def __init__(self,rd,wr): + self.rd = rd + self.wr = wr + def fileno(self): + if type(self.rd) is int: + return self.rd + return self.rd.fileno() + def recv(self, n=None): + return self.rd.recv(n) + def read(self, n=None): + return self.rd.recv(n) + def send(self, msg): + return self.wr.send(msg) + def write(self, msg): + return self.wr.send(msg) + + + class AutomatonException(Exception): + def __init__(self, msg, state=None, result=None): + Exception.__init__(self, msg) + self.state = state + self.result = result + + class AutomatonError(AutomatonException): + pass + class ErrorState(AutomatonException): + pass + class Stuck(AutomatonException): + pass + class AutomatonStopped(AutomatonException): + pass + + class Breakpoint(AutomatonStopped): + pass + class Singlestep(AutomatonStopped): + pass + class InterceptionPoint(AutomatonStopped): + def __init__(self, msg, state=None, result=None, packet=None): + Automaton.AutomatonStopped.__init__(self, msg, state=state, result=result) + self.packet = packet + + class CommandMessage(AutomatonException): + pass + + + ## Services + def debug(self, lvl, msg): + if self.debug_level >= lvl: + log_interactive.debug(msg) + + def send(self, pkt): + if self.state.state in self.interception_points: + self.debug(3,"INTERCEPT: packet intercepted: %s" % pkt.summary()) + self.intercepted_packet = pkt + cmd = Message(type = _ATMT_Command.INTERCEPT, state=self.state, pkt=pkt) + self.cmdout.send(cmd) + cmd = self.cmdin.recv() + self.intercepted_packet = None + if cmd.type == _ATMT_Command.REJECT: + self.debug(3,"INTERCEPT: packet rejected") + return + elif cmd.type == _ATMT_Command.REPLACE: + pkt = cmd.pkt + self.debug(3,"INTERCEPT: packet replaced by: %s" % pkt.summary()) + elif cmd.type == _ATMT_Command.ACCEPT: + self.debug(3,"INTERCEPT: packet accepted") + else: + raise self.AutomatonError("INTERCEPT: unkown verdict: %r" % cmd.type) + self.my_send(pkt) + self.debug(3,"SENT : %s" % pkt.summary()) + self.packets.append(pkt.copy()) + + + ## Internals + def __init__(self, *args, **kargs): + external_fd = kargs.pop("external_fd",{}) + self.send_sock_class = kargs.pop("ll", conf.L3socket) + self.started = _thread.allocate_lock() + self.threadid = None + self.breakpointed = None + self.breakpoints = set() + self.interception_points = set() + self.intercepted_packet = None + self.debug_level=0 + self.init_args=args + self.init_kargs=kargs + self.io = type.__new__(type, "IOnamespace",(),{}) + self.oi = type.__new__(type, "IOnamespace",(),{}) + self.cmdin = ObjectPipe() + self.cmdout = ObjectPipe() + self.ioin = {} + self.ioout = {} + for n in self.ionames: + extfd = external_fd.get(n) + if type(extfd) is not tuple: + extfd = (extfd,extfd) + ioin,ioout = extfd + if ioin is None: + ioin = ObjectPipe() + #elif type(ioin) is not types.InstanceType: + else: + #print(type(ioin)) + ioin = self._IO_fdwrapper(ioin,None) + if ioout is None: + ioout = ObjectPipe() + #elif type(ioout) is not types.InstanceType: + else: + #print(type(ioout)) + ioout = self._IO_fdwrapper(None,ioout) + + self.ioin[n] = ioin + self.ioout[n] = ioout + ioin.ioname = n + ioout.ioname = n + setattr(self.io, n, self._IO_mixer(ioout,ioin)) + setattr(self.oi, n, self._IO_mixer(ioin,ioout)) + + for stname in self.states: + setattr(self, stname, + _instance_state(getattr(self, stname))) + + self.parse_args(*args, **kargs) + + self.start() + + def __iter__(self): + return self + + def __del__(self): + self.stop() + + def _run_condition(self, cond, *args, **kargs): + try: + self.debug(5, "Trying %s [%s]" % (cond.atmt_type, cond.atmt_condname)) + cond(self,*args, **kargs) + except ATMT.NewStateRequested as state_req: + self.debug(2, "%s [%s] taken to state [%s]" % (cond.atmt_type, cond.atmt_condname, state_req.state)) + if cond.atmt_type == ATMT.RECV: + self.packets.append(args[0]) + for action in self.actions[cond.atmt_condname]: + self.debug(2, " + Running action [%s]" % action.__name__) + action(self, *state_req.action_args, **state_req.action_kargs) + raise + except Exception as e: + self.debug(2, "%s [%s] raised exception [%s]" % (cond.atmt_type, cond.atmt_condname, e)) + raise + else: + self.debug(2, "%s [%s] not taken" % (cond.atmt_type, cond.atmt_condname)) + + def _do_start(self, *args, **kargs): + + _thread.start_new_thread(self._do_control, args, kargs) + + + def _do_control(self, *args, **kargs): + with self.started: + self.threadid = _thread.get_ident() + + # Update default parameters + a = args+self.init_args[len(args):] + k = self.init_kargs.copy() + k.update(kargs) + self.parse_args(*a,**k) + + # Start the automaton + self.state=self.initial_states[0](self) + self.send_sock = self.send_sock_class() + self.listen_sock = conf.L2listen(**self.socket_kargs) + self.packets = PacketList(name="session[%s]"%self.__class__.__name__) + + singlestep = True + iterator = self._do_iter() + self.debug(3, "Starting control thread [tid=%i]" % self.threadid) + try: + while True: + c = self.cmdin.recv() + self.debug(5, "Received command %s" % c.type) + if c.type == _ATMT_Command.RUN: + singlestep = False + elif c.type == _ATMT_Command.NEXT: + singlestep = True + elif c.type == _ATMT_Command.FREEZE: + continue + elif c.type == _ATMT_Command.STOP: + break + while True: + state = next(iterator) + if isinstance(state, self.CommandMessage): + break + elif isinstance(state, self.Breakpoint): + c = Message(type=_ATMT_Command.BREAKPOINT,state=state) + self.cmdout.send(c) + break + if singlestep: + c = Message(type=_ATMT_Command.SINGLESTEP,state=state) + self.cmdout.send(c) + break + except StopIteration as e: + c = Message(type=_ATMT_Command.END, result=e.args[0]) + self.cmdout.send(c) + except Exception as e: + self.debug(3, "Transfering exception [%s] from tid=%i"% (e,self.threadid)) + m = Message(type = _ATMT_Command.EXCEPTION, exception=e, exc_info=sys.exc_info()) + self.cmdout.send(m) + self.debug(3, "Stopping control thread (tid=%i)"%self.threadid) + self.threadid = None + + def _do_iter(self): + while True: + try: + self.debug(1, "## state=[%s]" % self.state.state) + + # Entering a new state. First, call new state function + if self.state.state in self.breakpoints and self.state.state != self.breakpointed: + self.breakpointed = self.state.state + yield self.Breakpoint("breakpoint triggered on state %s" % self.state.state, + state = self.state.state) + self.breakpointed = None + state_output = self.state.run() + if self.state.error: + raise self.ErrorState("Reached %s: [%r]" % (self.state.state, state_output), + result=state_output, state=self.state.state) + if self.state.final: + raise StopIteration(state_output) + + if state_output is None: + state_output = () + elif type(state_output) is not list: + state_output = state_output, + + # Then check immediate conditions + for cond in self.conditions[self.state.state]: + self._run_condition(cond, *state_output) + + # If still there and no conditions left, we are stuck! + if ( len(self.recv_conditions[self.state.state]) == 0 and + len(self.ioevents[self.state.state]) == 0 and + len(self.timeout[self.state.state]) == 1 ): + raise self.Stuck("stuck in [%s]" % self.state.state, + state=self.state.state, result=state_output) + + # Finally listen and pay attention to timeouts + expirations = iter(self.timeout[self.state.state]) + next_timeout,timeout_func = next(expirations) + t0 = time.time() + + fds = [self.cmdin] + if len(self.recv_conditions[self.state.state]) > 0: + fds.append(self.listen_sock) + for ioev in self.ioevents[self.state.state]: + fds.append(self.ioin[ioev.atmt_ioname]) + while 1: + t = time.time()-t0 + if next_timeout is not None: + if next_timeout <= t: + self._run_condition(timeout_func, *state_output) + next_timeout,timeout_func = next(expirations) + if next_timeout is None: + remain = None + else: + remain = next_timeout-t + + self.debug(5, "Select on %r" % fds) + r,_,_ = select(fds,[],[],remain) + self.debug(5, "Selected %r" % r) + for fd in r: + self.debug(5, "Looking at %r" % fd) + if fd == self.cmdin: + yield self.CommandMessage("Received command message") + elif fd == self.listen_sock: + pkt = self.listen_sock.recv(MTU) + if pkt is not None: + if self.master_filter(pkt): + self.debug(3, "RECVD: %s" % pkt.summary()) + for rcvcond in self.recv_conditions[self.state.state]: + self._run_condition(rcvcond, pkt, *state_output) + else: + self.debug(4, "FILTR: %s" % pkt.summary()) + else: + self.debug(3, "IOEVENT on %s" % fd.ioname) + for ioevt in self.ioevents[self.state.state]: + if ioevt.atmt_ioname == fd.ioname: + self._run_condition(ioevt, fd, *state_output) + + except ATMT.NewStateRequested as state_req: + self.debug(2, "switching from [%s] to [%s]" % (self.state.state,state_req.state)) + self.state = state_req + yield state_req + + ## Public API + def add_interception_points(self, *ipts): + for ipt in ipts: + if hasattr(ipt,"atmt_state"): + ipt = ipt.atmt_state + self.interception_points.add(ipt) + + def remove_interception_points(self, *ipts): + for ipt in ipts: + if hasattr(ipt,"atmt_state"): + ipt = ipt.atmt_state + self.interception_points.discard(ipt) + + def add_breakpoints(self, *bps): + for bp in bps: + if hasattr(bp,"atmt_state"): + bp = bp.atmt_state + self.breakpoints.add(bp) + + def remove_breakpoints(self, *bps): + for bp in bps: + if hasattr(bp,"atmt_state"): + bp = bp.atmt_state + self.breakpoints.discard(bp) + + def start(self, *args, **kargs): + if not self.started.locked(): + self._do_start(*args, **kargs) + + def run(self, resume=None, wait=True): + if resume is None: + resume = Message(type = _ATMT_Command.RUN) + self.cmdin.send(resume) + if wait: + try: + c = self.cmdout.recv() + except KeyboardInterrupt: + self.cmdin.send(Message(type = _ATMT_Command.FREEZE)) + return + if c.type == _ATMT_Command.END: + return c.result + elif c.type == _ATMT_Command.INTERCEPT: + raise self.InterceptionPoint("packet intercepted", state=c.state.state, packet=c.pkt) + elif c.type == _ATMT_Command.SINGLESTEP: + raise self.Singlestep("singlestep state=[%s]"%c.state.state, state=c.state.state) + elif c.type == _ATMT_Command.BREAKPOINT: + raise self.Breakpoint("breakpoint triggered on state [%s]"%c.state.state, state=c.state.state) + elif c.type == _ATMT_Command.EXCEPTION: + raise c.exc_info[0](c.exc_info[1]) + #raise c.exc_info[0],c.exc_info[1],c.exc_info[2] + + def runbg(self, resume=None, wait=False): + self.run(resume, wait) + + def next(self): + return self.run(resume = Message(type=_ATMT_Command.NEXT)) + + def stop(self): + self.cmdin.send(Message(type=_ATMT_Command.STOP)) + with self.started: + # Flush command pipes + while True: + r,_,_ = select([self.cmdin, self.cmdout],[],[],0) + if not r: + break + for fd in r: + fd.recv() + + def restart(self, *args, **kargs): + self.stop() + self.start(*args, **kargs) + + def accept_packet(self, pkt=None, wait=False): + rsm = Message() + if pkt is None: + rsm.type = _ATMT_Command.ACCEPT + else: + rsm.type = _ATMT_Command.REPLACE + rsm.pkt = pkt + return self.run(resume=rsm, wait=wait) + + def reject_packet(self, wait=False): + rsm = Message(type = _ATMT_Command.REJECT) + return self.run(resume=rsm, wait=wait) + + + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/autorun.py b/scripts/external_libs/scapy-python3-0.18/scapy/autorun.py new file mode 100644 index 00000000..063d93dc --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/autorun.py @@ -0,0 +1,142 @@ +## 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 + +""" +Run commands when the Scapy interpreter starts. +""" + +import code,sys +from .config import conf +from .themes import * +from .error import Scapy_Exception +from .utils import tex_escape + + +######################### +##### Autorun stuff ##### +######################### + +class StopAutorun(Scapy_Exception): + code_run = "" + +class ScapyAutorunInterpreter(code.InteractiveInterpreter): + def __init__(self, *args, **kargs): + code.InteractiveInterpreter.__init__(self, *args, **kargs) + self.error = 0 + def showsyntaxerror(self, *args, **kargs): + self.error = 1 + return code.InteractiveInterpreter.showsyntaxerror(self, *args, **kargs) + def showtraceback(self, *args, **kargs): + self.error = 1 + exc_type, exc_value, exc_tb = sys.exc_info() + if isinstance(exc_value, StopAutorun): + raise exc_value + return code.InteractiveInterpreter.showtraceback(self, *args, **kargs) + + +def autorun_commands(cmds,my_globals=None,verb=0): + sv = conf.verb + import builtins + try: + try: + if my_globals is None: + my_globals = __import__("scapy.all").all.__dict__ + conf.verb = verb + interp = ScapyAutorunInterpreter(my_globals) + cmd = "" + cmds = cmds.splitlines() + cmds.append("") # ensure we finish multiline commands + cmds.reverse() + builtins.__dict__["_"] = None + while 1: + if cmd: + sys.stderr.write(sys.__dict__.get("ps2","... ")) + else: + sys.stderr.write(str(sys.__dict__.get("ps1",ColorPrompt()))) + + l = cmds.pop() + print(l) + cmd += "\n"+l + if interp.runsource(cmd): + continue + if interp.error: + return 0 + cmd = "" + if len(cmds) <= 1: + break + except SystemExit: + pass + finally: + conf.verb = sv + return _ + +def autorun_get_interactive_session(cmds, **kargs): + class StringWriter: + def __init__(self): + self.s = "" + def write(self, x): + self.s += x + + sw = StringWriter() + sstdout,sstderr = sys.stdout,sys.stderr + try: + try: + sys.stdout = sys.stderr = sw + res = autorun_commands(cmds, **kargs) + except StopAutorun as e: + e.code_run = sw.s + raise + finally: + sys.stdout,sys.stderr = sstdout,sstderr + return sw.s,res + +def autorun_get_text_interactive_session(cmds, **kargs): + ct = conf.color_theme + try: + conf.color_theme = NoTheme() + s,res = autorun_get_interactive_session(cmds, **kargs) + finally: + conf.color_theme = ct + return s,res + +def autorun_get_ansi_interactive_session(cmds, **kargs): + ct = conf.color_theme + try: + conf.color_theme = DefaultTheme() + s,res = autorun_get_interactive_session(cmds, **kargs) + finally: + conf.color_theme = ct + return s,res + +def autorun_get_html_interactive_session(cmds, **kargs): + ct = conf.color_theme + to_html = lambda s: s.replace("<","<").replace(">",">").replace("#[#","<").replace("#]#",">") + try: + try: + conf.color_theme = HTMLTheme2() + s,res = autorun_get_interactive_session(cmds, **kargs) + except StopAutorun as e: + e.code_run = to_html(e.code_run) + raise + finally: + conf.color_theme = ct + + return to_html(s),res + +def autorun_get_latex_interactive_session(cmds, **kargs): + ct = conf.color_theme + to_latex = lambda s: tex_escape(s).replace("@[@","{").replace("@]@","}").replace("@`@","\\") + try: + try: + conf.color_theme = LatexTheme2() + s,res = autorun_get_interactive_session(cmds, **kargs) + except StopAutorun as e: + e.code_run = to_latex(e.code_run) + raise + finally: + conf.color_theme = ct + return to_latex(s),res + + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/base_classes.py b/scripts/external_libs/scapy-python3-0.18/scapy/base_classes.py new file mode 100644 index 00000000..bace90d6 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/base_classes.py @@ -0,0 +1,237 @@ +## 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 + +""" +Generators and packet meta classes. +""" + +############### +## Generators ## +################ + +import re,random,socket +from types import GeneratorType +import scapy.config +from . import error + +class Gen(object): + def __iter__(self): + return iter([]) + +class SetGen(Gen): + def __init__(self, col, _iterpacket=1): + self._iterpacket=_iterpacket + if type(col) is list or isinstance(col, GeneratorType): + self.col = col + elif isinstance(col, BasePacketList): + self.col = list(col) + else: + self.col = [col] + # DEPRECATED + # def transf(self, element): + # return element + def __iter__(self): + for i in self.col: + if (type(i) is tuple) and (len(i) == 2) and type(i[0]) is int and type(i[1]) is int: + if (i[0] <= i[1]): + j=i[0] + while j <= i[1]: + yield j + j += 1 + elif isinstance(i, Gen) and (self._iterpacket or not isinstance(i,BasePacket)): + for j in i: + yield j + else: + yield i + def __repr__(self): + return "" % self.col.__repr__() + +class Net(Gen): + """Generate a list of IPs from a network address or a name""" + name = "ip" + ipaddress = re.compile(r"^(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)(/[0-3]?[0-9])?$") + + @staticmethod + def _parse_digit(a,netmask): + netmask = min(8,max(netmask,0)) + if a == "*": + a = (0,256) + elif a.find("-") >= 0: + x,y = map(int,a.split("-")) + if x > y: + y = x + a = (x & (0xff<>(8-netmask))))+1) + else: + a = (int(a) & (0xff<>(8-netmask)))+1) + return a + + @classmethod + def _parse_net(cls, net): + tmp=net.split('/')+["32"] + if not cls.ipaddress.match(net): + tmp[0]=socket.gethostbyname(tmp[0]) + netmask = int(tmp[1]) + #return map(lambda x,y: cls._parse_digit(x,y), tmp[0].split("."), map(lambda x,nm=netmask: x-nm, (8,16,24,32))),netmask + return list(map(lambda x,y: cls._parse_digit(x,y), tmp[0].split("."), [ i - netmask for i in (8,16,24,32)] )),netmask + + def __init__(self, net): + self.repr=net + self.parsed,self.netmask = self._parse_net(net) + + def __iter__(self): + for d in range(*self.parsed[3]): + for c in range(*self.parsed[2]): + for b in range(*self.parsed[1]): + for a in range(*self.parsed[0]): + yield "%i.%i.%i.%i" % (a,b,c,d) + def choice(self): + ip = [] + for v in self.parsed: + ip.append(str(random.randint(v[0],v[1]-1))) + return ".".join(ip) + + def __repr__(self): + return "Net(%r)" % self.repr + def __eq__(self, other): + if hasattr(other, "parsed"): + p2 = other.parsed + else: + p2,nm2 = self._parse_net(other) + return self.parsed == p2 + def __contains__(self, other): + if hasattr(other, "parsed"): + p2 = other.parsed + else: + p2,nm2 = self._parse_net(other) + for (a1,b1),(a2,b2) in zip(self.parsed,p2): + if a1 > a2 or b1 < b2: + return False + return True + def __rcontains__(self, other): + return self in self.__class__(other) + + +class OID(Gen): + name = "OID" + def __init__(self, oid): + self.oid = oid + self.cmpt = [] + fmt = [] + for i in oid.split("."): + if "-" in i: + fmt.append("%i") + self.cmpt.append(tuple(map(int, i.split("-")))) + else: + fmt.append(i) + self.fmt = ".".join(fmt) + def __repr__(self): + return "OID(%r)" % self.oid + def __iter__(self): + ii = [k[0] for k in self.cmpt] + while 1: + yield self.fmt % tuple(ii) + i = 0 + while 1: + if i >= len(ii): + raise StopIteration + if ii[i] < self.cmpt[i][1]: + ii[i]+=1 + break + else: + ii[i] = self.cmpt[i][0] + i += 1 + + + +###################################### +## Packet abstract and base classes ## +###################################### + +class Packet_metaclass(type): + def __new__(cls, name, bases, dct): + if "fields_desc" in dct: # perform resolution of references to other packets + current_fld = dct["fields_desc"] + resolved_fld = [] + for f in current_fld: + if isinstance(f, Packet_metaclass): # reference to another fields_desc + for f2 in f.fields_desc: + resolved_fld.append(f2) + else: + resolved_fld.append(f) + else: # look for a field_desc in parent classes + resolved_fld = None + for b in bases: + if hasattr(b,"fields_desc"): + resolved_fld = b.fields_desc + break + + if resolved_fld: # perform default value replacements + final_fld = [] + for f in resolved_fld: + if f.name in dct: + f = f.copy() + f.default = dct[f.name] + del(dct[f.name]) + final_fld.append(f) + + dct["fields_desc"] = final_fld + + newcls = super(Packet_metaclass, cls).__new__(cls, name, bases, dct) + if hasattr(newcls,"register_variant"): + newcls.register_variant() + for f in newcls.fields_desc: + f.register_owner(newcls) + scapy.config.conf.layers.register(newcls) + return newcls + + def __getattr__(self, attr): + for k in self.fields_desc: + if k.name == attr: + return k + raise AttributeError(attr) + + def __call__(cls, *args, **kargs): + if "dispatch_hook" in cls.__dict__: + cls = cls.dispatch_hook(*args, **kargs) + i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__) + i.__init__(*args, **kargs) + return i + + +class NewDefaultValues(Packet_metaclass): + """NewDefaultValues is deprecated (not needed anymore) + + remove this: + __metaclass__ = NewDefaultValues + and it should still work. + """ + def __new__(cls, name, bases, dct): + from error import log_loading + import traceback + try: + for tb in traceback.extract_stack()+[("??",-1,None,"")]: + f,l,_,line = tb + if line.startswith("class"): + break + except: + f,l="??",-1 + raise + log_loading.warning("Deprecated (no more needed) use of NewDefaultValues (%s l. %i)." % (f,l)) + + return super(NewDefaultValues, cls).__new__(cls, name, bases, dct) + +class BasePacket(Gen): + pass + + +############################# +## Packet list base classe ## +############################# + +class BasePacketList: + pass + + + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/config.py b/scripts/external_libs/scapy-python3-0.18/scapy/config.py new file mode 100644 index 00000000..88c324f8 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/config.py @@ -0,0 +1,394 @@ +## 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 + +""" +Implementation for of the configuration object. +""" + +import os,time,socket,sys +from .data import * +import scapy.base_classes +import scapy.themes +from .error import log_scapy + +############ +## Config ## +############ + +class ConfClass(object): + def configure(self, cnf): + self.__dict__ = cnf.__dict__.copy() + def __repr__(self): + return str(self) + def __str__(self): + s="" + keys = self.__class__.__dict__.copy() + keys.update(self.__dict__) + keys = list(keys.keys()) + keys.sort() + for i in keys: + if i[0] != "_": + r = repr(getattr(self, i)) + r = " ".join(r.split()) + wlen = 76-max(len(i),10) + if len(r) > wlen: + r = r[:wlen-3]+"..." + s += "%-10s = %s\n" % (i, r) + return s[:-1] + +class Interceptor(object): + def __init__(self, name, default, hook, args=None, kargs=None): + self.name = name + self.intname = "_intercepted_%s" % name + self.default=default + self.hook = hook + self.args = args if args is not None else [] + self.kargs = kargs if kargs is not None else {} + def __get__(self, obj, typ=None): + if not hasattr(obj, self.intname): + setattr(obj, self.intname, self.default) + return getattr(obj, self.intname) + def __set__(self, obj, val): + setattr(obj, self.intname, val) + self.hook(self.name, val, *self.args, **self.kargs) + + +class ProgPath(ConfClass): + pdfreader = "acroread" + psreader = "gv" + dot = "dot" + display = "display" + tcpdump = "tcpdump" + tcpreplay = "tcpreplay" + hexedit = "hexedit" + wireshark = "wireshark" + + +class ConfigFieldList: + def __init__(self): + self.fields = set() + self.layers = set() + @staticmethod + def _is_field(f): + return hasattr(f, "owners") + def _recalc_layer_list(self): + self.layers = set([owner for f in self.fields for owner in f.owners]) + def add(self, *flds): + self.fields |= set([f for f in flds if self._is_field(f)]) + self._recalc_layer_list() + def remove(self, *flds): + self.fields -= set(flds) + self._recalc_layer_list() + def __contains__(self, elt): + if isinstance(elt, scapy.base_classes.Packet_metaclass): + return elt in self.layers + return elt in self.fields + def __repr__(self): + return "<%s [%s]>" % (self.__class__.__name__," ".join(str(x) for x in self.fields)) + +class Emphasize(ConfigFieldList): + pass + +class Resolve(ConfigFieldList): + pass + + +class Num2Layer: + def __init__(self): + self.num2layer = {} + self.layer2num = {} + + def register(self, num, layer): + self.register_num2layer(num, layer) + self.register_layer2num(num, layer) + + def register_num2layer(self, num, layer): + self.num2layer[num] = layer + def register_layer2num(self, num, layer): + self.layer2num[layer] = num + + def __getitem__(self, item): + if isinstance(item, scapy.base_classes.Packet_metaclass): + return self.layer2num[item] + return self.num2layer[item] + def __contains__(self, item): + if isinstance(item, scapy.base_classes.Packet_metaclass): + return item in self.layer2num + return item in self.num2layer + def get(self, item, default=None): + if item in self: + return self[item] + return default + + def __repr__(self): + lst = [] + for num,layer in self.num2layer.items(): + if layer in self.layer2num and self.layer2num[layer] == num: + dir = "<->" + else: + dir = " ->" + lst.append((num,"%#6x %s %-20s (%s)" % (num,dir,layer.__name__,layer.name))) + for layer,num in self.layer2num.items(): + if num not in self.num2layer or self.num2layer[num] != layer: + lst.append((num,"%#6x <- %-20s (%s)" % (num,layer.__name__,layer.name))) + lst.sort() + return "\n".join(y for x,y in lst) + + +class LayersList(list): + def __repr__(self): + s=[] + for l in self: + s.append("%-20s: %s" % (l.__name__,l.name)) + return "\n".join(s) + def register(self, layer): + self.append(layer) + +class CommandsList(list): + def __repr__(self): + s=[] + for l in sorted(self,key=lambda x:x.__name__): + if l.__doc__: + doc = l.__doc__.split("\n")[0] + else: + doc = "--" + s.append("%-20s: %s" % (l.__name__,doc)) + return "\n".join(s) + def register(self, cmd): + self.append(cmd) + return cmd # return cmd so that method can be used as a decorator + +def lsc(): + print(repr(conf.commands)) + +class CacheInstance(dict): + def __init__(self, name="noname", timeout=None): + self.timeout = timeout + self.name = name + self._timetable = {} + def __getitem__(self, item): + val = dict.__getitem__(self,item) + if self.timeout is not None: + t = self._timetable[item] + if time.time()-t > self.timeout: + raise KeyError(item) + return val + def get(self, item, default=None): + # overloading this method is needed to force the dict to go through + # the timetable check + try: + return self[item] + except KeyError: + return default + def __setitem__(self, item, v): + try: + self._timetable[item] = time.time() + except AttributeError: + pass + dict.__setitem__(self, item,v) + def update(self, other): + dict.update(self, other) + self._timetable.update(other._timetable) + def items(self): + if self.timeout is None: + return dict.items(self) + t0=time.time() + return ((k,v) for (k,v) in dict.items(self) if t0-self._timetable[k] < self.timeout) + def keys(self): + if self.timeout is None: + return dict.keys(self) + t0=time.time() + return (k for k in dict.keys(self) if t0-self._timetable[k] < self.timeout) + def __iter__(self): + return self.keys() + def values(self): + if self.timeout is None: + return dict.values(self) + t0=time.time() + return (v for (k,v) in dict.items(self) if t0-self._timetable[k] < self.timeout) + def items(self): + if self.timeout is None: + return dict.items(self) + t0=time.time() + return [(k,v) for (k,v) in dict.items(self) if t0-self._timetable[k] < self.timeout] + def keys(self): + if self.timeout is None: + return dict.keys(self) + t0=time.time() + return [k for k in dict.keys(self) if t0-self._timetable[k] < self.timeout] + def values(self): + if self.timeout is None: + return dict.values(self) + t0=time.time() + return [v for (k,v) in dict.items(self) if t0-self._timetable[k] < self.timeout] + def __len__(self): + if self.timeout is None: + return dict.__len__(self) + return len(self.keys()) + def summary(self): + return "%s: %i valid items. Timeout=%rs" % (self.name, len(self), self.timeout) + def __repr__(self): + s = [] + if self: + mk = max(len(k) for k in self.keys()) + fmt = "%%-%is %%s" % (mk+1) + for item in self.items(): + s.append(fmt % item) + return "\n".join(s) + + + + +class NetCache: + def __init__(self): + self._caches_list = [] + + + def add_cache(self, cache): + self._caches_list.append(cache) + setattr(self,cache.name,cache) + def new_cache(self, name, timeout=None): + c = CacheInstance(name=name, timeout=timeout) + self.add_cache(c) + def __delattr__(self, attr): + raise AttributeError("Cannot delete attributes") + def update(self, other): + for co in other._caches_list: + if hasattr(self, co.name): + getattr(self,co.name).update(co) + else: + self.add_cache(co.copy()) + def flush(self): + for c in self._caches_list: + c.flush() + def __repr__(self): + return "\n".join(c.summary() for c in self._caches_list) + + +class LogLevel(object): + def __get__(self, obj, otype): + return obj._logLevel + def __set__(self,obj,val): + log_scapy.setLevel(val) + obj._logLevel = val + + + +def _prompt_changer(attr,val): + prompt = conf.prompt + try: + ct = val + if isinstance(ct, AnsiColorTheme) and ct.prompt(""): + ## ^A and ^B delimit invisible caracters for readline to count right. + ## And we need ct.prompt() to do change something or else ^A and ^B will be + ## displayed + prompt = "\001%s\002" % ct.prompt("\002"+prompt+"\001") + else: + prompt = ct.prompt(prompt) + except: + pass + sys.ps1 = prompt + + +class Conf(ConfClass): + """This object contains the configuration of scapy. +session : filename where the session will be saved +interactive_shell : If set to "ipython", use IPython as shell. Default: IPython. +ipython_embedded : If True use embedded ipython shell, standard ipython shell otherwise. +stealth : if 1, prevents any unwanted packet to go out (ARP, DNS, ...) +checkIPID: if 0, doesn't check that IPID matches between IP sent and ICMP IP citation received + if 1, checks that they either are equal or byte swapped equals (bug in some IP stacks) + if 2, strictly checks that they are equals +checkIPsrc: if 1, checks IP src in IP and ICMP IP citation match (bug in some NAT stacks) +check_TCPerror_seqack: if 1, also check that TCP seq and ack match the ones in ICMP citation +iff : selects the default output interface for srp() and sendp(). default:"eth0") +verb : level of verbosity, from 0 (almost mute) to 3 (verbose) +promisc : default mode for listening socket (to get answers if you spoof on a lan) +sniff_promisc : default mode for sniff() +filter : bpf filter added to every sniffing socket to exclude traffic from analysis +histfile : history file +padding : includes padding in desassembled packets +except_filter : BPF filter for packets to ignore +debug_match : when 1, store received packet that are not matched into debug.recv +route : holds the Scapy routing table and provides methods to manipulate it +warning_threshold : how much time between warnings from the same place +ASN1_default_codec: Codec used by default for ASN1 objects +mib : holds MIB direct access dictionnary +resolve : holds list of fields for which resolution should be done +noenum : holds list of enum fields for which conversion to string should NOT be done +AS_resolver: choose the AS resolver class to use +extensions_paths: path or list of paths where extensions are to be looked for +""" + version = "3.0.0" + session = "" + interactive = False + interactive_shell = "ipython" + ipython_embedded = True + stealth = "not implemented" + iface = None + readfunc = None + layers = LayersList() + commands = CommandsList() + logLevel = LogLevel() + checkIPID = 0 + checkIPsrc = 1 + checkIPaddr = 1 + check_TCPerror_seqack = 0 + verb = 2 + prompt = ">>> " + promisc = 1 + sniff_promisc = 1 + raw_layer = None + raw_summary = False + default_l2 = None + l2types = Num2Layer() + l3types = Num2Layer() + L3socket = None + L2socket = None + L2listen = None + histfile = os.path.join(os.path.expanduser("~"), ".scapy_history") + padding = 1 + except_filter = "" + debug_match = 0 + wepkey = "" + route = None # Filled by route.py + route6 = None # Filled by route6.py + auto_fragment = 1 + debug_dissector = 0 + color_theme = Interceptor("color_theme", scapy.themes.NoTheme(), _prompt_changer) + warning_threshold = 5 + prog = ProgPath() + resolve = Resolve() + noenum = Resolve() + emph = Emphasize() + use_dnet = False + use_winpcapy = False + use_netifaces = False + ipv6_enabled = socket.has_ipv6 + ethertypes = ETHER_TYPES + protocols = IP_PROTOS + services_tcp = TCP_SERVICES + services_udp = UDP_SERVICES + extensions_paths = "." + manufdb = MANUFDB + stats_classic_protocols = [] + stats_dot11_protocols = [] + temp_files = [] + netcache = NetCache() + load_layers = [ "l2", "inet", "dhcp", "dns", "dot11", "gprs", "hsrp", "inet6", "ir", "isakmp", "l2tp", + "mgcp", "mobileip", "netbios", "netflow", "ntp", "ppp", "radius", "rip", "rtp", + "sebek", "skinny", "smb", "snmp", "tftp", "x509", "bluetooth", "dhcp6", "llmnr", "sctp", "vrrp", + "ipsec" ] + +if not Conf.ipv6_enabled: + log_scapy.warning("IPv6 support disabled in Python. Cannot load scapy IPv6 layers.") + for m in ["inet6","dhcp6"]: + if m in Conf.load_layers: + Conf.load_layers.remove(m) + + +conf=Conf() +conf.logLevel=30 # 30=Warning + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/__init__.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/__init__.py new file mode 100644 index 00000000..99654377 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/__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 + +""" +Package of contrib modules that have to be loaded explicitly. +""" diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/avs.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/avs.py new file mode 100644 index 00000000..461b94b8 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/avs.py @@ -0,0 +1,57 @@ +#! /usr/bin/env python + +# http://trac.secdev.org/scapy/ticket/82 + +# scapy.contrib.description = AVS WLAN Monitor Header +# scapy.contrib.status = loads + +from scapy.packet import * +from scapy.fields import * +from scapy.layers.dot11 import * + +AVSWLANPhyType = { 0 : "Unknown", + 1 : "FHSS 802.11 '97", + 2 : "DSSS 802.11 '97", + 3 : "IR Baseband", + 4 : "DSSS 802.11b", + 5 : "PBCC 802.11b", + 6 : "OFDM 802.11g", + 7 : "PBCC 802.11g", + 8 : "OFDM 802.11a" } + +AVSWLANEncodingType = { 0 : "Unknown", + 1 : "CCK", + 2 : "PBCC", + 3 : "OFDM"} + +AVSWLANSSIType = { 0 : "None", + 1 : "Normalized RSSI", + 2 : "dBm", + 3 : "Raw RSSI"} + +AVSWLANPreambleType = { 0 : "Unknown", + 1 : "Short", + 2 : "Long" } + + +class AVSWLANHeader(Packet): + """ iwpriv eth1 set_prismhdr 1 """ + name = "AVS WLAN Monitor Header" + fields_desc = [ IntField("version",1), + IntField("len",64), + LongField("mactime",0), + LongField("hosttime",0), + IntEnumField("phytype",0, AVSWLANPhyType), + IntField("channel",0), + IntField("datarate",0), + IntField("antenna",0), + IntField("priority",0), + IntEnumField("ssi_type",0, AVSWLANSSIType), + SignedIntField("ssi_signal",0), + SignedIntField("ssi_noise",0), + IntEnumField("preamble",0, AVSWLANPreambleType), + IntEnumField("encoding",0, AVSWLANEncodingType), + ] + +bind_layers(AVSWLANHeader, Dot11) + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/bgp.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/bgp.py new file mode 100644 index 00000000..525dac5f --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/bgp.py @@ -0,0 +1,168 @@ +#! /usr/bin/env python + +# http://trac.secdev.org/scapy/ticket/162 + +# scapy.contrib.description = BGP +# scapy.contrib.status = loads + +from scapy.packet import * +from scapy.fields import * +from scapy.layers.inet import TCP + + +class BGPIPField(Field): + """Represents how bgp dose an ip prefix in (length, prefix)""" + def mask2iplen(self,mask): + """turn the mask into the length in bytes of the ip field""" + return (mask + 7) // 8 + def h2i(self, pkt, h): + """human x.x.x.x/y to internal""" + ip,mask = re.split( '/', h) + return int(mask), ip + def i2h( self, pkt, i): + mask, ip = i + return ip + '/' + str( mask ) + def i2repr( self, pkt, i): + """make it look nice""" + return self.i2h(pkt,i) + def i2len(self, pkt, i): + """rely on integer division""" + mask, ip = i + return self.mask2iplen(mask) + 1 + def i2m(self, pkt, i): + """internal (ip as bytes, mask as int) to machine""" + mask, ip = i + ip = inet_aton( ip ) + return struct.pack(">B",mask) + ip[:self.mask2iplen(mask)] + def addfield(self, pkt, s, val): + return s+self.i2m(pkt, val) + def getfield(self, pkt, s): + l = self.mask2iplen( struct.unpack(">B",s[0])[0] ) + 1 + return s[l:], self.m2i(pkt,s[:l]) + def m2i(self,pkt,m): + mask = struct.unpack(">B",m[0])[0] + ip = "".join( [ m[i + 1] if i < self.mask2iplen(mask) else '\x00' for i in range(4)] ) + return (mask,inet_ntoa(ip)) + +class BGPHeader(Packet): + """The first part of any BGP packet""" + name = "BGP header" + fields_desc = [ + XBitField("marker",0xffffffffffffffffffffffffffffffff, 0x80 ), + ShortField("len", None), + ByteEnumField("type", 4, {0:"none", 1:"open",2:"update",3:"notification",4:"keep_alive"}), + ] + def post_build(self, p, pay): + if self.len is None and pay: + l = len(p) + len(pay) + p = p[:16]+struct.pack("!H", l)+p[18:] + return p+pay + +class BGPOptionalParameter(Packet): + """Format of optional Parameter for BGP Open""" + name = "BGP Optional Parameters" + fields_desc = [ + ByteField("type", 2), + ByteField("len", None), + StrLenField("value", "", length_from = lambda x: x.len), + ] + def post_build(self,p,pay): + if self.len is None: + l = len(p) - 2 # 2 is length without value + p = p[:1]+struct.pack("!B", l)+p[2:] + return p+pay + def extract_padding(self, p): + """any thing after this packet is extracted is padding""" + return "",p + +class BGPOpen(Packet): + """ Opens a new BGP session""" + name = "BGP Open Header" + fields_desc = [ + ByteField("version", 4), + ShortField("AS", 0), + ShortField("hold_time", 0), + IPField("bgp_id","0.0.0.0"), + ByteField("opt_parm_len", None), + PacketListField("opt_parm",[], BGPOptionalParameter, length_from=lambda p:p.opt_parm_len), + ] + def post_build(self, p, pay): + if self.opt_parm_len is None: + l = len(p) - 10 # 10 is regular length with no additional options + p = p[:9] + struct.pack("!B",l) +p[10:] + return p+pay + +class BGPAuthenticationData(Packet): + name = "BGP Authentication Data" + fields_desc = [ + ByteField("AuthenticationCode", 0), + ByteField("FormMeaning", 0), + FieldLenField("Algorithm", 0), + ] + +class BGPPathAttribute(Packet): + "the attribute of total path" + name = "BGP Attribute fields" + fields_desc = [ + FlagsField("flags", 0x40, 8, ["NA0","NA1","NA2","NA3","Extended-Length","Partial","Transitive","Optional"]), #Extened leght may not work + ByteEnumField("type", 1, {1:"ORIGIN", 2:"AS_PATH", 3:"NEXT_HOP", 4:"MULTI_EXIT_DISC", 5:"LOCAL_PREF", 6:"ATOMIC_AGGREGATE", 7:"AGGREGATOR"}), + ByteField("attr_len", None), + StrLenField("value", "", length_from = lambda p: p.attr_len), + ] + def post_build(self, p, pay): + if self.attr_len is None: + l = len(p) - 3 # 3 is regular length with no additional options + p = p[:2] + struct.pack("!B",l) +p[3:] + return p+pay + def extract_padding(self, p): + """any thing after this packet is extracted is padding""" + return "",p + +class BGPUpdate(Packet): + """Update the routes WithdrawnRoutes = UnfeasiableRoutes""" + name = "BGP Update fields" + fields_desc = [ + ShortField("withdrawn_len", None), + FieldListField("withdrawn",[], BGPIPField("","0.0.0.0/0"), length_from=lambda p:p.withdrawn_len), + ShortField("tp_len", None), + PacketListField("total_path", [], BGPPathAttribute, length_from = lambda p: p.tp_len), + FieldListField("nlri",[], BGPIPField("","0.0.0.0/0"), length_from=lambda p:p.underlayer.len - 23 - p.tp_len - p.withdrawn_len), # len should be BGPHeader.len + ] + def post_build(self,p,pay): + wl = self.withdrawn_len + subpacklen = lambda p: len ( str( p )) + subfieldlen = lambda p: BGPIPField("", "0.0.0.0/0").i2len(self, p ) + if wl is None: + wl = sum ( map ( subfieldlen , self.withdrawn)) + p = p[:0]+struct.pack("!H", wl)+p[2:] + if self.tp_len is None: + l = sum ( map ( subpacklen , self.total_path)) + p = p[:2+wl]+struct.pack("!H", l)+p[4+wl:] + return p+pay + +class BGPNotification(Packet): + name = "BGP Notification fields" + fields_desc = [ + ByteEnumField("ErrorCode",0,{1:"Message Header Error",2:"OPEN Message Error",3:"UPDATE Messsage Error",4:"Hold Timer Expired",5:"Finite State Machine",6:"Cease"}), + ByteEnumField("ErrorSubCode",0,{1:"MessageHeader",2:"OPENMessage",3:"UPDATEMessage"}), + LongField("Data", 0), + ] + +class BGPErrorSubcodes(Packet): + name = "BGP Error Subcodes" + Fields_desc = [ + ByteEnumField("MessageHeader",0,{1:"Connection Not Synchronized",2:"Bad Message Length",3:"Bad Messsage Type"}), + ByteEnumField("OPENMessage",0,{1:"Unsupported Version Number",2:"Bad Peer AS",3:"Bad BGP Identifier",4:"Unsupported Optional Parameter",5:"Authentication Failure",6:"Unacceptable Hold Time"}), + ByteEnumField("UPDATEMessage",0,{1:"Malformed Attribute List",2:"Unrecognized Well-Known Attribute",3:"Missing Well-Known Attribute",4:"Attribute Flags Error",5:"Attribute Length Error",6:"Invalid ORIGIN Attribute",7:"AS Routing Loop",8:"Invalid NEXT_HOP Attribute",9:"Optional Attribute Error",10:"Invalid Network Field",11:"Malformed AS_PATH"}), + ] + +bind_layers( TCP, BGPHeader, dport=179) +bind_layers( TCP, BGPHeader, sport=179) +bind_layers( BGPHeader, BGPOpen, type=1) +bind_layers( BGPHeader, BGPUpdate, type=2) +bind_layers( BGPHeader, BGPHeader, type=4) + + +if __name__ == "__main__": + interact(mydict=globals(), mybanner="BGP addon .05") + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/carp.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/carp.py new file mode 100644 index 00000000..e785adef --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/carp.py @@ -0,0 +1,65 @@ + +# scapy.contrib.description = CARP +# scapy.contrib.status = loads + +from scapy.packet import * +from scapy.layers.inet import IP +from scapy.fields import BitField, ByteField, XShortField, IntField, XIntField +from scapy.utils import checksum +import struct, hmac, hashlib + +class CARP(Packet): + name = "CARP" + fields_desc = [ BitField("version", 4, 4), + BitField("type", 4, 4), + ByteField("vhid", 1), + ByteField("advskew", 0), + ByteField("authlen", 0), + ByteField("demotion", 0), + ByteField("advbase", 0), + XShortField("chksum", 0), + XIntField("counter1", 0), + XIntField("counter2", 0), + XIntField("hmac1", 0), + XIntField("hmac2", 0), + XIntField("hmac3", 0), + XIntField("hmac4", 0), + XIntField("hmac5", 0) + ] + + def post_build(self, pkt, pay): + if self.chksum == None: + pkt = pkt[:6] + struct.pack("!H", checksum(pkt)) + pkt[8:] + + return pkt + +def build_hmac_sha1(pkt, pw = '\0' * 20, ip4l = [], ip6l = []): + if not pkt.haslayer(CARP): + return None + + p = pkt[CARP] + h = hmac.new(pw, digestmod = hashlib.sha1) + # XXX: this is a dirty hack. it needs to pack version and type into a single 8bit field + h.update('\x21') + # XXX: mac addy if different from special link layer. comes before vhid + h.update(struct.pack('!B', p.vhid)) + + sl = [] + for i in ip4l: + # sort ips from smallest to largest + sl.append(inet_aton(i)) + sl.sort() + + for i in sl: + h.update(i) + + # XXX: do ip6l sorting + + return h.digest() + +""" +XXX: Usually CARP is multicast to 224.0.0.18 but because of virtual setup, it'll +be unicast between nodes. Uncomment the following line for normal use +bind_layers(IP, CARP, proto=112, dst='224.0.0.18') +""" +bind_layers(IP, CARP, proto=112) diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/cdp.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/cdp.py new file mode 100644 index 00000000..ed336162 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/cdp.py @@ -0,0 +1,306 @@ +#! /usr/bin/env python + +# scapy.contrib.description = Cisco Discovery Protocol +# scapy.contrib.status = loads + +############################################################################# +## ## +## cdp.py --- Cisco Discovery Protocol (CDP) extension for Scapy ## +## ## +## Copyright (C) 2006 Nicolas Bareil ## +## Arnaud Ebalard ## +## EADS/CRC security team ## +## ## +## 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. ## +## ## +############################################################################# + +from scapy.packet import * +from scapy.fields import * +from scapy.layers.inet6 import * + + +##################################################################### +# Helpers and constants +##################################################################### + +# CDP TLV classes keyed by type +_cdp_tlv_cls = { 0x0001: "CDPMsgDeviceID", + 0x0002: "CDPMsgAddr", + 0x0003: "CDPMsgPortID", + 0x0004: "CDPMsgCapabilities", + 0x0005: "CDPMsgSoftwareVersion", + 0x0006: "CDPMsgPlatform", + 0x0007: "CDPMsgIPPrefix", + 0x0008: "CDPMsgProtoHello", + 0x0009: "CDPMsgVTPMgmtDomain", # CDPv2 + 0x000a: "CDPMsgNativeVLAN", # CDPv2 + 0x000b: "CDPMsgDuplex", # +# 0x000c: "CDPMsgGeneric", +# 0x000d: "CDPMsgGeneric", + 0x000e: "CDPMsgVoIPVLANReply", + 0x000f: "CDPMsgVoIPVLANQuery", + 0x0010: "CDPMsgPower", + 0x0011: "CDPMsgMTU", +# 0x0012: "CDPMsgTrustBitmap", +# 0x0013: "CDPMsgUntrustedPortCoS", +# 0x0014: "CDPMsgSystemName", +# 0x0015: "CDPMsgSystemOID", + 0x0016: "CDPMsgMgmtAddr", +# 0x0017: "CDPMsgLocation", + 0x0019: "CDPMsgUnknown19", +# 0x001a: "CDPPowerAvailable" + } + +_cdp_tlv_types = { 0x0001: "Device ID", + 0x0002: "Addresses", + 0x0003: "Port ID", + 0x0004: "Capabilities", + 0x0005: "Software Version", + 0x0006: "Platform", + 0x0007: "IP Prefix", + 0x0008: "Protocol Hello", + 0x0009: "VTP Mangement Domain", # CDPv2 + 0x000a: "Native VLAN", # CDPv2 + 0x000b: "Duplex", # + 0x000c: "CDP Unknown command (send us a pcap file)", + 0x000d: "CDP Unknown command (send us a pcap file)", + 0x000e: "VoIP VLAN Reply", + 0x000f: "VoIP VLAN Query", + 0x0010: "Power", + 0x0011: "MTU", + 0x0012: "Trust Bitmap", + 0x0013: "Untrusted Port CoS", + 0x0014: "System Name", + 0x0015: "System OID", + 0x0016: "Management Address", + 0x0017: "Location", + 0x0018: "CDP Unknown command (send us a pcap file)", + 0x0019: "CDP Unknown command (send us a pcap file)", + 0x001a: "Power Available"} + +def _CDPGuessPayloadClass(p, **kargs): + cls = conf.raw_layer + if len(p) >= 2: + t = struct.unpack("!H", p[:2])[0] + clsname = _cdp_tlv_cls.get(t, "CDPMsgGeneric") + cls = globals()[clsname] + + return cls(p, **kargs) + +class CDPMsgGeneric(Packet): + name = "CDP Generic Message" + fields_desc = [ XShortEnumField("type", None, _cdp_tlv_types), + FieldLenField("len", None, "val", "!H"), + StrLenField("val", "", length_from=lambda x:x.len - 4) ] + + + def guess_payload_class(self, p): + return conf.padding_layer # _CDPGuessPayloadClass + +class CDPMsgDeviceID(CDPMsgGeneric): + name = "Device ID" + type = 0x0001 + +_cdp_addr_record_ptype = {0x01: "NLPID", 0x02: "802.2"} +_cdp_addrrecord_proto_ip = "\xcc" +_cdp_addrrecord_proto_ipv6 = "\xaa\xaa\x03\x00\x00\x00\x86\xdd" + +class CDPAddrRecord(Packet): + name = "CDP Address" + fields_desc = [ ByteEnumField("ptype", 0x01, _cdp_addr_record_ptype), + FieldLenField("plen", None, "proto", "B"), + StrLenField("proto", None, length_from=lambda x:x.plen), + FieldLenField("addrlen", None, length_of=lambda x:x.addr), + StrLenField("addr", None, length_from=lambda x:x.addrlen)] + + def guess_payload_class(self, p): + return conf.padding_layer + +class CDPAddrRecordIPv4(CDPAddrRecord): + name = "CDP Address IPv4" + fields_desc = [ ByteEnumField("ptype", 0x01, _cdp_addr_record_ptype), + FieldLenField("plen", 1, "proto", "B"), + StrLenField("proto", _cdp_addrrecord_proto_ip, length_from=lambda x:x.plen), + ShortField("addrlen", 4), + IPField("addr", "0.0.0.0")] + +class CDPAddrRecordIPv6(CDPAddrRecord): + name = "CDP Address IPv6" + fields_desc = [ ByteEnumField("ptype", 0x02, _cdp_addr_record_ptype), + FieldLenField("plen", 8, "proto", "B"), + StrLenField("proto", _cdp_addrrecord_proto_ipv6, length_from=lambda x:x.plen), + ShortField("addrlen", 16), + IP6Field("addr", "::1")] + +def _CDPGuessAddrRecord(p, **kargs): + cls = conf.raw_layer + if len(p) >= 2: + plen = struct.unpack("B", p[1])[0] + proto = ''.join(struct.unpack("s" * plen, p[2:plen + 2])[0:plen]) + + if proto == _cdp_addrrecord_proto_ip: + clsname = "CDPAddrRecordIPv4" + elif proto == _cdp_addrrecord_proto_ipv6: + clsname = "CDPAddrRecordIPv6" + else: + clsname = "CDPAddrRecord" + + cls = globals()[clsname] + + return cls(p, **kargs) + +class CDPMsgAddr(CDPMsgGeneric): + name = "Addresses" + fields_desc = [ XShortEnumField("type", 0x0002, _cdp_tlv_types), + ShortField("len", None), + FieldLenField("naddr", None, "addr", "!I"), + PacketListField("addr", [], _CDPGuessAddrRecord, count_from=lambda x:x.naddr) ] + + def post_build(self, pkt, pay): + if self.len is None: + l = 8 + len(self.addr) * 9 + pkt = pkt[:2] + struct.pack("!H", l) + pkt[4:] + p = pkt + pay + return p + +class CDPMsgPortID(CDPMsgGeneric): + name = "Port ID" + fields_desc = [ XShortEnumField("type", 0x0003, _cdp_tlv_types), + FieldLenField("len", None, "iface", "!H"), + StrLenField("iface", "Port 1", length_from=lambda x:x.len - 4) ] + + +_cdp_capabilities = [ "Router", + "TransparentBridge", + "SourceRouteBridge", + "Switch", + "Host", + "IGMPCapable", + "Repeater"] + ["Bit%d" % x for x in range(25,0,-1)] + + +class CDPMsgCapabilities(CDPMsgGeneric): + name = "Capabilities" + fields_desc = [ XShortEnumField("type", 0x0004, _cdp_tlv_types), + ShortField("len", 8), + FlagsField("cap", 0, 32, _cdp_capabilities) ] + + +class CDPMsgSoftwareVersion(CDPMsgGeneric): + name = "Software Version" + type = 0x0005 + + +class CDPMsgPlatform(CDPMsgGeneric): + name = "Platform" + type = 0x0006 + +_cdp_duplex = { 0x00: "Half", 0x01: "Full" } + +# ODR Routing +class CDPMsgIPPrefix(CDPMsgGeneric): + name = "IP Prefix" + type = 0x0007 + fields_desc = [ XShortEnumField("type", 0x0007, _cdp_tlv_types), + ShortField("len", 8), + IPField("defaultgw", "192.168.0.1") ] + +# TODO : Do me !!!!!! 0x0008 +class CDPMsgProtoHello(CDPMsgGeneric): + name = "Protocol Hello" + type = 0x0008 + +class CDPMsgVTPMgmtDomain(CDPMsgGeneric): + name = "VTP Management Domain" + type = 0x0009 + +class CDPMsgNativeVLAN(CDPMsgGeneric): + name = "Native VLAN" + fields_desc = [ XShortEnumField("type", 0x000a, _cdp_tlv_types), + ShortField("len", 6), + ShortField("vlan", 1) ] + +class CDPMsgDuplex(CDPMsgGeneric): + name = "Duplex" + fields_desc = [ XShortEnumField("type", 0x000b, _cdp_tlv_types), + ShortField("len", 5), + ByteEnumField("duplex", 0x00, _cdp_duplex) ] + +class CDPMsgVoIPVLANReply(CDPMsgGeneric): + name = "VoIP VLAN Reply" + fields_desc = [ XShortEnumField("type", 0x000e, _cdp_tlv_types), + ShortField("len", 7), + ByteField("status?", 1), + ShortField("vlan", 1)] + + +# TODO : Do me !!! 0x000F +class CDPMsgVoIPVLANQuery(CDPMsgGeneric): + name = "VoIP VLAN Query" + type = 0x000f + +# fields_desc = [XShortEnumField("type", 0x000f, _cdp_tlv_types), +# FieldLenField("len", None, "val", "!H") ] + + +class _CDPPowerField(ShortField): + def i2repr(self, pkt, x): + if x is None: + x = 0 + return "%d mW" % x + + +class CDPMsgPower(CDPMsgGeneric): + name = "Power" + # Check if field length is fixed (2 bytes) + fields_desc = [ XShortEnumField("type", 0x0010, _cdp_tlv_types), + ShortField("len", 6), + _CDPPowerField("power", 1337)] + + +class CDPMsgMTU(CDPMsgGeneric): + name = "MTU" + # Check if field length is fixed (2 bytes) + fields_desc = [ XShortEnumField("type", 0x0011, _cdp_tlv_types), + ShortField("len", 6), + ShortField("mtu", 1500)] + +class CDPMsgMgmtAddr(CDPMsgAddr): + name = "Management Address" + type = 0x0016 + +class CDPMsgUnknown19(CDPMsgGeneric): + name = "Unknown CDP Message" + type = 0x0019 + +class CDPMsg(CDPMsgGeneric): + name = "CDP " + fields_desc = [ XShortEnumField("type", None, _cdp_tlv_types), + FieldLenField("len", None, "val", "!H"), + StrLenField("val", "", length_from=lambda x:x.len - 4) ] + +class _CDPChecksum: + def post_build(self, pkt, pay): + p = pkt + pay + if self.cksum is None: + cksum = checksum(p) + p = p[:2] + struct.pack("!H", cksum) + p[4:] + return p + +class CDPv2_HDR(_CDPChecksum, CDPMsgGeneric): + name = "Cisco Discovery Protocol version 2" + fields_desc = [ ByteField("vers", 2), + ByteField("ttl", 180), + XShortField("cksum", None), + PacketListField("msg", [], _CDPGuessPayloadClass) ] + +bind_layers(SNAP, CDPv2_HDR, {"code": 0x2000, "OUI": 0xC}) + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/chdlc.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/chdlc.py new file mode 100644 index 00000000..6e483762 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/chdlc.py @@ -0,0 +1,42 @@ +# http://trac.secdev.org/scapy/ticket/88 + +# scapy.contrib.description = Cisco HDLC and SLARP +# scapy.contrib.status = loads + +# This layer is based on information from http://www.nethelp.no/net/cisco-hdlc.txt + +from scapy.packet import * +from scapy.fields import * +from scapy.layers.l2 import * +from scapy.layers.inet import * +from scapy.layers.inet6 import * + +class CHDLC(Packet): + name = "Cisco HDLC" + fields_desc = [ ByteEnumField("address", 0x0f, {0x0f : "unicast", 0x8f :"multicast"}), + ByteField("control", 0), + XShortField("proto", 0x0800)] + +class SLARP(Packet): + name = "SLARP" + fields_desc = [ IntEnumField("type", 2, {0 : "request", 1 : "reply", 2 :"line keepalive"}), + ConditionalField(IPField("address", "192.168.0.1"), + lambda pkt : pkt.type == 0 or pkt.type == 1), + ConditionalField(IPField("mask", "255.255.255.0"), + lambda pkt : pkt.type == 0 or pkt.type == 1), + ConditionalField(XShortField("unused", 0), + lambda pkt : pkt.type == 0 or pkt.type == 1), + ConditionalField(IntField("mysequence", 0), + lambda pkt : pkt.type == 2), + ConditionalField(IntField("yoursequence", 0), + lambda pkt : pkt.type == 2), + ConditionalField(XShortField("reliability", 0xffff), + lambda pkt : pkt.type == 2)] + +bind_layers( CHDLC, Dot3, proto=0x6558) +bind_layers( CHDLC, IP, proto=0x800) +bind_layers( CHDLC, IPv6, proto=0x86dd) +bind_layers( CHDLC, SLARP, proto=0x8035) +bind_layers( CHDLC, STP, proto=0x4242) + +conf.l2types.register(104, CHDLC) diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/dtp.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/dtp.py new file mode 100644 index 00000000..1907300b --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/dtp.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python + +# scapy.contrib.description = DTP +# scapy.contrib.status = loads + +""" + DTP Scapy Extension + ~~~~~~~~~~~~~~~~~~~ + + :version: 2008-12-22 + :author: Jochen Bartl + + :Thanks: + + - TLV code derived from the CDP implementation of scapy. (Thanks to Nicolas Bareil and Arnaud Ebalard) + http://trac.secdev.org/scapy/ticket/18 +""" + +from scapy.packet import * +from scapy.fields import * +from scapy.layers.l2 import SNAP,Dot3,LLC +from scapy.sendrecv import sendp + +class DtpGenericTlv(Packet): + name = "DTP Generic TLV" + fields_desc = [ XShortField("type", 0x0001), + FieldLenField("length", None, length_of=lambda pkt:pkt.value + 4), + StrLenField("value", "", length_from=lambda pkt:pkt.length - 4) + ] + + def guess_payload_class(self, p): + return conf.padding_layer + +class RepeatedTlvListField(PacketListField): + def __init__(self, name, default, cls): + PacketField.__init__(self, name, default, cls) + + def getfield(self, pkt, s): + lst = [] + remain = s + 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 remain,lst + + def addfield(self, pkt, s, val): + return s+reduce(str.__add__, map(str, val),"") + +_DTP_TLV_CLS = { + 0x0001 : "DTPDomain", + 0x0002 : "DTPStatus", + 0x0003 : "DTPType", + 0x0004 : "DTPNeighbor" + } + +class DTPDomain(DtpGenericTlv): + name = "DTP Domain" + fields_desc = [ ShortField("type", 1), + FieldLenField("length", None, "domain", adjust=lambda pkt,x:x + 4), + StrLenField("domain", "\x00", length_from=lambda pkt:pkt.length - 4) + ] + +class DTPStatus(DtpGenericTlv): + name = "DTP Status" + fields_desc = [ ShortField("type", 2), + FieldLenField("length", None, "status", adjust=lambda pkt,x:x + 4), + StrLenField("status", "\x03", length_from=lambda pkt:pkt.length - 4) + ] + +class DTPType(DtpGenericTlv): + name = "DTP Type" + fields_desc = [ ShortField("type", 3), + FieldLenField("length", None, "dtptype", adjust=lambda pkt,x:x + 4), + StrLenField("dtptype", "\xa5", length_from=lambda pkt:pkt.length - 4) + ] + +class DTPNeighbor(DtpGenericTlv): + name = "DTP Neighbor" + fields_desc = [ ShortField("type", 4), + #FieldLenField("length", None, "neighbor", adjust=lambda pkt,x:x + 4), + ShortField("len", 10), + MACField("neighbor", None) + ] + +def _DTPGuessPayloadClass(p, **kargs): + cls = conf.raw_layer + if len(p) >= 2: + t = struct.unpack("!H", p[:2])[0] + clsname = _DTP_TLV_CLS.get(t, "DtpGenericTlv") + cls = globals()[clsname] + return cls(p, **kargs) + +class DTP(Packet): + name = "DTP" + fields_desc = [ ByteField("ver", 1), + RepeatedTlvListField("tlvlist", [], _DTPGuessPayloadClass) + ] + +bind_layers(SNAP, DTP, code=0x2004, OUI=0xc) + + +def negotiate_trunk(iface=conf.iface, mymac=str(RandMAC())): + print("Trying to negotiate a trunk on interface %s" % iface) + p = Dot3(src=mymac, dst="01:00:0c:cc:cc:cc")/LLC()/SNAP()/DTP(tlvlist=[DTPDomain(),DTPStatus(),DTPType(),DTPNeighbor(neighbor=mymac)]) + sendp(p) + +if __name__ == "__main__": + from scapy.main import interact + interact(mydict=globals(), mybanner="DTP") diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/eigrp.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/eigrp.py new file mode 100644 index 00000000..f304ae68 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/eigrp.py @@ -0,0 +1,488 @@ +#!/usr/bin/env python + +# scapy.contrib.description = EIGRP +# scapy.contrib.status = loads + +""" + EIGRP Scapy Extension + ~~~~~~~~~~~~~~~~~~~~~ + + :version: 2009-08-13 + :copyright: 2009 by Jochen Bartl + :e-mail: lobo@c3a.de / jochen.bartl@gmail.com + :license: GPL v2 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + 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. + + :TODO + + - Replace TLV code with a more generic solution + * http://trac.secdev.org/scapy/ticket/90 + - Write function for calculating authentication data + + :Known bugs: + + - + + :Thanks: + + - TLV code derived from the CDP implementation of scapy. (Thanks to Nicolas Bareil and Arnaud Ebalard) + http://trac.secdev.org/scapy/ticket/18 + - IOS / EIGRP Version Representation FIX by Dirk Loss +""" + +from scapy.packet import * +from scapy.fields import * +from scapy.layers.inet import IP +from scapy.layers.inet6 import * + +class EigrpIPField(StrField, IPField): + """ + This is a special field type for handling ip addresses of destination networks in internal and + external route updates. + + EIGRP removes zeros from the host portion of the ip address if the netmask is 8, 16 or 24 bits. + """ + + def __init__(self, name, default, length=None, length_from=None): + StrField.__init__(self, name, default) + self.length_from = length_from + if length is not None: + self.length_from = lambda pkt,length=length: length + + def h2i(self, pkt, x): + return IPField.h2i(self, pkt, x) + + def i2m(self, pkt, x): + x = inet_aton(x) + l = self.length_from(pkt) + + if l <= 8: + return x[:1] + elif l <= 16: + return x[:2] + elif l <= 24: + return x[:3] + else: + return x + + def m2i(self, pkt, x): + l = self.length_from(pkt) + + if l <= 8: + x += "\x00\x00\x00" + elif l <= 16: + x += "\x00\x00" + elif l <= 24: + x += "\x00" + + return inet_ntoa(x) + + def prefixlen_to_bytelen(self, l): + if l <= 8: + l = 1 + elif l <= 16: + l = 2 + elif l <= 24: + l = 3 + else: + l = 4 + + return l + + def i2len(self, pkt, x): + l = self.length_from(pkt) + l = self.prefixlen_to_bytelen(l) + + return l + + def getfield(self, pkt, s): + l = self.length_from(pkt) + l = self.prefixlen_to_bytelen(l) + + return s[l:], self.m2i(pkt, s[:l]) + + def randval(self): + return IPField.randval(self) + +class EigrpIP6Field(StrField, IP6Field, EigrpIPField): + """ + This is a special field type for handling ip addresses of destination networks in internal and + external route updates. + + """ + + def __init__(self, name, default, length=None, length_from=None): + StrField.__init__(self, name, default) + self.length_from = length_from + if length is not None: + self.length_from = lambda pkt,length=length: length + + def any2i(self, pkt, x): + return IP6Field.any2i(self, pkt, x) + + def i2repr(self, pkt, x): + return IP6Field.i2repr(self, pkt, x) + + def h2i(self, pkt, x): + return IP6Field.h2i(self, pkt, x) + + def i2m(self, pkt, x): + x = inet_pton(socket.AF_INET6, x) + l = self.length_from(pkt) + l = self.prefixlen_to_bytelen(l) + + return x[:l] + + def m2i(self, pkt, x): + l = self.length_from(pkt) + + prefixlen = self.prefixlen_to_bytelen(l) + if l > 128: + warning("EigrpIP6Field: Prefix length is > 128. Dissection of this packet will fail") + else: + pad = "\x00" * (16 - prefixlen) + x += pad + + return inet_ntop(socket.AF_INET6, x) + + def prefixlen_to_bytelen(self, l): + l = l / 8 + + if l < 16: + l += 1 + + return l + + def i2len(self, pkt, x): + return EigrpIPField.i2len(self, pkt, x) + + def getfield(self, pkt, s): + return EigrpIPField.getfield(self, pkt, s) + +class ThreeBytesField(X3BytesField, ByteField): + def i2repr(self, pkt, x): + return ByteField.i2repr(self, pkt, x) + + +class EIGRPGeneric(Packet): + name = "EIGRP Generic TLV" + fields_desc = [ XShortField("type", 0x0000), + FieldLenField("len", None, "value", "!H", adjust=lambda pkt,x: x + 4), + StrLenField("value", "\x00", length_from=lambda pkt: pkt.len - 4)] + + def guess_payload_class(self, p): + return conf.padding_layer + +class EIGRPParam(EIGRPGeneric): + name = "EIGRP Parameters" + fields_desc = [ XShortField("type", 0x0001), + ShortField("len", 12), + # Bandwidth + ByteField("k1", 1), + # Load + ByteField("k2", 0), + # Delay + ByteField("k3", 1), + # Reliability + ByteField("k4", 0), + # MTU + ByteField("k5", 0), + ByteField("reserved", 0), + ShortField("holdtime", 15) + ] + +class EIGRPAuthData(EIGRPGeneric): + name = "EIGRP Authentication Data" + fields_desc = [ XShortField("type", 0x0002), + FieldLenField("len", None, "authdata", "!H", adjust=lambda pkt,x: x + 24), + ShortEnumField("authtype", 2, {2 : "MD5"}), + ShortField("keysize", None), + IntField("keyid", 1), + StrFixedLenField("nullpad", "\x00" * 12, 12), + StrLenField("authdata", RandString(16), length_from=lambda pkt: pkt.keysize) + ] + + def post_build(self, p, pay): + p += pay + + if self.keysize is None: + keysize = len(self.authdata) + p = p[:6] + chr((keysize >> 8) & 0xff) + chr(keysize & 0xff) + p[8:] + + return p + +class EIGRPSeq(EIGRPGeneric): + name = "EIGRP Sequence" + fields_desc = [ XShortField("type", 0x0003), + ShortField("len", None), + ByteField("addrlen", 4), + ConditionalField(IPField("ipaddr", "192.168.0.1"), + lambda pkt:pkt.addrlen == 4), + ConditionalField(IP6Field("ip6addr", "2001::"), + lambda pkt:pkt.addrlen == 16) + ] + + def post_build(self, p, pay): + p += pay + + if self.len is None: + l = len(p) + p = p[:2] + chr((l >> 8) & 0xff) + chr(l & 0xff) + p[4:] + + return p + +class ShortVersionField(ShortField): + def i2repr(self, pkt, x): + try: + minor = x & 0xff + major = (x >> 8) & 0xff + except TypeError: + return "unknown" + else: + # We print a leading 'v' so that these values don't look like floats + return "v%s.%s" % (major, minor) + + def h2i(self, pkt, x): + """The field accepts string values like v12.1, v1.1 or integer values. + String values have to start with a "v" folled by a floating point number. + Valid numbers are between 0 and 255. + """ + + if type(x) is str and x.startswith("v") and len(x) <= 8: + major = int(x.split(".")[0][1:]) + minor = int(x.split(".")[1]) + + return (major << 8) | minor + + elif type(x) is int and x >= 0 and x <= 65535: + return x + else: + if self.default != None: + warning("set value to default. Format of %r is invalid" % x) + return self.default + else: + raise Scapy_Exception("Format of value is invalid") + + def randval(self): + return RandShort() + +class EIGRPSwVer(EIGRPGeneric): + name = "EIGRP Software Version" + fields_desc = [ XShortField("type", 0x0004), + ShortField("len", 8), + ShortVersionField("ios", "v12.0"), + ShortVersionField("eigrp", "v1.2") + ] + +class EIGRPNms(EIGRPGeneric): + name = "EIGRP Next Multicast Sequence" + fields_desc = [ XShortField("type", 0x0005), + ShortField("len", 8), + IntField("nms", 2) + ] + +# Don't get confused by the term "receive-only". This flag is always set, when you configure +# one of the stub options. It's also the only flag set, when you configure "eigrp stub receive-only". +_EIGRP_STUB_FLAGS = ["connected", "static", "summary", "receive-only", "redistributed", "leak-map"] + +class EIGRPStub(EIGRPGeneric): + name = "EIGRP Stub Router" + fields_desc = [ XShortField("type", 0x0006), + ShortField("len", 6), + FlagsField("flags", 0x000d, 16, _EIGRP_STUB_FLAGS)] + +# Delay 0xffffffff == Destination Unreachable +class EIGRPIntRoute(EIGRPGeneric): + name = "EIGRP Internal Route" + fields_desc = [ XShortField("type", 0x0102), + FieldLenField("len", None, "dst", "!H", adjust=lambda pkt,x: x + 25), + IPField("nexthop", "192.168.0.0"), + IntField("delay", 128000), + IntField("bandwidth", 256), + ThreeBytesField("mtu", 1500), + ByteField("hopcount", 0), + ByteField("reliability", 255), + ByteField("load", 0), + XShortField("reserved", 0), + ByteField("prefixlen", 24), + EigrpIPField("dst", "192.168.1.0", length_from=lambda pkt: pkt.prefixlen), + ] + +_EIGRP_EXTERNAL_PROTOCOL_ID = { + 0x01 : "IGRP", + 0x02 : "EIGRP", + 0x03 : "Static Route", + 0x04 : "RIP", + 0x05 : "Hello", + 0x06 : "OSPF", + 0x07 : "IS-IS", + 0x08 : "EGP", + 0x09 : "BGP", + 0x0A : "IDRP", + 0x0B : "Connected Link" + } + +_EIGRP_EXTROUTE_FLAGS = ["external", "candidate-default"] + +class EIGRPExtRoute(EIGRPGeneric): + name = "EIGRP External Route" + fields_desc = [ XShortField("type", 0x0103), + FieldLenField("len", None, "dst", "!H", adjust=lambda pkt,x: x + 45), + IPField("nexthop", "192.168.0.0"), + IPField("originrouter", "192.168.0.1"), + IntField("originasn", 0), + IntField("tag", 0), + IntField("externalmetric", 0), + ShortField("reserved", 0), + ByteEnumField("extprotocolid", 3, _EIGRP_EXTERNAL_PROTOCOL_ID), + FlagsField("flags", 0, 8, _EIGRP_EXTROUTE_FLAGS), + IntField("delay", 0), + IntField("bandwidth", 256), + ThreeBytesField("mtu", 1500), + ByteField("hopcount", 0), + ByteField("reliability", 255), + ByteField("load", 0), + XShortField("reserved2", 0), + ByteField("prefixlen", 24), + EigrpIPField("dst", "192.168.1.0", length_from=lambda pkt: pkt.prefixlen) + ] + +class EIGRPv6IntRoute(EIGRPGeneric): + name = "EIGRP for IPv6 Internal Route" + fields_desc = [ XShortField("type", 0x0402), + FieldLenField("len", None, "dst", "!H", adjust=lambda pkt,x: x + 37), + IP6Field("nexthop", "::"), + IntField("delay", 128000), + IntField("bandwidth", 256000), + ThreeBytesField("mtu", 1500), + ByteField("hopcount", 1), + ByteField("reliability", 255), + ByteField("load", 0), + XShortField("reserved", 0), + ByteField("prefixlen", 16), + EigrpIP6Field("dst", "2001::", length_from=lambda pkt: pkt.prefixlen) + ] + +class EIGRPv6ExtRoute(EIGRPGeneric): + name = "EIGRP for IPv6 External Route" + fields_desc = [ XShortField("type", 0x0403), + FieldLenField("len", None, "dst", "!H", adjust=lambda pkt,x: x + 57), + IP6Field("nexthop", "::"), + IPField("originrouter", "192.168.0.1"), + IntField("originasn", 0), + IntField("tag", 0), + IntField("externalmetric", 0), + ShortField("reserved", 0), + ByteEnumField("extprotocolid", 3, _EIGRP_EXTERNAL_PROTOCOL_ID), + FlagsField("flags", 0, 8, _EIGRP_EXTROUTE_FLAGS), + IntField("delay", 0), + IntField("bandwidth", 256000), + ThreeBytesField("mtu", 1500), + ByteField("hopcount", 1), + ByteField("reliability", 0), + ByteField("load", 1), + XShortField("reserved2", 0), + ByteField("prefixlen", 8), + EigrpIP6Field("dst", "::", length_from=lambda pkt: pkt.prefixlen) + ] + +_eigrp_tlv_cls = { + 0x0001: "EIGRPParam", + 0x0002: "EIGRPAuthData", + 0x0003: "EIGRPSeq", + 0x0004: "EIGRPSwVer", + 0x0005: "EIGRPNms", + 0x0006: "EIGRPStub", + 0x0102: "EIGRPIntRoute", + 0x0103: "EIGRPExtRoute", + 0x0402: "EIGRPv6IntRoute", + 0x0403: "EIGRPv6ExtRoute" + } + +class RepeatedTlvListField(PacketListField): + def __init__(self, name, default, cls): + PacketField.__init__(self, name, default, cls) + + def getfield(self, pkt, s): + lst = [] + remain = s + 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 remain,lst + + def addfield(self, pkt, s, val): + return s + reduce(str.__add__, map(str, val), "") + +def _EIGRPGuessPayloadClass(p, **kargs): + cls = conf.raw_layer + if len(p) >= 2: + t = struct.unpack("!H", p[:2])[0] + clsname = _eigrp_tlv_cls.get(t, "EIGRPGeneric") + cls = globals()[clsname] + return cls(p, **kargs) + +_EIGRP_OPCODES = { 1 : "Update", + 2 : "Request", + 3 : "Query", + 4 : "Replay", + 5 : "Hello", + 6 : "IPX SAP", + 10 : "SIA Query", + 11 : "SIA Reply" } + +# The Conditional Receive bit is used for reliable multicast communication. +# Update-Flag: Not sure if Cisco calls it that way, but it's set when neighbors +# are exchanging routing information +_EIGRP_FLAGS = ["init", "cond-recv", "unknown", "update"] + +class EIGRP(Packet): + name = "EIGRP" + fields_desc = [ ByteField("ver", 2), + ByteEnumField("opcode", 5, _EIGRP_OPCODES), + XShortField("chksum", None), + FlagsField("flags", 0, 32, _EIGRP_FLAGS), + IntField("seq", 0), + IntField("ack", 0), + IntField("asn", 100), + RepeatedTlvListField("tlvlist", [], _EIGRPGuessPayloadClass) + ] + + def post_build(self, p, pay): + p += pay + if self.chksum is None: + c = checksum(p) + p = p[:2] + chr((c >> 8) & 0xff) + chr(c & 0xff) + p[4:] + return p + + def mysummary(self): + summarystr = "EIGRP (AS=%EIGRP.asn% Opcode=%EIGRP.opcode%" + if self.opcode == 5 and self.ack != 0: + summarystr += " (ACK)" + if self.flags != 0: + summarystr += " Flags=%EIGRP.flags%" + + return self.sprintf(summarystr + ")") + +bind_layers(IP, EIGRP, proto=88) +bind_layers(IPv6, EIGRP, nh=88) + +if __name__ == "__main__": + from scapy.main import interact + interact(mydict=globals(), mybanner="EIGRP") + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/etherip.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/etherip.py new file mode 100644 index 00000000..e331c146 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/etherip.py @@ -0,0 +1,19 @@ + +# http://trac.secdev.org/scapy/ticket/297 + +# scapy.contrib.description = EtherIP +# scapy.contrib.status = loads + +from scapy.fields import BitField +from scapy.packet import Packet, bind_layers +from scapy.layers.inet import IP +from scapy.layers.l2 import Ether + +class EtherIP(Packet): + name = "EtherIP / RFC 3378" + fields_desc = [ BitField("version", 3, 4), + BitField("reserved", 0, 12)] + +bind_layers( IP, EtherIP, frag=0, proto=0x61) +bind_layers( EtherIP, Ether) + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/gsm_um.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/gsm_um.py new file mode 100644 index 00000000..7b1354a4 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/gsm_um.py @@ -0,0 +1,13119 @@ +#!/usr/bin/env python + +# scapy.contrib.description = PPI +# scapy.contrib.status = loads + +""" + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +""" + + #################################################################### + # This file holds the GSM UM interface implementation for Scapy # + # author: Laurent Weber # + # # + # Some examples on how to use this script: # + # http://0xbadcab1e.lu/scapy_gsm_um-howto.txt # + # # + # tested on: scapy-version: 2.2.0 (dev) # + #################################################################### + +import logging +from types import IntType +from types import NoneType +from types import StringType +#from time import sleep +import socket +logging.getLogger("scapy").setLevel(1) +from scapy.all import * + +# This method is intended to send gsm air packets. It uses a unix domain +# socket. It opens a socket, sends the parameter to the socket and +# closes the socket. +# typeSock determines the type of the socket, can be: +# 0 for UDP Socket +# 1 for Unix Domain Socket +# 2 for TCP + + +def sendum(x, typeSock=0): + try: + if type(x) is not str: + x = str(x) + if typeSock is 0: + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + host = '127.0.0.1' + port = 28670 # default for openBTS + s.connect((host, port)) + elif typeSock is 1: + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + s.connect("/tmp/osmoL") + elif typeSock is 2: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + host = '127.0.0.1' + port = 43797 + s.connect((host, port)) + s.send(x) + s.close() + except: + print("[Error]: There was a problem when trying to transmit data.\ + Please make sure you started the socket server.") + +# Known Bugs/Problems: +# If a message uses multiple times the same IE you cannot set the values +# of this IE's if you use the preconfigured packets. You need to build +# the IE's by hand and than assemble them as entire messages. + +# The ErrorLength class is a custom exception that gets raised when a +# packet doesn't have the correct size. + + +class ErrorLength(Exception): + def __str__(self): + error = "ERROR: Please make sure you build entire, 8 bit fields." + return repr(error) +### +# This method computes the length of the actual IE. +# It computes how many "None" fields have to be removed (if any). +# The method returns an integer containing the number of bytes that have to be +# cut off the packet. +# parameter length contains the max length of the IE can be found in +# 0408 +# The parameter fields contains the value of the fields (not the default but +# the real, actual value. +# The parameter fields2 contains fields_desc. +# Location contains the location of the length field in the IE. Everything +# after the the length field has to be counted (04.07 11.2.1.1.2) + + +def adapt(min_length, max_length, fields, fields2, location=2): + # find out how much bytes there are between min_length and the location of + # the length field + location = min_length - location + i = len(fields) - 1 + rm = mysum = 0 + while i >= 0: + if fields[i] is None: + rm += 1 + try: + mysum += fields2[i].size + except AttributeError: # ByteFields don't have .size + mysum += 8 + else: + break + i -= 1 + if mysum % 8 is 0: + length = mysum / 8 # Number of bytes we have to delete + dyn_length = (max_length - min_length - length) + if dyn_length < 0: + dyn_length = 0 + if length is max_length: # Fix for packets that have all values set + length -= min_length # to None + return [length, dyn_length + location] + else: + raise ErrorLength() + + +def examples(example=None): + if example == None: + print("""This command presents some example to introduce scapy +gsm-um to new users. +The following parameters can be used: + examples("imsiDetach") + examples("call") + examples("dissect")""") + elif example == "imsiDetach": + print(""" +>>> a=imsiDetachIndication() +... a.typeOfId=1; a.odd=1; a.idDigit1=0xF; +... a.idDigit2_1=2; a.idDigit2=7; a.idDigit3_1=0; +... a.idDigit3=7; a.idDigit4_1=7; a.idDigit4=2; +... a.idDigit5_1=0; a.idDigit5=0; a.idDigit6_1=0; +... a.idDigit6=1; a.idDigit7_1=2; a.idDigit7=7; +... a.idDigit8_1=7; a.idDigit8=5; a.idDigit9_1=1; a.idDigit9=4; +>>> hexdump(a) +0000 05 01 00 08 F0 27 07 72 00 01 27 75 14 .....'.r..'u. +>>> sendum(a) +""") + elif example == "call": + print(""" +If you use an USRP and the testcall function this sets up a phonecall: +>>> sendum(setupMobileOriginated()) +>>> sendum(connectAcknowledge()) +""") + + +# Section 10.2/3 +class TpPd(Packet): + """Skip indicator and transaction identifier and Protocol Discriminator""" + name = "Skip Indicator And Transaction Identifier and Protocol \ +Discriminator" + fields_desc = [ + BitField("ti", 0x0, 4), + BitField("pd", 0x3, 4) + ] + + +class MessageType(Packet): + """Message Type Section 10.4""" + name = "Message Type" + fields_desc = [ + XByteField("mesType", 0x3C) + ] + + +## +# Message for Radio Resources management (RR) Section 9.1 +### + +# Network to MS +def additionalAssignment(MobileAllocation_presence=0, + StartingTime_presence=0): + """ADDITIONAL ASSIGNMENT Section 9.1.1""" + # Mandatory + a = TpPd(pd=0x6) + b = MessageType(mesType=0x3B) # 00111011 + c = ChannelDescription() + packet = a / b / c + # Not Mandatory + if MobileAllocation_presence is 1: + d = MobileAllocationHdr(ieiMA=0x72, eightBitMA=0x0) + packet = packet / d + if StartingTime_presence is 1: + e = StartingTimeHdr(ieiST=0x7C, eightBitST=0x0) + packet = packet / e + return packet + + +# Network to MS +def assignmentCommand(FrequencyList_presence=0, + CellChannelDescription_presence=0, + CellChannelDescription_presence1=0, + MultislotAllocation_presence=0, + ChannelMode_presence=0, ChannelMode_presence1=0, + ChannelMode_presence2=0, ChannelMode_presence3=0, + ChannelMode_presence4=0, ChannelMode_presence5=0, + ChannelMode_presence6=0, ChannelMode_presence7=0, + ChannelDescription=0, ChannelMode2_presence=0, + MobileAllocation_presence=0, StartingTime_presence=0, + FrequencyList_presence1=0, + ChannelDescription2_presence=0, + ChannelDescription_presence=0, + FrequencyChannelSequence_presence=0, + MobileAllocation_presence1=0, + CipherModeSetting_presence=0, + VgcsTargetModeIdentication_presence=0, + MultiRateConfiguration_presence=0): + """ASSIGNMENT COMMAND Section 9.1.2""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x2e) # 101110 + c = ChannelDescription2() + d = PowerCommand() + packet = a / b / c / d + if FrequencyList_presence is 1: + e = FrequencyListHdr(ieiFL=0x05, eightBitFL=0x0) + packet = packet / e + if CellChannelDescription_presence is 1: + f = CellChannelDescriptionHdr(ieiCCD=0x62, eightBitCCD=0x0) + packet = packet / f + if MultislotAllocation_presence is 1: + g = MultislotAllocationHdr(ieiMSA=0x10, eightBitMSA=0x0) + packet = packet / g + if ChannelMode_presence is 1: + h = ChannelModeHdr(ieiCM=0x63, eightBitCM=0x0) + packet = packet / h + if ChannelMode_presence1 is 1: + i = ChannelModeHdr(ieiCM=0x11, eightBitCM=0x0) + packet = packet / i + if ChannelMode_presence2 is 1: + j = ChannelModeHdr(ieiCM=0x13, eightBitCM=0x0) + packet = packet / j + if ChannelMode_presence3 is 1: + k = ChannelModeHdr(ieiCM=0x14, eightBitCM=0x0) + packet = packet / k + if ChannelMode_presence4 is 1: + l = ChannelModeHdr(ieiCM=0x15, eightBitCM=0x0) + packet = packet / l + if ChannelMode_presence5 is 1: + m = ChannelModeHdr(ieiCM=0x16, eightBitCM=0x0) + packet = packet / m + if ChannelMode_presence6 is 1: + n = ChannelModeHdr(ieiCM=0x17, eightBitCM=0x0) + packet = packet / n + if ChannelMode_presence7 is 1: + o = ChannelModeHdr(ieiCM=0x18, eightBitCM=0x0) + packet = packet / o + if ChannelDescription_presence is 1: + p = ChannelDescriptionHdr(ieiCD=0x64, eightBitCD=0x0) + packet = packet / p + if ChannelMode2_presence is 1: + q = ChannelMode2Hdr(ieiCM2=0x66, eightBitCM2=0x0) + packet = packet / q + if MobileAllocation_presence is 1: + r = MobileAllocationHdr(ieiMA=0x72, eightBitMA=0x0) + packet = packet / r + if StartingTime_presence is 1: + s = StartingTimeHdr(ieiST=0x7C, eightBitST=0x0) + packet = packet / s + if FrequencyList_presence1 is 1: + t = FrequencyListHdr(ieiFL=0x19, eightBitFL=0x0) + packet = packet / t + if ChannelDescription2_presence is 1: + u = ChannelDescription2Hdr(ieiCD2=0x1C, eightBitCD2=0x0) + packet = packet / u + if ChannelDescription_presence is 1: + v = ChannelDescriptionHdr(ieiCD=0x1D, eightBitCD=0x0) + packet = packet / v + if FrequencyChannelSequence_presence is 1: + w = FrequencyChannelSequenceHdr(ieiFCS=0x1E, eightBitFCS=0x0) + packet = packet / w + if MobileAllocation_presence1 is 1: + x = MobileAllocationHdr(ieiMA=0x21, eightBitMA=0x0) + packet = packet / x + if CipherModeSetting_presence is 1: + y = CipherModeSettingHdr(ieiCMS=0x9, eightBitCMS=0x0) + packet = packet / y + if VgcsTargetModeIdentication_presence is 1: + z = VgcsTargetModeIdenticationHdr(ieiVTMI=0x01, eightBitVTMI=0x0) + packet = packet / z + if MultiRateConfiguration_presence is 1: + aa = MultiRateConfigurationHdr(ieiMRC=0x03, eightBitMRC=0x0) + packet = packet / aa + return packet + + +# MS to Network +def assignmentComplete(): + """ASSIGNMENT COMPLETE Section 9.1.3""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x29) # 00101001 + c = RrCause() + packet = a / b / c + return packet + + +# MS to Network +def assignmentFailure(): + """ASSIGNMENT FAILURE Section 9.1.4""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x2F) # 00101111 + c = RrCause() + packet = a / b / c + return packet + + +# Network to MS +def channelModeModify(VgcsTargetModeIdentication_presence=0, + MultiRateConfiguration_presence=0): + """CHANNEL MODE MODIFY Section 9.1.5""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x8) # 0001000 + c = ChannelDescription2() + d = ChannelMode() + packet = a / b / c / d + if VgcsTargetModeIdentication is 1: + e = VgcsTargetModeIdenticationHdr(ieiVTMI=0x01, eightBitVTMI=0x0) + packet = packet / e + if MultiRateConfiguration is 1: + f = MultiRateConfigurationHdr(ieiMRC=0x03, eightBitMRC=0x0) + packet = packet / f + return packet + + +def channelModeModifyAcknowledge(): + """CHANNEL MODE MODIFY ACKNOWLEDGE Section 9.1.6""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x17) # 00010111 + c = ChannelDescription2() + d = ChannelMode() + packet = a / b / c / d + return packet + + +# Network to MS +def channelRelease(BaRange_presence=0, GroupChannelDescription_presence=0, + GroupCipherKeyNumber_presence=0, GprsResumption_presence=0, + BaListPref_presence=0): + """CHANNEL RELEASE Section 9.1.7""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0xD) # 00001101 + c = RrCause() + packet = a / b / c + if BaRange_presence is 1: + d = BaRangeHdr(ieiBR=0x73, eightBitBR=0x0) + packet = packet / d + if GroupChannelDescription_presence is 1: + e = GroupChannelDescriptionHdr(ieiGCD=0x74, eightBitGCD=0x0) + packet = packet / e + if GroupCipherKeyNumber_presence is 1: + f = GroupCipherKeyNumber(ieiGCKN=0x8) + packet = packet / f + if GprsResumption_presence is 1: + g = GprsResumptionHdr(ieiGR=0xC, eightBitGR=0x0) + packet = packet / g + if BaListPref_presence is 1: + h = BaListPrefHdr(ieiBLP=0x75, eightBitBLP=0x0) + packet = packet / h + return packet + + +class ChannelRequest(Packet): + """Channel request Section 9.1.8""" + name = "Channel Request" + fields_desc = [ + ByteField("estCause", 0x0) + ] + + +def channelRequest(): + return ChannelRequest() + + +# Network to MS +def cipheringModeCommand(): + """CIPHERING MODE COMMAND Section 9.1.9""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x35) # 00110101 + c = RrCause() + #d=cipherModeSetting() + #e=cipherResponse() + # FIX + d = CipherModeSettingAndcipherResponse() + packet = a / b / c / d + return packet + + +def cipheringModeComplete(MobileId_presence=0): + """CIPHERING MODE COMPLETE Section 9.1.10""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x32) # 00110010 + packet = a / b + if MobileId_presence is 1: + c = MobileIdHdr(ieiMI=0x17, eightBitMI=0x0) + packet = packet / c + return packet + + +# Network to MS +def classmarkChange(MobileStationClassmark3_presence=0): + """CLASSMARK CHANGE Section 9.1.11""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x16) # 00010110 + c = MobileStationClassmark2() + packet = a / b / c + if MobileStationClassmark3_presence is 1: + e = MobileStationClassmark3(ieiMSC3=0x20) + packet = packet / e + return packet + + +# Network to MS +def classmarkEnquiry(): + """CLASSMARK ENQUIRY Section 9.1.12""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x13) # 00010011 + packet = a / b + return packet +# 9.1.12a Spare + + +# Network to MS +def configurationChangeCommand(ChannelMode_presence=0, + ChannelMode_presence1=0, + ChannelMode_presence2=0, + ChannelMode_presence3=0, + ChannelMode_presence4=0, + ChannelMode_presence5=0, + ChannelMode_presence6=0, + ChannelMode_presence7=0): + """CONFIGURATION CHANGE COMMAND Section 9.1.12b""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x30) # 00110000 + c = MultislotAllocation() + packet = a / b / c + if ChannelMode_presence is 1: + d = ChannelModeHdr(ieiCM=0x63, eightBitCM=0x0) + packet = packet / d + if ChannelMode_presence1 is 1: + e = ChannelModeHdr(ieiCM=0x11, eightBitCM=0x0) + packet = packet / e + if ChannelMode_presence2 is 1: + f = ChannelModeHdr(ieiCM=0x13, eightBitCM=0x0) + packet = packet / f + if ChannelMode_presence3 is 1: + g = ChannelModeHdr(ieiCM=0x14, eightBitCM=0x0) + packet = packet / g + if ChannelMode_presence4 is 1: + h = ChannelModeHdr(ieiCM=0x15, eightBitCM=0x0) + packet = packet / h + if ChannelMode_presence5 is 1: + i = ChannelModeHdr(ieiCM=0x16, eightBitCM=0x0) + packet = packet / i + if ChannelMode_presence6 is 1: + j = ChannelModeHdr(ieiCM=0x17, eightBitCM=0x0) + packet = packet / j + if ChannelMode_presence7 is 1: + k = ChannelModeHdr(ieiCM=0x18, eightBitCM=0x0) + packet = packet / k + return packet + + +def configurationChangeAcknowledge(): + """CONFIGURATION CHANGE ACKNOWLEDGE Section 9.1.12c""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x31) # 00110001 + c = MobileId() + packet = a / b / c + return packet + + +def configurationChangeReject(): + """CONFIGURATION CHANGE REJECT Section 9.1.12d""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x33) # 00110011 + c = RrCause() + packet = a / b / c + return packet + + +# Network to MS +def frequencyRedefinition(CellChannelDescription_presence=0): + """Frequency redefinition Section 9.1.13""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x14) # 00010100 + c = ChannelDescription() + d = MobileAllocation() + e = StartingTime() + packet = a / b / c / d / e + if CellChannelDescription_presence is 1: + f = CellChannelDescriptionHdr(ieiCCD=0x62, eightBitCCD=0x0) + packet = packet / f + return packet + + +# Network to MS +def pdchAssignmentCommand(ChannelDescription_presence=0, + CellChannelDescription_presence=0, + MobileAllocation_presence=0, + StartingTime_presence=0, FrequencyList_presence=0, + ChannelDescription_presence1=0, + FrequencyChannelSequence_presence=0, + MobileAllocation_presence1=0, + PacketChannelDescription_presence=0, + DedicatedModeOrTBF_presence=0): + """PDCH ASSIGNMENT COMMAND Section 9.1.13a""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x23) # 00100011 + c = ChannelDescription() + packet = a / b / c + if ChannelDescription_presence is 1: + d = ChannelDescriptionHdr(ieiCD=0x62, eightBitCD=0x0) + packet = packet / d + if CellChannelDescription_presence is 1: + e = CellChannelDescriptionHdr(ieiCCD=0x05, eightBitCCD=0x0) + packet = packet / e + if MobileAllocation_presence is 1: + f = MobileAllocationHdr(ieiMA=0x72, eightBitMA=0x0) + packet = packet / f + if StartingTime_presence is 1: + g = StartingTimeHdr(ieiST=0x7C, eightBitST=0x0) + packet = packet / g + if FrequencyList_presence is 1: + h = FrequencyListHdr(ieiFL=0x19, eightBitFL=0x0) + packet = packet / h + if ChannelDescription_presence1 is 1: + i = ChannelDescriptionHdr(ieiCD=0x1C, eightBitCD=0x0) + packet = packet / i + if FrequencyChannelSequence_presence is 1: + j = FrequencyChannelSequenceHdr(ieiFCS=0x1E, eightBitFCS=0x0) + packet = packet / j + if MobileAllocation_presence1 is 1: + k = MobileAllocationHdr(ieiMA=0x21, eightBitMA=0x0) + packet = packet / k + if PacketChannelDescription_presence is 1: + l = PacketChannelDescription(ieiPCD=0x22) + packet = packet / l + if DedicatedModeOrTBF_presence is 1: + m = DedicatedModeOrTBFHdr(ieiDMOT=0x23, eightBitDMOT=0x0) + packet = packet / m + return packet + + +def gprsSuspensionRequest(): + """GPRS SUSPENSION REQUEST Section 9.1.13b""" + a = TpPd(pd=0x6) + b = MessageType() + c = Tlli() + d = RoutingAreaIdentification() + e = SuspensionCause() + packet = a / b / c / d / e + return packet + + +class HandoverAccess(Packet): + name = "Handover Access" # Section 9.1.14" + fields_desc = [ + ByteField("handover", None), + ] + + +# Network to MS +def handoverCommand(SynchronizationIndication_presence=0, + FrequencyShortList_presence=0, FrequencyList_presence=0, + CellChannelDescription_presence=0, + MultislotAllocation_presence=0, + ChannelMode_presence=0, ChannelMode_presence1=0, + ChannelMode_presence2=0, + ChannelMode_presence3=0, ChannelMode_presence4=0, + ChannelMode_presence5=0, + ChannelMode_presence6=0, ChannelMode_presence7=0, + ChannelDescription_presence1=0, ChannelMode2_presence=0, + FrequencyChannelSequence_presence=0, + MobileAllocation_presence=0, + StartingTime_presence=0, TimeDifference_presence=0, + TimingAdvance_presence=0, + FrequencyShortList_presence1=0, + FrequencyList_presence1=0, + ChannelDescription2_presence=0, + ChannelDescription_presence2=0, + FrequencyChannelSequence_presence1=0, + MobileAllocation_presence1=0, + CipherModeSetting_presence=0, + VgcsTargetModeIdentication_presence=0, + MultiRateConfiguration_presence=0): + """HANDOVER COMMAND Section 9.1.15""" + name = "Handover Command" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x2b) # 00101011 + c = CellDescription() + d = ChannelDescription2() + e = HandoverReference() + f = PowerCommandAndAccessType() + packet = a / b / c / d / e / f + if SynchronizationIndication_presence is 1: + g = SynchronizationIndicationHdr(ieiSI=0xD, eightBitSI=0x0) + packet = packet / g + if FrequencyShortList_presence is 1: + h = FrequencyShortListHdr(ieiFSL=0x02) + packet = packet / h + if FrequencyList_presence is 1: + i = FrequencyListHdr(ieiFL=0x05, eightBitFL=0x0) + packet = packet / i + if CellChannelDescription_presence is 1: + j = CellChannelDescriptionHdr(ieiCCD=0x62, eightBitCCD=0x0) + packet = packet / j + if MultislotAllocation_presence is 1: + k = MultislotAllocationHdr(ieiMSA=0x10, eightBitMSA=0x0) + packet = packet / k + if ChannelMode_presence is 1: + l = ChannelModeHdr(ieiCM=0x63, eightBitCM=0x0) + packet = packet / l + if ChannelMode_presence1 is 1: + m = ChannelModeHdr(ieiCM=0x11, eightBitCM=0x0) + packet = packet / m + if ChannelMode_presence2 is 1: + n = ChannelModeHdr(ieiCM=0x13, eightBitCM=0x0) + packet = packet / n + if ChannelMode_presence3 is 1: + o = ChannelModeHdr(ieiCM=0x14, eightBitCM=0x0) + packet = packet / o + if ChannelMode_presence4 is 1: + p = ChannelModeHdr(ieiCM=0x15, eightBitCM=0x0) + packet = packet / p + if ChannelMode_presence5 is 1: + q = ChannelModeHdr(ieiCM=0x16, eightBitCM=0x0) + packet = packet / q + if ChannelMode_presence6 is 1: + r = ChannelModeHdr(ieiCM=0x17, eightBitCM=0x0) + packet = packet / r + if ChannelMode_presence7 is 1: + s = ChannelModeHdr(ieiCM=0x18, eightBitCM=0x0) + packet = packet / s + if ChannelDescription_presence1 is 1: + s1 = ChannelDescriptionHdr(ieiCD=0x64, eightBitCD=0x0) + packet = packet / s1 + if ChannelMode2_presence is 1: + t = ChannelMode2Hdr(ieiCM2=0x66, eightBitCM2=0x0) + packet = packet / t + if FrequencyChannelSequence_presence is 1: + u = FrequencyChannelSequenceHdr(ieiFCS=0x69, eightBitFCS=0x0) + packet = packet / u + if MobileAllocation_presence is 1: + v = MobileAllocationHdr(ieiMA=0x72, eightBitMA=0x0) + packet = packet / v + if StartingTime_presence is 1: + w = StartingTimeHdr(ieiST=0x7C, eightBitST=0x0) + packet = packet / w + if TimeDifference_presence is 1: + x = TimeDifferenceHdr(ieiTD=0x7B, eightBitTD=0x0) + packet = packet / x + if TimingAdvance_presence is 1: + y = TimingAdvanceHdr(ieiTA=0x7D, eightBitTA=0x0) + packet = packet / y + if FrequencyShortList_presence1 is 1: + z = FrequencyShortListHdr(ieiFSL=0x12) + packet = packet / z + if FrequencyList_presence1 is 1: + aa = FrequencyListHdr(ieiFL=0x19, eightBitFL=0x0) + packet = packet / aa + if ChannelDescription2_presence is 1: + ab = ChannelDescription2Hdr(ieiCD2=0x1C, eightBitCD2=0x0) + packet = packet / ab + if ChannelDescription_presence2 is 1: + ac = ChannelDescriptionHdr(ieiCD=0x1D, eightBitCD=0x0) + packet = packet / ac + if FrequencyChannelSequence_presence1 is 1: + ad = FrequencyChannelSequenceHdr(ieiFCS=0x1E, eightBitFCS=0x0) + packet = packet / ad + if MobileAllocation_presence1 is 1: + ae = MobileAllocationHdr(ieiMA=0x21, eightBitMA=0x0) + packet = packet / ae + if CipherModeSetting_presence is 1: + af = CipherModeSettingHdr(ieiCMS=0x9, eightBitCMS=0x0) + packet = packet / af + if VgcsTargetModeIdentication_presence is 1: + ag = VgcsTargetModeIdenticationHdr(ieiVTMI=0x01, eightBitVTMI=0x0) + packet = packet / ag + if MultiRateConfiguration_presence is 1: + ah = MultiRateConfigurationHdr(ieiMRC=0x03, eightBitMRC=0x0) + packet = packet / ah + return packet + + +def handoverComplete(MobileTimeDifference_presence=0): + """HANDOVER COMPLETE Section 9.1.16""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x2c) # 00101100 + c = RrCause() + packet = a / b / c + if MobileTimeDifference_presence is 1: + d = MobileTimeDifferenceHdr(ieiMTD=0x77, eightBitMTD=0x0) + packet = packet / d + return packet + + +def handoverFailure(): + """HANDOVER FAILURE Section 9.1.17""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x28) # 00101000 + c = RrCause() + packet = a / b / c + return packet + + +#The L2 pseudo length of this message is the sum of lengths of all +#information elements present in the message except +#the IA Rest Octets and L2 Pseudo Length information elements. +# Network to MS +def immediateAssignment(ChannelDescription_presence=0, + PacketChannelDescription_presence=0, + StartingTime_presence=0): + """IMMEDIATE ASSIGNMENT Section 9.1.18""" + a = L2PseudoLength() + b = TpPd(pd=0x6) + c = MessageType(mesType=0x3F) # 00111111 + d = PageModeAndDedicatedModeOrTBF() + packet = a / b / c / d + if ChannelDescription_presence is 1: + f = ChannelDescription() + packet = packet / f + if PacketChannelDescription_presence is 1: + g = PacketChannelDescription() + packet = packet / g + h = RequestReference() + i = TimingAdvance() + j = MobileAllocation() + packet = packet / h / i / j + if StartingTime_presence is 1: + k = StartingTimeHdr(ieiST=0x7C, eightBitST=0x0) + packet = packet / k + l = IaRestOctets() + packet = packet / l + return packet + + +#The L2 pseudo length of this message is the sum of lengths of all +#information elements present in the message except +#the IAX Rest Octets and L2 Pseudo Length information elements. + +# Network to MS +def immediateAssignmentExtended(StartingTime_presence=0): + """IMMEDIATE ASSIGNMENT EXTENDED Section 9.1.19""" + a = L2PseudoLength() + b = TpPd(pd=0x6) + c = MessageType(mesType=0x39) # 00111001 + d = PageModeAndSpareHalfOctets() + f = ChannelDescription() + g = RequestReference() + h = TimingAdvance() + i = MobileAllocation() + packet = a / b / c / d / f / g / h / i + if StartingTime_presence is 1: + j = StartingTimeHdr(ieiST=0x7C, eightBitST=0x0) + packet = packet / j + k = IaxRestOctets() + packet = packet / k + return packet + + +# This message has L2 pseudo length 19 +# Network to MS +def immediateAssignmentReject(): + """IMMEDIATE ASSIGNMENT REJECT Section 9.1.20""" + a = L2PseudoLength(l2pLength=0x13) + b = TpPd(pd=0x6) + c = MessageType(mesType=0x3a) # 00111010 + d = PageModeAndSpareHalfOctets() + f = RequestReference() + g = WaitIndication() + h = RequestReference() + i = WaitIndication() + j = RequestReference() + k = WaitIndication() + l = RequestReference() + m = WaitIndication() + n = IraRestOctets() + packet = a / b / c / d / f / g / h / i / j / k / l / m / n + return packet + + +def measurementReport(): + """MEASUREMENT REPORT Section 9.1.21""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x15) # 00010101 + c = MeasurementResults() + packet = a / b / c + return packet + + +# len max 20 +class NotificationFacch(): + """NOTIFICATION/FACCH Section 9.1.21a""" + name = "Notification/facch" + fields_desc = [ + BitField("rr", 0x0, 1), + BitField("msgTyoe", 0x0, 5), + BitField("layer2Header", 0x0, 2), + BitField("frChanDes", 0x0, 24) + ] + + +# The L2 pseudo length of this message has a value one +# Network to MS +def notificationNch(): + """NOTIFICATION/NCH Section 9.1.21b""" + a = L2PseudoLength(l2pLength=0x01) + b = TpPd(pd=0x6) + c = MessageType(mesType=0x20) # 00100000 + d = NtNRestOctets() + packet = a / b / c / d + return packet + + +def notificationResponse(): + """NOTIFICATION RESPONSE Section 9.1.21d""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x26) # 00100110 + c = MobileStationClassmark2() + d = MobileId() + e = DescriptiveGroupOrBroadcastCallReference() + packet = a / b / c / d / e + return packet + + +# Network to MS +def rrCellChangeOrder(): + """RR-CELL CHANGE ORDER Section 9.1.21e""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x8) # 00001000 + c = CellDescription() + d = NcModeAndSpareHalfOctets() + packet = a / b / c / d + return packet + + +# Network to MS +def pagingRequestType1(MobileId_presence=0): + """PAGING REQUEST TYPE 1 Section 9.1.22""" + #The L2 pseudo length of this message is the sum of lengths of all + #information elements present in the message except + #the P1 Rest Octets and L2 Pseudo Length information elements. + a = L2PseudoLength() + b = TpPd(pd=0x6) + c = MessageType(mesType=0x21) # 00100001 + d = PageModeAndChannelNeeded() + f = MobileId() + packet = a / b / c / d / f + if MobileId_presence is 1: + g = MobileIdHdr(ieiMI=0x17, eightBitMI=0x0) + packet = packet / g + h = P1RestOctets() + packet = packet / h + return packet + + +# The L2 pseudo length of this message is the sum of lengths of all +# information elements present in the message except +# Network to MS +def pagingRequestType2(MobileId_presence=0): + """PAGING REQUEST TYPE 2 Section 9.1.23""" + a = L2PseudoLength() + b = TpPd(pd=0x6) + c = MessageType(mesType=0x22) # 00100010 + d = PageModeAndChannelNeeded() + f = MobileId() + g = MobileId() + packet = a / b / c / d / f / g + if MobileId_presence is 1: + h = MobileIdHdr(ieiMI=0x17, eightBitMI=0x0) + packet = packet / h + i = P2RestOctets() + packet = packet / i + return packet + + +# Network to MS +def pagingRequestType3(): + """PAGING REQUEST TYPE 3 Section 9.1.24""" +# This message has a L2 Pseudo Length of 19 + a = L2PseudoLength(l2pLength=0x13) + b = TpPd(pd=0x6) + c = MessageType(mesType=0x24) # 00100100 + d = PageModeAndChannelNeeded() + e = TmsiPTmsi() + f = TmsiPTmsi() + g = TmsiPTmsi() + h = TmsiPTmsi() + i = P3RestOctets() + packet = a / b / c / d / e / f / g / h / i + return packet + + +def pagingResponse(): + """PAGING RESPONSE Section 9.1.25""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x27) # 00100111 + c = CiphKeySeqNrAndSpareHalfOctets() + d = MobileStationClassmark2() + e = MobileId() + packet = a / b / c / d / e + return packet + + +# Network to MS +def partialRelease(): + """PARTIAL RELEASE Section 9.1.26""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0xa) # 00001010 + c = ChannelDescription() + packet = a / b / c + return packet + + +def partialReleaseComplete(): + """PARTIAL RELEASE COMPLETE Section 9.1.27""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0xf) # 00001111 + packet = a / b + return packet + + +# Network to MS +def physicalInformation(): + """PHYSICAL INFORMATION Section 9.1.28""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x2d) # 00101101 + c = TimingAdvance() + packet = a / b / c + return packet + + +def rrInitialisationRequest(): + """RR Initialisation Request Section 9.1.28.a""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x3c) # 00111100 + c = CiphKeySeqNrAndMacModeAndChannelCodingRequest() + e = MobileStationClassmark2() + f = Tlli() + g = ChannelRequestDescription() + h = GprsMeasurementResults() + packet = a / b / c / e / f / g / h + return packet + + +def rrStatus(): + """RR STATUS Section 9.1.29""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x12) # 00010010 + c = RrCause() + packet = a / b / c + return packet + + +# It does not +# follow the basic format. Its length is _25_ bits. The +# order of bit transmission is defined in GSM 04.04. +# Network to MS +class SynchronizationChannelInformation(): + """SYNCHRONIZATION CHANNEL INFORMATION Section 9.1.30""" + name = "Synchronization Channel Information" + fields_desc = [ + BitField("bsic", 0x0, 5), + BitField("t1Hi", 0x0, 3), + ByteField("t1Mi", 0x0), + BitField("t1Lo", 0x0, 1), + BitField("t2", 0x0, 5), + BitField("t3Hi", 0x0, 2), + BitField("t3Lo", 0x0, 1) + ] + + +# This message has a L2 Pseudo Length of 21. +# Network to MS +def systemInformationType1(): + """SYSTEM INFORMATION TYPE 1 Section 9.1.31""" + a = L2PseudoLength(l2pLength=0x15) + b = TpPd(pd=0x6) + c = MessageType(mesType=0x19) # 00011001 + d = CellChannelDescription() + e = RachControlParameters() + f = Si1RestOctets() + packet = a / b / c / d / e / f + return packet + + +# This message has a L2 Pseudo Length of 22. +# Network to MS +def systemInformationType2(): + """SYSTEM INFORMATION TYPE 2 Section 9.1.32""" + a = L2PseudoLength(l2pLength=0x16) + b = TpPd(pd=0x6) + c = MessageType(mesType=0x1a) # 00011010 + d = NeighbourCellsDescription() + e = NccPermitted() + f = RachControlParameters() + packet = a / b / c / d / e / f + return packet + + +# This message has a L2 pseudo length of 21 +# Network to MS +def systemInformationType2bis(): + """SYSTEM INFORMATION TYPE 2bis Section 9.1.33""" + a = L2PseudoLength(l2pLength=0x15) + b = TpPd(pd=0x6) + c = MessageType(mesType=0x2) # 00000010 + d = NeighbourCellsDescription() + e = RachControlParameters() + f = Si2bisRestOctets() + packet = a / b / c / d / e / f + return packet + + +# This message has a L2 pseudo length of 18 +# Network to MS +def systemInformationType2ter(): + """SYSTEM INFORMATION TYPE 2ter Section 9.1.34""" + a = L2PseudoLength(l2pLength=0x12) + b = TpPd(pd=0x6) + c = MessageType(mesType=0x3) # 00000011 + d = NeighbourCellsDescription2() + e = Si2terRestOctets() + packet = a / b / c / d / e + return packet + + +# This message has a L2 Pseudo Length of 18 +# Network to MS +def systemInformationType3(): + """SYSTEM INFORMATION TYPE 3 Section 9.1.35""" + a = L2PseudoLength(l2pLength=0x12) + b = TpPd(pd=0x6) + c = MessageType(mesType=0x1b) # 00011011 + d = CellIdentity() + e = LocalAreaId() + f = ControlChannelDescription() + g = CellOptionsBCCH() + h = CellSelectionParameters() + i = RachControlParameters() + j = Si3RestOctets() + packet = a / b / c / d / e / f / g / h / i / j + return packet + + +#The L2 pseudo length of this message is the +#sum of lengths of all information elements present in the message except +#the SI 4 Rest Octets and L2 Pseudo Length +# Network to MS +def systemInformationType4(ChannelDescription_presence=0, + MobileAllocation_presence=0): + """SYSTEM INFORMATION TYPE 4 Section 9.1.36""" + a = L2PseudoLength() + b = TpPd(pd=0x6) + c = MessageType(mesType=0x1C) # 000111100 + d = LocalAreaId() + e = CellSelectionParameters() + f = RachControlParameters() + packet = a / b / c / d / e / f + if ChannelDescription_presence is 1: + g = ChannelDescriptionHdr(ieiCD=0x64, eightBitCD=0x0) + packet = packet / g + if MobileAllocation_presence is 1: + h = MobileAllocationHdr(ieiMA=0x72, eightBitMA=0x0) + packet = packet / h + i = Si4RestOctets() + packet = packet / i + return packet + + +#This message has a L2 Pseudo Length of 18 +# Network to MS +def systemInformationType5(): + """SYSTEM INFORMATION TYPE 5 Section 9.1.37""" + a = L2PseudoLength(l2pLength=0x12) + b = TpPd(pd=0x6) + c = MessageType(mesType=0x35) # 000110101 + d = NeighbourCellsDescription() + packet = a / b / c / d + return packet + + +#This message has a L2 Pseudo Length of 18 +# Network to MS +def systemInformationType5bis(): + """SYSTEM INFORMATION TYPE 5bis Section 9.1.38""" + a = L2PseudoLength(l2pLength=0x12) + b = TpPd(pd=0x6) + c = MessageType(mesType=0x5) # 00000101 + d = NeighbourCellsDescription() + packet = a / b / c / d + return packet + + +# This message has a L2 Pseudo Length of 18 +# Network to MS +def systemInformationType5ter(): + """SYSTEM INFORMATION TYPE 5ter Section 9.1.39""" + a = L2PseudoLength(l2pLength=0x12) + b = TpPd(pd=0x6) + c = MessageType(mesType=0x6) # 00000110 + d = NeighbourCellsDescription2() + packet = a / b / c / d + return packet + + +#This message has a L2 Pseudo Length of 11 +# Network to MS +def systemInformationType6(): + """SYSTEM INFORMATION TYPE 6 Section 9.1.40""" + a = L2PseudoLength(l2pLength=0x0b) + b = TpPd(pd=0x6) + c = MessageType(mesType=0x1e) # 00011011 + d = CellIdentity() + e = LocalAreaId() + f = CellOptionsBCCH() + g = NccPermitted() + h = Si6RestOctets() + packet = a / b / c / d / e / f / g + return packet + + +# The L2 pseudo length of this message has the value 1 +# Network to MS +def systemInformationType7(): + """SYSTEM INFORMATION TYPE 7 Section 9.1.41""" + a = L2PseudoLength(l2pLength=0x01) + b = TpPd(pd=0x6) + c = MessageType(mesType=0x37) # 000110111 + d = Si7RestOctets() + packet = a / b / c / d + return packet + + +# The L2 pseudo length of this message has the value 1 +# Network to MS +def systemInformationType8(): + """SYSTEM INFORMATION TYPE 8 Section 9.1.42""" + a = L2PseudoLength(l2pLength=0x01) + b = TpPd(pd=0x6) + c = MessageType(mesType=0x18) # 00011000 + d = Si8RestOctets() + packet = a / b / c / d + return packet + + +# The L2 pseudo length of this message has the value 1 +# Network to MS +def systemInformationType9(): + """SYSTEM INFORMATION TYPE 9 Section 9.1.43""" + a = L2PseudoLength(l2pLength=0x01) + b = TpPd(pd=0x6) + c = MessageType(mesType=0x4) # 00000100 + d = Si9RestOctets() + packet = a / b / c / d + return packet + + +# The L2 pseudo length of this message has the value 0 +# Network to MS +def systemInformationType13(): + """SYSTEM INFORMATION TYPE 13 Section 9.1.43a""" + a = L2PseudoLength(l2pLength=0x00) + b = TpPd(pd=0x6) + c = MessageType(mesType=0x0) # 00000000 + d = Si13RestOctets() + packet = a / b / c / d + return packet +# +# 9.1.43b / c spare +# + + +# The L2 pseudo length of this message has the value 1 +# Network to MS +def systemInformationType16(): + """SYSTEM INFORMATION TYPE 16 Section 9.1.43d""" + a = L2PseudoLength(l2pLength=0x01) + b = TpPd(pd=0x6) + c = MessageType(mesType=0x3d) # 00111101 + d = Si16RestOctets() + packet = a / b / c / d + return packet + + +# The L2 pseudo length of this message has the value 1 +# Network to MS +def systemInformationType17(): + """SYSTEM INFORMATION TYPE 17 Section 9.1.43e""" + a = L2PseudoLength(l2pLength=0x01) + b = TpPd(pd=0x6) + c = MessageType(mesType=0x3e) # 00111110 + d = Si17RestOctets() + packet = a / b / c / d + return packet + + +def talkerIndication(): + """TALKER INDICATION Section 9.1.44""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x11) # 00010001 + c = MobileStationClassmark2() + d = MobileId() + packet = a / b / c / d + return packet + + +class UplinkAccess(): + """UPLINK ACCESS Section 9.1.45""" + name = "Uplink Access" + fields_desc = [ + ByteField("establishment", 0x0) + ] + + +# Network to MS +def uplinkBusy(): + """UPLINK BUSY Section 9.1.46""" + name = "Uplink Busy" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x2a) # 00101010 + packet = a / b + return packet + + +# Network to MS +class UplinkFree(): + """UPLINK FREE Section 9.1.47""" + name = "Uplink Free" + fields_desc = [ + BitField("pd", 0x0, 1), + BitField("msgType", 0x0, 5), + BitField("layer2Header", 0x0, 2), + BitField("uplinkAccess", 0x0, 1), + BitField("lOrH", 0x0, 1), # 0 for L, 1 for H + BitField("upIdCode", 0x0, 6), + ] + + +def uplinkRelease(): + """UPLINK RELEASE Section 9.1.48""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0xe) # 00001110 + c = RrCause() + packet = a / b / c + return packet + + +# Network to MS +def vgcsUplinkGrant(): + """VGCS UPLINK GRANT Section 9.1.49""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x9) # 00001001 + c = RrCause() + d = RequestReference() + e = TimingAdvance() + packet = a / b / c / d / e + return packet + + +# Network to MS +def systemInformationType10(): + """SYSTEM INFORMATION TYPE 10 Section 9.1.50""" + name = "SyStem Information Type 10" + fields_desc = [ + BitField("pd", 0x0, 1), + BitField("msgType", 0x0, 5), + BitField("layer2Header", 0x0, 2), + BitField("si10", 0x0, 160) + ] + + +# Network to MS +# The L2 pseudo length of this message has the value 18 +def extendedMeasurementOrder(): + """EXTENDED MEASUREMENT ORDER Section 9.1.51""" + a = L2PseudoLength(l2pLength=0x12) + b = TpPd(pd=0x6) + c = MessageType(mesType=0x37) # 00110111 + d = ExtendedMeasurementFrequencyList() + packet = a / b / c / d + return packet + + +def extendedMeasurementReport(): + """EXTENDED MEASUREMENT REPORT Section 9.1.52""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x36) # 00110110 + c = ExtendedMeasurementResults() + packet = a / b / c + return packet + + +def applicationInformation(): + """APPLICATION INFORMATION Section 9.1.53""" + a = TpPd(pd=0x6) + b = MessageType(mesType=0x38) # 00111000 + c = ApduIDAndApduFlags() + e = ApduData() + packet = a / b / c / e + return packet +# +# 9.2 Messages for mobility management +# + + +# Network to MS +def authenticationReject(): + """AUTHENTICATION REJECT Section 9.2.1""" + a = TpPd(pd=0x5) + b = MessageType(mesType=0x11) # 00010001 + packet = a / b + return packet + + +# Network to MS +def authenticationRequest(): + """AUTHENTICATION REQUEST Section 9.2.2""" + a = TpPd(pd=0x5) + b = MessageType(mesType=0x12) # 00010010 + c = CiphKeySeqNrAndSpareHalfOctets() + d = AuthenticationParameterRAND() + packet = a / b / c / d + return packet + + +def authenticationResponse(): + """AUTHENTICATION RESPONSE Section 9.2.3""" + a = TpPd(pd=0x5) + b = MessageType(mesType=0x14) # 00010100 + c = AuthenticationParameterSRES() + packet = a / b / c + return packet + + +def cmReestablishmentRequest(LocalAreaId_presence=0): + """CM RE-ESTABLISHMENT REQUEST Section 9.2.4""" + a = TpPd(pd=0x5) + b = MessageType(mesType=0x28) # 00101000 + c = CiphKeySeqNrAndSpareHalfOctets() + e = MobileStationClassmark2() + f = MobileId() + if LocalAreaId_presence is 1: + g = LocalAreaId(iei=0x13, eightbit=0x0) + packet = packet / g + packet = a / b / c / e / f + return packet + + +# Network to MS +def cmServiceAccept(): + """CM SERVICE ACCEPT Section 9.2.5""" + a = TpPd(pd=0x5) + b = MessageType(mesType=0x21) # 00100001 + packet = a / b + return packet + + +# Network to MS +def cmServicePrompt(): + """CM SERVICE PROMPT Section 9.2.5a""" + a = TpPd(pd=0x5) + b = MessageType(mesType=0x25) # 00100101 + c = PdAndSapi() + packet = a / b / c + return packet + + +# Network to MS +def cmServiceReject(): + """CM SERVICE REJECT Section 9.2.6""" + a = TpPd(pd=0x5) + b = MessageType(mesType=0x22) # 00100010 + c = RejectCause() + packet = a / b / c + return packet + + +def cmServiceAbort(): + """CM SERVICE ABORT Section 9.2.7""" + a = TpPd(pd=0x5) + b = MessageType(mesType=0x23) # 00100011 + packet = a / b + return packet + + +# Network to MS +def abort(): + """ABORT Section 9.2.8""" + a = TpPd(pd=0x5) + b = MessageType(mesType=0x29) # 00101001 + c = RejectCause() + packet = a / b / c + return packet + + +def cmServiceRequest(PriorityLevel_presence=0): + """CM SERVICE REQUEST Section 9.2.9""" + a = TpPd(pd=0x5) + b = MessageType(mesType=0x24) # 00100100 + c = CmServiceTypeAndCiphKeySeqNr() + e = MobileStationClassmark2() + f = MobileId() + packet = a / b / c / e / f + if PriorityLevel_presence is 1: + g = PriorityLevelHdr(ieiPL=0x8, eightBitPL=0x0) + packet = packet / g + return packet + + +# Network to MS +def identityRequest(): + """IDENTITY REQUEST Section 9.2.10""" + a = TpPd(pd=0x5) + b = MessageType(mesType=0x8) # 00001000 + c = IdentityTypeAndSpareHalfOctets() + packet = a / b / c + return packet + + +def identityResponse(): + """IDENTITY RESPONSE Section 9.2.11""" + a = TpPd(pd=0x5) + b = MessageType(mesType=0x9) # 00001001 + c = MobileId() + packet = a / b / c + return packet + + +def imsiDetachIndication(): + """IMSI DETACH INDICATION Section 9.2.12""" + a = TpPd(pd=0x5) + b = MessageType(mesType=0x1) # 00000001 + c = MobileStationClassmark1() + d = MobileId() + packet = a / b / c / d + return packet + + +# Network to MS +def locationUpdatingAccept(MobileId_presence=0, + FollowOnProceed_presence=0, + CtsPermission_presence=0): + """LOCATION UPDATING ACCEPT Section 9.2.13""" + a = TpPd(pd=0x5) + b = MessageType(mesType=0x02) # 00000010 + c = LocalAreaId() + packet = a / b / c + if MobileId_presence is 1: + d = MobileIdHdr(ieiMI=0x17, eightBitMI=0x0) + packet = packet / d + if FollowOnProceed_presence is 1: + e = FollowOnProceed(ieiFOP=0xA1) + packet = packet / e + if CtsPermission_presence is 1: + f = CtsPermissionHdr(ieiCP=0xA2, eightBitCP=0x0) + packet = packet / f + return packet + + +# Network to MS +def locationUpdatingReject(): + """LOCATION UPDATING REJECT Section 9.2.14""" + a = TpPd(pd=0x5) + b = MessageType(mesType=0x4) # 0x00000100 + c = RejectCause() + packet = a / b / c + return packet + + +def locationUpdatingRequest(): + """LOCATION UPDATING REQUEST Section 9.2.15""" + a = TpPd(pd=0x5) + b = MessageType(mesType=0x8) # 00001000 + c = LocationUpdatingTypeAndCiphKeySeqNr() + e = LocalAreaId() + f = MobileStationClassmark1() + g = MobileId() + packet = a / b / c / e / f / g + return packet + + +# Network to MS +def mmInformation(NetworkName_presence=0, NetworkName_presence1=0, + TimeZone_presence=0, TimeZoneAndTime_presence=0, + LsaIdentifier_presence=0): + """MM INFORMATION Section 9.2.15a""" + a = TpPd(pd=0x5) + b = MessageType(mesType=0x32) # 00110010 + packet = a / b + if NetworkName_presence is 1: + c = NetworkNameHdr(ieiNN=0x43, eightBitNN=0x0) + packet = packet / c + if NetworkName_presence1 is 1: + d = NetworkNameHdr(ieiNN=0x45, eightBitNN=0x0) + packet = packet / d + if TimeZone_presence is 1: + e = TimeZoneHdr(ieiTZ=0x46, eightBitTZ=0x0) + packet = packet / e + if TimeZoneAndTime_presence is 1: + f = TimeZoneAndTimeHdr(ieiTZAT=0x47, eightBitTZAT=0x0) + packet = packet / f + if LsaIdentifier_presence is 1: + g = LsaIdentifierHdr(ieiLI=0x48, eightBitLI=0x0) + packet = packet / g + return packet + + +def mmStatus(): + """MM STATUS Section 9.2.16""" + a = TpPd(pd=0x5) + b = MessageType(mesType=0x31) # 00110001 + c = RejectCause() + packet = a / b / c + return packet + + +# Network to MS +def tmsiReallocationCommand(): + """TMSI REALLOCATION COMMAND Section 9.2.17""" + a = TpPd(pd=0x5) + b = MessageType(mesType=0x1a) # 00011010 + c = LocalAreaId() + d = MobileId() + packet = a / b / c / d + return packet + + +def tmsiReallocationComplete(): + """TMSI REALLOCATION COMPLETE Section 9.2.18""" + a = TpPd(pd=0x5) + b = MessageType(mesType=0x1b) # 00011011 + packet = a / b + return packet + + +def mmNull(): + """MM NULL Section 9.2.19""" + a = TpPd(pd=0x5) + b = MessageType(mesType=0x30) # 00110000 + packet = a / b + return packet + +# +# 9.3 Messages for circuit-switched call control +# + + +# Network to MS +def alertingNetToMs(Facility_presence=0, ProgressIndicator_presence=0, + UserUser_presence=0): + """ALERTING Section 9.3.1.1""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x1) # 00000001 + packet = a / b + if Facility_presence is 1: + c = FacilityHdr(ieiF=0x1C) + packet = packet / c + if ProgressIndicator_presence is 1: + d = ProgressIndicatorHdr(ieiPI=0x1E) + packet = packet / d + if UserUser_presence is 1: + e = UserUserHdr(ieiUU=0x7E) + packet = packet / e + return packet + + +def alertingMsToNet(Facility_presence=0, UserUser_presence=0, + SsVersionIndicator_presence=0): + """ALERTING Section 9.3.1.2""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x1) # 00000001 + packet = a / b + if Facility_presence is 1: + c = FacilityHdr(ieiF=0x1C, eightBitF=0x0) + packet = packet / c + if UserUser_presence is 1: + d = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) + packet = packet / d + if SsVersionIndicator_presence is 1: + e = SsVersionIndicatorHdr(ieiSVI=0x7F, eightBitSVI=0x0) + packet = packet / e + return packet + + +def callConfirmed(RepeatIndicator_presence=0, + BearerCapability_presence=0, BearerCapability_presence1=0, + Cause_presence=0, CallControlCapabilities_presence=0): + """CALL CONFIRMED Section 9.3.2""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x8) # 00001000 + packet = a / b + if RepeatIndicator_presence is 1: + c = RepeatIndicatorHdr(ieiRI=0xD, eightBitRI=0x0) + packet = packet / c + if BearerCapability_presence is 1: + d = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) + packet = packet / d + if BearerCapability_presence1 is 1: + e = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) + packet = packet / e + if Cause_presence is 1: + f = CauseHdr(ieiC=0x08, eightBitC=0x0) + packet = packet / f + if CallControlCapabilities_presence is 1: + g = CallControlCapabilitiesHdr(ieiCCC=0x15, eightBitCCC=0x0) + packet = packet / g + return packet + + +# Network to MS +def callProceeding(RepeatIndicator_presence=0, + BearerCapability_presence=0, + BearerCapability_presence1=0, + Facility_presence=0, ProgressIndicator_presence=0, + PriorityLevel_presence=0): + """CALL PROCEEDING Section 9.3.3""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x2) # 00000010 + packet = a / b + if RepeatIndicator_presence is 1: + c = RepeatIndicatorHdr(ieiRI=0xD, eightBitRI=0x0) + packet = packet / c + if BearerCapability_presence is 1: + d = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) + packet = packet / d + if BearerCapability_presence1 is 1: + e = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) + packet = packet / e + if Facility_presence is 1: + f = FacilityHdr(ieiF=0x1C, eightBitF=0x0) + packet = packet / f + if ProgressIndicator_presence is 1: + g = ProgressIndicatorHdr(ieiPI=0x1E, eightBitPI=0x0) + packet = packet / g + if PriorityLevel_presence is 1: + h = PriorityLevelHdr(ieiPL=0x80, eightBitPL=0x0) + packet = packet / h + return packet + + +# Network to MS +def congestionControl(Cause_presence=0): + """CONGESTION CONTROL Section 9.3.4""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x39) # 00111001 + c = CongestionLevelAndSpareHalfOctets() + packet = a / b / c + if Cause_presence is 1: + e = CauseHdr(ieiC=0x08, eightBitC=0x0) + packet = packet / e + return packet + + +# Network to MS +def connectNetToMs(Facility_presence=0, ProgressIndicator_presence=0, + ConnectedNumber_presence=0, ConnectedSubaddress_presence=0, + UserUser_presence=0): + """CONNECT Section 9.3.5.1""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x7) # 00000111 + packet = a / b + if Facility_presence is 1: + c = FacilityHdr(ieiF=0x1C, eightBitF=0x0) + packet = packet / c + if ProgressIndicator_presence is 1: + d = ProgressIndicatorHdr(ieiPI=0x1E, eightBitPI=0x0) + packet = packet / d + if ConnectedNumber_presence is 1: + e = ConnectedNumberHdr(ieiCN=0x4C, eightBitCN=0x0) + packet = packet / e + if ConnectedSubaddress_presence is 1: + f = ConnectedSubaddressHdr(ieiCS=0x4D, eightBitCS=0x0) + packet = packet / f + if UserUser_presence is 1: + g = UserUserHdr(ieiUU=0x7F, eightBitUU=0x0) + packet = packet / g + return packet + + +def connectMsToNet(Facility_presence=0, ConnectedSubaddress_presence=0, + UserUser_presence=0, SsVersionIndicator_presence=0): + """CONNECT Section 9.3.5.2""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x7) # 00000111 + packet = a / b + if Facility_presence is 1: + c = FacilityHdr(ieiF=0x1C, eightBitF=0x0) + packet = packet / c + if ConnectedSubaddress_presence is 1: + d = ConnectedSubaddressHdr(ieiCS=0x4D, eightBitCS=0x0) + packet = packet / d + if UserUser_presence is 1: + e = UserUserHdr(ieiUU=0x7F, eightBitUU=0x0) + packet = packet / e + if SsVersionIndicator_presence is 1: + f = SsVersionIndicatorHdr(ieiSVI=0x7F, eightBitSVI=0x0) + packet = packet / f + return packet + + +def connectAcknowledge(): + """CONNECT ACKNOWLEDGE Section 9.3.6""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0xf) # 00001111 + packet = a / b + return packet + + +# Network to MS +def disconnectNetToMs(Facility_presence=0, ProgressIndicator_presence=0, + UserUser_presence=0, AllowedActions_presence=0): + """DISCONNECT Section 9.3.7.1""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x25) # 00100101 + c = Cause() + packet = a / b / c + if Facility_presence is 1: + d = FacilityHdr(ieiF=0x1C, eightBitF=0x0) + packet = packet / d + if ProgressIndicator_presence is 1: + e = ProgressIndicatorHdr(ieiPI=0x1E, eightBitPI=0x0) + packet = packet / e + if UserUser_presence is 1: + f = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) + packet = packet / f + if AllowedActions_presence is 1: + g = AllowedActionsHdr(ieiAA=0x7B, eightBitAA=0x0) + packet = packet / g + return packet + + +def disconnectMsToNet(Facility_presence=0, UserUser_presence=0, + SsVersionIndicator_presence=0): + """Disconnect Section 9.3.7.2""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x25) # 00100101 + c = Cause() + packet = a / b / c + if Facility_presence is 1: + d = FacilityHdr(ieiF=0x1C, eightBitF=0x0) + packet = packet / d + if UserUser_presence is 1: + e = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) + packet = packet / e + if SsVersionIndicator_presence is 1: + f = SsVersionIndicatorHdr(ieiSVI=0x7F, eightBitSVI=0x0) + packet = packet / f + return packet + + +def emergencySetup(BearerCapability_presence=0): + """EMERGENCY SETUP Section 9.3.8""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0xe) # 00001110 + packet = a / b + if BearerCapability_presence is 1: + c = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) + packet = packet / c + return packet + + +# Network to MS +def facilityNetToMs(): + """FACILITY Section 9.3.9.1""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x3a) # 00111010 + c = Facility() + packet = a / b / c + return packet + + +def facilityMsToNet(SsVersionIndicator_presence=0): + """FACILITY Section 9.3.9.2""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x3a) # 00111010 + c = Facility() + packet = a / b / c + if SsVersionIndicator_presence is 1: + d = SsVersionIndicatorHdr(ieiSVI=0x7F, eightBitSVI=0x0) + packet = packet / d + return packet + + +def hold(): + """HOLD Section 9.3.10""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x18) # 00011000 + packet = a / b + return packet + + +# Network to MS +def holdAcknowledge(): + """HOLD ACKNOWLEDGE Section 9.3.11""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x19) # 00011001 + packet = a / b + return packet + + +# Network to MS +def holdReject(): + """HOLD REJECT Section 9.3.12""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x1a) # 00011010 + c = Cause() + packet = a / b / c + return packet + + +def modify(LowLayerCompatibility_presence=0, + HighLayerCompatibility_presence=0, + ReverseCallSetupDirection_presence=0): + """MODIFY Section 9.3.13""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x17) # 00010111 + c = BearerCapability() + packet = a / b / c + if LowLayerCompatibility_presence is 1: + d = LowLayerCompatibilityHdr(ieiLLC=0x7C, eightBitLLC=0x0) + packet = packet / d + if HighLayerCompatibility_presence is 1: + e = HighLayerCompatibilityHdr(ieiHLC=0x7D, eightBitHLC=0x0) + packet = packet / e + if ReverseCallSetupDirection_presence is 1: + f = ReverseCallSetupDirectionHdr(ieiRCSD=0xA3) + packet = packet / f + return packet + + +def modifyComplete(LowLayerCompatibility_presence=0, + HighLayerCompatibility_presence=0, + ReverseCallSetupDirection_presence=0): + """MODIFY COMPLETE Section 9.3.14""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x1f) # 00011111 + c = BearerCapability() + packet = a / b / c + if LowLayerCompatibility_presence is 1: + d = LowLayerCompatibilityHdr(ieiLLC=0x7C, eightBitLLC=0x0) + packet = packet / d + if HighLayerCompatibility_presence is 1: + e = HighLayerCompatibilityHdr(ieiHLC=0x7D, eightBitHLC=0x0) + packet = packet / e + if ReverseCallSetupDirection_presence is 1: + f = ReverseCallSetupDirection(ieiRCSD=0xA3) + packet = packet / f + return packet + + +def modifyReject(LowLayerCompatibility_presence=0, + HighLayerCompatibility_presence=0): + """MODIFY REJECT Section 9.3.15""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x13) # 00010011 + c = BearerCapability() + d = Cause() + packet = a / b / c / d + if LowLayerCompatibility_presence is 1: + e = LowLayerCompatibilityHdr(ieiLLC=0x7C, eightBitLLC=0x0) + packet = packet / e + if HighLayerCompatibility_presence is 1: + f = HighLayerCompatibilityHdr(ieiHLC=0x7D, eightBitHLC=0x0) + packet = packet / f + return packet + + +def notify(): + """NOTIFY Section 9.3.16""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x3e) # 00111110 + c = NotificationIndicator() + packet = a / b / c + return packet + + +# Network to MS +def progress(UserUser_presence=0): + """PROGRESS Section 9.3.17""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x3) # 00000011 + c = ProgressIndicator() + packet = a / b / c + if UserUser_presence is 1: + d = UserUserHdr() + packet = packet / d + return packet + + +# Network to MS +def ccEstablishment(): + """CC-ESTABLISHMENT Section 9.3.17a""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x4) # 00000100 + c = SetupContainer() + packet = a / b / c + return packet + + +def ccEstablishmentConfirmed(RepeatIndicator_presence=0, + BearerCapability_presence=0, + BearerCapability_presence1=0, + Cause_presence=0): + """CC-ESTABLISHMENT CONFIRMED Section 9.3.17b""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x6) # 00000110 + packet = a / b + if RepeatIndicator_presence is 1: + c = RepeatIndicatorHdr(ieiRI=0xD, eightBitRI=0x0) + packet = packet / c + if BearerCapability_presence is 1: + d = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) + packet = packet / d + if BearerCapability_presence1 is 1: + e = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) + packet = packet / e + if Cause_presence is 1: + f = CauseHdr(ieiC=0x08, eightBitC=0x0) + packet = packet / f + return packet + + +# Network to MS +def releaseNetToMs(): + """RELEASE Section 9.3.18.1""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x2d) # 00101101 + c = CauseHdr(ieiC=0x08, eightBitC=0x0) + d = CauseHdr(ieiC=0x08, eightBitC=0x0) + e = FacilityHdr(ieiF=0x1C, eightBitF=0x0) + f = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) + packet = a / b / c / d / e / f + return packet + + +def releaseMsToNet(Cause_presence=0, Cause_presence1=0, + Facility_presence=0, UserUser_presence=0, + SsVersionIndicator_presence=0): + """RELEASE Section 9.3.18.2""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x2d) # 00101101 + packet = a / b + if Cause_presence is 1: + c = CauseHdr(ieiC=0x08, eightBitC=0x0) + packet = packet / c + if Cause_presence1 is 1: + d = CauseHdr(ieiC=0x08, eightBitC=0x0) + packet = packet / d + if Facility_presence is 1: + e = FacilityHdr(ieiF=0x1C, eightBitF=0x0) + packet = packet / e + if UserUser_presence is 1: + f = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) + packet = packet / f + if SsVersionIndicator_presence is 1: + g = SsVersionIndicatorHdr(ieiSVI=0x7F, eightBitSVI=0x0) + packet = packet / g + return packet + + +# Network to MS +def recall(): + """RECALL Section 9.3.18a""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0xb) # 00001011 + c = RecallType() + d = Facility() + packet = a / b / c / d + return packet + + +# Network to MS +def releaseCompleteNetToMs(Cause_presence=0, Facility_presence=0, + UserUser_presence=0): + """RELEASE COMPLETE Section 9.3.19.1""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x2a) # 00101010 + packet = a / b + if Cause_presence is 1: + c = CauseHdr(ieiC=0x08, eightBitC=0x0) + packet = packet / c + if Facility_presence is 1: + d = FacilityHdr(ieiF=0x1C, eightBitF=0x0) + packet = packet / d + if UserUser_presence is 1: + e = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) + packet = packet / e + return packet + + +def releaseCompleteMsToNet(Cause_presence=0, Facility_presence=0, + UserUser_presence=0, SsVersionIndicator_presence=0): + """RELEASE COMPLETE Section 9.3.19.2""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x2a) # 00101010 + packet = a / b + if Cause_presence is 1: + c = CauseHdr(ieiC=0x08, eightBitC=0x0) + packet = packet / c + if Facility_presence is 1: + d = FacilityHdr(ieiF=0x1C, eightBitF=0x0) + packet = packet / d + if UserUser_presence is 1: + e = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) + packet = packet / e + if SsVersionIndicator_presence is 1: + f = SsVersionIndicatorHdr(ieiSVI=0x7F, eightBitSVI=0x0) + packet = packet / f + return packet + + +def retrieve(): + """RETRIEVE Section 9.3.20""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x1c) # 00011100 + packet = a / b + return packet + + +# Network to MS +def retrieveAcknowledge(): + """RETRIEVE ACKNOWLEDGE Section 9.3.21""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x1d) # 00011101 + packet = a / b + return packet + + +# Network to MS +def retrieveReject(): + """RETRIEVE REJECT Section 9.3.22""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x1e) # 00011110 + c = Cause() + packet = a / b / c + return packet + + +# Network to MS +def setupMobileTerminated(RepeatIndicator_presence=0, + BearerCapability_presence=0, + BearerCapability_presence1=0, + Facility_presence=0, ProgressIndicator_presence=0, + Signal_presence=0, + CallingPartyBcdNumber_presence=0, + CallingPartySubaddress_presence=0, + CalledPartyBcdNumber_presence=0, + CalledPartySubaddress_presence=0, +# RecallType_presence=0, + RedirectingPartyBcdNumber_presence=0, + RedirectingPartySubaddress_presence=0, + RepeatIndicator_presence1=0, + LowLayerCompatibility_presence=0, + LowLayerCompatibility_presence1=0, + RepeatIndicator_presence2=0, + HighLayerCompatibility_presence=0, + HighLayerCompatibility_presence1=0, + UserUser_presence=0, PriorityLevel_presence=0, + AlertingPattern_presence=0): + """SETUP Section 9.3.23.1""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x5) # 00000101 + packet = a / b + if RepeatIndicator_presence is 1: + c = RepeatIndicatorHdr(ieiRI=0xD, eightBitRI=0x0) + packet = packet / c + if BearerCapability_presence is 1: + d = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) + packet = packet / d + if BearerCapability_presence1 is 1: + e = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) + packet = packet / e + if Facility_presence is 1: + f = FacilityHdr(ieiF=0x1C, eightBitF=0x0) + packet = packet / f + if ProgressIndicator_presence is 1: + g = ProgressIndicatorHdr(ieiPI=0x1E, eightBitPI=0x0) + packet = packet / g + if Signal_presence is 1: + h = SignalHdr(ieiS=0x34, eightBitS=0x0) + packet = packet / h + if CallingPartyBcdNumber_presence is 1: + i = CallingPartyBcdNumberHdr(ieiCPBN=0x5C, eightBitCPBN=0x0) + packet = packet / i + if CallingPartySubaddress_presence is 1: + j = CallingPartySubaddressHdr(ieiCPS=0x5D, eightBitCPS=0x0) + packet = packet / j + if CalledPartyBcdNumber_presence is 1: + k = CalledPartyBcdNumberHdr(ieiCPBN=0x5E, eightBitCPBN=0x0) + packet = packet / k + if CalledPartySubaddress_presence is 1: + l = CalledPartySubaddressHdr(ieiCPS=0x6D, eightBitCPS=0x0) + packet = packet / l + if RedirectingPartyBcdNumber_presence is 1: + n = RedirectingPartyBcdNumberHdr(ieiRPBN=0x74, eightBitRPBN=0x0) + packet = packet / n + if RedirectingPartySubaddress_presence is 1: + m = RedirectingPartySubaddress_presence(ieiRPBN=0x75, eightBitRPBN=0x0) + packet = packet / m + if RepeatIndicator_presence1 is 1: + o = RepeatIndicatorHdr(ieiRI=0xD0, eightBitRI=0x0) + packet = packet / o + if LowLayerCompatibility_presence is 1: + p = LowLayerCompatibilityHdr(ieiLLC=0x7C, eightBitLLC=0x0) + packet = packet / p + if LowLayerCompatibility_presence1 is 1: + q = LowLayerCompatibilityHdr(ieiLLC=0x7C, eightBitLLC=0x0) + packet = packet / q + if RepeatIndicator_presence2 is 1: + r = RepeatIndicatorHdr(ieiRI=0xD, eightBitRI=0x0) + packet = packet / r + if HighLayerCompatibility_presence is 1: + s = HighLayerCompatibilityHdr(ieiHLC=0x7D, eightBitHLC=0x0) + packet = packet / s + if HighLayerCompatibility_presence1 is 1: + t = HighLayerCompatibilityHdr(ieiHLC=0x7D, eightBitHLC=0x0) + packet = packet / t + if UserUser_presence is 1: + u = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) + packet = packet / u + if PriorityLevel_presence is 1: + v = PriorityLevelHdr(ieiPL=0x8, eightBitPL=0x0) + packet = packet / v + if AlertingPattern_presence is 1: + w = AlertingPatternHdr(ieiAP=0x19, eightBitAP=0x0) + packet = packet / w + return packet + + +def setupMobileOriginated(RepeatIndicator_presence=0, + BearerCapability_presence=0, + BearerCapability_presence1=0, + Facility_presence=0, + CallingPartySubaddress_presence=0, + CalledPartyBcdNumber_presence=0, + CalledPartySubaddress_presence=0, + RepeatIndicator_presence1=0, + LowLayerCompatibility_presence=0, + LowLayerCompatibility_presence1=0, + RepeatIndicator_presence2=0, + HighLayerCompatibility_presence=0, + HighLayerCompatibility_presence1=0, + UserUser_presence=0, SsVersionIndicator_presence=0, + ClirSuppression_presence=0, + ClirInvocation_presence=0, + CallControlCapabilities_presence=0, + Facility_presence1=0, + Facility_presence2=0): + """SETUP Section 9.3.23.2""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x5) # 00000101 + packet = a / b + if RepeatIndicator_presence is 1: + c = RepeatIndicatorHdr(ieiRI=0xD, eightBitRI=0x0) + packet = packet / c + if BearerCapability_presence is 1: + d = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) + packet = packet / d + if BearerCapability_presence1 is 1: + e = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) + packet = packet / e + if Facility_presence is 1: + f = FacilityHdr(ieiF=0x1C, eightBitF=0x0) + packet = packet / f + if CallingPartySubaddress_presence is 1: + g = CallingPartySubaddressHdr(ieiCPS=0x5D, eightBitCPS=0x0) + packet = packet / g + if CalledPartyBcdNumber_presence is 1: + h = CalledPartyBcdNumberHdr(ieiCPBN=0x5E, eightBitCPBN=0x0) + packet = packet / h + if CalledPartySubaddress_presence is 1: + i = CalledPartySubaddressHdr(ieiCPS=0x6D, eightBitCPS=0x0) + packet = packet / i + if RepeatIndicator_presence1 is 1: + j = RepeatIndicatorHdr(ieiRI=0xD0, eightBitRI=0x0) + packet = packet / j + if LowLayerCompatibility_presence is 1: + k = LowLayerCompatibilityHdr(ieiLLC=0x7C, eightBitLLC=0x0) + packet = packet / k + if LowLayerCompatibility_presence1 is 1: + l = LowLayerCompatibilityHdr(ieiLLC=0x7C, eightBitLLC=0x0) + packet = packet / l + if RepeatIndicator_presence2 is 1: + m = RepeatIndicatorHdr(ieiRI=0xD, eightBitRI=0x0) + packet = packet / m + if HighLayerCompatibility_presence is 1: + n = HighLayerCompatibilityHdr(ieiHLC=0x7D, eightBitHLC=0x0) + packet = packet / n + if HighLayerCompatibility_presence1 is 1: + o = HighLayerCompatibilityHdr(ieiHLC=0x7D, eightBitHLC=0x0) + packet = packet / o + if UserUser_presence is 1: + p = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) + packet = packet / p + if SsVersionIndicator_presence is 1: + q = SsVersionIndicatorHdr(ieiSVI=0x7F, eightBitSVI=0x0) + packet = packet / q + if ClirSuppression_presence is 1: + r = ClirSuppressionHdr(ieiCS=0xA1, eightBitCS=0x0) + packet = packet / r + if ClirInvocation_presence is 1: + s = ClirInvocationHdr(ieiCI=0xA2, eightBitCI=0x0) + packet = packet / s + if CallControlCapabilities_presence is 1: + t = CallControlCapabilitiesHdr(ieiCCC=0x15, eightBitCCC=0x0) + packet = packet / t + if Facility_presence1 is 1: + u = FacilityHdr(ieiF=0x1D, eightBitF=0x0) + packet = packet / u + if Facility_presence2 is 1: + v = FacilityHdr(ieiF=0x1B, eightBitF=0x0) + packet = packet / v + return packet + + +def startCc(CallControlCapabilities_presence=0): + """START CC Section 9.3.23a""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x9) # 00001001 + packet = a / b + if CallControlCapabilities_presence is 1: + c = CallControlCapabilitiesHdr(ieiCCC=0x15, eightBitCCC=0x0) + packet = paclet / c + return packet + + +def startDtmf(): + """START DTMF Section 9.3.24""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x35) # 00110101 + c = KeypadFacilityHdr(ieiKF=0x2C, eightBitKF=0x0) + packet = a / b / c + return packet + + +# Network to MS +def startDtmfAcknowledge(): + """START DTMF ACKNOWLEDGE Section 9.3.25""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x32) # 00110010 + c = KeypadFacilityHdr(ieiKF=0x2C, eightBitKF=0x0) + packet = a / b / c + return packet + + +# Network to MS +def startDtmfReject(): + """ START DTMF REJECT Section 9.3.26""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x37) # 00110111 + c = Cause() + packet = a / b / c + return packet + + +def status(AuxiliaryStates_presence=0): + """STATUS Section 9.3.27""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x3d) # 00111101 + c = Cause() + d = CallState() + packet = a / b / c / d + if AuxiliaryStates_presence is 1: + e = AuxiliaryStatesHdr(ieiAS=0x24, eightBitAS=0x0) + packet = packet / e + return packet + + +def statusEnquiry(): + """STATUS ENQUIRY Section 9.3.28""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x34) # 00110100 + packet = a / b + return packet + + +def stopDtmf(): + """STOP DTMF Section 9.3.29""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x31) # 00110001 + packet = a / b + return packet + + +# Network to MS +def stopDtmfAcknowledge(): + """STOP DTMF ACKNOWLEDGE Section 9.3.30""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x32) # 00110010 + packet = a / b + return packet + + +def userInformation(MoreData_presence=0): + """USER INFORMATION Section 9.3.31""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x20) # 000100000 + c = UserUser() + packet = a / b / c + if MoreData_presence is 1: + d = MoreDataHdr(ieiMD=0xA0, eightBitMD=0x0) + packet = packet / d + return packet + +# +# 9.4 GPRS Mobility Management Messages +# + + +def attachRequest(PTmsiSignature_presence=0, GprsTimer_presence=0, + TmsiStatus_presence=0): + """ATTACH REQUEST Section 9.4.1""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x1) # 0000001 + c = MsNetworkCapability() + d = AttachTypeAndCiphKeySeqNr() + f = DrxParameter() + g = MobileId() + h = RoutingAreaIdentification() + i = MsRadioAccessCapability() + packet = a / b / c / d / f / g / h / i + if PTmsiSignature_presence is 1: + j = PTmsiSignature(ieiPTS=0x19) + packet = packet / j + if GprsTimer_presence is 1: + k = GprsTimer(ieiGT=0x17) + packet = packet / k + if TmsiStatus_presence is 1: + l = TmsiStatus(ieiTS=0x9) + packet = packet / l + return packet + + +def attachAccept(PTmsiSignature_presence=0, GprsTimer_presence=0, + MobileId_presence=0, MobileId_presence1=0, + GmmCause_presence=0): + """ATTACH ACCEPT Section 9.4.2""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x2) # 00000010 + c = AttachResult() + d = ForceToStandby() + e = GprsTimer() + f = RadioPriorityAndSpareHalfOctets() + h = RoutingAreaIdentification() + packet = a / b / c / d / e / f / h + if PTmsiSignature_presence is 1: + i = PTmsiSignature(ieiPTS=0x19) + packet = packet / i + if GprsTimer_presence is 1: + j = GprsTimer(ieiGT=0x17) + packet = packet / j + if MobileId_presence is 1: + k = MobileIdHdr(ieiMI=0x18, eightBitMI=0x0) + packet = packet / k + if MobileId_presence1 is 1: + l = MobileIdHdr(ieiMI=0x23, eightBitMI=0x0) + packet = packet / l + if GmmCause_presence is 1: + m = GmmCause(ieiGC=0x25) + packet = packet / m + return packet + + +def attachComplete(): + """ATTACH COMPLETE Section 9.4.3""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x3) # 00000011 + packet = a / b + return packet + + +def attachReject(): + """ATTACH REJECT Section 9.4.4""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x1) # 00000001 + c = GmmCause() + packet = a / b / c + return packet + + +def detachRequest(GmmCause_presence=0): + """DETACH REQUEST Section 9.4.5""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x5) # 00000101 + c = DetachTypeAndForceToStandby() + packet = a / b / c + if GmmCause_presence is 1: + e = GmmCause(ieiGC=0x25) + packet = packet / e + return packet + + +def detachRequestMsOriginating(): + """DETACH REQUEST Section 9.4.5.2""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x5) # 00000101 + c = DetachTypeAndSpareHalfOctets() + packet = a / b / c + return packet + + +def detachAcceptMsTerminated(): + """DETACH ACCEPT Section 9.4.6.1""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x6) # 00000110 + packet = a / b + return packet + + +def detachAcceptMsOriginating(): + """DETACH ACCEPT Section 9.4.6.2""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x6) # 00000110 + c = ForceToStandbyAndSpareHalfOctets() + packet = a / b / c + return packet + + +def ptmsiReallocationCommand(PTmsiSignature_presence=0): + """P-TMSI REALLOCATION COMMAND Section 9.4.7""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x10) # 00010000 + c = MobileId() + d = RoutingAreaIdentification() + e = ForceToStandbyAndSpareHalfOctets() + packet = a / b / c / d / e + if PTmsiSignature_presence is 1: + g = PTmsiSignature(ieiPTS=0x19) + packet = packet / g + return packet + + +def ptmsiReallocationComplete(): + """P-TMSI REALLOCATION COMPLETE Section 9.4.8""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x11) # 00010001 + packet = a / b + return packet + + +def authenticationAndCipheringRequest( + AuthenticationParameterRAND_presence=0, + CiphKeySeqNr_presence=0): + """AUTHENTICATION AND CIPHERING REQUEST Section 9.4.9""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x12) # 00010010 + d = CipheringAlgorithmAndImeisvRequest() + e = ForceToStandbyAndAcReferenceNumber() + packet = a / b / d / e + if AuthenticationParameterRAND_presence is 1: + g = AuthenticationParameterRAND(ieiAPR=0x21) + packet = packet / g + if CiphKeySeqNr_presence is 1: + h = CiphKeySeqNrHdr(ieiCKSN=0x08, eightBitCKSN=0x0) + packet = packet / h + return packet + + +def authenticationAndCipheringResponse( + AuthenticationParameterSRES_presence=0, + MobileId_presence=0): + """AUTHENTICATION AND CIPHERING RESPONSE Section 9.4.10""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x13) # 00010011 + c = AcReferenceNumberAndSpareHalfOctets() + packet = a / b / c + if AuthenticationParameterSRES_presence is 1: + e = AuthenticationParameterSRES(ieiAPS=0x22) + packet = packet / e + if MobileId_presence is 1: + f = MobileIdHdr(ieiMI=0x23, eightBitMI=0x0) + packet = packet / f + return packet + + +def authenticationAndCipheringReject(): + """AUTHENTICATION AND CIPHERING REJECT Section 9.4.11""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x14) # 00010100 + packet = a / b + return packet + + +def identityRequest(): + """IDENTITY REQUEST Section 9.4.12""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x15) # 00010101 + c = IdentityType2AndforceToStandby() + packet = a / b / c + return packet + + +def identityResponse(): + """IDENTITY RESPONSE Section 9.4.13""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x16) # 00010110 + c = MobileId() + packet = a / b / c + return packet + + +def routingAreaUpdateRequest(PTmsiSignature_presence=0, + GprsTimer_presence=0, + DrxParameter_presence=0, + TmsiStatus_presence=0): + """ROUTING AREA UPDATE REQUEST Section 9.4.14""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x8) # 00001000 + c = UpdateTypeAndCiphKeySeqNr() + e = RoutingAreaIdentification() + f = MsNetworkCapability() + packet = a / b / c / e / f + if PTmsiSignature_presence is 1: + g = PTmsiSignature(ieiPTS=0x19) + packet = packet / g + if GprsTimer_presence is 1: + h = GprsTimer(ieiGT=0x17) + packet = packet / h + if DrxParameter_presence is 1: + i = DrxParameter(ieiDP=0x27) + packet = packet / i + if TmsiStatus_presence is 1: + j = TmsiStatus(ieiTS=0x9) + packet = packet / j + return packet + + +def routingAreaUpdateAccept(PTmsiSignature_presence=0, + MobileId_presence=0, MobileId_presence1=0, + ReceiveNpduNumbersList_presence=0, + GprsTimer_presence=0, GmmCause_presence=0): + """ROUTING AREA UPDATE ACCEPT Section 9.4.15""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x9) # 00001001 + c = ForceToStandbyAndUpdateResult() + e = GprsTimer() + f = RoutingAreaIdentification() + packet = a / b / c / e / f + if PTmsiSignature_presence is 1: + g = PTmsiSignature(ieiPTS=0x19) + packet = packet / g + if MobileId_presence is 1: + h = MobileIdHdr(ieiMI=0x18, eightBitMI=0x0) + packet = packet / h + if MobileId_presence1 is 1: + i = MobileIdHdr(ieiMI=0x23, eightBitMI=0x0) + packet = packet / i + if ReceiveNpduNumbersList_presence is 1: + j = ReceiveNpduNumbersList(ieiRNNL=0x26) + packet = packet / j + if GprsTimer_presence is 1: + k = GprsTimer(ieiGT=0x17) + packet = packet / k + if GmmCause_presence is 1: + l = GmmCause(ieiGC=0x25) + packet = packet / l + return packet + + +def routingAreaUpdateComplete(ReceiveNpduNumbersList_presence=0): + """ROUTING AREA UPDATE COMPLETE Section 9.4.16""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0xa) # 00001010 + packet = a / b + if ReceiveNpduNumbersList_presence is 1: + c = ReceiveNpduNumbersList(ieiRNNL=0x26) + packet = packet / c + return packet + + +def routingAreaUpdateReject(): + """ROUTING AREA UPDATE REJECT Section 9.4.17""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0xb) # 00001011 + c = GmmCause() + d = ForceToStandbyAndSpareHalfOctets() + packet = a / b / c / d + return packet + + +def gmmStatus(): + """GMM STATUS Section 9.4.18""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x20) # 00100000 + c = GmmCause() + packet = a / b / c + return packet + + +def gmmInformation(NetworkName_presence=0, NetworkName_presence1=0, + TimeZone_presence=0, TimeZoneAndTime_presence=0, + LsaIdentifier_presence=0): + """GMM INFORMATION Section 9.4.19""" + a = TpPd(pd=0x3) + b = MessageType(mesType=0x21) # 00100001 + packet = a / b + if NetworkName_presence is 1: + c = NetworkNameHdr(ieiNN=0x43, eightBitNN=0x0) + packet = packet / c + if NetworkName_presence1 is 1: + d = NetworkNameHdr(ieiNN=0x45, eightBitNN=0x0) + packet = packet / d + if TimeZone_presence is 1: + e = TimeZoneHdr(ieiTZ=0x46, eightBitTZ=0x0) + packet = packet / e + if TimeZoneAndTime_presence is 1: + f = TimeZoneAndTimeHdr(ieiTZAT=0x47, eightBitTZAT=0x0) + packet = packet / f + if LsaIdentifier_presence is 1: + g = LsaIdentifierHdr(ieiLI=0x48, eightBitLI=0x0) + packet = packet / g + return packet + +# +# 9.5 GPRS Session Management Messages +# + + +def activatePdpContextRequest(AccessPointName_presence=0, + ProtocolConfigurationOptions_presence=0): + """ACTIVATE PDP CONTEXT REQUEST Section 9.5.1""" + a = TpPd(pd=0x8) + b = MessageType(mesType=0x41) # 01000001 + c = NetworkServiceAccessPointIdentifier() + d = LlcServiceAccessPointIdentifier() + e = QualityOfService() + f = PacketDataProtocolAddress() + packet = a / b / c / d / e / f + if AccessPointName_presence is 1: + g = AccessPointName(ieiAPN=0x28) + packet = packet / g + if ProtocolConfigurationOptions_presence is 1: + h = ProtocolConfigurationOptions(ieiPCO=0x27) + packet = packet / h + return packet + + +def activatePdpContextAccept(PacketDataProtocolAddress_presence=0, + ProtocolConfigurationOptions_presence=0): + """ACTIVATE PDP CONTEXT ACCEPT Section 9.5.2""" + a = TpPd(pd=0x8) + b = MessageType(mesType=0x42) # 01000010 + c = LlcServiceAccessPointIdentifier() + d = QualityOfService() + e = RadioPriorityAndSpareHalfOctets() + packet = a / b / c / d / e + if PacketDataProtocolAddress_presence is 1: + f = PacketDataProtocolAddress(ieiPDPA=0x2B) + packet = packet / f + if ProtocolConfigurationOptions_presence is 1: + g = ProtocolConfigurationOptions(ieiPCO=0x27) + packet = packet / g + return packet + + +def activatePdpContextReject(ProtocolConfigurationOptions_presence=0): + """ACTIVATE PDP CONTEXT REJECT Section 9.5.3""" + a = TpPd(pd=0x8) + b = MessageType(mesType=0x43) # 01000011 + c = SmCause() + packet = a / b / c + if ProtocolConfigurationOptions_presence is 1: + d = ProtocolConfigurationOptions(ieiPCO=0x27) + packet = packet / d + return packet + + +def requestPdpContextActivation(AccessPointName_presence=0): + """REQUEST PDP CONTEXT ACTIVATION Section 9.5.4""" + a = TpPd(pd=0x8) + b = MessageType(mesType=0x44) # 01000100 + c = PacketDataProtocolAddress() + packet = a / b / c + if AccessPointName_presence is 1: + d = AccessPointName(ieiAPN=0x28) + packet = packet / d + return packet + + +def requestPdpContextActivationReject(): + """REQUEST PDP CONTEXT ACTIVATION REJECT Section 9.5.5""" + a = TpPd(pd=0x8) + b = MessageType(mesType=0x45) # 01000101 + c = SmCause() + packet = a / b / c + return packet + + +def modifyPdpContextRequest(): + """MODIFY PDP CONTEXT REQUEST Section 9.5.6""" + a = TpPd(pd=0x8) + b = MessageType(mesType=0x48) # 01001000 + c = RadioPriorityAndSpareHalfOctets() + d = LlcServiceAccessPointIdentifier() + e = QualityOfService() + packet = a / b / c / d / e + return packet + + +def modifyPdpContextAccept(): + """MODIFY PDP CONTEXT ACCEPT Section 9.5.7""" + a = TpPd(pd=0x8) + b = MessageType(mesType=0x45) # 01000101 + packet = a / b + return packet + + +def deactivatePdpContextRequest(): + """DEACTIVATE PDP CONTEXT REQUEST Section 9.5.8""" + a = TpPd(pd=0x8) + b = MessageType(mesType=0x46) # 01000110 + c = SmCause() + packet = a / b / c + return packet + + +def deactivatePdpContextAccept(): + """DEACTIVATE PDP CONTEXT ACCEPT Section 9.5.9""" + a = TpPd(pd=0x8) + b = MessageType(mesType=0x47) # 01000111 + packet = a / b + return packet + + +def activateAaPdpContextRequest(AccessPointName_presence=0, + ProtocolConfigurationOptions_presence=0, + GprsTimer_presence=0): + """ACTIVATE AA PDP CONTEXT REQUEST Section 9.5.10""" + a = TpPd(pd=0x8) + b = MessageType(mesType=0x50) # 01010000 + c = NetworkServiceAccessPointIdentifier() + d = LlcServiceAccessPointIdentifier() + e = QualityOfService() + f = PacketDataProtocolAddress() + packet = a / b / c / d / e / f + if AccessPointName_presence is 1: + g = AccessPointName(ieiAPN=0x28) + packet = packet / g + if ProtocolConfigurationOptions_presence is 1: + h = ProtocolConfigurationOptions(ieiPCO=0x27) + packet = packet / h + if GprsTimer_presence is 1: + i = GprsTimer(ieiGT=0x29) + packet = packet / i + return packet + + +def activateAaPdpContextAccept(ProtocolConfigurationOptions_presence=0, + GprsTimer_presence=0): + """ACTIVATE AA PDP CONTEXT ACCEPT Section 9.5.11""" + a = TpPd(pd=0x8) + b = MessageType(mesType=0x51) # 01010001 + c = LlcServiceAccessPointIdentifier() + d = QualityOfService() + e = MobileId() + f = PacketDataProtocolAddress() + g = RadioPriorityAndSpareHalfOctets() + packet = a / b / c / d / e / f / g + if ProtocolConfigurationOptions_presence is 1: + i = ProtocolConfigurationOptions(ieiPCO=0x27) + packet = packet / i + if GprsTimer_presence is 1: + j = GprsTimer(ieiGT=0x29) + packet = packet / j + return packet + + +def activateAaPdpContextReject(ProtocolConfigurationOptions_presence=0): + """ACTIVATE AA PDP CONTEXT REJECT Section 9.5.12""" + a = TpPd(pd=0x8) + b = MessageType(mesType=0x52) # 01010010 + c = SmCause() + packet = a / b / c + if ProtocolConfigurationOptions_presence is 1: + d = ProtocolConfigurationOptions(ieiPCO=0x27) + packet = packet / d + return packet + + +def deactivateAaPdpContextRequest(): + """DEACTIVATE AA PDP CONTEXT REQUEST Section 9.5.13""" + a = TpPd(pd=0x8) + b = MessageType(mesType=0x53) # 01010011 + c = AaDeactivationCauseAndSpareHalfOctets() + packet = a / b / c + return packet + + +def deactivateAaPdpContextAccept(): + """DEACTIVATE AA PDP CONTEXT ACCEPT Section 9.5.14""" + a = TpPd(pd=0x8) + b = MessageType(mesType=0x54) # 01010100 + packet = a / b + return packet + + +def smStatus(): + """SM STATUS Section 9.5.15""" + a = TpPd(pd=0x8) + b = MessageType(mesType=0x55) # 01010101 + c = SmCause() + packet = a / b / c + return packet + + +# ============================================# +# Information Elements contents (Section 10) # +# =========================================== # + +#### +# This section contains the elements we need to build the messages +#### + +# +# Common information elements: +# +class CellIdentityHdr(Packet): + """ Cell identity Section 10.5.1.1 """ + name = "Cell Identity" + fields_desc = [ + BitField("eightBitCI", None, 1), + XBitField("ieiCI", None, 7), + ByteField("ciValue1", 0x0), + ByteField("ciValue2", 0x0) + ] + + +class CiphKeySeqNrHdr(Packet): + """ Ciphering Key Sequence Number Section 10.5.1.2 """ + name = "Cipher Key Sequence Number" + fields_desc = [ + XBitField("ieiCKSN", None, 4), + BitField("spare", 0x0, 1), + BitField("keySeq", 0x0, 3) + ] + + +# Fix 1/2 len problem +class CiphKeySeqNrAndSpareHalfOctets(Packet): + name = "Cipher Key Sequence Number and Spare Half Octets" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("keySeq", 0x0, 3), + BitField("spareHalfOctets", 0x0, 4) + ] + + +# Fix 1/2 len problem +class CiphKeySeqNrAndMacModeAndChannelCodingRequest(Packet): + name = "Cipher Key Sequence Number and Mac Mode And Channel Coding Request" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("keySeq", 0x0, 3), + BitField("macMode", 0x0, 2), + BitField("cs", 0x0, 2) + ] + + +class LocalAreaIdHdr(Packet): + """ Local Area Identification Section 10.5.1.3 """ + name = "Location Area Identification" + fields_desc = [ + BitField("eightBitLAI", None, 1), + XBitField("ieiLAI", None, 7), + BitField("mccDigit2", 0x0, 4), + BitField("mccDigit1", 0x0, 4), + BitField("mncDigit3", 0x0, 4), + BitField("mccDigit3", 0x0, 4), + BitField("mncDigit2", 0x0, 4), + BitField("mncDigit1", 0x0, 4), + ByteField("lac1", 0x0), + ByteField("lac2", 0x0) + ] +# +# The Mobile Identity is a type 4 information element with a minimum +# length of 3 octet and 11 octets length maximal. +# + + +# len 3 - 11 +class MobileIdHdr(Packet): + """ Mobile Identity Section 10.5.1.4 """ + name = "Mobile Identity" + fields_desc = [ + BitField("eightBitMI", 0x0, 1), + XBitField("ieiMI", 0x0, 7), + + XByteField("lengthMI", None), + + BitField("idDigit1", 0x0, 4), + BitField("oddEven", 0x0, 1), + BitField("typeOfId", 0x0, 3), + + BitField("idDigit2_1", None, 4), # optional + BitField("idDigit2", None, 4), + + BitField("idDigit3_1", None, 4), + BitField("idDigit3", None, 4), + + BitField("idDigit4_1", None, 4), + BitField("idDigit4", None, 4), + + BitField("idDigit5_1", None, 4), + BitField("idDigit5", None, 4), + + BitField("idDigit6_1", None, 4), + BitField("idDigit6", None, 4), + BitField("idDigit7_1", None, 4), + BitField("idDigit7", None, 4), + BitField("idDigit8_1", None, 4), + BitField("idDigit8", None, 4), + BitField("idDigit9_1", None, 4), + BitField("idDigit9", None, 4), + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i, None)) # this list holds the values of +# the variables, the INTERESSTING value! + res = adapt(3, 11, a, self.fields_desc) + if self.lengthMI is None: + p = p[:1] + struct.pack(">B", res[1]) + p[2:] + if res[0] is not 0: + p = p[:-res[0]] + print(repr(p)) + return p + pay + + +class MobileStationClassmark1Hdr(Packet): + """ Mobile Station Classmark 1 Section 10.5.1.5 """ + name = "Mobile Station Classmark 1" + fields_desc = [ + BitField("eightBitiMSC1", None, 1), + XBitField("ieiMSC1", None, 7), + BitField("spare", 0x0, 1), + BitField("revisionLvl", 0x0, 2), + BitField("esInd", 0x0, 1), + BitField("a51", 0x0, 1), + BitField("rfPowerCap", 0x0, 3) + ] + + +class MobileStationClassmark2Hdr(Packet): + """ Mobile Station Classmark 2 Section 10.5.1.6 """ + name = "Mobile Station Classmark 2" + fields_desc = [ + BitField("eightBitMSC2", None, 1), + XBitField("ieiMSC2", None, 7), + XByteField("lengthMSC2", 0x3), + BitField("spare", 0x0, 1), + BitField("revisionLvl", 0x0, 2), + BitField("esInd", 0x0, 1), + BitField("a51", 0x0, 1), + BitField("rfPowerCap", 0x0, 3), + BitField("spare1", 0x0, 1), + BitField("psCap", 0x0, 1), + BitField("ssScreenInd", 0x0, 2), + BitField("smCaPabi", 0x0, 1), + BitField("vbs", 0x0, 1), + BitField("vgcs", 0x0, 1), + BitField("fc", 0x0, 1), + BitField("cm3", 0x0, 1), + BitField("spare2", 0x0, 1), + BitField("lcsvaCap", 0x0, 1), + BitField("spare3", 0x0, 1), + BitField("soLsa", 0x0, 1), + BitField("cmsp", 0x0, 1), + BitField("a53", 0x0, 1), + BitField("a52", 0x0, 1) + ] + + +# len max 14 +class MobileStationClassmark3(Packet): + """ Mobile Station Classmark 3 Section 10.5.1.7 """ + name = "Mobile Station Classmark 3" + fields_desc = [ + # FIXME + ByteField("ieiMSC3", 0x0), + ByteField("byte2", 0x0), + ByteField("byte3", 0x0), + ByteField("byte4", 0x0), + ByteField("byte5", 0x0), + ByteField("byte6", 0x0), + ByteField("byte7", 0x0), + ByteField("byte8", 0x0), + ByteField("byte9", 0x0), + ByteField("byte10", 0x0), + ByteField("byte11", 0x0), + ByteField("byte12", 0x0), + ByteField("byte13", 0x0), + ByteField("byte14", 0x0) + ] + + +class SpareHalfOctets(Packet): + """ Spare Half Octet Section 10.5.1.8 """ + name = "Spare Half Octet" + fields_desc = [ + BitField("filler", None, 4), + BitField("spareHalfOctets", 0x0, 4) + ] + + +class DescriptiveGroupOrBroadcastCallReferenceHdr(Packet): + """ Descriptive group or broadcast call reference Section 10.5.1.9 """ + name = "Descriptive Group or Broadcast Call Reference" + fields_desc = [ + BitField("eightBitDGOBCR", None, 1), + XBitField("ieiDGOBCR", None, 7), + BitField("binCallRef", 0x0, 27), + BitField("sf", 0x0, 1), + BitField("fa", 0x0, 1), + BitField("callPrio", 0x0, 3), + BitField("cipherInfo", 0x0, 4), + BitField("spare1", 0x0, 1), + BitField("spare2", 0x0, 1), + BitField("spare3", 0x0, 1), + BitField("spare4", 0x0, 1) + ] + + +class GroupCipherKeyNumber(Packet): + """ Group Cipher Key Number reference Section 10.5.1.10 """ + name = "Group Cipher Key Number" + fields_desc = [ + XBitField("ieiGCKN", None, 4), + BitField("groupCipher", 0x0, 4) + ] + + +class PdAndSapiHdr(Packet): + """ PD and SAPI $(CCBS)$ Section 10.5.1.10a """ + name = "PD and SAPI $(CCBS)$" + fields_desc = [ + BitField("eightBitPAS", None, 1), + XBitField("ieiPAS", None, 7), + BitField("spare", 0x0, 1), + BitField("spare1", 0x0, 1), + BitField("sapi", 0x0, 2), + BitField("pd", 0x0, 4) + ] + + +class PriorityLevelHdr(Packet): + """ Priority Level Section 10.5.1.11 """ + name = "Priority Level" + fields_desc = [ + XBitField("ieiPL", None, 4), + BitField("spare", 0x0, 1), + BitField("callPrio", 0x0, 3) + ] + +# +# Radio Resource management information elements +# + + +# len 6 to max for L3 message (251) +class BaRangeHdr(Packet): + """ BA Range Section 10.5.2.1a """ + name = "BA Range" + fields_desc = [ + BitField("eightBitBR", None, 1), + XBitField("ieiBR", None, 7), + + XByteField("lengthBR", None), +#error: byte format requires -128 <= number <= 127 + ByteField("nrOfRanges", 0x0), +# # rX = range X +# # L o = Lower H i = higher +# # H p = high Part Lp = low Part + ByteField("r1LoHp", 0x0), + + BitField("r1LoLp", 0x0, 3), + BitField("r1HiHp", 0x0, 5), + + BitField("r1HiLp", 0x0, 4), + BitField("r2LoHp", 0x0, 4), + # optional + BitField("r2LoLp", None, 5), + BitField("r2HiHp", None, 3), + + ByteField("r2HiLp", None), + ByteField("r3LoHp", None), + + BitField("r3LoLp", None, 5), + BitField("r3HiHp", None, 3), + + ByteField("r3HiLp", None), + ByteField("r4LoHp", None), + + BitField("r4LoLp", None, 5), + BitField("r4HiHp", None, 3), + ByteField("r4HiLp", None), + ByteField("r5LoHp", None), + + BitField("r5LoLp", None, 5), + BitField("r5HiHp", None, 3), + ByteField("r5HiLp", None), + ByteField("r6LoHp", None), + + BitField("r6LoLp", None, 5), + BitField("r6HiHp", None, 3), + ByteField("r6HiLp", None), + ByteField("r7LoHp", None), + + BitField("r7LoLp", None, 5), + BitField("r7HiHp", None, 3), + ByteField("r7HiLp", None), + ByteField("r8LoHp", None), + + BitField("r8LoLp", None, 5), + BitField("r8HiHp", None, 3), + ByteField("r8HiLp", None), + ByteField("r9LoHp", None), + + BitField("r9LoLp", None, 5), + BitField("r9HiHp", None, 3), + ByteField("r9HiLp", None), + ByteField("r10LoHp", None), + + BitField("r10LoLp", None, 5), + BitField("r10HiHp", None, 3), + ByteField("r10HiLp", None), + ByteField("r11LoHp", None), + + BitField("r11LoLp", None, 5), + BitField("r11HiHp", None, 3), + ByteField("r11HiLp", None), + ByteField("r12LoHp", None), + + BitField("r12LoLp", None, 5), + BitField("r12HiHp", None, 3), + ByteField("r12HiLp", None), + ByteField("r13LoHp", None), + + BitField("r13LoLp", None, 5), + BitField("r13HiHp", None, 3), + ByteField("r13HiLp", None), + ByteField("r14LoHp", None), + + BitField("r14LoLp", None, 5), + BitField("r14HiHp", None, 3), + ByteField("r14HiLp", None), + ByteField("r15LoHp", None), + + BitField("r15LoLp", None, 5), + BitField("r15HiHp", None, 3), + ByteField("r15HiLp", None), + ByteField("r16LoHp", None), + + BitField("r16LoLp", None, 5), + BitField("r16HiHp", None, 3), + ByteField("r16HiLp", None), + ByteField("r17LoHp", None), + + BitField("r17LoLp", None, 5), + BitField("r17HiHp", None, 3), + ByteField("r17HiLp", None), + ByteField("r18LoHp", None), + + BitField("r18LoLp", None, 5), + BitField("r18HiHp", None, 3), + ByteField("r18HiLp", None), + ByteField("r19LoHp", None), + + BitField("r19LoLp", None, 5), + BitField("r19HiHp", None, 3), + ByteField("r19HiLp", None), + ByteField("r20LoHp", None), + + BitField("r20LoLp", None, 5), + BitField("r20HiHp", None, 3), + ByteField("r20HiLp", None), + ByteField("r21LoHp", None), + + BitField("r21LoLp", None, 5), + BitField("r21HiHp", None, 3), + ByteField("r21HiLp", None), + ByteField("r22LoHp", None), + + BitField("r22LoLp", None, 5), + BitField("r22HiHp", None, 3), + ByteField("r22HiLp", None), + ByteField("r23LoHp", None), + + BitField("r23LoLp", None, 5), + BitField("r23HiHp", None, 3), + ByteField("r23HiLp", None), + ByteField("r24LoHp", None), + + BitField("r24LoLp", None, 5), + BitField("r24HiHp", None, 3), + ByteField("r24HiLp", None), + ByteField("r25LoHp", None), + + BitField("r25LoLp", None, 5), + BitField("r25HiHp", None, 3), + ByteField("r25HiLp", None), + ByteField("r26LoHp", None), + + BitField("r26LoLp", None, 5), + BitField("r26HiHp", None, 3), + ByteField("r26HiLp", None), + ByteField("r27LoHp", None), + + BitField("r27LoLp", None, 5), + BitField("r27HiHp", None, 3), + ByteField("r27HiLp", None), + ByteField("r28LoHp", None), + + BitField("r28LoLp", None, 5), + BitField("r28HiHp", None, 3), + ByteField("r28HiLp", None), + ByteField("r29LoHp", None), + + BitField("r29LoLp", None, 5), + BitField("r29HiHp", None, 3), + ByteField("r29HiLp", None), + ByteField("r30LoHp", None), + + BitField("r30LoLp", None, 5), + BitField("r30HiHp", None, 3), + ByteField("r30HiLp", None), + ByteField("r31LoHp", None), + + BitField("r31LoLp", None, 5), + BitField("r31HiHp", None, 3), + ByteField("r31HiLp", None), + ByteField("r32LoHp", None), + + BitField("r32LoLp", None, 5), + BitField("r32HiHp", None, 3), + ByteField("r32HiLp", None), + ByteField("r33LoHp", None), + + BitField("r33LoLp", None, 5), + BitField("r33HiHp", None, 3), + ByteField("r33HiLp", None), + ByteField("r34LoHp", None), + + BitField("r34LoLp", None, 5), + BitField("r34HiHp", None, 3), + ByteField("r34HiLp", None), + ByteField("r35LoHp", None), + + BitField("r35LoLp", None, 5), + BitField("r35HiHp", None, 3), + ByteField("r35HiLp", None), + ByteField("r36LoHp", None), + + BitField("r36LoLp", None, 5), + BitField("r36HiHp", None, 3), + ByteField("r36HiLp", None), + ByteField("r37LoHp", None), + + BitField("r37LoLp", None, 5), + BitField("r37HiHp", None, 3), + ByteField("r37HiLp", None), + ByteField("r38LoHp", None), + + BitField("r38LoLp", None, 5), + BitField("r38HiHp", None, 3), + ByteField("r38HiLp", None), + ByteField("r39LoHp", None), + + BitField("r39LoLp", None, 5), + BitField("r39HiHp", None, 3), + ByteField("r39HiLp", None), + ByteField("r40LoHp", None), + + BitField("r40LoLp", None, 5), + BitField("r40HiHp", None, 3), + ByteField("r40HiLp", None), + ByteField("r41LoHp", None), + + BitField("r41LoLp", None, 5), + BitField("r41HiHp", None, 3), + ByteField("r41HiLp", None), + ByteField("r42LoHp", None), + + BitField("r42LoLp", None, 5), + BitField("r42HiHp", None, 3), + ByteField("r42HiLp", None), + ByteField("r43LoHp", None), + + BitField("r43LoLp", None, 5), + BitField("r43HiHp", None, 3), + ByteField("r43HiLp", None), + ByteField("r44LoHp", None), + + BitField("r44LoLp", None, 5), + BitField("r44HiHp", None, 3), + ByteField("r44HiLp", None), + ByteField("r45LoHp", None), + + BitField("r45LoLp", None, 5), + BitField("r45HiHp", None, 3), + ByteField("r45HiLp", None), + ByteField("r46LoHp", None), + + BitField("r46LoLp", None, 5), + BitField("r46HiHp", None, 3), + ByteField("r46HiLp", None), + ByteField("r47LoHp", None), + + BitField("r47LoLp", None, 5), + BitField("r47HiHp", None, 3), + ByteField("r47HiLp", None), + ByteField("r48LoHp", None), + + BitField("r48LoLp", None, 5), + BitField("r48HiHp", None, 3), + ByteField("r48HiLp", None), + ByteField("r49LoHp", None), + + BitField("r49LoLp", None, 5), + BitField("r49HiHp", None, 3), + ByteField("r49HiLp", None), + ByteField("r50LoHp", None), + + BitField("r50LoLp", None, 5), + BitField("r50HiHp", None, 3), + ByteField("r50HiLp", None), + ByteField("r51LoHp", None), + + BitField("r51LoLp", None, 5), + BitField("r51HiHp", None, 3), + ByteField("r51HiLp", None), + ByteField("r52LoHp", None), + + BitField("r52LoLp", None, 5), + BitField("r52HiHp", None, 3), + ByteField("r52HiLp", None), + ByteField("r53LoHp", None), + + BitField("r53LoLp", None, 5), + BitField("r53HiHp", None, 3), + ByteField("r53HiLp", None), + ByteField("r54LoHp", None), + + BitField("r54LoLp", None, 5), + BitField("r54HiHp", None, 3), + ByteField("r54HiLp", None), + ByteField("r55LoHp", None), + + BitField("r55LoLp", None, 5), + BitField("r55HiHp", None, 3), + ByteField("r55HiLp", None), + ByteField("r56LoHp", None), + + BitField("r56LoLp", None, 5), + BitField("r56HiHp", None, 3), + ByteField("r56HiLp", None), + ByteField("r57LoHp", None), + + BitField("r57LoLp", None, 5), + BitField("r57HiHp", None, 3), + ByteField("r57HiLp", None), + ByteField("r58LoHp", None), + + BitField("r58LoLp", None, 5), + BitField("r58HiHp", None, 3), + ByteField("r58HiLp", None), + ByteField("r59LoHp", None), + + BitField("r59LoLp", None, 5), + BitField("r59HiHp", None, 3), + ByteField("r59HiLp", None), + ByteField("r60LoHp", None), + + BitField("r60LoLp", None, 5), + BitField("r60HiHp", None, 3), + ByteField("r60HiLp", None), + ByteField("r61LoHp", None), + + BitField("r61LoLp", None, 5), + BitField("r61HiHp", None, 3), + ByteField("r61HiLp", None), + ByteField("r62LoHp", None), + + BitField("r62LoLp", None, 5), + BitField("r62HiHp", None, 3), + ByteField("r62HiLp", None), + ByteField("r63LoHp", None), + + BitField("r63LoLp", None, 5), + BitField("r63HiHp", None, 3), + ByteField("r63HiLp", None), + ByteField("r64LoHp", None), + + BitField("r64LoLp", None, 5), + BitField("r64HiHp", None, 3), + ByteField("r64HiLp", None), + ByteField("r65LoHp", None), + + BitField("r65LoLp", None, 5), + BitField("r65HiHp", None, 3), + ByteField("r65HiLp", None), + ByteField("r66LoHp", None), + + BitField("r66LoLp", None, 5), + BitField("r66HiHp", None, 3), + ByteField("r66HiLp", None), + ByteField("r67LoHp", None), + + BitField("r67LoLp", None, 5), + BitField("r67HiHp", None, 3), + ByteField("r67HiLp", None), + ByteField("r68LoHp", None), + + BitField("r68LoLp", None, 5), + BitField("r68HiHp", None, 3), + ByteField("r68HiLp", None), + ByteField("r69LoHp", None), + + BitField("r69LoLp", None, 5), + BitField("r69HiHp", None, 3), + ByteField("r69HiLp", None), + ByteField("r70LoHp", None), + + BitField("r70LoLp", None, 5), + BitField("r70HiHp", None, 3), + ByteField("r70HiLp", None), + ByteField("r71LoHp", None), + + BitField("r71LoLp", None, 5), + BitField("r71HiHp", None, 3), + ByteField("r71HiLp", None), + ByteField("r72LoHp", None), + + BitField("r72LoLp", None, 5), + BitField("r72HiHp", None, 3), + ByteField("r72HiLp", None), + ByteField("r73LoHp", None), + + BitField("r73LoLp", None, 5), + BitField("r73HiHp", None, 3), + ByteField("r73HiLp", None), + ByteField("r74LoHp", None), + + BitField("r74LoLp", None, 5), + BitField("r74HiHp", None, 3), + ByteField("r74HiLp", None), + ByteField("r75LoHp", None), + + BitField("r75LoLp", None, 5), + BitField("r75HiHp", None, 3), + ByteField("r75HiLp", None), + ByteField("r76LoHp", None), + + BitField("r76LoLp", None, 5), + BitField("r76HiHp", None, 3), + ByteField("r76HiLp", None), + ByteField("r77LoHp", None), + + BitField("r77LoLp", None, 5), + BitField("r77HiHp", None, 3), + ByteField("r77HiLp", None), + ByteField("r78LoHp", None), + + BitField("r78LoLp", None, 5), + BitField("r78HiHp", None, 3), + ByteField("r78HiLp", None), + ByteField("r79LoHp", None), + + BitField("r79LoLp", None, 5), + BitField("r79HiHp", None, 3), + ByteField("r79HiLp", None), + ByteField("r80LoHp", None), + + BitField("r80LoLp", None, 5), + BitField("r80HiHp", None, 3), + ByteField("r80HiLp", None), + ByteField("r81LoHp", None), + + BitField("r81LoLp", None, 5), + BitField("r81HiHp", None, 3), + ByteField("r81HiLp", None), + ByteField("r82LoHp", None), + + BitField("r82LoLp", None, 5), + BitField("r82HiHp", None, 3), + ByteField("r82HiLp", None), + ByteField("r83LoHp", None), + + BitField("r83LoLp", None, 5), + BitField("r83HiHp", None, 3), + ByteField("r83HiLp", None), + ByteField("r84LoHp", None), + + BitField("r84LoLp", None, 5), + BitField("r84HiHp", None, 3), + ByteField("r84HiLp", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + print("i is %s" % (i,)) + aList.append(self.fields_desc[i].name) + print("aList %s" % (len(aList))) + print("self.fields_desc %s" % (len(self.fields_desc))) + for i in aList: + a.append(getattr(self, i)) + res = adapt(6, 251, a, self.fields_desc) + if self.lengthBR is None: + p = p[:1] + struct.pack(">B", res[1]) + p[2:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +# len 3 to max for L3 message (251) +class BaListPrefHdr(Packet): + """ BA List Pref Section 10.5.2.1c """ + name = "BA List Pref" + fields_desc = [ + # FIXME dynamic + BitField("eightBitBLP", None, 1), + XBitField("ieiBLP", None, 7), + + XByteField("lengthBLP", None), + + BitField("fixBit", 0x0, 1), + BitField("rangeLower", 0x0, 10), + BitField("fixBit2", 0x0, 1), + BitField("rangeUpper", 0x0, 10), + BitField("baFreq", 0x0, 10), + BitField("sparePad", 0x0, 8) + ] + + +# len 17 || Have a look at the specs for the field format +# Bit map 0 format +# Range 1024 format +# Range 512 format +# Range 256 format +# Range 128 format +# Variable bit map format +class CellChannelDescriptionHdr(Packet): + """ Cell Channel Description Section 10.5.2.1b """ + name = "Cell Channel Description " + fields_desc = [ + BitField("eightBitCCD", None, 1), + XBitField("ieiCCD", None, 7), + BitField("bit128", 0x0, 1), + BitField("bit127", 0x0, 1), + BitField("spare1", 0x0, 1), + BitField("spare2", 0x0, 1), + BitField("bit124", 0x0, 1), + BitField("bit123", 0x0, 1), + BitField("bit122", 0x0, 1), + BitField("bit121", 0x0, 1), + ByteField("bit120", 0x0), + ByteField("bit112", 0x0), + ByteField("bit104", 0x0), + ByteField("bit96", 0x0), + ByteField("bit88", 0x0), + ByteField("bit80", 0x0), + ByteField("bit72", 0x0), + ByteField("bit64", 0x0), + ByteField("bit56", 0x0), + ByteField("bit48", 0x0), + ByteField("bit40", 0x0), + ByteField("bit32", 0x0), + ByteField("bit24", 0x0), + ByteField("bit16", 0x0), + ByteField("bit8", 0x0) + ] + + +class CellDescriptionHdr(Packet): + """ Cell Description Section 10.5.2.2 """ + name = "Cell Description" + fields_desc = [ + BitField("eightBitCD", None, 1), + XBitField("ieiCD", None, 7), + BitField("bcchHigh", 0x0, 2), + BitField("ncc", 0x0, 3), + BitField("bcc", 0x0, 3), + ByteField("bcchLow", 0x0) + ] + + +class CellOptionsBCCHHdr(Packet): + """ Cell Options (BCCH) Section 10.5.2.3 """ + name = "Cell Options (BCCH)" + fields_desc = [ + BitField("eightBitCOB", None, 1), + XBitField("ieiCOB", None, 7), + BitField("spare", 0x0, 1), + BitField("pwrc", 0x0, 1), + BitField("dtx", 0x0, 2), + BitField("rLinkTout", 0x0, 4) + ] + + +class CellOptionsSACCHHdr(Packet): + """ Cell Options (SACCH) Section 10.5.2.3a """ + name = "Cell Options (SACCH)" + fields_desc = [ + BitField("eightBitCOS", None, 1), + XBitField("ieiCOS", None, 7), + BitField("dtx", 0x0, 1), + BitField("pwrc", 0x0, 1), + BitField("dtx", 0x0, 1), + BitField("rLinkTout", 0x0, 4) + ] + + +class CellSelectionParametersHdr(Packet): + """ Cell Selection Parameters Section 10.5.2.4 """ + name = "Cell Selection Parameters" + fields_desc = [ + BitField("eightBitCSP", None, 1), + XBitField("ieiCSP", None, 7), + BitField("cellReselect", 0x0, 3), + BitField("msTxPwrMax", 0x0, 5), + BitField("acs", None, 1), + BitField("neci", None, 1), + BitField("rxlenAccMin", None, 6) + ] + + +class MacModeAndChannelCodingRequestHdr(Packet): + """ MAC Mode and Channel Coding Requested Section 10.5.2.4a """ + name = "MAC Mode and Channel Coding Requested" + fields_desc = [ + XBitField("ieiMMACCR", None, 4), + BitField("macMode", 0x0, 2), + BitField("cs", 0x0, 2) + ] + + +class ChannelDescriptionHdr(Packet): + """ Channel Description Section 10.5.2.5 """ + name = "Channel Description" + fields_desc = [ + BitField("eightBitCD", None, 1), + XBitField("ieiCD", None, 7), + + BitField("channelTyp", 0x0, 5), + BitField("tn", 0x0, 3), + + BitField("tsc", 0x0, 3), + BitField("h", 0x1, 1), + # if h=1 maybe we find a better solution here... + BitField("maioHi", 0x0, 4), + + BitField("maioLo", 0x0, 2), + BitField("hsn", 0x0, 6) + #BitField("spare", 0x0, 2), + #BitField("arfcnHigh", 0x0, 2), + #ByteField("arfcnLow", 0x0) + ] + + +class ChannelDescription2Hdr(Packet): + """ Channel Description 2 Section 10.5.2.5a """ + name = "Channel Description 2" + fields_desc = [ + BitField("eightBitCD2", None, 1), + XBitField("ieiCD2", None, 7), + BitField("channelTyp", 0x0, 5), + BitField("tn", 0x0, 3), + BitField("tsc", 0x0, 3), + BitField("h", 0x0, 1), + # if h=1 + # BitField("maioHi", 0x0, 4), + # BitField("maioLo", 0x0, 2), + # BitField("hsn", 0x0, 6) + BitField("spare", 0x0, 2), + BitField("arfcnHigh", 0x0, 2), + ByteField("arfcnLow", 0x0) + ] + + +class ChannelModeHdr(Packet): + """ Channel Mode Section 10.5.2.6 """ + name = "Channel Mode" + fields_desc = [ + BitField("eightBitCM", None, 1), + XBitField("ieiCM", None, 7), + ByteField("mode", 0x0) + ] + + +class ChannelMode2Hdr(Packet): + """ Channel Mode 2 Section 10.5.2.7 """ + name = "Channel Mode 2" + fields_desc = [ + BitField("eightBitCM2", None, 1), + XBitField("ieiCM2", None, 7), + ByteField("mode", 0x0) + ] + + +class ChannelNeededHdr(Packet): + """ Channel Needed Section 10.5.2.8 """ + name = "Channel Needed" + fields_desc = [ + XBitField("ieiCN", None, 4), + BitField("channel2", 0x0, 2), + BitField("channel1", 0x0, 2), + ] + + +class ChannelRequestDescriptionHdr(Packet): + """Channel Request Description Section 10.5.2.8a """ + name = "Channel Request Description" + fields_desc = [ + BitField("eightBitCRD", None, 1), + XBitField("ieiCRD", None, 7), + BitField("mt", 0x0, 1), + ConditionalField(BitField("spare", 0x0, 39), + lambda pkt: pkt.mt == 0), + ConditionalField(BitField("spare", 0x0, 3), + lambda pkt: pkt.mt == 1), + ConditionalField(BitField("priority", 0x0, 2), + lambda pkt: pkt.mt == 1), + ConditionalField(BitField("rlcMode", 0x0, 1), + lambda pkt: pkt.mt == 1), + ConditionalField(BitField("llcFrame", 0x1, 1), + lambda pkt: pkt.mt == 1), + ConditionalField(ByteField("reqBandMsb", 0x0), + lambda pkt: pkt.mt == 1), + ConditionalField(ByteField("reqBandLsb", 0x0), + lambda pkt: pkt.mt == 1), + ConditionalField(ByteField("rlcMsb", 0x0), + lambda pkt: pkt.mt == 1), + ConditionalField(ByteField("rlcLsb", 0x0), + lambda pkt: pkt.mt == 1) + ] + + +class CipherModeSettingHdr(Packet): + """Cipher Mode Setting Section 10.5.2.9 """ + name = "Cipher Mode Setting" + fields_desc = [ + XBitField("ieiCMS", None, 4), + BitField("algoId", 0x0, 3), + BitField("sc", 0x0, 1), + ] + + +class CipherResponseHdr(Packet): + """Cipher Response Section 10.5.2.10 """ + name = "Cipher Response" + fields_desc = [ + XBitField("ieiCR", None, 4), + BitField("spare", 0x0, 3), + BitField("cr", 0x0, 1), + ] + + +# This packet fixes the problem with the 1/2 Byte length. Concatenation +# of cipherModeSetting and cipherResponse +class CipherModeSettingAndcipherResponse(Packet): + name = "Cipher Mode Setting And Cipher Response" + fields_desc = [ + BitField("algoId", 0x0, 3), + BitField("sc", 0x0, 1), + BitField("spare", 0x0, 3), + BitField("cr", 0x0, 1) + ] + + +class ControlChannelDescriptionHdr(Packet): + """Control Channel Description Section 10.5.2.11 """ + name = "Control Channel Description" + fields_desc = [ + BitField("eightBitCCD", None, 1), + XBitField("ieiCCD", None, 7), + + BitField("spare", 0x0, 1), + BitField("att", 0x0, 1), + BitField("bsAgBlksRes", 0x0, 3), + BitField("ccchConf", 0x0, 3), + + BitField("spare", 0x0, 1), + BitField("spare1", 0x0, 1), + BitField("spare2", 0x0, 1), + BitField("spare3", 0x0, 1), + BitField("spare4", 0x0, 1), + BitField("bsPaMfrms", 0x0, 3), + + ByteField("t3212", 0x0) + ] + + +class FrequencyChannelSequenceHdr(Packet): + """Frequency Channel Sequence Section 10.5.2.12""" + name = "Frequency Channel Sequence" + fields_desc = [ + BitField("eightBitFCS", None, 1), + XBitField("ieiFCS", None, 7), + BitField("spare", 0x0, 1), + BitField("lowestArfcn", 0x0, 7), + BitField("skipArfcn01", 0x0, 4), + BitField("skipArfcn02", 0x0, 4), + BitField("skipArfcn03", 0x0, 4), + BitField("skipArfcn04", 0x0, 4), + BitField("skipArfcn05", 0x0, 4), + BitField("skipArfcn06", 0x0, 4), + BitField("skipArfcn07", 0x0, 4), + BitField("skipArfcn08", 0x0, 4), + BitField("skipArfcn09", 0x0, 4), + BitField("skipArfcn10", 0x0, 4), + BitField("skipArfcn11", 0x0, 4), + BitField("skipArfcn12", 0x0, 4), + BitField("skipArfcn13", 0x0, 4), + BitField("skipArfcn14", 0x0, 4), + BitField("skipArfcn15", 0x0, 4), + BitField("skipArfcn16", 0x0, 4) + ] + + +class FrequencyListHdr(Packet): + """Frequency List Section 10.5.2.13""" + name = "Frequency List" + # Problem: + # There are several formats for the Frequency List information + # element, distinguished by the "format indicator" subfield. + # Some formats are frequency bit maps, the others use a special encoding + # scheme. + fields_desc = [ + BitField("eightBitFL", None, 1), + XBitField("ieiFL", None, 7), + XByteField("lengthFL", None), + + BitField("formatID", 0x0, 2), + BitField("spare", 0x0, 2), + BitField("arfcn124", 0x0, 1), + BitField("arfcn123", 0x0, 1), + BitField("arfcn122", 0x0, 1), + BitField("arfcn121", 0x0, 1), + + ByteField("arfcn120", 0x0), + ByteField("arfcn112", 0x0), + ByteField("arfcn104", 0x0), + ByteField("arfcn96", 0x0), + ByteField("arfcn88", 0x0), + ByteField("arfcn80", 0x0), + ByteField("arfcn72", 0x0), + ByteField("arfcn64", 0x0), + ByteField("arfcn56", 0x0), + ByteField("arfcn48", 0x0), + ByteField("arfcn40", 0x0), + ByteField("arfcn32", 0x0), + ByteField("arfcn24", 0x0), + ByteField("arfcn16", 0x0), + ByteField("arfcn8", 0x0) + ] + + +class FrequencyShortListHdr(Packet): + """Frequency Short List Section 10.5.2.14""" + name = "Frequency Short List" +# len is 10 +#This element is encoded exactly as the Frequency List information element, +#except that it has a fixed length instead of a +#variable length and does not contain a length indicator and that it +#shall not be encoded in bitmap 0 format. + fields_desc = [ + ByteField("ieiFSL", 0x0), + ByteField("byte2", 0x0), + ByteField("byte3", 0x0), + ByteField("byte4", 0x0), + ByteField("byte5", 0x0), + ByteField("byte6", 0x0), + ByteField("byte7", 0x0), + ByteField("byte8", 0x0), + ByteField("byte9", 0x0), + ByteField("byte10", 0x0) + ] + + +class FrequencyShortListHdr2(Packet): + """Frequency Short List2 Section 10.5.2.14a""" + name = "Frequency Short List 2" + fields_desc = [ + ByteField("byte1", 0x0), + ByteField("byte2", 0x0), + ByteField("byte3", 0x0), + ByteField("byte4", 0x0), + ByteField("byte5", 0x0), + ByteField("byte6", 0x0), + ByteField("byte7", 0x0), + ByteField("byte8", 0x0) + ] + + +# len 4 to 13 +class GroupChannelDescriptionHdr(Packet): + """Group Channel Description Section 10.5.2.14b""" + name = "Group Channel Description" + fields_desc = [ + BitField("eightBitGCD", None, 1), + XBitField("ieiGCD", None, 7), + + XByteField("lengthGCD", None), + + BitField("channelType", 0x0, 5), + BitField("tn", 0x0, 3), + + BitField("tsc", 0x0, 3), + BitField("h", 0x0, 1), + # if h == 0 the packet looks the following way: + ConditionalField(BitField("spare", 0x0, 2), + lambda pkt: pkt. h == 0x0), + ConditionalField(BitField("arfcnHi", 0x0, 2), + lambda pkt: pkt. h == 0x0), + ConditionalField(ByteField("arfcnLo", None), + lambda pkt: pkt. h == 0x0), + # if h == 1 the packet looks the following way: + ConditionalField(BitField("maioHi", 0x0, 4), + lambda pkt: pkt. h == 0x1), + ConditionalField(BitField("maioLo", None, 2), + lambda pkt: pkt. h == 0x1), + ConditionalField(BitField("hsn", None, 6), + lambda pkt: pkt. h == 0x1), + # finished with conditional fields + ByteField("maC6", None), + ByteField("maC7", None), + ByteField("maC8", None), + ByteField("maC9", None), + ByteField("maC10", None), + ByteField("maC11", None), + ByteField("maC12", None), + ByteField("maC13", None), + ByteField("maC14", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(4, 13, a, self.fields_desc) + if self.lengthGCD is None: + p = p[:1] + struct.pack(">B", res[1]) + p[2:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +class GprsResumptionHdr(Packet): + """GPRS Resumption Section 10.5.2.14c""" + name = "GPRS Resumption" + fields_desc = [ + XBitField("ieiGR", None, 4), + BitField("spare", 0x0, 3), + BitField("ack", 0x0, 1) + ] + + +class HandoverReferenceHdr(Packet): + """Handover Reference Section 10.5.2.15""" + name = "Handover Reference" + fields_desc = [ + BitField("eightBitHR", None, 1), + XBitField("ieiHR", None, 7), + ByteField("handoverRef", 0x0) + ] + + +# len 1-12 +class IaRestOctets(Packet): + """IA Rest Octets Section 10.5.2.16""" + name = "IA Rest Octets" + fields_desc = [ + ByteField("ieiIRO", 0x0), + # FIXME brainfuck packet + XByteField("lengthIRO", None), + ByteField("byte2", None), + ByteField("byte3", None), + ByteField("byte4", None), + ByteField("byte5", None), + ByteField("byte6", None), + ByteField("byte7", None), + ByteField("byte8", None), + ByteField("byte9", None), + ByteField("byte10", None), + ByteField("byte11", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(1, 12, a, self.fields_desc) + if self.lengthIRO is None: + if res[1] < 0: # FIXME better fix + res[1] = 0 + p = p[:1] + struct.pack(">B", res[1]) + p[2:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +class IraRestOctetsHdr(Packet): + """IAR Rest Octets Section 10.5.2.17""" + name = "IAR Rest Octets" + fields_desc = [ + BitField("eightBitIRO", None, 1), + XBitField("ieiIRO", None, 7), + BitField("spare01", 0x0, 1), + BitField("spare02", 0x0, 1), + BitField("spare03", 0x1, 1), + BitField("spare04", 0x0, 1), + BitField("spare05", 0x1, 1), + BitField("spare06", 0x0, 1), + BitField("spare07", 0x1, 1), + BitField("spare08", 0x1, 1), + BitField("spare09", 0x0, 1), + BitField("spare10", 0x0, 1), + BitField("spare11", 0x1, 1), + BitField("spare12", 0x0, 1), + BitField("spare13", 0x1, 1), + BitField("spare14", 0x0, 1), + BitField("spare15", 0x1, 1), + BitField("spare16", 0x1, 1), + BitField("spare17", 0x0, 1), + BitField("spare18", 0x0, 1), + BitField("spare19", 0x1, 1), + BitField("spare20", 0x0, 1), + BitField("spare21", 0x1, 1), + BitField("spare22", 0x0, 1), + BitField("spare23", 0x1, 1), + BitField("spare24", 0x1, 1) + ] + + +# len is 1 to 5 what do we do with the variable size? no lenght +# field?! WTF +class IaxRestOctetsHdr(Packet): + """IAX Rest Octets Section 10.5.2.18""" + name = "IAX Rest Octets" + fields_desc = [ + BitField("eightBitIRO", None, 1), + XBitField("ieiIRO", None, 7), + BitField("spare01", 0x0, 1), + BitField("spare02", 0x0, 1), + BitField("spare03", 0x1, 1), + BitField("spare04", 0x0, 1), + BitField("spare05", 0x1, 1), + BitField("spare06", 0x0, 1), + BitField("spare07", 0x1, 1), + BitField("spare08", 0x1, 1), + ByteField("spareB1", None), + ByteField("spareB2", None), + ByteField("spareB3", None) + ] + + +class L2PseudoLengthHdr(Packet): + """L2 Pseudo Length Section 10.5.2.19""" + name = "L2 Pseudo Length" + fields_desc = [ + BitField("eightBitPL", None, 1), + XBitField("ieiPL", None, 7), + BitField("l2pLength", None, 6), + BitField("bit2", 0x0, 1), + BitField("bit1", 0x1, 1) + ] + + +class MeasurementResultsHdr(Packet): + """Measurement Results Section 10.5.2.20""" + name = "Measurement Results" + fields_desc = [ + BitField("eightBitMR", None, 1), + XBitField("ieiMR", None, 7), + BitField("baUsed", 0x0, 1), + BitField("dtxUsed", 0x0, 1), + BitField("rxLevFull", 0x0, 6), + BitField("spare", 0x0, 1), + BitField("measValid", 0x0, 1), + BitField("rxLevSub", 0x0, 6), + BitField("spare0", 0x0, 1), + BitField("rxqualFull", 0x0, 3), + BitField("rxqualSub", 0x0, 3), + BitField("noNcellHi", 0x0, 1), + BitField("noNcellLo", 0x0, 2), + BitField("rxlevC1", 0x0, 6), + BitField("bcchC1", 0x0, 5), + BitField("bsicC1Hi", 0x0, 3), + BitField("bsicC1Lo", 0x0, 3), + BitField("rxlevC2", 0x0, 5), + BitField("rxlevC2Lo", 0x0, 1), + BitField("bcchC2", 0x0, 5), + BitField("bsicC1Hi", 0x0, 2), + BitField("bscicC2Lo", 0x0, 4), + BitField("bscicC2Hi", 0x0, 4), + + BitField("rxlevC3Lo", 0x0, 2), + BitField("bcchC3", 0x0, 5), + BitField("rxlevC3Hi", 0x0, 1), + + BitField("bsicC3Lo", 0x0, 5), + BitField("bsicC3Hi", 0x0, 3), + + BitField("rxlevC4Lo", 0x0, 3), + BitField("bcchC4", 0x0, 5), + + BitField("bsicC4", 0x0, 6), + BitField("rxlevC5Hi", 0x0, 2), + + BitField("rxlevC5Lo", 0x0, 4), + BitField("bcchC5Hi", 0x0, 4), + + BitField("bcchC5Lo", 0x0, 1), + BitField("bsicC5", 0x0, 6), + BitField("rxlevC6", 0x0, 1), + + BitField("rxlevC6Lo", 0x0, 5), + BitField("bcchC6Hi", 0x0, 3), + + BitField("bcchC6Lo", 0x0, 3), + BitField("bsicC6", 0x0, 5) + ] + + +class GprsMeasurementResultsHdr(Packet): + """GPRS Measurement Results Section 10.5.2.20a""" + name = "GPRS Measurement Results" + fields_desc = [ + BitField("eightBitGMR", None, 1), + XBitField("ieiGMR", None, 7), + BitField("cValue", 0x0, 6), + BitField("rxqualHi", 0x0, 2), + BitField("rxqL", 0x0, 1), + BitField("spare", 0x0, 1), + BitField("signVar", 0x0, 6) + ] + + +# len 3 to 10 +class MobileAllocationHdr(Packet): + """Mobile Allocation Section 10.5.2.21""" + name = "Mobile Allocation" + fields_desc = [ + BitField("eightBitMA", None, 1), + XBitField("ieiMA", None, 7), + XByteField("lengthMA", None), + ByteField("maC64", 0x12), + ByteField("maC56", None), # optional fields start here + ByteField("maC48", None), + ByteField("maC40", None), + ByteField("maC32", None), + ByteField("maC24", None), + ByteField("maC16", None), + ByteField("maC8", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(3, 10, a, self.fields_desc) + if self.lengthMA is None: + p = p[:1] + struct.pack(">B", res[1]) + p[2:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +class MobileTimeDifferenceHdr(Packet): + """Mobile Time Difference Section 10.5.2.21a""" + name = "Mobile Time Difference" + fields_desc = [ + BitField("eightBitMTD", None, 1), + XBitField("ieiMTD", None, 7), + XByteField("lengthMTD", 0x5), + ByteField("valueHi", 0x0), + ByteField("valueCnt", 0x0), + BitField("valueLow", 0x0, 5), + BitField("spare", 0x0, 1), + BitField("spare1", 0x0, 1), + BitField("spare2", 0x0, 1) + ] + + +# min 4 octets max 8 +class MultiRateConfigurationHdr(Packet): + """ MultiRate configuration Section 10.5.2.21aa""" + name = "MultiRate Configuration" + fields_desc = [ + BitField("eightBitMRC", None, 1), + XBitField("ieiMRC", None, 7), + + XByteField("lengthMRC", None), + + BitField("mrVersion", 0x0, 3), + BitField("spare", 0x0, 1), + BitField("icmi", 0x0, 1), + BitField("spare", 0x0, 1), + BitField("startMode", 0x0, 2), + + ByteField("amrCodec", 0x0), + + BitField("spare", None, 2), + BitField("threshold1", None, 6), + + BitField("hysteresis1", None, 4), + BitField("threshold2", None, 4), + + BitField("threshold2cnt", None, 2), + BitField("hysteresis2", None, 4), + BitField("threshold3", None, 2), + + BitField("threshold3cnt", None, 4), + BitField("hysteresis3", None, 4) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(4, 8, a, self.fields_desc) + if self.lengthMRC is None: + p = p[:1] + struct.pack(">B", res[1]) + p[2:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +# len 3 to 12 +class MultislotAllocationHdr(Packet): + """Multislot Allocation Section 10.5.2.21b""" + name = "Multislot Allocation" + fields_desc = [ + BitField("eightBitMSA", None, 1), + XBitField("ieiMSA", None, 7), + XByteField("lengthMSA", None), + BitField("ext0", 0x1, 1), + BitField("da", 0x0, 7), + ConditionalField(BitField("ext1", 0x1, 1), # optional + lambda pkt: pkt.ext0 == 0), + ConditionalField(BitField("ua", 0x0, 7), + lambda pkt: pkt.ext0 == 0), + ByteField("chan1", None), + ByteField("chan2", None), + ByteField("chan3", None), + ByteField("chan4", None), + ByteField("chan5", None), + ByteField("chan6", None), + ByteField("chan7", None), + ByteField("chan8", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(3, 12, a, self.fields_desc) + if res[0] is not 0: + p = p[:-res[0]] + if self.lengthMSA is None: + p = p[:1] + struct.pack(">B", len(p)-2) + p[2:] + return p + pay + + +class NcModeHdr(Packet): + """NC mode Section 10.5.2.21c""" + name = "NC Mode" + fields_desc = [ + XBitField("ieiNM", None, 4), + BitField("spare", 0x0, 2), + BitField("ncMode", 0x0, 2) + ] + + +# Fix for len problem +# concatenation NC Mode And Spare Half Octets +class NcModeAndSpareHalfOctets(Packet): + name = "NC Mode And Spare Half Octets" + fields_desc = [ + BitField("spare", 0x0, 2), + BitField("ncMode", 0x0, 2), + BitField("spareHalfOctets", 0x0, 4) + ] + + +class NeighbourCellsDescriptionHdr(Packet): + """Neighbour Cells Description Section 10.5.2.22""" + name = "Neighbour Cells Description" + fields_desc = [ + BitField("eightBitNCD", None, 1), + XBitField("ieiNCD", None, 7), + BitField("bit128", 0x0, 1), + BitField("bit127", 0x0, 1), + BitField("extInd", 0x0, 1), + BitField("baInd", 0x0, 1), + BitField("bit124", 0x0, 1), + BitField("bit123", 0x0, 1), + BitField("bit122", 0x0, 1), + BitField("bit121", 0x0, 1), + BitField("120bits", 0x0, 120) + ] + + +class NeighbourCellsDescription2Hdr(Packet): + """Neighbour Cells Description 2 Section 10.5.2.22a""" + name = "Neighbour Cells Description 2" + fields_desc = [ + BitField("eightBitNCD2", None, 1), + XBitField("ieiNCD2", None, 7), + BitField("bit128", 0x0, 1), + BitField("multiband", 0x0, 2), + BitField("baInd", 0x0, 1), + BitField("bit124", 0x0, 1), + BitField("bit123", 0x0, 1), + BitField("bit122", 0x0, 1), + BitField("bit121", 0x0, 1), + BitField("120bits", 0x0, 120) + ] + + +class NtNRestOctets(Packet): + """NT/N Rest Octets Section 10.5.2.22c""" + name = "NT/N Rest Octets" + fields_desc = [ + BitField("nln", 0x0, 2), + BitField("ncnInfo", 0x0, 4), + BitField("spare", 0x0, 2) + ] + + +# +# The following packet has no length info! +# +# len 1-18 +class P1RestOctets(Packet): + """P1 Rest Octets Section 10.5.2.23""" + name = "P1 Rest Octets" + fields_desc = [ + BitField("nln", 0x0, 2), + BitField("nlnStatus", 0x0, 1), + BitField("prio1", 0x0, 3), + BitField("prio2", 0x0, 3), + # optional + BitField("pageIndication1", 0x0, 1), + BitField("pageIndication2", 0x0, 1), + BitField("spare", 0x0, 5), + ByteField("spareB1", None), + ByteField("spareB2", None), + ByteField("spareB3", None), + ByteField("spareB4", None), + ByteField("spareB5", None), + ByteField("spareB6", None), + ByteField("spareB7", None), + ByteField("spareB8", None), + ByteField("spareB9", None), + ByteField("spareB10", None), + ByteField("spareB11", None), + ByteField("spareB12", None), + ByteField("spareB13", None), + ByteField("spareB14", None), + ByteField("spareB15", None), + ByteField("spareB16", None), + ] + + +# len 2-12 +class P2RestOctets(Packet): + """P2 Rest Octets Section 10.5.2.24""" + name = "P2 Rest Octets" + fields_desc = [ + BitField("cn3", 0x0, 2), + BitField("nln", 0x0, 2), + BitField("nlnStatus", 0x0, 1), + BitField("prio1", 0x0, 3), + + BitField("prio2", 0x0, 3), + BitField("prio3", 0x0, 3), + BitField("pageIndication3", 0x0, 1), + BitField("spare", 0x0, 1), + + # optinal (No length field!) + ByteField("spareB1", None), + ByteField("spareB2", None), + ByteField("spareB3", None), + ByteField("spareB4", None), + + ByteField("spareB5", None), + ByteField("spareB6", None), + ByteField("spareB7", None), + ByteField("spareB8", None), + + ByteField("spareB9", None), + ByteField("spareB10", None) + ] + + +# len 4 +class P3RestOctets(Packet): + """P3 Rest Octets Section 10.5.2.25""" + name = "P3 Rest Octets" + fields_desc = [ + BitField("cn3", 0x0, 2), + BitField("cn4", 0x0, 2), + BitField("nln", 0x0, 2), + BitField("nlnStatus", 0x0, 1), + BitField("prio1", 0x0, 3), + BitField("prio2", 0x0, 3), + BitField("prio3", 0x0, 3), + BitField("prio4", 0x0, 3), + BitField("spare", 0x0, 5) + ] + + +# len 4 +# strange packet, lots of valid formats + +# ideas for the dynamic packets: +# 1] for user interaction: Create an interactive "builder" based on a +# Q/A process (not very scapy like) +# 2] for usage in scripts, create an alternative packet for every +# possible packet layout +# + + +class PacketChannelDescription(Packet): + """Packet Channel Description Section 10.5.2.25a""" + name = "Packet Channel Description" + fields_desc = [ + ByteField("ieiPCD", None), + BitField("chanType", 0x0, 5), # This packet has multiple + # possible layouts. I moddeled the first one + BitField("tn", 0x0, 3), # maybe build an + #"interactive" builder. Like + # a Q/A then propose a + # packet? + BitField("tsc", 0x0, 3), + BitField("chooser1", 0x0, 1), + BitField("chooser2", 0x0, 1), + BitField("spare1", 0x0, 1), + BitField("arfcn", 0x0, 10), + ] + + +class DedicatedModeOrTBFHdr(Packet): + """Dedicated mode or TBF Section 10.5.2.25b""" + name = "Dedicated Mode or TBF" + fields_desc = [ + XBitField("ieiDMOT", None, 4), + BitField("spare", 0x0, 1), + BitField("tma", 0x0, 1), + BitField("downlink", 0x0, 1), + BitField("td", 0x0, 1) + ] + + +# FIXME add implementation +class RrPacketUplinkAssignment(Packet): + """RR Packet Uplink Assignment Section 10.5.2.25c""" + name = "RR Packet Uplink Assignment" + fields_desc = [ + # Fill me + ] + + +class PageModeHdr(Packet): + """Page Mode Section 10.5.2.26""" + name = "Page Mode" + fields_desc = [ + XBitField("ieiPM", None, 4), + BitField("spare", 0x0, 1), + BitField("spare1", 0x0, 1), + BitField("pm", 0x0, 2) + ] + + +# Fix for 1/2 len problem +# concatenation: pageMode and dedicatedModeOrTBF +class PageModeAndDedicatedModeOrTBF(Packet): + name = "Page Mode and Dedicated Mode Or TBF" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("spare1", 0x0, 1), + BitField("pm", 0x0, 2), + BitField("spare", 0x0, 1), + BitField("tma", 0x0, 1), + BitField("downlink", 0x0, 1), + BitField("td", 0x0, 1) + ] + + +# Fix for 1/2 len problem +# concatenation: pageMode and spareHalfOctets +class PageModeAndSpareHalfOctets(Packet): + name = "Page Mode and Spare Half Octets" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("spare1", 0x0, 1), + BitField("pm", 0x0, 2), + BitField("spareHalfOctets", 0x0, 4) + ] + + +# Fix for 1/2 len problem +# concatenation: pageMode and Channel Needed +class PageModeAndChannelNeeded(Packet): + name = "Page Mode and Channel Needed" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("spare1", 0x0, 1), + BitField("pm", 0x0, 2), + BitField("channel2", 0x0, 2), + BitField("channel1", 0x0, 2) + ] + + +class NccPermittedHdr(Packet): + """NCC Permitted Section 10.5.2.27""" + name = "NCC Permited" + fields_desc = [ + BitField("eightBitNP", None, 1), + XBitField("ieiNP", None, 7), + ByteField("nccPerm", 0x0) + ] + + +class PowerCommandHdr(Packet): + """Power Command Section 10.5.2.28""" + name = "Power Command" + fields_desc = [ + BitField("eightBitPC", None, 1), + XBitField("ieiPC", None, 7), + BitField("spare", 0x0, 1), + BitField("spare1", 0x0, 1), + BitField("spare2", 0x0, 1), + BitField("powerLvl", 0x0, 5) + ] + + +class PowerCommandAndAccessTypeHdr(Packet): + """Power Command and access type Section 10.5.2.28a""" + name = "Power Command and Access Type" + fields_desc = [ + BitField("eightBitPCAAT", None, 1), + XBitField("ieiPCAAT", None, 7), + BitField("atc", 0x0, 1), + BitField("spare", 0x0, 1), + BitField("spare1", 0x0, 1), + BitField("powerLvl", 0x0, 5) + ] + + +class RachControlParametersHdr(Packet): + """RACH Control Parameters Section 10.5.2.29""" + name = "RACH Control Parameters" + fields_desc = [ + BitField("eightBitRCP", None, 1), + XBitField("ieiRCP", None, 7), + BitField("maxRetrans", 0x0, 2), + BitField("txInteger", 0x0, 4), + BitField("cellBarrAccess", 0x0, 1), + BitField("re", 0x0, 1), + BitField("ACC15", 0x0, 1), + BitField("ACC14", 0x0, 1), + BitField("ACC13", 0x0, 1), + BitField("ACC12", 0x0, 1), + BitField("ACC11", 0x0, 1), + BitField("ACC10", 0x0, 1), + BitField("ACC09", 0x0, 1), + BitField("ACC08", 0x0, 1), + BitField("ACC07", 0x0, 1), + BitField("ACC06", 0x0, 1), + BitField("ACC05", 0x0, 1), + BitField("ACC04", 0x0, 1), + BitField("ACC03", 0x0, 1), + BitField("ACC02", 0x0, 1), + BitField("ACC01", 0x0, 1), + BitField("ACC00", 0x0, 1), + ] + + +class RequestReferenceHdr(Packet): + """Request Reference Section 10.5.2.30""" + name = "Request Reference" + fields_desc = [ + BitField("eightBitRR", None, 1), + XBitField("ieiRR", None, 7), + ByteField("ra", 0x0), + BitField("t1", 0x0, 5), + BitField("t3Hi", 0x0, 3), + BitField("t3Lo", 0x0, 3), + BitField("t2", 0x0, 5) + ] + + +class RrCauseHdr(Packet): + """RR Cause Section 10.5.2.31""" + name = "RR Cause" + fields_desc = [ + BitField("eightBitRC", None, 1), + XBitField("ieiRC", None, 7), + ByteField("rrCause", 0x0) + ] + + +class Si1RestOctets(Packet): + """SI 1 Rest Octets Section 10.5.2.32""" + name = "SI 1 Rest Octets" + fields_desc = [ + ByteField("nchPos", 0x0) + ] + + +class Si2bisRestOctets(Packet): + """SI 2bis Rest Octets Section 10.5.2.33""" + name = "SI 2bis Rest Octets" + fields_desc = [ + ByteField("spare", 0x0) + ] + + +class Si2terRestOctets(Packet): + """SI 2ter Rest Octets Section 10.5.2.33a""" + name = "SI 2ter Rest Octets" + fields_desc = [ + ByteField("spare1", 0x0), + ByteField("spare2", 0x0), + ByteField("spare3", 0x0), + ByteField("spare4", 0x0) + ] + + +# len 5 +class Si3RestOctets(Packet): + """SI 3 Rest Octets Section 10.5.2.34""" + name = "SI 3 Rest Octets" + fields_desc = [ + ByteField("byte1", 0x0), + ByteField("byte2", 0x0), + ByteField("byte3", 0x0), + ByteField("byte4", 0x0), + ByteField("byte5", 0x0) + ] + + +# len 1 to 11 +class Si4RestOctets(Packet): + """SI 4 Rest Octets Section 10.5.2.35""" + name = "SI 4 Rest Octets" + fields_desc = [ + XByteField("lengthSI4", None), + ByteField("byte2", None), + ByteField("byte3", None), + ByteField("byte4", None), + ByteField("byte5", None), + ByteField("byte6", None), + ByteField("byte7", None), + ByteField("byte8", None), + ByteField("byte9", None), + ByteField("byte10", None), + ByteField("byte11", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(1, 11, a, self.fields_desc, 1) + if self.lengthSI4 is None: + p = struct.pack(">B", res[1]) + p[1:] + if res[0] is not 0: + p = p[:-res[0]] + if len(p) is 1: # length of this packet can be 0, but packet is + p = '' # but the IE is manadatory 0_o + return p + pay + + +class Si6RestOctets(Packet): + """SI 6 Rest Octets Section 10.5.2.35a""" + name = "SI 4 Rest Octets" + fields_desc = [ + # FIXME + ] + + +# len 21 +class Si7RestOctets(Packet): + """SI 7 Rest Octets Section 10.5.2.36""" + name = "SI 7 Rest Octets" + fields_desc = [ + # FIXME + XByteField("lengthSI7", 0x15), + ByteField("byte2", 0x0), + ByteField("byte3", 0x0), + ByteField("byte4", 0x0), + ByteField("byte5", 0x0), + ByteField("byte6", 0x0), + ByteField("byte7", 0x0), + ByteField("byte8", 0x0), + ByteField("byte9", 0x0), + ByteField("byte10", 0x0), + ByteField("byte11", 0x0), + ByteField("byte12", 0x0), + ByteField("byte13", 0x0), + ByteField("byte14", 0x0), + ByteField("byte15", 0x0), + ByteField("byte16", 0x0), + ByteField("byte17", 0x0), + ByteField("byte18", 0x0), + ByteField("byte19", 0x0), + ByteField("byte20", 0x0), + ByteField("byte21", 0x0) + ] + + +# len 21 +class Si8RestOctets(Packet): + """SI 8 Rest Octets Section 10.5.2.37""" + name = "SI 8 Rest Octets" + fields_desc = [ + # FIXME + XByteField("lengthSI8", 0x15), + ByteField("byte2", 0x0), + ByteField("byte3", 0x0), + ByteField("byte4", 0x0), + ByteField("byte5", 0x0), + ByteField("byte6", 0x0), + ByteField("byte7", 0x0), + ByteField("byte8", 0x0), + ByteField("byte9", 0x0), + ByteField("byte10", 0x0), + ByteField("byte11", 0x0), + ByteField("byte12", 0x0), + ByteField("byte13", 0x0), + ByteField("byte14", 0x0), + ByteField("byte15", 0x0), + ByteField("byte16", 0x0), + ByteField("byte17", 0x0), + ByteField("byte18", 0x0), + ByteField("byte19", 0x0), + ByteField("byte20", 0x0), + ByteField("byte21", 0x0) + ] + + +#len 17 +class Si9RestOctets(Packet): + """SI 9 Rest Octets Section 10.5.2.37a""" + name = "SI 9 Rest Octets" + fields_desc = [ + # FIXME + XByteField("lengthSI9", 0x11), + ByteField("byte2", 0x0), + ByteField("byte3", 0x0), + ByteField("byte4", 0x0), + ByteField("byte5", 0x0), + ByteField("byte6", 0x0), + ByteField("byte7", 0x0), + ByteField("byte8", 0x0), + ByteField("byte9", 0x0), + ByteField("byte10", 0x0), + ByteField("byte11", 0x0), + ByteField("byte12", 0x0), + ByteField("byte13", 0x0), + ByteField("byte14", 0x0), + ByteField("byte15", 0x0), + ByteField("byte16", 0x0), + ByteField("byte17", 0x0) + ] + + +# len 21 +class Si13RestOctets(Packet): + """SI 13 Rest Octets Section 10.5.2.37b""" + name = "SI 13 Rest Octets" + fields_desc = [ + # FIXME + XByteField("lengthSI3", 0x15), + ByteField("byte2", 0x0), + ByteField("byte3", 0x0), + ByteField("byte4", 0x0), + ByteField("byte5", 0x0), + ByteField("byte6", 0x0), + ByteField("byte7", 0x0), + ByteField("byte8", 0x0), + ByteField("byte9", 0x0), + ByteField("byte10", 0x0), + ByteField("byte11", 0x0), + ByteField("byte12", 0x0), + ByteField("byte13", 0x0), + ByteField("byte14", 0x0), + ByteField("byte15", 0x0), + ByteField("byte16", 0x0), + ByteField("byte17", 0x0), + ByteField("byte18", 0x0), + ByteField("byte19", 0x0), + ByteField("byte20", 0x0), + ByteField("byte21", 0x0) + ] + + +# 10.5.2.37c [spare] +# 10.5.2.37d [spare] + + +# len 21 +class Si16RestOctets(Packet): + """SI 16 Rest Octets Section 10.5.2.37e""" + name = "SI 16 Rest Octets" + fields_desc = [ + # FIXME + XByteField("lengthSI16", 0x15), + ByteField("byte2", 0x0), + ByteField("byte3", 0x0), + ByteField("byte4", 0x0), + ByteField("byte5", 0x0), + ByteField("byte6", 0x0), + ByteField("byte7", 0x0), + ByteField("byte8", 0x0), + ByteField("byte9", 0x0), + ByteField("byte10", 0x0), + ByteField("byte11", 0x0), + ByteField("byte12", 0x0), + ByteField("byte13", 0x0), + ByteField("byte14", 0x0), + ByteField("byte15", 0x0), + ByteField("byte16", 0x0), + ByteField("byte17", 0x0), + ByteField("byte18", 0x0), + ByteField("byte19", 0x0), + ByteField("byte20", 0x0), + ByteField("byte21", 0x0) + ] + + +# len 21 +class Si17RestOctets(Packet): + """SI 17 Rest Octets Section 10.5.2.37f""" + name = "SI 17 Rest Octets" + fields_desc = [ + # FIXME + XByteField("lengthSI17", 0x15), + ByteField("byte2", 0x0), + ByteField("byte3", 0x0), + ByteField("byte4", 0x0), + ByteField("byte5", 0x0), + ByteField("byte6", 0x0), + ByteField("byte7", 0x0), + ByteField("byte8", 0x0), + ByteField("byte9", 0x0), + ByteField("byte10", 0x0), + ByteField("byte11", 0x0), + ByteField("byte12", 0x0), + ByteField("byte13", 0x0), + ByteField("byte14", 0x0), + ByteField("byte15", 0x0), + ByteField("byte16", 0x0), + ByteField("byte17", 0x0), + ByteField("byte18", 0x0), + ByteField("byte19", 0x0), + ByteField("byte20", 0x0), + ByteField("byte21", 0x0) + ] + + +class StartingTimeHdr(Packet): + """Starting Time Section 10.5.2.38""" + name = "Starting Time" + fields_desc = [ + BitField("eightBitST", None, 1), + XBitField("ieiST", None, 7), + ByteField("ra", 0x0), + BitField("t1", 0x0, 5), + BitField("t3Hi", 0x0, 3), + BitField("t3Lo", 0x0, 3), + BitField("t2", 0x0, 5) + ] + + +class SynchronizationIndicationHdr(Packet): + """Synchronization Indication Section 10.5.2.39""" + name = "Synchronization Indication" + fields_desc = [ + XBitField("ieiSI", None, 4), + BitField("nci", 0x0, 1), + BitField("rot", 0x0, 1), + BitField("si", 0x0, 2) + ] + + +class TimingAdvanceHdr(Packet): + """Timing Advance Section 10.5.2.40""" + name = "Timing Advance" + fields_desc = [ + BitField("eightBitTA", None, 1), + XBitField("ieiTA", None, 7), + BitField("spare", 0x0, 1), + BitField("spare1", 0x0, 1), + BitField("timingVal", 0x0, 6) + ] + + +class TimeDifferenceHdr(Packet): + """ Time Difference Section 10.5.2.41""" + name = "Time Difference" + fields_desc = [ + BitField("eightBitTD", None, 1), + XBitField("ieiTD", None, 7), + XByteField("lengthTD", 0x3), + ByteField("timeValue", 0x0) + ] + + +class TlliHdr(Packet): + """ TLLI Section Section 10.5.2.41a""" + name = "TLLI" + fields_desc = [ + BitField("eightBitT", None, 1), + XBitField("ieiT", None, 7), + ByteField("value", 0x0), + ByteField("value1", 0x0), + ByteField("value2", 0x0), + ByteField("value3", 0x0) + ] + + +class TmsiPTmsiHdr(Packet): + """ TMSI/P-TMSI Section 10.5.2.42""" + name = "TMSI/P-TMSI" + fields_desc = [ + BitField("eightBitTPT", None, 1), + XBitField("ieiTPT", None, 7), + ByteField("value", 0x0), + ByteField("value1", 0x0), + ByteField("value2", 0x0), + ByteField("value3", 0x0) + ] + + +class VgcsTargetModeIdenticationHdr(Packet): + """ VGCS target Mode Indication 10.5.2.42a""" + name = "VGCS Target Mode Indication" + fields_desc = [ + BitField("eightBitVTMI", None, 1), + XBitField("ieiVTMI", None, 7), + XByteField("lengthVTMI", 0x2), + BitField("targerMode", 0x0, 2), + BitField("cipherKeyNb", 0x0, 4), + BitField("spare", 0x0, 1), + BitField("spare1", 0x0, 1) + ] + + +class WaitIndicationHdr(Packet): + """ Wait Indication Section 10.5.2.43""" + name = "Wait Indication" + fields_desc = [ # asciiart of specs strange + BitField("eightBitWI", None, 1), + XBitField("ieiWI", None, 7), + ByteField("timeoutVal", 0x0) + ] + + +# len 17 +class ExtendedMeasurementResultsHdr(Packet): + """EXTENDED MEASUREMENT RESULTS Section 10.5.2.45""" + name = "Extended Measurement Results" + fields_desc = [ + BitField("eightBitEMR", None, 1), + XBitField("ieiEMR", None, 7), + + BitField("scUsed", None, 1), + BitField("dtxUsed", None, 1), + BitField("rxLevC0", None, 6), + + BitField("rxLevC1", None, 6), + BitField("rxLevC2Hi", None, 2), + + BitField("rxLevC2Lo", None, 4), + BitField("rxLevC3Hi", None, 4), + + BitField("rxLevC3Lo", None, 3), + BitField("rxLevC4", None, 5), + + BitField("rxLevC5", None, 6), + BitField("rxLevC6Hi", None, 2), + + BitField("rxLevC6Lo", None, 4), + BitField("rxLevC7Hi", None, 4), + + BitField("rxLevC7Lo", None, 2), + BitField("rxLevC8", None, 6), + + BitField("rxLevC9", None, 6), + BitField("rxLevC10Hi", None, 2), + + BitField("rxLevC10Lo", None, 4), + BitField("rxLevC11Hi", None, 4), + + BitField("rxLevC13Lo", None, 2), + BitField("rxLevC12", None, 6), + + BitField("rxLevC13", None, 6), + BitField("rxLevC14Hi", None, 2), + + BitField("rxLevC14Lo", None, 4), + BitField("rxLevC15Hi", None, 4), + + BitField("rxLevC15Lo", None, 2), + BitField("rxLevC16", None, 6), + + + BitField("rxLevC17", None, 6), + BitField("rxLevC18Hi", None, 2), + + BitField("rxLevC18Lo", None, 4), + BitField("rxLevC19Hi", None, 4), + + BitField("rxLevC19Lo", None, 2), + BitField("rxLevC20", None, 6) + ] + + +# len 17 +class ExtendedMeasurementFrequencyListHdr(Packet): + """Extended Measurement Frequency List Section 10.5.2.46""" + name = "Extended Measurement Frequency List" + fields_desc = [ + BitField("eightBitEMFL", None, 1), + XBitField("ieiEMFL", None, 7), + + BitField("bit128", 0x0, 1), + BitField("bit127", 0x0, 1), + BitField("spare", 0x0, 1), + BitField("seqCode", 0x0, 1), + BitField("bit124", 0x0, 1), + BitField("bit123", 0x0, 1), + BitField("bit122", 0x0, 1), + BitField("bit121", 0x0, 1), + + BitField("bitsRest", 0x0, 128) + ] + + +class SuspensionCauseHdr(Packet): + """Suspension Cause Section 10.5.2.47""" + name = "Suspension Cause" + fields_desc = [ + BitField("eightBitSC", None, 1), + XBitField("ieiSC", None, 7), + ByteField("suspVal", 0x0) + ] + + +class ApduIDHdr(Packet): + """APDU Flags Section 10.5.2.48""" + name = "Apdu Id" + fields_desc = [ + XBitField("ieiAI", None, 4), + BitField("id", None, 4) + ] + + +class ApduFlagsHdr(Packet): + """APDU Flags Section 10.5.2.49""" + name = "Apdu Flags" + fields_desc = [ + XBitField("iei", None, 4), + BitField("spare", 0x0, 1), + BitField("cr", 0x0, 1), + BitField("firstSeg", 0x0, 1), + BitField("lastSeg", 0x0, 1) + ] + + +# Fix 1/2 len problem +class ApduIDAndApduFlags(Packet): + name = "Apu Id and Apdu Flags" + fields_desc = [ + BitField("id", None, 4), + BitField("spare", 0x0, 1), + BitField("cr", 0x0, 1), + BitField("firstSeg", 0x0, 1), + BitField("lastSeg", 0x0, 1) + ] + + +# len 2 to max L3 (251) (done) +class ApduDataHdr(Packet): + """APDU Data Section 10.5.2.50""" + name = "Apdu Data" + fields_desc = [ + BitField("eightBitAD", None, 1), + XBitField("ieiAD", None, 7), + XByteField("lengthAD", None), + #optional + ByteField("apuInfo1", None), + ByteField("apuInfo2", None), + ByteField("apuInfo3", None), + ByteField("apuInfo4", None), + ByteField("apuInfo5", None), + ByteField("apuInfo6", None), + ByteField("apuInfo7", None), + ByteField("apuInfo8", None), + ByteField("apuInfo9", None), + ByteField("apuInfo10", None), + ByteField("apuInfo11", None), + ByteField("apuInfo12", None), + ByteField("apuInfo13", None), + ByteField("apuInfo14", None), + ByteField("apuInfo15", None), + ByteField("apuInfo16", None), + ByteField("apuInfo17", None), + ByteField("apuInfo18", None), + ByteField("apuInfo19", None), + ByteField("apuInfo20", None), + ByteField("apuInfo21", None), + ByteField("apuInfo22", None), + ByteField("apuInfo23", None), + ByteField("apuInfo24", None), + ByteField("apuInfo25", None), + ByteField("apuInfo26", None), + ByteField("apuInfo27", None), + ByteField("apuInfo28", None), + ByteField("apuInfo29", None), + ByteField("apuInfo30", None), + ByteField("apuInfo31", None), + ByteField("apuInfo32", None), + ByteField("apuInfo33", None), + ByteField("apuInfo34", None), + ByteField("apuInfo35", None), + ByteField("apuInfo36", None), + ByteField("apuInfo37", None), + ByteField("apuInfo38", None), + ByteField("apuInfo39", None), + ByteField("apuInfo40", None), + ByteField("apuInfo41", None), + ByteField("apuInfo42", None), + ByteField("apuInfo43", None), + ByteField("apuInfo44", None), + ByteField("apuInfo45", None), + ByteField("apuInfo46", None), + ByteField("apuInfo47", None), + ByteField("apuInfo48", None), + ByteField("apuInfo49", None), + ByteField("apuInfo50", None), + ByteField("apuInfo51", None), + ByteField("apuInfo52", None), + ByteField("apuInfo53", None), + ByteField("apuInfo54", None), + ByteField("apuInfo55", None), + ByteField("apuInfo56", None), + ByteField("apuInfo57", None), + ByteField("apuInfo58", None), + ByteField("apuInfo59", None), + ByteField("apuInfo60", None), + ByteField("apuInfo61", None), + ByteField("apuInfo62", None), + ByteField("apuInfo63", None), + ByteField("apuInfo64", None), + ByteField("apuInfo65", None), + ByteField("apuInfo66", None), + ByteField("apuInfo67", None), + ByteField("apuInfo68", None), + ByteField("apuInfo69", None), + ByteField("apuInfo70", None), + ByteField("apuInfo71", None), + ByteField("apuInfo72", None), + ByteField("apuInfo73", None), + ByteField("apuInfo74", None), + ByteField("apuInfo75", None), + ByteField("apuInfo76", None), + ByteField("apuInfo77", None), + ByteField("apuInfo78", None), + ByteField("apuInfo79", None), + ByteField("apuInfo80", None), + ByteField("apuInfo81", None), + ByteField("apuInfo82", None), + ByteField("apuInfo83", None), + ByteField("apuInfo84", None), + ByteField("apuInfo85", None), + ByteField("apuInfo86", None), + ByteField("apuInfo87", None), + ByteField("apuInfo88", None), + ByteField("apuInfo89", None), + ByteField("apuInfo90", None), + ByteField("apuInfo91", None), + ByteField("apuInfo92", None), + ByteField("apuInfo93", None), + ByteField("apuInfo94", None), + ByteField("apuInfo95", None), + ByteField("apuInfo96", None), + ByteField("apuInfo97", None), + ByteField("apuInfo98", None), + ByteField("apuInfo99", None), + ByteField("apuInfo100", None), + ByteField("apuInfo101", None), + ByteField("apuInfo102", None), + ByteField("apuInfo103", None), + ByteField("apuInfo104", None), + ByteField("apuInfo105", None), + ByteField("apuInfo106", None), + ByteField("apuInfo107", None), + ByteField("apuInfo108", None), + ByteField("apuInfo109", None), + ByteField("apuInfo110", None), + ByteField("apuInfo111", None), + ByteField("apuInfo112", None), + ByteField("apuInfo113", None), + ByteField("apuInfo114", None), + ByteField("apuInfo115", None), + ByteField("apuInfo116", None), + ByteField("apuInfo117", None), + ByteField("apuInfo118", None), + ByteField("apuInfo119", None), + ByteField("apuInfo120", None), + ByteField("apuInfo121", None), + ByteField("apuInfo122", None), + ByteField("apuInfo123", None), + ByteField("apuInfo124", None), + ByteField("apuInfo125", None), + ByteField("apuInfo126", None), + ByteField("apuInfo127", None), + ByteField("apuInfo128", None), + ByteField("apuInfo129", None), + ByteField("apuInfo130", None), + ByteField("apuInfo131", None), + ByteField("apuInfo132", None), + ByteField("apuInfo133", None), + ByteField("apuInfo134", None), + ByteField("apuInfo135", None), + ByteField("apuInfo136", None), + ByteField("apuInfo137", None), + ByteField("apuInfo138", None), + ByteField("apuInfo139", None), + ByteField("apuInfo140", None), + ByteField("apuInfo141", None), + ByteField("apuInfo142", None), + ByteField("apuInfo143", None), + ByteField("apuInfo144", None), + ByteField("apuInfo145", None), + ByteField("apuInfo146", None), + ByteField("apuInfo147", None), + ByteField("apuInfo148", None), + ByteField("apuInfo149", None), + ByteField("apuInfo150", None), + ByteField("apuInfo151", None), + ByteField("apuInfo152", None), + ByteField("apuInfo153", None), + ByteField("apuInfo154", None), + ByteField("apuInfo155", None), + ByteField("apuInfo156", None), + ByteField("apuInfo157", None), + ByteField("apuInfo158", None), + ByteField("apuInfo159", None), + ByteField("apuInfo160", None), + ByteField("apuInfo161", None), + ByteField("apuInfo162", None), + ByteField("apuInfo163", None), + ByteField("apuInfo164", None), + ByteField("apuInfo165", None), + ByteField("apuInfo166", None), + ByteField("apuInfo167", None), + ByteField("apuInfo168", None), + ByteField("apuInfo169", None), + ByteField("apuInfo170", None), + ByteField("apuInfo171", None), + ByteField("apuInfo172", None), + ByteField("apuInfo173", None), + ByteField("apuInfo174", None), + ByteField("apuInfo175", None), + ByteField("apuInfo176", None), + ByteField("apuInfo177", None), + ByteField("apuInfo178", None), + ByteField("apuInfo179", None), + ByteField("apuInfo180", None), + ByteField("apuInfo181", None), + ByteField("apuInfo182", None), + ByteField("apuInfo183", None), + ByteField("apuInfo184", None), + ByteField("apuInfo185", None), + ByteField("apuInfo186", None), + ByteField("apuInfo187", None), + ByteField("apuInfo188", None), + ByteField("apuInfo189", None), + ByteField("apuInfo190", None), + ByteField("apuInfo191", None), + ByteField("apuInfo192", None), + ByteField("apuInfo193", None), + ByteField("apuInfo194", None), + ByteField("apuInfo195", None), + ByteField("apuInfo196", None), + ByteField("apuInfo197", None), + ByteField("apuInfo198", None), + ByteField("apuInfo199", None), + ByteField("apuInfo200", None), + ByteField("apuInfo201", None), + ByteField("apuInfo202", None), + ByteField("apuInfo203", None), + ByteField("apuInfo204", None), + ByteField("apuInfo205", None), + ByteField("apuInfo206", None), + ByteField("apuInfo207", None), + ByteField("apuInfo208", None), + ByteField("apuInfo209", None), + ByteField("apuInfo210", None), + ByteField("apuInfo211", None), + ByteField("apuInfo212", None), + ByteField("apuInfo213", None), + ByteField("apuInfo214", None), + ByteField("apuInfo215", None), + ByteField("apuInfo216", None), + ByteField("apuInfo217", None), + ByteField("apuInfo218", None), + ByteField("apuInfo219", None), + ByteField("apuInfo220", None), + ByteField("apuInfo221", None), + ByteField("apuInfo222", None), + ByteField("apuInfo223", None), + ByteField("apuInfo224", None), + ByteField("apuInfo225", None), + ByteField("apuInfo226", None), + ByteField("apuInfo227", None), + ByteField("apuInfo228", None), + ByteField("apuInfo229", None), + ByteField("apuInfo230", None), + ByteField("apuInfo231", None), + ByteField("apuInfo232", None), + ByteField("apuInfo233", None), + ByteField("apuInfo234", None), + ByteField("apuInfo235", None), + ByteField("apuInfo236", None), + ByteField("apuInfo237", None), + ByteField("apuInfo238", None), + ByteField("apuInfo239", None), + ByteField("apuInfo240", None), + ByteField("apuInfo241", None), + ByteField("apuInfo242", None), + ByteField("apuInfo243", None), + ByteField("apuInfo244", None), + ByteField("apuInfo245", None), + ByteField("apuInfo246", None), + ByteField("apuInfo247", None), + ByteField("apuInfo248", None), + ByteField("apuInfo249", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(2, 251, a, self.fields_desc) + if self.lengthAD is None: + p = p[:1] + struct.pack(">B", res[1]) + p[2:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + +# +# 10.5.3 Mobility management information elements +# + + +class AuthenticationParameterRAND(Packet): + """Authentication parameter RAND Section 10.5.3.1""" + name = "Authentication Parameter Rand" + fields_desc = [ + ByteField("ieiAPR", None), + BitField("randValue", 0x0, 128) + ] + + +class AuthenticationParameterSRES(Packet): + """Authentication parameter SRES Section 10.5.3.2""" + name = "Authentication Parameter Sres" + fields_desc = [ + ByteField("ieiAPS", None), + BitField("sresValue", 0x0, 40) + ] + + +class CmServiceType(Packet): + """CM service type Section 10.5.3.3""" + name = "CM Service Type" + fields_desc = [ + XBitField("ieiCST", 0x0, 4), + BitField("serviceType", 0x0, 4) + ] + + +class CmServiceTypeAndCiphKeySeqNr(Packet): + name = "CM Service Type and Cipher Key Sequence Number" + fields_desc = [ + BitField("keySeq", 0x0, 3), + BitField("spare", 0x0, 1), + BitField("serviceType", 0x0, 4) + ] + + +class IdentityType(Packet): + """Identity type Section 10.5.3.4""" + name = "Identity Type" + fields_desc = [ + XBitField("ieiIT", 0x0, 4), + BitField("spare", 0x0, 1), + BitField("idType", 0x1, 3) + ] + + +# Fix 1/2 len problem +class IdentityTypeAndSpareHalfOctet(Packet): + name = "Identity Type and Spare Half Octet" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("idType", 0x1, 3), + BitField("spareHalfOctets", 0x0, 4) + ] + + +class LocationUpdatingType(Packet): + """Location updating type Section 10.5.3.5""" + name = "Location Updating Type" + fields_desc = [ + XBitField("ieiLUT", 0x0, 4), + BitField("for", 0x0, 1), + BitField("spare", 0x0, 1), + BitField("lut", 0x0, 2) + ] + + +class LocationUpdatingTypeAndCiphKeySeqNr(Packet): + name = "Location Updating Type and Cipher Key Sequence Number" + fields_desc = [ + BitField("for", 0x0, 1), + BitField("spare", 0x0, 1), + BitField("lut", 0x0, 2), + BitField("spare", 0x0, 1), + BitField("keySeq", 0x0, 3) + ] + + +# len 3 to L3 max (251) (done) +class NetworkNameHdr(Packet): + """Network Name Section 10.5.3.5a""" + name = "Network Name" + fields_desc = [ + BitField("eightBitNN", None, 1), + XBitField("ieiNN", None, 7), + + XByteField("lengthNN", None), + + BitField("ext1", 0x1, 1), + BitField("codingScheme", 0x0, 3), + BitField("addCi", 0x0, 1), + BitField("nbSpare", 0x0, 3), + # optional + ByteField("txtString1", None), + ByteField("txtString2", None), + ByteField("txtString3", None), + ByteField("txtString4", None), + ByteField("txtString5", None), + ByteField("txtString6", None), + ByteField("txtString7", None), + ByteField("txtString8", None), + ByteField("txtString9", None), + ByteField("txtString10", None), + ByteField("txtString11", None), + ByteField("txtString12", None), + ByteField("txtString13", None), + ByteField("txtString14", None), + ByteField("txtString15", None), + ByteField("txtString16", None), + ByteField("txtString17", None), + ByteField("txtString18", None), + ByteField("txtString19", None), + ByteField("txtString20", None), + ByteField("txtString21", None), + ByteField("txtString22", None), + ByteField("txtString23", None), + ByteField("txtString24", None), + ByteField("txtString25", None), + ByteField("txtString26", None), + ByteField("txtString27", None), + ByteField("txtString28", None), + ByteField("txtString29", None), + ByteField("txtString30", None), + ByteField("txtString31", None), + ByteField("txtString32", None), + ByteField("txtString33", None), + ByteField("txtString34", None), + ByteField("txtString35", None), + ByteField("txtString36", None), + ByteField("txtString37", None), + ByteField("txtString38", None), + ByteField("txtString39", None), + ByteField("txtString40", None), + ByteField("txtString41", None), + ByteField("txtString42", None), + ByteField("txtString43", None), + ByteField("txtString44", None), + ByteField("txtString45", None), + ByteField("txtString46", None), + ByteField("txtString47", None), + ByteField("txtString48", None), + ByteField("txtString49", None), + ByteField("txtString50", None), + ByteField("txtString51", None), + ByteField("txtString52", None), + ByteField("txtString53", None), + ByteField("txtString54", None), + ByteField("txtString55", None), + ByteField("txtString56", None), + ByteField("txtString57", None), + ByteField("txtString58", None), + ByteField("txtString59", None), + ByteField("txtString60", None), + ByteField("txtString61", None), + ByteField("txtString62", None), + ByteField("txtString63", None), + ByteField("txtString64", None), + ByteField("txtString65", None), + ByteField("txtString66", None), + ByteField("txtString67", None), + ByteField("txtString68", None), + ByteField("txtString69", None), + ByteField("txtString70", None), + ByteField("txtString71", None), + ByteField("txtString72", None), + ByteField("txtString73", None), + ByteField("txtString74", None), + ByteField("txtString75", None), + ByteField("txtString76", None), + ByteField("txtString77", None), + ByteField("txtString78", None), + ByteField("txtString79", None), + ByteField("txtString80", None), + ByteField("txtString81", None), + ByteField("txtString82", None), + ByteField("txtString83", None), + ByteField("txtString84", None), + ByteField("txtString85", None), + ByteField("txtString86", None), + ByteField("txtString87", None), + ByteField("txtString88", None), + ByteField("txtString89", None), + ByteField("txtString90", None), + ByteField("txtString91", None), + ByteField("txtString92", None), + ByteField("txtString93", None), + ByteField("txtString94", None), + ByteField("txtString95", None), + ByteField("txtString96", None), + ByteField("txtString97", None), + ByteField("txtString98", None), + ByteField("txtString99", None), + ByteField("txtString100", None), + ByteField("txtString101", None), + ByteField("txtString102", None), + ByteField("txtString103", None), + ByteField("txtString104", None), + ByteField("txtString105", None), + ByteField("txtString106", None), + ByteField("txtString107", None), + ByteField("txtString108", None), + ByteField("txtString109", None), + ByteField("txtString110", None), + ByteField("txtString111", None), + ByteField("txtString112", None), + ByteField("txtString113", None), + ByteField("txtString114", None), + ByteField("txtString115", None), + ByteField("txtString116", None), + ByteField("txtString117", None), + ByteField("txtString118", None), + ByteField("txtString119", None), + ByteField("txtString120", None), + ByteField("txtString121", None), + ByteField("txtString122", None), + ByteField("txtString123", None), + ByteField("txtString124", None), + ByteField("txtString125", None), + ByteField("txtString126", None), + ByteField("txtString127", None), + ByteField("txtString128", None), + ByteField("txtString129", None), + ByteField("txtString130", None), + ByteField("txtString131", None), + ByteField("txtString132", None), + ByteField("txtString133", None), + ByteField("txtString134", None), + ByteField("txtString135", None), + ByteField("txtString136", None), + ByteField("txtString137", None), + ByteField("txtString138", None), + ByteField("txtString139", None), + ByteField("txtString140", None), + ByteField("txtString141", None), + ByteField("txtString142", None), + ByteField("txtString143", None), + ByteField("txtString144", None), + ByteField("txtString145", None), + ByteField("txtString146", None), + ByteField("txtString147", None), + ByteField("txtString148", None), + ByteField("txtString149", None), + ByteField("txtString150", None), + ByteField("txtString151", None), + ByteField("txtString152", None), + ByteField("txtString153", None), + ByteField("txtString154", None), + ByteField("txtString155", None), + ByteField("txtString156", None), + ByteField("txtString157", None), + ByteField("txtString158", None), + ByteField("txtString159", None), + ByteField("txtString160", None), + ByteField("txtString161", None), + ByteField("txtString162", None), + ByteField("txtString163", None), + ByteField("txtString164", None), + ByteField("txtString165", None), + ByteField("txtString166", None), + ByteField("txtString167", None), + ByteField("txtString168", None), + ByteField("txtString169", None), + ByteField("txtString170", None), + ByteField("txtString171", None), + ByteField("txtString172", None), + ByteField("txtString173", None), + ByteField("txtString174", None), + ByteField("txtString175", None), + ByteField("txtString176", None), + ByteField("txtString177", None), + ByteField("txtString178", None), + ByteField("txtString179", None), + ByteField("txtString180", None), + ByteField("txtString181", None), + ByteField("txtString182", None), + ByteField("txtString183", None), + ByteField("txtString184", None), + ByteField("txtString185", None), + ByteField("txtString186", None), + ByteField("txtString187", None), + ByteField("txtString188", None), + ByteField("txtString189", None), + ByteField("txtString190", None), + ByteField("txtString191", None), + ByteField("txtString192", None), + ByteField("txtString193", None), + ByteField("txtString194", None), + ByteField("txtString195", None), + ByteField("txtString196", None), + ByteField("txtString197", None), + ByteField("txtString198", None), + ByteField("txtString199", None), + ByteField("txtString200", None), + ByteField("txtString201", None), + ByteField("txtString202", None), + ByteField("txtString203", None), + ByteField("txtString204", None), + ByteField("txtString205", None), + ByteField("txtString206", None), + ByteField("txtString207", None), + ByteField("txtString208", None), + ByteField("txtString209", None), + ByteField("txtString210", None), + ByteField("txtString211", None), + ByteField("txtString212", None), + ByteField("txtString213", None), + ByteField("txtString214", None), + ByteField("txtString215", None), + ByteField("txtString216", None), + ByteField("txtString217", None), + ByteField("txtString218", None), + ByteField("txtString219", None), + ByteField("txtString220", None), + ByteField("txtString221", None), + ByteField("txtString222", None), + ByteField("txtString223", None), + ByteField("txtString224", None), + ByteField("txtString225", None), + ByteField("txtString226", None), + ByteField("txtString227", None), + ByteField("txtString228", None), + ByteField("txtString229", None), + ByteField("txtString230", None), + ByteField("txtString231", None), + ByteField("txtString232", None), + ByteField("txtString233", None), + ByteField("txtString234", None), + ByteField("txtString235", None), + ByteField("txtString236", None), + ByteField("txtString237", None), + ByteField("txtString238", None), + ByteField("txtString239", None), + ByteField("txtString240", None), + ByteField("txtString241", None), + ByteField("txtString242", None), + ByteField("txtString243", None), + ByteField("txtString244", None), + ByteField("txtString245", None), + ByteField("txtString246", None), + ByteField("txtString247", None), + ByteField("txtString248", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(3, 251, a, self.fields_desc) + if self.lengthNN is None: + p = p[:1] + struct.pack(">B", res[1]) + p[2:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +class RejectCause(Packet): + """Reject cause Section 10.5.3.6""" + name = "Reject Cause" + fields_desc = [ + ByteField("ieiRC", 0x0), + ByteField("rejCause", 0x0) + ] + + +class FollowOnProceed(Packet): + """Follow-on Proceed Section 10.5.3.7""" + name = "Follow-on Proceed" + fields_desc = [ + ByteField("ieiFOP", 0x0), + ] + + +class TimeZoneHdr(Packet): + """Time Zone Section 10.5.3.8""" + name = "Time Zone" + fields_desc = [ + BitField("eightBitTZ", None, 1), + XBitField("ieiTZ", None, 7), + ByteField("timeZone", 0x0), + ] + + +class TimeZoneAndTimeHdr(Packet): + """Time Zone and Time Section 10.5.3.9""" + name = "Time Zone and Time" + fields_desc = [ + BitField("eightBitTZAT", None, 1), + XBitField("ieiTZAT", None, 7), + ByteField("year", 0x0), + ByteField("month", 0x0), + ByteField("day", 0x0), + ByteField("hour", 0x0), + ByteField("minute", 0x0), + ByteField("second", 0x0), + ByteField("timeZone", 0x0) + ] + + +class CtsPermissionHdr(Packet): + """CTS permission Section 10.5.3.10""" + name = "Cts Permission" + fields_desc = [ + BitField("eightBitCP", None, 1), + XBitField("ieiCP", None, 7), + ] + + +class LsaIdentifierHdr(Packet): + """LSA Identifier Section 10.5.3.11""" + name = "Lsa Identifier" + fields_desc = [ + BitField("eightBitLI", None, 1), + XBitField("ieiLI", None, 7), + ByteField("lsaID", 0x0), + ByteField("lsaID1", 0x0), + ByteField("lsaID2", 0x0) + ] + + +# +# 10.5.4 Call control information elements +# + +#10.5.4.1 Extensions of codesets +# This is only text and no packet + +class LockingShiftProcedureHdr(Packet): + """Locking shift procedure Section 10.5.4.2""" + name = "Locking Shift Procedure" + fields_desc = [ + XBitField("ieiLSP", None, 4), + BitField("lockShift", 0x0, 1), + BitField("codesetId", 0x0, 3) + ] + + +class NonLockingShiftProcedureHdr(Packet): + """Non-locking shift procedure Section 10.5.4.3""" + name = "Non-locking Shift Procedure" + fields_desc = [ + XBitField("ieiNLSP", None, 4), + BitField("nonLockShift", 0x1, 1), + BitField("codesetId", 0x0, 3) + ] + + +class AuxiliaryStatesHdr(Packet): + """Auxiliary states Section 10.5.4.4""" + name = "Auxiliary States" + fields_desc = [ + BitField("eightBitAS", None, 1), + XBitField("ieiAS", None, 7), + XByteField("lengthAS", 0x3), + BitField("ext", 0x1, 1), + BitField("spare", 0x0, 3), + BitField("holdState", 0x0, 2), + BitField("mptyState", 0x0, 2) + ] + + +# len 3 to 15 +class BearerCapabilityHdr(Packet): + """Bearer capability Section 10.5.4.5""" + name = "Bearer Capability" + fields_desc = [ + BitField("eightBitBC", None, 1), + XBitField("ieiBC", None, 7), + + XByteField("lengthBC", None), + + BitField("ext0", 0x1, 1), + BitField("radioChReq", 0x1, 2), + BitField("codingStd", 0x0, 1), + BitField("transMode", 0x0, 1), + BitField("infoTransCa", 0x0, 3), + # optional + ConditionalField(BitField("ext1", 0x1, 1), + lambda pkt: pkt.ext0 == 0), + ConditionalField(BitField("coding", None, 1), + lambda pkt: pkt.ext0 == 0), + ConditionalField(BitField("spare", None, 2), + lambda pkt: pkt.ext0 == 0), + ConditionalField(BitField("speechVers", 0x0, 4), + lambda pkt: pkt.ext0 == 0), + + ConditionalField(BitField("ext2", 0x1, 1), + lambda pkt: pkt.ext1 == 0), + ConditionalField(BitField("compress", None, 1), + lambda pkt: pkt.ext1 == 0), + ConditionalField(BitField("structure", None, 2), + lambda pkt: pkt.ext1 == 0), + ConditionalField(BitField("dupMode", None, 1), + lambda pkt: pkt.ext1 == 0), + ConditionalField(BitField("config", None, 1), + lambda pkt: pkt.ext1 == 0), + ConditionalField(BitField("nirr", None, 1), + lambda pkt: pkt.ext1 == 0), + ConditionalField(BitField("establi", 0x0, 1), + lambda pkt: pkt.ext1 == 0), + + BitField("ext3", None, 1), + BitField("accessId", None, 2), + BitField("rateAda", None, 2), + BitField("signaling", None, 3), + + ConditionalField(BitField("ext4", None, 1), + lambda pkt: pkt.ext3 == 0), + ConditionalField(BitField("otherITC", None, 2), + lambda pkt: pkt.ext3 == 0), + ConditionalField(BitField("otherRate", None, 2), + lambda pkt: pkt.ext3 == 0), + ConditionalField(BitField("spare1", 0x0, 3), + lambda pkt: pkt.ext3 == 0), + + ConditionalField(BitField("ext5", 0x1, 1), + lambda pkt: pkt.ext4 == 0), + ConditionalField(BitField("hdr", None, 1), + lambda pkt: pkt.ext4 == 0), + ConditionalField(BitField("multiFr", None, 1), + lambda pkt: pkt.ext4 == 0), + ConditionalField(BitField("mode", None, 1), + lambda pkt: pkt.ext4 == 0), + ConditionalField(BitField("lli", None, 1), + lambda pkt: pkt.ext4 == 0), + ConditionalField(BitField("assig", None, 1), + lambda pkt: pkt.ext4 == 0), + ConditionalField(BitField("inbNeg", None, 1), + lambda pkt: pkt.ext4 == 0), + ConditionalField(BitField("spare2", 0x0, 1), + lambda pkt: pkt.ext4 == 0), + + BitField("ext6", None, 1), + BitField("layer1Id", None, 2), + BitField("userInf", None, 4), + BitField("sync", None, 1), + + ConditionalField(BitField("ext7", None, 1), + lambda pkt: pkt.ext6 == 0), + ConditionalField(BitField("stopBit", None, 1), + lambda pkt: pkt.ext6 == 0), + ConditionalField(BitField("negoc", None, 1), + lambda pkt: pkt.ext6 == 0), + ConditionalField(BitField("nbDataBit", None, 1), + lambda pkt: pkt.ext6 == 0), + ConditionalField(BitField("userRate", None, 4), + lambda pkt: pkt.ext6 == 0), + + ConditionalField(BitField("ext8", None, 1), + lambda pkt: pkt.ext7 == 0), + ConditionalField(BitField("interRate", None, 2), + lambda pkt: pkt.ext7 == 0), + ConditionalField(BitField("nicTX", None, 1), + lambda pkt: pkt.ext7 == 0), + ConditionalField(BitField("nicRX", None, 1), + lambda pkt: pkt.ext7 == 0), + ConditionalField(BitField("parity", None, 3), + lambda pkt: pkt.ext7 == 0), + + ConditionalField(BitField("ext9", None, 1), + lambda pkt: pkt.ext8 == 0), + ConditionalField(BitField("connEle", None, 2), + lambda pkt: pkt.ext8 == 0), + ConditionalField(BitField("modemType", None, 5), + lambda pkt: pkt.ext8 == 0), + + ConditionalField(BitField("ext10", None, 1), + lambda pkt: pkt.ext9 == 0), + ConditionalField(BitField("otherModemType", None, 2), + lambda pkt: pkt.ext9 == 0), + ConditionalField(BitField("netUserRate", None, 5), + lambda pkt: pkt.ext9 == 0), + + ConditionalField(BitField("ext11", None, 1), + lambda pkt: pkt.ext10 == 0), + ConditionalField(BitField("chanCoding", None, 4), + lambda pkt: pkt.ext10 == 0), + ConditionalField(BitField("maxTrafficChan", None, 3), + lambda pkt: pkt.ext10 == 0), + + ConditionalField(BitField("ext12", None, 1), + lambda pkt: pkt.ext11 == 0), + ConditionalField(BitField("uimi", None, 3), + lambda pkt: pkt.ext11 == 0), + ConditionalField(BitField("airInterfaceUserRate", None, 4), + lambda pkt: pkt.ext11 == 0), + + ConditionalField(BitField("ext13", 0x1, 1), + lambda pkt: pkt.ext12 == 0), + ConditionalField(BitField("layer2Ch", None, 2), + lambda pkt: pkt.ext12 == 0), + ConditionalField(BitField("userInfoL2", 0x0, 5), + lambda pkt: pkt.ext12 == 0) + ] + +# We have a bug here. packet is not working if used in message + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(3, 15, a, self.fields_desc) + if res[0] is not 0: + p = p[:-res[0]] + # avoids a bug. find better way + if len(p) is 5: + p = p[:-2] + if self.lengthBC is None: + print("len von a %s" % (len(p),)) + p = p[:1] + struct.pack(">B", len(p)-3) + p[2:] + return p + pay + + +class CallControlCapabilitiesHdr(Packet): + """Call Control Capabilities Section 10.5.4.5a""" + name = "Call Control Capabilities" + fields_desc = [ + BitField("eightBitCCC", None, 1), + XBitField("ieiCCC", None, 7), + XByteField("lengthCCC", 0x3), + BitField("spare", 0x0, 6), + BitField("pcp", 0x0, 1), + BitField("dtmf", 0x0, 1) + ] + + +class CallStateHdr(Packet): + """Call State Section 10.5.4.6""" + name = "Call State" + fields_desc = [ + BitField("eightBitCS", None, 1), + XBitField("ieiCS", None, 7), + BitField("codingStd", 0x0, 2), + BitField("stateValue", 0x0, 6) + ] + + +# len 3 to 43 +class CalledPartyBcdNumberHdr(Packet): + """Called party BCD number Section 10.5.4.7""" + name = "Called Party BCD Number" + fields_desc = [ + BitField("eightBitCPBN", None, 1), + XBitField("ieiCPBN", None, 7), + XByteField("lengthCPBN", None), + BitField("ext", 0x1, 1), + BitField("typeNb", 0x0, 3), + BitField("nbPlanId", 0x0, 4), + # optional + BitField("nbDigit2", None, 4), + BitField("nbDigit1", None, 4), + BitField("nbDigit4", None, 4), + BitField("nbDigit3", None, 4), + + BitField("nbDigit6", None, 4), + BitField("nbDigit5", None, 4), + BitField("nbDigit8", None, 4), + BitField("nbDigit7", None, 4), + + BitField("nbDigit10", None, 4), + BitField("nbDigit9", None, 4), + BitField("nbDigit12", None, 4), + BitField("nbDigit11", None, 4), + + BitField("nbDigit14", None, 4), + BitField("nbDigit13", None, 4), + BitField("nbDigit16", None, 4), + BitField("nbDigit15", None, 4), + + BitField("nbDigit18", None, 4), + BitField("nbDigit17", None, 4), + BitField("nbDigit20", None, 4), + BitField("nbDigit19", None, 4), + + BitField("nbDigit22", None, 4), + BitField("nbDigit21", None, 4), + BitField("nbDigit24", None, 4), + BitField("nbDigit23", None, 4), + + BitField("nbDigit26", None, 4), + BitField("nbDigit25", None, 4), + BitField("nbDigit28", None, 4), + BitField("nbDigit27", None, 4), + + BitField("nbDigit30", None, 4), + BitField("nbDigit29", None, 4), + BitField("nbDigit32", None, 4), + BitField("nbDigit31", None, 4), + + BitField("nbDigit34", None, 4), + BitField("nbDigit33", None, 4), + BitField("nbDigit36", None, 4), + BitField("nbDigit35", None, 4), + + BitField("nbDigit38", None, 4), + BitField("nbDigit37", None, 4), + BitField("nbDigit40", None, 4), + BitField("nbDigit39", None, 4), +# ^^^^^^ 20 first optional bytes ^^^^^^^^^^^^^^^ + BitField("nbDigit42", None, 4), + BitField("nbDigit41", None, 4), + BitField("nbDigit44", None, 4), + BitField("nbDigit43", None, 4), + + BitField("nbDigit46", None, 4), + BitField("nbDigit45", None, 4), + BitField("nbDigit48", None, 4), + BitField("nbDigit47", None, 4), + + BitField("nbDigit50", None, 4), + BitField("nbDigit49", None, 4), + BitField("nbDigit52", None, 4), + BitField("nbDigit51", None, 4), + + BitField("nbDigit54", None, 4), + BitField("nbDigit53", None, 4), + BitField("nbDigit56", None, 4), + BitField("nbDigit55", None, 4), + + BitField("nbDigit58", None, 4), + BitField("nbDigit57", None, 4), + BitField("nbDigit60", None, 4), + BitField("nbDigit59", None, 4), + + BitField("nbDigit62", None, 4), + BitField("nbDigit61", None, 4), + BitField("nbDigit64", None, 4), + BitField("nbDigit63", None, 4), + + BitField("nbDigit66", None, 4), + BitField("nbDigit65", None, 4), + BitField("nbDigit68", None, 4), + BitField("nbDigit67", None, 4), + + BitField("nbDigit70", None, 4), + BitField("nbDigit69", None, 4), + BitField("nbDigit72", None, 4), + BitField("nbDigit71", None, 4), + + BitField("nbDigit74", None, 4), + BitField("nbDigit73", None, 4), + BitField("nbDigit76", None, 4), + BitField("nbDigit75", None, 4), + + BitField("nbDigit78", None, 4), + BitField("nbDigit77", None, 4), + BitField("nbDigit80", None, 4), + BitField("nbDigit79", None, 4), + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(3, 43, a, self.fields_desc, 2) + if self.lengthCPBN is None: + p = p[:1] + struct.pack(">B", res[1]) + p[2:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +# len 2 to 23 +class CalledPartySubaddressHdr(Packet): + """Called party subaddress Section 10.5.4.8""" + name = "Called Party Subaddress" + fields_desc = [ + BitField("eightBitCPS", None, 1), + XBitField("ieiCPS", None, 7), + XByteField("lengthCPS", None), + # optional + BitField("ext", None, 1), + BitField("subAddr", None, 3), + BitField("oddEven", None, 1), + BitField("spare", None, 3), + + ByteField("subInfo0", None), + ByteField("subInfo1", None), + ByteField("subInfo2", None), + ByteField("subInfo3", None), + ByteField("subInfo4", None), + ByteField("subInfo5", None), + ByteField("subInfo6", None), + ByteField("subInfo7", None), + ByteField("subInfo8", None), + ByteField("subInfo9", None), + ByteField("subInfo10", None), + ByteField("subInfo11", None), + ByteField("subInfo12", None), + ByteField("subInfo13", None), + ByteField("subInfo14", None), + ByteField("subInfo15", None), + ByteField("subInfo16", None), + ByteField("subInfo17", None), + ByteField("subInfo18", None), + ByteField("subInfo19", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(2, 23, a, self.fields_desc) + if self.lengthCPS is None: + p = p[:1] + struct.pack(">B", res[1]) + p[2:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +# len 3 to 14 +class CallingPartyBcdNumberHdr(Packet): + """Called party subaddress Section 10.5.4.9""" + name = "Called Party Subaddress" + fields_desc = [ + BitField("eightBitCPBN", None, 1), + XBitField("ieiCPBN", None, 7), + XByteField("lengthCPBN", None), + BitField("ext", 0x1, 1), + BitField("typeNb", 0x0, 3), + BitField("nbPlanId", 0x0, 4), + # optional + ConditionalField(BitField("ext1", 0x1, 1), + lambda pkt: pkt.ext == 0), + ConditionalField(BitField("presId", None, 2), + lambda pkt: pkt.ext == 0), + ConditionalField(BitField("spare", None, 3), + lambda pkt: pkt.ext == 0), + ConditionalField(BitField("screenId", 0x0, 2), + lambda pkt: pkt.ext == 0), + + BitField("nbDigit2", None, 4), + BitField("nbDigit1", None, 4), + + BitField("nbDigit4", None, 4), + BitField("nbDigit3", None, 4), + + BitField("nbDigit6", None, 4), + BitField("nbDigit5", None, 4), + + BitField("nbDigit8", None, 4), + BitField("nbDigit7", None, 4), + + BitField("nbDigit10", None, 4), + BitField("nbDigit9", None, 4), + + BitField("nbDigit12", None, 4), + BitField("nbDigit11", None, 4), + + BitField("nbDigit14", None, 4), + BitField("nbDigit13", None, 4), + + BitField("nbDigit16", None, 4), + BitField("nbDigit15", None, 4), + + BitField("nbDigit18", None, 4), + BitField("nbDigit17", None, 4), + + BitField("nbDigit20", None, 4), + BitField("nbDigit19", None, 4), + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(4, 14, a, self.fields_desc) + if res[0] is not 0: + p = p[:-res[0]] + if self.lengthCPBN is None: + p = p[:1] + struct.pack(">B", len(p)-2) + p[2:] + return p + pay + + +# len 2 to 23 +class CallingPartySubaddressHdr(Packet): + """Calling party subaddress Section 10.5.4.10""" + name = "Calling Party Subaddress" + fields_desc = [ + BitField("eightBitCPS", None, 1), + XBitField("ieiCPS", None, 7), + XByteField("lengthCPS", None), + # optional + BitField("ext1", None, 1), + BitField("typeAddr", None, 3), + BitField("oddEven", None, 1), + BitField("spare", None, 3), + + ByteField("subInfo0", None), + ByteField("subInfo1", None), + ByteField("subInfo2", None), + ByteField("subInfo3", None), + ByteField("subInfo4", None), + ByteField("subInfo5", None), + ByteField("subInfo6", None), + ByteField("subInfo7", None), + ByteField("subInfo8", None), + ByteField("subInfo9", None), + ByteField("subInfo10", None), + ByteField("subInfo11", None), + ByteField("subInfo12", None), + ByteField("subInfo13", None), + ByteField("subInfo14", None), + ByteField("subInfo15", None), + ByteField("subInfo16", None), + ByteField("subInfo17", None), + ByteField("subInfo18", None), + ByteField("subInfo19", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(2, 23, a, self.fields_desc) + if self.lengthCPS is None: + p = p[:1] + struct.pack(">B", res[1]) + p[2:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +# len 4 to 32 +class CauseHdr(Packet): + """Cause Section 10.5.4.11""" + name = "Cause" + fields_desc = [ + BitField("eightBitC", None, 1), + XBitField("ieiC", None, 7), + + XByteField("lengthC", None), + + BitField("ext", 0x1, 1), + BitField("codingStd", 0x0, 2), + BitField("spare", 0x0, 1), + BitField("location", 0x0, 4), + + ConditionalField(BitField("ext1", 0x1, 1), + lambda pkt: pkt.ext == 0), + ConditionalField(BitField("recommendation", 0x0, 7), + lambda pkt: pkt.ext == 0), + # optional + BitField("ext2", None, 1), + BitField("causeValue", None, 7), + + ByteField("diagnositc0", None), + ByteField("diagnositc1", None), + ByteField("diagnositc2", None), + ByteField("diagnositc3", None), + ByteField("diagnositc4", None), + ByteField("diagnositc5", None), + ByteField("diagnositc6", None), + ByteField("diagnositc7", None), + ByteField("diagnositc8", None), + ByteField("diagnositc9", None), + ByteField("diagnositc10", None), + ByteField("diagnositc11", None), + ByteField("diagnositc12", None), + ByteField("diagnositc13", None), + ByteField("diagnositc14", None), + ByteField("diagnositc15", None), + ByteField("diagnositc16", None), + ByteField("diagnositc17", None), + ByteField("diagnositc18", None), + ByteField("diagnositc19", None), + ByteField("diagnositc20", None), + ByteField("diagnositc21", None), + ByteField("diagnositc22", None), + ByteField("diagnositc23", None), + ByteField("diagnositc24", None), + ByteField("diagnositc25", None), + ByteField("diagnositc26", None), + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(4, 32, a, self.fields_desc) + if res[0] is not 0: + p = p[:-res[0]] + if self.lengthC is None: + p = p[:1] + struct.pack(">B", len(p)-2) + p[2:] + return p + pay + + +class ClirSuppressionHdr(Packet): + """CLIR suppression Section 10.5.4.11a""" + name = "Clir Suppression" + fields_desc = [ + BitField("eightBitCS", None, 1), + XBitField("ieiCS", None, 7), + ] + + +class ClirInvocationHdr(Packet): + """CLIR invocation Section 10.5.4.11b""" + name = "Clir Invocation" + fields_desc = [ + BitField("eightBitCI", None, 1), + XBitField("ieiCI", None, 7), + ] + + +class CongestionLevelHdr(Packet): + """Congestion level Section 10.5.4.12""" + name = "Congestion Level" + fields_desc = [ + XBitField("ieiCL", None, 4), + BitField("notDef", 0x0, 4) + ] + + +# Fix 1/2 len problem +class CongestionLevelAndSpareHalfOctets(Packet): + name = "Congestion Level and Spare Half Octets" + fields_desc = [ + BitField("ieiCL", 0x0, 4), + BitField("spareHalfOctets", 0x0, 4) + ] + + +# len 3 to 14 +class ConnectedNumberHdr(Packet): + """Connected number Section 10.5.4.13""" + name = "Connected Number" + fields_desc = [ + BitField("eightBitCN", None, 1), + XBitField("ieiCN", None, 7), + + XByteField("lengthCN", None), + + BitField("ext", 0x1, 1), + BitField("typeNb", 0x0, 3), + BitField("typePlanId", 0x0, 4), + # optional + ConditionalField(BitField("ext1", 0x1, 1), + lambda pkt: pkt.ext == 0), + ConditionalField(BitField("presId", None, 2), + lambda pkt: pkt.ext == 0), + ConditionalField(BitField("spare", None, 3), + lambda pkt: pkt.ext == 0), + ConditionalField(BitField("screenId", None, 2), + lambda pkt: pkt.ext == 0), + + BitField("nbDigit2", None, 4), + BitField("nbDigit1", None, 4), + + BitField("nbDigit4", None, 4), + BitField("nbDigit3", None, 4), + + BitField("nbDigit6", None, 4), + BitField("nbDigit5", None, 4), + + BitField("nbDigit8", None, 4), + BitField("nbDigit7", None, 4), + + BitField("nbDigit10", None, 4), + BitField("nbDigit9", None, 4), + + BitField("nbDigit12", None, 4), + BitField("nbDigit11", None, 4), + + BitField("nbDigit14", None, 4), + BitField("nbDigit13", None, 4), + + BitField("nbDigit16", None, 4), + BitField("nbDigit15", None, 4), + + BitField("nbDigit18", None, 4), + BitField("nbDigit17", None, 4), + + BitField("nbDigit20", None, 4), + BitField("nbDigit19", None, 4) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + sum1 = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(3, 14, a, self.fields_desc) + if res[0] is not 0: + p = p[:-res[0]] + if self.lengthCN is None: + p = p[:1] + struct.pack(">B", len(p)-2) + p[2:] + return p + pay + + +# len 2 to 23 +class ConnectedSubaddressHdr(Packet): + """Connected subaddress Section 10.5.4.14""" + name = "Connected Subaddress" + fields_desc = [ + BitField("eightBitCS", None, 1), + XBitField("ieiCS", None, 7), + + XByteField("lengthCS", None), + # optional + BitField("ext", None, 1), + BitField("typeOfSub", None, 3), + BitField("oddEven", None, 1), + BitField("spare", None, 3), + + ByteField("subInfo0", None), + ByteField("subInfo1", None), + ByteField("subInfo2", None), + ByteField("subInfo3", None), + ByteField("subInfo4", None), + ByteField("subInfo5", None), + ByteField("subInfo6", None), + ByteField("subInfo7", None), + ByteField("subInfo8", None), + ByteField("subInfo9", None), + ByteField("subInfo10", None), + ByteField("subInfo11", None), + ByteField("subInfo12", None), + ByteField("subInfo13", None), + ByteField("subInfo14", None), + ByteField("subInfo15", None), + ByteField("subInfo16", None), + ByteField("subInfo17", None), + ByteField("subInfo18", None), + ByteField("subInfo19", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(2, 23, a, self.fields_desc) + if self.lengthCS is None: + p = p[:1] + struct.pack(">B", res[1]) + p[2:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +# len 2 to L3 (251) (done) +class FacilityHdr(Packet): + """Facility Section 10.5.4.15""" + name = "Facility" + fields_desc = [ + BitField("eightBitF", None, 1), + XBitField("ieiF", None, 7), + XByteField("lengthF", None), + # optional + ByteField("facilityInfo1", None), + ByteField("facilityInfo2", None), + ByteField("facilityInfo3", None), + ByteField("facilityInfo4", None), + ByteField("facilityInfo5", None), + ByteField("facilityInfo6", None), + ByteField("facilityInfo7", None), + ByteField("facilityInfo8", None), + ByteField("facilityInfo9", None), + ByteField("facilityInfo10", None), + ByteField("facilityInfo11", None), + ByteField("facilityInfo12", None), + ByteField("facilityInfo13", None), + ByteField("facilityInfo14", None), + ByteField("facilityInfo15", None), + ByteField("facilityInfo16", None), + ByteField("facilityInfo17", None), + ByteField("facilityInfo18", None), + ByteField("facilityInfo19", None), + ByteField("facilityInfo20", None), + ByteField("facilityInfo21", None), + ByteField("facilityInfo22", None), + ByteField("facilityInfo23", None), + ByteField("facilityInfo24", None), + ByteField("facilityInfo25", None), + ByteField("facilityInfo26", None), + ByteField("facilityInfo27", None), + ByteField("facilityInfo28", None), + ByteField("facilityInfo29", None), + ByteField("facilityInfo30", None), + ByteField("facilityInfo31", None), + ByteField("facilityInfo32", None), + ByteField("facilityInfo33", None), + ByteField("facilityInfo34", None), + ByteField("facilityInfo35", None), + ByteField("facilityInfo36", None), + ByteField("facilityInfo37", None), + ByteField("facilityInfo38", None), + ByteField("facilityInfo39", None), + ByteField("facilityInfo40", None), + ByteField("facilityInfo41", None), + ByteField("facilityInfo42", None), + ByteField("facilityInfo43", None), + ByteField("facilityInfo44", None), + ByteField("facilityInfo45", None), + ByteField("facilityInfo46", None), + ByteField("facilityInfo47", None), + ByteField("facilityInfo48", None), + ByteField("facilityInfo49", None), + ByteField("facilityInfo50", None), + ByteField("facilityInfo51", None), + ByteField("facilityInfo52", None), + ByteField("facilityInfo53", None), + ByteField("facilityInfo54", None), + ByteField("facilityInfo55", None), + ByteField("facilityInfo56", None), + ByteField("facilityInfo57", None), + ByteField("facilityInfo58", None), + ByteField("facilityInfo59", None), + ByteField("facilityInfo60", None), + ByteField("facilityInfo61", None), + ByteField("facilityInfo62", None), + ByteField("facilityInfo63", None), + ByteField("facilityInfo64", None), + ByteField("facilityInfo65", None), + ByteField("facilityInfo66", None), + ByteField("facilityInfo67", None), + ByteField("facilityInfo68", None), + ByteField("facilityInfo69", None), + ByteField("facilityInfo70", None), + ByteField("facilityInfo71", None), + ByteField("facilityInfo72", None), + ByteField("facilityInfo73", None), + ByteField("facilityInfo74", None), + ByteField("facilityInfo75", None), + ByteField("facilityInfo76", None), + ByteField("facilityInfo77", None), + ByteField("facilityInfo78", None), + ByteField("facilityInfo79", None), + ByteField("facilityInfo80", None), + ByteField("facilityInfo81", None), + ByteField("facilityInfo82", None), + ByteField("facilityInfo83", None), + ByteField("facilityInfo84", None), + ByteField("facilityInfo85", None), + ByteField("facilityInfo86", None), + ByteField("facilityInfo87", None), + ByteField("facilityInfo88", None), + ByteField("facilityInfo89", None), + ByteField("facilityInfo90", None), + ByteField("facilityInfo91", None), + ByteField("facilityInfo92", None), + ByteField("facilityInfo93", None), + ByteField("facilityInfo94", None), + ByteField("facilityInfo95", None), + ByteField("facilityInfo96", None), + ByteField("facilityInfo97", None), + ByteField("facilityInfo98", None), + ByteField("facilityInfo99", None), + ByteField("facilityInfo100", None), + ByteField("facilityInfo101", None), + ByteField("facilityInfo102", None), + ByteField("facilityInfo103", None), + ByteField("facilityInfo104", None), + ByteField("facilityInfo105", None), + ByteField("facilityInfo106", None), + ByteField("facilityInfo107", None), + ByteField("facilityInfo108", None), + ByteField("facilityInfo109", None), + ByteField("facilityInfo110", None), + ByteField("facilityInfo111", None), + ByteField("facilityInfo112", None), + ByteField("facilityInfo113", None), + ByteField("facilityInfo114", None), + ByteField("facilityInfo115", None), + ByteField("facilityInfo116", None), + ByteField("facilityInfo117", None), + ByteField("facilityInfo118", None), + ByteField("facilityInfo119", None), + ByteField("facilityInfo120", None), + ByteField("facilityInfo121", None), + ByteField("facilityInfo122", None), + ByteField("facilityInfo123", None), + ByteField("facilityInfo124", None), + ByteField("facilityInfo125", None), + ByteField("facilityInfo126", None), + ByteField("facilityInfo127", None), + ByteField("facilityInfo128", None), + ByteField("facilityInfo129", None), + ByteField("facilityInfo130", None), + ByteField("facilityInfo131", None), + ByteField("facilityInfo132", None), + ByteField("facilityInfo133", None), + ByteField("facilityInfo134", None), + ByteField("facilityInfo135", None), + ByteField("facilityInfo136", None), + ByteField("facilityInfo137", None), + ByteField("facilityInfo138", None), + ByteField("facilityInfo139", None), + ByteField("facilityInfo140", None), + ByteField("facilityInfo141", None), + ByteField("facilityInfo142", None), + ByteField("facilityInfo143", None), + ByteField("facilityInfo144", None), + ByteField("facilityInfo145", None), + ByteField("facilityInfo146", None), + ByteField("facilityInfo147", None), + ByteField("facilityInfo148", None), + ByteField("facilityInfo149", None), + ByteField("facilityInfo150", None), + ByteField("facilityInfo151", None), + ByteField("facilityInfo152", None), + ByteField("facilityInfo153", None), + ByteField("facilityInfo154", None), + ByteField("facilityInfo155", None), + ByteField("facilityInfo156", None), + ByteField("facilityInfo157", None), + ByteField("facilityInfo158", None), + ByteField("facilityInfo159", None), + ByteField("facilityInfo160", None), + ByteField("facilityInfo161", None), + ByteField("facilityInfo162", None), + ByteField("facilityInfo163", None), + ByteField("facilityInfo164", None), + ByteField("facilityInfo165", None), + ByteField("facilityInfo166", None), + ByteField("facilityInfo167", None), + ByteField("facilityInfo168", None), + ByteField("facilityInfo169", None), + ByteField("facilityInfo170", None), + ByteField("facilityInfo171", None), + ByteField("facilityInfo172", None), + ByteField("facilityInfo173", None), + ByteField("facilityInfo174", None), + ByteField("facilityInfo175", None), + ByteField("facilityInfo176", None), + ByteField("facilityInfo177", None), + ByteField("facilityInfo178", None), + ByteField("facilityInfo179", None), + ByteField("facilityInfo180", None), + ByteField("facilityInfo181", None), + ByteField("facilityInfo182", None), + ByteField("facilityInfo183", None), + ByteField("facilityInfo184", None), + ByteField("facilityInfo185", None), + ByteField("facilityInfo186", None), + ByteField("facilityInfo187", None), + ByteField("facilityInfo188", None), + ByteField("facilityInfo189", None), + ByteField("facilityInfo190", None), + ByteField("facilityInfo191", None), + ByteField("facilityInfo192", None), + ByteField("facilityInfo193", None), + ByteField("facilityInfo194", None), + ByteField("facilityInfo195", None), + ByteField("facilityInfo196", None), + ByteField("facilityInfo197", None), + ByteField("facilityInfo198", None), + ByteField("facilityInfo199", None), + ByteField("facilityInfo200", None), + ByteField("facilityInfo201", None), + ByteField("facilityInfo202", None), + ByteField("facilityInfo203", None), + ByteField("facilityInfo204", None), + ByteField("facilityInfo205", None), + ByteField("facilityInfo206", None), + ByteField("facilityInfo207", None), + ByteField("facilityInfo208", None), + ByteField("facilityInfo209", None), + ByteField("facilityInfo210", None), + ByteField("facilityInfo211", None), + ByteField("facilityInfo212", None), + ByteField("facilityInfo213", None), + ByteField("facilityInfo214", None), + ByteField("facilityInfo215", None), + ByteField("facilityInfo216", None), + ByteField("facilityInfo217", None), + ByteField("facilityInfo218", None), + ByteField("facilityInfo219", None), + ByteField("facilityInfo220", None), + ByteField("facilityInfo221", None), + ByteField("facilityInfo222", None), + ByteField("facilityInfo223", None), + ByteField("facilityInfo224", None), + ByteField("facilityInfo225", None), + ByteField("facilityInfo226", None), + ByteField("facilityInfo227", None), + ByteField("facilityInfo228", None), + ByteField("facilityInfo229", None), + ByteField("facilityInfo230", None), + ByteField("facilityInfo231", None), + ByteField("facilityInfo232", None), + ByteField("facilityInfo233", None), + ByteField("facilityInfo234", None), + ByteField("facilityInfo235", None), + ByteField("facilityInfo236", None), + ByteField("facilityInfo237", None), + ByteField("facilityInfo238", None), + ByteField("facilityInfo239", None), + ByteField("facilityInfo240", None), + ByteField("facilityInfo241", None), + ByteField("facilityInfo242", None), + ByteField("facilityInfo243", None), + ByteField("facilityInfo244", None), + ByteField("facilityInfo245", None), + ByteField("facilityInfo246", None), + ByteField("facilityInfo247", None), + ByteField("facilityInfo248", None), + ByteField("facilityInfo249", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(2, 251, a, self.fields_desc) + if self.lengthF is None: + p = p[:1] + struct.pack(">B", res[1]) + p[2:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +#len 2 to 5 +class HighLayerCompatibilityHdr(Packet): + """High layer compatibility Section 10.5.4.16""" + name = "High Layer Compatibility" + fields_desc = [ + BitField("eightBitHLC", None, 1), + XBitField("ieiHLC", None, 7), + + XByteField("lengthHLC", None), + # optional + BitField("ext", None, 1), + BitField("codingStd", None, 2), + BitField("interpret", None, 3), + BitField("presMeth", None, 2), + + BitField("ext1", None, 1), + BitField("highLayerId", None, 7), + + ConditionalField(BitField("ext2", 0x1, 1), + lambda pkt: pkt.ext1 == 0), + ConditionalField(BitField("exHiLayerId", 0x0, 7), + lambda pkt: pkt.ext1 == 0) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(2, 5, a, self.fields_desc) + if res[0] is not 0: + p = p[:-res[0]] + if self.lengthHLC is None: + p = p[:1] + struct.pack(">B", len(p)-2) + p[2:] + return p + pay +# +# 10.5.4.16.1 Static conditions for the high layer +# compatibility IE contents +# + + +class KeypadFacilityHdr(Packet): + """Keypad facility Section 10.5.4.17""" + name = "Keypad Facility" + fields_desc = [ + BitField("eightBitKF", None, 1), + XBitField("ieiKF", None, 7), + BitField("spare", 0x0, 1), + BitField("keyPadInfo", 0x0, 7) + ] + + +# len 2 to 15 +class LowLayerCompatibilityHdr(Packet): + """Low layer compatibility Section 10.5.4.18""" + name = "Low Layer Compatibility" + fields_desc = [ + BitField("eightBitLLC", None, 1), + XBitField("ieiLLC", None, 7), + + XByteField("lengthLLC", None), + # optional + ByteField("rest0", None), + ByteField("rest1", None), + ByteField("rest2", None), + ByteField("rest3", None), + ByteField("rest4", None), + ByteField("rest5", None), + ByteField("rest6", None), + ByteField("rest7", None), + ByteField("rest8", None), + ByteField("rest9", None), + ByteField("rest10", None), + ByteField("rest11", None), + ByteField("rest12", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(2, 15, a, self.fields_desc) + if self.lengthLLC is None: + p = p[:1] + struct.pack(">B", res[1]) + p[2:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +class MoreDataHdr(Packet): + """More data Section 10.5.4.19""" + name = "More Data" + fields_desc = [ + BitField("eightBitMD", None, 1), + XBitField("ieiMD", None, 7), + ] + + +class NotificationIndicatorHdr(Packet): + """Notification indicator Section 10.5.4.20""" + name = "Notification Indicator" + fields_desc = [ + BitField("eightBitNI", None, 1), + XBitField("ieiNI", None, 7), + BitField("ext", 0x1, 1), + BitField("notifDesc", 0x0, 7) + ] + + +class ProgressIndicatorHdr(Packet): + """Progress indicator Section 10.5.4.21""" + name = "Progress Indicator" + fields_desc = [ + BitField("eightBitPI", None, 1), + XBitField("ieiPI", None, 7), + XByteField("lengthPI", 0x2), + BitField("ext", 0x1, 1), + BitField("codingStd", 0x0, 2), + BitField("spare", 0x0, 1), + BitField("location", 0x0, 4), + BitField("ext1", 0x1, 1), + BitField("progressDesc", 0x0, 7) + ] + + +class RecallTypeHdr(Packet): + """Recall type $(CCBS)$ Section 10.5.4.21a""" + name = "Recall Type $(CCBS)$" + fields_desc = [ + BitField("eightBitRT", None, 1), + XBitField("ieiRT", None, 7), + BitField("spare", 0x0, 5), + BitField("recallType", 0x0, 3) + ] + + +# len 3 to 19 +class RedirectingPartyBcdNumberHdr(Packet): + """Redirecting party BCD number Section 10.5.4.21b""" + name = "Redirecting Party BCD Number" + fields_desc = [ + BitField("eightBitRPBN", None, 1), + XBitField("ieiRPBN", None, 7), + + XByteField("lengthRPBN", None), + + BitField("ext", 0x1, 1), + BitField("typeNb", 0x0, 3), + BitField("numberingPlan", 0x0, 4), + # optional + ConditionalField(BitField("ext1", 0x1, 1), + lambda pkt: pkt.ext == 0), + ConditionalField(BitField("presId", None, 2), + lambda pkt: pkt.ext == 0), + ConditionalField(BitField("spare", None, 3), + lambda pkt: pkt.ext == 0), + ConditionalField(BitField("screenId", None, 2), + lambda pkt: pkt.ext == 0), + + BitField("nbDigit2", None, 4), + BitField("nbDigit1", None, 4), + + BitField("nbDigit4", None, 4), + BitField("nbDigit3", None, 4), + + BitField("nbDigit6", None, 4), + BitField("nbDigit5", None, 4), + + BitField("nbDigit8", None, 4), + BitField("nbDigit7", None, 4), + + BitField("nbDigit10", None, 4), + BitField("nbDigit9", None, 4), + + BitField("nbDigit12", None, 4), + BitField("nbDigit11", None, 4), + + BitField("nbDigit14", None, 4), + BitField("nbDigit13", None, 4), + + BitField("nbDigit16", None, 4), + BitField("nbDigit15", None, 4), + + BitField("nbDigit18", None, 4), + BitField("nbDigit17", None, 4), + + BitField("nbDigit20", None, 4), + BitField("nbDigit19", None, 4), + + BitField("nbDigit22", None, 4), + BitField("nbDigit21", None, 4), + + BitField("nbDigit24", None, 4), + BitField("nbDigit23", None, 4), + + BitField("nbDigit26", None, 4), + BitField("nbDigit25", None, 4), + + BitField("nbDigit28", None, 4), + BitField("nbDigit27", None, 4), + + BitField("nbDigit30", None, 4), + BitField("nbDigit29", None, 4), + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(3, 19, a, self.fields_desc) + if res[0] is not 0: + p = p[:-res[0]] + if self.lengthRPBN is None: + p = p[:1] + struct.pack(">B", len(p)-2) + p[2:] + return p + pay + + +# length 2 to 23 +class RedirectingPartySubaddressHdr(Packet): + """Redirecting party subaddress Section 10.5.4.21c""" + name = "Redirecting Party BCD Number" + fields_desc = [ + BitField("eightBitRPS", None, 1), + XBitField("ieiRPS", None, 7), + + XByteField("lengthRPS", None), + # optional + BitField("ext", None, 1), + BitField("typeSub", None, 3), + BitField("oddEven", None, 1), + BitField("spare", None, 3), + + ByteField("subInfo0", None), + ByteField("subInfo1", None), + ByteField("subInfo2", None), + ByteField("subInfo3", None), + ByteField("subInfo4", None), + ByteField("subInfo5", None), + ByteField("subInfo6", None), + ByteField("subInfo7", None), + ByteField("subInfo8", None), + ByteField("subInfo9", None), + ByteField("subInfo10", None), + ByteField("subInfo11", None), + ByteField("subInfo12", None), + ByteField("subInfo13", None), + ByteField("subInfo14", None), + ByteField("subInfo15", None), + ByteField("subInfo16", None), + ByteField("subInfo17", None), + ByteField("subInfo18", None), + ByteField("subInfo19", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(2, 23, a, self.fields_desc) + if self.lengthRPS is None: + p = p[:1] + struct.pack(">B", res[1]) + p[2:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +class RepeatIndicatorHdr(Packet): + """Repeat indicator Section 10.5.4.22""" + name = "Repeat Indicator" + fields_desc = [ + XBitField("ieiRI", None, 4), + BitField("repeatIndic", 0x0, 4) + ] + + +class ReverseCallSetupDirectionHdr(Packet): + """Reverse call setup direction Section 10.5.4.22a""" + name = "Reverse Call Setup Direction" + fields_desc = [ + ByteField("ieiRCSD", 0x0) + ] + + +# no upper length min 2(max for L3) (251) +class SetupContainerHdr(Packet): + """SETUP Container $(CCBS)$ Section 10.5.4.22b""" + name = "Setup Container $(CCBS)$" + fields_desc = [ + BitField("eightBitSC", None, 1), + XBitField("ieiSC", None, 7), + XByteField("lengthSC", None), + # optional + ByteField("mess1", None), + ByteField("mess2", None), + ByteField("mess3", None), + ByteField("mess4", None), + ByteField("mess5", None), + ByteField("mess6", None), + ByteField("mess7", None), + ByteField("mess8", None), + ByteField("mess9", None), + ByteField("mess10", None), + ByteField("mess11", None), + ByteField("mess12", None), + ByteField("mess13", None), + ByteField("mess14", None), + ByteField("mess15", None), + ByteField("mess16", None), + ByteField("mess17", None), + ByteField("mess18", None), + ByteField("mess19", None), + ByteField("mess20", None), + ByteField("mess21", None), + ByteField("mess22", None), + ByteField("mess23", None), + ByteField("mess24", None), + ByteField("mess25", None), + ByteField("mess26", None), + ByteField("mess27", None), + ByteField("mess28", None), + ByteField("mess29", None), + ByteField("mess30", None), + ByteField("mess31", None), + ByteField("mess32", None), + ByteField("mess33", None), + ByteField("mess34", None), + ByteField("mess35", None), + ByteField("mess36", None), + ByteField("mess37", None), + ByteField("mess38", None), + ByteField("mess39", None), + ByteField("mess40", None), + ByteField("mess41", None), + ByteField("mess42", None), + ByteField("mess43", None), + ByteField("mess44", None), + ByteField("mess45", None), + ByteField("mess46", None), + ByteField("mess47", None), + ByteField("mess48", None), + ByteField("mess49", None), + ByteField("mess50", None), + ByteField("mess51", None), + ByteField("mess52", None), + ByteField("mess53", None), + ByteField("mess54", None), + ByteField("mess55", None), + ByteField("mess56", None), + ByteField("mess57", None), + ByteField("mess58", None), + ByteField("mess59", None), + ByteField("mess60", None), + ByteField("mess61", None), + ByteField("mess62", None), + ByteField("mess63", None), + ByteField("mess64", None), + ByteField("mess65", None), + ByteField("mess66", None), + ByteField("mess67", None), + ByteField("mess68", None), + ByteField("mess69", None), + ByteField("mess70", None), + ByteField("mess71", None), + ByteField("mess72", None), + ByteField("mess73", None), + ByteField("mess74", None), + ByteField("mess75", None), + ByteField("mess76", None), + ByteField("mess77", None), + ByteField("mess78", None), + ByteField("mess79", None), + ByteField("mess80", None), + ByteField("mess81", None), + ByteField("mess82", None), + ByteField("mess83", None), + ByteField("mess84", None), + ByteField("mess85", None), + ByteField("mess86", None), + ByteField("mess87", None), + ByteField("mess88", None), + ByteField("mess89", None), + ByteField("mess90", None), + ByteField("mess91", None), + ByteField("mess92", None), + ByteField("mess93", None), + ByteField("mess94", None), + ByteField("mess95", None), + ByteField("mess96", None), + ByteField("mess97", None), + ByteField("mess98", None), + ByteField("mess99", None), + ByteField("mess100", None), + ByteField("mess101", None), + ByteField("mess102", None), + ByteField("mess103", None), + ByteField("mess104", None), + ByteField("mess105", None), + ByteField("mess106", None), + ByteField("mess107", None), + ByteField("mess108", None), + ByteField("mess109", None), + ByteField("mess110", None), + ByteField("mess111", None), + ByteField("mess112", None), + ByteField("mess113", None), + ByteField("mess114", None), + ByteField("mess115", None), + ByteField("mess116", None), + ByteField("mess117", None), + ByteField("mess118", None), + ByteField("mess119", None), + ByteField("mess120", None), + ByteField("mess121", None), + ByteField("mess122", None), + ByteField("mess123", None), + ByteField("mess124", None), + ByteField("mess125", None), + ByteField("mess126", None), + ByteField("mess127", None), + ByteField("mess128", None), + ByteField("mess129", None), + ByteField("mess130", None), + ByteField("mess131", None), + ByteField("mess132", None), + ByteField("mess133", None), + ByteField("mess134", None), + ByteField("mess135", None), + ByteField("mess136", None), + ByteField("mess137", None), + ByteField("mess138", None), + ByteField("mess139", None), + ByteField("mess140", None), + ByteField("mess141", None), + ByteField("mess142", None), + ByteField("mess143", None), + ByteField("mess144", None), + ByteField("mess145", None), + ByteField("mess146", None), + ByteField("mess147", None), + ByteField("mess148", None), + ByteField("mess149", None), + ByteField("mess150", None), + ByteField("mess151", None), + ByteField("mess152", None), + ByteField("mess153", None), + ByteField("mess154", None), + ByteField("mess155", None), + ByteField("mess156", None), + ByteField("mess157", None), + ByteField("mess158", None), + ByteField("mess159", None), + ByteField("mess160", None), + ByteField("mess161", None), + ByteField("mess162", None), + ByteField("mess163", None), + ByteField("mess164", None), + ByteField("mess165", None), + ByteField("mess166", None), + ByteField("mess167", None), + ByteField("mess168", None), + ByteField("mess169", None), + ByteField("mess170", None), + ByteField("mess171", None), + ByteField("mess172", None), + ByteField("mess173", None), + ByteField("mess174", None), + ByteField("mess175", None), + ByteField("mess176", None), + ByteField("mess177", None), + ByteField("mess178", None), + ByteField("mess179", None), + ByteField("mess180", None), + ByteField("mess181", None), + ByteField("mess182", None), + ByteField("mess183", None), + ByteField("mess184", None), + ByteField("mess185", None), + ByteField("mess186", None), + ByteField("mess187", None), + ByteField("mess188", None), + ByteField("mess189", None), + ByteField("mess190", None), + ByteField("mess191", None), + ByteField("mess192", None), + ByteField("mess193", None), + ByteField("mess194", None), + ByteField("mess195", None), + ByteField("mess196", None), + ByteField("mess197", None), + ByteField("mess198", None), + ByteField("mess199", None), + ByteField("mess200", None), + ByteField("mess201", None), + ByteField("mess202", None), + ByteField("mess203", None), + ByteField("mess204", None), + ByteField("mess205", None), + ByteField("mess206", None), + ByteField("mess207", None), + ByteField("mess208", None), + ByteField("mess209", None), + ByteField("mess210", None), + ByteField("mess211", None), + ByteField("mess212", None), + ByteField("mess213", None), + ByteField("mess214", None), + ByteField("mess215", None), + ByteField("mess216", None), + ByteField("mess217", None), + ByteField("mess218", None), + ByteField("mess219", None), + ByteField("mess220", None), + ByteField("mess221", None), + ByteField("mess222", None), + ByteField("mess223", None), + ByteField("mess224", None), + ByteField("mess225", None), + ByteField("mess226", None), + ByteField("mess227", None), + ByteField("mess228", None), + ByteField("mess229", None), + ByteField("mess230", None), + ByteField("mess231", None), + ByteField("mess232", None), + ByteField("mess233", None), + ByteField("mess234", None), + ByteField("mess235", None), + ByteField("mess236", None), + ByteField("mess237", None), + ByteField("mess238", None), + ByteField("mess239", None), + ByteField("mess240", None), + ByteField("mess241", None), + ByteField("mess242", None), + ByteField("mess243", None), + ByteField("mess244", None), + ByteField("mess245", None), + ByteField("mess246", None), + ByteField("mess247", None), + ByteField("mess248", None), + ByteField("mess249", None), + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(2, 251, a, self.fields_desc) + if self.lengthSC is None: + p = p[:1] + struct.pack(">B", res[1]) + p[2:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +class SignalHdr(Packet): + """Signal Section 10.5.4.23""" + name = "Signal" + fields_desc = [ + BitField("eightBitS", None, 1), + XBitField("ieiS", None, 7), + ByteField("sigValue", 0x0) + ] + + +# length 2 to max for L3 message (251) +class SsVersionIndicatorHdr(Packet): + """SS Version Indicator Section 10.5.4.24""" + name = "SS Version Indicator" + fields_desc = [ + BitField("eightBitSVI", None, 1), + XBitField("ieiSVI", None, 7), + XByteField("lengthSVI", None), + # optional + ByteField("info1", None), + ByteField("info2", None), + ByteField("info3", None), + ByteField("info4", None), + ByteField("info5", None), + ByteField("info6", None), + ByteField("info7", None), + ByteField("info8", None), + ByteField("info9", None), + ByteField("info10", None), + ByteField("info11", None), + ByteField("info12", None), + ByteField("info13", None), + ByteField("info14", None), + ByteField("info15", None), + ByteField("info16", None), + ByteField("info17", None), + ByteField("info18", None), + ByteField("info19", None), + ByteField("info20", None), + ByteField("info21", None), + ByteField("info22", None), + ByteField("info23", None), + ByteField("info24", None), + ByteField("info25", None), + ByteField("info26", None), + ByteField("info27", None), + ByteField("info28", None), + ByteField("info29", None), + ByteField("info30", None), + ByteField("info31", None), + ByteField("info32", None), + ByteField("info33", None), + ByteField("info34", None), + ByteField("info35", None), + ByteField("info36", None), + ByteField("info37", None), + ByteField("info38", None), + ByteField("info39", None), + ByteField("info40", None), + ByteField("info41", None), + ByteField("info42", None), + ByteField("info43", None), + ByteField("info44", None), + ByteField("info45", None), + ByteField("info46", None), + ByteField("info47", None), + ByteField("info48", None), + ByteField("info49", None), + ByteField("info50", None), + ByteField("info51", None), + ByteField("info52", None), + ByteField("info53", None), + ByteField("info54", None), + ByteField("info55", None), + ByteField("info56", None), + ByteField("info57", None), + ByteField("info58", None), + ByteField("info59", None), + ByteField("info60", None), + ByteField("info61", None), + ByteField("info62", None), + ByteField("info63", None), + ByteField("info64", None), + ByteField("info65", None), + ByteField("info66", None), + ByteField("info67", None), + ByteField("info68", None), + ByteField("info69", None), + ByteField("info70", None), + ByteField("info71", None), + ByteField("info72", None), + ByteField("info73", None), + ByteField("info74", None), + ByteField("info75", None), + ByteField("info76", None), + ByteField("info77", None), + ByteField("info78", None), + ByteField("info79", None), + ByteField("info80", None), + ByteField("info81", None), + ByteField("info82", None), + ByteField("info83", None), + ByteField("info84", None), + ByteField("info85", None), + ByteField("info86", None), + ByteField("info87", None), + ByteField("info88", None), + ByteField("info89", None), + ByteField("info90", None), + ByteField("info91", None), + ByteField("info92", None), + ByteField("info93", None), + ByteField("info94", None), + ByteField("info95", None), + ByteField("info96", None), + ByteField("info97", None), + ByteField("info98", None), + ByteField("info99", None), + ByteField("info100", None), + ByteField("info101", None), + ByteField("info102", None), + ByteField("info103", None), + ByteField("info104", None), + ByteField("info105", None), + ByteField("info106", None), + ByteField("info107", None), + ByteField("info108", None), + ByteField("info109", None), + ByteField("info110", None), + ByteField("info111", None), + ByteField("info112", None), + ByteField("info113", None), + ByteField("info114", None), + ByteField("info115", None), + ByteField("info116", None), + ByteField("info117", None), + ByteField("info118", None), + ByteField("info119", None), + ByteField("info120", None), + ByteField("info121", None), + ByteField("info122", None), + ByteField("info123", None), + ByteField("info124", None), + ByteField("info125", None), + ByteField("info126", None), + ByteField("info127", None), + ByteField("info128", None), + ByteField("info129", None), + ByteField("info130", None), + ByteField("info131", None), + ByteField("info132", None), + ByteField("info133", None), + ByteField("info134", None), + ByteField("info135", None), + ByteField("info136", None), + ByteField("info137", None), + ByteField("info138", None), + ByteField("info139", None), + ByteField("info140", None), + ByteField("info141", None), + ByteField("info142", None), + ByteField("info143", None), + ByteField("info144", None), + ByteField("info145", None), + ByteField("info146", None), + ByteField("info147", None), + ByteField("info148", None), + ByteField("info149", None), + ByteField("info150", None), + ByteField("info151", None), + ByteField("info152", None), + ByteField("info153", None), + ByteField("info154", None), + ByteField("info155", None), + ByteField("info156", None), + ByteField("info157", None), + ByteField("info158", None), + ByteField("info159", None), + ByteField("info160", None), + ByteField("info161", None), + ByteField("info162", None), + ByteField("info163", None), + ByteField("info164", None), + ByteField("info165", None), + ByteField("info166", None), + ByteField("info167", None), + ByteField("info168", None), + ByteField("info169", None), + ByteField("info170", None), + ByteField("info171", None), + ByteField("info172", None), + ByteField("info173", None), + ByteField("info174", None), + ByteField("info175", None), + ByteField("info176", None), + ByteField("info177", None), + ByteField("info178", None), + ByteField("info179", None), + ByteField("info180", None), + ByteField("info181", None), + ByteField("info182", None), + ByteField("info183", None), + ByteField("info184", None), + ByteField("info185", None), + ByteField("info186", None), + ByteField("info187", None), + ByteField("info188", None), + ByteField("info189", None), + ByteField("info190", None), + ByteField("info191", None), + ByteField("info192", None), + ByteField("info193", None), + ByteField("info194", None), + ByteField("info195", None), + ByteField("info196", None), + ByteField("info197", None), + ByteField("info198", None), + ByteField("info199", None), + ByteField("info200", None), + ByteField("info201", None), + ByteField("info202", None), + ByteField("info203", None), + ByteField("info204", None), + ByteField("info205", None), + ByteField("info206", None), + ByteField("info207", None), + ByteField("info208", None), + ByteField("info209", None), + ByteField("info210", None), + ByteField("info211", None), + ByteField("info212", None), + ByteField("info213", None), + ByteField("info214", None), + ByteField("info215", None), + ByteField("info216", None), + ByteField("info217", None), + ByteField("info218", None), + ByteField("info219", None), + ByteField("info220", None), + ByteField("info221", None), + ByteField("info222", None), + ByteField("info223", None), + ByteField("info224", None), + ByteField("info225", None), + ByteField("info226", None), + ByteField("info227", None), + ByteField("info228", None), + ByteField("info229", None), + ByteField("info230", None), + ByteField("info231", None), + ByteField("info232", None), + ByteField("info233", None), + ByteField("info234", None), + ByteField("info235", None), + ByteField("info236", None), + ByteField("info237", None), + ByteField("info238", None), + ByteField("info239", None), + ByteField("info240", None), + ByteField("info241", None), + ByteField("info242", None), + ByteField("info243", None), + ByteField("info244", None), + ByteField("info245", None), + ByteField("info246", None), + ByteField("info247", None), + ByteField("info248", None), + ByteField("info249", None), + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(2, 251, a, self.fields_desc) + if self.lengthSVI is None: + p = p[:1] + struct.pack(">B", res[1]) + p[2:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +# length 3 to 35 or 131 +class UserUserHdr(Packet): + """User-user Section 10.5.4.25""" + name = "User-User" + fields_desc = [ + BitField("eightBitUU", None, 1), + XBitField("ieiUU", None, 7), + + XByteField("lengthUU", None), # dynamic length of field depending + # of the type of message + # let user decide which length he + # wants to take + # => more fuzzing options + ByteField("userUserPD", 0x0), + # optional + ByteField("userUserInfo1", None), + ByteField("userUserInfo2", None), + ByteField("userUserInfo3", None), + ByteField("userUserInfo4", None), + ByteField("userUserInfo5", None), + ByteField("userUserInfo6", None), + ByteField("userUserInfo7", None), + ByteField("userUserInfo8", None), + ByteField("userUserInfo9", None), + ByteField("userUserInfo10", None), + ByteField("userUserInfo11", None), + ByteField("userUserInfo12", None), + ByteField("userUserInfo13", None), + ByteField("userUserInfo14", None), + ByteField("userUserInfo15", None), + ByteField("userUserInfo16", None), + ByteField("userUserInfo17", None), + ByteField("userUserInfo18", None), + ByteField("userUserInfo19", None), + ByteField("userUserInfo20", None), + ByteField("userUserInfo21", None), + ByteField("userUserInfo22", None), + ByteField("userUserInfo23", None), + ByteField("userUserInfo24", None), + ByteField("userUserInfo25", None), + ByteField("userUserInfo26", None), + ByteField("userUserInfo27", None), + ByteField("userUserInfo28", None), + ByteField("userUserInfo29", None), + ByteField("userUserInfo30", None), + ByteField("userUserInfo31", None), + ByteField("userUserInfo32", None), + # long packet + ByteField("userUserInfo33", None), + ByteField("userUserInfo34", None), + ByteField("userUserInfo35", None), + ByteField("userUserInfo36", None), + ByteField("userUserInfo37", None), + ByteField("userUserInfo38", None), + ByteField("userUserInfo39", None), + ByteField("userUserInfo40", None), + ByteField("userUserInfo41", None), + ByteField("userUserInfo42", None), + ByteField("userUserInfo43", None), + ByteField("userUserInfo44", None), + ByteField("userUserInfo45", None), + ByteField("userUserInfo46", None), + ByteField("userUserInfo47", None), + ByteField("userUserInfo48", None), + ByteField("userUserInfo49", None), + ByteField("userUserInfo50", None), + ByteField("userUserInfo51", None), + ByteField("userUserInfo52", None), + ByteField("userUserInfo53", None), + ByteField("userUserInfo54", None), + ByteField("userUserInfo55", None), + ByteField("userUserInfo56", None), + ByteField("userUserInfo57", None), + ByteField("userUserInfo58", None), + ByteField("userUserInfo59", None), + ByteField("userUserInfo60", None), + ByteField("userUserInfo61", None), + ByteField("userUserInfo62", None), + ByteField("userUserInfo63", None), + ByteField("userUserInfo64", None), + ByteField("userUserInfo65", None), + ByteField("userUserInfo66", None), + ByteField("userUserInfo67", None), + ByteField("userUserInfo68", None), + ByteField("userUserInfo69", None), + ByteField("userUserInfo70", None), + ByteField("userUserInfo71", None), + ByteField("userUserInfo72", None), + ByteField("userUserInfo73", None), + ByteField("userUserInfo74", None), + ByteField("userUserInfo75", None), + ByteField("userUserInfo76", None), + ByteField("userUserInfo77", None), + ByteField("userUserInfo78", None), + ByteField("userUserInfo79", None), + ByteField("userUserInfo80", None), + ByteField("userUserInfo81", None), + ByteField("userUserInfo82", None), + ByteField("userUserInfo83", None), + ByteField("userUserInfo84", None), + ByteField("userUserInfo85", None), + ByteField("userUserInfo86", None), + ByteField("userUserInfo87", None), + ByteField("userUserInfo88", None), + ByteField("userUserInfo89", None), + ByteField("userUserInfo90", None), + ByteField("userUserInfo91", None), + ByteField("userUserInfo92", None), + ByteField("userUserInfo93", None), + ByteField("userUserInfo94", None), + ByteField("userUserInfo95", None), + ByteField("userUserInfo96", None), + ByteField("userUserInfo97", None), + ByteField("userUserInfo98", None), + ByteField("userUserInfo99", None), + ByteField("userUserInfo100", None), + ByteField("userUserInfo101", None), + ByteField("userUserInfo102", None), + ByteField("userUserInfo103", None), + ByteField("userUserInfo104", None), + ByteField("userUserInfo105", None), + ByteField("userUserInfo106", None), + ByteField("userUserInfo107", None), + ByteField("userUserInfo108", None), + ByteField("userUserInfo109", None), + ByteField("userUserInfo110", None), + ByteField("userUserInfo111", None), + ByteField("userUserInfo112", None), + ByteField("userUserInfo113", None), + ByteField("userUserInfo114", None), + ByteField("userUserInfo115", None), + ByteField("userUserInfo116", None), + ByteField("userUserInfo117", None), + ByteField("userUserInfo118", None), + ByteField("userUserInfo119", None), + ByteField("userUserInfo120", None), + ByteField("userUserInfo121", None), + ByteField("userUserInfo122", None), + ByteField("userUserInfo123", None), + ByteField("userUserInfo124", None), + ByteField("userUserInfo125", None), + ByteField("userUserInfo126", None), + ByteField("userUserInfo127", None), + ByteField("userUserInfo128", None), + ByteField("userUserInfo129", None), + ByteField("userUserInfo130", None), + ByteField("userUserInfo131", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(3, 131, a, self.fields_desc) + if self.lengthUU is None: + p = p[:1] + struct.pack(">B", res[1]) + p[2:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +class AlertingPatternHdr(Packet): + """Alerting Pattern 10.5.4.26""" + name = "Alerting Pattern" + fields_desc = [ + BitField("eightBitAP", None, 1), + XBitField("ieiAP", None, 7), + XByteField("lengthAP", 0x3), + BitField("spare", 0x0, 4), + BitField("alertingValue", 0x0, 4) + ] + + +class AllowedActionsHdr(Packet): + """Allowed actions $(CCBS)$ Section 10.5.4.26""" + name = "Allowed Actions $(CCBS)$" + fields_desc = [ + BitField("eightBitAA", None, 1), + XBitField("ieiAA", None, 7), + XByteField("lengthAP", 0x3), + BitField("CCBS", 0x0, 1), + BitField("spare", 0x0, 7) + ] + + +# +# 10.5.5 GPRS mobility management information elements +# + +class AttachResult(Packet): + """Attach result Section 10.5.5.1""" + name = "Attach Result" + fields_desc = [ + XBitField("ieiAR", 0x0, 4), + BitField("spare", 0x0, 1), + BitField("result", 0x1, 3) + ] + + +class AttachTypeHdr(Packet): + """Attach type Section 10.5.5.2""" + name = "Attach Type" + fields_desc = [ + XBitField("ieiAT", None, 4), + BitField("spare", 0x0, 1), + BitField("type", 0x1, 3) + ] + + +# Fix 1/2 len problem +class AttachTypeAndCiphKeySeqNr(Packet): + name = "Attach Type and Cipher Key Sequence Number" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("type", 0x1, 3), + BitField("spareHalfOctets", 0x0, 4) + ] + + +class CipheringAlgorithm(Packet): + """Ciphering algorithm Section 10.5.5.3""" + name = "Ciphering Algorithm" + fields_desc = [ + XBitField("ieiCA", 0x0, 4), + BitField("spare", 0x0, 1), + BitField("type", 0x1, 3) + ] + + +# Fix 1/2 len problem +class CipheringAlgorithmAndImeisvRequest(Packet): + name = "Ciphering Algorithm and Imeisv Request" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("type", 0x1, 3), + BitField("spare", 0x0, 1), + BitField("imeisvVal", 0x0, 3) + ] + + +# [Spare] +class TmsiStatus(Packet): + """[Spare] TMSI status Section 10.5.5.4""" + name = "[Spare] TMSI Status" + fields_desc = [ + XBitField("ieiTS", None, 4), + BitField("spare", 0x0, 3), + BitField("flag", 0x1, 1) + ] + + +class DetachType(Packet): + """Detach type Section 10.5.5.5""" + name = "Detach Type" + fields_desc = [ + XBitField("ieiDT", 0x0, 4), + BitField("poweroff", 0x0, 1), + BitField("type", 0x1, 3) + ] + + +# Fix 1/2 len problem +class DetachTypeAndForceToStandby(Packet): + name = "Detach Type and Force To Standby" + fields_desc = [ + BitField("poweroff", 0x0, 1), + BitField("type", 0x1, 3), + BitField("spare", 0x0, 1), + BitField("forceStandby", 0x0, 3) + ] + + +# Fix 1/2 len problem +class DetachTypeAndSpareHalfOctets(Packet): + name = "Detach Type and Spare Half Octets" + fields_desc = [ + BitField("poweroff", 0x0, 1), + BitField("type", 0x1, 3), + BitField("spareHalfOctets", 0x0, 4) + ] + + +class DrxParameter(Packet): + """DRX parameter Section 10.5.5.6""" + name = "DRX Parameter" + fields_desc = [ + ByteField("ieiDP", 0x0), + ByteField("splitPG", 0x0), + BitField("spare", 0x0, 4), + BitField("splitCCCH", 0x0, 1), + BitField("NonDrxTimer", 0x1, 3) + ] + + +class ForceToStandby(Packet): + """Force to standby Section 10.5.5.7""" + name = "Force To Standby" + fields_desc = [ + XBitField("ieiFTS", 0x0, 4), + BitField("spare", 0x0, 1), + BitField("forceStandby", 0x0, 3) + ] + + +# Fix 1/2 len problem +class ForceToStandbyAndAcReferenceNumber(Packet): + name = "Force To Standby And Ac Reference Number" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("forceStandby", 0x0, 3), + BitField("acRefVal", 0x0, 4) + ] + + +# Fix 1/2 len problem +class ForceToStandbyAndUpdateResult(Packet): + name = "Force To Standby And Update Result" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("forceStandby", 0x0, 3), + BitField("spare", 0x0, 1), + BitField("updateResVal", 0x0, 3) + ] + + +# Fix 1/2 len problem +class ForceToStandbyAndSpareHalfOctets(Packet): + name = "Force To Standby And Spare Half Octets" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("forceStandby", 0x0, 3), + BitField("spareHalfOctets", 0x0, 4) + ] + + +class PTmsiSignature(Packet): + """P-TMSI signature Section 10.5.5.8""" + name = "P-TMSI Signature" + fields_desc = [ + ByteField("ieiPTS", 0x0), + BitField("sgnature", 0x0, 24) + ] + + +class IdentityType2(Packet): + """Identity type 2 Section 10.5.5.9""" + name = "Identity Type 2" + fields_desc = [ + XBitField("ieiIT2", 0x0, 4), + BitField("spare", 0x0, 1), + BitField("typeOfIdentity", 0x0, 3) + ] + + +# Fix 1/2 len problem +class IdentityType2AndforceToStandby(Packet): + name = "Identity Type 2 and Force to Standby" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("typeOfIdentity", 0x0, 3), + BitField("spare", 0x0, 1), + BitField("forceStandby", 0x0, 3) + ] + + +class ImeisvRequest(Packet): + """IMEISV request Section 10.5.5.10""" + name = "IMEISV Request" + fields_desc = [ + XBitField("ieiIR", 0x0, 4), + BitField("spare", 0x0, 1), + BitField("imeisvVal", 0x0, 3) + ] + + +# Fix 1/2 len problem +class ImeisvRequestAndForceToStandby(Packet): + name = "IMEISV Request and Force To Standby" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("imeisvVal", 0x0, 3), + BitField("spareHalfOctets", 0x0, 4) + ] + + +# length 4 to 19 +class ReceiveNpduNumbersList(Packet): + """Receive N-PDU Numbers list Section 10.5.5.11""" + name = "Receive N-PDU Numbers list" + fields_desc = [ + ByteField("ieiRNNL", 0x0), + + XByteField("lengthRNNL", None), + + BitField("nbList0", 0x0, 16), + # optional + ByteField("nbList1", None), + ByteField("nbList2", None), + ByteField("nbList3", None), + ByteField("nbList4", None), + ByteField("nbList5", None), + ByteField("nbList6", None), + ByteField("nbList7", None), + ByteField("nbList8", None), + ByteField("nbList9", None), + ByteField("nbList10", None), + ByteField("nbList11", None), + ByteField("nbList12", None), + ByteField("nbList13", None), + ByteField("nbList14", None), + ByteField("nbList15", None), + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(4, 19, a, self.fields_desc) + if self.lengthRNNL is None: + p = p[:1] + struct.pack(">B", res[1]) + p[2:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +class MsNetworkCapability(Packet): + """MS network capability Section 10.5.5.12""" + name = "MS Network Capability" + fields_desc = [ + ByteField("ieiMNC", 0x0), + XByteField("lengthMNC", 0x3), + ByteField("msNetValue", 0x0) + ] + + +# length 6 to 14 +class MsRadioAccessCapability(Packet): + """MS Radio Access capability Section 10.5.5.12a""" + name = "MS Radio Access Capability" + fields_desc = [ + ByteField("ieiMRAC", 0x24), + + XByteField("lengthMRAC", None), + + BitField("spare1", 0x0, 1), # ... + + BitField("accessCap", 0x0, 4), + BitField("accessTechType", 0x0, 4), + # access capability + BitField("bool", 0x0, 1), + BitField("lengthContent", 0x0, 7), + BitField("spare1", 0x0, 1), # ... + # content + BitField("pwrCap", 0x0, 3), + BitField("bool1", 0x0, 1), + BitField("a51", 0x0, 1), + BitField("a52", 0x0, 1), + BitField("a53", 0x0, 1), + BitField("a54", 0x0, 1), + + BitField("a55", 0x0, 1), + BitField("a56", 0x0, 1), + BitField("a57", 0x0, 1), + BitField("esInd", 0x0, 1), + BitField("ps", 0x0, 1), + BitField("vgcs", 0x0, 1), + BitField("vbs", 0x0, 1), + BitField("bool2", 0x0, 1), + # multislot + BitField("bool3", 0x0, 1), + BitField("hscsd", 0x0, 5), + + BitField("bool4", 0x0, 1), + BitField("gprs", 0x0, 5), + BitField("gprsExt", 0x0, 1), + BitField("bool5", 0x0, 1), + + BitField("smsVal", 0x0, 4), + BitField("smVal", 0x0, 4) + ] + + +# 10.5.5.13 Spare +# This is intentionally left spare. + +class GmmCause(Packet): + """GMM cause Section 10.5.5.14""" + name = "GMM Cause" + fields_desc = [ + ByteField("ieiGC", 0x0), + ByteField("causeValue", 0x0) + ] + + +class RoutingAreaIdentification(Packet): + """Routing area identification Section 10.5.5.15""" + name = "Routing Area Identification" + fields_desc = [ + ByteField("ieiRAI", 0x0), + BitField("mccDigit2", 0x0, 4), + BitField("mccDigit1", 0x0, 4), + BitField("mncDigit3", 0x0, 4), + BitField("mccDigit3", 0x0, 4), + BitField("mccDigit2", 0x0, 4), + BitField("mccDigit1", 0x0, 4), + ByteField("LAC", 0x0), + ByteField("LAC1", 0x0), + ByteField("LAC", 0x0) + ] +# 10.5.5.16 Spare +# This is intentionally left spare. + + +class UpdateResult(Packet): + """Update result Section 10.5.5.17""" + name = "Update Result" + fields_desc = [ + XBitField("ieiUR", 0x0, 4), + BitField("spare", 0x0, 1), + BitField("updateResVal", 0x0, 3) + ] + + +class UpdateType(Packet): + """Update type Section 10.5.5.18""" + name = "Update Type" + fields_desc = [ + XBitField("ieiUT", 0x0, 4), + BitField("spare", 0x0, 1), + BitField("updateTypeVal", 0x0, 3) + ] + + +# Fix 1/2 len problem +class UpdateTypeAndCiphKeySeqNr(Packet): + name = "Update Type and Cipher Key Sequence Number" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("updateTypeVal", 0x0, 3), + BitField("spare", 0x0, 1), + BitField("keySeq", 0x0, 3) + ] + + +class AcReferenceNumber(Packet): + """A&C reference number Section 10.5.5.19""" + name = "A&C Reference Number" + fields_desc = [ + XBitField("ieiARN", 0x0, 4), + BitField("acRefVal", 0x0, 4) + ] + + +# Fix 1/2 len problem +class AcReferenceNumberAndSpareHalfOctets(Packet): + name = "A&C Reference Number and Spare Half Octets" + fields_desc = [ + BitField("acRefVal", 0x0, 4), + BitField("spareHalfOctets", 0x0, 4) + ] +# +# 10.5.6 Session management information elements +# +# length 3 to 102 + + +class AccessPointName(Packet): + """Access Point Name Section 10.5.6.1""" + name = "Access Point Name" + fields_desc = [ + ByteField("ieiAPN", 0x0), + XByteField("lengthAPN", None), + ByteField("apName", 0x0), + # optional + ByteField("apName1", None), + ByteField("apName2", None), + ByteField("apName3", None), + ByteField("apName4", None), + ByteField("apName5", None), + ByteField("apName6", None), + ByteField("apName7", None), + ByteField("apName8", None), + ByteField("apName9", None), + ByteField("apName10", None), + ByteField("apName11", None), + ByteField("apName12", None), + ByteField("apName13", None), + ByteField("apName14", None), + ByteField("apName15", None), + ByteField("apName16", None), + ByteField("apName17", None), + ByteField("apName18", None), + ByteField("apName19", None), + ByteField("apName20", None), + ByteField("apName21", None), + ByteField("apName22", None), + ByteField("apName23", None), + ByteField("apName24", None), + ByteField("apName25", None), + ByteField("apName26", None), + ByteField("apName27", None), + ByteField("apName28", None), + ByteField("apName29", None), + ByteField("apName30", None), + ByteField("apName31", None), + ByteField("apName32", None), + ByteField("apName33", None), + ByteField("apName34", None), + ByteField("apName35", None), + ByteField("apName36", None), + ByteField("apName37", None), + ByteField("apName38", None), + ByteField("apName39", None), + ByteField("apName40", None), + ByteField("apName41", None), + ByteField("apName42", None), + ByteField("apName43", None), + ByteField("apName44", None), + ByteField("apName45", None), + ByteField("apName46", None), + ByteField("apName47", None), + ByteField("apName48", None), + ByteField("apName49", None), + ByteField("apName50", None), + ByteField("apName51", None), + ByteField("apName52", None), + ByteField("apName53", None), + ByteField("apName54", None), + ByteField("apName55", None), + ByteField("apName56", None), + ByteField("apName57", None), + ByteField("apName58", None), + ByteField("apName59", None), + ByteField("apName60", None), + ByteField("apName61", None), + ByteField("apName62", None), + ByteField("apName63", None), + ByteField("apName64", None), + ByteField("apName65", None), + ByteField("apName66", None), + ByteField("apName67", None), + ByteField("apName68", None), + ByteField("apName69", None), + ByteField("apName70", None), + ByteField("apName71", None), + ByteField("apName72", None), + ByteField("apName73", None), + ByteField("apName74", None), + ByteField("apName75", None), + ByteField("apName76", None), + ByteField("apName77", None), + ByteField("apName78", None), + ByteField("apName79", None), + ByteField("apName80", None), + ByteField("apName81", None), + ByteField("apName82", None), + ByteField("apName83", None), + ByteField("apName84", None), + ByteField("apName85", None), + ByteField("apName86", None), + ByteField("apName87", None), + ByteField("apName88", None), + ByteField("apName89", None), + ByteField("apName90", None), + ByteField("apName91", None), + ByteField("apName92", None), + ByteField("apName93", None), + ByteField("apName94", None), + ByteField("apName95", None), + ByteField("apName96", None), + ByteField("apName97", None), + ByteField("apName98", None), + ByteField("apName99", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(3, 102, a, self.fields_desc) + if self.lengthAPN is None: + p = p[:1] + struct.pack(">B", res[1]) + p[2:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +class NetworkServiceAccessPointIdentifier(Packet): + """Network service access point identifier Section 10.5.6.2""" + name = "Network Service Access Point Identifier" + fields_desc = [ + ByteField("ieiNSAPI", 0x0), + BitField("spare", 0x0, 4), + BitField("nsapiVal", 0x0, 4) + ] + + +# length 2 to 253 +class ProtocolConfigurationOptions(Packet): + """Protocol configuration options Section 10.5.6.3""" + name = "Protocol Configuration Options" + fields_desc = [ + ByteField("ieiPCO", 0x0), + + XByteField("lengthPCO", None), + # optional + BitField("ext", None, 1), + BitField("spare", None, 4), + BitField("configProto", None, 3), + + ByteField("protoId1", None), + ByteField("lenProto1", None), + ByteField("proto1Content", None), + + ByteField("protoId2", None), + ByteField("lenProto2", None), + ByteField("proto2Content", None), + + ByteField("protoId3", None), + ByteField("lenProto3", None), + ByteField("proto3Content", None), + + ByteField("protoId4", None), + ByteField("lenProto4", None), + ByteField("proto4Content", None), + + + ByteField("protoId5", None), + ByteField("lenProto5", None), + ByteField("proto5Content", None), + + ByteField("protoId6", None), + ByteField("lenProto6", None), + ByteField("proto6Content", None), + + ByteField("protoId7", None), + ByteField("lenProto7", None), + ByteField("proto7Content", None), + + ByteField("protoId8", None), + ByteField("lenProto8", None), + ByteField("proto8Content", None), + + ByteField("protoId9", None), + ByteField("lenProto9", None), + ByteField("proto9Content", None), + + ByteField("protoId10", None), + ByteField("lenProto10", None), + ByteField("proto10Content", None), + + ByteField("protoId11", None), + ByteField("lenProto11", None), + ByteField("proto11Content", None), + + ByteField("protoId12", None), + ByteField("lenProto12", None), + ByteField("proto12Content", None), + + ByteField("protoId13", None), + ByteField("lenProto13", None), + ByteField("proto13Content", None), + + ByteField("protoId14", None), + ByteField("lenProto14", None), + ByteField("proto14Content", None), + + ByteField("protoId15", None), + ByteField("lenProto15", None), + ByteField("proto15Content", None), + + ByteField("protoId16", None), + ByteField("lenProto16", None), + ByteField("proto16Content", None), + + ByteField("protoId17", None), + ByteField("lenProto17", None), + ByteField("proto17Content", None), + + ByteField("protoId18", None), + ByteField("lenProto18", None), + ByteField("proto18Content", None), + + ByteField("protoId19", None), + ByteField("lenProto19", None), + ByteField("proto19Content", None), + + ByteField("protoId20", None), + ByteField("lenProto20", None), + ByteField("proto20Content", None), + + ByteField("protoId21", None), + ByteField("lenProto21", None), + ByteField("proto21Content", None), + + ByteField("protoId22", None), + ByteField("lenProto22", None), + ByteField("proto22Content", None), + + ByteField("protoId23", None), + ByteField("lenProto23", None), + ByteField("proto23Content", None), + + ByteField("protoId24", None), + ByteField("lenProto24", None), + ByteField("proto24Content", None), + + ByteField("protoId25", None), + ByteField("lenProto25", None), + ByteField("proto25Content", None), + + ByteField("protoId26", None), + ByteField("lenProto26", None), + ByteField("proto26Content", None), + + ByteField("protoId27", None), + ByteField("lenProto27", None), + ByteField("proto27Content", None), + + ByteField("protoId28", None), + ByteField("lenProto28", None), + ByteField("proto28Content", None), + + ByteField("protoId29", None), + ByteField("lenProto29", None), + ByteField("proto29Content", None), + + ByteField("protoId30", None), + ByteField("lenProto30", None), + ByteField("proto30Content", None), + + ByteField("protoId31", None), + ByteField("lenProto31", None), + ByteField("proto31Content", None), + + ByteField("protoId32", None), + ByteField("lenProto32", None), + ByteField("proto32Content", None), + + ByteField("protoId33", None), + ByteField("lenProto33", None), + ByteField("proto33Content", None), + + ByteField("protoId34", None), + ByteField("lenProto34", None), + ByteField("proto34Content", None), + + ByteField("protoId35", None), + ByteField("lenProto35", None), + ByteField("proto35Content", None), + + ByteField("protoId36", None), + ByteField("lenProto36", None), + ByteField("proto36Content", None), + + ByteField("protoId37", None), + ByteField("lenProto37", None), + ByteField("proto37Content", None), + + ByteField("protoId38", None), + ByteField("lenProto38", None), + ByteField("proto38Content", None), + + ByteField("protoId39", None), + ByteField("lenProto39", None), + ByteField("proto39Content", None), + + ByteField("protoId40", None), + ByteField("lenProto40", None), + ByteField("proto40Content", None), + + ByteField("protoId41", None), + ByteField("lenProto41", None), + ByteField("proto41Content", None), + + ByteField("protoId42", None), + ByteField("lenProto42", None), + ByteField("proto42Content", None), + + ByteField("protoId43", None), + ByteField("lenProto43", None), + ByteField("proto43Content", None), + + ByteField("protoId44", None), + ByteField("lenProto44", None), + ByteField("proto44Content", None), + + ByteField("protoId45", None), + ByteField("lenProto45", None), + ByteField("proto45Content", None), + + ByteField("protoId46", None), + ByteField("lenProto46", None), + ByteField("proto46Content", None), + + ByteField("protoId47", None), + ByteField("lenProto47", None), + ByteField("proto47Content", None), + + ByteField("protoId48", None), + ByteField("lenProto48", None), + ByteField("proto48Content", None), + + ByteField("protoId49", None), + ByteField("lenProto49", None), + ByteField("proto49Content", None), + + ByteField("protoId50", None), + ByteField("lenProto50", None), + ByteField("proto50Content", None), + + ByteField("protoId51", None), + ByteField("lenProto51", None), + ByteField("proto51Content", None), + + ByteField("protoId52", None), + ByteField("lenProto52", None), + ByteField("proto52Content", None), + + ByteField("protoId53", None), + ByteField("lenProto53", None), + ByteField("proto53Content", None), + + ByteField("protoId54", None), + ByteField("lenProto54", None), + ByteField("proto54Content", None), + + ByteField("protoId55", None), + ByteField("lenProto55", None), + ByteField("proto55Content", None), + + ByteField("protoId56", None), + ByteField("lenProto56", None), + ByteField("proto56Content", None), + + ByteField("protoId57", None), + ByteField("lenProto57", None), + ByteField("proto57Content", None), + + ByteField("protoId58", None), + ByteField("lenProto58", None), + ByteField("proto58Content", None), + + ByteField("protoId59", None), + ByteField("lenProto59", None), + ByteField("proto59Content", None), + + ByteField("protoId60", None), + ByteField("lenProto60", None), + ByteField("proto60Content", None), + + ByteField("protoId61", None), + ByteField("lenProto61", None), + ByteField("proto61Content", None), + + ByteField("protoId62", None), + ByteField("lenProto62", None), + ByteField("proto62Content", None), + + ByteField("protoId63", None), + ByteField("lenProto63", None), + ByteField("proto63Content", None), + + ByteField("protoId64", None), + ByteField("lenProto64", None), + ByteField("proto64Content", None), + + ByteField("protoId65", None), + ByteField("lenProto65", None), + ByteField("proto65Content", None), + + ByteField("protoId66", None), + ByteField("lenProto66", None), + ByteField("proto66Content", None), + + ByteField("protoId67", None), + ByteField("lenProto67", None), + ByteField("proto67Content", None), + + ByteField("protoId68", None), + ByteField("lenProto68", None), + ByteField("proto68Content", None), + + ByteField("protoId69", None), + ByteField("lenProto69", None), + ByteField("proto69Content", None), + + ByteField("protoId70", None), + ByteField("lenProto70", None), + ByteField("proto70Content", None), + + ByteField("protoId71", None), + ByteField("lenProto71", None), + ByteField("proto71Content", None), + + ByteField("protoId72", None), + ByteField("lenProto72", None), + ByteField("proto72Content", None), + + ByteField("protoId73", None), + ByteField("lenProto73", None), + ByteField("proto73Content", None), + + ByteField("protoId74", None), + ByteField("lenProto74", None), + ByteField("proto74Content", None), + + ByteField("protoId75", None), + ByteField("lenProto75", None), + ByteField("proto75Content", None), + + ByteField("protoId76", None), + ByteField("lenProto76", None), + ByteField("proto76Content", None), + + ByteField("protoId77", None), + ByteField("lenProto77", None), + ByteField("proto77Content", None), + + ByteField("protoId78", None), + ByteField("lenProto78", None), + ByteField("proto78Content", None), + + ByteField("protoId79", None), + ByteField("lenProto79", None), + ByteField("proto79Content", None), + + ByteField("protoId80", None), + ByteField("lenProto80", None), + ByteField("proto80Content", None), + + ByteField("protoId81", None), + ByteField("lenProto81", None), + ByteField("proto81Content", None), + + ByteField("protoId82", None), + ByteField("lenProto82", None), + ByteField("proto82Content", None), + + ByteField("protoId83", None), + ByteField("lenProto83", None), + ByteField("proto83Content", None), + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(2, 253, a, self.fields_desc) + if self.lengthPCO is None: + p = p[:1] + struct.pack(">B", res[1]) + p[2:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +# len 4 to 20 +class PacketDataProtocolAddress(Packet): + """Packet data protocol address Section 10.5.6.4""" + name = "Packet Data Protocol Address" + fields_desc = [ + ByteField("ieiPDPA", 0x0), + + XByteField("lengthPDPA", None), + + BitField("spare", 0x0, 4), + BitField("pdpTypeOrga", 0x0, 4), + + ByteField("pdpTypeNb", 0x0), + # optional + ByteField("addressInfo1", None), + ByteField("addressInfo2", None), + ByteField("addressInfo3", None), + ByteField("addressInfo4", None), + ByteField("addressInfo5", None), + ByteField("addressInfo6", None), + ByteField("addressInfo7", None), + ByteField("addressInfo8", None), + ByteField("addressInfo9", None), + ByteField("addressInfo10", None), + ByteField("addressInfo11", None), + ByteField("addressInfo12", None), + ByteField("addressInfo13", None), + ByteField("addressInfo14", None), + ByteField("addressInfo15", None), + ByteField("addressInfo16", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(4, 20, a, self.fields_desc) + if self.lengthPDPA is None: + p = p[:1] + struct.pack(">B", res[1]) + p[2:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +class QualityOfService(Packet): + """Quality of service Section 10.5.6.5""" + name = "Quality of Service" + fields_desc = [ + ByteField("ieiQOS", 0x0), + XByteField("lengthQOS", 0x5), + + BitField("spare", 0x0, 2), + BitField("delayClass", 0x0, 3), + BitField("reliaClass", 0x0, 3), + + BitField("peak", 0x0, 4), + BitField("spare", 0x0, 1), + BitField("precedenceCl", 0x0, 3), + + BitField("spare", 0x0, 3), + BitField("mean", 0x0, 5) + ] + + +class SmCause(Packet): + """SM cause Section 10.5.6.6""" + name = "SM Cause" + fields_desc = [ + ByteField("ieiSC", 0x0), + ByteField("causeVal", 0x0) + ] + +# 10.5.6.7 Spare +# This is intentionally left spare. + + +class AaDeactivationCause(Packet): + """AA deactivation cause Section 10.5.6.8""" + name = "AA Deactivation Cause" + fields_desc = [ + XBitField("ieiADC", 0x0, 4), + BitField("spare", 0x0, 1), + BitField("aaVal", 0x0, 3) + ] + + +# Fix 1/2 len problem +class AaDeactivationCauseAndSpareHalfOctets(Packet): + name = "AA Deactivation Cause and Spare Half Octets" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("aaVal", 0x0, 3), + BitField("spareHalfOctets", 0x0, 4) + ] + + +class LlcServiceAccessPointIdentifier(Packet): + """LLC service access point identifier Section 10.5.6.9""" + name = "LLC Service Access Point Identifier" + fields_desc = [ + ByteField("ieiLSAPI", None), + BitField("spare", 0x0, 4), + BitField("llcVal", 0x0, 4) + ] + + +# +# 10.5.7 GPRS Common information elements +# + +# 10.5.7.1 [Spare] + +class RadioPriority(Packet): + """Radio priority Section 10.5.7.2""" + name = "Radio Priority" + fields_desc = [ + XBitField("ieiRP", 0x0, 4), + BitField("spare", 0x1, 1), + BitField("rplv", 0x0, 3) + ] + + +# Fix 1/2 len problem +class RadioPriorityAndSpareHalfOctets(Packet): + name = "Radio Priority and Spare Half Octets" + fields_desc = [ + BitField("spare", 0x1, 1), + BitField("rplv", 0x0, 3), + BitField("spareHalfOctets", 0x0, 4) + ] + + +class GprsTimer(Packet): + """GPRS Timer Section 10.5.7.3""" + name = "GPRS Timer" + fields_desc = [ + ByteField("ieiGT", 0x0), + BitField("unit", 0x0, 3), + BitField("timerVal", 0x0, 5) + ] + + +class CellIdentity(Packet): + """ Cell identity Section 10.5.1.1 """ + name = "Cell Identity" + fields_desc = [ + ByteField("ciValue1", 0x0), + ByteField("ciValue2", 0x0) + ] + + +class CiphKeySeqNr(Packet): + """ Ciphering Key Sequence Number Section 10.5.1.2 """ + name = "Cipher Key Sequence Number" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("keySeq", 0x0, 3) + ] + + +class LocalAreaId(Packet): + """ Local Area Identification Section 10.5.1.3 """ + name = "Location Area Identification" + fields_desc = [ + BitField("mccDigit2", 0x0, 4), + BitField("mccDigit1", 0x0, 4), + BitField("mncDigit3", 0x0, 4), + BitField("mccDigit3", 0x0, 4), + BitField("mncDigit2", 0x0, 4), + BitField("mncDigit1", 0x0, 4), + ByteField("lac1", 0x0), + ByteField("lac2", 0x0) + ] +# +# The Mobile Identity is a type 4 information element with a minimum +# length of 3 octet and 11 octets length maximal. +# + + +# len 3 - 11 +class MobileId(Packet): + """ Mobile Identity Section 10.5.1.4 """ + name = "Mobile Identity" + fields_desc = [ + XByteField("lengthMI", None), + BitField("idDigit1", 0x0, 4), + BitField("oddEven", 0x0, 1), + BitField("typeOfId", 0x0, 3), + + BitField("idDigit2_1", None, 4), # optional + BitField("idDigit2", None, 4), + BitField("idDigit3_1", None, 4), + BitField("idDigit3", None, 4), + BitField("idDigit4_1", None, 4), + BitField("idDigit4", None, 4), + BitField("idDigit5_1", None, 4), + BitField("idDigit5", None, 4), + BitField("idDigit6_1", None, 4), + BitField("idDigit6", None, 4), + BitField("idDigit7_1", None, 4), + BitField("idDigit7", None, 4), + BitField("idDigit8_1", None, 4), + BitField("idDigit8", None, 4), + BitField("idDigit9_1", None, 4), + BitField("idDigit9", None, 4), + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(2, 10, a, self.fields_desc, 1) + if self.lengthMI is None: + p = struct.pack(">B", res[1]) + p[1:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +class MobileStationClassmark1(Packet): + """ Mobile Station Classmark 1 Section 10.5.1.5 """ + name = "Mobile Station Classmark 1" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("revisionLvl", 0x0, 2), + BitField("esInd", 0x0, 1), + BitField("a51", 0x0, 1), + BitField("rfPowerCap", 0x0, 3) + ] + + +class MobileStationClassmark2(Packet): + """ Mobile Station Classmark 2 Section 10.5.1.6 """ + name = "Mobile Station Classmark 2" + fields_desc = [ + XByteField("lengthMSC2", 0x3), + BitField("spare", 0x0, 1), + BitField("revisionLvl", 0x0, 2), + BitField("esInd", 0x0, 1), + BitField("a51", 0x0, 1), + BitField("rfPowerCap", 0x0, 3), + BitField("spare1", 0x0, 1), + BitField("psCap", 0x0, 1), + BitField("ssScreenInd", 0x0, 2), + BitField("smCaPabi", 0x0, 1), + BitField("vbs", 0x0, 1), + BitField("vgcs", 0x0, 1), + BitField("fc", 0x0, 1), + BitField("cm3", 0x0, 1), + BitField("spare2", 0x0, 1), + BitField("lcsvaCap", 0x0, 1), + BitField("spare3", 0x0, 1), + BitField("soLsa", 0x0, 1), + BitField("cmsp", 0x0, 1), + BitField("a53", 0x0, 1), + BitField("a52", 0x0, 1) + ] + + +class DescriptiveGroupOrBroadcastCallReference(Packet): + """ Descriptive group or broadcast call reference Section 10.5.1.9 """ + name = "Descriptive Group or Broadcast Call Reference" + fields_desc = [ + BitField("binCallRef", 0x0, 27), + BitField("sf", 0x0, 1), + BitField("fa", 0x0, 1), + BitField("callPrio", 0x0, 3), + BitField("cipherInfo", 0x0, 4), + BitField("spare1", 0x0, 1), + BitField("spare2", 0x0, 1), + BitField("spare3", 0x0, 1), + BitField("spare4", 0x0, 1) + ] + + +class PdAndSapi(Packet): + """ PD and SAPI $(CCBS)$ Section 10.5.1.10a """ + name = "PD and SAPI $(CCBS)$" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("spare1", 0x0, 1), + BitField("sapi", 0x0, 2), + BitField("pd", 0x0, 4) + ] + + +class PriorityLevel(Packet): + """ Priority Level Section 10.5.1.11 """ + name = "Priority Level" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("callPrio", 0x0, 3) + ] + +# +# Radio Resource management information elements +# + + +# len 6 to max for L3 message (251) +class BaRange(Packet): + """ BA Range Section 10.5.2.1a """ + name = "BA Range" + fields_desc = [ + + XByteField("lengthBR", None), +#error: byte format requires -128 <= number <= 127 + ByteField("nrOfRanges", 0x0), +# # rX = range X +# # L o = Lower H i = higher +# # H p = high Part Lp = low Part + ByteField("r1LoHp", 0x0), + + BitField("r1LoLp", 0x0, 3), + BitField("r1HiHp", 0x0, 5), + + BitField("r1HiLp", 0x0, 4), + BitField("r2LoHp", 0x0, 4), + # optional + BitField("r2LoLp", None, 5), + BitField("r2HiHp", None, 3), + + ByteField("r2HiLp", None), + ByteField("r3LoHp", None), + + BitField("r3LoLp", None, 5), + BitField("r3HiHp", None, 3), + + ByteField("r3HiLp", None), + ByteField("r4LoHp", None), + + BitField("r4LoLp", None, 5), + BitField("r4HiHp", None, 3), + ByteField("r4HiLp", None), + ByteField("r5LoHp", None), + + BitField("r5LoLp", None, 5), + BitField("r5HiHp", None, 3), + ByteField("r5HiLp", None), + ByteField("r6LoHp", None), + + BitField("r6LoLp", None, 5), + BitField("r6HiHp", None, 3), + ByteField("r6HiLp", None), + ByteField("r7LoHp", None), + + BitField("r7LoLp", None, 5), + BitField("r7HiHp", None, 3), + ByteField("r7HiLp", None), + ByteField("r8LoHp", None), + + BitField("r8LoLp", None, 5), + BitField("r8HiHp", None, 3), + ByteField("r8HiLp", None), + ByteField("r9LoHp", None), + + BitField("r9LoLp", None, 5), + BitField("r9HiHp", None, 3), + ByteField("r9HiLp", None), + ByteField("r10LoHp", None), + + BitField("r10LoLp", None, 5), + BitField("r10HiHp", None, 3), + ByteField("r10HiLp", None), + ByteField("r11LoHp", None), + + BitField("r11LoLp", None, 5), + BitField("r11HiHp", None, 3), + ByteField("r11HiLp", None), + ByteField("r12LoHp", None), + + BitField("r12LoLp", None, 5), + BitField("r12HiHp", None, 3), + ByteField("r12HiLp", None), + ByteField("r13LoHp", None), + + BitField("r13LoLp", None, 5), + BitField("r13HiHp", None, 3), + ByteField("r13HiLp", None), + ByteField("r14LoHp", None), + + BitField("r14LoLp", None, 5), + BitField("r14HiHp", None, 3), + ByteField("r14HiLp", None), + ByteField("r15LoHp", None), + + BitField("r15LoLp", None, 5), + BitField("r15HiHp", None, 3), + ByteField("r15HiLp", None), + ByteField("r16LoHp", None), + + BitField("r16LoLp", None, 5), + BitField("r16HiHp", None, 3), + ByteField("r16HiLp", None), + ByteField("r17LoHp", None), + + BitField("r17LoLp", None, 5), + BitField("r17HiHp", None, 3), + ByteField("r17HiLp", None), + ByteField("r18LoHp", None), + + BitField("r18LoLp", None, 5), + BitField("r18HiHp", None, 3), + ByteField("r18HiLp", None), + ByteField("r19LoHp", None), + + BitField("r19LoLp", None, 5), + BitField("r19HiHp", None, 3), + ByteField("r19HiLp", None), + ByteField("r20LoHp", None), + + BitField("r20LoLp", None, 5), + BitField("r20HiHp", None, 3), + ByteField("r20HiLp", None), + ByteField("r21LoHp", None), + + BitField("r21LoLp", None, 5), + BitField("r21HiHp", None, 3), + ByteField("r21HiLp", None), + ByteField("r22LoHp", None), + + BitField("r22LoLp", None, 5), + BitField("r22HiHp", None, 3), + ByteField("r22HiLp", None), + ByteField("r23LoHp", None), + + BitField("r23LoLp", None, 5), + BitField("r23HiHp", None, 3), + ByteField("r23HiLp", None), + ByteField("r24LoHp", None), + + BitField("r24LoLp", None, 5), + BitField("r24HiHp", None, 3), + ByteField("r24HiLp", None), + ByteField("r25LoHp", None), + + BitField("r25LoLp", None, 5), + BitField("r25HiHp", None, 3), + ByteField("r25HiLp", None), + ByteField("r26LoHp", None), + + BitField("r26LoLp", None, 5), + BitField("r26HiHp", None, 3), + ByteField("r26HiLp", None), + ByteField("r27LoHp", None), + + BitField("r27LoLp", None, 5), + BitField("r27HiHp", None, 3), + ByteField("r27HiLp", None), + ByteField("r28LoHp", None), + + BitField("r28LoLp", None, 5), + BitField("r28HiHp", None, 3), + ByteField("r28HiLp", None), + ByteField("r29LoHp", None), + + BitField("r29LoLp", None, 5), + BitField("r29HiHp", None, 3), + ByteField("r29HiLp", None), + ByteField("r30LoHp", None), + + BitField("r30LoLp", None, 5), + BitField("r30HiHp", None, 3), + ByteField("r30HiLp", None), + ByteField("r31LoHp", None), + + BitField("r31LoLp", None, 5), + BitField("r31HiHp", None, 3), + ByteField("r31HiLp", None), + ByteField("r32LoHp", None), + + BitField("r32LoLp", None, 5), + BitField("r32HiHp", None, 3), + ByteField("r32HiLp", None), + ByteField("r33LoHp", None), + + BitField("r33LoLp", None, 5), + BitField("r33HiHp", None, 3), + ByteField("r33HiLp", None), + ByteField("r34LoHp", None), + + BitField("r34LoLp", None, 5), + BitField("r34HiHp", None, 3), + ByteField("r34HiLp", None), + ByteField("r35LoHp", None), + + BitField("r35LoLp", None, 5), + BitField("r35HiHp", None, 3), + ByteField("r35HiLp", None), + ByteField("r36LoHp", None), + + BitField("r36LoLp", None, 5), + BitField("r36HiHp", None, 3), + ByteField("r36HiLp", None), + ByteField("r37LoHp", None), + + BitField("r37LoLp", None, 5), + BitField("r37HiHp", None, 3), + ByteField("r37HiLp", None), + ByteField("r38LoHp", None), + + BitField("r38LoLp", None, 5), + BitField("r38HiHp", None, 3), + ByteField("r38HiLp", None), + ByteField("r39LoHp", None), + + BitField("r39LoLp", None, 5), + BitField("r39HiHp", None, 3), + ByteField("r39HiLp", None), + ByteField("r40LoHp", None), + + BitField("r40LoLp", None, 5), + BitField("r40HiHp", None, 3), + ByteField("r40HiLp", None), + ByteField("r41LoHp", None), + + BitField("r41LoLp", None, 5), + BitField("r41HiHp", None, 3), + ByteField("r41HiLp", None), + ByteField("r42LoHp", None), + + BitField("r42LoLp", None, 5), + BitField("r42HiHp", None, 3), + ByteField("r42HiLp", None), + ByteField("r43LoHp", None), + + BitField("r43LoLp", None, 5), + BitField("r43HiHp", None, 3), + ByteField("r43HiLp", None), + ByteField("r44LoHp", None), + + BitField("r44LoLp", None, 5), + BitField("r44HiHp", None, 3), + ByteField("r44HiLp", None), + ByteField("r45LoHp", None), + + BitField("r45LoLp", None, 5), + BitField("r45HiHp", None, 3), + ByteField("r45HiLp", None), + ByteField("r46LoHp", None), + + BitField("r46LoLp", None, 5), + BitField("r46HiHp", None, 3), + ByteField("r46HiLp", None), + ByteField("r47LoHp", None), + + BitField("r47LoLp", None, 5), + BitField("r47HiHp", None, 3), + ByteField("r47HiLp", None), + ByteField("r48LoHp", None), + + BitField("r48LoLp", None, 5), + BitField("r48HiHp", None, 3), + ByteField("r48HiLp", None), + ByteField("r49LoHp", None), + + BitField("r49LoLp", None, 5), + BitField("r49HiHp", None, 3), + ByteField("r49HiLp", None), + ByteField("r50LoHp", None), + + BitField("r50LoLp", None, 5), + BitField("r50HiHp", None, 3), + ByteField("r50HiLp", None), + ByteField("r51LoHp", None), + + BitField("r51LoLp", None, 5), + BitField("r51HiHp", None, 3), + ByteField("r51HiLp", None), + ByteField("r52LoHp", None), + + BitField("r52LoLp", None, 5), + BitField("r52HiHp", None, 3), + ByteField("r52HiLp", None), + ByteField("r53LoHp", None), + + BitField("r53LoLp", None, 5), + BitField("r53HiHp", None, 3), + ByteField("r53HiLp", None), + ByteField("r54LoHp", None), + + BitField("r54LoLp", None, 5), + BitField("r54HiHp", None, 3), + ByteField("r54HiLp", None), + ByteField("r55LoHp", None), + + BitField("r55LoLp", None, 5), + BitField("r55HiHp", None, 3), + ByteField("r55HiLp", None), + ByteField("r56LoHp", None), + + BitField("r56LoLp", None, 5), + BitField("r56HiHp", None, 3), + ByteField("r56HiLp", None), + ByteField("r57LoHp", None), + + BitField("r57LoLp", None, 5), + BitField("r57HiHp", None, 3), + ByteField("r57HiLp", None), + ByteField("r58LoHp", None), + + BitField("r58LoLp", None, 5), + BitField("r58HiHp", None, 3), + ByteField("r58HiLp", None), + ByteField("r59LoHp", None), + + BitField("r59LoLp", None, 5), + BitField("r59HiHp", None, 3), + ByteField("r59HiLp", None), + ByteField("r60LoHp", None), + + BitField("r60LoLp", None, 5), + BitField("r60HiHp", None, 3), + ByteField("r60HiLp", None), + ByteField("r61LoHp", None), + + BitField("r61LoLp", None, 5), + BitField("r61HiHp", None, 3), + ByteField("r61HiLp", None), + ByteField("r62LoHp", None), + + BitField("r62LoLp", None, 5), + BitField("r62HiHp", None, 3), + ByteField("r62HiLp", None), + ByteField("r63LoHp", None), + + BitField("r63LoLp", None, 5), + BitField("r63HiHp", None, 3), + ByteField("r63HiLp", None), + ByteField("r64LoHp", None), + + BitField("r64LoLp", None, 5), + BitField("r64HiHp", None, 3), + ByteField("r64HiLp", None), + ByteField("r65LoHp", None), + + BitField("r65LoLp", None, 5), + BitField("r65HiHp", None, 3), + ByteField("r65HiLp", None), + ByteField("r66LoHp", None), + + BitField("r66LoLp", None, 5), + BitField("r66HiHp", None, 3), + ByteField("r66HiLp", None), + ByteField("r67LoHp", None), + + BitField("r67LoLp", None, 5), + BitField("r67HiHp", None, 3), + ByteField("r67HiLp", None), + ByteField("r68LoHp", None), + + BitField("r68LoLp", None, 5), + BitField("r68HiHp", None, 3), + ByteField("r68HiLp", None), + ByteField("r69LoHp", None), + + BitField("r69LoLp", None, 5), + BitField("r69HiHp", None, 3), + ByteField("r69HiLp", None), + ByteField("r70LoHp", None), + + BitField("r70LoLp", None, 5), + BitField("r70HiHp", None, 3), + ByteField("r70HiLp", None), + ByteField("r71LoHp", None), + + BitField("r71LoLp", None, 5), + BitField("r71HiHp", None, 3), + ByteField("r71HiLp", None), + ByteField("r72LoHp", None), + + BitField("r72LoLp", None, 5), + BitField("r72HiHp", None, 3), + ByteField("r72HiLp", None), + ByteField("r73LoHp", None), + + BitField("r73LoLp", None, 5), + BitField("r73HiHp", None, 3), + ByteField("r73HiLp", None), + ByteField("r74LoHp", None), + + BitField("r74LoLp", None, 5), + BitField("r74HiHp", None, 3), + ByteField("r74HiLp", None), + ByteField("r75LoHp", None), + + BitField("r75LoLp", None, 5), + BitField("r75HiHp", None, 3), + ByteField("r75HiLp", None), + ByteField("r76LoHp", None), + + BitField("r76LoLp", None, 5), + BitField("r76HiHp", None, 3), + ByteField("r76HiLp", None), + ByteField("r77LoHp", None), + + BitField("r77LoLp", None, 5), + BitField("r77HiHp", None, 3), + ByteField("r77HiLp", None), + ByteField("r78LoHp", None), + + BitField("r78LoLp", None, 5), + BitField("r78HiHp", None, 3), + ByteField("r78HiLp", None), + ByteField("r79LoHp", None), + + BitField("r79LoLp", None, 5), + BitField("r79HiHp", None, 3), + ByteField("r79HiLp", None), + ByteField("r80LoHp", None), + + BitField("r80LoLp", None, 5), + BitField("r80HiHp", None, 3), + ByteField("r80HiLp", None), + ByteField("r81LoHp", None), + + BitField("r81LoLp", None, 5), + BitField("r81HiHp", None, 3), + ByteField("r81HiLp", None), + ByteField("r82LoHp", None), + + BitField("r82LoLp", None, 5), + BitField("r82HiHp", None, 3), + ByteField("r82HiLp", None), + ByteField("r83LoHp", None), + + BitField("r83LoLp", None, 5), + BitField("r83HiHp", None, 3), + ByteField("r83HiLp", None), + ByteField("r84LoHp", None), + + BitField("r84LoLp", None, 5), + BitField("r84HiHp", None, 3), + ByteField("r84HiLp", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(5, 253, a, self.fields_desc, 1) + if self.lengthBR is None: + p = struct.pack(">B", res[1]) + p[1:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +# len 3 to max for L3 message (251) +class BaListPref(Packet): + """ BA List Pref Section 10.5.2.1c """ + name = "BA List Pref" + fields_desc = [ + XByteField("lengthBLP", None), + + BitField("fixBit", 0x0, 1), + BitField("rangeLower", 0x0, 10), + BitField("fixBit2", 0x0, 1), + BitField("rangeUpper", 0x0, 10), + BitField("baFreq", 0x0, 10), + BitField("sparePad", 0x0, 8) + ] + + +# len 17 || Have a look at the specs for the field format +# Bit map 0 format +# Range 1024 format +# Range 512 format +# Range 256 format +# Range 128 format +# Variable bit map format +class CellChannelDescription(Packet): + """ Cell Channel Description Section 10.5.2.1b """ + name = "Cell Channel Description " + fields_desc = [ + BitField("bit128", 0x0, 1), + BitField("bit127", 0x0, 1), + BitField("spare1", 0x0, 1), + BitField("spare2", 0x0, 1), + BitField("bit124", 0x0, 1), + BitField("bit123", 0x0, 1), + BitField("bit122", 0x0, 1), + BitField("bit121", 0x0, 1), + ByteField("bit120", 0x0), + ByteField("bit112", 0x0), + ByteField("bit104", 0x0), + ByteField("bit96", 0x0), + ByteField("bit88", 0x0), + ByteField("bit80", 0x0), + ByteField("bit72", 0x0), + ByteField("bit64", 0x0), + ByteField("bit56", 0x0), + ByteField("bit48", 0x0), + ByteField("bit40", 0x0), + ByteField("bit32", 0x0), + ByteField("bit24", 0x0), + ByteField("bit16", 0x0), + ByteField("bit8", 0x0) + ] + + +class CellDescription(Packet): + """ Cell Description Section 10.5.2.2 """ + name = "Cell Description" + fields_desc = [ + BitField("bcchHigh", 0x0, 2), + BitField("ncc", 0x0, 3), + BitField("bcc", 0x0, 3), + ByteField("bcchLow", 0x0) + ] + + +class CellOptionsBCCH(Packet): + """ Cell Options (BCCH) Section 10.5.2.3 """ + name = "Cell Options (BCCH)" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("pwrc", 0x0, 1), + BitField("dtx", 0x0, 2), + BitField("rLinkTout", 0x0, 4) + ] + + +class CellOptionsSACCH(Packet): + """ Cell Options (SACCH) Section 10.5.2.3a """ + name = "Cell Options (SACCH)" + fields_desc = [ + BitField("dtx", 0x0, 1), + BitField("pwrc", 0x0, 1), + BitField("dtx", 0x0, 1), + BitField("rLinkTout", 0x0, 4) + ] + + +class CellSelectionParameters(Packet): + """ Cell Selection Parameters Section 10.5.2.4 """ + name = "Cell Selection Parameters" + fields_desc = [ + BitField("cellReselect", 0x0, 3), + BitField("msTxPwrMax", 0x0, 5), + BitField("acs", None, 1), + BitField("neci", None, 1), + BitField("rxlenAccMin", None, 6) + ] + + +class MacModeAndChannelCodingRequest(Packet): + """ MAC Mode and Channel Coding Requested Section 10.5.2.4a """ + name = "MAC Mode and Channel Coding Requested" + fields_desc = [ + BitField("macMode", 0x0, 2), + BitField("cs", 0x0, 2) + ] + + +class ChannelDescription(Packet): + """ Channel Description Section 10.5.2.5 """ + name = "Channel Description" + fields_desc = [ + + BitField("channelTyp", 0x0, 5), + BitField("tn", 0x0, 3), + + BitField("tsc", 0x0, 3), + BitField("h", 0x1, 1), + BitField("maioHi", 0x0, 4), + + BitField("maioLo", 0x0, 2), + BitField("hsn", 0x0, 6) + ] + + +class ChannelDescription2(Packet): + """ Channel Description 2 Section 10.5.2.5a """ + name = "Channel Description 2" + fields_desc = [ + BitField("channelTyp", 0x0, 5), + BitField("tn", 0x0, 3), + BitField("tsc", 0x0, 3), + BitField("h", 0x0, 1), + # if h=1 + # BitField("maioHi", 0x0, 4), + # BitField("maioLo", 0x0, 2), + # BitField("hsn", 0x0, 6) + BitField("spare", 0x0, 2), + BitField("arfcnHigh", 0x0, 2), + ByteField("arfcnLow", 0x0) + ] + + +class ChannelMode(Packet): + """ Channel Mode Section 10.5.2.6 """ + name = "Channel Mode" + fields_desc = [ + ByteField("mode", 0x0) + ] + + +class ChannelMode2(Packet): + """ Channel Mode 2 Section 10.5.2.7 """ + name = "Channel Mode 2" + fields_desc = [ + ByteField("mode", 0x0) + ] + + +class ChannelNeeded(Packet): + """ Channel Needed Section 10.5.2.8 """ + name = "Channel Needed" + fields_desc = [ + BitField("channel2", 0x0, 2), + BitField("channel1", 0x0, 2), + ] + + +class ChannelRequestDescription(Packet): + """Channel Request Description Section 10.5.2.8a """ + name = "Channel Request Description" + fields_desc = [ + BitField("mt", 0x0, 1), + ConditionalField(BitField("spare", 0x0, 39), + lambda pkt: pkt.mt == 0), + ConditionalField(BitField("spare", 0x0, 3), + lambda pkt: pkt.mt == 1), + ConditionalField(BitField("priority", 0x0, 2), + lambda pkt: pkt.mt == 1), + ConditionalField(BitField("rlcMode", 0x0, 1), + lambda pkt: pkt.mt == 1), + ConditionalField(BitField("llcFrame", 0x1, 1), + lambda pkt: pkt.mt == 1), + ConditionalField(ByteField("reqBandMsb", 0x0), + lambda pkt: pkt.mt == 1), + ConditionalField(ByteField("reqBandLsb", 0x0), + lambda pkt: pkt.mt == 1), + ConditionalField(ByteField("rlcMsb", 0x0), + lambda pkt: pkt.mt == 1), + ConditionalField(ByteField("rlcLsb", 0x0), + lambda pkt: pkt.mt == 1) + ] + + +class CipherModeSetting(Packet): + """Cipher Mode Setting Section 10.5.2.9 """ + name = "Cipher Mode Setting" + fields_desc = [ + BitField("algoId", 0x0, 3), + BitField("sc", 0x0, 1), + ] + + +class CipherResponse(Packet): + """Cipher Response Section 10.5.2.10 """ + name = "Cipher Response" + fields_desc = [ + BitField("spare", 0x0, 3), + BitField("cr", 0x0, 1), + ] + + +class ControlChannelDescription(Packet): + """Control Channel Description Section 10.5.2.11 """ + name = "Control Channel Description" + fields_desc = [ + + BitField("spare", 0x0, 1), + BitField("att", 0x0, 1), + BitField("bsAgBlksRes", 0x0, 3), + BitField("ccchConf", 0x0, 3), + + BitField("spare", 0x0, 1), + BitField("spare1", 0x0, 1), + BitField("spare2", 0x0, 1), + BitField("spare3", 0x0, 1), + BitField("spare4", 0x0, 1), + BitField("bsPaMfrms", 0x0, 3), + + ByteField("t3212", 0x0) + ] + + +class FrequencyChannelSequence(Packet): + """Frequency Channel Sequence Section 10.5.2.12""" + name = "Frequency Channel Sequence" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("lowestArfcn", 0x0, 7), + BitField("skipArfcn01", 0x0, 4), + BitField("skipArfcn02", 0x0, 4), + BitField("skipArfcn03", 0x0, 4), + BitField("skipArfcn04", 0x0, 4), + BitField("skipArfcn05", 0x0, 4), + BitField("skipArfcn06", 0x0, 4), + BitField("skipArfcn07", 0x0, 4), + BitField("skipArfcn08", 0x0, 4), + BitField("skipArfcn09", 0x0, 4), + BitField("skipArfcn10", 0x0, 4), + BitField("skipArfcn11", 0x0, 4), + BitField("skipArfcn12", 0x0, 4), + BitField("skipArfcn13", 0x0, 4), + BitField("skipArfcn14", 0x0, 4), + BitField("skipArfcn15", 0x0, 4), + BitField("skipArfcn16", 0x0, 4) + ] + + +class FrequencyList(Packet): + """Frequency List Section 10.5.2.13""" + name = "Frequency List" + # Problem: + # There are several formats for the Frequency List information + # element, distinguished by the "format indicator" subfield. + # Some formats are frequency bit maps, the others use a special encoding + # scheme. + fields_desc = [ + XByteField("lengthFL", None), + + BitField("formatID", 0x0, 2), + BitField("spare", 0x0, 2), + BitField("arfcn124", 0x0, 1), + BitField("arfcn123", 0x0, 1), + BitField("arfcn122", 0x0, 1), + BitField("arfcn121", 0x0, 1), + + ByteField("arfcn120", 0x0), + ByteField("arfcn112", 0x0), + ByteField("arfcn104", 0x0), + ByteField("arfcn96", 0x0), + ByteField("arfcn88", 0x0), + ByteField("arfcn80", 0x0), + ByteField("arfcn72", 0x0), + ByteField("arfcn64", 0x0), + ByteField("arfcn56", 0x0), + ByteField("arfcn48", 0x0), + ByteField("arfcn40", 0x0), + ByteField("arfcn32", 0x0), + ByteField("arfcn24", 0x0), + ByteField("arfcn16", 0x0), + ByteField("arfcn8", 0x0) + ] + + +# len 4 to 13 +class GroupChannelDescription(Packet): + """Group Channel Description Section 10.5.2.14b""" + name = "Group Channel Description" + fields_desc = [ + XByteField("lengthGCD", None), + + BitField("channelType", 0x0, 5), + BitField("tn", 0x0, 3), + + BitField("tsc", 0x0, 3), + BitField("h", 0x0, 1), + # if h == 0 the packet looks the following way: + ConditionalField(BitField("spare", 0x0, 2), + lambda pkt: pkt. h == 0x0), + ConditionalField(BitField("arfcnHi", 0x0, 2), + lambda pkt: pkt. h == 0x0), + ConditionalField(ByteField("arfcnLo", None), + lambda pkt: pkt. h == 0x0), + # if h == 1 the packet looks the following way: + ConditionalField(BitField("maioHi", 0x0, 4), + lambda pkt: pkt. h == 0x1), + ConditionalField(BitField("maioLo", None, 2), + lambda pkt: pkt. h == 0x1), + ConditionalField(BitField("hsn", None, 6), + lambda pkt: pkt. h == 0x1), + # finished with conditional fields + ByteField("maC6", None), + ByteField("maC7", None), + ByteField("maC8", None), + ByteField("maC9", None), + ByteField("maC10", None), + ByteField("maC11", None), + ByteField("maC12", None), + ByteField("maC13", None), + ByteField("maC14", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(4, 13, a, self.fields_desc, 1) + if self.lengthGCD is None: + p = struct.pack(">B", res[1]) + p[1:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +class GprsResumption(Packet): + """GPRS Resumption Section 10.5.2.14c""" + name = "GPRS Resumption" + fields_desc = [ + BitField("spare", 0x0, 3), + BitField("ack", 0x0, 1) + ] + + +class HandoverReference(Packet): + """Handover Reference Section 10.5.2.15""" + name = "Handover Reference" + fields_desc = [ + ByteField("handoverRef", 0x0) + ] + + +class IraRestOctets(Packet): + """IAR Rest Octets Section 10.5.2.17""" + name = "IAR Rest Octets" + fields_desc = [ + BitField("spare01", 0x0, 1), + BitField("spare02", 0x0, 1), + BitField("spare03", 0x1, 1), + BitField("spare04", 0x0, 1), + BitField("spare05", 0x1, 1), + BitField("spare06", 0x0, 1), + BitField("spare07", 0x1, 1), + BitField("spare08", 0x1, 1), + BitField("spare09", 0x0, 1), + BitField("spare10", 0x0, 1), + BitField("spare11", 0x1, 1), + BitField("spare12", 0x0, 1), + BitField("spare13", 0x1, 1), + BitField("spare14", 0x0, 1), + BitField("spare15", 0x1, 1), + BitField("spare16", 0x1, 1), + BitField("spare17", 0x0, 1), + BitField("spare18", 0x0, 1), + BitField("spare19", 0x1, 1), + BitField("spare20", 0x0, 1), + BitField("spare21", 0x1, 1), + BitField("spare22", 0x0, 1), + BitField("spare23", 0x1, 1), + BitField("spare24", 0x1, 1) + ] + + +# len is 1 to 5 what do we do with the variable size? no lenght +# field?! WTF +class IaxRestOctets(Packet): + """IAX Rest Octets Section 10.5.2.18""" + name = "IAX Rest Octets" + fields_desc = [ + BitField("spare01", 0x0, 1), + BitField("spare02", 0x0, 1), + BitField("spare03", 0x1, 1), + BitField("spare04", 0x0, 1), + BitField("spare05", 0x1, 1), + BitField("spare06", 0x0, 1), + BitField("spare07", 0x1, 1), + BitField("spare08", 0x1, 1), + ByteField("spareB1", None), + ByteField("spareB2", None), + ByteField("spareB3", None) + ] + + +class L2PseudoLength(Packet): + """L2 Pseudo Length Section 10.5.2.19""" + name = "L2 Pseudo Length" + fields_desc = [ + BitField("l2pLength", None, 6), + BitField("bit2", 0x0, 1), + BitField("bit1", 0x1, 1) + ] + + +class MeasurementResults(Packet): + """Measurement Results Section 10.5.2.20""" + name = "Measurement Results" + fields_desc = [ + BitField("baUsed", 0x0, 1), + BitField("dtxUsed", 0x0, 1), + BitField("rxLevFull", 0x0, 6), + + BitField("spare", 0x0, 1), + BitField("measValid", 0x0, 1), + BitField("rxLevSub", 0x0, 6), + + BitField("spare0", 0x0, 1), + BitField("rxqualFull", 0x0, 3), + BitField("rxqualSub", 0x0, 3), + BitField("noNcellHi", 0x0, 1), + + BitField("noNcellLo", 0x0, 2), + BitField("rxlevC1", 0x0, 6), + + BitField("bcchC1", 0x0, 5), + BitField("bsicC1Hi", 0x0, 3), + + BitField("bsicC1Lo", 0x0, 3), + BitField("rxlevC2", 0x0, 5), + + BitField("rxlevC2Lo", 0x0, 1), + BitField("bcchC2", 0x0, 5), + BitField("bsicC2Hi", 0x0, 2), + + BitField("bscicC2Lo", 0x0, 4), + BitField("bscicC2Hi", 0x0, 4), + + BitField("rxlevC3Lo", 0x0, 2), + BitField("bcchC3", 0x0, 5), + BitField("rxlevC3Hi", 0x0, 1), + + BitField("bsicC3Lo", 0x0, 5), + BitField("bsicC3Hi", 0x0, 3), + + BitField("rxlevC4Lo", 0x0, 3), + BitField("bcchC4", 0x0, 5), + + BitField("bsicC4", 0x0, 6), + BitField("rxlevC5Hi", 0x0, 2), + + BitField("rxlevC5Lo", 0x0, 4), + BitField("bcchC5Hi", 0x0, 4), + + BitField("bcchC5Lo", 0x0, 1), + BitField("bsicC5", 0x0, 6), + BitField("rxlevC6", 0x0, 1), + + BitField("rxlevC6Lo", 0x0, 5), + BitField("bcchC6Hi", 0x0, 3), + + BitField("bcchC6Lo", 0x0, 3), + BitField("bsicC6", 0x0, 5) + ] + + +class GprsMeasurementResults(Packet): + """GPRS Measurement Results Section 10.5.2.20a""" + name = "GPRS Measurement Results" + fields_desc = [ + BitField("cValue", 0x0, 6), + BitField("rxqualHi", 0x0, 2), + BitField("rxqL", 0x0, 1), + BitField("spare", 0x0, 1), + BitField("signVar", 0x0, 6) + ] + + +# len 3 to 10 +class MobileAllocation(Packet): + """Mobile Allocation Section 10.5.2.21""" + name = "Mobile Allocation" + fields_desc = [ + XByteField("lengthMA", None), + ByteField("maC64", 0x12), + ByteField("maC56", None), # optional fields start here + ByteField("maC48", None), + ByteField("maC40", None), + ByteField("maC32", None), + ByteField("maC24", None), + ByteField("maC16", None), + ByteField("maC8", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(2, 9, a, self.fields_desc, 1) + if self.lengthMA is None: + p = struct.pack(">B", res[1]) + p[1:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +class MobileTimeDifference(Packet): + """Mobile Time Difference Section 10.5.2.21a""" + name = "Mobile Time Difference" + fields_desc = [ + XByteField("lengthMTD", 0x5), + ByteField("valueHi", 0x0), + ByteField("valueCnt", 0x0), + BitField("valueLow", 0x0, 5), + BitField("spare", 0x0, 1), + BitField("spare1", 0x0, 1), + BitField("spare2", 0x0, 1) + ] + + +# min 4 octets max 8 +class MultiRateConfiguration(Packet): + """ MultiRate configuration Section 10.5.2.21aa""" + name = "MultiRate Configuration" + # This packet has a variable length and hence structure. This packet + # implements the longuest possible packet. If you biuild a shorter + # packet, for example having only 6 bytes, the last 4 bytes are named + # "Spare" in the specs. Here they are named "threshold2" + fields_desc = [ + XByteField("lengthMRC", None), + + BitField("mrVersion", 0x0, 3), + BitField("spare", 0x0, 1), + BitField("icmi", 0x0, 1), + BitField("spare", 0x0, 1), + BitField("startMode", 0x0, 2), + + ByteField("amrCodec", None), + + BitField("spare", None, 2), + BitField("threshold1", None, 6), + + BitField("hysteresis1", None, 4), + BitField("threshold2", None, 4), + + BitField("threshold2cnt", None, 2), + BitField("hysteresis2", None, 4), + BitField("threshold3", None, 2), + + BitField("threshold3cnt", None, 4), + BitField("hysteresis3", None, 4) + ] + + def post_build(self, p, pay): + # we set the length + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(3, 7, a, self.fields_desc, 1) + if self.lengthMRC is None: + p = struct.pack(">B", res[1]) + p[1:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +# len 2 to 11 +class MultislotAllocation(Packet): + """Multislot Allocation Section 10.5.2.21b""" + name = "Multislot Allocation" + fields_desc = [ + XByteField("lengthMSA", None), + BitField("ext0", 0x1, 1), + BitField("da", 0x0, 7), + ConditionalField(BitField("ext1", 0x1, 1), # optional + lambda pkt: pkt.ext0 == 0), + ConditionalField(BitField("ua", 0x0, 7), + lambda pkt: pkt.ext0 == 0), + ByteField("chan1", None), + ByteField("chan2", None), + ByteField("chan3", None), + ByteField("chan4", None), + ByteField("chan5", None), + ByteField("chan6", None), + ByteField("chan7", None), + ByteField("chan8", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(1, 11, a, self.fields_desc, 1) + if res[0] is not 0: + p = p[:-res[0]] + if self.lengthMSA is None: + p = struct.pack(">B", len(p)-1) + p[1:] + return p + pay + + +class NcMode(Packet): + """NC mode Section 10.5.2.21c""" + name = "NC Mode" + fields_desc = [ + BitField("spare", 0x0, 2), + BitField("ncMode", 0x0, 2) + ] + + +class NeighbourCellsDescription(Packet): + """Neighbour Cells Description Section 10.5.2.22""" + name = "Neighbour Cells Description" + fields_desc = [ + BitField("bit128", 0x0, 1), + BitField("bit127", 0x0, 1), + BitField("extInd", 0x0, 1), + BitField("baInd", 0x0, 1), + BitField("bit124", 0x0, 1), + BitField("bit123", 0x0, 1), + BitField("bit122", 0x0, 1), + BitField("bit121", 0x0, 1), + BitField("120bits", 0x0, 120) + ] + + +class NeighbourCellsDescription2(Packet): + """Neighbour Cells Description 2 Section 10.5.2.22a""" + name = "Neighbour Cells Description 2" + fields_desc = [ + BitField("bit128", 0x0, 1), + BitField("multiband", 0x0, 2), + BitField("baInd", 0x0, 1), + BitField("bit124", 0x0, 1), + BitField("bit123", 0x0, 1), + BitField("bit122", 0x0, 1), + BitField("bit121", 0x0, 1), + BitField("120bits", 0x0, 120) + ] + + +# len 4 +# strange packet, lots of valid formats + +# ideas for the dynamic packets: +# 1] for user interaction: Create an interactive "builder" based on a +# Q/A process (not very scapy like) +# 2] for usage in scripts, create an alternative packet for every +# possible packet layout +# + +class DedicatedModeOrTBF(Packet): + """Dedicated mode or TBF Section 10.5.2.25b""" + name = "Dedicated Mode or TBF" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("tma", 0x0, 1), + BitField("downlink", 0x0, 1), + BitField("td", 0x0, 1) + ] + + +class PageMode(Packet): + """Page Mode Section 10.5.2.26""" + name = "Page Mode" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("spare1", 0x0, 1), + BitField("pm", 0x0, 2) + ] + + +class NccPermitted(Packet): + """NCC Permitted Section 10.5.2.27""" + name = "NCC Permited" + fields_desc = [ + ByteField("nccPerm", 0x0) + ] + + +class PowerCommand(Packet): + """Power Command Section 10.5.2.28""" + name = "Power Command" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("spare1", 0x0, 1), + BitField("spare2", 0x0, 1), + BitField("powerLvl", 0x0, 5) + ] + + +class PowerCommandAndAccessType(Packet): + """Power Command and access type Section 10.5.2.28a""" + name = "Power Command and Access Type" + fields_desc = [ + BitField("atc", 0x0, 1), + BitField("spare", 0x0, 1), + BitField("spare1", 0x0, 1), + BitField("powerLvl", 0x0, 5) + ] + + +class RachControlParameters(Packet): + """RACH Control Parameters Section 10.5.2.29""" + name = "RACH Control Parameters" + fields_desc = [ + BitField("maxRetrans", 0x0, 2), + BitField("txInteger", 0x0, 4), + BitField("cellBarrAccess", 0x0, 1), + BitField("re", 0x0, 1), + BitField("ACC15", 0x0, 1), + BitField("ACC14", 0x0, 1), + BitField("ACC13", 0x0, 1), + BitField("ACC12", 0x0, 1), + BitField("ACC11", 0x0, 1), + BitField("ACC10", 0x0, 1), + BitField("ACC09", 0x0, 1), + BitField("ACC08", 0x0, 1), + BitField("ACC07", 0x0, 1), + BitField("ACC06", 0x0, 1), + BitField("ACC05", 0x0, 1), + BitField("ACC04", 0x0, 1), + BitField("ACC03", 0x0, 1), + BitField("ACC02", 0x0, 1), + BitField("ACC01", 0x0, 1), + BitField("ACC00", 0x0, 1), + ] + + +class RequestReference(Packet): + """Request Reference Section 10.5.2.30""" + name = "Request Reference" + fields_desc = [ + ByteField("ra", 0x0), + BitField("t1", 0x0, 5), + BitField("t3Hi", 0x0, 3), + BitField("t3Lo", 0x0, 3), + BitField("t2", 0x0, 5) + ] + + +class RrCause(Packet): + """RR Cause Section 10.5.2.31""" + name = "RR Cause" + fields_desc = [ + ByteField("rrCause", 0x0) + ] + + +class StartingTime(Packet): + """Starting Time Section 10.5.2.38""" + name = "Starting Time" + fields_desc = [ + ByteField("ra", 0x0), + BitField("t1", 0x0, 5), + BitField("t3Hi", 0x0, 3), + BitField("t3Lo", 0x0, 3), + BitField("t2", 0x0, 5) + ] + + +class SynchronizationIndication(Packet): + """Synchronization Indication Section 10.5.2.39""" + name = "Synchronization Indication" + fields_desc = [ + BitField("nci", 0x0, 1), + BitField("rot", 0x0, 1), + BitField("si", 0x0, 2) + ] + + +class TimingAdvance(Packet): + """Timing Advance Section 10.5.2.40""" + name = "Timing Advance" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("spare1", 0x0, 1), + BitField("timingVal", 0x0, 6) + ] + + +class TimeDifference(Packet): + """ Time Difference Section 10.5.2.41""" + name = "Time Difference" + fields_desc = [ + XByteField("lengthTD", 0x3), + ByteField("timeValue", 0x0) + ] + + +class Tlli(Packet): + """ TLLI Section Section 10.5.2.41a""" + name = "TLLI" + fields_desc = [ + ByteField("value", 0x0), + ByteField("value1", 0x0), + ByteField("value2", 0x0), + ByteField("value3", 0x0) + ] + + +class TmsiPTmsi(Packet): + """ TMSI/P-TMSI Section 10.5.2.42""" + name = "TMSI/P-TMSI" + fields_desc = [ + ByteField("value", 0x0), + ByteField("value1", 0x0), + ByteField("value2", 0x0), + ByteField("value3", 0x0) + ] + + +class VgcsTargetModeIdentication(Packet): + """ VGCS target Mode Indication 10.5.2.42a""" + name = "VGCS Target Mode Indication" + fields_desc = [ + XByteField("lengthVTMI", 0x2), + BitField("targerMode", 0x0, 2), + BitField("cipherKeyNb", 0x0, 4), + BitField("spare", 0x0, 1), + BitField("spare1", 0x0, 1) + ] + + +class WaitIndication(Packet): + """ Wait Indication Section 10.5.2.43""" + name = "Wait Indication" + fields_desc = [ # asciiart of specs strange + ByteField("timeoutVal", 0x0) + ] + + +#class Si10RestOctets(Packet): +# """SI10 rest octets 10.5.2.44""" +# name = "SI10 rest octets" +# fields_desc = [ + + +# len 17 +class ExtendedMeasurementResults(Packet): + """EXTENDED MEASUREMENT RESULTS Section 10.5.2.45""" + name = "Extended Measurement Results" + fields_desc = [ + + BitField("scUsed", None, 1), + BitField("dtxUsed", None, 1), + BitField("rxLevC0", None, 6), + + BitField("rxLevC1", None, 6), + BitField("rxLevC2Hi", None, 2), + + BitField("rxLevC2Lo", None, 4), + BitField("rxLevC3Hi", None, 4), + + BitField("rxLevC3Lo", None, 3), + BitField("rxLevC4", None, 5), + + BitField("rxLevC5", None, 6), + BitField("rxLevC6Hi", None, 2), + + BitField("rxLevC6Lo", None, 4), + BitField("rxLevC7Hi", None, 4), + + BitField("rxLevC7Lo", None, 2), + BitField("rxLevC8", None, 6), + + BitField("rxLevC9", None, 6), + BitField("rxLevC10Hi", None, 2), + + BitField("rxLevC10Lo", None, 4), + BitField("rxLevC11Hi", None, 4), + + BitField("rxLevC13Lo", None, 2), + BitField("rxLevC12", None, 6), + + BitField("rxLevC13", None, 6), + BitField("rxLevC14Hi", None, 2), + + BitField("rxLevC14Lo", None, 4), + BitField("rxLevC15Hi", None, 4), + + BitField("rxLevC15Lo", None, 2), + BitField("rxLevC16", None, 6), + + + BitField("rxLevC17", None, 6), + BitField("rxLevC18Hi", None, 2), + + BitField("rxLevC18Lo", None, 4), + BitField("rxLevC19Hi", None, 4), + + BitField("rxLevC19Lo", None, 2), + BitField("rxLevC20", None, 6) + ] + + +# len 17 +class ExtendedMeasurementFrequencyList(Packet): + """Extended Measurement Frequency List Section 10.5.2.46""" + name = "Extended Measurement Frequency List" + fields_desc = [ + + BitField("bit128", 0x0, 1), + BitField("bit127", 0x0, 1), + BitField("spare", 0x0, 1), + BitField("seqCode", 0x0, 1), + BitField("bit124", 0x0, 1), + BitField("bit123", 0x0, 1), + BitField("bit122", 0x0, 1), + BitField("bit121", 0x0, 1), + + BitField("bitsRest", 0x0, 128) + ] + + +class SuspensionCause(Packet): + """Suspension Cause Section 10.5.2.47""" + name = "Suspension Cause" + fields_desc = [ + ByteField("suspVal", 0x0) + ] + + +class ApduID(Packet): + """APDU Flags Section 10.5.2.48""" + name = "Apdu Id" + fields_desc = [ + BitField("id", None, 4) + ] + + +class ApduFlags(Packet): + """APDU Flags Section 10.5.2.49""" + name = "Apdu Flags" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("cr", 0x0, 1), + BitField("firstSeg", 0x0, 1), + BitField("lastSeg", 0x0, 1) + ] + + +# len 1 to max L3 (251) (done) +class ApduData(Packet): + """APDU Data Section 10.5.2.50""" + name = "Apdu Data" + fields_desc = [ + XByteField("lengthAD", None), + #optional + ByteField("apuInfo1", None), + ByteField("apuInfo2", None), + ByteField("apuInfo3", None), + ByteField("apuInfo4", None), + ByteField("apuInfo5", None), + ByteField("apuInfo6", None), + ByteField("apuInfo7", None), + ByteField("apuInfo8", None), + ByteField("apuInfo9", None), + ByteField("apuInfo10", None), + ByteField("apuInfo11", None), + ByteField("apuInfo12", None), + ByteField("apuInfo13", None), + ByteField("apuInfo14", None), + ByteField("apuInfo15", None), + ByteField("apuInfo16", None), + ByteField("apuInfo17", None), + ByteField("apuInfo18", None), + ByteField("apuInfo19", None), + ByteField("apuInfo20", None), + ByteField("apuInfo21", None), + ByteField("apuInfo22", None), + ByteField("apuInfo23", None), + ByteField("apuInfo24", None), + ByteField("apuInfo25", None), + ByteField("apuInfo26", None), + ByteField("apuInfo27", None), + ByteField("apuInfo28", None), + ByteField("apuInfo29", None), + ByteField("apuInfo30", None), + ByteField("apuInfo31", None), + ByteField("apuInfo32", None), + ByteField("apuInfo33", None), + ByteField("apuInfo34", None), + ByteField("apuInfo35", None), + ByteField("apuInfo36", None), + ByteField("apuInfo37", None), + ByteField("apuInfo38", None), + ByteField("apuInfo39", None), + ByteField("apuInfo40", None), + ByteField("apuInfo41", None), + ByteField("apuInfo42", None), + ByteField("apuInfo43", None), + ByteField("apuInfo44", None), + ByteField("apuInfo45", None), + ByteField("apuInfo46", None), + ByteField("apuInfo47", None), + ByteField("apuInfo48", None), + ByteField("apuInfo49", None), + ByteField("apuInfo50", None), + ByteField("apuInfo51", None), + ByteField("apuInfo52", None), + ByteField("apuInfo53", None), + ByteField("apuInfo54", None), + ByteField("apuInfo55", None), + ByteField("apuInfo56", None), + ByteField("apuInfo57", None), + ByteField("apuInfo58", None), + ByteField("apuInfo59", None), + ByteField("apuInfo60", None), + ByteField("apuInfo61", None), + ByteField("apuInfo62", None), + ByteField("apuInfo63", None), + ByteField("apuInfo64", None), + ByteField("apuInfo65", None), + ByteField("apuInfo66", None), + ByteField("apuInfo67", None), + ByteField("apuInfo68", None), + ByteField("apuInfo69", None), + ByteField("apuInfo70", None), + ByteField("apuInfo71", None), + ByteField("apuInfo72", None), + ByteField("apuInfo73", None), + ByteField("apuInfo74", None), + ByteField("apuInfo75", None), + ByteField("apuInfo76", None), + ByteField("apuInfo77", None), + ByteField("apuInfo78", None), + ByteField("apuInfo79", None), + ByteField("apuInfo80", None), + ByteField("apuInfo81", None), + ByteField("apuInfo82", None), + ByteField("apuInfo83", None), + ByteField("apuInfo84", None), + ByteField("apuInfo85", None), + ByteField("apuInfo86", None), + ByteField("apuInfo87", None), + ByteField("apuInfo88", None), + ByteField("apuInfo89", None), + ByteField("apuInfo90", None), + ByteField("apuInfo91", None), + ByteField("apuInfo92", None), + ByteField("apuInfo93", None), + ByteField("apuInfo94", None), + ByteField("apuInfo95", None), + ByteField("apuInfo96", None), + ByteField("apuInfo97", None), + ByteField("apuInfo98", None), + ByteField("apuInfo99", None), + ByteField("apuInfo100", None), + ByteField("apuInfo101", None), + ByteField("apuInfo102", None), + ByteField("apuInfo103", None), + ByteField("apuInfo104", None), + ByteField("apuInfo105", None), + ByteField("apuInfo106", None), + ByteField("apuInfo107", None), + ByteField("apuInfo108", None), + ByteField("apuInfo109", None), + ByteField("apuInfo110", None), + ByteField("apuInfo111", None), + ByteField("apuInfo112", None), + ByteField("apuInfo113", None), + ByteField("apuInfo114", None), + ByteField("apuInfo115", None), + ByteField("apuInfo116", None), + ByteField("apuInfo117", None), + ByteField("apuInfo118", None), + ByteField("apuInfo119", None), + ByteField("apuInfo120", None), + ByteField("apuInfo121", None), + ByteField("apuInfo122", None), + ByteField("apuInfo123", None), + ByteField("apuInfo124", None), + ByteField("apuInfo125", None), + ByteField("apuInfo126", None), + ByteField("apuInfo127", None), + ByteField("apuInfo128", None), + ByteField("apuInfo129", None), + ByteField("apuInfo130", None), + ByteField("apuInfo131", None), + ByteField("apuInfo132", None), + ByteField("apuInfo133", None), + ByteField("apuInfo134", None), + ByteField("apuInfo135", None), + ByteField("apuInfo136", None), + ByteField("apuInfo137", None), + ByteField("apuInfo138", None), + ByteField("apuInfo139", None), + ByteField("apuInfo140", None), + ByteField("apuInfo141", None), + ByteField("apuInfo142", None), + ByteField("apuInfo143", None), + ByteField("apuInfo144", None), + ByteField("apuInfo145", None), + ByteField("apuInfo146", None), + ByteField("apuInfo147", None), + ByteField("apuInfo148", None), + ByteField("apuInfo149", None), + ByteField("apuInfo150", None), + ByteField("apuInfo151", None), + ByteField("apuInfo152", None), + ByteField("apuInfo153", None), + ByteField("apuInfo154", None), + ByteField("apuInfo155", None), + ByteField("apuInfo156", None), + ByteField("apuInfo157", None), + ByteField("apuInfo158", None), + ByteField("apuInfo159", None), + ByteField("apuInfo160", None), + ByteField("apuInfo161", None), + ByteField("apuInfo162", None), + ByteField("apuInfo163", None), + ByteField("apuInfo164", None), + ByteField("apuInfo165", None), + ByteField("apuInfo166", None), + ByteField("apuInfo167", None), + ByteField("apuInfo168", None), + ByteField("apuInfo169", None), + ByteField("apuInfo170", None), + ByteField("apuInfo171", None), + ByteField("apuInfo172", None), + ByteField("apuInfo173", None), + ByteField("apuInfo174", None), + ByteField("apuInfo175", None), + ByteField("apuInfo176", None), + ByteField("apuInfo177", None), + ByteField("apuInfo178", None), + ByteField("apuInfo179", None), + ByteField("apuInfo180", None), + ByteField("apuInfo181", None), + ByteField("apuInfo182", None), + ByteField("apuInfo183", None), + ByteField("apuInfo184", None), + ByteField("apuInfo185", None), + ByteField("apuInfo186", None), + ByteField("apuInfo187", None), + ByteField("apuInfo188", None), + ByteField("apuInfo189", None), + ByteField("apuInfo190", None), + ByteField("apuInfo191", None), + ByteField("apuInfo192", None), + ByteField("apuInfo193", None), + ByteField("apuInfo194", None), + ByteField("apuInfo195", None), + ByteField("apuInfo196", None), + ByteField("apuInfo197", None), + ByteField("apuInfo198", None), + ByteField("apuInfo199", None), + ByteField("apuInfo200", None), + ByteField("apuInfo201", None), + ByteField("apuInfo202", None), + ByteField("apuInfo203", None), + ByteField("apuInfo204", None), + ByteField("apuInfo205", None), + ByteField("apuInfo206", None), + ByteField("apuInfo207", None), + ByteField("apuInfo208", None), + ByteField("apuInfo209", None), + ByteField("apuInfo210", None), + ByteField("apuInfo211", None), + ByteField("apuInfo212", None), + ByteField("apuInfo213", None), + ByteField("apuInfo214", None), + ByteField("apuInfo215", None), + ByteField("apuInfo216", None), + ByteField("apuInfo217", None), + ByteField("apuInfo218", None), + ByteField("apuInfo219", None), + ByteField("apuInfo220", None), + ByteField("apuInfo221", None), + ByteField("apuInfo222", None), + ByteField("apuInfo223", None), + ByteField("apuInfo224", None), + ByteField("apuInfo225", None), + ByteField("apuInfo226", None), + ByteField("apuInfo227", None), + ByteField("apuInfo228", None), + ByteField("apuInfo229", None), + ByteField("apuInfo230", None), + ByteField("apuInfo231", None), + ByteField("apuInfo232", None), + ByteField("apuInfo233", None), + ByteField("apuInfo234", None), + ByteField("apuInfo235", None), + ByteField("apuInfo236", None), + ByteField("apuInfo237", None), + ByteField("apuInfo238", None), + ByteField("apuInfo239", None), + ByteField("apuInfo240", None), + ByteField("apuInfo241", None), + ByteField("apuInfo242", None), + ByteField("apuInfo243", None), + ByteField("apuInfo244", None), + ByteField("apuInfo245", None), + ByteField("apuInfo246", None), + ByteField("apuInfo247", None), + ByteField("apuInfo248", None), + ByteField("apuInfo249", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(1, 250, a, self.fields_desc, 1) + if self.lengthAD is None: + p = struct.pack(">B", res[1]) + p[1:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay +# +# 10.5.3 Mobility management information elements +# + + +# len 3 to L3 max (251) (done) +class NetworkName(Packet): + """Network Name Section 10.5.3.5a""" + name = "Network Name" + fields_desc = [ + + XByteField("lengthNN", None), + + BitField("ext", 0x1, 1), + BitField("codingScheme", 0x0, 3), + BitField("addCi", 0x0, 1), + BitField("nbSpare", 0x0, 3), + # optional + ByteField("txtString1", None), + ByteField("txtString2", None), + ByteField("txtString3", None), + ByteField("txtString4", None), + ByteField("txtString5", None), + ByteField("txtString6", None), + ByteField("txtString7", None), + ByteField("txtString8", None), + ByteField("txtString9", None), + ByteField("txtString10", None), + ByteField("txtString11", None), + ByteField("txtString12", None), + ByteField("txtString13", None), + ByteField("txtString14", None), + ByteField("txtString15", None), + ByteField("txtString16", None), + ByteField("txtString17", None), + ByteField("txtString18", None), + ByteField("txtString19", None), + ByteField("txtString20", None), + ByteField("txtString21", None), + ByteField("txtString22", None), + ByteField("txtString23", None), + ByteField("txtString24", None), + ByteField("txtString25", None), + ByteField("txtString26", None), + ByteField("txtString27", None), + ByteField("txtString28", None), + ByteField("txtString29", None), + ByteField("txtString30", None), + ByteField("txtString31", None), + ByteField("txtString32", None), + ByteField("txtString33", None), + ByteField("txtString34", None), + ByteField("txtString35", None), + ByteField("txtString36", None), + ByteField("txtString37", None), + ByteField("txtString38", None), + ByteField("txtString39", None), + ByteField("txtString40", None), + ByteField("txtString41", None), + ByteField("txtString42", None), + ByteField("txtString43", None), + ByteField("txtString44", None), + ByteField("txtString45", None), + ByteField("txtString46", None), + ByteField("txtString47", None), + ByteField("txtString48", None), + ByteField("txtString49", None), + ByteField("txtString50", None), + ByteField("txtString51", None), + ByteField("txtString52", None), + ByteField("txtString53", None), + ByteField("txtString54", None), + ByteField("txtString55", None), + ByteField("txtString56", None), + ByteField("txtString57", None), + ByteField("txtString58", None), + ByteField("txtString59", None), + ByteField("txtString60", None), + ByteField("txtString61", None), + ByteField("txtString62", None), + ByteField("txtString63", None), + ByteField("txtString64", None), + ByteField("txtString65", None), + ByteField("txtString66", None), + ByteField("txtString67", None), + ByteField("txtString68", None), + ByteField("txtString69", None), + ByteField("txtString70", None), + ByteField("txtString71", None), + ByteField("txtString72", None), + ByteField("txtString73", None), + ByteField("txtString74", None), + ByteField("txtString75", None), + ByteField("txtString76", None), + ByteField("txtString77", None), + ByteField("txtString78", None), + ByteField("txtString79", None), + ByteField("txtString80", None), + ByteField("txtString81", None), + ByteField("txtString82", None), + ByteField("txtString83", None), + ByteField("txtString84", None), + ByteField("txtString85", None), + ByteField("txtString86", None), + ByteField("txtString87", None), + ByteField("txtString88", None), + ByteField("txtString89", None), + ByteField("txtString90", None), + ByteField("txtString91", None), + ByteField("txtString92", None), + ByteField("txtString93", None), + ByteField("txtString94", None), + ByteField("txtString95", None), + ByteField("txtString96", None), + ByteField("txtString97", None), + ByteField("txtString98", None), + ByteField("txtString99", None), + ByteField("txtString100", None), + ByteField("txtString101", None), + ByteField("txtString102", None), + ByteField("txtString103", None), + ByteField("txtString104", None), + ByteField("txtString105", None), + ByteField("txtString106", None), + ByteField("txtString107", None), + ByteField("txtString108", None), + ByteField("txtString109", None), + ByteField("txtString110", None), + ByteField("txtString111", None), + ByteField("txtString112", None), + ByteField("txtString113", None), + ByteField("txtString114", None), + ByteField("txtString115", None), + ByteField("txtString116", None), + ByteField("txtString117", None), + ByteField("txtString118", None), + ByteField("txtString119", None), + ByteField("txtString120", None), + ByteField("txtString121", None), + ByteField("txtString122", None), + ByteField("txtString123", None), + ByteField("txtString124", None), + ByteField("txtString125", None), + ByteField("txtString126", None), + ByteField("txtString127", None), + ByteField("txtString128", None), + ByteField("txtString129", None), + ByteField("txtString130", None), + ByteField("txtString131", None), + ByteField("txtString132", None), + ByteField("txtString133", None), + ByteField("txtString134", None), + ByteField("txtString135", None), + ByteField("txtString136", None), + ByteField("txtString137", None), + ByteField("txtString138", None), + ByteField("txtString139", None), + ByteField("txtString140", None), + ByteField("txtString141", None), + ByteField("txtString142", None), + ByteField("txtString143", None), + ByteField("txtString144", None), + ByteField("txtString145", None), + ByteField("txtString146", None), + ByteField("txtString147", None), + ByteField("txtString148", None), + ByteField("txtString149", None), + ByteField("txtString150", None), + ByteField("txtString151", None), + ByteField("txtString152", None), + ByteField("txtString153", None), + ByteField("txtString154", None), + ByteField("txtString155", None), + ByteField("txtString156", None), + ByteField("txtString157", None), + ByteField("txtString158", None), + ByteField("txtString159", None), + ByteField("txtString160", None), + ByteField("txtString161", None), + ByteField("txtString162", None), + ByteField("txtString163", None), + ByteField("txtString164", None), + ByteField("txtString165", None), + ByteField("txtString166", None), + ByteField("txtString167", None), + ByteField("txtString168", None), + ByteField("txtString169", None), + ByteField("txtString170", None), + ByteField("txtString171", None), + ByteField("txtString172", None), + ByteField("txtString173", None), + ByteField("txtString174", None), + ByteField("txtString175", None), + ByteField("txtString176", None), + ByteField("txtString177", None), + ByteField("txtString178", None), + ByteField("txtString179", None), + ByteField("txtString180", None), + ByteField("txtString181", None), + ByteField("txtString182", None), + ByteField("txtString183", None), + ByteField("txtString184", None), + ByteField("txtString185", None), + ByteField("txtString186", None), + ByteField("txtString187", None), + ByteField("txtString188", None), + ByteField("txtString189", None), + ByteField("txtString190", None), + ByteField("txtString191", None), + ByteField("txtString192", None), + ByteField("txtString193", None), + ByteField("txtString194", None), + ByteField("txtString195", None), + ByteField("txtString196", None), + ByteField("txtString197", None), + ByteField("txtString198", None), + ByteField("txtString199", None), + ByteField("txtString200", None), + ByteField("txtString201", None), + ByteField("txtString202", None), + ByteField("txtString203", None), + ByteField("txtString204", None), + ByteField("txtString205", None), + ByteField("txtString206", None), + ByteField("txtString207", None), + ByteField("txtString208", None), + ByteField("txtString209", None), + ByteField("txtString210", None), + ByteField("txtString211", None), + ByteField("txtString212", None), + ByteField("txtString213", None), + ByteField("txtString214", None), + ByteField("txtString215", None), + ByteField("txtString216", None), + ByteField("txtString217", None), + ByteField("txtString218", None), + ByteField("txtString219", None), + ByteField("txtString220", None), + ByteField("txtString221", None), + ByteField("txtString222", None), + ByteField("txtString223", None), + ByteField("txtString224", None), + ByteField("txtString225", None), + ByteField("txtString226", None), + ByteField("txtString227", None), + ByteField("txtString228", None), + ByteField("txtString229", None), + ByteField("txtString230", None), + ByteField("txtString231", None), + ByteField("txtString232", None), + ByteField("txtString233", None), + ByteField("txtString234", None), + ByteField("txtString235", None), + ByteField("txtString236", None), + ByteField("txtString237", None), + ByteField("txtString238", None), + ByteField("txtString239", None), + ByteField("txtString240", None), + ByteField("txtString241", None), + ByteField("txtString242", None), + ByteField("txtString243", None), + ByteField("txtString244", None), + ByteField("txtString245", None), + ByteField("txtString246", None), + ByteField("txtString247", None), + ByteField("txtString248", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(2, 250, a, self.fields_desc, 1) + if self.lengthNN is None: + p = struct.pack(">B", res[1]) + p[1:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +class TimeZone(Packet): + """Time Zone Section 10.5.3.8""" + name = "Time Zone" + fields_desc = [ + ByteField("timeZone", 0x0), + ] + + +class TimeZoneAndTime(Packet): + """Time Zone and Time Section 10.5.3.9""" + name = "Time Zone and Time" + fields_desc = [ + ByteField("year", 0x0), + ByteField("month", 0x0), + ByteField("day", 0x0), + ByteField("hour", 0x0), + ByteField("minute", 0x0), + ByteField("second", 0x0), + ByteField("timeZone", 0x0) + ] + + +class CtsPermission(Packet): + """CTS permission Section 10.5.3.10""" + name = "Cts Permission" + fields_desc = [ + ] + + +class LsaIdentifier(Packet): + """LSA Identifier Section 10.5.3.11""" + name = "Lsa Identifier" + fields_desc = [ + ByteField("lsaID", 0x0), + ByteField("lsaID1", 0x0), + ByteField("lsaID2", 0x0) + ] + + +# +# 10.5.4 Call control information elements +# + +#10.5.4.1 Extensions of codesets +# This is only text and no packet + +class LockingShiftProcedure(Packet): + """Locking shift procedure Section 10.5.4.2""" + name = "Locking Shift Procedure" + fields_desc = [ + BitField("lockShift", 0x0, 1), + BitField("codesetId", 0x0, 3) + ] + + +class NonLockingShiftProcedure(Packet): + """Non-locking shift procedure Section 10.5.4.3""" + name = "Non-locking Shift Procedure" + fields_desc = [ + BitField("nonLockShift", 0x1, 1), + BitField("codesetId", 0x0, 3) + ] + + +class AuxiliaryStates(Packet): + """Auxiliary states Section 10.5.4.4""" + name = "Auxiliary States" + fields_desc = [ + XByteField("lengthAS", 0x3), + BitField("ext", 0x1, 1), + BitField("spare", 0x0, 3), + BitField("holdState", 0x0, 2), + BitField("mptyState", 0x0, 2) + ] + + +# len 3 to 15 +class BearerCapability(Packet): + """Bearer capability Section 10.5.4.5""" + name = "Bearer Capability" + fields_desc = [ + + XByteField("lengthBC", None), + + BitField("ext0", 0x1, 1), + BitField("radioChReq", 0x1, 2), + BitField("codingStd", 0x0, 1), + BitField("transMode", 0x0, 1), + BitField("infoTransCa", 0x0, 3), + # optional + ConditionalField(BitField("ext1", 0x1, 1), + lambda pkt: pkt.ext0 == 0), + ConditionalField(BitField("coding", None, 1), + lambda pkt: pkt.ext0 == 0), + ConditionalField(BitField("spare", None, 2), + lambda pkt: pkt.ext0 == 0), + ConditionalField(BitField("speechVers", 0x0, 4), + lambda pkt: pkt.ext0 == 0), + + ConditionalField(BitField("ext2", 0x1, 1), + lambda pkt: pkt.ext1 == 0), + ConditionalField(BitField("compress", None, 1), + lambda pkt: pkt.ext1 == 0), + ConditionalField(BitField("structure", None, 2), + lambda pkt: pkt.ext1 == 0), + ConditionalField(BitField("dupMode", None, 1), + lambda pkt: pkt.ext1 == 0), + ConditionalField(BitField("config", None, 1), + lambda pkt: pkt.ext1 == 0), + ConditionalField(BitField("nirr", None, 1), + lambda pkt: pkt.ext1 == 0), + ConditionalField(BitField("establi", 0x0, 1), + lambda pkt: pkt.ext1 == 0), + + BitField("ext3", None, 1), + BitField("accessId", None, 2), + BitField("rateAda", None, 2), + BitField("signaling", None, 3), + + ConditionalField(BitField("ext4", None, 1), + lambda pkt: pkt.ext3 == 0), + ConditionalField(BitField("otherITC", None, 2), + lambda pkt: pkt.ext3 == 0), + ConditionalField(BitField("otherRate", None, 2), + lambda pkt: pkt.ext3 == 0), + ConditionalField(BitField("spare1", 0x0, 3), + lambda pkt: pkt.ext3 == 0), + + ConditionalField(BitField("ext5", 0x1, 1), + lambda pkt: pkt.ext4 == 0), + ConditionalField(BitField("hdr", None, 1), + lambda pkt: pkt.ext4 == 0), + ConditionalField(BitField("multiFr", None, 1), + lambda pkt: pkt.ext4 == 0), + ConditionalField(BitField("mode", None, 1), + lambda pkt: pkt.ext4 == 0), + ConditionalField(BitField("lli", None, 1), + lambda pkt: pkt.ext4 == 0), + ConditionalField(BitField("assig", None, 1), + lambda pkt: pkt.ext4 == 0), + ConditionalField(BitField("inbNeg", None, 1), + lambda pkt: pkt.ext4 == 0), + ConditionalField(BitField("spare2", 0x0, 1), + lambda pkt: pkt.ext4 == 0), + + BitField("ext6", None, 1), + BitField("layer1Id", None, 2), + BitField("userInf", None, 4), + BitField("sync", None, 1), + + ConditionalField(BitField("ext7", None, 1), + lambda pkt: pkt.ext6 == 0), + ConditionalField(BitField("stopBit", None, 1), + lambda pkt: pkt.ext6 == 0), + ConditionalField(BitField("negoc", None, 1), + lambda pkt: pkt.ext6 == 0), + ConditionalField(BitField("nbDataBit", None, 1), + lambda pkt: pkt.ext6 == 0), + ConditionalField(BitField("userRate", None, 4), + lambda pkt: pkt.ext6 == 0), + + ConditionalField(BitField("ext8", None, 1), + lambda pkt: pkt.ext7 == 0), + ConditionalField(BitField("interRate", None, 2), + lambda pkt: pkt.ext7 == 0), + ConditionalField(BitField("nicTX", None, 1), + lambda pkt: pkt.ext7 == 0), + ConditionalField(BitField("nicRX", None, 1), + lambda pkt: pkt.ext7 == 0), + ConditionalField(BitField("parity", None, 3), + lambda pkt: pkt.ext7 == 0), + + ConditionalField(BitField("ext9", None, 1), + lambda pkt: pkt.ext8 == 0), + ConditionalField(BitField("connEle", None, 2), + lambda pkt: pkt.ext8 == 0), + ConditionalField(BitField("modemType", None, 5), + lambda pkt: pkt.ext8 == 0), + + ConditionalField(BitField("ext10", None, 1), + lambda pkt: pkt.ext9 == 0), + ConditionalField(BitField("otherModemType", None, 2), + lambda pkt: pkt.ext9 == 0), + ConditionalField(BitField("netUserRate", None, 5), + lambda pkt: pkt.ext9 == 0), + + ConditionalField(BitField("ext11", None, 1), + lambda pkt: pkt.ext10 == 0), + ConditionalField(BitField("chanCoding", None, 4), + lambda pkt: pkt.ext10 == 0), + ConditionalField(BitField("maxTrafficChan", None, 3), + lambda pkt: pkt.ext10 == 0), + + ConditionalField(BitField("ext12", None, 1), + lambda pkt: pkt.ext11 == 0), + ConditionalField(BitField("uimi", None, 3), + lambda pkt: pkt.ext11 == 0), + ConditionalField(BitField("airInterfaceUserRate", None, 4), + lambda pkt: pkt.ext11 == 0), + + ConditionalField(BitField("ext13", 0x1, 1), + lambda pkt: pkt.ext12 == 0), + ConditionalField(BitField("layer2Ch", None, 2), + lambda pkt: pkt.ext12 == 0), + ConditionalField(BitField("userInfoL2", 0x0, 5), + lambda pkt: pkt.ext12 == 0) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(2, 15, a, self.fields_desc, 1) + if res[0] is not 0: + p = p[:-res[0]] + if self.lengthBC is None: + p = struct.pack(">B", len(p)-1) + p[1:] + return p + pay + + +class CallControlCapabilities(Packet): + """Call Control Capabilities Section 10.5.4.5a""" + name = "Call Control Capabilities" + fields_desc = [ + XByteField("lengthCCC", 0x3), + BitField("spare", 0x0, 6), + BitField("pcp", 0x0, 1), + BitField("dtmf", 0x0, 1) + ] + + +class CallState(Packet): + """Call State Section 10.5.4.6""" + name = "Call State" + fields_desc = [ + BitField("codingStd", 0x0, 2), + BitField("stateValue", 0x0, 6) + ] + + +# len 3 to 43 +class CalledPartyBcdNumber(Packet): + """Called party BCD number Section 10.5.4.7""" + name = "Called Party BCD Number" + fields_desc = [ + XByteField("lengthCPBN", None), + BitField("ext", 0x1, 1), + BitField("typeNb", 0x0, 3), + BitField("nbPlanId", 0x0, 4), + # optional + BitField("nbDigit2", None, 4), + BitField("nbDigit1", None, 4), + BitField("nbDigit4", None, 4), + BitField("nbDigit3", None, 4), + + BitField("nbDigit6", None, 4), + BitField("nbDigit5", None, 4), + BitField("nbDigit8", None, 4), + BitField("nbDigit7", None, 4), + + BitField("nbDigit10", None, 4), + BitField("nbDigit9", None, 4), + BitField("nbDigit12", None, 4), + BitField("nbDigit11", None, 4), + + BitField("nbDigit14", None, 4), + BitField("nbDigit13", None, 4), + BitField("nbDigit16", None, 4), + BitField("nbDigit15", None, 4), + + BitField("nbDigit18", None, 4), + BitField("nbDigit17", None, 4), + BitField("nbDigit20", None, 4), + BitField("nbDigit19", None, 4), + + BitField("nbDigit22", None, 4), + BitField("nbDigit21", None, 4), + BitField("nbDigit24", None, 4), + BitField("nbDigit23", None, 4), + + BitField("nbDigit26", None, 4), + BitField("nbDigit25", None, 4), + BitField("nbDigit28", None, 4), + BitField("nbDigit27", None, 4), + + BitField("nbDigit30", None, 4), + BitField("nbDigit29", None, 4), + BitField("nbDigit32", None, 4), + BitField("nbDigit31", None, 4), + + BitField("nbDigit34", None, 4), + BitField("nbDigit33", None, 4), + BitField("nbDigit36", None, 4), + BitField("nbDigit35", None, 4), + + BitField("nbDigit38", None, 4), + BitField("nbDigit37", None, 4), + BitField("nbDigit40", None, 4), + BitField("nbDigit39", None, 4), +# ^^^^^^ 20 first optional bytes ^^^^^^^^^^^^^^^ + BitField("nbDigit42", None, 4), + BitField("nbDigit41", None, 4), + BitField("nbDigit44", None, 4), + BitField("nbDigit43", None, 4), + + BitField("nbDigit46", None, 4), + BitField("nbDigit45", None, 4), + BitField("nbDigit48", None, 4), + BitField("nbDigit47", None, 4), + + BitField("nbDigit50", None, 4), + BitField("nbDigit49", None, 4), + BitField("nbDigit52", None, 4), + BitField("nbDigit51", None, 4), + + BitField("nbDigit54", None, 4), + BitField("nbDigit53", None, 4), + BitField("nbDigit56", None, 4), + BitField("nbDigit55", None, 4), + + BitField("nbDigit58", None, 4), + BitField("nbDigit57", None, 4), + BitField("nbDigit60", None, 4), + BitField("nbDigit59", None, 4), + + BitField("nbDigit62", None, 4), + BitField("nbDigit61", None, 4), + BitField("nbDigit64", None, 4), + BitField("nbDigit63", None, 4), + + BitField("nbDigit66", None, 4), + BitField("nbDigit65", None, 4), + BitField("nbDigit68", None, 4), + BitField("nbDigit67", None, 4), + + BitField("nbDigit70", None, 4), + BitField("nbDigit69", None, 4), + BitField("nbDigit72", None, 4), + BitField("nbDigit71", None, 4), + + BitField("nbDigit74", None, 4), + BitField("nbDigit73", None, 4), + BitField("nbDigit76", None, 4), + BitField("nbDigit75", None, 4), + + BitField("nbDigit78", None, 4), + BitField("nbDigit77", None, 4), + BitField("nbDigit80", None, 4), + BitField("nbDigit79", None, 4), + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(2, 42, a, self.fields_desc, 1) + if self.lengthCPBN is None: + p = struct.pack(">B", res[1]) + p[1:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +# len 2 to 23 +class CalledPartySubaddress(Packet): + """Called party subaddress Section 10.5.4.8""" + name = "Called Party Subaddress" + fields_desc = [ + XByteField("lengthCPS", None), + # optional + BitField("ext", None, 1), + BitField("subAddr", None, 3), + BitField("oddEven", None, 1), + BitField("spare", None, 3), + + ByteField("subInfo0", None), + ByteField("subInfo1", None), + ByteField("subInfo2", None), + ByteField("subInfo3", None), + ByteField("subInfo4", None), + ByteField("subInfo5", None), + ByteField("subInfo6", None), + ByteField("subInfo7", None), + ByteField("subInfo8", None), + ByteField("subInfo9", None), + ByteField("subInfo10", None), + ByteField("subInfo11", None), + ByteField("subInfo12", None), + ByteField("subInfo13", None), + ByteField("subInfo14", None), + ByteField("subInfo15", None), + ByteField("subInfo16", None), + ByteField("subInfo17", None), + ByteField("subInfo18", None), + ByteField("subInfo19", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(2, 23, a, self.fields_desc, 1) + if self.lengthCPS is None: + p = struct.pack(">B", res[1]) + p[1:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +# len 3 to 14 +class CallingPartyBcdNumber(Packet): + """Called party subaddress Section 10.5.4.9""" + name = "Called Party Subaddress" + fields_desc = [ + XByteField("lengthCPBN", None), + BitField("ext", 0x1, 1), + BitField("typeNb", 0x0, 3), + BitField("nbPlanId", 0x0, 4), + # optional + ConditionalField(BitField("ext1", 0x1, 1), + lambda pkt: pkt.ext == 0), + ConditionalField(BitField("presId", None, 2), + lambda pkt: pkt.ext == 0), + ConditionalField(BitField("spare", None, 3), + lambda pkt: pkt.ext == 0), + ConditionalField(BitField("screenId", 0x0, 2), + lambda pkt: pkt.ext == 0), + + BitField("nbDigit2", None, 4), + BitField("nbDigit1", None, 4), + + BitField("nbDigit4", None, 4), + BitField("nbDigit3", None, 4), + + BitField("nbDigit6", None, 4), + BitField("nbDigit5", None, 4), + + BitField("nbDigit8", None, 4), + BitField("nbDigit7", None, 4), + + BitField("nbDigit10", None, 4), + BitField("nbDigit9", None, 4), + + BitField("nbDigit12", None, 4), + BitField("nbDigit11", None, 4), + + BitField("nbDigit14", None, 4), + BitField("nbDigit13", None, 4), + + BitField("nbDigit16", None, 4), + BitField("nbDigit15", None, 4), + + BitField("nbDigit18", None, 4), + BitField("nbDigit17", None, 4), + + BitField("nbDigit20", None, 4), + BitField("nbDigit19", None, 4), + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(2, 13, a, self.fields_desc, 1) + if res[0] is not 0: + p = p[:-res[0]] + if self.lengthCPBN is None: + p = struct.pack(">B", len(p)-1) + p[1:] + return p + pay + + +# len 2 to 23 +class CallingPartySubaddress(Packet): + """Calling party subaddress Section 10.5.4.10""" + name = "Calling Party Subaddress" + fields_desc = [ + XByteField("lengthCPS", None), + # optional + BitField("ext1", None, 1), + BitField("typeAddr", None, 3), + BitField("oddEven", None, 1), + BitField("spare", None, 3), + + ByteField("subInfo0", None), + ByteField("subInfo1", None), + ByteField("subInfo2", None), + ByteField("subInfo3", None), + ByteField("subInfo4", None), + ByteField("subInfo5", None), + ByteField("subInfo6", None), + ByteField("subInfo7", None), + ByteField("subInfo8", None), + ByteField("subInfo9", None), + ByteField("subInfo10", None), + ByteField("subInfo11", None), + ByteField("subInfo12", None), + ByteField("subInfo13", None), + ByteField("subInfo14", None), + ByteField("subInfo15", None), + ByteField("subInfo16", None), + ByteField("subInfo17", None), + ByteField("subInfo18", None), + ByteField("subInfo19", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(1, 22, a, self.fields_desc, 1) + if self.lengthCPS is None: + p = struct.pack(">B", res[1]) + p[1:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +# len 4 to 32 +class Cause(Packet): + """Cause Section 10.5.4.11""" + name = "Cause" + fields_desc = [ + + XByteField("lengthC", None), + + BitField("ext", 0x1, 1), + BitField("codingStd", 0x0, 2), + BitField("spare", 0x0, 1), + BitField("location", 0x0, 4), + + ConditionalField(BitField("ext1", 0x1, 1), + lambda pkt: pkt.ext == 0), + ConditionalField(BitField("recommendation", 0x1, 7), + lambda pkt: pkt.ext == 0), + # optional + BitField("ext2", None, 1), + BitField("causeValue", None, 7), + + ByteField("diagnositc0", None), + ByteField("diagnositc1", None), + ByteField("diagnositc2", None), + ByteField("diagnositc3", None), + ByteField("diagnositc4", None), + ByteField("diagnositc5", None), + ByteField("diagnositc6", None), + ByteField("diagnositc7", None), + ByteField("diagnositc8", None), + ByteField("diagnositc9", None), + ByteField("diagnositc10", None), + ByteField("diagnositc11", None), + ByteField("diagnositc12", None), + ByteField("diagnositc13", None), + ByteField("diagnositc14", None), + ByteField("diagnositc15", None), + ByteField("diagnositc16", None), + ByteField("diagnositc17", None), + ByteField("diagnositc18", None), + ByteField("diagnositc19", None), + ByteField("diagnositc20", None), + ByteField("diagnositc21", None), + ByteField("diagnositc22", None), + ByteField("diagnositc23", None), + ByteField("diagnositc24", None), + ByteField("diagnositc25", None), + ByteField("diagnositc26", None), + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(3, 31, a, self.fields_desc, 1) + if res[0] is not 0: + p = p[:-res[0]] + if self.lengthC is None: + p = struct.pack(">B", len(p)-1) + p[1:] + return p + pay + + +class ClirSuppression(Packet): + """CLIR suppression Section 10.5.4.11a""" + name = "Clir Suppression" + fields_desc = [ + ] + + +class ClirInvocation(Packet): + """CLIR invocation Section 10.5.4.11b""" + name = "Clir Invocation" + fields_desc = [ + ] + + +class CongestionLevel(Packet): + """Congestion level Section 10.5.4.12""" + name = "Congestion Level" + fields_desc = [ + BitField("notDef", 0x0, 4) # not defined by the std + ] + + +# len 3 to 14 +class ConnectedNumber(Packet): + """Connected number Section 10.5.4.13""" + name = "Connected Number" + fields_desc = [ + + XByteField("lengthCN", None), + + BitField("ext", 0x1, 1), + BitField("typeNb", 0x0, 3), + BitField("typePlanId", 0x0, 4), + # optional + ConditionalField(BitField("ext1", 0x1, 1), + lambda pkt: pkt.ext == 0), + ConditionalField(BitField("presId", None, 2), + lambda pkt: pkt.ext == 0), + ConditionalField(BitField("spare", None, 3), + lambda pkt: pkt.ext == 0), + ConditionalField(BitField("screenId", None, 2), + lambda pkt: pkt.ext == 0), + + BitField("nbDigit2", None, 4), + BitField("nbDigit1", None, 4), + + BitField("nbDigit4", None, 4), + BitField("nbDigit3", None, 4), + + BitField("nbDigit6", None, 4), + BitField("nbDigit5", None, 4), + + BitField("nbDigit8", None, 4), + BitField("nbDigit7", None, 4), + + BitField("nbDigit10", None, 4), + BitField("nbDigit9", None, 4), + + BitField("nbDigit12", None, 4), + BitField("nbDigit11", None, 4), + + BitField("nbDigit14", None, 4), + BitField("nbDigit13", None, 4), + + BitField("nbDigit16", None, 4), + BitField("nbDigit15", None, 4), + + BitField("nbDigit18", None, 4), + BitField("nbDigit17", None, 4), + + BitField("nbDigit20", None, 4), + BitField("nbDigit19", None, 4) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(2, 13, a, self.fields_desc, 1) + if res[0] is not 0: + p = p[:-res[0]] + if self.lengthCN is None: + p = struct.pack(">B", len(p)-1) + p[1:] + return p + pay + + +# len 2 to 23 +class ConnectedSubaddress(Packet): + """Connected subaddress Section 10.5.4.14""" + name = "Connected Subaddress" + fields_desc = [ + + XByteField("lengthCS", None), + # optional + BitField("ext", None, 1), + BitField("typeOfSub", None, 3), + BitField("oddEven", None, 1), + BitField("spare", None, 3), + + ByteField("subInfo0", None), + ByteField("subInfo1", None), + ByteField("subInfo2", None), + ByteField("subInfo3", None), + ByteField("subInfo4", None), + ByteField("subInfo5", None), + ByteField("subInfo6", None), + ByteField("subInfo7", None), + ByteField("subInfo8", None), + ByteField("subInfo9", None), + ByteField("subInfo10", None), + ByteField("subInfo11", None), + ByteField("subInfo12", None), + ByteField("subInfo13", None), + ByteField("subInfo14", None), + ByteField("subInfo15", None), + ByteField("subInfo16", None), + ByteField("subInfo17", None), + ByteField("subInfo18", None), + ByteField("subInfo19", None) + ] + + def post_build(self, p, pay): + aList = [] + a = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + for i in aList: + a.append(getattr(self, i)) + res = adapt(1, 22, a, self.fields_desc, 1) + if self.lengthCS is None: + p = struct.pack(">B", res[1]) + p[1:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +# len 2 to L3 (251) (done) +class Facility(Packet): + """Facility Section 10.5.4.15""" + name = "Facility" + fields_desc = [ + XByteField("lengthF", None), + # optional + ByteField("facilityInfo1", None), + ByteField("facilityInfo2", None), + ByteField("facilityInfo3", None), + ByteField("facilityInfo4", None), + ByteField("facilityInfo5", None), + ByteField("facilityInfo6", None), + ByteField("facilityInfo7", None), + ByteField("facilityInfo8", None), + ByteField("facilityInfo9", None), + ByteField("facilityInfo10", None), + ByteField("facilityInfo11", None), + ByteField("facilityInfo12", None), + ByteField("facilityInfo13", None), + ByteField("facilityInfo14", None), + ByteField("facilityInfo15", None), + ByteField("facilityInfo16", None), + ByteField("facilityInfo17", None), + ByteField("facilityInfo18", None), + ByteField("facilityInfo19", None), + ByteField("facilityInfo20", None), + ByteField("facilityInfo21", None), + ByteField("facilityInfo22", None), + ByteField("facilityInfo23", None), + ByteField("facilityInfo24", None), + ByteField("facilityInfo25", None), + ByteField("facilityInfo26", None), + ByteField("facilityInfo27", None), + ByteField("facilityInfo28", None), + ByteField("facilityInfo29", None), + ByteField("facilityInfo30", None), + ByteField("facilityInfo31", None), + ByteField("facilityInfo32", None), + ByteField("facilityInfo33", None), + ByteField("facilityInfo34", None), + ByteField("facilityInfo35", None), + ByteField("facilityInfo36", None), + ByteField("facilityInfo37", None), + ByteField("facilityInfo38", None), + ByteField("facilityInfo39", None), + ByteField("facilityInfo40", None), + ByteField("facilityInfo41", None), + ByteField("facilityInfo42", None), + ByteField("facilityInfo43", None), + ByteField("facilityInfo44", None), + ByteField("facilityInfo45", None), + ByteField("facilityInfo46", None), + ByteField("facilityInfo47", None), + ByteField("facilityInfo48", None), + ByteField("facilityInfo49", None), + ByteField("facilityInfo50", None), + ByteField("facilityInfo51", None), + ByteField("facilityInfo52", None), + ByteField("facilityInfo53", None), + ByteField("facilityInfo54", None), + ByteField("facilityInfo55", None), + ByteField("facilityInfo56", None), + ByteField("facilityInfo57", None), + ByteField("facilityInfo58", None), + ByteField("facilityInfo59", None), + ByteField("facilityInfo60", None), + ByteField("facilityInfo61", None), + ByteField("facilityInfo62", None), + ByteField("facilityInfo63", None), + ByteField("facilityInfo64", None), + ByteField("facilityInfo65", None), + ByteField("facilityInfo66", None), + ByteField("facilityInfo67", None), + ByteField("facilityInfo68", None), + ByteField("facilityInfo69", None), + ByteField("facilityInfo70", None), + ByteField("facilityInfo71", None), + ByteField("facilityInfo72", None), + ByteField("facilityInfo73", None), + ByteField("facilityInfo74", None), + ByteField("facilityInfo75", None), + ByteField("facilityInfo76", None), + ByteField("facilityInfo77", None), + ByteField("facilityInfo78", None), + ByteField("facilityInfo79", None), + ByteField("facilityInfo80", None), + ByteField("facilityInfo81", None), + ByteField("facilityInfo82", None), + ByteField("facilityInfo83", None), + ByteField("facilityInfo84", None), + ByteField("facilityInfo85", None), + ByteField("facilityInfo86", None), + ByteField("facilityInfo87", None), + ByteField("facilityInfo88", None), + ByteField("facilityInfo89", None), + ByteField("facilityInfo90", None), + ByteField("facilityInfo91", None), + ByteField("facilityInfo92", None), + ByteField("facilityInfo93", None), + ByteField("facilityInfo94", None), + ByteField("facilityInfo95", None), + ByteField("facilityInfo96", None), + ByteField("facilityInfo97", None), + ByteField("facilityInfo98", None), + ByteField("facilityInfo99", None), + ByteField("facilityInfo100", None), + ByteField("facilityInfo101", None), + ByteField("facilityInfo102", None), + ByteField("facilityInfo103", None), + ByteField("facilityInfo104", None), + ByteField("facilityInfo105", None), + ByteField("facilityInfo106", None), + ByteField("facilityInfo107", None), + ByteField("facilityInfo108", None), + ByteField("facilityInfo109", None), + ByteField("facilityInfo110", None), + ByteField("facilityInfo111", None), + ByteField("facilityInfo112", None), + ByteField("facilityInfo113", None), + ByteField("facilityInfo114", None), + ByteField("facilityInfo115", None), + ByteField("facilityInfo116", None), + ByteField("facilityInfo117", None), + ByteField("facilityInfo118", None), + ByteField("facilityInfo119", None), + ByteField("facilityInfo120", None), + ByteField("facilityInfo121", None), + ByteField("facilityInfo122", None), + ByteField("facilityInfo123", None), + ByteField("facilityInfo124", None), + ByteField("facilityInfo125", None), + ByteField("facilityInfo126", None), + ByteField("facilityInfo127", None), + ByteField("facilityInfo128", None), + ByteField("facilityInfo129", None), + ByteField("facilityInfo130", None), + ByteField("facilityInfo131", None), + ByteField("facilityInfo132", None), + ByteField("facilityInfo133", None), + ByteField("facilityInfo134", None), + ByteField("facilityInfo135", None), + ByteField("facilityInfo136", None), + ByteField("facilityInfo137", None), + ByteField("facilityInfo138", None), + ByteField("facilityInfo139", None), + ByteField("facilityInfo140", None), + ByteField("facilityInfo141", None), + ByteField("facilityInfo142", None), + ByteField("facilityInfo143", None), + ByteField("facilityInfo144", None), + ByteField("facilityInfo145", None), + ByteField("facilityInfo146", None), + ByteField("facilityInfo147", None), + ByteField("facilityInfo148", None), + ByteField("facilityInfo149", None), + ByteField("facilityInfo150", None), + ByteField("facilityInfo151", None), + ByteField("facilityInfo152", None), + ByteField("facilityInfo153", None), + ByteField("facilityInfo154", None), + ByteField("facilityInfo155", None), + ByteField("facilityInfo156", None), + ByteField("facilityInfo157", None), + ByteField("facilityInfo158", None), + ByteField("facilityInfo159", None), + ByteField("facilityInfo160", None), + ByteField("facilityInfo161", None), + ByteField("facilityInfo162", None), + ByteField("facilityInfo163", None), + ByteField("facilityInfo164", None), + ByteField("facilityInfo165", None), + ByteField("facilityInfo166", None), + ByteField("facilityInfo167", None), + ByteField("facilityInfo168", None), + ByteField("facilityInfo169", None), + ByteField("facilityInfo170", None), + ByteField("facilityInfo171", None), + ByteField("facilityInfo172", None), + ByteField("facilityInfo173", None), + ByteField("facilityInfo174", None), + ByteField("facilityInfo175", None), + ByteField("facilityInfo176", None), + ByteField("facilityInfo177", None), + ByteField("facilityInfo178", None), + ByteField("facilityInfo179", None), + ByteField("facilityInfo180", None), + ByteField("facilityInfo181", None), + ByteField("facilityInfo182", None), + ByteField("facilityInfo183", None), + ByteField("facilityInfo184", None), + ByteField("facilityInfo185", None), + ByteField("facilityInfo186", None), + ByteField("facilityInfo187", None), + ByteField("facilityInfo188", None), + ByteField("facilityInfo189", None), + ByteField("facilityInfo190", None), + ByteField("facilityInfo191", None), + ByteField("facilityInfo192", None), + ByteField("facilityInfo193", None), + ByteField("facilityInfo194", None), + ByteField("facilityInfo195", None), + ByteField("facilityInfo196", None), + ByteField("facilityInfo197", None), + ByteField("facilityInfo198", None), + ByteField("facilityInfo199", None), + ByteField("facilityInfo200", None), + ByteField("facilityInfo201", None), + ByteField("facilityInfo202", None), + ByteField("facilityInfo203", None), + ByteField("facilityInfo204", None), + ByteField("facilityInfo205", None), + ByteField("facilityInfo206", None), + ByteField("facilityInfo207", None), + ByteField("facilityInfo208", None), + ByteField("facilityInfo209", None), + ByteField("facilityInfo210", None), + ByteField("facilityInfo211", None), + ByteField("facilityInfo212", None), + ByteField("facilityInfo213", None), + ByteField("facilityInfo214", None), + ByteField("facilityInfo215", None), + ByteField("facilityInfo216", None), + ByteField("facilityInfo217", None), + ByteField("facilityInfo218", None), + ByteField("facilityInfo219", None), + ByteField("facilityInfo220", None), + ByteField("facilityInfo221", None), + ByteField("facilityInfo222", None), + ByteField("facilityInfo223", None), + ByteField("facilityInfo224", None), + ByteField("facilityInfo225", None), + ByteField("facilityInfo226", None), + ByteField("facilityInfo227", None), + ByteField("facilityInfo228", None), + ByteField("facilityInfo229", None), + ByteField("facilityInfo230", None), + ByteField("facilityInfo231", None), + ByteField("facilityInfo232", None), + ByteField("facilityInfo233", None), + ByteField("facilityInfo234", None), + ByteField("facilityInfo235", None), + ByteField("facilityInfo236", None), + ByteField("facilityInfo237", None), + ByteField("facilityInfo238", None), + ByteField("facilityInfo239", None), + ByteField("facilityInfo240", None), + ByteField("facilityInfo241", None), + ByteField("facilityInfo242", None), + ByteField("facilityInfo243", None), + ByteField("facilityInfo244", None), + ByteField("facilityInfo245", None), + ByteField("facilityInfo246", None), + ByteField("facilityInfo247", None), + ByteField("facilityInfo248", None), + ByteField("facilityInfo249", None) + ] + + def post_build(self, p, pay): + aList = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + a = [] + for i in aList: + a.append(getattr(self, i)) + res = adapt(7, 250, a, self.fields_desc, 1) + if self.lengthF is None: + p = struct.pack(">B", res[1]) + p[1:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +#len 2 to 5 +class HighLayerCompatibility(Packet): + """High layer compatibility Section 10.5.4.16""" + name = "High Layer Compatibility" + fields_desc = [ + + XByteField("lengthHLC", None), + # optional + BitField("ext", None, 1), + BitField("codingStd", None, 2), + BitField("interpret", None, 3), + BitField("presMeth", None, 2), + + BitField("ext1", None, 1), + BitField("highLayerId", None, 7), + + ConditionalField(BitField("ext2", 0x1, 1), + lambda pkt: pkt.ext1 == 0), + ConditionalField(BitField("exHiLayerId", 0x0, 7), + lambda pkt: pkt.ext1 == 0), + ] + + def post_build(self, p, pay): + aList = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + a = [] + for i in aList: + a.append(getattr(self, i)) + res = adapt(1, 4, a, self.fields_desc, 1) + if res[0] is not 0: + p = p[:-res[0]] + if self.lengthHLC is None: + p = struct.pack(">B", len(p)-1) + p[1:] + return p + pay +# +# 10.5.4.16.1 Static conditions for the high layer +# compatibility IE contents +# + + +class KeypadFacility(Packet): + """Keypad facility Section 10.5.4.17""" + name = "Keypad Facility" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("keyPadInfo", 0x0, 7) + ] + + +# len 2 to 15 +class LowLayerCompatibility(Packet): + """Low layer compatibility Section 10.5.4.18""" + name = "Low Layer Compatibility" + fields_desc = [ + + XByteField("lengthLLC", None), + # optional + ByteField("rest0", None), + ByteField("rest1", None), + ByteField("rest2", None), + ByteField("rest3", None), + ByteField("rest4", None), + ByteField("rest5", None), + ByteField("rest6", None), + ByteField("rest7", None), + ByteField("rest8", None), + ByteField("rest9", None), + ByteField("rest10", None), + ByteField("rest11", None), + ByteField("rest12", None) + ] + + def post_build(self, p, pay): + aList = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + a = [] + for i in aList: + a.append(getattr(self, i)) + res = adapt(1, 14, a, self.fields_desc, 1) + if self.lengthLLC is None: + p = struct.pack(">B", res[1]) + p[1:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +class MoreData(Packet): + """More data Section 10.5.4.19""" + name = "More Data" + fields_desc = [ + ] + + +class NotificationIndicator(Packet): + """Notification indicator Section 10.5.4.20""" + name = "Notification Indicator" + fields_desc = [ + BitField("ext1", 0x1, 1), + BitField("notifDesc", 0x0, 7) + ] + + +class ProgressIndicator(Packet): + """Progress indicator Section 10.5.4.21""" + name = "Progress Indicator" + fields_desc = [ + XByteField("lengthPI", 0x2), + BitField("ext", 0x1, 1), + BitField("codingStd", 0x0, 2), + BitField("spare", 0x0, 1), + BitField("location", 0x0, 4), + BitField("ext1", 0x1, 1), + BitField("progressDesc", 0x0, 7) + ] + + +class RecallType(Packet): + """Recall type $(CCBS)$ Section 10.5.4.21a""" + name = "Recall Type $(CCBS)$" + fields_desc = [ + BitField("spare", 0x0, 5), + BitField("recallType", 0x0, 3) + ] + + +# len 3 to 19 +class RedirectingPartyBcdNumber(Packet): + """Redirecting party BCD number Section 10.5.4.21b""" + name = "Redirecting Party BCD Number" + fields_desc = [ + + XByteField("lengthRPBN", None), + + BitField("ext", 0x1, 1), + BitField("typeNb", 0x0, 3), + BitField("numberingPlan", 0x0, 4), + # optional + ConditionalField(BitField("ext1", 0x1, 1), + lambda pkt: pkt.ext == 0), + ConditionalField(BitField("presId", 0x0, 2), + lambda pkt: pkt.ext == 0), + ConditionalField(BitField("spare", 0x0, 3), + lambda pkt: pkt.ext == 0), + ConditionalField(BitField("screenId", 0x0, 2), + lambda pkt: pkt.ext == 0), + + BitField("nbDigit2", None, 4), + BitField("nbDigit1", None, 4), + + BitField("nbDigit4", None, 4), + BitField("nbDigit3", None, 4), + + BitField("nbDigit6", None, 4), + BitField("nbDigit5", None, 4), + + BitField("nbDigit8", None, 4), + BitField("nbDigit7", None, 4), + + BitField("nbDigit10", None, 4), + BitField("nbDigit9", None, 4), + + BitField("nbDigit12", None, 4), + BitField("nbDigit11", None, 4), + + BitField("nbDigit14", None, 4), + BitField("nbDigit13", None, 4), + + BitField("nbDigit16", None, 4), + BitField("nbDigit15", None, 4), + + BitField("nbDigit18", None, 4), + BitField("nbDigit17", None, 4), + + BitField("nbDigit20", None, 4), + BitField("nbDigit19", None, 4), + + BitField("nbDigit22", None, 4), + BitField("nbDigit21", None, 4), + + BitField("nbDigit24", None, 4), + BitField("nbDigit23", None, 4), + + BitField("nbDigit26", None, 4), + BitField("nbDigit25", None, 4), + + BitField("nbDigit28", None, 4), + BitField("nbDigit27", None, 4), + + BitField("nbDigit30", None, 4), + BitField("nbDigit29", None, 4), + ] + + def post_build(self, p, pay): + aList = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + a = [] + for i in aList: + a.append(getattr(self, i)) + res = adapt(2, 18, a, self.fields_desc, 1) + if res[0] is not 0: + p = p[:-res[0]] + if self.lengthRPBN is None: + p = struct.pack(">B", len(p)-1) + p[1:] + return p + pay + + +# length 2 to 23 +class RedirectingPartySubaddress(Packet): + """Redirecting party subaddress Section 10.5.4.21c""" + name = "Redirecting Party BCD Number" + fields_desc = [ + + XByteField("lengthRPS", None), + # optional + BitField("ext", None, 1), + BitField("typeSub", None, 3), + BitField("oddEven", None, 1), + BitField("spare", None, 3), + + ByteField("subInfo0", None), + ByteField("subInfo1", None), + ByteField("subInfo2", None), + ByteField("subInfo3", None), + ByteField("subInfo4", None), + ByteField("subInfo5", None), + ByteField("subInfo6", None), + ByteField("subInfo7", None), + ByteField("subInfo8", None), + ByteField("subInfo9", None), + ByteField("subInfo10", None), + ByteField("subInfo11", None), + ByteField("subInfo12", None), + ByteField("subInfo13", None), + ByteField("subInfo14", None), + ByteField("subInfo15", None), + ByteField("subInfo16", None), + ByteField("subInfo17", None), + ByteField("subInfo18", None), + ByteField("subInfo19", None) + ] + + def post_build(self, p, pay): + aList = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + a = [] + for i in aList: + a.append(getattr(self, i)) + res = adapt(1, 22, a, self.fields_desc, 1) + if self.lengthRPS is None: + p = struct.pack(">B", res[1]) + p[1:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +class RepeatIndicator(Packet): + """Repeat indicator Section 10.5.4.22""" + name = "Repeat Indicator" + fields_desc = [ + BitField("repeatIndic", 0x0, 4) + ] + + +# no upper length min 2(max for L3) (251) +class SetupContainer(Packet): + """SETUP Container $(CCBS)$ Section 10.5.4.22b""" + name = "Setup Container $(CCBS)$" + fields_desc = [ + XByteField("lengthSC", None), + # optional + ByteField("mess1", None), + ByteField("mess2", None), + ByteField("mess3", None), + ByteField("mess4", None), + ByteField("mess5", None), + ByteField("mess6", None), + ByteField("mess7", None), + ByteField("mess8", None), + ByteField("mess9", None), + ByteField("mess10", None), + ByteField("mess11", None), + ByteField("mess12", None), + ByteField("mess13", None), + ByteField("mess14", None), + ByteField("mess15", None), + ByteField("mess16", None), + ByteField("mess17", None), + ByteField("mess18", None), + ByteField("mess19", None), + ByteField("mess20", None), + ByteField("mess21", None), + ByteField("mess22", None), + ByteField("mess23", None), + ByteField("mess24", None), + ByteField("mess25", None), + ByteField("mess26", None), + ByteField("mess27", None), + ByteField("mess28", None), + ByteField("mess29", None), + ByteField("mess30", None), + ByteField("mess31", None), + ByteField("mess32", None), + ByteField("mess33", None), + ByteField("mess34", None), + ByteField("mess35", None), + ByteField("mess36", None), + ByteField("mess37", None), + ByteField("mess38", None), + ByteField("mess39", None), + ByteField("mess40", None), + ByteField("mess41", None), + ByteField("mess42", None), + ByteField("mess43", None), + ByteField("mess44", None), + ByteField("mess45", None), + ByteField("mess46", None), + ByteField("mess47", None), + ByteField("mess48", None), + ByteField("mess49", None), + ByteField("mess50", None), + ByteField("mess51", None), + ByteField("mess52", None), + ByteField("mess53", None), + ByteField("mess54", None), + ByteField("mess55", None), + ByteField("mess56", None), + ByteField("mess57", None), + ByteField("mess58", None), + ByteField("mess59", None), + ByteField("mess60", None), + ByteField("mess61", None), + ByteField("mess62", None), + ByteField("mess63", None), + ByteField("mess64", None), + ByteField("mess65", None), + ByteField("mess66", None), + ByteField("mess67", None), + ByteField("mess68", None), + ByteField("mess69", None), + ByteField("mess70", None), + ByteField("mess71", None), + ByteField("mess72", None), + ByteField("mess73", None), + ByteField("mess74", None), + ByteField("mess75", None), + ByteField("mess76", None), + ByteField("mess77", None), + ByteField("mess78", None), + ByteField("mess79", None), + ByteField("mess80", None), + ByteField("mess81", None), + ByteField("mess82", None), + ByteField("mess83", None), + ByteField("mess84", None), + ByteField("mess85", None), + ByteField("mess86", None), + ByteField("mess87", None), + ByteField("mess88", None), + ByteField("mess89", None), + ByteField("mess90", None), + ByteField("mess91", None), + ByteField("mess92", None), + ByteField("mess93", None), + ByteField("mess94", None), + ByteField("mess95", None), + ByteField("mess96", None), + ByteField("mess97", None), + ByteField("mess98", None), + ByteField("mess99", None), + ByteField("mess100", None), + ByteField("mess101", None), + ByteField("mess102", None), + ByteField("mess103", None), + ByteField("mess104", None), + ByteField("mess105", None), + ByteField("mess106", None), + ByteField("mess107", None), + ByteField("mess108", None), + ByteField("mess109", None), + ByteField("mess110", None), + ByteField("mess111", None), + ByteField("mess112", None), + ByteField("mess113", None), + ByteField("mess114", None), + ByteField("mess115", None), + ByteField("mess116", None), + ByteField("mess117", None), + ByteField("mess118", None), + ByteField("mess119", None), + ByteField("mess120", None), + ByteField("mess121", None), + ByteField("mess122", None), + ByteField("mess123", None), + ByteField("mess124", None), + ByteField("mess125", None), + ByteField("mess126", None), + ByteField("mess127", None), + ByteField("mess128", None), + ByteField("mess129", None), + ByteField("mess130", None), + ByteField("mess131", None), + ByteField("mess132", None), + ByteField("mess133", None), + ByteField("mess134", None), + ByteField("mess135", None), + ByteField("mess136", None), + ByteField("mess137", None), + ByteField("mess138", None), + ByteField("mess139", None), + ByteField("mess140", None), + ByteField("mess141", None), + ByteField("mess142", None), + ByteField("mess143", None), + ByteField("mess144", None), + ByteField("mess145", None), + ByteField("mess146", None), + ByteField("mess147", None), + ByteField("mess148", None), + ByteField("mess149", None), + ByteField("mess150", None), + ByteField("mess151", None), + ByteField("mess152", None), + ByteField("mess153", None), + ByteField("mess154", None), + ByteField("mess155", None), + ByteField("mess156", None), + ByteField("mess157", None), + ByteField("mess158", None), + ByteField("mess159", None), + ByteField("mess160", None), + ByteField("mess161", None), + ByteField("mess162", None), + ByteField("mess163", None), + ByteField("mess164", None), + ByteField("mess165", None), + ByteField("mess166", None), + ByteField("mess167", None), + ByteField("mess168", None), + ByteField("mess169", None), + ByteField("mess170", None), + ByteField("mess171", None), + ByteField("mess172", None), + ByteField("mess173", None), + ByteField("mess174", None), + ByteField("mess175", None), + ByteField("mess176", None), + ByteField("mess177", None), + ByteField("mess178", None), + ByteField("mess179", None), + ByteField("mess180", None), + ByteField("mess181", None), + ByteField("mess182", None), + ByteField("mess183", None), + ByteField("mess184", None), + ByteField("mess185", None), + ByteField("mess186", None), + ByteField("mess187", None), + ByteField("mess188", None), + ByteField("mess189", None), + ByteField("mess190", None), + ByteField("mess191", None), + ByteField("mess192", None), + ByteField("mess193", None), + ByteField("mess194", None), + ByteField("mess195", None), + ByteField("mess196", None), + ByteField("mess197", None), + ByteField("mess198", None), + ByteField("mess199", None), + ByteField("mess200", None), + ByteField("mess201", None), + ByteField("mess202", None), + ByteField("mess203", None), + ByteField("mess204", None), + ByteField("mess205", None), + ByteField("mess206", None), + ByteField("mess207", None), + ByteField("mess208", None), + ByteField("mess209", None), + ByteField("mess210", None), + ByteField("mess211", None), + ByteField("mess212", None), + ByteField("mess213", None), + ByteField("mess214", None), + ByteField("mess215", None), + ByteField("mess216", None), + ByteField("mess217", None), + ByteField("mess218", None), + ByteField("mess219", None), + ByteField("mess220", None), + ByteField("mess221", None), + ByteField("mess222", None), + ByteField("mess223", None), + ByteField("mess224", None), + ByteField("mess225", None), + ByteField("mess226", None), + ByteField("mess227", None), + ByteField("mess228", None), + ByteField("mess229", None), + ByteField("mess230", None), + ByteField("mess231", None), + ByteField("mess232", None), + ByteField("mess233", None), + ByteField("mess234", None), + ByteField("mess235", None), + ByteField("mess236", None), + ByteField("mess237", None), + ByteField("mess238", None), + ByteField("mess239", None), + ByteField("mess240", None), + ByteField("mess241", None), + ByteField("mess242", None), + ByteField("mess243", None), + ByteField("mess244", None), + ByteField("mess245", None), + ByteField("mess246", None), + ByteField("mess247", None), + ByteField("mess248", None), + ByteField("mess249", None), + ] + + def post_build(self, p, pay): + aList = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + a = [] + for i in aList: + a.append(getattr(self, i)) + res = adapt(1, 250, a, self.fields_desc, 1) + if self.lengthSC is None: + p = struct.pack(">B", res[1]) + p[1:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +class Signal(Packet): + """Signal Section 10.5.4.23""" + name = "Signal" + fields_desc = [ + ByteField("sigValue", 0x0) + ] + + +# length 2 to max for L3 message (251) +class SsVersionIndicator(Packet): + """SS Version Indicator Section 10.5.4.24""" + name = "SS Version Indicator" + fields_desc = [ + XByteField("lengthSVI", None), + # optional + ByteField("info1", None), + ByteField("info2", None), + ByteField("info3", None), + ByteField("info4", None), + ByteField("info5", None), + ByteField("info6", None), + ByteField("info7", None), + ByteField("info8", None), + ByteField("info9", None), + ByteField("info10", None), + ByteField("info11", None), + ByteField("info12", None), + ByteField("info13", None), + ByteField("info14", None), + ByteField("info15", None), + ByteField("info16", None), + ByteField("info17", None), + ByteField("info18", None), + ByteField("info19", None), + ByteField("info20", None), + ByteField("info21", None), + ByteField("info22", None), + ByteField("info23", None), + ByteField("info24", None), + ByteField("info25", None), + ByteField("info26", None), + ByteField("info27", None), + ByteField("info28", None), + ByteField("info29", None), + ByteField("info30", None), + ByteField("info31", None), + ByteField("info32", None), + ByteField("info33", None), + ByteField("info34", None), + ByteField("info35", None), + ByteField("info36", None), + ByteField("info37", None), + ByteField("info38", None), + ByteField("info39", None), + ByteField("info40", None), + ByteField("info41", None), + ByteField("info42", None), + ByteField("info43", None), + ByteField("info44", None), + ByteField("info45", None), + ByteField("info46", None), + ByteField("info47", None), + ByteField("info48", None), + ByteField("info49", None), + ByteField("info50", None), + ByteField("info51", None), + ByteField("info52", None), + ByteField("info53", None), + ByteField("info54", None), + ByteField("info55", None), + ByteField("info56", None), + ByteField("info57", None), + ByteField("info58", None), + ByteField("info59", None), + ByteField("info60", None), + ByteField("info61", None), + ByteField("info62", None), + ByteField("info63", None), + ByteField("info64", None), + ByteField("info65", None), + ByteField("info66", None), + ByteField("info67", None), + ByteField("info68", None), + ByteField("info69", None), + ByteField("info70", None), + ByteField("info71", None), + ByteField("info72", None), + ByteField("info73", None), + ByteField("info74", None), + ByteField("info75", None), + ByteField("info76", None), + ByteField("info77", None), + ByteField("info78", None), + ByteField("info79", None), + ByteField("info80", None), + ByteField("info81", None), + ByteField("info82", None), + ByteField("info83", None), + ByteField("info84", None), + ByteField("info85", None), + ByteField("info86", None), + ByteField("info87", None), + ByteField("info88", None), + ByteField("info89", None), + ByteField("info90", None), + ByteField("info91", None), + ByteField("info92", None), + ByteField("info93", None), + ByteField("info94", None), + ByteField("info95", None), + ByteField("info96", None), + ByteField("info97", None), + ByteField("info98", None), + ByteField("info99", None), + ByteField("info100", None), + ByteField("info101", None), + ByteField("info102", None), + ByteField("info103", None), + ByteField("info104", None), + ByteField("info105", None), + ByteField("info106", None), + ByteField("info107", None), + ByteField("info108", None), + ByteField("info109", None), + ByteField("info110", None), + ByteField("info111", None), + ByteField("info112", None), + ByteField("info113", None), + ByteField("info114", None), + ByteField("info115", None), + ByteField("info116", None), + ByteField("info117", None), + ByteField("info118", None), + ByteField("info119", None), + ByteField("info120", None), + ByteField("info121", None), + ByteField("info122", None), + ByteField("info123", None), + ByteField("info124", None), + ByteField("info125", None), + ByteField("info126", None), + ByteField("info127", None), + ByteField("info128", None), + ByteField("info129", None), + ByteField("info130", None), + ByteField("info131", None), + ByteField("info132", None), + ByteField("info133", None), + ByteField("info134", None), + ByteField("info135", None), + ByteField("info136", None), + ByteField("info137", None), + ByteField("info138", None), + ByteField("info139", None), + ByteField("info140", None), + ByteField("info141", None), + ByteField("info142", None), + ByteField("info143", None), + ByteField("info144", None), + ByteField("info145", None), + ByteField("info146", None), + ByteField("info147", None), + ByteField("info148", None), + ByteField("info149", None), + ByteField("info150", None), + ByteField("info151", None), + ByteField("info152", None), + ByteField("info153", None), + ByteField("info154", None), + ByteField("info155", None), + ByteField("info156", None), + ByteField("info157", None), + ByteField("info158", None), + ByteField("info159", None), + ByteField("info160", None), + ByteField("info161", None), + ByteField("info162", None), + ByteField("info163", None), + ByteField("info164", None), + ByteField("info165", None), + ByteField("info166", None), + ByteField("info167", None), + ByteField("info168", None), + ByteField("info169", None), + ByteField("info170", None), + ByteField("info171", None), + ByteField("info172", None), + ByteField("info173", None), + ByteField("info174", None), + ByteField("info175", None), + ByteField("info176", None), + ByteField("info177", None), + ByteField("info178", None), + ByteField("info179", None), + ByteField("info180", None), + ByteField("info181", None), + ByteField("info182", None), + ByteField("info183", None), + ByteField("info184", None), + ByteField("info185", None), + ByteField("info186", None), + ByteField("info187", None), + ByteField("info188", None), + ByteField("info189", None), + ByteField("info190", None), + ByteField("info191", None), + ByteField("info192", None), + ByteField("info193", None), + ByteField("info194", None), + ByteField("info195", None), + ByteField("info196", None), + ByteField("info197", None), + ByteField("info198", None), + ByteField("info199", None), + ByteField("info200", None), + ByteField("info201", None), + ByteField("info202", None), + ByteField("info203", None), + ByteField("info204", None), + ByteField("info205", None), + ByteField("info206", None), + ByteField("info207", None), + ByteField("info208", None), + ByteField("info209", None), + ByteField("info210", None), + ByteField("info211", None), + ByteField("info212", None), + ByteField("info213", None), + ByteField("info214", None), + ByteField("info215", None), + ByteField("info216", None), + ByteField("info217", None), + ByteField("info218", None), + ByteField("info219", None), + ByteField("info220", None), + ByteField("info221", None), + ByteField("info222", None), + ByteField("info223", None), + ByteField("info224", None), + ByteField("info225", None), + ByteField("info226", None), + ByteField("info227", None), + ByteField("info228", None), + ByteField("info229", None), + ByteField("info230", None), + ByteField("info231", None), + ByteField("info232", None), + ByteField("info233", None), + ByteField("info234", None), + ByteField("info235", None), + ByteField("info236", None), + ByteField("info237", None), + ByteField("info238", None), + ByteField("info239", None), + ByteField("info240", None), + ByteField("info241", None), + ByteField("info242", None), + ByteField("info243", None), + ByteField("info244", None), + ByteField("info245", None), + ByteField("info246", None), + ByteField("info247", None), + ByteField("info248", None), + ByteField("info249", None), + ] + + def post_build(self, p, pay): + aList = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + a = [] + for i in aList: + a.append(getattr(self, i)) + res = adapt(1, 250, a, self.fields_desc, 1) + if self.lengthSVI is None: + p = struct.pack(">B", res[1]) + p[1:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +# length 3 to 35 or 131 +class UserUser(Packet): + """User-user Section 10.5.4.25""" + name = "User-User" + fields_desc = [ + + XByteField("lengthUU", None), # dynamic length of field depending + # of the type of message + # let user decide which length he + # wants to take + # => more fuzzing options + ByteField("userUserPD", 0x0), + # optional + ByteField("userUserInfo1", None), + ByteField("userUserInfo2", None), + ByteField("userUserInfo3", None), + ByteField("userUserInfo4", None), + ByteField("userUserInfo5", None), + ByteField("userUserInfo6", None), + ByteField("userUserInfo7", None), + ByteField("userUserInfo8", None), + ByteField("userUserInfo9", None), + ByteField("userUserInfo10", None), + ByteField("userUserInfo11", None), + ByteField("userUserInfo12", None), + ByteField("userUserInfo13", None), + ByteField("userUserInfo14", None), + ByteField("userUserInfo15", None), + ByteField("userUserInfo16", None), + ByteField("userUserInfo17", None), + ByteField("userUserInfo18", None), + ByteField("userUserInfo19", None), + ByteField("userUserInfo20", None), + ByteField("userUserInfo21", None), + ByteField("userUserInfo22", None), + ByteField("userUserInfo23", None), + ByteField("userUserInfo24", None), + ByteField("userUserInfo25", None), + ByteField("userUserInfo26", None), + ByteField("userUserInfo27", None), + ByteField("userUserInfo28", None), + ByteField("userUserInfo29", None), + ByteField("userUserInfo30", None), + ByteField("userUserInfo31", None), + ByteField("userUserInfo32", None), + # long packet + ByteField("userUserInfo33", None), + ByteField("userUserInfo34", None), + ByteField("userUserInfo35", None), + ByteField("userUserInfo36", None), + ByteField("userUserInfo37", None), + ByteField("userUserInfo38", None), + ByteField("userUserInfo39", None), + ByteField("userUserInfo40", None), + ByteField("userUserInfo41", None), + ByteField("userUserInfo42", None), + ByteField("userUserInfo43", None), + ByteField("userUserInfo44", None), + ByteField("userUserInfo45", None), + ByteField("userUserInfo46", None), + ByteField("userUserInfo47", None), + ByteField("userUserInfo48", None), + ByteField("userUserInfo49", None), + ByteField("userUserInfo50", None), + ByteField("userUserInfo51", None), + ByteField("userUserInfo52", None), + ByteField("userUserInfo53", None), + ByteField("userUserInfo54", None), + ByteField("userUserInfo55", None), + ByteField("userUserInfo56", None), + ByteField("userUserInfo57", None), + ByteField("userUserInfo58", None), + ByteField("userUserInfo59", None), + ByteField("userUserInfo60", None), + ByteField("userUserInfo61", None), + ByteField("userUserInfo62", None), + ByteField("userUserInfo63", None), + ByteField("userUserInfo64", None), + ByteField("userUserInfo65", None), + ByteField("userUserInfo66", None), + ByteField("userUserInfo67", None), + ByteField("userUserInfo68", None), + ByteField("userUserInfo69", None), + ByteField("userUserInfo70", None), + ByteField("userUserInfo71", None), + ByteField("userUserInfo72", None), + ByteField("userUserInfo73", None), + ByteField("userUserInfo74", None), + ByteField("userUserInfo75", None), + ByteField("userUserInfo76", None), + ByteField("userUserInfo77", None), + ByteField("userUserInfo78", None), + ByteField("userUserInfo79", None), + ByteField("userUserInfo80", None), + ByteField("userUserInfo81", None), + ByteField("userUserInfo82", None), + ByteField("userUserInfo83", None), + ByteField("userUserInfo84", None), + ByteField("userUserInfo85", None), + ByteField("userUserInfo86", None), + ByteField("userUserInfo87", None), + ByteField("userUserInfo88", None), + ByteField("userUserInfo89", None), + ByteField("userUserInfo90", None), + ByteField("userUserInfo91", None), + ByteField("userUserInfo92", None), + ByteField("userUserInfo93", None), + ByteField("userUserInfo94", None), + ByteField("userUserInfo95", None), + ByteField("userUserInfo96", None), + ByteField("userUserInfo97", None), + ByteField("userUserInfo98", None), + ByteField("userUserInfo99", None), + ByteField("userUserInfo100", None), + ByteField("userUserInfo101", None), + ByteField("userUserInfo102", None), + ByteField("userUserInfo103", None), + ByteField("userUserInfo104", None), + ByteField("userUserInfo105", None), + ByteField("userUserInfo106", None), + ByteField("userUserInfo107", None), + ByteField("userUserInfo108", None), + ByteField("userUserInfo109", None), + ByteField("userUserInfo110", None), + ByteField("userUserInfo111", None), + ByteField("userUserInfo112", None), + ByteField("userUserInfo113", None), + ByteField("userUserInfo114", None), + ByteField("userUserInfo115", None), + ByteField("userUserInfo116", None), + ByteField("userUserInfo117", None), + ByteField("userUserInfo118", None), + ByteField("userUserInfo119", None), + ByteField("userUserInfo120", None), + ByteField("userUserInfo121", None), + ByteField("userUserInfo122", None), + ByteField("userUserInfo123", None), + ByteField("userUserInfo124", None), + ByteField("userUserInfo125", None), + ByteField("userUserInfo126", None), + ByteField("userUserInfo127", None), + ByteField("userUserInfo128", None), + ByteField("userUserInfo129", None), + ByteField("userUserInfo130", None), + ByteField("userUserInfo131", None) + ] + + def post_build(self, p, pay): + aList = [] + i = 0 + for i in range(0, len(self.fields_desc)): + aList.append(self.fields_desc[i].name) + a = [] + for i in aList: + a.append(getattr(self, i)) + res = adapt(2, 133, a, self.fields_desc, 1) + if self.lengthUU is None: + p = struct.pack(">B", res[1]) + p[1:] + if res[0] is not 0: + p = p[:-res[0]] + return p + pay + + +class AlertingPattern(Packet): + """Alerting Pattern 10.5.4.26""" + name = "Alerting Pattern" + fields_desc = [ + XByteField("lengthAP", 0x3), + BitField("spare", 0x0, 4), + BitField("alertingValue", 0x0, 4) + ] + + +class AllowedActions(Packet): + """Allowed actions $(CCBS)$ Section 10.5.4.26""" + name = "Allowed Actions $(CCBS)$" + fields_desc = [ + XByteField("lengthAP", 0x3), + BitField("CCBS", 0x0, 1), + BitField("spare", 0x0, 7) + ] + + +# +# 10.5.5 GPRS mobility management information elements +# + + +class AttachType(Packet): + """Attach type Section 10.5.5.2""" + name = "Attach Type" + fields_desc = [ + BitField("spare", 0x0, 1), + BitField("type", 0x1, 3) + ] + + +if __name__ == "__main__": + interact(mydict=globals(), mybanner="Scapy GSM-UM (Air) Addon") diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/gtp.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/gtp.py new file mode 100644 index 00000000..008a0200 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/gtp.py @@ -0,0 +1,546 @@ +#! /usr/bin/env python + +## Copyright (C) 2014 Guillaume Valadon +## 2014 Alexis Sultan +## 2012 ffranz +## +## This program is published under a GPLv2 license + +# scapy.contrib.description = GTP +# scapy.contrib.status = loads + +import time +import logging + +from scapy.packet import * +from scapy.fields import * +from scapy.layers.inet import IP, UDP + +# GTP Data types + +GTPmessageType = { 1: "echo_request", + 2: "echo_response", + 16: "create_pdp_context_req", + 17: "create_pdp_context_res", + 20: "delete_pdp_context_req", + 21: "delete_pdp_context_res", + 26: "error_indication", + 27: "pdu_notification_req", + 255: "gtp_u_header" } + +IEType = { 1: "Cause", + 2: "IMSI", + 3: "RAI", + 4: "TLLI", + 5: "P_TMSI", + 14: "Recovery", + 15: "SelectionMode", + 16: "TEIDI", + 17: "TEICP", + 19: "TeardownInd", + 20: "NSAPI", + 26: "ChargingChrt", + 27: "TraceReference", + 28: "TraceType", + 128: "EndUserAddress", + 131: "AccessPointName", + 132: "ProtocolConfigurationOptions", + 133: "GSNAddress", + 134: "MSInternationalNumber", + 135: "QoS", + 148: "CommonFlags", + 151: "RatType", + 152: "UserLocationInformation", + 153: "MSTimeZone", + 154: "IMEI" } + +CauseValues = { 0: "Request IMSI", + 1: "Request IMEI", + 2: "Request IMSI and IMEI", + 3: "No identity needed", + 4: "MS Refuses", + 5: "MS is not GPRS Responding", + 128: "Request accepted", + 129: "New PDP type due to network preference", + 130: "New PDP type due to single address bearer only", + 192: "Non-existent", + 193: "Invalid message format", + 194: "IMSI not known", + 195: "MS is GPRS Detached", + 196: "MS is not GPRS Responding", + 197: "MS Refuses", + 198: "Version not supported", + 199: "No resources available", + 200: "Service not supported", + 201: "Mandatory IE incorrect", + 202: "Mandatory IE missing", + 203: "Optional IE incorrect", + 204: "System failure", + 205: "Roaming restriction", + 206: "P-TMSI Signature mismatch", + 207: "GPRS connection suspended", + 208: "Authentication failure", + 209: "User authentication failed", + 210: "Context not found", + 211: "All dynamic PDP addresses are occupied", + 212: "No memory is available", + 213: "Reallocation failure", + 214: "Unknown mandatory extension header", + 215: "Semantic error in the TFT operation", + 216: "Syntactic error in TFT operation", + 217: "Semantic errors in packet filter(s)", + 218: "Syntactic errors in packet filter(s)", + 219: "Missing or unknown APN", + 220: "Unknown PDP address or PDP type", + 221: "PDP context without TFT already activated", + 222: "APN access denied : no subscription", + 223: "APN Restriction type incompatibility with currently active PDP Contexts", + 224: "MS MBMS Capabilities Insufficient", + 225: "Invalid Correlation : ID", + 226: "MBMS Bearer Context Superseded", + 227: "Bearer Control Mode violation", + 228: "Collision with network initiated request" } + +Selection_Mode = { 11111100: "MS or APN", + 11111101: "MS", + 11111110: "NET", + 11111111: "FutureUse" } + +TeardownInd_value = { 254: "False", + 255: "True" } + +class TBCDByteField(StrFixedLenField): + + def i2h(self, pkt, val): + ret = [] + for i in range(len(val)): + byte = ord(val[i]) + left = byte >> 4 + right = byte & 0xF + if left == 0xF: + ret += [ "%d" % right ] + else: + ret += [ "%d" % right, "%d" % left ] + return "".join(ret) + + def i2repr(self, pkt, x): + return repr(self.i2h(pkt,x)) + + def i2m(self, pkt, val): + ret_string = "" + for i in range(0, len(val), 2): + tmp = val[i:i+2] + if len(tmp) == 2: + ret_string += chr(int(tmp[1] + tmp[0], 16)) + else: + ret_string += chr(int("F" + tmp[0], 16)) + return ret_string + +class GTPHeader(Packet): + # 3GPP TS 29.060 V9.1.0 (2009-12) + name = "GTP Header" + fields_desc=[ BitField("version", 1, 3), + BitField("PT", 1, 1), + BitField("reserved", 0, 1), + BitField("E", 0, 1), + BitField("S", 1, 1), + BitField("PN", 0, 1), + ByteEnumField("gtp_type", None, GTPmessageType), + ShortField("length", None), + IntField("teid", 0) ] + + def post_build(self, p, pay): + p += pay + if self.length is None: + l = len(p)-8 + p = p[:2] + struct.pack("!H", l)+ p[4:] + return p + + def hashret(self): + return struct.pack("B", self.version) + self.payload.hashret() + + def answers(self, other): + return (isinstance(other, GTPHeader) and + self.version == other.version and + self.payload.answers(other.payload)) + +class GTPEchoRequest(Packet): + # 3GPP TS 29.060 V9.1.0 (2009-12) + name = "GTP Echo Request" + fields_desc = [ XBitField("seq", 0, 16), + ByteField("npdu", 0), + ByteField("next_ex", 0),] + + def hashret(self): + return struct.pack("H", self.seq) + +class IE_Cause(Packet): + name = "Cause" + fields_desc = [ ByteEnumField("ietype", 1, IEType), + BitField("Response", None, 1), + BitField("Rejection", None, 1), + BitEnumField("CauseValue", None, 6, CauseValues) ] + def extract_padding(self, pkt): + return "",pkt + +class IE_IMSI(Packet): + name = "IMSI - Subscriber identity of the MS" + fields_desc = [ ByteEnumField("ietype", 2, IEType), + TBCDByteField("imsi", str(RandNum(0, 999999999999999)), 8) ] + def extract_padding(self, pkt): + return "",pkt + +class IE_Routing(Packet): + name = "Routing Area Identity" + fields_desc = [ ByteEnumField("ietype", 3, IEType), + TBCDByteField("MCC", "", 2), + # MNC: if the third digit of MCC is 0xf, then the length of MNC is 1 byte + TBCDByteField("MNC", "", 1), + ShortField("LAC", None), + ByteField("RAC", None) ] + def extract_padding(self, pkt): + return "",pkt + +class IE_Recovery(Packet): + name = "Recovery" + fields_desc = [ ByteEnumField("ietype", 14, IEType), + ByteField("res-counter", 24) ] + def extract_padding(self, pkt): + return "",pkt + +class IE_SelectionMode(Packet): + # Indicates the origin of the APN in the message + name = "Selection Mode" + fields_desc = [ ByteEnumField("ietype", 15, IEType), + BitEnumField("SelectionMode", "MS or APN", 8, Selection_Mode) ] + def extract_padding(self, pkt): + return "",pkt + +class IE_TEIDI(Packet): + name = "Tunnel Endpoint Identifier Data" + fields_desc = [ ByteEnumField("ietype", 16, IEType), + XIntField("TEIDI", RandInt()) ] + def extract_padding(self, pkt): + return "",pkt + +class IE_TEICP(Packet): + name = "Tunnel Endpoint Identifier Control Plane" + fields_desc = [ ByteEnumField("ietype", 17, IEType), + XIntField("TEICI", RandInt())] + def extract_padding(self, pkt): + return "",pkt + +class IE_Teardown(Packet): + name = "Teardown Indicator" + fields_desc = [ ByteEnumField("ietype", 19, IEType), + ByteEnumField("indicator", "True", TeardownInd_value) ] + def extract_padding(self, pkt): + return "",pkt + +class IE_NSAPI(Packet): + # Identifies a PDP context in a mobility management context specified by TEICP + name = "NSAPI" + fields_desc = [ ByteEnumField("ietype", 20, IEType), + XBitField("sparebits", 0x0000, 4), + XBitField("NSAPI", RandNum(0, 15), 4) ] + def extract_padding(self, pkt): + return "",pkt + +class IE_ChargingCharacteristics(Packet): + # Way of informing both the SGSN and GGSN of the rules for + name = "Charging Characteristics" + fields_desc = [ ByteEnumField("ietype", 26, IEType), + # producing charging information based on operator configured triggers. + # 0000 .... .... .... : spare + # .... 1... .... .... : normal charging + # .... .0.. .... .... : prepaid charging + # .... ..0. .... .... : flat rate charging + # .... ...0 .... .... : hot billing charging + # .... .... 0000 0000 : reserved + XBitField("Ch_ChSpare", None, 4), + XBitField("normal_charging", None, 1), + XBitField("prepaid_charging", None, 1), + XBitField("flat_rate_charging", None, 1), + XBitField("hot_billing_charging", None, 1), + XBitField("Ch_ChReserved", 0, 8) ] + def extract_padding(self, pkt): + return "",pkt + +class IE_TraceReference(Packet): + # Identifies a record or a collection of records for a particular trace. + name = "Trace Reference" + fields_desc = [ ByteEnumField("ietype", 27, IEType), + XBitField("Trace_reference", None, 16) ] + def extract_padding(self, pkt): + return "",pkt + +class IE_TraceType(Packet): + # Indicates the type of the trace + name = "Trace Type" + fields_desc = [ ByteEnumField("ietype", 28, IEType), + XBitField("Trace_type", None, 16) ] + def extract_padding(self, pkt): + return "",pkt + +class IE_EndUserAddress(Packet): + # Supply protocol specific information of the external packet + name = "End User Addresss" + fields_desc = [ ByteEnumField("ietype", 128, IEType), + # data network accessed by the GGPRS subscribers. + # - Request + # 1 Type (1byte) + # 2-3 Length (2bytes) - value 2 + # 4 Spare + PDP Type Organization + # 5 PDP Type Number + # - Response + # 6-n PDP Address + BitField("EndUserAddressLength", 2, 16), + BitField("EndUserAddress", 1111, 4), + BitField("PDPTypeOrganization", 1, 4), + XByteField("PDPTypeNumber", None) ] + def extract_padding(self, pkt): + return "",pkt + +class APNStrLenField(StrLenField): + # Inspired by DNSStrField + def m2i(self, pkt, s): + ret_s = "" + tmp_s = s + while tmp_s: + tmp_len = struct.unpack("!B", tmp_s[0])[0] + 1 + if tmp_len > len(tmp_s): + warning("APN 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:] + if len(tmp_s) : + ret_s += "." + s = ret_s + return s + def i2m(self, pkt, s): + s = "".join(map(lambda x: chr(len(x))+x, s.split("."))) + return s + + +class IE_AccessPointName(Packet): + # Sent by SGSN or by GGSN as defined in 3GPP TS 23.060 + name = "Access Point Name" + fields_desc = [ ByteEnumField("ietype", 131, IEType), + ShortField("length", None), + APNStrLenField("APN", "nternet", length_from=lambda x: x.length) ] + def extract_padding(self, pkt): + return "",pkt + def post_build(self, p, pay): + if self.length is None: + l = len(p)-3 + p = p[:2] + struct.pack("!B", l)+ p[3:] + return p + +class IE_ProtocolConfigurationOptions(Packet): + name = "Protocol Configuration Options" + fields_desc = [ ByteEnumField("ietype", 132, IEType), + ShortField("length", 4), + StrLenField("Protocol Configuration", "", length_from=lambda x: x.length) ] + def extract_padding(self, pkt): + return "",pkt + +class IE_GSNAddress(Packet): + name = "GSN Address" + fields_desc = [ ByteEnumField("ietype", 133, IEType), + ShortField("length", 4), + IPField("address", RandIP()) ] + def extract_padding(self, pkt): + return "",pkt + +class IE_MSInternationalNumber(Packet): + name = "MS International Number" + fields_desc = [ ByteEnumField("ietype", 134, IEType), + ShortField("length", None), + FlagsField("flags", 0x91, 8, ["Extension","","","International Number","","","","ISDN numbering"]), + TBCDByteField("digits", "33607080910", length_from=lambda x: x.length-1) ] + def extract_padding(self, pkt): + return "",pkt + +class IE_UserLocationInformation(Packet): + name = "User Location Information" + fields_desc = [ ByteEnumField("ietype", 152, IEType), + ShortField("length", None), + ByteField("type", 1), + # Only type 1 is currently supported + TBCDByteField("MCC", "", 2), + # MNC: if the third digit of MCC is 0xf, then the length of MNC is 1 byte + TBCDByteField("MNC", "", 1), + ShortField("LAC", None), + ShortField("SAC", None) ] + def extract_padding(self, pkt): + return "",pkt + +class IE_IMEI(Packet): + name = "IMEI" + fields_desc = [ ByteEnumField("ietype", 154, IEType), + ShortField("length", None), + TBCDByteField("IMEI", "", length_from=lambda x: x.length) ] + def extract_padding(self, pkt): + return "",pkt + +class IE_NotImplementedTLV(Packet): + name = "IE not implemented" + fields_desc = [ ByteEnumField("ietype", 0, IEType), + ShortField("length", None), + StrLenField("data", "", length_from=lambda x: x.length) ] + def extract_padding(self, pkt): + return "",pkt + +ietypecls = { 1: IE_Cause, 2: IE_IMSI, 3: IE_Routing, 14: IE_Recovery, 15: IE_SelectionMode, 16: IE_TEIDI, + 17: IE_TEICP, 19: IE_Teardown, 20: IE_NSAPI, 26: IE_ChargingCharacteristics, + 27: IE_TraceReference, 28: IE_TraceType, + 128: IE_EndUserAddress, 131: IE_AccessPointName, 132: IE_ProtocolConfigurationOptions, + 133: IE_GSNAddress, 134: IE_MSInternationalNumber, 152: IE_UserLocationInformation, 154: IE_IMEI } + +def IE_Dispatcher(s): + """Choose the correct Information Element class.""" + + if len(s) < 1: + return Raw(s) + + # Get the IE type + ietype = ord(s[0]) + cls = ietypecls.get(ietype, Raw) + + # if ietype greater than 128 are TLVs + if cls == Raw and ietype & 128 == 128: + cls = IE_NotImplementedTLV + + return cls(s) + +class GTPEchoResponse(Packet): + # 3GPP TS 29.060 V9.1.0 (2009-12) + name = "GTP Echo Response" + fields_desc = [ XBitField("seq", 0, 16), + ByteField("npdu", 0), + ByteField("next_ex", 0), + PacketListField("IE_list", [], IE_Dispatcher) ] + + def hashret(self): + return struct.pack("H", self.seq) + + def answers(self, other): + return self.seq == other.seq + + +class GTPCreatePDPContextRequest(Packet): + # 3GPP TS 29.060 V9.1.0 (2009-12) + name = "GTP Create PDP Context Request" + fields_desc = [ ShortField("seq", RandShort()), + ByteField("npdu", 0), + ByteField("next_ex", 0), + PacketListField("IE_list", [ IE_TEIDI(), IE_NSAPI(), IE_GSNAddress(), + IE_GSNAddress(), + IE_NotImplementedTLV(ietype=135, length=15,data=RandString(15)) ], + IE_Dispatcher) ] + def hashret(self): + return struct.pack("H", self.seq) + +class GTPCreatePDPContextResponse(Packet): + # 3GPP TS 29.060 V9.1.0 (2009-12) + name = "GTP Create PDP Context Response" + fields_desc = [ ShortField("seq", RandShort()), + ByteField("npdu", 0), + ByteField("next_ex", 0), + PacketListField("IE_list", [], IE_Dispatcher) ] + + def hashret(self): + return struct.pack("H", self.seq) + + def answers(self, other): + return self.seq == other.seq + +class GTPErrorIndication(Packet): + # 3GPP TS 29.060 V9.1.0 (2009-12) + name = "GTP Error Indication" + fields_desc = [ XBitField("seq", 0, 16), + ByteField("npdu", 0), + ByteField("next_ex",0), + PacketListField("IE_list", [], IE_Dispatcher) ] + +class GTPDeletePDPContextRequest(Packet): + # 3GPP TS 29.060 V9.1.0 (2009-12) + name = "GTP Delete PDP Context Request" + fields_desc = [ XBitField("seq", 0, 16), + ByteField("npdu", 0), + ByteField("next_ex", 0), + PacketListField("IE_list", [], IE_Dispatcher) ] + +class GTPDeletePDPContextResponse(Packet): + # 3GPP TS 29.060 V9.1.0 (2009-12) + name = "GTP Delete PDP Context Response" + fields_desc = [ XBitField("seq", 0, 16), + ByteField("npdu", 0), + ByteField("next_ex",0), + PacketListField("IE_list", [], IE_Dispatcher) ] + +class GTPPDUNotificationRequest(Packet): + # 3GPP TS 29.060 V9.1.0 (2009-12) + name = "GTP PDU Notification Request" + fields_desc = [ XBitField("seq", 0, 16), + ByteField("npdu", 0), + ByteField("next_ex", 0), + PacketListField("IE_list", [ IE_IMSI(), + IE_TEICP(TEICI=RandInt()), + IE_EndUserAddress(PDPTypeNumber=0x21), + IE_AccessPointName(), + IE_GSNAddress(address="127.0.0.1"), + ], IE_Dispatcher) ] + +class GTP_U_Header(Packet): + # 3GPP TS 29.060 V9.1.0 (2009-12) + name = "GTP-U Header" + # GTP-U protocol is used to transmit T-PDUs between GSN pairs (or between an SGSN and an RNC in UMTS), + # encapsulated in G-PDUs. A G-PDU is a packet including a GTP-U header and a T-PDU. The Path Protocol + # defines the path and the GTP-U header defines the tunnel. Several tunnels may be multiplexed on a single path. + fields_desc = [ BitField("version", 1,3), + BitField("PT", 1, 1), + BitField("Reserved", 0, 1), + BitField("E", 0,1), + BitField("S", 0, 1), + BitField("PN", 0, 1), + ByteEnumField("gtp_type", None, GTPmessageType), + BitField("length", None, 16), + XBitField("TEID", 0, 32), + ConditionalField(XBitField("seq", 0, 16), lambda pkt:pkt.E==1 or pkt.S==1 or pkt.PN==1), + ConditionalField(ByteField("npdu", 0), lambda pkt:pkt.E==1 or pkt.S==1 or pkt.PN==1), + ConditionalField(ByteField("next_ex", 0), lambda pkt:pkt.E==1 or pkt.S==1 or pkt.PN==1), + ] + + def post_build(self, p, pay): + p += pay + if self.length is None: + l = len(p)-8 + p = p[:2] + struct.pack("!H", l)+ p[4:] + return p + +class GTPmorethan1500(Packet): + # 3GPP TS 29.060 V9.1.0 (2009-12) + name = "GTP More than 1500" + fields_desc = [ ByteEnumField("IE_Cause", "Cause", IEType), + BitField("IE", 1, 12000),] + +# Bind GTP-C +bind_layers(UDP, GTPHeader, dport = 2123) +bind_layers(UDP, GTPHeader, sport = 2123) +bind_layers(GTPHeader, GTPEchoRequest, gtp_type = 1) +bind_layers(GTPHeader, GTPEchoResponse, gtp_type = 2) +bind_layers(GTPHeader, GTPCreatePDPContextRequest, gtp_type = 16) +bind_layers(GTPHeader, GTPCreatePDPContextResponse, gtp_type = 17) +bind_layers(GTPHeader, GTPDeletePDPContextRequest, gtp_type = 20) +bind_layers(GTPHeader, GTPDeletePDPContextResponse, gtp_type = 21) +bind_layers(GTPHeader, GTPPDUNotificationRequest, gtp_type = 27) + +# Bind GTP-U +bind_layers(UDP, GTP_U_Header, dport = 2152) +bind_layers(UDP, GTP_U_Header, sport = 2152) +bind_layers(GTP_U_Header, IP, gtp_type = 255) + +if __name__ == "__main__": + from scapy.all import * + interact(mydict=globals(), mybanner="GTPv1 add-on") + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/igmp.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/igmp.py new file mode 100644 index 00000000..b3505810 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/igmp.py @@ -0,0 +1,171 @@ +#! /usr/bin/env python + +# scapy.contrib.description = IGMP/IGMPv2 +# scapy.contrib.status = loads + + +# TODO: scapy 2 has function getmacbyip, maybe it can replace igmpize +# at least from the MAC layer + +from scapy.all import * + +#-------------------------------------------------------------------------- +def isValidMCAddr(ip): + """convert dotted quad string to long and check the first octet""" + FirstOct=atol(ip)>>24 & 0xFF + return (FirstOct >= 224) and (FirstOct <= 239) + +#-------------------------------------------------------------------------- + +class IGMP(Packet): + """IGMP Message Class for v1 and v2. + +This class is derived from class Packet. You need to "igmpize" +the IP and Ethernet layers before a full packet is sent. +a=Ether(src="00:01:02:03:04:05") +b=IP(src="1.2.3.4") +c=IGMP(type=0x12, gaddr="224.2.3.4") +c.igmpize(b, a) +print("Joining IP " + c.gaddr + " MAC " + a.dst) +sendp(a/b/c, iface="en0") + + Parameters: + type IGMP type field, 0x11, 0x12, 0x16 or 0x17 + mrtime Maximum Response time (zero for v1) + gaddr Multicast Group Address 224.x.x.x/4 + +See RFC2236, Section 2. Introduction for definitions of proper +IGMPv2 message format http://www.faqs.org/rfcs/rfc2236.html + + """ + name = "IGMP" + + igmptypes = { 0x11 : "Group Membership Query", + 0x12 : "Version 1 - Membership Report", + 0x16 : "Version 2 - Membership Report", + 0x17 : "Leave Group"} + + fields_desc = [ ByteEnumField("type", 0x11, igmptypes), + ByteField("mrtime",20), + XShortField("chksum", None), + IPField("gaddr", "0.0.0.0")] + +#-------------------------------------------------------------------------- + def post_build(self, p, pay): + """Called implicitly before a packet is sent to compute and place IGMP checksum. + + Parameters: + self The instantiation of an IGMP class + p The IGMP message in hex in network byte order + pay Additional payload for the IGMP message + """ + p += pay + if self.chksum is None: + ck = checksum(p) + p = p[:2]+chr(ck>>8)+chr(ck&0xff)+p[4:] + return p + +#-------------------------------------------------------------------------- + def mysummary(self): + """Display a summary of the IGMP object.""" + + if isinstance(self.underlayer, IP): + return self.underlayer.sprintf("IGMP: %IP.src% > %IP.dst% %IGMP.type% %IGMP.gaddr%") + else: + return self.sprintf("IGMP %IGMP.type% %IGMP.gaddr%") + +#-------------------------------------------------------------------------- + def igmpize(self, ip=None, ether=None): + """Called to explicitely fixup associated IP and Ethernet headers + + Parameters: + self The instantiation of an IGMP class. + ip The instantiation of the associated IP class. + ether The instantiation of the associated Ethernet. + + Returns: + True The tuple ether/ip/self passed all check and represents + a proper IGMP packet. + False One of more validation checks failed and no fields + were adjusted. + + The function will examine the IGMP message to assure proper format. + Corrections will be attempted if possible. The IP header is then properly + adjusted to ensure correct formatting and assignment. The Ethernet header + is then adjusted to the proper IGMP packet format. + """ + +# The rules are: +# 1. the Max Response time is meaningful only in Membership Queries and should be zero +# otherwise (RFC 2236, section 2.2) + + if (self.type != 0x11): #rule 1 + self.mrtime = 0 + + if (self.adjust_ip(ip) == True): + if (self.adjust_ether(ip, ether) == True): return True + return False + +#-------------------------------------------------------------------------- + def adjust_ether (self, ip=None, ether=None): + """Called to explicitely fixup an associated Ethernet header + + The function adjusts the ethernet header destination MAC address based on + the destination IP address. + """ +# The rules are: +# 1. send to the group mac address address corresponding to the IP.dst + if ip != None and ip.haslayer(IP) and ether != None and ether.haslayer(Ether): + iplong = atol(ip.dst) + ether.dst = "01:00:5e:%02x:%02x:%02x" % ( (iplong>>16)&0x7F, (iplong>>8)&0xFF, (iplong)&0xFF ) + # print "igmpize ip " + ip.dst + " as mac " + ether.dst + return True + else: + return False + +#-------------------------------------------------------------------------- + def adjust_ip (self, ip=None): + """Called to explicitely fixup an associated IP header + + The function adjusts the IP header based on conformance rules + and the group address encoded in the IGMP message. + The rules are: + 1. Send General Group Query to 224.0.0.1 (all systems) + 2. Send Leave Group to 224.0.0.2 (all routers) + 3a.Otherwise send the packet to the group address + 3b.Send reports/joins to the group address + 4. ttl = 1 (RFC 2236, section 2) + 5. send the packet with the router alert IP option (RFC 2236, section 2) + """ + if ip != None and ip.haslayer(IP): + if (self.type == 0x11): + if (self.gaddr == "0.0.0.0"): + ip.dst = "224.0.0.1" # IP rule 1 + retCode = True + elif isValidMCAddr(self.gaddr): + ip.dst = self.gaddr # IP rule 3a + retCode = True + else: + print("Warning: Using invalid Group Address") + retCode = False + elif ((self.type == 0x17) and isValidMCAddr(self.gaddr)): + ip.dst = "224.0.0.2" # IP rule 2 + retCode = True + elif ((self.type == 0x12) or (self.type == 0x16)) and (isValidMCAddr(self.gaddr)): + ip.dst = self.gaddr # IP rule 3b + retCode = True + else: + print("Warning: Using invalid IGMP Type") + retCode = False + else: + print("Warning: No IGMP Group Address set") + retCode = False + if retCode == True: + ip.ttl=1 # IP Rule 4 + ip.options=[IPOption_Router_Alert()] # IP rule 5 + return retCode + + +bind_layers( IP, IGMP, frag=0, proto=2) + + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/igmpv3.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/igmpv3.py new file mode 100644 index 00000000..8322c7a3 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/igmpv3.py @@ -0,0 +1,270 @@ +#! /usr/bin/env python + +# http://trac.secdev.org/scapy/ticket/31 + +# scapy.contrib.description = IGMPv3 +# scapy.contrib.status = loads + +from scapy.packet import * + +""" Based on the following references + http://www.iana.org/assignments/igmp-type-numbers + http://www.rfc-editor.org/rfc/pdfrfc/rfc3376.txt.pdf + +""" + +# TODO: Merge IGMPv3 packet Bindlayers correct for +# membership source/Group records +# ConditionalField parameters for IGMPv3 commented out +# +# See RFC3376, Section 4. Message Formats for definitions of proper IGMPv3 message format +# http://www.faqs.org/rfcs/rfc3376.html +# +# See RFC4286, For definitions of proper messages for Multicast Router Discovery. +# http://www.faqs.org/rfcs/rfc4286.html +# + +#import sys, socket, struct, time +from scapy.all import * +print("IGMPv3 is still under development - Nov 2010") + + +class IGMPv3gr(Packet): + """IGMP Group Record for IGMPv3 Membership Report + + This class is derived from class Packet and should be concatenated to an + instantiation of class IGMPv3. Within the IGMPv3 instantiation, the numgrp + element will need to be manipulated to indicate the proper number of + group records. + """ + name = "IGMPv3gr" + igmpv3grtypes = { 1 : "Mode Is Include", + 2 : "Mode Is Exclude", + 3 : "Change To Include Mode", + 4 : "Change To Exclude Mode", + 5 : "Allow New Sources", + 6 : "Block Old Sources"} + + fields_desc = [ ByteEnumField("rtype", 1, igmpv3grtypes), + ByteField("auxdlen",0), + FieldLenField("numsrc", None, "srcaddrs"), + IPField("maddr", "0.0.0.0"), + FieldListField("srcaddrs", None, IPField("sa", "0.0.0.0"), "numsrc") ] + #show_indent=0 +#-------------------------------------------------------------------------- + def post_build(self, p, pay): + """Called implicitly before a packet is sent. + """ + p += pay + if self.auxdlen != 0: + print("NOTICE: A properly formatted and complaint V3 Group Record should have an Auxiliary Data length of zero (0).") + print(" Subsequent Group Records are lost!") + return p +#-------------------------------------------------------------------------- + def mysummary(self): + """Display a summary of the IGMPv3 group record.""" + return self.sprintf("IGMPv3 Group Record %IGMPv3gr.type% %IGMPv3gr.maddr%") + + +class IGMPv3(Packet): + """IGMP Message Class for v3. + + This class is derived from class Packet. + The fields defined below are a + direct interpretation of the v3 Membership Query Message. + Fields 'type' through 'qqic' are directly assignable. + For 'numsrc', do not assign a value. + Instead add to the 'srcaddrs' list to auto-set 'numsrc'. To + assign values to 'srcaddrs', use the following methods: + c = IGMPv3() + c.srcaddrs = ['1.2.3.4', '5.6.7.8'] + c.srcaddrs += ['192.168.10.24'] + At this point, 'c.numsrc' is three (3) + + 'chksum' is automagically calculated before the packet is sent. + + 'mrcode' is also the Advertisement Interval field + + """ + name = "IGMPv3" + igmpv3types = { 0x11 : "Membership Query", + 0x22 : "Version 3 Membership Report", + 0x30 : "Multicast Router Advertisement", + 0x31 : "Multicast Router Solicitation", + 0x32 : "Multicast Router Termination"} + + fields_desc = [ ByteEnumField("type", 0x11, igmpv3types), + ByteField("mrcode",0), + XShortField("chksum", None), + IPField("gaddr", "0.0.0.0") + ] + # use float_encode() + + # if type = 0x11 (Membership Query), the next field is group address + # ConditionalField(IPField("gaddr", "0.0.0.0"), "type", lambda x:x==0x11), + # else if type = 0x22 (Membership Report), the next fields are + # reserved and number of group records + #ConditionalField(ShortField("rsvd2", 0), "type", lambda x:x==0x22), + #ConditionalField(ShortField("numgrp", 0), "type", lambda x:x==0x22), +# FieldLenField("numgrp", None, "grprecs")] + # else if type = 0x30 (Multicast Router Advertisement), the next fields are + # query interval and robustness + #ConditionalField(ShortField("qryIntvl", 0), "type", lambda x:x==0x30), + #ConditionalField(ShortField("robust", 0), "type", lambda x:x==0x30), +# The following are only present for membership queries + # ConditionalField(BitField("resv", 0, 4), "type", lambda x:x==0x11), + # ConditionalField(BitField("s", 0, 1), "type", lambda x:x==0x11), + # ConditionalField(BitField("qrv", 0, 3), "type", lambda x:x==0x11), + # ConditionalField(ByteField("qqic",0), "type", lambda x:x==0x11), + # ConditionalField(FieldLenField("numsrc", None, "srcaddrs"), "type", lambda x:x==0x11), + # ConditionalField(FieldListField("srcaddrs", None, IPField("sa", "0.0.0.0"), "numsrc"), "type", lambda x:x==0x11), + +#-------------------------------------------------------------------------- + def float_encode(self, value): + """Convert the integer value to its IGMPv3 encoded time value if needed. + + If value < 128, return the value specified. If >= 128, encode as a floating + point value. Value can be 0 - 31744. + """ + if value < 128: + code = value + elif value > 31743: + code = 255 + else: + exp=0 + value>>=3 + while(value>31): + exp+=1 + value>>=1 + exp<<=4 + code = 0x80 | exp | (value & 0x0F) + return code + +#-------------------------------------------------------------------------- + def post_build(self, p, pay): + """Called implicitly before a packet is sent to compute and place IGMPv3 checksum. + + Parameters: + self The instantiation of an IGMPv3 class + p The IGMPv3 message in hex in network byte order + pay Additional payload for the IGMPv3 message + """ + p += pay + if self.type in [0, 0x31, 0x32, 0x22]: # for these, field is reserved (0) + p = p[:1]+chr(0)+p[2:] + if self.chksum is None: + ck = checksum(p) + p = p[:2]+chr(ck>>8)+chr(ck&0xff)+p[4:] + return p + +#-------------------------------------------------------------------------- + def mysummary(self): + """Display a summary of the IGMPv3 object.""" + + if isinstance(self.underlayer, IP): + return self.underlayer.sprintf("IGMPv3: %IP.src% > %IP.dst% %IGMPv3.type% %IGMPv3.gaddr%") + else: + return self.sprintf("IGMPv3 %IGMPv3.type% %IGMPv3.gaddr%") + +#-------------------------------------------------------------------------- + def igmpize(self, ip=None, ether=None): + """Called to explicitely fixup associated IP and Ethernet headers + + Parameters: + self The instantiation of an IGMP class. + ip The instantiation of the associated IP class. + ether The instantiation of the associated Ethernet. + + Returns: + True The tuple ether/ip/self passed all check and represents + a proper IGMP packet. + False One of more validation checks failed and no fields + were adjusted. + + The function will examine the IGMP message to assure proper format. + Corrections will be attempted if possible. The IP header is then properly + adjusted to ensure correct formatting and assignment. The Ethernet header + is then adjusted to the proper IGMP packet format. + """ + +# The rules are: +# 1. ttl = 1 (RFC 2236, section 2) +# igmp_binds = [ (IP, IGMP, { "proto": 2 , "ttl": 1 }), +# 2. tos = 0xC0 (RFC 3376, section 4) +# (IP, IGMPv3, { "proto": 2 , "ttl": 1, "tos":0xc0 }), +# (IGMPv3, IGMPv3gr, { }) ] +# The rules are: +# 1. the Max Response time is meaningful only in Membership Queries and should be zero +# otherwise (RFC 2236, section 2.2) + + if (self.type != 0x11): #rule 1 + self.mrtime = 0 + + if (self.adjust_ip(ip) == True): + if (self.adjust_ether(ip, ether) == True): return True + return False + +#-------------------------------------------------------------------------- + def adjust_ether (self, ip=None, ether=None): + """Called to explicitely fixup an associated Ethernet header + + The function adjusts the ethernet header destination MAC address based on + the destination IP address. + """ +# The rules are: +# 1. send to the group mac address address corresponding to the IP.dst + if ip != None and ip.haslayer(IP) and ether != None and ether.haslayer(Ether): + iplong = atol(ip.dst) + ether.dst = "01:00:5e:%02x:%02x:%02x" % ( (iplong>>16)&0x7F, (iplong>>8)&0xFF, (iplong)&0xFF ) + # print "igmpize ip " + ip.dst + " as mac " + ether.dst + return True + else: + return False + +#-------------------------------------------------------------------------- + def adjust_ip (self, ip=None): + """Called to explicitely fixup an associated IP header + + The function adjusts the IP header based on conformance rules + and the group address encoded in the IGMP message. + The rules are: + 1. Send General Group Query to 224.0.0.1 (all systems) + 2. Send Leave Group to 224.0.0.2 (all routers) + 3a.Otherwise send the packet to the group address + 3b.Send reports/joins to the group address + 4. ttl = 1 (RFC 2236, section 2) + 5. send the packet with the router alert IP option (RFC 2236, section 2) + """ + if ip != None and ip.haslayer(IP): + if (self.type == 0x11): + if (self.gaddr == "0.0.0.0"): + ip.dst = "224.0.0.1" # IP rule 1 + retCode = True + elif isValidMCAddr(self.gaddr): + ip.dst = self.gaddr # IP rule 3a + retCode = True + else: + print("Warning: Using invalid Group Address") + retCode = False + elif ((self.type == 0x17) and isValidMCAddr(self.gaddr)): + ip.dst = "224.0.0.2" # IP rule 2 + retCode = True + elif ((self.type == 0x12) or (self.type == 0x16)) and (isValidMCAddr(self.gaddr)): + ip.dst = self.gaddr # IP rule 3b + retCode = True + else: + print("Warning: Using invalid IGMP Type") + retCode = False + else: + print("Warning: No IGMP Group Address set") + retCode = False + if retCode == True: + ip.ttl=1 # IP Rule 4 + ip.options=[IPOption_Router_Alert()] # IP rule 5 + return retCode + + +bind_layers( IP, IGMPv3, frag=0, proto=2, ttl=1, tos=0xc0) +bind_layers( IGMPv3, IGMPv3gr, frag=0, proto=2) +bind_layers( IGMPv3gr, IGMPv3gr, frag=0, proto=2) + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/ikev2.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/ikev2.py new file mode 100644 index 00000000..fd38b80c --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/ikev2.py @@ -0,0 +1,362 @@ +#!/usr/bin/env python + +# http://trac.secdev.org/scapy/ticket/353 + +# scapy.contrib.description = IKEv2 +# scapy.contrib.status = loads + +from scapy.all import * +import logging + + +## Modified from the original ISAKMP code by Yaron Sheffer , June 2010. + +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/ikev2-parameters for details +IKEv2AttributeTypes= { "Encryption": (1, { "DES-IV64" : 1, + "DES" : 2, + "3DES" : 3, + "RC5" : 4, + "IDEA" : 5, + "CAST" : 6, + "Blowfish" : 7, + "3IDEA" : 8, + "DES-IV32" : 9, + "AES-CBC" : 12, + "AES-CTR" : 13, + "AES-CCM-8" : 14, + "AES-CCM-12" : 15, + "AES-CCM-16" : 16, + "AES-GCM-8ICV" : 18, + "AES-GCM-12ICV" : 19, + "AES-GCM-16ICV" : 20, + "Camellia-CBC" : 23, + "Camellia-CTR" : 24, + "Camellia-CCM-8ICV" : 25, + "Camellia-CCM-12ICV" : 26, + "Camellia-CCM-16ICV" : 27, + }, 0), + "PRF": (2, {"PRF_HMAC_MD5":1, + "PRF_HMAC_SHA1":2, + "PRF_HMAC_TIGER":3, + "PRF_AES128_XCBC":4, + "PRF_HMAC_SHA2_256":5, + "PRF_HMAC_SHA2_384":6, + "PRF_HMAC_SHA2_512":7, + "PRF_AES128_CMAC":8, + }, 0), + "Integrity": (3, { "HMAC-MD5-96": 1, + "HMAC-SHA1-96": 2, + "DES-MAC": 3, + "KPDK-MD5": 4, + "AES-XCBC-96": 5, + "HMAC-MD5-128": 6, + "HMAC-SHA1-160": 7, + "AES-CMAC-96": 8, + "AES-128-GMAC": 9, + "AES-192-GMAC": 10, + "AES-256-GMAC": 11, + "SHA2-256-128": 12, + "SHA2-384-192": 13, + "SHA2-512-256": 14, + }, 0), + "GroupDesc": (4, { "768MODPgr" : 1, + "1024MODPgr" : 2, + "1536MODPgr" : 5, + "2048MODPgr" : 14, + "3072MODPgr" : 15, + "4096MODPgr" : 16, + "6144MODPgr" : 17, + "8192MODPgr" : 18, + "256randECPgr" : 19, + "384randECPgr" : 20, + "521randECPgr" : 21, + "1024MODP160POSgr" : 22, + "2048MODP224POSgr" : 23, + "2048MODP256POSgr" : 24, + "192randECPgr" : 25, + "224randECPgr" : 26, + }, 0), + "Extended Sequence Number": (5, {"No ESN": 0, + "ESN": 1, }, 0), + } + +# the name 'IKEv2TransformTypes' is actually a misnomer (since the table +# holds info for all IKEv2 Attribute types, not just transforms, but we'll +# keep it for backwards compatibility... for now at least +IKEv2TransformTypes = IKEv2AttributeTypes + +IKEv2TransformNum = {} +for n in IKEv2TransformTypes: + val = IKEv2TransformTypes[n] + tmp = {} + for e in val[1]: + tmp[val[1][e]] = e + IKEv2TransformNum[val[0]] = (n,tmp, val[2]) + +IKEv2Transforms = {} +for n in IKEv2TransformTypes: + IKEv2Transforms[IKEv2TransformTypes[n][0]]=n + +del(n) +del(e) +del(tmp) +del(val) + +# Note: Transform and Proposal can only be used inside the SA payload +IKEv2_payload_type = ["None", "", "Proposal", "Transform"] + +IKEv2_payload_type.extend([""] * 29) +IKEv2_payload_type.extend(["SA","KE","IDi","IDr", "CERT","CERTREQ","AUTH","Nonce","Notify","Delete", + "VendorID","TSi","TSr","Encrypted","CP","EAP"]) + +IKEv2_exchange_type = [""] * 34 +IKEv2_exchange_type.extend(["IKE_SA_INIT","IKE_AUTH","CREATE_CHILD_SA", + "INFORMATIONAL", "IKE_SESSION_RESUME"]) + + +class IKEv2_class(Packet): + def guess_payload_class(self, payload): + np = self.next_payload + logging.debug("For IKEv2_class np=%d" % np) + if np == 0: + return conf.raw_layer + elif np < len(IKEv2_payload_type): + pt = IKEv2_payload_type[np] + logging.debug(globals().get("IKEv2_payload_%s" % pt, IKEv2_payload)) + return globals().get("IKEv2_payload_%s" % pt, IKEv2_payload) + else: + return IKEv2_payload + + +class IKEv2(IKEv2_class): # rfc4306 + name = "IKEv2" + fields_desc = [ + StrFixedLenField("init_SPI","",8), + StrFixedLenField("resp_SPI","",8), + ByteEnumField("next_payload",0,IKEv2_payload_type), + XByteField("version",0x20), # IKEv2, right? + ByteEnumField("exch_type",0,IKEv2_exchange_type), + FlagsField("flags",0, 8, ["res0","res1","res2","Initiator","Version","Response","res6","res7"]), + IntField("id",0), + IntField("length",None) + ] + + def guess_payload_class(self, payload): + if self.flags & 1: + return conf.raw_layer + return IKEv2_class.guess_payload_class(self, payload) + + def answers(self, other): + if isinstance(other, IKEv2): + if other.init_SPI == self.init_SPI: + 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 IKEv2_Key_Length_Attribute(IntField): + # We only support the fixed-length Key Length attribute (the only one currently defined) + name="key length" + def __init__(self, name): + IntField.__init__(self, name, "0x800E0000") + + def i2h(self, pkt, x): + return IntField.i2h(self, pkt, x & 0xFFFF) + + def h2i(self, pkt, x): + return IntField.h2i(self, pkt, struct.pack("!I", 0x800E0000 | int(x, 0))) + + +class IKEv2_Transform_ID(ShortField): + def i2h(self, pkt, x): + if pkt == None: + return None + else: + map = IKEv2TransformNum[pkt.transform_type][1] + return map[x] + + def h2i(self, pkt, x): + if pkt == None: + return None + else: + map = IKEv2TransformNum[pkt.transform_type][1] + for k in keys(map): + if map[k] == x: + return k + return None + +class IKEv2_payload_Transform(IKEv2_class): + name = "IKE Transform" + fields_desc = [ + ByteEnumField("next_payload",None,{0:"last", 3:"Transform"}), + ByteField("res",0), + ShortField("length",8), + ByteEnumField("transform_type",None,IKEv2Transforms), + ByteField("res2",0), + IKEv2_Transform_ID("transform_id", 0), + ConditionalField(IKEv2_Key_Length_Attribute("key_length"), lambda pkt: pkt.length > 8), + ] + +class IKEv2_payload_Proposal(IKEv2_class): + name = "IKEv2 Proposal" + fields_desc = [ + ByteEnumField("next_payload",None,{0:"last", 2:"Proposal"}), + ByteField("res",0), + FieldLenField("length",None,"trans","H", adjust=lambda pkt,x:x+8), + ByteField("proposal",1), + ByteEnumField("proto",1,{1:"IKEv2"}), + FieldLenField("SPIsize",None,"SPI","B"), + ByteField("trans_nb",None), + StrLenField("SPI","",length_from=lambda x:x.SPIsize), + PacketLenField("trans",conf.raw_layer(),IKEv2_payload_Transform,length_from=lambda x:x.length-8), + ] + + +class IKEv2_payload(IKEv2_class): + name = "IKEv2 Payload" + fields_desc = [ + ByteEnumField("next_payload",None,IKEv2_payload_type), + FlagsField("flags",0, 8, ["critical","res1","res2","res3","res4","res5","res6","res7"]), + FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), + StrLenField("load","",length_from=lambda x:x.length-4), + ] + + +class IKEv2_payload_VendorID(IKEv2_class): + name = "IKEv2 Vendor ID" + overload_fields = { IKEv2: { "next_payload":43 }} + fields_desc = [ + ByteEnumField("next_payload",None,IKEv2_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 IKEv2_payload_Delete(IKEv2_class): + name = "IKEv2 Vendor ID" + overload_fields = { IKEv2: { "next_payload":42 }} + fields_desc = [ + ByteEnumField("next_payload",None,IKEv2_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 IKEv2_payload_SA(IKEv2_class): + name = "IKEv2 SA" + overload_fields = { IKEv2: { "next_payload":33 }} + fields_desc = [ + ByteEnumField("next_payload",None,IKEv2_payload_type), + ByteField("res",0), + FieldLenField("length",None,"prop","H", adjust=lambda pkt,x:x+4), + PacketLenField("prop",conf.raw_layer(),IKEv2_payload_Proposal,length_from=lambda x:x.length-4), + ] + +class IKEv2_payload_Nonce(IKEv2_class): + name = "IKEv2 Nonce" + overload_fields = { IKEv2: { "next_payload":40 }} + fields_desc = [ + ByteEnumField("next_payload",None,IKEv2_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 IKEv2_payload_Notify(IKEv2_class): + name = "IKEv2 Notify" + overload_fields = { IKEv2: { "next_payload":41 }} + fields_desc = [ + ByteEnumField("next_payload",None,IKEv2_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 IKEv2_payload_KE(IKEv2_class): + name = "IKEv2 Key Exchange" + overload_fields = { IKEv2: { "next_payload":34 }} + fields_desc = [ + ByteEnumField("next_payload",None,IKEv2_payload_type), + ByteField("res",0), + FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+6), + ShortEnumField("group", 0, IKEv2TransformTypes['GroupDesc'][1]), + StrLenField("load","",length_from=lambda x:x.length-6), + ] + +class IKEv2_payload_IDi(IKEv2_class): + name = "IKEv2 Identification - Initiator" + overload_fields = { IKEv2: { "next_payload":35 }} + fields_desc = [ + ByteEnumField("next_payload",None,IKEv2_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 IKEv2_payload_IDr(IKEv2_class): + name = "IKEv2 Identification - Responder" + overload_fields = { IKEv2: { "next_payload":36 }} + fields_desc = [ + ByteEnumField("next_payload",None,IKEv2_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 IKEv2_payload_Encrypted(IKEv2_class): + name = "IKEv2 Encrypted and Authenticated" + overload_fields = { IKEv2: { "next_payload":46 }} + fields_desc = [ + ByteEnumField("next_payload",None,IKEv2_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), + ] + + + +IKEv2_payload_type_overload = {} +for i in range(len(IKEv2_payload_type)): + name = "IKEv2_payload_%s" % IKEv2_payload_type[i] + if name in globals(): + IKEv2_payload_type_overload[globals()[name]] = {"next_payload":i} + +del(i) +del(name) +IKEv2_class.overload_fields = IKEv2_payload_type_overload.copy() + +split_layers(UDP, ISAKMP, sport=500) +split_layers(UDP, ISAKMP, dport=500) + +bind_layers( UDP, IKEv2, dport=500, sport=500) # TODO: distinguish IKEv1/IKEv2 +bind_layers( UDP, IKEv2, dport=4500, sport=4500) + +def ikev2scan(ip): + return sr(IP(dst=ip)/UDP()/IKEv2(init_SPI=RandString(8), + exch_type=34)/IKEv2_payload_SA(prop=IKEv2_payload_Proposal())) + +# conf.debug_dissector = 1 + +if __name__ == "__main__": + interact(mydict=globals(), mybanner="IKEv2 alpha-level protocol implementation") diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/ldp.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/ldp.py new file mode 100644 index 00000000..bc2464ab --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/ldp.py @@ -0,0 +1,475 @@ +# scapy.contrib.description = Label Distribution Protocol (LDP) +# scapy.contrib.status = loads + +# http://git.savannah.gnu.org/cgit/ldpscapy.git/snapshot/ldpscapy-5285b81d6e628043df2a83301b292f24a95f0ba1.tar.gz + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# 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. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Copyright (C) 2010 Florian Duraffourg + +import struct + +from scapy.packet import * +from scapy.fields import * +from scapy.ansmachine import * +from scapy.layers.inet import UDP +from scapy.layers.inet import TCP +from scapy.base_classes import Net + + +# Guess payload +def guess_payload(p): + LDPTypes = { + 0x0001: LDPNotification, + 0x0100: LDPHello, + 0x0200: LDPInit, + 0x0201: LDPKeepAlive, + 0x0300: LDPAddress, + 0x0301: LDPAddressWM, + 0x0400: LDPLabelMM, + 0x0401: LDPLabelReqM, + 0x0404: LDPLabelARM, + 0x0402: LDPLabelWM, + 0x0403: LDPLabelRelM, + } + type = struct.unpack("!H",p[0:2])[0] + type = type & 0x7fff + if type == 0x0001 and struct.unpack("!H",p[2:4])[0] > 20: + return LDP + if type in LDPTypes: + return LDPTypes[type] + else: + return conf.raw_layer + +## Fields ## + +# 3.4.1. FEC TLV + +class FecTLVField(StrField): + islist=1 + def m2i(self, pkt, x): + nbr = struct.unpack("!H",x[2:4])[0] + used = 0 + x=x[4:] + list=[] + while x: + #if x[0] == 1: + # list.append('Wildcard') + #else: + #mask=ord(x[8*i+3]) + #add=inet_ntoa(x[8*i+4:8*i+8]) + mask=ord(x[3]) + nbroctets = mask / 8 + if mask % 8: + nbroctets += 1 + add=inet_ntoa(x[4:4+nbroctets]+"\x00"*(4-nbroctets)) + list.append( (add, mask) ) + used += 4 + nbroctets + x=x[4+nbroctets:] + return list + def i2m(self, pkt, x): + if type(x) is str: + return x + s = "\x01\x00" + l = 0 + fec = "" + for o in x: + fec += "\x02\x00\x01" + # mask length + fec += struct.pack("!B",o[1]) + # Prefix + fec += inet_aton(o[0]) + l += 8 + s += struct.pack("!H",l) + s += fec + return s + def size(self, s): + """Get the size of this field""" + l = 4 + struct.unpack("!H",s[2:4])[0] + return l + def getfield(self, pkt, s): + l = self.size(s) + return s[l:],self.m2i(pkt, s[:l]) + + +# 3.4.2.1. Generic Label TLV + +class LabelTLVField(StrField): + def m2i(self, pkt, x): + return struct.unpack("!I",x[4:8])[0] + def i2m(self, pkt, x): + if type(x) is str: + return x + s = "\x02\x00\x00\x04" + s += struct.pack("!I",x) + return s + def size(self, s): + """Get the size of this field""" + l = 4 + struct.unpack("!H",s[2:4])[0] + return l + def getfield(self, pkt, s): + l = self.size(s) + return s[l:],self.m2i(pkt, s[:l]) + + +# 3.4.3. Address List TLV + +class AddressTLVField(StrField): + islist=1 + def m2i(self, pkt, x): + nbr = struct.unpack("!H",x[2:4])[0] - 2 + nbr /= 4 + x=x[6:] + list=[] + for i in range(0,nbr): + add = x[4*i:4*i+4] + list.append(inet_ntoa(add)) + return list + def i2m(self, pkt, x): + if type(x) is str: + return x + l=2+len(x)*4 + s = "\x01\x01"+struct.pack("!H",l)+"\x00\x01" + for o in x: + s += inet_aton(o) + return s + def size(self, s): + """Get the size of this field""" + l = 4 + struct.unpack("!H",s[2:4])[0] + return l + def getfield(self, pkt, s): + l = self.size(s) + return s[l:],self.m2i(pkt, s[:l]) + + +# 3.4.6. Status TLV + +class StatusTLVField(StrField): + islist=1 + def m2i(self, pkt, x): + l = [] + statuscode = struct.unpack("!I",x[4:8])[0] + l.append( (statuscode & 2**31) >> 31) + l.append( (statuscode & 2**30) >> 30) + l.append( statuscode & 0x3FFFFFFF ) + l.append( struct.unpack("!I", x[8:12])[0] ) + l.append( struct.unpack("!H", x[12:14])[0] ) + return l + def i2m(self, pkt, x): + if type(x) is str: + return x + s = "\x03\x00" + struct.pack("!H",10) + statuscode = 0 + if x[0] != 0: + statuscode += 2**31 + if x[1] != 0: + statuscode += 2**30 + statuscode += x[2] + s += struct.pack("!I",statuscode) + if len(x) > 3: + s += struct.pack("!I",x[3]) + else: + s += "\x00\x00\x00\x00" + if len(x) > 4: + s += struct.pack("!H",x[4]) + else: + s += "\x00\x00" + return s + def getfield(self, pkt, s): + l = 14 + return s[l:],self.m2i(pkt, s[:l]) + + +# 3.5.2 Common Hello Parameters TLV +class CommonHelloTLVField(StrField): + islist = 1 + def m2i(self, pkt, x): + list = [] + v = struct.unpack("!H",x[4:6])[0] + list.append(v) + flags = struct.unpack("B",x[6])[0] + v = ( flags & 0x80 ) >> 7 + list.append(v) + v = ( flags & 0x40 ) >> 7 + list.append(v) + return list + def i2m(self, pkt, x): + if type(x) is str: + return x + s = "\x04\x00\x00\x04" + s += struct.pack("!H",x[0]) + byte = 0 + if x[1] == 1: + byte += 0x80 + if x[2] == 1: + byte += 0x40 + s += struct.pack("!B",byte) + s += "\x00" + return s + def getfield(self, pkt, s): + l = 8 + return s[l:],self.m2i(pkt, s[:l]) + + +# 3.5.3 Common Session Parameters TLV +class CommonSessionTLVField(StrField): + islist = 1 + def m2i(self, pkt, x): + l = [] + l.append(struct.unpack("!H",x[6:8])[0]) + octet = struct.unpack("B",x[8:9])[0] + l.append( (octet & 2**7 ) >> 7 ) + l.append( (octet & 2**6 ) >> 6 ) + l.append( struct.unpack("B",x[9:10])[0] ) + l.append( struct.unpack("!H",x[10:12])[0] ) + l.append( inet_ntoa(x[12:16]) ) + l.append( struct.unpack("!H",x[16:18])[0] ) + return l + def i2m(self, pkt, x): + if type(x) is str: + return x + s = "\x05\x00\x00\x0E\x00\x01" + s += struct.pack("!H",x[0]) + octet = 0 + if x[1] != 0: + octet += 2**7 + if x[2] != 0: + octet += 2**6 + s += struct.pack("!B",octet) + s += struct.pack("!B",x[3]) + s += struct.pack("!H",x[4]) + s += inet_aton(x[5]) + s += struct.pack("!H",x[6]) + return s + def getfield(self, pkt, s): + l = 18 + return s[l:],self.m2i(pkt, s[:l]) + + + +## Messages ## + +# 3.5.1. Notification Message +class LDPNotification(Packet): + name = "LDPNotification" + fields_desc = [ BitField("u",0,1), + BitField("type", 0x0001, 15), + ShortField("len", None), + IntField("id", 0) , + StatusTLVField("status",(0,0,0,0,0)) ] + def post_build(self, p, pay): + if self.len is None: + l = len(p) - 4 + p = p[:2]+struct.pack("!H", l)+p[4:] + return p+pay + def guess_payload_class(self, p): + return guess_payload(p) + + +# 3.5.2. Hello Message +class LDPHello(Packet): + name = "LDPHello" + fields_desc = [ BitField("u",0,1), + BitField("type", 0x0100, 15), + ShortField("len", None), + IntField("id", 0) , + CommonHelloTLVField("params",[180,0,0]) ] + def post_build(self, p, pay): + if self.len is None: + l = len(p) - 4 + p = p[:2]+struct.pack("!H", l)+p[4:] + return p+pay + def guess_payload_class(self, p): + return guess_payload(p) + + +# 3.5.3. Initialization Message +class LDPInit(Packet): + name = "LDPInit" + fields_desc = [ BitField("u",0,1), + XBitField("type", 0x0200, 15), + ShortField("len", None), + IntField("id", 0), + CommonSessionTLVField("params",None)] + def post_build(self, p, pay): + if self.len is None: + l = len(p) - 4 + p = p[:2]+struct.pack("!H", l)+p[4:] + return p+pay + def guess_payload_class(self, p): + return guess_payload(p) + + +# 3.5.4. KeepAlive Message +class LDPKeepAlive(Packet): + name = "LDPKeepAlive" + fields_desc = [ BitField("u",0,1), + XBitField("type", 0x0201, 15), + ShortField("len", None), + IntField("id", 0)] + def post_build(self, p, pay): + if self.len is None: + l = len(p) - 4 + p = p[:2]+struct.pack("!H", l)+p[4:] + return p+pay + def guess_payload_class(self, p): + return guess_payload(p) + + +# 3.5.5. Address Message + +class LDPAddress(Packet): + name = "LDPAddress" + fields_desc = [ BitField("u",0,1), + XBitField("type", 0x0300, 15), + ShortField("len", None), + IntField("id", 0), + AddressTLVField("address",None) ] + def post_build(self, p, pay): + if self.len is None: + l = len(p) - 4 + p = p[:2]+struct.pack("!H", l)+p[4:] + return p+pay + def guess_payload_class(self, p): + return guess_payload(p) + + +# 3.5.6. Address Withdraw Message + +class LDPAddressWM(Packet): + name = "LDPAddressWM" + fields_desc = [ BitField("u",0,1), + XBitField("type", 0x0301, 15), + ShortField("len", None), + IntField("id", 0), + AddressTLVField("address",None) ] + def post_build(self, p, pay): + if self.len is None: + l = len(p) - 4 + p = p[:2]+struct.pack("!H", l)+p[4:] + return p+pay + def guess_payload_class(self, p): + return guess_payload(p) + + +# 3.5.7. Label Mapping Message + +class LDPLabelMM(Packet): + name = "LDPLabelMM" + fields_desc = [ BitField("u",0,1), + XBitField("type", 0x0400, 15), + ShortField("len", None), + IntField("id", 0), + FecTLVField("fec",None), + LabelTLVField("label",0)] + def post_build(self, p, pay): + if self.len is None: + l = len(p) - 4 + p = p[:2]+struct.pack("!H", l)+p[4:] + return p+pay + def guess_payload_class(self, p): + return guess_payload(p) + +# 3.5.8. Label Request Message + +class LDPLabelReqM(Packet): + name = "LDPLabelReqM" + fields_desc = [ BitField("u",0,1), + XBitField("type", 0x0401, 15), + ShortField("len", None), + IntField("id", 0), + FecTLVField("fec",None)] + def post_build(self, p, pay): + if self.len is None: + l = len(p) - 4 + p = p[:2]+struct.pack("!H", l)+p[4:] + return p+pay + def guess_payload_class(self, p): + return guess_payload(p) + + +# 3.5.9. Label Abort Request Message + +class LDPLabelARM(Packet): + name = "LDPLabelARM" + fields_desc = [ BitField("u",0,1), + XBitField("type", 0x0404, 15), + ShortField("len", None), + IntField("id", 0), + FecTLVField("fec",None), + IntField("labelRMid",0)] + def post_build(self, p, pay): + if self.len is None: + l = len(p) - 4 + p = p[:2]+struct.pack("!H", l)+p[4:] + return p+pay + def guess_payload_class(self, p): + return guess_payload(p) + + +# 3.5.10. Label Withdraw Message + +class LDPLabelWM(Packet): + name = "LDPLabelWM" + fields_desc = [ BitField("u",0,1), + XBitField("type", 0x0402, 15), + ShortField("len", None), + IntField("id", 0), + FecTLVField("fec",None), + LabelTLVField("label",0)] + def post_build(self, p, pay): + if self.len is None: + l = len(p) - 4 + p = p[:2]+struct.pack("!H", l)+p[4:] + return p+pay + def guess_payload_class(self, p): + return guess_payload(p) + + +# 3.5.11. Label Release Message + +class LDPLabelRelM(Packet): + name = "LDPLabelRelM" + fields_desc = [ BitField("u",0,1), + XBitField("type", 0x0403, 15), + ShortField("len", None), + IntField("id", 0), + FecTLVField("fec",None), + LabelTLVField("label",0)] + def post_build(self, p, pay): + if self.len is None: + l = len(p) - 4 + p = p[:2]+struct.pack("!H", l)+p[4:] + return p+pay + def guess_payload_class(self, p): + return guess_payload(p) + + +# 3.1. LDP PDUs +class LDP(Packet): + name = "LDP" + fields_desc = [ ShortField("version",1), + ShortField("len", None), + IPField("id","127.0.0.1"), + ShortField("space",0) ] + def post_build(self, p, pay): + if self.len is None: + l = len(p)+len(pay)-4 + p = p[:2]+struct.pack("!H", l)+p[4:] + return p+pay + def guess_payload_class(self, p): + return guess_payload(p) + +bind_layers( TCP, LDP, sport=646, dport=646 ) +bind_layers( UDP, LDP, sport=646, dport=646 ) diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/mpls.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/mpls.py new file mode 100644 index 00000000..037278c5 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/mpls.py @@ -0,0 +1,17 @@ +# http://trac.secdev.org/scapy/ticket/31 + +# scapy.contrib.description = MPLS +# scapy.contrib.status = loads + +from scapy.packet import Packet,bind_layers +from scapy.fields import BitField,ByteField +from scapy.layers.l2 import Ether + +class MPLS(Packet): + name = "MPLS" + fields_desc = [ BitField("label", 3, 20), + BitField("cos", 0, 3), + BitField("s", 1, 1), + ByteField("ttl", 0) ] + +bind_layers(Ether, MPLS, type=0x8847) diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/ospf.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/ospf.py new file mode 100644 index 00000000..a6422bd8 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/ospf.py @@ -0,0 +1,833 @@ +#!/usr/bin/env python + +# scapy.contrib.description = OSPF +# scapy.contrib.status = loads + +""" +OSPF extension for Scapy + +This module provides Scapy layers for the Open Shortest Path First +routing protocol as defined in RFC 2328 and RFC 5340. + +Copyright (c) 2008 Dirk Loss : mail dirk-loss de +Copyright (c) 2010 Jochen Bartl : jochen.bartl gmail com + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +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. +""" + + +from scapy.all import * + +EXT_VERSION = "v0.9.2" + + +class OSPFOptionsField(FlagsField): + + def __init__(self, name="options", default=0, size=8, + names=["MT", "E", "MC", "NP", "L", "DC", "O", "DN"]): + FlagsField.__init__(self, name, default, size, names) + + +_OSPF_types = {1: "Hello", + 2: "DBDesc", + 3: "LSReq", + 4: "LSUpd", + 5: "LSAck"} + + +class OSPF_Hdr(Packet): + name = "OSPF Header" + fields_desc = [ + ByteField("version", 2), + ByteEnumField("type", 1, _OSPF_types), + ShortField("len", None), + IPField("src", "1.1.1.1"), + IPField("area", "0.0.0.0"), # default: backbone + XShortField("chksum", None), + ShortEnumField("authtype", 0, {0:"Null", 1:"Simple", 2:"Crypto"}), + # Null or Simple Authentication + ConditionalField(XLongField("authdata", 0), lambda pkt:pkt.authtype != 2), + # Crypto Authentication + ConditionalField(XShortField("reserved", 0), lambda pkt:pkt.authtype == 2), + ConditionalField(ByteField("keyid", 1), lambda pkt:pkt.authtype == 2), + ConditionalField(ByteField("authdatalen", 0), lambda pkt:pkt.authtype == 2), + ConditionalField(XIntField("seq", 0), lambda pkt:pkt.authtype == 2), + # TODO: Support authdata (which is appended to the packets as if it were padding) + ] + + def post_build(self, p, pay): + # TODO: Remove LLS data from pay + # LLS data blocks may be attached to OSPF Hello and DD packets + # The length of the LLS block shall not be included into the length of OSPF packet + # See + p += pay + l = self.len + if l is None: + l = len(p) + p = p[:2] + struct.pack("!H", l) + p[4:] + if self.chksum is None: + if self.authtype == 2: + ck = 0 # Crypto, see RFC 2328, D.4.3 + else: + # Checksum is calculated without authentication data + # Algorithm is the same as in IP() + ck = checksum(p[:16] + p[24:]) + p = p[:12] + chr(ck >> 8) + chr(ck & 0xff) + p[14:] + # TODO: Handle Crypto: Add message digest (RFC 2328, D.4.3) + return p + + def hashret(self): + return struct.pack("H", self.area) + self.payload.hashret() + + def answers(self, other): + if (isinstance(other, OSPF_Hdr) and + self.area == other.area and + self.type == 5): # Only acknowledgements answer other packets + return self.payload.answers(other.payload) + return 0 + + +class OSPF_Hello(Packet): + name = "OSPF Hello" + fields_desc = [IPField("mask", "255.255.255.0"), + ShortField("hellointerval", 10), + OSPFOptionsField(), + ByteField("prio", 1), + IntField("deadinterval", 40), + IPField("router", "0.0.0.0"), + IPField("backup", "0.0.0.0"), + FieldListField("neighbors", [], IPField("", "0.0.0.0"), length_from=lambda pkt: (pkt.underlayer.len - 44))] + + def guess_payload_class(self, payload): + # check presence of LLS data block flag + if self.options & 0x10 == 0x10: + return OSPF_LLS_Hdr + else: + return Packet.guess_payload_class(self, payload) + + +class LLS_Generic_TLV(Packet): + name = "LLS Generic" + fields_desc = [ShortField("type", 1), + FieldLenField("len", None, length_of=lambda x: x.val), + StrLenField("val", "", length_from=lambda x: x.len)] + + def guess_payload_class(self, p): + return conf.padding_layer + + +class LLS_ExtendedOptionsField(FlagsField): + + def __init__(self, name="options", default=0, size=32, + names=["LR", "RS"]): + FlagsField.__init__(self, name, default, size, names) + + +class LLS_Extended_Options(LLS_Generic_TLV): + name = "LLS Extended Options and Flags" + fields_desc = [ShortField("type", 1), + ShortField("len", 4), + LLS_ExtendedOptionsField()] + + +class LLS_Crypto_Auth(LLS_Generic_TLV): + name = "LLS Cryptographic Authentication" + fields_desc = [ShortField("type", 2), + FieldLenField("len", 20, fmt="B", length_of=lambda x: x.authdata), + XIntField("sequence", "\x00\x00\x00\x00"), + StrLenField("authdata", "\x00" * 16, length_from=lambda x: x.len)] + + def post_build(self, p, pay): + p += pay + l = self.len + + if l is None: + # length = len(sequence) + len(authdata) + len(payload) + l = len(p[3:]) + p = p[:2] + struct.pack("!H", l) + p[3:] + + return p + +_OSPF_LLSclasses = {1: "LLS_Extended_Options", + 2: "LLS_Crypto_Auth"} + + +def _LLSGuessPayloadClass(p, **kargs): + """ Guess the correct LLS class for a given payload """ + + cls = conf.raw_layer + if len(p) >= 4: + typ = struct.unpack("!H", p[0:2])[0] + clsname = _OSPF_LLSclasses.get(typ, "LLS_Generic_TLV") + cls = globals()[clsname] + return cls(p, **kargs) + + +class OSPF_LLS_Hdr(Packet): + name = "OSPF Link-local signaling" + fields_desc = [XShortField("chksum", None), + # FIXME Length should be displayed in 32-bit words + ShortField("len", None), + PacketListField("llstlv", [], _LLSGuessPayloadClass)] + + def post_build(self, p, pay): + p += pay + l = self.len + if l is None: + # Length in 32-bit words + l = len(p) / 4 + p = p[:2] + struct.pack("!H", l) + p[4:] + if self.chksum is None: + c = checksum(p) + p = chr((c >> 8) & 0xff) + chr(c & 0xff) + p[2:] + return p + +_OSPF_LStypes = {1: "router", + 2: "network", + 3: "summaryIP", + 4: "summaryASBR", + 5: "external", + 7: "NSSAexternal"} + +_OSPF_LSclasses = {1: "OSPF_Router_LSA", + 2: "OSPF_Network_LSA", + 3: "OSPF_SummaryIP_LSA", + 4: "OSPF_SummaryASBR_LSA", + 5: "OSPF_External_LSA", + 7: "OSPF_NSSA_External_LSA"} + + +def ospf_lsa_checksum(lsa): + """ Fletcher checksum for OSPF LSAs, returned as a 2 byte string. + + Give the whole LSA packet as argument. + For details on the algorithm, see RFC 2328 chapter 12.1.7 and RFC 905 Annex B. + """ + # This is based on the GPLed C implementation in Zebra + + CHKSUM_OFFSET = 16 + + if len(lsa) < CHKSUM_OFFSET: + raise Exception("LSA Packet too short (%s bytes)" % len(lsa)) + + c0 = c1 = 0 + # Calculation is done with checksum set to zero + lsa = lsa[:CHKSUM_OFFSET] + "\x00\x00" + lsa[CHKSUM_OFFSET + 2:] + for char in lsa[2:]: # leave out age + c0 += ord(char) + c1 += c0 + + c0 %= 255 + c1 %= 255 + + x = ((len(lsa) - CHKSUM_OFFSET - 1) * c0 - c1) % 255 + + if (x <= 0): + x += 255 + + y = 510 - c0 - x + + if (y > 255): + y -= 255 + #checksum = (x << 8) + y + + return chr(x) + chr(y) + + +class OSPF_LSA_Hdr(Packet): + name = "OSPF LSA Header" + fields_desc = [ShortField("age", 1), + OSPFOptionsField(), + ByteEnumField("type", 1, _OSPF_LStypes), + IPField("id", "192.168.0.0"), + IPField("adrouter", "1.1.1.1"), + XIntField("seq", 0x80000001), + XShortField("chksum", 0), + ShortField("len", 36)] + + def extract_padding(self, s): + return "", s + + +_OSPF_Router_LSA_types = {1: "p2p", + 2: "transit", + 3: "stub", + 4: "virtual"} + + +class OSPF_Link(Packet): + name = "OSPF Link" + fields_desc = [IPField("id", "192.168.0.0"), + IPField("data", "255.255.255.0"), + ByteEnumField("type", 3, _OSPF_Router_LSA_types), + ByteField("toscount", 0), + ShortField("metric", 10), + # TODO: define correct conditions + ConditionalField(ByteField("tos", 0), lambda pkt: False), + ConditionalField(ByteField("reserved", 0), lambda pkt: False), + ConditionalField(ShortField("tosmetric", 0), lambda pkt: False)] + + def extract_padding(self, s): + return "", s + + +def _LSAGuessPayloadClass(p, **kargs): + """ Guess the correct LSA class for a given payload """ + # This is heavily based on scapy-cdp.py by Nicolas Bareil and Arnaud Ebalard + # XXX: This only works if all payload + cls = conf.raw_layer + if len(p) >= 4: + typ = struct.unpack("!B", p[3])[0] + clsname = _OSPF_LSclasses.get(typ, "Raw") + cls = globals()[clsname] + return cls(p, **kargs) + + +class OSPF_BaseLSA(Packet): + """ An abstract base class for Link State Advertisements """ + + def post_build(self, p, pay): + length = self.len + if length is None: + length = len(p) + p = p[:18] + struct.pack("!H", length) + p[20:] + if self.chksum is None: + chksum = ospf_lsa_checksum(p) + p = p[:16] + chksum + p[18:] + return p # p+pay? + + def extract_padding(self, s): + length = self.len + return "", s + + +class OSPF_Router_LSA(OSPF_BaseLSA): + name = "OSPF Router LSA" + fields_desc = [ShortField("age", 1), + OSPFOptionsField(), + ByteField("type", 1), + IPField("id", "1.1.1.1"), + IPField("adrouter", "1.1.1.1"), + XIntField("seq", 0x80000001), + XShortField("chksum", None), + ShortField("len", None), + FlagsField("flags", 0, 8, ["B", "E", "V", "W", "Nt"]), + ByteField("reserved", 0), + FieldLenField("linkcount", None, count_of="linklist"), + PacketListField("linklist", [], OSPF_Link, + count_from=lambda pkt: pkt.linkcount, + length_from=lambda pkt: pkt.linkcount * 12)] + + +class OSPF_Network_LSA(OSPF_BaseLSA): + name = "OSPF Network LSA" + fields_desc = [ShortField("age", 1), + OSPFOptionsField(), + ByteField("type", 2), + IPField("id", "192.168.0.0"), + IPField("adrouter", "1.1.1.1"), + XIntField("seq", 0x80000001), + XShortField("chksum", None), + ShortField("len", None), + IPField("mask", "255.255.255.0"), + FieldListField("routerlist", [], IPField("", "1.1.1.1"), + length_from=lambda pkt: pkt.len - 24)] + + +class OSPF_SummaryIP_LSA(OSPF_BaseLSA): + name = "OSPF Summary LSA (IP Network)" + fields_desc = [ShortField("age", 1), + OSPFOptionsField(), + ByteField("type", 3), + IPField("id", "192.168.0.0"), + IPField("adrouter", "1.1.1.1"), + XIntField("seq", 0x80000001), + XShortField("chksum", None), + ShortField("len", None), + IPField("mask", "255.255.255.0"), + ByteField("reserved", 0), + X3BytesField("metric", 10), + # TODO: Define correct conditions + ConditionalField(ByteField("tos", 0), lambda pkt:False), + ConditionalField(X3BytesField("tosmetric", 0), lambda pkt:False)] + + +class OSPF_SummaryASBR_LSA(OSPF_SummaryIP_LSA): + name = "OSPF Summary LSA (AS Boundary Router)" + type = 4 + id = "2.2.2.2" + mask = "0.0.0.0" + metric = 20 + + +class OSPF_External_LSA(OSPF_BaseLSA): + name = "OSPF External LSA (ASBR)" + fields_desc = [ShortField("age", 1), + OSPFOptionsField(), + ByteField("type", 5), + IPField("id", "192.168.0.0"), + IPField("adrouter", "2.2.2.2"), + XIntField("seq", 0x80000001), + XShortField("chksum", None), + ShortField("len", None), + IPField("mask", "255.255.255.0"), + FlagsField("ebit", 0, 1, ["E"]), + BitField("reserved", 0, 7), + X3BytesField("metric", 20), + IPField("fwdaddr", "0.0.0.0"), + XIntField("tag", 0), + # TODO: Define correct conditions + ConditionalField(ByteField("tos", 0), lambda pkt:False), + ConditionalField(X3BytesField("tosmetric", 0), lambda pkt:False)] + + +class OSPF_NSSA_External_LSA(OSPF_External_LSA): + name = "OSPF NSSA External LSA" + type = 7 + + +class OSPF_DBDesc(Packet): + name = "OSPF Database Description" + fields_desc = [ShortField("mtu", 1500), + OSPFOptionsField(), + FlagsField("dbdescr", 0, 8, ["MS", "M", "I", "R", "4", "3", "2", "1"]), + IntField("ddseq", 1), + PacketListField("lsaheaders", None, OSPF_LSA_Hdr, + count_from = lambda pkt: None, + length_from = lambda pkt: pkt.underlayer.len - 24 - 8)] + + def guess_payload_class(self, payload): + # check presence of LLS data block flag + if self.options & 0x10 == 0x10: + return OSPF_LLS_Hdr + else: + return Packet.guess_payload_class(self, payload) + + +class OSPF_LSReq_Item(Packet): + name = "OSPF Link State Request (item)" + fields_desc = [IntEnumField("type", 1, _OSPF_LStypes), + IPField("id", "1.1.1.1"), + IPField("adrouter", "1.1.1.1")] + + def extract_padding(self, s): + return "", s + + +class OSPF_LSReq(Packet): + name = "OSPF Link State Request (container)" + fields_desc = [PacketListField("requests", None, OSPF_LSReq_Item, + count_from = lambda pkt:None, + length_from = lambda pkt:pkt.underlayer.len - 24)] + + +class OSPF_LSUpd(Packet): + name = "OSPF Link State Update" + fields_desc = [FieldLenField("lsacount", None, fmt="!I", count_of="lsalist"), + PacketListField("lsalist", [], _LSAGuessPayloadClass, + count_from = lambda pkt: pkt.lsacount, + length_from = lambda pkt: pkt.underlayer.len - 24)] + + +class OSPF_LSAck(Packet): + name = "OSPF Link State Acknowledgement" + fields_desc = [PacketListField("lsaheaders", None, OSPF_LSA_Hdr, + count_from = lambda pkt: None, + length_from = lambda pkt: pkt.underlayer.len - 24)] + + def answers(self, other): + if isinstance(other, OSPF_LSUpd): + for reqLSA in other.lsalist: + for ackLSA in self.lsaheaders: + if (reqLSA.type == ackLSA.type and + reqLSA.seq == ackLSA.seq): + return 1 + return 0 + + +#------------------------------------------------------------------------------ +# OSPFv3 +#------------------------------------------------------------------------------ +# TODO: Add length_from / adjust functionality to IP6Field and remove this class +class OspfIP6Field(StrField, IP6Field): + """ + Special IP6Field for prefix fields in OSPFv3 LSAs + """ + + def __init__(self, name, default, length=None, length_from=None): + StrField.__init__(self, name, default) + self.length_from = length_from + if length is not None: + self.length_from = lambda pkt, length = length: length + + def any2i(self, pkt, x): + return IP6Field.any2i(self, pkt, x) + + def i2repr(self, pkt, x): + return IP6Field.i2repr(self, pkt, x) + + def h2i(self, pkt, x): + return IP6Field.h2i(self, pkt, x) + + def i2m(self, pkt, x): + x = inet_pton(socket.AF_INET6, x) + l = self.length_from(pkt) + l = self.prefixlen_to_bytelen(l) + + return x[:l] + + def m2i(self, pkt, x): + l = self.length_from(pkt) + + prefixlen = self.prefixlen_to_bytelen(l) + if l > 128: + warning("OspfIP6Field: Prefix length is > 128. Dissection of this packet will fail") + else: + pad = "\x00" * (16 - prefixlen) + x += pad + + return inet_ntop(socket.AF_INET6, x) + + def prefixlen_to_bytelen(self, l): + if l <= 32: + return 4 + elif l <= 64: + return 8 + elif l <= 96: + return 12 + else: + return 16 + + def i2len(self, pkt, x): + l = self.length_from(pkt) + l = self.prefixlen_to_bytelen(l) + + return l + + def getfield(self, pkt, s): + l = self.length_from(pkt) + l = self.prefixlen_to_bytelen(l) + + return s[l:], self.m2i(pkt, s[:l]) + + +class OSPFv3_Hdr(Packet): + name = "OSPFv3 Header" + fields_desc = [ByteField("version", 3), + ByteEnumField("type", 1, _OSPF_types), + ShortField("len", None), + IPField("src", "1.1.1.1"), + IPField("area", "0.0.0.0"), + XShortField("chksum", None), + ByteField("instance", 0), + ByteField("reserved", 0)] + + 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:] + + if self.chksum is None: + chksum = in6_chksum(89, self.underlayer, p) + p = p[:12] + chr(chksum >> 8) + chr(chksum & 0xff) + p[14:] + + return p + + +class OSPFv3OptionsField(FlagsField): + + def __init__(self, name="options", default=0, size=24, + names=["V6", "E", "MC", "N", "R", "DC", "AF", "L", "I", "F"]): + FlagsField.__init__(self, name, default, size, names) + + +class OSPFv3_Hello(Packet): + name = "OSPFv3 Hello" + fields_desc = [IntField("intid", 0), + ByteField("prio", 1), + OSPFv3OptionsField(), + ShortField("hellointerval", 10), + ShortField("deadinterval", 40), + IPField("router", "0.0.0.0"), + IPField("backup", "0.0.0.0"), + FieldListField("neighbors", [], IPField("", "0.0.0.0"), + length_from=lambda pkt: (pkt.underlayer.len - 36))] + + +_OSPFv3_LStypes = {0x2001: "router", + 0x2002: "network", + 0x2003: "interAreaPrefix", + 0x2004: "interAreaRouter", + 0x4005: "asExternal", + 0x2007: "type7", + 0x0008: "link", + 0x2009: "intraAreaPrefix"} + +_OSPFv3_LSclasses = {0x2001: "OSPFv3_Router_LSA", + 0x2002: "OSPFv3_Network_LSA", + 0x2003: "OSPFv3_Inter_Area_Prefix_LSA", + 0x2004: "OSPFv3_Inter_Area_Router_LSA", + 0x4005: "OSPFv3_AS_External_LSA", + 0x2007: "OSPFv3_Type_7_LSA", + 0x0008: "OSPFv3_Link_LSA", + 0x2009: "OSPFv3_Intra_Area_Prefix_LSA"} + + +class OSPFv3_LSA_Hdr(Packet): + name = "OSPFv3 LSA Header" + fields_desc = [ShortField("age", 1), + ShortEnumField("type", 0x2001, _OSPFv3_LStypes), + IPField("id", "0.0.0.0"), + IPField("adrouter", "1.1.1.1"), + XIntField("seq", 0x80000001), + XShortField("chksum", 0), + ShortField("len", 36)] + + def extract_padding(self, s): + return "", s + + +def _OSPFv3_LSAGuessPayloadClass(p, **kargs): + """ Guess the correct OSPFv3 LSA class for a given payload """ + + cls = conf.raw_layer + + if len(p) >= 6: + typ = struct.unpack("!H", p[2:4])[0] + clsname = _OSPFv3_LSclasses.get(typ, "Raw") + cls = globals()[clsname] + + return cls(p, **kargs) + + +_OSPFv3_Router_LSA_types = {1: "p2p", + 2: "transit", + 3: "reserved", + 4: "virtual"} + + +class OSPFv3_Link(Packet): + name = "OSPFv3 Link" + fields_desc = [ByteEnumField("type", 1, _OSPFv3_Router_LSA_types), + ByteField("reserved", 0), + ShortField("metric", 10), + IntField("intid", 0), + IntField("neighintid", 0), + IPField("neighbor", "2.2.2.2")] + + def extract_padding(self, s): + return "", s + + +class OSPFv3_Router_LSA(OSPF_BaseLSA): + name = "OSPFv3 Router LSA" + fields_desc = [ShortField("age", 1), + ShortEnumField("type", 0x2001, _OSPFv3_LStypes), + IPField("id", "0.0.0.0"), + IPField("adrouter", "1.1.1.1"), + XIntField("seq", 0x80000001), + XShortField("chksum", None), + ShortField("len", None), + FlagsField("flags", 0, 8, ["B", "E", "V", "W"]), + OSPFv3OptionsField(), + PacketListField("linklist", [], OSPFv3_Link, + length_from=lambda pkt:pkt.len - 24)] + + +class OSPFv3_Network_LSA(OSPF_BaseLSA): + name = "OSPFv3 Network LSA" + fields_desc = [ShortField("age", 1), + ShortEnumField("type", 0x2002, _OSPFv3_LStypes), + IPField("id", "0.0.0.0"), + IPField("adrouter", "1.1.1.1"), + XIntField("seq", 0x80000001), + XShortField("chksum", None), + ShortField("len", None), + ByteField("reserved", 0), + OSPFv3OptionsField(), + FieldListField("routerlist", [], IPField("", "0.0.0.1"), + length_from=lambda pkt: pkt.len - 24)] + + +class OSPFv3PrefixOptionsField(FlagsField): + + def __init__(self, name="prefixoptions", default=0, size=8, + names=["NU", "LA", "MC", "P"]): + FlagsField.__init__(self, name, default, size, names) + + +class OSPFv3_Inter_Area_Prefix_LSA(OSPF_BaseLSA): + name = "OSPFv3 Inter Area Prefix LSA" + fields_desc = [ShortField("age", 1), + ShortEnumField("type", 0x2003, _OSPFv3_LStypes), + IPField("id", "0.0.0.0"), + IPField("adrouter", "1.1.1.1"), + XIntField("seq", 0x80000001), + XShortField("chksum", None), + ShortField("len", None), + ByteField("reserved", 0), + X3BytesField("metric", 10), + ByteField("prefixlen", 64), + OSPFv3PrefixOptionsField(), + ShortField("reserved2", 0), + OspfIP6Field("prefix", "2001:db8:0:42::", length_from=lambda pkt: pkt.prefixlen)] + + +class OSPFv3_Inter_Area_Router_LSA(OSPF_BaseLSA): + name = "OSPFv3 Inter Area Router LSA" + fields_desc = [ShortField("age", 1), + ShortEnumField("type", 0x2004, _OSPFv3_LStypes), + IPField("id", "0.0.0.0"), + IPField("adrouter", "1.1.1.1"), + XIntField("seq", 0x80000001), + XShortField("chksum", None), + ShortField("len", None), + ByteField("reserved", 0), + X3BytesField("metric", 1), + IPField("router", "2.2.2.2")] + + +class OSPFv3_AS_External_LSA(OSPF_BaseLSA): + name = "OSPFv3 AS External LSA" + fields_desc = [ShortField("age", 1), + ShortEnumField("type", 0x4005, _OSPFv3_LStypes), + IPField("id", "0.0.0.0"), + IPField("adrouter", "1.1.1.1"), + XIntField("seq", 0x80000001), + XShortField("chksum", None), + ShortField("len", None), + FlagsField("flags", 0, 8, ["T", "F", "E"]), + X3BytesField("metric", 20), + ByteField("prefixlen", 64), + OSPFv3PrefixOptionsField(), + ShortEnumField("reflstype", 0, _OSPFv3_LStypes), + OspfIP6Field("prefix", "2001:db8:0:42::", length_from=lambda pkt: pkt.prefixlen), + ConditionalField(IP6Field("fwaddr", "::"), lambda pkt: pkt.flags & 0x02 == 0x02), + ConditionalField(IntField("tag", 0), lambda pkt: pkt.flags & 0x01 == 0x01), + ConditionalField(IPField("reflsid", 0), lambda pkt: pkt.reflstype != 0)] + + +class OSPFv3_Type_7_LSA(OSPFv3_AS_External_LSA): + name = "OSPFv3 Type 7 LSA" + type = 0x2007 + + +class OSPFv3_Prefix_Item(Packet): + name = "OSPFv3 Link Prefix Item" + fields_desc = [ByteField("prefixlen", 64), + OSPFv3PrefixOptionsField(), + ShortField("metric", 10), + OspfIP6Field("prefix", "2001:db8:0:42::", length_from=lambda pkt: pkt.prefixlen)] + + def extract_padding(self, s): + return "", s + + +class OSPFv3_Link_LSA(OSPF_BaseLSA): + name = "OSPFv3 Link LSA" + fields_desc = [ShortField("age", 1), + ShortEnumField("type", 0x0008, _OSPFv3_LStypes), + IPField("id", "0.0.0.0"), + IPField("adrouter", "1.1.1.1"), + XIntField("seq", 0x80000001), + XShortField("chksum", None), + ShortField("len", None), + ByteField("prio", 1), + OSPFv3OptionsField(), + IP6Field("lladdr", "fe80::"), + IntField("prefixes", 0), + PacketListField("prefixlist", None, OSPFv3_Prefix_Item, + count_from = lambda pkt: pkt.prefixes)] + + +class OSPFv3_Intra_Area_Prefix_LSA(OSPF_BaseLSA): + name = "OSPFv3 Intra Area Prefix LSA" + fields_desc = [ShortField("age", 1), + ShortEnumField("type", 0x2009, _OSPFv3_LStypes), + IPField("id", "0.0.0.0"), + IPField("adrouter", "1.1.1.1"), + XIntField("seq", 0x80000001), + XShortField("chksum", None), + ShortField("len", None), + ShortField("prefixes", 0), + ShortEnumField("reflstype", 0, _OSPFv3_LStypes), + IPField("reflsid", "0.0.0.0"), + IPField("refadrouter", "0.0.0.0"), + PacketListField("prefixlist", None, OSPFv3_Prefix_Item, + count_from = lambda pkt: pkt.prefixes)] + + +class OSPFv3_DBDesc(Packet): + name = "OSPFv3 Database Description" + fields_desc = [ByteField("reserved", 0), + OSPFv3OptionsField(), + ShortField("mtu", 1500), + ByteField("reserved2", 0), + FlagsField("dbdescr", 0, 8, ["MS", "M", "I", "R"]), + IntField("ddseq", 1), + PacketListField("lsaheaders", None, OSPFv3_LSA_Hdr, + count_from = lambda pkt:None, + length_from = lambda pkt:pkt.underlayer.len - 28)] + + +class OSPFv3_LSReq_Item(Packet): + name = "OSPFv3 Link State Request (item)" + fields_desc = [ShortField("reserved", 0), + ShortEnumField("type", 0x2001, _OSPFv3_LStypes), + IPField("id", "1.1.1.1"), + IPField("adrouter", "1.1.1.1")] + + def extract_padding(self, s): + return "", s + + +class OSPFv3_LSReq(Packet): + name = "OSPFv3 Link State Request (container)" + fields_desc = [PacketListField("requests", None, OSPFv3_LSReq_Item, + count_from = lambda pkt:None, + length_from = lambda pkt:pkt.underlayer.len - 16)] + + +class OSPFv3_LSUpd(Packet): + name = "OSPFv3 Link State Update" + fields_desc = [FieldLenField("lsacount", None, fmt="!I", count_of="lsalist"), + PacketListField("lsalist", [], _OSPFv3_LSAGuessPayloadClass, + count_from = lambda pkt:pkt.lsacount, + length_from = lambda pkt:pkt.underlayer.len - 16)] + + +class OSPFv3_LSAck(Packet): + name = "OSPFv3 Link State Acknowledgement" + fields_desc = [PacketListField("lsaheaders", None, OSPFv3_LSA_Hdr, + count_from = lambda pkt:None, + length_from = lambda pkt:pkt.underlayer.len - 16)] + + +bind_layers(IP, OSPF_Hdr, proto=89) +bind_layers(OSPF_Hdr, OSPF_Hello, type=1) +bind_layers(OSPF_Hdr, OSPF_DBDesc, type=2) +bind_layers(OSPF_Hdr, OSPF_LSReq, type=3) +bind_layers(OSPF_Hdr, OSPF_LSUpd, type=4) +bind_layers(OSPF_Hdr, OSPF_LSAck, type=5) + +bind_layers(IPv6, OSPFv3_Hdr, nh=89) +bind_layers(OSPFv3_Hdr, OSPFv3_Hello, type=1) +bind_layers(OSPFv3_Hdr, OSPFv3_DBDesc, type=2) +bind_layers(OSPFv3_Hdr, OSPFv3_LSReq, type=3) +bind_layers(OSPFv3_Hdr, OSPFv3_LSUpd, type=4) +bind_layers(OSPFv3_Hdr, OSPFv3_LSAck, type=5) + + +if __name__ == "__main__": + interact(mydict=globals(), mybanner="OSPF extension %s" % EXT_VERSION) diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/ppi.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/ppi.py new file mode 100644 index 00000000..f4364096 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/ppi.py @@ -0,0 +1,86 @@ +## This file is (hopefully) part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## +## This program is published under a GPLv2 license + +# scapy.contrib.description = PPI +# scapy.contrib.status = loads + + +""" +PPI (Per-Packet Information). +""" +import logging,struct +from scapy.config import conf +from scapy.packet import * +from scapy.fields import * +from scapy.layers.l2 import Ether +from scapy.layers.dot11 import Dot11 + +# Dictionary to map the TLV type to the class name of a sub-packet +_ppi_types = {} +def addPPIType(id, value): + _ppi_types[id] = value +def getPPIType(id, default="default"): + return _ppi_types.get(id, _ppi_types.get(default, None)) + + +# Default PPI Field Header +class PPIGenericFldHdr(Packet): + name = "PPI Field Header" + fields_desc = [ LEShortField('pfh_type', 0), + FieldLenField('pfh_length', None, length_of="value", fmt='= 4: + t,pfh_len = struct.unpack(" pfh_len): + out.payload.payload = conf.padding_layer(p[pfh_len:]) + elif (len(p) > pfh_len): + out.payload = conf.padding_layer(p[pfh_len:]) + + else: + out = conf.raw_layer(p, **kargs) + return out + + + + +class PPI(Packet): + name = "PPI Packet Header" + fields_desc = [ ByteField('pph_version', 0), + ByteField('pph_flags', 0), + FieldLenField('pph_len', None, length_of="PPIFieldHeaders", fmt=" +## This program is published under a GPLv2 license + +# scapy.contrib.description = PPI CACE +# scapy.contrib.status = loads + +""" +CACE PPI types +""" +import logging,struct +from scapy.config import conf +from scapy.packet import * +from scapy.fields import * +from scapy.layers.l2 import Ether +from scapy.layers.dot11 import Dot11 +from scapy.contrib.ppi import * + +PPI_DOT11COMMON = 2 +PPI_DOT11NMAC = 3 +PPI_DOT11NMACPHY = 4 +PPI_SPECTRUMMAP = 5 +PPI_PROCESSINFO = 6 +PPI_CAPTUREINFO = 7 +PPI_AGGREGATION = 8 +PPI_DOT3 = 9 + +# PPI 802.11 Common Field Header Fields +class dBmByteField(Field): + def __init__(self, name, default): + Field.__init__(self, name, default, "b") + def i2repr(self, pkt, val): + if (val != None): + val = "%4d dBm" % val + return val + +class PPITSFTField(LELongField): + def i2h(self, pkt, val): + flags = 0 + if (pkt): + flags = pkt.getfieldval("Pkt_Flags") + if not flags: + flags = 0 + if (flags & 0x02): + scale = 1e-3 + else: + scale = 1e-6 + tout = scale * float(val) + return tout + def h2i(self, pkt, val): + scale = 1e6 + if pkt: + flags = pkt.getfieldval("Pkt_Flags") + if flags: + if (flags & 0x02): + scale = 1e3 + tout = int((scale * val) + 0.5) + return tout + +_PPIDot11CommonChFlags = ['','','','','Turbo','CCK','OFDM','2GHz','5GHz', + 'PassiveOnly','Dynamic CCK-OFDM','GSFK'] + +_PPIDot11CommonPktFlags = ['FCS','TSFT_ms','FCS_Invalid','PHY_Error'] + +# PPI 802.11 Common Field Header +class Dot11Common(Packet): + name = "PPI 802.11-Common" + fields_desc = [ LEShortField('pfh_type',PPI_DOT11COMMON), + LEShortField('pfh_length', 20), + PPITSFTField('TSF_Timer', 0), + FlagsField('Pkt_Flags',0, -16, _PPIDot11CommonPktFlags), + LEShortField('Rate',0), + LEShortField('Ch_Freq',0), + FlagsField('Ch_Flags', 0, -16, _PPIDot11CommonChFlags), + ByteField('FHSS_Hop',0), + ByteField('FHSS_Pat',0), + dBmByteField('Antsignal',-128), + dBmByteField('Antnoise',-128)] + + def extract_padding(self, p): + return "",p +#Hopefully other CACE defined types will be added here. + +#Add the dot11common layer to the PPI array +addPPIType(PPI_DOT11COMMON, Dot11Common) + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/ppi_geotag.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/ppi_geotag.py new file mode 100644 index 00000000..19371512 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/ppi_geotag.py @@ -0,0 +1,464 @@ +## This file is (hopefully) part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## +## This program is published under a GPLv2 license + +# scapy.contrib.description = PPI GEOLOCATION +# scapy.contrib.status = loads + + +""" +PPI-GEOLOCATION tags +""" +import struct +from scapy.packet import * +from scapy.fields import * +from scapy.contrib.ppi import PPIGenericFldHdr,addPPIType + +CURR_GEOTAG_VER = 2 #Major revision of specification + +PPI_GPS = 30002 +PPI_VECTOR = 30003 +PPI_SENSOR = 30004 +PPI_ANTENNA = 30005 +#The FixedX_Y Fields are used to store fixed point numbers in a variety of fields in the GEOLOCATION-TAGS specification +class Fixed3_6Field(LEIntField): + def i2h(self, pkt, x): + if x is not None: + if (x < 0): + warning("Fixed3_6: Internal value too negative: %d" % x) + x = 0 + elif (x > 999999999): + warning("Fixed3_6: Internal value too positive: %d" % x) + x = 999999999 + x = x * 1e-6 + return x + def h2i(self, pkt, x): + if x is not None: + if (x <= -0.5e-6): + warning("Fixed3_6: Input value too negative: %.7f" % x) + x = 0 + elif (x >= 999.9999995): + warning("Fixed3_6: Input value too positive: %.7f" % x) + x = 999.999999 + x = int(round(x * 1e6)) + return x + def i2m(self, pkt, x): + """Convert internal value to machine value""" + if x is None: + #Try to return zero if undefined + x = self.h2i(pkt, 0) + return x + + def i2repr(self,pkt,x): + if x is None: + y=0 + else: + y=self.i2h(pkt,x) + return "%3.6f"%(y) +class Fixed3_7Field(LEIntField): + def i2h(self, pkt, x): + if x is not None: + if (x < 0): + warning("Fixed3_7: Internal value too negative: %d" % x) + x = 0 + elif (x > 3600000000): + warning("Fixed3_7: Internal value too positive: %d" % x) + x = 3600000000 + x = (x - 1800000000) * 1e-7 + return x + def h2i(self, pkt, x): + if x is not None: + if (x <= -180.00000005): + warning("Fixed3_7: Input value too negative: %.8f" % x) + x = -180.0 + elif (x >= 180.00000005): + warning("Fixed3_7: Input value too positive: %.8f" % x) + x = 180.0 + x = int(round((x + 180.0) * 1e7)) + return x + def i2m(self, pkt, x): + """Convert internal value to machine value""" + if x is None: + #Try to return zero if undefined + x = self.h2i(pkt, 0) + return x + def i2repr(self,pkt,x): + if x is None: + y=0 + else: + y=self.i2h(pkt,x) + return "%3.7f"%(y) + +class Fixed6_4Field(LEIntField): + def i2h(self, pkt, x): + if x is not None: + if (x < 0): + warning("Fixed6_4: Internal value too negative: %d" % x) + x = 0 + elif (x > 3600000000): + warning("Fixed6_4: Internal value too positive: %d" % x) + x = 3600000000 + x = (x - 1800000000) * 1e-4 + return x + def h2i(self, pkt, x): + if x is not None: + if (x <= -180000.00005): + warning("Fixed6_4: Input value too negative: %.5f" % x) + x = -180000.0 + elif (x >= 180000.00005): + warning("Fixed6_4: Input value too positive: %.5f" % x) + x = 180000.0 + x = int(round((x + 180000.0) * 1e4)) + return x + def i2m(self, pkt, x): + """Convert internal value to machine value""" + if x is None: + #Try to return zero if undefined + x = self.h2i(pkt, 0) + return x + def i2repr(self,pkt,x): + if x is None: + y=0 + else: + y=self.i2h(pkt,x) + return "%6.4f"%(y) +#The GPS timestamps fractional time counter is stored in a 32-bit unsigned ns counter. +#The ept field is as well, +class NSCounter_Field(LEIntField): + def i2h(self, pkt, x): #converts nano-seconds to seconds for output + if x is not None: + if (x < 0): + warning("NSCounter_Field: Internal value too negative: %d" % x) + x = 0 + elif (x >= 2**32): + warning("NSCounter_Field: Internal value too positive: %d" % x) + x = 2**32-1 + x = (x / 1e9) + return x + def h2i(self, pkt, x): #converts input in seconds into nano-seconds for storage + if x is not None: + if (x < 0): + warning("NSCounter_Field: Input value too negative: %.10f" % x) + x = 0 + elif (x >= (2**32) / 1e9): + warning("NSCounter_Field: Input value too positive: %.10f" % x) + x = (2**32-1) / 1e9 + x = int(round((x * 1e9))) + return x + def i2repr(self,pkt,x): + if x is None: + y=0 + else: + y=self.i2h(pkt,x) + return "%1.9f"%(y) + +class UTCTimeField(IntField): + def __init__(self, name, default, epoch=time.gmtime(0), strf="%a, %d %b %Y %H:%M:%S +0000"): + IntField.__init__(self, name, default) + self.epoch = epoch + self.delta = time.mktime(epoch) - time.mktime(time.gmtime(0)) + self.strf = strf + def i2repr(self, pkt, x): + if x is None: + x = 0 + x = int(x) + self.delta + t = time.strftime(self.strf, time.gmtime(x)) + return "%s (%d)" % (t, x) + +class LETimeField(UTCTimeField,LEIntField): + def __init__(self, name, default, epoch=time.gmtime(0), strf="%a, %d %b %Y %H:%M:%S +0000"): + LEIntField.__init__(self, name, default) + self.epoch = epoch + self.delta = time.mktime(epoch) - time.mktime(time.gmtime(0)) + self.strf = strf + +class SignedByteField(Field): + def __init__(self, name, default): + Field.__init__(self, name, default, "b") + def randval(self): + return RandSByte() + +class XLEShortField(LEShortField,XShortField): + def i2repr(self, pkt, x): + return XShortField.i2repr(self, pkt, x) + +class XLEIntField(LEIntField,XIntField): + def i2repr(self, pkt, x): + return XIntField.i2repr(self, pkt, x) + +class GPSTime_Field(LETimeField): + def __init__(self, name, default): + return LETimeField.__init__(self, name, default, strf="%a, %d %b %Y %H:%M:%S UTC") + +class VectorFlags_Field(XLEIntField): + """Represents te VectorFlags field. Handles the RelativeTo:sub-field""" + _fwdstr = "DefinesForward" + _resmask = 0xfffffff8 + _relmask = 0x6 + _relnames = ["RelativeToForward", "RelativeToEarth", "RelativeToCurrent", "RelativeToReserved"] + _relvals = [0x00, 0x02, 0x04, 0x06] + def i2repr(self, pkt, x): + if x is None: + return str(x) + r = [] + if (x & 0x1): + r.append(self._fwdstr) + i = (x & self._relmask) >> 1 + r.append(self._relnames[i]) + i = x & self._resmask + if (i): + r.append("ReservedBits:%08X" % i) + sout = "+".join(r) + return sout + def any2i(self, pkt, x): + if type(x) is str: + r = x.split("+") + y = 0 + for value in r: + if (value == self._fwdstr): + y |= 0x1 + elif (value in self._relnames): + i = self._relnames.index(value) + y &= (~self._relmask) + y |= self._relvals[i] + else: + #logging.warning("Unknown VectorFlags Argument: %s" % value) + pass + else: + y = x + #print "any2i: %s --> %s" % (str(x), str(y)) + return y + +class HCSIFlagsField(FlagsField): + """ A FlagsField where each bit/flag turns a conditional field on or off. + If the value is None when building a packet, i2m() will check the value of + every field in self.names. If the field's value is not None, the corresponding + flag will be set. """ + def i2m(self, pkt, val): + if val is None: + val = 0 + if (pkt): + for i in range(len(self.names)): + name = self.names[i][0] + value = pkt.getfieldval(name) + if value is not None: + val |= 1 << i + return val + +class HCSINullField(StrFixedLenField): + def __init__(self, name, default): + return StrFixedLenField.__init__(self, name, default, length=0) + +class HCSIDescField(StrFixedLenField): + def __init__(self, name, default): + return StrFixedLenField.__init__(self, name, default, length=32) + +class HCSIAppField(StrFixedLenField): + def __init__(self, name, default): + return StrFixedLenField.__init__(self, name, default, length=60) + +def _FlagsList(myfields): + flags = [] + for i in range(32): + flags.append("Reserved%02d" % i) + for i in myfields.keys(): + flags[i] = myfields[i] + return flags + +# Define all geolocation-tag flags lists +_hcsi_gps_flags = _FlagsList({0:"No Fix Available", 1:"GPS", 2:"Differential GPS", + 3:"Pulse Per Second", 4:"Real Time Kinematic", + 5:"Float Real Time Kinematic", 6:"Estimated (Dead Reckoning)", + 7:"Manual Input", 8:"Simulation"}) + +#_hcsi_vector_flags = _FlagsList({0:"ForwardFrame", 1:"RotationsAbsoluteXYZ", 5:"OffsetFromGPS_XYZ"}) +#This has been replaced with the VectorFlags_Field class, in order to handle the RelativeTo:subfield + +_hcsi_vector_char_flags = _FlagsList({0:"Antenna", 1:"Direction of Travel", + 2:"Front of Vehicle", 3:"Angle of Arrival", 4:"Transmitter Position", + 8:"GPS Derived", 9:"INS Derived", 10:"Compass Derived", + 11:"Acclerometer Derived", 12:"Human Derived"}) + +_hcsi_antenna_flags = _FlagsList({ 1:"Horizontal Polarization", 2:"Vertical Polarization", + 3:"Circular Polarization Left", 4:"Circular Polarization Right", + 16:"Electronically Steerable", 17:"Mechanically Steerable"}) + +""" HCSI PPI Fields are similar to RadioTap. A mask field called "present" specifies if each field +is present. All other fields are conditional. When dissecting a packet, each field is present if +"present" has the corresponding bit set. When building a packet, if "present" is None, the mask is +set to include every field that does not have a value of None. Otherwise, if the mask field is +not None, only the fields specified by "present" will be added to the packet. + +To build each Packet type, build a list of the fields normally, excluding the present bitmask field. +The code will then construct conditional versions of each field and add the present field. +See GPS_Fields as an example. """ + +# Conditional test for all HCSI Fields +def _HCSITest(pkt, ibit, name): + if pkt.present is None: + return (pkt.getfieldval(name) is not None) + return pkt.present & ibit + +# Wrap optional fields in ConditionalField, add HCSIFlagsField +def _HCSIBuildFields(fields): + names = [f.name for f in fields] + cond_fields = [ HCSIFlagsField('present', None, -len(names), names)] + for i in range(len(names)): + ibit = 1 << i + seval = "lambda pkt:_HCSITest(pkt,%s,'%s')" % (ibit, names[i]) + test = eval(seval) + cond_fields.append(ConditionalField(fields[i], test)) + return cond_fields + +class HCSIPacket(Packet): + name = "PPI HCSI" + fields_desc = [ LEShortField('pfh_type', None), + LEShortField('pfh_length', None), + ByteField('geotag_ver', CURR_GEOTAG_VER), + ByteField('geotag_pad', 0), + LEShortField('geotag_len', None)] + def post_build(self, p, pay): + if self.pfh_length is None: + l = len(p) - 4 + sl = struct.pack('>8)&0xff)+chr(l&0xff)+p[8:] + if self.chksum is None: + ck = checksum(p) + p = p[:2]+chr(ck>>8)+chr(ck&0xff)+p[4:] + return p + +rsvptypes = { 0x01 : "Session", + 0x03 : "HOP", + 0x04 : "INTEGRITY", + 0x05 : "TIME_VALUES", + 0x06 : "ERROR_SPEC", + 0x07 : "SCOPE", + 0x08 : "STYLE", + 0x09 : "FLOWSPEC", + 0x0A : "FILTER_SPEC", + 0x0B : "SENDER_TEMPLATE", + 0x0C : "SENDER_TSPEC", + 0x0D : "ADSPEC", + 0x0E : "POLICY_DATA", + 0x0F : "RESV_CONFIRM", + 0x10 : "RSVP_LABEL", + 0x11 : "HOP_COUNT", + 0x12 : "STRICT_SOURCE_ROUTE", + 0x13 : "LABEL_REQUEST", + 0x14 : "EXPLICIT_ROUTE", + 0x15 : "ROUTE_RECORD", + 0x16 : "HELLO", + 0x17 : "MESSAGE_ID", + 0x18 : "MESSAGE_ID_ACK", + 0x19 : "MESSAGE_ID_LIST", + 0x1E : "DIAGNOSTIC", + 0x1F : "ROUTE", + 0x20 : "DIAG_RESPONSE", + 0x21 : "DIAG_SELECT", + 0x22 : "RECOVERY_LABEL", + 0x23 : "UPSTREAM_LABEL", + 0x24 : "LABEL_SET", + 0x25 : "PROTECTION", + 0x26 : "PRIMARY PATH ROUTE", + 0x2A : "DSBM IP ADDRESS", + 0x2B : "SBM_PRIORITY", + 0x2C : "DSBM TIMER INTERVALS", + 0x2D : "SBM_INFO", + 0x32 : "S2L_SUB_LSP", + 0x3F : "DETOUR", + 0x40 : "CHALLENGE", + 0x41 : "DIFF-SERV", + 0x42 : "CLASSTYPE", + 0x43 : "LSP_REQUIRED_ATTRIBUTES", + 0x80 : "NODE_CHAR", + 0x81 : "SUGGESTED_LABEL", + 0x82 : "ACCEPTABLE_LABEL_SET", + 0x83 : "RESTART_CA", + 0x84 : "SESSION-OF-INTEREST", + 0x85 : "LINK_CAPABILITY", + 0x86 : "Capability Object", + 0xA1 : "RSVP_HOP_L2", + 0xA2 : "LAN_NHOP_L2", + 0xA3 : "LAN_NHOP_L3", + 0xA4 : "LAN_LOOPBACK", + 0xA5 : "TCLASS", + 0xC0 : "TUNNEL", + 0xC1 : "LSP_TUNNEL_INTERFACE_ID", + 0xC2 : "USER_ERROR_SPEC", + 0xC3 : "NOTIFY_REQUEST", + 0xC4 : "ADMIN-STATUS", + 0xC5 : "LSP_ATTRIBUTES", + 0xC6 : "ALARM_SPEC", + 0xC7 : "ASSOCIATION", + 0xC8 : "SECONDARY_EXPLICIT_ROUTE", + 0xC9 : "SECONDARY_RECORD_ROUTE", + 0xCD : "FAST_REROUTE", + 0xCF : "SESSION_ATTRIBUTE", + 0xE1 : "DCLASS", + 0xE2 : "PACKETCABLE EXTENSIONS", + 0xE3 : "ATM_SERVICECLASS", + 0xE4 : "CALL_OPS (ASON)", + 0xE5 : "GENERALIZED_UNI", + 0xE6 : "CALL_ID", + 0xE7 : "3GPP2_Object", + 0xE8 : "EXCLUDE_ROUTE" +} + +class RSVP_Object(Packet): + name = "RSVP_Object" + fields_desc = [ ShortField("Length",4), + ByteEnumField("Class",0x01, rsvptypes), + ByteField("C-Type",1)] + def guess_payload_class(self, payload): + if self.Class == 0x03: + return RSVP_HOP + elif self.Class == 0x05: + return RSVP_Time + elif self.Class == 0x0c: + return RSVP_SenderTSPEC + elif self.Class == 0x13: + return RSVP_LabelReq + elif self.Class == 0xCF: + return RSVP_SessionAttrb + else: + return RSVP_Data + + + +class RSVP_Data(Packet): + name = "Data" + fields_desc = [StrLenField("Data","",length_from= lambda pkt:pkt.underlayer.Length - 4)] + def default_payload_class(self, payload): + return RSVP_Object + +class RSVP_HOP(Packet): + name = "HOP" + fields_desc = [ IPField("neighbor","0.0.0.0"), + BitField("inface",1,32)] + def default_payload_class(self, payload): + return RSVP_Object + +class RSVP_Time(Packet): + name = "Time Val" + fields_desc = [ BitField("refresh",1,32)] + def default_payload_class(self, payload): + return RSVP_Object + +class RSVP_SenderTSPEC(Packet): + name = "Sender_TSPEC" + fields_desc = [ ByteField("Msg_Format",0), + ByteField("reserve",0), + ShortField("Data_Length",4), + ByteField("Srv_hdr",1), + ByteField("reserve2",0), + ShortField("Srv_Length",4), + StrLenField("Tokens","",length_from= lambda pkt:pkt.underlayer.Length - 12) ] + def default_payload_class(self, payload): + return RSVP_Object + +class RSVP_LabelReq(Packet): + name = "Lable Req" + fields_desc = [ ShortField("reserve",1), + ShortField("L3PID",1)] + def default_payload_class(self, payload): + return RSVP_Object + +class RSVP_SessionAttrb(Packet): + name = "Session_Attribute" + fields_desc = [ ByteField("Setup_priority",1), + ByteField("Hold_priority",1), + ByteField("flags",1), + ByteField("Name_length",1), + StrLenField("Name","",length_from= lambda pkt:pkt.underlayer.Length - 8), + ] + def default_payload_class(self, payload): + return RSVP_Object + +bind_layers( IP, RSVP, { "proto" : 46} ) +bind_layers( RSVP, RSVP_Object, {}) diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/skinny.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/skinny.py new file mode 100644 index 00000000..47935c9e --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/skinny.py @@ -0,0 +1,499 @@ +#! /usr/bin/env python + +# scapy.contrib.description = Skinny Call Control Protocol (SCCP) +# scapy.contrib.status = loads + + +############################################################################# +## ## +## scapy-skinny.py --- Skinny Call Control Protocol (SCCP) extension ## +## ## +## Copyright (C) 2006 Nicolas Bareil ## +## EADS/CRC security team ## +## ## +## 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. ## +## ## +############################################################################# + +from scapy.all import * +import builtins + +##################################################################### +# Helpers and constants +##################################################################### + +skinny_messages_cls = { +# Station -> Callmanager + 0x0000: "SkinnyMessageKeepAlive", + 0x0001: "SkinnyMessageRegister", + 0x0002: "SkinnyMessageIpPort", + 0x0003: "SkinnyMessageKeypadButton", + 0x0004: "SkinnyMessageEnblocCall", + 0x0005: "SkinnyMessageStimulus", + 0x0006: "SkinnyMessageOffHook", + 0x0007: "SkinnyMessageOnHook", + 0x0008: "SkinnyMessageHookFlash", + 0x0009: "SkinnyMessageForwardStatReq", + 0x000A: "SkinnyMessageSpeedDialStatReq", + 0x000B: "SkinnyMessageLineStatReq", + 0x000C: "SkinnyMessageConfigStatReq", + 0x000D: "SkinnyMessageTimeDateReq", + 0x000E: "SkinnyMessageButtonTemplateReq", + 0x000F: "SkinnyMessageVersionReq", + 0x0010: "SkinnyMessageCapabilitiesRes", + 0x0011: "SkinnyMessageMediaPortList", + 0x0012: "SkinnyMessageServerReq", + 0x0020: "SkinnyMessageAlarm", + 0x0021: "SkinnyMessageMulticastMediaReceptionAck", + 0x0022: "SkinnyMessageOpenReceiveChannelAck", + 0x0023: "SkinnyMessageConnectionStatisticsRes", + 0x0024: "SkinnyMessageOffHookWithCgpn", + 0x0025: "SkinnyMessageSoftKeySetReq", + 0x0026: "SkinnyMessageSoftKeyEvent", + 0x0027: "SkinnyMessageUnregister", + 0x0028: "SkinnyMessageSoftKeyTemplateReq", + 0x0029: "SkinnyMessageRegisterTokenReq", + 0x002A: "SkinnyMessageMediaTransmissionFailure", + 0x002B: "SkinnyMessageHeadsetStatus", + 0x002C: "SkinnyMessageMediaResourceNotification", + 0x002D: "SkinnyMessageRegisterAvailableLines", + 0x002E: "SkinnyMessageDeviceToUserData", + 0x002F: "SkinnyMessageDeviceToUserDataResponse", + 0x0030: "SkinnyMessageUpdateCapabilities", + 0x0031: "SkinnyMessageOpenMultiMediaReceiveChannelAck", + 0x0032: "SkinnyMessageClearConference", + 0x0033: "SkinnyMessageServiceURLStatReq", + 0x0034: "SkinnyMessageFeatureStatReq", + 0x0035: "SkinnyMessageCreateConferenceRes", + 0x0036: "SkinnyMessageDeleteConferenceRes", + 0x0037: "SkinnyMessageModifyConferenceRes", + 0x0038: "SkinnyMessageAddParticipantRes", + 0x0039: "SkinnyMessageAuditConferenceRes", + 0x0040: "SkinnyMessageAuditParticipantRes", + 0x0041: "SkinnyMessageDeviceToUserDataVersion1", +# Callmanager -> Station */ + 0x0081: "SkinnyMessageRegisterAck", + 0x0082: "SkinnyMessageStartTone", + 0x0083: "SkinnyMessageStopTone", + 0x0085: "SkinnyMessageSetRinger", + 0x0086: "SkinnyMessageSetLamp", + 0x0087: "SkinnyMessageSetHkFDetect", + 0x0088: "SkinnyMessageSpeakerMode", + 0x0089: "SkinnyMessageSetMicroMode", + 0x008A: "SkinnyMessageStartMediaTransmission", + 0x008B: "SkinnyMessageStopMediaTransmission", + 0x008C: "SkinnyMessageStartMediaReception", + 0x008D: "SkinnyMessageStopMediaReception", + 0x008F: "SkinnyMessageCallInfo", + 0x0090: "SkinnyMessageForwardStat", + 0x0091: "SkinnyMessageSpeedDialStat", + 0x0092: "SkinnyMessageLineStat", + 0x0093: "SkinnyMessageConfigStat", + 0x0094: "SkinnyMessageTimeDate", + 0x0095: "SkinnyMessageStartSessionTransmission", + 0x0096: "SkinnyMessageStopSessionTransmission", + 0x0097: "SkinnyMessageButtonTemplate", + 0x0098: "SkinnyMessageVersion", + 0x0099: "SkinnyMessageDisplayText", + 0x009A: "SkinnyMessageClearDisplay", + 0x009B: "SkinnyMessageCapabilitiesReq", + 0x009C: "SkinnyMessageEnunciatorCommand", + 0x009D: "SkinnyMessageRegisterReject", + 0x009E: "SkinnyMessageServerRes", + 0x009F: "SkinnyMessageReset", + 0x0100: "SkinnyMessageKeepAliveAck", + 0x0101: "SkinnyMessageStartMulticastMediaReception", + 0x0102: "SkinnyMessageStartMulticastMediaTransmission", + 0x0103: "SkinnyMessageStopMulticastMediaReception", + 0x0104: "SkinnyMessageStopMulticastMediaTransmission", + 0x0105: "SkinnyMessageOpenReceiveChannel", + 0x0106: "SkinnyMessageCloseReceiveChannel", + 0x0107: "SkinnyMessageConnectionStatisticsReq", + 0x0108: "SkinnyMessageSoftKeyTemplateRes", + 0x0109: "SkinnyMessageSoftKeySetRes", + 0x0110: "SkinnyMessageSoftKeyEvent", + 0x0111: "SkinnyMessageCallState", + 0x0112: "SkinnyMessagePromptStatus", + 0x0113: "SkinnyMessageClearPromptStatus", + 0x0114: "SkinnyMessageDisplayNotify", + 0x0115: "SkinnyMessageClearNotify", + 0x0116: "SkinnyMessageCallPlane", + 0x0117: "SkinnyMessageCallPlane", + 0x0118: "SkinnyMessageUnregisterAck", + 0x0119: "SkinnyMessageBackSpaceReq", + 0x011A: "SkinnyMessageRegisterTokenAck", + 0x011B: "SkinnyMessageRegisterTokenReject", + 0x0042: "SkinnyMessageDeviceToUserDataResponseVersion1", + 0x011C: "SkinnyMessageStartMediaFailureDetection", + 0x011D: "SkinnyMessageDialedNumber", + 0x011E: "SkinnyMessageUserToDeviceData", + 0x011F: "SkinnyMessageFeatureStat", + 0x0120: "SkinnyMessageDisplayPriNotify", + 0x0121: "SkinnyMessageClearPriNotify", + 0x0122: "SkinnyMessageStartAnnouncement", + 0x0123: "SkinnyMessageStopAnnouncement", + 0x0124: "SkinnyMessageAnnouncementFinish", + 0x0127: "SkinnyMessageNotifyDtmfTone", + 0x0128: "SkinnyMessageSendDtmfTone", + 0x0129: "SkinnyMessageSubscribeDtmfPayloadReq", + 0x012A: "SkinnyMessageSubscribeDtmfPayloadRes", + 0x012B: "SkinnyMessageSubscribeDtmfPayloadErr", + 0x012C: "SkinnyMessageUnSubscribeDtmfPayloadReq", + 0x012D: "SkinnyMessageUnSubscribeDtmfPayloadRes", + 0x012E: "SkinnyMessageUnSubscribeDtmfPayloadErr", + 0x012F: "SkinnyMessageServiceURLStat", + 0x0130: "SkinnyMessageCallSelectStat", + 0x0131: "SkinnyMessageOpenMultiMediaChannel", + 0x0132: "SkinnyMessageStartMultiMediaTransmission", + 0x0133: "SkinnyMessageStopMultiMediaTransmission", + 0x0134: "SkinnyMessageMiscellaneousCommand", + 0x0135: "SkinnyMessageFlowControlCommand", + 0x0136: "SkinnyMessageCloseMultiMediaReceiveChannel", + 0x0137: "SkinnyMessageCreateConferenceReq", + 0x0138: "SkinnyMessageDeleteConferenceReq", + 0x0139: "SkinnyMessageModifyConferenceReq", + 0x013A: "SkinnyMessageAddParticipantReq", + 0x013B: "SkinnyMessageDropParticipantReq", + 0x013C: "SkinnyMessageAuditConferenceReq", + 0x013D: "SkinnyMessageAuditParticipantReq", + 0x013F: "SkinnyMessageUserToDeviceDataVersion1", + } + +skinny_callstates = { + 0x1: "Off Hook", + 0x2: "On Hook", + 0x3: "Ring out", + 0xc: "Proceeding", +} + + +skinny_ring_type = { + 0x1: "Ring off" +} + +skinny_speaker_modes = { + 0x1: "Speaker on", + 0x2: "Speaker off" +} + +skinny_lamp_mode = { + 0x1: "Off (?)", + 0x2: "On", +} + +skinny_stimulus = { + 0x9: "Line" +} + + +############ +## Fields ## +############ + +class SkinnyDateTimeField(StrFixedLenField): + def __init__(self, name, default): + StrFixedLenField.__init__(self, name, default, 32) + + def m2i(self, pkt, s): + year,month,dow,day,hour,min,sec,milisecond=struct.unpack('<8I', s) + return (year, month, day, hour, min, sec) + + def i2m(self, pkt, val): + if type(val) is str: + val = self.h2i(pkt, val) + l= val[:2] + (0,) + val[2:7] + (0,) + return struct.pack('<8I', *l) + + def i2h(self, pkt, x): + if type(x) is str: + return x + else: + return time.ctime(time.mktime(x+(0,0,0))) + + def i2repr(self, pkt, x): + return self.i2h(pkt, x) + + def h2i(self, pkt, s): + t = () + if type(s) is str: + t = time.strptime(s) + t = t[:2] + t[2:-3] + else: + if not s: + y,m,d,h,min,sec,rest,rest,rest = time.gmtime(time.time()) + t = (y,m,d,h,min,sec) + else: + t=s + return t + + +########################### +## Packet abstract class ## +########################### + +class SkinnyMessageGeneric(Packet): + name='Generic message' + +class SkinnyMessageKeepAlive(Packet): + name='keep alive' + +class SkinnyMessageKeepAliveAck(Packet): + name='keep alive ack' + +class SkinnyMessageOffHook(Packet): + name = 'Off Hook' + fields_desc = [ LEIntField("unknown1", 0), + LEIntField("unknown2", 0),] + +class SkinnyMessageOnHook(SkinnyMessageOffHook): + name = 'On Hook' + +class SkinnyMessageCallState(Packet): + name='Skinny Call state message' + fields_desc = [ LEIntEnumField("state", 1, skinny_callstates), + LEIntField("instance", 1), + LEIntField("callid", 0), + LEIntField("unknown1", 4), + LEIntField("unknown2", 0), + LEIntField("unknown3", 0) ] + +class SkinnyMessageSoftKeyEvent(Packet): + name='Soft Key Event' + fields_desc = [ LEIntField("key", 0), + LEIntField("instance", 1), + LEIntField("callid", 0)] + +class SkinnyMessageSetRinger(Packet): + name='Ring message' + fields_desc = [ LEIntEnumField("ring", 0x1, skinny_ring_type), + LEIntField("unknown1", 0), + LEIntField("unknown2", 0), + LEIntField("unknown3", 0) ] + +_skinny_tones = { + 0x21: 'Inside dial tone', + 0x22: 'xxx', + 0x23: 'xxx', + 0x24: 'Alerting tone', + 0x25: 'Reorder Tone' + } + +class SkinnyMessageStartTone(Packet): + name='Start tone' + fields_desc = [ LEIntEnumField("tone", 0x21, _skinny_tones), + LEIntField("unknown1", 0), + LEIntField("instance", 1), + LEIntField("callid", 0)] + +class SkinnyMessageStopTone(SkinnyMessageGeneric): + name='stop tone' + fields_desc = [ LEIntField("instance", 1), + LEIntField("callid", 0)] + + +class SkinnyMessageSpeakerMode(Packet): + name='Speaker mdoe' + fields_desc = [ LEIntEnumField("ring", 0x1, skinny_speaker_modes) ] + +class SkinnyMessageSetLamp(Packet): + name='Lamp message (light of the phone)' + fields_desc = [ LEIntEnumField("stimulus", 0x5, skinny_stimulus), + LEIntField("instance", 1), + LEIntEnumField("mode", 2, skinny_lamp_mode) ] + +class SkinnyMessageSoftKeyEvent(Packet): + name=' Call state message' + fields_desc = [ LEIntField("instance", 1), + LEIntField("callid", 0), + LEIntField("set", 0), + LEIntField("map", 0xffff)] + +class SkinnyMessagePromptStatus(Packet): + name='Prompt status' + fields_desc = [ LEIntField("timeout", 0), + StrFixedLenField("text", "\0"*32, 32), + LEIntField("instance", 1), + LEIntField("callid", 0)] + +class SkinnyMessageCallPlane(Packet): + name='Activate/Desactivate Call Plane Message' + fields_desc = [ LEIntField("instance", 1)] + +class SkinnyMessageTimeDate(Packet): + name='Setting date and time' + fields_desc = [ SkinnyDateTimeField("settime", None), + LEIntField("timestamp", 0) ] + +class SkinnyMessageClearPromptStatus(Packet): + name='clear prompt status' + fields_desc = [ LEIntField("instance", 1), + LEIntField("callid", 0)] + +class SkinnyMessageKeypadButton(Packet): + name='keypad button' + fields_desc = [ LEIntField("key", 0), + LEIntField("instance", 1), + LEIntField("callid", 0)] + +class SkinnyMessageDialedNumber(Packet): + name='dialed number' + fields_desc = [ StrFixedLenField("number", "1337", 24), + LEIntField("instance", 1), + LEIntField("callid", 0)] + +_skinny_message_callinfo_restrictions = ['CallerName' + , 'CallerNumber' + , 'CalledName' + , 'CalledNumber' + , 'OriginalCalledName' + , 'OriginalCalledNumber' + , 'LastRedirectName' + , 'LastRedirectNumber'] + ['Bit%d' % i for i in range(8,15)] +class SkinnyMessageCallInfo(Packet): + name='call information' + fields_desc = [ StrFixedLenField("callername", "Jean Valjean", 40), + StrFixedLenField("callernum", "1337", 24), + StrFixedLenField("calledname", "Causette", 40), + StrFixedLenField("callednum", "1034", 24), + LEIntField("lineinstance", 1), + LEIntField("callid", 0), + StrFixedLenField("originalcalledname", "Causette", 40), + StrFixedLenField("originalcallednum", "1034", 24), + StrFixedLenField("lastredirectingname", "Causette", 40), + StrFixedLenField("lastredirectingnum", "1034", 24), + LEIntField("originalredirectreason", 0), + LEIntField("lastredirectreason", 0), + StrFixedLenField('voicemailboxG', '\0'*24, 24), + StrFixedLenField('voicemailboxD', '\0'*24, 24), + StrFixedLenField('originalvoicemailboxD', '\0'*24, 24), + StrFixedLenField('lastvoicemailboxD', '\0'*24, 24), + LEIntField('security', 0), + FlagsField('restriction', 0, 16, _skinny_message_callinfo_restrictions), + LEIntField('unknown', 0)] + + +class SkinnyRateField(LEIntField): + def i2repr(self, pkt, x): + if x is None: + x=0 + return '%d ms/pkt' % x + +_skinny_codecs = { + 0x0: 'xxx', + 0x1: 'xxx', + 0x2: 'xxx', + 0x3: 'xxx', + 0x4: 'G711 ulaw 64k' + } + +_skinny_echo = { + 0x0: 'echo cancelation off', + 0x1: 'echo cancelation on' + } + +class SkinnyMessageOpenReceiveChannel(Packet): + name='open receive channel' + fields_desc = [LEIntField('conference', 0), + LEIntField('passthru', 0), + SkinnyRateField('rate', 20), + LEIntEnumField('codec', 4, _skinny_codecs), + LEIntEnumField('echo', 0, _skinny_echo), + LEIntField('unknown1', 0), + LEIntField('callid', 0)] + + def guess_payload_class(self, p): + return conf.padding_layer + +_skinny_receive_channel_status = { + 0x0: 'ok', + 0x1: 'ko' + } + +class SkinnyMessageOpenReceiveChannelAck(Packet): + name='open receive channel' + fields_desc = [LEIntEnumField('status', 0, _skinny_receive_channel_status), + IPField('remote', '0.0.0.0'), + LEIntField('port', RandShort()), + LEIntField('passthru', 0), + LEIntField('callid', 0)] + +_skinny_silence = { + 0x0: 'silence suppression off', + 0x1: 'silence suppression on', + } + +class SkinnyFramePerPacketField(LEIntField): + def i2repr(self, pkt, x): + if x is None: + x=0 + return '%d frames/pkt' % x + +class SkinnyMessageStartMediaTransmission(Packet): + name='start multimedia transmission' + fields_desc = [LEIntField('conference', 0), + LEIntField('passthru', 0), + IPField('remote', '0.0.0.0'), + LEIntField('port', RandShort()), + SkinnyRateField('rate', 20), + LEIntEnumField('codec', 4, _skinny_codecs), + LEIntField('precedence', 200), + LEIntEnumField('silence', 0, _skinny_silence), + SkinnyFramePerPacketField('maxframes', 0), + LEIntField('unknown1', 0), + LEIntField('callid', 0)] + + def guess_payload_class(self, p): + return conf.padding_layer + +class SkinnyMessageCloseReceiveChannel(Packet): + name='close receive channel' + fields_desc = [LEIntField('conference', 0), + LEIntField('passthru', 0), + IPField('remote', '0.0.0.0'), + LEIntField('port', RandShort()), + SkinnyRateField('rate', 20), + LEIntEnumField('codec', 4, _skinny_codecs), + LEIntField('precedence', 200), + LEIntEnumField('silence', 0, _skinny_silence), + LEIntField('callid', 0)] + +class SkinnyMessageStopMultiMediaTransmission(Packet): + name='stop multimedia transmission' + fields_desc = [LEIntField('conference', 0), + LEIntField('passthru', 0), + LEIntField('callid', 0)] + +class Skinny(Packet): + name="Skinny" + fields_desc = [ LEIntField("len", None), + LEIntField("res",0), + LEIntEnumField("msg",0, skinny_messages) ] + + def post_build(self, pkt, p): + if self.len is None: + l=len(p)+len(pkt)-8 # on compte pas les headers len et reserved + pkt=struct.pack('@I', l)+pkt[4:] + return pkt+p + +# An helper +def get_cls(name, fallback_cls): + return globals().get(name, fallback_cls) + #return builtins.__dict__.get(name, fallback_cls) + +for msgid,strcls in skinny_messages_cls.items(): + cls=get_cls(strcls, SkinnyMessageGeneric) + bind_layers(Skinny, cls, {"msg": msgid}) + +bind_layers(TCP, Skinny, { "dport": 2000 } ) +bind_layers(TCP, Skinny, { "sport": 2000 } ) + +if __name__ == "__main__": + interact(mydict=globals(),mybanner="Welcome to Skinny add-on") + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/ubberlogger.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/ubberlogger.py new file mode 100644 index 00000000..1c01db2f --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/ubberlogger.py @@ -0,0 +1,101 @@ +# Author: Sylvain SARMEJEANNE +# http://trac.secdev.org/scapy/ticket/1 + +# scapy.contrib.description = Ubberlogger dissectors +# scapy.contrib.status = untested + +from scapy.packet import * +from scapy.fields import * + +# Syscalls known by Uberlogger +uberlogger_sys_calls = {0:"READ_ID", + 1:"OPEN_ID", + 2:"WRITE_ID", + 3:"CHMOD_ID", + 4:"CHOWN_ID", + 5:"SETUID_ID", + 6:"CHROOT_ID", + 7:"CREATE_MODULE_ID", + 8:"INIT_MODULE_ID", + 9:"DELETE_MODULE_ID", + 10:"CAPSET_ID", + 11:"CAPGET_ID", + 12:"FORK_ID", + 13:"EXECVE_ID"} + +# First part of the header +class Uberlogger_honeypot_caract(Packet): + name = "Uberlogger honeypot_caract" + fields_desc = [ByteField("honeypot_id", 0), + ByteField("reserved", 0), + ByteField("os_type_and_version", 0)] + +# Second part of the header +class Uberlogger_uber_h(Packet): + name = "Uberlogger uber_h" + fields_desc = [ByteEnumField("syscall_type", 0, uberlogger_sys_calls), + IntField("time_sec", 0), + IntField("time_usec", 0), + IntField("pid", 0), + IntField("uid", 0), + IntField("euid", 0), + IntField("cap_effective", 0), + IntField("cap_inheritable", 0), + IntField("cap_permitted", 0), + IntField("res", 0), + IntField("length", 0)] + +# The 9 following classes are options depending on the syscall type +class Uberlogger_capget_data(Packet): + name = "Uberlogger capget_data" + fields_desc = [IntField("target_pid", 0)] + +class Uberlogger_capset_data(Packet): + name = "Uberlogger capset_data" + fields_desc = [IntField("target_pid", 0), + IntField("effective_cap", 0), + IntField("permitted_cap", 0), + IntField("inheritable_cap", 0)] + +class Uberlogger_chmod_data(Packet): + name = "Uberlogger chmod_data" + fields_desc = [ShortField("mode", 0)] + +class Uberlogger_chown_data(Packet): + name = "Uberlogger chown_data" + fields_desc = [IntField("uid", 0), + IntField("gid", 0)] + +class Uberlogger_open_data(Packet): + name = "Uberlogger open_data" + fields_desc = [IntField("flags", 0), + IntField("mode", 0)] + +class Uberlogger_read_data(Packet): + name = "Uberlogger read_data" + fields_desc = [IntField("fd", 0), + IntField("count", 0)] + +class Uberlogger_setuid_data(Packet): + name = "Uberlogger setuid_data" + fields_desc = [IntField("uid", 0)] + +class Uberlogger_create_module_data(Packet): + name = "Uberlogger create_module_data" + fields_desc = [IntField("size", 0)] + +class Uberlogger_execve_data(Packet): + name = "Uberlogger execve_data" + fields_desc = [IntField("nbarg", 0)] + +# Layer bounds for Uberlogger +bind_layers(Uberlogger_honeypot_caract,Uberlogger_uber_h) +bind_layers(Uberlogger_uber_h,Uberlogger_capget_data) +bind_layers(Uberlogger_uber_h,Uberlogger_capset_data) +bind_layers(Uberlogger_uber_h,Uberlogger_chmod_data) +bind_layers(Uberlogger_uber_h,Uberlogger_chown_data) +bind_layers(Uberlogger_uber_h,Uberlogger_open_data) +bind_layers(Uberlogger_uber_h,Uberlogger_read_data) +bind_layers(Uberlogger_uber_h,Uberlogger_setuid_data) +bind_layers(Uberlogger_uber_h,Uberlogger_create_module_data) +bind_layers(Uberlogger_uber_h,Uberlogger_execve_data) diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/vqp.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/vqp.py new file mode 100644 index 00000000..9328cea4 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/vqp.py @@ -0,0 +1,58 @@ + +# http://trac.secdev.org/scapy/ticket/147 + +# scapy.contrib.description = VLAN Query Protocol +# scapy.contrib.status = loads + +from scapy.packet import * +from scapy.fields import * +from scapy.layers.inet import UDP + +class VQP(Packet): + name = "VQP" + fields_desc = [ + ByteField("const", 1), + ByteEnumField("type", 1, { + 1:"requestPort", 2:"responseVLAN", + 3:"requestReconfirm", 4:"responseReconfirm" + }), + ByteEnumField("errorcodeaction", 0, { + 0:"none",3:"accessDenied", + 4:"shutdownPort", 5:"wrongDomain" + }), + ByteEnumField("unknown", 2, { + 2:"inGoodResponse", 6:"inRequests" + }), + IntField("seq",0), + ] + +class VQPEntry(Packet): + name = "VQPEntry" + fields_desc = [ + IntEnumField("datatype", 0, { + 3073:"clientIPAddress", 3074:"portName", + 3075:"VLANName", 3076:"Domain", 3077:"ethernetPacket", + 3078:"ReqMACAddress", 3079:"unknown", + 3080:"ResMACAddress" + }), + FieldLenField("len", None), + ConditionalField(IPField("datatom", "0.0.0.0"), + lambda p:p.datatype==3073), + ConditionalField(MACField("data", "00:00:00:00:00:00"), + lambda p:p.datatype==3078), + ConditionalField(MACField("data", "00:00:00:00:00:00"), + lambda p:p.datatype==3080), + ConditionalField(StrLenField("data", None, + length_from=lambda p:p.len), + lambda p:p.datatype not in [3073, 3078, 3080]), + ] + def post_build(self, p, pay): + if self.len is None: + l = len(p.data) + p = p[:2]+struct.pack("!H",l)+p[4:] + return p + +bind_layers(UDP, VQP, sport=1589) +bind_layers(UDP, VQP, dport=1589) +bind_layers(VQP, VQPEntry, ) +bind_layers(VQPEntry, VQPEntry, ) diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/vtp.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/vtp.py new file mode 100644 index 00000000..af5c2823 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/vtp.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python + +# scapy.contrib.description = VLAN Trunking Protocol (VTP) +# scapy.contrib.status = loads + +""" + VTP Scapy Extension + ~~~~~~~~~~~~~~~~~~~~~ + + :version: 2009-02-15 + :copyright: 2009 by Jochen Bartl + :e-mail: lobo@c3a.de / jochen.bartl@gmail.com + :license: GPL v2 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + 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. + + :TODO + + - Join messages + - RE MD5 hash calculation + - Have a closer look at 8 byte padding in summary adv. + "debug sw-vlan vtp packets" sais the TLV length is invalid, + when I change the values + '\x00\x00\x00\x01\x06\x01\x00\x02' + * \x00\x00 ? + * \x00\x01 tlvtype? + * \x06 length? + * \x00\x02 value? + - h2i function for VTPTimeStampField + + :References: + + - Understanding VLAN Trunk Protocol (VTP) + http://www.cisco.com/en/US/tech/tk389/tk689/technologies_tech_note09186a0080094c52.shtml +""" + +from scapy.all import * + +_VTP_VLAN_TYPE = { + 1 : 'Ethernet', + 2 : 'FDDI', + 3 : 'TrCRF', + 4 : 'FDDI-net', + 5 : 'TrBRF' + } + +_VTP_VLANINFO_TLV_TYPE = { + 0x01 : 'Source-Routing Ring Number', + 0x02 : 'Source-Routing Bridge Number', + 0x03 : 'Spanning-Tree Protocol Type', + 0x04 : 'Parent VLAN', + 0x05 : 'Translationally Bridged VLANs', + 0x06 : 'Pruning', + 0x07 : 'Bridge Type', + 0x08 : 'Max ARE Hop Count', + 0x09 : 'Max STE Hop Count', + 0x0A : 'Backup CRF Mode' + } + + +class VTPVlanInfoTlv(Packet): + name = "VTP VLAN Info TLV" + fields_desc = [ + ByteEnumField("type", 0, _VTP_VLANINFO_TLV_TYPE), + ByteField("length", 0), + StrLenField("value", None, length_from=lambda pkt : pkt.length + 1) + ] + + def guess_payload_class(self, p): + return conf.padding_layer + +class VTPVlanInfo(Packet): + name = "VTP VLAN Info" + fields_desc = [ + ByteField("len", None), # FIXME: compute length + ByteEnumField("status", 0, {0 : "active", 1 : "suspended"}), + ByteEnumField("type", 1, _VTP_VLAN_TYPE), + FieldLenField("vlannamelen", None, "vlanname", "B"), + ShortField("vlanid", 1), + ShortField("mtu", 1500), + XIntField("dot10index", None), + StrLenField("vlanname", "default", length_from=lambda pkt:4 * ((pkt.vlannamelen + 3) / 4)), + ConditionalField(PacketListField("tlvlist", [], VTPVlanInfoTlv, + length_from=lambda pkt:pkt.len - 12 - (4 * ((pkt.vlannamelen + 3) / 4))), + lambda pkt:pkt.type not in [1, 2]) + ] + + def post_build(self, p, pay): + vlannamelen = 4 * ((len(self.vlanname) + 3) / 4) + + if self.len == None: + l = vlannamelen + 12 + p = chr(l & 0xff) + p[1:] + + # Pad vlan name with zeros if vlannamelen > len(vlanname) + l = vlannamelen - len(self.vlanname) + if l != 0: + p += "\x00" * l + + p += pay + + return p + + def guess_payload_class(self, p): + return conf.padding_layer + +_VTP_Types = { + 1 : 'Summary Advertisement', + 2 : 'Subset Advertisements', + 3 : 'Advertisement Request', + 4 : 'Join' + } + +class VTPTimeStampField(StrFixedLenField): + def __init__(self, name, default): + StrFixedLenField.__init__(self, name, default, 12) + + def i2repr(self, pkt, x): + return "%s-%s-%s %s:%s:%s" % (x[:2], x[2:4], x[4:6], x[6:8], x[8:10], x[10:12]) + +class VTP(Packet): + name = "VTP" + fields_desc = [ + ByteField("ver", 2), + ByteEnumField("code", 1, _VTP_Types), + ConditionalField(ByteField("followers", 1), + lambda pkt:pkt.code == 1), + ConditionalField(ByteField("seq", 1), + lambda pkt:pkt.code == 2), + ConditionalField(ByteField("reserved", 0), + lambda pkt:pkt.code == 3), + ByteField("domnamelen", None), + StrFixedLenField("domname", "manbearpig", 32), + ConditionalField(SignedIntField("rev", 0), + lambda pkt:pkt.code == 1 or + pkt.code == 2), + # updater identity + ConditionalField(IPField("uid", "192.168.0.1"), + lambda pkt:pkt.code == 1), + ConditionalField(VTPTimeStampField("timestamp", '930301000000'), + lambda pkt:pkt.code == 1), + ConditionalField(StrFixedLenField("md5", "\x00" * 16, 16), + lambda pkt:pkt.code == 1), + ConditionalField( + PacketListField("vlaninfo", [], VTPVlanInfo), + lambda pkt: pkt.code == 2), + ConditionalField(ShortField("startvalue", 0), + lambda pkt:pkt.code == 3) + ] + + def post_build(self, p, pay): + if self.domnamelen == None: + domnamelen = len(self.domname.strip("\x00")) + p = p[:3] + chr(domnamelen & 0xff) + p[4:] + + p += pay + + return p + +bind_layers(SNAP, VTP, code=0x2003) + +if __name__ == '__main__': + interact(mydict=globals(), mybanner="VTP") diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/contrib/wpa_eapol.py b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/wpa_eapol.py new file mode 100644 index 00000000..084eedd8 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/contrib/wpa_eapol.py @@ -0,0 +1,35 @@ + +# http://trac.secdev.org/scapy/ticket/104 + +# scapy.contrib.description = WPA EAPOL dissector +# scapy.contrib.status = loads + +from scapy.packet import * +from scapy.fields import * +from scapy.layers.l2 import * + +class WPA_key(Packet): + name = "WPA_key" + fields_desc = [ ByteField("descriptor_type", 1), + ShortField("key_info",0), + LenField("len", None, "H"), + StrFixedLenField("replay_counter", "", 8), + StrFixedLenField("nonce", "", 32), + StrFixedLenField("key_iv", "", 16), + StrFixedLenField("wpa_key_rsc", "", 8), + StrFixedLenField("wpa_key_id", "", 8), + StrFixedLenField("wpa_key_mic", "", 16), + LenField("wpa_key_length", None, "H"), + StrLenField("wpa_key", "", length_from=lambda pkt:pkt.wpa_key_length) ] + 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,WPA_key): + return 1 + return 0 + + +bind_layers( EAPOL, WPA_key, type=3) diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/crypto/__init__.py b/scripts/external_libs/scapy-python3-0.18/scapy/crypto/__init__.py new file mode 100644 index 00000000..b441863e --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/crypto/__init__.py @@ -0,0 +1,17 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Arnaud Ebalard +## This program is published under a GPLv2 license + +""" +Tools for handling with digital certificates. +""" + +try: + import Crypto +except ImportError: + import logging + log_loading = logging.getLogger("scapy.loading") + log_loading.info("Can't import python Crypto lib. Disabled certificate manipulation tools") +else: + from scapy.crypto.cert import * diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/crypto/cert.py b/scripts/external_libs/scapy-python3-0.18/scapy/crypto/cert.py new file mode 100644 index 00000000..c4291059 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/crypto/cert.py @@ -0,0 +1,2486 @@ +## This file is part of Scapy +## See http://www.secdev.org/projects/scapy for more informations +## Copyright (C) Arnaud Ebalard +## This program is published under a GPLv2 license + +""" +Cryptographic certificates. +""" + +import os, sys, math, socket, struct, hmac, string, time, random, tempfile +from subprocess import Popen, PIPE +from scapy.utils import strxor +try: + HAS_HASHLIB=True + import hashlib +except: + HAS_HASHLIB=False + +from Crypto.PublicKey import * +from Crypto.Cipher import * +from Crypto.Hash import * +from Crypto.Util import number + +# Maximum allowed size in bytes for a certificate file, to avoid +# loading huge file when importing a cert +MAX_KEY_SIZE=50*1024 +MAX_CERT_SIZE=50*1024 +MAX_CRL_SIZE=10*1024*1024 # some are that big + +##################################################################### +# Some helpers +##################################################################### + +def popen3(cmd): + p = Popen(cmd, shell=False, stdin=PIPE, stdout=PIPE, stderr=PIPE, + close_fds=True) + return p.stdout, p.stdin, p.stderr + +def warning(m): + print("WARNING: %s" % m) + +def randstring(l): + """ + Returns a random string of length l (l >= 0) + """ + tmp = map(lambda x: struct.pack("B", random.randrange(0, 256, 1)), [""]*l) + return "".join(tmp) + +def zerofree_randstring(l): + """ + Returns a random string of length l (l >= 0) without zero in it. + """ + tmp = map(lambda x: struct.pack("B", random.randrange(1, 256, 1)), [""]*l) + return "".join(tmp) + +def strand(s1, s2): + """ + Returns the binary AND of the 2 provided strings s1 and s2. s1 and s2 + must be of same length. + """ + return "".join(map(lambda x,y:chr(ord(x)&ord(y)), s1, s2)) + +# OS2IP function defined in RFC 3447 for octet string to integer conversion +def pkcs_os2ip(x): + """ + Accepts a byte string as input parameter and return the associated long + value: + + Input : x octet string to be converted + + Output: x corresponding nonnegative integer + + Reverse function is pkcs_i2osp() + """ + return number.bytes_to_long(x) + +# IP2OS function defined in RFC 3447 for octet string to integer conversion +def pkcs_i2osp(x,xLen): + """ + Converts a long (the first parameter) to the associated byte string + representation of length l (second parameter). Basically, the length + parameters allow the function to perform the associated padding. + + Input : x nonnegative integer to be converted + xLen intended length of the resulting octet string + + Output: x corresponding nonnegative integer + + Reverse function is pkcs_os2ip(). + """ + z = number.long_to_bytes(x) + padlen = max(0, xLen-len(z)) + return '\x00'*padlen + z + +# for every hash function a tuple is provided, giving access to +# - hash output length in byte +# - associated hash function that take data to be hashed as parameter +# XXX I do not provide update() at the moment. +# - DER encoding of the leading bits of digestInfo (the hash value +# will be concatenated to create the complete digestInfo). +# +# Notes: +# - MD4 asn.1 value should be verified. Also, as stated in +# PKCS#1 v2.1, MD4 should not be used. +# - hashlib is available from http://code.krypto.org/python/hashlib/ +# - 'tls' one is the concatenation of both md5 and sha1 hashes used +# by SSL/TLS when signing/verifying things +_hashFuncParams = { + "md2" : (16, + lambda x: MD2.new(x).digest(), + '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02\x05\x00\x04\x10'), + "md4" : (16, + lambda x: MD4.new(x).digest(), + '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x04\x05\x00\x04\x10'), # is that right ? + "md5" : (16, + lambda x: MD5.new(x).digest(), + '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10'), + "sha1" : (20, + lambda x: SHA.new(x).digest(), + '\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'), + "tls" : (36, + lambda x: MD5.new(x).digest() + SHA.new(x).digest(), + '') } + +if HAS_HASHLIB: + _hashFuncParams["sha224"] = (28, + lambda x: hashlib.sha224(x).digest(), + '\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c') + _hashFuncParams["sha256"] = (32, + lambda x: hashlib.sha256(x).digest(), + '\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20') + _hashFuncParams["sha384"] = (48, + lambda x: hashlib.sha384(x).digest(), + '\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30') + _hashFuncParams["sha512"] = (64, + lambda x: hashlib.sha512(x).digest(), + '\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40') +else: + warning("hashlib support is not available. Consider installing it") + warning("if you need sha224, sha256, sha384 and sha512 algs.") + +def pkcs_mgf1(mgfSeed, maskLen, h): + """ + Implements generic MGF1 Mask Generation function as described in + Appendix B.2.1 of RFC 3447. The hash function is passed by name. + valid values are 'md2', 'md4', 'md5', 'sha1', 'tls, 'sha256', + 'sha384' and 'sha512'. Returns None on error. + + Input: + mgfSeed: seed from which mask is generated, an octet string + maskLen: intended length in octets of the mask, at most 2^32 * hLen + hLen (see below) + h : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls', + 'sha256', 'sha384'). hLen denotes the length in octets of + the hash function output. + + Output: + an octet string of length maskLen + """ + + # steps are those of Appendix B.2.1 + if not h in _hashFuncParams: + warning("pkcs_mgf1: invalid hash (%s) provided") + return None + hLen = _hashFuncParams[h][0] + hFunc = _hashFuncParams[h][1] + if maskLen > 2**32 * hLen: # 1) + warning("pkcs_mgf1: maskLen > 2**32 * hLen") + return None + T = "" # 2) + maxCounter = math.ceil(float(maskLen) / float(hLen)) # 3) + counter = 0 + while counter < maxCounter: + C = pkcs_i2osp(counter, 4) + T += hFunc(mgfSeed + C) + counter += 1 + return T[:maskLen] + + +def pkcs_emsa_pss_encode(M, emBits, h, mgf, sLen): + """ + Implements EMSA-PSS-ENCODE() function described in Sect. 9.1.1 of RFC 3447 + + Input: + M : message to be encoded, an octet string + emBits: maximal bit length of the integer resulting of pkcs_os2ip(EM), + where EM is the encoded message, output of the function. + h : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls', + 'sha256', 'sha384'). hLen denotes the length in octets of + the hash function output. + mgf : the mask generation function f : seed, maskLen -> mask + sLen : intended length in octets of the salt + + Output: + encoded message, an octet string of length emLen = ceil(emBits/8) + + On error, None is returned. + """ + + # 1) is not done + hLen = _hashFuncParams[h][0] # 2) + hFunc = _hashFuncParams[h][1] + mHash = hFunc(M) + emLen = int(math.ceil(emBits/8.)) + if emLen < hLen + sLen + 2: # 3) + warning("encoding error (emLen < hLen + sLen + 2)") + return None + salt = randstring(sLen) # 4) + MPrime = '\x00'*8 + mHash + salt # 5) + H = hFunc(MPrime) # 6) + PS = '\x00'*(emLen - sLen - hLen - 2) # 7) + DB = PS + '\x01' + salt # 8) + dbMask = mgf(H, emLen - hLen - 1) # 9) + maskedDB = strxor(DB, dbMask) # 10) + l = (8*emLen - emBits)/8 # 11) + rem = 8*emLen - emBits - 8*l # additionnal bits + andMask = l*'\x00' + if rem: + j = chr(reduce(lambda x,y: x+y, map(lambda x: 1< mask + sLen : intended length in octets of the salt + + Output: + True if the verification is ok, False otherwise. + """ + + # 1) is not done + hLen = _hashFuncParams[h][0] # 2) + hFunc = _hashFuncParams[h][1] + mHash = hFunc(M) + emLen = int(math.ceil(emBits/8.)) # 3) + if emLen < hLen + sLen + 2: + return False + if EM[-1] != '\xbc': # 4) + return False + l = emLen - hLen - 1 # 5) + maskedDB = EM[:l] + H = EM[l:l+hLen] + l = (8*emLen - emBits)/8 # 6) + rem = 8*emLen - emBits - 8*l # additionnal bits + andMask = l*'\xff' + if rem: + val = reduce(lambda x,y: x+y, map(lambda x: 1< n-1: + warning("Key._rsaep() expects a long between 0 and n-1") + return None + + return self.key.encrypt(m, "")[0] + + + def _rsaes_pkcs1_v1_5_encrypt(self, M): + """ + Implements RSAES-PKCS1-V1_5-ENCRYPT() function described in section + 7.2.1 of RFC 3447. + + Input: + M: message to be encrypted, an octet string of length mLen, where + mLen <= k - 11 (k denotes the length in octets of the key modulus) + + Output: + ciphertext, an octet string of length k + + On error, None is returned. + """ + + # 1) Length checking + mLen = len(M) + k = self.modulusLen / 8 + if mLen > k - 11: + warning("Key._rsaes_pkcs1_v1_5_encrypt(): message too " + "long (%d > %d - 11)" % (mLen, k)) + return None + + # 2) EME-PKCS1-v1_5 encoding + PS = zerofree_randstring(k - mLen - 3) # 2.a) + EM = '\x00' + '\x02' + PS + '\x00' + M # 2.b) + + # 3) RSA encryption + m = pkcs_os2ip(EM) # 3.a) + c = self._rsaep(m) # 3.b) + C = pkcs_i2osp(c, k) # 3.c) + + return C # 4) + + + def _rsaes_oaep_encrypt(self, M, h=None, mgf=None, L=None): + """ + Internal method providing RSAES-OAEP-ENCRYPT as defined in Sect. + 7.1.1 of RFC 3447. Not intended to be used directly. Please, see + encrypt() method for type "OAEP". + + + Input: + M : message to be encrypted, an octet string of length mLen + where mLen <= k - 2*hLen - 2 (k denotes the length in octets + of the RSA modulus and hLen the length in octets of the hash + function output) + h : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls', + 'sha256', 'sha384'). hLen denotes the length in octets of + the hash function output. 'sha1' is used by default if not + provided. + mgf: the mask generation function f : seed, maskLen -> mask + L : optional label to be associated with the message; the default + value for L, if not provided is the empty string + + Output: + ciphertext, an octet string of length k + + On error, None is returned. + """ + # The steps below are the one described in Sect. 7.1.1 of RFC 3447. + # 1) Length Checking + # 1.a) is not done + mLen = len(M) + if h is None: + h = "sha1" + if not h in _hashFuncParams: + warning("Key._rsaes_oaep_encrypt(): unknown hash function %s.", h) + return None + hLen = _hashFuncParams[h][0] + hFun = _hashFuncParams[h][1] + k = self.modulusLen / 8 + if mLen > k - 2*hLen - 2: # 1.b) + warning("Key._rsaes_oaep_encrypt(): message too long.") + return None + + # 2) EME-OAEP encoding + if L is None: # 2.a) + L = "" + lHash = hFun(L) + PS = '\x00'*(k - mLen - 2*hLen - 2) # 2.b) + DB = lHash + PS + '\x01' + M # 2.c) + seed = randstring(hLen) # 2.d) + if mgf is None: # 2.e) + mgf = lambda x,y: pkcs_mgf1(x,y,h) + dbMask = mgf(seed, k - hLen - 1) + maskedDB = strxor(DB, dbMask) # 2.f) + seedMask = mgf(maskedDB, hLen) # 2.g) + maskedSeed = strxor(seed, seedMask) # 2.h) + EM = '\x00' + maskedSeed + maskedDB # 2.i) + + # 3) RSA Encryption + m = pkcs_os2ip(EM) # 3.a) + c = self._rsaep(m) # 3.b) + C = pkcs_i2osp(c, k) # 3.c) + + return C # 4) + + + def encrypt(self, m, t=None, h=None, mgf=None, L=None): + """ + Encrypt message 'm' using 't' encryption scheme where 't' can be: + + - None: the message 'm' is directly applied the RSAEP encryption + primitive, as described in PKCS#1 v2.1, i.e. RFC 3447 + Sect 5.1.1. Simply put, the message undergo a modular + exponentiation using the public key. Additionnal method + parameters are just ignored. + + - 'pkcs': the message 'm' is applied RSAES-PKCS1-V1_5-ENCRYPT encryption + scheme as described in section 7.2.1 of RFC 3447. In that + context, other parameters ('h', 'mgf', 'l') are not used. + + - 'oaep': the message 'm' is applied the RSAES-OAEP-ENCRYPT encryption + scheme, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect + 7.1.1. In that context, + + o 'h' parameter provides the name of the hash method to use. + Possible values are "md2", "md4", "md5", "sha1", "tls", + "sha224", "sha256", "sha384" and "sha512". if none is provided, + sha1 is used. + + o 'mgf' is the mask generation function. By default, mgf + is derived from the provided hash function using the + generic MGF1 (see pkcs_mgf1() for details). + + o 'L' is the optional label to be associated with the + message. If not provided, the default value is used, i.e + the empty string. No check is done on the input limitation + of the hash function regarding the size of 'L' (for + instance, 2^61 - 1 for SHA-1). You have been warned. + """ + + if t is None: # Raw encryption + m = pkcs_os2ip(m) + c = self._rsaep(m) + return pkcs_i2osp(c, self.modulusLen/8) + + elif t == "pkcs": + return self._rsaes_pkcs1_v1_5_encrypt(m) + + elif t == "oaep": + return self._rsaes_oaep_encrypt(m, h, mgf, L) + + else: + warning("Key.encrypt(): Unknown encryption type (%s) provided" % t) + return None + + ### Below are verification related methods + + def _rsavp1(self, s): + """ + Internal method providing raw RSA verification, i.e. simple modular + exponentiation of the given signature representative 'c', an integer + between 0 and n-1. + + This is the signature verification primitive RSAVP1 described in + PKCS#1 v2.1, i.e. RFC 3447 Sect. 5.2.2. + + Input: + s: signature representative, an integer between 0 and n-1, + where n is the key modulus. + + Output: + message representative, an integer between 0 and n-1 + + Not intended to be used directly. Please, see verify() method. + """ + return self._rsaep(s) + + def _rsassa_pss_verify(self, M, S, h=None, mgf=None, sLen=None): + """ + Implements RSASSA-PSS-VERIFY() function described in Sect 8.1.2 + of RFC 3447 + + Input: + M: message whose signature is to be verified + S: signature to be verified, an octet string of length k, where k + is the length in octets of the RSA modulus n. + + Output: + True is the signature is valid. False otherwise. + """ + + # Set default parameters if not provided + if h is None: # By default, sha1 + h = "sha1" + if not h in _hashFuncParams: + warning("Key._rsassa_pss_verify(): unknown hash function " + "provided (%s)" % h) + return False + if mgf is None: # use mgf1 with underlying hash function + mgf = lambda x,y: pkcs_mgf1(x, y, h) + if sLen is None: # use Hash output length (A.2.3 of RFC 3447) + hLen = _hashFuncParams[h][0] + sLen = hLen + + # 1) Length checking + modBits = self.modulusLen + k = modBits / 8 + if len(S) != k: + return False + + # 2) RSA verification + s = pkcs_os2ip(S) # 2.a) + m = self._rsavp1(s) # 2.b) + emLen = math.ceil((modBits - 1) / 8.) # 2.c) + EM = pkcs_i2osp(m, emLen) + + # 3) EMSA-PSS verification + Result = pkcs_emsa_pss_verify(M, EM, modBits - 1, h, mgf, sLen) + + return Result # 4) + + + def _rsassa_pkcs1_v1_5_verify(self, M, S, h): + """ + Implements RSASSA-PKCS1-v1_5-VERIFY() function as described in + Sect. 8.2.2 of RFC 3447. + + Input: + M: message whose signature is to be verified, an octet string + S: signature to be verified, an octet string of length k, where + k is the length in octets of the RSA modulus n + h: hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls', + 'sha256', 'sha384'). + + Output: + True if the signature is valid. False otherwise. + """ + + # 1) Length checking + k = self.modulusLen / 8 + if len(S) != k: + warning("invalid signature (len(S) != k)") + return False + + # 2) RSA verification + s = pkcs_os2ip(S) # 2.a) + m = self._rsavp1(s) # 2.b) + EM = pkcs_i2osp(m, k) # 2.c) + + # 3) EMSA-PKCS1-v1_5 encoding + EMPrime = pkcs_emsa_pkcs1_v1_5_encode(M, k, h) + if EMPrime is None: + warning("Key._rsassa_pkcs1_v1_5_verify(): unable to encode.") + return False + + # 4) Comparison + return EM == EMPrime + + + def verify(self, M, S, t=None, h=None, mgf=None, sLen=None): + """ + Verify alleged signature 'S' is indeed the signature of message 'M' using + 't' signature scheme where 't' can be: + + - None: the alleged signature 'S' is directly applied the RSAVP1 signature + primitive, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect + 5.2.1. Simply put, the provided signature is applied a moular + exponentiation using the public key. Then, a comparison of the + result is done against 'M'. On match, True is returned. + Additionnal method parameters are just ignored. + + - 'pkcs': the alleged signature 'S' and message 'M' are applied + RSASSA-PKCS1-v1_5-VERIFY signature verification scheme as + described in Sect. 8.2.2 of RFC 3447. In that context, + the hash function name is passed using 'h'. Possible values are + "md2", "md4", "md5", "sha1", "tls", "sha224", "sha256", "sha384" + and "sha512". If none is provided, sha1 is used. Other additionnal + parameters are ignored. + + - 'pss': the alleged signature 'S' and message 'M' are applied + RSASSA-PSS-VERIFY signature scheme as described in Sect. 8.1.2. + of RFC 3447. In that context, + + o 'h' parameter provides the name of the hash method to use. + Possible values are "md2", "md4", "md5", "sha1", "tls", "sha224", + "sha256", "sha384" and "sha512". if none is provided, sha1 + is used. + + o 'mgf' is the mask generation function. By default, mgf + is derived from the provided hash function using the + generic MGF1 (see pkcs_mgf1() for details). + + o 'sLen' is the length in octet of the salt. You can overload the + default value (the octet length of the hash value for provided + algorithm) by providing another one with that parameter. + """ + if t is None: # RSAVP1 + S = pkcs_os2ip(S) + n = self.modulus + if S > n-1: + warning("Signature to be verified is too long for key modulus") + return False + m = self._rsavp1(S) + if m is None: + return False + l = int(math.ceil(math.log(m, 2) / 8.)) # Hack + m = pkcs_i2osp(m, l) + return M == m + + elif t == "pkcs": # RSASSA-PKCS1-v1_5-VERIFY + if h is None: + h = "sha1" + return self._rsassa_pkcs1_v1_5_verify(M, S, h) + + elif t == "pss": # RSASSA-PSS-VERIFY + return self._rsassa_pss_verify(M, S, h, mgf, sLen) + + else: + warning("Key.verify(): Unknown signature type (%s) provided" % t) + return None + +class _DecryptAndSignMethods(OSSLHelper): + ### Below are decryption related methods. Encryption ones are inherited + ### from PubKey + + def _rsadp(self, c): + """ + Internal method providing raw RSA decryption, i.e. simple modular + exponentiation of the given ciphertext representative 'c', a long + between 0 and n-1. + + This is the decryption primitive RSADP described in PKCS#1 v2.1, + i.e. RFC 3447 Sect. 5.1.2. + + Input: + c: ciphertest representative, a long between 0 and n-1, where + n is the key modulus. + + Output: + ciphertext representative, a long between 0 and n-1 + + Not intended to be used directly. Please, see encrypt() method. + """ + + n = self.modulus + if type(c) is int: + c = long(c) + if type(c) is not long or c > n-1: + warning("Key._rsaep() expects a long between 0 and n-1") + return None + + return self.key.decrypt(c) + + + def _rsaes_pkcs1_v1_5_decrypt(self, C): + """ + Implements RSAES-PKCS1-V1_5-DECRYPT() function described in section + 7.2.2 of RFC 3447. + + Input: + C: ciphertext to be decrypted, an octet string of length k, where + k is the length in octets of the RSA modulus n. + + Output: + an octet string of length k at most k - 11 + + on error, None is returned. + """ + + # 1) Length checking + cLen = len(C) + k = self.modulusLen / 8 + if cLen != k or k < 11: + warning("Key._rsaes_pkcs1_v1_5_decrypt() decryption error " + "(cLen != k or k < 11)") + return None + + # 2) RSA decryption + c = pkcs_os2ip(C) # 2.a) + m = self._rsadp(c) # 2.b) + EM = pkcs_i2osp(m, k) # 2.c) + + # 3) EME-PKCS1-v1_5 decoding + + # I am aware of the note at the end of 7.2.2 regarding error + # conditions reporting but the one provided below are for _local_ + # debugging purposes. --arno + + if EM[0] != '\x00': + warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error " + "(first byte is not 0x00)") + return None + + if EM[1] != '\x02': + warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error " + "(second byte is not 0x02)") + return None + + tmp = EM[2:].split('\x00', 1) + if len(tmp) != 2: + warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error " + "(no 0x00 to separate PS from M)") + return None + + PS, M = tmp + if len(PS) < 8: + warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error " + "(PS is less than 8 byte long)") + return None + + return M # 4) + + + def _rsaes_oaep_decrypt(self, C, h=None, mgf=None, L=None): + """ + Internal method providing RSAES-OAEP-DECRYPT as defined in Sect. + 7.1.2 of RFC 3447. Not intended to be used directly. Please, see + encrypt() method for type "OAEP". + + + Input: + C : ciphertext to be decrypted, an octet string of length k, where + k = 2*hLen + 2 (k denotes the length in octets of the RSA modulus + and hLen the length in octets of the hash function output) + h : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls', + 'sha256', 'sha384'). 'sha1' is used if none is provided. + mgf: the mask generation function f : seed, maskLen -> mask + L : optional label whose association with the message is to be + verified; the default value for L, if not provided is the empty + string. + + Output: + message, an octet string of length k mLen, where mLen <= k - 2*hLen - 2 + + On error, None is returned. + """ + # The steps below are the one described in Sect. 7.1.2 of RFC 3447. + + # 1) Length Checking + # 1.a) is not done + if h is None: + h = "sha1" + if not h in _hashFuncParams: + warning("Key._rsaes_oaep_decrypt(): unknown hash function %s.", h) + return None + hLen = _hashFuncParams[h][0] + hFun = _hashFuncParams[h][1] + k = self.modulusLen / 8 + cLen = len(C) + if cLen != k: # 1.b) + warning("Key._rsaes_oaep_decrypt(): decryption error. " + "(cLen != k)") + return None + if k < 2*hLen + 2: + warning("Key._rsaes_oaep_decrypt(): decryption error. " + "(k < 2*hLen + 2)") + return None + + # 2) RSA decryption + c = pkcs_os2ip(C) # 2.a) + m = self._rsadp(c) # 2.b) + EM = pkcs_i2osp(m, k) # 2.c) + + # 3) EME-OAEP decoding + if L is None: # 3.a) + L = "" + lHash = hFun(L) + Y = EM[:1] # 3.b) + if Y != '\x00': + warning("Key._rsaes_oaep_decrypt(): decryption error. " + "(Y is not zero)") + return None + maskedSeed = EM[1:1+hLen] + maskedDB = EM[1+hLen:] + if mgf is None: + mgf = lambda x,y: pkcs_mgf1(x, y, h) + seedMask = mgf(maskedDB, hLen) # 3.c) + seed = strxor(maskedSeed, seedMask) # 3.d) + dbMask = mgf(seed, k - hLen - 1) # 3.e) + DB = strxor(maskedDB, dbMask) # 3.f) + + # I am aware of the note at the end of 7.1.2 regarding error + # conditions reporting but the one provided below are for _local_ + # debugging purposes. --arno + + lHashPrime = DB[:hLen] # 3.g) + tmp = DB[hLen:].split('\x01', 1) + if len(tmp) != 2: + warning("Key._rsaes_oaep_decrypt(): decryption error. " + "(0x01 separator not found)") + return None + PS, M = tmp + if PS != '\x00'*len(PS): + warning("Key._rsaes_oaep_decrypt(): decryption error. " + "(invalid padding string)") + return None + if lHash != lHashPrime: + warning("Key._rsaes_oaep_decrypt(): decryption error. " + "(invalid hash)") + return None + return M # 4) + + + def decrypt(self, C, t=None, h=None, mgf=None, L=None): + """ + Decrypt ciphertext 'C' using 't' decryption scheme where 't' can be: + + - None: the ciphertext 'C' is directly applied the RSADP decryption + primitive, as described in PKCS#1 v2.1, i.e. RFC 3447 + Sect 5.1.2. Simply, put the message undergo a modular + exponentiation using the private key. Additionnal method + parameters are just ignored. + + - 'pkcs': the ciphertext 'C' is applied RSAES-PKCS1-V1_5-DECRYPT + decryption scheme as described in section 7.2.2 of RFC 3447. + In that context, other parameters ('h', 'mgf', 'l') are not + used. + + - 'oaep': the ciphertext 'C' is applied the RSAES-OAEP-DECRYPT decryption + scheme, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect + 7.1.2. In that context, + + o 'h' parameter provides the name of the hash method to use. + Possible values are "md2", "md4", "md5", "sha1", "tls", + "sha224", "sha256", "sha384" and "sha512". if none is provided, + sha1 is used by default. + + o 'mgf' is the mask generation function. By default, mgf + is derived from the provided hash function using the + generic MGF1 (see pkcs_mgf1() for details). + + o 'L' is the optional label to be associated with the + message. If not provided, the default value is used, i.e + the empty string. No check is done on the input limitation + of the hash function regarding the size of 'L' (for + instance, 2^61 - 1 for SHA-1). You have been warned. + """ + if t is None: + C = pkcs_os2ip(C) + c = self._rsadp(C) + l = int(math.ceil(math.log(c, 2) / 8.)) # Hack + return pkcs_i2osp(c, l) + + elif t == "pkcs": + return self._rsaes_pkcs1_v1_5_decrypt(C) + + elif t == "oaep": + return self._rsaes_oaep_decrypt(C, h, mgf, L) + + else: + warning("Key.decrypt(): Unknown decryption type (%s) provided" % t) + return None + + ### Below are signature related methods. Verification ones are inherited from + ### PubKey + + def _rsasp1(self, m): + """ + Internal method providing raw RSA signature, i.e. simple modular + exponentiation of the given message representative 'm', an integer + between 0 and n-1. + + This is the signature primitive RSASP1 described in PKCS#1 v2.1, + i.e. RFC 3447 Sect. 5.2.1. + + Input: + m: message representative, an integer between 0 and n-1, where + n is the key modulus. + + Output: + signature representative, an integer between 0 and n-1 + + Not intended to be used directly. Please, see sign() method. + """ + return self._rsadp(m) + + + def _rsassa_pss_sign(self, M, h=None, mgf=None, sLen=None): + """ + Implements RSASSA-PSS-SIGN() function described in Sect. 8.1.1 of + RFC 3447. + + Input: + M: message to be signed, an octet string + + Output: + signature, an octet string of length k, where k is the length in + octets of the RSA modulus n. + + On error, None is returned. + """ + + # Set default parameters if not provided + if h is None: # By default, sha1 + h = "sha1" + if not h in _hashFuncParams: + warning("Key._rsassa_pss_sign(): unknown hash function " + "provided (%s)" % h) + return None + if mgf is None: # use mgf1 with underlying hash function + mgf = lambda x,y: pkcs_mgf1(x, y, h) + if sLen is None: # use Hash output length (A.2.3 of RFC 3447) + hLen = _hashFuncParams[h][0] + sLen = hLen + + # 1) EMSA-PSS encoding + modBits = self.modulusLen + k = modBits / 8 + EM = pkcs_emsa_pss_encode(M, modBits - 1, h, mgf, sLen) + if EM is None: + warning("Key._rsassa_pss_sign(): unable to encode") + return None + + # 2) RSA signature + m = pkcs_os2ip(EM) # 2.a) + s = self._rsasp1(m) # 2.b) + S = pkcs_i2osp(s, k) # 2.c) + + return S # 3) + + + def _rsassa_pkcs1_v1_5_sign(self, M, h): + """ + Implements RSASSA-PKCS1-v1_5-SIGN() function as described in + Sect. 8.2.1 of RFC 3447. + + Input: + M: message to be signed, an octet string + h: hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls' + 'sha256', 'sha384'). + + Output: + the signature, an octet string. + """ + + # 1) EMSA-PKCS1-v1_5 encoding + k = self.modulusLen / 8 + EM = pkcs_emsa_pkcs1_v1_5_encode(M, k, h) + if EM is None: + warning("Key._rsassa_pkcs1_v1_5_sign(): unable to encode") + return None + + # 2) RSA signature + m = pkcs_os2ip(EM) # 2.a) + s = self._rsasp1(m) # 2.b) + S = pkcs_i2osp(s, k) # 2.c) + + return S # 3) + + + def sign(self, M, t=None, h=None, mgf=None, sLen=None): + """ + Sign message 'M' using 't' signature scheme where 't' can be: + + - None: the message 'M' is directly applied the RSASP1 signature + primitive, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect + 5.2.1. Simply put, the message undergo a modular exponentiation + using the private key. Additionnal method parameters are just + ignored. + + - 'pkcs': the message 'M' is applied RSASSA-PKCS1-v1_5-SIGN signature + scheme as described in Sect. 8.2.1 of RFC 3447. In that context, + the hash function name is passed using 'h'. Possible values are + "md2", "md4", "md5", "sha1", "tls", "sha224", "sha256", "sha384" + and "sha512". If none is provided, sha1 is used. Other additionnal + parameters are ignored. + + - 'pss' : the message 'M' is applied RSASSA-PSS-SIGN signature scheme as + described in Sect. 8.1.1. of RFC 3447. In that context, + + o 'h' parameter provides the name of the hash method to use. + Possible values are "md2", "md4", "md5", "sha1", "tls", "sha224", + "sha256", "sha384" and "sha512". if none is provided, sha1 + is used. + + o 'mgf' is the mask generation function. By default, mgf + is derived from the provided hash function using the + generic MGF1 (see pkcs_mgf1() for details). + + o 'sLen' is the length in octet of the salt. You can overload the + default value (the octet length of the hash value for provided + algorithm) by providing another one with that parameter. + """ + + if t is None: # RSASP1 + M = pkcs_os2ip(M) + n = self.modulus + if M > n-1: + warning("Message to be signed is too long for key modulus") + return None + s = self._rsasp1(M) + if s is None: + return None + return pkcs_i2osp(s, self.modulusLen/8) + + elif t == "pkcs": # RSASSA-PKCS1-v1_5-SIGN + if h is None: + h = "sha1" + return self._rsassa_pkcs1_v1_5_sign(M, h) + + elif t == "pss": # RSASSA-PSS-SIGN + return self._rsassa_pss_sign(M, h, mgf, sLen) + + else: + warning("Key.sign(): Unknown signature type (%s) provided" % t) + return None + + +def openssl_parse_RSA(fmt="PEM"): + return popen3(['openssl', 'rsa', '-text', '-pubin', '-inform', fmt, '-noout']) +def openssl_convert_RSA(infmt="PEM", outfmt="DER"): + return ['openssl', 'rsa', '-pubin', '-inform', infmt, '-outform', outfmt] + +class PubKey(OSSLHelper, _EncryptAndVerify): + # Below are the fields we recognize in the -text output of openssl + # and from which we extract information. We expect them in that + # order. Number of spaces does matter. + possible_fields = [ "Modulus (", + "Exponent:" ] + possible_fields_count = len(possible_fields) + + def __init__(self, keypath): + error_msg = "Unable to import key." + + # XXX Temporary hack to use PubKey inside Cert + if type(keypath) is tuple: + e, m, mLen = keypath + self.modulus = m + self.modulusLen = mLen + self.pubExp = e + return + + fields_dict = {} + for k in self.possible_fields: + fields_dict[k] = None + + self.keypath = None + rawkey = None + + if (not '\x00' in keypath) and os.path.isfile(keypath): # file + self.keypath = keypath + key_size = os.path.getsize(keypath) + if key_size > MAX_KEY_SIZE: + raise Exception(error_msg) + try: + f = open(keypath) + rawkey = f.read() + f.close() + except: + raise Exception(error_msg) + else: + rawkey = keypath + + if rawkey is None: + raise Exception(error_msg) + + self.rawkey = rawkey + + key_header = "-----BEGIN PUBLIC KEY-----" + key_footer = "-----END PUBLIC KEY-----" + l = rawkey.split(key_header, 1) + if len(l) == 2: # looks like PEM + tmp = l[1] + l = tmp.split(key_footer, 1) + if len(l) == 2: + tmp = l[0] + rawkey = "%s%s%s\n" % (key_header, tmp, key_footer) + else: + raise Exception(error_msg) + r,w,e = openssl_parse_RSA("PEM") + w.write(rawkey) + w.close() + textkey = r.read() + r.close() + res = e.read() + e.close() + if res == '': + self.format = "PEM" + self.pemkey = rawkey + self.textkey = textkey + cmd = openssl_convert_RSA_cmd("PEM", "DER") + self.derkey = self._apply_ossl_cmd(cmd, rawkey) + else: + raise Exception(error_msg) + else: # not PEM, try DER + r,w,e = openssl_parse_RSA("DER") + w.write(rawkey) + w.close() + textkey = r.read() + r.close() + res = e.read() + if res == '': + self.format = "DER" + self.derkey = rawkey + self.textkey = textkey + cmd = openssl_convert_RSA_cmd("DER", "PEM") + self.pemkey = self._apply_ossl_cmd(cmd, rawkey) + cmd = openssl_convert_RSA_cmd("DER", "DER") + self.derkey = self._apply_ossl_cmd(cmd, rawkey) + else: + try: # Perhaps it is a cert + c = Cert(keypath) + except: + raise Exception(error_msg) + # TODO: + # Reconstruct a key (der and pem) and provide: + # self.format + # self.derkey + # self.pemkey + # self.textkey + # self.keypath + + self.osslcmdbase = ['openssl', 'rsa', '-pubin', '-inform', self.format] + + self.keypath = keypath + + # Parse the -text output of openssl to make things available + l = self.textkey.split('\n', 1) + if len(l) != 2: + raise Exception(error_msg) + cur, tmp = l + i = 0 + k = self.possible_fields[i] # Modulus ( + cur = cur[len(k):] + '\n' + while k: + l = tmp.split('\n', 1) + if len(l) != 2: # Over + fields_dict[k] = cur + break + l, tmp = l + + newkey = 0 + # skip fields we have already seen, this is the purpose of 'i' + for j in range(i, self.possible_fields_count): + f = self.possible_fields[j] + if l.startswith(f): + fields_dict[k] = cur + cur = l[len(f):] + '\n' + k = f + newkey = 1 + i = j+1 + break + if newkey == 1: + continue + cur += l + '\n' + + # modulus and modulus length + v = fields_dict["Modulus ("] + self.modulusLen = None + if v: + v, rem = v.split(' bit):', 1) + self.modulusLen = int(v) + rem = rem.replace('\n','').replace(' ','').replace(':','') + self.modulus = long(rem, 16) + if self.modulus is None: + raise Exception(error_msg) + + # public exponent + v = fields_dict["Exponent:"] + self.pubExp = None + if v: + self.pubExp = long(v.split('(', 1)[0]) + if self.pubExp is None: + raise Exception(error_msg) + + self.key = RSA.construct((self.modulus, self.pubExp, )) + + def __str__(self): + return self.derkey + + +class Key(_DecryptAndSignMethods, _EncryptAndVerify): + # Below are the fields we recognize in the -text output of openssl + # and from which we extract information. We expect them in that + # order. Number of spaces does matter. + possible_fields = [ "Private-Key: (", + "modulus:", + "publicExponent:", + "privateExponent:", + "prime1:", + "prime2:", + "exponent1:", + "exponent2:", + "coefficient:" ] + possible_fields_count = len(possible_fields) + + def __init__(self, keypath): + error_msg = "Unable to import key." + + fields_dict = {} + for k in self.possible_fields: + fields_dict[k] = None + + self.keypath = None + rawkey = None + + if (not '\x00' in keypath) and os.path.isfile(keypath): + self.keypath = keypath + key_size = os.path.getsize(keypath) + if key_size > MAX_KEY_SIZE: + raise Exception(error_msg) + try: + f = open(keypath) + rawkey = f.read() + f.close() + except: + raise Exception(error_msg) + else: + rawkey = keypath + + if rawkey is None: + raise Exception(error_msg) + + self.rawkey = rawkey + + # Let's try to get file format : PEM or DER. + fmtstr = 'openssl rsa -text -inform %s -noout' + convertstr = 'openssl rsa -inform %s -outform %s' + key_header = "-----BEGIN RSA PRIVATE KEY-----" + key_footer = "-----END RSA PRIVATE KEY-----" + l = rawkey.split(key_header, 1) + if len(l) == 2: # looks like PEM + tmp = l[1] + l = tmp.split(key_footer, 1) + if len(l) == 2: + tmp = l[0] + rawkey = "%s%s%s\n" % (key_header, tmp, key_footer) + else: + raise Exception(error_msg) + r,w,e = popen3((fmtstr % "PEM").split(" ")) + w.write(rawkey) + w.close() + textkey = r.read() + r.close() + res = e.read() + e.close() + if res == '': + self.format = "PEM" + self.pemkey = rawkey + self.textkey = textkey + cmd = (convertstr % ("PEM", "DER")).split(" ") + self.derkey = self._apply_ossl_cmd(cmd, rawkey) + else: + raise Exception(error_msg) + else: # not PEM, try DER + r,w,e = popen3((fmtstr % "DER").split(" ")) + w.write(rawkey) + w.close() + textkey = r.read() + r.close() + res = e.read() + if res == '': + self.format = "DER" + self.derkey = rawkey + self.textkey = textkey + cmd = (convertstr % ("DER", "PEM")).split(" ") + self.pemkey = self._apply_ossl_cmd(cmd, rawkey) + cmd = (convertstr % ("DER", "DER")).split(" ") + self.derkey = self._apply_ossl_cmd(cmd, rawkey) + else: + raise Exception(error_msg) + + self.osslcmdbase = ['openssl', 'rsa', '-inform', self.format] + + r,w,e = popen3(["openssl", "asn1parse", "-inform", "DER"]) + w.write(self.derkey) + w.close() + self.asn1parsekey = r.read() + r.close() + res = e.read() + e.close() + if res != '': + raise Exception(error_msg) + + self.keypath = keypath + + # Parse the -text output of openssl to make things available + l = self.textkey.split('\n', 1) + if len(l) != 2: + raise Exception(error_msg) + cur, tmp = l + i = 0 + k = self.possible_fields[i] # Private-Key: ( + cur = cur[len(k):] + '\n' + while k: + l = tmp.split('\n', 1) + if len(l) != 2: # Over + fields_dict[k] = cur + break + l, tmp = l + + newkey = 0 + # skip fields we have already seen, this is the purpose of 'i' + for j in range(i, self.possible_fields_count): + f = self.possible_fields[j] + if l.startswith(f): + fields_dict[k] = cur + cur = l[len(f):] + '\n' + k = f + newkey = 1 + i = j+1 + break + if newkey == 1: + continue + cur += l + '\n' + + # modulus length + v = fields_dict["Private-Key: ("] + self.modulusLen = None + if v: + self.modulusLen = int(v.split(' bit', 1)[0]) + if self.modulusLen is None: + raise Exception(error_msg) + + # public exponent + v = fields_dict["publicExponent:"] + self.pubExp = None + if v: + self.pubExp = long(v.split('(', 1)[0]) + if self.pubExp is None: + raise Exception(error_msg) + + tmp = {} + for k in ["modulus:", "privateExponent:", "prime1:", "prime2:", + "exponent1:", "exponent2:", "coefficient:"]: + v = fields_dict[k] + if v: + s = v.replace('\n', '').replace(' ', '').replace(':', '') + tmp[k] = long(s, 16) + else: + raise Exception(error_msg) + + self.modulus = tmp["modulus:"] + self.privExp = tmp["privateExponent:"] + self.prime1 = tmp["prime1:"] + self.prime2 = tmp["prime2:"] + self.exponent1 = tmp["exponent1:"] + self.exponent2 = tmp["exponent2:"] + self.coefficient = tmp["coefficient:"] + + self.key = RSA.construct((self.modulus, self.pubExp, self.privExp)) + + def __str__(self): + return self.derkey + + +# We inherit from PubKey to get access to all encryption and verification +# methods. To have that working, we simply need Cert to provide +# modulusLen and key attribute. +# XXX Yes, it is a hack. +class Cert(OSSLHelper, _EncryptAndVerify): + # Below are the fields we recognize in the -text output of openssl + # and from which we extract information. We expect them in that + # order. Number of spaces does matter. + possible_fields = [ " Version:", + " Serial Number:", + " Signature Algorithm:", + " Issuer:", + " Not Before:", + " Not After :", + " Subject:", + " Public Key Algorithm:", + " Modulus (", + " Exponent:", + " X509v3 Subject Key Identifier:", + " X509v3 Authority Key Identifier:", + " keyid:", + " DirName:", + " serial:", + " X509v3 Basic Constraints:", + " X509v3 Key Usage:", + " X509v3 Extended Key Usage:", + " X509v3 CRL Distribution Points:", + " Authority Information Access:", + " Signature Algorithm:" ] + possible_fields_count = len(possible_fields) + + def __init__(self, certpath): + error_msg = "Unable to import certificate." + + fields_dict = {} + for k in self.possible_fields: + fields_dict[k] = None + + self.certpath = None + rawcert = None + + if (not '\x00' in certpath) and os.path.isfile(certpath): # file + self.certpath = certpath + cert_size = os.path.getsize(certpath) + if cert_size > MAX_CERT_SIZE: + raise Exception(error_msg) + try: + f = open(certpath) + rawcert = f.read() + f.close() + except: + raise Exception(error_msg) + else: + rawcert = certpath + + if rawcert is None: + raise Exception(error_msg) + + self.rawcert = rawcert + + # Let's try to get file format : PEM or DER. + fmtstr = 'openssl x509 -text -inform %s -noout' + convertstr = 'openssl x509 -inform %s -outform %s' + cert_header = "-----BEGIN CERTIFICATE-----" + cert_footer = "-----END CERTIFICATE-----" + l = rawcert.split(cert_header, 1) + if len(l) == 2: # looks like PEM + tmp = l[1] + l = tmp.split(cert_footer, 1) + if len(l) == 2: + tmp = l[0] + rawcert = "%s%s%s\n" % (cert_header, tmp, cert_footer) + else: + raise Exception(error_msg) + r,w,e = popen3((fmtstr % "PEM").split(" ")) + w.write(rawcert) + w.close() + textcert = r.read() + r.close() + res = e.read() + e.close() + if res == '': + self.format = "PEM" + self.pemcert = rawcert + self.textcert = textcert + cmd = (convertstr % ("PEM", "DER")).split(" ") + self.dercert = self._apply_ossl_cmd(cmd, rawcert) + else: + raise Exception(error_msg) + else: # not PEM, try DER + r,w,e = popen3((fmtstr % "DER").split(" ")) + w.write(rawcert) + w.close() + textcert = r.read() + r.close() + res = e.read() + if res == '': + self.format = "DER" + self.dercert = rawcert + self.textcert = textcert + cmd = (convertstr % ("DER", "PEM")).split(" ") + self.pemcert = self._apply_ossl_cmd(cmd, rawcert) + cmd = (convertstr % ("DER", "DER")).split(" ") + self.dercert = self._apply_ossl_cmd(cmd, rawcert) + else: + raise Exception(error_msg) + + self.osslcmdbase = ['openssl', 'x509', '-inform', self.format] + + r,w,e = popen3('openssl asn1parse -inform DER'.split(' ')) + w.write(self.dercert) + w.close() + self.asn1parsecert = r.read() + r.close() + res = e.read() + e.close() + if res != '': + raise Exception(error_msg) + + # Grab _raw_ X509v3 Authority Key Identifier, if any. + tmp = self.asn1parsecert.split(":X509v3 Authority Key Identifier", 1) + self.authorityKeyID = None + if len(tmp) == 2: + tmp = tmp[1] + tmp = tmp.split("[HEX DUMP]:", 1)[1] + self.authorityKeyID=tmp.split('\n',1)[0] + + # Grab _raw_ X509v3 Subject Key Identifier, if any. + tmp = self.asn1parsecert.split(":X509v3 Subject Key Identifier", 1) + self.subjectKeyID = None + if len(tmp) == 2: + tmp = tmp[1] + tmp = tmp.split("[HEX DUMP]:", 1)[1] + self.subjectKeyID=tmp.split('\n',1)[0] + + # Get tbsCertificate using the worst hack. output of asn1parse + # looks like that: + # + # 0:d=0 hl=4 l=1298 cons: SEQUENCE + # 4:d=1 hl=4 l=1018 cons: SEQUENCE + # ... + # + l1,l2 = self.asn1parsecert.split('\n', 2)[:2] + hl1 = int(l1.split("hl=",1)[1].split("l=",1)[0]) + rem = l2.split("hl=",1)[1] + hl2, rem = rem.split("l=",1) + hl2 = int(hl2) + l = int(rem.split("cons",1)[0]) + self.tbsCertificate = self.dercert[hl1:hl1+hl2+l] + + # Parse the -text output of openssl to make things available + tmp = self.textcert.split('\n', 2)[2] + l = tmp.split('\n', 1) + if len(l) != 2: + raise Exception(error_msg) + cur, tmp = l + i = 0 + k = self.possible_fields[i] # Version: + cur = cur[len(k):] + '\n' + while k: + l = tmp.split('\n', 1) + if len(l) != 2: # Over + fields_dict[k] = cur + break + l, tmp = l + + newkey = 0 + # skip fields we have already seen, this is the purpose of 'i' + for j in range(i, self.possible_fields_count): + f = self.possible_fields[j] + if l.startswith(f): + fields_dict[k] = cur + cur = l[len(f):] + '\n' + k = f + newkey = 1 + i = j+1 + break + if newkey == 1: + continue + cur += l + '\n' + + # version + v = fields_dict[" Version:"] + self.version = None + if v: + self.version = int(v[1:2]) + if self.version is None: + raise Exception(error_msg) + + # serial number + v = fields_dict[" Serial Number:"] + self.serial = None + if v: + v = v.replace('\n', '').strip() + if "0x" in v: + v = v.split("0x", 1)[1].split(')', 1)[0] + v = v.replace(':', '').upper() + if len(v) % 2: + v = '0' + v + self.serial = v + if self.serial is None: + raise Exception(error_msg) + + # Signature Algorithm + v = fields_dict[" Signature Algorithm:"] + self.sigAlg = None + if v: + v = v.split('\n',1)[0] + v = v.strip() + self.sigAlg = v + if self.sigAlg is None: + raise Exception(error_msg) + + # issuer + v = fields_dict[" Issuer:"] + self.issuer = None + if v: + v = v.split('\n',1)[0] + v = v.strip() + self.issuer = v + if self.issuer is None: + raise Exception(error_msg) + + # not before + v = fields_dict[" Not Before:"] + self.notBefore_str = None + if v: + v = v.split('\n',1)[0] + v = v.strip() + self.notBefore_str = v + if self.notBefore_str is None: + raise Exception(error_msg) + try: + self.notBefore = time.strptime(self.notBefore_str, + "%b %d %H:%M:%S %Y %Z") + except: + self.notBefore = time.strptime(self.notBefore_str, + "%b %d %H:%M:%S %Y") + self.notBefore_str_simple = time.strftime("%x", self.notBefore) + + # not after + v = fields_dict[" Not After :"] + self.notAfter_str = None + if v: + v = v.split('\n',1)[0] + v = v.strip() + self.notAfter_str = v + if self.notAfter_str is None: + raise Exception(error_msg) + try: + self.notAfter = time.strptime(self.notAfter_str, + "%b %d %H:%M:%S %Y %Z") + except: + self.notAfter = time.strptime(self.notAfter_str, + "%b %d %H:%M:%S %Y") + self.notAfter_str_simple = time.strftime("%x", self.notAfter) + + # subject + v = fields_dict[" Subject:"] + self.subject = None + if v: + v = v.split('\n',1)[0] + v = v.strip() + self.subject = v + if self.subject is None: + raise Exception(error_msg) + + # Public Key Algorithm + v = fields_dict[" Public Key Algorithm:"] + self.pubKeyAlg = None + if v: + v = v.split('\n',1)[0] + v = v.strip() + self.pubKeyAlg = v + if self.pubKeyAlg is None: + raise Exception(error_msg) + + # Modulus + v = fields_dict[" Modulus ("] + self.modulus = None + if v: + v,t = v.split(' bit):',1) + self.modulusLen = int(v) + t = t.replace(' ', '').replace('\n', ''). replace(':', '') + self.modulus_hexdump = t + self.modulus = long(t, 16) + if self.modulus is None: + raise Exception(error_msg) + + # Exponent + v = fields_dict[" Exponent:"] + self.exponent = None + if v: + v = v.split('(',1)[0] + self.exponent = long(v) + if self.exponent is None: + raise Exception(error_msg) + + # Public Key instance + self.key = RSA.construct((self.modulus, self.exponent, )) + + # Subject Key Identifier + + # Authority Key Identifier: keyid, dirname and serial + self.authorityKeyID_keyid = None + self.authorityKeyID_dirname = None + self.authorityKeyID_serial = None + if self.authorityKeyID: # (hex version already done using asn1parse) + v = fields_dict[" keyid:"] + if v: + v = v.split('\n',1)[0] + v = v.strip().replace(':', '') + self.authorityKeyID_keyid = v + v = fields_dict[" DirName:"] + if v: + v = v.split('\n',1)[0] + self.authorityKeyID_dirname = v + v = fields_dict[" serial:"] + if v: + v = v.split('\n',1)[0] + v = v.strip().replace(':', '') + self.authorityKeyID_serial = v + + # Basic constraints + self.basicConstraintsCritical = False + self.basicConstraints=None + v = fields_dict[" X509v3 Basic Constraints:"] + if v: + self.basicConstraints = {} + v,t = v.split('\n',2)[:2] + if "critical" in v: + self.basicConstraintsCritical = True + if "CA:" in t: + self.basicConstraints["CA"] = t.split('CA:')[1][:4] == "TRUE" + if "pathlen:" in t: + self.basicConstraints["pathlen"] = int(t.split('pathlen:')[1]) + + # X509v3 Key Usage + self.keyUsage = [] + v = fields_dict[" X509v3 Key Usage:"] + if v: + # man 5 x509v3_config + ku_mapping = {"Digital Signature": "digitalSignature", + "Non Repudiation": "nonRepudiation", + "Key Encipherment": "keyEncipherment", + "Data Encipherment": "dataEncipherment", + "Key Agreement": "keyAgreement", + "Certificate Sign": "keyCertSign", + "CRL Sign": "cRLSign", + "Encipher Only": "encipherOnly", + "Decipher Only": "decipherOnly"} + v = v.split('\n',2)[1] + l = map(lambda x: x.strip(), v.split(',')) + while l: + c = l.pop() + if c in ku_mapping: + self.keyUsage.append(ku_mapping[c]) + else: + self.keyUsage.append(c) # Add it anyway + print("Found unknown X509v3 Key Usage: '%s'" % c) + print("Report it to arno (at) natisbad.org for addition") + + # X509v3 Extended Key Usage + self.extKeyUsage = [] + v = fields_dict[" X509v3 Extended Key Usage:"] + if v: + # man 5 x509v3_config: + eku_mapping = {"TLS Web Server Authentication": "serverAuth", + "TLS Web Client Authentication": "clientAuth", + "Code Signing": "codeSigning", + "E-mail Protection": "emailProtection", + "Time Stamping": "timeStamping", + "Microsoft Individual Code Signing": "msCodeInd", + "Microsoft Commercial Code Signing": "msCodeCom", + "Microsoft Trust List Signing": "msCTLSign", + "Microsoft Encrypted File System": "msEFS", + "Microsoft Server Gated Crypto": "msSGC", + "Netscape Server Gated Crypto": "nsSGC", + "IPSec End System": "iPsecEndSystem", + "IPSec Tunnel": "iPsecTunnel", + "IPSec User": "iPsecUser"} + v = v.split('\n',2)[1] + l = map(lambda x: x.strip(), v.split(',')) + while l: + c = l.pop() + if c in eku_mapping: + self.extKeyUsage.append(eku_mapping[c]) + else: + self.extKeyUsage.append(c) # Add it anyway + print("Found unknown X509v3 Extended Key Usage: '%s'" % c) + print("Report it to arno (at) natisbad.org for addition") + + # CRL Distribution points + self.cRLDistributionPoints = [] + v = fields_dict[" X509v3 CRL Distribution Points:"] + if v: + v = v.split("\n\n", 1)[0] + v = v.split("URI:")[1:] + self.CRLDistributionPoints = map(lambda x: x.strip(), v) + + # Authority Information Access: list of tuples ("method", "location") + self.authorityInfoAccess = [] + v = fields_dict[" Authority Information Access:"] + if v: + v = v.split("\n\n", 1)[0] + v = v.split("\n")[1:] + for e in v: + method, location = map(lambda x: x.strip(), e.split(" - ", 1)) + self.authorityInfoAccess.append((method, location)) + + # signature field + v = fields_dict[" Signature Algorithm:" ] + self.sig = None + if v: + v = v.split('\n',1)[1] + v = v.replace(' ', '').replace('\n', '') + self.sig = "".join(map(lambda x: chr(int(x, 16)), v.split(':'))) + self.sigLen = len(self.sig) + if self.sig is None: + raise Exception(error_msg) + + def isIssuerCert(self, other): + """ + True if 'other' issued 'self', i.e.: + - self.issuer == other.subject + - self is signed by other + """ + # XXX should be done on raw values, instead of their textual repr + if self.issuer != other.subject: + return False + + # Sanity check regarding modulus length and the + # signature length + keyLen = (other.modulusLen + 7)/8 + if keyLen != self.sigLen: + return False + + unenc = other.encrypt(self.sig) # public key encryption, i.e. decrypt + + # XXX Check block type (00 or 01 and type of padding) + unenc = unenc[1:] + if not '\x00' in unenc: + return False + pos = unenc.index('\x00') + unenc = unenc[pos+1:] + + found = None + for k in _hashFuncParams.keys(): + if self.sigAlg.startswith(k): + found = k + break + if not found: + return False + hlen, hfunc, digestInfo = _hashFuncParams[k] + + if len(unenc) != (hlen+len(digestInfo)): + return False + + if not unenc.startswith(digestInfo): + return False + + h = unenc[-hlen:] + myh = hfunc(self.tbsCertificate) + + return h == myh + + def chain(self, certlist): + """ + Construct the chain of certificates leading from 'self' to the + self signed root using the certificates in 'certlist'. If the + list does not provide all the required certs to go to the root + the function returns a incomplete chain starting with the + certificate. This fact can be tested by tchecking if the last + certificate of the returned chain is self signed (if c is the + result, c[-1].isSelfSigned()) + """ + d = {} + for c in certlist: + # XXX we should check if we have duplicate + d[c.subject] = c + res = [self] + cur = self + while not cur.isSelfSigned(): + if cur.issuer in d: + possible_issuer = d[cur.issuer] + if cur.isIssuerCert(possible_issuer): + res.append(possible_issuer) + cur = possible_issuer + else: + break + return res + + def remainingDays(self, now=None): + """ + Based on the value of notBefore field, returns the number of + days the certificate will still be valid. The date used for the + comparison is the current and local date, as returned by + time.localtime(), except if 'now' argument is provided another + one. 'now' argument can be given as either a time tuple or a string + representing the date. Accepted format for the string version + are: + + - '%b %d %H:%M:%S %Y %Z' e.g. 'Jan 30 07:38:59 2008 GMT' + - '%m/%d/%y' e.g. '01/30/08' (less precise) + + If the certificate is no more valid at the date considered, then, + a negative value is returned representing the number of days + since it has expired. + + The number of days is returned as a float to deal with the unlikely + case of certificates that are still just valid. + """ + if now is None: + now = time.localtime() + elif type(now) is str: + try: + if '/' in now: + now = time.strptime(now, '%m/%d/%y') + else: + now = time.strptime(now, '%b %d %H:%M:%S %Y %Z') + except: + warning("Bad time string provided '%s'. Using current time" % now) + now = time.localtime() + + now = time.mktime(now) + nft = time.mktime(self.notAfter) + diff = (nft - now)/(24.*3600) + return diff + + + # return SHA-1 hash of cert embedded public key + # !! At the moment, the trailing 0 is in the hashed string if any + def keyHash(self): + m = self.modulus_hexdump + res = [] + i = 0 + l = len(m) + while i MAX_CRL_SIZE: + raise Exception(error_msg) + try: + f = open(crlpath) + rawcrl = f.read() + f.close() + except: + raise Exception(error_msg) + else: + rawcrl = crlpath + + if rawcrl is None: + raise Exception(error_msg) + + self.rawcrl = rawcrl + + # Let's try to get file format : PEM or DER. + fmtstr = 'openssl crl -text -inform %s -noout' + convertstr = 'openssl crl -inform %s -outform %s' + crl_header = "-----BEGIN X509 CRL-----" + crl_footer = "-----END X509 CRL-----" + l = rawcrl.split(crl_header, 1) + if len(l) == 2: # looks like PEM + tmp = l[1] + l = tmp.split(crl_footer, 1) + if len(l) == 2: + tmp = l[0] + rawcrl = "%s%s%s\n" % (crl_header, tmp, crl_footer) + else: + raise Exception(error_msg) + r,w,e = popen3((fmtstr % "PEM").split(" ")) + w.write(rawcrl) + w.close() + textcrl = r.read() + r.close() + res = e.read() + e.close() + if res == '': + self.format = "PEM" + self.pemcrl = rawcrl + self.textcrl = textcrl + cmd = (convertstr % ("PEM", "DER")).split(" ") + self.dercrl = self._apply_ossl_cmd(cmd, rawcrl) + else: + raise Exception(error_msg) + else: # not PEM, try DER + r,w,e = popen3((fmtstr % "DER").split(' ')) + w.write(rawcrl) + w.close() + textcrl = r.read() + r.close() + res = e.read() + if res == '': + self.format = "DER" + self.dercrl = rawcrl + self.textcrl = textcrl + cmd = (convertstr % ("DER", "PEM")).split(" ") + self.pemcrl = self._apply_ossl_cmd(cmd, rawcrl) + cmd = (convertstr % ("DER", "DER")).split(" ") + self.dercrl = self._apply_ossl_cmd(cmd, rawcrl) + else: + raise Exception(error_msg) + + self.osslcmdbase = ['openssl', 'crl', '-inform', self.format] + + r,w,e = popen3(('openssl asn1parse -inform DER').split(" ")) + w.write(self.dercrl) + w.close() + self.asn1parsecrl = r.read() + r.close() + res = e.read() + e.close() + if res != '': + raise Exception(error_msg) + + # Grab _raw_ X509v3 Authority Key Identifier, if any. + tmp = self.asn1parsecrl.split(":X509v3 Authority Key Identifier", 1) + self.authorityKeyID = None + if len(tmp) == 2: + tmp = tmp[1] + tmp = tmp.split("[HEX DUMP]:", 1)[1] + self.authorityKeyID=tmp.split('\n',1)[0] + + # Parse the -text output of openssl to make things available + tmp = self.textcrl.split('\n', 1)[1] + l = tmp.split('\n', 1) + if len(l) != 2: + raise Exception(error_msg) + cur, tmp = l + i = 0 + k = self.possible_fields[i] # Version + cur = cur[len(k):] + '\n' + while k: + l = tmp.split('\n', 1) + if len(l) != 2: # Over + fields_dict[k] = cur + break + l, tmp = l + + newkey = 0 + # skip fields we have already seen, this is the purpose of 'i' + for j in range(i, self.possible_fields_count): + f = self.possible_fields[j] + if l.startswith(f): + fields_dict[k] = cur + cur = l[len(f):] + '\n' + k = f + newkey = 1 + i = j+1 + break + if newkey == 1: + continue + cur += l + '\n' + + # version + v = fields_dict[" Version"] + self.version = None + if v: + self.version = int(v[1:2]) + if self.version is None: + raise Exception(error_msg) + + # signature algorithm + v = fields_dict[" Signature Algorithm:"] + self.sigAlg = None + if v: + v = v.split('\n',1)[0] + v = v.strip() + self.sigAlg = v + if self.sigAlg is None: + raise Exception(error_msg) + + # issuer + v = fields_dict[" Issuer:"] + self.issuer = None + if v: + v = v.split('\n',1)[0] + v = v.strip() + self.issuer = v + if self.issuer is None: + raise Exception(error_msg) + + # last update + v = fields_dict[" Last Update:"] + self.lastUpdate_str = None + if v: + v = v.split('\n',1)[0] + v = v.strip() + self.lastUpdate_str = v + if self.lastUpdate_str is None: + raise Exception(error_msg) + self.lastUpdate = time.strptime(self.lastUpdate_str, + "%b %d %H:%M:%S %Y %Z") + self.lastUpdate_str_simple = time.strftime("%x", self.lastUpdate) + + # next update + v = fields_dict[" Next Update:"] + self.nextUpdate_str = None + if v: + v = v.split('\n',1)[0] + v = v.strip() + self.nextUpdate_str = v + if self.nextUpdate_str is None: + raise Exception(error_msg) + self.nextUpdate = time.strptime(self.nextUpdate_str, + "%b %d %H:%M:%S %Y %Z") + self.nextUpdate_str_simple = time.strftime("%x", self.nextUpdate) + + # XXX Do something for Issuer Alternative Name + + # Authority Key Identifier: keyid, dirname and serial + self.authorityKeyID_keyid = None + self.authorityKeyID_dirname = None + self.authorityKeyID_serial = None + if self.authorityKeyID: # (hex version already done using asn1parse) + v = fields_dict[" keyid:"] + if v: + v = v.split('\n',1)[0] + v = v.strip().replace(':', '') + self.authorityKeyID_keyid = v + v = fields_dict[" DirName:"] + if v: + v = v.split('\n',1)[0] + self.authorityKeyID_dirname = v + v = fields_dict[" serial:"] + if v: + v = v.split('\n',1)[0] + v = v.strip().replace(':', '') + self.authorityKeyID_serial = v + + # number + v = fields_dict[" X509v3 CRL Number:"] + self.number = None + if v: + v = v.split('\n',2)[1] + v = v.strip() + self.number = int(v) + + # Get the list of serial numbers of revoked certificates + self.revoked_cert_serials = [] + v = fields_dict["Revoked Certificates:"] + t = fields_dict["No Revoked Certificates."] + if (t is None and v is not None): + v = v.split("Serial Number: ")[1:] + for r in v: + s,d = r.split('\n', 1) + s = s.split('\n', 1)[0] + d = d.split("Revocation Date:", 1)[1] + d = time.strptime(d.strip(), "%b %d %H:%M:%S %Y %Z") + self.revoked_cert_serials.append((s,d)) + + # signature field + v = fields_dict[" Signature Algorithm:" ] + self.sig = None + if v: + v = v.split('\n',1)[1] + v = v.replace(' ', '').replace('\n', '') + self.sig = "".join(map(lambda x: chr(int(x, 16)), v.split(':'))) + self.sigLen = len(self.sig) + if self.sig is None: + raise Exception(error_msg) + + def __str__(self): + return self.dercrl + + # Print main informations stored in CRL + def show(self): + print("Version: %d" % self.version) + print("sigAlg: " + self.sigAlg) + print("Issuer: " + self.issuer) + print("lastUpdate: %s" % self.lastUpdate_str_simple) + print("nextUpdate: %s" % self.nextUpdate_str_simple) + + def verify(self, anchors): + """ + Return True if the CRL is signed by one of the provided + anchors. False on error (invalid signature, missing anchorand, ...) + """ + cafile = create_temporary_ca_file(anchors) + if cafile is None: + return False + try: + cmd = self.osslcmdbase + ["-noout", "-CAfile", cafile] + cmdres = self._apply_ossl_cmd(cmd, self.rawcrl) + except: + os.unlink(cafile) + return False + os.unlink(cafile) + return "verify OK" in cmdres + + + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/dadict.py b/scripts/external_libs/scapy-python3-0.18/scapy/dadict.py new file mode 100644 index 00000000..0fdcc135 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/dadict.py @@ -0,0 +1,91 @@ +## 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 + +""" +Direct Access dictionary. +""" + +from .error import Scapy_Exception + +############################### +## Direct Access dictionnary ## +############################### + +def fixname(x): + if x and x[0] in "0123456789": + x = "n_"+x + return x.translate("________________________________________________0123456789_______ABCDEFGHIJKLMNOPQRSTUVWXYZ______abcdefghijklmnopqrstuvwxyz_____________________________________________________________________________________________________________________________________") + + +class DADict_Exception(Scapy_Exception): + pass + +class DADict: + def __init__(self, _name="DADict", **kargs): + self._name=_name + self.__dict__.update(kargs) + def fixname(self,val): + return fixname(val) + def __contains__(self, val): + return val in self.__dict__ + def __getitem__(self, attr): + return getattr(self, attr) + def __setitem__(self, attr, val): + return setattr(self, self.fixname(attr), val) + def __iter__(self): + #return iter(map(lambda (x,y):y,filter(lambda (x,y):x and x[0]!="_", self.__dict__.items()))) + #return iter(map(lambda a:a[1],filter(lambda a:a[0] and a[0][0]!="_", self.__dict__.items()))) + return iter([a[1] for a in self.__dict__.items() if a[0] and a[0][0]!=" "]) + def _show(self): + for k in self.__dict__.keys(): + if k and k[0] != "_": + print("%10s = %r" % (k,getattr(self,k))) + def __repr__(self): + #return "<%s/ %s>" % (self._name," ".join(filter(lambda x:x and x[0]!="_",self.__dict__.keys()))) + return "<%s/ %s>" % (self._name," ".join([ x for x in self.__dict__.keys() if x and x[0]!="_"])) + + def _branch(self, br, uniq=0): + if uniq and br._name in self: + raise DADict_Exception("DADict: [%s] already branched in [%s]" % (br._name, self._name)) + self[br._name] = br + + def _my_find(self, *args, **kargs): + if args and self._name not in args: + return False + for k in kargs: + if k not in self or self[k] != kargs[k]: + return False + return True + + def _find(self, *args, **kargs): + return self._recurs_find((), *args, **kargs) + def _recurs_find(self, path, *args, **kargs): + if self in path: + return None + if self._my_find(*args, **kargs): + return self + for o in self: + if isinstance(o, DADict): + p = o._recurs_find(path+(self,), *args, **kargs) + if p is not None: + return p + return None + def _find_all(self, *args, **kargs): + return self._recurs_find_all((), *args, **kargs) + def _recurs_find_all(self, path, *args, **kargs): + r = [] + if self in path: + return r + if self._my_find(*args, **kargs): + r.append(self) + for o in self: + if isinstance(o, DADict): + p = o._recurs_find_all(path+(self,), *args, **kargs) + r += p + return r + def keys(self): + #return filter(lambda x:x and x[0]!="_", self.__dict__.keys()) + return [ x for x in self.__dict__.keys() if x and x[0]!="_" ] + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/data.py b/scripts/external_libs/scapy-python3-0.18/scapy/data.py new file mode 100644 index 00000000..fc92ebe2 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/data.py @@ -0,0 +1,215 @@ +## 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 + +""" +Global variables and functions for handling external data sets. +""" + +import os,sys,re +from .dadict import DADict +from .error import log_loading + +############ +## Consts ## +############ + +ETHER_ANY = b"\x00"*6 +ETHER_BROADCAST = b"\xff"*6 + +ETH_P_ALL = 3 +ETH_P_IP = 0x800 +ETH_P_ARP = 0x806 +ETH_P_IPV6 = 0x86dd + +# From net/if_arp.h +ARPHDR_ETHER = 1 +ARPHDR_METRICOM = 23 +ARPHDR_PPP = 512 +ARPHDR_LOOPBACK = 772 +ARPHDR_TUN = 65534 + + +# From net/ipv6.h on Linux (+ Additions) +IPV6_ADDR_UNICAST = 0x01 +IPV6_ADDR_MULTICAST = 0x02 +IPV6_ADDR_CAST_MASK = 0x0F +IPV6_ADDR_LOOPBACK = 0x10 +IPV6_ADDR_GLOBAL = 0x00 +IPV6_ADDR_LINKLOCAL = 0x20 +IPV6_ADDR_SITELOCAL = 0x40 # deprecated since Sept. 2004 by RFC 3879 +IPV6_ADDR_SCOPE_MASK = 0xF0 +#IPV6_ADDR_COMPATv4 = 0x80 # deprecated; i.e. ::/96 +#IPV6_ADDR_MAPPED = 0x1000 # i.e.; ::ffff:0.0.0.0/96 +IPV6_ADDR_6TO4 = 0x0100 # Added to have more specific info (should be 0x0101 ?) +IPV6_ADDR_UNSPECIFIED = 0x10000 + + + + +MTU = 0x7fff # a.k.a give me all you have + +WINDOWS=sys.platform.startswith("win") + + +# file parsing to get some values : + +def load_protocols(filename): + spaces = re.compile("[ \t]+|\n") + dct = DADict(_name=filename) + try: + for l in open(filename): + try: + shrp = l.find("#") + if shrp >= 0: + l = l[:shrp] + l = l.strip() + if not l: + continue + lt = tuple(re.split(spaces, l)) + if len(lt) < 2 or not lt[0]: + continue + dct[lt[0]] = int(lt[1]) + except Exception as e: + log_loading.info("Couldn't parse file [%s]: line [%r] (%s)" % (filename,l,e)) + except IOError: + log_loading.info("Can't open %s file" % filename) + return dct + +def load_ethertypes(filename): + spaces = re.compile("[ \t]+|\n") + dct = DADict(_name=filename) + try: + f=open(filename) + for l in f: + try: + shrp = l.find("#") + if shrp >= 0: + l = l[:shrp] + l = l.strip() + if not l: + continue + lt = tuple(re.split(spaces, l)) + if len(lt) < 2 or not lt[0]: + continue + dct[lt[0]] = int(lt[1], 16) + except Exception as e: + log_loading.info("Couldn't parse file [%s]: line [%r] (%s)" % (filename,l,e)) + f.close() + except IOError as msg: + pass + return dct + +def load_services(filename): + spaces = re.compile("[ \t]+|\n") + tdct=DADict(_name="%s-tcp"%filename) + udct=DADict(_name="%s-udp"%filename) + try: + f=open(filename) + for l in f: + try: + shrp = l.find("#") + if shrp >= 0: + l = l[:shrp] + l = l.strip() + if not l: + continue + lt = tuple(re.split(spaces, l)) + if len(lt) < 2 or not lt[0]: + continue + if lt[1].endswith("/tcp"): + tdct[lt[0]] = int(lt[1].split('/')[0]) + elif lt[1].endswith("/udp"): + udct[lt[0]] = int(lt[1].split('/')[0]) + except Exception as e: + log_loading.warning("Couldn't file [%s]: line [%r] (%s)" % (filename,l,e)) + f.close() + except IOError: + log_loading.info("Can't open /etc/services file") + return tdct,udct + + +class ManufDA(DADict): + def fixname(self, val): + return val + def _get_manuf_couple(self, mac): + oui = ":".join(mac.split(":")[:3]).upper() + return self.__dict__.get(oui,(mac,mac)) + def _get_manuf(self, mac): + return self._get_manuf_couple(mac)[1] + def _get_short_manuf(self, mac): + return self._get_manuf_couple(mac)[0] + def _resolve_MAC(self, mac): + oui = ":".join(mac.split(":")[:3]).upper() + if oui in self: + return ":".join([self[oui][0]]+ mac.split(":")[3:]) + return mac + + + + +def load_manuf(filename): + try: + manufdb=ManufDA(_name=filename) + for l in open(filename, "r", encoding = 'utf-8'): + try: + l = l.strip() + if not l or l.startswith("#"): + continue + oui,shrt=l.split()[:2] + i = l.find("#") + if i < 0: + lng=shrt + else: + lng = l[i+2:] + manufdb[oui] = shrt,lng + except Exception as e: + log_loading.warning("Couldn't parse one line from [%s] [%r] (%s)" % (filename, l, e)) + except IOError: + #log_loading.warning("Couldn't open [%s] file" % filename) + pass + return manufdb + + + +if WINDOWS: + ETHER_TYPES=load_ethertypes("ethertypes") + IP_PROTOS=load_protocols(os.environ["SystemRoot"]+"\system32\drivers\etc\protocol") + TCP_SERVICES,UDP_SERVICES=load_services(os.environ["SystemRoot"] + "\system32\drivers\etc\services") + MANUFDB = load_manuf(os.environ["ProgramFiles"] + "\\wireshark\\manuf") +else: + IP_PROTOS=load_protocols("/etc/protocols") + ETHER_TYPES=load_ethertypes("/etc/ethertypes") + TCP_SERVICES,UDP_SERVICES=load_services("/etc/services") + MANUFDB = load_manuf("/usr/share/wireshark/manuf") + + + +##################### +## knowledge bases ## +##################### + +class KnowledgeBase: + def __init__(self, filename): + self.filename = filename + self.base = None + + def lazy_init(self): + self.base = "" + + def reload(self, filename = None): + if filename is not None: + self.filename = filename + oldbase = self.base + self.base = None + self.lazy_init() + if self.base is None: + self.base = oldbase + + def get_base(self): + if self.base is None: + self.lazy_init() + return self.base + + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/error.py b/scripts/external_libs/scapy-python3-0.18/scapy/error.py new file mode 100644 index 00000000..1753d523 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/error.py @@ -0,0 +1,60 @@ +## 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 + +""" +Logging subsystem and basic exception class. +""" + +############################# +##### Logging subsystem ##### +############################# + +class Scapy_Exception(Exception): + pass + +import logging,traceback,time + +class ScapyFreqFilter(logging.Filter): + def __init__(self): + logging.Filter.__init__(self) + self.warning_table = {} + def filter(self, record): + from .config import conf + wt = conf.warning_threshold + if wt > 0: + stk = traceback.extract_stack() + caller=None + for f,l,n,c in stk: + if n == 'warning': + break + caller = l + tm,nb = self.warning_table.get(caller, (0,0)) + ltm = time.time() + if ltm-tm > wt: + tm = ltm + nb = 0 + else: + if nb < 2: + nb += 1 + if nb == 2: + record.msg = "more "+record.msg + else: + return 0 + self.warning_table[caller] = (tm,nb) + return 1 + +log_scapy = logging.getLogger("scapy") +console_handler = logging.StreamHandler() +console_handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s")) +log_scapy.addHandler(console_handler) +log_runtime = logging.getLogger("scapy.runtime") # logs at runtime +log_runtime.addFilter(ScapyFreqFilter()) +log_interactive = logging.getLogger("scapy.interactive") # logs in interactive functions +log_loading = logging.getLogger("scapy.loading") # logs when loading scapy + + +def warning(x): + log_runtime.warning(x) + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/fields.py b/scripts/external_libs/scapy-python3-0.18/scapy/fields.py new file mode 100644 index 00000000..5482ce87 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/fields.py @@ -0,0 +1,935 @@ +## 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 + +""" +Fields: basic data structures that make up parts of packets. +""" + +import struct,copy,socket +from .config import conf +from .volatile import * +from .data import * +from .utils import * +from .base_classes import BasePacket,Gen,Net + + +############ +## Fields ## +############ + +class Field: + """For more informations on how this work, please refer to + http://www.secdev.org/projects/scapy/files/scapydoc.pdf + chapter ``Adding a New Field''""" + islist=0 + holds_packets=0 + def __init__(self, name, default, fmt="H"): + self.name = name + if fmt[0] in "@=<>!": + self.fmt = fmt + else: + self.fmt = "!"+fmt + self.default = self.any2i(None,default) + self.sz = struct.calcsize(self.fmt) + self.owners = [] + self.offset =0; + + + def get_size_bytes (self): + if hasattr(self, 'size'): + return 0; # bitfield + else: + return self.sz + + def register_owner(self, cls): + self.owners.append(cls) + + def i2len(self, pkt, x): + """Convert internal value to a length usable by a FieldLenField""" + return self.sz + def i2count(self, pkt, x): + """Convert internal value to a number of elements usable by a FieldLenField. + Always 1 except for list fields""" + return 1 + def i2b(self, pkt, x): + """Convert internal value to internal value""" + if type(x) is str: + x = bytes([ ord(i) for i in x ]) + return x + def h2i(self, pkt, x): + """Convert human value to internal value""" + if type(x) is str: + x = bytes([ ord(i) for i in x ]) + return x + def i2h(self, pkt, x): + """Convert internal value to human value""" + return x + def m2i(self, pkt, x): + """Convert machine value to internal value""" + return x + def i2m(self, pkt, x): + """Convert internal value to machine value""" + if x is None: + x = 0 + return x + def any2i(self, pkt, x): + """Try to understand the most input values possible and make an internal value from them""" + return self.h2i(pkt, x) + def i2repr(self, pkt, x): + """Convert internal value to a nice representation""" + return repr(self.i2h(pkt,x)) + def addfield(self, pkt, s, val): + """Add an internal value to a string""" + return s+struct.pack(self.fmt, self.i2m(pkt,val)) + def getfield(self, pkt, s): + """Extract an internal value from a string""" + return s[self.sz:], self.m2i(pkt, struct.unpack(self.fmt, s[:self.sz])[0]) + def do_copy(self, x): + if hasattr(x, "copy"): + return x.copy() + if type(x) is list: + x = x[:] + for i in range(len(x)): + if isinstance(x[i], BasePacket): + x[i] = x[i].copy() + return x + def __repr__(self): + return "" % (",".join(x.__name__ for x in self.owners),self.name) + def copy(self): + return copy.deepcopy(self) + def randval(self): + """Return a volatile object whose value is both random and suitable for this field""" + fmtt = self.fmt[-1] + if fmtt in "BHIQ": + return {"B":RandByte,"H":RandShort,"I":RandInt, "Q":RandLong}[fmtt]() + elif fmtt == "s": + if self.fmt[0] in "0123456789": + l = int(self.fmt[:-1]) + else: + l = int(self.fmt[1:-1]) + return RandBin(l) + else: + warning("no random class for [%s] (fmt=%s)." % (self.name, self.fmt)) + + + + +class Emph: + fld = b"" + def __init__(self, fld): + self.fld = fld + def __getattr__(self, attr): + return getattr(self.fld,attr) + def __hash__(self): + return hash(self.fld) + def __eq__(self, other): + return self.fld == other + + +class ActionField: + _fld = None + def __init__(self, fld, action_method, **kargs): + self._fld = fld + self._action_method = action_method + self._privdata = kargs + def any2i(self, pkt, val): + getattr(pkt, self._action_method)(val, self._fld, **self._privdata) + return getattr(self._fld, "any2i")(pkt, val) + def __getattr__(self, attr): + return getattr(self._fld,attr) + + +class ConditionalField: + fld = None + def __init__(self, fld, cond): + self.fld = fld + self.cond = cond + def _evalcond(self,pkt): + return self.cond(pkt) + + def getfield(self, pkt, s): + if self._evalcond(pkt): + return self.fld.getfield(pkt,s) + else: + return s,None + + def addfield(self, pkt, s, val): + if self._evalcond(pkt): + return self.fld.addfield(pkt,s,val) + else: + return s + def __getattr__(self, attr): + return getattr(self.fld,attr) + + +class PadField: + """Add bytes after the proxified field so that it ends at the specified + alignment from its begining""" + _fld = None + def __init__(self, fld, align, padwith=None): + self._fld = fld + self._align = align + self._padwith = padwith or b"" + + def padlen(self, flen): + return -flen%self._align + + def getfield(self, pkt, s): + remain,val = self._fld.getfield(pkt,s) + padlen = self.padlen(len(s)-len(remain)) + return remain[padlen:], val + + def addfield(self, pkt, s, val): + sval = self._fld.addfield(pkt, b"", val) + return s+sval+struct.pack("%is" % (self.padlen(len(sval))), self._padwith) + + def __getattr__(self, attr): + return getattr(self._fld,attr) + + +class MACField(Field): + def __init__(self, name, default): + Field.__init__(self, name, default, "6s") + def i2m(self, pkt, x): + if x is None: + return b"\0\0\0\0\0\0" + return mac2str(x) + def m2i(self, pkt, x): + return str2mac(x) + def any2i(self, pkt, x): + if type(x) is bytes and len(x) is 6: + x = self.m2i(pkt, x) + return x + def i2repr(self, pkt, x): + x = self.i2h(pkt, x) + if self in conf.resolve: + x = conf.manufdb._resolve_MAC(x) + return x + def randval(self): + return RandMAC() + + +class IPField(Field): + def __init__(self, name, default): + Field.__init__(self, name, default, "4s") + def h2i(self, pkt, x): + if type(x) is str: + try: + inet_aton(x) + except socket.error: + x = Net(x) + elif type(x) is list: + x = [self.h2i(pkt, n) for n in x] + return x + def resolve(self, x): + if self in conf.resolve: + try: + ret = socket.gethostbyaddr(x)[0] + except: + pass + else: + if ret: + return ret + return x + def i2m(self, pkt, x): + return inet_aton(x) + def m2i(self, pkt, x): + return inet_ntoa(x) + def any2i(self, pkt, x): + return self.h2i(pkt,x) + def i2repr(self, pkt, x): + return self.resolve(self.i2h(pkt, x)) + def randval(self): + return RandIP() + +class SourceIPField(IPField): + def __init__(self, name, dstname): + IPField.__init__(self, name, None) + self.dstname = dstname + def i2m(self, pkt, x): + if x is None: + iff,x,gw = pkt.route() + if x is None: + x = "0.0.0.0" + return IPField.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.route.route, dst) + r = [ conf.route.route(i) for i in dst ] + r.sort() + if r[0] != r[-1]: + warning("More than one possible route for %s"%repr(dst)) + iff,x,gw = r[0] + else: + iff,x,gw = conf.route.route(dst) + return IPField.i2h(self, pkt, x) + + + + +class ByteField(Field): + def __init__(self, name, default): + Field.__init__(self, name, default, "B") + +class XByteField(ByteField): + def i2repr(self, pkt, x): + return lhex(self.i2h(pkt, x)) + +class OByteField(ByteField): + def i2repr(self, pkt, x): + return "%03o"%self.i2h(pkt, x) + +class X3BytesField(XByteField): + def __init__(self, name, default): + Field.__init__(self, name, default, "!I") + def addfield(self, pkt, s, val): + return s+struct.pack(self.fmt, self.i2m(pkt,val))[1:4] + def getfield(self, pkt, s): + return s[3:], self.m2i(pkt, struct.unpack(self.fmt, b"\x00"+s[:3])[0]) + + +class ThreeBytesField(X3BytesField, ByteField): + def i2repr(self, pkt, x): + return ByteField.i2repr(self, pkt, x) + + +class ShortField(Field): + def __init__(self, name, default): + Field.__init__(self, name, default, "H") + +class LEShortField(Field): + def __init__(self, name, default): + Field.__init__(self, name, default, ">4))+chr(0x41+(ord(x)&0xf)), x)) + x = b"".join([ bytes([0x41+(i>>4),0x41+(i&0xf)]) for i in x ]) + x = b" "+x + return x + def m2i(self, pkt, x): + x = x.strip(b"\x00").strip(b" ") + #return b"".join(map(lambda x,y: chr((((ord(x)-1)&0xf)<<4)+((ord(y)-1)&0xf)), x[::2],x[1::2])) + return b"".join(map(lambda x,y: bytes([(((x-1)&0xf)<<4)+((y-1)&0xf)]), x[::2],x[1::2])) + +class StrLenField(StrField): + def __init__(self, name, default, fld=None, length_from=None): + StrField.__init__(self, name, default) + self.length_from = length_from + def getfield(self, pkt, s): + l = self.length_from(pkt) + return s[l:], self.m2i(pkt,s[:l]) + +class FieldListField(Field): + islist=1 + def __init__(self, name, default, field, length_from=None, count_from=None): + if default is None: + default = [] # Create a new list for each instance + Field.__init__(self, name, default) + self.count_from = count_from + self.length_from = length_from + self.field = field + + def i2count(self, pkt, val): + if type(val) is list: + return len(val) + return 1 + def i2len(self, pkt, val): + return sum( self.field.i2len(pkt,v) for v in val ) + + def i2m(self, pkt, val): + if val is None: + val = [] + return val + def any2i(self, pkt, x): + if type(x) is not list: + return [x] + else: + return x + def addfield(self, pkt, s, val): + val = self.i2m(pkt, val) + for v in val: + s = self.field.addfield(pkt, s, v) + return s + 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) + + val = [] + ret=b"" + if l is not None: + s,ret = s[:l],s[l:] + + while s: + if c is not None: + if c <= 0: + break + c -= 1 + s,v = self.field.getfield(pkt, s) + val.append(v) + return s+ret, val + +class FieldLenField(Field): + def __init__(self, name, default, length_of=None, fmt = "H", count_of=None, adjust=lambda pkt,x:x, fld=None): + Field.__init__(self, name, default, fmt) + self.length_of=length_of + self.count_of=count_of + self.adjust=adjust + if fld is not None: + FIELD_LENGTH_MANAGEMENT_DEPRECATION(self.__class__.__name__) + self.length_of = fld + def i2m(self, pkt, x): + if x is None: + if self.length_of is not None: + fld,fval = pkt.getfield_and_val(self.length_of) + f = fld.i2len(pkt, fval) + else: + fld,fval = pkt.getfield_and_val(self.count_of) + f = fld.i2count(pkt, fval) + x = self.adjust(pkt,f) + return x + +class StrNullField(StrField): + def addfield(self, pkt, s, val): + return s+self.i2m(pkt, val)+b"\x00" + def getfield(self, pkt, s): + l = s.find(b"\x00") + if l < 0: + #XXX \x00 not found + return "",s + return s[l+1:],self.m2i(pkt, s[:l]) + def randval(self): + return RandTermString(RandNum(0,1200),b"\x00") + +class StrStopField(StrField): + def __init__(self, name, default, stop, additionnal=0): + Field.__init__(self, name, default) + self.stop=stop + self.additionnal=additionnal + def getfield(self, pkt, s): + l = s.find(self.stop) + if l < 0: + return b"",s +# raise Scapy_Exception,"StrStopField: stop value [%s] not found" %stop + l += len(self.stop)+self.additionnal + return s[l:],s[:l] + def randval(self): + return RandTermString(RandNum(0,1200),self.stop) + +class LenField(Field): + def i2m(self, pkt, x): + if x is None: + x = len(pkt.payload) + return x + +class BCDFloatField(Field): + def i2m(self, pkt, x): + return int(256*x) + def m2i(self, pkt, x): + return x/256.0 + +class BitField(Field): + def __init__(self, name, default, size): + Field.__init__(self, name, default) + self.rev = size < 0 + self.size = abs(size) + def reverse(self, val): + if self.size == 16: + val = socket.ntohs(val) + elif self.size == 32: + val = socket.ntohl(val) + return val + + def addfield(self, pkt, s, val): + val = self.i2m(pkt, val) + if type(s) is tuple: + s,bitsdone,v = s + else: + bitsdone = 0 + v = 0 + if self.rev: + val = self.reverse(val) + v <<= self.size + v |= val & ((1<= 8: + bitsdone -= 8 + s = s+struct.pack("!B", v >> bitsdone) + v &= (1<> (nb_bytes*8 - self.size - bn) + + if self.rev: + b = self.reverse(b) + + bn += self.size + s = s[bn//8:] + bn = bn%8 + b = self.m2i(pkt, b) + if bn: + return (s,bn),b + else: + return s,b + def randval(self): + return RandNum(0,2**self.size-1) + + +class BitFieldLenField(BitField): + def __init__(self, name, default, size, length_of=None, count_of=None, adjust=lambda pkt,x:x): + BitField.__init__(self, name, default, size) + self.length_of=length_of + self.count_of=count_of + self.adjust=adjust + def i2m(self, pkt, x): + #return FieldLenField.i2m.im_func(self, pkt, x) + return FieldLenField.i2m(self, pkt, x) + + +class XBitField(BitField): + def i2repr(self, pkt, x): + return lhex(self.i2h(pkt,x)) + + +class EnumField(Field): + def __init__(self, name, default, enum, fmt = "H"): + i2s = self.i2s = {} + s2i = self.s2i = {} + if type(enum) is list: + keys = range(len(enum)) + else: + keys = enum.keys() + if list(filter(lambda x: type(x) is str, keys)): + i2s,s2i = s2i,i2s + for k in keys: + i2s[k] = enum[k] + s2i[enum[k]] = k + Field.__init__(self, name, default, fmt) + def any2i_one(self, pkt, x): + if type(x) is str: + x = self.s2i[x] + return x + def i2repr_one(self, pkt, x): + if self not in conf.noenum and not isinstance(x,VolatileValue) and x in self.i2s: + return self.i2s[x] + return repr(x) + + def any2i(self, pkt, x): + if type(x) is list: + return list(map(lambda z,pkt=pkt:self.any2i_one(pkt,z), x)) + else: + return self.any2i_one(pkt,x) + def i2repr(self, pkt, x): + if type(x) is list: + return list(map(lambda z,pkt=pkt:self.i2repr_one(pkt,z), x)) + else: + return self.i2repr_one(pkt,x) + +class CharEnumField(EnumField): + def __init__(self, name, default, enum, fmt = "1s"): + EnumField.__init__(self, name, default, enum, fmt) + k = self.i2s.keys() + if k and len(k[0]) != 1: + self.i2s,self.s2i = self.s2i,self.i2s + def any2i_one(self, pkt, x): + if len(x) != 1: + x = self.s2i[x] + return x + +class BitEnumField(BitField,EnumField): + def __init__(self, name, default, size, enum): + EnumField.__init__(self, name, default, enum) + self.rev = size < 0 + self.size = abs(size) + def any2i(self, pkt, x): + return EnumField.any2i(self, pkt, x) + def i2repr(self, pkt, x): + return EnumField.i2repr(self, pkt, x) + +class ShortEnumField(EnumField): + def __init__(self, name, default, enum): + EnumField.__init__(self, name, default, enum, "H") + +class LEShortEnumField(EnumField): + def __init__(self, name, default, enum): + EnumField.__init__(self, name, default, enum, ">= 1 + if self.multi: + r = "+".join(r) + return r + + + + +class FixedPointField(BitField): + def __init__(self, name, default, size, frac_bits=16): + self.frac_bits = frac_bits + BitField.__init__(self, name, default, size) + + def any2i(self, pkt, val): + if val is None: + return val + ival = int(val) + fract = int( (val-ival) * 2**self.frac_bits ) + return (ival << self.frac_bits) | fract + + def i2h(self, pkt, val): + int_part = val >> self.frac_bits + frac_part = val & (1 << self.frac_bits) - 1 + frac_part /= 2.0**self.frac_bits + return int_part+frac_part + def i2repr(self, pkt, val): + return self.i2h(pkt, val) diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/layers/__init__.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/__init__.py new file mode 100644 index 00000000..a3f2afb9 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/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-python3-0.18/scapy/layers/all.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/all.py new file mode 100644 index 00000000..8104b6a2 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/layers/all.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 + +""" +All layers. Configurable with conf.load_layers. +""" + +import importlib +from scapy.config import conf +from scapy.error import log_loading +import logging +log = logging.getLogger("scapy.loading") + +#log_loading.info("Please, report issues to https://github.com/phaethon/scapy") + +def _import_star(m): + #mod = __import__("." + m, globals(), locals()) + mod = importlib.import_module("scapy.layers." + m) + for k,v in mod.__dict__.items(): + globals()[k] = v + + +for _l in ['l2','inet','inet6']: + log_loading.debug("Loading layer %s" % _l) + #print "load ",_l + _import_star(_l) + +#def _import_star(m): + #mod = __import__("." + m, globals(), locals()) +# mod = importlib.import_module("scapy.layers." + m) +# for k,v in mod.__dict__.items(): +# globals()[k] = v + +#for _l in conf.load_layers: +# log_loading.debug("Loading layer %s" % _l) +# try: +# _import_star(_l) +# except Exception as e: +# log.warning("can't import layer %s: %s" % (_l,e)) + + + + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/layers/bluetooth.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/bluetooth.py new file mode 100644 index 00000000..5dd365a4 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/layers/bluetooth.py @@ -0,0 +1,213 @@ +## 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:] + p = p[:2]+bytes([(l&0xff),((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:] + p = p[:2]+bytes([(l&0xff),((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:] + p = p[:2]+bytes([(l&0xff),((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", 0xffffffff,0xffffffff,0xffffffff,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-python3-0.18/scapy/layers/dhcp.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/dhcp.py new file mode 100644 index 00000000..e2b7c1f1 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/layers/dhcp.py @@ -0,0 +1,381 @@ +## 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 +from scapy.utils import str2bytes + +dhcpmagic=b"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",b"", "16s"), + Field("sname",b"","64s"), + Field("file",b"","128s"), + StrField("options",b"") ] + 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 b"", 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.items(): + 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 = list(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 v[0] in DHCPRevOptions 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 b"", self.m2i(pkt, s) + + def m2i(self, pkt, x): + opt = [] + while x: + #o = ord(x[0]) + o = 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: + if len(x) < 2 or len(x) < x[1]+2: + opt.append(x) + break + elif o in DHCPOptions: + f = DHCPOptions[o] + + if isinstance(f, str): + #olen = ord(x[1]) + olen = x[1] + opt.append( (f,x[2:olen+2]) ) + x = x[olen+2:] + else: + olen = 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]) + olen = 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 = b"" + 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, b"".join(lval) + elif name in DHCPRevOptions: + onum, f = DHCPRevOptions[name] + if f is not None: + lval = [f.addfield(pkt,b"",f.any2i(pkt,val)) for val in lval] + oval = b"".join(lval) + else: + warning("Unknown field option %s" % name) + continue + + s += bytes([onum]) + s += bytes([len(oval)]) + s += oval + + elif (type(o) is str and o in DHCPRevOptions and + DHCPRevOptions[o][1] == None): + s += bytes([DHCPRevOptions[o][0]]) + elif type(o) is int: + s += chr(o)+b"\0" + elif type(o) is str: + s += str2bytes(o) + elif type(o) is bytes: + s += o + else: + warning("Malformed option %s" % o) + return s + + +class DHCP(Packet): + name = "DHCP options" + fields_desc = [ DHCPOptionsField("options",b"") ] + + +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=b'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 + 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 mac in self.leases: + 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-python3-0.18/scapy/layers/dhcp6.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/dhcp6.py new file mode 100644 index 00000000..a11a4149 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/layers/dhcp6.py @@ -0,0 +1,1718 @@ +## 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",b"") ] + +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", b"", + 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 bytes(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", b"", + 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(bytes(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 y in dhcp6opts: + 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 b"".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", b"A"*8, 8), # TODO: XXX + StrLenField("authinfo", b"", + 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", b"", + 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(bytes(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 = b"" + 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", b"", + 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", b"", + 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", b"", + 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] != b'\x00': + while x and x[0] != 0: + l = (x[0]) + cur.append(x[1:l+1]) + x = x[l+1:] + res.append(b".".join(cur)) + if x and x[0] == 0: + x = x[1:] + return res + + def i2m(self, pkt, x): + def conditionalTrailingDot(z): + if z and z[-1] == 0: + return z + return z+b'\x00' + res = b"" + x = [ i.encode('ascii') for i in x if type(i) is str ] + tmp = map(lambda y: map((lambda z: chr(len(z)).encode('ascii')+z), y.split(b'.')), x) + return b"".join(map(lambda x: conditionalTrailingDot(b"".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", b"", + 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 = (x[0]) + cur.append(x[1:1+l]) + x = x[l+1:] + ret_str = b".".join(cur) + return ret_str + + def i2m(self, pkt, x): + if not x: + return b"" + tmp = b"".join(map(lambda z: chr(len(z)).encode('ascii')+z, x.split(b'.'))) + 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", b"", + 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", b"", + 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((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((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) + mac = ":".join(map(lambda x: "%.02x" % x, 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()) + l = [ x for x in in6_getifaddr() if x[2] == iface and in6_islladdr(x[0]) ] + 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 bytes(duid) != bytes(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) + opsaddr = [ x for x in l if isinstance(x, DHCP6OptIAAddress) ] + 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 bytes(duid) != bytes(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-python3-0.18/scapy/layers/dns.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/dns.py new file mode 100644 index 00000000..8a01e273 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/layers/dns.py @@ -0,0 +1,712 @@ +## 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 +from scapy.utils import str2bytes + +class DNSStrField(StrField): + + def h2i(self, pkt, x): + if type(x) == str: + x = x.encode('ascii') + if x == b"": + return b"." + return x + + def i2m(self, pkt, x): + if type(x) == str: + x = x.encode('ascii') + if x == b".": + return b"\x00" + + x = [k[:63] for k in x.split(b".")] # Truncate chunks that cannot be encoded (more than 63 bytes..) + x = map(lambda y: bytes([len(y)]) + y, x) + x = b"".join(x) + if x[-1] != 0: + x += b"\x00" + return x + + def getfield(self, pkt, s): + n = b"" + + #if ord(s[0]) == 0: + if (s[0]) == 0: + return s[1:], b"." + + while 1: + #l = ord(s[0]) + l = (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]+b"." + 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 = b"" + 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]) + l = 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) + 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]+b"." + 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 b"" + return bytes(x) + def decodeRR(self, name, s, p): + ret = s[p:p+10] + type,cls,ttl,rdlen = struct.unpack("!HHIH", ret) + p += 10 + rr = DNSRR(b"\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](b"\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,b"" + 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(b"\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 = b"" + 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", bytes([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: + if type(s) is bytes: + s = s.decode('ascii') + s = inet_aton(s) + elif pkt.type in [2,3,4,5]: # NS, MD, MF, CNAME + s = b"".join(map(lambda x: bytes([len(x)]) + x, s.split(b"."))) + #if ord(s[-1]): + if s[-1]: + s += b"\x00" + elif pkt.type == 16: # TXT + if s: + ret_s = b"" + # The initial string must be splitted into a list of strings + # prepended with theirs sizes. + while len(s) >= 255: + ret_s += b"\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.getstrval("rdata") + else: + type = "Qry" + if self.qdcount > 0 and isinstance(self.qd, DNSQR): + name = ' "%s"' % self.qd.getstrval("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",b""), + 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", b"", length_from=lambda pkt: pkt.optlen) ] + + def extract_padding(self, p): + return b"", p + +class DNSRROPT(Packet): + name = "DNS OPT Resource Record" + fields_desc = [ DNSStrField("rrname",b""), + 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 + window_block = (bitmap[0]) # window number + offset = 256*window_block # offset of the Ressource Record + #bitmap_len = ord(bitmap[0]) # length of the bitmap in bytes + bitmap_len = (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 range(len(tmp_bitmap)): + v = 128 + for i in range(8): + #if ord(tmp_bitmap[b]) & v: + if (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 = b"" + lst = list(set(lst)) + lst.sort() + + #lst = filter(lambda x: x <= 65535, lst) + #lst = map(lambda x: abs(x), lst) + lst = [ abs(x) for x in lst if x<= 65535 ] + + # 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 range(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 = [ x for x in lst if 256*wb <= x and x < 256*(wb+1) ] + 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 + bs = 1 + else: + max = rrlist[-1] - 256*wb + #bs = int(math.ceil(max / 8)) + 1 # use at least 1 byte + bs = int(max // 8) + 1 # use at least 1 byte + if bs > 32: # Don't encode more than 256 bits / values + bs = 32 + + bitmap += struct.pack("B", wb) + bitmap += struct.pack("B", bs) + + # Generate the bitmap + for tmp in range(bs): + 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) + tmp_rrlist = [ x for x in rrlist if 256*wb+8*tmp <= x and x < 256*wb+8*tmp+8 ] + 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) + v = sum(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(b"", 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",b""), + ShortEnumField("type", 6, dnstypes), + ShortEnumField("rclass", 1, dnsclasses), + IntField("ttl", 0), + ShortField("rdlen", None), + DNSStrField("mname", b""), + DNSStrField("rname", b""), + 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",b""), + 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", b""), + StrField("signature", b"") + ] + + +class DNSRRNSEC(_DNSRRdummy): + name = "DNS NSEC Resource Record" + fields_desc = [ DNSStrField("rrname",b""), + ShortEnumField("type", 47, dnstypes), + ShortEnumField("rclass", 1, dnsclasses), + IntField("ttl", 0), + ShortField("rdlen", None), + DNSStrField("nextname", b""), + RRlistField("typebitmaps", b"") + ] + + +class DNSRRDNSKEY(_DNSRRdummy): + name = "DNS DNSKEY Resource Record" + fields_desc = [ DNSStrField("rrname",b""), + 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", b"") + ] + + +class DNSRRDS(_DNSRRdummy): + name = "DNS DS Resource Record" + fields_desc = [ DNSStrField("rrname",b""), + 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", b"") + ] + + +# 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",b""), + 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", b"", length_from=lambda x: x.saltlength), + FieldLenField("hashlength", 0, fmt="!B", length_of="nexthashedownername"), + StrLenField("nexthashedownername", b"", length_from=lambda x: x.hashlength), + RRlistField("typebitmaps", b"") + ] + + +class DNSRRNSEC3PARAM(_DNSRRdummy): + name = "DNS NSEC3PARAM Resource Record" + fields_desc = [ DNSStrField("rrname",b""), + 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", b"", 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 ] + ret = False + for i in list: + ret = ret or i + return ret + +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=b"")]), + 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-python3-0.18/scapy/layers/dot11.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/dot11.py new file mode 100644 index 00000000..417a470e --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/layers/dot11.py @@ -0,0 +1,560 @@ +## 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", b"\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 b"" + + 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)) + data = [ x.getlayer(Dot11) for x in self.res if x.haslayer(Dot11) and x.type == 2 ] + 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-python3-0.18/scapy/layers/gprs.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/gprs.py new file mode 100644 index 00000000..31a931fe --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/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-python3-0.18/scapy/layers/hsrp.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/hsrp.py new file mode 100644 index 00000000..7193b97e --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/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-python3-0.18/scapy/layers/inet.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/inet.py new file mode 100644 index 00000000..04b99e89 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/layers/inet.py @@ -0,0 +1,1569 @@ +## 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,types +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 b"",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 = 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 = 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 = x[1] + if olen < 2: + warning("Malformed TCP option (announced length is %i)" % olen) + olen = 2 + oval = x[2:olen] + if onum in TCPOptions[0]: + 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 = b"" + for oname,oval in x: + if type(oname) is str: + if oname == "NOP": + opt += b"\x01" + continue + elif oname == "EOL": + opt += b"\x00" + continue + elif oname in TCPOptions[1]: + onum = TCPOptions[1][oname] + ofmt = TCPOptions[0][onum][1] + if onum == 5: #SAck + ofmt += b"%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 += bytes([(onum), (2+len(oval))]) + oval + return opt+b"\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("dst", "127.0.0.1")), + + 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 += b"\0"*((-len(p))%4) # pad IP options if needed + if ihl is None: + ihl = len(p)//4 + p = bytes([((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]+bytes([ck>>8])+bytes([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(bytes(p), (p.dst,0)) + except socket.error as msg: + log_runtime.error(msg) + if slp: + time.sleep(slp) + def route(self): + dst = self.dst + if isinstance(dst,Gen): + dst = next(iter(dst)) + 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 = bytes(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]+bytes([(dataofs << 4) | (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]+bytes([ck>>8, 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): + fields_desc = [ ShortEnumField("sport", 20, TCP_SERVICES), + ShortEnumField("dport", 80, TCP_SERVICES), + IntField("seq", 0) ] + name = "TCP in ICMP" + def post_build(self, p, pay): + p += pay + return p + 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 = bytes(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.values(): + 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__(bytes(p))) + return nofrag,defrag2,missfrag + +@conf.commands.register +def defragment(plist): + """defragment(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.values(): + 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__(bytes(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) + d = map(lambda a: (a[0]%2000,((a[0]-c[0][0])-((a[1]-c[0][1])/1000.0))),c) + return plt.plot(d, **kargs) + +#PacketList.timeskew_graph = types.MethodType(_packetlist_timeskew_graph, None) + + +### Create a new packet list +class TracerouteResult(SndRcvList): + def __init__(self, res=None, name="Traceroute", stats=None): + PacketList.__init__(self, res, name, stats, vector_index = 1) + 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}"), + 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): + raw_trace = {} + for s,r in self.res: + if IP not in s: + continue + d = s[IP].dst + if d not in raw_trace: + raw_trace[d] = {} + raw_trace[d][s[IP].ttl] = r[IP].src, ICMP not in r + + trace = {} + for k in raw_trace.keys(): + m = [ x for x in raw_trace[k].keys() if raw_trace[k][x][1] ] + if not m: + trace[k] = raw_trace[k] + else: + m = min(m) + trace[k] = {i: raw_trace[k][i] for i in raw_trace[k].keys() if not raw_trace[k][i][1] or i<=m} + + 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 = next(forecol) + 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 + +## world_trace needs to be reimplemented as gnuplot dependency is removed +# 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 trace_id in ports_done: +# 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 n in trace: + trace[n] = next(unknown_label) + if not rtk in ports_done: + 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 = next(backcolorlist) + 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" % repr(rtk) + s += '\t\tedge [color="#%s%s%s"];\n' % next(forecolorlist) + 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") + format: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option + figsize: w,h tuple in inches. See matplotlib documentation + target: filename. If None uses matplotlib to display + 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 = next(iter(Net(ip))) + 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 = (bytes(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:]))) + classes = [idlst[0]]+list(map(lambda x:x[1],filter(lambda a: abs(a[0]-a[1])>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,end=" ") + + +# 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-python3-0.18/scapy/layers/inet6.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/inet6.py new file mode 100644 index 00000000..c2e4a037 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/layers/inet6.py @@ -0,0 +1,3047 @@ +#! /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 hasattr(socket, "IPPROTO_IPIP"): + socket.IPPROTO_IPIP = 4 + +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), range(8, 129)) + tuple = [ x for x in range(8, 129) if m8(x) ] + + 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) & (0xff<>(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 range(*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 = b"" + 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 = b'' + 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]) + t = 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.maxsize): # 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) + return _mip6_mhtype2cls.get(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 = next(iter(dst)) + 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 = bytes(selfup) + s2 = bytes(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 = bytes(selfup) + s2 = bytes(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 = bytes(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 = b"" + 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 + o = x[0] # Option type + cls = self.cls + if o in _hbhoptcls: + 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 = b"" + 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 b"".join(map(bytes, x)) + + curpos = self.curpos + s = b"" + for p in x: + d = p.alignment_delta(curpos) + curpos += d + if d == 1: + s += bytes(Pad1()) + elif d != 0: + s += bytes(PadN(optdata=b'\x00'*(d-2))) + pstr = bytes(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 += bytes(Pad1()) + elif d != 0: + s += bytes(PadN(optdata=b'\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 = [ x for x in pktlist if IPv6ExtHdrFragment in x ] # remove non fragments + if not l: + return [] + + id = l[0][IPv6ExtHdrFragment].id + + llen = len(l) + l = [ x for x in l if x[IPv6ExtHdrFragment].id == id ] + 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 = b"" + 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 += b"X"*(offset - len(fragmentable)) + fragmentable += bytes(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(bytes(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(bytes(pkt[IPv6ExtHdrFragment])) > 65535: + warning("An IPv6 packet can'be bigger than 65535, please use a Jumbo payload.") + return [] + + s = bytes(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 = bytes(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 ? + return get_cls(icmp6ndoptscls.get(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",0xffffffff), + XIntField("preferredlifetime",0xffffffff), + 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 = bytes(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", b"\x00"*6, 6), + TruncPktLenField("pkt", b"", 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 += b'\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 b"" + if l in [2, 3]: + return x[:8*(l-1)] + + return x + b'\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.getbyteval("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.getbyteval("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.encode('ascii') + x = [x.encode('ascii')] + elif type(x) is bytes: + if x and x[-1] == 0: + return x + x = [x] + + res = [] + for n in x: + if type(n) is str: + n = n.encode('ascii') + termin = b"\x00" + if n.count(b'.') == 0: # single-component gets one more + termin += bytes([0]) + n = b"".join(map(lambda y: chr(len(y)).encode('ascii')+y, n.split(b"."))) + termin + res.append(n) + return b"".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 = b"" + if type(x) is str: + x = x.encode('ascii') + while x: + #l = ord(x[0]) + l = x[0] + x = x[1:] + if l == 0: + if cur and cur[-1] == ord('.'): + cur = cur[:-1] + res.append(cur) + cur = b"" + #if x and ord(x[0]) == 0: # single component + if x and 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]+b"." + 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 = b"" + 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]) + l = 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, b"") + 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 b"", (1, s) + + def addfield(self, pkt, s, val): + if ((type(val) is tuple and val[1] is None) or + val is None): + val = (1, b"") + t = val[0] + if t == 1: + if type(val[1]) is str: + tmp = val[1].encode('ascii') + else: + tmp = val[1] + return s + tmp + 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.encode('ascii') + x = [x] + elif type(x) is bytes: + 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 or type(x) is bytes: + 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 or type(x) is bytes: + return (0, x) + return x + + return (qtype, list(map(addttl, x))) + + return (qtype, x) + + + def addfield(self, pkt, s, val): + t,tmp = val + if tmp is None: + tmp = b"" + 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)) + return s + b"".join(map(lambda a: struct.pack("!I", a[0])+inet_pton(socket.AF_INET6, a[1]), tmp)) + elif t == 4: + #return s + "".join(map(lambda (x,y): struct.pack("!I", x)+inet_pton(socket.AF_INET, y), tmp)) + return s + b"".join(map(lambda a: struct.pack("!I", a[0])+inet_pton(socket.AF_INET, a[1]), tmp)) + else: + return s + tmp + + def getfield(self, pkt, s): + code = getattr(pkt, "code") + if code != 0: + return s, (0, b"") + + qtype = getattr(pkt, "qtype") + if qtype == 0: # NOOP + return s, (0, b"") + + elif qtype == 2: + if len(s) < 4: + return s, (0, b"") + ttl = struct.unpack("!I", s[:4])[0] + return b"", (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 b"", (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 "[ %s ]" % (", ".join(map(lambda a: "(%d, %s)" % a, 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]) + type = 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]) + code = 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 = bytes([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", b"\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 + o = x[0] # Option type + cls = self.cls + if o in moboptcls: + 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 = b"" + 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 b"".join(map(str, x)) + + curpos = self.curpos + s = b"" + for p in x: + d = p.alignment_delta(curpos) + curpos += d + if d == 1: + s += bytes(Pad1()) + elif d != 0: + s += bytes(PadN(optdata=b'\x00'*(d-2))) + pstr = bytes(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 +=bytes(Pad1()) + elif d != 0: + s += bytes(PadN(optdata=b'\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 b"\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 ! + 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()) + m = [ x for x in k.keys() if k[x][1] ] + 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-python3-0.18/scapy/layers/ipsec.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/ipsec.py new file mode 100644 index 00000000..a14925fb --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/layers/ipsec.py @@ -0,0 +1,995 @@ +############################################################################# +## 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(b'testdata') +>>> p = IP(bytes(p)) +>>> p +>> +>>> +>>> e = sa.encrypt(p) +>>> e +> +>>> +>>> d = sa.decrypt(e) +>>> d +>> +>>> +>>> d == p +True +""" + +import socket + +if not hasattr(socket, 'IPPROTO_AH'): + socket.IPPROTO_AH = 51 +if not hasattr(socket, 'IPPROTO_ESP'): + socket.IPPROTO_ESP = 50 + + +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 bytes(self.data) + self.padding + chr(self.padlen).encode('ascii') + chr(self.nh).encode('ascii') + +#------------------------------------------------------------------------------ +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 type(key) is str: + key = key.encode('ascii') + 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 += bytes([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]) + padlen = (data[-2]) + #nh = ord(data[-1]) + nh = (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 type(key) is str: + key = key.encode('ascii') + if self.mac is XCBCMAC: + # specific case here, ciphermod instead of digestmod + return self.mac.new(key, ciphermod=self.digestmod) + else: + print(self.mac) + 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(bytes(pkt[ESP])) + pkt[ESP].data += mac.digest()[:self.icv_size] + + elif pkt.haslayer(AH): + clone = zero_mutable_fields(pkt.copy(), sending=True) + mac.update(bytes(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(bytes(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(bytes(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, str): + 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__(bytes(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__(bytes(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__(bytes(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__(bytes(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__(bytes(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__(bytes(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-python3-0.18/scapy/layers/ir.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/ir.py new file mode 100644 index 00000000..90935aa3 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/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", 0xffffffff), + 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-python3-0.18/scapy/layers/isakmp.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/isakmp.py new file mode 100644 index 00000000..97def8f5 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/layers/isakmp.py @@ -0,0 +1,355 @@ +## 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)): + def type2num(self, typval): + typ = typval[0] + val = typval[1] + type_val,enc_dict,tlv = ISAKMPTransformTypes.get(typval[0], (typval[0],{},0)) + val = enc_dict.get(val, val) + s = b"" + if (val & ~0xffff): + if not tlv: + warning("%r should not be TLV but is too big => using TLV encoding" % typval[0]) + n = 0 + while val: + s = bytes([(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 b"" + i = map(self.type2num, i) + return b"".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] + r = 0 + for i in struct.unpack("!%s" % ("B"*len(value),), value): + r = (r << 8) | i + value = r + #value = reduce(lambda x,y: (x<<8)|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]+bytes([((l>>8)&0xff),(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-python3-0.18/scapy/layers/l2.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/l2.py new file mode 100644 index 00000000..0d0a1c78 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/layers/l2.py @@ -0,0 +1,543 @@ +## 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 = next(iter(ip)) + ip = inet_ntoa(inet_aton(ip)) + tmp = 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() + return bytes([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]+bytes([((l>>8)&0xff),(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 = next(iter(dst)) + return conf.route.route(dst) + def extract_padding(self, s): + return b"",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]+bytes([((c>>8)&0xff),(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,b".") + 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 a: conf.padding_layer in a[1] and hexstr(a[1][conf.padding_layer].load), + filter="arp", **kargs) + + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/layers/l2tp.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/l2tp.py new file mode 100644 index 00000000..0b56db21 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/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-python3-0.18/scapy/layers/llmnr.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/llmnr.py new file mode 100644 index 00000000..65ecad41 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/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-python3-0.18/scapy/layers/mgcp.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/mgcp.py new file mode 100644 index 00000000..5d8a064e --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/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-python3-0.18/scapy/layers/mobileip.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/mobileip.py new file mode 100644 index 00000000..bbaa8ce7 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/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-python3-0.18/scapy/layers/netbios.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/netbios.py new file mode 100644 index 00000000..f06e9307 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/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,{0:"B node",1:"P node",2:"M node",3:"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,{0:"B node",1:"P node",2:"M node",3:"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",b"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",b"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-python3-0.18/scapy/layers/netflow.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/netflow.py new file mode 100644 index 00000000..44567737 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/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-python3-0.18/scapy/layers/ntp.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/ntp.py new file mode 100644 index 00000000..6d11966c --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/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-python3-0.18/scapy/layers/pflog.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/pflog.py new file mode 100644 index 00000000..a8fc9fe0 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/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-python3-0.18/scapy/layers/ppp.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/ppp.py new file mode 100644 index 00000000..08cf62cd --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/layers/ppp.py @@ -0,0 +1,349 @@ +## 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] == 0xff: + 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", b"", length_from=lambda p:max(0,p.len-2)) ] + def extract_padding(self, pay): + return b"",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]) + o = (_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 b"",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]) + o = (_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-python3-0.18/scapy/layers/radius.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/radius.py new file mode 100644 index 00000000..13239603 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/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-python3-0.18/scapy/layers/rip.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/rip.py new file mode 100644 index 00000000..1507fe5c --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/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-python3-0.18/scapy/layers/rtp.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/rtp.py new file mode 100644 index 00000000..629dccdd --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/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-python3-0.18/scapy/layers/sctp.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/sctp.py new file mode 100644 index 00000000..57712112 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/layers/sctp.py @@ -0,0 +1,439 @@ +## 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>>8) ^ crc32c_table[(crc^(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 = 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]) + t = (m[0]) * 256 + (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 b"",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", b"", + length_from=lambda pkt: pkt.len-4), + 4, padwith=b"\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", b"", + length_from=lambda pkt: pkt.len-4), + 4, padwith=b"\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", b"", + length_from=lambda pkt: pkt.len-4), + 4, padwith=b"\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", b"", + length_from=lambda pkt: pkt.len-4), + 4, padwith=b"\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=b"\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=b"\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", b"", length_from=lambda pkt: pkt.len-4), + 4, padwith=b"\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", b"", length_from=lambda pkt: pkt.len-4), + 4, padwith=b"\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", b"", length_from=lambda pkt: pkt.len-4), + 4, padwith=b"\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-python3-0.18/scapy/layers/sebek.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/sebek.py new file mode 100644 index 00000000..c54e6728 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/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-python3-0.18/scapy/layers/skinny.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/skinny.py new file mode 100644 index 00000000..9fb6ac06 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/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-python3-0.18/scapy/layers/smb.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/smb.py new file mode 100644 index 00000000..f8e0da7a --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/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",0xD6228000), + 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",0xD6228000), + 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",0xD6228000), + 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-python3-0.18/scapy/layers/snmp.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/snmp.py new file mode 100644 index 00000000..dddd4e27 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/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",b"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=b"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-python3-0.18/scapy/layers/tftp.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/tftp.py new file mode 100644 index 00000000..1535e99c --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/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-python3-0.18/scapy/layers/vrrp.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/vrrp.py new file mode 100644 index 00000000..e2818381 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/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]+bytes([(ck>>8),(ck&0xff)])+p[8:] + return p + +bind_layers( IP, VRRP, proto=IPPROTO_VRRP) diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/layers/x509.py b/scripts/external_libs/scapy-python3-0.18/scapy/layers/x509.py new file mode 100644 index 00000000..18aaa5e3 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/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","") + ) + + + + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/main.py b/scripts/external_libs/scapy-python3-0.18/scapy/main.py new file mode 100644 index 00000000..47805443 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/main.py @@ -0,0 +1,380 @@ +## 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 + +""" +Main module for interactive startup. +""" + +import os,sys,socket +import glob +import builtins +import types +import gzip +from .error import * +from . import utils + + +def _probe_config_file(cf): + cf_path = os.path.join(os.path.expanduser("~"), cf) + try: + os.stat(cf_path) + except OSError: + return None + else: + return cf_path + +def _read_config_file(cf): + log_loading.debug("Loading config file [%s]" % cf) + try: + exec(open(cf).read()) + except IOError as e: + log_loading.warning("Cannot read config file [%s] [%s]" % (cf,e)) + except Exception as e: + log_loading.exception("Error during evaluation of config file [%s]" % cf) + + +DEFAULT_PRESTART_FILE = _probe_config_file(".scapy_prestart.py") +DEFAULT_STARTUP_FILE = _probe_config_file(".scapy_startup.py") + +def _usage(): + print("""Usage: scapy.py [-s sessionfile] [-c new_startup_file] [-p new_prestart_file] [-C] [-P] + -C: do not read startup file + -P: do not read pre-startup file""") + sys.exit(0) + + +from .config import conf +from .themes import DefaultTheme + + +###################### +## Extension system ## +###################### + + +def _load(module): + try: + mod = __import__(module,globals(),locals(),".") + builtins.__dict__.update(mod.__dict__) + except Exception as e: + log_interactive.error(e) + +def load_module(name): + _load("scapy.modules."+name) + +def load_layer(name): + _load("scapy.layers."+name) + +def load_contrib(name): + _load("scapy.contrib."+name) + +def list_contrib(name=None): + if name is None: + name="*.py" + elif "*" not in name and "?" not in name and not name.endswith(".py"): + name += ".py" + name = os.path.join(os.path.dirname(__file__), "contrib", name) + for f in glob.glob(name): + mod = os.path.basename(f) + if mod.startswith("__"): + continue + if mod.endswith(".py"): + mod = mod[:-3] + desc = { "description":"-", "status":"?", "name":mod } + for l in open(f): + p = l.find("scapy.contrib.") + if p >= 0: + p += 14 + q = l.find("=", p) + key = l[p:q].strip() + value = l[q+1:].strip() + desc[key] = value + print("%(name)-20s: %(description)-40s status=%(status)s" % desc) + + + + + + +############################## +## Session saving/restoring ## +############################## + + +def save_session(fname=None, session=None, pickleProto=4): + import dill as pickle + + if fname is None: + fname = conf.session + if not fname: + conf.session = fname = utils.get_temp_file(keep=True) + log_interactive.info("Use [%s] as session file" % fname) + if session is None: + session = builtins.__dict__["scapy_session"] + + to_be_saved = session.copy() + + for k in list(to_be_saved.keys()): + if k in ["__builtins__", "In", "Out", "conf"] or k.startswith("_") or \ + (hasattr(to_be_saved[k], "__module__") and str(to_be_saved[k].__module__).startswith('IPython')): + del(to_be_saved[k]) + continue + if type(to_be_saved[k]) in [type, types.ModuleType, types.MethodType]: + log_interactive.info("[%s] (%s) can't be saved." % (k, type(to_be_saved[k]))) + del(to_be_saved[k]) + + try: + os.rename(fname, fname+".bak") + except OSError: + pass + f=gzip.open(fname,"wb") + for i in to_be_saved.keys(): + #d = {i: to_be_saved[i]} + #pickle.dump(d, f, pickleProto) + pickle.dump(to_be_saved, f, pickleProto) + f.close() + +def load_session(fname=None): + if conf.interactive_shell.lower() == "ipython": + log_interactive.error("There are issues with load_session in ipython. Use python for interactive shell, or use -s parameter to load session") + return + + import dill as pickle + + if fname is None: + fname = conf.session + try: + s = pickle.load(gzip.open(fname,"rb")) + except IOError: + s = pickle.load(open(fname,"rb")) + scapy_session = builtins.__dict__["scapy_session"] + scapy_session.clear() + scapy_session.update(s) + +def update_session(fname=None): + import dill as pickle + if fname is None: + fname = conf.session + try: + s = pickle.load(gzip.open(fname,"rb")) + except IOError: + s = pickle.load(open(fname,"rb")) + scapy_session = builtins.__dict__["scapy_session"] + scapy_session.update(s) + + +################ +##### Main ##### +################ + +def scapy_delete_temp_files(): + for f in conf.temp_files: + try: + os.unlink(f) + except: + pass + +def scapy_write_history_file(readline): + if conf.histfile: + try: + readline.write_history_file(conf.histfile) + except IOError as e: + try: + warning("Could not write history to [%s]\n\t (%s)" % (conf.histfile,e)) + tmp = utils.get_temp_file(keep=True) + readline.write_history_file(tmp) + warning("Wrote history to [%s]" % tmp) + except: + warning("Cound not write history to [%s]. Discarded" % tmp) + + +def interact(mydict=None,argv=None,mybanner=None,loglevel=20): + global session + import code,sys,pickle,os,getopt,re + from .config import conf + conf.interactive = True + if loglevel is not None: + conf.logLevel=loglevel + + the_banner = "Welcome to Scapy (%s)" + if mybanner is not None: + the_banner += "\n" + the_banner += mybanner + + if argv is None: + argv = sys.argv + + import atexit + try: + import rlcompleter,readline + except ImportError: + log_loading.info("Can't load Python libreadline or completer") + READLINE=0 + else: + READLINE=1 + class ScapyCompleter(rlcompleter.Completer): + def global_matches(self, text): + matches = [] + n = len(text) + for lst in [dir(builtins), session.keys()]: + for word in lst: + if word[:n] == text and word != "__builtins__": + matches.append(word) + return matches + + + def attr_matches(self, text): + m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text) + if not m: + return + expr, attr = m.group(1, 3) + try: + object = eval(expr) + except: + object = eval(expr, session) + if isinstance(object, Packet) or isinstance(object, Packet_metaclass): + #words = filter(lambda x: x[0]!="_",dir(object)) + words = [ x for x in dir(object) if x[0]!="_" ] + words += [x.name for x in object.fields_desc] + else: + words = dir(object) + if hasattr( object,"__class__" ): + words = words + rlcompleter.get_class_members(object.__class__) + matches = [] + n = len(attr) + for word in words: + if word[:n] == attr and word != "__builtins__": + matches.append("%s.%s" % (expr, word)) + return matches + + readline.set_completer(ScapyCompleter().complete) + readline.parse_and_bind("C-o: operate-and-get-next") + readline.parse_and_bind("tab: complete") + + + session=None + session_name="" + STARTUP_FILE = DEFAULT_STARTUP_FILE + PRESTART_FILE = DEFAULT_PRESTART_FILE + + + iface = None + try: + opts=getopt.getopt(argv[1:], "hs:Cc:Pp:d") + for opt, parm in opts[0]: + if opt == "-h": + _usage() + elif opt == "-s": + session_name = parm + elif opt == "-c": + STARTUP_FILE = parm + elif opt == "-C": + STARTUP_FILE = None + elif opt == "-p": + PRESTART_FILE = parm + elif opt == "-P": + PRESTART_FILE = None + elif opt == "-d": + conf.logLevel = max(1,conf.logLevel-10) + + if len(opts[1]) > 0: + raise getopt.GetoptError("Too many parameters : [%s]" % " ".join(opts[1])) + + + except getopt.GetoptError as msg: + log_loading.error(msg) + sys.exit(1) + + if PRESTART_FILE: + _read_config_file(PRESTART_FILE) + + scapy_builtins = __import__("scapy.all",globals(),locals(),".").__dict__ + builtins.__dict__.update(scapy_builtins) + globkeys = list(scapy_builtins.keys()) + globkeys.append("scapy_session") + scapy_builtins=None # XXX replace with "with" statement + if mydict is not None: + builtins.__dict__.update(mydict) + globkeys += mydict.keys() + + + conf.color_theme = DefaultTheme() + if STARTUP_FILE: + _read_config_file(STARTUP_FILE) + + if session_name: + try: + os.stat(session_name) + except OSError: + log_loading.info("New session [%s]" % session_name) + else: + try: + try: + session = pickle.load(gzip.open(session_name,"rb")) + except IOError: + session = pickle.load(open(session_name,"rb")) + log_loading.info("Using session [%s]" % session_name) + except EOFError: + log_loading.error("Error opening session [%s]" % session_name) + except AttributeError: + log_loading.error("Error opening session [%s]. Attribute missing" % session_name) + + if session: + if "conf" in session: + conf.configure(session["conf"]) + session["conf"] = conf + else: + conf.session = session_name + session={"conf":conf} + + else: + session={"conf": conf} + + builtins.__dict__["scapy_session"] = session + + + if READLINE: + if conf.histfile: + try: + readline.read_history_file(conf.histfile) + except IOError: + pass + atexit.register(scapy_write_history_file,readline) + + atexit.register(scapy_delete_temp_files) + + IPYTHON=False + if conf.interactive_shell.lower() == "ipython": + try: + import IPython + IPYTHON=True + except ImportError as e: + log_loading.warning("IPython not available. Using standard Python shell instead.") + IPYTHON=False + + if IPYTHON: + banner = the_banner % (conf.version) + " using IPython %s" % IPython.__version__ + + if conf.ipython_embedded: + IPython.embed(user_ns=session, banner2=banner) + else: + IPython.start_ipython(argv=[], user_ns=session) + + else: + code.interact(banner = the_banner % (conf.version), + local=session, readfunc=conf.readfunc) + + if conf.session: + save_session(conf.session, session) + + + for k in globkeys: + try: + del(builtins.__dict__[k]) + except: + pass + +if __name__ == "__main__": + interact() diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/modules/__init__.py b/scripts/external_libs/scapy-python3-0.18/scapy/modules/__init__.py new file mode 100644 index 00000000..6303dad0 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/modules/__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 + +""" +Package of extension modules that have to be loaded explicitly. +""" diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/modules/geoip.py b/scripts/external_libs/scapy-python3-0.18/scapy/modules/geoip.py new file mode 100644 index 00000000..7396fe96 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/modules/geoip.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 + +""" +GeoIP: find out the geographical location of IP addresses +""" + +from scapy.data import KnowledgeBase +from scapy.config import conf + +conf.IPCountry_base = "GeoIPCountry4Scapy.gz" +conf.countryLoc_base = "countryLoc.csv" + +########################## +## IP location database ## +########################## + +class IPCountryKnowledgeBase(KnowledgeBase): + """ +How to generate the base : +db = [] +for l in open("GeoIPCountryWhois.csv").readlines(): + s,e,c = l.split(",")[2:5] + db.append((int(s[1:-1]),int(e[1:-1]),c[1:-1])) +cPickle.dump(gzip.open("xxx","w"),db) +""" + def lazy_init(self): + self.base = load_object(self.filename) + + +class CountryLocKnowledgeBase(KnowledgeBase): + def lazy_init(self): + f=open(self.filename) + self.base = {} + while 1: + l = f.readline() + if not l: + break + l = l.strip().split(",") + if len(l) != 3: + continue + c,lat,long = l + + self.base[c] = (float(long),float(lat)) + f.close() + + + +@conf.commands.register +def locate_ip(ip): + """Get geographic coordinates from IP using geoip database""" + ip=map(int,ip.split(".")) + ip = ip[3]+(ip[2]<<8)+(ip[1]<<16)+(ip[0]<<24) + + cloc = country_loc_kdb.get_base() + db = IP_country_kdb.get_base() + + d=0 + f=len(db)-1 + while (f-d) > 1: + guess = (d+f)/2 + if ip > db[guess][0]: + d = guess + else: + f = guess + s,e,c = db[guess] + if s <= ip and ip <= e: + return cloc.get(c,None) + + + + + +conf.IP_country_kdb = IPCountryKnowledgeBase(conf.IPCountry_base) +conf.country_loc_kdb = CountryLocKnowledgeBase(conf.countryLoc_base) diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/modules/nmap.py b/scripts/external_libs/scapy-python3-0.18/scapy/modules/nmap.py new file mode 100644 index 00000000..07ec7a93 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/modules/nmap.py @@ -0,0 +1,215 @@ +## 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 + +""" +Clone of Nmap's first generation OS fingerprinting. +""" + +import os + +from scapy.data import KnowledgeBase +from scapy.config import conf +from scapy.arch import WINDOWS + + +if WINDOWS: + conf.nmap_base=os.environ["ProgramFiles"] + "\\nmap\\nmap-os-fingerprints" +else: + conf.nmap_base ="/usr/share/nmap/nmap-os-fingerprints" + + +###################### +## nmap OS fp stuff ## +###################### + + +class NmapKnowledgeBase(KnowledgeBase): + def lazy_init(self): + try: + f=open(self.filename) + except IOError: + return + + self.base = [] + name = None + try: + for l in f: + l = l.strip() + if not l or l[0] == "#": + continue + if l[:12] == "Fingerprint ": + if name is not None: + self.base.append((name,sig)) + name = l[12:].strip() + sig={} + p = self.base + continue + elif l[:6] == "Class ": + continue + op = l.find("(") + cl = l.find(")") + if op < 0 or cl < 0: + warning("error reading nmap os fp base file") + continue + test = l[:op] + s = map(lambda x: x.split("="), l[op+1:cl].split("%")) + si = {} + for n,v in s: + si[n] = v + sig[test]=si + if name is not None: + self.base.append((name,sig)) + except: + self.base = None + warning("Can't read nmap database [%s](new nmap version ?)" % self.filename) + f.close() + +nmap_kdb = NmapKnowledgeBase(conf.nmap_base) + +def TCPflags2str(f): + fl="FSRPAUEC" + s="" + for i in range(len(fl)): + if f & 1: + s = fl[i]+s + f >>= 1 + return s + +def nmap_tcppacket_sig(pkt): + r = {} + if pkt is not None: +# r["Resp"] = "Y" + r["DF"] = (pkt.flags & 2) and "Y" or "N" + r["W"] = "%X" % pkt.window + r["ACK"] = pkt.ack==2 and "S++" or pkt.ack==1 and "S" or "O" + r["Flags"] = TCPflags2str(pkt.payload.flags) + r["Ops"] = "".join(map(lambda x: x[0][0],pkt.payload.options)) + else: + r["Resp"] = "N" + return r + + +def nmap_udppacket_sig(S,T): + r={} + if T is None: + r["Resp"] = "N" + else: + r["DF"] = (T.flags & 2) and "Y" or "N" + r["TOS"] = "%X" % T.tos + r["IPLEN"] = "%X" % T.len + r["RIPTL"] = "%X" % T.payload.payload.len + r["RID"] = S.id == T.payload.payload.id and "E" or "F" + r["RIPCK"] = S.chksum == T.getlayer(IPerror).chksum and "E" or T.getlayer(IPerror).chksum == 0 and "0" or "F" + r["UCK"] = S.payload.chksum == T.getlayer(UDPerror).chksum and "E" or T.getlayer(UDPerror).chksum ==0 and "0" or "F" + r["ULEN"] = "%X" % T.getlayer(UDPerror).len + r["DAT"] = T.getlayer(conf.raw_layer) is None and "E" or S.getlayer(conf.raw_layer).load == T.getlayer(conf.raw_layer).load and "E" or "F" + return r + + + +def nmap_match_one_sig(seen, ref): + c = 0 + for k in seen.keys(): + if k in ref: + if seen[k] in ref[k].split("|"): + c += 1 + if c == 0 and seen.get("Resp") == "N": + return 0.7 + else: + return 1.0*c/len(seen.keys()) + + +def nmap_sig(target, oport=80, cport=81, ucport=1): + res = {} + + tcpopt = [ ("WScale", 10), + ("NOP",None), + ("MSS", 256), + ("Timestamp",(123,0)) ] + tests = [ IP(dst=target, id=1)/TCP(seq=1, sport=5001, dport=oport, options=tcpopt, flags="CS"), + IP(dst=target, id=1)/TCP(seq=1, sport=5002, dport=oport, options=tcpopt, flags=0), + IP(dst=target, id=1)/TCP(seq=1, sport=5003, dport=oport, options=tcpopt, flags="SFUP"), + IP(dst=target, id=1)/TCP(seq=1, sport=5004, dport=oport, options=tcpopt, flags="A"), + IP(dst=target, id=1)/TCP(seq=1, sport=5005, dport=cport, options=tcpopt, flags="S"), + IP(dst=target, id=1)/TCP(seq=1, sport=5006, dport=cport, options=tcpopt, flags="A"), + IP(dst=target, id=1)/TCP(seq=1, sport=5007, dport=cport, options=tcpopt, flags="FPU"), + IP(str(IP(dst=target)/UDP(sport=5008,dport=ucport)/(300*"i"))) ] + + ans, unans = sr(tests, timeout=2) + ans += map(lambda x: (x,None), unans) + + for S,T in ans: + if S.sport == 5008: + res["PU"] = nmap_udppacket_sig(S,T) + else: + t = "T%i" % (S.sport-5000) + if T is not None and T.haslayer(ICMP): + warning("Test %s answered by an ICMP" % t) + T=None + res[t] = nmap_tcppacket_sig(T) + + return res + +def nmap_probes2sig(tests): + tests=tests.copy() + res = {} + if "PU" in tests: + res["PU"] = nmap_udppacket_sig(*tests["PU"]) + del(tests["PU"]) + for k in tests: + res[k] = nmap_tcppacket_sig(tests[k]) + return res + + +def nmap_search(sigs): + guess = 0,[] + for os,fp in nmap_kdb.get_base(): + c = 0.0 + for t in sigs.keys(): + if t in fp: + c += nmap_match_one_sig(sigs[t], fp[t]) + c /= len(sigs.keys()) + if c > guess[0]: + guess = c,[ os ] + elif c == guess[0]: + guess[1].append(os) + return guess + + +@conf.commands.register +def nmap_fp(target, oport=80, cport=81): + """nmap fingerprinting +nmap_fp(target, [oport=80,] [cport=81,]) -> list of best guesses with accuracy +""" + sigs = nmap_sig(target, oport, cport) + return nmap_search(sigs) + + +@conf.commands.register +def nmap_sig2txt(sig): + torder = ["TSeq","T1","T2","T3","T4","T5","T6","T7","PU"] + korder = ["Class", "gcd", "SI", "IPID", "TS", + "Resp", "DF", "W", "ACK", "Flags", "Ops", + "TOS", "IPLEN", "RIPTL", "RID", "RIPCK", "UCK", "ULEN", "DAT" ] + txt=[] + for i in sig.keys(): + if i not in torder: + torder.append(i) + for t in torder: + sl = sig.get(t) + if sl is None: + continue + s = [] + for k in korder: + v = sl.get(k) + if v is None: + continue + s.append("%s=%s"%(k,v)) + txt.append("%s(%s)" % (t, "%".join(s))) + return "\n".join(txt) + + + + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/modules/p0f.py b/scripts/external_libs/scapy-python3-0.18/scapy/modules/p0f.py new file mode 100644 index 00000000..289ef531 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/modules/p0f.py @@ -0,0 +1,549 @@ +## 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 + +""" +Clone of p0f passive OS fingerprinting +""" + +from scapy.data import KnowledgeBase +from scapy.config import conf +from scapy.error import warning +from scapy.layers.inet import IP, TCP, TCPOptions +from scapy.packet import NoPayload + +conf.p0f_base ="/etc/p0f/p0f.fp" +conf.p0fa_base ="/etc/p0f/p0fa.fp" +conf.p0fr_base ="/etc/p0f/p0fr.fp" +#conf.p0fo_base ="/etc/p0f/p0fo.fp" + + +############### +## p0f stuff ## +############### + +# File format (according to p0f.fp) : +# +# wwww:ttt:D:ss:OOO...:QQ:OS:Details +# +# wwww - window size +# ttt - initial TTL +# D - don't fragment bit (0=unset, 1=set) +# ss - overall SYN packet size +# OOO - option value and order specification +# QQ - quirks list +# OS - OS genre +# details - OS description + +class p0fKnowledgeBase(KnowledgeBase): + def __init__(self, filename): + KnowledgeBase.__init__(self, filename) + #self.ttl_range=[255] + def lazy_init(self): + try: + f=open(self.filename) + except IOError: + warning("Can't open base %s" % self.filename) + return + try: + self.base = [] + for l in f: + if l[0] in ["#","\n"]: + continue + l = tuple(l.split(":")) + if len(l) < 8: + continue + def a2i(x): + if x.isdigit(): + return int(x) + return x + li = [ a2i(i) for i in l[1:4] ] + #if li[0] not in self.ttl_range: + # self.ttl_range.append(li[0]) + # self.ttl_range.sort() + self.base.append((l[0], li[0], li[1], li[2], l[4], l[5], l[6], l[7][:-1])) + except: + warning("Can't parse p0f database (new p0f version ?)") + self.base = None + f.close() + +p0f_kdb = p0fKnowledgeBase(conf.p0f_base) +p0fa_kdb = p0fKnowledgeBase(conf.p0fa_base) +p0fr_kdb = p0fKnowledgeBase(conf.p0fr_base) +#p0fo_kdb = p0fKnowledgeBase(conf.p0fo_base) + +def p0f_selectdb(flags): + # tested flags: S, R, A + if flags & 0x16 == 0x2: + # SYN + return p0f_kdb + elif flags & 0x16 == 0x12: + # SYN/ACK + return p0fa_kdb + elif flags & 0x16 in [ 0x4, 0x14 ]: + # RST RST/ACK + return p0fr_kdb +# elif flags & 0x16 == 0x10: + # ACK +# return p0fo_kdb + else: + return None + +def packet2p0f(pkt): + pkt = pkt.copy() + pkt = pkt.__class__(bytes(pkt)) + while pkt.haslayer(IP) and pkt.haslayer(TCP): + pkt = pkt.getlayer(IP) + if isinstance(pkt.payload, TCP): + break + pkt = pkt.payload + + if not isinstance(pkt, IP) or not isinstance(pkt.payload, TCP): + raise TypeError("Not a TCP/IP packet") + #if pkt.payload.flags & 0x7 != 0x02: #S,!F,!R + # raise TypeError("Not a SYN or SYN/ACK packet") + + db = p0f_selectdb(pkt.payload.flags) + + #t = p0f_kdb.ttl_range[:] + #t += [pkt.ttl] + #t.sort() + #ttl=t[t.index(pkt.ttl)+1] + ttl = pkt.ttl + + df = (pkt.flags & 2) / 2 + ss = len(pkt) + # from p0f/config.h : PACKET_BIG = 100 + if ss > 100: + if db == p0fr_kdb: + # p0fr.fp: "Packet size may be wildcarded. The meaning of + # wildcard is, however, hardcoded as 'size > + # PACKET_BIG'" + ss = '*' + else: + ss = 0 +# if db == p0fo_kdb: + # p0fo.fp: "Packet size MUST be wildcarded." +# ss = '*' + + ooo = "" + mss = -1 + qqT = False + qqP = False + #qqBroken = False + ilen = (pkt.payload.dataofs << 2) - 20 # from p0f.c + for option in pkt.payload.options: + ilen -= 1 + if option[0] == "MSS": + ooo += "M" + str(option[1]) + "," + mss = option[1] + # FIXME: qqBroken + ilen -= 3 + elif option[0] == "WScale": + ooo += "W" + str(option[1]) + "," + # FIXME: qqBroken + ilen -= 2 + elif option[0] == "Timestamp": + if option[1][0] == 0: + ooo += "T0," + else: + ooo += "T," + if option[1][1] != 0: + qqT = True + ilen -= 9 + elif option[0] == "SAckOK": + ooo += "S," + ilen -= 1 + elif option[0] == "NOP": + ooo += "N," + elif option[0] == "EOL": + ooo += "E," + if ilen > 0: + qqP = True + else: + if type(option[0]) is str: + ooo += "?%i," % TCPOptions[1][option[0]] + else: + ooo += "?%i," % option[0] + # FIXME: ilen + ooo = ooo[:-1] + if ooo == "": ooo = "." + + win = pkt.payload.window + if mss != -1: + if mss != 0 and win % mss == 0: + win = "S" + str(win/mss) + elif win % (mss + 40) == 0: + win = "T" + str(win/(mss+40)) + win = str(win) + + qq = "" + + if db == p0fr_kdb: + if pkt.payload.flags & 0x10 == 0x10: + # p0fr.fp: "A new quirk, 'K', is introduced to denote + # RST+ACK packets" + qq += "K" + # The two next cases should also be only for p0f*r*, but although + # it's not documented (or I have not noticed), p0f seems to + # support the '0' and 'Q' quirks on any databases (or at the least + # "classical" p0f.fp). + if pkt.payload.seq == pkt.payload.ack: + # p0fr.fp: "A new quirk, 'Q', is used to denote SEQ number + # equal to ACK number." + qq += "Q" + if pkt.payload.seq == 0: + # p0fr.fp: "A new quirk, '0', is used to denote packets + # with SEQ number set to 0." + qq += "0" + if qqP: + qq += "P" + if pkt.id == 0: + qq += "Z" + if pkt.options != []: + qq += "I" + if pkt.payload.urgptr != 0: + qq += "U" + if pkt.payload.reserved != 0: + qq += "X" + if pkt.payload.ack != 0: + qq += "A" + if qqT: + qq += "T" +# if db == p0fo_kdb: +# if pkt.payload.flags & 0x20 != 0: + # U + # p0fo.fp: "PUSH flag is excluded from 'F' quirk checks" +# qq += "F" +# else: +# if pkt.payload.flags & 0x28 != 0: + # U or P + qq += "F" + #if db != p0fo_kdb and not isinstance(pkt.payload.payload, NoPayload): + if not isinstance(pkt.payload.payload, NoPayload): + # p0fo.fp: "'D' quirk is not checked for." + qq += "D" + # FIXME : "!" - broken options segment: not handled yet + + if qq == "": + qq = "." + + return (db, (win, ttl, df, ss, ooo, qq)) + +def p0f_correl(x,y): + d = 0 + # wwww can be "*" or "%nn". "Tnn" and "Snn" should work fine with + # the x[0] == y[0] test. + d += (x[0] == y[0] or y[0] == "*" or (y[0][0] == "%" and x[0].isdigit() and (int(x[0]) % int(y[0][1:])) == 0)) + # ttl + d += (y[1] >= x[1] and y[1] - x[1] < 32) + for i in [2, 5]: + d += (x[i] == y[i] or y[i] == '*') + # '*' has a special meaning for ss + d += x[3] == y[3] + xopt = x[4].split(",") + yopt = y[4].split(",") + if len(xopt) == len(yopt): + same = True + for i in range(len(xopt)): + if not (xopt[i] == yopt[i] or + (len(yopt[i]) == 2 and len(xopt[i]) > 1 and + yopt[i][1] == "*" and xopt[i][0] == yopt[i][0]) or + (len(yopt[i]) > 2 and len(xopt[i]) > 1 and + yopt[i][1] == "%" and xopt[i][0] == yopt[i][0] and + int(xopt[i][1:]) % int(yopt[i][2:]) == 0)): + same = False + break + if same: + d += len(xopt) + return d + + +@conf.commands.register +def p0f(pkt): + """Passive OS fingerprinting: which OS emitted this TCP packet ? +p0f(packet) -> accuracy, [list of guesses] +""" + db, sig = packet2p0f(pkt) + if db: + pb = db.get_base() + else: + pb = [] + if not pb: + warning("p0f base empty.") + return [] + #s = len(pb[0][0]) + r = [] + max = len(sig[4].split(",")) + 5 + for b in pb: + d = p0f_correl(sig,b) + if d == max: + r.append((b[6], b[7], b[1] - pkt[IP].ttl)) + return r + +def prnp0f(pkt): + # we should print which DB we use + try: + r = p0f(pkt) + except: + return + if r == []: + r = ("UNKNOWN", "[" + ":".join([ str(i) for i in packet2p0f(pkt)[1]]) + ":?:?]", None) + else: + r = r[0] + uptime = None + try: + uptime = pkt2uptime(pkt) + except: + pass + if uptime == 0: + uptime = None + res = pkt.sprintf("%IP.src%:%TCP.sport% - " + r[0] + " " + r[1]) + if uptime is not None: + res += pkt.sprintf(" (up: " + str(uptime//3600) + " hrs)\n -> %IP.dst%:%TCP.dport% (%TCP.flags%)") + else: + res += pkt.sprintf("\n -> %IP.dst%:%TCP.dport% (%TCP.flags%)") + if r[2] is not None: + res += " (distance " + str(r[2]) + ")" + print(res) + +@conf.commands.register +def pkt2uptime(pkt, HZ=100): + """Calculate the date the machine which emitted the packet booted using TCP timestamp +pkt2uptime(pkt, [HZ=100])""" + if not isinstance(pkt, Packet): + raise TypeError("Not a TCP packet") + if isinstance(pkt,NoPayload): + raise TypeError("Not a TCP packet") + if not isinstance(pkt, TCP): + return pkt2uptime(pkt.payload) + for opt in pkt.options: + if opt[0] == "Timestamp": + #t = pkt.time - opt[1][0] * 1.0/HZ + #return time.ctime(t) + t = opt[1][0] / HZ + return t + raise TypeError("No timestamp option") + +def p0f_impersonate(pkt, osgenre=None, osdetails=None, signature=None, + extrahops=0, mtu=1500, uptime=None): + """Modifies pkt so that p0f will think it has been sent by a +specific OS. If osdetails is None, then we randomly pick up a +personality matching osgenre. If osgenre and signature are also None, +we use a local signature (using p0f_getlocalsigs). If signature is +specified (as a tuple), we use the signature. + +For now, only TCP Syn packets are supported. +Some specifications of the p0f.fp file are not (yet) implemented.""" + pkt = pkt.copy() + #pkt = pkt.__class__(str(pkt)) + while pkt.haslayer(IP) and pkt.haslayer(TCP): + pkt = pkt.getlayer(IP) + if isinstance(pkt.payload, TCP): + break + pkt = pkt.payload + + if not isinstance(pkt, IP) or not isinstance(pkt.payload, TCP): + raise TypeError("Not a TCP/IP packet") + + if uptime is None: + uptime = random.randint(120,100*60*60*24*365) + + db = p0f_selectdb(pkt.payload.flags) + if osgenre: + pb = db.get_base() + if pb is None: + pb = [] + #pb = filter(lambda x: x[6] == osgenre, pb) + pb = [ x for x in pb if x[6] == osgenre ] + if osdetails: + #pb = filter(lambda x: x[7] == osdetails, pb) + pb = [ x for x in pb if x[7] == osdetails ] + elif signature: + pb = [signature] + else: + pb = p0f_getlocalsigs()[db] + if db == p0fr_kdb: + # 'K' quirk <=> RST+ACK + if pkt.payload.flags & 0x4 == 0x4: + #pb = filter(lambda x: 'K' in x[5], pb) + pb = [ x for x in pb if 'K' in x[5] ] + else: + #pb = filter(lambda x: 'K' not in x[5], pb) + pb = [ x for x in pb if 'K' not in x[5] ] + if not pb: + raise Scapy_Exception("No match in the p0f database") + pers = pb[random.randint(0, len(pb) - 1)] + + # options (we start with options because of MSS) + ## TODO: let the options already set if they are valid + options = [] + if pers[4] != '.': + for opt in pers[4].split(','): + if opt[0] == 'M': + # MSS might have a maximum size because of window size + # specification + if pers[0][0] == 'S': + maxmss = (2**16-1) / int(pers[0][1:]) + else: + maxmss = (2**16-1) + # If we have to randomly pick up a value, we cannot use + # scapy RandXXX() functions, because the value has to be + # set in case we need it for the window size value. That's + # why we use random.randint() + if opt[1:] == '*': + options.append(('MSS', random.randint(1,maxmss))) + elif opt[1] == '%': + coef = int(opt[2:]) + options.append(('MSS', coef*random.randint(1,maxmss/coef))) + else: + options.append(('MSS', int(opt[1:]))) + elif opt[0] == 'W': + if opt[1:] == '*': + options.append(('WScale', RandByte())) + elif opt[1] == '%': + coef = int(opt[2:]) + options.append(('WScale', coef*RandNum(min=1, + max=(2**8-1)/coef))) + else: + options.append(('WScale', int(opt[1:]))) + elif opt == 'T0': + options.append(('Timestamp', (0, 0))) + elif opt == 'T': + if 'T' in pers[5]: + # FIXME: RandInt() here does not work (bug (?) in + # TCPOptionsField.m2i often raises "OverflowError: + # long int too large to convert to int" in: + # oval = struct.pack(ofmt, *oval)" + # Actually, this is enough to often raise the error: + # struct.pack('I', RandInt()) + options.append(('Timestamp', (uptime, random.randint(1,2**32-1)))) + else: + options.append(('Timestamp', (uptime, 0))) + elif opt == 'S': + options.append(('SAckOK', '')) + elif opt == 'N': + options.append(('NOP', None)) + elif opt == 'E': + options.append(('EOL', None)) + elif opt[0] == '?': + if int(opt[1:]) in TCPOptions[0]: + optname = TCPOptions[0][int(opt[1:])][0] + optstruct = TCPOptions[0][int(opt[1:])][1] + options.append((optname, + struct.unpack(optstruct, + RandString(struct.calcsize(optstruct))._fix()))) + else: + options.append((int(opt[1:]), '')) + ## FIXME: qqP not handled + else: + warning("unhandled TCP option " + opt) + pkt.payload.options = options + + # window size + if pers[0] == '*': + pkt.payload.window = RandShort() + elif pers[0].isdigit(): + pkt.payload.window = int(pers[0]) + elif pers[0][0] == '%': + coef = int(pers[0][1:]) + pkt.payload.window = coef * RandNum(min=1,max=(2**16-1)/coef) + elif pers[0][0] == 'T': + pkt.payload.window = mtu * int(pers[0][1:]) + elif pers[0][0] == 'S': + ## needs MSS set + #MSS = filter(lambda x: x[0] == 'MSS', options) + MSS = [ x for x in options if x[0] == 'MSS' ] + if not MSS: + raise Scapy_Exception("TCP window value requires MSS, and MSS option not set") + pkt.payload.window = MSS[0][1] * int(pers[0][1:]) + else: + raise Scapy_Exception('Unhandled window size specification') + + # ttl + pkt.ttl = pers[1]-extrahops + # DF flag + pkt.flags |= (2 * pers[2]) + ## FIXME: ss (packet size) not handled (how ? may be with D quirk + ## if present) + # Quirks + if pers[5] != '.': + for qq in pers[5]: + ## FIXME: not handled: P, I, X, ! + # T handled with the Timestamp option + if qq == 'Z': pkt.id = 0 + elif qq == 'U': pkt.payload.urgptr = RandShort() + elif qq == 'A': pkt.payload.ack = RandInt() + elif qq == 'F': + #if db == p0fo_kdb: + # pkt.payload.flags |= 0x20 # U + #else: + pkt.payload.flags |= RandChoice(8, 32, 40) #P / U / PU + elif qq == 'D' and db != p0fo_kdb: + pkt /= conf.raw_layer(load=RandString(random.randint(1, 10))) # XXX p0fo.fp + elif qq == 'Q': pkt.payload.seq = pkt.payload.ack + #elif qq == '0': pkt.payload.seq = 0 + #if db == p0fr_kdb: + # '0' quirk is actually not only for p0fr.fp (see + # packet2p0f()) + if '0' in pers[5]: + pkt.payload.seq = 0 + elif pkt.payload.seq == 0: + pkt.payload.seq = RandInt() + + while pkt.underlayer: + pkt = pkt.underlayer + return pkt + +def p0f_getlocalsigs(): + """This function returns a dictionary of signatures indexed by p0f +db (e.g., p0f_kdb, p0fa_kdb, ...) for the local TCP/IP stack. + +You need to have your firewall at least accepting the TCP packets +from/to a high port (30000 <= x <= 40000) on your loopback interface. + +Please note that the generated signatures come from the loopback +interface and may (are likely to) be different than those generated on +"normal" interfaces.""" + pid = os.fork() + port = random.randint(30000, 40000) + if pid > 0: + # parent: sniff + result = {} + def addresult(res): + # TODO: wildcard window size in some cases? and maybe some + # other values? + if res[0] not in result: + result[res[0]] = [res[1]] + else: + if res[1] not in result[res[0]]: + result[res[0]].append(res[1]) + # XXX could we try with a "normal" interface using other hosts + iface = conf.route.route('127.0.0.1')[0] + # each packet is seen twice: S + RA, S + SA + A + FA + A + # XXX are the packets also seen twice on non Linux systems ? + count=14 + pl = sniff(iface=iface, filter='tcp and port ' + str(port), count = count, timeout=3) + map(addresult, map(packet2p0f, pl)) + os.waitpid(pid,0) + elif pid < 0: + log_runtime.error("fork error") + else: + # child: send + # XXX erk + time.sleep(1) + s1 = socket.socket(socket.AF_INET, type = socket.SOCK_STREAM) + # S & RA + try: + s1.connect(('127.0.0.1', port)) + except socket.error: + pass + # S, SA, A, FA, A + s1.bind(('127.0.0.1', port)) + s1.connect(('127.0.0.1', port)) + # howto: get an RST w/o ACK packet + s1.close() + os._exit(0) + return result + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/modules/queso.py b/scripts/external_libs/scapy-python3-0.18/scapy/modules/queso.py new file mode 100644 index 00000000..fbc7d06b --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/modules/queso.py @@ -0,0 +1,113 @@ +## 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 + +""" +Clone of queso OS fingerprinting +""" + +from scapy.data import KnowledgeBase +from scapy.config import conf +from scapy.layers.inet import IP,TCP +#from + +conf.queso_base ="/etc/queso.conf" + + +################# +## Queso stuff ## +################# + + +def quesoTCPflags(flags): + if flags == "-": + return "-" + flv = "FSRPAUXY" + v = 0 + for i in flags: + v |= 2**flv.index(i) + return "%x" % v + +class QuesoKnowledgeBase(KnowledgeBase): + def lazy_init(self): + try: + f = open(self.filename) + except IOError: + return + self.base = {} + p = None + try: + for l in f: + l = l.strip() + if not l or l[0] == ';': + continue + if l[0] == '*': + if p is not None: + p[""] = name + name = l[1:].strip() + p = self.base + continue + if l[0] not in list("0123456"): + continue + res = l[2:].split() + res[-1] = quesoTCPflags(res[-1]) + res = " ".join(res) + if not res in p: + p[res] = {} + p = p[res] + if p is not None: + p[""] = name + except: + self.base = None + warning("Can't load queso base [%s]", self.filename) + f.close() + + +queso_kdb = QuesoKnowledgeBase(conf.queso_base) + + +def queso_sig(target, dport=80, timeout=3): + p = queso_kdb.get_base() + ret = [] + for flags in ["S", "SA", "F", "FA", "SF", "P", "SEC"]: + ans, unans = sr(IP(dst=target)/TCP(dport=dport,flags=flags,seq=RandInt()), + timeout=timeout, verbose=0) + if len(ans) == 0: + rs = "- - - -" + else: + s,r = ans[0] + rs = "%i" % (r.seq != 0) + if not r.ack: + r += " 0" + elif r.ack-s.seq > 666: + rs += " R" % 0 + else: + rs += " +%i" % (r.ack-s.seq) + rs += " %X" % r.window + rs += " %x" % r.payload.flags + ret.append(rs) + return ret + +def queso_search(sig): + p = queso_kdb.get_base() + sig.reverse() + ret = [] + try: + while sig: + s = sig.pop() + p = p[s] + if "" in p: + ret.append(p[""]) + except KeyError: + pass + return ret + + +@conf.commands.register +def queso(*args,**kargs): + """Queso OS fingerprinting +queso(target, dport=80, timeout=3)""" + return queso_search(queso_sig(*args, **kargs)) + + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/modules/voip.py b/scripts/external_libs/scapy-python3-0.18/scapy/modules/voip.py new file mode 100644 index 00000000..70000a54 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/modules/voip.py @@ -0,0 +1,149 @@ +## 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 + +""" +VoIP (Voice over IP) related functions +""" + +import os +################### +## Testing stuff ## +################### + +from fcntl import fcntl +from scapy.sendrecv import sniff +from scapy.layers.inet import IP,UDP +from scapy.layers.rtp import RTP +from scapy.utils import get_temp_file + + +def merge(x,y,sample_size=2): + if len(x) > len(y): + y += "\x00"*(len(x)-len(y)) + elif len(x) < len(y): + x += "\x00"*(len(y)-len(x)) + m = "" + ss=sample_size + for i in range(len(x)/ss): + m += x[ss*i:ss*(i+1)]+y[ss*i:ss*(i+1)] + return m +# return "".join(map(str.__add__, x, y)) + + +def voip_play(s1,list=None,**kargs): + FIFO=get_temp_file() + FIFO1=FIFO % 1 + FIFO2=FIFO % 2 + + os.mkfifo(FIFO1) + os.mkfifo(FIFO2) + try: + os.system("soxmix -t .ul %s -t .ul %s -t ossdsp /dev/dsp &" % (FIFO1,FIFO2)) + + c1=open(FIFO1,"w", 4096) + c2=open(FIFO2,"w", 4096) + fcntl.fcntl(c1.fileno(),fcntl.F_SETFL, os.O_NONBLOCK) + fcntl.fcntl(c2.fileno(),fcntl.F_SETFL, os.O_NONBLOCK) + + # dsp,rd = os.popen2("sox -t .ul -c 2 - -t ossdsp /dev/dsp") + def play(pkt,last=[]): + if not pkt: + return + if not pkt.haslayer(UDP): + return + ip=pkt.getlayer(IP) + if s1 in [ip.src, ip.dst]: + if not last: + last.append(pkt) + return + load=last.pop() + # x1 = load.load[12:] + c1.write(load.load[12:]) + if load.getlayer(IP).src == ip.src: + # x2 = "" + c2.write("\x00"*len(load.load[12:])) + last.append(pkt) + else: + # x2 = pkt.load[:12] + c2.write(pkt.load[12:]) + # dsp.write(merge(x1,x2)) + + if list is None: + sniff(store=0, prn=play, **kargs) + else: + for p in list: + play(p) + finally: + os.unlink(FIFO1) + os.unlink(FIFO2) + + + +def voip_play1(s1,list=None,**kargs): + + + dsp,rd = os.popen2("sox -t .ul - -t ossdsp /dev/dsp") + def play(pkt): + if not pkt: + return + if not pkt.haslayer(UDP): + return + ip=pkt.getlayer(IP) + if s1 in [ip.src, ip.dst]: + dsp.write(pkt.getlayer(conf.raw_layer).load[12:]) + try: + if list is None: + sniff(store=0, prn=play, **kargs) + else: + for p in list: + play(p) + finally: + dsp.close() + rd.close() + +def voip_play2(s1,**kargs): + dsp,rd = os.popen2("sox -t .ul -c 2 - -t ossdsp /dev/dsp") + def play(pkt,last=[]): + if not pkt: + return + if not pkt.haslayer(UDP): + return + ip=pkt.getlayer(IP) + if s1 in [ip.src, ip.dst]: + if not last: + last.append(pkt) + return + load=last.pop() + x1 = load.load[12:] +# c1.write(load.load[12:]) + if load.getlayer(IP).src == ip.src: + x2 = "" +# c2.write("\x00"*len(load.load[12:])) + last.append(pkt) + else: + x2 = pkt.load[:12] +# c2.write(pkt.load[12:]) + dsp.write(merge(x1,x2)) + + sniff(store=0, prn=play, **kargs) + +def voip_play3(lst=None,**kargs): + dsp,rd = os.popen2("sox -t .ul - -t ossdsp /dev/dsp") + try: + def play(pkt, dsp=dsp): + if pkt and pkt.haslayer(UDP) and pkt.haslayer(conf.raw_layer): + dsp.write(pkt.getlayer(RTP).load) + if lst is None: + sniff(store=0, prn=play, **kargs) + else: + for p in lst: + play(p) + finally: + try: + dsp.close() + rd.close() + except: + pass + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/packet.py b/scripts/external_libs/scapy-python3-0.18/scapy/packet.py new file mode 100644 index 00000000..c92a27b1 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/packet.py @@ -0,0 +1,1360 @@ +## 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 + +""" +Packet class. Binding mechanism. fuzz() method. +""" + +import time,itertools,os +import sys,traceback +import copy +from .fields import StrField,ConditionalField,Emph,PacketListField +from .config import conf +from .base_classes import BasePacket,Gen,SetGen,Packet_metaclass,NewDefaultValues +from .volatile import VolatileValue +from .utils import import_hexcap,tex_escape,colgen,get_temp_file +from .error import Scapy_Exception,log_runtime, warning +import subprocess +import pprint + +class CGlobal: + ONCE =False + +try: + import pyx +except ImportError: + pass + + +class RawVal: + def __init__(self, val=b""): + assert type(val) == bytes + self.val = val + def __str__(self): + return str(self.val) + def __repr__(self): + return "" % self.val + def bytes(self): + return self.val + + +class CPacketRes: + pass; + +class Packet(BasePacket, metaclass = Packet_metaclass): + name=None + + fields_desc = [] + + aliastypes = [] + overload_fields = {} + + underlayer = None + + sent_time = None + payload_guess = [] + initialized = 0 + show_indent=1 + explicit = 0 + raw_packet_cache = None + + @classmethod + def from_hexcap(cls): + return cls(import_hexcap()) + + @classmethod + def upper_bonds(self): + for fval,upper in self.payload_guess: + print("%-20s %s" % (upper.__name__, ", ".join("%-12s" % ("%s=%r"%i) for i in fval.items()))) + + @classmethod + def lower_bonds(self): + for lower,fval in self.overload_fields.items(): + print("%-20s %s" % (lower.__name__, ", ".join("%-12s" % ("%s=%r"%i) for i in fval.items()))) + + def __init__(self, _pkt=b"", post_transform=None, _internal=0, _underlayer=None, **fields): + self.time = time.time() + self.sent_time = 0 + if self.name is None: + self.name = self.__class__.__name__ + self.aliastypes = [ self.__class__ ] + self.aliastypes + self.default_fields = {} + self.offset=0; # offset of the object + self.offset_fields = {} # ofsset of each field + self.overloaded_fields = {} + self.fields={} + self.fieldtype={} + self.packetfields=[] + self.__dict__["payload"] = NoPayload() + self.init_fields() + self.underlayer = _underlayer + self.initialized = 1 + self.original = _pkt + if _pkt: + self.dissect(_pkt) + if not _internal: + self.dissection_done(self) + for f in fields.keys(): + self.fields[f] = self.get_field(f).any2i(self,fields[f]) + if type(post_transform) is list: + self.post_transforms = post_transform + elif post_transform is None: + self.post_transforms = [] + else: + self.post_transforms = [post_transform] + + def init_fields(self): + self.do_init_fields(self.fields_desc) + + def do_init_fields(self, flist): + for f in flist: + self.default_fields[f.name] = copy.deepcopy(f.default) + self.fieldtype[f.name] = f + if f.holds_packets: + self.packetfields.append(f) + + def dissection_done(self,pkt): + """DEV: will be called after a dissection is completed""" + self.post_dissection(pkt) + self.payload.dissection_done(pkt) + + def post_dissection(self, pkt): + """DEV: is called after the dissection of the whole packet""" + pass + + def get_field(self, fld): + """DEV: returns the field instance from the name of the field""" + return self.fieldtype[fld] + + def add_payload(self, payload): + if payload is None: + return + elif not isinstance(self.payload, NoPayload): + self.payload.add_payload(payload) + else: + if isinstance(payload, Packet): + self.__dict__["payload"] = payload + payload.add_underlayer(self) + for t in self.aliastypes: + if t in payload.overload_fields: + self.overloaded_fields = payload.overload_fields[t] + break + #elif type(payload) is str: + elif type(payload) is bytes: + self.__dict__["payload"] = conf.raw_layer(load=payload) + else: + raise TypeError("payload must be either 'Packet' or 'bytes', not [%s]" % repr(payload)) + def remove_payload(self): + self.payload.remove_underlayer(self) + self.__dict__["payload"] = NoPayload() + self.overloaded_fields = {} + def add_underlayer(self, underlayer): + self.underlayer = underlayer + def remove_underlayer(self,other): + self.underlayer = None + def copy(self): + """Returns a deep copy of the instance.""" + clone = self.__class__() + clone.fields = self.fields.copy() + for k in clone.fields: + clone.fields[k] = self.get_field(k).do_copy(clone.fields[k]) + clone.default_fields = self.default_fields.copy() + clone.overloaded_fields = self.overloaded_fields.copy() + clone.overload_fields = self.overload_fields.copy() + clone.offset=self.offset + clone.underlayer = self.underlayer + clone.explicit = self.explicit + clone.raw_packet_cache = self.raw_packet_cache + clone.post_transforms = self.post_transforms[:] + clone.__dict__["payload"] = self.payload.copy() + clone.payload.add_underlayer(clone) + return clone + + def dump_offsets (self): + print ("obj-id {0} {1} {2}".format(id(self),self.name ,self.offset)) + if self.payload: + self.payload.dump_offsets() + + def getfieldval(self, attr): + if attr in self.fields: + return self.fields[attr] + if attr in self.overloaded_fields: + return self.overloaded_fields[attr] + if attr in self.default_fields: + return self.default_fields[attr] + return self.payload.getfieldval(attr) + + def getbyteval(self, attr): + fld,v = self.getfield_and_val(attr) + return fld.i2b(self, v) + + def getstrval(self, attr): + fld,v = self.getfield_and_val(attr) + return fld.i2repr(self, v) + + def getfield_and_val(self, attr): + if attr in self.fields: + return self.get_field(attr),self.fields[attr] + if attr in self.overloaded_fields: + return self.get_field(attr),self.overloaded_fields[attr] + if attr in self.default_fields: + return self.get_field(attr),self.default_fields[attr] + return self.payload.getfield_and_val(attr) + + def __getattr__(self, attr): + if self.initialized: + fld,v = self.getfield_and_val(attr) + if fld is not None: + return fld.i2h(self, v) + return v + raise AttributeError(attr) + + def setfieldval(self, attr, val): + if attr in self.default_fields: + fld = self.get_field(attr) + if fld is None: + any2i = lambda x,y: y + else: + any2i = fld.any2i + self.fields[attr] = any2i(self, val) + self.explicit = 0 + self.raw_packet_cache = None + elif attr == "payload": + self.remove_payload() + self.add_payload(val) + else: + self.payload.setfieldval(attr,val) + + def __setattr__(self, attr, val): + if self.initialized: + try: + self.setfieldval(attr,val) + except AttributeError: + pass + else: + return + self.__dict__[attr] = val + + def delfieldval(self, attr): + if attr in self.fields: + del(self.fields[attr]) + self.explicit = 0 # in case a default value must be explicited + self.raw_packet_cache = None + elif attr in self.default_fields: + pass + elif attr == "payload": + self.remove_payload() + else: + self.payload.delfieldval(attr) + + def __delattr__(self, attr): + if self.initialized: + try: + self.delfieldval(attr) + except AttributeError: + pass + else: + return + if attr in self.__dict__: + del(self.__dict__[attr]) + else: + raise AttributeError(attr) + + def __repr__(self): + s = "" + ct = conf.color_theme + for f in self.fields_desc: + if isinstance(f, ConditionalField) and not f._evalcond(self): + continue + if f.name in self.fields: + val = f.i2repr(self, self.fields[f.name]) + elif f.name in self.overloaded_fields: + val = f.i2repr(self, self.overloaded_fields[f.name]) + else: + continue + if isinstance(f, Emph) or f in conf.emph: + ncol = ct.emph_field_name + vcol = ct.emph_field_value + else: + ncol = ct.field_name + vcol = ct.field_value + + + s += " %s%s%s" % (ncol(f.name), + ct.punct("="), + vcol(val)) + return "%s%s %s %s%s%s"% (ct.punct("<"), + ct.layer_name(self.__class__.__name__), + s, + ct.punct("|"), + repr(self.payload), + ct.punct(">")) + #def __str__(self): + #TODO3 FIX + def __str__(self): + warning("Unless called manually, this could indicate deprecated use. Should be changed to bytes(self)") + return repr(bytes(self)) + def __bytes__(self): + return self.build() + def __div__(self, other): + if isinstance(other, Packet): + cloneA = self.copy() + cloneB = other.copy() + cloneA.add_payload(cloneB) + return cloneA + elif type(other) is str: + return self/conf.raw_layer(load=other.encode('ascii')) + elif type(other) is bytes: + return self/conf.raw_layer(load=other) + else: + return other.__rdiv__(self) + __truediv__ = __div__ + def __rdiv__(self, other): + if type(other) is str: + return conf.raw_layer(load=other.encode('ascii'))/self + if type(other) is bytes: + return conf.raw_layer(load=other)/self + else: + raise TypeError + __rtruediv__ = __rdiv__ + def __mul__(self, other): + if type(other) is int: + return [self]*other + else: + raise TypeError + def __rmul__(self,other): + return self.__mul__(other) + + def __nonzero__(self): + return True + + def __len__(self): + return len(bytes(self)) + + def dump_fields_offsets (self): + for f in self.fields_desc: + print ("field %-40s %02d %02d" % (f.name, f.offset,f.get_size_bytes () ) ); + + def self_build(self, field_pos_list=None): + if self.raw_packet_cache is not None: + return self.raw_packet_cache + p=b"" + for f in self.fields_desc: + #print(f.name) + if type(p) is tuple : + f.offset=len(p[0]) + else: + assert(type(p)==bytes) + f.offset=len(p) + + val = self.getfieldval(f.name) + if isinstance(val, RawVal): + #sval = str(val) + sval = bytes(val) + p += sval + if field_pos_list is not None: + field_pos_list.append( (f.name, sval.encode("string_escape"), len(p), len(sval) ) ) + f.offset= val + else: + p = f.addfield(self, p, val) + return p + + def do_build_payload(self): + return self.payload.do_build(None) + + def do_update_payload_offset(self,pkt): + #print "obj-id ",id(self) + #print "current offset ",self.name," ",self.offset + #print "current header size ",len(pkt) + self.payload.offset = self.offset + len(pkt) + + def dump_layers_offset (self): + p=self; + while True: + print (p.name, "offset :",p.offset) + p=p.payload + if p ==None or isinstance(p,NoPayload): + break; + + + def do_build(self,result): + if not self.explicit: + self = next(self.__iter__()) + pkt = self.self_build() + for t in self.post_transforms: + pkt = t(pkt) + self.do_update_payload_offset(pkt) + pay = self.do_build_payload() + p = self.post_build(pkt,pay) + if result != None: + result.pkt = self; + return p + + def build_padding(self): + return self.payload.build_padding() + + def update_build_info (self,other): + p=self; + o=other; + while True: + assert(p.aliastypes==o.aliastypes) + assert(type(p) == type(o) ) + + #copy + p.offset=o.offset + + #next + p=p.payload + o=o.payload + if p ==None or isinstance(p,NoPayload): + break; + + def build(self): + result = CPacketRes; + p = self.do_build(result) + p += self.build_padding() + p = self.build_done(p) + self.update_build_info (result.pkt) + return p + + def post_build(self, pkt, pay): + """DEV: called right after the current layer is build.""" + return pkt+pay + + def build_done(self, p): + return self.payload.build_done(p) + + def do_build_ps(self): + p=b"" + pl = [] + q=b"" + for f in self.fields_desc: + if isinstance(f, ConditionalField) and not f._evalcond(self): + continue + p = f.addfield(self, p, self.getfieldval(f.name) ) + if type(p) is bytes: + r = p[len(q):] + q = p + else: + r = b"" + pl.append( (f, f.i2repr(self,self.getfieldval(f.name)), r) ) + + pkt,lst = self.payload.build_ps(internal=1) + p += pkt + lst.append( (self, pl) ) + + return p,lst + + def build_ps(self,internal=0): + p,lst = self.do_build_ps() +# if not internal: +# pkt = self +# while pkt.haslayer(conf.padding_layer): +# pkt = pkt.getlayer(conf.padding_layer) +# lst.append( (pkt, [ ("loakjkjd", pkt.load, pkt.load) ] ) ) +# p += pkt.load +# pkt = pkt.payload + return p,lst + + + def psdump(self, filename=None, **kargs): + """psdump(filename=None, layer_shift=0, rebuild=1) +Creates an EPS file describing a packet. If filename is not provided a temporary file is created and gs is called.""" + canvas = self.canvas_dump(**kargs) + if filename is None: + fname = get_temp_file(autoext=".eps") + canvas.writeEPSfile(fname) + subprocess.Popen([conf.prog.psreader, fname+".eps"]) + else: + canvas.writeEPSfile(filename) + + def pdfdump(self, filename=None, **kargs): + """pdfdump(filename=None, layer_shift=0, rebuild=1) + Creates a PDF file describing a packet. If filename is not provided a temporary file is created and xpdf is called.""" + canvas = self.canvas_dump(**kargs) + if filename is None: + fname = get_temp_file(autoext=".pdf") + canvas.writePDFfile(fname) + subprocess.Popen([conf.prog.pdfreader, fname+".pdf"]) + else: + canvas.writePDFfile(filename) + + + def canvas_dump(self, layer_shift=0, rebuild=1): + canvas = pyx.canvas.canvas() + if rebuild: + p,t = self.__class__(bytes(self)).build_ps() + else: + p,t = self.build_ps() + YTXT=len(t) + for n,l in t: + YTXT += len(l) + YTXT = float(YTXT) + YDUMP=YTXT + + XSTART = 1 + XDSTART = 10 + y = 0.0 + yd = 0.0 + xd = 0 + XMUL= 0.55 + YMUL = 0.4 + + backcolor=colgen(0.6, 0.8, 1.0, trans=pyx.color.rgb) + forecolor=colgen(0.2, 0.5, 0.8, trans=pyx.color.rgb) +# backcolor=makecol(0.376, 0.729, 0.525, 1.0) + + + def hexstr(x): + s = [] + for c in x: + s.append("%02x" % c) + return " ".join(s) + + + def make_dump_txt(x,y,txt): + return pyx.text.text(XDSTART+x*XMUL, (YDUMP-y)*YMUL, r"\tt{%s}"%hexstr(txt), [pyx.text.size.Large]) + + def make_box(o): + return pyx.box.rect(o.left(), o.bottom(), o.width(), o.height(), relcenter=(0.5,0.5)) + + def make_frame(lst): + if len(lst) == 1: + b = lst[0].bbox() + b.enlarge(pyx.unit.u_pt) + return b.path() + else: + fb = lst[0].bbox() + fb.enlarge(pyx.unit.u_pt) + lb = lst[-1].bbox() + lb.enlarge(pyx.unit.u_pt) + if len(lst) == 2 and fb.left() > lb.right(): + return pyx.path.path(pyx.path.moveto(fb.right(), fb.top()), + pyx.path.lineto(fb.left(), fb.top()), + pyx.path.lineto(fb.left(), fb.bottom()), + pyx.path.lineto(fb.right(), fb.bottom()), + pyx.path.moveto(lb.left(), lb.top()), + pyx.path.lineto(lb.right(), lb.top()), + pyx.path.lineto(lb.right(), lb.bottom()), + pyx.path.lineto(lb.left(), lb.bottom())) + else: + # XXX + gb = lst[1].bbox() + if gb != lb: + gb.enlarge(pyx.unit.u_pt) + kb = lst[-2].bbox() + if kb != gb and kb != lb: + kb.enlarge(pyx.unit.u_pt) + return pyx.path.path(pyx.path.moveto(fb.left(), fb.top()), + pyx.path.lineto(fb.right(), fb.top()), + pyx.path.lineto(fb.right(), kb.bottom()), + pyx.path.lineto(lb.right(), kb.bottom()), + pyx.path.lineto(lb.right(), lb.bottom()), + pyx.path.lineto(lb.left(), lb.bottom()), + pyx.path.lineto(lb.left(), gb.top()), + pyx.path.lineto(fb.left(), gb.top()), + pyx.path.closepath(),) + + + def make_dump(s, shift=0, y=0, col=None, bkcol=None, larg=16): + c = pyx.canvas.canvas() + tlist = [] + while s: + dmp,s = s[:larg-shift],s[larg-shift:] + txt = make_dump_txt(shift, y, dmp) + tlist.append(txt) + shift += len(dmp) + if shift >= 16: + shift = 0 + y += 1 + if col is None: + col = pyx.color.rgb.red + if bkcol is None: + col = pyx.color.rgb.white + c.stroke(make_frame(tlist),[col,pyx.deco.filled([bkcol]),pyx.style.linewidth.Thick]) + for txt in tlist: + c.insert(txt) + return c, tlist[-1].bbox(), shift, y + + + last_shift,last_y=0,0.0 + while t: + bkcol = next(backcolor) + proto,fields = t.pop() + y += 0.5 + pt = pyx.text.text(XSTART, (YTXT-y)*YMUL, r"\font\cmssfont=cmss10\cmssfont{%s}" % proto.name, [ pyx.text.size.Large]) + y += 1 + ptbb=pt.bbox() + ptbb.enlarge(pyx.unit.u_pt*2) + canvas.stroke(ptbb.path(),[pyx.color.rgb.black, pyx.deco.filled([bkcol])]) + canvas.insert(pt) + for fname, fval, fdump in fields: + col = next(forecolor) + ft = pyx.text.text(XSTART, (YTXT-y)*YMUL, r"\font\cmssfont=cmss10\cmssfont{%s}" % tex_escape(fname.name)) + if isinstance(fval, str): + if len(fval) > 18: + fval = fval[:18]+"[...]" + else: + fval="" + vt = pyx.text.text(XSTART+3, (YTXT-y)*YMUL, r"\font\cmssfont=cmss10\cmssfont{%s}" % tex_escape(fval)) + y += 1.0 + if fdump: + dt,target,last_shift,last_y = make_dump(fdump, last_shift, last_y, col, bkcol) + + dtb = dt.bbox() + dtb=target + vtb = vt.bbox() + bxvt = make_box(vtb) + bxdt = make_box(dtb) + dtb.enlarge(pyx.unit.u_pt) + try: + if yd < 0: + cnx = pyx.connector.curve(bxvt,bxdt,absangle1=0, absangle2=-90) + else: + cnx = pyx.connector.curve(bxvt,bxdt,absangle1=0, absangle2=90) + except: + pass + else: + canvas.stroke(cnx,[pyx.style.linewidth.thin,pyx.deco.earrow.small,col]) + + canvas.insert(dt) + + canvas.insert(ft) + canvas.insert(vt) + last_y += layer_shift + + return canvas + + + + def extract_padding(self, s): + """DEV: to be overloaded to extract current layer's padding. Return a couple of strings (actual layer, padding)""" + return s,None + + def post_dissect(self, s): + """DEV: is called right after the current layer has been dissected""" + return s + + def pre_dissect(self, s): + """DEV: is called right before the current layer is dissected""" + return s + + def do_dissect(self, s): + flist = self.fields_desc[:] + flist.reverse() + raw = s + while s and flist: + f = flist.pop() + #print(f, end = " = ") + s,fval = f.getfield(self, s) + #print('fval') + self.fields[f.name] = fval + assert(raw.endswith(s)) + if s: + self.raw_packet_cache = raw[:-len(s)] + else: + self.raw_packet_cache = raw + self.explicit = 1 + return s + + def do_dissect_payload(self, s): + if s: + cls = self.guess_payload_class(s) + try: + p = cls(s, _internal=1, _underlayer=self) + except KeyboardInterrupt: + raise + except: + if conf.debug_dissector: + if isinstance(cls,type) and issubclass(cls,Packet): + log_runtime.error("%s dissector failed" % cls.name) + else: + log_runtime.error("%s.guess_payload_class() returned [%s]" % (self.__class__.__name__,repr(cls))) + if cls is not None: + raise + p = conf.raw_layer(s, _internal=1, _underlayer=self) + self.add_payload(p) + + def dissect(self, s): + s = self.pre_dissect(s) + + s = self.do_dissect(s) + + s = self.post_dissect(s) + + payl,pad = self.extract_padding(s) + self.do_dissect_payload(payl) + if pad and conf.padding: + self.add_payload(conf.padding_layer(pad)) + + + def guess_payload_class(self, payload): + """DEV: Guesses the next payload class from layer bonds. Can be overloaded to use a different mechanism.""" + for t in self.aliastypes: + for fval, cls in t.payload_guess: + ok = 1 + for k in fval.keys(): + if not hasattr(self, k) or fval[k] != self.getfieldval(k): + ok = 0 + break + if ok: + return cls + return self.default_payload_class(payload) + + def default_payload_class(self, payload): + """DEV: Returns the default payload class if nothing has been found by the guess_payload_class() method.""" + return conf.raw_layer + + def hide_defaults(self): + """Removes fields' values that are the same as default values.""" + for k in list(self.fields.keys()): + if k in self.default_fields: + if self.default_fields[k] == self.fields[k]: + del(self.fields[k]) + self.payload.hide_defaults() + + def clone_with(self, payload=None, **kargs): + pkt = self.__class__() + pkt.explicit = 1 + pkt.fields = kargs + pkt.offset=self.offset + pkt.time = self.time + pkt.underlayer = self.underlayer + pkt.overload_fields = self.overload_fields.copy() + pkt.post_transforms = self.post_transforms + if payload is not None: + pkt.add_payload(payload) + return pkt + + + def __iter__(self): + def loop(todo, done, self=self): + if todo: + eltname = todo.pop() + elt = self.getfieldval(eltname) + if not isinstance(elt, Gen): + if self.get_field(eltname).islist: + elt = SetGen([elt]) + else: + elt = SetGen(elt) + for e in elt: + done[eltname]=e + for x in loop(todo[:], done): + yield x + else: + if isinstance(self.payload,NoPayload): + payloads = [None] + else: + payloads = self.payload + for payl in payloads: + done2=done.copy() + for k in done2: + if isinstance(done2[k], VolatileValue): + done2[k] = done2[k]._fix() + pkt = self.clone_with(payload=payl, **done2) + yield pkt + + if self.explicit: + todo = [] + done = self.fields + else: + todo = [ k for (k,v) in itertools.chain(self.default_fields.items(), + self.overloaded_fields.items()) + if isinstance(v, VolatileValue) ] + list(self.fields.keys()) + done = {} + return loop(todo, done) + + def __gt__(self, other): + """True if other is an answer from self (self ==> other).""" + if isinstance(other, Packet): + return other < self + elif type(other) is str: + return 1 + else: + raise TypeError((self, other)) + def __lt__(self, other): + """True if self is an answer from other (other ==> self).""" + if isinstance(other, Packet): + return self.answers(other) + elif type(other) is str: + return 1 + else: + raise TypeError((self, other)) + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return False + for f in self.fields_desc: + if f not in other.fields_desc: + return False + if self.getfieldval(f.name) != other.getfieldval(f.name): + return False + return self.payload == other.payload + + def __ne__(self, other): + return not self.__eq__(other) + + def hashret(self): + """DEV: returns a string that has the same value for a request and its answer.""" + return self.payload.hashret() + def answers(self, other): + """DEV: true if self is an answer from other""" + if other.__class__ == self.__class__: + return self.payload.answers(other.payload) + return 0 + + def haslayer(self, cls): + """true if self has a layer that is an instance of cls. Superseded by "cls in self" syntax.""" + if self.__class__ == cls or self.__class__.__name__ == cls: + return 1 + for f in self.packetfields: + fvalue_gen = self.getfieldval(f.name) + if fvalue_gen is None: + continue + if not f.islist: + fvalue_gen = SetGen(fvalue_gen,_iterpacket=0) + for fvalue in fvalue_gen: + if isinstance(fvalue, Packet): + ret = fvalue.haslayer(cls) + if ret: + return ret + return self.payload.haslayer(cls) + def getlayer(self, cls, nb=1, _track=None): + """Return the nb^th layer that is an instance of cls.""" + if type(cls) is int: + nb = cls+1 + cls = None + if type(cls) is str and "." in cls: + ccls,fld = cls.split(".",1) + else: + ccls,fld = cls,None + if cls is None or self.__class__ == cls or self.__class__.name == ccls: + if nb == 1: + if fld is None: + return self + else: + return self.getfieldval(fld) + else: + nb -=1 + for f in self.packetfields: + fvalue_gen = self.getfieldval(f.name) + if fvalue_gen is None: + continue + if not f.islist: + fvalue_gen = SetGen(fvalue_gen,_iterpacket=0) + for fvalue in fvalue_gen: + if isinstance(fvalue, Packet): + track=[] + ret = fvalue.getlayer(cls, nb, _track=track) + if ret is not None: + return ret + nb = track[0] + return self.payload.getlayer(cls,nb,_track=_track) + + def firstlayer(self): + q = self + while q.underlayer is not None: + q = q.underlayer + return q + + def __getitem__(self, cls): + if type(cls) is slice: + lname = cls.start + if cls.stop: + ret = self.getlayer(cls.start, cls.stop) + else: + ret = self.getlayer(cls.start) + if ret is None and cls.step is not None: + ret = cls.step + else: + lname=cls + ret = self.getlayer(cls) + if ret is None: + if type(lname) is Packet_metaclass: + lname = lname.__name__ + elif type(lname) is not str: + lname = repr(lname) + raise IndexError("Layer [%s] not found" % lname) + return ret + + def __delitem__(self, cls): + del(self[cls].underlayer.payload) + + def __setitem__(self, cls, val): + self[cls].underlayer.payload = val + + def __contains__(self, cls): + """"cls in self" returns true if self has a layer which is an instance of cls.""" + return self.haslayer(cls) + + def route(self): + return (None,None,None) + + def fragment(self, *args, **kargs): + return self.payload.fragment(*args, **kargs) + + + def display(self,*args,**kargs): # Deprecated. Use show() + """Deprecated. Use show() method.""" + self.show(*args,**kargs) + def show(self, indent=3, lvl="", label_lvl=""): + """Prints a hierarchical view of the packet. "indent" gives the size of indentation for each layer.""" + ct = conf.color_theme + print("%s%s %s %s" % (label_lvl, + ct.punct("###["), + ct.layer_name(self.name), + ct.punct("]###"))) + for f in self.fields_desc: + if isinstance(f, ConditionalField) and not f._evalcond(self): + continue + if isinstance(f, Emph) or f in conf.emph: + ncol = ct.emph_field_name + vcol = ct.emph_field_value + else: + ncol = ct.field_name + vcol = ct.field_value + fvalue = self.getfieldval(f.name) + if isinstance(fvalue, Packet) or (f.islist and f.holds_packets and type(fvalue) is list): + print("%s \\%-10s\\" % (label_lvl+lvl, ncol(f.name))) + fvalue_gen = SetGen(fvalue,_iterpacket=0) + for fvalue in fvalue_gen: + fvalue.show(indent=indent, label_lvl=label_lvl+lvl+" |") + else: + begn = "%s %-10s%s " % (label_lvl+lvl, + ncol(f.name), + ct.punct("="),) + reprval = f.i2repr(self,fvalue) + if type(reprval) is str: + reprval = reprval.replace("\n", "\n"+" "*(len(label_lvl) + +len(lvl) + +len(f.name) + +4)) + print("%s%s" % (begn,vcol(reprval))) + self.payload.show(indent=indent, lvl=lvl+(" "*indent*self.show_indent), label_lvl=label_lvl) + def show2(self): + """Prints a hierarchical view of an assembled version of the packet, so that automatic fields are calculated (checksums, etc.)""" + self.__class__(bytes(self)).show() + + def sprintf(self, fmt, relax=1): + """sprintf(format, [relax=1]) -> str +where format is a string that can include directives. A directive begins and +ends by % and has the following format %[fmt[r],][cls[:nb].]field%. + +fmt is a classic printf directive, "r" can be appended for raw substitution +(ex: IP.flags=0x18 instead of SA), nb is the number of the layer we want +(ex: for IP/IP packets, IP:2.src is the src of the upper IP layer). +Special case : "%.time%" is the creation time. +Ex : p.sprintf("%.time% %-15s,IP.src% -> %-15s,IP.dst% %IP.chksum% " + "%03xr,IP.proto% %r,TCP.flags%") + +Moreover, the format string can include conditionnal statements. A conditionnal +statement looks like : {layer:string} where layer is a layer name, and string +is the string to insert in place of the condition if it is true, i.e. if layer +is present. If layer is preceded by a "!", the result si inverted. Conditions +can be imbricated. A valid statement can be : + p.sprintf("This is a{TCP: TCP}{UDP: UDP}{ICMP:n ICMP} packet") + p.sprintf("{IP:%IP.dst% {ICMP:%ICMP.type%}{TCP:%TCP.dport%}}") + +A side effect is that, to obtain "{" and "}" characters, you must use +"%(" and "%)". +""" + + escape = { "%": "%", + "(": "{", + ")": "}" } + + + # Evaluate conditions + while "{" in fmt: + i = fmt.rindex("{") + j = fmt[i+1:].index("}") + cond = fmt[i+1:i+j+1] + k = cond.find(":") + if k < 0: + raise Scapy_Exception("Bad condition in format string: [%s] (read sprintf doc!)"%cond) + cond,format = cond[:k],cond[k+1:] + res = False + if cond[0] == "!": + res = True + cond = cond[1:] + if self.haslayer(cond): + res = not res + if not res: + format = "" + fmt = fmt[:i]+format+fmt[i+j+2:] + + # Evaluate directives + s = "" + while "%" in fmt: + i = fmt.index("%") + s += fmt[:i] + fmt = fmt[i+1:] + if fmt and fmt[0] in escape: + s += escape[fmt[0]] + fmt = fmt[1:] + continue + try: + i = fmt.index("%") + sfclsfld = fmt[:i] + fclsfld = sfclsfld.split(",") + if len(fclsfld) == 1: + f = "s" + clsfld = fclsfld[0] + elif len(fclsfld) == 2: + f,clsfld = fclsfld + else: + raise Scapy_Exception + if "." in clsfld: + cls,fld = clsfld.split(".") + else: + cls = self.__class__.__name__ + fld = clsfld + num = 1 + if ":" in cls: + cls,num = cls.split(":") + num = int(num) + fmt = fmt[i+1:] + except: + raise Scapy_Exception("Bad format string [%%%s%s]" % (fmt[:25], fmt[25:] and "...")) + else: + if fld == "time": + val = time.strftime("%H:%M:%S.%%06i", time.localtime(self.time)) % int((self.time-int(self.time))*1000000) + elif cls == self.__class__.__name__ and hasattr(self, fld): + if num > 1: + val = self.payload.sprintf("%%%s,%s:%s.%s%%" % (f,cls,num-1,fld), relax) + f = "s" + elif f[-1] == "r": # Raw field value + val = getattr(self,fld) + f = f[:-1] + if not f: + f = "s" + else: + val = getattr(self,fld) + if fld in self.fieldtype: + val = self.fieldtype[fld].i2repr(self,val) + else: + val = self.payload.sprintf("%%%s%%" % sfclsfld, relax) + f = "s" + s += ("%"+f) % val + + s += fmt + return s + + def mysummary(self): + """DEV: can be overloaded to return a string that summarizes the layer. + Only one mysummary() is used in a whole packet summary: the one of the upper layer, + except if a mysummary() also returns (as a couple) a list of layers whose + mysummary() must be called if they are present.""" + return "" + + def _do_summary(self): + found,s,needed = self.payload._do_summary() + if s: + s = " / "+s + ret = "" + if not found or self.__class__ in needed: + ret = self.mysummary() + if type(ret) is tuple: + ret,n = ret + needed += n + if ret or needed: + found = 1 + if not ret: + ret = self.__class__.__name__ + if self.__class__ in conf.emph: + impf = [] + for f in self.fields_desc: + if f in conf.emph: + impf.append("%s=%s" % (f.name, f.i2repr(self, self.getfieldval(f.name)))) + ret = "%s [%s]" % (ret," ".join(impf)) + ret = "%s%s" % (ret,s) + return found,ret,needed + + def summary(self, intern=0): + """Prints a one line summary of a packet.""" + found,s,needed = self._do_summary() + return s + + + def lastlayer(self,layer=None): + """Returns the uppest layer of the packet""" + return self.payload.lastlayer(self) + + def decode_payload_as(self,cls): + """Reassembles the payload and decode it using another packet class""" + s = bytes(self.payload) + self.payload = cls(s, _internal=1, _underlayer=self) + pp = self + while pp.underlayer is not None: + pp = pp.underlayer + self.payload.dissection_done(pp) + + def libnet(self): + """Not ready yet. Should give the necessary C code that interfaces with libnet to recreate the packet""" + print("libnet_build_%s(" % self.__class__.name.lower()) + det = self.__class__(str(self)) + for f in self.fields_desc: + val = det.getfieldval(f.name) + if val is None: + val = 0 + elif type(val) is int: + val = str(val) + else: + val = '"%s"' % str(val) + print("\t%s, \t\t/* %s */" % (val,f.name)) + print(");") + def command(self): + """Returns a string representing the command you have to type to obtain the same packet""" + f = [] + for fn,fv in self.fields.items(): + fld = self.get_field(fn) + if isinstance(fv, Packet): + fv = fv.command() + elif fld.islist and fld.holds_packets and type(fv) is list: + #fv = "[%s]" % ",".join( map(Packet.command, fv)) + fv = "[%s]" % ",".join([ Packet.command(i) for i in fv ]) + else: + fv = repr(fv) + f.append("%s=%s" % (fn, fv)) + c = "%s(%s)" % (self.__class__.__name__, ", ".join(f)) + pc = self.payload.command() + if pc: + c += "/"+pc + return c + +class NoPayload(Packet): + def __new__(cls, *args, **kargs): + singl = cls.__dict__.get("__singl__") + if singl is None: + cls.__singl__ = singl = Packet.__new__(cls) + Packet.__init__(singl) + return singl + def __init__(self, *args, **kargs): + pass + def dissection_done(self,pkt): + return + def add_payload(self, payload): + raise Scapy_Exception("Can't add payload to NoPayload instance") + def remove_payload(self): + pass + def add_underlayer(self,underlayer): + pass + def remove_underlayer(self,other): + pass + def copy(self): + return self + def __repr__(self): + return "" + def __str__(self): + return "" + def __nonzero__(self): + return False + def do_build(self,result): + return b"" + def build(self): + return b"" + def build_padding(self): + return b"" + def build_done(self, p): + return p + def build_ps(self, internal=0): + return b"",[] + def getfieldval(self, attr): + raise AttributeError(attr) + def getfield_and_val(self, attr): + raise AttributeError(attr) + def setfieldval(self, attr, val): + raise AttributeError(attr) + def delfieldval(self, attr): + raise AttributeError(attr) + def __getattr__(self, attr): + if attr in self.__dict__: + return self.__dict__[attr] + elif attr in self.__class__.__dict__: + return self.__class__.__dict__[attr] + else: + raise AttributeError(attr) + def hide_defaults(self): + pass + def __iter__(self): + return iter([]) + def __eq__(self, other): + if isinstance(other, NoPayload): + return True + return False + def hashret(self): + return b"" + def answers(self, other): + return isinstance(other, NoPayload) or isinstance(other, conf.padding_layer) + def haslayer(self, cls): + return 0 + def getlayer(self, cls, nb=1, _track=None): + if _track is not None: + _track.append(nb) + return None + def fragment(self, *args, **kargs): + raise Scapy_Exception("cannot fragment this packet") + def show(self, indent=3, lvl="", label_lvl=""): + pass + def sprintf(self, fmt, relax): + if relax: + return "??" + else: + raise Scapy_Exception("Format not found [%s]"%fmt) + def _do_summary(self): + return 0,"",[] + def lastlayer(self,layer): + return layer + def command(self): + return "" + +#################### +## packet classes ## +#################### + + +class Raw(Packet): + name = "Raw" + fields_desc = [ StrField("load", b"") ] + def answers(self, other): + return 1 +# s = str(other) +# t = self.load +# l = min(len(s), len(t)) +# return s[:l] == t[:l] + def mysummary(self): + cs = conf.raw_summary + if cs: + if callable(cs): + return "Raw %s" % cs(self.load) + else: + return "Raw %r" % self.load + return Packet.mysummary(self) + +class Padding(Raw): + name = "Padding" + def self_build(self): + return b"" + def build_padding(self): + return (self.getbyteval("load") if self.raw_packet_cache is None + else self.raw_packet_cache) + self.payload.build_padding() + +conf.raw_layer = Raw +conf.padding_layer = Padding +if conf.default_l2 is None: + conf.default_l2 = Raw + +################# +## Bind layers ## +################# + + +def bind_bottom_up(lower, upper, __fval=None, **fval): + if __fval is not None: + fval.update(__fval) + lower.payload_guess = lower.payload_guess[:] + lower.payload_guess.append((fval, upper)) + + +def bind_top_down(lower, upper, __fval=None, **fval): + if __fval is not None: + fval.update(__fval) + upper.overload_fields = upper.overload_fields.copy() + upper.overload_fields[lower] = fval + +@conf.commands.register +def bind_layers(lower, upper, __fval=None, **fval): + """Bind 2 layers on some specific fields' values""" + if __fval is not None: + fval.update(__fval) + bind_top_down(lower, upper, **fval) + bind_bottom_up(lower, upper, **fval) + +def split_bottom_up(lower, upper, __fval=None, **fval): + if __fval is not None: + fval.update(__fval) + #def do_filter((f,u),upper=upper,fval=fval): + def do_filter(s,upper=upper,fval=fval): + if s[1] != upper: + return True + for k in fval: + if k not in s[0] or s[0][k] != fval[k]: + return True + return False + lower.payload_guess = list(filter(do_filter, lower.payload_guess)) + +def split_top_down(lower, upper, __fval=None, **fval): + if __fval is not None: + fval.update(__fval) + if lower in upper.overload_fields: + ofval = upper.overload_fields[lower] + for k in fval: + if k not in ofval or ofval[k] != fval[k]: + return + upper.overload_fields = upper.overload_fields.copy() + del(upper.overload_fields[lower]) + +@conf.commands.register +def split_layers(lower, upper, __fval=None, **fval): + """Split 2 layers previously bound""" + if __fval is not None: + fval.update(__fval) + split_bottom_up(lower, upper, **fval) + split_top_down(lower, upper, **fval) + + +@conf.commands.register +def ls(obj=None): + """List available layers, or infos on a given layer""" + if obj is None: + + import builtins + all = builtins.__dict__.copy() + all.update(globals()) + objlst = sorted(conf.layers, key=lambda x:x.__name__) + for o in objlst: + print("%-10s : %s" %(o.__name__,o.name)) + else: + if isinstance(obj, type) and issubclass(obj, Packet): + for f in obj.fields_desc: + print("%-10s : %-20s = (%s)" % (f.name, f.__class__.__name__, repr(f.default))) + elif isinstance(obj, Packet): + for f in obj.fields_desc: + print("%-10s : %-20s = %-15s (%s)" % (f.name, f.__class__.__name__, repr(getattr(obj,f.name)), repr(f.default))) + if not isinstance(obj.payload, NoPayload): + print("--") + ls(obj.payload) + + + else: + print("Not a packet class. Type 'ls()' to list packet classes.") + + + +############# +## Fuzzing ## +############# + +@conf.commands.register +def fuzz(p, _inplace=0): + """Transform a layer into a fuzzy layer by replacing some default values by random objects""" + if not _inplace: + p = p.copy() + q = p + while not isinstance(q, NoPayload): + for f in q.fields_desc: + if isinstance(f, PacketListField): + for r in getattr(q, f.name): + print("fuzzing", repr(r)) + fuzz(r, _inplace=1) + elif f.default is not None: + rnd = f.randval() + if rnd is not None: + q.default_fields[f.name] = rnd + q = q.payload + return p + + + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/pipetool.py b/scripts/external_libs/scapy-python3-0.18/scapy/pipetool.py new file mode 100644 index 00000000..2dc28cb5 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/pipetool.py @@ -0,0 +1,566 @@ +#! /usr/bin/env python + +## 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 + +from __future__ import with_statement + +import scapy.utils +from scapy.config import conf +import os,_thread,select +import subprocess +import itertools +import collections +import time +from scapy.error import log_interactive,warning +import queue + +class PipeEngine: + pipes = {} + @classmethod + def list_pipes(cls): + for pn,pc in sorted(cls.pipes.items()): + doc = pc.__doc__ or "" + if doc: + doc = doc.splitlines()[0] + print("%20s: %s" % (pn, doc)) + @classmethod + def list_pipes_detailed(cls): + for pn,pc in sorted(cls.pipes.items()): + if pc.__doc__: + print("###### %s\n %s" % (pn ,pc.__doc__)) + else: + print("###### %s" % pn) + + def __init__(self, *pipes): + self.active_pipes = set() + self.active_sources = set() + self.active_drains = set() + self.active_sinks = set() + self._add_pipes(*pipes) + self.thread_lock = _thread.allocate_lock() + self.command_lock = _thread.allocate_lock() + self.__fdr,self.__fdw = os.pipe() + self.threadid = None + def __getattr__(self, attr): + if attr.startswith("spawn_"): + dname = attr[6:] + if dname in self.pipes: + def f(*args, **kargs): + k = self.pipes[dname] + p = k(*args, **kargs) + self.add(p) + return p + return f + raise AttributeError(attr) + + def add_one_pipe(self, pipe): + self.active_pipes.add(pipe) + if isinstance(pipe, Source): + self.active_sources.add(pipe) + if isinstance(pipe, Drain): + self.active_drains.add(pipe) + if isinstance(pipe, Sink): + self.active_sinks.add(pipe) + + def get_pipe_list(self, pipe): + def flatten(p, l): + l.add(p) + for q in p.sources|p.sinks|p.high_sources|p.high_sinks: + if q not in l: + flatten(q, l) + pl = set() + flatten(pipe, pl) + return pl + + def _add_pipes(self, *pipes): + pl = set() + for p in pipes: + pl |= self.get_pipe_list(p) + pl -= self.active_pipes + for q in pl: + self.add_one_pipe(q) + return pl + + + def run(self): + log_interactive.info("Pipe engine thread started.") + try: + for p in self.active_pipes: + p.start() + sources = self.active_sources + sources.add(self.__fdr) + exhausted = set([]) + RUN=True + STOP_IF_EXHAUSTED = False + while RUN and (not STOP_IF_EXHAUSTED or len(sources) > 1): + fds,fdo,fde=select.select(sources,[],[]) + for fd in fds: + if fd is self.__fdr: + cmd = os.read(self.__fdr,1) + if cmd == "X": + RUN=False + break + elif cmd == "B": + STOP_IF_EXHAUSTED = True + elif cmd == "A": + sources = self.active_sources-exhausted + sources.add(self.__fdr) + else: + warning("Unknown internal pipe engine command: %r. Ignoring." % cmd) + elif fd in sources: + try: + fd.deliver() + except Exception as e: + log_interactive.exception("piping from %s failed: %s" % (fd.name, e)) + else: + if fd.exhausted(): + exhausted.add(fd) + sources.remove(fd) + except KeyboardInterrupt: + pass + finally: + try: + for p in self.active_pipes: + p.stop() + finally: + self.thread_lock.release() + log_interactive.info("Pipe engine thread stopped.") + + def start(self): + if self.thread_lock.acquire(0): + self.threadid = _thread.start_new_thread(self.run,()) + else: + warning("Pipe engine already running") + def wait_and_stop(self): + self.stop(_cmd="B") + def stop(self, _cmd="X"): + try: + with self.command_lock: + if self.threadid is not None: + os.write(self.__fdw, _cmd) + while not self.thread_lock.acquire(0): + time.sleep(0.01) # interruptible wait for thread to terminate + self.thread_lock.release() # (not using .join() because it needs 'threading' module) + else: + warning("Pipe engine thread not running") + except KeyboardInterrupt: + print("Interrupted by user.") + + def add(self, *pipes): + pipes = self._add_pipes(*pipes) + with self.command_lock: + if self.threadid is not None: + for p in pipes: + p.start() + os.write(self.__fdw, "A") + + def graph(self,**kargs): + g=['digraph "pipe" {',"\tnode [shape=rectangle];",] + for p in self.active_pipes: + g.append('\t"%i" [label="%s"];' % (id(p), p.name)) + g.append("") + g.append("\tedge [color=blue, arrowhead=vee];") + for p in self.active_pipes: + for q in p.sinks: + g.append('\t"%i" -> "%i";' % (id(p), id(q))) + g.append("") + g.append("\tedge [color=red, arrowhead=veevee];") + for p in self.active_pipes: + for q in p.high_sinks: + g.append('\t"%i" -> "%i" [color="red"];' % (id(p), id(q))) + g.append('}') + graph = "\n".join(g) + scapy.utils.do_graph(graph, **kargs) + + +class _ConnectorLogic(object): + def __init__(self): + self.sources = set() + self.sinks = set() + self.high_sources = set() + self.high_sinks = set() + + def __lt__(self, other): + other.sinks.add(self) + self.sources.add(other) + return other + def __gt__(self, other): + self.sinks.add(other) + other.sources.add(self) + return other + def __eq__(self, other): + self > other + other > self + return other + + def __lshift__(self, other): + self.high_sources.add(other) + other.high_sinks.add(self) + return other + def __rshift__(self, other): + self.high_sinks.add(other) + other.high_sources.add(self) + return other + def __floordiv__(self, other): + self >> other + other >> self + return other + + +class Pipe(_ConnectorLogic): + #TODO3 Move to new metaclass syntax + class __metaclass__(type): + def __new__(cls, name, bases, dct): + c = type.__new__(cls, name, bases, dct) + PipeEngine.pipes[name] = c + return c + def __init__(self, name=None): + _ConnectorLogic.__init__(self) + if name is None: + name = "%s" % (self.__class__.__name__) + self.name = name + def _send(self, msg): + for s in self.sinks: + s.push(msg) + def _high_send(self, msg): + for s in self.high_sinks: + s.high_push(msg) + + def __repr__(self): + ct = conf.color_theme + s = "%s%s" % (ct.punct("<"), ct.layer_name(self.name)) + if self.sources or self.sinks: + s+= " %s" % ct.punct("[") + if self.sources: + s+="%s%s" % (ct.punct(",").join(ct.field_name(s.name) for s in self.sources), + ct.field_value(">")) + s += ct.layer_name("#") + if self.sinks: + s+="%s%s" % (ct.field_value(">"), + ct.punct(",").join(ct.field_name(s.name) for s in self.sinks)) + s += ct.punct("]") + + if self.high_sources or self.high_sinks: + s+= " %s" % ct.punct("[") + if self.high_sources: + s+="%s%s" % (ct.punct(",").join(ct.field_name(s.name) for s in self.high_sources), + ct.field_value(">>")) + s += ct.layer_name("#") + if self.high_sinks: + s+="%s%s" % (ct.field_value(">>"), + ct.punct(",").join(ct.field_name(s.name) for s in self.high_sinks)) + s += ct.punct("]") + + + s += ct.punct(">") + return s + +class Source(Pipe): + def __init__(self, name=None): + Pipe.__init__(self, name=name) + self.is_exhausted = False + def _read_message(self): + return Message() + def deliver(self): + msg = self._read_message + self._send(msg) + def fileno(self): + return None + def exhausted(self): + return self.is_exhausted + def start(self): + pass + def stop(self): + pass + +class Drain(Pipe): + """Repeat messages from low/high entries to (resp.) low/high exits + +-------+ + >>-|-------|->> + | | + >-|-------|-> + +-------+ +""" + def push(self, msg): + self._send(msg) + def high_push(self, msg): + self._high_send(msg) + def start(self): + pass + def stop(self): + pass + +class Sink(Pipe): + def push(self, msg): + pass + def high_push(self, msg): + pass + def start(self): + pass + def stop(self): + pass + + +class AutoSource(Source): + def __init__(self, name=None): + Source.__init__(self, name=name) + self.__fdr,self.__fdw = os.pipe() + self._queue = collections.deque() + def fileno(self): + return self.__fdr + def _gen_data(self, msg): + self._queue.append((msg,False)) + self._wake_up() + def _gen_high_data(self, msg): + self._queue.append((msg,True)) + self._wake_up() + def _wake_up(self): + os.write(self.__fdw,"x") + def deliver(self): + os.read(self.__fdr,1) + try: + msg,high = self._queue.popleft() + except IndexError: #empty queue. Exhausted source + pass + else: + if high: + self._high_send(msg) + else: + self._send(msg) + +class ThreadGenSource(AutoSource): + def __init__(self, name=None): + AutoSource.__init__(self, name=name) + self.RUN = False + def generate(self): + pass + def start(self): + self.RUN = True + _thread.start_new_thread(self.generate,()) + def stop(self): + self.RUN = False + + + +class ConsoleSink(Sink): + """Print messages on low and high entries + +-------+ + >>-|--. |->> + | print | + >-|--' |-> + +-------+ +""" + def push(self, msg): + print(">%r" % msg) + def high_push(self, msg): + print(">>%r" % msg) + +class RawConsoleSink(Sink): + """Print messages on low and high entries + +-------+ + >>-|--. |->> + | write | + >-|--' |-> + +-------+ +""" + def __init__(self, name=None, newlines=True): + Sink.__init__(self, name=name) + self.newlines = newlines + def push(self, msg): + if self.newlines: + msg += "\n" + os.write(1, str(msg)) + def high_push(self, msg): + if self.newlines: + msg += "\n" + os.write(1, str(msg)) + +class CLIFeeder(AutoSource): + """Send messages from python command line + +--------+ + >>-| |->> + | send() | + >-| `----|-> + +--------+ +""" + def send(self, msg): + self._gen_data(msg) + def close(self): + self.is_exhausted = True + +class CLIHighFeeder(CLIFeeder): + """Send messages from python command line to high output + +--------+ + >>-| .----|->> + | send() | + >-| |-> + +--------+ +""" + def send(self, msg): + self._gen_high_data(msg) + + +class PeriodicSource(ThreadGenSource): + """Generage messages periodically on low exit + +-------+ + >>-| |->> + | msg,T | + >-| `----|-> + +-------+ +""" + def __init__(self, msg, period, period2=0, name=None): + ThreadGenSource.__init__(self,name=name) + if not hasattr(msg, "__iter__"): + msg=[msg] + self.msg = msg + self.period = period + self.period2 = period2 + def generate(self): + while self.RUN: + empty_gen = True + for m in self.msg: + empty_gen = False + self._gen_data(m) + time.sleep(self.period) + if empty_gen: + self.is_exhausted = True + self._wake_up() + time.sleep(self.period2) + +class TermSink(Sink): + """Print messages on low and high entries on a separate terminal + +-------+ + >>-|--. |->> + | print | + >-|--' |-> + +-------+ +""" + def __init__(self, name=None, keepterm=True, newlines=True, openearly=True): + Sink.__init__(self, name=name) + self.keepterm = keepterm + self.newlines = newlines + self.openearly = openearly + self.opened = False + if self.openearly: + self.start() + + def start(self): + if not self.opened: + self.opened = True + self.__r,self.__w = os.pipe() + cmd = ["xterm"] + if self.name is not None: + cmd.extend(["-title",self.name]) + if self.keepterm: + cmd.append("-hold") + cmd.extend(["-e", "cat 0<&%i" % self.__r]) + self.__p = subprocess.Popen(cmd) + os.close(self.__r) + def stop(self): + if not self.keepterm: + self.opened = False + os.close(self.__w) + self.__p.kill() + self.__p.wait() + def _print(self, s): + if self.newlines: + s+="\n" + os.write(self.__w, s) + + def push(self, msg): + self._print(str(msg)) + def high_push(self, msg): + self._print(str(msg)) + + +class QueueSink(Sink): + """Collect messages from high and low entries and queue them. Messages are unqueued with the .recv() method. + +-------+ + >>-|--. |->> + | queue | + >-|--' |-> + +-------+ +""" + def __init__(self, name=None): + Sink.__init__(self, name=name) + self.q = queue.Queue() + def push(self, msg): + self.q.put(msg) + def high_push(self, msg): + self.q.put(msg) + def recv(self): + while True: + try: + return self.q.get(True, timeout=0.1) + except queue.Empty: + pass + + +class TransformDrain(Drain): + """Apply a function to messages on low and high entry + +-------+ + >>-|--[f]--|->> + | | + >-|--[f]--|-> + +-------+ +""" + def __init__(self, f, name=None): + Drain.__init__(self, name=name) + self.f = f + def push(self, msg): + self._send(self.f(msg)) + def high_push(self, msg): + self._high_send(self.f(msg)) + +class UpDrain(Drain): + """Repeat messages from low entry to high exit + +-------+ + >>-| ,--|->> + | / | + >-|--' |-> + +-------+ +""" + def push(self, msg): + self._high_send(msg) + def high_push(self, msg): + pass + +class DownDrain(Drain): + """Repeat messages from high entry to low exit + +-------+ + >>-|--. |->> + | \ | + >-| `--|-> + +-------+ +""" + def push(self, msg): + pass + def high_push(self, msg): + self._send(msg) + + +def _testmain(): + s = PeriodicSource("hello", 1, name="src") + d1 = Drain(name="d1") + c = ConsoleSink(name="c") + tf = TransformDrain(lambda x:"Got %r" % x) + t = TermSink(name="t", keepterm=False) + + s > d1 > c + d1 > tf > t + + p = PipeEngine(s) + + p.graph(type="png",target="> /tmp/pipe.png") + + p.start() + print(p.threadid) + time.sleep(5) + p.stop() + + +if __name__ == "__main__": + _testmain() diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/plist.py b/scripts/external_libs/scapy-python3-0.18/scapy/plist.py new file mode 100644 index 00000000..bdf0b757 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/plist.py @@ -0,0 +1,517 @@ +## 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 + +""" +PacketList: holds several packets and allows to do operations on them. +""" + + +import os,subprocess +from .config import conf +from .base_classes import BasePacket,BasePacketList +from collections import defaultdict + +from .utils import do_graph,hexdump,make_table,make_lined_table,make_tex_table,get_temp_file + +from scapy.arch import NETWORKX +if NETWORKX: + import networkx as nx + + +############# +## Results ## +############# + +class PacketList(BasePacketList): + res = [] + def __init__(self, res=None, name="PacketList", stats=None, vector_index = None): + """create a packet list from a list of packets + res: the list of packets + stats: a list of classes that will appear in the stats (defaults to [TCP,UDP,ICMP])""" + if stats is None: + stats = conf.stats_classic_protocols + self.stats = stats + if res is None: + res = [] + if isinstance(res, PacketList): + res = res.res + self.res = res + self.listname = name + self.vector_index = vector_index + def __len__(self): + return len(self.res) + def _elt2pkt(self, elt): + if self.vector_index == None: + return elt + else: + return elt[self.vector_index] + def _elt2sum(self, elt): + if self.vector_index == None: + return elt.summary() + else: + return "%s ==> %s" % (elt[0].summary(),elt[1].summary()) + + def _elt2show(self, elt): + return self._elt2sum(elt) + def __repr__(self): + stats=dict.fromkeys(self.stats,0) + other = 0 + for r in self.res: + f = 0 + for p in stats: + if self._elt2pkt(r).haslayer(p): + stats[p] += 1 + f = 1 + break + if not f: + other += 1 + s = "" + ct = conf.color_theme + for p in self.stats: + s += " %s%s%s" % (ct.packetlist_proto(p.name), + ct.punct(":"), + ct.packetlist_value(stats[p])) + s += " %s%s%s" % (ct.packetlist_proto("Other"), + ct.punct(":"), + ct.packetlist_value(other)) + return "%s%s%s%s%s" % (ct.punct("<"), + ct.packetlist_name(self.listname), + ct.punct(":"), + s, + ct.punct(">")) + def __getattr__(self, attr): + return getattr(self.res, attr) + def __getitem__(self, item): + if isinstance(item,type) and issubclass(item,BasePacket): + #return self.__class__(filter(lambda x: item in self._elt2pkt(x),self.res), + return self.__class__([ x for x in self.res if item in self._elt2pkt(x) ], + name="%s from %s"%(item.__name__,self.listname)) + if type(item) is slice: + return self.__class__(self.res.__getitem__(item), + name = "mod %s" % self.listname) + return self.res.__getitem__(item) + def __getslice__(self, *args, **kargs): + return self.__class__(self.res.__getslice__(*args, **kargs), + name="mod %s"%self.listname) + def __add__(self, other): + return self.__class__(self.res+other.res, + name="%s+%s"%(self.listname,other.listname)) + def summary(self, prn=None, lfilter=None): + """prints a summary of each packet +prn: function to apply to each packet instead of lambda x:x.summary() +lfilter: truth function to apply to each packet to decide whether it will be displayed""" + for r in self.res: + if lfilter is not None: + if not lfilter(r): + continue + if prn is None: + print(self._elt2sum(r)) + else: + print(prn(r)) + def nsummary(self,prn=None, lfilter=None): + """prints a summary of each packet with the packet's number +prn: function to apply to each packet instead of lambda x:x.summary() +lfilter: truth function to apply to each packet to decide whether it will be displayed""" + for i, p in enumerate(self.res): + if lfilter is not None: + if not lfilter(p): + continue + print(conf.color_theme.id(i,fmt="%04i"), end = " ") + if prn is None: + print(self._elt2sum(p)) + else: + print(prn(p)) + def display(self): # Deprecated. Use show() + """deprecated. is show()""" + self.show() + def show(self, *args, **kargs): + """Best way to display the packet list. Defaults to nsummary() method""" + return self.nsummary(*args, **kargs) + + def filter(self, func): + """Returns a packet list filtered by a truth function""" + return self.__class__(list(filter(func,self.res)), + name="filtered %s"%self.listname) + + def plot(self, f, lfilter=None,**kargs): + """Applies a function to each packet to get a value that will be plotted with matplotlib. A matplotlib object is returned + lfilter: a truth function that decides whether a packet must be ploted""" + + return plt.plot([ f(i) for i in self.res if not lfilter or lfilter(i) ], **kargs) + + def diffplot(self, f, delay=1, lfilter=None, **kargs): + """diffplot(f, delay=1, lfilter=None) + Applies a function to couples (l[i],l[i+delay])""" + + return plt.plot([ f(i, j) for i in self.res[:-delay] for j in self.res[delay:] if not lfilter or (lfilter(i) and lfilter(j))], + **kargs) + + def multiplot(self, f, lfilter=None, **kargs): + """Uses a function that returns a label and a value for this label, then plots all the values label by label""" + + d = defaultdict(list) + for i in self.res: + if lfilter and not lfilter(i): + continue + k, v = f(i) + d[k].append(v) + + figure = plt.figure() + ax = figure.add_axes(plt.axes()) + for i in d: + ax.plot(d[i], **kargs) + return figure + + + def rawhexdump(self): + """Prints an hexadecimal dump of each packet in the list""" + for p in self: + hexdump(self._elt2pkt(p)) + + def hexraw(self, lfilter=None): + """Same as nsummary(), except that if a packet has a Raw layer, it will be hexdumped + lfilter: a truth function that decides whether a packet must be displayed""" + for i,p in enumerate(self.res): + p1 = self._elt2pkt(p) + if lfilter is not None and not lfilter(p1): + continue + print("%s %s %s" % (conf.color_theme.id(i,fmt="%04i"), + p1.sprintf("%.time%"), + self._elt2sum(p))) + if p1.haslayer(conf.raw_layer): + hexdump(p1.getlayer(conf.raw_layer).load) + + def hexdump(self, lfilter=None): + """Same as nsummary(), except that packets are also hexdumped + lfilter: a truth function that decides whether a packet must be displayed""" + for i,p in enumerate(self.res): + p1 = self._elt2pkt(p) + if lfilter is not None and not lfilter(p1): + continue + print("%s %s %s" % (conf.color_theme.id(i,fmt="%04i"), + p1.sprintf("%.time%"), + self._elt2sum(p))) + hexdump(p1) + + def padding(self, lfilter=None): + """Same as hexraw(), for Padding layer""" + for i,p in enumerate(self.res): + p1 = self._elt2pkt(p) + if p1.haslayer(conf.padding_layer): + if lfilter is None or lfilter(p1): + print("%s %s %s" % (conf.color_theme.id(i,fmt="%04i"), + p1.sprintf("%.time%"), + self._elt2sum(p))) + hexdump(p1.getlayer(conf.padding_layer).load) + + def nzpadding(self, lfilter=None): + """Same as padding() but only non null padding""" + for i,p in enumerate(self.res): + p1 = self._elt2pkt(p) + if p1.haslayer(conf.padding_layer): + pad = p1.getlayer(conf.padding_layer).load + if pad == pad[0]*len(pad): + continue + if lfilter is None or lfilter(p1): + print("%s %s %s" % (conf.color_theme.id(i,fmt="%04i"), + p1.sprintf("%.time%"), + self._elt2sum(p))) + hexdump(p1.getlayer(conf.padding_layer).load) + + + def conversations(self, getsrcdst=None, draw = True, **kargs): + """Graphes a conversations between sources and destinations and display it + (using graphviz) + getsrcdst: a function that takes an element of the list and return the source and dest + by defaults, return source and destination IP + if networkx library is available returns a DiGraph, or draws it if draw = True otherwise graphviz is used + format: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option + target: output filename. If None, matplotlib is used to display + prog: which graphviz program to use""" + if getsrcdst is None: + getsrcdst = lambda x:(x['IP'].src, x['IP'].dst) + conv = {} + for p in self.res: + p = self._elt2pkt(p) + try: + c = getsrcdst(p) + except: + #XXX warning() + continue + conv[c] = conv.get(c,0)+1 + + if NETWORKX: # networkx is available + gr = nx.DiGraph() + for s,d in conv: + if s not in gr: + gr.add_node(s) + if d not in gr: + gr.add_node(d) + gr.add_edge(s, d) + if draw: + return do_graph(gr, **kargs) + else: + return gr + else: + gr = 'digraph "conv" {\n' + for s,d in conv: + gr += '\t "%s" -> "%s"\n' % (s,d) + gr += "}\n" + return do_graph(gr, **kargs) + + def afterglow(self, src=None, event=None, dst=None, **kargs): + """Experimental clone attempt of http://sourceforge.net/projects/afterglow + each datum is reduced as src -> event -> dst and the data are graphed. + by default we have IP.src -> IP.dport -> IP.dst""" + if src is None: + src = lambda x: x['IP'].src + if event is None: + event = lambda x: x['IP'].dport + if dst is None: + dst = lambda x: x['IP'].dst + sl = {} + el = {} + dl = {} + for i in self.res: + try: + s,e,d = src(i),event(i),dst(i) + if s in sl: + n,l = sl[s] + n += 1 + if e not in l: + l.append(e) + sl[s] = (n,l) + else: + sl[s] = (1,[e]) + if e in el: + n,l = el[e] + n+=1 + if d not in l: + l.append(d) + el[e] = (n,l) + else: + el[e] = (1,[d]) + dl[d] = dl.get(d,0)+1 + except: + continue + + import math + def normalize(n): + return 2+math.log(n)/4.0 + + def minmax(x): + m,M = min(x),max(x) + if m == M: + m = 0 + if M == 0: + M = 1 + return m,M + + #mins,maxs = minmax(map(lambda (x,y): x, sl.values())) + mins,maxs = minmax([ a[0] for a in sl.values()]) + #mine,maxe = minmax(map(lambda (x,y): x, el.values())) + mine,maxe = minmax([ a[0] for a in el.values()]) + mind,maxd = minmax(dl.values()) + + gr = 'digraph "afterglow" {\n\tedge [len=2.5];\n' + + gr += "# src nodes\n" + for s in sl: + n,l = sl[s]; n = 1+(n-mins)/(maxs-mins) + gr += '"src.%s" [label = "%s", shape=box, fillcolor="#FF0000", style=filled, fixedsize=1, height=%.2f,width=%.2f];\n' % (repr(s),repr(s),n,n) + gr += "# event nodes\n" + for e in el: + n,l = el[e]; n = n = 1+(n-mine)/(maxe-mine) + gr += '"evt.%s" [label = "%s", shape=circle, fillcolor="#00FFFF", style=filled, fixedsize=1, height=%.2f, width=%.2f];\n' % (repr(e),repr(e),n,n) + for d in dl: + n = dl[d]; n = n = 1+(n-mind)/(maxd-mind) + gr += '"dst.%s" [label = "%s", shape=triangle, fillcolor="#0000ff", style=filled, fixedsize=1, height=%.2f, width=%.2f];\n' % (repr(d),repr(d),n,n) + + gr += "###\n" + for s in sl: + n,l = sl[s] + for e in l: + gr += ' "src.%s" -> "evt.%s";\n' % (repr(s),repr(e)) + for e in el: + n,l = el[e] + for d in l: + gr += ' "evt.%s" -> "dst.%s";\n' % (repr(e),repr(d)) + + gr += "}" + return do_graph(gr, **kargs) + + + def _dump_document(self, **kargs): + import pyx + d = pyx.document.document() + l = len(self.res) + for i in range(len(self.res)): + elt = self.res[i] + c = self._elt2pkt(elt).canvas_dump(**kargs) + cbb = c.bbox() + c.text(cbb.left(),cbb.top()+1,r"\font\cmssfont=cmss12\cmssfont{Frame %i/%i}" % (i,l),[pyx.text.size.LARGE]) + if conf.verb >= 2: + os.write(1,b".") + d.append(pyx.document.page(c, paperformat=pyx.document.paperformat.A4, + margin=1*pyx.unit.t_cm, + fittosize=1)) + return d + + + + def psdump(self, filename = None, **kargs): + """Creates a multipage poscript file with a psdump of every packet + filename: name of the file to write to. If empty, a temporary file is used and + conf.prog.psreader is called""" + d = self._dump_document(**kargs) + if filename is None: + filename = get_temp_file(autoext=".ps") + d.writePSfile(filename) + subprocess.Popen([conf.prog.psreader, filename+".ps"]) + else: + d.writePSfile(filename) + print + + def pdfdump(self, filename = None, **kargs): + """Creates a PDF file with a psdump of every packet + filename: name of the file to write to. If empty, a temporary file is used and + conf.prog.pdfreader is called""" + d = self._dump_document(**kargs) + if filename is None: + filename = get_temp_file(autoext=".pdf") + d.writePDFfile(filename) + subprocess.Popen([conf.prog.pdfreader, filename+".pdf"]) + else: + d.writePDFfile(filename) + print + + def sr(self,multi=0): + """sr([multi=1]) -> (SndRcvList, PacketList) + Matches packets in the list and return ( (matched couples), (unmatched packets) )""" + remain = self.res[:] + sr = [] + i = 0 + while i < len(remain): + s = remain[i] + j = i + while j < len(remain)-1: + j += 1 + r = remain[j] + if r.answers(s): + sr.append((s,r)) + if multi: + remain[i]._answered=1 + remain[j]._answered=2 + continue + del(remain[j]) + del(remain[i]) + i -= 1 + break + i += 1 + if multi: + remain = filter(lambda x:not hasattr(x,"_answered"), remain) + return SndRcvList(sr),PacketList(remain) + + def sessions(self, session_extractor=None): + if session_extractor is None: + def session_extractor(p): + sess = "Other" + if 'Ether' in p: + if 'IP' in p: + if 'TCP' in p: + sess = p.sprintf("TCP %IP.src%:%r,TCP.sport% > %IP.dst%:%r,TCP.dport%") + elif 'UDP' in p: + sess = p.sprintf("UDP %IP.src%:%r,UDP.sport% > %IP.dst%:%r,UDP.dport%") + elif 'ICMP' in p: + sess = p.sprintf("ICMP %IP.src% > %IP.dst% type=%r,ICMP.type% code=%r,ICMP.code% id=%ICMP.id%") + else: + sess = p.sprintf("IP %IP.src% > %IP.dst% proto=%IP.proto%") + elif 'ARP' in p: + sess = p.sprintf("ARP %ARP.psrc% > %ARP.pdst%") + else: + sess = p.sprintf("Ethernet type=%04xr,Ether.type%") + return sess + sessions = defaultdict(self.__class__) + for p in self.res: + sess = session_extractor(self._elt2pkt(p)) + sessions[sess].append(p) + return dict(sessions) + + def replace(self, *args, **kargs): + """ + lst.replace(,[,]) + lst.replace( (fld,[ov],nv),(fld,[ov,]nv),...) + if ov is None, all values are replaced + ex: + lst.replace( IP.src, "192.168.1.1", "10.0.0.1" ) + lst.replace( IP.ttl, 64 ) + lst.replace( (IP.ttl, 64), (TCP.sport, 666, 777), ) + """ + delete_checksums = kargs.get("delete_checksums",False) + x=PacketList(name="Replaced %s" % self.listname) + if type(args[0]) is not tuple: + args = (args,) + for p in self.res: + p = self._elt2pkt(p) + copied = False + for scheme in args: + fld = scheme[0] + old = scheme[1] # not used if len(scheme) == 2 + new = scheme[-1] + for o in fld.owners: + if o in p: + if len(scheme) == 2 or p[o].getfieldval(fld.name) == old: + if not copied: + p = p.copy() + if delete_checksums: + p.delete_checksums() + copied = True + setattr(p[o], fld.name, new) + x.append(p) + return x + + +class SndRcvList(PacketList): + def __init__(self, res=None, name="Results", stats=None): + PacketList.__init__(self, res, name, stats, vector_index = 1) + def summary(self, prn=None, lfilter=None): + """prints a summary of each SndRcv packet pair +prn: function to apply to each packet pair instead of lambda s, r: "%s ==> %s" % (s.summary(),r.summary()) +lfilter: truth function to apply to each packet pair to decide whether it will be displayed""" + for s, r in self.res: + if lfilter is not None: + if not lfilter(s, r): + continue + if prn is None: + print(self._elt2sum((s, r))) + else: + print(prn(s, r)) + def nsummary(self,prn=None, lfilter=None): + """prints a summary of each SndRcv packet pair with the pair's number +prn: function to apply to each packet pair instead of lambda s, r: "%s ==> %s" % (s.summary(),r.summary()) +lfilter: truth function to apply to each packet pair to decide whether it will be displayed""" + for i, (s, r) in enumerate(self.res): + if lfilter is not None: + if not lfilter(s, r): + continue + print(conf.color_theme.id(i,fmt="%04i"), end = " ") + if prn is None: + print(self._elt2sum((s, r))) + else: + print(prn(s, r)) + def filter(self, func): + """Returns a SndRcv list filtered by a truth function""" + return self.__class__( [ i for i in self.res if func(*i) ], name='filtered %s'%self.listname) + + def make_table(self, *args, **kargs): + """Prints a table using a function that returs for each packet its head column value, head row value and displayed value + ex: p.make_table(lambda s, r:(s[IP].dst, r[TCP].sport, s[TCP].sprintf("%flags%")) """ + return make_table(self.res, *args, **kargs) + def make_lined_table(self, *args, **kargs): + """Same as make_table, but print a table with lines""" + return make_lined_table(self.res, *args, **kargs) + def make_tex_table(self, *args, **kargs): + """Same as make_table, but print a table with LaTeX syntax""" + return make_tex_table(self.res, *args, **kargs) diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/pton_ntop.py b/scripts/external_libs/scapy-python3-0.18/scapy/pton_ntop.py new file mode 100644 index 00000000..1629edee --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/pton_ntop.py @@ -0,0 +1,90 @@ +## 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 + +""" +Convert IPv6 addresses between textual representation and binary. + +These functions are missing when python is compiled +without IPv6 support, on Windows for instance. +""" + +import socket,struct + +def inet_pton(af, addr): + """Convert an IP address from text representation into binary form""" + print('hello') + if af == socket.AF_INET: + return inet_aton(addr) + elif af == socket.AF_INET6: + # IPv6: The use of "::" indicates one or more groups of 16 bits of zeros. + # We deal with this form of wildcard using a special marker. + JOKER = b"*" + while b"::" in addr: + addr = addr.replace(b"::", b":" + JOKER + b":") + joker_pos = None + + # The last part of an IPv6 address can be an IPv4 address + ipv4_addr = None + if b"." in addr: + ipv4_addr = addr.split(b":")[-1] + + result = b"" + parts = addr.split(b":") + for part in parts: + if part == JOKER: + # Wildcard is only allowed once + if joker_pos is None: + joker_pos = len(result) + else: + raise Exception("Illegal syntax for IP address") + elif part == ipv4_addr: # FIXME: Make sure IPv4 can only be last part + # FIXME: inet_aton allows IPv4 addresses with less than 4 octets + result += socket.inet_aton(ipv4_addr) + else: + # Each part must be 16bit. Add missing zeroes before decoding. + try: + result += part.rjust(4, b"0").decode("hex") + except TypeError: + raise Exception("Illegal syntax for IP address") + + # If there's a wildcard, fill up with zeros to reach 128bit (16 bytes) + if JOKER in addr: + result = (result[:joker_pos] + b"\x00" * (16 - len(result)) + + result[joker_pos:]) + + if len(result) != 16: + raise Exception("Illegal syntax for IP address") + return result + else: + raise Exception("Address family not supported") + + +def inet_ntop(af, addr): + """Convert an IP address from binary form into text represenation""" + if af == socket.AF_INET: + return inet_ntoa(addr) + elif af == socket.AF_INET6: + # IPv6 addresses have 128bits (16 bytes) + if len(addr) != 16: + raise Exception("Illegal syntax for IP address") + parts = [] + for left in [0, 2, 4, 6, 8, 10, 12, 14]: + try: + value = struct.unpack("!H", addr[left:left+2])[0] + hexstr = hex(value)[2:] + except TypeError: + raise Exception("Illegal syntax for IP address") + parts.append(hexstr.lstrip("0").lower()) + result = b":".join(parts) + while b":::" in result: + result = result.replace(b":::", b"::") + # Leaving out leading and trailing zeros is only allowed with :: + if result.endswith(b":") and not result.endswith(b"::"): + result = result + b"0" + if result.startswith(b":") and not result.startswith(b"::"): + result = b"0" + result + return result + else: + raise Exception("Address family not supported yet") diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/route.py b/scripts/external_libs/scapy-python3-0.18/scapy/route.py new file mode 100644 index 00000000..bccc43a2 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/route.py @@ -0,0 +1,175 @@ +## 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 + +""" +Routing and handling of network interfaces. +""" + +import socket +from scapy.arch import read_routes,get_if_addr,LOOPBACK_NAME +from scapy.utils import atol,ltoa,itom +from scapy.config import conf +from scapy.error import Scapy_Exception,warning + +############################## +## Routing/Interfaces stuff ## +############################## + +class Route: + def __init__(self): + self.resync() + self.s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.cache = {} + + def invalidate_cache(self): + self.cache = {} + + def resync(self): + self.invalidate_cache() + self.routes = read_routes() + + def __repr__(self): + rt = "Network Netmask Gateway Iface Output IP\n" + for net,msk,gw,iface,addr in self.routes: + rt += "%-15s %-15s %-15s %-15s %-15s\n" % (ltoa(net), + ltoa(msk), + gw, + iface, + addr) + return rt + + def make_route(self, host=None, net=None, gw=None, dev=None): + if host is not None: + thenet,msk = host,32 + elif net is not None: + thenet,msk = net.split("/") + msk = int(msk) + else: + raise Scapy_Exception("make_route: Incorrect parameters. You should specify a host or a net") + if gw is None: + gw="0.0.0.0" + if dev is None: + if gw: + nhop = gw + else: + nhop = thenet + dev,ifaddr,x = self.route(nhop) + else: + ifaddr = get_if_addr(dev) + return (atol(thenet), itom(msk), gw, dev, ifaddr) + + def add(self, *args, **kargs): + """Ex: + add(net="192.168.1.0/24",gw="1.2.3.4") + """ + self.invalidate_cache() + self.routes.append(self.make_route(*args,**kargs)) + + + def delt(self, *args, **kargs): + """delt(host|net, gw|dev)""" + self.invalidate_cache() + route = self.make_route(*args,**kargs) + try: + i=self.routes.index(route) + del(self.routes[i]) + except ValueError: + warning("no matching route found") + + def ifchange(self, iff, addr): + self.invalidate_cache() + the_addr,the_msk = (addr.split("/")+["32"])[:2] + the_msk = itom(int(the_msk)) + the_rawaddr = atol(the_addr) + the_net = the_rawaddr & the_msk + + + for i in range(len(self.routes)): + net,msk,gw,iface,addr = self.routes[i] + if iface != iff: + continue + if gw == '0.0.0.0': + self.routes[i] = (the_net,the_msk,gw,iface,the_addr) + else: + self.routes[i] = (net,msk,gw,iface,the_addr) + conf.netcache.flush() + + + + def ifdel(self, iff): + self.invalidate_cache() + new_routes=[] + for rt in self.routes: + if rt[3] != iff: + new_routes.append(rt) + self.routes=new_routes + + def ifadd(self, iff, addr): + self.invalidate_cache() + the_addr,the_msk = (addr.split("/")+["32"])[:2] + the_msk = itom(int(the_msk)) + the_rawaddr = atol(the_addr) + the_net = the_rawaddr & the_msk + self.routes.append((the_net,the_msk,'0.0.0.0',iff,the_addr)) + + + def route(self,dest,verbose=None): + if type(dest) is list and dest: + dest = dest[0] + if dest in self.cache: + return self.cache[dest] + if verbose is None: + verbose=conf.verb + # Transform "192.168.*.1-5" to one IP of the set + dst = dest.split("/")[0] + dst = dst.replace("*","0") + while True: + l = dst.find("-") + if l < 0: + break + m = (dst[l:]+".").find(".") + dst = dst[:l]+dst[l+m:] + + + dst = atol(dst) + pathes=[] + for d,m,gw,i,a in self.routes: + aa = atol(a) + #Commented out after issue with virtual network with local address 0.0.0.0 + #if aa == dst: + # pathes.append((0xffffffff,(LOOPBACK_NAME,a,"0.0.0.0"))) + if (dst & m) == (d & m): + pathes.append((m,(i,a,gw))) + if not pathes: + if verbose: + warning("No route found (no default route?)") + return LOOPBACK_NAME,"0.0.0.0","0.0.0.0" #XXX linux specific! + # Choose the more specific route (greatest netmask). + # XXX: we don't care about metrics + pathes.sort() + ret = pathes[-1][1] + self.cache[dest] = ret + return ret + + def get_if_bcast(self, iff): + for net, msk, gw, iface, addr in self.routes: + if (iff == iface and net != 0): + bcast = atol(addr)|(~msk&0xffffffff); # FIXME: check error in atol() + return ltoa(bcast); + warning("No broadcast address found for iface %s\n" % iff); + +#conf.route=Route() + +conf.route=None; +_betteriface = None + + + +#XXX use "with" +#_betteriface = conf.route.route("0.0.0.0", verbose=0)[0] + +if _betteriface != LOOPBACK_NAME: + conf.iface = _betteriface +del(_betteriface) diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/route6.py b/scripts/external_libs/scapy-python3-0.18/scapy/route6.py new file mode 100644 index 00000000..44a66735 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/route6.py @@ -0,0 +1,288 @@ +## 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 + +""" +Routing and network interface handling for IPv6. +""" + +############################################################################# +############################################################################# +### Routing/Interfaces stuff ### +############################################################################# +############################################################################# + +import socket +from .config import conf +from .utils6 import * +from .arch import * + + +class Route6: + + def __init__(self): + self.invalidate_cache() + self.resync() + + def invalidate_cache(self): + self.cache = {} + + def flush(self): + self.invalidate_cache() + self.routes = [] + + def resync(self): + # TODO : At the moment, resync will drop existing Teredo routes + # if any. Change that ... + self.invalidate_cache() + self.routes = read_routes6() + if self.routes == []: + log_loading.info("No IPv6 support in kernel") + + def __repr__(self): + rtlst = [('Destination', 'Next Hop', "iface", "src candidates")] + + for net,msk,gw,iface,cset in self.routes: + rtlst.append(('%s/%i'% (net,msk), gw, iface, ", ".join(cset))) + + #colwidth = map(lambda x: max(map(lambda y: len(y), x)), apply(zip, rtlst)) + rtlst = zip(rtlst) + colwidth = [ max([len(y) for y in x]) for x in rtlst] + fmt = " ".join(map(lambda x: "%%-%ds"%x, colwidth)) + rt = "\n".join([ fmt % x for x in rtlst]) + + return rt + + + # Unlike Scapy's Route.make_route() function, we do not have 'host' and 'net' + # parameters. We only have a 'dst' parameter that accepts 'prefix' and + # 'prefix/prefixlen' values. + # WARNING: Providing a specific device will at the moment not work correctly. + def make_route(self, dst, gw=None, dev=None): + """Internal function : create a route for 'dst' via 'gw'. + """ + prefix, plen = (dst.split("/")+["128"])[:2] + plen = int(plen) + + if gw is None: + gw = "::" + if dev is None: + dev, ifaddr, x = self.route(gw) + else: + # TODO: do better than that + # replace that unique address by the list of all addresses + lifaddr = in6_getifaddr() + #filter(lambda x: x[2] == dev, lifaddr) + devaddrs = [ i for i in lifaddr if i[2] == dev] + ifaddr = construct_source_candidate_set(prefix, plen, devaddrs, LOOPBACK_NAME) + + return (prefix, plen, gw, dev, ifaddr) + + + def add(self, *args, **kargs): + """Ex: + add(dst="2001:db8:cafe:f000::/56") + add(dst="2001:db8:cafe:f000::/56", gw="2001:db8:cafe::1") + add(dst="2001:db8:cafe:f000::/64", gw="2001:db8:cafe::1", dev="eth0") + """ + self.invalidate_cache() + self.routes.append(self.make_route(*args, **kargs)) + + + def delt(self, dst, gw=None): + """ Ex: + delt(dst="::/0") + delt(dst="2001:db8:cafe:f000::/56") + delt(dst="2001:db8:cafe:f000::/56", gw="2001:db8:deca::1") + """ + tmp = dst+b"/128" + dst, plen = tmp.split(b'/')[:2] + dst = in6_ptop(dst) + plen = int(plen) + #l = filter(lambda x: in6_ptop(x[0]) == dst and x[1] == plen, self.routes) + l = [ x for x in self.routes if in6_ptop(x[0]) == dst and x[1] == plen ] + if gw: + gw = in6_ptop(gw) + #l = filter(lambda x: in6_ptop(x[0]) == gw, self.routes) + l = [ x for x in self.routes if in6_ptop(x[0]) == gw ] + if len(l) == 0: + warning("No matching route found") + elif len(l) > 1: + warning("Found more than one match. Aborting.") + else: + i=self.routes.index(l[0]) + self.invalidate_cache() + del(self.routes[i]) + + def ifchange(self, iff, addr): + the_addr, the_plen = (addr.split("/")+["128"])[:2] + the_plen = int(the_plen) + + naddr = inet_pton(socket.AF_INET6, the_addr) + nmask = in6_cidr2mask(the_plen) + the_net = inet_ntop(socket.AF_INET6, in6_and(nmask,naddr)) + + for i in range(len(self.routes)): + net,plen,gw,iface,addr = self.routes[i] + if iface != iff: + continue + if gw == '::': + self.routes[i] = (the_net,the_plen,gw,iface,the_addr) + else: + self.routes[i] = (net,the_plen,gw,iface,the_addr) + self.invalidate_cache() + ip6_neigh_cache.flush() + + def ifdel(self, iff): + """ removes all route entries that uses 'iff' interface. """ + new_routes=[] + for rt in self.routes: + if rt[3] != iff: + new_routes.append(rt) + self.invalidate_cache() + self.routes = new_routes + + + def ifadd(self, iff, addr): + """ + Add an interface 'iff' with provided address into routing table. + + Ex: ifadd('eth0', '2001:bd8:cafe:1::1/64') will add following entry into + Scapy6 internal routing table: + + Destination Next Hop iface Def src @ + 2001:bd8:cafe:1::/64 :: eth0 2001:bd8:cafe:1::1 + + prefix length value can be omitted. In that case, a value of 128 + will be used. + """ + addr, plen = (addr.split(b"/")+[b"128"])[:2] + addr = in6_ptop(addr) + plen = int(plen) + naddr = inet_pton(socket.AF_INET6, addr) + nmask = in6_cidr2mask(plen) + prefix = inet_ntop(socket.AF_INET6, in6_and(nmask,naddr)) + self.invalidate_cache() + self.routes.append((prefix,plen,'::',iff,[addr])) + + def route(self, dst, dev=None): + """ + Provide best route to IPv6 destination address, based on Scapy6 + internal routing table content. + + When a set of address is passed (e.g. 2001:db8:cafe:*::1-5) an address + of the set is used. Be aware of that behavior when using wildcards in + upper parts of addresses ! + + If 'dst' parameter is a FQDN, name resolution is performed and result + is used. + + if optional 'dev' parameter is provided a specific interface, filtering + is performed to limit search to route associated to that interface. + """ + # Transform "2001:db8:cafe:*::1-5:0/120" to one IPv6 address of the set + dst = dst.split("/")[0] + savedst = dst # In case following inet_pton() fails + dst = dst.replace("*","0") + l = dst.find("-") + while l >= 0: + m = (dst[l:]+":").find(":") + dst = dst[:l]+dst[l+m:] + l = dst.find("-") + + try: + inet_pton(socket.AF_INET6, dst) + except socket.error: + dst = socket.getaddrinfo(savedst, None, socket.AF_INET6)[0][-1][0] + # TODO : Check if name resolution went well + + # Deal with dev-specific request for cache search + k = dst + if dev is not None: + k = dst + "%%" + dev + if k in self.cache: + return self.cache[k] + + pathes = [] + + # TODO : review all kinds of addresses (scope and *cast) to see + # if we are able to cope with everything possible. I'm convinced + # it's not the case. + # -- arnaud + for p, plen, gw, iface, cset in self.routes: + if dev is not None and iface != dev: + continue + if in6_isincluded(dst, p, plen): + pathes.append((plen, (iface, cset, gw))) + elif (in6_ismlladdr(dst) and in6_islladdr(p) and in6_islladdr(cset[0])): + pathes.append((plen, (iface, cset, gw))) + + if not pathes: + warning("No route found for IPv6 destination %s (no default route?). This affects only IPv6" % dst) + return (LOOPBACK_NAME, "::", "::") # XXX Linux specific + + # Sort with longest prefix first + pathes.sort(reverse=True) + + best_plen = pathes[0][0] + pathes = filter(lambda x: x[0] == best_plen, pathes) + + res = [] + for p in pathes: # Here we select best source address for every route + tmp = p[1] + srcaddr = get_source_addr_from_candidate_set(dst, p[1][1]) + if srcaddr is not None: + res.append((p[0], (tmp[0], srcaddr, tmp[2]))) + + if res == []: + warning("Found a route for IPv6 destination '%s', but no possible source address. This affects only IPv6" % dst) + return (LOOPBACK_NAME, b"::", b"::") # XXX Linux specific + + # Symptom : 2 routes with same weight (our weight is plen) + # Solution : + # - dst is unicast global. Check if it is 6to4 and we have a source + # 6to4 address in those available + # - dst is link local (unicast or multicast) and multiple output + # interfaces are available. Take main one (conf.iface6) + # - if none of the previous or ambiguity persists, be lazy and keep + # first one + # XXX TODO : in a _near_ future, include metric in the game + + if len(res) > 1: + tmp = [] + if in6_isgladdr(dst) and in6_isaddr6to4(dst): + # TODO : see if taking the longest match between dst and + # every source addresses would provide better results + #tmp = filter(lambda x: in6_isaddr6to4(x[1][1]), res) + tmp = [ x for x in res if in6_isaddr6to4(x[1][1]) ] + elif in6_ismaddr(dst) or in6_islladdr(dst): + # TODO : I'm sure we are not covering all addresses. Check that + #tmp = filter(lambda x: x[1][0] == conf.iface6, res) + tmp = [ x for x in res if x[1][0] == conf.iface6 ] + + if tmp: + res = tmp + + # Fill the cache (including dev-specific request) + k = dst + if dev is not None: + k = dst + "%%" + dev + self.cache[k] = res[0][1] + + return res[0][1] + +conf.route6 = Route6() + +#TBD-hhaim no need for that +#_res = conf.route6.route("::/0") +_res = None; + +if _res: + iff, gw, addr = _res + conf.iface6 = iff +del(_res) + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/scapypipes.py b/scripts/external_libs/scapy-python3-0.18/scapy/scapypipes.py new file mode 100644 index 00000000..aa67277e --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/scapypipes.py @@ -0,0 +1,123 @@ +## 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 + +from .pipetool import Source,Drain,Sink +from .config import conf + + +class SniffSource(Source): + """Read packets from an interface and send them to low exit. + +-----------+ + >>-| |->> + | | + >-| [iface]--|-> + +-----------+ +""" + def __init__(self, iface=None, filter=None, name=None): + Source.__init__(self, name=name) + self.iface = iface + self.filter = filter + def start(self): + self.s = conf.L2listen(iface=self.iface, filter=self.filter) + def stop(self): + self.s.close() + def fileno(self): + return self.s.fileno() + def deliver(self): + self._send(self.s.recv()) + +class RdpcapSource(Source): + """Read packets from a PCAP file send them to low exit. + +----------+ + >>-| |->> + | | + >-| [pcap]--|-> + +----------+ +""" + def __init__(self, fname, name=None): + Source.__init__(self, name=name) + self.fname = fname + self.f = PcapReader(self.fname) + def start(self): + print("start") + self.f = PcapReader(self.fname) + self.is_exhausted = False + def stop(self): + print("stop") + self.f.close() + def fileno(self): + return self.f.fileno() + def deliver(self): + p = self.f.recv() + print("deliver %r" % p) + if p is None: + self.is_exhausted = True + else: + self._send(p) + + +class InjectSink(Sink): + """Packets received on low input are injected to an interface + +-----------+ + >>-| |->> + | | + >-|--[iface] |-> + +-----------+ +""" + def __init__(self, iface=None, name=None): + Sink.__init__(self, name=name) + if iface == None: + iface = conf.iface + self.iface = iface + def start(self): + self.s = conf.L2socket(iface=self.iface) + def stop(self): + self.s.close() + def push(self, msg): + self.s.send(msg) + +class Inject3Sink(InjectSink): + def start(self): + self.s = conf.L3socket(iface=self.iface) + + +class WrpcapSink(Sink): + """Packets received on low input are written to PCA file + +----------+ + >>-| |->> + | | + >-|--[pcap] |-> + +----------+ +""" + def __init__(self, fname, name=None): + Sink.__init__(self, name=name) + self.f = PcapWriter(fname) + def stop(self): + self.f.flush() + def push(self, msg): + self.f.write(msg) + + +class UDPDrain(Drain): + """Apply a function to messages on low and high entry + +-------------+ + >>-|--[payload]--|->> + | X | + >-|----[UDP]----|-> + +-------------+ +""" + def __init__(self, ip="127.0.0.1", port=1234): + Drain.__init__(self) + self.ip = ip + self.port = port + + def push(self, msg): + if IP in msg and msg[IP].proto == 17 and UDP in msg: + payload = msg[UDP].payload + self._high_send(str(payload)) + def high_push(self, msg): + p = IP(dst=self.ip)/UDP(sport=1234,dport=self.port)/msg + self._send(p) + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/sendrecv.py b/scripts/external_libs/scapy-python3-0.18/scapy/sendrecv.py new file mode 100644 index 00000000..8649c14d --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/sendrecv.py @@ -0,0 +1,678 @@ +## 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 + +""" +Functions to send and receive packets. +""" + +import pickle,os,sys,time,subprocess,itertools +from select import select +from .data import * +import scapy.arch +from .config import conf +from .packet import Gen +from .utils import warning,get_temp_file,PcapReader,wrpcap +from . import plist +from .error import log_runtime,log_interactive +from .base_classes import SetGen + +################# +## Debug class ## +################# + +class debug: + recv=[] + sent=[] + match=[] + + +#################### +## Send / Receive ## +#################### + + + + +def sndrcv(pks, pkt, timeout = None, inter = 0, verbose=None, chainCC=0, retry=0, multi=0): + if not isinstance(pkt, Gen): + pkt = SetGen(pkt) + + if verbose is None: + verbose = conf.verb + debug.recv = plist.PacketList([],"Unanswered") + debug.sent = plist.PacketList([],"Sent") + debug.match = plist.SndRcvList([]) + nbrecv=0 + ans = [] + # do it here to fix random fields, so that parent and child have the same + all_stimuli = tobesent = [p for p in pkt] + notans = len(tobesent) + + hsent={} + for i in tobesent: + h = i.hashret() + if h in hsent: + hsent[h].append(i) + else: + hsent[h] = [i] + if retry < 0: + retry = -retry + autostop=retry + else: + autostop=0 + + + while retry >= 0: + found=0 + + if timeout < 0: + timeout = None + + rdpipe,wrpipe = os.pipe() + rdpipe=os.fdopen(rdpipe, "rb") + wrpipe=os.fdopen(wrpipe,"wb") + + pid=1 + try: + pid = os.fork() + if pid == 0: + try: + sys.stdin.close() + rdpipe.close() + try: + i = 0 + if verbose: + print("Begin emission:") + for p in tobesent: + pks.send(p) + i += 1 + time.sleep(inter) + if verbose: + print("Finished to send %i packets." % i) + except SystemExit: + pass + except KeyboardInterrupt: + pass + except: + log_runtime.exception("--- Error in child %i" % os.getpid()) + log_runtime.info("--- Error in child %i" % os.getpid()) + finally: + try: + os.setpgrp() # Chance process group to avoid ctrl-C + sent_times = [p.sent_time for p in all_stimuli if p.sent_time] + pickle.dump( (conf.netcache,sent_times), wrpipe ) + wrpipe.close() + except: + pass + elif pid < 0: + log_runtime.error("fork error") + else: + wrpipe.close() + stoptime = 0 + remaintime = None + inmask = [rdpipe,pks] + try: + try: + while 1: + if stoptime: + remaintime = stoptime-time.time() + if remaintime <= 0: + break + r = None + if scapy.arch.FREEBSD or scapy.arch.DARWIN: + inp, out, err = select(inmask,[],[], 0.05) + if len(inp) == 0 or pks in inp: + r = pks.nonblock_recv() + else: + inp, out, err = select(inmask,[],[], remaintime) + if len(inp) == 0: + break + if pks in inp: + r = pks.recv(MTU) + if rdpipe in inp: + if timeout: + stoptime = time.time()+timeout + del(inmask[inmask.index(rdpipe)]) + if r is None: + continue + ok = 0 + h = r.hashret() + if h in hsent: + hlst = hsent[h] + for i in range(len(hlst)): + if r.answers(hlst[i]): + ans.append((hlst[i],r)) + if verbose > 1: + os.write(1, b"*") + ok = 1 + if not multi: + del(hlst[i]) + notans -= 1; + else: + if not hasattr(hlst[i], '_answered'): + notans -= 1; + hlst[i]._answered = 1; + break + if notans == 0 and not multi: + break + if not ok: + if verbose > 1: + os.write(1, b".") + nbrecv += 1 + if conf.debug_match: + debug.recv.append(r) + except KeyboardInterrupt: + if chainCC: + raise + finally: + try: + nc,sent_times = pickle.load(rdpipe) + except EOFError: + warning("Child died unexpectedly. Packets may have not been sent %i"%os.getpid()) + else: + conf.netcache.update(nc) + for p,t in zip(all_stimuli, sent_times): + p.sent_time = t + os.waitpid(pid,0) + finally: + if pid == 0: + os._exit(0) + + #remain = reduce(list.__add__, hsent.values(), []) + remain = list(itertools.chain(*[ i for i in hsent.values() ])) + if multi: + #remain = filter(lambda p: not hasattr(p, '_answered'), remain); + remain = [ p for p in remain if not hasattr(p, '_answered')] + + if autostop and len(remain) > 0 and len(remain) != len(tobesent): + retry = autostop + + tobesent = remain + if len(tobesent) == 0: + break + retry -= 1 + + if conf.debug_match: + debug.sent=plist.PacketList(remain[:],"Sent") + debug.match=plist.SndRcvList(ans[:]) + + #clean the ans list to delete the field _answered + if (multi): + for s,r in ans: + if hasattr(s, '_answered'): + del(s._answered) + + if verbose: + print("\nReceived %i packets, got %i answers, remaining %i packets" % (nbrecv+len(ans), len(ans), notans)) + return plist.SndRcvList(ans),plist.PacketList(remain,"Unanswered") + + +def __gen_send(s, x, inter=0, loop=0, count=None, verbose=None, realtime=None, *args, **kargs): + if type(x) is bytes: + x = conf.raw_layer(load=x) + if type(x) is str: + x = conf.raw_layer(load=x.encode('ascii')) + if not isinstance(x, Gen): + x = SetGen(x) + if verbose is None: + verbose = conf.verb + n = 0 + if count is not None: + loop = -count + elif not loop: + loop=-1 + try: + while loop: + dt0 = None + for p in x: + if realtime: + ct = time.time() + if dt0: + st = dt0+p.time-ct + if st > 0: + time.sleep(st) + else: + dt0 = ct-p.time + s.send(p) + n += 1 + if verbose: + os.write(1,b".") + time.sleep(inter) + if loop < 0: + loop += 1 + except KeyboardInterrupt: + pass + s.close() + if verbose: + print("\nSent %i packets." % n) + +@conf.commands.register +def send(x, inter=0, loop=0, count=None, verbose=None, realtime=None, *args, **kargs): + """Send packets at layer 3 +send(packets, [inter=0], [loop=0], [verbose=conf.verb]) -> None""" + __gen_send(conf.L3socket(*args, **kargs), x, inter=inter, loop=loop, count=count,verbose=verbose, realtime=realtime) + +@conf.commands.register +def sendp(x, inter=0, loop=0, iface=None, iface_hint=None, count=None, verbose=None, realtime=None, *args, **kargs): + """Send packets at layer 2 +sendp(packets, [inter=0], [loop=0], [verbose=conf.verb]) -> None""" + if iface is None and iface_hint is not None: + iface = conf.route.route(iface_hint)[0] + __gen_send(conf.L2socket(iface=iface, *args, **kargs), x, inter=inter, loop=loop, count=count, verbose=verbose, realtime=realtime) + +@conf.commands.register +def sendpfast(x, pps=None, mbps=None, realtime=None, loop=0, file_cache=False, iface=None, verbose=True): + """Send packets at layer 2 using tcpreplay for performance + pps: packets per second + mpbs: MBits per second + realtime: use packet's timestamp, bending time with realtime value + loop: number of times to process the packet list + file_cache: cache packets in RAM instead of reading from disk at each iteration + iface: output interface + verbose: if False, discard tcpreplay output """ + if iface is None: + iface = conf.iface + argv = [conf.prog.tcpreplay, "--intf1=%s" % iface ] + if pps is not None: + argv.append("--pps=%i" % pps) + elif mbps is not None: + argv.append("--mbps=%f" % mbps) + elif realtime is not None: + argv.append("--multiplier=%i" % realtime) + else: + argv.append("--topspeed") + if not verbose: + argv.append("-q") + if loop: + argv.append("--loop=%i" % loop) + if file_cache: + argv.append("--enable-file-cache") + + f = get_temp_file() + argv.append(f) + wrpcap(f, x) + with open(os.devnull, "wb") as null: + proc_output = null if not verbose else None + try: + subprocess.check_call(argv, + stdout=proc_output, + stderr=proc_output) + except KeyboardInterrupt: + log_interactive.info("Interrupted by user") + except Exception as e: + log_interactive.error("while trying to exec [%s]: %s" % (argv[0],e)) + finally: + os.unlink(f) + + + + + +@conf.commands.register +def sr(x,filter=None, iface=None, nofilter=0, *args,**kargs): + """Send and receive packets at layer 3 +nofilter: put 1 to avoid use of bpf filters +retry: if positive, how many times to resend unanswered packets + if negative, how many times to retry when no more packets are answered +timeout: how much time to wait after the last packet has been sent +verbose: set verbosity level +multi: whether to accept multiple answers for the same stimulus +filter: provide a BPF filter +iface: listen answers only on the given interface""" + if not "timeout" in kargs: + kargs["timeout"] = -1 + s = conf.L3socket(filter=filter, iface=iface, nofilter=nofilter) + a,b=sndrcv(s,x,*args,**kargs) + s.close() + return a,b + +@conf.commands.register +def sr1(x,filter=None,iface=None, nofilter=0, *args,**kargs): + """Send packets at layer 3 and return only the first answer +nofilter: put 1 to avoid use of bpf filters +retry: if positive, how many times to resend unanswered packets + if negative, how many times to retry when no more packets are answered +timeout: how much time to wait after the last packet has been sent +verbose: set verbosity level +multi: whether to accept multiple answers for the same stimulus +filter: provide a BPF filter +iface: listen answers only on the given interface""" + if not "timeout" in kargs: + kargs["timeout"] = -1 + s=conf.L3socket(filter=filter, nofilter=nofilter, iface=iface) + a,b=sndrcv(s,x,*args,**kargs) + s.close() + if len(a) > 0: + return a[0][1] + else: + return None + +@conf.commands.register +def srp(x,iface=None, iface_hint=None, filter=None, nofilter=0, type=ETH_P_ALL, *args,**kargs): + """Send and receive packets at layer 2 +nofilter: put 1 to avoid use of bpf filters +retry: if positive, how many times to resend unanswered packets + if negative, how many times to retry when no more packets are answered +timeout: how much time to wait after the last packet has been sent +verbose: set verbosity level +multi: whether to accept multiple answers for the same stimulus +filter: provide a BPF filter +iface: work only on the given interface""" + if not "timeout" in kargs: + kargs["timeout"] = -1 + if iface is None and iface_hint is not None: + iface = conf.route.route(iface_hint)[0] + s = conf.L2socket(iface=iface, filter=filter, nofilter=nofilter, type=type) + a,b=sndrcv(s ,x,*args,**kargs) + s.close() + return a,b + +@conf.commands.register +def srp1(*args,**kargs): + """Send and receive packets at layer 2 and return only the first answer +nofilter: put 1 to avoid use of bpf filters +retry: if positive, how many times to resend unanswered packets + if negative, how many times to retry when no more packets are answered +timeout: how much time to wait after the last packet has been sent +verbose: set verbosity level +multi: whether to accept multiple answers for the same stimulus +filter: provide a BPF filter +iface: work only on the given interface""" + if not "timeout" in kargs: + kargs["timeout"] = -1 + a,b=srp(*args,**kargs) + if len(a) > 0: + return a[0][1] + else: + return None + +def __sr_loop(srfunc, pkts, prn=lambda x:x[1].summary(), prnfail=lambda x:x.summary(), inter=1, timeout=None, count=None, verbose=None, store=1, *args, **kargs): + n = 0 + r = 0 + ct = conf.color_theme + if verbose is None: + verbose = conf.verb + parity = 0 + ans=[] + unans=[] + if timeout is None: + timeout = min(2*inter, 5) + try: + while 1: + parity ^= 1 + col = [ct.even,ct.odd][parity] + if count is not None: + if count == 0: + break + count -= 1 + start = time.time() + print("\rsend...\r", end = " ") + res = srfunc(pkts, timeout=timeout, verbose=0, chainCC=1, *args, **kargs) + n += len(res[0])+len(res[1]) + r += len(res[0]) + if verbose > 1 and prn and len(res[0]) > 0: + msg = "RECV %i:" % len(res[0]) + print( "\r"+ct.success(msg), end = " ") + for p in res[0]: + print(col(prn(p))) + print(" "*len(msg), end = " ") + if verbose > 1 and prnfail and len(res[1]) > 0: + msg = "fail %i:" % len(res[1]) + print("\r"+ct.fail(msg), end = " ") + for p in res[1]: + print(col(prnfail(p))) + print(" "*len(msg), end = " ") + if verbose > 1 and not (prn or prnfail): + print("recv:%i fail:%i" % tuple(map(len, res[:2]))) + if store: + ans += res[0] + unans += res[1] + end=time.time() + if end-start < inter: + time.sleep(inter+start-end) + except KeyboardInterrupt: + pass + + if verbose and n>0: + print(ct.normal("\nSent %i packets, received %i packets. %3.1f%% hits." % (n,r,100.0*r/n))) + return plist.SndRcvList(ans),plist.PacketList(unans) + +@conf.commands.register +def srloop(pkts, *args, **kargs): + """Send a packet at layer 3 in loop and print the answer each time +srloop(pkts, [prn], [inter], [count], ...) --> None""" + return __sr_loop(sr, pkts, *args, **kargs) + +@conf.commands.register +def srploop(pkts, *args, **kargs): + """Send a packet at layer 2 in loop and print the answer each time +srloop(pkts, [prn], [inter], [count], ...) --> None""" + return __sr_loop(srp, pkts, *args, **kargs) + + +#def sndrcvflood(pks, pkt, prn=lambda (s,r):r.summary(), chainCC=0, store=1, unique=0): +def sndrcvflood(pks, pkt, prn=lambda a:a[1].summary(), chainCC=0, store=1, unique=0): + if not isinstance(pkt, Gen): + pkt = SetGen(pkt) + tobesent = [p for p in pkt] + received = plist.SndRcvList() + seen = {} + + hsent={} + for i in tobesent: + h = i.hashret() + if h in hsent: + hsent[h].append(i) + else: + hsent[h] = [i] + + def send_in_loop(tobesent): + while 1: + for p in tobesent: + yield p + + packets_to_send = send_in_loop(tobesent) + + ssock = rsock = pks.fileno() + + try: + while 1: + readyr,readys,_ = select([rsock],[ssock],[]) + if ssock in readys: + pks.send(next(packets_to_send)) + + if rsock in readyr: + p = pks.recv(MTU) + if p is None: + continue + h = p.hashret() + if h in hsent: + hlst = hsent[h] + for i in hlst: + if p.answers(i): + res = prn((i,p)) + if unique: + if res in seen: + continue + seen[res] = None + if res is not None: + print(res) + if store: + received.append((i,p)) + except KeyboardInterrupt: + if chainCC: + raise + return received + +@conf.commands.register +def srflood(x,filter=None, iface=None, nofilter=None, *args,**kargs): + """Flood and receive packets at layer 3 +prn: function applied to packets received. Ret val is printed if not None +store: if 1 (default), store answers and return them +unique: only consider packets whose print +nofilter: put 1 to avoid use of bpf filters +filter: provide a BPF filter +iface: listen answers only on the given interface""" + s = conf.L3socket(filter=filter, iface=iface, nofilter=nofilter) + r=sndrcvflood(s,x,*args,**kargs) + s.close() + return r + +@conf.commands.register +def srpflood(x,filter=None, iface=None, iface_hint=None, nofilter=None, *args,**kargs): + """Flood and receive packets at layer 2 +prn: function applied to packets received. Ret val is printed if not None +store: if 1 (default), store answers and return them +unique: only consider packets whose print +nofilter: put 1 to avoid use of bpf filters +filter: provide a BPF filter +iface: listen answers only on the given interface""" + if iface is None and iface_hint is not None: + iface = conf.route.route(iface_hint)[0] + s = conf.L2socket(filter=filter, iface=iface, nofilter=nofilter) + r=sndrcvflood(s,x,*args,**kargs) + s.close() + return r + + + + +@conf.commands.register +def sniff(count=0, store=1, offline=None, prn = None, lfilter=None, L2socket=None, timeout=None, + opened_socket=None, stop_filter=None, *arg, **karg): + """Sniff packets +sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2ListenSocket args) -> list of packets + + count: number of packets to capture. 0 means infinity + store: wether to store sniffed packets or discard them + prn: function to apply to each packet. If something is returned, + it is displayed. Ex: + ex: prn = lambda x: x.summary() +lfilter: python function applied to each packet to determine + if further action may be done + ex: lfilter = lambda x: x.haslayer(Padding) +offline: pcap file to read packets from, instead of sniffing them +timeout: stop sniffing after a given time (default: None) +L2socket: use the provided L2socket +opened_socket: provide an object ready to use .recv() on +stop_filter: python function applied to each packet to determine + if we have to stop the capture after this packet + ex: stop_filter = lambda x: x.haslayer(TCP) + """ + c = 0 + + if opened_socket is not None: + s = opened_socket + else: + if offline is None: + if L2socket is None: + L2socket = conf.L2listen + s = L2socket(type=ETH_P_ALL, *arg, **karg) + else: + s = PcapReader(offline) + + lst = [] + if timeout is not None: + stoptime = time.time()+timeout + remain = None + try: + while 1: + if timeout is not None: + remain = stoptime-time.time() + if remain <= 0: + break + sel = select([s],[],[],remain) + if s in sel[0]: + p = s.recv(MTU) + if p is None: + break + if lfilter and not lfilter(p): + continue + if store: + lst.append(p) + c += 1 + if prn: + r = prn(p) + if r is not None: + print(r) + if stop_filter and stop_filter(p): + break + if count > 0 and c >= count: + break + except KeyboardInterrupt: + pass + if opened_socket is None: + s.close() + return plist.PacketList(lst,"Sniffed") + + +@conf.commands.register +def bridge_and_sniff(if1, if2, count=0, store=1, offline=None, prn = None, lfilter=None, L2socket=None, timeout=None, + stop_filter=None, *args, **kargs): + """Forward traffic between two interfaces and sniff packets exchanged +bridge_and_sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2Socket args) -> list of packets + + count: number of packets to capture. 0 means infinity + store: wether to store sniffed packets or discard them + prn: function to apply to each packet. If something is returned, + it is displayed. Ex: + ex: prn = lambda x: x.summary() +lfilter: python function applied to each packet to determine + if further action may be done + ex: lfilter = lambda x: x.haslayer(Padding) +timeout: stop sniffing after a given time (default: None) +L2socket: use the provided L2socket +stop_filter: python function applied to each packet to determine + if we have to stop the capture after this packet + ex: stop_filter = lambda x: x.haslayer(TCP) + """ + c = 0 + if L2socket is None: + L2socket = conf.L2socket + s1 = L2socket(iface=if1) + s2 = L2socket(iface=if2) + peerof={s1:s2,s2:s1} + label={s1:if1, s2:if2} + + lst = [] + if timeout is not None: + stoptime = time.time()+timeout + remain = None + try: + while True: + if timeout is not None: + remain = stoptime-time.time() + if remain <= 0: + break + ins,outs,errs = select([s1,s2],[],[], remain) + for s in ins: + p = s.recv() + if p is not None: + peerof[s].send(p.original) + if lfilter and not lfilter(p): + continue + if store: + p.sniffed_on = label[s] + lst.append(p) + c += 1 + if prn: + r = prn(p) + if r is not None: + print("%s: %s" % (label[s],r)) + if stop_filter and stop_filter(p): + break + if count > 0 and c >= count: + break + except KeyboardInterrupt: + pass + finally: + return plist.PacketList(lst,"Sniffed") + + +@conf.commands.register +def tshark(*args,**kargs): + """Sniff packets and print them calling pkt.show(), a bit like text wireshark""" + sniff(prn=lambda x: x.display(),*args,**kargs) + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/supersocket.py b/scripts/external_libs/scapy-python3-0.18/scapy/supersocket.py new file mode 100644 index 00000000..b87f9c16 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/supersocket.py @@ -0,0 +1,141 @@ +## 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 + +""" +SuperSocket. +""" + +import socket,time +from .config import conf +from .data import * +from scapy.error import warning, log_runtime + +class _SuperSocket_metaclass(type): + def __repr__(self): + if self.desc is not None: + return "<%s: %s>" % (self.__name__,self.desc) + else: + return "<%s>" % self.__name__ + + +class SuperSocket(metaclass = _SuperSocket_metaclass): + desc = None + closed=0 + def __init__(self, family=socket.AF_INET,type=socket.SOCK_STREAM, proto=0): + self.ins = socket.socket(family, type, proto) + self.outs = self.ins + self.promisc=None + def send(self, x): + sx = bytes(x) + if hasattr(x, "sent_time"): + x.sent_time = time.time() + return self.outs.send(sx) + def recv(self, x=MTU): + return conf.raw_layer(self.ins.recv(x)) + def fileno(self): + return self.ins.fileno() + def close(self): + if self.closed: + return + self.closed=1 + if self.ins != self.outs: + if self.outs and self.outs.fileno() != -1: + self.outs.close() + if self.ins and self.ins.fileno() != -1: + self.ins.close() + def sr(self, *args, **kargs): + return sendrecv.sndrcv(self, *args, **kargs) + def sr1(self, *args, **kargs): + a,b = sendrecv.sndrcv(self, *args, **kargs) + if len(a) > 0: + return a[0][1] + else: + return None + def sniff(self, *args, **kargs): + return sendrecv.sniff(opened_socket=self, *args, **kargs) + +class L3RawSocket(SuperSocket): + desc = "Layer 3 using Raw sockets (PF_INET/SOCK_RAW)" + def __init__(self, type = ETH_P_IP, filter=None, iface=None, promisc=None, nofilter=0): + self.outs = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW) + self.outs.setsockopt(socket.SOL_IP, socket.IP_HDRINCL, 1) + self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) + if iface is not None: + self.ins.bind((iface, type)) + def recv(self, x=MTU): + pkt, sa_ll = self.ins.recvfrom(x) + if sa_ll[2] == socket.PACKET_OUTGOING: + return None + if sa_ll[3] in conf.l2types: + cls = conf.l2types[sa_ll[3]] + lvl = 2 + elif sa_ll[1] in conf.l3types: + cls = conf.l3types[sa_ll[1]] + lvl = 3 + else: + cls = conf.default_l2 + warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using %s" % (sa_ll[0],sa_ll[1],sa_ll[3],cls.name)) + lvl = 3 + + try: + pkt = cls(pkt) + except KeyboardInterrupt: + raise + except: + if conf.debug_dissector: + raise + pkt = conf.raw_layer(pkt) + if lvl == 2: + pkt = pkt.payload + + if pkt is not None: + from arch import get_last_packet_timestamp + pkt.time = get_last_packet_timestamp(self.ins) + return pkt + def send(self, x): + try: + #sx = str(x) + sx = x + x.sent_time = time.time() + self.outs.sendto(sx,(x.dst,0)) + except socket.error as msg: + log_runtime.error(msg) + +class SimpleSocket(SuperSocket): + desc = "wrapper arround a classic socket" + def __init__(self, sock): + self.ins = sock + self.outs = sock + + +class StreamSocket(SimpleSocket): + desc = "transforms a stream socket into a layer 2" + def __init__(self, sock, basecls=None): + if basecls is None: + basecls = conf.raw_layer + SimpleSocket.__init__(self, sock) + self.basecls = basecls + + def recv(self, x=MTU): + pkt = self.ins.recv(x, socket.MSG_PEEK) + x = len(pkt) + if x == 0: + raise socket.error((100,"Underlying stream socket tore down")) + pkt = self.basecls(pkt) + pad = pkt.getlayer(conf.padding_layer) + if pad is not None and pad.underlayer is not None: + del(pad.underlayer.payload) + while pad is not None and not isinstance(pad, NoPayload): + x -= len(pad.load) + pad = pad.payload + self.ins.recv(x) + return pkt + + + +if conf.L3socket is None: + conf.L3socket = L3RawSocket + +import scapy.sendrecv diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/themes.py b/scripts/external_libs/scapy-python3-0.18/scapy/themes.py new file mode 100644 index 00000000..f519ad7e --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/themes.py @@ -0,0 +1,277 @@ +## 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 + +""" +Color themes for the interactive console. +""" + +################## +## Color themes ## +################## + +class Color: + normal = "\033[0m" + black = "\033[30m" + red = "\033[31m" + green = "\033[32m" + yellow = "\033[33m" + blue = "\033[34m" + purple = "\033[35m" + cyan = "\033[36m" + grey = "\033[37m" + + bold = "\033[1m" + uline = "\033[4m" + blink = "\033[5m" + invert = "\033[7m" + + +def create_styler(fmt=None, before="", after="", fmt2="%s"): + def do_style(val, fmt=fmt, before=before, after=after, fmt2=fmt2): + if fmt is None: + if type(val) is not str: + val = str(val) + else: + val = fmt % val + return fmt2 % (before+val+after) + return do_style + +class ColorTheme: + def __repr__(self): + return "<%s>" % self.__class__.__name__ + def __getattr__(self, attr): + return create_styler() + + +class NoTheme(ColorTheme): + pass + + +class AnsiColorTheme(ColorTheme): + def __getattr__(self, attr): + if attr.startswith("__"): + raise AttributeError(attr) + s = "style_%s" % attr + if s in self.__class__.__dict__: + before = getattr(self, s) + after = self.style_normal + else: + before = after = "" + + return create_styler(before=before, after=after) + + + style_normal = "" + style_prompt = "" + style_punct = "" + style_id = "" + style_not_printable = "" + style_layer_name = "" + style_field_name = "" + style_field_value = "" + style_emph_field_name = "" + style_emph_field_value = "" + style_packetlist_name = "" + style_packetlist_proto = "" + style_packetlist_value = "" + style_fail = "" + style_success = "" + style_odd = "" + style_even = "" + style_opening = "" + style_active = "" + style_closed = "" + style_left = "" + style_right = "" + +class BlackAndWhite(AnsiColorTheme): + pass + +class DefaultTheme(AnsiColorTheme): + style_normal = Color.normal + style_prompt = Color.blue+Color.bold + style_punct = Color.normal + style_id = Color.blue+Color.bold + style_not_printable = Color.grey + style_layer_name = Color.red+Color.bold + style_field_name = Color.blue + style_field_value = Color.purple + style_emph_field_name = Color.blue+Color.uline+Color.bold + style_emph_field_value = Color.purple+Color.uline+Color.bold + style_packetlist_name = Color.red+Color.bold + style_packetlist_proto = Color.blue + style_packetlist_value = Color.purple + style_fail = Color.red+Color.bold + style_success = Color.blue+Color.bold + style_even = Color.black+Color.bold + style_odd = Color.black + style_opening = Color.yellow + style_active = Color.black + style_closed = Color.grey + style_left = Color.blue+Color.invert + style_right = Color.red+Color.invert + +class BrightTheme(AnsiColorTheme): + style_normal = Color.normal + style_punct = Color.normal + style_id = Color.yellow+Color.bold + style_layer_name = Color.red+Color.bold + style_field_name = Color.yellow+Color.bold + style_field_value = Color.purple+Color.bold + style_emph_field_name = Color.yellow+Color.bold + style_emph_field_value = Color.green+Color.bold + style_packetlist_name = Color.red+Color.bold + style_packetlist_proto = Color.yellow+Color.bold + style_packetlist_value = Color.purple+Color.bold + style_fail = Color.red+Color.bold + style_success = Color.blue+Color.bold + style_even = Color.black+Color.bold + style_odd = Color.black + style_left = Color.cyan+Color.invert + style_right = Color.purple+Color.invert + + +class RastaTheme(AnsiColorTheme): + style_normal = Color.normal+Color.green+Color.bold + style_prompt = Color.yellow+Color.bold + style_punct = Color.red + style_id = Color.green+Color.bold + style_not_printable = Color.green + style_layer_name = Color.red+Color.bold + style_field_name = Color.yellow+Color.bold + style_field_value = Color.green+Color.bold + style_emph_field_name = Color.green + style_emph_field_value = Color.green + style_packetlist_name = Color.red+Color.bold + style_packetlist_proto = Color.yellow+Color.bold + style_packetlist_value = Color.green+Color.bold + style_fail = Color.red + style_success = Color.red+Color.bold + style_even = Color.yellow + style_odd = Color.green + style_left = Color.yellow+Color.invert + style_right = Color.red+Color.invert + +class ColorOnBlackTheme(AnsiColorTheme): + """Color theme for black backgrounds""" + style_normal = Color.normal + style_prompt = Color.green+Color.bold + style_punct = Color.normal + style_id = Color.green + style_not_printable = Color.black+Color.bold + style_layer_name = Color.yellow+Color.bold + style_field_name = Color.cyan + style_field_value = Color.purple+Color.bold + style_emph_field_name = Color.cyan+Color.bold + style_emph_field_value = Color.red+Color.bold + style_packetlist_name = Color.black+Color.bold + style_packetlist_proto = Color.yellow+Color.bold + style_packetlist_value = Color.purple+Color.bold + style_fail = Color.red+Color.bold + style_success = Color.green + style_even = Color.black+Color.bold + style_odd = Color.grey + style_opening = Color.yellow + style_active = Color.grey+Color.bold + style_closed = Color.black+Color.bold + style_left = Color.cyan+Color.bold + style_right = Color.red+Color.bold + + +class FormatTheme(ColorTheme): + def __getattr__(self, attr): + if attr.startswith("__"): + raise AttributeError(attr) + colfmt = self.__class__.__dict__.get("style_%s" % attr, "%s") + return create_styler(fmt2 = colfmt) + +class LatexTheme(FormatTheme): + style_prompt = r"\textcolor{blue}{%s}" + style_not_printable = r"\textcolor{gray}{%s}" + style_layer_name = r"\textcolor{red}{\bf %s}" + style_field_name = r"\textcolor{blue}{%s}" + style_field_value = r"\textcolor{purple}{%s}" + style_emph_field_name = r"\textcolor{blue}{\underline{%s}}" #ul + style_emph_field_value = r"\textcolor{purple}{\underline{%s}}" #ul + style_packetlist_name = r"\textcolor{red}{\bf %s}" + style_packetlist_proto = r"\textcolor{blue}{%s}" + style_packetlist_value = r"\textcolor{purple}{%s}" + style_fail = r"\textcolor{red}{\bf %s}" + style_success = r"\textcolor{blue}{\bf %s}" + style_left = r"\textcolor{blue}{%s}" + style_right = r"\textcolor{red}{%s}" +# style_even = r"}{\bf " +# style_odd = "" + +class LatexTheme2(FormatTheme): + style_prompt = r"@`@textcolor@[@blue@]@@[@%s@]@" + style_not_printable = r"@`@textcolor@[@gray@]@@[@%s@]@" + style_layer_name = r"@`@textcolor@[@red@]@@[@@`@bfseries@[@@]@%s@]@" + style_field_name = r"@`@textcolor@[@blue@]@@[@%s@]@" + style_field_value = r"@`@textcolor@[@purple@]@@[@%s@]@" + style_emph_field_name = r"@`@textcolor@[@blue@]@@[@@`@underline@[@%s@]@@]@" + style_emph_field_value = r"@`@textcolor@[@purple@]@@[@@`@underline@[@%s@]@@]@" + style_packetlist_name = r"@`@textcolor@[@red@]@@[@@`@bfseries@[@@]@%s@]@" + style_packetlist_proto = r"@`@textcolor@[@blue@]@@[@%s@]@" + style_packetlist_value = r"@`@textcolor@[@purple@]@@[@%s@]@" + style_fail = r"@`@textcolor@[@red@]@@[@@`@bfseries@[@@]@%s@]@" + style_success = r"@`@textcolor@[@blue@]@@[@@`@bfserices@[@@]@%s@]@" + style_even = r"@`@textcolor@[@gray@]@@[@@`@bfseries@[@@]@%s@]@" +# style_odd = r"@`@textcolor@[@black@]@@[@@`@bfseries@[@@]@%s@]@" + style_left = r"@`@textcolor@[@blue@]@@[@%s@]@" + style_right = r"@`@textcolor@[@red@]@@[@%s@]@" + +class HTMLTheme(FormatTheme): + style_prompt = "%s" + style_not_printable = "%s" + style_layer_name = "%s" + style_field_name = "%s" + style_field_value = "%s" + style_emph_field_name = "%s" + style_emph_field_value = "%s" + style_packetlist_name = "%s" + style_packetlist_proto = "%s" + style_packetlist_value = "%s" + style_fail = "%s" + style_success = "%s" + style_even = "%s" + style_odd = "%s" + style_left = "%s" + style_right = "%s" + +class HTMLTheme2(HTMLTheme): + style_prompt = "#[#span class=prompt#]#%s#[#/span#]#" + style_not_printable = "#[#span class=not_printable#]#%s#[#/span#]#" + style_layer_name = "#[#span class=layer_name#]#%s#[#/span#]#" + style_field_name = "#[#span class=field_name#]#%s#[#/span#]#" + style_field_value = "#[#span class=field_value#]#%s#[#/span#]#" + style_emph_field_name = "#[#span class=emph_field_name#]#%s#[#/span#]#" + style_emph_field_value = "#[#span class=emph_field_value#]#%s#[#/span#]#" + style_packetlist_name = "#[#span class=packetlist_name#]#%s#[#/span#]#" + style_packetlist_proto = "#[#span class=packetlist_proto#]#%s#[#/span#]#" + style_packetlist_value = "#[#span class=packetlist_value#]#%s#[#/span#]#" + style_fail = "#[#span class=fail#]#%s#[#/span#]#" + style_success = "#[#span class=success#]#%s#[#/span#]#" + style_even = "#[#span class=even#]#%s#[#/span#]#" + style_odd = "#[#span class=odd#]#%s#[#/span#]#" + style_left = "#[#span class=left#]#%s#[#/span#]#" + style_right = "#[#span class=right#]#%s#[#/span#]#" + + +class ColorPrompt: + __prompt = ">>> " + def __str__(self): + try: + ct = scapy.config.conf.color_theme + if isinstance(ct, AnsiColorTheme): + ## ^A and ^B delimit invisible caracters for readline to count right + return "\001%s\002" % ct.prompt("\002"+scapy.config.conf.prompt+"\001") + else: + return ct.prompt(scapy.config.conf.prompt) + except: + return self.__prompt + + +import scapy.config diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/tools/UTscapy.py b/scripts/external_libs/scapy-python3-0.18/scapy/tools/UTscapy.py new file mode 100644 index 00000000..212aa123 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/tools/UTscapy.py @@ -0,0 +1,677 @@ +## 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 + +""" +Unit testing infrastructure for Scapy +""" + +import sys,getopt,imp +import bz2, base64, os.path, time, traceback, zlib, hashlib + + +#### Import tool #### + +def import_module(name): + name = os.path.realpath(name) + thepath = os.path.dirname(name) + name = os.path.basename(name) + if name.endswith(".py"): + name = name[:-3] + f,path,desc = imp.find_module(name,[thepath]) + + try: + return imp.load_module(name, f, path, desc) + finally: + if f: + f.close() + + +#### INTERNAL/EXTERNAL FILE EMBEDDING #### + +class File: + def __init__(self, name, URL, local): + self.name = name + self.local = local + self.URL = URL + def get_local(self): + return bz2.decompress(base64.decodestring(self.local)) + def get_URL(self): + return URL + def write(self, dir): + if dir: + dir += "/" + open(dir+self.name,"w").write(self.get_local()) + + +# Embed a base64 encoded bziped version of js and css files +# to work if you can't reach Internet. +class External_Files: + UTscapy_js = File("UTscapy.js", "http://www.secdev.org/projects/UTscapy/UTscapy.js", +"""QlpoOTFBWSZTWWVijKQAAXxfgERUYOvAChIhBAC/79+qQAH8AFA0poANAMjQAAAG +ABo0NGEZNBo00BhgAaNDRhGTQaNNAYFURJinplGaKbRkJiekzSenqmpA0Gm1LFMp +RUklVQlK9WUTZYpNFI1IiEWEFT09Sfj5uO+qO6S5DQwKIxM92+Zku94wL6V/1KTK +an2c66Ug6SmVKy1ZIrgauxMVLF5xLH0lJRQuKlqLF10iatlTzqvw7S9eS3+h4lu3 +GZyMgoOude3NJ1pQy8eo+X96IYZw+ynehsiPj73m0rnvQ3QXZ9BJQiZQYQ5/uNcl +2WOlC5vyQqV/BWsnr2NZYLYXQLDs/Bffk4ZfR4/SH6GfA5Xlek4xHNHqbSsRbREO +gueXo3kcYi94K6hSO3ldD2O/qJXOFqJ8o3TE2aQahxtQpCVUKQMvODHwu2YkaORY +ZC6gihEallcHDIAtRPScBACAJnUggYhLDX6DEko7nC9GvAw5OcEkiyDUbLdiGCzD +aXWMC2DuQ2Y6sGf6NcRuON7QSbhHsPc4KKmZ/xdyRThQkGVijKQ=""") + UTscapy_css = File("UTscapy.css","http://www.secdev.org/projects/UTscapy/UTscapy.css", +"""QlpoOTFBWSZTWTbBCNEAAE7fgHxwSB//+Cpj2QC//9/6UAR+63dxbNzO3ccmtGEk +pM0m1I9E/Qp6g9Q09TNQ9QDR6gMgAkiBFG9U9TEGRkGgABoABoBmpJkRAaAxD1AN +Gh6gNADQBzAATJgATCYJhDAEYAEiQkwIyJk0n6qenpqeoaMUeo9RgIxp6pX78kfx +Jx4MUhDHKEb2pJAYAelG1cybiZBBDipH8ocxNyHDAqTUxiQmIAEDE3ApIBUUECAT +7Lvlf4xA/sVK0QHkSlYtT0JmErdOjx1v5NONPYSjrIhQnbl1MbG5m+InMYmVAWJp +uklD9cNdmQv2YigxbEtgUrsY2pDDV/qMT2SHnHsViu2rrp2LA01YJIHZqjYCGIQN +sGNobFxAYHLqqMOj9TI2Y4GRpRCUGu82PnMnXUBgDSkTY4EfmygaqvUwbGMbPwyE +220Q4G+sDvw7+6in3CAOS634pcOEAdREUW+QqMjvWvECrGISo1piv3vqubTGOL1c +ssrFnnSfU4T6KSCbPs98HJ2yjWN4i8Bk5WrM/JmELLNeZ4vgMkA4JVQInNnWTUTe +gmMSlJd/b7JuRwiM5RUzXOBTa0e3spO/rsNJiylu0rCxygdRo2koXdSJzmUVjJUm +BOFIkUKq8LrE+oT9h2qUqqUQ25fGV7e7OFkpmZopqUi0WeIBzlXdYY0Zz+WUJUTC +RC+CIPFIYh1RkopswMAop6ZjuZKRqR0WNuV+rfuF5aCXPpxAm0F14tPyhf42zFMT +GJUMxxowJnoauRq4xGQk+2lYFxbQ0FiC43WZSyYLHMuo5NTJ92QLAgs4FgOyZQqQ +xpsGKMA0cIisNeiootpnlWQvkPzNGUTPg8jqkwTvqQLguZLKJudha1hqfBib1IfO +LNChcU6OqF+3wyPKg5Y5oSbSJPAMcRDANwmS2i9oZm6vsD1pLkWtFGbAkEjjCuEU +W1ev1IsF2UVmWYFtJkqLT708ApUBK/ig3rbJWSq7RGQd3sSrOKu3lyKzTBdkXK2a +BGLV5dS1XURdKxaRkMplLLQxsimBYZEAa8KQkYyI+4EagMqycRR7RgwtZFxJSu0T +1q5wS2JG82iETHplbNj8DYo9IkmKzNAiw4FxK8bRfIYvwrbshbEagL11AQJFsqeZ +WeXDoWEx2FMyyZRAB5QyCFnwYtwtWAQmmITY8aIM2SZyRnHH9Wi8+Sr2qyCscFYo +vzM985aHXOHAxQN2UQZbQkUv3D4Vc+lyvalAffv3Tyg4ks3a22kPXiyeCGweviNX +0K8TKasyOhGsVamTUAZBXfQVw1zmdS4rHDnbHgtIjX3DcCt6UIr0BHTYjdV0JbPj +r1APYgXihjQwM2M83AKIhwQQJv/F3JFOFCQNsEI0QA==""") + def get_local_dict(cls): + #return dict(map(lambda (x,y): (x, y.name), filter(lambda (x,y): isinstance(y, File), cls.__dict__.items()))) + return dict(map(lambda a: (a[0], a[1].name), filter(lambda a: isinstance(a[1], File), cls.__dict__.items()))) + get_local_dict = classmethod(get_local_dict) + def get_URL_dict(cls): + #return dict(map(lambda (x,y): (x, y.URL), filter(lambda (x,y): isinstance(y, File), cls.__dict__.items()))) + return dict(map(lambda a: (a[0], a[1].URL), filter(lambda a: isinstance(a[1], File), cls.__dict__.items()))) + get_URL_dict = classmethod(get_URL_dict) + + +#### HELPER CLASSES FOR PARAMETRING OUTPUT FORMAT #### + +class EnumClass: + def from_string(cls,x): + return cls.__dict__[x.upper()] + from_string = classmethod(from_string) + +class Format(EnumClass): + TEXT = 1 + ANSI = 2 + HTML = 3 + LATEX = 4 + XUNIT = 5 + + +#### TEST CLASSES #### + +class TestClass: + def __getitem__(self, item): + return getattr(self, item) + def add_keywords(self, kw): + if kw is str: + self.keywords.append(kw) + else: + self.keywords += kw + +class TestCampaign(TestClass): + def __init__(self, title): + self.title = title + self.filename = None + self.headcomments = "" + self.campaign = [] + self.keywords = [] + self.crc = None + self.sha = None + self.preexec = None + self.preexec_output = None + def add_testset(self, testset): + self.campaign.append(testset) + def __iter__(self): + return self.campaign.__iter__() + def all_tests(self): + for ts in self: + for t in ts: + yield t + +class TestSet(TestClass): + def __init__(self, name): + self.name = name + self.set = [] + self.comments = "" + self.keywords = [] + self.crc = None + self.expand = 1 + def add_test(self, test): + self.set.append(test) + def __iter__(self): + return self.set.__iter__() + +class UnitTest(TestClass): + def __init__(self, name): + self.name = name + self.test = "" + self.comments = "" + self.result = "" + self.res = True # must be True at init to have a different truth value than None + self.output = "" + self.num = -1 + self.keywords = [] + self.crc = None + self.expand = 1 + def __nonzero__(self): + return self.res + + +#### PARSE CAMPAIGN #### + +def parse_campaign_file(campaign_file): + test_campaign = TestCampaign("Test campaign") + test_campaign.filename= campaign_file.name + testset = None + test = None + testnb = 0 + + for l in campaign_file.readlines(): + if l[0] == '#': + continue + if l[0] == "~": + (test or testset or campaign_file).add_keywords(l[1:].split()) + elif l[0] == "%": + test_campaign.title = l[1:].strip() + elif l[0] == "+": + testset = TestSet(l[1:].strip()) + test_campaign.add_testset(testset) + test = None + elif l[0] == "=": + test = UnitTest(l[1:].strip()) + test.num = testnb + testnb += 1 + testset.add_test(test) + elif l[0] == "*": + if test is not None: + + test.comments += l[1:] + elif testset is not None: + testset.comments += l[1:] + else: + test_campaign.headcomments += l[1:] + else: + if test is None: + if l.strip(): + print("Unknown content [%s]" % l.strip(), file = sys.stderr) + else: + test.test += l + return test_campaign + +def dump_campaign(test_campaign): + print("#"*(len(test_campaign.title)+6)) + print("## %(title)s ##" % test_campaign) + print("#"*(len(test_campaign.title)+6)) + if test_campaign.sha and test_campaign.crc: + print("CRC=[%(crc)s] SHA=[%(sha)s]" % test_campaign) + print("from file %(filename)s" % test_campaign) + print() + for ts in test_campaign: + if ts.crc: + print("+--[%s]%s(%s)--" % (ts.name,"-"*max(2,80-len(ts.name)-18),ts.crc)) + else: + print("+--[%s]%s" % (ts.name,"-"*max(2,80-len(ts.name)-6))) + if ts.keywords: + print(" kw=%s" % ",".join(ts.keywords)) + for t in ts: + print("%(num)03i %(name)s" % t) + c = k = "" + if t.keywords: + k = "kw=%s" % ",".join(t.keywords) + if t.crc: + c = "[%(crc)s] " % t + if c or k: + print(" %s%s" % (c,k) ) + +#### COMPUTE CAMPAIGN DIGESTS #### + +def crc32(x): + return "%08X" % (0xffffffff & zlib.crc32(x)) + +def sha1(x): + return hashlib.sha1(x).hexdigest().upper() + +def compute_campaign_digests(test_campaign): + dc = b"" + for ts in test_campaign: + dts = b"" + for t in ts: + dt = t.test.strip().encode('ascii') + t.crc = crc32(dt) + dts += b"\0"+dt + ts.crc = crc32(dts) + dc += b"\0\x01"+dts + test_campaign.crc = crc32(dc) + if type(test_campaign.filename) is str and test_campaign.filename != '': + test = open(test_campaign.filename, 'rb').read() + elif test_campaign.filename == '': + test = sys.stdin.read().encode('ascii') + else: + raise Exception("Unknown test source %s" % test_campaign.filename) + test_campaign.sha = sha1(test) + + +#### FILTER CAMPAIGN ##### + +def filter_tests_on_numbers(test_campaign, num): + if num: + for ts in test_campaign: + #ts.set = filter(lambda t: t.num in num, ts.set) + ts.set = [ t for t in ts.set if t.num in num ] + #test_campaign.campaign = filter(lambda ts: len(ts.set) > 0, test_campaign.campaign) + test_campaign.campaign = [ ts for ts in test_campaign.campaign if len(ts.set) > 0 ] + +def filter_tests_keep_on_keywords(test_campaign, kw): + def kw_match(lst, kw): + for k in lst: + if k in kw: + return True + return False + + if kw: + for ts in test_campaign: + #ts.set = filter(lambda t: kw_match(t.keywords, kw), ts.set) + ts.set = [ t for t in ts.set if kw_match(t.keywords, kw) ] + +def filter_tests_remove_on_keywords(test_campaign, kw): + def kw_match(lst, kw): + for k in kw: + if k not in lst: + return False + return True + + if kw: + for ts in test_campaign: + #ts.set = filter(lambda t: not kw_match(t.keywords, kw), ts.set) + ts.set = [ t for t in ts.set if not kw_match(t.keywords, kw) ] + + +def remove_empty_testsets(test_campaign): + #test_campaign.campaign = filter(lambda ts: len(ts.set) > 0, test_campaign.campaign) + test_campaign.campaign = [ ts for ts in test_campaign.campaign if len(ts.set) > 0 ] + + +#### RUN CAMPAIGN ##### + +def run_campaign(test_campaign, get_interactive_session, verb=2): + passed=failed=0 + if test_campaign.preexec: + test_campaign.preexec_output = get_interactive_session(test_campaign.preexec.strip())[0] + for testset in test_campaign: + for t in testset: + t.output,res = get_interactive_session(t.test.strip()) + the_res = False + try: + if res is None or res: + the_res= True + except Exception as msg: + t.output+="UTscapy: Error during result interpretation:\n" + t.output+="".join(traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback,)) + if the_res: + t.res = True + res = "passed" + passed += 1 + else: + t.res = False + res = "failed" + failed += 1 + t.result = res + if verb > 1: + print("%(result)6s %(crc)s %(name)s" % t, file = sys.stderr) + test_campaign.passed = passed + test_campaign.failed = failed + if verb: + print("Campaign CRC=%(crc)s SHA=%(sha)s" % test_campaign, file = sys.stderr) + print("PASSED=%i FAILED=%i" % (passed, failed), file = sys.stderr) + + +#### INFO LINES #### + +def info_line(test_campaign): + filename = test_campaign.filename + if filename is None: + return "Run %s by UTscapy" % time.ctime() + else: + return "Run %s from [%s] by UTscapy" % (time.ctime(), filename) + +def html_info_line(test_campaign): + filename = test_campaign.filename + if filename is None: + return """Run %s by UTscapy
""" % time.ctime() + else: + return """Run %s from [%s] by UTscapy
""" % (time.ctime(), filename) + + +#### CAMPAIGN TO something #### + +def campaign_to_TEXT(test_campaign): + output="%(title)s\n" % test_campaign + output += "-- "+info_line(test_campaign)+"\n\n" + output += "Passed=%(passed)i\nFailed=%(failed)i\n\n%(headcomments)s\n" % test_campaign + + for testset in test_campaign: + output += "######\n## %(name)s\n######\n%(comments)s\n\n" % testset + for t in testset: + if t.expand: + output += "###(%(num)03i)=[%(result)s] %(name)s\n%(comments)s\n%(output)s\n\n" % t + + return output + +def campaign_to_ANSI(test_campaign): + output="%(title)s\n" % test_campaign + output += "-- "+info_line(test_campaign)+"\n\n" + output += "Passed=%(passed)i\nFailed=%(failed)i\n\n%(headcomments)s\n" % test_campaign + + for testset in test_campaign: + output += "######\n## %(name)s\n######\n%(comments)s\n\n" % testset + for t in testset: + if t.expand: + output += "###(%(num)03i)=[%(result)s] %(name)s\n%(comments)s\n%(output)s\n\n" % t + + return output + +def campaign_to_xUNIT(test_campaign): + output='\n\n' + for testset in test_campaign: + for t in testset: + output += ' + +%(title)s + + + + + +

%(title)s

+ +Shrink All +Expand All +Expand Passed +Expand Failed +

+""" % test_campaign + + if local: + External_Files.UTscapy_js.write(os.path.dirname(test_campaign.output_file.name)) + External_Files.UTscapy_css.write(os.path.dirname(test_campaign.output_file.name)) + output %= External_Files.get_local_dict() + else: + output %= External_Files.get_URL_dict() + + if test_campaign.crc is not None and test_campaign.sha is not None: + output += "CRC=%(crc)s SHA=%(sha)s
" % test_campaign + output += ""+html_info_line(test_campaign)+"" + output += test_campaign.headcomments + "\n

PASSED=%(passed)i FAILED=%(failed)i

\n\n" % test_campaign + for ts in test_campaign: + for t in ts: + output += """%(num)03i\n""" % t + output += "\n\n" + + for testset in test_campaign: + output += "

" % testset + if testset.crc is not None: + output += "%(crc)s " % testset + output += "%(name)s

\n%(comments)s\n
    \n" % testset + for t in testset: + output += """
  • \n""" % t + if t.expand == 2: + output +=""" + +-%(num)03i- +""" % t + else: + output += """ ++%(num)03i+ + +""" % t + if t.crc is not None: + output += "%(crc)s\n" % t + output += """%(name)s\n +""" % t + output += "\n
\n\n" + + output += "" + return output + +def campaign_to_LATEX(test_campaign): + output = r"""\documentclass{report} +\usepackage{alltt} +\usepackage{xcolor} +\usepackage{a4wide} +\usepackage{hyperref} + +\title{%(title)s} +\date{%%s} + +\begin{document} +\maketitle +\tableofcontents + +\begin{description} +\item[Passed:] %(passed)i +\item[Failed:] %(failed)i +\end{description} + +%(headcomments)s + +""" % test_campaign + output %= info_line(test_campaign) + + for testset in test_campaign: + output += "\\chapter{%(name)s}\n\n%(comments)s\n\n" % testset + for t in testset: + if t.expand: + output += r"""\section{%(name)s} + +[%(num)03i] [%(result)s] + +%(comments)s +\begin{alltt} +%(output)s +\end{alltt} + +""" % t + + output += "\\end{document}\n" + return output + + + +#### USAGE #### + +def usage(): + print("""Usage: UTscapy [-m module] [-f {text|ansi|HTML|LaTeX}] [-o output_file] + [-t testfile] [-k keywords [-k ...]] [-K keywords [-K ...]] + [-l] [-d|-D] [-F] [-q[q]] [-P preexecute_python_code] + [-s /path/to/scpay] +-l\t\t: generate local files +-F\t\t: expand only failed tests +-d\t\t: dump campaign +-D\t\t: dump campaign and stop +-C\t\t: don't calculate CRC and SHA +-s\t\t: path to scapy.py +-q\t\t: quiet mode +-qq\t\t: [silent mode] +-n \t: only tests whose numbers are given (eg. 1,3-7,12) +-m \t: additional module to put in the namespace +-k ,,...\t: include only tests with one of those keywords (can be used many times) +-K ,,...\t: remove tests with one of those keywords (can be used many times) +-P +""", file = sys.stderr) + raise SystemExit + + +#### MAIN #### + +def main(argv): + import builtins + + # Parse arguments + + FORMAT = Format.ANSI + TESTFILE = sys.stdin + OUTPUTFILE = sys.stdout + LOCAL = 0 + NUM=None + KW_OK = [] + KW_KO = [] + DUMP = 0 + CRC = 1 + ONLYFAILED = 0 + VERB=2 + PREEXEC="" + SCAPY="scapy" + MODULES = [] + try: + opts = getopt.getopt(argv, "o:t:f:hln:m:k:K:DdCFqP:s:") + for opt,optarg in opts[0]: + if opt == "-h": + usage() + elif opt == "-F": + ONLYFAILED = 1 + elif opt == "-q": + VERB -= 1 + elif opt == "-D": + DUMP = 2 + elif opt == "-d": + DUMP = 1 + elif opt == "-C": + CRC = 0 + elif opt == "-s": + SCAPY = optarg + elif opt == "-P": + PREEXEC += "\n"+optarg + elif opt == "-f": + try: + FORMAT = Format.from_string(optarg) + except KeyError as msg: + raise getopt.GetoptError("Unknown output format %s" % msg) + elif opt == "-t": + TESTFILE = open(optarg) + elif opt == "-o": + OUTPUTFILE = open(optarg, "w") + elif opt == "-l": + LOCAL = 1 + elif opt == "-n": + NUM = [] + for v in map( lambda x: x.strip(), optarg.split(",") ): + try: + NUM.append(int(v)) + except ValueError: + v1,v2 = map(int, v.split("-")) + for vv in range(v1,v2+1): + NUM.append(vv) + elif opt == "-m": + MODULES.append(optarg) + elif opt == "-k": + KW_OK.append(optarg.split(",")) + elif opt == "-K": + KW_KO.append(optarg.split(",")) + + + try: + from scapy import all as scapy + except ImportError as e: + raise getopt.GetoptError("cannot import [%s]: %s" % (SCAPY,e)) + + for m in MODULES: + try: + mod = import_module(m) + builtins.__dict__.update(mod.__dict__) + except ImportError as e: + raise getopt.GetoptError("cannot import [%s]: %s" % (m,e)) + + except getopt.GetoptError as msg: + print("ERROR:",msg, file = sys.stderr) + raise SystemExit + + autorun_func = { + Format.TEXT: scapy.autorun_get_text_interactive_session, + Format.ANSI: scapy.autorun_get_ansi_interactive_session, + Format.HTML: scapy.autorun_get_html_interactive_session, + Format.LATEX: scapy.autorun_get_latex_interactive_session, + Format.XUNIT: scapy.autorun_get_text_interactive_session, + } + + # Parse test file + test_campaign = parse_campaign_file(TESTFILE) + + # Report parameters + if PREEXEC: + test_campaign.preexec = PREEXEC + + + # Compute campaign CRC and SHA + if CRC: + compute_campaign_digests(test_campaign) + + # Filter out unwanted tests + filter_tests_on_numbers(test_campaign, NUM) + for k in KW_OK: + filter_tests_keep_on_keywords(test_campaign, k) + for k in KW_KO: + filter_tests_remove_on_keywords(test_campaign, k) + + remove_empty_testsets(test_campaign) + + + # Dump campaign + if DUMP: + dump_campaign(test_campaign) + if DUMP > 1: + sys.exit() + + # Run tests + test_campaign.output_file = OUTPUTFILE + run_campaign(test_campaign, autorun_func[FORMAT], verb=VERB) + + # Shrink passed + if ONLYFAILED: + for t in test_campaign.all_tests(): + if t: + t.expand = 0 + else: + t.expand = 2 + + # Generate report + if FORMAT == Format.TEXT: + output = campaign_to_TEXT(test_campaign) + elif FORMAT == Format.ANSI: + output = campaign_to_ANSI(test_campaign) + elif FORMAT == Format.HTML: + output = campaign_to_HTML(test_campaign, local=LOCAL) + elif FORMAT == Format.LATEX: + output = campaign_to_LATEX(test_campaign) + elif FORMAT == Format.XUNIT: + output = campaign_to_xUNIT(test_campaign) + + OUTPUTFILE.write(output) + OUTPUTFILE.close() + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/tools/__init__.py b/scripts/external_libs/scapy-python3-0.18/scapy/tools/__init__.py new file mode 100644 index 00000000..af6eec74 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/tools/__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 + +""" +Additional tools to be run separately +""" diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/tools/check_asdis.py b/scripts/external_libs/scapy-python3-0.18/scapy/tools/check_asdis.py new file mode 100644 index 00000000..3e45007f --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/tools/check_asdis.py @@ -0,0 +1,103 @@ +#! /usr/bin/env python + +import getopt + +def usage(): + print("""Usage: check_asdis -i [-o ] + -v increase verbosity + -d hexdiff packets that differ + -z compress output pcap + -a open pcap file in append mode""", file = sys.stderr) + +def main(argv): + PCAP_IN = None + PCAP_OUT = None + COMPRESS=False + APPEND=False + DIFF=False + VERBOSE=0 + try: + opts=getopt.getopt(argv, "hi:o:azdv") + for opt, parm in opts[0]: + if opt == "-h": + usage() + raise SystemExit + elif opt == "-i": + PCAP_IN = parm + elif opt == "-o": + PCAP_OUT = parm + elif opt == "-v": + VERBOSE += 1 + elif opt == "-d": + DIFF = True + elif opt == "-a": + APPEND = True + elif opt == "-z": + COMPRESS = True + + + if PCAP_IN is None: + raise getopt.GetoptError("Missing pcap file (-i)") + + except getopt.GetoptError as e: + print("ERROR: %s" % e, file = sys.stderr) + raise SystemExit + + + + from scapy.config import conf + from scapy.utils import RawPcapReader,RawPcapWriter,hexdiff + from scapy.layers import all + + + pcap = RawPcapReader(PCAP_IN) + pcap_out = None + if PCAP_OUT: + pcap_out = RawPcapWriter(PCAP_OUT, append=APPEND, gz=COMPRESS, linktype=pcap.linktype) + pcap_out._write_header(None) + + LLcls = conf.l2types.get(pcap.linktype) + if LLcls is None: + print(" Unknown link type [%i]. Can't test anything!" % pcap.linktype, file = sys.stderr) + raise SystemExit + + + i=-1 + differ=0 + failed=0 + for p1,meta in pcap: + i += 1 + try: + p2d = LLcls(p1) + p2 = str(p2d) + except KeyboardInterrupt: + raise + except Exception as e: + print("Dissection error on packet %i" % i) + failed += 1 + else: + if p1 == p2: + if VERBOSE >= 2: + print("Packet %i ok" % i) + continue + else: + print("Packet %i differs" % i) + differ += 1 + if VERBOSE >= 1: + print(repr(p2d)) + if DIFF: + hexdiff(p1,p2) + if pcap_out is not None: + pcap_out.write(p1) + i+=1 + correct = i-differ-failed + print("%i total packets. %i ok, %i differed, %i failed. %.2f%% correct." % (i, correct, differ, + failed, i and 100.0*(correct)/i)) + + +if __name__ == "__main__": + import sys + try: + main(sys.argv[1:]) + except KeyboardInterrupt: + print("Interrupted by user.", file = sys.stderr) diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/utils.py b/scripts/external_libs/scapy-python3-0.18/scapy/utils.py new file mode 100644 index 00000000..252109bb --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/utils.py @@ -0,0 +1,1054 @@ +## 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 + +""" +General utility functions. +""" + +import os,sys,socket,types +import random,time +import gzip,zlib +import re,struct,array,stat +import subprocess + +import warnings +warnings.filterwarnings("ignore","tempnam",RuntimeWarning, __name__) + +from .config import conf +from .data import MTU +from .error import log_runtime,log_loading,log_interactive, Scapy_Exception +from .base_classes import BasePacketList,BasePacket + + +WINDOWS=sys.platform.startswith("win32") + +########### +## Tools ## +########### + +def get_temp_file(keep=False, autoext=""): + import tempfile + fd, fname = tempfile.mkstemp(suffix = ".scapy" + autoext) + os.close(fd) + if not keep: + conf.temp_files.append(fname) + return fname + +def str2bytes(x): + """Convert input argument to bytes""" + if type(x) is bytes: + return x + elif type(x) is str: + return bytes([ ord(i) for i in x ]) + else: + return str2bytes(str(x)) + +def chb(x): + if type(x) is str: + return x + else: + return chr(x) + +def orb(x): + if type(x) is str: + return ord(x) + else: + return x + +def any2b(x): + if type(x) is not str and type(x) is not bytes: + try: + x=bytes(x) + except: + x = str(x) + if type(x) is str: + x = bytes([ ord(i) for i in x ]) + return x + +def sane_color(x): + r="" + for i in x: + j = orb(i) + if (j < 32) or (j >= 127): + r=r+conf.color_theme.not_printable(".") + else: + r=r+chb(i) + return r + +def sane(x): + r="" + for i in x: + if type(x) is str: + j = ord(i) + else: + j = i + if (j < 32) or (j >= 127): + r=r+"." + else: + r=r+chb(i) + return r + +def lhex(x): + if type(x) is int: + return hex(x) + elif type(x) is tuple: + return "(%s)" % ", ".join(map(lhex, x)) + elif type(x) is list: + return "[%s]" % ", ".join(map(lhex, x)) + else: + return x + +@conf.commands.register +def hexdump(x): + if type(x) is not str and type(x) is not bytes: + try: + x=bytes(x) + except: + x = str(x) + l = len(x) + i = 0 + while i < l: + print("%04x " % i,end = " ") + for j in range(16): + if i+j < l: + print("%02X" % orb(x[i+j]), end = " ") + else: + print(" ", end = " ") + if j%16 == 7: + print("", end = " ") + print(" ", end = " ") + print(sane_color(x[i:i+16])) + i += 16 + +@conf.commands.register +def linehexdump(x, onlyasc=0, onlyhex=0): + if type(x) is not str and type(x) is not bytes: + try: + x=bytes(x) + except: + x = str(x) + l = len(x) + if not onlyasc: + for i in range(l): + print("%02X" % orb(x[i]), end = " ") + print("", end = " ") + if not onlyhex: + print(sane_color(x)) + +def chexdump(x): + if type(x) is not str and type(x) is not bytes: + try: + x=bytes(x) + except: + x = str(x) + print(", ".join(map(lambda x: "%#04x"%orb(x), x))) + +def hexstr(x, onlyasc=0, onlyhex=0): + s = [] + if not onlyasc: + s.append(" ".join(map(lambda x:"%02x"%orb(x), x))) + if not onlyhex: + s.append(sane(x)) + return " ".join(s) + + +@conf.commands.register +def hexdiff(x,y): + """Show differences between 2 binary strings""" + x=any2b(x)[::-1] + y=any2b(y)[::-1] + SUBST=1 + INSERT=1 + d={} + d[-1,-1] = 0,(-1,-1) + for j in range(len(y)): + d[-1,j] = d[-1,j-1][0]+INSERT, (-1,j-1) + for i in range(len(x)): + d[i,-1] = d[i-1,-1][0]+INSERT, (i-1,-1) + + for j in range(len(y)): + for i in range(len(x)): + d[i,j] = min( ( d[i-1,j-1][0]+SUBST*(x[i] != y[j]), (i-1,j-1) ), + ( d[i-1,j][0]+INSERT, (i-1,j) ), + ( d[i,j-1][0]+INSERT, (i,j-1) ) ) + + + backtrackx = [] + backtracky = [] + i=len(x)-1 + j=len(y)-1 + while not (i == j == -1): + i2,j2 = d[i,j][1] + backtrackx.append(x[i2+1:i+1]) + backtracky.append(y[j2+1:j+1]) + i,j = i2,j2 + + + + x = y = i = 0 + colorize = { 0: lambda x:x, + -1: conf.color_theme.left, + 1: conf.color_theme.right } + + dox=1 + doy=0 + l = len(backtrackx) + while i < l: + separate=0 + linex = backtrackx[i:i+16] + liney = backtracky[i:i+16] + xx = sum(len(k) for k in linex) + yy = sum(len(k) for k in liney) + if dox and not xx: + dox = 0 + doy = 1 + if dox and linex == liney: + doy=1 + + if dox: + xd = y + j = 0 + while not linex[j]: + j += 1 + xd -= 1 + print(colorize[doy-dox]("%04x" % xd), end = " ") + x += xx + line=linex + else: + print(" ", end = " ") + if doy: + yd = y + j = 0 + while not liney[j]: + j += 1 + yd -= 1 + print(colorize[doy-dox]("%04x" % yd), end = " ") + y += yy + line=liney + else: + print(" ", end = " ") + + print(" ", end = " ") + + cl = "" + for j in range(16): + if i+j < l: + if line[j]: + col = colorize[(linex[j]!=liney[j])*(doy-dox)] + print(col("%02X" % line[j][0]), end = " ") + if linex[j]==liney[j]: + cl += sane_color(line[j]) + else: + cl += col(sane(line[j])) + else: + print(" ", end = " ") + cl += " " + else: + print(" ", end = " ") + if j == 7: + print("", end = " ") + + + print(" ",cl) + + if doy or not yy: + doy=0 + dox=1 + i += 16 + else: + if yy: + dox=0 + doy=1 + else: + i += 16 + + +crc32 = zlib.crc32 + +if struct.pack("H",1) == b"\x00\x01": # big endian + def checksum(pkt): + if len(pkt) % 2 == 1: + pkt += b"\0" + s = sum(array.array("H", pkt)) + s = (s >> 16) + (s & 0xffff) + s += s >> 16 + s = ~s + return s & 0xffff +else: + def checksum(pkt): + if len(pkt) % 2 == 1: + pkt += b"\0" + s = sum(array.array("H", pkt)) + s = (s >> 16) + (s & 0xffff) + s += s >> 16 + s = ~s + return (((s>>8)&0xff)|s<<8) & 0xffff + +def warning(x): + log_runtime.warning(x) + +def mac2str(mac): + #return "".join(map(lambda x: chr(int(x,16)), mac.split(":"))) + if type(mac) != str: + mac = mac.decode('ascii') + return b''.join([ bytes([int(i, 16)]) for i in mac.split(":") ]) + +def str2mac(s): + return ("%02x:"*6)[:-1] % tuple(s) + +def strxor(x,y): + #return "".join(map(lambda i,j:chr(ord(i)^ord(j)),x,y)) + return bytes([ i[0] ^ i[1] for i in zip(x,y) ] ) + +# Workarround bug 643005 : https://sourceforge.net/tracker/?func=detail&atid=105470&aid=643005&group_id=5470 +try: + socket.inet_aton("255.255.255.255") +except socket.error: + def inet_aton(x): + if x == "255.255.255.255": + return b"\xff"*4 + else: + return socket.inet_aton(x) +else: + inet_aton = socket.inet_aton + +inet_ntoa = socket.inet_ntoa +try: + inet_ntop = socket.inet_ntop + inet_pton = socket.inet_pton +except AttributeError: + from scapy.pton_ntop import * + log_loading.info("inet_ntop/pton functions not found. Python IPv6 support not present") + + +def atol(x): + try: + ip = inet_aton(x) + except socket.error: + ip = inet_aton(socket.gethostbyname(x)) + return struct.unpack("!I", ip)[0] +def ltoa(x): + return inet_ntoa(struct.pack("!I", x&0xffffffff)) + +def itom(x): + return (0xffffffff00000000>>x)&0xffffffff + +def do_graph(graph,prog=None,format='png',target=None,string=False,options=None, figsize = (12, 12), **kargs): + """do_graph(graph, prog=conf.prog.dot, format="png", + target=None, options=None, string=False): + if networkx library is available and graph is instance of Graph, use networkx.draw + + string: if not False, simply return the graph string + graph: GraphViz graph description + format: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option. Ignored if target==None + target: filename. If None uses matplotlib to display + prog: which graphviz program to use + options: options to be passed to prog""" + + from scapy.arch import NETWORKX + if NETWORKX: + import networkx as nx + + if NETWORKX and isinstance(graph, nx.Graph): + nx.draw(graph, with_labels = True, edge_color = '0.75', **kargs) + else: # otherwise use dot as in scapy 2.x + if string: + return graph + if prog is None: + prog = conf.prog.dot + + if not target or not format: + format = 'png' + format = "-T %s" % format + + p = subprocess.Popen("%s %s %s" % (prog,options or "", format or ""), shell = True, stdin = subprocess.PIPE, stdout = subprocess.PIPE) + w, r = p.stdin, p.stdout + w.write(graph.encode('utf-8')) + w.close() + if target: + with open(target, 'wb') as f: + f.write(r.read()) + else: + try: + import matplotlib.image as mpimg + import matplotlib.pyplot as plt + plt.figure(figsize = figsize) + plt.axis('off') + return plt.imshow(mpimg.imread(r, format = format), **kargs) + + except ImportError: + warning('matplotlib.image required for interactive graph viewing. Use target option to write to a file') + +_TEX_TR = { + "{":"{\\tt\\char123}", + "}":"{\\tt\\char125}", + "\\":"{\\tt\\char92}", + "^":"\\^{}", + "$":"\\$", + "#":"\\#", + "~":"\\~", + "_":"\\_", + "&":"\\&", + "%":"\\%", + "|":"{\\tt\\char124}", + "~":"{\\tt\\char126}", + "<":"{\\tt\\char60}", + ">":"{\\tt\\char62}", + } + +def tex_escape(x): + s = "" + for c in x: + s += _TEX_TR.get(c,c) + return s + +def colgen(*lstcol,**kargs): + """Returns a generator that mixes provided quantities forever + trans: a function to convert the three arguments into a color. lambda x,y,z:(x,y,z) by default""" + if len(lstcol) < 2: + lstcol *= 2 + trans = kargs.get("trans", lambda x,y,z: (x,y,z)) + while 1: + for i in range(len(lstcol)): + for j in range(len(lstcol)): + for k in range(len(lstcol)): + if i != j or j != k or k != i: + yield trans(lstcol[(i+j)%len(lstcol)],lstcol[(j+k)%len(lstcol)],lstcol[(k+i)%len(lstcol)]) + +def incremental_label(label="tag%05i", start=0): + while True: + yield label % start + start += 1 + +######################### +#### Enum management #### +######################### + +class EnumElement: + _value=None + def __init__(self, key, value): + self._key = key + self._value = value + def __repr__(self): + return "<%s %s[%r]>" % (self.__dict__.get("_name", self.__class__.__name__), self._key, self._value) + def __getattr__(self, attr): + return getattr(self._value, attr) + def __str__(self): + return self._key + def __eq__(self, other): + #return self._value == int(other) + return self._value == hash(other) + def __hash__(self): + return self._value + + +class Enum_metaclass(type): + element_class = EnumElement + def __new__(cls, name, bases, dct): + rdict={} + for k,v in dct.items(): + if type(v) is int: + v = cls.element_class(k,v) + dct[k] = v + rdict[v] = k + dct["__rdict__"] = rdict + return super(Enum_metaclass, cls).__new__(cls, name, bases, dct) + def __getitem__(self, attr): + return self.__rdict__[attr] + def __contains__(self, val): + return val in self.__rdict__ + def get(self, attr, val=None): + return self._rdict__.get(attr, val) + def __repr__(self): + return "<%s>" % self.__dict__.get("name", self.__name__) + + + +################### +## Object saving ## +################### + + +def export_object(obj): + import dill as pickle + import base64 + return base64.b64encode(gzip.zlib.compress(pickle.dumps(obj,4),9)).decode('utf-8') + + +def import_object(obj): + import dill as pickle + import base64 +# if obj is None: +# obj = sys.stdin.read().strip().encode('utf-8') + if obj is str: + obj = obj.strip().encode('utf-8') + return pickle.loads(gzip.zlib.decompress(base64.b64decode(obj))) + + +def save_object(fname, obj): + import dill as pickle + pickle.dump(obj,gzip.open(fname,"wb")) + +def load_object(fname): + import dill as pickle + return pickle.load(gzip.open(fname,"rb")) + +@conf.commands.register +def corrupt_bytes(s, p=0.01, n=None): + """Corrupt a given percentage or number of bytes from bytes""" + s = str2bytes(s) + s = array.array("B",s) + l = len(s) + if n is None: + n = max(1,int(l*p)) + for i in random.sample(range(l), n): + s[i] = (s[i]+random.randint(1,255))%256 + return s.tobytes() + +@conf.commands.register +def corrupt_bits(s, p=0.01, n=None): + """Flip a given percentage or number of bits from bytes""" + s = str2bytes(s) + s = array.array("B",s) + l = len(s)*8 + if n is None: + n = max(1, int(l*p)) + for i in random.sample(range(l), n): + s[i//8] ^= 1 << (i%8) + return s.tobytes() + + +############################# +## pcap capture file stuff ## +############################# + +@conf.commands.register +def wrpcap(filename, pkt, *args, **kargs): + """Write a list of packets to a pcap file +gz: set to 1 to save a gzipped capture +linktype: force linktype value +endianness: "<" or ">", force endianness""" + with PcapWriter(filename, *args, **kargs) as pcap: + pcap.write(pkt) + +@conf.commands.register +def rdpcap(filename, count=-1): + """Read a pcap file and return a packet list +count: read only packets""" + with PcapReader(filename) as pcap: + return pcap.read_all(count=count) + +class RawPcapReader: + """A stateful pcap reader. Each packet is returned as bytes""" + def __init__(self, filename): + self.filename = filename + try: + if not stat.S_ISREG(os.stat(filename).st_mode): + raise IOError("GZIP detection works only for regular files") + self.f = gzip.open(filename,"rb") + magic = self.f.read(4) + except IOError: + self.f = open(filename,"rb") + magic = self.f.read(4) + if magic == b"\xa1\xb2\xc3\xd4": #big endian + self.endian = ">" + self.reader = _RawPcapOldReader(self.f, self.endian) + elif magic == b"\xd4\xc3\xb2\xa1": #little endian + self.endian = "<" + self.reader = _RawPcapOldReader(self.f, self.endian) + elif magic == b"\x0a\x0d\x0d\x0a": #PcapNG + self.reader = _RawPcapNGReader(self.f) + else: + raise Scapy_Exception("Not a pcap capture file (bad magic)") + + def __enter__(self): + return self.reader + + def __exit__(self, exc_type, exc_value, tracback): + self.close() + + def __iter__(self): + return self.reader.__iter__() + + def dispatch(self, callback): + """call the specified callback routine for each packet read + + This is just a convienience function for the main loop + that allows for easy launching of packet processing in a + thread. + """ + for p in self: + callback(p) + + def read_all(self,count=-1): + """return a list of all packets in the pcap file + """ + res=[] + while count != 0: + count -= 1 + p = self.read_packet() + if p is None: + break + res.append(p) + return res + + def recv(self, size=MTU): + """ Emulate a socket + """ + return self.read_packet(size)[0] + + def fileno(self): + return self.f.fileno() + + def close(self): + return self.f.close() + + def read_packet(self, size = MTU): + return self.reader.read_packet(size) + +def align32(n): + return n + (4 - n % 4) % 4 + +class _RawPcapNGReader: + def __init__(self, filep): + self.filep = filep + self.filep.seek(0, 0) + self.endian = '<' + self.tsresol = 6 + self.linktype = None + + def __iter__(self): + return self + + def __next__(self): + """implement the iterator protocol on a set of packets in a pcapng file""" + pkt = self.read_packet() + if pkt == None: + raise StopIteration + return pkt + + def read_packet(self, size = MTU): + while True: + buf = self._read_bytes(4, check = False) + if len(buf) == 0: + return None + elif len(buf) != 4: + raise IOError("PacketNGReader: Premature end of file") + block_type, = struct.unpack(self.endian + 'i', buf) + if block_type == 168627466: #Section Header b'\x0a\x0d\x0d\x0a' + self.read_section_header() + elif block_type == 1: + self.read_interface_description() + elif block_type == 6: + return self.read_enhanced_packet(size) + else: + warning("PacketNGReader: Unparsed block type %d/#%x" % (block_type, block_type)) + self.read_generic_block() + + def _read_bytes(self, n, check = True): + buf = self.filep.read(n) + if check and len(buf) < n: + raise IOError("PacketNGReader: Premature end of file") + return buf + + def read_generic_block(self): + block_length, = struct.unpack(self.endian + 'I', self._read_bytes(4)) + self._read_bytes(block_length - 12) + self._check_length(block_length) + + def read_section_header(self): + buf = self._read_bytes(16) + if buf[4:8] == b'\x1a\x2b\x3c\x4d': + self.endian = '>' + elif buf[4:8] == b'\x4d\x3c\x2b\x1a': + self.endian = '<' + else: + raise Scapy_Exception('Cannot read byte order value') + block_length, _, major_version, minor_version, section_length = struct.unpack(self.endian + 'IIHHi', buf) + options = self._read_bytes(block_length - 24) + if options: + opt = self.parse_options(options) + for i in opt.keys(): + if not i & (0b1 << 15): + warning("PcapNGReader: Unparsed option %d/#%x in section header" % (i, i)) + self._check_length(block_length) + + def read_interface_description(self): + buf = self._read_bytes(12) + block_length, self.linktype, reserved, self.snaplen = struct.unpack(self.endian + 'IHHI', buf) + options = self._read_bytes(block_length - 20) + if options: + opt = self.parse_options(options) + for i in opt.keys(): + if 9 in opt: + self.tsresol = opt[9][0] + elif not i & (0b1 << 15): + warning("PcapNGReader: Unparsed option %d/#%x in enhanced packet block" % (i, i)) + try: + self.LLcls = conf.l2types[self.linktype] + except KeyError: + warning("RawPcapReader: unknown LL type [%i]/[%#x]. Using Raw packets" % (self.linktype,self.linktype)) + self.LLcls = conf.raw_layer + + self._check_length(block_length) + + def read_enhanced_packet(self, size = MTU): + buf = self._read_bytes(24) + block_length, interface, ts_high, ts_low, caplen, wirelen = struct.unpack(self.endian + 'IIIIII', buf) + timestamp = (ts_high << 32) + ts_low + + pkt = self._read_bytes(align32(caplen))[:caplen] + options = self._read_bytes(block_length - align32(caplen) - 32) + if options: + opt = self.parse_options(options) + for i in opt.keys(): + if not i & (0b1 << 15): + warning("PcapNGReader: Unparsed option %d/#%x in enhanced packet block" % (i, i)) + self._check_length(block_length) + return pkt[:MTU], (self.parse_sec(timestamp), self.parse_usec(timestamp), wirelen) + + def parse_sec(self, t): + if self.tsresol & 0b10000000: + return t >> self.tsresol + else: + return t // pow(10, self.tsresol) + + def parse_usec(self, t): + if self.tsresol & 0b10000000: + return t & (1 << self.tsresol) - 1 + else: + return t % pow(10, self.tsresol) + + def parse_options(self, opt): + buf = opt + options = {} + while buf: + opt_type, opt_len = struct.unpack(self.endian + 'HH', buf[:4]) + if opt_type == 0: + return options + options[opt_type] = buf[4:4 + opt_len] + buf = buf[ 4 + align32(opt_len):] + return options + + def _check_length(self, block_length): + check_length, = struct.unpack(self.endian + 'I', self._read_bytes(4)) + if check_length != block_length: + raise Scapy_Exception('Block length values are not equal') + +class _RawPcapOldReader: + def __init__(self, filep, endianness): + self.endian = endianness + self.f = filep + hdr = self.f.read(20) + if len(hdr)<20: + raise Scapy_Exception("Invalid pcap file (too short)") + vermaj,vermin,tz,sig,snaplen,linktype = struct.unpack(self.endian+"HHIIII",hdr) + + self.linktype = linktype + try: + self.LLcls = conf.l2types[self.linktype] + except KeyError: + warning("RawPcapReader: unknown LL type [%i]/[%#x]. Using Raw packets" % (self.linktype,self.linktype)) + self.LLcls = conf.raw_layer + + def __iter__(self): + return self + + def __next__(self): + """implement the iterator protocol on a set of packets in a pcap file""" + pkt = self.read_packet() + if pkt == None: + raise StopIteration + return pkt + + def read_packet(self, size=MTU): + """return a single packet read from the file + bytes, (sec, #timestamp seconds + usec, #timestamp microseconds + wirelen) #actual length of packet + returns None when no more packets are available + """ + hdr = self.f.read(16) + if len(hdr) < 16: + return None + sec,usec,caplen,wirelen = struct.unpack(self.endian+"IIII", hdr) + s = self.f.read(caplen)[:MTU] + return s,(sec,usec,wirelen) # caplen = len(s) + + +class PcapReader(RawPcapReader): + def __init__(self, filename): + RawPcapReader.__init__(self, filename) + def __enter__(self): + return self + def __iter__(self): + return self + def __next__(self): + """implement the iterator protocol on a set of packets in a pcap file""" + pkt = self.read_packet() + if pkt == None: + raise StopIteration + return pkt + def read_packet(self, size=MTU): + rp = RawPcapReader.read_packet(self,size) + if rp is None: + return None + s,(sec,usec,wirelen) = rp + + + try: + p = self.reader.LLcls(s) + except KeyboardInterrupt: + raise + except: + if conf.debug_dissector: + raise + p = conf.raw_layer(s) + p.time = sec+0.000001*usec + p.wirelen = wirelen + return p + + def read_all(self,count=-1): + res = RawPcapReader.read_all(self, count) + import scapy.plist + return scapy.plist.PacketList(res,name = os.path.basename(self.filename)) + def recv(self, size=MTU): + return self.read_packet(size) + + +class RawPcapWriter: + """A stream PCAP writer with more control than wrpcap()""" + def __init__(self, filename, linktype=None, gz=False, endianness="", append=False, sync=False): + """ + linktype: force linktype to a given value. If None, linktype is taken + from the first writter packet + gz: compress the capture on the fly + endianness: force an endianness (little:"<", big:">"). Default is native + append: append packets to the capture file instead of truncating it + sync: do not bufferize writes to the capture file + """ + + self.linktype = linktype + self.header_present = 0 + self.append=append + self.gz = gz + self.endian = endianness + self.filename=filename + self.sync=sync + bufsz=4096 + if sync: + bufsz=0 + + self.f = [open,gzip.open][gz](filename,append and "ab" or "wb", gz and 9 or bufsz) + + def fileno(self): + return self.f.fileno() + + def _write_header(self, pkt): + self.header_present=1 + + if self.append: + # Even if prone to race conditions, this seems to be + # safest way to tell whether the header is already present + # because we have to handle compressed streams that + # are not as flexible as basic files + g = [open,gzip.open][self.gz](self.filename,"rb") + if g.read(16): + return + + self.f.write(struct.pack(self.endian+"IHHIIII", 0xa1b2c3d4, + 2, 4, 0, 0, MTU, self.linktype)) + self.f.flush() + + + def write(self, pkt): + """accepts a either a single packet or a list of packets + to be written to the dumpfile + """ + if not self.header_present: + self._write_header(pkt) + if type(pkt) is bytes: + self._write_packet(pkt) + else: + for p in pkt: + self._write_packet(p) + + def _write_packet(self, packet, sec=None, usec=None, caplen=None, wirelen=None): + """writes a single packet to the pcap file + """ + if caplen is None: + caplen = len(packet) + if wirelen is None: + wirelen = caplen + if sec is None or usec is None: + t=time.time() + it = int(t) + if sec is None: + sec = it + if usec is None: + usec = int(round((t-it)*1000000)) + self.f.write(struct.pack(self.endian+"IIII", sec, usec, caplen, wirelen)) + self.f.write(packet) + if self.gz and self.sync: + self.f.flush() + + def flush(self): + return self.f.flush() + + def close(self): + return self.f.close() + + def __enter__(self): + return self + def __exit__(self, exc_type, exc_value, tracback): + self.flush() + self.close() + + +class PcapWriter(RawPcapWriter): + def _write_header(self, pkt): + if self.linktype == None: + if type(pkt) is list or type(pkt) is tuple or isinstance(pkt,BasePacketList): + pkt = pkt[0] + try: + self.linktype = conf.l2types[pkt.__class__] + except KeyError: + warning("PcapWriter: unknown LL type for %s. Using type 1 (Ethernet)" % pkt.__class__.__name__) + self.linktype = 1 + RawPcapWriter._write_header(self, pkt) + + def _write_packet(self, packet): + try: + sec = int(packet.time) + usec = int(round((packet.time-sec)*1000000)) + s = bytes(packet) + caplen = len(s) + RawPcapWriter._write_packet(self, s, sec, usec, caplen, caplen) + except Exception as e: + log_interactive.error(e) + def write(self, pkt): + """accepts a either a single packet or a list of packets + to be written to the dumpfile + """ + if not self.header_present: + self._write_header(pkt) + if isinstance(pkt, BasePacket): + self._write_packet(pkt) + else: + for p in pkt: + self._write_packet(p) + + +re_extract_hexcap = re.compile("^((0x)?[0-9a-fA-F]{2,}[ :\t]{,3}|) *(([0-9a-fA-F]{2} {,2}){,16})") + +def import_hexcap(): + p = "" + try: + while 1: + l = raw_input().strip() + try: + p += re_extract_hexcap.match(l).groups()[2] + except: + warning("Parsing error during hexcap") + continue + except EOFError: + pass + + p = p.replace(" ","") + return p.decode("hex") + + + +@conf.commands.register +def wireshark(pktlist, *args): + """Run wireshark on a list of packets""" + fname = get_temp_file() + wrpcap(fname, pktlist) + subprocess.Popen([conf.prog.wireshark, "-r", fname] + list(args)) + +@conf.commands.register +def tdecode(pkt, *args): + """Run tshark to decode and display the packet. If no args defined uses -V""" + if not args: + args = [ "-V" ] + fname = get_temp_file() + wrpcap(fname,[pkt]) + subprocess.call(["tshark", "-r", fname] + list(args)) + +@conf.commands.register +def hexedit(x): + """Run external hex editor on a packet or bytes. Set editor in conf.prog.hexedit""" + x = bytes(x) + fname = get_temp_file() + with open(fname,"wb") as f: + f.write(x) + subprocess.call([conf.prog.hexedit, fname]) + with open(fname, "rb") as f: + x = f.read() + return x + +def __make_table(yfmtfunc, fmtfunc, endline, items, fxyz, sortx=None, sorty=None, seplinefunc=None): + vx = {} + vy = {} + vz = {} + vxf = {} + vyf = {} + max_length = 0 + for record in items: + xx,yy,zz = map(str, fxyz(record[0], record[1])) + max_length = max(len(yy),max_length) + vx[xx] = max(vx.get(xx,0), len(xx), len(zz)) + vy[yy] = None + vz[(xx,yy)] = zz + + vxk = list(vx.keys()) + vyk = list(vy.keys()) + if sortx: + vxk.sort(sortx) + else: + try: + vxk.sort(key = lambda x: atol(x)) + except: + vxk.sort() + if sorty: + vyk.sort(sorty) + else: + try: + vyk.sort(key = lambda x: atol(x)) + except: + vyk.sort() + + + if seplinefunc: + sepline = seplinefunc(max_length, [vx[x] for x in vxk]) + print(sepline) + + fmt = yfmtfunc(max_length) + print(fmt % "", end = " ") + for x in vxk: + vxf[x] = fmtfunc(vx[x]) + print(vxf[x] % x, end = " ") + print(endline) + if seplinefunc: + print(sepline) + for y in vyk: + print(fmt % y, end = " ") + for x in vxk: + print(vxf[x] % vz.get((x,y), "-"), end = " ") + print(endline) + if seplinefunc: + print(sepline) + +def make_table(*args, **kargs): + __make_table(lambda l:"%%-%is" % l, lambda l:"%%-%is" % l, "", *args, **kargs) + +def make_lined_table(*args, **kargs): + __make_table(lambda l:"%%-%is |" % l, lambda l:"%%-%is |" % l, "", + seplinefunc=lambda max_length,x:"+".join([ "-"*(y+2) for y in [max_length-1]+x+[-2]]), + *args, **kargs) + +def make_tex_table(*args, **kargs): + __make_table(lambda l: "%s", lambda l: "& %s", "\\\\", seplinefunc=lambda a,x:"\\hline", *args, **kargs) + diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/utils6.py b/scripts/external_libs/scapy-python3-0.18/scapy/utils6.py new file mode 100644 index 00000000..d9112aa5 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/utils6.py @@ -0,0 +1,823 @@ +## 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 + +""" +Utility functions for IPv6. +""" + +import itertools +from .config import conf +from .data import * +from .utils import * + +def cmp_to_key(mycmp): + 'Convert a cmp= function into a key= function' + class K(object): + def __init__(self, obj, *args): + self.obj = obj + def __lt__(self, other): + return mycmp(self.obj, other.obj) < 0 + def __gt__(self, other): + return mycmp(self.obj, other.obj) > 0 + def __eq__(self, other): + return mycmp(self.obj, other.obj) == 0 + def __le__(self, other): + return mycmp(self.obj, other.obj) <= 0 + def __ge__(self, other): + return mycmp(self.obj, other.obj) >= 0 + def __ne__(self, other): + return mycmp(self.obj, other.obj) != 0 + return K + + + +def construct_source_candidate_set(addr, plen, laddr, loname): + """ + Given all addresses assigned to a specific interface ('laddr' parameter), + this function returns the "candidate set" associated with 'addr/plen'. + + Basically, the function filters all interface addresses to keep only those + that have the same scope as provided prefix. + + This is on this list of addresses that the source selection mechanism + will then be performed to select the best source address associated + with some specific destination that uses this prefix. + """ + def cset_sort(x,y): + x_global = 0 + if in6_isgladdr(x): + x_global = 1 + y_global = 0 + if in6_isgladdr(y): + y_global = 1 + res = y_global - x_global + if res != 0 or y_global != 1: + return res + # two global addresses: if one is native, it wins. + if not in6_isaddr6to4(x): + return -1; + return -res + + cset = [] + if in6_isgladdr(addr) or in6_isuladdr(addr): + cset = [ x for x in laddr if x[1] == IPV6_ADDR_GLOBAL ] + elif in6_islladdr(addr): + cset = [ x for x in laddr if x[1] == IPV6_ADDR_LINKLOCAL ] + elif in6_issladdr(addr): + cset = [ x for x in laddr if x[1] == IPV6_ADDR_SITELOCAL ] + elif in6_ismaddr(addr): + if in6_ismnladdr(addr): + cset = [('::1', 16, loname)] + elif in6_ismgladdr(addr): + cset = [ x for x in laddr if x[1] == IPV6_ADDR_GLOBAL ] + elif in6_ismlladdr(addr): + cset = [ x for x in laddr if x[1] == IPV6_ADDR_LINKLOCAL ] + elif in6_ismsladdr(addr): + cset = [ x for x in laddr if x[1] == IPV6_ADDR_SITELOCAL ] + elif addr == '::' and plen == 0: + cset = [ x for x in laddr if x[1] == IPV6_ADDR_GLOBAL ] + cset = [ x[0] for x in cset ] + cset.sort(key = cmp_to_key(cset_sort)) # Sort with global addresses first + return cset + +def get_source_addr_from_candidate_set(dst, candidate_set): + """ + This function implement a limited version of source address selection + algorithm defined in section 5 of RFC 3484. The format is very different + from that described in the document because it operates on a set + of candidate source address for some specific route. + """ + + def scope_cmp(a, b): + """ + Given two addresses, returns -1, 0 or 1 based on comparison of + their scope + """ + scope_mapper = {IPV6_ADDR_GLOBAL: 4, + IPV6_ADDR_SITELOCAL: 3, + IPV6_ADDR_LINKLOCAL: 2, + IPV6_ADDR_LOOPBACK: 1} + sa = in6_getscope(a) + if sa == -1: + sa = IPV6_ADDR_LOOPBACK + sb = in6_getscope(b) + if sb == -1: + sb = IPV6_ADDR_LOOPBACK + + sa = scope_mapper[sa] + sb = scope_mapper[sb] + + if sa == sb: + return 0 + if sa > sb: + return 1 + return -1 + + def rfc3484_cmp(source_a, source_b): + """ + The function implements a limited version of the rules from Source + Address selection algorithm defined section of RFC 3484. + """ + + # Rule 1: Prefer same address + if source_a == dst: + return 1 + if source_b == dst: + return 1 + + # Rule 2: Prefer appropriate scope + tmp = scope_cmp(source_a, source_b) + if tmp == -1: + if scope_cmp(source_a, dst) == -1: + return 1 + else: + return -1 + elif tmp == 1: + if scope_cmp(source_b, dst) == -1: + return 1 + else: + return -1 + + # Rule 3: cannot be easily implemented + # Rule 4: cannot be easily implemented + # Rule 5: does not make sense here + # Rule 6: cannot be implemented + # Rule 7: cannot be implemented + + # Rule 8: Longest prefix match + tmp1 = in6_get_common_plen(source_a, dst) + tmp2 = in6_get_common_plen(source_b, dst) + if tmp1 > tmp2: + return 1 + elif tmp2 > tmp1: + return -1 + return 0 + + if not candidate_set: + # Should not happen + return None + + candidate_set.sort(key=cmp_to_key(rfc3484_cmp), reverse=True) + + return candidate_set[0] + + +def find_ifaddr2(addr, plen, laddr): + dstAddrType = in6_getAddrType(addr) + + if dstAddrType == IPV6_ADDR_UNSPECIFIED: # Shouldn't happen as dst addr + return None + + if dstAddrType == IPV6_ADDR_LOOPBACK: + return None + + #tmp = [[]] + map(lambda (x,y,z): (in6_getAddrType(x), x, y, z), laddr) + tmp = [[]] + map(lambda a: (in6_getAddrType(a[0]), a[0], a[1], a[2]), laddr) + #def filterSameScope(l, t): + # if (t[0] & dstAddrType & IPV6_ADDR_SCOPE_MASK) == 0: + # l.append(t) + # return l + #sameScope = reduce(filterSameScope, tmp) + sameScope = itertools.chain(*[ t for t in tmp if (t[0] & dstAddrType & IPV6_ADDR_SCOPE_MASK) == 0 ]) + + l = len(sameScope) + if l == 1: # Only one address for our scope + return sameScope[0][1] + + elif l > 1: # Muliple addresses for our scope + #stfAddr = filter(lambda x: x[0] & IPV6_ADDR_6TO4, sameScope) + stfAddr = [ x for x in sameScope if x[0] & IPV6_ADDR_6TO4 ] + #nativeAddr = filter(lambda x: not (x[0] & IPV6_ADDR_6TO4), sameScope) + nativeAddr = [ x for x in sameScope if not (x[0] & IPV6_ADDR_6TO4) ] + + if not (dstAddrType & IPV6_ADDR_6TO4): # destination is not 6to4 + if len(nativeAddr) != 0: + return nativeAddr[0][1] + return stfAddr[0][1] + + else: # Destination is 6to4, try to use source 6to4 addr if any + if len(stfAddr) != 0: + return stfAddr[0][1] + return nativeAddr[0][1] + else: + return None + +# Think before modify it : for instance, FE::1 does exist and is unicast +# there are many others like that. +# TODO : integrate Unique Local Addresses +def in6_getAddrType(addr): + naddr = inet_pton(socket.AF_INET6, addr) + paddr = inet_ntop(socket.AF_INET6, naddr) # normalize + addrType = 0 + # _Assignable_ Global Unicast Address space + # is defined in RFC 3513 as those in 2000::/3 + #if ((struct.unpack("B", naddr[0])[0] & 0xE0) == 0x20): + if (((naddr[0]) & 0xE0) == 0x20): + addrType = (IPV6_ADDR_UNICAST | IPV6_ADDR_GLOBAL) + if naddr[:2] == b' \x02': # Mark 6to4 @ + addrType |= IPV6_ADDR_6TO4 + elif naddr[0] == 0xff: # multicast + addrScope = paddr[3] + if addrScope == '2': + addrType = (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_MULTICAST) + elif addrScope == 'e': + addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_MULTICAST) + else: + addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_MULTICAST) + elif ((naddr[0] == 0xfe) and ((int(paddr[2], 16) & 0xC) == 0x8)): + addrType = (IPV6_ADDR_UNICAST | IPV6_ADDR_LINKLOCAL) + elif paddr == "::1": + addrType = IPV6_ADDR_LOOPBACK + elif paddr == "::": + addrType = IPV6_ADDR_UNSPECIFIED + else: + # Everything else is global unicast (RFC 3513) + # Even old deprecated (RFC3879) Site-Local addresses + addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST) + + return addrType + +def in6_mactoifaceid(mac, ulbit=None): + """ + Compute the interface ID in modified EUI-64 format associated + to the Ethernet address provided as input. + value taken by U/L bit in the interface identifier is basically + the reversed value of that in given MAC address it can be forced + to a specific value by using optional 'ulbit' parameter. + """ + if len(mac) != 17: return None + m = "".join(mac.split(':')) + if len(m) != 12: return None + first = int(m[0:2], 16) + if ulbit is None or not (ulbit == 0 or ulbit == 1): + ulbit = [1,'-',0][first & 0x02] + ulbit *= 2 + first = "%.02x" % ((first & 0xFD) | ulbit) + eui64 = first + m[2:4] + ":" + m[4:6] + "FF:FE" + m[6:8] + ":" + m[8:12] + return eui64.upper() + +def in6_ifaceidtomac(ifaceid): # TODO: finish commenting function behavior + """ + Extract the mac address from provided iface ID. Iface ID is provided + in printable format ("XXXX:XXFF:FEXX:XXXX", eventually compressed). None + is returned on error. + """ + try: + ifaceid = inet_pton(socket.AF_INET6, "::"+ifaceid)[8:16] + except: + return None + if ifaceid[3:5] != b'\xff\xfe': + return None + first = struct.unpack("B", ifaceid[:1])[0] + ulbit = 2*[1,'-',0][first & 0x02] + first = struct.pack("B", ((first & 0xFD) | ulbit)) + oui = first + ifaceid[1:3] + end = ifaceid[5:] + #l = map(lambda x: "%.02x" % struct.unpack("B", x)[0], list(oui+end)) + l = map(lambda x: "%.02x" % x, list(oui+end)) + return ":".join(l) + +def in6_addrtomac(addr): + """ + Extract the mac address from provided address. None is returned + on error. + """ + mask = inet_pton(socket.AF_INET6, "::ffff:ffff:ffff:ffff") + x = in6_and(mask, inet_pton(socket.AF_INET6, addr)) + ifaceid = inet_ntop(socket.AF_INET6, x)[2:] + return in6_ifaceidtomac(ifaceid) + +def in6_addrtovendor(addr): + """ + Extract the MAC address from a modified EUI-64 constructed IPv6 + address provided and use the IANA oui.txt file to get the vendor. + The database used for the conversion is the one loaded by Scapy, + based on Wireshark (/usr/share/wireshark/wireshark/manuf) None + is returned on error, "UNKNOWN" if the vendor is unknown. + """ + mac = in6_addrtomac(addr) + if mac is None: + return None + + res = conf.manufdb._get_manuf(mac) + if len(res) == 17 and res.count(b':') != 5: # Mac address, i.e. unknown + res = "UNKNOWN" + + return res + +def in6_getLinkScopedMcastAddr(addr, grpid=None, scope=2): + """ + Generate a Link-Scoped Multicast Address as described in RFC 4489. + Returned value is in printable notation. + + 'addr' parameter specifies the link-local address to use for generating + Link-scoped multicast address IID. + + By default, the function returns a ::/96 prefix (aka last 32 bits of + returned address are null). If a group id is provided through 'grpid' + parameter, last 32 bits of the address are set to that value (accepted + formats : '\x12\x34\x56\x78' or '12345678' or 0x12345678 or 305419896). + + By default, generated address scope is Link-Local (2). That value can + be modified by passing a specific 'scope' value as an argument of the + function. RFC 4489 only authorizes scope values <= 2. Enforcement + is performed by the function (None will be returned). + + If no link-local address can be used to generate the Link-Scoped IPv6 + Multicast address, or if another error occurs, None is returned. + """ + if not scope in [0, 1, 2]: + return None + try: + if not in6_islladdr(addr): + return None + addr = inet_pton(socket.AF_INET6, addr) + except: + warning("in6_getLinkScopedMcastPrefix(): Invalid address provided") + return None + + iid = addr[8:] + + if grpid is None: + grpid = b'\x00\x00\x00\x00' + else: + if type(grpid) is str: + grpid = grpid.encode('ascii') + if type(grpid) is bytes: + if len(grpid) == 8: + try: + grpid = int(grpid, 16) & 0xffffffff + except: + warning("in6_getLinkScopedMcastPrefix(): Invalid group id provided") + return None + elif len(grpid) == 4: + try: + grpid = struct.unpack("!I", grpid)[0] + except: + warning("in6_getLinkScopedMcastPrefix(): Invalid group id provided") + return None + grpid = struct.pack("!I", grpid) + + flgscope = struct.pack("B", 0xff & ((0x3 << 4) | scope)) + plen = b'\xff' + res = b'\x00' + a = b'\xff' + flgscope + res + plen + iid + grpid + + return inet_ntop(socket.AF_INET6, a) + +def in6_get6to4Prefix(addr): + """ + Returns the /48 6to4 prefix associated with provided IPv4 address + On error, None is returned. No check is performed on public/private + status of the address + """ + try: + addr = inet_pton(socket.AF_INET, addr) + addr = inet_ntop(socket.AF_INET6, b'\x20\x02'+addr+b'\x00'*10) + except: + return None + return addr + +def in6_6to4ExtractAddr(addr): + """ + Extract IPv4 address embbeded in 6to4 address. Passed address must be + a 6to4 addrees. None is returned on error. + """ + try: + addr = inet_pton(socket.AF_INET6, addr) + except: + return None + if addr[:2] != b" \x02": + return None + return inet_ntop(socket.AF_INET, addr[2:6]) + + +def in6_getLocalUniquePrefix(): + """ + Returns a pseudo-randomly generated Local Unique prefix. Function + follows recommandation of Section 3.2.2 of RFC 4193 for prefix + generation. + """ + # 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. + + # epoch = (1900, 1, 1, 0, 0, 0, 5, 1, 0) + # x = time.time() + # from time import gmtime, strftime, gmtime, mktime + # delta = mktime(gmtime(0)) - mktime(self.epoch) + # x = x-delta + + tod = time.time() # time of day. Will bother with epoch later + i = int(tod) + j = int((tod - i)*(2**32)) + tod = struct.pack("!II", i,j) + # TODO: Add some check regarding system address gathering + rawmac = get_if_raw_hwaddr(conf.iface6) + mac = b":".join(map(lambda x: b"%.02x" % ord(x), list(rawmac))) + # construct modified EUI-64 ID + eui64 = inet_pton(socket.AF_INET6, '::' + in6_mactoifaceid(mac))[8:] + import sha + globalid = sha.new(tod+eui64).digest()[:5] + return inet_ntop(socket.AF_INET6, b'\xfd' + globalid + b'\x00'*10) + +def in6_getRandomizedIfaceId(ifaceid, previous=None): + """ + Implements the interface ID generation algorithm described in RFC 3041. + The function takes the Modified EUI-64 interface identifier generated + as described in RFC 4291 and an optional previous history value (the + first element of the output of this function). If no previous interface + identifier is provided, a random one is generated. The function returns + a tuple containing the randomized interface identifier and the history + value (for possible future use). Input and output values are provided in + a "printable" format as depicted below. + + ex: + + >>> in6_getRandomizedIfaceId('20b:93ff:feeb:2d3') + ('4c61:76ff:f46a:a5f3', 'd006:d540:db11:b092') + + >>> in6_getRandomizedIfaceId('20b:93ff:feeb:2d3', + previous='d006:d540:db11:b092') + ('fe97:46fe:9871:bd38', 'eeed:d79c:2e3f:62e') + """ + + s = [] + if previous is None: + #d = b"".join(map(chr, range(256))) + d = list(range(256)) + for i in range(8): + s.append(random.choice(d)) + s = bytes(s) + previous = s + s = inet_pton(socket.AF_INET6, "::"+ifaceid)[8:] + previous + import hashlib + s = hashlib.md5(s).digest() + s1,s2 = s[:8],s[8:] + s1 = bytes([(s1[0]) | 0x04]) + s1[1:] + s1 = inet_ntop(socket.AF_INET6, b"\xff"*8 + s1)[20:] + s2 = inet_ntop(socket.AF_INET6, b"\xff"*8 + s2)[20:] + return (s1, s2) + + +_rfc1924map = [ '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E', + 'F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T', + 'U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i', + 'j','k','l','m','n','o','p','q','r','s','t','u','v','w','x', + 'y','z','!','#','$','%','&','(',')','*','+','-',';','<','=', + '>','?','@','^','_','`','{','|','}','~' ] + +def in6_ctop(addr): + """ + Convert an IPv6 address in Compact Representation Notation + (RFC 1924) to printable representation ;-) + Returns None on error. + """ + #if len(addr) != 20 or not reduce(lambda x,y: x and y, map(lambda x: x in _rfc1924map, addr)): + if len(addr) != 20 or not all(map(lambda x: x in _rfc1924map, addr)): + return None + i = 0 + for c in addr: + j = _rfc1924map.index(c) + i = 85*i + j + res = [] + for j in range(4): + res.append(struct.pack("!I", i%2**32)) + i = i//(2**32) + res.reverse() + return inet_ntop(socket.AF_INET6, b"".join(res)) + +def in6_ptoc(addr): + """ + Converts an IPv6 address in printable representation to RFC + 1924 Compact Representation ;-) + Returns None on error. + """ + try: + d=struct.unpack("!IIII", inet_pton(socket.AF_INET6, addr)) + except: + return None + res = 0 + m = [2**96, 2**64, 2**32, 1] + for i in range(4): + res += d[i]*m[i] + rem = res + res = [] + while rem: + res.append(_rfc1924map[rem%85]) + rem = rem//85 + res.reverse() + return "".join(res) + + +def in6_isaddr6to4(x): + """ + Return True if provided address (in printable format) is a 6to4 + address (being in 2002::/16). + """ + x = inet_pton(socket.AF_INET6, x) + return x[:2] == ' \x02' + +conf.teredoPrefix = "2001::" # old one was 3ffe:831f (it is a /32) +conf.teredoServerPort = 3544 + +def in6_isaddrTeredo(x): + """ + Return True if provided address is a Teredo, meaning it is under + the /32 conf.teredoPrefix prefix value (by default, 2001::). + Otherwise, False is returned. Address must be passed in printable + format. + """ + our = inet_pton(socket.AF_INET6, x)[0:4] + teredoPrefix = inet_pton(socket.AF_INET6, conf.teredoPrefix)[0:4] + return teredoPrefix == our + +def teredoAddrExtractInfo(x): + """ + Extract information from a Teredo address. Return value is + a 4-tuple made of IPv4 address of Teredo server, flag value (int), + mapped address (non obfuscated) and mapped port (non obfuscated). + No specific checks are performed on passed address. + """ + addr = inet_pton(socket.AF_INET6, x) + server = inet_ntop(socket.AF_INET, addr[4:8]) + flag = struct.unpack("!H",addr[8:10])[0] + mappedport = struct.unpack("!H",strxor(addr[10:12],b'\xff'*2))[0] + mappedaddr = inet_ntop(socket.AF_INET, strxor(addr[12:16],b'\xff'*4)) + return server, flag, mappedaddr, mappedport + +def in6_iseui64(x): + """ + Return True if provided address has an interface identifier part + created in modified EUI-64 format (meaning it matches *::*:*ff:fe*:*). + Otherwise, False is returned. Address must be passed in printable + format. + """ + eui64 = inet_pton(socket.AF_INET6, '::ff:fe00:0') + x = in6_and(inet_pton(socket.AF_INET6, x), eui64) + return x == eui64 + +def in6_isanycast(x): # RFC 2526 + if in6_iseui64(x): + s = '::fdff:ffff:ffff:ff80' + packed_x = inet_pton(socket.AF_INET6, x) + packed_s = inet_pton(socket.AF_INET6, s) + x_and_s = in6_and(packed_x, packed_s) + return x_and_s == packed_s + else: + # not EUI-64 + #| n bits | 121-n bits | 7 bits | + #+---------------------------------+------------------+------------+ + #| subnet prefix | 1111111...111111 | anycast ID | + #+---------------------------------+------------------+------------+ + # | interface identifier field | + warning('in6_isanycast(): TODO not EUI-64') + return 0 + +def _in6_bitops(a1, a2, operator=0): + a1 = struct.unpack('4I', a1) + a2 = struct.unpack('4I', a2) + fop = [ lambda x,y: x | y, + lambda x,y: x & y, + lambda x,y: x ^ y + ] + ret = map(fop[operator%len(fop)], a1, a2) + t = b''.join(map(lambda x: struct.pack('I', x), ret)) + return t + +def in6_or(a1, a2): + """ + Provides a bit to bit OR of provided addresses. They must be + passed in network format. Return value is also an IPv6 address + in network format. + """ + return _in6_bitops(a1, a2, 0) + +def in6_and(a1, a2): + """ + Provides a bit to bit AND of provided addresses. They must be + passed in network format. Return value is also an IPv6 address + in network format. + """ + return _in6_bitops(a1, a2, 1) + +def in6_xor(a1, a2): + """ + Provides a bit to bit XOR of provided addresses. They must be + passed in network format. Return value is also an IPv6 address + in network format. + """ + return _in6_bitops(a1, a2, 2) + +def in6_cidr2mask(m): + """ + Return the mask (bitstring) associated with provided length + value. For instance if function is called on 48, return value is + '\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'. + + """ + if m > 128 or m < 0: + raise Scapy_Exception("value provided to in6_cidr2mask outside [0, 128] domain (%d)" % m) + + t = [] + for i in range(0, 4): + t.append(max(0, 2**32 - 2**(32-min(32, m)))) + m -= 32 + + return b"".join([ struct.pack('!I', i) for i in t ]) + +def in6_getnsma(a): + """ + Return link-local solicited-node multicast address for given + address. Passed address must be provided in network format. + Returned value is also in network format. + """ + + r = in6_and(a, inet_pton(socket.AF_INET6, '::ff:ffff')) + r = in6_or(inet_pton(socket.AF_INET6, 'ff02::1:ff00:0'), r) + return r + +def in6_getnsmac(a): # return multicast Ethernet address associated with multicast v6 destination + """ + Return the multicast mac address associated with provided + IPv6 address. Passed address must be in network format. + """ + + a = struct.unpack('16B', a)[-4:] + mac = '33:33:' + mac += (':'.join(map(lambda x: '%.2x' %x, a))) + return mac + +def in6_getha(prefix): + """ + Return the anycast address associated with all home agents on a given + subnet. + """ + r = in6_and(inet_pton(socket.AF_INET6, prefix), in6_cidr2mask(64)) + r = in6_or(r, inet_pton(socket.AF_INET6, '::fdff:ffff:ffff:fffe')) + return inet_ntop(socket.AF_INET6, r) + +def in6_ptop(s): + """ + Normalizes IPv6 addresses provided in printable format, returning the + same address in printable format. (2001:0db8:0:0::1 -> 2001:db8::1) + """ + return inet_ntop(socket.AF_INET6, inet_pton(socket.AF_INET6, s)) + +def in6_isincluded(addr, prefix, plen): + """ + Returns True when 'addr' belongs to prefix/plen. False otherwise. + """ + temp = inet_pton(socket.AF_INET6, addr) + pref = in6_cidr2mask(plen) + zero = inet_pton(socket.AF_INET6, prefix) + return zero == in6_and(temp, pref) + +def in6_isdocaddr(s): + """ + Returns True if provided address in printable format belongs to + 2001:db8::/32 address space reserved for documentation (as defined + in RFC 3849). + """ + return in6_isincluded(s, '2001:db8::', 32) + +def in6_islladdr(s): + """ + Returns True if provided address in printable format belongs to + _allocated_ link-local unicast address space (fe80::/10) + """ + return in6_isincluded(s, 'fe80::', 10) + +def in6_issladdr(s): + """ + Returns True if provided address in printable format belongs to + _allocated_ site-local address space (fec0::/10). This prefix has + been deprecated, address being now reserved by IANA. Function + will remain for historic reasons. + """ + return in6_isincluded(s, 'fec0::', 10) + +def in6_isuladdr(s): + """ + Returns True if provided address in printable format belongs to + Unique local address space (fc00::/7). + """ + return in6_isincluded(s, 'fc00::', 7) + +# TODO : we should see the status of Unique Local addresses against +# global address space. +# Up-to-date information is available through RFC 3587. +# We should review function behavior based on its content. +def in6_isgladdr(s): + """ + Returns True if provided address in printable format belongs to + _allocated_ global address space (2000::/3). Please note that, + Unique Local addresses (FC00::/7) are not part of global address + space, and won't match. + """ + return in6_isincluded(s, '2000::', 3) + +def in6_ismaddr(s): + """ + Returns True if provided address in printable format belongs to + allocated Multicast address space (ff00::/8). + """ + return in6_isincluded(s, 'ff00::', 8) + +def in6_ismnladdr(s): + """ + Returns True if address belongs to node-local multicast address + space (ff01::/16) as defined in RFC + """ + return in6_isincluded(s, 'ff01::', 16) + +def in6_ismgladdr(s): + """ + Returns True if address belongs to global multicast address + space (ff0e::/16). + """ + return in6_isincluded(s, 'ff0e::', 16) + +def in6_ismlladdr(s): + """ + Returns True if address belongs to link-local multicast address + space (ff02::/16) + """ + return in6_isincluded(s, 'ff02::', 16) + +def in6_ismsladdr(s): + """ + Returns True if address belongs to site-local multicast address + space (ff05::/16). Site local address space has been deprecated. + Function remains for historic reasons. + """ + return in6_isincluded(s, 'ff05::', 16) + +def in6_isaddrllallnodes(s): + """ + Returns True if address is the link-local all-nodes multicast + address (ff02::1). + """ + return (inet_pton(socket.AF_INET6, "ff02::1") == + inet_pton(socket.AF_INET6, s)) + +def in6_isaddrllallservers(s): + """ + Returns True if address is the link-local all-servers multicast + address (ff02::2). + """ + return (inet_pton(socket.AF_INET6, "ff02::2") == + inet_pton(socket.AF_INET6, s)) + +def in6_getscope(addr): + """ + Returns the scope of the address. + """ + if in6_isgladdr(addr) or in6_isuladdr(addr): + scope = IPV6_ADDR_GLOBAL + elif in6_islladdr(addr): + scope = IPV6_ADDR_LINKLOCAL + elif in6_issladdr(addr): + scope = IPV6_ADDR_SITELOCAL + elif in6_ismaddr(addr): + if in6_ismgladdr(addr): + scope = IPV6_ADDR_GLOBAL + elif in6_ismlladdr(addr): + scope = IPV6_ADDR_LINKLOCAL + elif in6_ismsladdr(addr): + scope = IPV6_ADDR_SITELOCAL + elif in6_ismnladdr(addr): + scope = IPV6_ADDR_LOOPBACK + else: + scope = -1 + elif addr == '::1': + scope = IPV6_ADDR_LOOPBACK + else: + scope = -1 + return scope + +def in6_get_common_plen(a, b): + """ + Return common prefix length of IPv6 addresses a and b. + """ + def matching_bits(byte1, byte2): + for i in range(8): + cur_mask = 0x80 >> i + if (byte1 & cur_mask) != (byte2 & cur_mask): + return i + return 8 + + tmpA = inet_pton(socket.AF_INET6, a) + tmpB = inet_pton(socket.AF_INET6, b) + for i in range(16): + #mbits = matching_bits(ord(tmpA[i]), ord(tmpB[i])) + mbits = matching_bits((tmpA[i]), (tmpB[i])) + if mbits != 8: + return 8*i + mbits + return 128 diff --git a/scripts/external_libs/scapy-python3-0.18/scapy/volatile.py b/scripts/external_libs/scapy-python3-0.18/scapy/volatile.py new file mode 100644 index 00000000..ed5c26e4 --- /dev/null +++ b/scripts/external_libs/scapy-python3-0.18/scapy/volatile.py @@ -0,0 +1,685 @@ +## 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 + +""" +Fields that hold random numbers. +""" + +import random,time,math +from .base_classes import Net +from .utils import corrupt_bits,corrupt_bytes + +#################### +## Random numbers ## +#################### + + +class RandomEnumeration: + """iterate through a sequence in random order. + When all the values have been drawn, if forever=1, the drawing is done again. + If renewkeys=0, the draw will be in the same order, guaranteeing that the same + number will be drawn in not less than the number of integers of the sequence""" + def __init__(self, inf, sup, seed=None, forever=1, renewkeys=0): + self.forever = forever + self.renewkeys = renewkeys + self.inf = inf + self.rnd = random.Random(seed) + self.sbox_size = 256 + + self.top = sup-inf+1 + + n=0 + while (1<>= self.fs + lsb ^= self.sbox[ct%self.sbox_size] + ct |= lsb << (self.n-self.fs) + + if ct < self.top: + return self.inf+ct + self.i = 0 + if not self.forever: + raise StopIteration + +class _MetaVolatile(type): + def __init__(cls, name, bases, dct): + def special_gen(special_method): + def special_wrapper(self): + return getattr(getattr(self, "_fix")(), special_method) + return special_wrapper + + #This is from scapy2 code. Usage places should be identified and fixed as there is no more __cmp__ in python3 + # if attr == "__cmp__": + # x = self._fix() + # def cmp2(y,x=x): + # if type(x) != type(y): + # return -1 + # return x.__cmp__(y) + # return cmp2 + + type.__init__(cls, name, bases, dct) + for i in ["__int__", "__repr__", "__str__", "__index__", "__add__", "__radd__", "__bytes__","__mul__","__rmul__"]: + setattr(cls, i, property(special_gen(i))) + + +class VolatileValue(metaclass = _MetaVolatile): + def __repr__(self): + return "<%s>" % self.__class__.__name__ + def __getattr__(self, attr): + if attr == "__setstate__": + raise AttributeError("__setstate__") + return getattr(self._fix(),attr) + def _fix(self): + return None + + +class RandField(VolatileValue): + pass + +class RandNum(RandField): + """Instances evaluate to random integers in selected range""" + min = 0 + max = 0 + def __init__(self, min, max): + self.min = min + self.max = max + def _fix(self): + return random.randrange(self.min, self.max+1) + +class RandNumGamma(RandField): + def __init__(self, alpha, beta): + self.alpha = alpha + self.beta = beta + def _fix(self): + return int(round(random.gammavariate(self.alpha, self.beta))) + +class RandNumGauss(RandField): + def __init__(self, mu, sigma): + self.mu = mu + self.sigma = sigma + def _fix(self): + return int(round(random.gauss(self.mu, self.sigma))) + +class RandNumExpo(RandField): + def __init__(self, lambd, base=0): + self.lambd = lambd + self.base = base + def _fix(self): + return self.base+int(round(random.expovariate(self.lambd))) + +class RandEnum(RandNum): + """Instances evaluate to integer sampling without replacement from the given interval""" + def __init__(self, min, max): + self.seq = RandomEnumeration(min,max) + def _fix(self): + return next(self.seq) + +class RandByte(RandNum): + def __init__(self): + RandNum.__init__(self, 0, 2**8-1) + +class RandSByte(RandNum): + def __init__(self): + RandNum.__init__(self, -2**7, 2**7-1) + +class RandShort(RandNum): + def __init__(self): + RandNum.__init__(self, 0, 2**16-1) + +class RandSShort(RandNum): + def __init__(self): + RandNum.__init__(self, -2**15, 2**15-1) + +class RandInt(RandNum): + def __init__(self): + RandNum.__init__(self, 0, 2**32-1) + +class RandSInt(RandNum): + def __init__(self): + RandNum.__init__(self, -2**31, 2**31-1) + +class RandLong(RandNum): + def __init__(self): + RandNum.__init__(self, 0, 2**64-1) + +class RandSLong(RandNum): + def __init__(self): + RandNum.__init__(self, -2**63, 2**63-1) + +class RandEnumByte(RandEnum): + def __init__(self): + RandEnum.__init__(self, 0, 2**8-1) + +class RandEnumSByte(RandEnum): + def __init__(self): + RandEnum.__init__(self, -2**7, 2**7-1) + +class RandEnumShort(RandEnum): + def __init__(self): + RandEnum.__init__(self, 0, 2**16-1) + +class RandEnumSShort(RandEnum): + def __init__(self): + RandEnum.__init__(self, -2**15, 2**15-1) + +class RandEnumInt(RandEnum): + def __init__(self): + RandEnum.__init__(self, 0, 2**32-1) + +class RandEnumSInt(RandEnum): + def __init__(self): + RandEnum.__init__(self, -2**31, 2**31-1) + +class RandEnumLong(RandEnum): + def __init__(self): + RandEnum.__init__(self, 0, 2**64-1) + +class RandEnumSLong(RandEnum): + def __init__(self): + RandEnum.__init__(self, -2**63, 2**63-1) + +class RandChoice(RandField): + def __init__(self, *args): + if not args: + raise TypeError("RandChoice needs at least one choice") + self._choice = args + def _fix(self): + return random.choice(self._choice) + +class RandString(RandField): + def __init__(self, size=None, chars=b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"): + if size is None: + size = RandNumExpo(0.01) + self.size = size + self.chars = chars + def _fix(self): + s = [] + for i in range(self.size): + s.append(random.choice(self.chars)) + return bytes(s) + +class RandBin(RandString): + def __init__(self, size=None): + #RandString.__init__(self, size, b"".join(map(chr,range(256)))) + RandString.__init__(self, size, b"".join([bytes([i]) for i in range(256)])) + + +class RandTermString(RandString): + def __init__(self, size, term): + #RandString.__init__(self, size, b"".join(map(chr,range(1,256)))) + RandString.__init__(self, size, bytes([i for i in range(1,256)])) + self.term = term + def _fix(self): + return RandString._fix(self)+self.term + + +class RandIP(RandString): + def __init__(self, iptemplate="0.0.0.0/0"): + self.ip = Net(iptemplate) + def _fix(self): + return self.ip.choice() + +class RandMAC(RandString): + def __init__(self, template="*"): + template += ":*:*:*:*:*" + template = template.split(":") + self.mac = () + for i in range(6): + if template[i] == "*": + v = RandByte() + elif "-" in template[i]: + x,y = template[i].split("-") + v = RandNum(int(x,16), int(y,16)) + else: + v = int(template[i],16) + self.mac += (v,) + def _fix(self): + return "%02x:%02x:%02x:%02x:%02x:%02x" % self.mac + +class RandIP6(RandString): + def __init__(self, ip6template="**"): + self.tmpl = ip6template + self.sp = self.tmpl.split(":") + for i,v in enumerate(self.sp): + if not v or v == "**": + continue + if "-" in v: + a,b = v.split("-") + elif v == "*": + a=b="" + else: + a=b=v + + if not a: + a = "0" + if not b: + b = "ffff" + if a==b: + self.sp[i] = int(a,16) + else: + self.sp[i] = RandNum(int(a,16), int(b,16)) + self.variable = "" in self.sp + self.multi = self.sp.count("**") + def _fix(self): + done = 0 + nbm = self.multi + ip = [] + for i,n in enumerate(self.sp): + if n == "**": + nbm -= 1 + remain = 8-(len(self.sp)-i-1)-len(ip)+nbm + if "" in self.sp: + remain += 1 + if nbm or self.variable: + remain = random.randint(0,remain) + for j in range(remain): + ip.append("%04x" % random.randint(0,65535)) + elif n == 0: + ip.append("0") + elif not n: + ip.append("") + else: + ip.append("%04x" % n) + if len(ip) == 9: + ip.remove("") + if ip[-1] == "": + ip[-1] = 0 + return ":".join(ip) + +class RandOID(RandString): + def __init__(self, fmt=None, depth=RandNumExpo(0.1), idnum=RandNumExpo(0.01)): + self.ori_fmt = fmt + if fmt is not None: + fmt = fmt.split(".") + for i in range(len(fmt)): + if "-" in fmt[i]: + fmt[i] = tuple(map(int, fmt[i].split("-"))) + self.fmt = fmt + self.depth = depth + self.idnum = idnum + def __repr__(self): + if self.ori_fmt is None: + return "<%s>" % self.__class__.__name__ + else: + return "<%s [%s]>" % (self.__class__.__name__, self.ori_fmt) + def _fix(self): + if self.fmt is None: + return ".".join(map(str, [self.idnum for i in range(1+self.depth)])) + else: + oid = [] + for i in self.fmt: + if i == "*": + oid.append(str(self.idnum)) + elif i == "**": + oid += map(str, [self.idnum for i in range(1+self.depth)]) + elif type(i) is tuple: + oid.append(str(random.randrange(*i))) + else: + oid.append(i) + return ".".join(oid) + + +class RandRegExp(RandField): + def __init__(self, regexp, lambda_=0.3,): + self._regexp = regexp + self._lambda = lambda_ + + @staticmethod + def choice_expand(s): #XXX does not support special sets like (ex ':alnum:') + m = "" + invert = s and s[0] == "^" + while True: + p = s.find("-") + if p < 0: + break + if p == 0 or p == len(s)-1: + m = "-" + if p: + s = s[:-1] + else: + s = s[1:] + else: + c1 = s[p-1] + c2 = s[p+1] + rng = "".join(map(chr, range(ord(c1),ord(c2)+1))) + s = s[:p-1]+rng+s[p+1:] + res = m+s + if invert: + res = "".join([chr(x) for x in range(256) if chr(x) not in res]) + return res + + @staticmethod + def stack_fix(lst, index): + r = "" + mul = 1 + for e in lst: + if type(e) is list: + if mul != 1: + mul = mul-1 + r += RandRegExp.stack_fix(e[1:]*mul, index) + # only the last iteration should be kept for back reference + f = RandRegExp.stack_fix(e[1:], index) + for i,idx in enumerate(index): + if e is idx: + index[i] = f + r += f + mul = 1 + elif type(e) is tuple: + kind,val = e + if kind == "cite": + r += index[val-1] + elif kind == "repeat": + mul = val + + elif kind == "choice": + if mul == 1: + c = random.choice(val) + r += RandRegExp.stack_fix(c[1:], index) + else: + r += RandRegExp.stack_fix([e]*mul, index) + mul = 1 + else: + if mul != 1: + r += RandRegExp.stack_fix([e]*mul, index) + mul = 1 + else: + r += str(e) + return r + + def _fix(self): + stack = [None] + index = [] + current = stack + i = 0 + ln = len(self._regexp) + interp = True + while i < ln: + c = self._regexp[i] + i+=1 + + if c == '(': + current = [current] + current[0].append(current) + elif c == '|': + p = current[0] + ch = p[-1] + if type(ch) is not tuple: + ch = ("choice",[current]) + p[-1] = ch + else: + ch[1].append(current) + current = [p] + elif c == ')': + ch = current[0][-1] + if type(ch) is tuple: + ch[1].append(current) + index.append(current) + current = current[0] + elif c == '[' or c == '{': + current = [current] + current[0].append(current) + interp = False + elif c == ']': + current = current[0] + choice = RandRegExp.choice_expand("".join(current.pop()[1:])) + current.append(RandChoice(*list(choice))) + interp = True + elif c == '}': + current = current[0] + num = "".join(current.pop()[1:]) + e = current.pop() + if "," not in num: + n = int(num) + current.append([current]+[e]*n) + else: + num_min,num_max = num.split(",") + if not num_min: + num_min = "0" + if num_max: + n = RandNum(int(num_min),int(num_max)) + else: + n = RandNumExpo(self._lambda,base=int(num_min)) + current.append(("repeat",n)) + current.append(e) + interp = True + elif c == '\\': + c = self._regexp[i] + if c == "s": + c = RandChoice(" ","\t") + elif c in "0123456789": + c = ("cite",ord(c)-0x30) + current.append(c) + i += 1 + elif not interp: + current.append(c) + elif c == '+': + e = current.pop() + current.append([current]+[e]*(int(random.expovariate(self._lambda))+1)) + elif c == '*': + e = current.pop() + current.append([current]+[e]*int(random.expovariate(self._lambda))) + elif c == '?': + if random.randint(0,1): + current.pop() + elif c == '.': + current.append(RandChoice(*[chr(x) for x in range(256)])) + elif c == '$' or c == '^': + pass + else: + current.append(c) + + return RandRegExp.stack_fix(stack[1:], index) + def __repr__(self): + return "<%s [%r]>" % (self.__class__.__name__, self._regexp) + +class RandSingularity(RandChoice): + pass + +class RandSingNum(RandSingularity): + @staticmethod + def make_power_of_two(end): + sign = 1 + if end == 0: + end = 1 + if end < 0: + end = -end + sign = -1 + end_n = int(math.log(end)/math.log(2))+1 + return set([sign*2**i for i in range(end_n)]) + + def __init__(self, mn, mx): + sing = set([0, mn, mx, int((mn+mx)/2)]) + sing |= self.make_power_of_two(mn) + sing |= self.make_power_of_two(mx) + for i in sing.copy(): + sing.add(i+1) + sing.add(i-1) + for i in sing.copy(): + if not mn <= i <= mx: + sing.remove(i) + self._choice = list(sing) + + +class RandSingByte(RandSingNum): + def __init__(self): + RandSingNum.__init__(self, 0, 2**8-1) + +class RandSingSByte(RandSingNum): + def __init__(self): + RandSingNum.__init__(self, -2**7, 2**7-1) + +class RandSingShort(RandSingNum): + def __init__(self): + RandSingNum.__init__(self, 0, 2**16-1) + +class RandSingSShort(RandSingNum): + def __init__(self): + RandSingNum.__init__(self, -2**15, 2**15-1) + +class RandSingInt(RandSingNum): + def __init__(self): + RandSingNum.__init__(self, 0, 2**32-1) + +class RandSingSInt(RandSingNum): + def __init__(self): + RandSingNum.__init__(self, -2**31, 2**31-1) + +class RandSingLong(RandSingNum): + def __init__(self): + RandSingNum.__init__(self, 0, 2**64-1) + +class RandSingSLong(RandSingNum): + def __init__(self): + RandSingNum.__init__(self, -2**63, 2**63-1) + +class RandSingString(RandSingularity): #TODO3 + def __init__(self): + self._choice = [ b"", + b"%x", + b"%%", + b"%s", + b"%i", + b"%n", + b"%x%x%x%x%x%x%x%x%x", + b"%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + b"%", + b"%%%", + b"A"*4096, + b"\x00"*4096, + b"\xff"*4096, + b"\x7f"*4096, + b"\x80"*4096, + b" "*4096, + b"\\"*4096, + b"("*4096, + b"../"*1024, + b"/"*1024, + b"${HOME}"*512, + b" or 1=1 --", + b"' or 1=1 --", + b'" or 1=1 --', + b" or 1=1; #", + b"' or 1=1; #", + b'" or 1=1; #', + b";reboot;", + b"$(reboot)", + b"`reboot`", + b"index.php%00", + b"\x00", + b"%00", + b"\\", + b"../../../../../../../../../../../../../../../../../etc/passwd", + b"%2e%2e%2f" * 20 + b"etc/passwd", + b"%252e%252e%252f" * 20 + b"boot.ini", + b"..%c0%af" * 20 + b"etc/passwd", + b"..%c0%af" * 20 + b"boot.ini", + b"//etc/passwd", + br"..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\boot.ini", + b"AUX:", + b"CLOCK$", + b"COM:", + b"CON:", + b"LPT:", + b"LST:", + b"NUL:", + b"CON:", + br"C:\CON\CON", + br"C:\boot.ini", + br"\\myserver\share", + b"foo.exe:", + b"foo.exe\\", ] + + +class RandPool(RandField): + def __init__(self, *args): + """Each parameter is a volatile object or a couple (volatile object, weight)""" + pool = [] + for p in args: + w = 1 + if type(p) is tuple: + p,w = p + pool += [p]*w + self._pool = pool + def _fix(self): + r = random.choice(self._pool) + return r._fix() + +# Automatic timestamp + +class AutoTime(VolatileValue): + def __init__(self, base=None): + if base == None: + self.diff = 0 + else: + self.diff = time.time()-base + def _fix(self): + return time.time()-self.diff + +class IntAutoTime(AutoTime): + def _fix(self): + return int(time.time()-self.diff) + + +class ZuluTime(AutoTime): + def __init__(self, diff=0): + self.diff=diff + def _fix(self): + return time.strftime("%y%m%d%H%M%SZ",time.gmtime(time.time()+self.diff)) + + +class DelayedEval(VolatileValue): + """ Example of usage: DelayedEval("time.time()") """ + def __init__(self, expr): + self.expr = expr + def _fix(self): + return eval(self.expr) + + +class IncrementalValue(VolatileValue): + def __init__(self, start=0, step=1, restart=-1): + self.start = self.val = start + self.step = step + self.restart = restart + def _fix(self): + v = self.val + if self.val == self.restart : + self.val = self.start + else: + self.val += self.step + return v + +class CorruptedBytes(VolatileValue): + def __init__(self, s, p=0.01, n=None): + self.s = s + self.p = p + self.n = n + def _fix(self): + return corrupt_bytes(self.s, p = self.p, n = self.n) + +class CorruptedBits(CorruptedBytes): + def _fix(self): + return corrupt_bits(self.s, p = self.p, n = self.n) + diff --git a/scripts/external_libs/scapy3-0.18-origin.rar b/scripts/external_libs/scapy3-0.18-origin.rar new file mode 100644 index 0000000000000000000000000000000000000000..0fda86fa33e772f48e0e638f49af3be86ed792c9 GIT binary patch literal 367282 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

y^eKKcJYrv|2W|1+e-jYo(^n)c;u5PQn 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 z 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 z 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%0D5ZRr91coq$W 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 z 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%0D2-30R`DnTqpu(w-GL@_^t?e{+-Shqy; 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�F+772Dj8l0s*>$ExLF`^67k~!)Z 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

CE+G6PX&)`IA-|zcGzic?&m zqRJnNLv_Kxh*7tH6=%4>29>*2N9uxS*x`(NT7+h1ywn?VT3huzL2X!cXS%8~ZBk>W ze`(R;*avkXhuI0X2VULl&Xk(k^=JpL!h$h+!W8XbO3sslmzY6e!^14M!4_WBVY+wR zJ)&&JSibttaI7D{|NS1wcndI)s8~9G3UC-%zk2>M*uMWJC~g}FvVQ!4{o2HGVu~wx zxPN5HsNm3euh9vK7#kY7|0Iy6Xhmj-{}aT7KPS_9V8?=PtyV{rf`;ag*iNjCN&5Pa zWf#06mH1$}x)$gy%EuG9}D|StKW0pUNNx z_qeB7YV}5%If#5D*9tfVBo{$6oP4%lBu1)frUyZM8P2M(`FZmTkzS@of)P?M(f6OT z9JmAKZbcap_aVxGO0{M*XUuJ`Yd(Ghmb&4DYwVZ-Tb{X-e_Ih>;+o)*-PP>a6h4Dq z^X5iTI)QNJ>#DKh`;4~;>%h{6uLrAkTS?7B`ON8xrX{VPW6zHu*sOD?ze4LU%vCCf zz<`RBsJgoOn@1Q#BNS>j>T1fMYcwi-OAGDl*t4O6B8ad#@yRXWZ)vL)bLsHLyMNV8 zzl;5T1J~l>ynE+7Nyr#BNq3O;)d0Cr8wDi%lwkIYp|hO(Ih(U2;4$bcJS|4Y?e{9<>^bwUq3FZg;ymWwxN)uRXD-v@Qr?J?6qIUHd6Q&NDVB~x1W z!-jTU@VJzBY?~mUV7qM!d2{cNZcdsUFm)3v&;jI^7WpE*jOPniXB5~jyq=eIQOfo< zerWU8pOG=C|6u-B#l@E{l!DHOi(O2aIZuaU9(5oMF@x!1ur#9TO2*_P@T;ECn+&09 z9be);M8mV|>1A3KLTkT5Q;muH^V8GgQ>iG0buA@pGM1_RN>U8&h}T*G?>uMzQh^?e zrG+~jWRm>KP=pAIHW_akE<{9RH}=#8RGzgPfH>lmu>-n!A}2AEO+&D${R< zX@(Mryk&=~A6l6!g;O(4JfxXY$Aw~vT#bp0VjVd<48X6@Ny0hpYGXDe*t5UJyPUoa zIuyhHOpc`3vU;Bwo4Si9l0@gbwyW?YwJE|121#NZsl}8&)}Wkfmw5JcSc6#R`nrpI z+*qO_Z`=LkX+|r82R|@EDcU$DnMd|`d@)cI~`L_H=9r)tYqdCeodb{ zpT9Mk|KzqZ4NMV;L+vnRvr9&eFoJq+opz8BA*Z8Gy|}9^TX-o5oeS6{egdvNDCMMb z`7)V6=;?Q5FXji7;m;xiV@rD51QZyeQPzPq=Zua%@xe~Htw+%NoHxuiL-Vn6*qjm$ zzP&d!kPxO&BV0RBy7+;PzOi4kuV*bLfuN}*)@2yRp8_3epyP|k_`Xv6NSpJdZuzW6p8AW0*0f?o)0PUW! zGKXqy8tHX(v_Olz082=OLhL+OXj^}NQ)x1btC6T+U@~q2XR1+&y#Hv9M^xq(6Hz2u z9G=aOEV`aRHzS+0fje4pdYxa~ z{d=rmS8FuBrdj4f^Qv-M&h&*kTPe+JAmx#<$c0RC) z%UQ>HW>j5PXjrRi;8Ew9UZFsYUgXDt@4*1RDzUjgqPDMbob8n8C<#kp?> zB)a!Qhm*`&$0tzX+09AREODtRzm6$*=XTZLj)u3eCFcWMSX96^;!A0JAGhD&qqH8$ zb&-b^0dqt`Vedya-h{Oxg&)iTEX2}-SXT71+nM+l&YMe#E`F>KbvC{^R?sM0lwg)A2|BkrHQEFZ(V(W0TH?J3- z%d*|tYn)iBO>yzEcxq%i7EQ#cij`0JOA+AqNl{>UIGQV>^y4I363Xl!nYLPwgpbLm0b25 zA{4+PE_5`+@>@GwTPX{cL>gSI&#fMzb?Zw=gLsD*BTUYA14v8iI4@jLkLp8&u-iMJ zM7!S|&JgLFOiU6mG$RCk{2)e?wKK}fB$gR4oA6iKYALxL6*_&XGJJHJMWa}({43>@ zq=dI#(3p7_+$g0^FtHG_5DBuPBb!A}mM}QNSFBLxe%+FDd>i&eEf8Kar_&xggA4oN zbrw!1q8%=ed*Wl$A1l>gtd`3jM4>Ws`M#3tT-z<^I>S@k(+7!BzgF2fNWj=Xw|f8l z8))W}j6Ek>C0C|d)o+QLT!;Of2?+rIg0oe66?hs#oPO#{Xfj?0LZs%|lI~T&Z%52w z({3fS`S+KAr``_@wHOUWgAE9o8VFB1=NjX;J|ao?{qOsLnSp<&_uv0ghIB&o8^W!Z zYSo4F`20z(=0G@PDX(S~1e9^?J4$H3TXJ9gDN* z1tONLUU@J$3@lwg_*Nq<^fycbm3&|HQ_3YB+`AZ`*_k&#=}zsamHj@&J6?oOcB`2U z5~rCxE`UlYdR}>K{E%2`l4!iI#wD3Nb^N!1V*m_z3a&H@xnWcsu@zuTHIFHyH^E0{ zMM?5}%cFrjY9x>KgNG18Oj|!RbzPQz8|b01iRK7r%5#9zp@tfueW;*8|zG*x*} zmGx)a{LIpwaY~ptgk;#Wu(K;V9Nk~Qi}BNJh%#L1ADrYA=g{A2&H5nfh@~LE9s5?X z#VX=|HH-2KKq1x8N8j&|E4yUPMxde-dRltqrq~W(l_%C3MyV$%nI@Yj07+>J2-0CS zk2x#z&tg2tr_G-iHP+aXL|OE!oXB(u(I+O1KUP1Yo9HSCKdx|De&J3{w|FmCQb;2ajMEEDM<+eF8BdLa55>Pyu@$jYg$XJj#zXh10?TuM+h{N-J{Ct1 zw+P4*n7e(CE;jzm->?Aj0s{e!|J)$q27TtN3jF_1UJt8hdyRZCG&2EWkSi5GFG5dOWxpO7VrBpDX6P3B&Gs zji8t&bHYtDhnpeJN||EfJR9MtgJWb zvZo@szr-#?vRQ#$0IgC%t2zf25E4!L3nl5P=^;3_Hc@%MHTZEIoS~S^KG3FB_*+0H z%By(PZe8Pmst2B1gzP~Z+nd>v5u<5(uTz$0P+F6;>wyLBv)g>yA+?subtjSIN6YvB zULgPV67_JEL}Nq&Li+jsZ?{ohBWDv!T@NdJQwI+hphjS3tyqj77{mX?{5<~y2J`>G zfEtbBbi1qmSK%)9OhAoFp&I<)KjF;()9Ud5vAT(ay{og8F;HWYnkmhH<-_zpA>jTK z;q2w;>HySOw5+CC^1lJt8hM#H|LoWaK|Xu)f0L7)gQ=UX*$*O#U&U(U2f_G1Gs?rFUZh literal 0 HcmV?d00001 -- 2.16.6