Check DoH servers with a query to a random name

The issue with benchmarking DoH servers is that some responses can
be directly served by a CDN, while others require a round trip to
the origin that can be significantly more expensive.

Random padding was an attempt at mitigating this. Unfortunately,
some servers (Tencent) ignore the padding. We end up with a query
for the root zone served by the Tencent CDN very quickly, but
anything else is orders of magnitude slower.

So, measure a query within the reserved "test." zone instead.
Caching resolvers should either know that "test." is undelegated,
or have it in their negative cache already, so this is unlikely to
trigger an actual query to authoritative servers.

Take it as an opportunity to check that we don't get anything but
a NXDOMAIN response for nonexistent domains.
This commit is contained in:
Frank Denis 2020-08-05 14:39:09 +02:00
parent 60d4c98f31
commit f3157b0a42
1 changed files with 31 additions and 0 deletions

View File

@ -390,6 +390,29 @@ func dohTestPacket(msgID uint16) []byte {
return body
}
func dohNXTestPacket(msgID uint16) []byte {
msg := dns.Msg{}
qName := make([]byte, 16)
charset := "abcdefghijklmnopqrstuvwxyz"
for i := range qName {
qName[i] = charset[rand.Intn(len(charset))]
}
msg.SetQuestion(string(qName)+".dnscrypt.test.", dns.TypeNS)
msg.Id = msgID
msg.MsgHdr.RecursionDesired = true
msg.SetEdns0(uint16(MaxDNSPacketSize), false)
ext := new(dns.EDNS0_PADDING)
ext.Padding = make([]byte, 16)
crypto_rand.Read(ext.Padding)
edns0 := msg.IsEdns0()
edns0.Option = append(edns0.Option, ext)
body, err := msg.Pack()
if err != nil {
dlog.Fatal(err)
}
return body
}
func fetchDoHServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isNew bool) (ServerInfo, error) {
// If an IP has been provided, use it forever.
// Or else, if the fallback server and the DoH server are operated
@ -424,6 +447,7 @@ func fetchDoHServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isN
}
dlog.Debugf("Server [%s] doesn't appear to support POST; falling back to GET requests", name)
}
body = dohNXTestPacket(0xcafe)
serverResponse, tls, rtt, err := proxy.xTransport.DoHQuery(useGet, url, body, proxy.timeout)
if err != nil {
return ServerInfo{}, err
@ -431,6 +455,13 @@ func fetchDoHServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isN
if tls == nil || !tls.HandshakeComplete {
return ServerInfo{}, errors.New("TLS handshake failed")
}
msg := dns.Msg{}
if err := msg.Unpack(serverResponse); err != nil {
return ServerInfo{}, err
}
if msg.Rcode != dns.RcodeNameError {
dlog.Criticalf("[%s] may be a lying resolver", name)
}
protocol := tls.NegotiatedProtocol
if len(protocol) == 0 {
protocol = "h1"