Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions dns/rdtypes/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
6 changes: 6 additions & 0 deletions doc/whatsnew.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 20 additions & 0 deletions tests/test_rdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down