]> Cypherpunks repositories - gostls13.git/commitdiff
net: add IsNotFound field to DNSError
authorShubham Sharma <shubham.sha12@gmail.com>
Thu, 21 Mar 2019 15:40:12 +0000 (21:10 +0530)
committerBrad Fitzpatrick <bradfitz@golang.org>
Tue, 23 Apr 2019 14:11:33 +0000 (14:11 +0000)
This adds the ability to determine if a lookup error was
due to a non-existent hostname. Previously users needed
to do string matching on the DNSError.Err value.

Fixes #28635

Change-Id: If4bd3ad32cbc2db5614f2c6b72e0a9161d813efa
Reviewed-on: https://go-review.googlesource.com/c/go/+/168597
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
src/net/cgo_unix.go
src/net/dnsclient_unix.go
src/net/dnsclient_unix_test.go
src/net/lookup.go
src/net/lookup_test.go
src/net/lookup_windows.go
src/net/net.go

index 6420fd05e7aae355e0083814eec4c195b7e94fd2..2baab5f193396886ac28767e24019fb838d18fd7 100644 (file)
@@ -158,6 +158,7 @@ func cgoLookupIPCNAME(network, name string) (addrs []IPAddr, cname string, err e
        var res *C.struct_addrinfo
        gerrno, err := C.getaddrinfo((*C.char)(unsafe.Pointer(&h[0])), nil, &hints, &res)
        if gerrno != 0 {
+               isErrorNoSuchHost := false
                switch gerrno {
                case C.EAI_SYSTEM:
                        if err == nil {
@@ -172,10 +173,12 @@ func cgoLookupIPCNAME(network, name string) (addrs []IPAddr, cname string, err e
                        }
                case C.EAI_NONAME:
                        err = errNoSuchHost
+                       isErrorNoSuchHost = true
                default:
                        err = addrinfoErrno(gerrno)
                }
-               return nil, "", &DNSError{Err: err.Error(), Name: name}
+
+               return nil, "", &DNSError{Err: err.Error(), Name: name, IsNotFound: isErrorNoSuchHost}
        }
        defer C.freeaddrinfo(res)
 
index 7ed4ea8708c2c212680f86c0d1af647057a16007..478ee51a81cc5a13da24fad57bd3982ffcac0779 100644 (file)
@@ -284,10 +284,8 @@ func (r *Resolver) tryOneName(ctx context.Context, cfg *dnsConfig, name string,
                                if err == errNoSuchHost {
                                        // The name does not exist, so trying
                                        // another server won't help.
-                                       //
-                                       // TODO: indicate this in a more
-                                       // obvious way, such as a field on
-                                       // DNSError?
+
+                                       dnsErr.IsNotFound = true
                                        return p, server, dnsErr
                                }
                                lastErr = dnsErr
@@ -306,9 +304,8 @@ func (r *Resolver) tryOneName(ctx context.Context, cfg *dnsConfig, name string,
                        if err == errNoSuchHost {
                                // The name does not exist, so trying another
                                // server won't help.
-                               //
-                               // TODO: indicate this in a more obvious way,
-                               // such as a field on DNSError?
+
+                               lastErr.(*DNSError).IsNotFound = true
                                return p, server, lastErr
                        }
                }
@@ -398,7 +395,7 @@ func (r *Resolver) lookup(ctx context.Context, name string, qtype dnsmessage.Typ
                // Other lookups might allow broader name syntax
                // (for example Multicast DNS allows UTF-8; see RFC 6762).
                // For consistency with libc resolvers, report no such host.
-               return dnsmessage.Parser{}, "", &DNSError{Err: errNoSuchHost.Error(), Name: name}
+               return dnsmessage.Parser{}, "", &DNSError{Err: errNoSuchHost.Error(), Name: name, IsNotFound: true}
        }
        resolvConf.tryUpdate("/etc/resolv.conf")
        resolvConf.mu.RLock()
@@ -575,7 +572,7 @@ func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, name string, order
        }
        if !isDomainName(name) {
                // See comment in func lookup above about use of errNoSuchHost.
-               return nil, dnsmessage.Name{}, &DNSError{Err: errNoSuchHost.Error(), Name: name}
+               return nil, dnsmessage.Name{}, &DNSError{Err: errNoSuchHost.Error(), Name: name, IsNotFound: true}
        }
        resolvConf.tryUpdate("/etc/resolv.conf")
        resolvConf.mu.RLock()
index f1ed58c837d856db3bba008551bc0dc09a36855d..1b67494e51ed35e8dbe60c126387a958560b2222 100644 (file)
@@ -666,7 +666,7 @@ func TestErrorForOriginalNameWhenSearching(t *testing.T) {
                wantErr      *DNSError
        }{
                {true, &DNSError{Name: fqdn, Err: "server misbehaving", IsTemporary: true}},
-               {false, &DNSError{Name: fqdn, Err: errNoSuchHost.Error()}},
+               {false, &DNSError{Name: fqdn, Err: errNoSuchHost.Error(), IsNotFound: true}},
        }
        for _, tt := range cases {
                r := Resolver{PreferGo: true, StrictErrors: tt.strictErrors, Dial: fake.DialContext}
@@ -1138,9 +1138,10 @@ func TestStrictErrorsLookupIP(t *testing.T) {
        }
        makeNxDomain := func() error {
                return &DNSError{
-                       Err:    errNoSuchHost.Error(),
-                       Name:   name,
-                       Server: server,
+                       Err:        errNoSuchHost.Error(),
+                       Name:       name,
+                       Server:     server,
+                       IsNotFound: true,
                }
        }
 
@@ -1472,6 +1473,32 @@ func TestIssue8434(t *testing.T) {
        }
 }
 
+func TestIssueNoSuchHostExists(t *testing.T) {
+       err := lookupWithFake(fakeDNSServer{
+               rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
+                       return dnsmessage.Message{
+                               Header: dnsmessage.Header{
+                                       ID:       q.ID,
+                                       Response: true,
+                                       RCode:    dnsmessage.RCodeNameError,
+                               },
+                               Questions: q.Questions,
+                       }, nil
+               },
+       }, "golang.org.", dnsmessage.TypeALL)
+       if err == nil {
+               t.Fatal("expected an error")
+       }
+       if _, ok := err.(Error); !ok {
+               t.Fatalf("err = %#v; wanted something supporting net.Error", err)
+       }
+       if de, ok := err.(*DNSError); !ok {
+               t.Fatalf("err = %#v; wanted a *net.DNSError", err)
+       } else if !de.IsNotFound {
+               t.Fatalf("IsNotFound = false for err = %#v; want IsNotFound == true", err)
+       }
+}
+
 // TestNoSuchHost verifies that tryOneName works correctly when the domain does
 // not exist.
 //
@@ -1541,6 +1568,9 @@ func TestNoSuchHost(t *testing.T) {
                        if de.Err != errNoSuchHost.Error() {
                                t.Fatalf("Err = %#v; wanted %q", de.Err, errNoSuchHost.Error())
                        }
+                       if !de.IsNotFound {
+                               t.Fatalf("IsNotFound = %v wanted true", de.IsNotFound)
+                       }
                })
        }
 }
