diff --git a/docs/source/pcapkit/protocols/link/l2tp.rst b/docs/source/pcapkit/protocols/link/l2tp.rst index 05d5ec71ae..bfee94cd44 100644 --- a/docs/source/pcapkit/protocols/link/l2tp.rst +++ b/docs/source/pcapkit/protocols/link/l2tp.rst @@ -26,6 +26,16 @@ versions do not share one. Only the first 16-bit word, with a version nibble at bits 12-15, is common to all of them; a base that parsed further would assume one version's layout for every version. +.. autoclass:: pcapkit.protocols.link.l2tp.L2TP + :no-members: + :show-inheritance: + + .. autoproperty:: info_name + + .. automethod:: id + + .. automethod:: __index__ + Versions Not Implemented ------------------------ @@ -51,8 +61,8 @@ since callers take element zero as canonical. Version Dispatch ---------------- -Nothing dispatches on the version nibble, because only one version exists. A -second would follow the precedent of +Nothing dispatches on the version nibble, because L2TPv2 is the only version +implemented. Another would follow the precedent of :class:`~pcapkit.protocols.application.http.HTTP`, which reads a version and delegates to a per-version class. L2TP is the easier case: :meth:`HTTP._guess_version ` @@ -61,16 +71,6 @@ line, else trial-parsing each candidate -- because HTTP's wire format carries no version field, whereas L2TP states its version explicitly in those four bits. A deterministic switch on ``Ver`` is enough, and no new registry is needed. -.. autoclass:: pcapkit.protocols.link.l2tp.L2TP - :no-members: - :show-inheritance: - - .. autoproperty:: info_name - - .. automethod:: id - - .. automethod:: __index__ - .. rubric:: Footnotes .. [*] https://en.wikipedia.org/wiki/Layer_2_Tunneling_Protocol diff --git a/docs/source/pcapkit/protocols/link/l2tpv2.rst b/docs/source/pcapkit/protocols/link/l2tpv2.rst index 94f7d380a3..12cb236f1b 100644 --- a/docs/source/pcapkit/protocols/link/l2tpv2.rst +++ b/docs/source/pcapkit/protocols/link/l2tpv2.rst @@ -22,17 +22,17 @@ reading ``2``. It is dispatched from ------- ----- --------------------- ------------------------------------------ 0 1 ``l2tp.flags.len`` Length ------- ----- --------------------- ------------------------------------------ - 0 2 Reserved (must be zero ``x00``) + 0 2 Reserved (must be zero) ------- ----- --------------------- ------------------------------------------ 0 4 ``l2tp.flags.seq`` Sequence ------- ----- --------------------- ------------------------------------------ - 0 5 Reserved (must be zero ``x00``) + 0 5 Reserved (must be zero) ------- ----- --------------------- ------------------------------------------ 0 6 ``l2tp.flags.offset`` Offset ------- ----- --------------------- ------------------------------------------ 0 7 ``l2tp.flags.prio`` Priority ------- ----- --------------------- ------------------------------------------ - 1 8 Reserved (must be zero ``x00``) + 1 8 Reserved (must be zero) ------- ----- --------------------- ------------------------------------------ 1 12 ``l2tp.version`` Version (``2``) ------- ----- --------------------- ------------------------------------------ @@ -57,11 +57,6 @@ reading ``2``. It is dispatched from whichever version was on the wire. The version is reported by :attr:`~pcapkit.protocols.link.l2tpv2.L2TPv2.alias` instead. - IANA protocol number 115 (``L2TP``) is deliberately left unbound: it - references :rfc:`3931`, i.e. **L2TPv3**, whose headers have a different shape, - so the binding waits on an ``L2TPv3`` class rather than this one. See - :mod:`pcapkit.protocols.link.l2tp`. - .. autoclass:: pcapkit.protocols.link.l2tpv2.L2TPv2 :no-members: :show-inheritance: @@ -96,6 +91,11 @@ Type Stubs :members: :show-inheritance: +Auxiliary Functions +~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: pcapkit.protocols.schema.link.l2tp.l2tp_payload_length + Data Models ----------- diff --git a/docs/source/pcapkit/protocols/link/link.rst b/docs/source/pcapkit/protocols/link/link.rst index 791dfb6ac4..cab20c58f7 100644 --- a/docs/source/pcapkit/protocols/link/link.rst +++ b/docs/source/pcapkit/protocols/link/link.rst @@ -5,8 +5,8 @@ Base Protocol :mod:`pcapkit.protocols.link.link` contains :class:`~pcapkit.protocols.link.link.Link`, which is a base class for link layer protocols, e.g. :class:`~pcapkit.protocols.link.arp.ARP`/InARP, -:class:`~pcapkit.protocols.link.ethernet.Ethernet`, :class:`~pcapkit.protocols.link.l2tp.L2TP`, -:class:`~pcapkit.protocols.link.vlan.VLAN` and etc. +:class:`~pcapkit.protocols.link.ethernet.Ethernet`, :class:`~pcapkit.protocols.link.l2tp.L2TP` +and :class:`~pcapkit.protocols.link.vlan.VLAN`. .. autoclass:: pcapkit.protocols.link.link.Link :no-members: diff --git a/docs/source/pcapkit/protocols/link/s_tag.rst b/docs/source/pcapkit/protocols/link/s_tag.rst index f77f56f2c7..409edab009 100644 --- a/docs/source/pcapkit/protocols/link/s_tag.rst +++ b/docs/source/pcapkit/protocols/link/s_tag.rst @@ -19,11 +19,6 @@ It has a module of its own, apart from *different* registry indices, ``0x88A8`` against ``0x8100``; see :mod:`pcapkit.protocols.link.vlan` for the rule. -.. note:: - - 802.1ad is part of IEEE 802.1Q-2011. The ``802.1ad`` name is kept because it - is what the provider-bridging tag is universally called. - .. autoclass:: pcapkit.protocols.link.s_tag.S_Tag :no-members: :show-inheritance: diff --git a/docs/source/pcapkit/protocols/transport/tcp.rst b/docs/source/pcapkit/protocols/transport/tcp.rst index dd64ffc387..3096499d91 100644 --- a/docs/source/pcapkit/protocols/transport/tcp.rst +++ b/docs/source/pcapkit/protocols/transport/tcp.rst @@ -318,11 +318,6 @@ Type Stubs :members: :show-inheritance: -.. autoclass:: pcapkit.protocols.schema.transport.tcp.Flags - :no-index: - :members: - :show-inheritance: - .. autoclass:: pcapkit.protocols.schema.transport.tcp.POCProfile :members: :show-inheritance: diff --git a/docs/source/pcapkit/protocols/transport/transport.rst b/docs/source/pcapkit/protocols/transport/transport.rst index 47cdbaaf56..51e1182717 100644 --- a/docs/source/pcapkit/protocols/transport/transport.rst +++ b/docs/source/pcapkit/protocols/transport/transport.rst @@ -5,7 +5,7 @@ Base Protocol :mod:`pcapkit.protocols.transport.transport` contains :class:`~pcapkit.protocols.transport.transport.Transport`, -which is a base class for transport layer protocols, eg. +which is a base class for transport layer protocols, e.g. :class:`~pcapkit.protocols.transport.tcp.TCP`, :class:`~pcapkit.protocols.transport.udp.UDP` and :class:`~pcapkit.protocols.transport.sctp.SCTP`. diff --git a/pcapkit/protocols/data/transport/tcp.py b/pcapkit/protocols/data/transport/tcp.py index 9b172ee952..922d18af7f 100644 --- a/pcapkit/protocols/data/transport/tcp.py +++ b/pcapkit/protocols/data/transport/tcp.py @@ -364,7 +364,7 @@ class UserTimeout(Option): #: User timeout. timeout: 'timedelta' - #: Granularity (``G``): the timeout is in minutes if set, in seconds if not. + #: Granularity (``G``). The timeout is in minutes if set, in seconds if not. granularity: 'bool' if TYPE_CHECKING: @@ -427,8 +427,8 @@ class MPTCPCapableFlag(Data): deny_join: 'bool' #: Reserved flags (``D`` to ``G``). reserved: 'int' - #: HMAC algorithm flag (``H``): HMAC-SHA1 under version 0 (:rfc:`6824`), - #: HMAC-SHA256 under version 1 (:rfc:`8684`). + #: HMAC algorithm flag (``H``), selecting HMAC-SHA1 under version 0 + #: (:rfc:`6824`) and HMAC-SHA256 under version 1 (:rfc:`8684`). hsa: 'bool' if TYPE_CHECKING: diff --git a/pcapkit/protocols/link/__init__.py b/pcapkit/protocols/link/__init__.py index b1391e6a1e..6f579788b1 100644 --- a/pcapkit/protocols/link/__init__.py +++ b/pcapkit/protocols/link/__init__.py @@ -5,8 +5,8 @@ .. module:: pcapkit.protocols.link -:mod:`pcapkit.protocols.link` is collection of all protocols in -link layer, with detailed implementation and methods. +:mod:`pcapkit.protocols.link` is a collection of all protocols in +the link layer, with detailed implementation and methods. """ # TODO: Implements DSL, EAPOL, FDDI, ISDN, NDP, PPP. diff --git a/pcapkit/protocols/link/l2tp.py b/pcapkit/protocols/link/l2tp.py index fa25bb45d5..3cb1746f98 100644 --- a/pcapkit/protocols/link/l2tp.py +++ b/pcapkit/protocols/link/l2tp.py @@ -68,15 +68,16 @@ Selecting a Version ------------------- -Nothing *dispatches* on the version nibble, because only one version exists --- but :meth:`L2TPv2.read ` does -**check** it, and refuses anything other than ``2``. That is the half of the -mechanism which is useful with one version implemented: it keeps v3 traffic on -port 1701 (:rfc:`3931` §4.1.2 shares the port, so this is ordinary capture +Nothing *dispatches* on the version nibble, because L2TPv2 is the only version +implemented -- but +:meth:`L2TPv2.read ` does **check** +it, and refuses anything other than ``2``. That is the half of the mechanism +which is useful with no other version implemented: it keeps v3 traffic on port +1701 (:rfc:`3931` §4.1.2 shares the port, so this is ordinary capture traffic rather than a corner case) from being reported as v2 with a tunnel and session ID read out of v3's Control Connection ID. -When a second version lands, the remaining half -- delegation rather than refusal +When another version lands, the remaining half -- delegation rather than refusal -- already has a precedent in :class:`~pcapkit.protocols.application.http.HTTP`, which reads a version and delegates to a per-version class. L2TP is the easier case: HTTP has to diff --git a/pcapkit/protocols/link/l2tpv2.py b/pcapkit/protocols/link/l2tpv2.py index 120c8b3906..4b35073412 100644 --- a/pcapkit/protocols/link/l2tpv2.py +++ b/pcapkit/protocols/link/l2tpv2.py @@ -21,17 +21,17 @@ ------- ----- --------------------- ------------------------------------------ 0 1 ``l2tp.flags.len`` Length ------- ----- --------------------- ------------------------------------------ - 0 2 Reserved (must be zero ``x00``) + 0 2 Reserved (must be zero) ------- ----- --------------------- ------------------------------------------ 0 4 ``l2tp.flags.seq`` Sequence ------- ----- --------------------- ------------------------------------------ - 0 5 Reserved (must be zero ``x00``) + 0 5 Reserved (must be zero) ------- ----- --------------------- ------------------------------------------ 0 6 ``l2tp.flags.offset`` Offset ------- ----- --------------------- ------------------------------------------ 0 7 ``l2tp.flags.prio`` Priority ------- ----- --------------------- ------------------------------------------ - 1 8 Reserved (must be zero ``x00``) + 1 8 Reserved (must be zero) ------- ----- --------------------- ------------------------------------------ 1 12 ``l2tp.version`` Version (``2``) ------- ----- --------------------- ------------------------------------------ @@ -106,8 +106,8 @@ class L2TPv2(L2TP[Data_L2TP, Schema_L2TP], ``L2TPv3`` class rather than on this one. Binding *this* class there does not work (:issue:`548`): over IP the v3 session header carries no version nibble at all, so this class cannot recognise that the datagram - is not its own. See - :class:`~pcapkit.protocols.link.l2tp.L2TP` for the measurement. + is not its own. See :mod:`pcapkit.protocols.link.l2tp` for the + versions not implemented. The class subclasses :class:`~pcapkit.protocols.link.link.Link` and so reports ``layer == 'Link'`` even though it is carried inside UDP: a diff --git a/pcapkit/protocols/link/link.py b/pcapkit/protocols/link/link.py index 99d9e0c627..3ddf5c6815 100644 --- a/pcapkit/protocols/link/link.py +++ b/pcapkit/protocols/link/link.py @@ -11,9 +11,8 @@ :class:`~pcapkit.protocols.link.arp.ARP`/:class:`~pcapkit.protocols.link.arp.InARP`, :class:`~pcapkit.protocols.link.ethernet.Ethernet`, :class:`~pcapkit.protocols.link.l2tp.L2TP`, -:class:`~pcapkit.protocols.link.loopback.Loopback`, -:class:`~pcapkit.protocols.link.vlan.VLAN` -and etc. +:class:`~pcapkit.protocols.link.loopback.Loopback` and +:class:`~pcapkit.protocols.link.vlan.VLAN`. """ import collections @@ -117,8 +116,8 @@ def register(cls, code: 'Enum_EtherType', protocol: 'ModuleDescriptor[ProtocolBa r"""Register a new protocol class. Notes: - The full qualified class name of the new protocol class - should be as ``{protocol.module}.{protocol.name}``. + The fully qualified class name of the new protocol class + should be ``{protocol.module}.{protocol.name}``. Arguments: code: protocol code as in :class:`~pcapkit.const.reg.ethertype.EtherType` @@ -137,7 +136,7 @@ def register(cls, code: 'Enum_EtherType', protocol: 'ModuleDescriptor[ProtocolBa :meth:`ProtocolBase.register ` for the guard this shares with ``register_protocol``, and for the - shipped descriptor a restore puts back (:issue:`1504`). + shipped descriptor that a restore puts back (:issue:`1504`). .. versionchanged:: 1.3.0 diff --git a/pcapkit/protocols/schema/link/arp.py b/pcapkit/protocols/schema/link/arp.py index f3f1ac7629..a166e40449 100644 --- a/pcapkit/protocols/schema/link/arp.py +++ b/pcapkit/protocols/schema/link/arp.py @@ -22,15 +22,25 @@ class ARP(Schema): """Header schema for ARP packet.""" + #: Hardware type. htype: 'Enum_Hardware' = EnumField(length=2, namespace=Enum_Hardware) + #: Protocol type. ptype: 'Enum_EtherType' = EnumField(length=2, namespace=Enum_EtherType) + #: Hardware address length. hlen: 'int' = UInt8Field() + #: Protocol address length. plen: 'int' = UInt8Field() + #: Operation code. oper: 'Enum_Operation' = EnumField(length=2, namespace=Enum_Operation) + #: Sender hardware address. sha: 'bytes' = BytesField(length=lambda pkt: pkt['hlen']) + #: Sender protocol address. spa: 'bytes' = BytesField(length=lambda pkt: pkt['plen']) + #: Target hardware address. tha: 'bytes' = BytesField(length=lambda pkt: pkt['hlen']) + #: Target protocol address. tpa: 'bytes' = BytesField(length=lambda pkt: pkt['plen']) + #: Payload. payload: 'bytes' = PayloadField() if TYPE_CHECKING: diff --git a/pcapkit/protocols/schema/link/l2tp.py b/pcapkit/protocols/schema/link/l2tp.py index 20b1327dd3..9ea029e0ef 100644 --- a/pcapkit/protocols/schema/link/l2tp.py +++ b/pcapkit/protocols/schema/link/l2tp.py @@ -23,15 +23,15 @@ class FlagsType(TypedDict): """Flags of L2TP packet.""" - #: Type of L2TP packet. + #: Type flag (``T``). type: int - #: Length of L2TP packet. + #: Length flag (``L``). len: int - #: Sequence number of L2TP packet. + #: Sequence flag (``S``). seq: int - #: Offset size of L2TP packet. + #: Offset flag (``O``). offset: int - #: Priority of L2TP packet. + #: Priority flag (``P``). prio: int #: Reserved bits 2 and 3 of L2TP packet. reserved_1: int diff --git a/pcapkit/protocols/schema/transport/sctp.py b/pcapkit/protocols/schema/transport/sctp.py index 6e9ce825ae..dc624614b0 100644 --- a/pcapkit/protocols/schema/transport/sctp.py +++ b/pcapkit/protocols/schema/transport/sctp.py @@ -181,8 +181,8 @@ def nested_length(base: 'int') -> 'Callable[[dict[str, Any]], int]': The chunks that carry a nested list -- INIT, INIT ACK, HEARTBEAT, HEARTBEAT ACK, ABORT and ERROR -- deliberately have no separate :class:`~pcapkit.corekit.fields.strings.PaddingField`, and fold the chunk's - trailing padding into the nested list's own span instead. There are two - reasons, and both matter: + trailing padding into the nested list's own span instead, which parsing and + construction each need for a reason of their own: * On parsing, a sender is allowed by :rfc:`9260#section-3.2` to leave the final parameter's padding out of the chunk length, so the padding has to diff --git a/pcapkit/protocols/schema/transport/tcp.py b/pcapkit/protocols/schema/transport/tcp.py index 9623aeeb5f..b0f8e717a1 100644 --- a/pcapkit/protocols/schema/transport/tcp.py +++ b/pcapkit/protocols/schema/transport/tcp.py @@ -82,7 +82,7 @@ class Flags(TypedDict): cwr: int #: ECN-Echo. ece: int - #: Urgent pointer. + #: Urgent. urg: int #: Acknowledgment. ack: int @@ -234,6 +234,10 @@ def mptcp_data_selector(pkt: 'dict[str, Any]') -> 'Field': :class:`~pcapkit.protocols.schema.transport.tcp.MPTCP` subclass instance. + Raises: + FieldError: If an MP_JOIN option is carried by a segment with + neither SYN nor ACK set. + """ subtype = Enum_MPTCPOption.get(pkt['test']['subtype']) pkt['test']['subtype'] = subtype @@ -258,10 +262,13 @@ def mptcp_add_address_selector(pkt: 'dict[str, Any]') -> 'Field': pkt: Packet data. Returns: - * If the option length is one of the IPv4 lengths, a - :class:`~pcapkit.corekit.fields.ipaddress.IPv4AddressField` instance. - * If the option length is one of the IPv6 lengths, a - :class:`~pcapkit.corekit.fields.ipaddress.IPv6AddressField` instance. + An :class:`~pcapkit.corekit.fields.ipaddress.IPv4AddressField` instance + if the option length is one of the IPv4 lengths, or an + :class:`~pcapkit.corekit.fields.ipaddress.IPv6AddressField` instance if + it is one of the IPv6 lengths. + + Raises: + FieldError: If the option length is neither. Note: :rfc:`8684` section 3.4.1 figure 12 has no IP version field: the @@ -313,12 +320,11 @@ def mptcp_dss_ack_selector(pkt: 'dict[str, Any]') -> 'Field': pkt: Packet data. Returns: - * If ``A`` is clear, a :class:`~pcapkit.corekit.fields.misc.NoValueField` - instance -- the field is absent from the wire. - * If ``A`` is set and ``a`` is set, a - :class:`~pcapkit.corekit.fields.numbers.UInt64Field` instance. - * If ``A`` is set and ``a`` is clear, a - :class:`~pcapkit.corekit.fields.numbers.UInt32Field` instance. + A :class:`~pcapkit.corekit.fields.misc.NoValueField` instance if ``A`` + is clear, since the field is then absent from the wire; otherwise a + :class:`~pcapkit.corekit.fields.numbers.UInt64Field` instance if ``a`` + is set, or a :class:`~pcapkit.corekit.fields.numbers.UInt32Field` + instance if it is clear. Note: This is a :class:`~pcapkit.corekit.fields.misc.SwitchField` selector @@ -367,12 +373,11 @@ def mptcp_dss_dsn_selector(pkt: 'dict[str, Any]') -> 'Field': pkt: Packet data. Returns: - * If ``M`` is clear, a :class:`~pcapkit.corekit.fields.misc.NoValueField` - instance -- the field is absent from the wire. - * If ``M`` is set and ``m`` is set, a - :class:`~pcapkit.corekit.fields.numbers.UInt64Field` instance. - * If ``M`` is set and ``m`` is clear, a - :class:`~pcapkit.corekit.fields.numbers.UInt32Field` instance. + A :class:`~pcapkit.corekit.fields.misc.NoValueField` instance if ``M`` + is clear, since the field is then absent from the wire; otherwise a + :class:`~pcapkit.corekit.fields.numbers.UInt64Field` instance if ``m`` + is set, or a :class:`~pcapkit.corekit.fields.numbers.UInt32Field` + instance if it is clear. Note: Identical in shape to :func:`mptcp_dss_ack_selector`; see that function's @@ -774,9 +779,9 @@ class MPTCP(EnumSchema[Enum_MPTCPOption]): #: MPTCP length. length: 'int' = UInt8Field() - # NOTE: ``subtype`` stays an annotation rather than becoming a third real - # field alongside ``kind``/``length`` above -- deliberately, and the - # difference from those two is why. ``kind`` and ``length`` are each the + # NOTE: ``subtype`` stays an annotation rather than becoming a real field + # alongside ``kind``/``length`` above -- deliberately, and the difference + # from those fields is why. ``kind`` and ``length`` are each the # *sole* source of their own octet: nothing else in the schema packs them, # so declaring them as real fields was the only way to get them onto the # wire at all. ``subtype`` is not like that: every concrete subclass diff --git a/pcapkit/protocols/transport/__init__.py b/pcapkit/protocols/transport/__init__.py index a23193e85a..85c9989fa4 100644 --- a/pcapkit/protocols/transport/__init__.py +++ b/pcapkit/protocols/transport/__init__.py @@ -5,8 +5,8 @@ .. module:: pcapkit.protocols.transport -:mod:`pcapkit.protocols.transport` is collection of all protocols in -transport layer, with detailed implementation and methods. +:mod:`pcapkit.protocols.transport` is a collection of all protocols in +the transport layer, with detailed implementation and methods. """ # TODO: Implements DCCP, RSVP. diff --git a/pcapkit/protocols/transport/sctp.py b/pcapkit/protocols/transport/sctp.py index a3fee39e0d..1cddbd1b62 100644 --- a/pcapkit/protocols/transport/sctp.py +++ b/pcapkit/protocols/transport/sctp.py @@ -321,13 +321,14 @@ class SCTP(Transport[Data_SCTP, Schema_SCTP], therefore keyed by PPID rather than by port number, and is populated through :meth:`SCTP.register`:: - >>> SCTP.register(Enum_PayloadProtocolIdentifier.PayloadProtocolIdentifier_3GPP_NG_Application_Protocol, NGAP) + >>> from pcapkit.const.sctp.payload_protocol_identifier import PayloadProtocolIdentifier + >>> SCTP.register(PayloadProtocolIdentifier.PayloadProtocolIdentifier_3GPP_NG_Application_Protocol, NGAP) >>> SCTP.register(60, NGAP) # equivalent, PPID given as a plain integer - Two PPIDs are registered by default, both to - :class:`~pcapkit.protocols.application.ngap.NGAP`: 60 - (``NG_Application_Protocol``) and 66 (``NGAP_over_DTLS_over_SCTP``). Every - other PPID resolves to :class:`~pcapkit.protocols.misc.raw.Raw`. + By default, PPIDs 60 (``NG_Application_Protocol``) and 66 + (``NGAP_over_DTLS_over_SCTP``) are registered to + :class:`~pcapkit.protocols.application.ngap.NGAP`, and every other PPID + resolves to :class:`~pcapkit.protocols.misc.raw.Raw`. Only PPID 60 actually decodes. A PPID 66 payload is an NGAP PDU wrapped in a DTLS record, and :mod:`pcapkit` has no DTLS implementation, so the bytes @@ -425,7 +426,7 @@ class SCTP(Transport[Data_SCTP, Schema_SCTP], - :meth:`~pcapkit.protocols.transport.sctp.SCTP._read_param_addrtypes` - :meth:`~pcapkit.protocols.transport.sctp.SCTP._make_param_addrtypes` - This class supports parsing of all thirteen error causes defined + This class supports parsing of every error cause defined by :rfc:`9260#section-3.3.10`, which are directly mapped to the :class:`pcapkit.const.sctp.cause_code.CauseCode` enumeration; see :attr:`self.__cause__ ` for the mapping. Unknown cause @@ -493,8 +494,8 @@ class SCTP(Transport[Data_SCTP, Schema_SCTP], }, ) # type: DefaultDict[int, str | tuple[ChunkParser, ChunkConstructor]] - #: DefaultDict[Enum_Parameter, str | tuple[ParameterParser, ParameterConstructor]]: - #: Chunk parameter type to method mapping, c.f. :meth:`_read_sctp_parameters` + #: DefaultDict[Enum_Parameter, str | tuple[ParameterParser, ParameterConstructor]]: Chunk + #: parameter type to method mapping, c.f. :meth:`_read_sctp_parameters` #: and :meth:`_make_sctp_parameters`. Method names are expected to be #: referred to the class by ``_read_param_${name}`` and #: ``_make_param_${name}``, and if such name not found, the value should then @@ -709,8 +710,8 @@ def register(cls, code: 'Enum_PayloadProtocolIdentifier | int', protocol: 'Modul r"""Register a new protocol class for a payload protocol identifier. Notes: - The full qualified class name of the new protocol class - should be as ``{protocol.module}.{protocol.name}``. + The fully qualified class name of the new protocol class + should be ``{protocol.module}.{protocol.name}``. Arguments: code: payload protocol identifier (PPID), as in @@ -762,9 +763,9 @@ def register_chunk(cls, code: 'Enum_Chunk', meth: 'str | tuple[ChunkParser, Chun Warns: RegistryWarning: If a different entry is already registered for ``code``; it is overwritten. Handing back the entry already - stored, or one equal to it, is a silent no-op. The entry - ``code`` shipped with is accepted like any other, so an - override can be undone (:issue:`1362`, :issue:`1363`). + stored, or one equal to it, is a silent no-op. Re-registering + the entry ``code`` shipped with is accepted like any other, + so an override can be undone (:issue:`1362`, :issue:`1363`). """ incumbent = cls.__chunk__.get(code) @@ -785,9 +786,9 @@ def register_parameter(cls, code: 'Enum_Parameter', meth: 'str | tuple[Parameter Warns: RegistryWarning: If a different entry is already registered for ``code``; it is overwritten. Handing back the entry already - stored, or one equal to it, is a silent no-op. The entry - ``code`` shipped with is accepted like any other, so an - override can be undone (:issue:`1362`, :issue:`1363`). + stored, or one equal to it, is a silent no-op. Re-registering + the entry ``code`` shipped with is accepted like any other, + so an override can be undone (:issue:`1362`, :issue:`1363`). """ incumbent = cls.__parameter__.get(code) @@ -808,9 +809,9 @@ def register_cause(cls, code: 'Enum_CauseCode', meth: 'str | tuple[CauseParser, Warns: RegistryWarning: If a different entry is already registered for ``code``; it is overwritten. Handing back the entry already - stored, or one equal to it, is a silent no-op. The entry - ``code`` shipped with is accepted like any other, so an - override can be undone (:issue:`1362`, :issue:`1363`). + stored, or one equal to it, is a silent no-op. Re-registering + the entry ``code`` shipped with is accepted like any other, + so an override can be undone (:issue:`1362`, :issue:`1363`). """ incumbent = cls.__cause__.get(code) diff --git a/pcapkit/protocols/transport/tcp.py b/pcapkit/protocols/transport/tcp.py index b7e2b6437e..311516e5fa 100644 --- a/pcapkit/protocols/transport/tcp.py +++ b/pcapkit/protocols/transport/tcp.py @@ -310,7 +310,7 @@ class TCP(Transport[Data_TCP, Schema_TCP], :obj:`int` (:issue:`616`). * A constructed MPTCP option sets its ``subtype`` (:issue:`566`), and MP_CAPABLE is written at the :rfc:`8684` lengths (:issue:`567`) and - read at all five of them (:issue:`1042`). + read at each of them (:issue:`1042`). * A rebuild keeps what was parsed: the option order and padding (:issue:`1151`), the reserved bits of the header and of the POCSP, Quick-Start and MPTCP options (:issue:`1211`, :issue:`1212`), the @@ -352,7 +352,7 @@ class TCP(Transport[Data_TCP, Schema_TCP], # rather than as an HTTP alternate, and traffic there is TLS-wrapped, # which pcapkit does not parse. # - # Both HTTP ports bind the version-dispatching + # The HTTP ports bind the version-dispatching # :class:`pcapkit.protocols.application.http.HTTP` rather than # ``httpv1.HTTP``, because HTTP/1 and HTTP/2 share these ports on the # wire -- RFC 9113 calls the cleartext form ``http`` and keeps it on @@ -609,6 +609,11 @@ def make(self, Returns: Constructed packet data. + .. versionchanged:: 1.5.0 + + An out-of-range ``srcport`` or ``dstport`` raises + :exc:`ValueError` rather than being accepted (:issue:`758`). + """ # NOTE: the connection control flags are resolved *before* the options are # built, because option makers reached from ``_make_tcp_options`` read @@ -686,9 +691,9 @@ def register_option(cls, code: 'Enum_Option', meth: 'str | tuple[OptionParser, O Warns: RegistryWarning: If a different entry is already registered for ``code``; it is overwritten. Handing back the entry already - stored, or one equal to it, is a silent no-op. The entry - ``code`` shipped with is accepted like any other, so an - override can be undone (:issue:`1362`, :issue:`1363`). + stored, or one equal to it, is a silent no-op. Re-registering + the entry ``code`` shipped with is accepted like any other, + so an override can be undone (:issue:`1362`, :issue:`1363`). """ incumbent = cls.__option__.get(code) @@ -709,9 +714,9 @@ def register_mp_option(cls, code: 'Enum_MPTCPOption', meth: 'str | tuple[MPOptio Warns: RegistryWarning: If a different entry is already registered for ``code``; it is overwritten. Handing back the entry already - stored, or one equal to it, is a silent no-op. The entry - ``code`` shipped with is accepted like any other, so an - override can be undone (:issue:`1362`, :issue:`1363`). + stored, or one equal to it, is a silent no-op. Re-registering + the entry ``code`` shipped with is accepted like any other, + so an override can be undone (:issue:`1362`, :issue:`1363`). """ incumbent = cls.__mp_option__.get(code) @@ -789,7 +794,7 @@ def _read_tcp_options(self, size: 'int') -> 'Option': Extracted TCP options. Raises: - ProtocolError: If the threshold is **NOT** matching. + ProtocolError: If the options run past ``size``. """ counter = 0 # length of read option list @@ -821,7 +826,7 @@ def _read_tcp_options(self, size: 'int') -> 'Option': return options def _read_mode_donone(self, schema: 'Schema_UnassignedOption', *, options: 'Option') -> 'Data_UnassignedOption': # pylint: disable=unused-argument - """Read options request no process. + """Read TCP unassigned option. Arguments: schema: parsed option schema @@ -922,7 +927,7 @@ def _read_mode_mss(self, schema: 'Schema_MaximumSegmentSize', *, options: 'Optio return data def _read_mode_ws(self, schema: 'Schema_WindowScale', *, options: 'Option') -> 'Data_WindowScale': # pylint: disable=unused-argument - """Read TCP windows scale option. + """Read TCP window scale option. Structure of TCP window scale option [:rfc:`7323`]: diff --git a/pcapkit/protocols/transport/transport.py b/pcapkit/protocols/transport/transport.py index fdbae0bc40..029ad51e83 100644 --- a/pcapkit/protocols/transport/transport.py +++ b/pcapkit/protocols/transport/transport.py @@ -6,7 +6,7 @@ :mod:`pcapkit.protocols.transport.transport` contains :class:`~pcapkit.protocols.transport.transport.Transport`, -which is a base class for transport layer protocols, eg. +which is a base class for transport layer protocols, e.g. :class:`~pcapkit.protocols.transport.tcp.TCP`, :class:`~pcapkit.protocols.transport.udp.UDP` and :class:`~pcapkit.protocols.transport.sctp.SCTP`. @@ -73,8 +73,8 @@ def register(cls, code: 'int', protocol: 'ModuleDescriptor[ProtocolBase] | Type[ """Register a new protocol class. Notes: - The full qualified class name of the new protocol class - should be as ``{protocol.module}.{protocol.name}``. + The fully qualified class name of the new protocol class + should be ``{protocol.module}.{protocol.name}``. Arguments: code: port number @@ -101,14 +101,15 @@ def register(cls, code: 'int', protocol: 'ModuleDescriptor[ProtocolBase] | Type[ :meth:`ProtocolBase.register ` for the guard this shares with ``register_protocol``, and for the - shipped descriptor a restore puts back (:issue:`1504`). + shipped descriptor that a restore puts back (:issue:`1504`). Note: :class:`~pcapkit.protocols.transport.tcp.TCP` and :class:`~pcapkit.protocols.transport.udp.UDP` each define their own ``__proto__`` rather than sharing one on :class:`Transport`. - ``register_apptype`` reaches this method once per protocol and - inspects two different registries, so it cannot warn spuriously. + ``register_apptype`` reaches this method once per transport + protocol, each time on that protocol's own registry, so it cannot + warn spuriously. .. versionchanged:: 1.3.0 diff --git a/pcapkit/protocols/transport/udp.py b/pcapkit/protocols/transport/udp.py index 6b0e7f60f3..8036772a10 100644 --- a/pcapkit/protocols/transport/udp.py +++ b/pcapkit/protocols/transport/udp.py @@ -65,7 +65,7 @@ class UDP(Transport[Data_UDP, Schema_UDP], - :class:`pcapkit.protocols.application.http.HTTP` Note: - Both HTTP ports resolve to + The HTTP ports resolve to :class:`pcapkit.protocols.application.http.HTTP`, which identifies the version from the payload and delegates. :attr:`TCP.__proto__ ` @@ -99,7 +99,7 @@ class UDP(Transport[Data_UDP, Schema_UDP], # 1701 l2tp l2tp # 8080 http-alt HTTP Alternate (see port 80) # - # Both HTTP entries bind the version-dispatching + # The HTTP entries bind the version-dispatching # :class:`pcapkit.protocols.application.http.HTTP`, as TCP's table # does. c.f. the note in the class docstring. 80: ModuleDescriptor('pcapkit.protocols.application.http', 'HTTP'), @@ -215,6 +215,14 @@ def make(self, Returns: Constructed packet data. + .. versionchanged:: 1.5.0 + + An out-of-range ``srcport`` or ``dstport`` raises + :exc:`ValueError` rather than being accepted (:issue:`758`). + ``total_length`` sits before ``payload`` (:issue:`1155`), so a + caller passing ``payload`` positionally sets ``total_length`` + instead. + """ # NOTE: An explicit ``total_length`` is written as given, so that a # ``from_data`` rebuild of a capture truncated by snaplen keeps the