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