git » dnss » commit 031d3e0

httpserver: Retry DNS lookups over TCP

author Alberto Bertogli
2021-06-12 14:18:17 UTC
committer Alberto Bertogli
2021-06-12 14:23:02 UTC
parent 900fbffe9a08268130519d24666518d1d859fa5c

httpserver: Retry DNS lookups over TCP

When a DNS lookup over UDP fails (which often is because of the response
being too large), retry them over TCP.

internal/httpserver/server.go +25 -1
tests/minidns.go +8 -2

diff --git a/internal/httpserver/server.go b/internal/httpserver/server.go
index b9205dd..380445f 100644
--- a/internal/httpserver/server.go
+++ b/internal/httpserver/server.go
@@ -115,7 +115,7 @@ func (s *Server) resolveDoH(tr *trace.Trace, w http.ResponseWriter, dnsQuery []b
 	tr.Question(r.Question)
 
 	// Do the DNS request, get the reply.
-	fromUp, err := dns.Exchange(r, s.Upstream)
+	fromUp, err := exchange(tr, r, s.Upstream)
 	if err != nil {
 		err = tr.Errorf("dns exchange error: %v", err)
 		http.Error(w, err.Error(), http.StatusFailedDependency)
@@ -143,3 +143,27 @@ func (s *Server) resolveDoH(tr *trace.Trace, w http.ResponseWriter, dnsQuery []b
 	w.WriteHeader(http.StatusOK)
 	w.Write(packed)
 }
+
+func exchange(tr *trace.Trace, r *dns.Msg, addr string) (*dns.Msg, error) {
+	reply, err := dns.Exchange(r, addr)
+	if err == nil && !reply.Truncated {
+		tr.Printf("UDP exchange successful")
+		return reply, err
+	}
+
+	// If we had issues over UDP, or the message was truncated, retry over
+	// TCP. We don't try beyond that.
+	if err != nil {
+		tr.Printf("error on UDP exchange: %v", err)
+	} else if reply.Truncated {
+		tr.Printf("UDP exchange returned truncated reply: %v", reply.MsgHdr)
+	}
+	tr.Printf("retrying on TCP")
+
+	c := &dns.Client{
+		Net: "tcp",
+	}
+
+	reply, _, err = c.Exchange(r, addr)
+	return reply, err
+}
diff --git a/tests/minidns.go b/tests/minidns.go
index 2a6b80a..14b1eaf 100644
--- a/tests/minidns.go
+++ b/tests/minidns.go
@@ -109,7 +109,7 @@ func (m *miniDNS) listenAndServeUDP(addr string) {
 			continue
 		}
 		q := msg.Questions[0]
-		log.Infof("%v/%-5d   Q: %s %s %s",
+		log.Infof("[UDP] %v/%-5d   Q: %s %s %s",
 			addr, msg.ID, q.Name, q.Type, q.Class)
 
 		reply := m.handle(msg)
@@ -117,6 +117,12 @@ func (m *miniDNS) listenAndServeUDP(addr string) {
 		if err != nil {
 			log.Fatalf("error packing reply: %v", err)
 		}
+		if len(rbuf) > 512 {
+			log.Infof("truncating UDP response of %v bytes", len(rbuf))
+			reply.Truncated = true
+			reply.Answers = nil
+			rbuf, err = reply.Pack()
+		}
 
 		conn.WriteTo(rbuf, addr)
 	}
@@ -151,7 +157,7 @@ func (m *miniDNS) listenAndServeTCP(addr string) {
 			continue
 		}
 		q := msg.Questions[0]
-		log.Infof("%v/%-5d   Q: %s %s %s",
+		log.Infof("[TCP] %v/%-5d   Q: %s %s %s",
 			addr, msg.ID, q.Name, q.Type, q.Class)
 
 		reply := m.handle(msg)