Skip to content

Instantly share code, notes, and snippets.

@a7md0
Last active May 9, 2025 13:18
Show Gist options
  • Save a7md0/82e04e6b26bfd92d4a5bd4901e0822f7 to your computer and use it in GitHub Desktop.
Save a7md0/82e04e6b26bfd92d4a5bd4901e0822f7 to your computer and use it in GitHub Desktop.
Generate dns-rr line that can be used in dnsmasq for SVCB and possibly HTTPS records for DNS over HTTPS (DoH), DNS over QUIC (DoQ) and possibly DNS over TLS (DoT)
#!/usr/bin/python3
import json
import binascii
# pip3 install dnspython
import dns.name
import dns.rdataclass
import dns.rdatatype
import dns.rdata
def encode_rr_line(owner: str, rdata_text: str, rdatatype: dns.rdatatype):
rdata = dns.rdata.from_text(dns.rdataclass.IN, rdatatype, rdata_text)
rdata_wire = rdata.to_wire(origin=None, compress=False)
rdata_type_number = int(rdatatype)
hex_encoded = binascii.hexlify(rdata_wire).decode().upper()
# dnsmasq output
dns_rr_line = f'dns-rr={owner},{rdata_type_number},{hex_encoded}'
return {
"rdata_text": rdata_text,
'dns_rr_line': dns_rr_line,
'rdatatype': rdatatype.name
}
owner = '_dns.resolver.arpa.'
host = 'unbound.local.'
svcb_doh_rdata = f'1 {host} alpn="h2" port=443 ipv4hint=192.168.2.254 ipv6hint=fd2c:7841:b288:: key7="/dns-query{{?dns}}"'
svcb_doh = encode_rr_line(owner, svcb_doh_rdata, dns.rdatatype.SVCB)
print(json.dumps(svcb_doh, indent=4))
svcb_dot_rdata = f'2 {host} alpn="dot" port=853 ipv4hint=192.168.2.254 ipv6hint=fd2c:7841:b288::'
svcb_dot = encode_rr_line(owner, svcb_dot_rdata, dns.rdatatype.SVCB)
print(json.dumps(svcb_dot, indent=4))
# Warning: Didn't work parsing error on response
# https_doh_rdata = f'1 {host} alpn="h2" port=443 ipv4hint=192.168.2.254 ipv6hint=fd2c:7841:b288:: dohpath="/dns-query"'
# https_doh = encode_rr_line(owner, https_doh_rdata, dns.rdatatype.HTTPS)
# print(json.dumps(https_doh, indent=4))
@a7md0
Copy link
Author

a7md0 commented May 9, 2025

@a7md0
Copy link
Author

a7md0 commented May 9, 2025

What does Cloudflare return for querying _dns.resolver.arpa of TYPE 64 (SVCB)?

$ dig @1.1.1.1 TYPE64 _dns.resolver.arpa +qr
;; ANSWER SECTION:
_dns.resolver.arpa.     300     IN      SVCB    1 one.one.one.one. alpn="h2,h3" port=443 ipv4hint=1.1.1.1,1.0.0.1 ipv6hint=2606:4700:4700::1111,2606:4700:4700::1001 key7="/dns-query{?dns}"
_dns.resolver.arpa.     300     IN      SVCB    2 one.one.one.one. alpn="dot" port=853 ipv4hint=1.1.1.1,1.0.0.1 ipv6hint=2606:4700:4700::1111,2606:4700:4700::1001

Findings

  1. It seems the host is mandatory as SNI for TLS
  2. alpn is mandatory comma separated for supported protocols (h2 = HTTP2 / h3 = HTTP3 [QUIC])
  3. port is optional
  4. ipv4hint is optional comma separated list of IPv4 addresses to connect to without resolving the host
  5. ipv6hint is optional comma separated list of IPv6 addresses to connect to without resolving the host
  6. key7 is required for DoH and it should have the weird {?dns} after the path or it would break the response

@a7md0
Copy link
Author

a7md0 commented May 9, 2025

dnsmasq return zero TTL when using dns-rr entries, which can be changed by declaring local-ttl=60

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment