Forráskód Böngészése

wgengine/netstack: handle multiple magicDNS queries per UDP socket (#4708)

Fixes: #4686

Signed-off-by: Tom DNetto <[email protected]>
Tom 3 éve
szülő
commit
fc5839864b
1 módosított fájl, 26 hozzáadás és 11 törlés
  1. 26 11
      wgengine/netstack/netstack.go

+ 26 - 11
wgengine/netstack/netstack.go

@@ -386,7 +386,6 @@ func (ns *Impl) handleLocalPackets(p *packet.Parsed, t *tstun.Wrapper) filter.Re
 		}
 	}
 
-
 	var pn tcpip.NetworkProtocolNumber
 	switch p.IPVersion {
 	case 4:
@@ -900,20 +899,36 @@ func (ns *Impl) handleMagicDNSUDP(srcAddr netaddr.IPPort, c *gonet.UDPConn) {
 	// In practice, implementations are advised not to exceed 512 bytes
 	// due to fragmenting. Just to be sure, we bump all the way to the MTU.
 	const maxUDPReqSize = mtu
+	// Packets are being generated by the local host, so there should be
+	// very, very little latency. 150ms was chosen as something of an upper
+	// bound on resource usage, while hopefully still being long enough for
+	// a heavily loaded system.
+	const readDeadline = 150 * time.Millisecond
 
 	defer c.Close()
 	q := make([]byte, maxUDPReqSize)
-	n, err := c.Read(q)
-	if err != nil {
-		ns.logf("dns udp read: %v", err)
-		return
-	}
-	resp, err := ns.dns.Query(context.Background(), q[:n], srcAddr)
-	if err != nil {
-		ns.logf("dns udp query: %v", err)
-		return
+
+	// libresolv from glibc is quite adamant that transmitting multiple DNS
+	// requests down the same UDP socket is valid. To support this, we read
+	// in a loop (with a tight deadline so we don't chew too many resources).
+	//
+	// See: https://github.com/bminor/glibc/blob/f7fbb99652eceb1b6b55e4be931649df5946497c/resolv/res_send.c#L995
+	for {
+		c.SetReadDeadline(time.Now().Add(readDeadline))
+		n, _, err := c.ReadFrom(q)
+		if err != nil {
+			if oe, ok := err.(*net.OpError); !(ok && oe.Timeout()) {
+				ns.logf("dns udp read: %v", err) // log non-timeout errors
+			}
+			return
+		}
+		resp, err := ns.dns.Query(context.Background(), q[:n], srcAddr)
+		if err != nil {
+			ns.logf("dns udp query: %v", err)
+			return
+		}
+		c.Write(resp)
 	}
-	c.Write(resp)
 }
 
 // forwardUDP proxies between client (with addr clientAddr) and dstAddr.