diff --git a/scapy/arch/bpf/supersocket.py b/scapy/arch/bpf/supersocket.py index e3a6b6d003662f94adb6b59269469bcced12031d..3903fa4574ea2e5c64382bc6a39cf8133e100c8a 100644 --- a/scapy/arch/bpf/supersocket.py +++ b/scapy/arch/bpf/supersocket.py @@ -4,25 +4,28 @@ Scapy *BSD native support - BPF sockets """ +import errno +import fcntl +import os +from select import select +import struct +import time + +from scapy.arch.bpf.core import get_dev_bpf, attach_filter +from scapy.arch.bpf.consts import BIOCGBLEN, BIOCGDLT, BIOCGSTATS, \ + BIOCIMMEDIATE, BIOCPROMISC, BIOCSBLEN, BIOCSETIF, BIOCSHDRCMPLT, \ + BPF_BUFFER_LENGTH from scapy.config import conf +from scapy.consts import FREEBSD, NETBSD +from scapy.data import ETH_P_ALL from scapy.error import Scapy_Exception, warning from scapy.supersocket import SuperSocket -from scapy.layers.l2 import Ether -from scapy.layers.inet import IP -from scapy.layers.inet6 import IPv6 -from scapy.packet import Raw -from scapy.data import ETH_P_ALL -from scapy.consts import FREEBSD, OPENBSD, NETBSD -from scapy.arch.bpf.core import get_dev_bpf, attach_filter -from scapy.arch.bpf.consts import * -import struct -import fcntl -import os -import time -import errno -from select import select +if FREEBSD or NETBSD: + BPF_ALIGNMENT = 8 # sizeof(long) +else: + BPF_ALIGNMENT = 4 # sizeof(int32_t) # SuperSockets definitions @@ -56,16 +59,15 @@ class _L2bpfSocket(SuperSocket): # Set the BPF buffer length try: fcntl.ioctl(self.ins, BIOCSBLEN, struct.pack('I', BPF_BUFFER_LENGTH)) - except IOError as err: - msg = "BIOCSBLEN failed on /dev/bpf%i" % self.dev_bpf - raise Scapy_Exception(msg) + except IOError: + raise Scapy_Exception("BIOCSBLEN failed on /dev/bpf%i" % + self.dev_bpf) # Assign the network interface to the BPF handle try: fcntl.ioctl(self.ins, BIOCSETIF, struct.pack("16s16x", self.iface)) - except IOError as err: - msg = "BIOCSETIF failed on %s" % self.iface - raise Scapy_Exception(msg) + except IOError: + raise Scapy_Exception("BIOCSETIF failed on %s" % self.iface) self.assigned_interface = self.iface # Set the interface into promiscuous @@ -75,17 +77,17 @@ class _L2bpfSocket(SuperSocket): # Don't block on read try: fcntl.ioctl(self.ins, BIOCIMMEDIATE, struct.pack('I', 1)) - except IOError as err: - msg = "BIOCIMMEDIATE failed on /dev/bpf%i" % self.dev_bpf - raise Scapy_Exception(msg) + except IOError: + raise Scapy_Exception("BIOCIMMEDIATE failed on /dev/bpf%i" % + self.dev_bpf) # Scapy will provide the link layer source address # Otherwise, it is written by the kernel try: fcntl.ioctl(self.ins, BIOCSHDRCMPLT, struct.pack('i', 1)) - except IOError as err: - msg = "BIOCSHDRCMPLT failed on /dev/bpf%i" % self.dev_bpf - raise Scapy_Exception(msg) + except IOError: + raise Scapy_Exception("BIOCSHDRCMPLT failed on /dev/bpf%i" % + self.dev_bpf) # Configure the BPF filter if not nofilter: @@ -105,13 +107,16 @@ class _L2bpfSocket(SuperSocket): try: fcntl.ioctl(self.ins, BIOCPROMISC, struct.pack('i', value)) - except IOError as err: - msg = "Can't put your interface (%s) into promiscuous mode !" % self.iface - raise Scapy_Exception(msg) + except IOError: + raise Scapy_Exception("Cannot set promiscuous mode on interface " + "(%s)!" % self.iface) def __del__(self): """Close the file descriptor on delete""" - self.close() + # When the socket is deleted on Scapy exits, __del__ is + # sometimes called "too late", and self is None + if self is not None: + self.close() def guess_cls(self): """Guess the packet class that must be used on the interface""" @@ -120,21 +125,18 @@ class _L2bpfSocket(SuperSocket): try: ret = fcntl.ioctl(self.ins, BIOCGDLT, struct.pack('I', 0)) ret = struct.unpack('I', ret)[0] - except IOError as err: - warning("BIOCGDLT failed: unable to guess type. Using Ethernet !") - return Ether - - # *BSD loopback interface - if OPENBSD and ret == 12: # DTL_NULL on OpenBSD - return Loopback + except IOError: + cls = conf.default_l2 + warning("BIOCGDLT failed: unable to guess type. Using %s !", + cls.name) + return cls # Retrieve the corresponding class - cls = conf.l2types.get(ret, None) - if cls is None: - cls = Ether - warning("Unable to guess type. Using Ethernet !") - - return cls + try: + return conf.l2types[ret] + except KeyError: + cls = conf.default_l2 + warning("Unable to guess type (type %i). Using %s", ret, cls.name) def set_nonblock(self, set_flag=True): """Set the non blocking flag on the socket""" @@ -143,8 +145,8 @@ class _L2bpfSocket(SuperSocket): if self.fd_flags is None: try: self.fd_flags = fcntl.fcntl(self.ins, fcntl.F_GETFL) - except IOError as err: - warning("Can't get flags on this file descriptor !") + except IOError: + warning("Cannot get flags on this file descriptor !") return # Set the non blocking flag @@ -165,7 +167,7 @@ class _L2bpfSocket(SuperSocket): try: ret = fcntl.ioctl(self.ins, BIOCGSTATS, struct.pack("2I", 0, 0)) return struct.unpack("2I", ret) - except IOError as err: + except IOError: warning("Unable to get stats from BPF !") return (None, None) @@ -175,7 +177,7 @@ class _L2bpfSocket(SuperSocket): try: ret = fcntl.ioctl(self.ins, BIOCGBLEN, struct.pack("I", 0)) return struct.unpack("I", ret)[0] - except IOError as err: + except IOError: warning("Unable to get the BPF buffer length") return @@ -211,25 +213,15 @@ class L2bpfListenSocket(_L2bpfSocket): def get_frame(self): """Get a frame or packet from the received list""" - if self.received_frames: - pkt = self.received_frames.pop(0) - if isinstance(self, L3bpfSocket): - pkt = pkt.payload - return pkt + return self.received_frames.pop(0) - return None - - def bpf_align(self, bh_h, bh_c): + @staticmethod + def bpf_align(bh_h, bh_c): """Return the index to the end of the current packet""" - if FREEBSD or NETBSD: - BPF_ALIGNMENT = 8 # sizeof(long) - else: - BPF_ALIGNMENT = 4 # sizeof(int32_t) - - x = bh_h + bh_c - return ((x)+(BPF_ALIGNMENT-1)) & ~(BPF_ALIGNMENT-1) # from <net/bpf.h> + # from <net/bpf.h> + return ((bh_h + bh_c)+(BPF_ALIGNMENT-1)) & ~(BPF_ALIGNMENT-1) def extract_frames(self, bpf_buffer): """Extract all frames from the buffer and stored them in the received list.""" @@ -263,7 +255,7 @@ class L2bpfListenSocket(_L2bpfSocket): except: if conf.debug_dissector: raise - pkt = Raw(frame_str) + pkt = conf.raw_layer(frame_str) self.received_frames.append(pkt) # Extract the next frame @@ -278,20 +270,17 @@ class L2bpfListenSocket(_L2bpfSocket): # Get a frame from the buffer return self.get_frame() - else: - # Get data from BPF - try: - bpf_buffer = os.read(self.ins, x) - except EnvironmentError as e: - if e.errno == errno.EAGAIN: - return - else: - warning("BPF recv(): %s" % e) - return + # Get data from BPF + try: + bpf_buffer = os.read(self.ins, x) + except EnvironmentError as exc: + if exc.errno != errno.EAGAIN: + warning("BPF recv()", exc_info=True) + return - # Extract all frames from the BPF buffer - self.extract_frames(bpf_buffer) - return self.get_frame() + # Extract all frames from the BPF buffer + self.extract_frames(bpf_buffer) + return self.get_frame() class L2bpfSocket(L2bpfListenSocket): @@ -308,34 +297,35 @@ class L2bpfSocket(L2bpfListenSocket): # Get a frame from the buffer return self.get_frame() - else: - # Set the non blocking flag, read from the socket, and unset the flag - self.set_nonblock(True) - pkt = L2bpfListenSocket.recv(self) - self.set_nonblock(False) - return pkt + # Set the non blocking flag, read from the socket, and unset the flag + self.set_nonblock(True) + pkt = L2bpfListenSocket.recv(self) + self.set_nonblock(False) + return pkt class L3bpfSocket(L2bpfSocket): + def get_frame(self): + """Get a frame or packet from the received list""" + pkt = super(L3bpfSocket, self).get_frame() + if pkt is not None: + return pkt.payload + def send(self, pkt): """Send a packet""" # Use the routing table to find the output interface - if isinstance(pkt, IPv6): - iff, a, gw = conf.route6.route(pkt.dst) - if isinstance(pkt, IP): - iff, a, gw = conf.route.route(pkt.dst) - else: + iff = pkt.route()[0] + if iff is None: iff = conf.iface # Assign the network interface to the BPF handle if self.assigned_interface != iff: try: fcntl.ioctl(self.outs, BIOCSETIF, struct.pack("16s16x", iff)) - except IOError as err: - msg = "BIOCSETIF failed on %s" % iff - raise Scapy_Exception(msg) + except IOError: + raise Scapy_Exception("BIOCSETIF failed on %s" % iff) self.assigned_interface = iff # Build the frame diff --git a/scapy/error.py b/scapy/error.py index 793e220bb8a8acab44356881c68b7f96fc094696..d7273447d018e12e15534362ebf43fd3ffdfccf8 100644 --- a/scapy/error.py +++ b/scapy/error.py @@ -64,14 +64,13 @@ log_interactive = logging.getLogger("scapy.interactive") # logs in interactive log_loading = logging.getLogger("scapy.loading") # logs when loading Scapy -def warning(x, onlyOnce=None): +def warning(x, *args, **kargs): """ Prints a warning during runtime. onlyOnce - if True, the warning will never be printed again. """ - if onlyOnce: + if kargs.pop("onlyOnce", False): from scapy.config import conf conf.warning_next_only_once = True - log_runtime.warning(x) - + log_runtime.warning(x, *args, **kargs) diff --git a/scapy/layers/all.py b/scapy/layers/all.py index 18e8e2a1508db0f63122f2461d7413e9736e4f9c..20a991d0778577a117fd26b3dd838c4c7cb4b141 100644 --- a/scapy/layers/all.py +++ b/scapy/layers/all.py @@ -10,6 +10,7 @@ All layers. Configurable with conf.load_layers. from __future__ import absolute_import from scapy.config import conf from scapy.error import log_loading +from scapy.main import load_layer import logging, importlib import scapy.modules.six as six ignored = list(six.moves.builtins.__dict__.keys()) + ["sys"] @@ -17,36 +18,11 @@ log = logging.getLogger("scapy.loading") __all__ = [] - -def _validate_local(x): - """Returns whether or not a variable should be imported. - Will return False for any default modules (sys), or if - they are detected as private vars (starting with a _)""" - global ignored - return x[0] != "_" and not x in ignored - -def _import_star(m): - mod = importlib.import_module("." + m, "scapy.layers") - if '__all__' in mod.__dict__: - # only import the exported symbols in __all__ - for name in mod.__dict__['__all__']: - __all__.append(name) - globals()[name] = mod.__dict__[name] - else: - # import all the non-private symbols - for name, sym in six.iteritems(mod.__dict__): - if _validate_local(name): - __all__.append(name) - globals()[name] = sym - -LAYER_ALIASES = { - "tls": "tls.all", -} - for _l in conf.load_layers: log_loading.debug("Loading layer %s" % _l) try: - _import_star(LAYER_ALIASES.get(_l, _l)) + load_layer(_l, globals_dict=globals(), symb_list=__all__) except Exception as e: log.warning("can't import layer %s: %s" % (_l,e)) +del _l diff --git a/scapy/layers/dhcp6.py b/scapy/layers/dhcp6.py index 616048600ec93903164fd14121663e29ece11fdd..e87eba4a2bcedec8a49a5c7c653054997eafbbe6 100644 --- a/scapy/layers/dhcp6.py +++ b/scapy/layers/dhcp6.py @@ -12,16 +12,25 @@ DHCPv6: Dynamic Host Configuration Protocol for IPv6. [RFC 3315] from __future__ import print_function import socket +import struct +import time -from scapy.packet import * -from scapy.fields import * -from scapy.data import * -from scapy.utils6 import * -from scapy.themes import Color -from scapy.layers.inet6 import * from scapy.ansmachine import AnsweringMachine -from scapy.sendrecv import * +from scapy.arch import get_if_raw_hwaddr, in6_getifaddr +from scapy.config import conf +from scapy.data import EPOCH, ETHER_ANY from scapy.error import warning +from scapy.fields import BitField, ByteEnumField, ByteField, FieldLenField, \ + FlagsField, IntEnumField, IntField, MACField, PacketField, \ + PacketListField, ShortEnumField, ShortField, StrField, StrFixedLenField, \ + StrLenField, UTCTimeField, X3BytesField, XIntField, XShortEnumField +from scapy.layers.inet import UDP +from scapy.layers.inet6 import DomainNameListField, IP6Field, IP6ListField, IPv6 +from scapy.packet import Packet, bind_bottom_up +from scapy.pton_ntop import inet_pton +from scapy.sendrecv import send +from scapy.themes import Color +from scapy.utils6 import in6_addrtovendor, in6_islladdr ############################################################################# # Helpers ## @@ -1330,9 +1339,8 @@ DHCPv6_am.parse_options( dns="2001:500::1035", domain="localdomain, local", self.duid = duid else: # Timeval - from time import gmtime, strftime, mktime epoch = (2000, 1, 1, 0, 0, 0, 5, 1, 0) - delta = mktime(epoch) - EPOCH + delta = time.mktime(epoch) - EPOCH timeval = time.time() - delta # Mac Address @@ -1368,7 +1376,6 @@ DHCPv6_am.parse_options( dns="2001:500::1035", domain="localdomain, local", 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 : @@ -1451,7 +1458,7 @@ DHCPv6_am.parse_options( dns="2001:500::1035", domain="localdomain, local", it = it.payload addrs = [bo + x + n for x in addrs] - if debug: + if self.debug: msg = r + "[DEBUG]" + n + " Received " + g + "Decline" + n msg += " from " + bo + src + vendor + " for " msg += ", ".join(addrs)+ n @@ -1515,12 +1522,8 @@ DHCPv6_am.parse_options( dns="2001:500::1035", domain="localdomain, local", 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 diff --git a/scapy/layers/inet.py b/scapy/layers/inet.py index 6c469416003fd4a38ecbb9ab0c3f870c4a9419ea..be474e653b4279436ecdf3b2abb2e15a3e740f11 100644 --- a/scapy/layers/inet.py +++ b/scapy/layers/inet.py @@ -18,7 +18,7 @@ from scapy.base_classes import Gen from scapy.data import * from scapy.layers.l2 import * from scapy.config import conf -from scapy.consts import WINDOWS +from scapy.consts import OPENBSD, WINDOWS from scapy.fields import * from scapy.packet import * from scapy.volatile import * @@ -796,6 +796,7 @@ 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( Loopback, IP, type=0) bind_layers( Loopback, IP, type=2) bind_layers( IPerror, IPerror, frag=0, proto=4) bind_layers( IPerror, ICMPerror, frag=0, proto=1) @@ -808,7 +809,9 @@ 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) +if not OPENBSD: + # see scapy.layers.l2.py + conf.l2types.register_num2layer(12, IP) conf.l2types.register(DLT_IPV4, IP) conf.l3types.register(ETH_P_IP, IP) diff --git a/scapy/layers/l2.py b/scapy/layers/l2.py index ae6dc5c0b8db7b12cc4e507b2c968bf221891fc3..d8f540d77bcb1e49499510a7763c0d4067337131 100644 --- a/scapy/layers/l2.py +++ b/scapy/layers/l2.py @@ -13,6 +13,7 @@ import os, struct, time, socket from scapy.base_classes import Net from scapy.config import conf +from scapy.consts import OPENBSD from scapy.data import * from scapy.packet import * from scapy.ansmachine import * @@ -447,6 +448,9 @@ conf.l2types.register_layer2num(ARPHDR_ETHER, Dot3) conf.l2types.register(144, CookedLinux) # called LINUX_IRDA, similar to CookedLinux conf.l2types.register(113, CookedLinux) conf.l2types.register(DLT_NULL, Loopback) +# Under OpenBSD, for some reason, DLT_NULL == 12 +if OPENBSD: + conf.l2types.register(12, Loopback) conf.l3types.register(ETH_P_ARP, ARP) diff --git a/scapy/main.py b/scapy/main.py index 287bcd7d3857169aa135ffe4e68c935b9807485c..fda7e96847502566b18efe5305f88289dac5e16e 100644 --- a/scapy/main.py +++ b/scapy/main.py @@ -18,7 +18,10 @@ import importlib ignored = list(six.moves.builtins.__dict__.keys()) from scapy.error import * -from scapy.layers.all import LAYER_ALIASES + +LAYER_ALIASES = { + "tls": "tls.all" +} def _probe_config_file(cf): cf_path = os.path.join(os.path.expanduser("~"), cf) @@ -65,28 +68,57 @@ from scapy.themes import DefaultTheme ###################### -def _load(module): +def _load(module, globals_dict=None, symb_list=None): + """Loads a Python module to make variables, objects and functions +available globally. + + The idea is to load the module using importlib, then copy the +symbols to the global symbol table. + + """ + if globals_dict is None: + globals_dict = six.moves.builtins.__dict__ try: mod = importlib.import_module(module) if '__all__' in mod.__dict__: # import listed symbols for name in mod.__dict__['__all__']: - six.moves.builtins.__dict__[name] = mod.__dict__[name] + if symb_list is not None: + symb_list.append(name) + globals_dict[name] = mod.__dict__[name] else: # only import non-private symbols for name, sym in six.iteritems(mod.__dict__): if _validate_local(name): - six.moves.builtins.__dict__[name] = sym - except Exception as e: - log_interactive.error(e) + if symb_list is not None: + symb_list.append(name) + globals_dict[name] = sym + except Exception: + log_interactive.error("Loading module %s", module, exc_info=True) def load_module(name): + """Loads a Scapy module to make variables, objects and functions + available globally. + + """ _load("scapy.modules."+name) -def load_layer(name): - _load("scapy.layers." + LAYER_ALIASES.get(name, name)) +def load_layer(name, globals_dict=None, symb_list=None): + """Loads a Scapy layer module to make variables, objects and functions + available globally. + + """ + _load("scapy.layers." + LAYER_ALIASES.get(name, name), + globals_dict=globals_dict, symb_list=symb_list) def load_contrib(name): + """Loads a Scapy contrib module to make variables, objects and + functions available globally. + + If no contrib module can be found with the given name, try to find + a layer module, since a contrib module may become a layer module. + + """ try: importlib.import_module("scapy.contrib." + name) _load("scapy.contrib." + name) diff --git a/scapy/route.py b/scapy/route.py index c8b8fa4e67d9859e49b84486d056165a20d5ad42..c6a973de5b60688d3e79c46203ef483913fa6b69 100644 --- a/scapy/route.py +++ b/scapy/route.py @@ -8,7 +8,6 @@ Routing and handling of network interfaces. """ from __future__ import absolute_import -import socket from scapy.consts import LOOPBACK_NAME, LOOPBACK_INTERFACE from scapy.utils import atol, ltoa, itom, pretty_routes from scapy.config import conf