1 // Copyright 2013 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
23 "golang_org/x/net/dns/dnsmessage"
26 var goResolver
= Resolver
{PreferGo
: true}
28 // Test address from 192.0.2.0/24 block, reserved by RFC 5737 for documentation.
29 var TestAddr
= [4]byte{0xc0, 0x00, 0x02, 0x01}
31 // Test address from 2001:db8::/32 block, reserved by RFC 3849 for documentation.
32 var VarTestAddr6
= [16]byte{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
34 func mustNewName(name
string) dnsmessage
.Name
{
35 nn
, err
:= dnsmessage
.NewName(name
)
37 panic(fmt
.Sprint("creating name: ", err
))
42 func mustQuestion(name
string, qtype dnsmessage
.Type
, class dnsmessage
.Class
) dnsmessage
.Question
{
43 return dnsmessage
.Question
{
44 Name
: mustNewName(name
),
50 var dnsTransportFallbackTests
= []struct {
52 question dnsmessage
.Question
54 rcode dnsmessage
.RCode
56 // Querying "com." with qtype=255 usually makes an answer
57 // which requires more than 512 bytes.
58 {"8.8.8.8:53", mustQuestion("com.", dnsmessage
.TypeALL
, dnsmessage
.ClassINET
), 2, dnsmessage
.RCodeSuccess
},
59 {"8.8.4.4:53", mustQuestion("com.", dnsmessage
.TypeALL
, dnsmessage
.ClassINET
), 4, dnsmessage
.RCodeSuccess
},
62 func TestDNSTransportFallback(t
*testing
.T
) {
63 fake
:= fakeDNSServer
{
64 rh
: func(n
, _
string, q dnsmessage
.Message
, _ time
.Time
) (dnsmessage
.Message
, error
) {
65 r
:= dnsmessage
.Message
{
66 Header
: dnsmessage
.Header
{
69 RCode
: dnsmessage
.RCodeSuccess
,
71 Questions
: q
.Questions
,
74 r
.Header
.Truncated
= true
79 r
:= Resolver
{PreferGo
: true, Dial
: fake
.DialContext
}
80 for _
, tt
:= range dnsTransportFallbackTests
{
81 ctx
, cancel
:= context
.WithCancel(context
.Background())
83 _
, h
, err
:= r
.exchange(ctx
, tt
.server
, tt
.question
, time
.Second
)
88 if h
.RCode
!= tt
.rcode
{
89 t
.Errorf("got %v from %v; want %v", h
.RCode
, tt
.server
, tt
.rcode
)
95 // See RFC 6761 for further information about the reserved, pseudo
97 var specialDomainNameTests
= []struct {
98 question dnsmessage
.Question
99 rcode dnsmessage
.RCode
101 // Name resolution APIs and libraries should not recognize the
102 // followings as special.
103 {mustQuestion("1.0.168.192.in-addr.arpa.", dnsmessage
.TypePTR
, dnsmessage
.ClassINET
), dnsmessage
.RCodeNameError
},
104 {mustQuestion("test.", dnsmessage
.TypeALL
, dnsmessage
.ClassINET
), dnsmessage
.RCodeNameError
},
105 {mustQuestion("example.com.", dnsmessage
.TypeALL
, dnsmessage
.ClassINET
), dnsmessage
.RCodeSuccess
},
107 // Name resolution APIs and libraries should recognize the
108 // followings as special and should not send any queries.
109 // Though, we test those names here for verifying negative
110 // answers at DNS query-response interaction level.
111 {mustQuestion("localhost.", dnsmessage
.TypeALL
, dnsmessage
.ClassINET
), dnsmessage
.RCodeNameError
},
112 {mustQuestion("invalid.", dnsmessage
.TypeALL
, dnsmessage
.ClassINET
), dnsmessage
.RCodeNameError
},
115 func TestSpecialDomainName(t
*testing
.T
) {
116 fake
:= fakeDNSServer
{rh
: func(_
, _
string, q dnsmessage
.Message
, _ time
.Time
) (dnsmessage
.Message
, error
) {
117 r
:= dnsmessage
.Message
{
118 Header
: dnsmessage
.Header
{
122 Questions
: q
.Questions
,
125 switch q
.Questions
[0].Name
.String() {
127 r
.Header
.RCode
= dnsmessage
.RCodeSuccess
129 r
.Header
.RCode
= dnsmessage
.RCodeNameError
134 r
:= Resolver
{PreferGo
: true, Dial
: fake
.DialContext
}
135 server
:= "8.8.8.8:53"
136 for _
, tt
:= range specialDomainNameTests
{
137 ctx
, cancel
:= context
.WithCancel(context
.Background())
139 _
, h
, err
:= r
.exchange(ctx
, server
, tt
.question
, 3*time
.Second
)
144 if h
.RCode
!= tt
.rcode
{
145 t
.Errorf("got %v from %v; want %v", h
.RCode
, server
, tt
.rcode
)
151 // Issue 13705: don't try to resolve onion addresses, etc
152 func TestAvoidDNSName(t
*testing
.T
) {
160 {"foo.onion.", true},
163 {"foo.ONION.", true},
165 // But do resolve *.local address; Issue 16739
166 {"foo.local.", false},
167 {"foo.local", false},
168 {"foo.LOCAL", false},
169 {"foo.LOCAL.", false},
171 {"", true}, // will be rejected earlier too
173 // Without stuff before onion/local, they're fine to
174 // use DNS. With a search path,
175 // "onion.vegegtables.com" can use DNS. Without a
176 // search path (or with a trailing dot), the queries
177 // are just kinda useless, but don't reveal anything
184 for _
, tt
:= range tests
{
185 got
:= avoidDNS(tt
.name
)
187 t
.Errorf("avoidDNS(%q) = %v; want %v", tt
.name
, got
, tt
.avoid
)
192 var fakeDNSServerSuccessful
= fakeDNSServer
{rh
: func(_
, _
string, q dnsmessage
.Message
, _ time
.Time
) (dnsmessage
.Message
, error
) {
193 r
:= dnsmessage
.Message
{
194 Header
: dnsmessage
.Header
{
198 Questions
: q
.Questions
,
200 if len(q
.Questions
) == 1 && q
.Questions
[0].Type
== dnsmessage
.TypeA
{
201 r
.Answers
= []dnsmessage
.Resource
{
203 Header
: dnsmessage
.ResourceHeader
{
204 Name
: q
.Questions
[0].Name
,
205 Type
: dnsmessage
.TypeA
,
206 Class
: dnsmessage
.ClassINET
,
209 Body
: &dnsmessage
.AResource
{
218 // Issue 13705: don't try to resolve onion addresses, etc
219 func TestLookupTorOnion(t
*testing
.T
) {
220 defer dnsWaitGroup
.Wait()
221 r
:= Resolver
{PreferGo
: true, Dial
: fakeDNSServerSuccessful
.DialContext
}
222 addrs
, err
:= r
.LookupIPAddr(context
.Background(), "foo.onion")
224 t
.Fatalf("lookup = %v; want nil", err
)
227 t
.Errorf("unexpected addresses: %v", addrs
)
231 type resolvConfTest
struct {
237 func newResolvConfTest() (*resolvConfTest
, error
) {
238 dir
, err
:= ioutil
.TempDir("", "go-resolvconftest")
242 conf
:= &resolvConfTest
{
244 path
: path
.Join(dir
, "resolv.conf"),
245 resolverConfig
: &resolvConf
,
247 conf
.initOnce
.Do(conf
.init
)
251 func (conf
*resolvConfTest
) writeAndUpdate(lines
[]string) error
{
252 f
, err
:= os
.OpenFile(conf
.path
, os
.O_CREATE|os
.O_TRUNC|os
.O_WRONLY
, 0600)
256 if _
, err
:= f
.WriteString(strings
.Join(lines
, "\n")); err
!= nil {
261 if err
:= conf
.forceUpdate(conf
.path
, time
.Now().Add(time
.Hour
)); err
!= nil {
267 func (conf
*resolvConfTest
) forceUpdate(name
string, lastChecked time
.Time
) error
{
268 dnsConf
:= dnsReadConfig(name
)
270 conf
.dnsConfig
= dnsConf
272 for i
:= 0; i
< 5; i
++ {
273 if conf
.tryAcquireSema() {
274 conf
.lastChecked
= lastChecked
279 return fmt
.Errorf("tryAcquireSema for %s failed", name
)
282 func (conf
*resolvConfTest
) servers() []string {
284 servers
:= conf
.dnsConfig
.servers
289 func (conf
*resolvConfTest
) teardown() error
{
290 err
:= conf
.forceUpdate("/etc/resolv.conf", time
.Time
{})
291 os
.RemoveAll(conf
.dir
)
295 var updateResolvConfTests
= []struct {
296 name
string // query name
297 lines
[]string // resolver configuration lines
298 servers
[]string // expected name servers
302 lines
: []string{"nameserver 8.8.8.8"},
303 servers
: []string{"8.8.8.8:53"},
307 lines
: nil, // an empty resolv.conf should use defaultNS as name servers
311 name
: "www.example.com",
312 lines
: []string{"nameserver 8.8.4.4"},
313 servers
: []string{"8.8.4.4:53"},
317 func TestUpdateResolvConf(t
*testing
.T
) {
318 defer dnsWaitGroup
.Wait()
320 r
:= Resolver
{PreferGo
: true, Dial
: fakeDNSServerSuccessful
.DialContext
}
322 conf
, err
:= newResolvConfTest()
326 defer conf
.teardown()
328 for i
, tt
:= range updateResolvConfTests
{
329 if err
:= conf
.writeAndUpdate(tt
.lines
); err
!= nil {
334 var wg sync
.WaitGroup
337 for j
:= 0; j
< N
; j
++ {
338 go func(name
string) {
340 ips
, err
:= r
.LookupIPAddr(context
.Background(), name
)
346 t
.Errorf("no records for %s", name
)
353 servers
:= conf
.servers()
354 if !reflect
.DeepEqual(servers
, tt
.servers
) {
355 t
.Errorf("#%d: got %v; want %v", i
, servers
, tt
.servers
)
361 var goLookupIPWithResolverConfigTests
= []struct {
363 lines
[]string // resolver configuration lines
365 a
, aaaa
bool // whether response contains A, AAAA-record
367 // no records, transport timeout
369 "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j",
371 "options timeout:1 attempts:1",
372 "nameserver 255.255.255.255", // please forgive us for abuse of limited broadcast address
374 &DNSError
{Name
: "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", Server
: "255.255.255.255:53", IsTimeout
: true},
378 // no records, non-existent domain
380 "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j",
382 "options timeout:3 attempts:1",
383 "nameserver 8.8.8.8",
385 &DNSError
{Name
: "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", Server
: "8.8.8.8:53", IsTimeout
: false},
389 // a few A records, no AAAA records
393 "nameserver 8.8.8.8",
394 "nameserver 2001:4860:4860::8888",
403 "nameserver 2001:4860:4860::8888",
404 "nameserver 8.8.8.8",
412 "search x.golang.org y.golang.org",
413 "nameserver 2001:4860:4860::8888",
414 "nameserver 8.8.8.8",
420 // no A records, a few AAAA records
424 "nameserver 2001:4860:4860::8888",
425 "nameserver 8.8.8.8",
434 "nameserver 8.8.8.8",
435 "nameserver 2001:4860:4860::8888",
443 "search x.golang.org y.golang.org",
444 "nameserver 8.8.8.8",
445 "nameserver 2001:4860:4860::8888",
451 // both A and AAAA records
453 "hostname.as112.net", // see RFC 7534
456 "nameserver 2001:4860:4860::8888",
457 "nameserver 8.8.8.8",
463 "hostname.as112.net", // see RFC 7534
465 "search x.golang.org y.golang.org",
466 "nameserver 2001:4860:4860::8888",
467 "nameserver 8.8.8.8",
474 func TestGoLookupIPWithResolverConfig(t
*testing
.T
) {
475 defer dnsWaitGroup
.Wait()
476 fake
:= fakeDNSServer
{rh
: func(n
, s
string, q dnsmessage
.Message
, _ time
.Time
) (dnsmessage
.Message
, error
) {
478 case "[2001:4860:4860::8888]:53", "8.8.8.8:53":
481 time
.Sleep(10 * time
.Millisecond
)
482 return dnsmessage
.Message
{}, poll
.ErrTimeout
484 r
:= dnsmessage
.Message
{
485 Header
: dnsmessage
.Header
{
489 Questions
: q
.Questions
,
491 for _
, question
:= range q
.Questions
{
492 switch question
.Type
{
493 case dnsmessage
.TypeA
:
494 switch question
.Name
.String() {
495 case "hostname.as112.net.":
497 case "ipv4.google.com.":
498 r
.Answers
= append(r
.Answers
, dnsmessage
.Resource
{
499 Header
: dnsmessage
.ResourceHeader
{
500 Name
: q
.Questions
[0].Name
,
501 Type
: dnsmessage
.TypeA
,
502 Class
: dnsmessage
.ClassINET
,
505 Body
: &dnsmessage
.AResource
{
512 case dnsmessage
.TypeAAAA
:
513 switch question
.Name
.String() {
514 case "hostname.as112.net.":
516 case "ipv6.google.com.":
517 r
.Answers
= append(r
.Answers
, dnsmessage
.Resource
{
518 Header
: dnsmessage
.ResourceHeader
{
519 Name
: q
.Questions
[0].Name
,
520 Type
: dnsmessage
.TypeAAAA
,
521 Class
: dnsmessage
.ClassINET
,
524 Body
: &dnsmessage
.AAAAResource
{
533 r
:= Resolver
{PreferGo
: true, Dial
: fake
.DialContext
}
535 conf
, err
:= newResolvConfTest()
539 defer conf
.teardown()
541 for _
, tt
:= range goLookupIPWithResolverConfigTests
{
542 if err
:= conf
.writeAndUpdate(tt
.lines
); err
!= nil {
546 addrs
, err
:= r
.LookupIPAddr(context
.Background(), tt
.name
)
548 if err
, ok
:= err
.(*DNSError
); !ok || tt
.error
!= nil && (err
.Name
!= tt
.error
.(*DNSError
).Name || err
.Server
!= tt
.error
.(*DNSError
).Server || err
.IsTimeout
!= tt
.error
.(*DNSError
).IsTimeout
) {
549 t
.Errorf("got %v; want %v", err
, tt
.error
)
554 t
.Errorf("no records for %s", tt
.name
)
556 if !tt
.a
&& !tt
.aaaa
&& len(addrs
) > 0 {
557 t
.Errorf("unexpected %v for %s", addrs
, tt
.name
)
559 for _
, addr
:= range addrs
{
560 if !tt
.a
&& addr
.IP
.To4() != nil {
561 t
.Errorf("got %v; must not be IPv4 address", addr
)
563 if !tt
.aaaa
&& addr
.IP
.To16() != nil && addr
.IP
.To4() == nil {
564 t
.Errorf("got %v; must not be IPv6 address", addr
)
570 // Test that goLookupIPOrder falls back to the host file when no DNS servers are available.
571 func TestGoLookupIPOrderFallbackToFile(t
*testing
.T
) {
572 defer dnsWaitGroup
.Wait()
574 fake
:= fakeDNSServer
{rh
: func(n
, s
string, q dnsmessage
.Message
, tm time
.Time
) (dnsmessage
.Message
, error
) {
575 r
:= dnsmessage
.Message
{
576 Header
: dnsmessage
.Header
{
580 Questions
: q
.Questions
,
584 r
:= Resolver
{PreferGo
: true, Dial
: fake
.DialContext
}
586 // Add a config that simulates no dns servers being available.
587 conf
, err
:= newResolvConfTest()
591 if err
:= conf
.writeAndUpdate([]string{}); err
!= nil {
594 // Redirect host file lookups.
595 defer func(orig
string) { testHookHostsPath
= orig
}(testHookHostsPath
)
596 testHookHostsPath
= "testdata/hosts"
598 for _
, order
:= range []hostLookupOrder
{hostLookupFilesDNS
, hostLookupDNSFiles
} {
599 name
:= fmt
.Sprintf("order %v", order
)
601 // First ensure that we get an error when contacting a non-existent host.
602 _
, _
, err
:= r
.goLookupIPCNAMEOrder(context
.Background(), "notarealhost", order
)
604 t
.Errorf("%s: expected error while looking up name not in hosts file", name
)
608 // Now check that we get an address when the name appears in the hosts file.
609 addrs
, _
, err
:= r
.goLookupIPCNAMEOrder(context
.Background(), "thor", order
) // entry is in "testdata/hosts"
611 t
.Errorf("%s: expected to successfully lookup host entry", name
)
615 t
.Errorf("%s: expected exactly one result, but got %v", name
, addrs
)
618 if got
, want
:= addrs
[0].String(), "127.1.1.1"; got
!= want
{
619 t
.Errorf("%s: address doesn't match expectation. got %v, want %v", name
, got
, want
)
622 defer conf
.teardown()
626 // When using search domains, return the error encountered
627 // querying the original name instead of an error encountered
628 // querying a generated name.
629 func TestErrorForOriginalNameWhenSearching(t
*testing
.T
) {
630 defer dnsWaitGroup
.Wait()
632 const fqdn
= "doesnotexist.domain"
634 conf
, err
:= newResolvConfTest()
638 defer conf
.teardown()
640 if err
:= conf
.writeAndUpdate([]string{"search servfail"}); err
!= nil {
644 fake
:= fakeDNSServer
{rh
: func(_
, _
string, q dnsmessage
.Message
, _ time
.Time
) (dnsmessage
.Message
, error
) {
645 r
:= dnsmessage
.Message
{
646 Header
: dnsmessage
.Header
{
650 Questions
: q
.Questions
,
653 switch q
.Questions
[0].Name
.String() {
654 case fqdn
+ ".servfail.":
655 r
.Header
.RCode
= dnsmessage
.RCodeServerFailure
657 r
.Header
.RCode
= dnsmessage
.RCodeNameError
667 {true, &DNSError
{Name
: fqdn
, Err
: "server misbehaving", IsTemporary
: true}},
668 {false, &DNSError
{Name
: fqdn
, Err
: errNoSuchHost
.Error()}},
670 for _
, tt
:= range cases
{
671 r
:= Resolver
{PreferGo
: true, StrictErrors
: tt
.strictErrors
, Dial
: fake
.DialContext
}
672 _
, err
= r
.LookupIPAddr(context
.Background(), fqdn
)
674 t
.Fatal("expected an error")
678 if err
, ok
:= err
.(*DNSError
); !ok || err
.Name
!= want
.Name || err
.Err
!= want
.Err || err
.IsTemporary
!= want
.IsTemporary
{
679 t
.Errorf("got %v; want %v", err
, want
)
684 // Issue 15434. If a name server gives a lame referral, continue to the next.
685 func TestIgnoreLameReferrals(t
*testing
.T
) {
686 defer dnsWaitGroup
.Wait()
688 conf
, err
:= newResolvConfTest()
692 defer conf
.teardown()
694 if err
:= conf
.writeAndUpdate([]string{"nameserver 192.0.2.1", // the one that will give a lame referral
695 "nameserver 192.0.2.2"}); err
!= nil {
699 fake
:= fakeDNSServer
{rh
: func(_
, s
string, q dnsmessage
.Message
, _ time
.Time
) (dnsmessage
.Message
, error
) {
701 r
:= dnsmessage
.Message
{
702 Header
: dnsmessage
.Header
{
706 Questions
: q
.Questions
,
709 if s
== "192.0.2.2:53" {
710 r
.Header
.RecursionAvailable
= true
711 if q
.Questions
[0].Type
== dnsmessage
.TypeA
{
712 r
.Answers
= []dnsmessage
.Resource
{
714 Header
: dnsmessage
.ResourceHeader
{
715 Name
: q
.Questions
[0].Name
,
716 Type
: dnsmessage
.TypeA
,
717 Class
: dnsmessage
.ClassINET
,
720 Body
: &dnsmessage
.AResource
{
730 r
:= Resolver
{PreferGo
: true, Dial
: fake
.DialContext
}
732 addrs
, err
:= r
.LookupIPAddr(context
.Background(), "www.golang.org")
737 if got
:= len(addrs
); got
!= 1 {
738 t
.Fatalf("got %d addresses, want 1", got
)
741 if got
, want
:= addrs
[0].String(), "192.0.2.1"; got
!= want
{
742 t
.Fatalf("got address %v, want %v", got
, want
)
746 func BenchmarkGoLookupIP(b
*testing
.B
) {
747 testHookUninstaller
.Do(uninstallTestHooks
)
748 ctx
:= context
.Background()
751 for i
:= 0; i
< b
.N
; i
++ {
752 goResolver
.LookupIPAddr(ctx
, "www.example.com")
756 func BenchmarkGoLookupIPNoSuchHost(b
*testing
.B
) {
757 testHookUninstaller
.Do(uninstallTestHooks
)
758 ctx
:= context
.Background()
761 for i
:= 0; i
< b
.N
; i
++ {
762 goResolver
.LookupIPAddr(ctx
, "some.nonexistent")
766 func BenchmarkGoLookupIPWithBrokenNameServer(b
*testing
.B
) {
767 testHookUninstaller
.Do(uninstallTestHooks
)
769 conf
, err
:= newResolvConfTest()
773 defer conf
.teardown()
776 "nameserver 203.0.113.254", // use TEST-NET-3 block, see RFC 5737
777 "nameserver 8.8.8.8",
779 if err
:= conf
.writeAndUpdate(lines
); err
!= nil {
782 ctx
:= context
.Background()
785 for i
:= 0; i
< b
.N
; i
++ {
786 goResolver
.LookupIPAddr(ctx
, "www.example.com")
790 type fakeDNSServer
struct {
791 rh
func(n
, s
string, q dnsmessage
.Message
, t time
.Time
) (dnsmessage
.Message
, error
)
795 func (server
*fakeDNSServer
) DialContext(_ context
.Context
, n
, s
string) (Conn
, error
) {
796 if server
.alwaysTCP || n
== "tcp" || n
== "tcp4" || n
== "tcp6" {
797 return &fakeDNSConn
{tcp
: true, server
: server
, n
: n
, s
: s
}, nil
799 return &fakeDNSPacketConn
{fakeDNSConn
: fakeDNSConn
{tcp
: false, server
: server
, n
: n
, s
: s
}}, nil
802 type fakeDNSConn
struct {
805 server
*fakeDNSServer
813 func (f
*fakeDNSConn
) Close() error
{
817 func (f
*fakeDNSConn
) Read(b
[]byte) (int, error
) {
824 resp
, err
:= f
.server
.rh(f
.n
, f
.s
, f
.q
, f
.t
)
829 bb
:= make([]byte, 2, 514)
830 bb
, err
= resp
.AppendPack(bb
)
832 return 0, fmt
.Errorf("cannot marshal DNS message: %v", err
)
844 if len(b
) < len(bb
) {
845 return 0, errors
.New("read would fragment DNS message")
852 func (f
*fakeDNSConn
) Write(b
[]byte) (int, error
) {
853 if f
.tcp
&& len(b
) >= 2 {
856 if f
.q
.Unpack(b
) != nil {
857 return 0, fmt
.Errorf("cannot unmarshal DNS message fake %s (%d)", f
.n
, len(b
))
862 func (f
*fakeDNSConn
) SetDeadline(t time
.Time
) error
{
867 type fakeDNSPacketConn
struct {
872 func (f
*fakeDNSPacketConn
) SetDeadline(t time
.Time
) error
{
873 return f
.fakeDNSConn
.SetDeadline(t
)
876 func (f
*fakeDNSPacketConn
) Close() error
{
877 return f
.fakeDNSConn
.Close()
880 // UDP round-tripper algorithm should ignore invalid DNS responses (issue 13281).
881 func TestIgnoreDNSForgeries(t
*testing
.T
) {
884 b
:= make([]byte, 512)
891 var msg dnsmessage
.Message
892 if msg
.Unpack(b
[:n
]) != nil {
893 t
.Error("invalid DNS query:", err
)
897 s
.Write([]byte("garbage DNS response packet"))
899 msg
.Header
.Response
= true
900 msg
.Header
.ID
++ // make invalid ID
902 if b
, err
= msg
.Pack(); err
!= nil {
903 t
.Error("failed to pack DNS response:", err
)
908 msg
.Header
.ID
-- // restore original ID
909 msg
.Answers
= []dnsmessage
.Resource
{
911 Header
: dnsmessage
.ResourceHeader
{
912 Name
: mustNewName("www.example.com."),
913 Type
: dnsmessage
.TypeA
,
914 Class
: dnsmessage
.ClassINET
,
917 Body
: &dnsmessage
.AResource
{
925 t
.Error("failed to pack DNS response:", err
)
931 msg
:= dnsmessage
.Message
{
932 Header
: dnsmessage
.Header
{
935 Questions
: []dnsmessage
.Question
{
937 Name
: mustNewName("www.example.com."),
938 Type
: dnsmessage
.TypeA
,
939 Class
: dnsmessage
.ClassINET
,
946 t
.Fatal("Pack failed:", err
)
949 p
, _
, err
:= dnsPacketRoundTrip(c
, 42, msg
.Questions
[0], b
)
951 t
.Fatalf("dnsPacketRoundTrip failed: %v", err
)
955 as
, err
:= p
.AllAnswers()
957 t
.Fatal("AllAnswers failed:", err
)
959 if got
:= as
[0].Body
.(*dnsmessage
.AResource
).A
; got
!= TestAddr
{
960 t
.Errorf("got address %v, want %v", got
, TestAddr
)
964 // Issue 16865. If a name server times out, continue to the next.
965 func TestRetryTimeout(t
*testing
.T
) {
966 defer dnsWaitGroup
.Wait()
968 conf
, err
:= newResolvConfTest()
972 defer conf
.teardown()
974 testConf
:= []string{
975 "nameserver 192.0.2.1", // the one that will timeout
976 "nameserver 192.0.2.2",
978 if err
:= conf
.writeAndUpdate(testConf
); err
!= nil {
982 var deadline0 time
.Time
984 fake
:= fakeDNSServer
{rh
: func(_
, s
string, q dnsmessage
.Message
, deadline time
.Time
) (dnsmessage
.Message
, error
) {
985 t
.Log(s
, q
, deadline
)
987 if deadline
.IsZero() {
988 t
.Error("zero deadline")
991 if s
== "192.0.2.1:53" {
993 time
.Sleep(10 * time
.Millisecond
)
994 return dnsmessage
.Message
{}, poll
.ErrTimeout
997 if deadline
.Equal(deadline0
) {
998 t
.Error("deadline didn't change")
1001 return mockTXTResponse(q
), nil
1003 r
:= &Resolver
{PreferGo
: true, Dial
: fake
.DialContext
}
1005 _
, err
= r
.LookupTXT(context
.Background(), "www.golang.org")
1010 if deadline0
.IsZero() {
1011 t
.Error("deadline0 still zero", deadline0
)
1015 func TestRotate(t
*testing
.T
) {
1016 // without rotation, always uses the first server
1017 testRotate(t
, false, []string{"192.0.2.1", "192.0.2.2"}, []string{"192.0.2.1:53", "192.0.2.1:53", "192.0.2.1:53"})
1019 // with rotation, rotates through back to first
1020 testRotate(t
, true, []string{"192.0.2.1", "192.0.2.2"}, []string{"192.0.2.1:53", "192.0.2.2:53", "192.0.2.1:53"})
1023 func testRotate(t
*testing
.T
, rotate
bool, nameservers
, wantServers
[]string) {
1024 defer dnsWaitGroup
.Wait()
1026 conf
, err
:= newResolvConfTest()
1030 defer conf
.teardown()
1032 var confLines
[]string
1033 for _
, ns
:= range nameservers
{
1034 confLines
= append(confLines
, "nameserver "+ns
)
1037 confLines
= append(confLines
, "options rotate")
1040 if err
:= conf
.writeAndUpdate(confLines
); err
!= nil {
1044 var usedServers
[]string
1045 fake
:= fakeDNSServer
{rh
: func(_
, s
string, q dnsmessage
.Message
, deadline time
.Time
) (dnsmessage
.Message
, error
) {
1046 usedServers
= append(usedServers
, s
)
1047 return mockTXTResponse(q
), nil
1049 r
:= Resolver
{PreferGo
: true, Dial
: fake
.DialContext
}
1051 // len(nameservers) + 1 to allow rotation to get back to start
1052 for i
:= 0; i
< len(nameservers
)+1; i
++ {
1053 if _
, err
:= r
.LookupTXT(context
.Background(), "www.golang.org"); err
!= nil {
1058 if !reflect
.DeepEqual(usedServers
, wantServers
) {
1059 t
.Errorf("rotate=%t got used servers:\n%v\nwant:\n%v", rotate
, usedServers
, wantServers
)
1063 func mockTXTResponse(q dnsmessage
.Message
) dnsmessage
.Message
{
1064 r
:= dnsmessage
.Message
{
1065 Header
: dnsmessage
.Header
{
1068 RecursionAvailable
: true,
1070 Questions
: q
.Questions
,
1071 Answers
: []dnsmessage
.Resource
{
1073 Header
: dnsmessage
.ResourceHeader
{
1074 Name
: q
.Questions
[0].Name
,
1075 Type
: dnsmessage
.TypeTXT
,
1076 Class
: dnsmessage
.ClassINET
,
1078 Body
: &dnsmessage
.TXTResource
{
1079 TXT
: []string{"ok"},
1088 // Issue 17448. With StrictErrors enabled, temporary errors should make
1089 // LookupIP fail rather than return a partial result.
1090 func TestStrictErrorsLookupIP(t
*testing
.T
) {
1091 defer dnsWaitGroup
.Wait()
1093 conf
, err
:= newResolvConfTest()
1097 defer conf
.teardown()
1099 confData
:= []string{
1100 "nameserver 192.0.2.53",
1101 "search x.golang.org y.golang.org",
1103 if err
:= conf
.writeAndUpdate(confData
); err
!= nil {
1107 const name
= "test-issue19592"
1108 const server
= "192.0.2.53:53"
1109 const searchX
= "test-issue19592.x.golang.org."
1110 const searchY
= "test-issue19592.y.golang.org."
1111 const ip4
= "192.0.2.1"
1112 const ip6
= "2001:db8::1"
1114 type resolveWhichEnum
int
1116 resolveOK resolveWhichEnum
= iota
1122 makeTempError
:= func(err
string) error
{
1130 makeTimeout
:= func() error
{
1132 Err
: poll
.ErrTimeout
.Error(),
1138 makeNxDomain
:= func() error
{
1140 Err
: errNoSuchHost
.Error(),
1148 resolveWhich
func(quest dnsmessage
.Question
) resolveWhichEnum
1155 resolveWhich
: func(quest dnsmessage
.Question
) resolveWhichEnum
{
1158 wantIPs
: []string{ip4
, ip6
},
1161 desc
: "searchX error fails in strict mode",
1162 resolveWhich
: func(quest dnsmessage
.Question
) resolveWhichEnum
{
1163 if quest
.Name
.String() == searchX
{
1164 return resolveTimeout
1168 wantStrictErr
: makeTimeout(),
1169 wantIPs
: []string{ip4
, ip6
},
1172 desc
: "searchX IPv4-only timeout fails in strict mode",
1173 resolveWhich
: func(quest dnsmessage
.Question
) resolveWhichEnum
{
1174 if quest
.Name
.String() == searchX
&& quest
.Type
== dnsmessage
.TypeA
{
1175 return resolveTimeout
1179 wantStrictErr
: makeTimeout(),
1180 wantIPs
: []string{ip4
, ip6
},
1183 desc
: "searchX IPv6-only servfail fails in strict mode",
1184 resolveWhich
: func(quest dnsmessage
.Question
) resolveWhichEnum
{
1185 if quest
.Name
.String() == searchX
&& quest
.Type
== dnsmessage
.TypeAAAA
{
1186 return resolveServfail
1190 wantStrictErr
: makeTempError("server misbehaving"),
1191 wantIPs
: []string{ip4
, ip6
},
1194 desc
: "searchY error always fails",
1195 resolveWhich
: func(quest dnsmessage
.Question
) resolveWhichEnum
{
1196 if quest
.Name
.String() == searchY
{
1197 return resolveTimeout
1201 wantStrictErr
: makeTimeout(),
1202 wantLaxErr
: makeNxDomain(), // This one reaches the "test." FQDN.
1205 desc
: "searchY IPv4-only socket error fails in strict mode",
1206 resolveWhich
: func(quest dnsmessage
.Question
) resolveWhichEnum
{
1207 if quest
.Name
.String() == searchY
&& quest
.Type
== dnsmessage
.TypeA
{
1208 return resolveOpError
1212 wantStrictErr
: makeTempError("write: socket on fire"),
1213 wantIPs
: []string{ip6
},
1216 desc
: "searchY IPv6-only timeout fails in strict mode",
1217 resolveWhich
: func(quest dnsmessage
.Question
) resolveWhichEnum
{
1218 if quest
.Name
.String() == searchY
&& quest
.Type
== dnsmessage
.TypeAAAA
{
1219 return resolveTimeout
1223 wantStrictErr
: makeTimeout(),
1224 wantIPs
: []string{ip4
},
1228 for i
, tt
:= range cases
{
1229 fake
:= fakeDNSServer
{rh
: func(_
, s
string, q dnsmessage
.Message
, deadline time
.Time
) (dnsmessage
.Message
, error
) {
1232 switch tt
.resolveWhich(q
.Questions
[0]) {
1235 case resolveOpError
:
1236 return dnsmessage
.Message
{}, &OpError
{Op
: "write", Err
: fmt
.Errorf("socket on fire")}
1237 case resolveServfail
:
1238 return dnsmessage
.Message
{
1239 Header
: dnsmessage
.Header
{
1242 RCode
: dnsmessage
.RCodeServerFailure
,
1244 Questions
: q
.Questions
,
1246 case resolveTimeout
:
1247 return dnsmessage
.Message
{}, poll
.ErrTimeout
1249 t
.Fatal("Impossible resolveWhich")
1252 switch q
.Questions
[0].Name
.String() {
1253 case searchX
, name
+ ".":
1254 // Return NXDOMAIN to utilize the search list.
1255 return dnsmessage
.Message
{
1256 Header
: dnsmessage
.Header
{
1259 RCode
: dnsmessage
.RCodeNameError
,
1261 Questions
: q
.Questions
,
1264 // Return records below.
1266 return dnsmessage
.Message
{}, fmt
.Errorf("Unexpected Name: %v", q
.Questions
[0].Name
)
1269 r
:= dnsmessage
.Message
{
1270 Header
: dnsmessage
.Header
{
1274 Questions
: q
.Questions
,
1276 switch q
.Questions
[0].Type
{
1277 case dnsmessage
.TypeA
:
1278 r
.Answers
= []dnsmessage
.Resource
{
1280 Header
: dnsmessage
.ResourceHeader
{
1281 Name
: q
.Questions
[0].Name
,
1282 Type
: dnsmessage
.TypeA
,
1283 Class
: dnsmessage
.ClassINET
,
1286 Body
: &dnsmessage
.AResource
{
1291 case dnsmessage
.TypeAAAA
:
1292 r
.Answers
= []dnsmessage
.Resource
{
1294 Header
: dnsmessage
.ResourceHeader
{
1295 Name
: q
.Questions
[0].Name
,
1296 Type
: dnsmessage
.TypeAAAA
,
1297 Class
: dnsmessage
.ClassINET
,
1300 Body
: &dnsmessage
.AAAAResource
{
1306 return dnsmessage
.Message
{}, fmt
.Errorf("Unexpected Type: %v", q
.Questions
[0].Type
)
1311 for _
, strict
:= range []bool{true, false} {
1312 r
:= Resolver
{PreferGo
: true, StrictErrors
: strict
, Dial
: fake
.DialContext
}
1313 ips
, err
:= r
.LookupIPAddr(context
.Background(), name
)
1317 wantErr
= tt
.wantStrictErr
1319 wantErr
= tt
.wantLaxErr
1321 if !reflect
.DeepEqual(err
, wantErr
) {
1322 t
.Errorf("#%d (%s) strict=%v: got err %#v; want %#v", i
, tt
.desc
, strict
, err
, wantErr
)
1325 gotIPs
:= map[string]struct{}{}
1326 for _
, ip
:= range ips
{
1327 gotIPs
[ip
.String()] = struct{}{}
1329 wantIPs
:= map[string]struct{}{}
1331 for _
, ip
:= range tt
.wantIPs
{
1332 wantIPs
[ip
] = struct{}{}
1335 if !reflect
.DeepEqual(gotIPs
, wantIPs
) {
1336 t
.Errorf("#%d (%s) strict=%v: got ips %v; want %v", i
, tt
.desc
, strict
, gotIPs
, wantIPs
)
1342 // Issue 17448. With StrictErrors enabled, temporary errors should make
1343 // LookupTXT stop walking the search list.
1344 func TestStrictErrorsLookupTXT(t
*testing
.T
) {
1345 defer dnsWaitGroup
.Wait()
1347 conf
, err
:= newResolvConfTest()
1351 defer conf
.teardown()
1353 confData
:= []string{
1354 "nameserver 192.0.2.53",
1355 "search x.golang.org y.golang.org",
1357 if err
:= conf
.writeAndUpdate(confData
); err
!= nil {
1362 const server
= "192.0.2.53:53"
1363 const searchX
= "test.x.golang.org."
1364 const searchY
= "test.y.golang.org."
1365 const txt
= "Hello World"
1367 fake
:= fakeDNSServer
{rh
: func(_
, s
string, q dnsmessage
.Message
, deadline time
.Time
) (dnsmessage
.Message
, error
) {
1370 switch q
.Questions
[0].Name
.String() {
1372 return dnsmessage
.Message
{}, poll
.ErrTimeout
1374 return mockTXTResponse(q
), nil
1376 return dnsmessage
.Message
{}, fmt
.Errorf("Unexpected Name: %v", q
.Questions
[0].Name
)
1380 for _
, strict
:= range []bool{true, false} {
1381 r
:= Resolver
{StrictErrors
: strict
, Dial
: fake
.DialContext
}
1382 p
, _
, err
:= r
.lookup(context
.Background(), name
, dnsmessage
.TypeTXT
)
1386 wantErr
= &DNSError
{
1387 Err
: poll
.ErrTimeout
.Error(),
1395 if !reflect
.DeepEqual(err
, wantErr
) {
1396 t
.Errorf("strict=%v: got err %#v; want %#v", strict
, err
, wantErr
)
1398 a
, err
:= p
.AllAnswers()
1402 if len(a
) != wantRRs
{
1403 t
.Errorf("strict=%v: got %v; want %v", strict
, len(a
), wantRRs
)
1408 // Test for a race between uninstalling the test hooks and closing a
1409 // socket connection. This used to fail when testing with -race.
1410 func TestDNSGoroutineRace(t
*testing
.T
) {
1411 defer dnsWaitGroup
.Wait()
1413 fake
:= fakeDNSServer
{rh
: func(n
, s
string, q dnsmessage
.Message
, t time
.Time
) (dnsmessage
.Message
, error
) {
1414 time
.Sleep(10 * time
.Microsecond
)
1415 return dnsmessage
.Message
{}, poll
.ErrTimeout
1417 r
:= Resolver
{PreferGo
: true, Dial
: fake
.DialContext
}
1419 // The timeout here is less than the timeout used by the server,
1420 // so the goroutine started to query the (fake) server will hang
1421 // around after this test is done if we don't call dnsWaitGroup.Wait.
1422 ctx
, cancel
:= context
.WithTimeout(context
.Background(), 2*time
.Microsecond
)
1424 _
, err
:= r
.LookupIPAddr(ctx
, "where.are.they.now")
1426 t
.Fatal("fake DNS lookup unexpectedly succeeded")
1430 // Issue 8434: verify that Temporary returns true on an error when rcode
1432 func TestIssue8434(t
*testing
.T
) {
1433 msg
:= dnsmessage
.Message
{
1434 Header
: dnsmessage
.Header
{
1435 RCode
: dnsmessage
.RCodeServerFailure
,
1438 b
, err
:= msg
.Pack()
1440 t
.Fatal("Pack failed:", err
)
1442 var p dnsmessage
.Parser
1443 h
, err
:= p
.Start(b
)
1445 t
.Fatal("Start failed:", err
)
1447 if err
:= p
.SkipAllQuestions(); err
!= nil {
1448 t
.Fatal("SkipAllQuestions failed:", err
)
1451 err
= checkHeader(&p
, h
, "golang.org", "foo:53")
1453 t
.Fatal("expected an error")
1455 if ne
, ok
:= err
.(Error
); !ok
{
1456 t
.Fatalf("err = %#v; wanted something supporting net.Error", err
)
1457 } else if !ne
.Temporary() {
1458 t
.Fatalf("Temporary = false for err = %#v; want Temporary == true", err
)
1460 if de
, ok
:= err
.(*DNSError
); !ok
{
1461 t
.Fatalf("err = %#v; wanted a *net.DNSError", err
)
1462 } else if !de
.IsTemporary
{
1463 t
.Fatalf("IsTemporary = false for err = %#v; want IsTemporary == true", err
)
1467 // Issue 12778: verify that NXDOMAIN without RA bit errors as
1468 // "no such host" and not "server misbehaving"
1470 // Issue 25336: verify that NXDOMAIN errors fail fast.
1471 func TestIssue12778(t
*testing
.T
) {
1473 fake
:= fakeDNSServer
{
1474 rh
: func(n
, _
string, q dnsmessage
.Message
, _ time
.Time
) (dnsmessage
.Message
, error
) {
1476 return dnsmessage
.Message
{
1477 Header
: dnsmessage
.Header
{
1480 RCode
: dnsmessage
.RCodeNameError
,
1481 RecursionAvailable
: false,
1483 Questions
: q
.Questions
,
1487 r
:= Resolver
{PreferGo
: true, Dial
: fake
.DialContext
}
1489 resolvConf
.mu
.RLock()
1490 conf
:= resolvConf
.dnsConfig
1491 resolvConf
.mu
.RUnlock()
1493 ctx
, cancel
:= context
.WithCancel(context
.Background())
1496 _
, _
, err
:= r
.tryOneName(ctx
, conf
, ".", dnsmessage
.TypeALL
)
1499 t
.Errorf("got %d lookups, wanted 1", lookups
)
1503 t
.Fatal("expected an error")
1505 de
, ok
:= err
.(*DNSError
)
1507 t
.Fatalf("err = %#v; wanted a *net.DNSError", err
)
1509 if de
.Err
!= errNoSuchHost
.Error() {
1510 t
.Fatalf("Err = %#v; wanted %q", de
.Err
, errNoSuchHost
.Error())
1514 // Issue 26573: verify that Conns that don't implement PacketConn are treated
1515 // as streams even when udp was requested.
1516 func TestDNSDialTCP(t
*testing
.T
) {
1517 fake
:= fakeDNSServer
{
1518 rh
: func(n
, _
string, q dnsmessage
.Message
, _ time
.Time
) (dnsmessage
.Message
, error
) {
1519 r
:= dnsmessage
.Message
{
1520 Header
: dnsmessage
.Header
{
1523 RCode
: dnsmessage
.RCodeSuccess
,
1525 Questions
: q
.Questions
,
1531 r
:= Resolver
{PreferGo
: true, Dial
: fake
.DialContext
}
1532 ctx
:= context
.Background()
1533 _
, _
, err
:= r
.exchange(ctx
, "0.0.0.0", mustQuestion("com.", dnsmessage
.TypeALL
, dnsmessage
.ClassINET
), time
.Second
)
1535 t
.Fatal("exhange failed:", err
)