Skip to content

Fix double escaping of non-printable bytes in SVCB string-list values#1280

Open
gaoflow wants to merge 1 commit into
rthalley:mainfrom
gaoflow:fix-svcb-stringlist-escaping
Open

Fix double escaping of non-printable bytes in SVCB string-list values#1280
gaoflow wants to merge 1 commit into
rthalley:mainfrom
gaoflow:fix-svcb-stringlist-escaping

Conversation

@gaoflow

@gaoflow gaoflow commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

_StringList.to_text() (used by the alpn and docpath SvcParams) applies the two SVCB escaping levels in the wrong order for bytes outside 0x20-0x7e: the list-level escape renders 0x00 as \000, and dns.rdata._escapify() then escapes that backslash, emitting \\000. The parser composes the levels correctly per RFC 9460 appendix A.1 (_unescape decodes \ddd, then _split handles \, and \\), so it reads \\000 back as the three literal characters 000 — a record's own to_text() output re-parses to a different value. ALPN ids are opaque bytes on the wire, so a record received from the wire and dumped to a zone file can silently change value when reloaded:

>>> rd = dns.rdata.from_text("IN", "HTTPS", r'1 . alpn="h\0002"')
>>> rd.params[1].ids
(b'h\x002',)
>>> rd.to_text()
'1 . alpn="h\\\\0002"'
>>> dns.rdata.from_text("IN", "HTTPS", rd.to_text()).params[1].ids
(b'h0002',)

The fix makes the inner escape purely list-level (\ and ,, operating on bytes) and leaves all character-string escaping to the single outer dns.rdata._escapify(). Comma/backslash escaping in printable ids is emitted exactly as before; non-printables now come out as \ddd and a quote byte as \", both of which the existing parser already accepts.

test_misc_escape pinned the old \\010\\010 output; updated it to the round-trippable form plus an assertion that the expected text parses back equal.

The list-level _escapify escaped non-printable bytes into \ddd, and
dns.rdata._escapify() then escaped that backslash, emitting \ddd,
which parses back as the literal characters 'ddd'.  Make the inner
escape purely list-level (',' and '\') and let dns.rdata._escapify()
apply the character-string escaping once.
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