diff --git a/pcapkit/protocols/misc/pcapng.py b/pcapkit/protocols/misc/pcapng.py index d0cee5873a..2e614a6396 100644 --- a/pcapkit/protocols/misc/pcapng.py +++ b/pcapkit/protocols/misc/pcapng.py @@ -3556,7 +3556,7 @@ def _read_option_epb_flags(self, schema: 'Schema_EPB_FlagsOption', *, raise ProtocolError(f'PCAP-NG: [epb_flags] option must be only one, ' f'but {self._opt[schema.type] + 1} found.') if schema.length != 4: - raise ProtocolError(f'PCAP-NG: [epb_flags] invalid length (expected 8, got {schema.length})') + raise ProtocolError(f'PCAP-NG: [epb_flags] invalid length (expected 4, got {schema.length})') option = Data_EPB_FlagsOption( type=schema.type, @@ -4018,7 +4018,7 @@ def _read_option_pack_flags(self, schema: 'Schema_PACK_FlagsOption', *, raise ProtocolError(f'PCAP-NG: [pack_flags] option must be only one, ' f'but {self._opt[schema.type] + 1} found.') if schema.length != 4: - raise ProtocolError(f'PCAP-NG: [pack_flags] invalid length (expected 8, got {schema.length})') + raise ProtocolError(f'PCAP-NG: [pack_flags] invalid length (expected 4, got {schema.length})') option = Data_PACK_FlagsOption( type=schema.type, @@ -6013,7 +6013,7 @@ def _make_option_ns_dnsipv4(self, type: 'Enum_OptionType', option: 'Optional[Dat ) def _make_option_ns_dnsipv6(self, type: 'Enum_OptionType', option: 'Optional[Data_NS_DNSIP6AddrOption]' = None, *, - ip: 'str | bytes | IPv6Address | int' = '8.8.8.8', + ip: 'str | bytes | IPv6Address | int' = '2001:4860:4860::8888', **kwargs: 'Any') -> 'Schema_NS_DNSIP6AddrOption': """Make PCAP-NG ``ns_dnsIP6addr`` option. @@ -6573,7 +6573,7 @@ def _make_record_ipv4(self, type: 'Enum_RecordType', record: 'Optional[Data_IPv4 ) def _make_record_ipv6(self, type: 'Enum_RecordType', record: 'Optional[Data_IPv6Record]' = None, *, - ip: 'IPv6Address | str | bytes | int' = '127.0.0.1', + ip: 'IPv6Address | str | bytes | int' = '::1', names: 'Optional[list[str]]' = None, **kwargs: 'Any') -> 'Schema_IPv6Record': """Make PCAP-NG ``nrb_record_ipv6`` name resolution record. diff --git a/tests/protocols/misc/test_pcapng_flags_length_ipv6_defaults_2168_unit.py b/tests/protocols/misc/test_pcapng_flags_length_ipv6_defaults_2168_unit.py new file mode 100644 index 0000000000..781faf029e --- /dev/null +++ b/tests/protocols/misc/test_pcapng_flags_length_ipv6_defaults_2168_unit.py @@ -0,0 +1,92 @@ +# -*- coding: utf-8 -*- +"""GitHub issue #2168: flags-option length messages and IPv6 NRB maker defaults. + +* The ``epb_flags`` and ``pack_flags`` length checks reject anything but 4 + octets, yet their error said "expected 8". +* ``_make_option_ns_dnsipv6`` and ``_make_record_ipv6`` defaulted to IPv4 + addresses, so making either with no address raised + :exc:`~pcapkit.utilities.exceptions.FieldValueError`. They default to + ``2001:4860:4860::8888`` and ``::1``, the IPv6 counterparts of the IPv4 + makers' ``8.8.8.8`` and ``127.0.0.1``. + +:mod:`pcapkit` is imported inside each test, after +:func:`~tests._support.reimport_once_per_class`. + +""" + +import struct +import unittest +import warnings + +from tests.protocols.misc.test_pcapng_roundtrip2_unit import H, PCAPNGTestCase, _epb_with_options + +#: Test tier (#1958): hand-picked error-message and make-default cases, not a sweep. +__tier__ = 'ALL' + + +def _pb_with_option(option: 'bytes') -> 'bytes': + """An obsolete Packet Block on interface 0 carrying ``abcd`` and ``option``.""" + body = H('0000 0000 00000000 00000000 04000000 04000000 61626364') + option + bytes(4) + length = len(body) + 12 + return struct.pack(' None: + from pcapkit.utilities.exceptions import ProtocolError + + octets = _epb_with_options(H('0200 0800 0000000000000000')) + with self.assertRaisesRegex(ProtocolError, r'\[epb_flags\] invalid length \(expected 4, got 8\)'): + self._parse(octets) + + def test_pack_flags_reports_expected_4(self) -> None: + from pcapkit.utilities.exceptions import ProtocolError + + octets = _pb_with_option(H('0200 0800 0000000000000000')) + with warnings.catch_warnings(): + warnings.simplefilter('ignore') # the Packet Block is obsolete + with self.assertRaisesRegex(ProtocolError, r'\[pack_flags\] invalid length \(expected 4, got 8\)'): + self._parse(octets) + + +class TestPCAPNGIPv6MakeDefaults(PCAPNGTestCase): + """Pin the IPv6 addresses the NRB makers use when given none.""" + + def test_record_ipv6_defaults_to_loopback(self) -> None: + from ipaddress import IPv6Address + + from pcapkit.const.pcapng.block_type import BlockType + from pcapkit.const.pcapng.record_type import RecordType + from pcapkit.protocols.misc.pcapng import PCAPNG + + context = self._context() + block = PCAPNG(num=2, sct=1, ctx=context, type=BlockType.Name_Resolution_Block, + block={'records': [(RecordType.nrb_record_ipv6, {'names': ['a']}), + (RecordType.nrb_record_end, {})]}) + record = self._parse(block.data, context).info.records[RecordType.nrb_record_ipv6] + self.assertEqual(record.ip, IPv6Address('::1')) + self.assertRebuilds(block.data, context) + + def test_ns_dnsip6addr_defaults_to_dns_server(self) -> None: + from ipaddress import IPv6Address + + from pcapkit.const.pcapng.block_type import BlockType + from pcapkit.const.pcapng.option_type import OptionType + from pcapkit.const.pcapng.record_type import RecordType + from pcapkit.protocols.misc.pcapng import PCAPNG + + context = self._context() + block = PCAPNG(num=2, sct=1, ctx=context, type=BlockType.Name_Resolution_Block, + block={'records': [(RecordType.nrb_record_ipv4, {'names': ['a']}), + (RecordType.nrb_record_end, {})], + 'options': [(OptionType.ns_dnsIP6addr, {}), + (OptionType.opt_endofopt, {})]}) + option = self._parse(block.data, context).info.options[OptionType.ns_dnsIP6addr] + self.assertEqual(option.ip, IPv6Address('2001:4860:4860::8888')) + self.assertRebuilds(block.data, context) + + +if __name__ == '__main__': + unittest.main()