Source code for pcapkit.protocols.schema.misc.pcap.header
# -*- coding: utf-8 -*-
# mypy: disable-error-code=assignment
"""header schema for global header of PCAP file format"""
from typing import TYPE_CHECKING
from pcapkit.const.reg.linktype import LinkType as Enum_LinkType
from pcapkit.corekit.fields.numbers import EnumField, Int32Field, UInt16Field, UInt32Field
from pcapkit.corekit.fields.strings import BytesField
from pcapkit.protocols.schema.schema import Schema, schema_final
from pcapkit.utilities.exceptions import ProtocolError
__all__ = ['Header']
if TYPE_CHECKING:
from typing import Any, Dict
from typing_extensions import Literal
from pcapkit.corekit.fields.numbers import NumberField
#: Length of the PCAP global header, which opens the file. The one home of
#: this value (:issue:`1739`); the format fixes it, so it is never configurable.
#:
#: .. versionadded:: 1.5.0
GLOBAL_HEADER_LEN = 24
#: PCAP magic numbers, as the octets open the file, mapped to the byte order
#: (``'big'`` or ``'little'``) and the timestamp resolution (:data:`True` for
#: nanoseconds, :data:`False` for microseconds) each one declares. The one home
#: of these values (:issue:`1739`); the format fixes them, so they are never
#: configurable.
#:
#: .. versionadded:: 1.5.0
MAGIC_NUMBER = {
b'\xa1\xb2\x3c\x4d': ('big', True),
b'\xa1\xb2\xc3\xd4': ('big', False),
b'\x4d\x3c\xb2\xa1': ('little', True),
b'\xd4\xc3\xb2\xa1': ('little', False),
} # type: Dict[bytes, tuple[Literal['big', 'little'], bool]]
#: Units per second of a record's ``ts_usec`` field, keyed by the nanosecond
#: flag its file's magic number declares (:data:`MAGIC_NUMBER`): microseconds,
#: or nanoseconds. The one home of these values (:issue:`1739`); the format
#: fixes them, so they are never configurable.
#:
#: .. versionadded:: 1.5.0
RESOLUTION = {
False: 1_000_000,
True: 1_000_000_000,
} # type: Dict[bool, int]
#: Decimal places of a ``ts_usec`` fraction at each resolution of
#: :data:`RESOLUTION`, keyed the same way -- six for microseconds, nine for
#: nanoseconds -- and derived from it, so that an epoch rounded to its file's
#: resolution follows the one home (:issue:`1739`).
#:
#: .. versionadded:: 1.5.0
RESOLUTION_PLACES = {
flag: len(str(units)) - 1 for flag, units in RESOLUTION.items()
} # type: Dict[bool, int]
def magic_number_callback(field: 'NumberField', packet: 'dict[str, Any]') -> 'None':
"""Calculate byte order of PCAP file.
Args:
field: Field instance.
packet: Packet data.
"""
magic_number = packet['magic_number']
# NOTE: looked up as :obj:`bytes`, so that a :obj:`bytearray` magic number
# is matched rather than rejected as unhashable.
declared = (MAGIC_NUMBER.get(bytes(magic_number))
if isinstance(magic_number, (bytes, bytearray, memoryview)) else None)
if declared is None:
raise ProtocolError('invalid magic number')
field._byteorder = declared[0]