Root Protocol¶
pcapkit.protocols.protocol contains
Protocol only, which is
an abstract base class for all protocol family, with pre-defined
utility arguments and methods of specified protocols.
- class pcapkit.protocols.protocol.Protocol(file=None, length=None, **kwargs)[source]¶
Bases:
ProtocolBase,Generic[_PT,_ST]Abstract base class for all protocol family.
- property payload: ProtocolBase¶
Payload of current instance.
- property protochain: ProtoChain¶
Protocol chain of current instance.
- property packet: Packet¶
Data_Packet data of the protocol.
Note
The split relies on
self.lengthbeing the length of the octets preceding the payload, and on the payload running from there to the end of the buffer. Both hold for a protocol laid out as a header followed by its payload, which is nearly all of them.A protocol that is not laid out that way has to override this: one whose
lengthcounts something else, or one carrying a trailer after the payload, gets a header that eats the payload and a payload ofb''. That is whatPCAPNGdid to every packet block – itslengthis the wire’s Block Total Length and the captured octets sit ahead of the option list and the trailing length field – andProtocolBase.__init__()injects this payload into every parsed_info, so the empty value reached the dumpers and corrupted the files they wrote. See #646.
- classmethod id()¶
Index ID of the protocol.
- classmethod register(code, protocol)¶
Register a new protocol class.
Notes
The full qualified class name of the new protocol class should be as
{protocol.module}.{protocol.name}.- Parameters:
code (
int) – protocol codeprotocol (
ModuleDescriptor|Type[ProtocolBase]) – module descriptor or aProtocolsubclass
- Raises:
pcapkit.utilities.exceptions.RegistryError – If
protocolis not aProtocolBasesubclass.- Warns:
pcapkit.utilities.warnings.RegistryWarning – If
codeis already registered. The warning names the displaced entry and its replacement, so a caller can tell what was lost rather than only that something was.
Note
The guard now matches
register_protocol’s: it fires only when the incumbent differs from the replacement, so re-registering the exact same class object under the samecodeis a silent no-op rather than a warning about nothing displaced. GitHub issue #718 corrected the previous presence-only guard here, which read every repeat registration as a caller mistake even when the value was unchanged. The identity check does not reintroduce the concern that guard was written to avoid: it is a plainiscomparison, so an incumbent left as an unresolvedModuleDescriptoris never equal to the resolved replacement without the descriptor being resolved – the comparison itself resolves nothing, so the deferred import stays deferred and such an incumbent still reports as different.
- classmethod analyze(proto, payload, **kwargs)¶
Analyse packet payload.
- Parameters:
- Return type:
- Returns:
Parsed payload as a
Protocolinstance.
- classmethod from_schema(schema)¶
Create protocol instance from schema.
- classmethod from_data(data)¶
Create protocol instance from data.
- abstractmethod read(length=None, **kwargs)¶
Read (parse) packet data.
- abstractmethod make(**kwargs)¶
Make (construct) packet data.
- Parameters:
**kwargs (
Any) – Arbitrary keyword arguments.- Return type:
TypeVar(_ST, bound= Schema)- Returns:
Curated protocol schema data.
Note
The
**kwargshere absorbs the keywords thatProtocolBase.__post_init__hands to the parse as well as to the construction, so an implementation is not expected to declare every keyword it is called with. It is not a place for a caller to put a keyword no signature declares: since #617, building a protocol through its constructor with such a keyword raisesUnsupportedCallfromProtocolBase.__init__rather than discarding it.Warning
Calling this method directly is not checked, and still discards an undeclared keyword in silence. The check lives in
ProtocolBase.__init__, so it coversSomeProtocol(...)and thepack()it leads to, but notSomeProtocol.make(...)on an instance obtained some other way –object.__new__(cls).make(**kwargs)is the idiom, used by this package’s own tests and byHTTP.maketo reach its versioned implementation. Covering it would mean interposing on everymakein the tree rather than on the one place their keywords converge, which is a larger change than #617 and deliberately not made here. Construct through the constructor to get the check.
- unpack(length=None, **kwargs)¶
Unpack (parse) packet data.
- Parameters:
- Return type:
TypeVar(_PT, bound= Data)- Returns:
Parsed packet data.
Notes
We used a special keyword argument
__packet__to pass the global packet data to underlying methods. This is useful when the packet data is not available in the current instance.
- pack(**kwargs)¶
Pack (construct) packet data.
- Parameters:
**kwargs (
Any) – Arbitrary keyword arguments.- Return type:
- Returns:
Constructed packet data.
Notes
We used a special keyword argument
__packet__to pass the global packet data to underlying methods. This is useful when the packet data is not available in the current instance.
- static decode(byte, *, encoding=None, errors='strict')¶
-
Should decoding failed using
encoding, the method will try again decoding thebytesas'unicode_escape'with'replace'for error handling.See also
The method is a wrapping function for
bytes.decode().- Parameters:
byte (
bytes) – Source bytestring.encoding (
str|None) – The encoding with which to decode thebytes. If not provided,pcapkitwill first try detecting its encoding usingchardet. The fallback encoding would is UTF-8.errors (
Literal['strict','ignore','replace']) – The error handling scheme to use for the handling of decoding errors. The default is'strict'meaning that decoding errors raise aUnicodeDecodeError. Other possible values are'ignore'and'replace'as well as any other name registered withcodecs.register_error()that can handleUnicodeDecodeError.
- Return type:
- static unquote(url, *, encoding='utf-8', errors='replace')¶
Unquote URLs into readable format.
Should decoding failed , the method will try again replacing
'%'with'\x'then decoding theurlas'unicode_escape'with'replace'for error handling.See also
This method is a wrapper function for
urllib.parse.unquote().- Parameters:
url (
str) – URL string.encoding (
str) – The encoding with which to decode thebytes.errors (
Literal['strict','ignore','replace']) – The error handling scheme to use for the handling of decoding errors. The default is'strict'meaning that decoding errors raise aUnicodeDecodeError. Other possible values are'ignore'and'replace'as well as any other name registered withcodecs.register_error()that can handleUnicodeDecodeError.
- Return type:
- static expand_comp(value)¶
Expand protocol class to protocol name.
The method is used to expand protocol class to protocol name, in the following manner:
If
valueis a protocol instance, the method will return the protocol class, and the protocol names in upper case obtained fromProtocol.id.If
valueis a protocol class, the method will return the protocol class itself, and the protocols names in upper case obtained fromProtocol.id.If
valueisstr, the method will attempt to search for the existing registered protocol class frompcapkit.protocols.__proto__and follow step 2; otherwise, return the value itself.
- Parameters:
value (
str|ProtocolBase|Type[ProtocolBase]) – Protocol class or name.- Return type:
- __layer__: Literal['Link', 'Internet', 'Transport', 'Application'] | None¶
Layer of protocol, can be one of
Link,Internet,TransportandApplication. For example, the layer ofEthernetisLink. However, certain protocols are not in any layer, such asRaw, and thus its layer isNone.
- __proto__: DefaultDict[int, ModuleDescriptor[ProtocolBase] | Type[ProtocolBase]]¶
Protocol index mapping for decoding next layer, c.f.
self._decode_next_layer&self._import_next_layer. The values should be a tuple representing the module name and class name, or aProtocolsubclass.
- _read_packet(length=None, *, header=None, payload=None, discard=False)¶
Read raw packet data.
- Overloads:
self, length (Optional[int]), header (None) → bytes
self, header (int), payload (Optional[int]), discard (Literal[True]) → bytes
self, header (int), payload (Optional[int]), discard (Literal[False]) → Data_Packet
- Parameters:
- _get_payload()¶
Get payload from
self.__header__.- Return type:
- Returns:
Payload of
self.__header__asbytes.
See also
This is a wrapper function for
pcapkit.protocols.schema.schema.Schema.get_payload().
- classmethod _make_data(data)¶
Create key-value pairs from
datafor protocol construction.
- classmethod _make_index(name, default=None, *, namespace=None, reversed=False, pack=False, size=4, signed=False, lilendian=False)¶
Return first index of
namefrom adictor enumeration.- Overloads:
cls, name (int | StdlibEnum | AenumEnum), pack (Literal[False]) → int
cls, name (int | StdlibEnum | AenumEnum), pack (Literal[True]), size (int), signed (bool), lilendian (bool) → bytes
cls, name (str), default (Optional[int]), namespace (Type[StdlibEnum] | Type[AenumEnum]), pack (Literal[False]) → int
cls, name (str), default (Optional[int]), namespace (Type[StdlibEnum] | Type[AenumEnum]), pack (Literal[True]), size (int), signed (bool), lilendian (bool) → bytes
cls, name (str), default (Optional[int]), namespace (dict[int, str]), reversed (Literal[False]), pack (Literal[False]) → int
cls, name (str), default (Optional[int]), namespace (dict[int, str]), reversed (Literal[False]), pack (Literal[True]), size (int), signed (bool), lilendian (bool) → bytes
cls, name (str), default (Optional[int]), namespace (dict[str, int]), reversed (Literal[True]), pack (Literal[False]) → int
cls, name (str), default (Optional[int]), namespace (dict[str, int]), reversed (Literal[True]), pack (Literal[True]), size (int), signed (bool), lilendian (bool) → bytes
cls, name (str | int | StdlibEnum | AenumEnum), default (Optional[int]), namespace (Optional[dict[str, int] | dict[int, str] | Type[StdlibEnum] | Type[AenumEnum]]), reversed (bool), pack (Literal[False]) → int
- Parameters:
namespace (
dict[str,int] |dict[int,str] |Type[IntEnum] |Type[IntEnum] |None) – namespace for itemreversed (
bool) – if namespace isstr -> intpairspack (
bool) – if needstruct.pack()to pack the resultsize (
int) – buffer sizesigned (
bool) – signed flaglilendian (
bool) – little-endian flag
- Returns:
Index of
namefrom a dict or enumeration. IfpackisTrue, returnsbytes; otherwise, returnsint.- Raises:
ProtocolNotImplemented – If
nameis NOT innamespaceanddefaultisNone.
- classmethod _make_payload(data)¶
Create payload from
datafor protocol construction.This method uses
__next_type__and__next_name__to determine the payload type and name. If either of them isNone, aNoPayloadinstance will be returned. Otherwise, the payload will be constructed byProtocol.from_data.- Parameters:
data (
Data) – protocol data- Return type:
- Returns:
Payload for protocol construction.
- static _lookup_registry(registry, code)¶
Look up a dispatch registry entry without recording a miss.
- Parameters:
registry (
DefaultDict[Any,TypeVar(_VT)]) – dispatch registry to read, i.e.self.__proto__or one of the per-protocol__option__/__chunk__/__block__family. Passed in rather than read from the class, so that a caller reaching the registry through an instance keeps doing so.code (
Any) – registry key to look up, i.e. the wire code being dispatched on
- Return type:
TypeVar(_VT)- Returns:
The entry registered for
code, or the fallbackregistrydeclares whencodeis not registered.
Important
Every one of these registries is a
collections.defaultdictheld on a class attribute, shared by every instance of the class in the process. Soregistry[code]inserts each code it misses, and parsing one packet carrying an unrecognised code is enough to grow the registry permanently.The inserted value is whatever the default factory would have produced anyway, so the entry buys nothing. It costs a spurious “already registered” warning from the next genuine
registercall for that code, and it makes “is this code registered?” unanswerable by inspection, since the answer depends on what has been parsed. The fallback is therefore read from the default factory directly rather than through a lookup that records it.
- static _lookup_next_layer(registry, proto)¶
Look up the protocol class registered for a next layer code.
- Parameters:
registry (
DefaultDict[int,ModuleDescriptor[ProtocolBase] |Type[ProtocolBase]]) – next layer protocol registry, i.e.self.__proto__. Passed in rather than read from the class, so that a caller reaching the registry through an instance keeps doing so.proto (
int) – next layer protocol index
- Return type:
- Returns:
The class registered for
proto, or the fallbackregistrydeclares – normallyRaw– whenprotois not registered.
Important
The lookup itself is
self._lookup_registry, so a miss does not grow the shared registry. What this adds is the next-layer-specific resolution step: a registered code may hold aModuleDescriptorrather than a class, and importing it is written back so the import happens once.That write-back is deliberately confined to a hit. Memoising the fallback’s resolution under
protowould be exactly the insertionself._lookup_registryexists to avoid.So a miss resolves its fallback descriptor again on every frame, and what keeps that affordable is
ModuleDescriptor.klassreadingsys.modulesinstead of re-enteringimportlib.import_module()– see #574. Memoising the resolved class here instead, whether underproto, inregistry’s default factory, or in a cache beside the registry, would retain a class thatimportlib.reload()then makes stale; #425 and #428 at this layer and #560 at the schema layer are all that same defect.
- _decode_next_layer(dict_, proto, length=None, *, packet=None)¶
Decode next layer protocol.
- Parameters:
- Return type:
TypeVar(_PT, bound= Data)- Returns:
Current protocol with next layer extracted.
Notes
We added a new key
__next_type__todict_to store the next layer protocol type, and a new key__next_name__to store the next layer protocol name. These two keys will NOT be included whenInfo.to_dictis called.
- _import_next_layer(proto, length=None, *, packet=None)¶
Import next layer extractor.
- Parameters:
- Return type:
- Returns:
Instance of next layer.
- _get_context(cls=None)¶
Get the caller supplied context for this protocol, if any.
The lookup is keyed on
self.id, so a protocol finds its own context without knowing how the caller spelled the registry.- Parameters:
cls (
Type[TypeVar(_CTX, bound= ProtocolContext)] |None) – Expected context class; when given, a context registered under this protocol’s name but of another type is ignored rather than returned for the implementation to trip over.- Return type:
- Returns:
The matching context, or
Nonewhen the caller supplied none.
See also
- _next: ProtocolBase¶
Next layer protocol instance.
- _protos: ProtoChain¶
Protocol chain instance.
- __data__: Type[_PT] = <class 'pcapkit.protocols.data.misc.raw.Raw'>¶
Protocol packet data definition.
- __init__(file=None, length=None, **kwargs)¶
Initialisation.
- Overloads:
self, file (IO[bytes] | bytes), length (Optional[int]), kwargs (Any) → None
self, kwargs (Any) → None
- Parameters:
_layer (
str) – Parse packet until_layer(self._exlayer). While parsing, the un-prefixedlayeris accepted as well – see the note below._protocol (
Union[str,Protocol,Type[Protocol]]) – Parse packet until_protocol(self._exproto). While parsing, the un-prefixedprotocolis accepted as well – see the note below.packet (
dict[str,Any]) – Packet context of the enclosing layer, as handed over byself._import_next_layer. While parsing, it is republished as__packet__so thatself.unpack– and through it the schema – can see it; see the note below.__context__ (
Union[ContextRegistry,ProtocolContext,Mapping[str,ProtocolContext],Iterable[ProtocolContext]]) – Caller supplied parsing context (self._exctx), c.f.pcapkit.corekit.context. It is consumed here rather than being forwarded toself.read, and is propagated to nested layers byself._import_next_layer.**kwargs (
Any) – Arbitrary keyword arguments.
- Raises:
UnsupportedCall – When constructing (
fileisNone), if a keyword names no parameter of this protocol’smake(),read(),pack(),unpack(),__post_init__()or__init__(), anywhere in the MRO, and is not listed in__keywords__. See #617; until then such a keyword was silently discarded. Parsing (fileis given) is unaffected.
Note
Three of the keywords above are out-of-band: they configure the parse rather than describing the packet, and every one of them is consumed here, at the one point each of a protocol’s producers passes through. That is deliberate, and it is what the normalisation below relies on – fixing a spelling here fixes it for the engines, for all four
_import_next_layerimplementations, and for any third party protocol that copied their shape, rather than one call site at a time.
- __post_init__(file=None, length=None, **kwargs)¶
Post initialisation hook.
- Overloads:
self, file (IO[bytes] | bytes), length (Optional[int]), kwargs (Any) → None
self, kwargs (Any) → None
- Parameters:
See also
For construction arguments, please refer to
self.make.
- classmethod __init_subclass__(schema=None, data=None, code=None, *args, **kwargs)[source]¶
Initialisation for subclasses.
- Parameters:
schema (
Type[TypeVar(_ST, bound= Schema)] |None) – Schema class.code (
Any) – Next-layer dispatch registration key(s).None(the default) skips registration entirely. SeeProtocolBase.__init_subclass__()for the accepted shapes and the enum-type inference rule.*args (
Any) – Arbitrary positional arguments.**kwargs (
Any) – Arbitrary keyword arguments.
This method is called when a subclass of
Protocolis defined. It is used to set theself.__schema__attribute of the subclass.Notes
When
schemaand/ordatais not specified, the method will first try to find the corresponding class in theschemaanddatamodules respectively. If the class is not found, the defaultRawandRawclasses will be used.This method also registers the subclass to the protocol registry, i.e.,
pcapkit.protocols.__proto__. That registration is unconditional – it is the name-keyed identity registry, unrelated to thecodekeyword – whereascode’s next-layer dispatch registration is opt-in; seeProtocolBase.__init_subclass__()for the latter.See also
For more information on the registry, please refer to
pcapkit.foundation.registry.protocols.register_protocol().
- __repr__()¶
Returns representation of parsed protocol data.
Example
>>> protocol <Frame alias='...' frame=(..., packet=b'...', sethernet=..., protocols='Ethernet:IPv6:Raw')>
- Return type:
- __str__()¶
Returns formatted hex representation of source data stream.
Example
>>> protocol Frame(..., packet=b"...", sethernet=..., protocols='Ethernet:IPv6:Raw') >>> print(protocol) 00 00 00 00 00 00 00 a6 87 f9 27 93 16 ee fe 80 00 00 00 ..........'........ 00 00 00 1c cd 7c 77 ba c7 46 b7 87 00 0e aa 00 00 00 00 .....|w..F......... fe 80 00 00 00 00 00 00 1c cd 7c 77 ba c7 46 b7 01 01 a4 ..........|w..F.... 5e 60 d9 6b 97 ^`.k.
- Return type:
- __getitem__(key)¶
Subscription (
getitem) support.If
keyis aProtocolobject, the method will fetch its indexes (self.id).Later, search the packet’s chain of protocols with the calculated
key.If no matches, then raises
ProtocolNotFound.
- Parameters:
- Return type:
- Returns:
The sub-packet from the current packet of indexed protocol.
- Raises:
ProtocolNotFound – If
keyis not in the current packet.
See also
The method calls
self.expand_compto handle thekeyand expand it for robust searching.
- __contains__(name)¶
Returns if certain protocol is in the instance.
See also
The method calls
self.expand_compto handle thenameand expand it for robust searching.- Return type:
- abstractmethod classmethod __index__()¶
Numeral registry index of the protocol.
- Return type:
IntEnum|IntEnum
- _exproto: str | ProtocolBase | Type[ProtocolBase] | None¶
Parse packet until such protocol.
- Type:
- _exctx: ContextRegistry | None = None¶
Caller supplied parsing context, c.f.
pcapkit.corekit.context.self.__init__replaces this with a realContextRegistry; the class levelNoneis what an instance built without going through__init__– e.g.object.__new__(SomeProtocol)– sees, so that reading it is always safe.
Data Models¶
Internal Definitions¶
- class pcapkit.protocols.protocol.ProtocolBase(file=None, length=None, **kwargs)[source]¶
-
Abstract base class for all protocol family.
Note
This class is for internal use only. For customisation, please use
Protocolinstead.
- class pcapkit.protocols.protocol.ProtocolMeta(name, bases, namespace, /, **kwargs)[source]¶
Bases:
ABCMetaMeta class to add dynamic support to
Protocol.This meta class is used to generate necessary attributes for the
Protocolclass. It can be useful to reduce unnecessary registry calls and simplify the customisation process.
Type Variables¶
- pcapkit.protocols.protocol._PT: pcapkit.protocols.data.data.Data¶
- pcapkit.protocols.protocol._ST: pcapkit.protocols.schema.schema.Schema¶