return dnsmessage.Parser{}, dnsmessage.Header{}, errors.New("no answer from DNS server")
}
-func checkHeaders(p *dnsmessage.Parser, h dnsmessage.Header, name, server string) error {
+// checkHeader performs basic sanity checks on the header.
+func checkHeader(p *dnsmessage.Parser, h dnsmessage.Header, name, server string) error {
_, err := p.AnswerHeader()
if err != nil && err != dnsmessage.ErrSectionDone {
return &DNSError{
return &DNSError{Err: "lame referral", Name: name, Server: server}
}
- // If answer errored for rcodes dnsRcodeSuccess or dnsRcodeNameError,
- // it means the response in msg was not useful and trying another
- // server probably won't help. Return now in those cases.
- // TODO: indicate this in a more obvious way, such as a field on DNSError?
- if h.RCode == dnsmessage.RCodeNameError {
- return &DNSError{Err: errNoSuchHost.Error(), Name: name, Server: server}
- }
- if h.RCode != dnsmessage.RCodeSuccess {
+ if h.RCode != dnsmessage.RCodeSuccess && h.RCode != dnsmessage.RCodeNameError {
// None of the error codes make sense
// for the query we sent. If we didn't get
// a name error and we didn't get success,
continue
}
- lastErr = checkHeaders(&p, h, name, server)
+ // 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?
+ if h.RCode == dnsmessage.RCodeNameError {
+ return dnsmessage.Parser{}, "", &DNSError{Err: errNoSuchHost.Error(), Name: name, Server: server}
+ }
+
+ lastErr = checkHeader(&p, h, name, server)
if lastErr != nil {
continue
}
func BenchmarkGoLookupIP(b *testing.B) {
testHookUninstaller.Do(uninstallTestHooks)
ctx := context.Background()
+ b.ReportAllocs()
for i := 0; i < b.N; i++ {
goResolver.LookupIPAddr(ctx, "www.example.com")
func BenchmarkGoLookupIPNoSuchHost(b *testing.B) {
testHookUninstaller.Do(uninstallTestHooks)
ctx := context.Background()
+ b.ReportAllocs()
for i := 0; i < b.N; i++ {
goResolver.LookupIPAddr(ctx, "some.nonexistent")
b.Fatal(err)
}
ctx := context.Background()
+ b.ReportAllocs()
for i := 0; i < b.N; i++ {
goResolver.LookupIPAddr(ctx, "www.example.com")
t.Fatal("SkipAllQuestions failed:", err)
}
- err = checkHeaders(&p, h, "golang.org", "foo:53")
+ err = checkHeader(&p, h, "golang.org", "foo:53")
if err == nil {
t.Fatal("expected an error")
}
// Issue 12778: verify that NXDOMAIN without RA bit errors as
// "no such host" and not "server misbehaving"
+//
+// Issue 25336: verify that NXDOMAIN errors fail fast.
func TestIssue12778(t *testing.T) {
- msg := dnsmessage.Message{
- Header: dnsmessage.Header{
- RCode: dnsmessage.RCodeNameError,
- RecursionAvailable: false,
+ lookups := 0
+ fake := fakeDNSServer{
+ rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
+ lookups++
+ return dnsmessage.Message{
+ Header: dnsmessage.Header{
+ ID: q.ID,
+ Response: true,
+ RCode: dnsmessage.RCodeNameError,
+ RecursionAvailable: false,
+ },
+ Questions: q.Questions,
+ }, nil
},
}
+ r := Resolver{PreferGo: true, Dial: fake.DialContext}
- b, err := msg.Pack()
- if err != nil {
- t.Fatal("Pack failed:", err)
- }
- var p dnsmessage.Parser
- h, err := p.Start(b)
- if err != nil {
- t.Fatal("Start failed:", err)
- }
- if err := p.SkipAllQuestions(); err != nil {
- t.Fatal("SkipAllQuestions failed:", err)
+ resolvConf.mu.RLock()
+ conf := resolvConf.dnsConfig
+ resolvConf.mu.RUnlock()
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ _, _, err := r.tryOneName(ctx, conf, ".", dnsmessage.TypeALL)
+
+ if lookups != 1 {
+ t.Errorf("got %d lookups, wanted 1", lookups)
}
- err = checkHeaders(&p, h, "golang.org", "foo:53")
if err == nil {
t.Fatal("expected an error")
}