DTLS Handshake-Message Reassembly¶
pcapkit.foundation.reassembly.dtls contains
DTLS only, which reconstructs a
DTLS handshake message split across several records back to origin
(RFC 6347 Section 4.2.3).
- class pcapkit.foundation.reassembly.dtls.DTLS[_PT: Info, _DT: Info, _IT: tuple, _BT: Info](*, strict=True, store=True)[source]¶
Bases:
ReassemblyBase[Packet[_AT],Datagram[_AT],tuple[_AT,int,_AT,int,int],Buffer],Generic[_AT]Reassembly for DTLS handshake messages.
- Parameters:
strict (
bool) – ifTrue, report a message that is not completely reassembled as the tuple of its received runs; otherwise as one contiguous payload, its holes zero-filledstore (
bool) – if store reassembled datagram in memory, i.e.,self._dtgram(if not, datagram will be discarded after callback)
Note
The buffer ID is the UDP 4-tuple plus the DTLS epoch: the record sequence number resets on each cipher-state change (RFC 6347 Section 4.1), so a new epoch opens a fresh buffer. Overlapping fragments are accepted (RFC 6347 Section 4.2.3, RFC 9147 Section 5.5). The
Extractorfeeds it throughpcapkit.toolkit.pcap.dtls_reassembly(), which reads the 4-tuple off the frame, anddtls_handshake()decodes what it reassembles. A complete message’spacketholds the same decode, run on first read. Both call the protocol’sanalyze_handshake(); thepacketcalls that of__protocol_type__(#2622).Example
>>> from pcapkit.foundation.reassembly.dtls import DTLS # Initialise instance: >>> dtls_reassembly = DTLS() # Call reassembly: >>> dtls_reassembly(packet_dict) # Fetch result: >>> result = dtls_reassembly.datagram
- property last: Datagram[_AT] | None¶
The message reassembled by the most recent call to
reassembly(), orNoneif that fragment did not complete one.This is a synchronous alternative to
datagram/fetch()for a caller that wants to know, immediately after feeding one fragment, whether that fragment was the one that completed its message, rather than waiting for the buffer (which may hold several other messages still in flight under the same buffer ID) to be flushed.
- submit(buf, *, bufid, checked=False, timeout=False)[source]¶
Submit reassembled payload.
- Parameters:
buf (
Buffer) – buffer dict of reassembled packetschecked (
bool) – unused; every message’s completeness is decided from its own hole list, so there is nothing for a caller to assert ahead of timetimeout (
bool) – whether this buffer is being submitted becauseexpire()abandoned it under the reassembly timeout
- Return type:
- Returns:
Every message still held in
buf, each reportedCOMPLETEif its hole list is empty, otherwiseTIMEOUTorPARTIAL.
- _datagram_from_fragment(frag, bufid, message_seq, completion)[source]¶
Build a
Datagramfrom a message’s bufferedFragment.- Parameters:
- Return type:
- Returns:
The reassembled (or partially reassembled) message. Its
packetis the decodedtls_handshake()runs, by this reassembly’s ownprotocol, deferred to its first read, if the message is complete, andNoneotherwise, as that function returns.
- static _received_runs(frag)[source]¶
Extract the octet runs actually received, for
strict=True’s report of an incomplete message.
- static _update_hole_descriptors(hdl, first, last)[source]¶
Punch a received byte range out of a message’s hole list.
Offsets are message-relative and inclusive. They never wrap and need no open upper bound, since every fragment of a message declares the same total
length(RFC 6347 Section 4.2.3).- Parameters:
hdl (
list[HoleDescriptor]) – the message’s hole descriptor list, mutated in placefirst (
int) – offset of the first octet the arriving fragment actually heldlast (
int) – offset of the last octet the arriving fragment actually held, inclusive
- __protocol_type__: Type[ProtocolBase] = <class 'pcapkit.protocols.application.dtls.DTLS'>¶
Protocol of current reassembly object. An extension point – a protocol named here need only define
analyze_handshake, with the signature ofDTLS.analyze_handshake, and is read for it when a message completes, so one without it raisesAttributeErrorthere.
Algorithm¶
See also
Holes are inclusive byte offsets into the message. They never wrap, and
the upper bound is known from the start: every fragment carries the
message’s total length (RFC 6347 Section 4.2.3).
A DTLS handshake record may carry more than one message fragment –
RFC 6347 Section 4.2.3 allows several handshake messages (or fragments of
them) to be coalesced into one record, and SSL._read_tls_handshake_messages
reads however many fit rather than assuming exactly one. A fragment names the
message it belongs to (message_seq), the message’s total length
(length), and its own byte range within that message (fragment_offset,
fragment_length). A message is reassembled by punching the arriving
fragment’s range out of the message’s hole list; it is whole once no hole
remains.
Terminology¶
- reasm.dtls.packet¶
Data structure for DTLS handshake-fragment reassembly (
DTLS.reassembly) isPacket– one fragment’s ownbufid,message_seq,fragment_offset,fragment_lengthand rawpayload, built bypcapkit.toolkit.pcap.dtls_reassembly()orpcapkit.toolkit.pcapng.dtls_reassembly()from a singleDTLSHandshake.- reasm.dtls.datagram¶
Data structure for reassembled DTLS handshake message (element of
DTLS.datagram) isDatagram–completed, the original packet identifier, the packet numbers that built it,body, the reassembled payload (a contiguousbytes, or – understrict=Trueand an incomplete message – atupleof the octet runs actually received), andpacket, whatdtls_handshake()decodesbodyto.packetis decoded on first read, as an IP or TCP datagram’s is, and isNonefor an incomplete message.- reasm.dtls.buffer¶
Data structure for internal buffering (
DTLS._buffer) isBuffer– every message under reconstruction for oneBufferID(one UDP association at one DTLS epoch), each keyed by itsmessage_seqand held as aFragment– its hole descriptor list (HoleDescriptor) and its zero-filled payload buffer.
Data Models¶
- class pcapkit.foundation.reassembly.data.dtls.Packet[_AT: (IPv4Address, IPv6Address)](bufid, num, msg_type, length, message_seq, fragment_offset, fragment_length, payload, timestamp)[source]¶
-
Data model for DTLS handshake-fragment representation.
- msg_type: HandshakeType¶
Handshake message type.
- length: int¶
Total message length (RFC 6347 Section 4.2.2’s
length), fragment headers and bodies excluded – the same on every fragment of one message.
- pcapkit.foundation.reassembly.data.dtls.BufferID¶
Buffer ID – the UDP 4-tuple (addresses and ports; the protocol is always UDP, so it is left out), plus the DTLS epoch: the record sequence number resets on every change of cipher state (RFC 6347 Section 4.1), so a fragment from a new epoch belongs to a fresh buffer rather than the one its 4-tuple would otherwise name.
- class pcapkit.foundation.reassembly.data.dtls.DatagramID[_AT: (IPv4Address, IPv6Address)](src, sport, dst, dport, epoch)[source]¶
-
Data model for DTLS original packet identifier.
- class pcapkit.foundation.reassembly.data.dtls.Datagram[_AT: (IPv4Address, IPv6Address)](completed, id, index, message_seq, msg_type, length, body, packet)[source]¶
Bases:
DeferredPacket,Info,Generic[_AT]Data model for DTLS reassembled handshake message.
- completed: Completion¶
How completely the message was reassembled, and why reassembly stopped; see
Completion.
- id: DatagramID[_AT]¶
Original packet identifier.
- msg_type: HandshakeType¶
Handshake message type.
- packet: DTLSHandshake | None¶
Decoded message, as
dtls_handshake()returns it unless the reassembly’s__protocol_type__names another protocol, whoseanalyze_handshakethen decodes it. Decoded on first read, not at construction time, andNonefor an incomplete message. ADeferredmay be passed in its place, and reading this attribute then runs it and keeps the result.
- class pcapkit.foundation.reassembly.data.dtls.HoleDescriptor(first, last)[source]¶
Bases:
InfoData model for DTLS hole descriptor.
Both bounds are inclusive byte offsets into the reassembled message, so a hole covers
last - first + 1octets. They never wrap and need no open-ended sentinel: every fragment declares the message’s totalFragment.length(RFC 6347 Section 4.2.3), so it is known from the first fragment.
- class pcapkit.foundation.reassembly.data.dtls.Fragment(ind, msg_type, length, raw, hdl)[source]¶
Bases:
InfoData model for DTLS message-under- construction, one per
Packet.message_seqbuffered under oneBufferID.- msg_type: HandshakeType¶
Handshake message type – read off the first-arriving fragment and trusted from there, same as
length.
- hdl: list[HoleDescriptor]¶
Hole descriptor list; empty once the message is whole.
- class pcapkit.foundation.reassembly.data.dtls.Buffer(msg, timestamp)[source]¶
Bases:
InfoData model for DTLS buffer entry – every handshake message being reassembled under one
BufferID, i.e. one UDP association at one epoch.- msg: dict[int, Fragment]¶
Messages under reconstruction, by
Packet.message_seq. Several may be in flight at once: aCertificate, aServerKeyExchangeand aServerHelloDonecan each fragment independently in the same flight.
Type Variables¶
- pcapkit.foundation.reassembly.data.dtls._AT: ipaddress.IPv4Address | ipaddress.IPv6Address¶
IP address type.