]> Cypherpunks.ru repositories - gostls13.git/blob - src/net/dnsclient_unix_test.go
cmd/compile/internal/inline: score call sites exposed by inlines
[gostls13.git] / src / net / dnsclient_unix_test.go
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.
4
5 //go:build unix
6
7 package net
8
9 import (
10         "context"
11         "errors"
12         "fmt"
13         "os"
14         "path"
15         "path/filepath"
16         "reflect"
17         "runtime"
18         "slices"
19         "strings"
20         "sync"
21         "sync/atomic"
22         "testing"
23         "time"
24
25         "golang.org/x/net/dns/dnsmessage"
26 )
27
28 var goResolver = Resolver{PreferGo: true}
29
30 // Test address from 192.0.2.0/24 block, reserved by RFC 5737 for documentation.
31 var TestAddr = [4]byte{0xc0, 0x00, 0x02, 0x01}
32
33 // Test address from 2001:db8::/32 block, reserved by RFC 3849 for documentation.
34 var TestAddr6 = [16]byte{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
35
36 func mustNewName(name string) dnsmessage.Name {
37         nn, err := dnsmessage.NewName(name)
38         if err != nil {
39                 panic(fmt.Sprint("creating name: ", err))
40         }
41         return nn
42 }
43
44 func mustQuestion(name string, qtype dnsmessage.Type, class dnsmessage.Class) dnsmessage.Question {
45         return dnsmessage.Question{
46                 Name:  mustNewName(name),
47                 Type:  qtype,
48                 Class: class,
49         }
50 }
51
52 var dnsTransportFallbackTests = []struct {
53         server   string
54         question dnsmessage.Question
55         timeout  int
56         rcode    dnsmessage.RCode
57 }{
58         // Querying "com." with qtype=255 usually makes an answer
59         // which requires more than 512 bytes.
60         {"8.8.8.8:53", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), 2, dnsmessage.RCodeSuccess},
61         {"8.8.4.4:53", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), 4, dnsmessage.RCodeSuccess},
62 }
63
64 func TestDNSTransportFallback(t *testing.T) {
65         fake := fakeDNSServer{
66                 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
67                         r := dnsmessage.Message{
68                                 Header: dnsmessage.Header{
69                                         ID:       q.Header.ID,
70                                         Response: true,
71                                         RCode:    dnsmessage.RCodeSuccess,
72                                 },
73                                 Questions: q.Questions,
74                         }
75                         if n == "udp" {
76                                 r.Header.Truncated = true
77                         }
78                         return r, nil
79                 },
80         }
81         r := Resolver{PreferGo: true, Dial: fake.DialContext}
82         for _, tt := range dnsTransportFallbackTests {
83                 ctx, cancel := context.WithCancel(context.Background())
84                 defer cancel()
85                 _, h, err := r.exchange(ctx, tt.server, tt.question, time.Second, useUDPOrTCP, false)
86                 if err != nil {
87                         t.Error(err)
88                         continue
89                 }
90                 if h.RCode != tt.rcode {
91                         t.Errorf("got %v from %v; want %v", h.RCode, tt.server, tt.rcode)
92                         continue
93                 }
94         }
95 }
96
97 // See RFC 6761 for further information about the reserved, pseudo
98 // domain names.
99 var specialDomainNameTests = []struct {
100         question dnsmessage.Question
101         rcode    dnsmessage.RCode
102 }{
103         // Name resolution APIs and libraries should not recognize the
104         // followings as special.
105         {mustQuestion("1.0.168.192.in-addr.arpa.", dnsmessage.TypePTR, dnsmessage.ClassINET), dnsmessage.RCodeNameError},
106         {mustQuestion("test.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeNameError},
107         {mustQuestion("example.com.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeSuccess},
108
109         // Name resolution APIs and libraries should recognize the
110         // followings as special and should not send any queries.
111         // Though, we test those names here for verifying negative
112         // answers at DNS query-response interaction level.
113         {mustQuestion("localhost.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeNameError},
114         {mustQuestion("invalid.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeNameError},
115 }
116
117 func TestSpecialDomainName(t *testing.T) {
118         fake := fakeDNSServer{rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
119                 r := dnsmessage.Message{
120                         Header: dnsmessage.Header{
121                                 ID:       q.ID,
122                                 Response: true,
123                         },
124                         Questions: q.Questions,
125                 }
126
127                 switch q.Questions[0].Name.String() {
128                 case "example.com.":
129                         r.Header.RCode = dnsmessage.RCodeSuccess
130                 default:
131                         r.Header.RCode = dnsmessage.RCodeNameError
132                 }
133
134                 return r, nil
135         }}
136         r := Resolver{PreferGo: true, Dial: fake.DialContext}
137         server := "8.8.8.8:53"
138         for _, tt := range specialDomainNameTests {
139                 ctx, cancel := context.WithCancel(context.Background())
140                 defer cancel()
141                 _, h, err := r.exchange(ctx, server, tt.question, 3*time.Second, useUDPOrTCP, false)
142                 if err != nil {
143                         t.Error(err)
144                         continue
145                 }
146                 if h.RCode != tt.rcode {
147                         t.Errorf("got %v from %v; want %v", h.RCode, server, tt.rcode)
148                         continue
149                 }
150         }
151 }
152
153 // Issue 13705: don't try to resolve onion addresses, etc
154 func TestAvoidDNSName(t *testing.T) {
155         tests := []struct {
156                 name  string
157                 avoid bool
158         }{
159                 {"foo.com", false},
160                 {"foo.com.", false},
161
162                 {"foo.onion.", true},
163                 {"foo.onion", true},
164                 {"foo.ONION", true},
165                 {"foo.ONION.", true},
166
167                 // But do resolve *.local address; Issue 16739
168                 {"foo.local.", false},
169                 {"foo.local", false},
170                 {"foo.LOCAL", false},
171                 {"foo.LOCAL.", false},
172
173                 {"", true}, // will be rejected earlier too
174
175                 // Without stuff before onion/local, they're fine to
176                 // use DNS. With a search path,
177                 // "onion.vegetables.com" can use DNS. Without a
178                 // search path (or with a trailing dot), the queries
179                 // are just kinda useless, but don't reveal anything
180                 // private.
181                 {"local", false},
182                 {"onion", false},
183                 {"local.", false},
184                 {"onion.", false},
185         }
186         for _, tt := range tests {
187                 got := avoidDNS(tt.name)
188                 if got != tt.avoid {
189                         t.Errorf("avoidDNS(%q) = %v; want %v", tt.name, got, tt.avoid)
190                 }
191         }
192 }
193
194 func TestNameListAvoidDNS(t *testing.T) {
195         c := &dnsConfig{search: []string{"go.dev.", "onion."}}
196         got := c.nameList("www")
197         if !slices.Equal(got, []string{"www.", "www.go.dev."}) {
198                 t.Fatalf(`nameList("www") = %v, want "www.", "www.go.dev."`, got)
199         }
200
201         got = c.nameList("www.onion")
202         if !slices.Equal(got, []string{"www.onion.go.dev."}) {
203                 t.Fatalf(`nameList("www.onion") = %v, want "www.onion.go.dev."`, got)
204         }
205 }
206
207 var fakeDNSServerSuccessful = fakeDNSServer{rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
208         r := dnsmessage.Message{
209                 Header: dnsmessage.Header{
210                         ID:       q.ID,
211                         Response: true,
212                 },
213                 Questions: q.Questions,
214         }
215         if len(q.Questions) == 1 && q.Questions[0].Type == dnsmessage.TypeA {
216                 r.Answers = []dnsmessage.Resource{
217                         {
218                                 Header: dnsmessage.ResourceHeader{
219                                         Name:   q.Questions[0].Name,
220                                         Type:   dnsmessage.TypeA,
221                                         Class:  dnsmessage.ClassINET,
222                                         Length: 4,
223                                 },
224                                 Body: &dnsmessage.AResource{
225                                         A: TestAddr,
226                                 },
227                         },
228                 }
229         }
230         return r, nil
231 }}
232
233 // Issue 13705: don't try to resolve onion addresses, etc
234 func TestLookupTorOnion(t *testing.T) {
235         defer dnsWaitGroup.Wait()
236         r := Resolver{PreferGo: true, Dial: fakeDNSServerSuccessful.DialContext}
237         addrs, err := r.LookupIPAddr(context.Background(), "foo.onion.")
238         if err != nil {
239                 t.Fatalf("lookup = %v; want nil", err)
240         }
241         if len(addrs) > 0 {
242                 t.Errorf("unexpected addresses: %v", addrs)
243         }
244 }
245
246 type resolvConfTest struct {
247         dir  string
248         path string
249         *resolverConfig
250 }
251
252 func newResolvConfTest() (*resolvConfTest, error) {
253         dir, err := os.MkdirTemp("", "go-resolvconftest")
254         if err != nil {
255                 return nil, err
256         }
257         conf := &resolvConfTest{
258                 dir:            dir,
259                 path:           path.Join(dir, "resolv.conf"),
260                 resolverConfig: &resolvConf,
261         }
262         conf.initOnce.Do(conf.init)
263         return conf, nil
264 }
265
266 func (conf *resolvConfTest) write(lines []string) error {
267         f, err := os.OpenFile(conf.path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
268         if err != nil {
269                 return err
270         }
271         if _, err := f.WriteString(strings.Join(lines, "\n")); err != nil {
272                 f.Close()
273                 return err
274         }
275         f.Close()
276         return nil
277 }
278
279 func (conf *resolvConfTest) writeAndUpdate(lines []string) error {
280         return conf.writeAndUpdateWithLastCheckedTime(lines, time.Now().Add(time.Hour))
281 }
282
283 func (conf *resolvConfTest) writeAndUpdateWithLastCheckedTime(lines []string, lastChecked time.Time) error {
284         if err := conf.write(lines); err != nil {
285                 return err
286         }
287         return conf.forceUpdate(conf.path, lastChecked)
288 }
289
290 func (conf *resolvConfTest) forceUpdate(name string, lastChecked time.Time) error {
291         dnsConf := dnsReadConfig(name)
292         if !conf.forceUpdateConf(dnsConf, lastChecked) {
293                 return fmt.Errorf("tryAcquireSema for %s failed", name)
294         }
295         return nil
296 }
297
298 func (conf *resolvConfTest) forceUpdateConf(c *dnsConfig, lastChecked time.Time) bool {
299         conf.dnsConfig.Store(c)
300         for i := 0; i < 5; i++ {
301                 if conf.tryAcquireSema() {
302                         conf.lastChecked = lastChecked
303                         conf.releaseSema()
304                         return true
305                 }
306         }
307         return false
308 }
309
310 func (conf *resolvConfTest) servers() []string {
311         return conf.dnsConfig.Load().servers
312 }
313
314 func (conf *resolvConfTest) teardown() error {
315         err := conf.forceUpdate("/etc/resolv.conf", time.Time{})
316         os.RemoveAll(conf.dir)
317         return err
318 }
319
320 var updateResolvConfTests = []struct {
321         name    string   // query name
322         lines   []string // resolver configuration lines
323         servers []string // expected name servers
324 }{
325         {
326                 name:    "golang.org",
327                 lines:   []string{"nameserver 8.8.8.8"},
328                 servers: []string{"8.8.8.8:53"},
329         },
330         {
331                 name:    "",
332                 lines:   nil, // an empty resolv.conf should use defaultNS as name servers
333                 servers: defaultNS,
334         },
335         {
336                 name:    "www.example.com",
337                 lines:   []string{"nameserver 8.8.4.4"},
338                 servers: []string{"8.8.4.4:53"},
339         },
340 }
341
342 func TestUpdateResolvConf(t *testing.T) {
343         defer dnsWaitGroup.Wait()
344
345         r := Resolver{PreferGo: true, Dial: fakeDNSServerSuccessful.DialContext}
346
347         conf, err := newResolvConfTest()
348         if err != nil {
349                 t.Fatal(err)
350         }
351         defer conf.teardown()
352
353         for i, tt := range updateResolvConfTests {
354                 if err := conf.writeAndUpdate(tt.lines); err != nil {
355                         t.Error(err)
356                         continue
357                 }
358                 if tt.name != "" {
359                         var wg sync.WaitGroup
360                         const N = 10
361                         wg.Add(N)
362                         for j := 0; j < N; j++ {
363                                 go func(name string) {
364                                         defer wg.Done()
365                                         ips, err := r.LookupIPAddr(context.Background(), name)
366                                         if err != nil {
367                                                 t.Error(err)
368                                                 return
369                                         }
370                                         if len(ips) == 0 {
371                                                 t.Errorf("no records for %s", name)
372                                                 return
373                                         }
374                                 }(tt.name)
375                         }
376                         wg.Wait()
377                 }
378                 servers := conf.servers()
379                 if !reflect.DeepEqual(servers, tt.servers) {
380                         t.Errorf("#%d: got %v; want %v", i, servers, tt.servers)
381                         continue
382                 }
383         }
384 }
385
386 var goLookupIPWithResolverConfigTests = []struct {
387         name  string
388         lines []string // resolver configuration lines
389         error
390         a, aaaa bool // whether response contains A, AAAA-record
391 }{
392         // no records, transport timeout
393         {
394                 "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j",
395                 []string{
396                         "options timeout:1 attempts:1",
397                         "nameserver 255.255.255.255", // please forgive us for abuse of limited broadcast address
398                 },
399                 &DNSError{Name: "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", Server: "255.255.255.255:53", IsTimeout: true},
400                 false, false,
401         },
402
403         // no records, non-existent domain
404         {
405                 "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j",
406                 []string{
407                         "options timeout:3 attempts:1",
408                         "nameserver 8.8.8.8",
409                 },
410                 &DNSError{Name: "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", Server: "8.8.8.8:53", IsTimeout: false},
411                 false, false,
412         },
413
414         // a few A records, no AAAA records
415         {
416                 "ipv4.google.com.",
417                 []string{
418                         "nameserver 8.8.8.8",
419                         "nameserver 2001:4860:4860::8888",
420                 },
421                 nil,
422                 true, false,
423         },
424         {
425                 "ipv4.google.com",
426                 []string{
427                         "domain golang.org",
428                         "nameserver 2001:4860:4860::8888",
429                         "nameserver 8.8.8.8",
430                 },
431                 nil,
432                 true, false,
433         },
434         {
435                 "ipv4.google.com",
436                 []string{
437                         "search x.golang.org y.golang.org",
438                         "nameserver 2001:4860:4860::8888",
439                         "nameserver 8.8.8.8",
440                 },
441                 nil,
442                 true, false,
443         },
444
445         // no A records, a few AAAA records
446         {
447                 "ipv6.google.com.",
448                 []string{
449                         "nameserver 2001:4860:4860::8888",
450                         "nameserver 8.8.8.8",
451                 },
452                 nil,
453                 false, true,
454         },
455         {
456                 "ipv6.google.com",
457                 []string{
458                         "domain golang.org",
459                         "nameserver 8.8.8.8",
460                         "nameserver 2001:4860:4860::8888",
461                 },
462                 nil,
463                 false, true,
464         },
465         {
466                 "ipv6.google.com",
467                 []string{
468                         "search x.golang.org y.golang.org",
469                         "nameserver 8.8.8.8",
470                         "nameserver 2001:4860:4860::8888",
471                 },
472                 nil,
473                 false, true,
474         },
475
476         // both A and AAAA records
477         {
478                 "hostname.as112.net", // see RFC 7534
479                 []string{
480                         "domain golang.org",
481                         "nameserver 2001:4860:4860::8888",
482                         "nameserver 8.8.8.8",
483                 },
484                 nil,
485                 true, true,
486         },
487         {
488                 "hostname.as112.net", // see RFC 7534
489                 []string{
490                         "search x.golang.org y.golang.org",
491                         "nameserver 2001:4860:4860::8888",
492                         "nameserver 8.8.8.8",
493                 },
494                 nil,
495                 true, true,
496         },
497 }
498
499 func TestGoLookupIPWithResolverConfig(t *testing.T) {
500         defer dnsWaitGroup.Wait()
501         fake := fakeDNSServer{rh: func(n, s string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
502                 switch s {
503                 case "[2001:4860:4860::8888]:53", "8.8.8.8:53":
504                         break
505                 default:
506                         time.Sleep(10 * time.Millisecond)
507                         return dnsmessage.Message{}, os.ErrDeadlineExceeded
508                 }
509                 r := dnsmessage.Message{
510                         Header: dnsmessage.Header{
511                                 ID:       q.ID,
512                                 Response: true,
513                         },
514                         Questions: q.Questions,
515                 }
516                 for _, question := range q.Questions {
517                         switch question.Type {
518                         case dnsmessage.TypeA:
519                                 switch question.Name.String() {
520                                 case "hostname.as112.net.":
521                                         break
522                                 case "ipv4.google.com.":
523                                         r.Answers = append(r.Answers, dnsmessage.Resource{
524                                                 Header: dnsmessage.ResourceHeader{
525                                                         Name:   q.Questions[0].Name,
526                                                         Type:   dnsmessage.TypeA,
527                                                         Class:  dnsmessage.ClassINET,
528                                                         Length: 4,
529                                                 },
530                                                 Body: &dnsmessage.AResource{
531                                                         A: TestAddr,
532                                                 },
533                                         })
534                                 default:
535
536                                 }
537                         case dnsmessage.TypeAAAA:
538                                 switch question.Name.String() {
539                                 case "hostname.as112.net.":
540                                         break
541                                 case "ipv6.google.com.":
542                                         r.Answers = append(r.Answers, dnsmessage.Resource{
543                                                 Header: dnsmessage.ResourceHeader{
544                                                         Name:   q.Questions[0].Name,
545                                                         Type:   dnsmessage.TypeAAAA,
546                                                         Class:  dnsmessage.ClassINET,
547                                                         Length: 16,
548                                                 },
549                                                 Body: &dnsmessage.AAAAResource{
550                                                         AAAA: TestAddr6,
551                                                 },
552                                         })
553                                 }
554                         }
555                 }
556                 return r, nil
557         }}
558         r := Resolver{PreferGo: true, Dial: fake.DialContext}
559
560         conf, err := newResolvConfTest()
561         if err != nil {
562                 t.Fatal(err)
563         }
564         defer conf.teardown()
565
566         for _, tt := range goLookupIPWithResolverConfigTests {
567                 if err := conf.writeAndUpdate(tt.lines); err != nil {
568                         t.Error(err)
569                         continue
570                 }
571                 addrs, err := r.LookupIPAddr(context.Background(), tt.name)
572                 if err != nil {
573                         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) {
574                                 t.Errorf("got %v; want %v", err, tt.error)
575                         }
576                         continue
577                 }
578                 if len(addrs) == 0 {
579                         t.Errorf("no records for %s", tt.name)
580                 }
581                 if !tt.a && !tt.aaaa && len(addrs) > 0 {
582                         t.Errorf("unexpected %v for %s", addrs, tt.name)
583                 }
584                 for _, addr := range addrs {
585                         if !tt.a && addr.IP.To4() != nil {
586                                 t.Errorf("got %v; must not be IPv4 address", addr)
587                         }
588                         if !tt.aaaa && addr.IP.To16() != nil && addr.IP.To4() == nil {
589                                 t.Errorf("got %v; must not be IPv6 address", addr)
590                         }
591                 }
592         }
593 }
594
595 // Test that goLookupIPOrder falls back to the host file when no DNS servers are available.
596 func TestGoLookupIPOrderFallbackToFile(t *testing.T) {
597         defer dnsWaitGroup.Wait()
598
599         fake := fakeDNSServer{rh: func(n, s string, q dnsmessage.Message, tm time.Time) (dnsmessage.Message, error) {
600                 r := dnsmessage.Message{
601                         Header: dnsmessage.Header{
602                                 ID:       q.ID,
603                                 Response: true,
604                         },
605                         Questions: q.Questions,
606                 }
607                 return r, nil
608         }}
609         r := Resolver{PreferGo: true, Dial: fake.DialContext}
610
611         // Add a config that simulates no dns servers being available.
612         conf, err := newResolvConfTest()
613         if err != nil {
614                 t.Fatal(err)
615         }
616         defer conf.teardown()
617
618         if err := conf.writeAndUpdate([]string{}); err != nil {
619                 t.Fatal(err)
620         }
621         // Redirect host file lookups.
622         defer func(orig string) { hostsFilePath = orig }(hostsFilePath)
623         hostsFilePath = "testdata/hosts"
624
625         for _, order := range []hostLookupOrder{hostLookupFilesDNS, hostLookupDNSFiles} {
626                 name := fmt.Sprintf("order %v", order)
627                 // First ensure that we get an error when contacting a non-existent host.
628                 _, _, err := r.goLookupIPCNAMEOrder(context.Background(), "ip", "notarealhost", order, nil)
629                 if err == nil {
630                         t.Errorf("%s: expected error while looking up name not in hosts file", name)
631                         continue
632                 }
633
634                 // Now check that we get an address when the name appears in the hosts file.
635                 addrs, _, err := r.goLookupIPCNAMEOrder(context.Background(), "ip", "thor", order, nil) // entry is in "testdata/hosts"
636                 if err != nil {
637                         t.Errorf("%s: expected to successfully lookup host entry", name)
638                         continue
639                 }
640                 if len(addrs) != 1 {
641                         t.Errorf("%s: expected exactly one result, but got %v", name, addrs)
642                         continue
643                 }
644                 if got, want := addrs[0].String(), "127.1.1.1"; got != want {
645                         t.Errorf("%s: address doesn't match expectation. got %v, want %v", name, got, want)
646                 }
647         }
648 }
649
650 // Issue 12712.
651 // When using search domains, return the error encountered
652 // querying the original name instead of an error encountered
653 // querying a generated name.
654 func TestErrorForOriginalNameWhenSearching(t *testing.T) {
655         defer dnsWaitGroup.Wait()
656
657         const fqdn = "doesnotexist.domain"
658
659         conf, err := newResolvConfTest()
660         if err != nil {
661                 t.Fatal(err)
662         }
663         defer conf.teardown()
664
665         if err := conf.writeAndUpdate([]string{"search servfail"}); err != nil {
666                 t.Fatal(err)
667         }
668
669         fake := fakeDNSServer{rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
670                 r := dnsmessage.Message{
671                         Header: dnsmessage.Header{
672                                 ID:       q.ID,
673                                 Response: true,
674                         },
675                         Questions: q.Questions,
676                 }
677
678                 switch q.Questions[0].Name.String() {
679                 case fqdn + ".servfail.":
680                         r.Header.RCode = dnsmessage.RCodeServerFailure
681                 default:
682                         r.Header.RCode = dnsmessage.RCodeNameError
683                 }
684
685                 return r, nil
686         }}
687
688         cases := []struct {
689                 strictErrors bool
690                 wantErr      *DNSError
691         }{
692                 {true, &DNSError{Name: fqdn, Err: "server misbehaving", IsTemporary: true}},
693                 {false, &DNSError{Name: fqdn, Err: errNoSuchHost.Error(), IsNotFound: true}},
694         }
695         for _, tt := range cases {
696                 r := Resolver{PreferGo: true, StrictErrors: tt.strictErrors, Dial: fake.DialContext}
697                 _, err = r.LookupIPAddr(context.Background(), fqdn)
698                 if err == nil {
699                         t.Fatal("expected an error")
700                 }
701
702                 want := tt.wantErr
703                 if err, ok := err.(*DNSError); !ok || err.Name != want.Name || err.Err != want.Err || err.IsTemporary != want.IsTemporary {
704                         t.Errorf("got %v; want %v", err, want)
705                 }
706         }
707 }
708
709 // Issue 15434. If a name server gives a lame referral, continue to the next.
710 func TestIgnoreLameReferrals(t *testing.T) {
711         defer dnsWaitGroup.Wait()
712
713         conf, err := newResolvConfTest()
714         if err != nil {
715                 t.Fatal(err)
716         }
717         defer conf.teardown()
718
719         if err := conf.writeAndUpdate([]string{"nameserver 192.0.2.1", // the one that will give a lame referral
720                 "nameserver 192.0.2.2"}); err != nil {
721                 t.Fatal(err)
722         }
723
724         fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
725                 t.Log(s, q)
726                 r := dnsmessage.Message{
727                         Header: dnsmessage.Header{
728                                 ID:       q.ID,
729                                 Response: true,
730                         },
731                         Questions: q.Questions,
732                 }
733
734                 if s == "192.0.2.2:53" {
735                         r.Header.RecursionAvailable = true
736                         if q.Questions[0].Type == dnsmessage.TypeA {
737                                 r.Answers = []dnsmessage.Resource{
738                                         {
739                                                 Header: dnsmessage.ResourceHeader{
740                                                         Name:   q.Questions[0].Name,
741                                                         Type:   dnsmessage.TypeA,
742                                                         Class:  dnsmessage.ClassINET,
743                                                         Length: 4,
744                                                 },
745                                                 Body: &dnsmessage.AResource{
746                                                         A: TestAddr,
747                                                 },
748                                         },
749                                 }
750                         }
751                 }
752
753                 return r, nil
754         }}
755         r := Resolver{PreferGo: true, Dial: fake.DialContext}
756
757         addrs, err := r.LookupIPAddr(context.Background(), "www.golang.org")
758         if err != nil {
759                 t.Fatal(err)
760         }
761
762         if got := len(addrs); got != 1 {
763                 t.Fatalf("got %d addresses, want 1", got)
764         }
765
766         if got, want := addrs[0].String(), "192.0.2.1"; got != want {
767                 t.Fatalf("got address %v, want %v", got, want)
768         }
769 }
770
771 func BenchmarkGoLookupIP(b *testing.B) {
772         testHookUninstaller.Do(uninstallTestHooks)
773         ctx := context.Background()
774         b.ReportAllocs()
775
776         for i := 0; i < b.N; i++ {
777                 goResolver.LookupIPAddr(ctx, "www.example.com")
778         }
779 }
780
781 func BenchmarkGoLookupIPNoSuchHost(b *testing.B) {
782         testHookUninstaller.Do(uninstallTestHooks)
783         ctx := context.Background()
784         b.ReportAllocs()
785
786         for i := 0; i < b.N; i++ {
787                 goResolver.LookupIPAddr(ctx, "some.nonexistent")
788         }
789 }
790
791 func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) {
792         testHookUninstaller.Do(uninstallTestHooks)
793
794         conf, err := newResolvConfTest()
795         if err != nil {
796                 b.Fatal(err)
797         }
798         defer conf.teardown()
799
800         lines := []string{
801                 "nameserver 203.0.113.254", // use TEST-NET-3 block, see RFC 5737
802                 "nameserver 8.8.8.8",
803         }
804         if err := conf.writeAndUpdate(lines); err != nil {
805                 b.Fatal(err)
806         }
807         ctx := context.Background()
808         b.ReportAllocs()
809
810         for i := 0; i < b.N; i++ {
811                 goResolver.LookupIPAddr(ctx, "www.example.com")
812         }
813 }
814
815 type fakeDNSServer struct {
816         rh        func(n, s string, q dnsmessage.Message, t time.Time) (dnsmessage.Message, error)
817         alwaysTCP bool
818 }
819
820 func (server *fakeDNSServer) DialContext(_ context.Context, n, s string) (Conn, error) {
821         if server.alwaysTCP || n == "tcp" || n == "tcp4" || n == "tcp6" {
822                 return &fakeDNSConn{tcp: true, server: server, n: n, s: s}, nil
823         }
824         return &fakeDNSPacketConn{fakeDNSConn: fakeDNSConn{tcp: false, server: server, n: n, s: s}}, nil
825 }
826
827 type fakeDNSConn struct {
828         Conn
829         tcp    bool
830         server *fakeDNSServer
831         n      string
832         s      string
833         q      dnsmessage.Message
834         t      time.Time
835         buf    []byte
836 }
837
838 func (f *fakeDNSConn) Close() error {
839         return nil
840 }
841
842 func (f *fakeDNSConn) Read(b []byte) (int, error) {
843         if len(f.buf) > 0 {
844                 n := copy(b, f.buf)
845                 f.buf = f.buf[n:]
846                 return n, nil
847         }
848
849         resp, err := f.server.rh(f.n, f.s, f.q, f.t)
850         if err != nil {
851                 return 0, err
852         }
853
854         bb := make([]byte, 2, 514)
855         bb, err = resp.AppendPack(bb)
856         if err != nil {
857                 return 0, fmt.Errorf("cannot marshal DNS message: %v", err)
858         }
859
860         if f.tcp {
861                 l := len(bb) - 2
862                 bb[0] = byte(l >> 8)
863                 bb[1] = byte(l)
864                 f.buf = bb
865                 return f.Read(b)
866         }
867
868         bb = bb[2:]
869         if len(b) < len(bb) {
870                 return 0, errors.New("read would fragment DNS message")
871         }
872
873         copy(b, bb)
874         return len(bb), nil
875 }
876
877 func (f *fakeDNSConn) Write(b []byte) (int, error) {
878         if f.tcp && len(b) >= 2 {
879                 b = b[2:]
880         }
881         if f.q.Unpack(b) != nil {
882                 return 0, fmt.Errorf("cannot unmarshal DNS message fake %s (%d)", f.n, len(b))
883         }
884         return len(b), nil
885 }
886
887 func (f *fakeDNSConn) SetDeadline(t time.Time) error {
888         f.t = t
889         return nil
890 }
891
892 type fakeDNSPacketConn struct {
893         PacketConn
894         fakeDNSConn
895 }
896
897 func (f *fakeDNSPacketConn) SetDeadline(t time.Time) error {
898         return f.fakeDNSConn.SetDeadline(t)
899 }
900
901 func (f *fakeDNSPacketConn) Close() error {
902         return f.fakeDNSConn.Close()
903 }
904
905 // UDP round-tripper algorithm should ignore invalid DNS responses (issue 13281).
906 func TestIgnoreDNSForgeries(t *testing.T) {
907         c, s := Pipe()
908         go func() {
909                 b := make([]byte, maxDNSPacketSize)
910                 n, err := s.Read(b)
911                 if err != nil {
912                         t.Error(err)
913                         return
914                 }
915
916                 var msg dnsmessage.Message
917                 if msg.Unpack(b[:n]) != nil {
918                         t.Error("invalid DNS query:", err)
919                         return
920                 }
921
922                 s.Write([]byte("garbage DNS response packet"))
923
924                 msg.Header.Response = true
925                 msg.Header.ID++ // make invalid ID
926
927                 if b, err = msg.Pack(); err != nil {
928                         t.Error("failed to pack DNS response:", err)
929                         return
930                 }
931                 s.Write(b)
932
933                 msg.Header.ID-- // restore original ID
934                 msg.Answers = []dnsmessage.Resource{
935                         {
936                                 Header: dnsmessage.ResourceHeader{
937                                         Name:   mustNewName("www.example.com."),
938                                         Type:   dnsmessage.TypeA,
939                                         Class:  dnsmessage.ClassINET,
940                                         Length: 4,
941                                 },
942                                 Body: &dnsmessage.AResource{
943                                         A: TestAddr,
944                                 },
945                         },
946                 }
947
948                 b, err = msg.Pack()
949                 if err != nil {
950                         t.Error("failed to pack DNS response:", err)
951                         return
952                 }
953                 s.Write(b)
954         }()
955
956         msg := dnsmessage.Message{
957                 Header: dnsmessage.Header{
958                         ID: 42,
959                 },
960                 Questions: []dnsmessage.Question{
961                         {
962                                 Name:  mustNewName("www.example.com."),
963                                 Type:  dnsmessage.TypeA,
964                                 Class: dnsmessage.ClassINET,
965                         },
966                 },
967         }
968
969         b, err := msg.Pack()
970         if err != nil {
971                 t.Fatal("Pack failed:", err)
972         }
973
974         p, _, err := dnsPacketRoundTrip(c, 42, msg.Questions[0], b)
975         if err != nil {
976                 t.Fatalf("dnsPacketRoundTrip failed: %v", err)
977         }
978
979         p.SkipAllQuestions()
980         as, err := p.AllAnswers()
981         if err != nil {
982                 t.Fatal("AllAnswers failed:", err)
983         }
984         if got := as[0].Body.(*dnsmessage.AResource).A; got != TestAddr {
985                 t.Errorf("got address %v, want %v", got, TestAddr)
986         }
987 }
988
989 // Issue 16865. If a name server times out, continue to the next.
990 func TestRetryTimeout(t *testing.T) {
991         defer dnsWaitGroup.Wait()
992
993         conf, err := newResolvConfTest()
994         if err != nil {
995                 t.Fatal(err)
996         }
997         defer conf.teardown()
998
999         testConf := []string{
1000                 "nameserver 192.0.2.1", // the one that will timeout
1001                 "nameserver 192.0.2.2",
1002         }
1003         if err := conf.writeAndUpdate(testConf); err != nil {
1004                 t.Fatal(err)
1005         }
1006
1007         var deadline0 time.Time
1008
1009         fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) {
1010                 t.Log(s, q, deadline)
1011
1012                 if deadline.IsZero() {
1013                         t.Error("zero deadline")
1014                 }
1015
1016                 if s == "192.0.2.1:53" {
1017                         deadline0 = deadline
1018                         time.Sleep(10 * time.Millisecond)
1019                         return dnsmessage.Message{}, os.ErrDeadlineExceeded
1020                 }
1021
1022                 if deadline.Equal(deadline0) {
1023                         t.Error("deadline didn't change")
1024                 }
1025
1026                 return mockTXTResponse(q), nil
1027         }}
1028         r := &Resolver{PreferGo: true, Dial: fake.DialContext}
1029
1030         _, err = r.LookupTXT(context.Background(), "www.golang.org")
1031         if err != nil {
1032                 t.Fatal(err)
1033         }
1034
1035         if deadline0.IsZero() {
1036                 t.Error("deadline0 still zero", deadline0)
1037         }
1038 }
1039
1040 func TestRotate(t *testing.T) {
1041         // without rotation, always uses the first server
1042         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"})
1043
1044         // with rotation, rotates through back to first
1045         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"})
1046 }
1047
1048 func testRotate(t *testing.T, rotate bool, nameservers, wantServers []string) {
1049         defer dnsWaitGroup.Wait()
1050
1051         conf, err := newResolvConfTest()
1052         if err != nil {
1053                 t.Fatal(err)
1054         }
1055         defer conf.teardown()
1056
1057         var confLines []string
1058         for _, ns := range nameservers {
1059                 confLines = append(confLines, "nameserver "+ns)
1060         }
1061         if rotate {
1062                 confLines = append(confLines, "options rotate")
1063         }
1064
1065         if err := conf.writeAndUpdate(confLines); err != nil {
1066                 t.Fatal(err)
1067         }
1068
1069         var usedServers []string
1070         fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) {
1071                 usedServers = append(usedServers, s)
1072                 return mockTXTResponse(q), nil
1073         }}
1074         r := Resolver{PreferGo: true, Dial: fake.DialContext}
1075
1076         // len(nameservers) + 1 to allow rotation to get back to start
1077         for i := 0; i < len(nameservers)+1; i++ {
1078                 if _, err := r.LookupTXT(context.Background(), "www.golang.org"); err != nil {
1079                         t.Fatal(err)
1080                 }
1081         }
1082
1083         if !reflect.DeepEqual(usedServers, wantServers) {
1084                 t.Errorf("rotate=%t got used servers:\n%v\nwant:\n%v", rotate, usedServers, wantServers)
1085         }
1086 }
1087
1088 func mockTXTResponse(q dnsmessage.Message) dnsmessage.Message {
1089         r := dnsmessage.Message{
1090                 Header: dnsmessage.Header{
1091                         ID:                 q.ID,
1092                         Response:           true,
1093                         RecursionAvailable: true,
1094                 },
1095                 Questions: q.Questions,
1096                 Answers: []dnsmessage.Resource{
1097                         {
1098                                 Header: dnsmessage.ResourceHeader{
1099                                         Name:  q.Questions[0].Name,
1100                                         Type:  dnsmessage.TypeTXT,
1101                                         Class: dnsmessage.ClassINET,
1102                                 },
1103                                 Body: &dnsmessage.TXTResource{
1104                                         TXT: []string{"ok"},
1105                                 },
1106                         },
1107                 },
1108         }
1109
1110         return r
1111 }
1112
1113 // Issue 17448. With StrictErrors enabled, temporary errors should make
1114 // LookupIP fail rather than return a partial result.
1115 func TestStrictErrorsLookupIP(t *testing.T) {
1116         defer dnsWaitGroup.Wait()
1117
1118         conf, err := newResolvConfTest()
1119         if err != nil {
1120                 t.Fatal(err)
1121         }
1122         defer conf.teardown()
1123
1124         confData := []string{
1125                 "nameserver 192.0.2.53",
1126                 "search x.golang.org y.golang.org",
1127         }
1128         if err := conf.writeAndUpdate(confData); err != nil {
1129                 t.Fatal(err)
1130         }
1131
1132         const name = "test-issue19592"
1133         const server = "192.0.2.53:53"
1134         const searchX = "test-issue19592.x.golang.org."
1135         const searchY = "test-issue19592.y.golang.org."
1136         const ip4 = "192.0.2.1"
1137         const ip6 = "2001:db8::1"
1138
1139         type resolveWhichEnum int
1140         const (
1141                 resolveOK resolveWhichEnum = iota
1142                 resolveOpError
1143                 resolveServfail
1144                 resolveTimeout
1145         )
1146
1147         makeTempError := func(err string) error {
1148                 return &DNSError{
1149                         Err:         err,
1150                         Name:        name,
1151                         Server:      server,
1152                         IsTemporary: true,
1153                 }
1154         }
1155         makeTimeout := func() error {
1156                 return &DNSError{
1157                         Err:       os.ErrDeadlineExceeded.Error(),
1158                         Name:      name,
1159                         Server:    server,
1160                         IsTimeout: true,
1161                 }
1162         }
1163         makeNxDomain := func() error {
1164                 return &DNSError{
1165                         Err:        errNoSuchHost.Error(),
1166                         Name:       name,
1167                         Server:     server,
1168                         IsNotFound: true,
1169                 }
1170         }
1171
1172         cases := []struct {
1173                 desc          string
1174                 resolveWhich  func(quest dnsmessage.Question) resolveWhichEnum
1175                 wantStrictErr error
1176                 wantLaxErr    error
1177                 wantIPs       []string
1178         }{
1179                 {
1180                         desc: "No errors",
1181                         resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum {
1182                                 return resolveOK
1183                         },
1184                         wantIPs: []string{ip4, ip6},
1185                 },
1186                 {
1187                         desc: "searchX error fails in strict mode",
1188                         resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum {
1189                                 if quest.Name.String() == searchX {
1190                                         return resolveTimeout
1191                                 }
1192                                 return resolveOK
1193                         },
1194                         wantStrictErr: makeTimeout(),
1195                         wantIPs:       []string{ip4, ip6},
1196                 },
1197                 {
1198                         desc: "searchX IPv4-only timeout fails in strict mode",
1199                         resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum {
1200                                 if quest.Name.String() == searchX && quest.Type == dnsmessage.TypeA {
1201                                         return resolveTimeout
1202                                 }
1203                                 return resolveOK
1204                         },
1205                         wantStrictErr: makeTimeout(),
1206                         wantIPs:       []string{ip4, ip6},
1207                 },
1208                 {
1209                         desc: "searchX IPv6-only servfail fails in strict mode",
1210                         resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum {
1211                                 if quest.Name.String() == searchX && quest.Type == dnsmessage.TypeAAAA {
1212                                         return resolveServfail
1213                                 }
1214                                 return resolveOK
1215                         },
1216                         wantStrictErr: makeTempError("server misbehaving"),
1217                         wantIPs:       []string{ip4, ip6},
1218                 },
1219                 {
1220                         desc: "searchY error always fails",
1221                         resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum {
1222                                 if quest.Name.String() == searchY {
1223                                         return resolveTimeout
1224                                 }
1225                                 return resolveOK
1226                         },
1227                         wantStrictErr: makeTimeout(),
1228                         wantLaxErr:    makeNxDomain(), // This one reaches the "test." FQDN.
1229                 },
1230                 {
1231                         desc: "searchY IPv4-only socket error fails in strict mode",
1232                         resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum {
1233                                 if quest.Name.String() == searchY && quest.Type == dnsmessage.TypeA {
1234                                         return resolveOpError
1235                                 }
1236                                 return resolveOK
1237                         },
1238                         wantStrictErr: makeTempError("write: socket on fire"),
1239                         wantIPs:       []string{ip6},
1240                 },
1241                 {
1242                         desc: "searchY IPv6-only timeout fails in strict mode",
1243                         resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum {
1244                                 if quest.Name.String() == searchY && quest.Type == dnsmessage.TypeAAAA {
1245                                         return resolveTimeout
1246                                 }
1247                                 return resolveOK
1248                         },
1249                         wantStrictErr: makeTimeout(),
1250                         wantIPs:       []string{ip4},
1251                 },
1252         }
1253
1254         for i, tt := range cases {
1255                 fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) {
1256                         t.Log(s, q)
1257
1258                         switch tt.resolveWhich(q.Questions[0]) {
1259                         case resolveOK:
1260                                 // Handle below.
1261                         case resolveOpError:
1262                                 return dnsmessage.Message{}, &OpError{Op: "write", Err: fmt.Errorf("socket on fire")}
1263                         case resolveServfail:
1264                                 return dnsmessage.Message{
1265                                         Header: dnsmessage.Header{
1266                                                 ID:       q.ID,
1267                                                 Response: true,
1268                                                 RCode:    dnsmessage.RCodeServerFailure,
1269                                         },
1270                                         Questions: q.Questions,
1271                                 }, nil
1272                         case resolveTimeout:
1273                                 return dnsmessage.Message{}, os.ErrDeadlineExceeded
1274                         default:
1275                                 t.Fatal("Impossible resolveWhich")
1276                         }
1277
1278                         switch q.Questions[0].Name.String() {
1279                         case searchX, name + ".":
1280                                 // Return NXDOMAIN to utilize the search list.
1281                                 return dnsmessage.Message{
1282                                         Header: dnsmessage.Header{
1283                                                 ID:       q.ID,
1284                                                 Response: true,
1285                                                 RCode:    dnsmessage.RCodeNameError,
1286                                         },
1287                                         Questions: q.Questions,
1288                                 }, nil
1289                         case searchY:
1290                                 // Return records below.
1291                         default:
1292                                 return dnsmessage.Message{}, fmt.Errorf("Unexpected Name: %v", q.Questions[0].Name)
1293                         }
1294
1295                         r := dnsmessage.Message{
1296                                 Header: dnsmessage.Header{
1297                                         ID:       q.ID,
1298                                         Response: true,
1299                                 },
1300                                 Questions: q.Questions,
1301                         }
1302                         switch q.Questions[0].Type {
1303                         case dnsmessage.TypeA:
1304                                 r.Answers = []dnsmessage.Resource{
1305                                         {
1306                                                 Header: dnsmessage.ResourceHeader{
1307                                                         Name:   q.Questions[0].Name,
1308                                                         Type:   dnsmessage.TypeA,
1309                                                         Class:  dnsmessage.ClassINET,
1310                                                         Length: 4,
1311                                                 },
1312                                                 Body: &dnsmessage.AResource{
1313                                                         A: TestAddr,
1314                                                 },
1315                                         },
1316                                 }
1317                         case dnsmessage.TypeAAAA:
1318                                 r.Answers = []dnsmessage.Resource{
1319                                         {
1320                                                 Header: dnsmessage.ResourceHeader{
1321                                                         Name:   q.Questions[0].Name,
1322                                                         Type:   dnsmessage.TypeAAAA,
1323                                                         Class:  dnsmessage.ClassINET,
1324                                                         Length: 16,
1325                                                 },
1326                                                 Body: &dnsmessage.AAAAResource{
1327                                                         AAAA: TestAddr6,
1328                                                 },
1329                                         },
1330                                 }
1331                         default:
1332                                 return dnsmessage.Message{}, fmt.Errorf("Unexpected Type: %v", q.Questions[0].Type)
1333                         }
1334                         return r, nil
1335                 }}
1336
1337                 for _, strict := range []bool{true, false} {
1338                         r := Resolver{PreferGo: true, StrictErrors: strict, Dial: fake.DialContext}
1339                         ips, err := r.LookupIPAddr(context.Background(), name)
1340
1341                         var wantErr error
1342                         if strict {
1343                                 wantErr = tt.wantStrictErr
1344                         } else {
1345                                 wantErr = tt.wantLaxErr
1346                         }
1347                         if !reflect.DeepEqual(err, wantErr) {
1348                                 t.Errorf("#%d (%s) strict=%v: got err %#v; want %#v", i, tt.desc, strict, err, wantErr)
1349                         }
1350
1351                         gotIPs := map[string]struct{}{}
1352                         for _, ip := range ips {
1353                                 gotIPs[ip.String()] = struct{}{}
1354                         }
1355                         wantIPs := map[string]struct{}{}
1356                         if wantErr == nil {
1357                                 for _, ip := range tt.wantIPs {
1358                                         wantIPs[ip] = struct{}{}
1359                                 }
1360                         }
1361                         if !reflect.DeepEqual(gotIPs, wantIPs) {
1362                                 t.Errorf("#%d (%s) strict=%v: got ips %v; want %v", i, tt.desc, strict, gotIPs, wantIPs)
1363                         }
1364                 }
1365         }
1366 }
1367
1368 // Issue 17448. With StrictErrors enabled, temporary errors should make
1369 // LookupTXT stop walking the search list.
1370 func TestStrictErrorsLookupTXT(t *testing.T) {
1371         defer dnsWaitGroup.Wait()
1372
1373         conf, err := newResolvConfTest()
1374         if err != nil {
1375                 t.Fatal(err)
1376         }
1377         defer conf.teardown()
1378
1379         confData := []string{
1380                 "nameserver 192.0.2.53",
1381                 "search x.golang.org y.golang.org",
1382         }
1383         if err := conf.writeAndUpdate(confData); err != nil {
1384                 t.Fatal(err)
1385         }
1386
1387         const name = "test"
1388         const server = "192.0.2.53:53"
1389         const searchX = "test.x.golang.org."
1390         const searchY = "test.y.golang.org."
1391         const txt = "Hello World"
1392
1393         fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) {
1394                 t.Log(s, q)
1395
1396                 switch q.Questions[0].Name.String() {
1397                 case searchX:
1398                         return dnsmessage.Message{}, os.ErrDeadlineExceeded
1399                 case searchY:
1400                         return mockTXTResponse(q), nil
1401                 default:
1402                         return dnsmessage.Message{}, fmt.Errorf("Unexpected Name: %v", q.Questions[0].Name)
1403                 }
1404         }}
1405
1406         for _, strict := range []bool{true, false} {
1407                 r := Resolver{StrictErrors: strict, Dial: fake.DialContext}
1408                 p, _, err := r.lookup(context.Background(), name, dnsmessage.TypeTXT, nil)
1409                 var wantErr error
1410                 var wantRRs int
1411                 if strict {
1412                         wantErr = &DNSError{
1413                                 Err:       os.ErrDeadlineExceeded.Error(),
1414                                 Name:      name,
1415                                 Server:    server,
1416                                 IsTimeout: true,
1417                         }
1418                 } else {
1419                         wantRRs = 1
1420                 }
1421                 if !reflect.DeepEqual(err, wantErr) {
1422                         t.Errorf("strict=%v: got err %#v; want %#v", strict, err, wantErr)
1423                 }
1424                 a, err := p.AllAnswers()
1425                 if err != nil {
1426                         a = nil
1427                 }
1428                 if len(a) != wantRRs {
1429                         t.Errorf("strict=%v: got %v; want %v", strict, len(a), wantRRs)
1430                 }
1431         }
1432 }
1433
1434 // Test for a race between uninstalling the test hooks and closing a
1435 // socket connection. This used to fail when testing with -race.
1436 func TestDNSGoroutineRace(t *testing.T) {
1437         defer dnsWaitGroup.Wait()
1438
1439         fake := fakeDNSServer{rh: func(n, s string, q dnsmessage.Message, t time.Time) (dnsmessage.Message, error) {
1440                 time.Sleep(10 * time.Microsecond)
1441                 return dnsmessage.Message{}, os.ErrDeadlineExceeded
1442         }}
1443         r := Resolver{PreferGo: true, Dial: fake.DialContext}
1444
1445         // The timeout here is less than the timeout used by the server,
1446         // so the goroutine started to query the (fake) server will hang
1447         // around after this test is done if we don't call dnsWaitGroup.Wait.
1448         ctx, cancel := context.WithTimeout(context.Background(), 2*time.Microsecond)
1449         defer cancel()
1450         _, err := r.LookupIPAddr(ctx, "where.are.they.now")
1451         if err == nil {
1452                 t.Fatal("fake DNS lookup unexpectedly succeeded")
1453         }
1454 }
1455
1456 func lookupWithFake(fake fakeDNSServer, name string, typ dnsmessage.Type) error {
1457         r := Resolver{PreferGo: true, Dial: fake.DialContext}
1458
1459         conf := getSystemDNSConfig()
1460
1461         ctx, cancel := context.WithCancel(context.Background())
1462         defer cancel()
1463
1464         _, _, err := r.tryOneName(ctx, conf, name, typ)
1465         return err
1466 }
1467
1468 // Issue 8434: verify that Temporary returns true on an error when rcode
1469 // is SERVFAIL
1470 func TestIssue8434(t *testing.T) {
1471         err := lookupWithFake(fakeDNSServer{
1472                 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
1473                         return dnsmessage.Message{
1474                                 Header: dnsmessage.Header{
1475                                         ID:       q.ID,
1476                                         Response: true,
1477                                         RCode:    dnsmessage.RCodeServerFailure,
1478                                 },
1479                                 Questions: q.Questions,
1480                         }, nil
1481                 },
1482         }, "golang.org.", dnsmessage.TypeALL)
1483         if err == nil {
1484                 t.Fatal("expected an error")
1485         }
1486         if ne, ok := err.(Error); !ok {
1487                 t.Fatalf("err = %#v; wanted something supporting net.Error", err)
1488         } else if !ne.Temporary() {
1489                 t.Fatalf("Temporary = false for err = %#v; want Temporary == true", err)
1490         }
1491         if de, ok := err.(*DNSError); !ok {
1492                 t.Fatalf("err = %#v; wanted a *net.DNSError", err)
1493         } else if !de.IsTemporary {
1494                 t.Fatalf("IsTemporary = false for err = %#v; want IsTemporary == true", err)
1495         }
1496 }
1497
1498 func TestIssueNoSuchHostExists(t *testing.T) {
1499         err := lookupWithFake(fakeDNSServer{
1500                 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
1501                         return dnsmessage.Message{
1502                                 Header: dnsmessage.Header{
1503                                         ID:       q.ID,
1504                                         Response: true,
1505                                         RCode:    dnsmessage.RCodeNameError,
1506                                 },
1507                                 Questions: q.Questions,
1508                         }, nil
1509                 },
1510         }, "golang.org.", dnsmessage.TypeALL)
1511         if err == nil {
1512                 t.Fatal("expected an error")
1513         }
1514         if _, ok := err.(Error); !ok {
1515                 t.Fatalf("err = %#v; wanted something supporting net.Error", err)
1516         }
1517         if de, ok := err.(*DNSError); !ok {
1518                 t.Fatalf("err = %#v; wanted a *net.DNSError", err)
1519         } else if !de.IsNotFound {
1520                 t.Fatalf("IsNotFound = false for err = %#v; want IsNotFound == true", err)
1521         }
1522 }
1523
1524 // TestNoSuchHost verifies that tryOneName works correctly when the domain does
1525 // not exist.
1526 //
1527 // Issue 12778: verify that NXDOMAIN without RA bit errors as "no such host"
1528 // and not "server misbehaving"
1529 //
1530 // Issue 25336: verify that NXDOMAIN errors fail fast.
1531 //
1532 // Issue 27525: verify that empty answers fail fast.
1533 func TestNoSuchHost(t *testing.T) {
1534         tests := []struct {
1535                 name string
1536                 f    func(string, string, dnsmessage.Message, time.Time) (dnsmessage.Message, error)
1537         }{
1538                 {
1539                         "NXDOMAIN",
1540                         func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
1541                                 return dnsmessage.Message{
1542                                         Header: dnsmessage.Header{
1543                                                 ID:                 q.ID,
1544                                                 Response:           true,
1545                                                 RCode:              dnsmessage.RCodeNameError,
1546                                                 RecursionAvailable: false,
1547                                         },
1548                                         Questions: q.Questions,
1549                                 }, nil
1550                         },
1551                 },
1552                 {
1553                         "no answers",
1554                         func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
1555                                 return dnsmessage.Message{
1556                                         Header: dnsmessage.Header{
1557                                                 ID:                 q.ID,
1558                                                 Response:           true,
1559                                                 RCode:              dnsmessage.RCodeSuccess,
1560                                                 RecursionAvailable: false,
1561                                                 Authoritative:      true,
1562                                         },
1563                                         Questions: q.Questions,
1564                                 }, nil
1565                         },
1566                 },
1567         }
1568
1569         for _, test := range tests {
1570                 t.Run(test.name, func(t *testing.T) {
1571                         lookups := 0
1572                         err := lookupWithFake(fakeDNSServer{
1573                                 rh: func(n, s string, q dnsmessage.Message, d time.Time) (dnsmessage.Message, error) {
1574                                         lookups++
1575                                         return test.f(n, s, q, d)
1576                                 },
1577                         }, ".", dnsmessage.TypeALL)
1578
1579                         if lookups != 1 {
1580                                 t.Errorf("got %d lookups, wanted 1", lookups)
1581                         }
1582
1583                         if err == nil {
1584                                 t.Fatal("expected an error")
1585                         }
1586                         de, ok := err.(*DNSError)
1587                         if !ok {
1588                                 t.Fatalf("err = %#v; wanted a *net.DNSError", err)
1589                         }
1590                         if de.Err != errNoSuchHost.Error() {
1591                                 t.Fatalf("Err = %#v; wanted %q", de.Err, errNoSuchHost.Error())
1592                         }
1593                         if !de.IsNotFound {
1594                                 t.Fatalf("IsNotFound = %v wanted true", de.IsNotFound)
1595                         }
1596                 })
1597         }
1598 }
1599
1600 // Issue 26573: verify that Conns that don't implement PacketConn are treated
1601 // as streams even when udp was requested.
1602 func TestDNSDialTCP(t *testing.T) {
1603         fake := fakeDNSServer{
1604                 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
1605                         r := dnsmessage.Message{
1606                                 Header: dnsmessage.Header{
1607                                         ID:       q.Header.ID,
1608                                         Response: true,
1609                                         RCode:    dnsmessage.RCodeSuccess,
1610                                 },
1611                                 Questions: q.Questions,
1612                         }
1613                         return r, nil
1614                 },
1615                 alwaysTCP: true,
1616         }
1617         r := Resolver{PreferGo: true, Dial: fake.DialContext}
1618         ctx := context.Background()
1619         _, _, err := r.exchange(ctx, "0.0.0.0", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), time.Second, useUDPOrTCP, false)
1620         if err != nil {
1621                 t.Fatal("exchange failed:", err)
1622         }
1623 }
1624
1625 // Issue 27763: verify that two strings in one TXT record are concatenated.
1626 func TestTXTRecordTwoStrings(t *testing.T) {
1627         fake := fakeDNSServer{
1628                 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
1629                         r := dnsmessage.Message{
1630                                 Header: dnsmessage.Header{
1631                                         ID:       q.Header.ID,
1632                                         Response: true,
1633                                         RCode:    dnsmessage.RCodeSuccess,
1634                                 },
1635                                 Questions: q.Questions,
1636                                 Answers: []dnsmessage.Resource{
1637                                         {
1638                                                 Header: dnsmessage.ResourceHeader{
1639                                                         Name:  q.Questions[0].Name,
1640                                                         Type:  dnsmessage.TypeA,
1641                                                         Class: dnsmessage.ClassINET,
1642                                                 },
1643                                                 Body: &dnsmessage.TXTResource{
1644                                                         TXT: []string{"string1 ", "string2"},
1645                                                 },
1646                                         },
1647                                         {
1648                                                 Header: dnsmessage.ResourceHeader{
1649                                                         Name:  q.Questions[0].Name,
1650                                                         Type:  dnsmessage.TypeA,
1651                                                         Class: dnsmessage.ClassINET,
1652                                                 },
1653                                                 Body: &dnsmessage.TXTResource{
1654                                                         TXT: []string{"onestring"},
1655                                                 },
1656                                         },
1657                                 },
1658                         }
1659                         return r, nil
1660                 },
1661         }
1662         r := Resolver{PreferGo: true, Dial: fake.DialContext}
1663         txt, err := r.lookupTXT(context.Background(), "golang.org")
1664         if err != nil {
1665                 t.Fatal("LookupTXT failed:", err)
1666         }
1667         if want := 2; len(txt) != want {
1668                 t.Fatalf("len(txt), got %d, want %d", len(txt), want)
1669         }
1670         if want := "string1 string2"; txt[0] != want {
1671                 t.Errorf("txt[0], got %q, want %q", txt[0], want)
1672         }
1673         if want := "onestring"; txt[1] != want {
1674                 t.Errorf("txt[1], got %q, want %q", txt[1], want)
1675         }
1676 }
1677
1678 // Issue 29644: support single-request resolv.conf option in pure Go resolver.
1679 // The A and AAAA queries will be sent sequentially, not in parallel.
1680 func TestSingleRequestLookup(t *testing.T) {
1681         defer dnsWaitGroup.Wait()
1682         var (
1683                 firstcalled int32
1684                 ipv4        int32 = 1
1685                 ipv6        int32 = 2
1686         )
1687         fake := fakeDNSServer{rh: func(n, s string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
1688                 r := dnsmessage.Message{
1689                         Header: dnsmessage.Header{
1690                                 ID:       q.ID,
1691                                 Response: true,
1692                         },
1693                         Questions: q.Questions,
1694                 }
1695                 for _, question := range q.Questions {
1696                         switch question.Type {
1697                         case dnsmessage.TypeA:
1698                                 if question.Name.String() == "slowipv4.example.net." {
1699                                         time.Sleep(10 * time.Millisecond)
1700                                 }
1701                                 if !atomic.CompareAndSwapInt32(&firstcalled, 0, ipv4) {
1702                                         t.Errorf("the A query was received after the AAAA query !")
1703                                 }
1704                                 r.Answers = append(r.Answers, dnsmessage.Resource{
1705                                         Header: dnsmessage.ResourceHeader{
1706                                                 Name:   q.Questions[0].Name,
1707                                                 Type:   dnsmessage.TypeA,
1708                                                 Class:  dnsmessage.ClassINET,
1709                                                 Length: 4,
1710                                         },
1711                                         Body: &dnsmessage.AResource{
1712                                                 A: TestAddr,
1713                                         },
1714                                 })
1715                         case dnsmessage.TypeAAAA:
1716                                 atomic.CompareAndSwapInt32(&firstcalled, 0, ipv6)
1717                                 r.Answers = append(r.Answers, dnsmessage.Resource{
1718                                         Header: dnsmessage.ResourceHeader{
1719                                                 Name:   q.Questions[0].Name,
1720                                                 Type:   dnsmessage.TypeAAAA,
1721                                                 Class:  dnsmessage.ClassINET,
1722                                                 Length: 16,
1723                                         },
1724                                         Body: &dnsmessage.AAAAResource{
1725                                                 AAAA: TestAddr6,
1726                                         },
1727                                 })
1728                         }
1729                 }
1730                 return r, nil
1731         }}
1732         r := Resolver{PreferGo: true, Dial: fake.DialContext}
1733
1734         conf, err := newResolvConfTest()
1735         if err != nil {
1736                 t.Fatal(err)
1737         }
1738         defer conf.teardown()
1739         if err := conf.writeAndUpdate([]string{"options single-request"}); err != nil {
1740                 t.Fatal(err)
1741         }
1742         for _, name := range []string{"hostname.example.net", "slowipv4.example.net"} {
1743                 firstcalled = 0
1744                 _, err := r.LookupIPAddr(context.Background(), name)
1745                 if err != nil {
1746                         t.Error(err)
1747                 }
1748         }
1749 }
1750
1751 // Issue 29358. Add configuration knob to force TCP-only DNS requests in the pure Go resolver.
1752 func TestDNSUseTCP(t *testing.T) {
1753         fake := fakeDNSServer{
1754                 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
1755                         r := dnsmessage.Message{
1756                                 Header: dnsmessage.Header{
1757                                         ID:       q.Header.ID,
1758                                         Response: true,
1759                                         RCode:    dnsmessage.RCodeSuccess,
1760                                 },
1761                                 Questions: q.Questions,
1762                         }
1763                         if n == "udp" {
1764                                 t.Fatal("udp protocol was used instead of tcp")
1765                         }
1766                         return r, nil
1767                 },
1768         }
1769         r := Resolver{PreferGo: true, Dial: fake.DialContext}
1770         ctx, cancel := context.WithCancel(context.Background())
1771         defer cancel()
1772         _, _, err := r.exchange(ctx, "0.0.0.0", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), time.Second, useTCPOnly, false)
1773         if err != nil {
1774                 t.Fatal("exchange failed:", err)
1775         }
1776 }
1777
1778 // Issue 34660: PTR response with non-PTR answers should ignore non-PTR
1779 func TestPTRandNonPTR(t *testing.T) {
1780         fake := fakeDNSServer{
1781                 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
1782                         r := dnsmessage.Message{
1783                                 Header: dnsmessage.Header{
1784                                         ID:       q.Header.ID,
1785                                         Response: true,
1786                                         RCode:    dnsmessage.RCodeSuccess,
1787                                 },
1788                                 Questions: q.Questions,
1789                                 Answers: []dnsmessage.Resource{
1790                                         {
1791                                                 Header: dnsmessage.ResourceHeader{
1792                                                         Name:  q.Questions[0].Name,
1793                                                         Type:  dnsmessage.TypePTR,
1794                                                         Class: dnsmessage.ClassINET,
1795                                                 },
1796                                                 Body: &dnsmessage.PTRResource{
1797                                                         PTR: dnsmessage.MustNewName("golang.org."),
1798                                                 },
1799                                         },
1800                                         {
1801                                                 Header: dnsmessage.ResourceHeader{
1802                                                         Name:  q.Questions[0].Name,
1803                                                         Type:  dnsmessage.TypeTXT,
1804                                                         Class: dnsmessage.ClassINET,
1805                                                 },
1806                                                 Body: &dnsmessage.TXTResource{
1807                                                         TXT: []string{"PTR 8 6 60 ..."}, // fake RRSIG
1808                                                 },
1809                                         },
1810                                 },
1811                         }
1812                         return r, nil
1813                 },
1814         }
1815         r := Resolver{PreferGo: true, Dial: fake.DialContext}
1816         names, err := r.lookupAddr(context.Background(), "192.0.2.123")
1817         if err != nil {
1818                 t.Fatalf("LookupAddr: %v", err)
1819         }
1820         if want := []string{"golang.org."}; !reflect.DeepEqual(names, want) {
1821                 t.Errorf("names = %q; want %q", names, want)
1822         }
1823 }
1824
1825 func TestCVE202133195(t *testing.T) {
1826         fake := fakeDNSServer{
1827                 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
1828                         r := dnsmessage.Message{
1829                                 Header: dnsmessage.Header{
1830                                         ID:                 q.Header.ID,
1831                                         Response:           true,
1832                                         RCode:              dnsmessage.RCodeSuccess,
1833                                         RecursionAvailable: true,
1834                                 },
1835                                 Questions: q.Questions,
1836                         }
1837                         switch q.Questions[0].Type {
1838                         case dnsmessage.TypeCNAME:
1839                                 r.Answers = []dnsmessage.Resource{}
1840                         case dnsmessage.TypeA: // CNAME lookup uses a A/AAAA as a proxy
1841                                 r.Answers = append(r.Answers,
1842                                         dnsmessage.Resource{
1843                                                 Header: dnsmessage.ResourceHeader{
1844                                                         Name:   dnsmessage.MustNewName("<html>.golang.org."),
1845                                                         Type:   dnsmessage.TypeA,
1846                                                         Class:  dnsmessage.ClassINET,
1847                                                         Length: 4,
1848                                                 },
1849                                                 Body: &dnsmessage.AResource{
1850                                                         A: TestAddr,
1851                                                 },
1852                                         },
1853                                 )
1854                         case dnsmessage.TypeSRV:
1855                                 n := q.Questions[0].Name
1856                                 if n.String() == "_hdr._tcp.golang.org." {
1857                                         n = dnsmessage.MustNewName("<html>.golang.org.")
1858                                 }
1859                                 r.Answers = append(r.Answers,
1860                                         dnsmessage.Resource{
1861                                                 Header: dnsmessage.ResourceHeader{
1862                                                         Name:   n,
1863                                                         Type:   dnsmessage.TypeSRV,
1864                                                         Class:  dnsmessage.ClassINET,
1865                                                         Length: 4,
1866                                                 },
1867                                                 Body: &dnsmessage.SRVResource{
1868                                                         Target: dnsmessage.MustNewName("<html>.golang.org."),
1869                                                 },
1870                                         },
1871                                         dnsmessage.Resource{
1872                                                 Header: dnsmessage.ResourceHeader{
1873                                                         Name:   n,
1874                                                         Type:   dnsmessage.TypeSRV,
1875                                                         Class:  dnsmessage.ClassINET,
1876                                                         Length: 4,
1877                                                 },
1878                                                 Body: &dnsmessage.SRVResource{
1879                                                         Target: dnsmessage.MustNewName("good.golang.org."),
1880                                                 },
1881                                         },
1882                                 )
1883                         case dnsmessage.TypeMX:
1884                                 r.Answers = append(r.Answers,
1885                                         dnsmessage.Resource{
1886                                                 Header: dnsmessage.ResourceHeader{
1887                                                         Name:   dnsmessage.MustNewName("<html>.golang.org."),
1888                                                         Type:   dnsmessage.TypeMX,
1889                                                         Class:  dnsmessage.ClassINET,
1890                                                         Length: 4,
1891                                                 },
1892                                                 Body: &dnsmessage.MXResource{
1893                                                         MX: dnsmessage.MustNewName("<html>.golang.org."),
1894                                                 },
1895                                         },
1896                                         dnsmessage.Resource{
1897                                                 Header: dnsmessage.ResourceHeader{
1898                                                         Name:   dnsmessage.MustNewName("good.golang.org."),
1899                                                         Type:   dnsmessage.TypeMX,
1900                                                         Class:  dnsmessage.ClassINET,
1901                                                         Length: 4,
1902                                                 },
1903                                                 Body: &dnsmessage.MXResource{
1904                                                         MX: dnsmessage.MustNewName("good.golang.org."),
1905                                                 },
1906                                         },
1907                                 )
1908                         case dnsmessage.TypeNS:
1909                                 r.Answers = append(r.Answers,
1910                                         dnsmessage.Resource{
1911                                                 Header: dnsmessage.ResourceHeader{
1912                                                         Name:   dnsmessage.MustNewName("<html>.golang.org."),
1913                                                         Type:   dnsmessage.TypeNS,
1914                                                         Class:  dnsmessage.ClassINET,
1915                                                         Length: 4,
1916                                                 },
1917                                                 Body: &dnsmessage.NSResource{
1918                                                         NS: dnsmessage.MustNewName("<html>.golang.org."),
1919                                                 },
1920                                         },
1921                                         dnsmessage.Resource{
1922                                                 Header: dnsmessage.ResourceHeader{
1923                                                         Name:   dnsmessage.MustNewName("good.golang.org."),
1924                                                         Type:   dnsmessage.TypeNS,
1925                                                         Class:  dnsmessage.ClassINET,
1926                                                         Length: 4,
1927                                                 },
1928                                                 Body: &dnsmessage.NSResource{
1929                                                         NS: dnsmessage.MustNewName("good.golang.org."),
1930                                                 },
1931                                         },
1932                                 )
1933                         case dnsmessage.TypePTR:
1934                                 r.Answers = append(r.Answers,
1935                                         dnsmessage.Resource{
1936                                                 Header: dnsmessage.ResourceHeader{
1937                                                         Name:   dnsmessage.MustNewName("<html>.golang.org."),
1938                                                         Type:   dnsmessage.TypePTR,
1939                                                         Class:  dnsmessage.ClassINET,
1940                                                         Length: 4,
1941                                                 },
1942                                                 Body: &dnsmessage.PTRResource{
1943                                                         PTR: dnsmessage.MustNewName("<html>.golang.org."),
1944                                                 },
1945                                         },
1946                                         dnsmessage.Resource{
1947                                                 Header: dnsmessage.ResourceHeader{
1948                                                         Name:   dnsmessage.MustNewName("good.golang.org."),
1949                                                         Type:   dnsmessage.TypePTR,
1950                                                         Class:  dnsmessage.ClassINET,
1951                                                         Length: 4,
1952                                                 },
1953                                                 Body: &dnsmessage.PTRResource{
1954                                                         PTR: dnsmessage.MustNewName("good.golang.org."),
1955                                                 },
1956                                         },
1957                                 )
1958                         }
1959                         return r, nil
1960                 },
1961         }
1962
1963         r := Resolver{PreferGo: true, Dial: fake.DialContext}
1964         // Change the default resolver to match our manipulated resolver
1965         originalDefault := DefaultResolver
1966         DefaultResolver = &r
1967         defer func() { DefaultResolver = originalDefault }()
1968         // Redirect host file lookups.
1969         defer func(orig string) { hostsFilePath = orig }(hostsFilePath)
1970         hostsFilePath = "testdata/hosts"
1971
1972         tests := []struct {
1973                 name string
1974                 f    func(*testing.T)
1975         }{
1976                 {
1977                         name: "CNAME",
1978                         f: func(t *testing.T) {
1979                                 expectedErr := &DNSError{Err: errMalformedDNSRecordsDetail, Name: "golang.org"}
1980                                 _, err := r.LookupCNAME(context.Background(), "golang.org")
1981                                 if err.Error() != expectedErr.Error() {
1982                                         t.Fatalf("unexpected error: %s", err)
1983                                 }
1984                                 _, err = LookupCNAME("golang.org")
1985                                 if err.Error() != expectedErr.Error() {
1986                                         t.Fatalf("unexpected error: %s", err)
1987                                 }
1988                         },
1989                 },
1990                 {
1991                         name: "SRV (bad record)",
1992                         f: func(t *testing.T) {
1993                                 expected := []*SRV{
1994                                         {
1995                                                 Target: "good.golang.org.",
1996                                         },
1997                                 }
1998                                 expectedErr := &DNSError{Err: errMalformedDNSRecordsDetail, Name: "golang.org"}
1999                                 _, records, err := r.LookupSRV(context.Background(), "target", "tcp", "golang.org")
2000                                 if err.Error() != expectedErr.Error() {
2001                                         t.Fatalf("unexpected error: %s", err)
2002                                 }
2003                                 if !reflect.DeepEqual(records, expected) {
2004                                         t.Error("Unexpected record set")
2005                                 }
2006                                 _, records, err = LookupSRV("target", "tcp", "golang.org")
2007                                 if err.Error() != expectedErr.Error() {
2008                                         t.Errorf("unexpected error: %s", err)
2009                                 }
2010                                 if !reflect.DeepEqual(records, expected) {
2011                                         t.Error("Unexpected record set")
2012                                 }
2013                         },
2014                 },
2015                 {
2016                         name: "SRV (bad header)",
2017                         f: func(t *testing.T) {
2018                                 _, _, err := r.LookupSRV(context.Background(), "hdr", "tcp", "golang.org.")
2019                                 if expected := "lookup golang.org.: SRV header name is invalid"; err == nil || err.Error() != expected {
2020                                         t.Errorf("Resolver.LookupSRV returned unexpected error, got %q, want %q", err, expected)
2021                                 }
2022                                 _, _, err = LookupSRV("hdr", "tcp", "golang.org.")
2023                                 if expected := "lookup golang.org.: SRV header name is invalid"; err == nil || err.Error() != expected {
2024                                         t.Errorf("LookupSRV returned unexpected error, got %q, want %q", err, expected)
2025                                 }
2026                         },
2027                 },
2028                 {
2029                         name: "MX",
2030                         f: func(t *testing.T) {
2031                                 expected := []*MX{
2032                                         {
2033                                                 Host: "good.golang.org.",
2034                                         },
2035                                 }
2036                                 expectedErr := &DNSError{Err: errMalformedDNSRecordsDetail, Name: "golang.org"}
2037                                 records, err := r.LookupMX(context.Background(), "golang.org")
2038                                 if err.Error() != expectedErr.Error() {
2039                                         t.Fatalf("unexpected error: %s", err)
2040                                 }
2041                                 if !reflect.DeepEqual(records, expected) {
2042                                         t.Error("Unexpected record set")
2043                                 }
2044                                 records, err = LookupMX("golang.org")
2045                                 if err.Error() != expectedErr.Error() {
2046                                         t.Fatalf("unexpected error: %s", err)
2047                                 }
2048                                 if !reflect.DeepEqual(records, expected) {
2049                                         t.Error("Unexpected record set")
2050                                 }
2051                         },
2052                 },
2053                 {
2054                         name: "NS",
2055                         f: func(t *testing.T) {
2056                                 expected := []*NS{
2057                                         {
2058                                                 Host: "good.golang.org.",
2059                                         },
2060                                 }
2061                                 expectedErr := &DNSError{Err: errMalformedDNSRecordsDetail, Name: "golang.org"}
2062                                 records, err := r.LookupNS(context.Background(), "golang.org")
2063                                 if err.Error() != expectedErr.Error() {
2064                                         t.Fatalf("unexpected error: %s", err)
2065                                 }
2066                                 if !reflect.DeepEqual(records, expected) {
2067                                         t.Error("Unexpected record set")
2068                                 }
2069                                 records, err = LookupNS("golang.org")
2070                                 if err.Error() != expectedErr.Error() {
2071                                         t.Fatalf("unexpected error: %s", err)
2072                                 }
2073                                 if !reflect.DeepEqual(records, expected) {
2074                                         t.Error("Unexpected record set")
2075                                 }
2076                         },
2077                 },
2078                 {
2079                         name: "Addr",
2080                         f: func(t *testing.T) {
2081                                 expected := []string{"good.golang.org."}
2082                                 expectedErr := &DNSError{Err: errMalformedDNSRecordsDetail, Name: "192.0.2.42"}
2083                                 records, err := r.LookupAddr(context.Background(), "192.0.2.42")
2084                                 if err.Error() != expectedErr.Error() {
2085                                         t.Fatalf("unexpected error: %s", err)
2086                                 }
2087                                 if !reflect.DeepEqual(records, expected) {
2088                                         t.Error("Unexpected record set")
2089                                 }
2090                                 records, err = LookupAddr("192.0.2.42")
2091                                 if err.Error() != expectedErr.Error() {
2092                                         t.Fatalf("unexpected error: %s", err)
2093                                 }
2094                                 if !reflect.DeepEqual(records, expected) {
2095                                         t.Error("Unexpected record set")
2096                                 }
2097                         },
2098                 },
2099         }
2100
2101         for _, tc := range tests {
2102                 t.Run(tc.name, tc.f)
2103         }
2104
2105 }
2106
2107 func TestNullMX(t *testing.T) {
2108         fake := fakeDNSServer{
2109                 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
2110                         r := dnsmessage.Message{
2111                                 Header: dnsmessage.Header{
2112                                         ID:       q.Header.ID,
2113                                         Response: true,
2114                                         RCode:    dnsmessage.RCodeSuccess,
2115                                 },
2116                                 Questions: q.Questions,
2117                                 Answers: []dnsmessage.Resource{
2118                                         {
2119                                                 Header: dnsmessage.ResourceHeader{
2120                                                         Name:  q.Questions[0].Name,
2121                                                         Type:  dnsmessage.TypeMX,
2122                                                         Class: dnsmessage.ClassINET,
2123                                                 },
2124                                                 Body: &dnsmessage.MXResource{
2125                                                         MX: dnsmessage.MustNewName("."),
2126                                                 },
2127                                         },
2128                                 },
2129                         }
2130                         return r, nil
2131                 },
2132         }
2133         r := Resolver{PreferGo: true, Dial: fake.DialContext}
2134         rrset, err := r.LookupMX(context.Background(), "golang.org")
2135         if err != nil {
2136                 t.Fatalf("LookupMX: %v", err)
2137         }
2138         if want := []*MX{&MX{Host: "."}}; !reflect.DeepEqual(rrset, want) {
2139                 records := []string{}
2140                 for _, rr := range rrset {
2141                         records = append(records, fmt.Sprintf("%v", rr))
2142                 }
2143                 t.Errorf("records = [%v]; want [%v]", strings.Join(records, " "), want[0])
2144         }
2145 }
2146
2147 func TestRootNS(t *testing.T) {
2148         // See https://golang.org/issue/45715.
2149         fake := fakeDNSServer{
2150                 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
2151                         r := dnsmessage.Message{
2152                                 Header: dnsmessage.Header{
2153                                         ID:       q.Header.ID,
2154                                         Response: true,
2155                                         RCode:    dnsmessage.RCodeSuccess,
2156                                 },
2157                                 Questions: q.Questions,
2158                                 Answers: []dnsmessage.Resource{
2159                                         {
2160                                                 Header: dnsmessage.ResourceHeader{
2161                                                         Name:  q.Questions[0].Name,
2162                                                         Type:  dnsmessage.TypeNS,
2163                                                         Class: dnsmessage.ClassINET,
2164                                                 },
2165                                                 Body: &dnsmessage.NSResource{
2166                                                         NS: dnsmessage.MustNewName("i.root-servers.net."),
2167                                                 },
2168                                         },
2169                                 },
2170                         }
2171                         return r, nil
2172                 },
2173         }
2174         r := Resolver{PreferGo: true, Dial: fake.DialContext}
2175         rrset, err := r.LookupNS(context.Background(), ".")
2176         if err != nil {
2177                 t.Fatalf("LookupNS: %v", err)
2178         }
2179         if want := []*NS{&NS{Host: "i.root-servers.net."}}; !reflect.DeepEqual(rrset, want) {
2180                 records := []string{}
2181                 for _, rr := range rrset {
2182                         records = append(records, fmt.Sprintf("%v", rr))
2183                 }
2184                 t.Errorf("records = [%v]; want [%v]", strings.Join(records, " "), want[0])
2185         }
2186 }
2187
2188 func TestGoLookupIPCNAMEOrderHostsAliasesFilesOnlyMode(t *testing.T) {
2189         defer func(orig string) { hostsFilePath = orig }(hostsFilePath)
2190         hostsFilePath = "testdata/aliases"
2191         mode := hostLookupFiles
2192
2193         for _, v := range lookupStaticHostAliasesTest {
2194                 testGoLookupIPCNAMEOrderHostsAliases(t, mode, v.lookup, absDomainName(v.res))
2195         }
2196 }
2197
2198 func TestGoLookupIPCNAMEOrderHostsAliasesFilesDNSMode(t *testing.T) {
2199         defer func(orig string) { hostsFilePath = orig }(hostsFilePath)
2200         hostsFilePath = "testdata/aliases"
2201         mode := hostLookupFilesDNS
2202
2203         for _, v := range lookupStaticHostAliasesTest {
2204                 testGoLookupIPCNAMEOrderHostsAliases(t, mode, v.lookup, absDomainName(v.res))
2205         }
2206 }
2207
2208 var goLookupIPCNAMEOrderDNSFilesModeTests = []struct {
2209         lookup, res string
2210 }{
2211         // 127.0.1.1
2212         {"invalid.invalid", "invalid.test"},
2213 }
2214
2215 func TestGoLookupIPCNAMEOrderHostsAliasesDNSFilesMode(t *testing.T) {
2216         defer func(orig string) { hostsFilePath = orig }(hostsFilePath)
2217         hostsFilePath = "testdata/aliases"
2218         mode := hostLookupDNSFiles
2219
2220         for _, v := range goLookupIPCNAMEOrderDNSFilesModeTests {
2221                 testGoLookupIPCNAMEOrderHostsAliases(t, mode, v.lookup, absDomainName(v.res))
2222         }
2223 }
2224
2225 func testGoLookupIPCNAMEOrderHostsAliases(t *testing.T, mode hostLookupOrder, lookup, lookupRes string) {
2226         fake := fakeDNSServer{
2227                 rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
2228                         var answers []dnsmessage.Resource
2229
2230                         if mode != hostLookupDNSFiles {
2231                                 t.Fatal("received unexpected DNS query")
2232                         }
2233
2234                         return dnsmessage.Message{
2235                                 Header: dnsmessage.Header{
2236                                         ID:       q.Header.ID,
2237                                         Response: true,
2238                                 },
2239                                 Questions: []dnsmessage.Question{q.Questions[0]},
2240                                 Answers:   answers,
2241                         }, nil
2242                 },
2243         }
2244
2245         r := Resolver{PreferGo: true, Dial: fake.DialContext}
2246         ins := []string{lookup, absDomainName(lookup), strings.ToLower(lookup), strings.ToUpper(lookup)}
2247         for _, in := range ins {
2248                 _, res, err := r.goLookupIPCNAMEOrder(context.Background(), "ip", in, mode, nil)
2249                 if err != nil {
2250                         t.Errorf("expected err == nil, but got error: %v", err)
2251                 }
2252                 if res.String() != lookupRes {
2253                         t.Errorf("goLookupIPCNAMEOrder(%v): got %v, want %v", in, res, lookupRes)
2254                 }
2255         }
2256 }
2257
2258 // Test that we advertise support for a larger DNS packet size.
2259 // This isn't a great test as it just tests the dnsmessage package
2260 // against itself.
2261 func TestDNSPacketSize(t *testing.T) {
2262         fake := fakeDNSServer{
2263                 rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
2264                         if len(q.Additionals) == 0 {
2265                                 t.Error("missing EDNS record")
2266                         } else if opt, ok := q.Additionals[0].Body.(*dnsmessage.OPTResource); !ok {
2267                                 t.Errorf("additional record type %T, expected OPTResource", q.Additionals[0])
2268                         } else if len(opt.Options) != 0 {
2269                                 t.Errorf("found %d Options, expected none", len(opt.Options))
2270                         } else {
2271                                 got := int(q.Additionals[0].Header.Class)
2272                                 t.Logf("EDNS packet size == %d", got)
2273                                 if got != maxDNSPacketSize {
2274                                         t.Errorf("EDNS packet size == %d, want %d", got, maxDNSPacketSize)
2275                                 }
2276                         }
2277
2278                         // Hand back a dummy answer to verify that
2279                         // LookupIPAddr completes.
2280                         r := dnsmessage.Message{
2281                                 Header: dnsmessage.Header{
2282                                         ID:       q.Header.ID,
2283                                         Response: true,
2284                                         RCode:    dnsmessage.RCodeSuccess,
2285                                 },
2286                                 Questions: q.Questions,
2287                         }
2288                         if q.Questions[0].Type == dnsmessage.TypeA {
2289                                 r.Answers = []dnsmessage.Resource{
2290                                         {
2291                                                 Header: dnsmessage.ResourceHeader{
2292                                                         Name:   q.Questions[0].Name,
2293                                                         Type:   dnsmessage.TypeA,
2294                                                         Class:  dnsmessage.ClassINET,
2295                                                         Length: 4,
2296                                                 },
2297                                                 Body: &dnsmessage.AResource{
2298                                                         A: TestAddr,
2299                                                 },
2300                                         },
2301                                 }
2302                         }
2303                         return r, nil
2304                 },
2305         }
2306
2307         r := &Resolver{PreferGo: true, Dial: fake.DialContext}
2308         if _, err := r.LookupIPAddr(context.Background(), "go.dev"); err != nil {
2309                 t.Errorf("lookup failed: %v", err)
2310         }
2311 }
2312
2313 func TestLongDNSNames(t *testing.T) {
2314         const longDNSsuffix = ".go.dev."
2315         const longDNSsuffixNoEndingDot = ".go.dev"
2316
2317         var longDNSPrefix = strings.Repeat("verylongdomainlabel.", 20)
2318
2319         var longDNSNamesTests = []struct {
2320                 req  string
2321                 fail bool
2322         }{
2323                 {req: longDNSPrefix[:255-len(longDNSsuffix)] + longDNSsuffix, fail: true},
2324                 {req: longDNSPrefix[:254-len(longDNSsuffix)] + longDNSsuffix},
2325                 {req: longDNSPrefix[:253-len(longDNSsuffix)] + longDNSsuffix},
2326
2327                 {req: longDNSPrefix[:253-len(longDNSsuffixNoEndingDot)] + longDNSsuffixNoEndingDot},
2328                 {req: longDNSPrefix[:254-len(longDNSsuffixNoEndingDot)] + longDNSsuffixNoEndingDot, fail: true},
2329         }
2330
2331         fake := fakeDNSServer{
2332                 rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
2333                         r := dnsmessage.Message{
2334                                 Header: dnsmessage.Header{
2335                                         ID:       q.Header.ID,
2336                                         Response: true,
2337                                         RCode:    dnsmessage.RCodeSuccess,
2338                                 },
2339                                 Questions: q.Questions,
2340                                 Answers: []dnsmessage.Resource{
2341                                         {
2342                                                 Header: dnsmessage.ResourceHeader{
2343                                                         Name:  q.Questions[0].Name,
2344                                                         Type:  q.Questions[0].Type,
2345                                                         Class: dnsmessage.ClassINET,
2346                                                 },
2347                                         },
2348                                 },
2349                         }
2350
2351                         switch q.Questions[0].Type {
2352                         case dnsmessage.TypeA:
2353                                 r.Answers[0].Body = &dnsmessage.AResource{A: TestAddr}
2354                         case dnsmessage.TypeAAAA:
2355                                 r.Answers[0].Body = &dnsmessage.AAAAResource{AAAA: TestAddr6}
2356                         case dnsmessage.TypeTXT:
2357                                 r.Answers[0].Body = &dnsmessage.TXTResource{TXT: []string{"."}}
2358                         case dnsmessage.TypeMX:
2359                                 r.Answers[0].Body = &dnsmessage.MXResource{
2360                                         MX: dnsmessage.MustNewName("go.dev."),
2361                                 }
2362                         case dnsmessage.TypeNS:
2363                                 r.Answers[0].Body = &dnsmessage.NSResource{
2364                                         NS: dnsmessage.MustNewName("go.dev."),
2365                                 }
2366                         case dnsmessage.TypeSRV:
2367                                 r.Answers[0].Body = &dnsmessage.SRVResource{
2368                                         Target: dnsmessage.MustNewName("go.dev."),
2369                                 }
2370                         case dnsmessage.TypeCNAME:
2371                                 r.Answers[0].Body = &dnsmessage.CNAMEResource{
2372                                         CNAME: dnsmessage.MustNewName("fake.cname."),
2373                                 }
2374                         default:
2375                                 panic("unknown dnsmessage type")
2376                         }
2377
2378                         return r, nil
2379                 },
2380         }
2381
2382         r := &Resolver{PreferGo: true, Dial: fake.DialContext}
2383
2384         methodTests := []string{"CNAME", "Host", "IP", "IPAddr", "MX", "NS", "NetIP", "SRV", "TXT"}
2385         query := func(t string, req string) error {
2386                 switch t {
2387                 case "CNAME":
2388                         _, err := r.LookupCNAME(context.Background(), req)
2389                         return err
2390                 case "Host":
2391                         _, err := r.LookupHost(context.Background(), req)
2392                         return err
2393                 case "IP":
2394                         _, err := r.LookupIP(context.Background(), "ip", req)
2395                         return err
2396                 case "IPAddr":
2397                         _, err := r.LookupIPAddr(context.Background(), req)
2398                         return err
2399                 case "MX":
2400                         _, err := r.LookupMX(context.Background(), req)
2401                         return err
2402                 case "NS":
2403                         _, err := r.LookupNS(context.Background(), req)
2404                         return err
2405                 case "NetIP":
2406                         _, err := r.LookupNetIP(context.Background(), "ip", req)
2407                         return err
2408                 case "SRV":
2409                         const service = "service"
2410                         const proto = "proto"
2411                         req = req[len(service)+len(proto)+4:]
2412                         _, _, err := r.LookupSRV(context.Background(), service, proto, req)
2413                         return err
2414                 case "TXT":
2415                         _, err := r.LookupTXT(context.Background(), req)
2416                         return err
2417                 }
2418                 panic("unknown query method")
2419         }
2420
2421         for i, v := range longDNSNamesTests {
2422                 for _, testName := range methodTests {
2423                         err := query(testName, v.req)
2424                         if v.fail {
2425                                 if err == nil {
2426                                         t.Errorf("%v: Lookup%v: unexpected success", i, testName)
2427                                         break
2428                                 }
2429
2430                                 expectedErr := DNSError{Err: errNoSuchHost.Error(), Name: v.req, IsNotFound: true}
2431                                 var dnsErr *DNSError
2432                                 errors.As(err, &dnsErr)
2433                                 if dnsErr == nil || *dnsErr != expectedErr {
2434                                         t.Errorf("%v: Lookup%v: unexpected error: %v", i, testName, err)
2435                                 }
2436                                 break
2437                         }
2438                         if err != nil {
2439                                 t.Errorf("%v: Lookup%v: unexpected error: %v", i, testName, err)
2440                         }
2441                 }
2442         }
2443 }
2444
2445 func TestDNSTrustAD(t *testing.T) {
2446         fake := fakeDNSServer{
2447                 rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
2448                         if q.Questions[0].Name.String() == "notrustad.go.dev." && q.Header.AuthenticData {
2449                                 t.Error("unexpected AD bit")
2450                         }
2451
2452                         if q.Questions[0].Name.String() == "trustad.go.dev." && !q.Header.AuthenticData {
2453                                 t.Error("expected AD bit")
2454                         }
2455
2456                         r := dnsmessage.Message{
2457                                 Header: dnsmessage.Header{
2458                                         ID:       q.Header.ID,
2459                                         Response: true,
2460                                         RCode:    dnsmessage.RCodeSuccess,
2461                                 },
2462                                 Questions: q.Questions,
2463                         }
2464                         if q.Questions[0].Type == dnsmessage.TypeA {
2465                                 r.Answers = []dnsmessage.Resource{
2466                                         {
2467                                                 Header: dnsmessage.ResourceHeader{
2468                                                         Name:   q.Questions[0].Name,
2469                                                         Type:   dnsmessage.TypeA,
2470                                                         Class:  dnsmessage.ClassINET,
2471                                                         Length: 4,
2472                                                 },
2473                                                 Body: &dnsmessage.AResource{
2474                                                         A: TestAddr,
2475                                                 },
2476                                         },
2477                                 }
2478                         }
2479
2480                         return r, nil
2481                 }}
2482
2483         r := &Resolver{PreferGo: true, Dial: fake.DialContext}
2484
2485         conf, err := newResolvConfTest()
2486         if err != nil {
2487                 t.Fatal(err)
2488         }
2489         defer conf.teardown()
2490
2491         err = conf.writeAndUpdate([]string{"nameserver 127.0.0.1"})
2492         if err != nil {
2493                 t.Fatal(err)
2494         }
2495
2496         if _, err := r.LookupIPAddr(context.Background(), "notrustad.go.dev"); err != nil {
2497                 t.Errorf("lookup failed: %v", err)
2498         }
2499
2500         err = conf.writeAndUpdate([]string{"nameserver 127.0.0.1", "options trust-ad"})
2501         if err != nil {
2502                 t.Fatal(err)
2503         }
2504
2505         if _, err := r.LookupIPAddr(context.Background(), "trustad.go.dev"); err != nil {
2506                 t.Errorf("lookup failed: %v", err)
2507         }
2508 }
2509
2510 func TestDNSConfigNoReload(t *testing.T) {
2511         r := &Resolver{PreferGo: true, Dial: func(ctx context.Context, network, address string) (Conn, error) {
2512                 if address != "192.0.2.1:53" {
2513                         return nil, errors.New("configuration unexpectedly changed")
2514                 }
2515                 return fakeDNSServerSuccessful.DialContext(ctx, network, address)
2516         }}
2517
2518         conf, err := newResolvConfTest()
2519         if err != nil {
2520                 t.Fatal(err)
2521         }
2522         defer conf.teardown()
2523
2524         err = conf.writeAndUpdateWithLastCheckedTime([]string{"nameserver 192.0.2.1", "options no-reload"}, time.Now().Add(-time.Hour))
2525         if err != nil {
2526                 t.Fatal(err)
2527         }
2528
2529         if _, err = r.LookupHost(context.Background(), "go.dev"); err != nil {
2530                 t.Fatal(err)
2531         }
2532
2533         err = conf.write([]string{"nameserver 192.0.2.200"})
2534         if err != nil {
2535                 t.Fatal(err)
2536         }
2537
2538         if _, err = r.LookupHost(context.Background(), "go.dev"); err != nil {
2539                 t.Fatal(err)
2540         }
2541 }
2542
2543 func TestLookupOrderFilesNoSuchHost(t *testing.T) {
2544         defer func(orig string) { hostsFilePath = orig }(hostsFilePath)
2545         if runtime.GOOS != "openbsd" {
2546                 defer setSystemNSS(getSystemNSS(), 0)
2547                 setSystemNSS(nssStr(t, "hosts: files"), time.Hour)
2548         }
2549
2550         conf, err := newResolvConfTest()
2551         if err != nil {
2552                 t.Fatal(err)
2553         }
2554         defer conf.teardown()
2555
2556         resolvConf := dnsConfig{servers: defaultNS}
2557         if runtime.GOOS == "openbsd" {
2558                 // Set error to ErrNotExist, so that the hostLookupOrder
2559                 // returns hostLookupFiles for openbsd.
2560                 resolvConf.err = os.ErrNotExist
2561         }
2562
2563         if !conf.forceUpdateConf(&resolvConf, time.Now().Add(time.Hour)) {
2564                 t.Fatal("failed to update resolv config")
2565         }
2566
2567         tmpFile := filepath.Join(t.TempDir(), "hosts")
2568         if err := os.WriteFile(tmpFile, []byte{}, 0660); err != nil {
2569                 t.Fatal(err)
2570         }
2571         hostsFilePath = tmpFile
2572
2573         const testName = "test.invalid"
2574
2575         order, _ := systemConf().hostLookupOrder(DefaultResolver, testName)
2576         if order != hostLookupFiles {
2577                 // skip test for systems which do not return hostLookupFiles
2578                 t.Skipf("hostLookupOrder did not return hostLookupFiles")
2579         }
2580
2581         var lookupTests = []struct {
2582                 name   string
2583                 lookup func(name string) error
2584         }{
2585                 {
2586                         name: "Host",
2587                         lookup: func(name string) error {
2588                                 _, err = DefaultResolver.LookupHost(context.Background(), name)
2589                                 return err
2590                         },
2591                 },
2592                 {
2593                         name: "IP",
2594                         lookup: func(name string) error {
2595                                 _, err = DefaultResolver.LookupIP(context.Background(), "ip", name)
2596                                 return err
2597                         },
2598                 },
2599                 {
2600                         name: "IPAddr",
2601                         lookup: func(name string) error {
2602                                 _, err = DefaultResolver.LookupIPAddr(context.Background(), name)
2603                                 return err
2604                         },
2605                 },
2606                 {
2607                         name: "NetIP",
2608                         lookup: func(name string) error {
2609                                 _, err = DefaultResolver.LookupNetIP(context.Background(), "ip", name)
2610                                 return err
2611                         },
2612                 },
2613         }
2614
2615         for _, v := range lookupTests {
2616                 err := v.lookup(testName)
2617
2618                 if err == nil {
2619                         t.Errorf("Lookup%v: unexpected success", v.name)
2620                         continue
2621                 }
2622
2623                 expectedErr := DNSError{Err: errNoSuchHost.Error(), Name: testName, IsNotFound: true}
2624                 var dnsErr *DNSError
2625                 errors.As(err, &dnsErr)
2626                 if dnsErr == nil || *dnsErr != expectedErr {
2627                         t.Errorf("Lookup%v: unexpected error: %v", v.name, err)
2628                 }
2629         }
2630 }
2631
2632 func TestExtendedRCode(t *testing.T) {
2633         fake := fakeDNSServer{
2634                 rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
2635                         fraudSuccessCode := dnsmessage.RCodeSuccess | 1<<10
2636
2637                         var edns0Hdr dnsmessage.ResourceHeader
2638                         edns0Hdr.SetEDNS0(maxDNSPacketSize, fraudSuccessCode, false)
2639
2640                         return dnsmessage.Message{
2641                                 Header: dnsmessage.Header{
2642                                         ID:       q.Header.ID,
2643                                         Response: true,
2644                                         RCode:    fraudSuccessCode,
2645                                 },
2646                                 Questions: []dnsmessage.Question{q.Questions[0]},
2647                                 Additionals: []dnsmessage.Resource{{
2648                                         Header: edns0Hdr,
2649                                         Body:   &dnsmessage.OPTResource{},
2650                                 }},
2651                         }, nil
2652                 },
2653         }
2654
2655         r := &Resolver{PreferGo: true, Dial: fake.DialContext}
2656         _, _, err := r.tryOneName(context.Background(), getSystemDNSConfig(), "go.dev.", dnsmessage.TypeA)
2657         var dnsErr *DNSError
2658         if !(errors.As(err, &dnsErr) && dnsErr.Err == errServerMisbehaving.Error()) {
2659                 t.Fatalf("r.tryOneName(): unexpected error: %v", err)
2660         }
2661 }