Source file
src/net/dnsclient_unix_test.go
Documentation: net
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 var goResolver = Resolver{PreferGo: true}
29
30
31 var TestAddr = [4]byte{0xc0, 0x00, 0x02, 0x01}
32
33
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
59
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
98
99 var specialDomainNameTests = []struct {
100 question dnsmessage.Question
101 rcode dnsmessage.RCode
102 }{
103
104
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
110
111
112
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
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
168 {"foo.local.", false},
169 {"foo.local", false},
170 {"foo.LOCAL", false},
171 {"foo.LOCAL.", false},
172
173 {"", true},
174
175
176
177
178
179
180
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
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
322 lines []string
323 servers []string
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,
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
389 error
390 a, aaaa bool
391 }{
392
393 {
394 "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j",
395 []string{
396 "options timeout:1 attempts:1",
397 "nameserver 255.255.255.255",
398 },
399 &DNSError{Name: "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", Server: "255.255.255.255:53", IsTimeout: true},
400 false, false,
401 },
402
403
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
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
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
477 {
478 "hostname.as112.net",
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",
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
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
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
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
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
635 addrs, _, err := r.goLookupIPCNAMEOrder(context.Background(), "ip", "thor", order, nil)
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
651
652
653
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
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",
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",
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
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++
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--
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
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",
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
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
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
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
1114
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(),
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
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
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
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
1369
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
1435
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
1446
1447
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
1469
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
1525
1526
1527
1528
1529
1530
1531
1532
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
1601
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
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
1679
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
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
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 ..."},
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:
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
1965 originalDefault := DefaultResolver
1966 DefaultResolver = &r
1967 defer func() { DefaultResolver = originalDefault }()
1968
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
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
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
2259
2260
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
2279
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
2559
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
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 }
2662
View as plain text