"time"
)
+// A dnsDialer provides dialing suitable for DNS queries.
+type dnsDialer interface {
+ dialDNS(string, string) (dnsConn, error)
+}
+
// A dnsConn represents a DNS transport endpoint.
type dnsConn interface {
- Conn
+ io.Closer
+
+ SetDeadline(time.Time) error
// readDNSResponse reads a DNS response message from the DNS
// transport endpoint and returns the received DNS response
// exchange sends a query on the connection and hopes for a response.
func exchange(server, name string, qtype uint16, timeout time.Duration) (*dnsMsg, error) {
- d := Dialer{Timeout: timeout}
+ d := testHookDNSDialer(timeout)
out := dnsMsg{
dnsMsgHdr: dnsMsgHdr{
recursion_desired: true,
conf := resolvConf.dnsConfig
resolvConf.mu.RUnlock()
type racer struct {
- rrs []dnsRR
+ fqdn string
+ rrs []dnsRR
error
}
lane := make(chan racer, 1)
for _, qtype := range qtypes {
go func(qtype uint16) {
_, rrs, err := tryOneName(conf, fqdn, qtype)
- lane <- racer{rrs, err}
+ lane <- racer{fqdn, rrs, err}
}(qtype)
}
for range qtypes {
racer := <-lane
if racer.error != nil {
- lastErr = racer.error
+ // Prefer error for original name.
+ if lastErr == nil || racer.fqdn == name+"." {
+ lastErr = racer.error
+ }
continue
}
addrs = append(addrs, addrRecordList(racer.rrs)...)
defer conf.teardown()
}
+// Issue 12712.
+// When using search domains, return the error encountered
+// querying the original name instead of an error encountered
+// querying a generated name.
+func TestErrorForOriginalNameWhenSearching(t *testing.T) {
+ const fqdn = "doesnotexist.domain"
+
+ origTestHookDNSDialer := testHookDNSDialer
+ defer func() { testHookDNSDialer = origTestHookDNSDialer }()
+
+ conf, err := newResolvConfTest()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer conf.teardown()
+
+ if err := conf.writeAndUpdate([]string{"search servfail"}); err != nil {
+ t.Fatal(err)
+ }
+
+ d := &fakeDNSConn{}
+ testHookDNSDialer = func(time.Duration) dnsDialer { return d }
+
+ d.rh = func(q *dnsMsg) (*dnsMsg, error) {
+ r := &dnsMsg{
+ dnsMsgHdr: dnsMsgHdr{
+ id: q.id,
+ },
+ }
+
+ switch q.question[0].Name {
+ case fqdn + ".servfail.":
+ r.rcode = dnsRcodeServerFailure
+ default:
+ r.rcode = dnsRcodeNameError
+ }
+
+ return r, nil
+ }
+
+ _, err = goLookupIP(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)
+ }
+}
+
func BenchmarkGoLookupIP(b *testing.B) {
testHookUninstaller.Do(uninstallTestHooks)
goLookupIP("www.example.com")
}
}
+
+type fakeDNSConn struct {
+ // last query
+ q *dnsMsg
+ // reply handler
+ rh func(*dnsMsg) (*dnsMsg, error)
+}
+
+func (f *fakeDNSConn) dialDNS(n, s string) (dnsConn, error) {
+ return f, nil
+}
+
+func (f *fakeDNSConn) Close() error {
+ return nil
+}
+
+func (f *fakeDNSConn) SetDeadline(time.Time) error {
+ return nil
+}
+
+func (f *fakeDNSConn) writeDNSQuery(q *dnsMsg) error {
+ f.q = q
+ return nil
+}
+
+func (f *fakeDNSConn) readDNSResponse() (*dnsMsg, error) {
+ return f.rh(f.q)
+}
package net
+import "time"
+
var (
testHookDialTCP = dialTCP
+ testHookDNSDialer = func(d time.Duration) dnsDialer { return &Dialer{Timeout: d} }
testHookHostsPath = "/etc/hosts"
testHookLookupIP = func(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, error) { return fn(host) }
testHookSetKeepAlive = func() {}