Skip to content

Commit b7adecb

Browse files
committed
Pickle - reconstructs the packet from field valies, payload and metadata
1 parent f303033 commit b7adecb

File tree

2 files changed

+79
-30
lines changed

2 files changed

+79
-30
lines changed

scapy/packet.py

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,22 @@
7979
_T = TypeVar("_T", Dict[str, Any], Optional[Dict[str, Any]])
8080

8181

82+
def _rebuild_pkt(cls, fields, payload, metadata):
83+
"""Helper for unpickling Packet instances via field values."""
84+
# Create the instance using the field values
85+
pkt = cls(**fields)
86+
if payload is not None:
87+
pkt.add_payload(payload)
88+
# Restore metadata
89+
pkt.time = metadata['time']
90+
pkt.sent_time = metadata['sent_time']
91+
pkt.direction = metadata['direction']
92+
pkt.sniffed_on = metadata['sniffed_on']
93+
pkt.wirelen = metadata['wirelen']
94+
pkt.comments = metadata['comments']
95+
return pkt
96+
97+
8298
class Packet(
8399
BasePacket,
84100
_CanvasDumpExtended,
@@ -214,15 +230,6 @@ def __init__(self,
214230
else:
215231
self.post_transforms = [post_transform]
216232

217-
_PickleType = Tuple[
218-
Union[EDecimal, float],
219-
Optional[Union[EDecimal, float, None]],
220-
Optional[int],
221-
Optional[_GlobInterfaceType],
222-
Optional[int],
223-
Optional[bytes],
224-
]
225-
226233
@property
227234
def comment(self):
228235
# type: () -> Optional[bytes]
@@ -244,27 +251,32 @@ def comment(self, value):
244251
self.comments = None
245252

246253
def __reduce__(self):
247-
# type: () -> Tuple[Type[Packet], Tuple[bytes], Packet._PickleType]
248-
"""Used by pickling methods"""
249-
return (self.__class__, (self.build(),), (
250-
self.time,
251-
self.sent_time,
252-
self.direction,
253-
self.sniffed_on,
254-
self.wirelen,
255-
self.comment
256-
))
257-
258-
def __setstate__(self, state):
259-
# type: (Packet._PickleType) -> Packet
260-
"""Rebuild state using pickable methods"""
261-
self.time = state[0]
262-
self.sent_time = state[1]
263-
self.direction = state[2]
264-
self.sniffed_on = state[3]
265-
self.wirelen = state[4]
266-
self.comment = state[5]
267-
return self
254+
# type: () -> Tuple[Any, ...]
255+
"""Used by pickling methods.
256+
257+
Reconstructs the packet from field values, payload, and metadata.
258+
"""
259+
# Store field values for unpickling
260+
fields = {}
261+
for f in self.fields_desc:
262+
if f.name in self.fields:
263+
fields[f.name] = self.fields[f.name]
264+
payload = self.payload
265+
if isinstance(payload, NoPayload):
266+
payload = None
267+
# Store metadata for unpickling
268+
metadata = {
269+
'time': self.time,
270+
'sent_time': self.sent_time,
271+
'direction': self.direction,
272+
'sniffed_on': self.sniffed_on,
273+
'wirelen': self.wirelen,
274+
'comments': self.comments,
275+
}
276+
return (
277+
_rebuild_pkt,
278+
(self.__class__, fields, payload, metadata),
279+
)
268280

269281
def __deepcopy__(self,
270282
memo, # type: Any

test/regression.uts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,43 @@ c = pickle.loads(b)
616616
assert c[IP].dst == "192.168.0.1"
617617
assert raw(c) == raw(a)
618618

619+
= Pickle preserves field values, payload and metadata
620+
621+
import pickle
622+
623+
p = IP(src='1.2.3.4', dst='5.6.7.8')/TCP(sport=1234, dport=80, flags='S')
624+
p.time = 12345.0
625+
p.sent_time = 12346.0
626+
p.direction = 1
627+
p.sniffed_on = 'eth0'
628+
p.wirelen = 100
629+
p.comment = b'test comment'
630+
p2 = pickle.loads(pickle.dumps(p))
631+
assert p2[IP].src == '1.2.3.4'
632+
assert p2[IP].dst == '5.6.7.8'
633+
assert p2[TCP].sport == 1234
634+
assert p2[TCP].dport == 80
635+
assert p2[TCP].flags == 'S'
636+
assert p2[IP].len is None
637+
assert p2[IP].chksum is None
638+
assert p2[TCP].chksum is None
639+
assert p2.time == 12345.0
640+
assert p2.sent_time == 12346.0
641+
assert p2.direction == 1
642+
assert p2.sniffed_on == 'eth0'
643+
assert p2.wirelen == 100
644+
assert p2.comment == b'test comment'
645+
assert raw(p2) == raw(p)
646+
647+
= Pickle a bare packet without payload
648+
649+
import pickle
650+
651+
p = IP(src='10.0.0.1')
652+
p2 = pickle.loads(pickle.dumps(p))
653+
assert p2.src == '10.0.0.1'
654+
assert raw(p2) == raw(p)
655+
619656
= Usage test
620657

621658
from scapy.main import _usage

0 commit comments

Comments
 (0)