Skip to content

Commit 9eaff5a

Browse files
committed
Big Sessions cleanup
1 parent c4ef778 commit 9eaff5a

File tree

18 files changed

+228
-240
lines changed

18 files changed

+228
-240
lines changed

doc/scapy/layers/automotive.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,7 +1158,7 @@ then casted to ``UDS`` objects through the ``basecls`` parameter
11581158
Usage example::
11591159

11601160
with PcapReader("test/contrib/automotive/ecu_trace.pcap") as sock:
1161-
udsmsgs = sniff(session=ISOTPSession, session_kwargs={"use_ext_addr":False, "basecls":UDS}, count=50, opened_socket=sock)
1161+
udsmsgs = sniff(session=ISOTPSession(use_ext_addr=False, basecls=UDS), count=50, opened_socket=sock)
11621162

11631163

11641164
ecu = Ecu()
@@ -1183,7 +1183,7 @@ Usage example::
11831183
session = EcuSession()
11841184

11851185
with PcapReader("test/contrib/automotive/ecu_trace.pcap") as sock:
1186-
udsmsgs = sniff(session=ISOTPSession, session_kwargs={"supersession": session, "use_ext_addr":False, "basecls":UDS}, count=50, opened_socket=sock)
1186+
udsmsgs = sniff(session=ISOTPSession(use_ext_addr=False, basecls=UDS, supersession=session)), count=50, opened_socket=sock)
11871187

11881188
ecu = session.ecu
11891189
print(ecu.log)

doc/scapy/usage.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,7 @@ Those sessions can be used using the ``session=`` parameter of ``sniff()``. Exam
783783

784784
.. note::
785785
To implement your own Session class, in order to support another flow-based protocol, start by copying a sample from `scapy/sessions.py <https://github.com/secdev/scapy/blob/master/scapy/sessions.py>`_
786-
Your custom ``Session`` class only needs to extend the :py:class:`~scapy.sessions.DefaultSession` class, and implement a ``on_packet_received`` function, such as in the example.
786+
Your custom ``Session`` class only needs to extend the :py:class:`~scapy.sessions.DefaultSession` class, and implement a ``process`` or a ``recv`` function, such as in the examples.
787787

788788

789789
How to use TCPSession to defragment TCP packets

