Source code for pcapkit.vendor.ipx.packet

# -*- coding: utf-8 -*-
# pylint: disable=wrong-import-position
"""IPX Packet Types
======================

.. module:: pcapkit.vendor.ipx.packet

This module contains the vendor crawler for **IPX Packet Types**,
which is automatically generating :class:`pcapkit.const.ipx.packet.Packet`.

"""

###############################################################################
# NOTE: fix duplicated name of ``socket```
import sys

path = sys.path.pop(0)
###############################################################################

import collections
from typing import TYPE_CHECKING

from pcapkit.vendor.default import Vendor

if TYPE_CHECKING:
    from collections import Counter

###############################################################################
sys.path.insert(0, path)
###############################################################################

__all__ = ['Packet']

###############################################################################
# NOTE: this crawler does not crawl: the registry below is maintained by hand;
# see #518. It is the same failure, on the same article and the same removal
# revision, that retired the sibling socket crawler in #507.
#
# Its source was the packet-type table in
# https://en.wikipedia.org/wiki/Internetwork_Packet_Exchange#IPX_packet_structure,
# and two separate things broke that. Wikipedia answers ``requests``'
# default ``python-requests/<version>`` User-Agent with HTTP 403 (measured
# 2026-09-19: 403 and 126 bytes of robot-policy text for the default agent, 200
# and 114117 bytes for a descriptive one). That half is fixed in
# ``Vendor._request``, which sends the descriptive agent
# :func:`~pcapkit.vendor.default.get_user_agent` builds -- but it only gets the
# crawler as far as a page that does not hold the data. The table was deleted
# from the article on 2026-08-25, in revision 1371327031 ("deleted some
# irrelevant technical tables"), leaving the live page (revision 1372814585)
# with a single ``wikitable`` -- the IPX header format one, headers
# ``['Octets', 'Field']`` -- so that ``find_all('table', class_='wikitable')[1]``
# raises ``IndexError`` even once the fetch succeeds.
#
# Pointing ``LINK`` at the last revision that still carries the table
# (``oldid=1368657333``, re-measured 2026-09-19: 200, 118834 bytes, 4
# ``wikitable``s, ``table[1]`` headers ``['Value', 'Meaning/Protocol']``, 8 body
# rows) would have worked, and running the crawler's original ``process()``
# against it
# reproduces the committed constant file byte for byte. It is still the wrong
# fix: a network fetch pinned to a frozen snapshot is all of the fragility of a
# crawler with none of the freshness, and it would go on selecting a table by
# index out of a document that nothing stops from changing shape again.
#
# What settles it is that the registry is closed. IPX packet types were Novell's
# to assign -- :rfc:`1362`, :rfc:`1551` and :rfc:`1634` all say "Packet Types
# also need to be assigned by Novell" -- and no Novell remains to assign
# them. IPX will not gain new packet types, so there is nothing for a
# crawler to pick up on its next run.
#
# The table below is therefore transcribed from that last revision that carried
# it, row for row, and each entry names the primary source for the assignment
# where one exists.
###############################################################################

