From e49d83954a45b08175fe82be75932f10ecac1dfe Mon Sep 17 00:00:00 2001 From: nashit hayyat Date: Mon, 2 Nov 2026 12:57:12 +0530 Subject: [PATCH] reject type 0 in NSEC/NSEC3/CSYNC type bitmaps from wire --- dns/rdtypes/util.py | 4 ++++ doc/whatsnew.rst | 6 ++++++ tests/test_rdata.py | 20 ++++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/dns/rdtypes/util.py b/dns/rdtypes/util.py index ef70da90..61772540 100644 --- a/dns/rdtypes/util.py +++ b/dns/rdtypes/util.py @@ -156,6 +156,10 @@ def __init__(self, windows: Iterable[tuple[int, bytes]] | None = None): # be included and trailing zero octets must be omitted, so the # last octet of a window's bitmap is always non-zero. raise ValueError(f"bad {self.type_name} octets: trailing zero octet") + if window == 0 and bitmap[0] & 0x80: + # Type 0 is reserved, and from_text() rejects it, so do the same + # here so that to_text() output can always be read back. + raise ValueError(f"bad {self.type_name} octets: bit 0 is set") self.windows.append((window, bitmap)) def to_text(self) -> str: diff --git a/doc/whatsnew.rst b/doc/whatsnew.rst index 11b1174b..29c76c4e 100644 --- a/doc/whatsnew.rst +++ b/doc/whatsnew.rst @@ -18,6 +18,12 @@ What's New in dnspython 2.9.1 (in development) ---------------------- +* The NSEC, NSEC3, and CSYNC wire parsers now reject a type bitmap with the + bit for type 0 set, raising ``dns.exception.FormError``. Type 0 is + reserved and was already rejected in text, but it was accepted from wire, + so ``to_text()`` of such a record, e.g. in a zone received by zone transfer, + produced text that could not be read back. + * LOC text parsing now converts the altitude, size, horizontal precision, and vertical precision fields to centimeters exactly, and requires them to be decimal numbers of meters with at most two fractional digits, as RFC 1876 diff --git a/tests/test_rdata.py b/tests/test_rdata.py index f83c7dd8..1a4d9333 100644 --- a/tests/test_rdata.py +++ b/tests/test_rdata.py @@ -1248,6 +1248,26 @@ def test_Bitmap(self): ba.append(0x01) # bit 15, for MX self.assertEqual(windows, [(0, bytes(ba))]) + def test_Bitmap_type0(self): + # Type 0 is rejected in text, so it must be rejected from wire too, or + # to_text() would produce text that from_text() cannot read. + with self.assertRaises(ValueError): + dns.rdtypes.util.Bitmap([(0, b"\x80")]) + cases = [ + ("NSEC", "next. A"), + ("NSEC3", "1 0 0 - 00 A"), + ("CSYNC", "1 0 A"), + ] + for rdtype, text in cases: + with self.subTest(rdtype=rdtype): + with self.assertRaises(dns.exception.SyntaxError): + dns.rdata.from_text("IN", rdtype, text.replace(" A", " TYPE0")) + wire = dns.rdata.from_text("IN", rdtype, text).to_wire() + # the last three octets are the bitmap: window 0, length 1, 0x40 + bad = wire[:-1] + bytes([0xC0]) + with self.assertRaises(dns.exception.FormError): + dns.rdata.from_wire("IN", rdtype, bad, 0, len(bad)) + def test_Bitmap_generator_windows(self): windows = [(0, b"@"), (1, b"\x80")] bitmap = dns.rdtypes.util.Bitmap(window for window in windows)