mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-26 11:35:35 +00:00
bcaae3e074
This change (subject to some limitations) looks for the EDNS OPT record in queries and responses, clamping the size field to fit within our DNS receive buffer. If the size field is smaller than the DNS receive buffer then it is left unchanged. I think we will eventually need to transition to fully processing the DNS queries to handle all situations, but this should cover the most common case. Mostly fixes #2066 Signed-off-by: Adrian Dewhurst <adrian@tailscale.com>
158 lines
3.5 KiB
Go
158 lines
3.5 KiB
Go
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package resolver
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/miekg/dns"
|
|
"inet.af/netaddr"
|
|
)
|
|
|
|
// This file exists to isolate the test infrastructure
|
|
// that depends on github.com/miekg/dns
|
|
// from the rest, which only depends on dnsmessage.
|
|
|
|
// resolveToIP returns a handler function which responds
|
|
// to queries of type A it receives with an A record containing ipv4,
|
|
// to queries of type AAAA with an AAAA record containing ipv6,
|
|
// to queries of type NS with an NS record containg name.
|
|
func resolveToIP(ipv4, ipv6 netaddr.IP, ns string) dns.HandlerFunc {
|
|
return func(w dns.ResponseWriter, req *dns.Msg) {
|
|
m := new(dns.Msg)
|
|
m.SetReply(req)
|
|
|
|
if len(req.Question) != 1 {
|
|
panic("not a single-question request")
|
|
}
|
|
question := req.Question[0]
|
|
|
|
var ans dns.RR
|
|
switch question.Qtype {
|
|
case dns.TypeA:
|
|
ans = &dns.A{
|
|
Hdr: dns.RR_Header{
|
|
Name: question.Name,
|
|
Rrtype: dns.TypeA,
|
|
Class: dns.ClassINET,
|
|
},
|
|
A: ipv4.IPAddr().IP,
|
|
}
|
|
case dns.TypeAAAA:
|
|
ans = &dns.AAAA{
|
|
Hdr: dns.RR_Header{
|
|
Name: question.Name,
|
|
Rrtype: dns.TypeAAAA,
|
|
Class: dns.ClassINET,
|
|
},
|
|
AAAA: ipv6.IPAddr().IP,
|
|
}
|
|
case dns.TypeNS:
|
|
ans = &dns.NS{
|
|
Hdr: dns.RR_Header{
|
|
Name: question.Name,
|
|
Rrtype: dns.TypeNS,
|
|
Class: dns.ClassINET,
|
|
},
|
|
Ns: ns,
|
|
}
|
|
}
|
|
|
|
m.Answer = append(m.Answer, ans)
|
|
w.WriteMsg(m)
|
|
}
|
|
}
|
|
|
|
// resolveToTXT returns a handler function which responds to queries of type TXT
|
|
// it receives with the strings in txts.
|
|
func resolveToTXT(txts []string, ednsMaxSize uint16) dns.HandlerFunc {
|
|
return func(w dns.ResponseWriter, req *dns.Msg) {
|
|
m := new(dns.Msg)
|
|
m.SetReply(req)
|
|
|
|
if len(req.Question) != 1 {
|
|
panic("not a single-question request")
|
|
}
|
|
question := req.Question[0]
|
|
|
|
if question.Qtype != dns.TypeTXT {
|
|
w.WriteMsg(m)
|
|
return
|
|
}
|
|
|
|
ans := &dns.TXT{
|
|
Hdr: dns.RR_Header{
|
|
Name: question.Name,
|
|
Rrtype: dns.TypeTXT,
|
|
Class: dns.ClassINET,
|
|
},
|
|
Txt: txts,
|
|
}
|
|
|
|
m.Answer = append(m.Answer, ans)
|
|
|
|
queryInfo := &dns.TXT{
|
|
Hdr: dns.RR_Header{
|
|
Name: "query-info.test.",
|
|
Rrtype: dns.TypeTXT,
|
|
Class: dns.ClassINET,
|
|
},
|
|
}
|
|
|
|
if edns := req.IsEdns0(); edns == nil {
|
|
queryInfo.Txt = []string{"EDNS=false"}
|
|
} else {
|
|
queryInfo.Txt = []string{"EDNS=true", fmt.Sprintf("maxSize=%v", edns.UDPSize())}
|
|
}
|
|
|
|
m.Extra = append(m.Extra, queryInfo)
|
|
|
|
if ednsMaxSize > 0 {
|
|
m.SetEdns0(ednsMaxSize, false)
|
|
}
|
|
|
|
if err := w.WriteMsg(m); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
var resolveToNXDOMAIN = dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
|
|
m := new(dns.Msg)
|
|
m.SetRcode(req, dns.RcodeNameError)
|
|
w.WriteMsg(m)
|
|
})
|
|
|
|
func serveDNS(tb testing.TB, addr string, records ...interface{}) *dns.Server {
|
|
if len(records)%2 != 0 {
|
|
panic("must have an even number of record values")
|
|
}
|
|
mux := dns.NewServeMux()
|
|
for i := 0; i < len(records); i += 2 {
|
|
name := records[i].(string)
|
|
handler := records[i+1].(dns.Handler)
|
|
mux.Handle(name, handler)
|
|
}
|
|
waitch := make(chan struct{})
|
|
server := &dns.Server{
|
|
Addr: addr,
|
|
Net: "udp",
|
|
Handler: mux,
|
|
NotifyStartedFunc: func() { close(waitch) },
|
|
ReusePort: true,
|
|
}
|
|
|
|
go func() {
|
|
err := server.ListenAndServe()
|
|
if err != nil {
|
|
panic(fmt.Sprintf("ListenAndServe(%q): %v", addr, err))
|
|
}
|
|
}()
|
|
|
|
<-waitch
|
|
return server
|
|
}
|