#: IPX packet type registry, transcribed from the last revision of the Wikipedia
#: article that still carried the table,
#: https://en.wikipedia.org/w/index.php?title=Internetwork_Packet_Exchange&oldid=1368657333
#:
#: Maps a packet type to the enumeration name it takes and the comment rendered
#: above it. Names go through :meth:`~Vendor.rename`, so the generated member is
#: the :meth:`~Vendor.safe_name` of the first element; comments are stored as the
#: scrape rendered them, reStructuredText markup and all, since the prose-mangling
#: the crawler's original ``process()`` did to Wikipedia's cell text has no source
#: left to mangle.
#:
#: Every value below is independently corroborated by Wireshark's IPX dissector,
#: ``epan/dissectors/packet-ipx.h`` (fetched 2026-09-19), which defines
#: ``IPX_PACKET_TYPE_IPX 0``, ``_RIP 1``, ``_ECHO 2``, ``_ERROR 3``, ``_PEP 4``,
#: ``_SPX 5``, ``_NCP 17`` and ``_WANBCAST 20`` -- the same eight values in the
#: same order. Types 1-5 are inherited from Xerox XNS IDP, which IPX was derived
#: from; no RFC assigns them, and the notes below say so where that is the case.
DATA = {
    # NOTE: not a protocol assignment. The scraped table listed 0 as "Unknown",
    # and it doubles as the IPX protocol's own default for the ``type`` field.
    # Wireshark calls the same value plain ``IPX``. The archived revision is the
    # only source for the word "Unknown".
    0: ('Unknown', 'Unknown'),

    # :rfc:`1582` ("Extensions to RIP to Support Demand Circuits", 1994) and
    # :rfc:`2091` ("Triggered Extensions to RIP to Support Demand Circuits",
    # 1997) are the citation the article itself carried, and they do describe
    # IPX RIP -- "the Routing Information Protocol (RIP) which runs over the
    # Internetwork Packet Exchange (IPX) protocol using socket number 453h".
    # Note honestly that they extend IPX RIP rather than assign it packet type
    # 1; for the assignment they defer to Novell, "IPX Router Specification",
    # Version 1.10, October 1992, which :rfc:`1582` lists as reference [3].
    1: ('RIP', '``RIP``, Routing Information Protocol ([:rfc:`1582`], [:rfc:`2091`])'),

    # XNS IDP inheritances. The archived revision is the only source found for
    # these two names: no RFC and no reachable Novell document lists
    # either against a packet-type number. Wireshark corroborates the values as
    # ``ECHO 2`` and ``ERROR 3``.
    2: ('Echo Packet', 'Echo Packet'),
    3: ('Error Packet', 'Error Packet'),

    # NOTE: the best-sourced row in the table, by a distance. :rfc:`1362`,
    # :rfc:`1551` and :rfc:`1634` all state "The packets use the IPX defined
    # packet type 04 defining a Packet Exchange Packet", and tabulate it as
    # ``| Packet Type | 04 | Packet Exchange Packet |``. :rfc:`1791` adds "UDP
    # over IPX uses the IPX packet type 4, a normal IPX packet type" and "TCP,
    # like UDP, uses IPX packet type 4". :rfc:`1132` gives it from the other
    # side -- "IPX packets may be unicast by setting the IPX header Packet Type
    # field to 0x04".
    4: ('PEP', '``PEP``, Packet Exchange Protocol, used for SAP (Service Advertising Protocol)'),

    # :rfc:`1553` names the protocol -- "The Sequenced Packet Exchange (SPX) is
    # the reliable connection-based transport protocol commonly used by
    # applications" -- but not its packet-type number, which rests on Wireshark
    # and the archived revision.
    5: ('SPX', '``SPX``, Sequenced Packet Exchange'),

    # :rfc:`1553` names this one too -- "the Netware Core Protocol (NCP), which
    # is used for file server access" -- and again without the number. 17
    # (0x11) rests on Wireshark and the archived revision.
    17: ('NCP', '``NCP``, NetWare Core Protocol'),

    # NOTE: :rfc:`1132`, "A Standard for the Transmission of 802.2 Packets over
    # IPX Networks", assigns this one outright: "IPX packets may be broadcast by
    # setting the IPX header Packet Type field to 0x14" -- 0x14 being 20.
    # Wireshark calls it ``WANBCAST`` / "NetBIOS Broadcast".
    #
    # The archived table rendered the row as ``Broadcast[4]``, where ``[4]`` is
    # Wikipedia's own footnote marker for its citation of that RFC rather than
    # any part of the name, and it leaked into the generated member name. Both
    # the name and the comment are kept verbatim so that regenerating the
    # constant file stays a no-op against the last scraped output: renaming the
    # member would break :attr:`pcapkit.const.ipx.packet.Packet.Broadcast_4` for
    # anyone using it, which is a call for the maintainer rather than for #518.
    # This is the same artefact, from the same article, as the ``LLC_4`` member
    # #507 flagged in :mod:`pcapkit.vendor.ipx.socket`.
    20: ('Broadcast[4]', 'Broadcast[4]'),
}  # type: dict[int, tuple[str, str]]


[docs] class Packet(Vendor): """IPX Packet Types""" #: Value limit checker. FLAG = 'isinstance(value, int) and 0 <= value <= 255' def request(self) -> 'dict[int, tuple[str, str]]': # type: ignore[override] # pylint: disable=arguments-differ """Fetch registry data. Returns: Registry data (:data:`~pcapkit.vendor.ipx.packet.DATA`). """ return DATA def count(self, data: 'dict[int, tuple[str, str]]') -> 'Counter[str]': # type: ignore[override] """Count field records. Args: data: Registry data. Returns: Field recordings. """ return collections.Counter(self.safe_name(name) for name, _ in data.values()) def process(self, data: 'dict[int, tuple[str, str]]') -> 'tuple[list[str], list[str]]': # type: ignore[override] """Process registry data. Args: data: Registry data. Returns: Enumeration fields and missing fields. """ enum = [] # type: list[str] miss = [ "return cls._unregistered_member(value, 'Unassigned')", ] for code, (name, desc) in data.items(): pval = str(code) renm = self.rename(name, pval) enum.append(f'#: {self.wrap_comment(desc)}\n {renm} = {pval}') return enum, miss
if __name__ == '__main__': sys.exit(Packet()) # type: ignore[arg-type]