Skip to content

Commit dc34e97

Browse files
Make FlagValue bound to a Packet (#4705)
- `FlagValue.pkt` attribute added. - `list_field.ensure_bound()` generalized as `Packet._ensure_bound_field_value()` and completed for `FlagValue`s. - `Packet.clear_cache()` automatically in `FlagValue.__setattr__()`.
1 parent 5a1f68f commit dc34e97

File tree

2 files changed

+39
-38
lines changed

2 files changed

+39
-38
lines changed

scapy/fields.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2973,7 +2973,7 @@ def __next__(self):
29732973

29742974

29752975
class FlagValue(object):
2976-
__slots__ = ["value", "names", "multi"]
2976+
__slots__ = ["pkt", "value", "names", "multi"]
29772977

29782978
def _fixvalue(self, value):
29792979
# type: (Any) -> int
@@ -2990,6 +2990,10 @@ def _fixvalue(self, value):
29902990

29912991
def __init__(self, value, names):
29922992
# type: (Union[List[str], int, str], Union[List[str], str]) -> None
2993+
2994+
#: Packet bound with this flag value.
2995+
self.pkt = None # type: Optional[Packet]
2996+
29932997
self.multi = isinstance(names, list)
29942998
self.names = names
29952999
self.value = self._fixvalue(value)
@@ -3125,6 +3129,10 @@ def __setattr__(self, attr, value):
31253129
self.value |= (2 ** self.names.index(attr))
31263130
else:
31273131
self.value &= ~(2 ** self.names.index(attr))
3132+
3133+
# Automatically call `Packet.clear_cache()` when the flags are modified.
3134+
if self.pkt is not None:
3135+
self.pkt.clear_cache(upwards=True, downwards=True)
31283136
else:
31293137
return super(FlagValue, self).__setattr__(attr, value)
31303138

scapy/packet.py

Lines changed: 30 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,7 @@ def __init__(self,
205205
continue
206206
if not isinstance(value, RawVal):
207207
value = self.get_field(fname).any2i(self, value)
208-
209-
# In case of a list, ensure we store a `list_field` instance, not a simple `list`.
210-
if isinstance(value, list):
211-
value = list_field.ensure_bound(self, value)
212-
208+
value = self._ensure_bound_field_value(value)
213209
self.fields[fname] = value
214210
# The remaining fields are unknown
215211
for fname in fields:
@@ -219,11 +215,7 @@ def __init__(self,
219215
fname = self._resolve_alias(fname)
220216
if not isinstance(value, RawVal):
221217
value = self.get_field(fname).any2i(self, value)
222-
223-
# In case of a list, ensure we store a `list_field` instance, not a simple `list`.
224-
if isinstance(value, list):
225-
value = list_field.ensure_bound(self, value)
226-
218+
value = self._ensure_bound_field_value(value)
227219
self.fields[fname] = value
228220
continue
229221
raise AttributeError(fname)
@@ -332,10 +324,7 @@ def do_init_cached_fields(self, for_dissect_only=False, init_fields=None):
332324

333325
# Fix: Use `copy_field_value()` instead of just `value.copy()`, in order to duplicate list items as well in case of a list.
334326
self.fields[fname] = self.copy_field_value(fname, self.default_fields[fname])
335-
336-
# In case of a list, ensure we store a `list_field` instance, not a simple `list`.
337-
if isinstance(self.fields[fname], list):
338-
self.fields[fname] = list_field.ensure_bound(self, self.fields[fname])
327+
self.fields[fname] = self._ensure_bound_field_value(self.fields[fname])
339328

340329
self._ensure_parent_of(self.fields[fname])
341330

@@ -682,11 +671,7 @@ def setfieldval(self, attr, val):
682671
any2i = fld.any2i
683672
if not isinstance(val, RawVal):
684673
val = any2i(self, val)
685-
686-
# In case of a list, ensure we store a `list_field` instance, not a simple `list`.
687-
if isinstance(val, list):
688-
val = list_field.ensure_bound(self, val)
689-
674+
val = self._ensure_bound_field_value(val)
690675
self.fields[attr] = val
691676
self.explicit = 0
692677
# Invalidate cache when the packet has changed.
@@ -708,6 +693,31 @@ def __setattr__(self, attr, val):
708693
pass
709694
return object.__setattr__(self, attr, val)
710695

696+
def _ensure_bound_field_value(
697+
self,
698+
value, # type: Any
699+
): # type: (...) -> list_field
700+
"""
701+
Ensures a field instance bound with ``self`` when applicable.
702+
"""
703+
if isinstance(value, list):
704+
# If `value` is a simple `list`, create a new `list_field` instance.
705+
# If `value` is already a `list_field` instance, we never know where this instance comes from, and what it's being used for.
706+
# Let's create a new `list_field` instance in any case.
707+
return list_field(self, value)
708+
709+
if isinstance(value, FlagValue):
710+
# We never know where the `FlagValue` instance comes from, and what it's being used for.
711+
# Let's create a new instance.
712+
value = value.copy()
713+
value.pkt = self
714+
return value
715+
716+
# Non-specific bound field.
717+
# Maybe a packet? rely on parentship in that case.
718+
# Return `value` as is.
719+
return value
720+
711721
def delfieldval(self, attr):
712722
# type: (str) -> None
713723
if attr in self.fields:
@@ -1233,11 +1243,7 @@ def do_dissect(self, s):
12331243
# Skip unused ConditionalField
12341244
if isinstance(f, ConditionalField) and fval is None:
12351245
continue
1236-
1237-
# In case of a list, ensure we store a `list_field` instance, not a simple `list`.
1238-
if isinstance(fval, list):
1239-
fval = list_field.ensure_bound(self, fval)
1240-
1246+
fval = self._ensure_bound_field_value(fval)
12411247
self.fields[f.name] = fval
12421248
# Nothing left to dissect
12431249
if not s and (isinstance(f, MayEnd) or
@@ -2231,19 +2237,6 @@ def __init__(
22312237
#: Packet bound with this list field.
22322238
self.pkt = pkt
22332239

2234-
@staticmethod
2235-
def ensure_bound(
2236-
pkt, # type: Packet
2237-
lst, # type: List[Any]
2238-
): # type: (...) -> list_field
2239-
"""
2240-
Ensures a :class:`list_field` instance bound with ``pkt``.
2241-
"""
2242-
# If `lst` is a simple `list`, this method intends to create a new `list_field` instance.
2243-
# If `lst` is already a `list_field` instance, we never know where this instance comes from, and what it's being used for.
2244-
# Let's create a new `list_field` instance in any case.
2245-
return list_field(pkt, lst)
2246-
22472240

22482241
####################
22492242
# packet classes #

0 commit comments

Comments
 (0)