scapy/base_classes.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,28 @@ def __new__(cls: Type[_T],
326326

327327
dct["fields_desc"] = final_fld
328328

329+
# Add default dispatch_hook if a minlength_hook is present
330+
if "minlength_hook" in dct:
331+
minlength_hook = dct.pop("minlength_hook")
332+
_dispatch_hook = dct.pop(
333+
"dispatch_hook",
334+
lambda *_, **__: cls
335+
)
336+
from scapy.config import conf
337+
338+
def dispatch_hook(
339+
_pkt: Optional[bytes] = None,
340+
*args: Any,
341+
**kwargs: Any
342+
) -> Type['Packet']:
343+
if _pkt and len(_pkt) < minlength_hook(_pkt):
344+
return conf.padding_layer
345+
return cast(
346+
Type['Packet'],
347+
_dispatch_hook(_pkt, *args, **kwargs),
348+
)
349+
dct["dispatch_hook"] = dispatch_hook
350+
329351
dct.setdefault("__slots__", [])
330352
for attr in ["name", "overload_fields"]:
331353
try:

scapy/contrib/automotive/ecu.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -469,17 +469,16 @@ class EcuSession(DefaultSession):
469469
"""
470470
def __init__(self, *args, **kwargs):
471471
# type: (Any, Any) -> None
472-
DefaultSession.__init__(self, *args, **kwargs)
473472
self.ecu = Ecu(logging=kwargs.pop("logging", True),
474473
verbose=kwargs.pop("verbose", True),
475474
store_supported_responses=kwargs.pop("store_supported_responses", True)) # noqa: E501
475+
super(EcuSession, self).__init__(*args, **kwargs)
476476

477-
def on_packet_received(self, pkt):
478-
# type: (Optional[Packet]) -> None
477+
def process(self, pkt: Packet) -> Optional[Packet]:
479478
if not pkt:
480-
return
479+
return None
481480
self.ecu.update(pkt)
482-
DefaultSession.on_packet_received(self, pkt)
481+
return pkt
483482

484483

485484
class EcuResponse:

scapy/contrib/isotp/isotp_utils.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@
1414
from scapy.utils import EDecimal
1515
from scapy.packet import Packet
1616
from scapy.sessions import DefaultSession
17+
from scapy.supersocket import SuperSocket
1718
from scapy.contrib.isotp.isotp_packet import ISOTP, N_PCI_CF, N_PCI_SF, \
1819
N_PCI_FF, N_PCI_FC
1920

2021
# Typing imports
2122
from typing import (
23+
cast,
2224
Iterable,
25+
Iterator,
2326
Optional,
2427
Union,
2528
List,
@@ -336,20 +339,23 @@ class ISOTPSession(DefaultSession):
336339

337340
def __init__(self, *args, **kwargs):
338341
# type: (Any, Any) -> None
339-
super(ISOTPSession, self).__init__(*args, **kwargs)
340342
self.m = ISOTPMessageBuilder(
341343
use_ext_address=kwargs.pop("use_ext_address", None),
342344
rx_id=kwargs.pop("rx_id", None),
343345
basecls=kwargs.pop("basecls", ISOTP))
346+
super(ISOTPSession, self).__init__(*args, **kwargs)
344347

345-
def on_packet_received(self, pkt):
346-
# type: (Optional[Packet]) -> None
348+
def recv(self, sock: SuperSocket) -> Iterator[Packet]:
349+
"""
350+
Will be called by sniff() to ask for a packet
351+
"""
352+
pkt = sock.recv()
347353
if not pkt:
348354
return
349355
self.m.feed(pkt)
350356
while len(self.m) > 0:
351-
rcvd = self.m.pop()
352-
if self._supersession:
353-
self._supersession.on_packet_received(rcvd)
354-
else:
355-
super(ISOTPSession, self).on_packet_received(rcvd)
357+
rcvd = cast(Optional[Packet], self.m.pop())
358+
if rcvd:
359+
rcvd = self.process(rcvd)
360+
if rcvd:
361+
yield rcvd

scapy/layers/dcerpc.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@
9191
EPacketListField,
9292
)
9393

94+
# Typing imports
95+
from typing import (
96+
Optional,
97+
)
98+
9499

95100
# DCE/RPC Packet
96101
DCE_RPC_TYPE = {
@@ -1895,12 +1900,11 @@ def _process_dcerpc_packet(self, pkt):
18951900
pkt = self._parse_with_opnum(pkt, opnum, opts)
18961901
return pkt
18971902

1898-
def on_packet_received(self, pkt):
1903+
def process(self, pkt: Packet) -> Optional[Packet]:
18991904
if DceRpc5 in pkt:
1900-
return super(DceRpcSession, self).on_packet_received(
1901-
self._process_dcerpc_packet(pkt)
1902-
)
1903-
return super(DceRpcSession, self).on_packet_received(pkt)
1905+
return self._process_dcerpc_packet(pkt)
1906+
else:
1907+
return pkt
19041908

19051909

19061910
# --- TODO cleanup below

scapy/layers/inet.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1897,7 +1897,7 @@ def parse_args(self, ip, port, *args, **kargs):
18971897
self.src = self.l4.src
18981898
self.sack = self.l4[TCP].ack
18991899
self.rel_seq = None
1900-
self.rcvbuf = TCPSession(prn=self._transmit_packet, store=False)
1900+
self.rcvbuf = TCPSession()
19011901
bpf = "host %s and host %s and port %i and port %i" % (self.src,
19021902
self.dst,
19031903
self.sport,
@@ -1982,7 +1982,7 @@ def receive_data(self, pkt):
19821982
# Answer with an Ack
19831983
self.send(self.l4)
19841984
# Process data - will be sent to the SuperSocket through this
1985-
self.rcvbuf.on_packet_received(pkt)
1985+
self._transmit_packet(self.rcvbuf.process(pkt))
19861986

19871987
@ATMT.ioevent(ESTABLISHED, name="tcp", as_supersocket="tcplink")
19881988
def outgoing_data_received(self, fd):

scapy/layers/netflow.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,16 @@
6464
)
6565
from scapy.packet import Packet, bind_layers, bind_bottom_up
6666
from scapy.plist import PacketList
67-
from scapy.sessions import IPSession, DefaultSession
67+
from scapy.sessions import IPSession
6868

6969
from scapy.layers.inet import UDP
7070
from scapy.layers.inet6 import IP6Field
7171

72+
# Typing imports
73+
from typing import (
74+
Optional,
75+
)
76+
7277

7378
class NetflowHeader(Packet):
7479
name = "Netflow Header"
@@ -1596,25 +1601,21 @@ class NetflowSession(IPSession):
15961601
See help(scapy.layers.netflow) for more infos.
15971602
"""
15981603
def __init__(self, *args, **kwargs):
1599-
IPSession.__init__(self, *args, **kwargs)
16001604
self.definitions = {}
16011605
self.definitions_opts = {}
16021606
self.ignored = set()
1607+
super(NetflowSession, self).__init__(*args, **kwargs)
16031608

1604-
def _process_packet(self, pkt):
1609+
def process(self, pkt: Packet) -> Optional[Packet]:
1610+
pkt = super(NetflowSession, self).process(pkt)
1611+
if not pkt:
1612+
return
16051613
_netflowv9_defragment_packet(pkt,
16061614
self.definitions,
16071615
self.definitions_opts,
16081616
self.ignored)
16091617
return pkt
16101618

1611-
def on_packet_received(self, pkt):
1612-
# First, defragment IP if necessary
1613-
pkt = self._ip_process_packet(pkt)
1614-
# Now handle NetflowV9 defragmentation
1615-
pkt = self._process_packet(pkt)
1616-
DefaultSession.on_packet_received(self, pkt)
1617-
16181619

16191620
class NetflowOptionsRecordScopeV9(NetflowRecordV9):
16201621
name = "Netflow Options Template Record V9/10 - Scope"

scapy/layers/tls/handshake.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ def tls_session_update(self, msg_str):
463463
# RFC 8701: GREASE of TLS will send unknown versions
464464
# here. We have to ignore them
465465
if ver in _tls_version:
466-
self.tls_session.advertised_tls_version = ver
466+
s.advertised_tls_version = ver
467467
break
468468
if isinstance(e, TLS_Ext_SignatureAlgorithms):
469469
s.advertised_sig_algs = e.sig_algs

scapy/layers/tls/record.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,12 @@ def dispatch_hook(cls, _pkt=None, *args, **kargs):
328328
return SSLv2
329329
# Not SSLv2: continuation
330330
return _TLSEncryptedContent
331+
if plen >= 5:
332+
# Check minimum length
333+
msglen = struct.unpack('!H', _pkt[3:5])[0] + 5
334+
if plen < msglen:
335+
# This is a fragment
336+
return conf.padding_layer
331337
# Check TLS 1.3
332338
if s and s.tls_version == 0x0304:
333339
_has_cipher = lambda x: (
@@ -567,12 +573,24 @@ def do_dissect_payload(self, s):
567573
as the TLS session to be used would get lost.
568574
"""
569575
if s:
576+
# Check minimum length
577+
if len(s) < 5:
578+
p = conf.raw_layer(s, _internal=1, _underlayer=self)
579+
self.add_payload(p)
580+
return
581+
msglen = struct.unpack('!H', s[3:5])[0] + 5
582+
if len(s) < msglen:
583+
# This is a fragment
584+
self.add_payload(conf.padding_layer(s))
585+
return
570586
try:
571587
p = TLS(s, _internal=1, _underlayer=self,
572588
tls_session=self.tls_session)
573589
except KeyboardInterrupt:
574590
raise
575591
except Exception:
592+
if conf.debug_dissector:
593+
raise
576594
p = conf.raw_layer(s, _internal=1, _underlayer=self)
577595
self.add_payload(p)
578596

0 commit comments

Comments
 (0)