Skip to content

Commit b9f54f2

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

File tree

2 files changed

+85
-30
lines changed

2 files changed

+85
-30
lines changed

scapy/packet.py

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

8181

82+
def _rebuild_pkt(
83+
cls, # type: Type[Packet]
84+
fields, # type: Dict[str, Any]
85+
payload, # type: Optional[Packet]
86+
metadata, # type: Dict[str, Any]
87+
):
88+
# type: (...) -> Packet
89+
"""Helper for unpickling Packet instances via field values."""
90+
# Create the instance using the field values
91+
pkt = cls(**fields)
92+
if payload is not None:
93+
pkt.add_payload(payload)
94+
# Restore metadata
95+
pkt.time = metadata['time']
96+
pkt.sent_time = metadata['sent_time']
97+
pkt.direction = metadata['direction']
98+
pkt.sniffed_on = metadata['sniffed_on']
99+
pkt.wirelen = metadata['wirelen']
100+
pkt.comments = metadata['comments']
101+
return pkt
102+
103+
82104
class Packet(
83105
BasePacket,
84106
_CanvasDumpExtended,
@@ -214,15 +236,6 @@ def __init__(self,
214236
else:
215237
self.post_transforms = [post_transform]
216238

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-
226239
@property
227240
def comment(self):
228241
# type: () -> Optional[bytes]
@@ -244,27 +257,32 @@ def comment(self, value):
244257
self.comments = None
245258

246259
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
260+
# type: () -> Tuple[Any, ...]
261+
"""Used by pickling methods.
262+
263+
Reconstructs the packet from field values, payload, and metadata.
264+
"""
265+
# Store field values for unpickling
266+
fields = {}
267+
for f in self.fields_desc:
268+
if f.name in self.fields:
269+
fields[f.name] = self.fields[f.name]
270+
payload = self.payload # type: Optional[Packet]
271+
if isinstance(payload, NoPayload):
272+
payload = None
273+
# Store metadata for unpickling
274+
metadata = {
275+
'time': self.time,
276+
'sent_time': self.sent_time,
277+
'direction': self.direction,
278+
'sniffed_on': self.sniffed_on,
279+
'wirelen': self.wirelen,
280+
'comments': self.comments,
281+
}
282+
return (
283+
_rebuild_pkt,
284+
(self.__class__, fields, payload, metadata),
285+
)
268286

269287
def __deepcopy__(self,
270288
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)