DNSCrypt / doh-server

Fast, mature, secure DoH and ODoH server proxy written in Rust. Previously known as doh-proxy and rust-doh.
MIT License
738 stars 63 forks source link

Retry over TCP #62

Closed mschirrmeister closed 3 years ago

mschirrmeister commented 3 years ago

Hello,

I noticed that if the configured upstream dns server has the truncate bit set, the doh-proxy does not retry via TCP. I think the standard is that a resolver should retry via TCP, if that tc bit is set. Is this a bug, or can there be anything configured?

Depending on the dns server, the doh-proxy returns either an empty response to the client or an error. I received an empty response when the server returned in his answer Additional data type OPT (9.9.9.9). If there was no additional data (1.1.1.1), then the client returns a message that the query failed.

doh-proxy started with default 9.9.9.9

marco@dust ~/m/g/p/doh-server (master)> ~/.cargo/bin/doh-proxy --allow-odoh-post --tls-cert-key-path localhost.pem --tls-cert-path localhost.pem

DNS query

marco@dust ~> kdig @127.0.0.1 -p 3000 +https +tls-ca=/Users/marco/mydata/git/public/doh-server/localhost.pem +tls-host=localhost akamai.com txt
;; TLS session (TLS1.3)-(ECDHE-X25519)-(RSA-PSS-RSAE-SHA512)-(AES-256-GCM)
;; HTTP session (HTTP/2-POST)-(localhost/dns-query)-(status: 200)
;; ->>HEADER<<- opcode: QUERY; status: NOERROR; id: 0
;; Flags: qr tc rd ra; QUERY: 1; ANSWER: 0; AUTHORITY: 0; ADDITIONAL: 1

;; EDNS PSEUDOSECTION:
;; Version: 0; flags: ; UDP size: 512 B; ext-rcode: NOERROR
;; PADDING: 25 B

;; QUESTION SECTION:
;; akamai.com.              IN  TXT

;; Received 68 B
;; Time 2021-06-08 22:46:25 CEST
;; From 127.0.0.1@3000(TCP) in 27.2 ms

doh-proxy started with cloudflare

marco@dust ~/m/g/p/doh-server (master)> ~/.cargo/bin/doh-proxy --allow-odoh-post --tls-cert-key-path localhost.pem --tls-cert-path localhost.pem --server-address 1.1.1.1:53

DNS query

marco@dust ~> kdig @127.0.0.1 -p 3000 +https +tls-ca=/Users/marco/mydata/git/public/doh-server/localhost.pem +tls-host=localhost akamai.com txt
;; WARNING: TLS, peer took too long to respond
;; ERROR: failed to query server 127.0.0.1@3000(TCP)

Besides the tcp retry, some dns servers seem to respond differently. If you try a lookup multiple times with 9.9.9.9 you will eventually get a large udp response, with fragmented packets.

jedisct1 commented 3 years ago

The use case for this is as a front-end for your own server.

Queries are sent over UDP, but with a large advertised UDP response size, so that TCP is never necessary. Cloudflare and Google enforce a maximum UDP size to mitigate DDoS attack vectors, but these restrictions don't apply to a local server.

Anyway, TCP retries are implemented in 485afd5 . Thanks!

pengelana commented 3 years ago

I love this doh-server because its small, very light and fast, even when the server is busy, it doesn't increase much on memory or CPU.

Just an idea if possible to add option to enable or disable the TCP?

jedisct1 commented 3 years ago

Such an option can be added, but unless you are using a server explicitly configured to block UDP packets above the size of a valid response, it will never use TCP.

Also, only 1/10 of the connections are allowed to use TCP.

mschirrmeister commented 3 years ago

I guess Quad9 ignores the large advertised UDP response size for a few queries before finally sending an answer over UDP. In what scenario does your implementation from 485afd5 kick in? I tested it again with Quad9, but it does not retry with TCP.

But as you mentioned, I will use it with a local dns server next to it, who is doing all the classic dns work.

jedisct1 commented 3 years ago

It should retry with TCP if it gets a truncated response.

mschirrmeister commented 3 years ago

It is unfortunately not doing the retry. I pulled the latest from github, ran again the cargo install command and tested same way as above with Quad9.