Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions pcapkit/protocols/internet/ipv4.py
Original file line number Diff line number Diff line change
Expand Up @@ -1255,12 +1255,23 @@ def _make_ipv4_options(self, options: 'list[Schema_Option | tuple[Enum_OptionNum
# force alignment to 32-bit boundary
if data_len % 4:
pad_len = 4 - (data_len % 4)
# NOTE: The terminator goes in as an EOOL option *schema*, the
# way the padding above goes in as a NOP schema. What used to be
# appended was ``Enum_OptionNumber.EOOL`` itself -- the wire code
# rather than an option -- and the enclosing ``options`` field
# takes only schemas and :obj:`bytes`, so packing the header
# failed with ``FieldValueError: Field options has invalid
# value``. Any option whose length is not already a multiple of
# four reaches this branch, so that made the packet unpackable
# whether it was built by hand or rebuilt from a parsed one. See
# #506.
pad_opt = self._make_opt_nop(Enum_OptionNumber.NOP) # type: ignore[arg-type]
end_opt = self._make_opt_eool(Enum_OptionNumber.EOOL) # type: ignore[arg-type]
total_length += pad_len

for _ in range(pad_len - 1):
options_list.append(pad_opt)
options_list.append(Enum_OptionNumber.EOOL) # type: ignore[arg-type]
options_list.append(end_opt)
return options_list, total_length

options_list = []
Expand All @@ -1286,12 +1297,17 @@ def _make_ipv4_options(self, options: 'list[Schema_Option | tuple[Enum_OptionNum
# force alignment to 32-bit boundary
if data_len % 4:
pad_len = 4 - (data_len % 4)
# NOTE: An EOOL option schema rather than the bare wire code, for the
# reason spelled out in the list branch above. This is the branch the
# ``from_data`` path takes, since a parsed packet hands its options
# back as a container. See #506.
pad_opt = self._make_opt_nop(Enum_OptionNumber.NOP) # type: ignore[arg-type]
end_opt = self._make_opt_eool(Enum_OptionNumber.EOOL) # type: ignore[arg-type]
total_length += pad_len

for _ in range(pad_len - 1):
options_list.append(pad_opt)
options_list.append(Enum_OptionNumber.EOOL) # type: ignore[arg-type]
options_list.append(end_opt)
return options_list, total_length

def _make_opt_unassigned(self, kind: 'Enum_OptionNumber', option: 'Optional[Data_UnassignedOption]' = None, *,
Expand Down
20 changes: 18 additions & 2 deletions pcapkit/protocols/schema/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -667,12 +667,28 @@ class will consider negative value as a placeholder.
data = self.__dict__.get(self.__map__.get(field.name, field.name))

if isinstance(field, PayloadField):
# NOTE: ``ProtocolBase``, not ``Protocol``. The two were one class
# until the metaclass revision split them, which renamed the base to
# ``ProtocolBase`` and kept ``Protocol`` as a thin subclass that adds
# auto-registration for externally defined engines. Every module under
# ``pcapkit.protocols.schema`` was updated to import the base under the
# old name; this one was missed, because its import is a runtime import
# inside a method rather than a ``TYPE_CHECKING`` one at module level.
# No protocol in the library subclasses ``Protocol``, so the branch
# below had been unreachable ever since: handing a payload field any
# protocol instance -- which is exactly what :meth:`ProtocolBase._make_payload
# <pcapkit.protocols.protocol.ProtocolBase._make_payload>` returns, and
# what ``make``'s own ``bytes | Protocol | Schema`` signature advertises
# -- fell through to the ``ProtocolUnbound`` below instead of being
# packed. That is what stopped ``from_data`` reconstructing any parsed
# packet. An external ``Protocol`` subclass is still a ``ProtocolBase``,
# so nothing that worked before is affected. See #506.
from pcapkit.protocols.protocol import \
Protocol # pylint: disable=import-outside-toplevel
ProtocolBase # pylint: disable=import-outside-toplevel

if data is None:
self.__buffer__[field.name] = b''
elif isinstance(data, Protocol):
elif isinstance(data, ProtocolBase):
self.__buffer__[field.name] = bytes(data)
elif isinstance(data, bytes):
self.__buffer__[field.name] = data
Expand Down
Loading
Loading