diff --git a/pcapkit/protocols/application/__init__.py b/pcapkit/protocols/application/__init__.py index c1a1a3adce..ee8ed03432 100644 --- a/pcapkit/protocols/application/__init__.py +++ b/pcapkit/protocols/application/__init__.py @@ -5,8 +5,8 @@ .. module:: pcapkit.protocols.application -:mod:`pcapkit.protocols.application` is collection of all protocols in -application layer, with detailed implementation and methods. +:mod:`pcapkit.protocols.application` is a collection of all protocols in +application layer. """ # TODO: Implements BGP, DHCP, DHCPv6, DNS, IMAP, LDAP, MQTT, @@ -17,7 +17,7 @@ # dependency, c.f. pcapkit.protocols.application.ngap. Importing the module # is free -- ``pycrate`` is imported inside the parse path, not here. -# Base Class for Internet Layer +# Base Class for Application Layer from pcapkit.protocols.application.application import Application # Utility Classes for Protocols @@ -31,7 +31,7 @@ # Base Classes from pcapkit.protocols.application.http import HTTP -# Transport Layer Protocol Numbers +# Application Layer Protocol Numbers from pcapkit.const.reg.apptype import AppType as APPTYPE __all__ = [ diff --git a/pcapkit/protocols/application/application.py b/pcapkit/protocols/application/application.py index b90cadfd50..b6fa721f69 100644 --- a/pcapkit/protocols/application/application.py +++ b/pcapkit/protocols/application/application.py @@ -6,10 +6,9 @@ :mod:`pcapkit.protocols.application.application` contains only :class:`~pcapkit.protocols.application.application.Application`, -which is a base class for application layer protocols, eg. -:class:`HTTP/1.* `, -:class:`HTTP/2 ` -and etc. +which is a base class for application layer protocols, e.g. +:class:`HTTP/1.* ` and +:class:`HTTP/2 `. """ from typing import TYPE_CHECKING, Generic, overload @@ -87,7 +86,7 @@ def __post_init__(self, file: 'Optional[IO[bytes] | bytes]' = None, # ``_decode_next_layer``, which already set the payload and the chain # (basis included); only a protocol that did not gets the empty default if getattr(self, '_next', None) is None: - #: pcapkit.protocols.null.NoPayload: Payload of current instance. + #: pcapkit.protocols.misc.null.NoPayload: Payload of current instance. self._next = NoPayload() #: pcapkit.corekit.protochain.ProtoChain: Protocol chain of current instance. self._protos = ProtoChain(self.__class__, self.alias) diff --git a/pcapkit/protocols/application/ftp.py b/pcapkit/protocols/application/ftp.py index b9832bbffb..4f0deac3d0 100644 --- a/pcapkit/protocols/application/ftp.py +++ b/pcapkit/protocols/application/ftp.py @@ -1,12 +1,15 @@ # -*- coding: utf-8 -*- -"""file transfer protocol +"""FTP - File Transfer Protocol +================================ .. module:: pcapkit.protocols.application.ftp :mod:`pcapkit.protocols.application.ftp` contains -:class:`~pcapkit.protocols.application.ftp.FTP` only, +:class:`~pcapkit.protocols.application.ftp.FTP`, which implements extractor for File Transfer Protocol -(FTP) [*]_. +(FTP) [*]_, and +:class:`~pcapkit.protocols.application.ftp.FTP_DATA` +for its data channel. .. [*] https://en.wikipedia.org/wiki/File_Transfer_Protocol diff --git a/pcapkit/protocols/application/http.py b/pcapkit/protocols/application/http.py index 027eeef92a..f604fc6998 100644 --- a/pcapkit/protocols/application/http.py +++ b/pcapkit/protocols/application/http.py @@ -229,10 +229,11 @@ def _guess_version(self, length: 'int', **kwargs: 'Any') -> 'HTTP': Raises: ProtocolError: If no version in the family accepts the payload, or if - an identified version's own parser refuses it. This is the - *only* exception this method raises for an unparseable payload -- - a candidate failing in any other way, on a version that was not - positively identified, is a fall-through and not an answer. + an identified version's own parser refuses it. Only a + ``ProtocolError`` from a trial parse falls through to the next + version, and the final HTTP/2 trial also absorbs + :exc:`struct.error`; any other exception from either HTTP/1 + arm, or from that HTTP/2 trial, propagates unconverted. Note: Two things this deliberately does **not** decide, because one payload diff --git a/pcapkit/protocols/application/httpv1.py b/pcapkit/protocols/application/httpv1.py index 47c256f6e2..99e21f0093 100644 --- a/pcapkit/protocols/application/httpv1.py +++ b/pcapkit/protocols/application/httpv1.py @@ -65,8 +65,9 @@ # Regular expression to match HTTP status code. Anchored for the same reason as # ``_RE_METHOD``, and it matters more here: this pattern is only a guard, and the # value is taken from ``int(para2)`` on the raw token, so an unanchored prefix -# match let ``200x`` and ``2000`` past the guard and then out of ``int()`` as a -# bare ``ValueError`` -- where ``_read_http_header`` documents ``ProtocolError``. +# match would let ``200x`` and ``2000`` past the guard and then out of ``int()`` +# as a bare ``ValueError`` -- where ``_read_http_header`` documents +# ``ProtocolError``. # :rfc:`9112#section-4` gives ``status-code = 3DIGIT``, exactly three -- the # grammar is in HTTP/1.1 because ``status-code`` is part of its ``status-line`` # production; :rfc:`9110#section-15` covers the code semantics and registry, not @@ -404,9 +405,7 @@ def _read_http_header(self, header: 'bytes') -> 'tuple[Data_Header, OrderedMulti # unfolded here -- the RFC's own remedy -- rather than treated as a field # line of its own. Deprecated, but present in real captures. Treating a # continuation as a field line of its own goes wrong twice: one carrying - # no colon leaves the split below one element long, so ``item[1]`` raises - # :exc:`IndexError`, which is neither a :exc:`ValueError` nor a - # ``ProtocolError`` and escapes ``HTTP._guess_version``'s suppression; + # no colon fails the colon check below, so a valid message is refused; # one that happens to contain a colon parses silently into a spurious # extra field (``X-Long: a`` plus ``b: c``, for a folded ``X-Long: a b``). # Unfolded, a folded message parses to the field it actually carries. @@ -421,10 +420,10 @@ def _read_http_header(self, header: 'bytes') -> 'tuple[Data_Header, OrderedMulti # continuation, because the production is ``obs-fold = OWS CRLF # RWS`` and it is the *whole* obs-fold that is replaced by a # single space -- the OWS before the CRLF belongs to the fold, - # not to the value. Stripping only the continuation left that OWS - # in place, so ``X: a \t\r\n\tb`` unfolded to ``'a \t b'`` - # rather than ``'a b'``: four of five folded/literal pairs - # disagreed, and a HTAB survived where the RFC prescribes SP. + # not to the value. Stripping only the continuation would leave + # that OWS in place, so ``X: a \t\r\n\tb`` would unfold to + # ``'a \t b'`` rather than ``'a b'``, keeping a HTAB where the + # RFC prescribes SP. fields[-1] = fields[-1].rstrip() + b' ' + line.strip() continue fields.append(line) diff --git a/pcapkit/protocols/application/httpv2.py b/pcapkit/protocols/application/httpv2.py index fbaa9a28ee..777e54a763 100644 --- a/pcapkit/protocols/application/httpv2.py +++ b/pcapkit/protocols/application/httpv2.py @@ -145,10 +145,11 @@ class HTTP(HTTPBase[Data_HTTP, Schema_HTTP], ########################################################################## #: DefaultDict[Enum_Frame, str | tuple[FrameParser, FrameConstructor]]: Frame - #: code to method mapping. Method names are expected to be referred to - #: the class by ``_read_http_${name}`` and/or ``_make_http_${name}``, and if - #: such name not found, the value should then be a method that can parse the - #: frame by itself. + #: code to method mapping. A :obj:`str` value names the + #: ``_read_http_${name}`` and ``_make_http_${name}`` methods, which fall back + #: to ``_read_http_none`` and ``_make_http_none`` when not defined, as an + #: unregistered code does; any other value is a ``(parser, constructor)`` + #: pair of callables. __frame__ = collections.defaultdict( lambda: 'none', { @@ -1009,6 +1010,7 @@ def _make_http_data(self, frame: 'Optional[Data_DataFrame]' = None, *, Args: frame: Frame data model. end_stream: End of stream flag. + pad_len: Padding length. data: Frame data. **kwargs: Arbitrary keyword arguments. @@ -1048,6 +1050,7 @@ def _make_http_headers(self, frame: 'Optional[Data_HeadersFrame]' = None, *, frame: Frame data model. end_stream: End of stream flag. end_headers: End of headers flag. + pad_len: Padding length. excl_dep: Exclusive dependency flag. sid_dep: Dependency stream identifier. weight: Priority weight value. @@ -1134,6 +1137,9 @@ def _make_http_rst_stream(self, frame: 'Optional[Data_RSTStreamFrame]' = None, * Args: frame: Frame data model. error: Error code. + error_default: Default value of error code. + error_namespace: Namespace of error code. + error_reversed: Reversed namespace of error code. **kwargs: Arbitrary keyword arguments. Returns: diff --git a/pcapkit/protocols/application/ngap.py b/pcapkit/protocols/application/ngap.py index 4481d0b694..823cbc6d1a 100644 --- a/pcapkit/protocols/application/ngap.py +++ b/pcapkit/protocols/application/ngap.py @@ -6,9 +6,10 @@ .. module:: pcapkit.protocols.application.ngap :mod:`pcapkit.protocols.application.ngap` contains -:class:`~pcapkit.protocols.application.ngap.NGAP` only, +:class:`~pcapkit.protocols.application.ngap.NGAP`, which implements extractor for the NG Application Protocol -(NGAP) [*]_, as specified in 3GPP TS 38.413. +(NGAP) [*]_, as specified in 3GPP TS 38.413, and the enumerations its +decoded fields resolve to. NGAP is the control plane between a 5G RAN node (gNB or ng-eNB) and an AMF. It runs over SCTP and is named by the DATA chunk's *payload protocol diff --git a/pcapkit/protocols/application/ospf.py b/pcapkit/protocols/application/ospf.py index 1906b6833c..bd33a8645c 100644 --- a/pcapkit/protocols/application/ospf.py +++ b/pcapkit/protocols/application/ospf.py @@ -17,19 +17,19 @@ ====== ===== ================== =============================== 0 0 ``ospf.version`` Version Number ------ ----- ------------------ ------------------------------- - 0 0 ``ospf.type`` Type + 1 8 ``ospf.type`` Type ------ ----- ------------------ ------------------------------- - 0 1 ``ospf.len`` Packet Length (header included) + 2 16 ``ospf.len`` Packet Length (header included) ------ ----- ------------------ ------------------------------- - 0 2 ``ospf.router_id`` Router ID + 4 32 ``ospf.router_id`` Router ID ------ ----- ------------------ ------------------------------- - 0 4 ``ospf.area_id`` Area ID + 8 64 ``ospf.area_id`` Area ID ------ ----- ------------------ ------------------------------- - 0 6 ``ospf.chksum`` Checksum + 12 96 ``ospf.chksum`` Checksum ------ ----- ------------------ ------------------------------- - 0 7 ``ospf.autype`` Authentication Type + 14 112 ``ospf.autype`` Authentication Type ------ ----- ------------------ ------------------------------- - 1 8 ``ospf.auth`` Authentication + 16 128 ``ospf.auth`` Authentication ====== ===== ================== =============================== .. [*] https://en.wikipedia.org/wiki/Open_Shortest_Path_First @@ -187,10 +187,8 @@ def read(self, length: 'Optional[int]' = None, **kwargs: 'Any') -> 'Data_OSPF': ]) # OSPF carries no next-protocol field -- the body is LSAs and packet-type # specific fields, which pcapkit does not dissect -- so dispatch on the - # -1 sentinel, as ARP does, rather than on a code read off the wire. - # Passing the remaining length here (as this did) dispatched on it as if - # it were an EtherType, which resolved to Raw only because a length - # rarely collides with a registered one. + # -1 sentinel, as ARP does, rather than on a code read off the wire; + # ``Application._decode_next_layer`` refuses any other value. return self._decode_next_layer(ospf, -1, length - self.length) def make(self, diff --git a/pcapkit/protocols/application/rarp.py b/pcapkit/protocols/application/rarp.py index 3d1ccc0e2d..49dc37dfc4 100644 --- a/pcapkit/protocols/application/rarp.py +++ b/pcapkit/protocols/application/rarp.py @@ -5,8 +5,9 @@ .. module:: pcapkit.protocols.application.rarp :mod:`pcapkit.protocols.application.rarp` contains -:class:`~pcapkit.protocols.application.rarp.RARP` only, -which implements extractor for (Dynamic) Reverse +:class:`~pcapkit.protocols.application.rarp.RARP` and +:class:`~pcapkit.protocols.application.rarp.DRARP`, +which implement extractors for (Dynamic) Reverse Address Resolution Protocol (RARP/DRARP) [*]_, whose structure is described as below: