Skip to content

Apply character-string escapes as bytes in URI, HINFO, X25, ISDN, NAPTR, and CAA - #1288

Open
gaoflow wants to merge 1 commit into
rthalley:mainfrom
gaoflow:fix-character-string-escapes
Open

Apply character-string escapes as bytes in URI, HINFO, X25, ISDN, NAPTR, and CAA#1288
gaoflow wants to merge 1 commit into
rthalley:mainfrom
gaoflow:fix-character-string-escapes

Conversation

@gaoflow

@gaoflow gaoflow commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

URI records crash on printing when the wire target is not valid UTF-8, and silently corrupt backslashes and quotes on a text round trip, because to_text() emits the raw target with a bare .decode() while from_text() unescapes:

>>> dns.rdata.from_wire("in", "uri", bytes.fromhex("0001000168747470ff"), 0, 9).to_text()
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 4

Auditing the other free-form string fields: HINFO, X25, ISDN, NAPTR, and CAA escape correctly on output but still parse \ddd through the str-based unescape(), so \255 becomes the two octets c3 bf instead of ff — the same double-encoding #321 fixed for TXT-like records with unescape_to_bytes(), which never reached these types. BIND round-trips \255 in these fields as a single octet.

This adds Tokenizer.get_bytes()/as_bytes() (bytes counterpart of get_string(), built on unescape_to_bytes(), with the length limit measured in bytes) and uses it for these fields, and URI.to_styled_text() now escapes the target with _escapify like its siblings. Tests cover the URI crash and round-trip repros, plus \ddd -> octet and from_text(to_text()) identity over all 256 octet values for all six types and TXT. Full pytest suite passes locally; ruff and pyright clean.

…TR, and CAA

URI.to_text() emitted the raw target with a bare decode(), crashing on
wire-legal non-UTF-8 targets and corrupting backslashes and quotes on a
text round trip.  The parse side of URI, HINFO, X25, ISDN, NAPTR, and
CAA also processed \ddd escapes as code points and then UTF-8 encoded
them, so escapes greater than \127 became two octets instead of one
(the double-encoding issue rthalley#321 fixed for TXT-like records).  Add
Tokenizer.get_bytes()/as_bytes() and use them for these fields, and
escape the URI target on output, so that from_text(to_text()) is the
identity for all octet values.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant