diff --git a/lookup.go b/lookup.go index 5dbe657..6d8ff84 100644 --- a/lookup.go +++ b/lookup.go @@ -59,7 +59,7 @@ func (r *Resolver) LookupHost(ctx context.Context, host string) (addrs []string, } ip, err := netdev.GetHostByName(host) if err != nil { - return nil, &DNSError{Err: err.Error(), Name: host} + return nil, newLookupError(host, err) } return []string{ip.String()}, nil } @@ -88,7 +88,7 @@ func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, err } ip, err := netdev.GetHostByName(host) if err != nil { - return nil, &DNSError{Err: err.Error(), Name: host} + return nil, newLookupError(host, err) } return []IPAddr{{IP: ip.AsSlice(), Zone: ip.Zone()}}, nil } @@ -104,7 +104,7 @@ func (r *Resolver) LookupNetIP(ctx context.Context, network, host string) ([]net } ip, err := netdev.GetHostByName(host) if err != nil { - return nil, &DNSError{Err: err.Error(), Name: host} + return nil, newLookupError(host, err) } return []netip.Addr{ip}, nil } @@ -149,3 +149,23 @@ func (r *Resolver) LookupTXT(ctx context.Context, name string) ([]string, error) // errNoSuchHost is returned when the host lookup finds no matching records. var errNoSuchHost = errors.New("no such host") + +// newLookupError wraps a netdev resolver error in a DNSError for host. +// +// TINYGO: the netdev can report a DNSError of its own, and callers read +// IsNotFound and IsTimeout on the outer error, so carry those flags out. +func newLookupError(host string, err error) *DNSError { + dnsErr := &DNSError{Err: err.Error(), Name: host, UnwrapErr: err} + if inner, ok := err.(*DNSError); ok { + // Take the inner description and not its Error(), which repeats the + // "lookup " prefix that this error adds. + dnsErr.Err = inner.Err + dnsErr.IsNotFound = inner.IsNotFound + dnsErr.IsTimeout = inner.IsTimeout + dnsErr.IsTemporary = inner.IsTemporary + if inner.Server != "" { + dnsErr.Server = inner.Server + } + } + return dnsErr +} diff --git a/netdev_native.go b/netdev_native.go index e522a08..d303f16 100644 --- a/netdev_native.go +++ b/netdev_native.go @@ -379,13 +379,25 @@ const ( // dnsLookup resolves name by querying the system nameservers over UDP, // preferring an IPv4 (A) answer and falling back to IPv6 (AAAA). func dnsLookup(name string) (netip.Addr, error) { - if addr, err := dnsLookupType(name, dnsTypeA); err == nil { + addr, errA := dnsLookupType(name, dnsTypeA) + if errA == nil { return addr, nil } - if addr, err := dnsLookupType(name, dnsTypeAAAA); err == nil { + addr, errAAAA := dnsLookupType(name, dnsTypeAAAA) + if errAAAA == nil { return addr, nil } - return netip.Addr{}, &DNSError{Err: "no address found", Name: name} + // A name that the servers answered NXDOMAIN for is a different condition + // from a name that they did not answer at all. + notFound := isNotFoundErr(errA) && isNotFoundErr(errAAAA) + return netip.Addr{}, &DNSError{Err: "no address found", Name: name, IsNotFound: notFound} +} + +// isNotFoundErr reports whether err is a DNSError for a name or a record type +// that does not exist. +func isNotFoundErr(err error) bool { + dnsErr, ok := err.(*DNSError) + return ok && dnsErr.IsNotFound } // dnsLookupType resolves name for a single DNS record type (A or AAAA).