Skip to content

Commit 8202b1f

Browse files
committed
Implement recv_raw in Pcap readers
- Allow the recv() function to accept an argument from a previously received recv_raw() - Remove duplicated code from pcapdnet.py
1 parent 626c6c4 commit 8202b1f

File tree

5 files changed

+136
-120
lines changed

5 files changed

+136
-120
lines changed

scapy/arch/linux.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -467,8 +467,8 @@ def recv_raw(self, x=MTU):
467467
ts = get_last_packet_timestamp(self.ins)
468468
return cls, pkt, ts
469469

470-
def recv(self, x=MTU):
471-
pkt = SuperSocket.recv(self, x)
470+
def recv(self, **kwargs):
471+
pkt = SuperSocket.recv(self, **kwargs)
472472
if pkt and self.lvl == 2:
473473
pkt = pkt.payload
474474
return pkt
@@ -613,14 +613,18 @@ def recv_raw(self, x=MTU):
613613
cls.name)
614614

615615
ts = get_last_packet_timestamp(self.ins)
616-
# direction = sa_ll[2]
617-
return cls, pkt, ts # , direction
618-
619-
def recv(self, x=MTU):
620-
# cls, pkt, ts, direction = self.recv_raw()
621-
# [Dissection stuff]
622-
pkt = SuperSocket.recv(self, x)
623-
# pkt.direction = direction
616+
direction = sa_ll[2]
617+
return cls, pkt, ts, direction
618+
619+
def recv(self, x=MTU, raw_data=None):
620+
if raw_data is None:
621+
raw_data = self.recv_raw(x)
622+
if raw_data is None:
623+
return None
624+
cls, pkt, ts, direction = raw_data
625+
pkt = SuperSocket.recv(self, x=x, raw_data=(cls, pkt, ts))
626+
if pkt is not None:
627+
pkt.direction = direction
624628
return pkt
625629

626630
def send(self, x):

scapy/arch/pcapdnet.py

Lines changed: 61 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,57 @@ class PcapTimeoutElapsed(Scapy_Exception):
4242

4343

4444
class _L2pcapdnetSocket(SuperSocket, SelectableObject):
45-
def check_recv(self):
46-
return True
47-
48-
def recv_raw(self, x=MTU):
49-
"""Receives a packet, then returns a tuple containing (cls, pkt_data, time)""" # noqa: E501
45+
def __init__(self, promisc=None, iface=None):
46+
self.type = type
47+
self.outs = None
48+
if iface is None:
49+
iface = conf.iface
50+
self.iface = iface
51+
if promisc is None:
52+
promisc = conf.sniff_promisc
53+
self.promisc = promisc
54+
55+
def _process_loaded_streams(self, type, filter, nofilter):
56+
"""This is an internal function, used while initializing pcap
57+
sockets"""
58+
# Guess cls
5059
ll = self.ins.datalink()
5160
if ll in conf.l2types:
52-
cls = conf.l2types[ll]
61+
self.cls = conf.l2types[ll]
5362
else:
54-
cls = conf.default_l2
63+
self.cls = conf.default_l2
5564
warning("Unable to guess datalink type (interface=%s linktype=%i). Using %s", # noqa: E501
5665
self.iface, ll, cls.name)
66+
# Enable immediate mode: reads return immediately upon packet reception
67+
try:
68+
ioctl(self.ins.fileno(), BIOCIMMEDIATE, struct.pack("I", 1))
69+
except:
70+
pass
71+
# Apply init filter & rules
72+
if nofilter:
73+
if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap # noqa: E501
74+
filter = "ether proto %i" % type
75+
else:
76+
filter = None
77+
else:
78+
if conf.except_filter:
79+
if filter:
80+
filter = "(%s) and not (%s)" % (filter, conf.except_filter) # noqa: E501
81+
else:
82+
filter = "not (%s)" % conf.except_filter
83+
if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap # noqa: E501
84+
if filter:
85+
filter = "(ether proto %i) and (%s)" % (type, filter)
86+
else:
87+
filter = "ether proto %i" % type
88+
if filter:
89+
self.ins.setfilter(filter)
90+
91+
def check_recv(self):
92+
return True
5793

