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
22 changes: 12 additions & 10 deletions pcapkit/protocols/internet/hip.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,10 +401,10 @@ class HIP(IPv6_Ext[Data_HIP, Schema_HIP], Internet[Data_HIP, Schema_HIP],

#: DefaultDict[Enum_Parameter, str | tuple[ParameterParser, ParameterConstructor]]:
#: Parameter code to method mapping, c.f. :meth:`_read_hip_param` and/or
#: :meth:`_make_hip_param`. Method names are expected to be referred to the
#: class by ``_read_param_${name}`` and/or ``_make_param_${name}``, and if
#: such name not found, the value should then be a method that can parse the
#: parameter by itself.
#: :meth:`_make_hip_param`. A :obj:`str` value names the methods
#: ``_read_param_${name}`` and ``_make_param_${name}``, falling back to
#: ``_read_param_unassigned`` / ``_make_param_unassigned`` when they do not
#: exist; any other value is a ``(parser, constructor)`` pair of callables.
__parameter__ = collections.defaultdict(
lambda: 'unassigned',
{
Expand Down Expand Up @@ -971,10 +971,10 @@ def _read_locator(locator: 'Schema_Locator') -> 'Data_LocatorData | IPv6Address'
locator: locator data

Returns:
* If ``kind`` is ``0`` and ``size`` is ``16``,
returns an :class:`~ipaddress.IPv4Address` object.
* If ``kind`` is ``1`` and ``size`` is ``20``,
returns a :class:`~pcapkit.protocols.data.internet.hip.Locator` object.
* If ``kind`` is ``0`` and the locator is 16 octets,
an :class:`~ipaddress.IPv6Address` object.
* If ``kind`` is ``1`` and the locator is 20 octets,
a :class:`~pcapkit.protocols.data.internet.hip.LocatorData` object.

Raises:
ProtocolError: in other cases
Expand Down Expand Up @@ -1052,7 +1052,8 @@ def _read_param_puzzle(self, schema: 'Schema_PuzzleParameter', *, version: 'int'
Parsed parameter data.

Raises:
ProtocolError: The parameter is **ONLY** supported in HIPv1.
ProtocolError: If ``version`` is ``1`` and ``schema.len`` is **NOT**
``12``.

"""
if version == 1 and schema.len != 12:
Expand Down Expand Up @@ -1110,7 +1111,8 @@ def _read_param_solution(self, schema: 'Schema_SolutionParameter', *, version: '
Parsed parameter data.

Raises:
ProtocolError: The parameter is **ONLY** supported in HIPv1.
ProtocolError: If ``version`` is ``1`` and ``schema.len`` is **NOT**
``20``, or if ``schema.len - 4`` is odd.

"""
if version == 1 and schema.len != 20:
Expand Down
29 changes: 15 additions & 14 deletions pcapkit/protocols/internet/hopopt.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
Octets Bits Name Description
======= ========= =================== =================================
0 0 ``hopopt.next`` Next Header
1 8 ``hopopt.length`` Header Extensive Length
1 8 ``hopopt.length`` Header Extension Length
2 16 ``hopopt.options`` Options
======= ========= =================== =================================

Expand Down Expand Up @@ -189,10 +189,10 @@ class HOPOPT(IPv6_Ext[Data_HOPOPT, Schema_HOPOPT],

#: DefaultDict[Enum_Option, str | tuple[OptionParser, OptionConstructor]]: Option
#: code to method mapping, c.f. :meth:`_read_hopopt_options` and/or
#: :meth:`_make_hopopt_options`. Method names are expected to be referred
#: to the class by ``_read_opt_${name}`` and/or ``_make_opt_${name}``, and
#: if such name not found, the value should then be a method that can parse
#: the option by itself.
#: :meth:`_make_hopopt_options`. A :obj:`str` value names the methods
#: ``_read_opt_${name}`` and ``_make_opt_${name}``, falling back to
#: ``_read_opt_none`` / ``_make_opt_none`` when they do not exist; any other
#: value is a ``(parser, constructor)`` pair of callables.
__option__ = collections.defaultdict(
lambda: 'none',
{
Expand Down Expand Up @@ -477,7 +477,7 @@ def _hopopt_option_length(schema_len: 'int') -> 'int':
whole option, which is what every ``_read_opt_*`` below reports back
as the parsed option's own ``.length``, is two octets more. This is
the ``+2``/``-2`` mismatch between the two (see ``Data_PadOption.length``
vs. ``Schema_PadOption.length`` below). The read-side half lives in this
vs. ``Schema_PadOption.len`` below). The read-side half lives in this
one helper so the arithmetic has a single home. Do NOT drop the
``+ 2``.

Expand Down Expand Up @@ -516,7 +516,8 @@ def _read_hopopt_options(self, length: 'int') -> 'Option':
Extracted HOPOPT options

Raises:
ProtocolError: If the threshold is **NOT** matching.
ProtocolError: If the parsed options do not total exactly ``length``
octets.

"""
counter = 0 # length of read options
Expand Down Expand Up @@ -657,7 +658,7 @@ def _read_opt_tun(self, schema: 'Schema_TunnelEncapsulationLimitOption', *, opti
Parsed option data.

Raises:
ProtocolError: If ``hopopt.tun.length`` is **NOT** ``1``.
ProtocolError: If ``schema.len`` is **NOT** ``1``.

"""
if schema.len != 1:
Expand Down Expand Up @@ -691,7 +692,7 @@ def _read_opt_ra(self, schema: 'Schema_RouterAlertOption', *, options: 'Option')
Parsed option data.

Raises:
ProtocolError: If ``hopopt.tun.length`` is **NOT** ``2``.
ProtocolError: If ``schema.len`` is **NOT** ``2``.

"""
if schema.len != 2:
Expand Down Expand Up @@ -868,7 +869,7 @@ def _read_opt_pdm(self, schema: 'Schema_PDMOption', *, options: 'Option') -> 'Da
Parsed option data.

Raises:
ProtocolError: If ``hopopt.pdm.length`` is **NOT** ``10``.
ProtocolError: If ``schema.len`` is **NOT** ``10``.

"""
if schema.len != 10:
Expand Down Expand Up @@ -990,7 +991,7 @@ def _read_opt_rpl(self, schema: 'Schema_RPLOption', *, options: 'Option') -> 'Da
Parsed option data.

Raises:
ProtocolError: If ``hopopt.rpl.length`` is **NOT** ``4``.
ProtocolError: If ``schema.len`` is **NOT** ``4``.

"""
if schema.len != 4:
Expand Down Expand Up @@ -1168,7 +1169,7 @@ def _read_opt_jumbo(self, schema: 'Schema_JumboPayloadOption', *, options: 'Opti
Parsed option data.

Raises:
ProtocolError: If ``hopopt.jumbo.length`` is **NOT** ``4``.
ProtocolError: If ``schema.len`` is **NOT** ``4``.

"""
if schema.len != 4:
Expand Down Expand Up @@ -1212,7 +1213,7 @@ def _read_opt_home(self, schema: 'Schema_HomeAddressOption', *, options: 'Option
Parsed option data.

Raises:
ProtocolError: If ``hopopt.jumbo.length`` is **NOT** ``16``.
ProtocolError: If ``schema.len`` is **NOT** ``16``.

"""
if schema.len != 16:
Expand Down Expand Up @@ -1250,7 +1251,7 @@ def _read_opt_ip_dff(self, schema: 'Schema_IPDFFOption', *, options: 'Option') -
Parsed option data.

Raises:
ProtocolError: If ``hopopt.ip_dff.length`` is **NOT** ``2``.
ProtocolError: If ``schema.len`` is **NOT** ``2``.

"""
if schema.len != 2:
Expand Down
10 changes: 5 additions & 5 deletions pcapkit/protocols/internet/ipv4.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,10 @@ class IPv4(IP[Data_IPv4, Schema_IPv4],

#: DefaultDict[Enum_OptionNumber, str | tuple[OptionParser, OptionConstructor]]:
#: Option code to method mapping, c.f. :meth:`_read_ipv4_options` and/or
#: :meth:`_make_ipv4_options`. Method names are expected to be referred
#: to the class by ``_read_opt_${name}`` and/or ``_make_opt_${name}``, and
#: if such name not found, the value should then be a method that can parse
#: the option by itself.
#: :meth:`_make_ipv4_options`. A :obj:`str` value names the methods
#: ``_read_opt_${name}`` and ``_make_opt_${name}``, falling back to
#: ``_read_opt_unassigned`` / ``_make_opt_unassigned`` when they do not
#: exist; any other value is a ``(parser, constructor)`` pair of callables.
__option__ = collections.defaultdict(
lambda: 'unassigned',
{
Expand Down Expand Up @@ -589,7 +589,7 @@ def _read_ipv4_options(self, length: 'int') -> 'Option':
Extracted IPv4 options.

Raises:
ProtocolError: If the threshold is **NOT** matching.
ProtocolError: If the parsed options total more than ``length`` octets.

"""
counter = 0 # length of read option list
Expand Down
47 changes: 21 additions & 26 deletions pcapkit/protocols/internet/ipv6.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,11 +321,11 @@ def _make_data(cls, data: 'Data_IPv6') -> 'dict[str, Any]': # type: ignore[over
}

def _read_ip_hextet(self) -> 'tuple[int, int, int]':
"""Read first four hextets of IPv6.
"""Read the first four octets of IPv6.

Returns:
Parsed hextets data, including version number, traffic class and
flow label.
Parsed data of those four octets: version number, traffic class
and flow label.

"""
_htet = self._read_fileng(4).hex()
Expand All @@ -350,7 +350,7 @@ def _decode_next_layer(self, ipv6: 'Data_IPv6', proto: 'Optional[int]' = None,

Arguments:
ipv6: info buffer
proto: next layer protocol name
proto: next layer protocol index
length: valid (*not padding*) length
packet: packet info (passed from :meth:`self.unpack <pcapkit.protocols.protocol.Protocol.unpack>`)

Expand All @@ -365,7 +365,7 @@ def _decode_next_layer(self, ipv6: 'Data_IPv6', proto: 'Optional[int]' = None,
raw_len = ipv6.payload # payload length
_protos = [] # ProtoChain buffer

# traverse if next header is an extensive header
# traverse if next header is an extension header
payload = self.__header__.get_payload()
while True:
try:
Expand Down Expand Up @@ -405,30 +405,25 @@ def _decode_next_layer(self, ipv6: 'Data_IPv6', proto: 'Optional[int]' = None,
payload = payload[next_.length:]

# A layer with no ``next`` field cannot safely continue the walk.
# This is a *structural* check -- does the parsed info even carry a
# ``next`` attribute? -- not a fixed set of codes, and deliberately
# so: HOPOPT, IPv6-Route, IPv6-Opts, MH, HIP, IPv6-Frag and AH all
# have dedicated parsers whose data carries ``next``, and Shim6 and
# any recognised header whose own parser raised are both handled by
# :class:`~pcapkit.protocols.internet.ipv6_ext.IPv6_Ext`, which also
# carries ``next`` (possibly :data:`None`, on an overrun -- see its
# module docstring). Every IANA extension header code without a
# dedicated parser -- ``253`` and ``254`` today, and whatever IANA
# assigns next -- has no generic fallback either (see
# :attr:`__generic_ext_codes__`'s docstring for why), so
# :meth:`_import_next_layer` returns a plain
# :class:`~pcapkit.protocols.misc.raw.Raw`, whose info carries no
# ``next`` at all. Reading ``info.next`` on that unconditionally
# would raise ``AttributeError`` and let a further-out
# :func:`~pcapkit.utilities.decorators.beholder` degrade the *whole*
# packet. Stopping here instead keeps this layer's own fields (still
# recorded above, in ``self._exthdr`` and in the packet dict) and
# reports no further next header, exactly like the overrun case.
# This is a *structural* check, not a fixed set of codes: every
# dedicated parser and
# :class:`~pcapkit.protocols.internet.ipv6_ext.IPv6_Ext` carry
# ``next`` (possibly :data:`None`, on an overrun) -- Shim6 has no
# dedicated parser but is registered to ``IPv6_Ext`` directly --
# while every other code with no dedicated parser -- ``253``,
# ``254``, or whatever IANA assigns next -- resolves to plain
# :class:`~pcapkit.protocols.misc.raw.Raw`, which does not (see
# :attr:`__generic_ext_codes__`). Reading
# ``info.next`` on that would raise ``AttributeError`` and let a
# further-out :func:`~pcapkit.utilities.decorators.beholder` degrade
# the *whole* packet. Stopping here instead keeps this layer's own
# fields (recorded above, in ``self._exthdr`` and in the packet
# dict) and reports no further next header, as on an overrun.
#
# This has to run -- and, on a hit, has to set ``proto`` --
# *before* the fragment-header special case below: IPv6-Frag
# always carries a real ``next`` (the ``hasattr`` check above
# never actually fires for it), and that ``next`` is the real
# always carries a real ``next`` (this ``hasattr`` check never
# fires for it), and that ``next`` is the real
# transport layer's code, which the fragment branch's own
# ``break`` must leave in ``proto`` for the final
# ``super()._decode_next_layer`` call below the loop to dispatch
Expand Down
33 changes: 17 additions & 16 deletions pcapkit/protocols/internet/ipv6_opts.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
Octets Bits Name Description
======= ========= =================== =================================
0 0 ``opt.next`` Next Header
1 8 ``opt.length`` Header Extensive Length
1 8 ``opt.length`` Header Extension Length
2 16 ``opt.options`` Options
======= ========= =================== =================================

Expand Down Expand Up @@ -193,10 +193,10 @@ class IPv6_Opts(IPv6_Ext[Data_IPv6_Opts, Schema_IPv6_Opts],

#: DefaultDict[Enum_Option, str | tuple[OptionParser, OptionConstructor]]: Option
#: code to method mapping, c.f. :meth:`_read_ipv6_opts` and/or
#: :meth:`_make_ipv6_opts`. Method names are expected to be referred to the
#: class by ``_read_opt_${name}`` and/or ``_make_opt_${name}``, and if such
#: name not found, the value should then be a method that can parse the
#: option by itself.
#: :meth:`_make_ipv6_opts`. A :obj:`str` value names the methods
#: ``_read_opt_${name}`` and ``_make_opt_${name}``, falling back to
#: ``_read_opt_none`` / ``_make_opt_none`` when they do not exist; any other
#: value is a ``(parser, constructor)`` pair of callables.
__option__ = collections.defaultdict(
lambda: 'none',
{
Expand Down Expand Up @@ -334,7 +334,7 @@ def make(self,
next_default: Default value of next header type.
next_namespace: Namespace of next header type.
next_reversed: If the namespace of next header type is reversed.
options: Hop-by-Hop Options.
options: Destination Options.
payload: Payload of current protocol.
**kwargs: Arbitrary keyword arguments.

Expand Down Expand Up @@ -469,7 +469,7 @@ def _ipv6_opts_option_length(schema_len: 'int') -> 'int':
whole option, which is what every ``_read_opt_*`` below reports back
as the parsed option's own ``.length``, is two octets more. This is
the ``+2``/``-2`` mismatch between the two (see ``Data_PadOption.length``
vs. ``Schema_PadOption.length`` below). The read-side half lives in this
vs. ``Schema_PadOption.len`` below). The read-side half lives in this
one helper so the arithmetic has a single home. Do NOT drop the
``+ 2``.

Expand Down Expand Up @@ -504,7 +504,8 @@ def _read_ipv6_opts(self, length: 'int') -> 'Option':
Extracted IPv6-Opts options

Raises:
ProtocolError: If the threshold is **NOT** matching.
ProtocolError: If the parsed options do not total exactly ``length``
octets.

"""
counter = 0 # length of read options
Expand Down Expand Up @@ -646,7 +647,7 @@ def _read_opt_tun(self, schema: 'Schema_TunnelEncapsulationLimitOption', *, opti
Parsed option data.

Raises:
ProtocolError: If ``ipv6_opts.tun.length`` is **NOT** ``1``.
ProtocolError: If ``schema.len`` is **NOT** ``1``.

"""
if schema.len != 1:
Expand Down Expand Up @@ -680,7 +681,7 @@ def _read_opt_ra(self, schema: 'Schema_RouterAlertOption', *, options: 'Option')
Parsed option data.

Raises:
ProtocolError: If ``ipv6_opts.tun.length`` is **NOT** ``2``.
ProtocolError: If ``schema.len`` is **NOT** ``2``.

"""
if schema.len != 2:
Expand Down Expand Up @@ -857,7 +858,7 @@ def _read_opt_pdm(self, schema: 'Schema_PDMOption', *, options: 'Option') -> 'Da
Parsed option data.

Raises:
ProtocolError: If ``ipv6_opts.pdm.length`` is **NOT** ``10``.
ProtocolError: If ``schema.len`` is **NOT** ``10``.

"""
if schema.len != 10:
Expand Down Expand Up @@ -979,7 +980,7 @@ def _read_opt_rpl(self, schema: 'Schema_RPLOption', *, options: 'Option') -> 'Da
Parsed option data.

Raises:
ProtocolError: If ``ipv6_opts.rpl.length`` is **NOT** ``4``.
ProtocolError: If ``schema.len`` is **NOT** ``4``.

"""
if schema.len != 4:
Expand Down Expand Up @@ -1157,7 +1158,7 @@ def _read_opt_jumbo(self, schema: 'Schema_JumboPayloadOption', *, options: 'Opti
Parsed option data.

Raises:
ProtocolError: If ``ipv6_opts.jumbo.length`` is **NOT** ``4``.
ProtocolError: If ``schema.len`` is **NOT** ``4``.

"""
if schema.len != 4:
Expand Down Expand Up @@ -1201,7 +1202,7 @@ def _read_opt_home(self, schema: 'Schema_HomeAddressOption', *, options: 'Option
Parsed option data.

Raises:
ProtocolError: If ``ipv6_opts.jumbo.length`` is **NOT** ``16``.
ProtocolError: If ``schema.len`` is **NOT** ``16``.

"""
if schema.len != 16:
Expand Down Expand Up @@ -1239,7 +1240,7 @@ def _read_opt_ip_dff(self, schema: 'Schema_IPDFFOption', *, options: 'Option') -
Parsed option data.

Raises:
ProtocolError: If ``ipv6_opts.ip_dff.length`` is **NOT** ``2``.
ProtocolError: If ``schema.len`` is **NOT** ``2``.

"""
if schema.len != 2:
Expand Down Expand Up @@ -1279,7 +1280,7 @@ def _make_pad_options(self, offset: 'int') -> 'tuple[list[Schema_PadOption], int
option to 8 octets on its own leaves the options area a multiple of
8 octets long, whereas :meth:`read` sizes it as
``hdr_ext_len * 8 + 6`` -- six short of a multiple of 8 -- so the
constructed header declared 6 octets more than it actually carried.
header would declare 6 octets more than it carried.

A ``PadN`` option spends two octets on its own type and
``Opt Data Len`` fields before any padding data, so occupying
Expand Down
Loading
Loading