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
65 changes: 64 additions & 1 deletion docs/source/pcapkit/protocols/protocol.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,23 @@ Internal Definitions below.
.. automethod:: _make_payload
.. automethod:: _packing_target

.. automethod:: _read_unpack
.. automethod:: _read_octets
.. automethod:: _read_uint
.. automethod:: _read_sint
.. automethod:: _read_vector
.. automethod:: _read_whole_vector
.. automethod:: _read_uints

.. automethod:: _make_pack
.. automethod:: _make_octets
.. automethod:: _fill_short_read
.. automethod:: _make_uint
.. automethod:: _make_sint
.. automethod:: _uint_size
.. automethod:: _make_vector
.. automethod:: _make_uints

.. automethod:: _read_mac_addr
.. automethod:: _make_mac_addr

Expand Down Expand Up @@ -140,6 +157,29 @@ The ``pyshark``, ``scapy`` and ``pypcapfile`` engines dissect with their own
parsers, which do not apply the frame's layer bound (:issue:`1609`). The
``dpkt`` toolkit reads what |dpkt|_ decodes, which applies neither bound.

Value Helpers
-------------

A protocol reads and makes its field values through the helpers
:class:`~pcapkit.protocols.protocol.Protocol` carries, rather than through
copies of its own (:issue:`2614`). A maker checks a caller's values through them
before packing, and raises :exc:`~pcapkit.utilities.exceptions.ProtocolError` for
one its field cannot carry rather than masking it (:issue:`2617`):

* an unsigned integer through :meth:`~Protocol._make_uint`, which takes a
:obj:`bool` for a one-bit field only;
* a two's-complement integer through :meth:`~Protocol._make_sint`, the inverse of
:meth:`~Protocol._read_sint`;
* the octets of a fixed-size field through :meth:`~Protocol._make_octets`, which
refuses any other width rather than padding or cutting it (:issue:`2676`). A
value taken from a model of a cut capture goes through
:meth:`~Protocol._fill_short_read` first, which zero-fills just the octets the
cut left out, for the rebuild to cut back.

:meth:`~Protocol._uint_size` counts the octets a variable-width integer needs,
and :func:`~pcapkit.corekit.fields.strings.pad_length` the padding that takes a
length to an alignment boundary.

Data Models
-----------