94+
def recv_raw(self, x=MTU):
95+
"""Receives a packet, then returns a tuple containing (cls, pkt_data, time)""" # noqa: E501
5896
pkt = None
5997
while pkt is None:
6098
pkt = self.ins.next()
@@ -64,7 +102,7 @@ def recv_raw(self, x=MTU):
64102
raise PcapTimeoutElapsed # To understand this behavior, have a look at L2pcapListenSocket's note # noqa: E501
65103
if pkt is None:
66104
return None, None, None
67-
return cls, pkt, ts
105+
return self.cls, pkt, ts
68106

69107
def nonblock_recv(self):
70108
"""Receives and dissect a packet in non-blocking mode.
@@ -397,37 +435,22 @@ def __del__(self):
397435
class L2pcapListenSocket(_L2pcapdnetSocket):
398436
desc = "read packets at layer 2 using libpcap"
399437

400-
def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, monitor=None): # noqa: E501
401-
self.type = type
402-
self.outs = None
403-
self.iface = iface
404-
if iface is None:
405-
iface = conf.iface
406-
if promisc is None:
407-
promisc = conf.sniff_promisc
408-
self.promisc = promisc
438+
def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, nofilter=0, # noqa: E501
439+
monitor=None): # noqa: E501
440+
_L2pcapdnetSocket.__init__(self, promisc=promisc, iface=iface)
409441
# Note: Timeout with Winpcap/Npcap
410442
# The 4th argument of open_pcap corresponds to timeout. In an ideal world, we would # noqa: E501
411443
# set it to 0 ==> blocking pcap_next_ex.
412444
# However, the way it is handled is very poor, and result in a jerky packet stream. # noqa: E501
413445
# To fix this, we set 100 and the implementation under windows is slightly different, as # noqa: E501
414446
# everything is always received as non-blocking
415-
self.ins = open_pcap(iface, MTU, self.promisc, 100, monitor=monitor) # noqa: E501
416-
try:
417-
ioctl(self.ins.fileno(), BIOCIMMEDIATE, struct.pack("I", 1))
418-
except:
419-
pass
420-
if type == ETH_P_ALL: # Do not apply any filter if Ethernet type is given # noqa: E501
421-
if conf.except_filter:
422-
if filter:
423-
filter = "(%s) and not (%s)" % (filter, conf.except_filter) # noqa: E501
424-
else:
425-
filter = "not (%s)" % conf.except_filter
426-
if filter:
427-
self.ins.setfilter(filter)
447+
self.ins = open_pcap(self.iface, MTU, self.promisc, 100, monitor=monitor) # noqa: E501
448+
# Post init routine
449+
self._process_loaded_streams(type, filter, nofilter)
428450

429451
def close(self):
430452
self.ins.close()
453+
self.closed = True
431454

432455
def send(self, x):
433456
raise Scapy_Exception("Can't send anything with L2pcapListenSocket") # noqa: E501
@@ -439,40 +462,16 @@ class L2pcapSocket(_L2pcapdnetSocket):
439462

440463
def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, nofilter=0, # noqa: E501
441464
monitor=None):
442-
if iface is None:
443-
iface = conf.iface
444-
self.iface = iface
445-
if promisc is None:
446-
promisc = 0
447-
self.promisc = promisc
465+
_L2pcapdnetSocket.__init__(self, promisc=promisc, iface=iface)
448466
# See L2pcapListenSocket for infos about this line
449-
self.ins = open_pcap(iface, MTU, self.promisc, 100, monitor=monitor) # noqa: E501
467+
self.ins = open_pcap(self.iface, MTU, self.promisc, 100, monitor=monitor) # noqa: E501
450468
# We need to have a different interface open because of an
451469
# access violation in Npcap that occurs in multi-threading
452470
# (see https://github.com/nmap/nmap/issues/982)
453-
self.outs = open_pcap(iface, MTU, self.promisc, 100)
454-
try:
455-
ioctl(self.ins.fileno(), BIOCIMMEDIATE, struct.pack("I", 1))
456-
except:
457-
pass
458-
if nofilter:
459-
if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap # noqa: E501
460-
filter = "ether proto %i" % type
461-
else:
462-
filter = None
463-
else:
464-
if conf.except_filter:
465-
if filter:
466-
filter = "(%s) and not (%s)" % (filter, conf.except_filter) # noqa: E501
467-
else:
468-
filter = "not (%s)" % conf.except_filter
469-
if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap # noqa: E501
470-
if filter:
471-
filter = "(ether proto %i) and (%s)" % (type, filter)
472-
else:
473-
filter = "ether proto %i" % type
474-
if filter:
475-
self.ins.setfilter(filter)
471+
self.outs = open_pcap(self.iface, MTU, self.promisc, 100, monitor=monitor)
472+
# Post init routine
473+
self._process_loaded_streams(type, filter, nofilter)
474+
476475

477476
def send(self, x):
478477
sx = raw(x)
@@ -493,8 +492,8 @@ class L3pcapSocket(L2pcapSocket):
493492
# def __init__(self, iface = None, type = ETH_P_ALL, filter=None, nofilter=0): # noqa: E501
494493
# L2pcapSocket.__init__(self, iface, type, filter, nofilter)
495494

496-
def recv(self, x=MTU):
497-
r = L2pcapSocket.recv(self, x)
495+
def recv(self, **kwargs):
496+
r = L2pcapSocket.recv(self, **kwargs)
498497
if r:
499498
return r.payload
500499
else:

scapy/sendrecv.py

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def _sndrcv_rcv(pks, hsent, stopevent, nbrecv, notans, verbose, chainCC,
103103
def _get_pkt():
104104
from scapy.arch.pcapdnet import PcapTimeoutElapsed
105105
try:
106-
return pks.recv(MTU)
106+
return pks.recv(x=MTU)
107107
except PcapTimeoutElapsed:
108108
return None
109109
elif conf.use_bpf:
@@ -927,7 +927,7 @@ class _InteruptSniff(Scapy_Exception):
927927
"""Used to interupt sniffing in multi-threading mode"""
928928
pass
929929

930-
def _process_pkt(p, sniff_sockets, c):
930+
def _process_pkt(p, sniff_sockets, c, dissector=None):
931931
"""Function to process a recieved packet"""
932932
if p is None:
933933
try:
@@ -961,19 +961,14 @@ def _async_process(q, stopevent):
961961
try:
962962
while not stopevent.is_set():
963963
try:
964-
pkt_a, sniff_sockets = q.get_nowait()
964+
pkt_a, dissector, sniff_sockets = q.get_nowait()
965965
except queue.Empty:
966966
continue
967-
cls, val, ts = pkt_a
968967
try:
969-
pkt = cls(val)
968+
pkt = dissector(raw_data=pkt_a)
970969
except Exception:
971-
if conf.debug_dissector:
972-
log_runtime.error(exc_info=True)
973-
stopevent.set()
974-
break
975-
pkt = conf.raw_layer(val)
976-
pkt.time = ts
970+
log_runtime.error(exc_info=True)
971+
stopevent.set()
977972
try:
978973
c = _process_pkt(pkt, sniff_sockets, c)
979974
except _InteruptSniff:
@@ -989,8 +984,8 @@ def _async_process(q, stopevent):
989984
q = queue.Queue()
990985
se = threading.Event()
991986

992-
def _append_pkt(p, sniff_sockets, c=None, q=q):
993-
q.put((p, sniff_sockets))
987+
def _append_pkt(p, sniff_sockets, c=None, q=q, dissector=None):
988+
q.put((p, dissector, sniff_sockets))
994989
if se.is_set():
995990
raise _InteruptSniff
996991
_action_pkt = _append_pkt
@@ -1017,7 +1012,7 @@ def _append_pkt(p, sniff_sockets, c=None, q=q):
10171012
p = _read_next(s)
10181013
except read_allowed_exceptions:
10191014
continue
1020-
c = _action_pkt(p, sniff_sockets, c)
1015+
c = _action_pkt(p, sniff_sockets, c, dissector=s.recv)
10211016
except KeyboardInterrupt:
10221017
pass
10231018
except _InteruptSniff:

scapy/supersocket.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,15 @@ def send(self, x):
4747
x.sent_time = time.time()
4848
return self.outs.send(sx)
4949

50-
def recv(self, x=MTU):
51-
cls, val, ts = self.recv_raw(x)
50+
def recv(self, x=MTU, raw_data=None):
51+
"""Receive a packet, and process it.
52+
53+
params:
54+
- x: maximum packet size (default: MTU)
55+
- raw_data: data received from .recv_raw() (default: None)
56+
if None, will be automatically fetched.
57+
"""
58+
cls, val, ts = raw_data if raw_data is not None else self.recv_raw(x)
5259
if not val or not cls:
5360
return
5461
try:

0 commit comments

Comments
 (0)