diff --git a/.travis.yml b/.travis.yml index 32e58f6dfc8..e139e665659 100644 --- a/.travis.yml +++ b/.travis.yml @@ -134,6 +134,8 @@ matrix: env: - TOXENV=linux_warnings -install: bash .travis/install.sh +install: + - bash .travis/install.sh + - python -c "from scapy.all import conf; print(repr(conf))" script: bash .travis/test.sh diff --git a/doc/scapy/graphics/animations/animation-scapy-asyncsniffer.svg b/doc/scapy/graphics/animations/animation-scapy-asyncsniffer.svg new file mode 100644 index 00000000000..a9418622584 --- /dev/null +++ b/doc/scapy/graphics/animations/animation-scapy-asyncsniffer.svg @@ -0,0 +1,44 @@ + + + + + + + + + + WARNING: No route found for IPv6 destination :: (no default route?) aSPY//YASa ayp ayyyyyyySCP//Pp syY//C | Version 2.4.3rc1.dev162 >>> >>> >>> t >>> t >>> t = >>> t = >>> t = AsyncSniffer() >>> t. >>> t.s >>> t.st >>> t.sta >>> t.star >>> t.start >>> t.start( >>> t.start() >>> p >>> pr >>> pri >>> prin >>> print >>> print( >>> print(" >>> print("h >>> print("he >>> print("hey >>> print("hey" >>> print("hey") hey >>> t.sto >>> t.stop >>> t.stop( >>> t.stop() INFO: Can't import matplotlib. Won't be able to plot.WARNING: No route found for IPv6 destination :: (no default route?) aSPY//YASa apyyyyCY//////////YCa | sY//////YSpcs scpCY//Pp | Welcome to Scapy ayp ayyyyyyySCP//Pp syY//C | Version 2.4.3rc1.dev162 AYAsAYYYYYYYY///Ps cY//S | pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy SPPPP///a pP///AC//Y | A//A cyP////C | Have fun! p///Ac sC///a | P////YCpc A//A | Craft me if you can. scccccp///pSP///p p//Y | -- IPv6 layer sY/////////y caa S//P | cayCyayP//Ya pY/Ya sY/PsY////YCc aC//Yp sc sccaCY//PCypaapyCP//YSs spCPY//////YPSps ccaacs using IPython 7.2.0>>> t = AsyncSniffer() >>> t.start() >>> print("hey") hey >>> t.stop() <Sniffed: TCP:199 UDP:268 ICMP:1 Other:73> >>> + \ No newline at end of file diff --git a/doc/scapy/usage.rst b/doc/scapy/usage.rst index 93bcc702dbf..f18ed79c437 100644 --- a/doc/scapy/usage.rst +++ b/doc/scapy/usage.rst @@ -698,6 +698,52 @@ We can sniff and do passive OS fingerprinting:: The number before the OS guess is the accuracy of the guess. +Asynchronous Sniffing +--------------------- + +.. index:: + single: AsyncSniffer() + +.. note:: + Asynchronous sniffing is only available since **Scapy 2.4.3** + +It is possible to sniff asynchronously. This allows to stop the sniffer programmatically, rather than with ctrl^C. +It provides ``start()``, ``stop()`` and ``join()`` utils. + +The basic usage would be: + +.. code-block:: python + + >>> t = AsyncSniffer() + >>> t.start() + >>> print("hey") + hey + [...] + >>> results = t.stop() + +.. image:: graphics/animations/animation-scapy-asyncsniffer.svg + +The ``AsyncSniffer`` class has a few useful keys, such as ``results`` (the packets collected) or ``running``, that can be used. +It accepts the same arguments than ``sniff()`` (in fact, their implementations are merged). For instance: + +.. code-block:: python + + >>> t = AsyncSniffer(iface="enp0s3", count=200) + >>> t.start() + >>> t.join() # this will hold until 200 packets are collected + >>> results = t.results + >>> print(len(results)) + 200 + +Another example: using ``prn`` and ``store=False`` + +.. code-block:: python + + >>> t = AsyncSniffer(prn=lambda x: x.summary(), store=False, filter="tcp") + >>> t.start() + >>> time.sleep(20) + >>> t.stop() + Advanced Sniffing - Sessions ---------------------------- diff --git a/scapy/arch/__init__.py b/scapy/arch/__init__.py index cc828b421f3..abf1e0d17fe 100644 --- a/scapy/arch/__init__.py +++ b/scapy/arch/__init__.py @@ -56,9 +56,9 @@ def get_if_hwaddr(iff): from scapy.arch.linux import * # noqa F403 elif BSD: from scapy.arch.unix import read_routes, read_routes6, in6_getifaddr # noqa: F401, E501 - - if not conf.use_pcap or conf.use_dnet: - from scapy.arch.bpf.core import * # noqa F403 + from scapy.arch.bpf.core import * # noqa F403 + if not (conf.use_pcap or conf.use_dnet): + # Native from scapy.arch.bpf.supersocket import * # noqa F403 conf.use_bpf = True elif SOLARIS: diff --git a/scapy/arch/bpf/supersocket.py b/scapy/arch/bpf/supersocket.py index 76feafaedf1..e674d0f954b 100644 --- a/scapy/arch/bpf/supersocket.py +++ b/scapy/arch/bpf/supersocket.py @@ -35,13 +35,12 @@ class _L2bpfSocket(SuperSocket): """"Generic Scapy BPF Super Socket""" desc = "read/write packets using BPF" - assigned_interface = None - fd_flags = None - ins = None - closed = False + nonblocking_socket = True def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, nofilter=0, monitor=False): + self.fd_flags = None + self.assigned_interface = None # SuperSocket mandatory variables if promisc is None: @@ -208,11 +207,15 @@ def close(self): def send(self, x): """Dummy send method""" - raise Exception("Can't send anything with %s" % self.__name__) + raise Exception( + "Can't send anything with %s" % self.__class__.__name__ + ) - def recv(self, x=BPF_BUFFER_LENGTH): + def recv_raw(self, x=BPF_BUFFER_LENGTH): """Dummy recv method""" - raise Exception("Can't recv anything with %s" % self.__name__) + raise Exception( + "Can't recv anything with %s" % self.__class__.__name__ + ) @staticmethod def select(sockets, remain=None): @@ -273,22 +276,20 @@ def extract_frames(self, bpf_buffer): # Get and store the Scapy object frame_str = bpf_buffer[bh_hdrlen:bh_hdrlen + bh_caplen] - try: - pkt = self.guessed_cls(frame_str) - except Exception: - if conf.debug_dissector: - raise - pkt = conf.raw_layer(frame_str) - self.received_frames.append(pkt) + self.received_frames.append( + (self.guessed_cls, frame_str, None) + ) # Extract the next frame end = self.bpf_align(bh_hdrlen, bh_caplen) if (len_bb - end) >= 20: self.extract_frames(bpf_buffer[end:]) - def recv(self, x=BPF_BUFFER_LENGTH): + def recv_raw(self, x=BPF_BUFFER_LENGTH): """Receive a frame from the network""" + x = min(x, BPF_BUFFER_LENGTH) + if self.buffered_frames(): # Get a frame from the buffer return self.get_frame() @@ -299,7 +300,7 @@ def recv(self, x=BPF_BUFFER_LENGTH): except EnvironmentError as exc: if exc.errno != errno.EAGAIN: warning("BPF recv()", exc_info=True) - return + return None, None, None # Extract all frames from the BPF buffer self.extract_frames(bpf_buffer) @@ -318,7 +319,7 @@ def nonblock_recv(self): if self.buffered_frames(): # Get a frame from the buffer - return self.get_frame() + return L2bpfListenSocket.recv(self) # Set the non blocking flag, read from the socket, and unset the flag self.set_nonblock(True) @@ -329,11 +330,11 @@ def nonblock_recv(self): 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 recv(self, x=BPF_BUFFER_LENGTH): + """Receive on layer 3""" + r = SuperSocket.recv(self, x) + if r: + return r.payload def send(self, pkt): """Send a packet""" @@ -363,7 +364,10 @@ def send(self, pkt): def isBPFSocket(obj): """Return True is obj is a BPF Super Socket""" - return isinstance(obj, L2bpfListenSocket) or isinstance(obj, L2bpfListenSocket) or isinstance(obj, L3bpfSocket) # noqa: E501 + return isinstance( + obj, + (L2bpfListenSocket, L2bpfListenSocket, L3bpfSocket) + ) def bpf_select(fds_list, timeout=None): diff --git a/scapy/arch/common.py b/scapy/arch/common.py index e2af46fcd7a..0adb388dd05 100644 --- a/scapy/arch/common.py +++ b/scapy/arch/common.py @@ -63,10 +63,6 @@ def get_if(iff, cmd): # SOCKET UTILS -class TimeoutElapsed(Scapy_Exception): - pass - - def _select_nonblock(sockets, remain=None): """This function is called during sendrecv() routine to select the available sockets. @@ -74,13 +70,11 @@ def _select_nonblock(sockets, remain=None): # pcap sockets aren't selectable, so we return all of them # and ask the selecting functions to use nonblock_recv instead of recv def _sleep_nonblock_recv(self): - try: - res = self.nonblock_recv() - if res is None: - time.sleep(conf.recv_poll_rate) - return res - except TimeoutElapsed: - return None + res = self.nonblock_recv() + if res is None: + time.sleep(conf.recv_poll_rate) + return res + # we enforce remain=None: don't wait. return sockets, _sleep_nonblock_recv # BPF HANDLERS diff --git a/scapy/arch/pcapdnet.py b/scapy/arch/pcapdnet.py index 6c176b23337..338cbe40b80 100644 --- a/scapy/arch/pcapdnet.py +++ b/scapy/arch/pcapdnet.py @@ -14,7 +14,7 @@ import time from scapy.automaton import SelectableObject -from scapy.arch.common import _select_nonblock, TimeoutElapsed +from scapy.arch.common import _select_nonblock from scapy.compat import raw, plain_str, chb from scapy.config import conf from scapy.consts import WINDOWS @@ -38,7 +38,7 @@ class _L2pcapdnetSocket(SuperSocket, SelectableObject): - read_allowed_exceptions = (TimeoutElapsed,) + nonblocking_socket = True def check_recv(self): return True @@ -58,8 +58,6 @@ def recv_raw(self, x=MTU): pkt = self.ins.next() if pkt is not None: ts, pkt = pkt - if pkt is None and scapy.consts.WINDOWS: - raise TimeoutElapsed # To understand this behavior, have a look at L2pcapListenSocket's note # noqa: E501 if pkt is None: return None, None, None return cls, pkt, ts @@ -344,15 +342,11 @@ def send(self, x): class L3pcapSocket(L2pcapSocket): desc = "read/write packets at layer 3 using only libpcap" - # def __init__(self, iface = None, type = ETH_P_ALL, filter=None, nofilter=0): # noqa: E501 - # 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): # Makes send detects when it should add Loopback(), Dot11... instead of Ether() # noqa: E501 diff --git a/scapy/arch/windows/native.py b/scapy/arch/windows/native.py index 82b800d698d..18a3ea54f15 100644 --- a/scapy/arch/windows/native.py +++ b/scapy/arch/windows/native.py @@ -43,13 +43,14 @@ the tests (windows.uts) for an example. """ +import io import os import socket import subprocess import time from scapy.automaton import SelectableObject -from scapy.arch.common import _select_nonblock, TimeoutElapsed +from scapy.arch.common import _select_nonblock from scapy.arch.windows.structures import GetIcmpStatistics from scapy.compat import raw from scapy.config import conf @@ -62,7 +63,7 @@ class L3WinSocket(SuperSocket, SelectableObject): desc = "a native Layer 3 (IPv4) raw socket under Windows" - read_allowed_exceptions = (TimeoutElapsed,) + nonblocking_socket = True __slots__ = ["promisc", "cls", "ipv6", "proto"] def __init__(self, iface=None, proto=socket.IPPROTO_IP, @@ -114,7 +115,8 @@ def __init__(self, iface=None, proto=socket.IPPROTO_IP, self.ins.setsockopt(socket.IPPROTO_IP, socket.IP_TTL, ttl) self.outs.setsockopt(socket.IPPROTO_IP, socket.IP_TTL, ttl) # Bind on all ports - host = iface.ip if iface else socket.gethostname() + iface = iface or conf.iface + host = iface.ip if iface.ip else socket.gethostname() self.ins.bind((host, 0)) self.ins.setblocking(False) # Get as much data as possible: reduce what is cropped @@ -163,7 +165,7 @@ def nonblock_recv(self, x=MTU): def recv_raw(self, x=MTU): try: data, address = self.ins.recvfrom(x) - except IOError: # BlockingIOError + except io.BlockingIOError: return None, None, None from scapy.layers.inet import IP from scapy.layers.inet6 import IPv6 @@ -190,7 +192,7 @@ def close(self): @staticmethod def select(sockets, remain=None): - return _select_nonblock(sockets, remain=None) + return _select_nonblock(sockets, remain=remain) class L3WinSocket6(L3WinSocket): diff --git a/scapy/automaton.py b/scapy/automaton.py index 34e6faf5828..b030f358d1d 100644 --- a/scapy/automaton.py +++ b/scapy/automaton.py @@ -162,7 +162,7 @@ def process(self): select_inputs.append(i) elif not self.remain and i.check_recv(): self.results.append(i) - else: + elif self.remain: i.wait_return(self._exit_door) if select_inputs: # Use default select function @@ -193,6 +193,8 @@ def select_objects(inputs, remain): class ObjectPipe(SelectableObject): + read_allowed_exceptions = () + def __init__(self): self.rd, self.wr = os.pipe() self.queue = deque() @@ -218,6 +220,11 @@ def recv(self, n=0): def read(self, n=0): return self.recv(n) + def close(self): + os.close(self.rd) + os.close(self.wr) + self.queue.clear() + class Message: def __init__(self, **args): diff --git a/scapy/contrib/cansocket_native.py b/scapy/contrib/cansocket_native.py index c9a93d294de..bbac4f941fb 100644 --- a/scapy/contrib/cansocket_native.py +++ b/scapy/contrib/cansocket_native.py @@ -28,6 +28,7 @@ class CANSocket(SuperSocket): desc = "read/write packets at a given CAN interface using PF_CAN sockets" + nonblocking_socket = True def __init__(self, iface=None, receive_own_messages=False, can_filters=None, remove_padding=True, basecls=CAN): diff --git a/scapy/contrib/cansocket_python_can.py b/scapy/contrib/cansocket_python_can.py index c62fde289f6..d30b2dd3e53 100644 --- a/scapy/contrib/cansocket_python_can.py +++ b/scapy/contrib/cansocket_python_can.py @@ -29,6 +29,7 @@ class CANSocketTimeoutElapsed(Scapy_Exception): class CANSocket(SuperSocket): read_allowed_exceptions = (CANSocketTimeoutElapsed,) + nonblocking_socket = True desc = "read/write packets at a given CAN interface " \ "using a python-can bus object" diff --git a/scapy/contrib/isotp.py b/scapy/contrib/isotp.py index 6a03bd9f551..107873c8f8d 100644 --- a/scapy/contrib/isotp.py +++ b/scapy/contrib/isotp.py @@ -533,6 +533,8 @@ class ISOTPSoftSocket(SuperSocket): * All background threads can be stopped by the garbage collector """ + nonblocking_socket = True + def __init__(self, can_socket=None, sid=0, diff --git a/scapy/contrib/isotp.uts b/scapy/contrib/isotp.uts index 53d2502ce59..156696b999a 100644 --- a/scapy/contrib/isotp.uts +++ b/scapy/contrib/isotp.uts @@ -23,6 +23,7 @@ iface1 = "vcan1" class MockCANSocket(SuperSocket): + nonblocking_socket = True def __init__(self, rcvd_queue=None): self.rcvd_queue = Queue() self.sent_queue = Queue() @@ -737,9 +738,9 @@ with ISOTPSoftSocket(cans, sid=0x641, did=0x241) as s: raise ex thread = threading.Thread(target=sender, name="sender") thread.start() - ready.wait() + ready.wait(15) msg = s.recv() - thread.join() + thread.join(15) if exception is not None: raise exception @@ -781,7 +782,7 @@ ready.wait() with ISOTPSoftSocket(cans, sid=0x641, did=0x241) as s: s.send(msg) -thread.join() +thread.join(15) succ.wait(2) assert(succ.is_set()) diff --git a/scapy/layers/l2.py b/scapy/layers/l2.py index ef884de61b4..b37d452e89b 100644 --- a/scapy/layers/l2.py +++ b/scapy/layers/l2.py @@ -93,7 +93,8 @@ def getmacbyip(ip, chainCC=0): verbose=0, chainCC=chainCC, nofilter=1) - except Exception: + except Exception as ex: + warning("getmacbyip failed on %s" % ex) return None if res is not None: mac = res.payload.hwsrc diff --git a/scapy/layers/usb.py b/scapy/layers/usb.py index 052e6540ff0..736b2fd28b6 100644 --- a/scapy/layers/usb.py +++ b/scapy/layers/usb.py @@ -213,6 +213,11 @@ class USBpcapSocket(SuperSocket): """ Read packets at layer 2 using USBPcapCMD """ + nonblocking_socket = True + + @staticmethod + def select(sockets, remain=None): + return sockets, None def __init__(self, iface=None, *args, **karg): _usbpcap_check() diff --git a/scapy/packet.py b/scapy/packet.py index d839c390491..c2596480cda 100644 --- a/scapy/packet.py +++ b/scapy/packet.py @@ -21,7 +21,7 @@ from scapy.compat import raw, orb, bytes_encode from scapy.base_classes import BasePacket, Gen, SetGen, Packet_metaclass, \ _CanvasDumpExtended -from scapy.volatile import VolatileValue, RandField +from scapy.volatile import RandField, VolatileValue from scapy.utils import import_hexcap, tex_escape, colgen, issubtype, \ pretty_list from scapy.error import Scapy_Exception, log_runtime, warning @@ -353,6 +353,8 @@ def setfieldval(self, attr, val): def __setattr__(self, attr, val): if attr in self.__all_slots__: + if attr == "sent_time": + self.update_sent_time(val) return object.__setattr__(self, attr, val) try: return self.setfieldval(attr, val) @@ -880,7 +882,11 @@ def hide_defaults(self): del self.fields[k] self.payload.hide_defaults() - def clone_with(self, payload=None, **kargs): + def update_sent_time(self, time): + """Use by clone_with to share the sent_time value""" + pass + + def clone_with(self, payload=None, share_time=False, **kargs): pkt = self.__class__() pkt.explicit = 1 pkt.fields = kargs @@ -896,9 +902,16 @@ def clone_with(self, payload=None, **kargs): pkt.wirelen = self.wirelen if payload is not None: pkt.add_payload(payload) + if share_time: + # This binds the subpacket .sent_time to this layer + def _up_time(x, parent=self): + parent.sent_time = x + pkt.update_sent_time = _up_time return pkt def __iter__(self): + """Iterates through all sub-packets generated by this Packet.""" + # We use __iterlen__ as low as possible, to lower processing time def loop(todo, done, self=self): if todo: eltname = todo.pop() @@ -914,15 +927,22 @@ def loop(todo, done, self=self): yield x else: if isinstance(self.payload, NoPayload): - payloads = [None] + payloads = SetGen([None]) else: payloads = self.payload + share_time = False + if self.fields == done and payloads.__iterlen__() == 1: + # In this case, the packets are identical. Let's bind + # their sent_time attribute for sending purpose + share_time = True for payl in payloads: + # Let's make sure subpackets are consistent done2 = done.copy() for k in done2: if isinstance(done2[k], VolatileValue): done2[k] = done2[k]._fix() - pkt = self.clone_with(payload=payl, **done2) + pkt = self.clone_with(payload=payl, share_time=share_time, + **done2) yield pkt if self.explicit or self.raw_packet_cache is not None: @@ -1001,7 +1021,8 @@ 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.""" # noqa: E501 + """DEV: returns a string that has the same value for a request + and its answer.""" return self.payload.hashret() def answers(self, other): diff --git a/scapy/scapypipes.py b/scapy/scapypipes.py index 5c36df23cbc..4b8a3764988 100644 --- a/scapy/scapypipes.py +++ b/scapy/scapypipes.py @@ -83,12 +83,10 @@ def __init__(self, fname, name=None): 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): @@ -98,12 +96,11 @@ def check_recv(self): return True def deliver(self): - p = self.f.recv() - print("deliver %r" % p) - if p is None: - self.is_exhausted = True - else: + try: + p = self.f.recv() self._send(p) + except EOFError: + self.is_exhausted = True class InjectSink(Sink): diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py index 90f416d1d93..c4a66501f8b 100644 --- a/scapy/sendrecv.py +++ b/scapy/sendrecv.py @@ -9,10 +9,9 @@ from __future__ import absolute_import, print_function import itertools -import threading +from threading import Thread, Event import os import re -import socket import subprocess import time import types @@ -20,12 +19,12 @@ from scapy.compat import plain_str from scapy.data import ETH_P_ALL from scapy.config import conf -from scapy.error import Scapy_Exception, warning -from scapy.packet import Packet, Gen +from scapy.error import warning +from scapy.packet import Gen from scapy.utils import get_temp_file, tcpdump, wrpcap, \ ContextManagerSubprocess, PcapReader from scapy.plist import PacketList, SndRcvList -from scapy.error import log_runtime, log_interactive +from scapy.error import log_runtime, log_interactive, Scapy_Exception from scapy.base_classes import SetGen from scapy.modules import six from scapy.modules.six.moves import map @@ -51,104 +50,6 @@ class debug: # Send / Receive # #################### - -def _sndrcv_snd(pks, timeout, inter, verbose, tobesent, hsent, timessent, stopevent): # noqa: E501 - """Function used in the sending thread of sndrcv()""" - try: - i = 0 - rec_time = timessent is not None - if verbose: - print("Begin emission:") - for p in tobesent: - # Populate the dictionary of _sndrcv_rcv - # _sndrcv_rcv won't miss the answer of a packet that has not been sent # noqa: E501 - hsent.setdefault(p.hashret(), []).append(p) - if stopevent.is_set(): - break - # Send packet - pks.send(p) - if rec_time: - timessent[i] = p.sent_time - i += 1 - time.sleep(inter) - if verbose: - print("Finished sending %i packets." % i) - except SystemExit: - pass - except KeyboardInterrupt: - pass - except Exception: - log_runtime.exception("--- Error sending packets") - if timeout is not None: - def _timeout(stopevent): - stopevent.wait(timeout) - stopevent.set() - thread = threading.Thread( - target=_timeout, args=(stopevent,) - ) - thread.setDaemon(True) - thread.start() - - -def _sndrcv_rcv(pks, hsent, stopevent, nbrecv, notans, verbose, chainCC, - multi, _storage_policy=None): - """Function used to receive packets and check their hashret""" - if not _storage_policy: - _storage_policy = lambda x, y: (x, y) - ans = [] - - def _get_pkt(): - # SuperSocket.select() returns, according to each socket type, - # the selected sockets + the function to recv() the packets (or None) - # (when sockets aren't selectable, should be nonblock_recv) - selected, read_func = pks.select([pks]) - read_func = read_func or pks.__class__.recv - if selected: - return read_func(selected[0]) - - try: - while True: - r = _get_pkt() - if stopevent.is_set(): - break - if r is None: - continue - ok = False - h = r.hashret() - if h in hsent: - hlst = hsent[h] - for i, sentpkt in enumerate(hlst): - if r.answers(sentpkt): - ans.append(_storage_policy(sentpkt, r)) - if verbose > 1: - os.write(1, b"*") - ok = True - if not multi: - del hlst[i] - notans -= 1 - else: - if not hasattr(sentpkt, '_answered'): - notans -= 1 - sentpkt._answered = 1 - break - if notans == 0 and not multi: - del r - break - if not ok: - if verbose > 1: - os.write(1, b".") - nbrecv += 1 - if conf.debug_match: - debug.recv.append(r) - del r - except KeyboardInterrupt: - if chainCC: - raise - finally: - stopevent.set() - return (hsent, ans, nbrecv, notans) - - _DOC_SNDRCV_PARAMS = """ pks: SuperSocket instance to send/receive packets pkt: the packet to send @@ -175,98 +76,185 @@ def _get_pkt(): """ -def sndrcv(pks, pkt, timeout=None, inter=0, verbose=None, chainCC=False, - retry=0, multi=False, rcv_pks=None, store_unanswered=True, - process=None, prebuild=False): - """Scapy raw function to send a packet and receive its answer. - WARNING: This is an internal function. Using sr/srp/sr1/srp is - more appropriate in many cases. - """ - if verbose is None: - verbose = conf.verb - use_prn_mode = False - _storage_policy = None - if process is not None: - use_prn_mode = True - _storage_policy = lambda x, y: process(x, y) - debug.recv = PacketList([], "Unanswered") - debug.sent = PacketList([], "Sent") - debug.match = SndRcvList([]) - nbrecv = 0 - ans = [] - listable = (isinstance(pkt, Packet) and pkt.__iterlen__() == 1) or isinstance(pkt, list) # noqa: E501 - # do it here to fix random fields, so that parent and child have the same - if isinstance(pkt, types.GeneratorType) or prebuild: - tobesent = [p for p in pkt] - notans = len(tobesent) - else: - tobesent = SetGen(pkt) if not isinstance(pkt, Gen) else pkt - notans = tobesent.__iterlen__() +class SndRcvHandler(object): + def __init__(self, pks, pkt, + timeout=None, inter=0, verbose=None, + chainCC=False, + retry=0, multi=False, rcv_pks=None, + prebuild=False, _flood=None): + # Instantiate all arguments + if verbose is None: + verbose = conf.verb + if conf.debug_match: + debug.recv = PacketList([], "Received") + debug.sent = PacketList([], "Sent") + debug.match = SndRcvList([], "Matched") + self.nbrecv = 0 + self.ans = [] + self.pks = pks + self.rcv_pks = rcv_pks or pks + self.inter = inter + self.verbose = verbose + self.chainCC = chainCC + self.multi = multi + self.timeout = timeout + # Instantiate packet holders + if _flood: + self.tobesent = pkt + self.notans = _flood[0] + else: + if isinstance(pkt, types.GeneratorType) or prebuild: + self.tobesent = [p for p in pkt] + self.notans = len(self.tobesent) + else: + self.tobesent = ( + SetGen(pkt) if not isinstance(pkt, Gen) else pkt + ) + self.notans = self.tobesent.__iterlen__() - if retry < 0: - autostop = retry = -retry - else: - autostop = 0 + if retry < 0: + autostop = retry = -retry + else: + autostop = 0 - while retry >= 0: if timeout is not None and timeout < 0: - timeout = None - stopevent = threading.Event() + self.timeout = None - hsent = {} - timessent = {} if listable else None + while retry >= 0: + self.hsent = {} - _sndrcv_snd(pks, timeout, inter, verbose, - tobesent, hsent, timessent, stopevent) - - hsent, newans, nbrecv, notans = _sndrcv_rcv( - (rcv_pks or pks), hsent, stopevent, nbrecv, notans, verbose, - chainCC, multi, _storage_policy=_storage_policy, - ) - - ans.extend(newans) + # Send packets in thread. + # https://github.com/secdev/scapy/issues/1791 + snd_thread = Thread( + target=self._sndrcv_snd + ) + snd_thread.setDaemon(True) + snd_thread.start() + # Receive packets + self._sndrcv_rcv() + if _flood: + # Flood: stop send thread + _flood[1]() - # Restore time_sent to original packets - if listable: - i = 0 - for p in (pkt if isinstance(pkt, list) else [pkt]): - p.sent_time = timessent[i] - i += 1 + snd_thread.join() - if store_unanswered: - remain = list(itertools.chain(*six.itervalues(hsent))) if multi: - remain = [p for p in remain if not hasattr(p, '_answered')] + remain = [ + p for p in itertools.chain(*six.itervalues(self.hsent)) + if not hasattr(p, '_answered') + ] + else: + remain = list(itertools.chain(*six.itervalues(self.hsent))) - if autostop and len(remain) > 0 and len(remain) != len(tobesent): + if autostop and len(remain) > 0 and \ + len(remain) != len(self.tobesent): retry = autostop - tobesent = remain - if len(tobesent) == 0: + self.tobesent = remain + if len(self.tobesent) == 0: break - else: - remain = [] - retry -= 1 + retry -= 1 - if conf.debug_match: - debug.sent = PacketList(remain[:], "Sent") - debug.match = SndRcvList(ans[:]) + if conf.debug_match: + debug.sent = PacketList(remain[:], "Sent") + debug.match = SndRcvList(self.ans[:]) - # Clean the ans list to delete the field _answered - if multi: - for snd, _ in ans: - if hasattr(snd, '_answered'): - del snd._answered + # Clean the ans list to delete the field _answered + if multi: + for snd, _ in self.ans: + if hasattr(snd, '_answered'): + del snd._answered - if verbose: - print("\nReceived %i packets, got %i answers, remaining %i packets" % (nbrecv + len(ans), len(ans), notans)) # noqa: E501 + if verbose: + print( + "\nReceived %i packets, got %i answers, " + "remaining %i packets" % ( + self.nbrecv + len(self.ans), len(self.ans), self.notans + ) + ) + + self.ans_result = SndRcvList(self.ans) + self.unans_result = PacketList(remain, "Unanswered") - if store_unanswered and use_prn_mode: - remain = [process(x, None) for x in remain] + def results(self): + return self.ans_result, self.unans_result - ans_result = ans if use_prn_mode else SndRcvList(ans) - unans_result = remain if use_prn_mode else (None if not store_unanswered else PacketList(remain, "Unanswered")) # noqa: E501 - return ans_result, unans_result + def _sndrcv_snd(self): + """Function used in the sending thread of sndrcv()""" + try: + if self.verbose: + print("Begin emission:") + i = 0 + for p in self.tobesent: + # Populate the dictionary of _sndrcv_rcv + # _sndrcv_rcv won't miss the answer of a packet that + # has not been sent + self.hsent.setdefault(p.hashret(), []).append(p) + # Send packet + self.pks.send(p) + time.sleep(self.inter) + i += 1 + if self.verbose: + print("Finished sending %i packets." % i) + except SystemExit: + pass + except Exception: + log_runtime.exception("--- Error sending packets") + + def _process_packet(self, r): + """Internal function used to process each packet.""" + if r is None: + return + ok = False + h = r.hashret() + if h in self.hsent: + hlst = self.hsent[h] + for i, sentpkt in enumerate(hlst): + if r.answers(sentpkt): + self.ans.append((sentpkt, r)) + if self.verbose > 1: + os.write(1, b"*") + ok = True + if not self.multi: + del hlst[i] + self.notans -= 1 + else: + if not hasattr(self.sentpkt, '_answered'): + self.notans -= 1 + self.sentpkt._answered = 1 + break + if self.notans <= 0 and not self.multi: + self.sniffer.stop(join=False) + if not ok: + if self.verbose > 1: + os.write(1, b".") + self.nbrecv += 1 + if conf.debug_match: + debug.recv.append(r) + + def _sndrcv_rcv(self): + """Function used to receive packets and check their hashret""" + self.sniffer = None + try: + self.sniffer = AsyncSniffer() + self.sniffer._run( + prn=self._process_packet, + timeout=self.timeout, + store=False, + opened_socket=self.pks + ) + except KeyboardInterrupt: + if self.chainCC: + raise + + +def sndrcv(*args, **kwargs): + """Scapy raw function to send a packet and receive its answer. + WARNING: This is an internal function. Using sr/srp/sr1/srp is + more appropriate in many cases. + """ + sndrcver = SndRcvHandler(*args, **kwargs) + return sndrcver.results() def __gen_send(s, x, inter=0, loop=0, count=None, verbose=None, realtime=None, return_packets=False, *args, **kargs): # noqa: E501 @@ -588,85 +576,28 @@ def srploop(pkts, *args, **kargs): # SEND/RECV FLOOD METHODS -def sndrcvflood(pks, pkt, inter=0, verbose=None, chainCC=False, - store_unanswered=True, process=None, timeout=None): - if not verbose: - verbose = conf.verb - listable = (isinstance(pkt, Packet) and pkt.__iterlen__() == 1) or isinstance(pkt, list) # noqa: E501 - tobesent = pkt - - use_prn_mode = False - _storage_policy = None - if process is not None: - use_prn_mode = True - _storage_policy = lambda x, y: process(x, y) - - stopevent = threading.Event() - count_packets = six.moves.queue.Queue() - hsent = {} - timessent = {} if listable else None - - def send_in_loop(tobesent, stopevent, count_packets=count_packets): - """Infinite generator that produces the same packet until stopevent is triggered.""" # noqa: E501 +def sndrcvflood(pks, pkt, inter=0, verbose=None, chainCC=False, timeout=None): + """sndrcv equivalent for flooding.""" + stopevent = Event() + + def send_in_loop(tobesent, stopevent): + """Infinite generator that produces the same + packet until stopevent is triggered.""" while True: for p in tobesent: if stopevent.is_set(): return - count_packets.put(0) yield p - infinite_gen = send_in_loop(tobesent, stopevent) - - def _timeout(timeout): - stopevent.wait(timeout) - stopevent.set() - - timeout_thread = threading.Thread( - target=_timeout, - args=(timeout,) + infinite_gen = send_in_loop(pkt, stopevent) + _flood_len = pkt.__iterlen__() if isinstance(pkt, Gen) else len(pkt) + _flood = [_flood_len, stopevent.set] + return sndrcv( + pks, infinite_gen, + inter=inter, verbose=verbose, + chainCC=chainCC, timeout=None, + _flood=_flood ) - timeout_thread.setDaemon(True) - timeout_thread.start() - - # We don't use _sndrcv_snd verbose (it messes the logs up as in a thread that ends after receiving) # noqa: E501 - thread = threading.Thread( - target=_sndrcv_snd, - args=(pks, None, inter, False, - infinite_gen, hsent, timessent, stopevent) - ) - thread.setDaemon(True) - thread.start() - - hsent, ans, nbrecv, notans = _sndrcv_rcv( - pks, hsent, stopevent, 0, len(tobesent), verbose, chainCC, False, - _storage_policy=_storage_policy - ) - thread.join() - - # Restore time_sent to original packets - if listable: - i = 0 - for p in (pkt if isinstance(pkt, list) else [pkt]): - p.sent_time = timessent[i] - i += 1 - - if process is not None: - ans = [(x, process(y)) for (x, y) in ans] # Apply process - - if store_unanswered: - if use_prn_mode: - remain = [process(x, None) for x in itertools.chain(*six.itervalues(hsent))] # noqa: E501 - else: - remain = list(itertools.chain(*six.itervalues(hsent))) - - if verbose: - print("\nReceived %i packets, got %i answers, remaining %i packets. Sent a total of %i packets." % (nbrecv + len(ans), len(ans), notans, count_packets.qsize())) # noqa: E501 - count_packets.empty() - del count_packets - - ans_result = ans if use_prn_mode else SndRcvList(ans) - unans_result = remain if use_prn_mode else (None if not store_unanswered else PacketList(remain, "Unanswered")) # noqa: E501 - return ans_result, unans_result @conf.commands.register @@ -735,12 +666,9 @@ def srp1flood(x, promisc=None, filter=None, iface=None, nofilter=0, *args, **kar # SNIFF METHODS -@conf.commands.register -def sniff(count=0, store=True, offline=None, prn=None, lfilter=None, - L2socket=None, timeout=None, opened_socket=None, - stop_filter=None, iface=None, started_callback=None, - session=None, *arg, **karg): - """Sniff packets and return a list of packets. +class AsyncSniffer(object): + """ + Sniff packets and return a list of packets. Args: count: number of packets to capture. 0 means infinity. @@ -773,7 +701,7 @@ def sniff(count=0, store=True, offline=None, prn=None, lfilter=None, element, a list of elements, or a dict object mapping an element to a label (see examples below). - Examples: + Examples: synchronous >>> sniff(filter="arp") >>> sniff(filter="tcp", ... session=IPSession, # defragment on-the-flow @@ -786,119 +714,231 @@ def sniff(count=0, store=True, offline=None, prn=None, lfilter=None, >>> sniff(iface={"eth0": "Ethernet", "mon0": "Wifi"}, ... prn=lambda pkt: "%s: %s" % (pkt.sniffed_on, ... pkt.summary())) + + Examples: asynchronous + >>> t = AsyncSniffer(iface="enp0s3") + >>> t.start() + >>> time.sleep(1) + >>> print("nice weather today") + >>> t.stop() """ - c = 0 - session = session or DefaultSession - session = session(prn, store) # instantiate session - sniff_sockets = {} # socket: label dict - if opened_socket is not None: - if isinstance(opened_socket, list): - sniff_sockets.update((s, "socket%d" % i) - for i, s in enumerate(opened_socket)) - elif isinstance(opened_socket, dict): - sniff_sockets.update((s, label) - for s, label in six.iteritems(opened_socket)) - else: - sniff_sockets[opened_socket] = "socket0" - if offline is not None: - flt = karg.get('filter') - - from scapy.arch.common import TCPDUMP - if not TCPDUMP and flt is not None: - message = "tcpdump is not available. Cannot use filter!" - raise Scapy_Exception(message) - - if isinstance(offline, list): - sniff_sockets.update((PcapReader( - fname if flt is None else - tcpdump(fname, args=["-w", "-", flt], getfd=True) - ), fname) for fname in offline) - elif isinstance(offline, dict): - sniff_sockets.update((PcapReader( - fname if flt is None else - tcpdump(fname, args=["-w", "-", flt], getfd=True) - ), label) for fname, label in six.iteritems(offline)) - else: - sniff_sockets[PcapReader( - offline if flt is None else - tcpdump(offline, args=["-w", "-", flt], getfd=True) - )] = offline - if not sniff_sockets or iface is not None: - if L2socket is None: - L2socket = conf.L2listen - if isinstance(iface, list): - sniff_sockets.update( - (L2socket(type=ETH_P_ALL, iface=ifname, *arg, **karg), ifname) - for ifname in iface - ) - elif isinstance(iface, dict): - sniff_sockets.update( - (L2socket(type=ETH_P_ALL, iface=ifname, *arg, **karg), iflabel) - for ifname, iflabel in six.iteritems(iface) - ) + def __init__(self, *args, **kwargs): + # Store keyword arguments + self.args = args + self.kwargs = kwargs + self.running = False + self.thread = None + self.results = None + + def _setup_thread(self): + # Prepare sniffing thread + self.thread = Thread( + target=self._run, + args=self.args, + kwargs=self.kwargs + ) + self.thread.setDaemon(True) + + def _run(self, + count=0, store=True, offline=None, + prn=None, lfilter=None, + L2socket=None, timeout=None, opened_socket=None, + stop_filter=None, iface=None, started_callback=None, + session=None, *arg, **karg): + self.running = True + # Start main thread + c = 0 + session = session or DefaultSession + session = session(prn, store) # instantiate session + # sniff_sockets follows: {socket: label} + sniff_sockets = {} + if opened_socket is not None: + if isinstance(opened_socket, list): + sniff_sockets.update( + (s, "socket%d" % i) + for i, s in enumerate(opened_socket) + ) + elif isinstance(opened_socket, dict): + sniff_sockets.update( + (s, label) + for s, label in six.iteritems(opened_socket) + ) + else: + sniff_sockets[opened_socket] = "socket0" + if offline is not None: + flt = karg.get('filter') + from scapy.arch.common import TCPDUMP + if not TCPDUMP and flt is not None: + message = "tcpdump is not available. Cannot use filter!" + raise Scapy_Exception(message) + + if isinstance(offline, list): + sniff_sockets.update((PcapReader( + fname if flt is None else + tcpdump(fname, args=["-w", "-", flt], getfd=True) + ), fname) for fname in offline) + elif isinstance(offline, dict): + sniff_sockets.update((PcapReader( + fname if flt is None else + tcpdump(fname, args=["-w", "-", flt], getfd=True) + ), label) for fname, label in six.iteritems(offline)) + else: + sniff_sockets[PcapReader( + offline if flt is None else + tcpdump(offline, args=["-w", "-", flt], getfd=True) + )] = offline + if not sniff_sockets or iface is not None: + if L2socket is None: + L2socket = conf.L2listen + if isinstance(iface, list): + sniff_sockets.update( + (L2socket(type=ETH_P_ALL, iface=ifname, *arg, **karg), + ifname) + for ifname in iface + ) + elif isinstance(iface, dict): + sniff_sockets.update( + (L2socket(type=ETH_P_ALL, iface=ifname, *arg, **karg), + iflabel) + for ifname, iflabel in six.iteritems(iface) + ) + else: + sniff_sockets[L2socket(type=ETH_P_ALL, iface=iface, + *arg, **karg)] = iface + + # Get select information from the sockets + _main_socket = next(iter(sniff_sockets)) + read_allowed_exceptions = _main_socket.read_allowed_exceptions + select_func = _main_socket.select + _backup_read_func = _main_socket.__class__.recv + nonblocking_socket = _main_socket.nonblocking_socket + # We check that all sockets use the same select(), or raise a warning + if not all(select_func == sock.select for sock in sniff_sockets): + warning("Warning: inconsistent socket types ! " + "The used select function " + "will be the one of the first socket") + + # Fill if empty + if not read_allowed_exceptions: + read_allowed_exceptions = (IOError,) + + if nonblocking_socket: + # select is non blocking + def stop_cb(): + self.continue_sniff = False + self.stop_cb = stop_cb + close_pipe = None else: - sniff_sockets[L2socket(type=ETH_P_ALL, iface=iface, - *arg, **karg)] = iface - if timeout is not None: - stoptime = time.time() + timeout - remain = None - - # Get select information from the sockets - _main_socket = next(iter(sniff_sockets)) - read_allowed_exceptions = _main_socket.read_allowed_exceptions - select_func = _main_socket.select - # We check that all sockets use the same select(), or raise a warning - if not all(select_func == sock.select for sock in sniff_sockets): - warning("Warning: inconsistent socket types ! The used select function" - "will be the one of the first socket") - # Now let's build the select function, used later on - _select = lambda sockets, remain: select_func(sockets, remain)[0] + # select is blocking: Add special control socket + from scapy.automaton import ObjectPipe + close_pipe = ObjectPipe() + sniff_sockets[close_pipe] = "control_socket" - try: - if started_callback: - started_callback() - continue_sniff = True - while sniff_sockets and continue_sniff: + def stop_cb(): + if self.running: + close_pipe.send(None) + self.continue_sniff = False + self.stop_cb = stop_cb + + try: + if started_callback: + started_callback() + self.continue_sniff = True + + # Start timeout if timeout is not None: - remain = stoptime - time.time() - if remain <= 0: - break - for s in _select(sniff_sockets, remain): - try: - p = s.recv() - except socket.error as ex: - warning("Socket %s failed with '%s' and thus" - " will be ignored" % (s, ex)) - del sniff_sockets[s] - continue - except read_allowed_exceptions: - continue - if p is None: + stoptime = time.time() + timeout + remain = None + + while sniff_sockets and self.continue_sniff: + if timeout is not None: + remain = stoptime - time.time() + if remain <= 0: + break + sockets, read_func = select_func(sniff_sockets, remain) + read_func = read_func or _backup_read_func + dead_sockets = [] + for s in sockets: + if s is close_pipe: + break try: - if s.promisc: - continue - except AttributeError: - pass + p = read_func(s) + except EOFError: + # End of stream + dead_sockets.append(s) + continue + except read_allowed_exceptions: + continue + except Exception as ex: + msg = " It was closed." + try: + # Make sure it's closed + s.close() + except Exception as ex: + msg = " close() failed with '%s'" % ex + warning( + "Socket %s failed with '%s'." % (s, ex) + msg + ) + dead_sockets.append(s) + continue + if p is None: + continue + if lfilter and not lfilter(p): + continue + p.sniffed_on = sniff_sockets[s] + c += 1 + # on_packet_received handles the prn/storage + session.on_packet_received(p) + # check + if (stop_filter and stop_filter(p)) or (0 < count <= c): + self.continue_sniff = False + break + # Removed dead sockets + for s in dead_sockets: del sniff_sockets[s] - break - if lfilter and not lfilter(p): - continue - p.sniffed_on = sniff_sockets[s] - c += 1 - # on_packet_received handles the prn/storage - session.on_packet_received(p) - if stop_filter and stop_filter(p): - continue_sniff = False - break - if 0 < count <= c: - continue_sniff = False - break - except KeyboardInterrupt: - pass - if opened_socket is None: - for s in sniff_sockets: - s.close() - return session.toPacketList() + except KeyboardInterrupt: + pass + self.running = False + if opened_socket is None: + for s in sniff_sockets: + s.close() + elif close_pipe: + close_pipe.close() + self.results = session.toPacketList() + + def start(self): + """Starts AsyncSniffer in async mode""" + self._setup_thread() + self.thread.start() + + def stop(self, join=True): + """Stops AsyncSniffer if not in async mode""" + if self.running: + try: + self.stop_cb() + except AttributeError: + raise Scapy_Exception( + "Unsupported (offline or unsupported socket)" + ) + if join: + self.join() + return self.results + else: + raise Scapy_Exception("Not started !") + + def join(self, *args, **kwargs): + if self.thread: + self.thread.join(*args, **kwargs) + + +@conf.commands.register +def sniff(*args, **kwargs): + sniffer = AsyncSniffer() + sniffer._run(*args, **kwargs) + return sniffer.results + + +sniff.__doc__ = AsyncSniffer.__doc__ @conf.commands.register diff --git a/scapy/supersocket.py b/scapy/supersocket.py index d1aac7bf52e..1b7890ee84a 100644 --- a/scapy/supersocket.py +++ b/scapy/supersocket.py @@ -36,6 +36,7 @@ def __repr__(self): class SuperSocket(six.with_metaclass(_SuperSocket_metaclass)): desc = None closed = 0 + nonblocking_socket = False read_allowed_exceptions = () def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0): # noqa: E501 @@ -67,7 +68,8 @@ def recv(self, x=MTU): debug.crashed_on = (cls, val) raise pkt = conf.raw_layer(val) - pkt.time = ts + if ts: + pkt.time = ts return pkt def fileno(self): @@ -196,6 +198,7 @@ def __init__(self, sock): class StreamSocket(SimpleSocket): desc = "transforms a stream socket into a layer 2" + nonblocking_socket = True def __init__(self, sock, basecls=None): if basecls is None: @@ -207,7 +210,7 @@ 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")) + return None pkt = self.basecls(pkt) pad = pkt.getlayer(conf.padding_layer) if pad is not None and pad.underlayer is not None: diff --git a/scapy/utils.py b/scapy/utils.py index 8b34fbf2700..a015c14eb92 100644 --- a/scapy/utils.py +++ b/scapy/utils.py @@ -921,6 +921,7 @@ class RawPcapReader(six.with_metaclass(PcapReader_metaclass)): """A stateful pcap reader. Each packet is returned as a string""" read_allowed_exceptions = () # emulate SuperSocket + nonblocking_socket = True PacketMetadata = collections.namedtuple("PacketMetadata", ["sec", "usec", "wirelen", "caplen"]) # noqa: E501 @@ -970,11 +971,11 @@ def read_packet(self, size=MTU): """return a single packet read from the file as a tuple containing (pkt_data, pkt_metadata) - returns None when no more packets are available + raise EOFError when no more packets are available """ hdr = self.f.read(16) if len(hdr) < 16: - return None + raise EOFError sec, usec, caplen, wirelen = struct.unpack(self.endian + "IIII", hdr) return (self.f.read(caplen)[:size], RawPcapReader.PacketMetadata(sec=sec, usec=usec, @@ -996,8 +997,9 @@ def read_all(self, count=-1): res = [] while count != 0: count -= 1 - p = self.read_packet() - if p is None: + try: + p = self.read_packet() + except EOFError: break res.append(p) return res @@ -1037,7 +1039,7 @@ def __init__(self, filename, fdesc, magic): def read_packet(self, size=MTU): rp = super(PcapReader, self).read_packet(size=size) if rp is None: - return None + raise EOFError s, pkt_info = rp try: @@ -1114,7 +1116,7 @@ def read_packet(self, size=MTU): blocktype, blocklen = struct.unpack(self.endian + "2I", self.f.read(8)) except struct.error: - return None + raise EOFError block = self.f.read(blocklen - 12) if blocklen % 4: pad = self.f.read(4 - (blocklen % 4)) @@ -1125,7 +1127,7 @@ def read_packet(self, size=MTU): self.f.read(4)): warning("PcapNg: Invalid pcapng block (bad blocklen)") except struct.error: - return None + raise EOFError res = self.blocktypes.get(blocktype, lambda block, size: None)(block, size) if res is not None: @@ -1205,7 +1207,7 @@ def __init__(self, filename, fdesc, magic): def read_packet(self, size=MTU): rp = super(PcapNgReader, self).read_packet(size=size) if rp is None: - return None + raise EOFError s, (linktype, tsresol, tshigh, tslow, wirelen) = rp try: p = conf.l2types[linktype](s) diff --git a/test/benchmark/latency_router.py b/test/benchmark/latency_router.py index 84db9ca2579..5a3e4d8271e 100644 --- a/test/benchmark/latency_router.py +++ b/test/benchmark/latency_router.py @@ -23,7 +23,7 @@ if send_icmp: pkts.append(b) -ans, unans = sr(pkts, filter="host {0}".format(dest), inter=0, timeout=1, prebuild=True, store_unanswered=False) +ans, unans = sr(pkts, filter="host {0}".format(dest), inter=0, timeout=1, prebuild=True) print("scapy version: {}".format(conf.version)) diff --git a/test/configs/osx.utsc b/test/configs/osx.utsc index 80e8b4c1fbe..cce2e6f33c5 100644 --- a/test/configs/osx.utsc +++ b/test/configs/osx.utsc @@ -21,6 +21,7 @@ "linux", "windows", "crypto_advanced", - "ipv6" + "ipv6", + "vcan_socket" ] } diff --git a/test/configs/windows.utsc b/test/configs/windows.utsc index 999743b9d26..956bccd637e 100644 --- a/test/configs/windows.utsc +++ b/test/configs/windows.utsc @@ -26,6 +26,7 @@ "mock_read_routes_bsd", "require_gui", "open_ssl_client", + "vcan_socket", "ipv6" ] } diff --git a/test/configs/windows2.utsc b/test/configs/windows2.utsc index 0480b723808..cea45879eb7 100644 --- a/test/configs/windows2.utsc +++ b/test/configs/windows2.utsc @@ -26,6 +26,7 @@ "mock_read_routes_bsd", "appveyor_only", "open_ssl_client", + "vcan_socket", "ipv6", "manufdb", "tcpdump", diff --git a/test/regression.uts b/test/regression.uts index 37abbd6ca64..e2faca37178 100644 --- a/test/regression.uts +++ b/test/regression.uts @@ -1429,7 +1429,7 @@ assert True ############ ############ -+ More complex tests ++ Generator tests = Implicit logic 1 ~ IP TCP @@ -1447,6 +1447,22 @@ ls(a, verbose=True) l = [p for p in a] len(l) == 7 += Implicit logic 3 + +# In case there's a single option: __iter__ should return self +a = Ether()/IP(src="127.0.0.1", dst="127.0.0.1")/ICMP() +for i in a: + i.sent_time = 1 + +assert a.sent_time == 1 + +# In case they are several, self should never be returned +a = Ether()/IP(src="127.0.0.1", dst="127.0.0.1")/ICMP(seq=(0, 5)) +for i in a: + i.sent_time = 1 + +assert a.sent_time is None + ############ ############ @@ -1494,30 +1510,6 @@ def _test(): retry_test(_test) -= Send & receive with process -~ netaccess IP ICMP -def _test(): - old_debug_dissector = conf.debug_dissector - conf.debug_dissector = False - ans, unans = sr(IP(dst=["www.google.fr", "www.google.com", "www.google.co.uk"])/ICMP(), process=lambda x, y: (x[IP].dst, y[IP].src), timeout=2) - conf.debug_dissector = old_debug_dissector - assert all(x == y for x, y in ans) - assert isinstance(unans, list) - -retry_test(_test) - -= Send & receive with store_unanswered=False -~ netaccess IP ICMP -def _test(): - old_debug_dissector = conf.debug_dissector - conf.debug_dissector = False - ans, unans = sr(IP(dst=["www.google.fr", "www.google.com", "www.google.co.uk"])/ICMP(), store_unanswered=False, timeout=2) - conf.debug_dissector = old_debug_dissector - assert isinstance(ans, PacketList) - assert unans == None - -retry_test(_test) - = Traceroute function ~ netaccess tcpdump * Let's test traceroute