index 0af1e2c28904a7b451c6497ef5f32a0783688811..24d0d25c3a28a46b2046b714e526d88b86840ab3 100644 (file)
@@ -177,7 +177,7 @@ func (r *Resolver) LookupHost(ctx context.Context, host string) (addrs []string,
        // Make sure that no matter what we do later, host=="" is rejected.
        // parseIP, for example, does accept empty strings.
        if host == "" {
-               return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host}
+               return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
        }
        if ip, _ := parseIPZone(host); ip != nil {
                return []string{host}, nil
@@ -238,7 +238,7 @@ func (r *Resolver) lookupIPAddr(ctx context.Context, network, host string) ([]IP
        // Make sure that no matter what we do later, host=="" is rejected.
        // parseIP, for example, does accept empty strings.
        if host == "" {
-               return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host}
+               return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
        }
        if ip, zone := parseIPZone(host); ip != nil {
                return []IPAddr{{IP: ip, Zone: zone}}, nil
index 28a895e15d13c03cf11d0cfd335f6ad5f8ef8546..ed477a78c9e79a0db7bc2b2b4d5822e0b4421473 100644 (file)
@@ -877,6 +877,9 @@ func TestLookupNonLDH(t *testing.T) {
        if !strings.HasSuffix(err.Error(), errNoSuchHost.Error()) {
                t.Fatalf("lookup error = %v, want %v", err, errNoSuchHost)
        }
+       if !err.(*DNSError).IsNotFound {
+               t.Fatalf("lookup error = %v, want true", err.(*DNSError).IsNotFound)
+       }
 }
 
 func TestLookupContextCancel(t *testing.T) {
index 8a68d18a674b3ff8f6fc611431b89e2c5ac1e8f7..cd071c54b00f2dfe960201aaaa609a9b9b729131 100644 (file)
@@ -56,7 +56,12 @@ func lookupProtocol(ctx context.Context, name string) (int, error) {
                        if proto, err := lookupProtocolMap(name); err == nil {
                                return proto, nil
                        }
-                       r.err = &DNSError{Err: r.err.Error(), Name: name}
+
+                       dnsError := &DNSError{Err: r.err.Error(), Name: name}
+                       if r.err == errNoSuchHost {
+                               dnsError.IsNotFound = true
+                       }
+                       r.err = dnsError
                }
                return r.proto, r.err
        case <-ctx.Done():
@@ -98,7 +103,12 @@ func (r *Resolver) lookupIP(ctx context.Context, network, name string) ([]IPAddr
                var result *syscall.AddrinfoW
                e := syscall.GetAddrInfoW(syscall.StringToUTF16Ptr(name), nil, &hints, &result)
                if e != nil {
-                       return nil, &DNSError{Err: winError("getaddrinfow", e).Error(), Name: name}
+                       err := winError("getaddrinfow", e)
+                       dnsError := &DNSError{Err: err.Error(), Name: name}
+                       if err == errNoSuchHost {
+                               dnsError.IsNotFound = true
+                       }
+                       return nil, dnsError
                }
                defer syscall.FreeAddrInfoW(result)
                addrs := make([]IPAddr, 0, 5)
@@ -176,7 +186,12 @@ func (r *Resolver) lookupPort(ctx context.Context, network, service string) (int
                if port, err := lookupPortMap(network, service); err == nil {
                        return port, nil
                }
-               return 0, &DNSError{Err: winError("getaddrinfow", e).Error(), Name: network + "/" + service}
+               err := winError("getaddrinfow", e)
+               dnsError := &DNSError{Err: err.Error(), Name: network + "/" + service}
+               if err == errNoSuchHost {
+                       dnsError.IsNotFound = true
+               }
+               return 0, dnsError
        }
        defer syscall.FreeAddrInfoW(result)
        if result == nil {
index b44ecb6711fae2a331d066effe2884ecf346fa71..0e078620a5e81abbe64ef1b18f50ec50a302f91b 100644 (file)
@@ -579,6 +579,7 @@ type DNSError struct {
        Server      string // server used
        IsTimeout   bool   // if true, timed out; not all timeouts set this
        IsTemporary bool   // if true, error is temporary; not all errors set this
+       IsNotFound  bool   // if true, host could not be found
 }
 
 func (e *DNSError) Error() string {