David Fifield 6 лет назад
Родитель
Сommit
0022cdd16c
2 измененных файлов с 66 добавлено и 17 удалено
  1. 6 5
      dns/dns.go
  2. 60 12
      dnstt-server/main.go

+ 6 - 5
dns/dns.go

@@ -52,10 +52,11 @@ const (
 	ClassIN = 1
 
 	// https://tools.ietf.org/html/rfc1035#section-4.1.1
-	RcodeNoError        = 0
-	RcodeFormatError    = 1
-	RcodeNameError      = 3 // a.k.a. NXDOMAIN
-	RcodeNotImplemented = 4
+	RcodeNoError         = 0
+	RcodeFormatError     = 1
+	RcodeNameError       = 3 // a.k.a. NXDOMAIN
+	RcodeNotImplemented  = 4
+	ExtendedRcodeBadVers = 16
 )
 
 // Name represents a domain name, a sequence of labels each of which is 63
@@ -385,7 +386,7 @@ func (builder *messageBuilder) writeName(name Name) {
 		// Has this suffix already been encoded in the message?
 		if ptr, ok := builder.nameCache[name[i:].String()]; ok && ptr&0x3fff == ptr {
 			// If so, we can write a compression pointer.
-			binary.Write(&builder.w, binary.BigEndian, uint16(0xc000 | ptr))
+			binary.Write(&builder.w, binary.BigEndian, uint16(0xc000|ptr))
 			return
 		}
 		// Not cached; we must encode this label verbatim. Store a cache

+ 60 - 12
dnstt-server/main.go

@@ -193,6 +193,54 @@ func responseFor(query *dns.Message, domain dns.Name) (*dns.Message, turbotunnel
 		// QR != 0, this is not a query. Don't even send a response.
 		return nil, clientID, nil
 	}
+
+	// Check for EDNS(0) support. Include our own OPT RR only if we receive
+	// one from the requestor.
+	// https://tools.ietf.org/html/rfc6891#section-6.1.1
+	// "Lack of presence of an OPT record in a request MUST be taken as an
+	// indication that the requestor does not implement any part of this
+	// specification and that the responder MUST NOT include an OPT record
+	// in its response."
+	payloadSize := 0
+	for _, rr := range query.Additional {
+		if rr.Type != dns.RRTypeOPT {
+			continue
+		}
+		if len(resp.Additional) != 0 {
+			// https://tools.ietf.org/html/rfc6891#section-6.1.1
+			// "If a query message with more than one OPT RR is
+			// received, a FORMERR (RCODE=1) MUST be returned."
+			resp.Flags |= dns.RcodeFormatError
+			return resp, clientID, nil
+		}
+		resp.Additional = append(resp.Additional, dns.RR{
+			Name:  dns.Name{},
+			Type:  dns.RRTypeOPT,
+			Class: 4096, // responder's UDP payload size
+			TTL:   0,
+			Data:  []byte{},
+		})
+		additional := &resp.Additional[0]
+
+		version := (rr.TTL >> 16) & 0xff
+		if version != 0 {
+			// https://tools.ietf.org/html/rfc6891#section-6.1.1
+			// "If a responder does not implement the VERSION level
+			// of the request, then it MUST respond with
+			// RCODE=BADVERS."
+			resp.Flags |= dns.ExtendedRcodeBadVers & 0xf
+			additional.TTL = (dns.ExtendedRcodeBadVers >> 4) << 24
+		}
+
+		payloadSize = int(rr.Class)
+		if payloadSize < 512 {
+			// https://tools.ietf.org/html/rfc6891#section-6.1.1
+			// "Values lower than 512 MUST be treated as equal to
+			// 512."
+			payloadSize = 512
+		}
+	}
+
 	if query.Flags&0x7800 != 0 {
 		// We don't support OPCODE != QUERY.
 		resp.Flags |= dns.RcodeNotImplemented
@@ -265,8 +313,7 @@ func loop(dnsConn net.PacketConn, domain dns.Name, ttConn *turbotunnel.QueuePack
 
 func recvLoop(domain dns.Name, dnsConn net.PacketConn, ttConn *turbotunnel.QueuePacketConn, ch chan<- *record) error {
 	for {
-		// One byte longer than we want, to check for truncation.
-		var buf [513]byte
+		var buf [4096]byte
 		n, addr, err := dnsConn.ReadFrom(buf[:])
 		if err != nil {
 			if err, ok := err.(net.Error); ok && err.Temporary() {
@@ -275,10 +322,6 @@ func recvLoop(domain dns.Name, dnsConn net.PacketConn, ttConn *turbotunnel.Queue
 			}
 			return err
 		}
-		if n == len(buf) {
-			log.Printf("%v: ReadFrom: truncated packet", addr)
-			continue
-		}
 
 		// Got a UDP packet. Try to parse it as a DNS message.
 		query, err := dns.MessageFromWireFormat(buf[:n])
@@ -326,6 +369,16 @@ func sendLoop(dnsConn net.PacketConn, ttConn *turbotunnel.QueuePacketConn, ch <-
 		if rec.Resp.Rcode() == dns.RcodeNoError && len(rec.Resp.Question) == 1 {
 			// If it's a non-error response, we can fill the Answer
 			// section with downstream packets.
+
+			rec.Resp.Answer = []dns.RR{
+				{
+					Name: rec.Resp.Question[0].Name,
+					Type: dns.RRTypeTXT,
+					TTL:  responseTTL,
+					Data: nil, // will be filled in below
+				},
+			}
+
 			var payload bytes.Buffer
 
 			limit := dnsMessageCapacity(rec.Resp.Question[0].Name)
@@ -377,12 +430,7 @@ func sendLoop(dnsConn net.PacketConn, ttConn *turbotunnel.QueuePacketConn, ch <-
 			}
 			timer.Stop()
 
-			rec.Resp.Answer = append(rec.Resp.Answer, dns.RR{
-				Name: rec.Resp.Question[0].Name,
-				Type: dns.RRTypeTXT,
-				TTL:  responseTTL,
-				Data: dns.EncodeRDataTXT(payload.Bytes()),
-			})
+			rec.Resp.Answer[0].Data = dns.EncodeRDataTXT(payload.Bytes())
 		}
 
 		buf, err := rec.Resp.WireFormat()