diff --git a/src/net/dnsclient_unix.go b/src/net/dnsclient_unix.go index 2bf01b314c..74029d2311 100644 --- a/src/net/dnsclient_unix.go +++ b/src/net/dnsclient_unix.go @@ -51,9 +51,9 @@ var ( errServerTemporarilyMisbehaving = errors.New("server misbehaving") ) -func newRequest(q dnsmessage.Question) (id uint16, udpReq, tcpReq []byte, err error) { +func newRequest(q dnsmessage.Question, ad bool) (id uint16, udpReq, tcpReq []byte, err error) { id = uint16(randInt()) - b := dnsmessage.NewBuilder(make([]byte, 2, 514), dnsmessage.Header{ID: id, RecursionDesired: true}) + b := dnsmessage.NewBuilder(make([]byte, 2, 514), dnsmessage.Header{ID: id, RecursionDesired: true, AuthenticData: ad}) if err := b.StartQuestions(); err != nil { return 0, nil, nil, err } @@ -157,9 +157,9 @@ func dnsStreamRoundTrip(c Conn, id uint16, query dnsmessage.Question, b []byte) } // exchange sends a query on the connection and hopes for a response. -func (r *Resolver) exchange(ctx context.Context, server string, q dnsmessage.Question, timeout time.Duration, useTCP bool) (dnsmessage.Parser, dnsmessage.Header, error) { +func (r *Resolver) exchange(ctx context.Context, server string, q dnsmessage.Question, timeout time.Duration, useTCP, ad bool) (dnsmessage.Parser, dnsmessage.Header, error) { q.Class = dnsmessage.ClassINET - id, udpReq, tcpReq, err := newRequest(q) + id, udpReq, tcpReq, err := newRequest(q, ad) if err != nil { return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotMarshalDNSMessage } @@ -273,7 +273,7 @@ func (r *Resolver) tryOneName(ctx context.Context, cfg *dnsConfig, name string, for j := uint32(0); j < sLen; j++ { server := cfg.servers[(serverOffset+j)%sLen] - p, h, err := r.exchange(ctx, server, q, cfg.timeout, cfg.useTCP) + p, h, err := r.exchange(ctx, server, q, cfg.timeout, cfg.useTCP, cfg.trustAD) if err != nil { dnsErr := &DNSError{ Err: err.Error(), diff --git a/src/net/dnsclient_unix_test.go b/src/net/dnsclient_unix_test.go index 17798e434b..553b220cbe 100644 --- a/src/net/dnsclient_unix_test.go +++ b/src/net/dnsclient_unix_test.go @@ -79,7 +79,7 @@ func TestDNSTransportFallback(t *testing.T) { for _, tt := range dnsTransportFallbackTests { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - _, h, err := r.exchange(ctx, tt.server, tt.question, time.Second, useUDPOrTCP) + _, h, err := r.exchange(ctx, tt.server, tt.question, time.Second, useUDPOrTCP, false) if err != nil { t.Error(err) continue @@ -135,7 +135,7 @@ func TestSpecialDomainName(t *testing.T) { for _, tt := range specialDomainNameTests { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - _, h, err := r.exchange(ctx, server, tt.question, 3*time.Second, useUDPOrTCP) + _, h, err := r.exchange(ctx, server, tt.question, 3*time.Second, useUDPOrTCP, false) if err != nil { t.Error(err) continue @@ -1593,7 +1593,7 @@ func TestDNSDialTCP(t *testing.T) { } r := Resolver{PreferGo: true, Dial: fake.DialContext} ctx := context.Background() - _, _, err := r.exchange(ctx, "0.0.0.0", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), time.Second, useUDPOrTCP) + _, _, err := r.exchange(ctx, "0.0.0.0", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), time.Second, useUDPOrTCP, false) if err != nil { t.Fatal("exhange failed:", err) } @@ -1746,7 +1746,7 @@ func TestDNSUseTCP(t *testing.T) { r := Resolver{PreferGo: true, Dial: fake.DialContext} ctx, cancel := context.WithCancel(context.Background()) defer cancel() - _, _, err := r.exchange(ctx, "0.0.0.0", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), time.Second, useTCPOnly) + _, _, err := r.exchange(ctx, "0.0.0.0", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), time.Second, useTCPOnly, false) if err != nil { t.Fatal("exchange failed:", err) } @@ -2344,3 +2344,68 @@ func TestLongDNSNames(t *testing.T) { } } } + +func TestDNSTrustAD(t *testing.T) { + fake := fakeDNSServer{ + rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { + if q.Questions[0].Name.String() == "notrustad.go.dev." && q.Header.AuthenticData { + t.Error("unexpected AD bit") + } + + if q.Questions[0].Name.String() == "trustad.go.dev." && !q.Header.AuthenticData { + t.Error("expected AD bit") + } + + r := dnsmessage.Message{ + Header: dnsmessage.Header{ + ID: q.Header.ID, + Response: true, + RCode: dnsmessage.RCodeSuccess, + }, + Questions: q.Questions, + } + if q.Questions[0].Type == dnsmessage.TypeA { + r.Answers = []dnsmessage.Resource{ + { + Header: dnsmessage.ResourceHeader{ + Name: q.Questions[0].Name, + Type: dnsmessage.TypeA, + Class: dnsmessage.ClassINET, + Length: 4, + }, + Body: &dnsmessage.AResource{ + A: TestAddr, + }, + }, + } + } + + return r, nil + }} + + r := &Resolver{PreferGo: true, Dial: fake.DialContext} + + conf, err := newResolvConfTest() + if err != nil { + t.Fatal(err) + } + defer conf.teardown() + + err = conf.writeAndUpdate([]string{"nameserver 127.0.0.1"}) + if err != nil { + t.Fatal(err) + } + + if _, err := r.LookupIPAddr(context.Background(), "notrustad.go.dev"); err != nil { + t.Errorf("lookup failed: %v", err) + } + + err = conf.writeAndUpdate([]string{"nameserver 127.0.0.1", "options trust-ad"}) + if err != nil { + t.Fatal(err) + } + + if _, err := r.LookupIPAddr(context.Background(), "trustad.go.dev"); err != nil { + t.Errorf("lookup failed: %v", err) + } +} diff --git a/src/net/dnsconfig.go b/src/net/dnsconfig.go index 091b548301..37252b5a0b 100644 --- a/src/net/dnsconfig.go +++ b/src/net/dnsconfig.go @@ -29,6 +29,7 @@ type dnsConfig struct { soffset uint32 // used by serverOffset singleRequest bool // use sequential A and AAAA queries instead of parallel queries useTCP bool // force usage of TCP for DNS resolutions + trustAD bool // add AD flag to queries } // serverOffset returns an offset that can be used to determine diff --git a/src/net/dnsconfig_unix.go b/src/net/dnsconfig_unix.go index 65098f6827..962314b4b6 100644 --- a/src/net/dnsconfig_unix.go +++ b/src/net/dnsconfig_unix.go @@ -113,6 +113,8 @@ func dnsReadConfig(filename string) *dnsConfig { // https://www.freebsd.org/cgi/man.cgi?query=resolv.conf&sektion=5&manpath=freebsd-release-ports // https://man.openbsd.org/resolv.conf.5 conf.useTCP = true + case s == "trust-ad": + conf.trustAD = true case s == "edns0": // We use EDNS by default. // Ignore this option.