dnspython
dnspython copied to clipboard
DNS-over-QUIC (RFC 9250)
Motivation It appears that DoQ might overtake DoT for authoritative servers in the future. Some auth servers are already offering it on an experimental basis. It would be good to be able to probe for and interact with those servers
Describe the solution you'd like.
Something that looks just like dns.query.tls and dns.query.https, but with QUIC as the transport.
I've looked into doing this already, and definitely would like to have the feature you describe. It's not totally easy though, as we have to do some I/O wrappers around the aioquic QUIC state machine for trio, sync, and perhaps curio too (if we keep supporting curio in the future). All of this looks very do-able, but I haven't done it yet.
In case it helps, you can use ns1.desec.io for testing (currently, only with the IPv4 endpoint):
`kdig` example
$ kdig -4 +quic DNSKEY dedyn.io @ns1.desec.io
;; QUIC session (QUICv1)-(TLS1.3)-(ECDHE-X25519)-(ECDSA-SECP256R1-SHA256)-(AES-128-GCM)
;; ->>HEADER<<- opcode: QUERY; status: NOERROR; id: 0
;; Flags: qr aa rd; QUERY: 1; ANSWER: 1; AUTHORITY: 0; ADDITIONAL: 1
;; EDNS PSEUDOSECTION:
;; Version: 0; flags: ; UDP size: 4096 B; ext-rcode: NOERROR
;; QUESTION SECTION:
;; dedyn.io. IN DNSKEY
;; ANSWER SECTION:
dedyn.io. 3600 IN DNSKEY 257 3 13 RE3z7xIaCEesKfIV3BjYhxF5TlRCPfjEvjiULKTs5TNZzZwvDkJojpIeFkj3n9nQMrjmr4cHl/xs6a3x/ccxCw==
;; Received 117 B
;; Time 2022-09-01 20:11:02 EDT
;; From 45.54.76.1@853(UDP) in 134.3 ms
@nils-wisiol
I had a crack at this again today, and managed to get the aioquic state machine to work with trio and actually do a quic DNS query to aioquic's sample DoQ server. I tried the one @peterthomassen listed but I got a self-signed certificate error from aioquic.
It's going to take a while to get this into a form that could be integrated into dnspython and which has sync and async support, but at least I know a way to do it now.
This is most excellent news! By all means let me know how I can help with testing and so on.
I tried the one @peterthomassen listed but I got a self-signed certificate error from aioquic.
RFC 9250 Section 5.1 says that the authentication mechanism is unspecified at the time. In particular, it has not been decided that DoQ certificates should rely on the usual TLS PKI because that may lead to conceptual chicken-egg problems. It may be more prudent to have in-band signaling e.g. via TLSA, but that creates a bunch of other questions. As usual, the devil is in the details. (Similarly, the DoT specification says explicitly that it does not deal with recursive-to-auth traffic.)
The bottomline is that DoQ authentication is currently ill-defined, and implementation of transport encryption is currently opportunistic in many implementations as of now. As an example, the kdig command above does not fail with a self-signed certificate. It may make sense to implement similar behavior in dnspython until the above questions are settled.
I'll leave a way to set the TLS verification mode in the API. If you set to to ssl.CERT_NONE, then it will just accept the certificate. If I do this, I can successfully query ns1.desec.io:
(venv) 34% python connection.py
; DoQ for dedyn.io DNSKEY at 45.54.76.1 port 853
id 0
opcode QUERY
rcode NOERROR
flags QR AA RD
;QUESTION
dedyn.io. IN DNSKEY
;ANSWER
dedyn.io. 3600 IN DNSKEY 257 3 13 RE3z7xIaCEesKfIV3BjYhxF5TlRCPfjE vjiULKTs5TNZzZwvDkJojpIeFkj3n9nQ Mrjmr4cHl/xs6a3x/ccxCw==
;AUTHORITY
;ADDITIONAL
Also note that when this code becomes more real, it's really only going to be suitable for ordinary queries, not giant AXFRs. We can probably do AXFR over QUIC (assuming some server implements it), but aioquic doesn't currently seem to have a way to apply a per-stream backpressure when reading, nor a per-stream backpressure when writing. So if we read a giant thing, we're going to buffer it into memory as fast as possible, and the consumer side might not keep up. Similarly if the code I have at the moment were used for a really really big output AXFR, it would buffer everything into the QUIC state machine's buffer as there's no way to block the writing thread/coroutine. I doubt this is much of a problem in practice for the near term.
I have working code for asyncio, trio, and synchronous I/O. The next steps are integrating into dsnpython on a branch, and writing tests.
Again, let us know when we can help here. Thanks!
The curious are invited to look at the quic branch, especially examples/doq.py.
Merged.