From 5f55c441e00111be00cf78b5f52ec1c7895505b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=BCrkalp=20Burak=20Kayranc=C4=B1o=C4=9Flu?= Date: Fri, 18 Jul 2025 22:56:35 +0300 Subject: [PATCH 01/13] Rename 'comment' to 'comments' in Packet class and related classes for consistency --- scapy/packet.py | 27 +++++++++++++++++++++++---- scapy/utils.py | 39 +++++++++++++++++++++------------------ 2 files changed, 44 insertions(+), 22 deletions(-) diff --git a/scapy/packet.py b/scapy/packet.py index 8c558513157..333bc13ccf4 100644 --- a/scapy/packet.py +++ b/scapy/packet.py @@ -101,7 +101,7 @@ class Packet( "direction", "sniffed_on", # handle snaplen Vs real length "wirelen", - "comment", + "comments", "process_information" ] name = None @@ -179,7 +179,7 @@ def __init__(self, self.wirelen = None # type: Optional[int] self.direction = None # type: Optional[int] self.sniffed_on = None # type: Optional[_GlobInterfaceType] - self.comment = None # type: Optional[bytes] + self.comments = None # type: Optional[List[bytes]] self.process_information = None # type: Optional[Dict[str, Any]] self.stop_dissection_after = stop_dissection_after if _pkt: @@ -223,6 +223,25 @@ def __init__(self, Optional[bytes], ] + @property + def comment(self): + # type: () -> Optional[bytes] + """Get the comment of the packet""" + if self.comments is not None and len(self.comments) > 0: + return self.comments[-1] + + @comment.setter + def comment(self, value): + """ + Set the comment of the packet. + If value is None, it will clear the comments. + """ + # type: (Optional[bytes]) -> None + if value is not None: + self.comments = [value] + else: + self.comments = None + def __reduce__(self): # type: () -> Tuple[Type[Packet], Tuple[bytes], Packet._PickleType] """Used by pickling methods""" @@ -435,7 +454,7 @@ def copy(self) -> Self: clone.payload = self.payload.copy() clone.payload.add_underlayer(clone) clone.time = self.time - clone.comment = self.comment + clone.comments = self.comments clone.direction = self.direction clone.sniffed_on = self.sniffed_on return clone @@ -1145,7 +1164,7 @@ def clone_with(self, payload=None, **kargs): self.raw_packet_cache_fields ) pkt.wirelen = self.wirelen - pkt.comment = self.comment + pkt.comments = self.comments pkt.sniffed_on = self.sniffed_on pkt.direction = self.direction if payload is not None: diff --git a/scapy/utils.py b/scapy/utils.py index f7ecf6d447c..250886f39e0 100644 --- a/scapy/utils.py +++ b/scapy/utils.py @@ -1648,7 +1648,7 @@ class RawPcapNgReader(RawPcapReader): PacketMetadata = collections.namedtuple("PacketMetadataNg", # type: ignore ["linktype", "tsresol", "tshigh", "tslow", "wirelen", - "comment", "ifname", "direction", + "comments", "ifname", "direction", "process_information"]) def __init__(self, filename, fdesc=None, magic=None): # type: ignore @@ -1802,7 +1802,9 @@ def _read_options(self, options): "%d !" % len(options)) raise EOFError if code != 0 and 4 + length <= len(options): - opts[code] = options[4:4 + length] + if code not in opts: + opts[code] = [] + opts[code].append(options[4:4 + length]) if code == 0: if length != 0: warning("PcapNg: invalid option " @@ -1890,7 +1892,7 @@ def _read_block_epb(self, block, size): warning("PcapNg: EPB invalid process information index " "(%d/%d) !" % (proc_index, len(self.process_information))) - comment = options.get(1, None) + comments = options.get(1, None) epb_flags_raw = options.get(2, None) if epb_flags_raw: try: @@ -1913,7 +1915,7 @@ def _read_block_epb(self, block, size): tshigh=tshigh, tslow=tslow, wirelen=wirelen, - comment=comment, + comments=comments, ifname=ifname, direction=direction, process_information=process_information)) @@ -2068,7 +2070,7 @@ def read_packet(self, size=MTU, **kwargs): rp = super(PcapNgReader, self)._read_packet(size=size) if rp is None: raise EOFError - s, (linktype, tsresol, tshigh, tslow, wirelen, comment, ifname, direction, process_information) = rp # noqa: E501 + s, (linktype, tsresol, tshigh, tslow, wirelen, comments, ifname, direction, process_information) = rp # noqa: E501 try: cls = conf.l2types.num2layer[linktype] # type: Type[Packet] p = cls(s, **kwargs) # type: Packet @@ -2084,7 +2086,7 @@ def read_packet(self, size=MTU, **kwargs): if tshigh is not None: p.time = EDecimal((tshigh << 32) + tslow) / tsresol p.wirelen = wirelen - p.comment = comment + p.comments = comments p.direction = direction p.process_information = process_information.copy() if ifname is not None: @@ -2110,9 +2112,9 @@ def _write_packet(self, usec=None, # type: Optional[int] caplen=None, # type: Optional[int] wirelen=None, # type: Optional[int] - comment=None, # type: Optional[bytes] ifname=None, # type: Optional[bytes] direction=None, # type: Optional[int] + comments=None, # type: Optional[List[bytes]] ): # type: (...) -> None raise NotImplementedError @@ -2193,7 +2195,7 @@ def write_packet(self, if wirelen is None: wirelen = caplen - comment = getattr(packet, "comment", None) + comments = getattr(packet, "comments", None) ifname = getattr(packet, "sniffed_on", None) direction = getattr(packet, "direction", None) if not isinstance(packet, bytes): @@ -2208,10 +2210,10 @@ def write_packet(self, rawpkt, sec=f_sec, usec=usec, caplen=caplen, wirelen=wirelen, - comment=comment, ifname=ifname, direction=direction, - linktype=linktype + linktype=linktype, + comments=comments ) @@ -2541,7 +2543,7 @@ def _write_block_epb(self, timestamp=None, # type: Optional[Union[EDecimal, float]] # noqa: E501 caplen=None, # type: Optional[int] orglen=None, # type: Optional[int] - comment=None, # type: Optional[bytes] + comments=None, # type: Optional[List[bytes]] flags=None, # type: Optional[int] ): # type: (...) -> None @@ -2576,11 +2578,12 @@ def _write_block_epb(self, # Options opts = b'' - if comment is not None: - comment = bytes_encode(comment) - opts += struct.pack(self.endian + "HH", 1, len(comment)) - # Pad Option Value to 32 bits - opts += self._add_padding(comment) + if comments is not None and len(comments) > 0: + for comment in comments: + comment = bytes_encode(comment) + opts += struct.pack(self.endian + "HH", 1, len(comment)) + # Pad Option Value to 32 bits + opts += self._add_padding(comment) if type(flags) == int: opts += struct.pack(self.endian + "HH", 2, 4) opts += struct.pack(self.endian + "I", flags) @@ -2597,7 +2600,7 @@ def _write_packet(self, # type: ignore usec=None, # type: Optional[int] caplen=None, # type: Optional[int] wirelen=None, # type: Optional[int] - comment=None, # type: Optional[bytes] + comments=None, # type: Optional[List[bytes]] ifname=None, # type: Optional[bytes] direction=None, # type: Optional[int] ): @@ -2655,7 +2658,7 @@ def _write_packet(self, # type: ignore flags = None self._write_block_epb(packet, timestamp=sec, caplen=caplen, - orglen=wirelen, comment=comment, ifid=ifid, flags=flags) + orglen=wirelen, comments=comments, ifid=ifid, flags=flags) if self.sync: self.f.flush() From e1cc6f584563d918f89953e2e8d56deb68a9e68d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=BCrkalp=20Burak=20Kayranc=C4=B1o=C4=9Flu?= Date: Sat, 19 Jul 2025 00:07:29 +0300 Subject: [PATCH 02/13] Add support for 'comment' parameter in GenericPcapWriter and RawPcapWriter classes for back compatibility --- scapy/utils.py | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/scapy/utils.py b/scapy/utils.py index 250886f39e0..2acf7d3ce34 100644 --- a/scapy/utils.py +++ b/scapy/utils.py @@ -1648,10 +1648,10 @@ class RawPcapNgReader(RawPcapReader): PacketMetadata = collections.namedtuple("PacketMetadataNg", # type: ignore ["linktype", "tsresol", "tshigh", "tslow", "wirelen", - "comments", "ifname", "direction", - "process_information"]) + "comment", "ifname", "direction", + "process_information", "comments", ]) - def __init__(self, filename, fdesc=None, magic=None): # type: ignore + def __init__(self, filename, fdesc=None, magic=None, comments=None): # type: ignore # type: (str, IO[bytes], bytes) -> None self.filename = filename self.f = fdesc @@ -1675,6 +1675,7 @@ def __init__(self, filename, fdesc=None, magic=None): # type: ignore } self.endian = "!" # Will be overwritten by first SHB self.process_information = [] # type: List[Dict[str, Any]] + self.comments = comments if magic != b"\x0a\x0d\x0d\x0a": # PcapNg: raise Scapy_Exception( @@ -1802,9 +1803,12 @@ def _read_options(self, options): "%d !" % len(options)) raise EOFError if code != 0 and 4 + length <= len(options): - if code not in opts: - opts[code] = [] - opts[code].append(options[4:4 + length]) + if code in [1, 2988, 2989, 19372, 19373]: + if code not in opts: + opts[code] = [] + opts[code].append(options[4:4 + length]) + else: + opts[code] = options[4:4 + length] if code == 0: if length != 0: warning("PcapNg: invalid option " @@ -1893,6 +1897,7 @@ def _read_block_epb(self, block, size): "(%d/%d) !" % (proc_index, len(self.process_information))) comments = options.get(1, None) + comment = comments[-1] if comments is not None and len(comments) > 0 else None epb_flags_raw = options.get(2, None) if epb_flags_raw: try: @@ -1915,10 +1920,11 @@ def _read_block_epb(self, block, size): tshigh=tshigh, tslow=tslow, wirelen=wirelen, - comments=comments, + comment=comment, ifname=ifname, direction=direction, - process_information=process_information)) + process_information=process_information, + comments=comments)) def _read_block_spb(self, block, size): # type: (bytes, int) -> Tuple[bytes, RawPcapNgReader.PacketMetadata] @@ -1945,7 +1951,8 @@ def _read_block_spb(self, block, size): comment=None, ifname=None, direction=None, - process_information={})) + process_information={}, + comments=None)) def _read_block_pkt(self, block, size): # type: (bytes, int) -> Tuple[bytes, RawPcapNgReader.PacketMetadata] @@ -1969,7 +1976,8 @@ def _read_block_pkt(self, block, size): comment=None, ifname=None, direction=None, - process_information={})) + process_information={}, + comments=None)) def _read_block_dsb(self, block, size): # type: (bytes, int) -> None @@ -2070,7 +2078,7 @@ def read_packet(self, size=MTU, **kwargs): rp = super(PcapNgReader, self)._read_packet(size=size) if rp is None: raise EOFError - s, (linktype, tsresol, tshigh, tslow, wirelen, comments, ifname, direction, process_information) = rp # noqa: E501 + s, (linktype, tsresol, tshigh, tslow, wirelen, comment, ifname, direction, process_information, comments) = rp # noqa: E501 try: cls = conf.l2types.num2layer[linktype] # type: Type[Packet] p = cls(s, **kwargs) # type: Packet @@ -2086,7 +2094,11 @@ def read_packet(self, size=MTU, **kwargs): if tshigh is not None: p.time = EDecimal((tshigh << 32) + tslow) / tsresol p.wirelen = wirelen - p.comments = comments + p.comment = comment + if comments is not None: + comments.remove(comment) + if len(comments) > 0: + p.comments = comments p.direction = direction p.process_information = process_information.copy() if ifname is not None: @@ -2112,6 +2124,7 @@ def _write_packet(self, usec=None, # type: Optional[int] caplen=None, # type: Optional[int] wirelen=None, # type: Optional[int] + comment=None, # type: Optional[bytes] ifname=None, # type: Optional[bytes] direction=None, # type: Optional[int] comments=None, # type: Optional[List[bytes]] @@ -2195,6 +2208,7 @@ def write_packet(self, if wirelen is None: wirelen = caplen + comment = getattr(packet, "comment", None) comments = getattr(packet, "comments", None) ifname = getattr(packet, "sniffed_on", None) direction = getattr(packet, "direction", None) @@ -2210,10 +2224,11 @@ def write_packet(self, rawpkt, sec=f_sec, usec=usec, caplen=caplen, wirelen=wirelen, + comment=comment, ifname=ifname, direction=direction, linktype=linktype, - comments=comments + comments=comments, ) @@ -2368,6 +2383,7 @@ def _write_packet(self, comment=None, # type: Optional[bytes] ifname=None, # type: Optional[bytes] direction=None, # type: Optional[int] + comments=None, # type: Optional[List[bytes] ): # type: (...) -> None """ @@ -2600,9 +2616,10 @@ def _write_packet(self, # type: ignore usec=None, # type: Optional[int] caplen=None, # type: Optional[int] wirelen=None, # type: Optional[int] - comments=None, # type: Optional[List[bytes]] + comment=None, # type: Optional[bytes] ifname=None, # type: Optional[bytes] direction=None, # type: Optional[int] + comments=None, # type: Optional[List[bytes]] ): # type: (...) -> None """ From 0a17862b3bc0c639ddd3eac9c6c06c9662357f79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=BCrkalp=20Burak=20Kayranc=C4=B1o=C4=9Flu?= Date: Sat, 19 Jul 2025 00:09:52 +0300 Subject: [PATCH 03/13] fix typo --- scapy/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scapy/utils.py b/scapy/utils.py index 2acf7d3ce34..b178747339a 100644 --- a/scapy/utils.py +++ b/scapy/utils.py @@ -2383,7 +2383,7 @@ def _write_packet(self, comment=None, # type: Optional[bytes] ifname=None, # type: Optional[bytes] direction=None, # type: Optional[int] - comments=None, # type: Optional[List[bytes] + comments=None, # type: Optional[List[bytes]] ): # type: (...) -> None """ From eb68ff681121428dccdb3cb061ca340f8a540558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=BCrkalp=20Burak=20Kayranc=C4=B1o=C4=9Flu?= Date: Sat, 19 Jul 2025 00:51:52 +0300 Subject: [PATCH 04/13] Add test and support for 'comment' parameter for back compatibility --- scapy/utils.py | 12 +++++------- test/regression.uts | 6 ++++++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/scapy/utils.py b/scapy/utils.py index b178747339a..66fe92ec8b4 100644 --- a/scapy/utils.py +++ b/scapy/utils.py @@ -2094,11 +2094,9 @@ def read_packet(self, size=MTU, **kwargs): if tshigh is not None: p.time = EDecimal((tshigh << 32) + tslow) / tsresol p.wirelen = wirelen - p.comment = comment - if comments is not None: - comments.remove(comment) - if len(comments) > 0: - p.comments = comments + p.comments = comments + if p.comments is None: + p.comment = comment p.direction = direction p.process_information = process_information.copy() if ifname is not None: @@ -2595,8 +2593,8 @@ def _write_block_epb(self, # Options opts = b'' if comments is not None and len(comments) > 0: - for comment in comments: - comment = bytes_encode(comment) + for c in comments: + comment = bytes_encode(c) opts += struct.pack(self.endian + "HH", 1, len(comment)) # Pad Option Value to 32 bits opts += self._add_padding(comment) diff --git a/test/regression.uts b/test/regression.uts index 6ff43dae945..5e6b109028f 100644 --- a/test/regression.uts +++ b/test/regression.uts @@ -2250,6 +2250,12 @@ wrpcapng(tmpfile, p) l = rdpcap(tmpfile) assert l[0].comment == p.comment +p = Ether() / IPv6() / TCP() +p.comments = [b"Hello!", b"Scapy!", b"Pcapng!"] +wrpcapng(tmpfile, p) +l = rdpcap(tmpfile) +assert l[0].comments == p.comments + = rdpcap on fifo ~ linux f = get_temp_file() From f5e5c07b53bf4d399de9bbcc977fa1e871a9676b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=BCrkalp=20Burak=20Kayranc=C4=B1o=C4=9Flu?= Date: Mon, 21 Jul 2025 12:35:03 +0300 Subject: [PATCH 05/13] Refactor type annotations to comply with mypy type checking --- scapy/packet.py | 3 ++- scapy/utils.py | 15 +++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/scapy/packet.py b/scapy/packet.py index 333bc13ccf4..df112687911 100644 --- a/scapy/packet.py +++ b/scapy/packet.py @@ -229,14 +229,15 @@ def comment(self): """Get the comment of the packet""" if self.comments is not None and len(self.comments) > 0: return self.comments[-1] + return None @comment.setter def comment(self, value): + # type: (Optional[bytes]) -> None """ Set the comment of the packet. If value is None, it will clear the comments. """ - # type: (Optional[bytes]) -> None if value is not None: self.comments = [value] else: diff --git a/scapy/utils.py b/scapy/utils.py index 66fe92ec8b4..825ad74ff78 100644 --- a/scapy/utils.py +++ b/scapy/utils.py @@ -1652,7 +1652,7 @@ class RawPcapNgReader(RawPcapReader): "process_information", "comments", ]) def __init__(self, filename, fdesc=None, magic=None, comments=None): # type: ignore - # type: (str, IO[bytes], bytes) -> None + # type: (str, IO[bytes], bytes, List[bytes]) -> None self.filename = filename self.f = fdesc # A list of (linktype, snaplen, tsresol); will be populated by IDBs. @@ -1793,8 +1793,8 @@ def _read_packet(self, size=MTU): # type: ignore return res def _read_options(self, options): - # type: (bytes) -> Dict[int, bytes] - opts = dict() + # type: (bytes) -> Dict[int, Union[bytes, List[bytes]]] + opts = dict() # type: Dict[int, Union[bytes, List[bytes]]] while len(options) >= 4: try: code, length = struct.unpack(self.endian + "HH", options[:4]) @@ -1806,7 +1806,7 @@ def _read_options(self, options): if code in [1, 2988, 2989, 19372, 19373]: if code not in opts: opts[code] = [] - opts[code].append(options[4:4 + length]) + cast(List[bytes], opts[code]).append(options[4:4 + length]) else: opts[code] = options[4:4 + length] if code == 0: @@ -1827,6 +1827,10 @@ def _read_block_idb(self, block, _): options_raw = self._read_options(block[8:]) options = self.default_options.copy() # type: Dict[str, Any] for c, v in options_raw.items(): + if isinstance(v, list): + # If the option is a list, we take the last value + # (as per pcapng spec) + v = v[-1] if c == 9: length = len(v) if length == 1: @@ -1883,6 +1887,7 @@ def _read_block_epb(self, block, size): process_information = {} for code, value in options.items(): if code in [0x8001, 0x8003]: # PCAPNG_EPB_PIB_INDEX, PCAPNG_EPB_E_PIB_INDEX + value = cast(bytes, value) try: proc_index = struct.unpack(self.endian + "I", value)[0] except struct.error: @@ -1900,6 +1905,7 @@ def _read_block_epb(self, block, size): comment = comments[-1] if comments is not None and len(comments) > 0 else None epb_flags_raw = options.get(2, None) if epb_flags_raw: + epb_flags_raw = cast(bytes, epb_flags_raw) try: epb_flags, = struct.unpack(self.endian + "I", epb_flags_raw) except struct.error: @@ -2048,6 +2054,7 @@ def _read_block_pib(self, block, _): # Get Options options = self._read_options(block) for code, value in options.items(): + value = cast(bytes, value) if code == 2: process_information["name"] = value.decode("ascii", "backslashreplace") elif code == 4: From 38f490f836cd0b318dc908991f64bb9aeec61239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=BCrkalp=20Burak=20Kayranc=C4=B1o=C4=9Flu?= Date: Wed, 30 Jul 2025 09:46:31 +0300 Subject: [PATCH 06/13] Remove unnecessary code related to comments definition --- scapy/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scapy/utils.py b/scapy/utils.py index 825ad74ff78..30b71df895b 100644 --- a/scapy/utils.py +++ b/scapy/utils.py @@ -1651,7 +1651,7 @@ class RawPcapNgReader(RawPcapReader): "comment", "ifname", "direction", "process_information", "comments", ]) - def __init__(self, filename, fdesc=None, magic=None, comments=None): # type: ignore + def __init__(self, filename, fdesc=None, magic=None): # type: ignore # type: (str, IO[bytes], bytes, List[bytes]) -> None self.filename = filename self.f = fdesc @@ -1675,7 +1675,6 @@ def __init__(self, filename, fdesc=None, magic=None, comments=None): # type: ig } self.endian = "!" # Will be overwritten by first SHB self.process_information = [] # type: List[Dict[str, Any]] - self.comments = comments if magic != b"\x0a\x0d\x0d\x0a": # PcapNg: raise Scapy_Exception( From 3b258747c66054514ce45483a6a6f66fda9fa79e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=BCrkalp=20Burak=20Kayranc=C4=B1o=C4=9Flu?= Date: Wed, 30 Jul 2025 12:53:35 +0300 Subject: [PATCH 07/13] Remove redundant fallback to comment for compatibility handling --- scapy/utils.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/scapy/utils.py b/scapy/utils.py index 30b71df895b..28ca59146ff 100644 --- a/scapy/utils.py +++ b/scapy/utils.py @@ -1648,7 +1648,7 @@ class RawPcapNgReader(RawPcapReader): PacketMetadata = collections.namedtuple("PacketMetadataNg", # type: ignore ["linktype", "tsresol", "tshigh", "tslow", "wirelen", - "comment", "ifname", "direction", + "ifname", "direction", "process_information", "comments", ]) def __init__(self, filename, fdesc=None, magic=None): # type: ignore @@ -1901,7 +1901,6 @@ def _read_block_epb(self, block, size): "(%d/%d) !" % (proc_index, len(self.process_information))) comments = options.get(1, None) - comment = comments[-1] if comments is not None and len(comments) > 0 else None epb_flags_raw = options.get(2, None) if epb_flags_raw: epb_flags_raw = cast(bytes, epb_flags_raw) @@ -1925,7 +1924,6 @@ def _read_block_epb(self, block, size): tshigh=tshigh, tslow=tslow, wirelen=wirelen, - comment=comment, ifname=ifname, direction=direction, process_information=process_information, @@ -1953,7 +1951,6 @@ def _read_block_spb(self, block, size): tshigh=None, tslow=None, wirelen=wirelen, - comment=None, ifname=None, direction=None, process_information={}, @@ -1978,7 +1975,6 @@ def _read_block_pkt(self, block, size): tshigh=tshigh, tslow=tslow, wirelen=wirelen, - comment=None, ifname=None, direction=None, process_information={}, @@ -2084,7 +2080,7 @@ def read_packet(self, size=MTU, **kwargs): rp = super(PcapNgReader, self)._read_packet(size=size) if rp is None: raise EOFError - s, (linktype, tsresol, tshigh, tslow, wirelen, comment, ifname, direction, process_information, comments) = rp # noqa: E501 + s, (linktype, tsresol, tshigh, tslow, wirelen, ifname, direction, process_information, comments) = rp # noqa: E501 try: cls = conf.l2types.num2layer[linktype] # type: Type[Packet] p = cls(s, **kwargs) # type: Packet @@ -2101,8 +2097,6 @@ def read_packet(self, size=MTU, **kwargs): p.time = EDecimal((tshigh << 32) + tslow) / tsresol p.wirelen = wirelen p.comments = comments - if p.comments is None: - p.comment = comment p.direction = direction p.process_information = process_information.copy() if ifname is not None: From e6959018c0dac4d21c82a81ef3e3ce0a72101ee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=BCrkalp=20Burak=20Kayranc=C4=B1o=C4=9Flu?= Date: Wed, 30 Jul 2025 13:09:38 +0300 Subject: [PATCH 08/13] add comment for option codes --- scapy/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scapy/utils.py b/scapy/utils.py index 28ca59146ff..123f7d46ad7 100644 --- a/scapy/utils.py +++ b/scapy/utils.py @@ -1802,7 +1802,7 @@ def _read_options(self, options): "%d !" % len(options)) raise EOFError if code != 0 and 4 + length <= len(options): - if code in [1, 2988, 2989, 19372, 19373]: + if code in [1, 2988, 2989, 19372, 19373]: # https://www.ietf.org/archive/id/draft-tuexen-opsawg-pcapng-05.html#name-options-format if code not in opts: opts[code] = [] cast(List[bytes], opts[code]).append(options[4:4 + length]) From 89fd52237eea84d9a18955d7497d2350801cb5f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=BCrkalp=20Burak=20Kayranc=C4=B1o=C4=9Flu?= Date: Sat, 2 Aug 2025 09:34:55 +0300 Subject: [PATCH 09/13] Remove legacy 'comment' field when writing packet; use 'comments' instead --- scapy/utils.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scapy/utils.py b/scapy/utils.py index 123f7d46ad7..d11702e41a1 100644 --- a/scapy/utils.py +++ b/scapy/utils.py @@ -2122,7 +2122,6 @@ def _write_packet(self, usec=None, # type: Optional[int] caplen=None, # type: Optional[int] wirelen=None, # type: Optional[int] - comment=None, # type: Optional[bytes] ifname=None, # type: Optional[bytes] direction=None, # type: Optional[int] comments=None, # type: Optional[List[bytes]] @@ -2206,7 +2205,6 @@ def write_packet(self, if wirelen is None: wirelen = caplen - comment = getattr(packet, "comment", None) comments = getattr(packet, "comments", None) ifname = getattr(packet, "sniffed_on", None) direction = getattr(packet, "direction", None) @@ -2222,7 +2220,6 @@ def write_packet(self, rawpkt, sec=f_sec, usec=usec, caplen=caplen, wirelen=wirelen, - comment=comment, ifname=ifname, direction=direction, linktype=linktype, @@ -2378,7 +2375,6 @@ def _write_packet(self, usec=None, # type: Optional[int] caplen=None, # type: Optional[int] wirelen=None, # type: Optional[int] - comment=None, # type: Optional[bytes] ifname=None, # type: Optional[bytes] direction=None, # type: Optional[int] comments=None, # type: Optional[List[bytes]] @@ -2614,7 +2610,6 @@ def _write_packet(self, # type: ignore usec=None, # type: Optional[int] caplen=None, # type: Optional[int] wirelen=None, # type: Optional[int] - comment=None, # type: Optional[bytes] ifname=None, # type: Optional[bytes] direction=None, # type: Optional[int] comments=None, # type: Optional[List[bytes]] From b36cf2ec773ac4dc850a352191b31cc846a15973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=BCrkalp=20Burak=20Kayranc=C4=B1o=C4=9Flu?= Date: Wed, 20 Aug 2025 23:38:51 +0300 Subject: [PATCH 10/13] remove casting and update comment property to take first item of list --- scapy/packet.py | 4 ++-- scapy/utils.py | 13 +++++-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/scapy/packet.py b/scapy/packet.py index df112687911..232cfc53bfc 100644 --- a/scapy/packet.py +++ b/scapy/packet.py @@ -227,8 +227,8 @@ def __init__(self, def comment(self): # type: () -> Optional[bytes] """Get the comment of the packet""" - if self.comments is not None and len(self.comments) > 0: - return self.comments[-1] + if self.comments and len(self.comments): + return self.comments[0] return None @comment.setter diff --git a/scapy/utils.py b/scapy/utils.py index d11702e41a1..638efbc1dac 100644 --- a/scapy/utils.py +++ b/scapy/utils.py @@ -1805,7 +1805,7 @@ def _read_options(self, options): if code in [1, 2988, 2989, 19372, 19373]: # https://www.ietf.org/archive/id/draft-tuexen-opsawg-pcapng-05.html#name-options-format if code not in opts: opts[code] = [] - cast(List[bytes], opts[code]).append(options[4:4 + length]) + opts[code].append(options[4:4 + length]) else: opts[code] = options[4:4 + length] if code == 0: @@ -1827,9 +1827,9 @@ def _read_block_idb(self, block, _): options = self.default_options.copy() # type: Dict[str, Any] for c, v in options_raw.items(): if isinstance(v, list): - # If the option is a list, we take the last value - # (as per pcapng spec) - v = v[-1] + # Spec allows multiple occurrences (see https://www.ietf.org/archive/id/draft-tuexen-opsawg-pcapng-05.html#section-4.2-8.6) + # but does not define which to use. We take the first for backward compatibility. + v = v[0] if c == 9: length = len(v) if length == 1: @@ -1886,7 +1886,6 @@ def _read_block_epb(self, block, size): process_information = {} for code, value in options.items(): if code in [0x8001, 0x8003]: # PCAPNG_EPB_PIB_INDEX, PCAPNG_EPB_E_PIB_INDEX - value = cast(bytes, value) try: proc_index = struct.unpack(self.endian + "I", value)[0] except struct.error: @@ -1903,7 +1902,6 @@ def _read_block_epb(self, block, size): comments = options.get(1, None) epb_flags_raw = options.get(2, None) if epb_flags_raw: - epb_flags_raw = cast(bytes, epb_flags_raw) try: epb_flags, = struct.unpack(self.endian + "I", epb_flags_raw) except struct.error: @@ -2049,7 +2047,6 @@ def _read_block_pib(self, block, _): # Get Options options = self._read_options(block) for code, value in options.items(): - value = cast(bytes, value) if code == 2: process_information["name"] = value.decode("ascii", "backslashreplace") elif code == 4: @@ -2588,7 +2585,7 @@ def _write_block_epb(self, # Options opts = b'' - if comments is not None and len(comments) > 0: + if comments and len(comments): for c in comments: comment = bytes_encode(c) opts += struct.pack(self.endian + "HH", 1, len(comment)) From 1fb46570b8dfcda6d7192d540f12faef64a2bcd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=BCrkalp=20Burak=20Kayranc=C4=B1o=C4=9Flu?= Date: Fri, 22 Aug 2025 14:10:46 +0300 Subject: [PATCH 11/13] fix linting and typing issues --- scapy/utils.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/scapy/utils.py b/scapy/utils.py index 638efbc1dac..f7d7c7f5fd9 100644 --- a/scapy/utils.py +++ b/scapy/utils.py @@ -1652,7 +1652,7 @@ class RawPcapNgReader(RawPcapReader): "process_information", "comments", ]) def __init__(self, filename, fdesc=None, magic=None): # type: ignore - # type: (str, IO[bytes], bytes, List[bytes]) -> None + # type: (str, IO[bytes], bytes) -> None self.filename = filename self.f = fdesc # A list of (linktype, snaplen, tsresol); will be populated by IDBs. @@ -1802,10 +1802,11 @@ def _read_options(self, options): "%d !" % len(options)) raise EOFError if code != 0 and 4 + length <= len(options): - if code in [1, 2988, 2989, 19372, 19373]: # https://www.ietf.org/archive/id/draft-tuexen-opsawg-pcapng-05.html#name-options-format + # https://www.ietf.org/archive/id/draft-tuexen-opsawg-pcapng-05.html#name-options-format + if code in [1, 2988, 2989, 19372, 19373]: if code not in opts: opts[code] = [] - opts[code].append(options[4:4 + length]) + opts[code].append(options[4:4 + length]) # type: ignore else: opts[code] = options[4:4 + length] if code == 0: @@ -1827,8 +1828,10 @@ def _read_block_idb(self, block, _): options = self.default_options.copy() # type: Dict[str, Any] for c, v in options_raw.items(): if isinstance(v, list): - # Spec allows multiple occurrences (see https://www.ietf.org/archive/id/draft-tuexen-opsawg-pcapng-05.html#section-4.2-8.6) - # but does not define which to use. We take the first for backward compatibility. + # Spec allows multiple occurrences (see + # https://www.ietf.org/archive/id/draft-tuexen-opsawg-pcapng-05.html#section-4.2-8.6) + # but does not define which to use. We take the first for + # backward compatibility. v = v[0] if c == 9: length = len(v) @@ -1885,11 +1888,13 @@ def _read_block_epb(self, block, size): process_information = {} for code, value in options.items(): - if code in [0x8001, 0x8003]: # PCAPNG_EPB_PIB_INDEX, PCAPNG_EPB_E_PIB_INDEX + # PCAPNG_EPB_PIB_INDEX, PCAPNG_EPB_E_PIB_INDEX + if code in [0x8001, 0x8003]: try: - proc_index = struct.unpack(self.endian + "I", value)[0] + proc_index = struct.unpack( + self.endian + "I", value)[0] # type: ignore except struct.error: - warning("PcapNg: EPB invalid proc index" + warning("PcapNg: EPB invalid proc index " "(expected 4 bytes, got %d) !" % len(value)) raise EOFError if proc_index < len(self.process_information): @@ -1901,7 +1906,7 @@ def _read_block_epb(self, block, size): comments = options.get(1, None) epb_flags_raw = options.get(2, None) - if epb_flags_raw: + if epb_flags_raw and isinstance(epb_flags_raw, bytes): try: epb_flags, = struct.unpack(self.endian + "I", epb_flags_raw) except struct.error: @@ -2048,10 +2053,11 @@ def _read_block_pib(self, block, _): options = self._read_options(block) for code, value in options.items(): if code == 2: - process_information["name"] = value.decode("ascii", "backslashreplace") + process_information["name"] = value.decode( # type: ignore + "ascii", "backslashreplace") elif code == 4: if len(value) == 16: - process_information["uuid"] = str(UUID(bytes=value)) + process_information["uuid"] = str(UUID(bytes=value)) # type: ignore else: warning("PcapNg: DPEB UUID length is invalid (%d)!", len(value)) From 6f2593cd5a44b448928c4a978de90cc831664126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=BCrkalp=20Burak=20Kayranc=C4=B1o=C4=9Flu?= Date: Fri, 22 Aug 2025 14:31:08 +0300 Subject: [PATCH 12/13] Reorder fields in PacketMetadata tuple for consistency and update unpacking in PcapNgReader --- scapy/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scapy/utils.py b/scapy/utils.py index f7d7c7f5fd9..2516ff9938b 100644 --- a/scapy/utils.py +++ b/scapy/utils.py @@ -1648,8 +1648,8 @@ class RawPcapNgReader(RawPcapReader): PacketMetadata = collections.namedtuple("PacketMetadataNg", # type: ignore ["linktype", "tsresol", "tshigh", "tslow", "wirelen", - "ifname", "direction", - "process_information", "comments", ]) + "comments", "ifname", "direction", + "process_information", ]) def __init__(self, filename, fdesc=None, magic=None): # type: ignore # type: (str, IO[bytes], bytes) -> None @@ -2083,7 +2083,7 @@ def read_packet(self, size=MTU, **kwargs): rp = super(PcapNgReader, self)._read_packet(size=size) if rp is None: raise EOFError - s, (linktype, tsresol, tshigh, tslow, wirelen, ifname, direction, process_information, comments) = rp # noqa: E501 + s, (linktype, tsresol, tshigh, tslow, wirelen, comments, ifname, direction, process_information) = rp # noqa: E501 try: cls = conf.l2types.num2layer[linktype] # type: Type[Packet] p = cls(s, **kwargs) # type: Packet From ecab9a6820726ea0cdaf193299f30996a6c92d04 Mon Sep 17 00:00:00 2001 From: gpotter2 <10530980+gpotter2@users.noreply.github.com> Date: Wed, 3 Sep 2025 18:49:26 +0200 Subject: [PATCH 13/13] Apply formatting suggestion by guedou Co-authored-by: Guillaume Valadon --- scapy/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scapy/utils.py b/scapy/utils.py index 2516ff9938b..648c8f01a19 100644 --- a/scapy/utils.py +++ b/scapy/utils.py @@ -1649,7 +1649,7 @@ class RawPcapNgReader(RawPcapReader): ["linktype", "tsresol", "tshigh", "tslow", "wirelen", "comments", "ifname", "direction", - "process_information", ]) + "process_information"]) def __init__(self, filename, fdesc=None, magic=None): # type: ignore # type: (str, IO[bytes], bytes) -> None