diff --git a/scapy/packet.py b/scapy/packet.py index 5d4f9e0683f..c70dc7e36fe 100644 --- a/scapy/packet.py +++ b/scapy/packet.py @@ -80,6 +80,12 @@ _T = TypeVar("_T", Dict[str, Any], Optional[Dict[str, Any]]) +def _resolved_fields_desc(fields_desc): + # type: (Sequence[Union[AnyField, Type['Packet']]]) -> List[AnyField] + """Return fields_desc entries after Packet_metaclass resolution.""" + return cast(List[AnyField], list(fields_desc)) + + class Packet( BasePacket, _CanvasDumpExtended, @@ -106,7 +112,7 @@ class Packet( "process_information" ] name = None - fields_desc = [] # type: ClassVar[List[AnyField]] + fields_desc = [] # type: ClassVar[List[Union[AnyField, Type[Packet]]]] # noqa: E501 deprecated_fields = {} # type: Dict[str, Tuple[str, str]] overload_fields = {} # type: Dict[Type[Packet], Dict[str, Any]] payload_guess = [] # type: List[Tuple[Dict[str, Any], Type[Packet]]] @@ -190,7 +196,7 @@ def __init__(self, # We use this strange initialization so that the fields # are initialized in their declaration order. # It is required to always support MultipleTypeField - for field in self.fields_desc: + for field in _resolved_fields_desc(self.fields_desc): fname = field.name try: value = fields.pop(fname) @@ -323,7 +329,7 @@ def init_fields(self, for_dissect_only=False): """ if self.class_dont_cache.get(self.__class__, False): - self.do_init_fields(self.fields_desc) + self.do_init_fields(_resolved_fields_desc(self.fields_desc)) else: self.do_init_cached_fields(for_dissect_only=for_dissect_only) @@ -354,7 +360,7 @@ def do_init_cached_fields(self, for_dissect_only=False): # Build the fields information if Packet.class_default_fields.get(cls_name, None) is None: - self.prepare_cached_fields(self.fields_desc) + self.prepare_cached_fields(_resolved_fields_desc(self.fields_desc)) # Use fields information from cache default_fields = Packet.class_default_fields.get(cls_name, None) @@ -398,7 +404,7 @@ def prepare_cached_fields(self, flist): if isinstance(f, MultipleTypeField): # Abort self.class_dont_cache[cls_name] = True - self.do_init_fields(self.fields_desc) + self.do_init_fields(_resolved_fields_desc(self.fields_desc)) return class_default_fields[f.name] = copy.deepcopy(f.default) @@ -631,7 +637,7 @@ def __repr__(self): # type: () -> str s = "" ct = conf.color_theme - for f in self.fields_desc: + for f in _resolved_fields_desc(self.fields_desc): if isinstance(f, ConditionalField) and not f._evalcond(self): continue if f.name in self.fields: @@ -770,7 +776,7 @@ def self_build(self): if self.raw_packet_cache is not None: return self.raw_packet_cache p = b"" - for f in self.fields_desc: + for f in _resolved_fields_desc(self.fields_desc): val = self.getfieldval(f.name) if isinstance(val, RawVal): p += bytes(val) @@ -851,7 +857,7 @@ def do_build_ps(self): p = b"" pl = [] q = b"" - for f in self.fields_desc: + for f in _resolved_fields_desc(self.fields_desc): if isinstance(f, ConditionalField) and not f._evalcond(self): continue p = f.addfield(self, p, self.getfieldval(f.name)) @@ -1079,7 +1085,7 @@ def do_dissect(self, s): # type: (bytes) -> bytes _raw = s self.raw_packet_cache_fields = {} - for f in self.fields_desc: + for f in _resolved_fields_desc(self.fields_desc): s, fval = f.getfield(self, s) # Skip unused ConditionalField if isinstance(f, ConditionalField) and fval is None: @@ -1291,8 +1297,8 @@ def __eq__(self, other): # type: (Any) -> bool if not isinstance(other, self.__class__): return False - for f in self.fields_desc: - if f not in other.fields_desc: + for f in _resolved_fields_desc(self.fields_desc): + if f not in _resolved_fields_desc(other.fields_desc): return False if self.getfieldval(f.name) != other.getfieldval(f.name): return False @@ -1503,7 +1509,7 @@ def _show_or_dump(self, ct.punct("###["), ct.layer_name(self.name), ct.punct("]###")) - fields = self.fields_desc.copy() + fields = _resolved_fields_desc(self.fields_desc).copy() while fields: f = fields.pop(0) if isinstance(f, ConditionalField) and not f._evalcond(self): @@ -1737,7 +1743,7 @@ def _do_summary(self): ret = self.__class__.__name__ if self.show_summary else "" if self.__class__ in conf.emph: impf = [] - for f in self.fields_desc: + for f in _resolved_fields_desc(self.fields_desc): if f in conf.emph: impf.append("%s=%s" % (f.name, f.i2repr(self, self.getfieldval(f.name)))) # noqa: E501 ret = "%s [%s]" % (ret, " ".join(impf)) @@ -1775,7 +1781,10 @@ def _command(self, json=False): f = [] iterator: Iterator[Tuple[str, Any]] if json: - iterator = ((x.name, self.getfieldval(x.name)) for x in self.fields_desc) + iterator = ( + (x.name, self.getfieldval(x.name)) + for x in _resolved_fields_desc(self.fields_desc) + ) else: iterator = iter(self.fields.items()) for fn, fv in iterator: @@ -2423,7 +2432,7 @@ def _pkt_ls(obj, # type: Union[Packet, Type[Packet]] if not issubtype(obj, Packet) and not is_pkt: raise ValueError fields = [] - for f in obj.fields_desc: + for f in _resolved_fields_desc(obj.fields_desc): cur_fld = f attrs = [] # type: List[str] long_attrs = [] # type: List[str] @@ -2581,7 +2590,7 @@ def rfc(cls, ret=False, legend=True): # Generate packet groups def _iterfields() -> Iterator[Tuple[str, int]]: - for f in cls.fields_desc: + for f in _resolved_fields_desc(cls.fields_desc): # Fancy field name fname = f.name.upper().replace("_", " ") fsize = int(f.sz * 8) @@ -2697,7 +2706,7 @@ def fuzz(p, # type: _P while not isinstance(q, NoPayload): new_default_fields = {} multiple_type_fields = [] # type: List[str] - for f in q.fields_desc: + for f in _resolved_fields_desc(q.fields_desc): if isinstance(f, PacketListField): for r in getattr(q, f.name): fuzz(r, _inplace=1)