Expand Down Expand Up @@ -205,6 +245,21 @@ Internal Definitions
_make_index
_make_payload
_packing_target
_read_unpack
_read_octets
_read_uint
_read_sint
_read_vector
_read_whole_vector
_read_uints
_make_pack
_make_octets
_fill_short_read
_make_uint
_make_sint
_uint_size
_make_vector
_make_uints
_read_mac_addr
_make_mac_addr
_lookup_registry
Expand Down Expand Up @@ -233,7 +288,15 @@ Internal Definitions
:meth:`~Protocol._read_packet`, :meth:`~Protocol._get_payload`,
:meth:`~Protocol._make_data`, :meth:`~Protocol._make_uint`,
:meth:`~Protocol._make_index`, :meth:`~Protocol._make_payload`,
:meth:`~Protocol._packing_target`, :meth:`~Protocol._read_mac_addr`,
:meth:`~Protocol._packing_target`, :meth:`~Protocol._read_unpack`,
:meth:`~Protocol._read_octets`, :meth:`~Protocol._read_uint`,
:meth:`~Protocol._read_sint`, :meth:`~Protocol._read_vector`,
:meth:`~Protocol._read_whole_vector`, :meth:`~Protocol._read_uints`,
:meth:`~Protocol._make_pack`, :meth:`~Protocol._make_octets`,
:meth:`~Protocol._fill_short_read`,
:meth:`~Protocol._make_uint`, :meth:`~Protocol._make_sint`,
:meth:`~Protocol._uint_size`, :meth:`~Protocol._make_vector`,
:meth:`~Protocol._make_uints`, :meth:`~Protocol._read_mac_addr`,
:meth:`~Protocol._make_mac_addr`,
:meth:`~Protocol._lookup_registry`, :meth:`~Protocol._lookup_next_layer`,
:meth:`~Protocol._decode_next_layer`,
Expand Down
41 changes: 30 additions & 11 deletions pcapkit/protocols/application/ospf.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,13 @@
verified on read under :data:`~pcapkit.utilities.config.CHECKSUM_VERIFY`
(:issue:`2475`).
:meth:`~pcapkit.protocols.application.ospf.OSPF.make` raises
:exc:`~pcapkit.utilities.exceptions.ProtocolError` for a checksum, or the
reserved octets of cryptographic authentication data, that is not two
octets, rather than padding or cutting it (:issue:`2676`).
"""
import collections.abc
import ipaddress
from typing import TYPE_CHECKING, cast

Expand Down Expand Up @@ -301,11 +307,12 @@ def make(self,
Raises:
ProtocolError: If ``auth_data`` is of an invalid type, is
:obj:`bytes` that are not exactly 8 octets long, or is a
:obj:`dict` missing a required key; if ``packet_length``
is not an :obj:`int` in ``0..65535``; or if ``type`` (8 bits)
or ``auth_type`` (16 bits) is a name, a :obj:`bool`, or a
member or code its field cannot carry.
:obj:`bytes` that are not exactly 8 octets long, is a
:obj:`dict` missing a required key, or carries ``reserved``
octets that are not two; if ``packet_length`` is not an
:obj:`int` in ``0..65535``; if ``checksum`` is not two octets;
or if ``type`` (8 bits) or ``auth_type`` (16 bits) is a name, a
:obj:`bool`, or a member or code its field cannot carry.
.. versionchanged:: 1.5.0
Expand Down Expand Up @@ -341,7 +348,8 @@ def make(self,
length=packet_length,
router_id=router_id,
area_id=area_id,
checksum=b'\x00\x00' if checksum is None else checksum,
checksum=(b'\x00\x00' if checksum is None
else self._make_octets(checksum, size=2, name='checksum', alias='OSPF')),
auth_type=auth_type_,
auth_data=data,
payload=payload,
Expand Down Expand Up @@ -391,6 +399,14 @@ def _make_data(cls, data: 'Data_OSPF') -> 'dict[str, Any]': # type: ignore[over
Key-value pairs for protocol construction.
"""
auth = data.auth # type: Any
# NOTE: Reserved octets a capture cut inside are zero-filled (#1458), in
# the data model or the dict form ``to_dict()`` gives of it.
if isinstance(auth, collections.abc.Mapping):
reserved = auth.get('reserved', b'\x00\x00')
filled = cls._fill_short_read(reserved, size=2, model=data, field='auth_data')
if filled is not reserved:
auth = {**auth, 'reserved': filled}
return {
'version': data.version,
'type': data.type,
Expand All @@ -399,7 +415,7 @@ def _make_data(cls, data: 'Data_OSPF') -> 'dict[str, Any]': # type: ignore[over
'area_id': data.area_id,
'checksum': data.chksum,
'auth_type': data.autype,
'auth_data': data.auth,
'auth_data': auth,
'payload': cls._make_payload(data),
# NOTE: And the octets captured past the Packet Length (#1455).
'trailer': data.get('trailer', b''),
Expand Down Expand Up @@ -497,8 +513,9 @@ def _make_encrypt_auth(self,
Raises:
ProtocolError: If ``auth_data`` is of an invalid type, is
:obj:`bytes` that are not exactly 8 octets long, or is a
:obj:`dict` missing a required key.
:obj:`bytes` that are not exactly 8 octets long, is a
:obj:`dict` missing a required key, or carries ``reserved``
octets that are not two.
"""
if isinstance(auth_data, Schema_CryptographicAuthentication):
Expand All @@ -509,7 +526,8 @@ def _make_encrypt_auth(self,
return Schema_CryptographicAuthentication.unpack(auth_data) # type: ignore[call-arg,misc]
if isinstance(auth_data, Data_CryptographicAuthentication):
return Schema_CryptographicAuthentication(
reserved=getattr(auth_data, 'reserved', b'\x00\x00'),
reserved=self._make_octets(getattr(auth_data, 'reserved', b'\x00\x00'), size=2,
name='reserved octets', alias='OSPF'),
key_id=auth_data.key_id,
len=auth_data.len,
seq=auth_data.seq,
Expand All @@ -519,7 +537,8 @@ def _make_encrypt_auth(self,
if isinstance(auth_data, dict):
try:
return Schema_CryptographicAuthentication(
reserved=auth_data.get('reserved', b'\x00\x00'),
reserved=self._make_octets(auth_data.get('reserved', b'\x00\x00'), size=2,
name='reserved octets', alias='OSPF'),
key_id=auth_data['key_id'],
len=auth_data['len'],
seq=auth_data['seq'],
Expand Down
Loading
Loading