Source code for pcapkit.protocols.schema.transport.udp

# -*- coding: utf-8 -*-
# mypy: disable-error-code=assignment
"""header schema for user datagram protocol"""

from typing import TYPE_CHECKING

from pcapkit.const.reg.apptype import AppType as Enum_AppType
from pcapkit.const.reg.apptype import TransportProtocol as Enum_TransportProtocol
from pcapkit.corekit.fields.misc import PayloadField
from pcapkit.corekit.fields.numbers import EnumField, UInt16Field
from pcapkit.corekit.fields.strings import BytesField
from pcapkit.protocols.schema.schema import Schema, schema_final
from pcapkit.utilities.exceptions import BaseError

__all__ = ['UDP']

if TYPE_CHECKING:
    from typing import Any

    from pcapkit.protocols.protocol import ProtocolBase


class PortEnumField(EnumField):
    """Enumerated value for protocol fields.

    Args:
        length: Field size (in bytes); if a callable is given, it should return
            an integer value and accept the current packet as its only argument.
        default: Field default value, if any.
        signed: Whether the field is signed.
        byteorder: Field byte order.
        bit_length: Field bit length.
        callback: Callback function to be called upon
            :meth:`self.__call__ <pcapkit.corekit.fields.field.FieldBase.__call__>`.

    Important:
        This class is specifically designed for :class:`~pcapkit.const.reg.apptype.AppType`
        as it is actually a :class:`~enum.StrEnum` class.

    """
    if TYPE_CHECKING:
        _namespace: 'Enum_AppType'

    def pre_process(self, value: 'int | Enum_AppType', packet: 'dict[str, Any]') -> 'int | bytes':
        """Process field value before construction (packing).

        Arguments:
            value: Field value.
            packet: Packet data.

        Returns:
            Processed field value.

        """
        if isinstance(value, Enum_AppType):
            value = value.port
        return super().pre_process(value, packet)

    def post_process(self, value: 'int | bytes', packet: 'dict[str, Any]') -> 'Enum_AppType':
        """Process field value after parsing (unpacked).

        Args:
            value: Field value.
            packet: Packet data.

        Returns:
            Processed field value -- the registry member declared for the
            port, or an unregistered member of the same registry, carrying
            the port itself, when the registry declares none. See GitHub
            issue #575.

        Notes:
            See :meth:`pcapkit.protocols.schema.transport.tcp.PortEnumField.post_process`,
            whose notes this mirrors verbatim aside from the transport.

        """
        value = super(EnumField, self).post_process(value, packet)
        proto = Enum_TransportProtocol.udp
        if not (isinstance(value, int) and 0 <= value < (1 << (8 * self.length))):
            return self._namespace.get(value, proto=proto)
        owner = self._namespace._dispatch(value, proto)  # pylint: disable=protected-access
        if not owner.__registry__.getlist(value):  # type: ignore[union-attr]
            try:
                declared = owner._missing_(value)  # pylint: disable=protected-access
            except ValueError as error:
                if isinstance(error, BaseError):
                    raise
                declared = None
            if declared is None:
                return self._unregistered_member(
                    owner, f'unknown [{value:d} - {proto.name}]',
                    svc='unknown', port=value, proto=proto)
        return self._namespace.get(value, proto=proto)


[docs] @schema_final class UDP(Schema): """Header schema for UDP packet.""" #: Source port. srcport: 'Enum_AppType' = PortEnumField(length=2, namespace=Enum_AppType) #: Destination port. dstport: 'Enum_AppType' = PortEnumField(length=2, namespace=Enum_AppType) #: Length of UDP packet. len: 'int' = UInt16Field() #: Checksum of UDP packet. checksum: 'bytes' = BytesField(length=2) #: Payload. payload: 'bytes' = PayloadField() if TYPE_CHECKING: def __init__(self, srcport: 'Enum_AppType | int', dstport: 'Enum_AppType | int', len: 'int', checksum: 'bytes', payload: 'bytes | Schema | ProtocolBase') -> 'None': ...