]> Cypherpunks repositories - gostls13.git/commitdiff
net: add Resolver.StrictErrors
authorPaul Marks <pmarks@google.com>
Wed, 2 Nov 2016 04:01:08 +0000 (21:01 -0700)
committerBrad Fitzpatrick <bradfitz@golang.org>
Thu, 9 Mar 2017 21:59:00 +0000 (21:59 +0000)
When LookupIP is performing multiple subqueries, this option causes a
timeout/servfail affecting a single query to abort the whole operation,
instead of returning a partial (IPv4/IPv6-only) result.

Similarly, operations that walk the DNS search list will also abort when
encountering one of these errors.

Fixes #17448

Change-Id: Ice22e4aceb555c5a80d19bd1fde8b8fe87ac9517
Reviewed-on: https://go-review.googlesource.com/32572
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/net/dnsclient_unix.go
src/net/dnsclient_unix_test.go
src/net/lookup.go
src/net/lookup_unix.go
src/net/netgo_unix_test.go

index 4dd4e16b0fdf153ced644c629f67394f5b3797c1..6613dc7593c4710c2ee8c6af616caa757b13d941 100644 (file)
@@ -200,6 +200,11 @@ func tryOneName(ctx context.Context, cfg *dnsConfig, name string, qtype uint16)
                                if nerr, ok := err.(Error); ok && nerr.Timeout() {
                                        lastErr.(*DNSError).IsTimeout = true
                                }
+                               // Set IsTemporary for socket-level errors. Note that this flag
+                               // may also be used to indicate a SERVFAIL response.
+                               if _, ok := err.(*OpError); ok {
+                                       lastErr.(*DNSError).IsTemporary = true
+                               }
                                continue
                        }
                        // libresolv continues to the next server when it receives
@@ -314,7 +319,7 @@ func (conf *resolverConfig) releaseSema() {
        <-conf.ch
 }
 
-func lookup(ctx context.Context, name string, qtype uint16) (cname string, rrs []dnsRR, err error) {
+func (r *Resolver) lookup(ctx context.Context, name string, qtype uint16) (cname string, rrs []dnsRR, err error) {
        if !isDomainName(name) {
                // We used to use "invalid domain name" as the error,
                // but that is a detail of the specific lookup mechanism.
@@ -332,6 +337,11 @@ func lookup(ctx context.Context, name string, qtype uint16) (cname string, rrs [
                if err == nil {
                        break
                }
+               if nerr, ok := err.(Error); ok && nerr.Temporary() && r.StrictErrors {
+                       // If we hit a temporary error with StrictErrors enabled,
+                       // stop immediately instead of trying more names.
+                       break
+               }
        }
        if err, ok := err.(*DNSError); ok {
                // Show original name passed to lookup, not suffixed one.
@@ -432,11 +442,11 @@ func (o hostLookupOrder) String() string {
 // Normally we let cgo use the C library resolver instead of
 // depending on our lookup code, so that Go and C get the same
 // answers.
-func goLookupHost(ctx context.Context, name string) (addrs []string, err error) {
-       return goLookupHostOrder(ctx, name, hostLookupFilesDNS)
+func (r *Resolver) goLookupHost(ctx context.Context, name string) (addrs []string, err error) {
+       return r.goLookupHostOrder(ctx, name, hostLookupFilesDNS)
 }
 
-func goLookupHostOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []string, err error) {
+func (r *Resolver) goLookupHostOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []string, err error) {
        if order == hostLookupFilesDNS || order == hostLookupFiles {
                // Use entries from /etc/hosts if they match.
                addrs = lookupStaticHost(name)
@@ -444,7 +454,7 @@ func goLookupHostOrder(ctx context.Context, name string, order hostLookupOrder)
                        return
                }
        }
-       ips, _, err := goLookupIPCNAMEOrder(ctx, name, order)
+       ips, _, err := r.goLookupIPCNAMEOrder(ctx, name, order)
        if err != nil {
                return
        }
@@ -470,13 +480,13 @@ func goLookupIPFiles(name string) (addrs []IPAddr) {
 
 // goLookupIP is the native Go implementation of LookupIP.
 // The libc versions are in cgo_*.go.
-func goLookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
+func (r *Resolver) goLookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
        order := systemConf().hostLookupOrder(host)
-       addrs, _, err = goLookupIPCNAMEOrder(ctx, host, order)
+       addrs, _, err = r.goLookupIPCNAMEOrder(ctx, host, order)
        return
 }
 
-func goLookupIPCNAMEOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []IPAddr, cname string, err error) {
+func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []IPAddr, cname string, err error) {
        if order == hostLookupFilesDNS || order == hostLookupFiles {
                addrs = goLookupIPFiles(name)
                if len(addrs) > 0 || order == hostLookupFiles {
@@ -506,11 +516,16 @@ func goLookupIPCNAMEOrder(ctx context.Context, name string, order hostLookupOrde
                                lane <- racer{cname, rrs, err}
                        }(qtype)
                }
+               hitStrictError := false
                for range qtypes {
                        racer := <-lane
                        if racer.error != nil {
-                               // Prefer error for original name.
-                               if lastErr == nil || fqdn == name+"." {
+                               if nerr, ok := racer.error.(Error); ok && nerr.Temporary() && r.StrictErrors {
+                                       // This error will abort the nameList loop.
+                                       hitStrictError = true
+                                       lastErr = racer.error
+                               } else if lastErr == nil || fqdn == name+"." {
+                                       // Prefer error for original name.
                                        lastErr = racer.error
                                }
                                continue
@@ -520,6 +535,13 @@ func goLookupIPCNAMEOrder(ctx context.Context, name string, order hostLookupOrde
                                cname = racer.cname
                        }
                }
+               if hitStrictError {
+                       // If either family hit an error with StrictErrors enabled,
+                       // discard all addresses. This ensures that network flakiness
+                       // cannot turn a dualstack hostname IPv4/IPv6-only.
+                       addrs = nil
+                       break
+               }
                if len(addrs) > 0 {
                        break
                }
@@ -543,9 +565,9 @@ func goLookupIPCNAMEOrder(ctx context.Context, name string, order hostLookupOrde
 }
 
 // goLookupCNAME is the native Go (non-cgo) implementation of LookupCNAME.
-func goLookupCNAME(ctx context.Context, host string) (cname string, err error) {
+func (r *Resolver) goLookupCNAME(ctx context.Context, host string) (cname string, err error) {
        order := systemConf().hostLookupOrder(host)
-       _, cname, err = goLookupIPCNAMEOrder(ctx, host, order)
+       _, cname, err = r.goLookupIPCNAMEOrder(ctx, host, order)
        return
 }
 
@@ -554,7 +576,7 @@ func goLookupCNAME(ctx context.Context, host string) (cname string, err error) {
 // only if cgoLookupPTR is the stub in cgo_stub.go).
 // Normally we let cgo use the C library resolver instead of depending
 // on our lookup code, so that Go and C get the same answers.
-func goLookupPTR(ctx context.Context, addr string) ([]string, error) {
+func (r *Resolver) goLookupPTR(ctx context.Context, addr string) ([]string, error) {
        names := lookupStaticAddr(addr)
        if len(names) > 0 {
                return names, nil
@@ -563,7 +585,7 @@ func goLookupPTR(ctx context.Context, addr string) ([]string, error) {
        if err != nil {
                return nil, err
        }
-       _, rrs, err := lookup(ctx, arpa, dnsTypePTR)
+       _, rrs, err := r.lookup(ctx, arpa, dnsTypePTR)
        if err != nil {
                return nil, err
        }
index 4464804c701e4cfe99d0dbcf3c602c0d6d0e1913..ec28af68fb43211c701482afde456336214c8499 100644 (file)
@@ -24,6 +24,9 @@ import (
 // Test address from 192.0.2.0/24 block, reserved by RFC 5737 for documentation.
 const TestAddr uint32 = 0xc0000201
 
+// Test address from 2001:db8::/32 block, reserved by RFC 3849 for documentation.
+var TestAddr6 = [16]byte{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
+
 var dnsTransportFallbackTests = []struct {
        server  string
        name    string
@@ -142,7 +145,7 @@ func TestAvoidDNSName(t *testing.T) {
 
 // Issue 13705: don't try to resolve onion addresses, etc
 func TestLookupTorOnion(t *testing.T) {
-       addrs, err := goLookupIP(context.Background(), "foo.onion")
+       addrs, err := DefaultResolver.goLookupIP(context.Background(), "foo.onion")
        if len(addrs) > 0 {
                t.Errorf("unexpected addresses: %v", addrs)
        }
@@ -258,7 +261,7 @@ func TestUpdateResolvConf(t *testing.T) {
                        for j := 0; j < N; j++ {
                                go func(name string) {
                                        defer wg.Done()
-                                       ips, err := goLookupIP(context.Background(), name)
+                                       ips, err := DefaultResolver.goLookupIP(context.Background(), name)
                                        if err != nil {
                                                t.Error(err)
                                                return
@@ -406,7 +409,7 @@ func TestGoLookupIPWithResolverConfig(t *testing.T) {
                        t.Error(err)
                        continue
                }
-               addrs, err := goLookupIP(context.Background(), tt.name)
+               addrs, err := DefaultResolver.goLookupIP(context.Background(), tt.name)
                if err != nil {
                        // This test uses external network connectivity.
                        // We need to take care with errors on both
@@ -456,14 +459,14 @@ func TestGoLookupIPOrderFallbackToFile(t *testing.T) {
                name := fmt.Sprintf("order %v", order)
 
                // First ensure that we get an error when contacting a non-existent host.
-               _, _, err := goLookupIPCNAMEOrder(context.Background(), "notarealhost", order)
+               _, _, err := DefaultResolver.goLookupIPCNAMEOrder(context.Background(), "notarealhost", order)
                if err == nil {
                        t.Errorf("%s: expected error while looking up name not in hosts file", name)
                        continue
                }
 
                // Now check that we get an address when the name appears in the hosts file.
-               addrs, _, err := goLookupIPCNAMEOrder(context.Background(), "thor", order) // entry is in "testdata/hosts"
+               addrs, _, err := DefaultResolver.goLookupIPCNAMEOrder(context.Background(), "thor", order) // entry is in "testdata/hosts"
                if err != nil {
                        t.Errorf("%s: expected to successfully lookup host entry", name)
                        continue
@@ -519,14 +522,24 @@ func TestErrorForOriginalNameWhenSearching(t *testing.T) {
                return r, nil
        }
 
-       _, err = goLookupIP(context.Background(), fqdn)
-       if err == nil {
-               t.Fatal("expected an error")
+       cases := []struct {
+               strictErrors bool
+               wantErr      *DNSError
+       }{
+               {true, &DNSError{Name: fqdn, Err: "server misbehaving", IsTemporary: true}},
+               {false, &DNSError{Name: fqdn, Err: errNoSuchHost.Error()}},
        }
+       for _, tt := range cases {
+               r := Resolver{StrictErrors: tt.strictErrors}
+               _, err = r.goLookupIP(context.Background(), fqdn)
+               if err == nil {
+                       t.Fatal("expected an error")
+               }
 
-       want := &DNSError{Name: fqdn, Err: errNoSuchHost.Error()}
-       if err, ok := err.(*DNSError); !ok || err.Name != want.Name || err.Err != want.Err {
-               t.Errorf("got %v; want %v", err, want)
+               want := tt.wantErr
+               if err, ok := err.(*DNSError); !ok || err.Name != want.Name || err.Err != want.Err || err.IsTemporary != want.IsTemporary {
+                       t.Errorf("got %v; want %v", err, want)
+               }
        }
 }
 
@@ -579,7 +592,7 @@ func TestIgnoreLameReferrals(t *testing.T) {
                return r, nil
        }
 
-       addrs, err := goLookupIP(context.Background(), "www.golang.org")
+       addrs, err := DefaultResolver.goLookupIP(context.Background(), "www.golang.org")
        if err != nil {
                t.Fatal(err)
        }
@@ -598,7 +611,7 @@ func BenchmarkGoLookupIP(b *testing.B) {
        ctx := context.Background()
 
        for i := 0; i < b.N; i++ {
-               goLookupIP(ctx, "www.example.com")
+               DefaultResolver.goLookupIP(ctx, "www.example.com")
        }
 }
 
@@ -607,7 +620,7 @@ func BenchmarkGoLookupIPNoSuchHost(b *testing.B) {
        ctx := context.Background()
 
        for i := 0; i < b.N; i++ {
-               goLookupIP(ctx, "some.nonexistent")
+               DefaultResolver.goLookupIP(ctx, "some.nonexistent")
        }
 }
 
@@ -630,7 +643,7 @@ func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) {
        ctx := context.Background()
 
        for i := 0; i < b.N; i++ {
-               goLookupIP(ctx, "www.example.com")
+               DefaultResolver.goLookupIP(ctx, "www.example.com")
        }
 }
 
@@ -861,3 +874,309 @@ func mockTXTResponse(q *dnsMsg) *dnsMsg {
 
        return r
 }
+
+// Issue 17448. With StrictErrors enabled, temporary errors should make
+// LookupIP fail rather than return a partial result.
+func TestStrictErrorsLookupIP(t *testing.T) {
+       origTestHookDNSDialer := testHookDNSDialer
+       defer func() { testHookDNSDialer = origTestHookDNSDialer }()
+
+       conf, err := newResolvConfTest()
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer conf.teardown()
+
+       confData := []string{
+               "nameserver 192.0.2.53",
+               "search x.golang.org y.golang.org",
+       }
+       if err := conf.writeAndUpdate(confData); err != nil {
+               t.Fatal(err)
+       }
+
+       const name = "test"
+       const server = "192.0.2.53:53"
+       const searchX = "test.x.golang.org."
+       const searchY = "test.y.golang.org."
+       const ip4 = "192.0.2.1"
+       const ip6 = "2001:db8::1"
+
+       type resolveWhichEnum int
+       const (
+               resolveOK resolveWhichEnum = iota
+               resolveOpError
+               resolveServfail
+               resolveTimeout
+       )
+
+       makeTempError := func(err string) error {
+               return &DNSError{
+                       Err:         err,
+                       Name:        name,
+                       Server:      server,
+                       IsTemporary: true,
+               }
+       }
+       makeTimeout := func() error {
+               return &DNSError{
+                       Err:       poll.ErrTimeout.Error(),
+                       Name:      name,
+                       Server:    server,
+                       IsTimeout: true,
+               }
+       }
+       makeNxDomain := func() error {
+               return &DNSError{
+                       Err:    errNoSuchHost.Error(),
+                       Name:   name,
+                       Server: server,
+               }
+       }
+
+       cases := []struct {
+               desc          string
+               resolveWhich  func(quest *dnsQuestion) resolveWhichEnum
+               wantStrictErr error
+               wantLaxErr    error
+               wantIPs       []string
+       }{
+               {
+                       desc: "No errors",
+                       resolveWhich: func(quest *dnsQuestion) resolveWhichEnum {
+                               return resolveOK
+                       },
+                       wantIPs: []string{ip4, ip6},
+               },
+               {
+                       desc: "searchX error fails in strict mode",
+                       resolveWhich: func(quest *dnsQuestion) resolveWhichEnum {
+                               if quest.Name == searchX {
+                                       return resolveTimeout
+                               }
+                               return resolveOK
+                       },
+                       wantStrictErr: makeTimeout(),
+                       wantIPs:       []string{ip4, ip6},
+               },
+               {
+                       desc: "searchX IPv4-only timeout fails in strict mode",
+                       resolveWhich: func(quest *dnsQuestion) resolveWhichEnum {
+                               if quest.Name == searchX && quest.Qtype == dnsTypeA {
+                                       return resolveTimeout
+                               }
+                               return resolveOK
+                       },
+                       wantStrictErr: makeTimeout(),
+                       wantIPs:       []string{ip4, ip6},
+               },
+               {
+                       desc: "searchX IPv6-only servfail fails in strict mode",
+                       resolveWhich: func(quest *dnsQuestion) resolveWhichEnum {
+                               if quest.Name == searchX && quest.Qtype == dnsTypeAAAA {
+                                       return resolveServfail
+                               }
+                               return resolveOK
+                       },
+                       wantStrictErr: makeTempError("server misbehaving"),
+                       wantIPs:       []string{ip4, ip6},
+               },
+               {
+                       desc: "searchY error always fails",
+                       resolveWhich: func(quest *dnsQuestion) resolveWhichEnum {
+                               if quest.Name == searchY {
+                                       return resolveTimeout
+                               }
+                               return resolveOK
+                       },
+                       wantStrictErr: makeTimeout(),
+                       wantLaxErr:    makeNxDomain(), // This one reaches the "test." FQDN.
+               },
+               {
+                       desc: "searchY IPv4-only socket error fails in strict mode",
+                       resolveWhich: func(quest *dnsQuestion) resolveWhichEnum {
+                               if quest.Name == searchY && quest.Qtype == dnsTypeA {
+                                       return resolveOpError
+                               }
+                               return resolveOK
+                       },
+                       wantStrictErr: makeTempError("write: socket on fire"),
+                       wantIPs:       []string{ip6},
+               },
+               {
+                       desc: "searchY IPv6-only timeout fails in strict mode",
+                       resolveWhich: func(quest *dnsQuestion) resolveWhichEnum {
+                               if quest.Name == searchY && quest.Qtype == dnsTypeAAAA {
+                                       return resolveTimeout
+                               }
+                               return resolveOK
+                       },
+                       wantStrictErr: makeTimeout(),
+                       wantIPs:       []string{ip4},
+               },
+       }
+
+       for i, tt := range cases {
+               d := &fakeDNSDialer{}
+               testHookDNSDialer = func() dnsDialer { return d }
+
+               d.rh = func(s string, q *dnsMsg, deadline time.Time) (*dnsMsg, error) {
+                       t.Log(s, q)
+
+                       switch tt.resolveWhich(&q.question[0]) {
+                       case resolveOK:
+                               // Handle below.
+                       case resolveOpError:
+                               return nil, &OpError{Op: "write", Err: fmt.Errorf("socket on fire")}
+                       case resolveServfail:
+                               return &dnsMsg{dnsMsgHdr: dnsMsgHdr{id: q.id, rcode: dnsRcodeServerFailure}}, nil
+                       case resolveTimeout:
+                               return nil, poll.ErrTimeout
+                       default:
+                               t.Fatal("Impossible resolveWhich")
+                       }
+
+                       switch q.question[0].Name {
+                       case searchX, name + ".":
+                               // Return NXDOMAIN to utilize the search list.
+                               return &dnsMsg{dnsMsgHdr: dnsMsgHdr{id: q.id, rcode: dnsRcodeNameError}}, nil
+                       case searchY:
+                               // Return records below.
+                       default:
+                               return nil, fmt.Errorf("Unexpected Name: %v", q.question[0].Name)
+                       }
+
+                       r := &dnsMsg{
+                               dnsMsgHdr: dnsMsgHdr{
+                                       id:       q.id,
+                                       response: true,
+                               },
+                               question: q.question,
+                       }
+                       switch q.question[0].Qtype {
+                       case dnsTypeA:
+                               r.answer = []dnsRR{
+                                       &dnsRR_A{
+                                               Hdr: dnsRR_Header{
+                                                       Name:     q.question[0].Name,
+                                                       Rrtype:   dnsTypeA,
+                                                       Class:    dnsClassINET,
+                                                       Rdlength: 4,
+                                               },
+                                               A: TestAddr,
+                                       },
+                               }
+                       case dnsTypeAAAA:
+                               r.answer = []dnsRR{
+                                       &dnsRR_AAAA{
+                                               Hdr: dnsRR_Header{
+                                                       Name:     q.question[0].Name,
+                                                       Rrtype:   dnsTypeAAAA,
+                                                       Class:    dnsClassINET,
+                                                       Rdlength: 16,
+                                               },
+                                               AAAA: TestAddr6,
+                                       },
+                               }
+                       default:
+                               return nil, fmt.Errorf("Unexpected Qtype: %v", q.question[0].Qtype)
+                       }
+                       return r, nil
+               }
+
+               for _, strict := range []bool{true, false} {
+                       r := Resolver{StrictErrors: strict}
+                       ips, err := r.goLookupIP(context.Background(), name)
+
+                       var wantErr error
+                       if strict {
+                               wantErr = tt.wantStrictErr
+                       } else {
+                               wantErr = tt.wantLaxErr
+                       }
+                       if !reflect.DeepEqual(err, wantErr) {
+                               t.Errorf("#%d (%s) strict=%v: got err %#v; want %#v", i, tt.desc, strict, err, wantErr)
+                       }
+
+                       gotIPs := map[string]struct{}{}
+                       for _, ip := range ips {
+                               gotIPs[ip.String()] = struct{}{}
+                       }
+                       wantIPs := map[string]struct{}{}
+                       if wantErr == nil {
+                               for _, ip := range tt.wantIPs {
+                                       wantIPs[ip] = struct{}{}
+                               }
+                       }
+                       if !reflect.DeepEqual(gotIPs, wantIPs) {
+                               t.Errorf("#%d (%s) strict=%v: got ips %v; want %v", i, tt.desc, strict, gotIPs, wantIPs)
+                       }
+               }
+       }
+}
+
+// Issue 17448. With StrictErrors enabled, temporary errors should make
+// LookupTXT stop walking the search list.
+func TestStrictErrorsLookupTXT(t *testing.T) {
+       origTestHookDNSDialer := testHookDNSDialer
+       defer func() { testHookDNSDialer = origTestHookDNSDialer }()
+
+       conf, err := newResolvConfTest()
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer conf.teardown()
+
+       confData := []string{
+               "nameserver 192.0.2.53",
+               "search x.golang.org y.golang.org",
+       }
+       if err := conf.writeAndUpdate(confData); err != nil {
+               t.Fatal(err)
+       }
+
+       const name = "test"
+       const server = "192.0.2.53:53"
+       const searchX = "test.x.golang.org."
+       const searchY = "test.y.golang.org."
+       const txt = "Hello World"
+
+       d := &fakeDNSDialer{}
+       testHookDNSDialer = func() dnsDialer { return d }
+
+       d.rh = func(s string, q *dnsMsg, deadline time.Time) (*dnsMsg, error) {
+               t.Log(s, q)
+
+               switch q.question[0].Name {
+               case searchX:
+                       return nil, poll.ErrTimeout
+               case searchY:
+                       return mockTXTResponse(q), nil
+               default:
+                       return nil, fmt.Errorf("Unexpected Name: %v", q.question[0].Name)
+               }
+       }
+
+       for _, strict := range []bool{true, false} {
+               r := Resolver{StrictErrors: strict}
+               _, rrs, err := r.lookup(context.Background(), name, dnsTypeTXT)
+               var wantErr error
+               var wantRRs int
+               if strict {
+                       wantErr = &DNSError{
+                               Err:       poll.ErrTimeout.Error(),
+                               Name:      name,
+                               Server:    server,
+                               IsTimeout: true,
+                       }
+               } else {
+                       wantRRs = 1
+               }
+               if !reflect.DeepEqual(err, wantErr) {
+                       t.Errorf("strict=%v: got err %#v; want %#v", strict, err, wantErr)
+               }
+               if len(rrs) != wantRRs {
+                       t.Errorf("strict=%v: got %v; want %v", strict, len(rrs), wantRRs)
+               }
+       }
+}
index cc2013e432516d914d3bac53022228699ca19184..463b374aff2166615ecbe010866e4906882e7088 100644 (file)
@@ -97,6 +97,16 @@ type Resolver struct {
        // GODEBUG=netdns=go, but scoped to just this resolver.
        PreferGo bool
 
+       // StrictErrors controls the behavior of temporary errors
+       // (including timeout, socket errors, and SERVFAIL) when using
+       // Go's built-in resolver. For a query composed of multiple
+       // sub-queries (such as an A+AAAA address lookup, or walking the
+       // DNS search list), this option causes such errors to abort the
+       // whole query instead of returning a partial result. This is
+       // not enabled by default because it may affect compatibility
+       // with resolvers that process AAAA queries incorrectly.
+       StrictErrors bool
+
        // TODO(bradfitz): optional interface impl override hook
        // TODO(bradfitz): Timeout time.Duration?
 }
index be2ced9c393fd62c5e1b60879da135bd129c6494..8d4b7bddf4e551e5742b762749349d7b015c4f40 100644 (file)
@@ -57,12 +57,12 @@ func (r *Resolver) lookupHost(ctx context.Context, host string) (addrs []string,
                // cgo not available (or netgo); fall back to Go's DNS resolver
                order = hostLookupFilesDNS
        }
-       return goLookupHostOrder(ctx, host, order)
+       return r.goLookupHostOrder(ctx, host, order)
 }
 
 func (r *Resolver) lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
        if r.PreferGo {
-               return goLookupIP(ctx, host)
+               return r.goLookupIP(ctx, host)
        }
        order := systemConf().hostLookupOrder(host)
        if order == hostLookupCgo {
@@ -72,7 +72,7 @@ func (r *Resolver) lookupIP(ctx context.Context, host string) (addrs []IPAddr, e
                // cgo not available (or netgo); fall back to Go's DNS resolver
                order = hostLookupFilesDNS
        }
-       addrs, _, err = goLookupIPCNAMEOrder(ctx, host, order)
+       addrs, _, err = r.goLookupIPCNAMEOrder(ctx, host, order)
        return
 }
 
@@ -98,17 +98,17 @@ func (r *Resolver) lookupCNAME(ctx context.Context, name string) (string, error)
                        return cname, err
                }
        }
-       return goLookupCNAME(ctx, name)
+       return r.goLookupCNAME(ctx, name)
 }
 
-func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
+func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
        var target string
        if service == "" && proto == "" {
                target = name
        } else {
                target = "_" + service + "._" + proto + "." + name
        }
-       cname, rrs, err := lookup(ctx, target, dnsTypeSRV)
+       cname, rrs, err := r.lookup(ctx, target, dnsTypeSRV)
        if err != nil {
                return "", nil, err
        }
@@ -121,8 +121,8 @@ func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (st
        return cname, srvs, nil
 }
 
-func (*Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) {
-       _, rrs, err := lookup(ctx, name, dnsTypeMX)
+func (*Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) {
+       _, rrs, err := r.lookup(ctx, name, dnsTypeMX)
        if err != nil {
                return nil, err
        }
@@ -135,8 +135,8 @@ func (*Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) {
        return mxs, nil
 }
 
-func (*Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) {
-       _, rrs, err := lookup(ctx, name, dnsTypeNS)
+func (*Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) {
+       _, rrs, err := r.lookup(ctx, name, dnsTypeNS)
        if err != nil {
                return nil, err
        }
@@ -148,7 +148,7 @@ func (*Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) {
 }
 
 func (r *Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) {
-       _, rrs, err := lookup(ctx, name, dnsTypeTXT)
+       _, rrs, err := r.lookup(ctx, name, dnsTypeTXT)
        if err != nil {
                return nil, err
        }
@@ -165,5 +165,5 @@ func (r *Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error
                        return ptrs, err
                }
        }
-       return goLookupPTR(ctx, addr)
+       return r.goLookupPTR(ctx, addr)
 }
index 5f1eb19e1240bdc9907810482a3102ddb18e707e..47901b03cf5ce1affd31d401eaacc047322e2db1 100644 (file)
@@ -22,7 +22,7 @@ func TestGoLookupIP(t *testing.T) {
        if err != nil {
                t.Error(err)
        }
-       if _, err := goLookupIP(ctx, host); err != nil {
+       if _, err := DefaultResolver.goLookupIP(ctx, host); err != nil {
                t.Error(err)
        }
 }