Source file
src/net/lookup_test.go
Documentation: net
1
2
3
4
5 package net
6
7 import (
8 "context"
9 "errors"
10 "fmt"
11 "internal/testenv"
12 "net/netip"
13 "reflect"
14 "runtime"
15 "sort"
16 "strings"
17 "sync"
18 "sync/atomic"
19 "testing"
20 "time"
21 )
22
23 func hasSuffixFold(s, suffix string) bool {
24 return strings.HasSuffix(strings.ToLower(s), strings.ToLower(suffix))
25 }
26
27 func lookupLocalhost(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
28 switch host {
29 case "localhost":
30 return []IPAddr{
31 {IP: IPv4(127, 0, 0, 1)},
32 {IP: IPv6loopback},
33 }, nil
34 default:
35 return fn(ctx, network, host)
36 }
37 }
38
39
40
41
42
43
44
45
46 var lookupGoogleSRVTests = []struct {
47 service, proto, name string
48 cname, target string
49 }{
50 {
51 "ldap", "tcp", "google.com",
52 "google.com.", "google.com.",
53 },
54 {
55 "ldap", "tcp", "google.com.",
56 "google.com.", "google.com.",
57 },
58
59
60 {
61 "", "", "_ldap._tcp.google.com",
62 "google.com.", "google.com.",
63 },
64 {
65 "", "", "_ldap._tcp.google.com.",
66 "google.com.", "google.com.",
67 },
68 }
69
70 var backoffDuration = [...]time.Duration{time.Second, 5 * time.Second, 30 * time.Second}
71
72 func TestLookupGoogleSRV(t *testing.T) {
73 t.Parallel()
74 mustHaveExternalNetwork(t)
75
76 if runtime.GOOS == "ios" {
77 t.Skip("no resolv.conf on iOS")
78 }
79
80 if !supportsIPv4() || !*testIPv4 {
81 t.Skip("IPv4 is required")
82 }
83
84 attempts := 0
85 for i := 0; i < len(lookupGoogleSRVTests); i++ {
86 tt := lookupGoogleSRVTests[i]
87 cname, srvs, err := LookupSRV(tt.service, tt.proto, tt.name)
88 if err != nil {
89 testenv.SkipFlakyNet(t)
90 if attempts < len(backoffDuration) {
91 dur := backoffDuration[attempts]
92 t.Logf("backoff %v after failure %v\n", dur, err)
93 time.Sleep(dur)
94 attempts++
95 i--
96 continue
97 }
98 t.Fatal(err)
99 }
100 if len(srvs) == 0 {
101 t.Error("got no record")
102 }
103 if !hasSuffixFold(cname, tt.cname) {
104 t.Errorf("got %s; want %s", cname, tt.cname)
105 }
106 for _, srv := range srvs {
107 if !hasSuffixFold(srv.Target, tt.target) {
108 t.Errorf("got %v; want a record containing %s", srv, tt.target)
109 }
110 }
111 }
112 }
113
114 var lookupGmailMXTests = []struct {
115 name, host string
116 }{
117 {"gmail.com", "google.com."},
118 {"gmail.com.", "google.com."},
119 }
120
121 func TestLookupGmailMX(t *testing.T) {
122 t.Parallel()
123 mustHaveExternalNetwork(t)
124
125 if runtime.GOOS == "ios" {
126 t.Skip("no resolv.conf on iOS")
127 }
128
129 if !supportsIPv4() || !*testIPv4 {
130 t.Skip("IPv4 is required")
131 }
132
133 attempts := 0
134 for i := 0; i < len(lookupGmailMXTests); i++ {
135 tt := lookupGmailMXTests[i]
136 mxs, err := LookupMX(tt.name)
137 if err != nil {
138 testenv.SkipFlakyNet(t)
139 if attempts < len(backoffDuration) {
140 dur := backoffDuration[attempts]
141 t.Logf("backoff %v after failure %v\n", dur, err)
142 time.Sleep(dur)
143 attempts++
144 i--
145 continue
146 }
147 t.Fatal(err)
148 }
149 if len(mxs) == 0 {
150 t.Error("got no record")
151 }
152 for _, mx := range mxs {
153 if !hasSuffixFold(mx.Host, tt.host) {
154 t.Errorf("got %v; want a record containing %s", mx, tt.host)
155 }
156 }
157 }
158 }
159
160 var lookupGmailNSTests = []struct {
161 name, host string
162 }{
163 {"gmail.com", "google.com."},
164 {"gmail.com.", "google.com."},
165 }
166
167 func TestLookupGmailNS(t *testing.T) {
168 t.Parallel()
169 mustHaveExternalNetwork(t)
170
171 if runtime.GOOS == "ios" {
172 t.Skip("no resolv.conf on iOS")
173 }
174
175 if !supportsIPv4() || !*testIPv4 {
176 t.Skip("IPv4 is required")
177 }
178
179 attempts := 0
180 for i := 0; i < len(lookupGmailNSTests); i++ {
181 tt := lookupGmailNSTests[i]
182 nss, err := LookupNS(tt.name)
183 if err != nil {
184 testenv.SkipFlakyNet(t)
185 if attempts < len(backoffDuration) {
186 dur := backoffDuration[attempts]
187 t.Logf("backoff %v after failure %v\n", dur, err)
188 time.Sleep(dur)
189 attempts++
190 i--
191 continue
192 }
193 t.Fatal(err)
194 }
195 if len(nss) == 0 {
196 t.Error("got no record")
197 }
198 for _, ns := range nss {
199 if !hasSuffixFold(ns.Host, tt.host) {
200 t.Errorf("got %v; want a record containing %s", ns, tt.host)
201 }
202 }
203 }
204 }
205
206 var lookupGmailTXTTests = []struct {
207 name, txt, host string
208 }{
209 {"gmail.com", "spf", "google.com"},
210 {"gmail.com.", "spf", "google.com"},
211 }
212
213 func TestLookupGmailTXT(t *testing.T) {
214 if runtime.GOOS == "plan9" {
215 t.Skip("skipping on plan9; see https://golang.org/issue/29722")
216 }
217 t.Parallel()
218 mustHaveExternalNetwork(t)
219
220 if runtime.GOOS == "ios" {
221 t.Skip("no resolv.conf on iOS")
222 }
223
224 if !supportsIPv4() || !*testIPv4 {
225 t.Skip("IPv4 is required")
226 }
227
228 attempts := 0
229 for i := 0; i < len(lookupGmailTXTTests); i++ {
230 tt := lookupGmailTXTTests[i]
231 txts, err := LookupTXT(tt.name)
232 if err != nil {
233 testenv.SkipFlakyNet(t)
234 if attempts < len(backoffDuration) {
235 dur := backoffDuration[attempts]
236 t.Logf("backoff %v after failure %v\n", dur, err)
237 time.Sleep(dur)
238 attempts++
239 i--
240 continue
241 }
242 t.Fatal(err)
243 }
244 if len(txts) == 0 {
245 t.Error("got no record")
246 }
247 found := false
248 for _, txt := range txts {
249 if strings.Contains(txt, tt.txt) && (strings.HasSuffix(txt, tt.host) || strings.HasSuffix(txt, tt.host+".")) {
250 found = true
251 break
252 }
253 }
254 if !found {
255 t.Errorf("got %v; want a record containing %s, %s", txts, tt.txt, tt.host)
256 }
257 }
258 }
259
260 var lookupGooglePublicDNSAddrTests = []string{
261 "8.8.8.8",
262 "8.8.4.4",
263 "2001:4860:4860::8888",
264 "2001:4860:4860::8844",
265 }
266
267 func TestLookupGooglePublicDNSAddr(t *testing.T) {
268 mustHaveExternalNetwork(t)
269
270 if !supportsIPv4() || !supportsIPv6() || !*testIPv4 || !*testIPv6 {
271 t.Skip("both IPv4 and IPv6 are required")
272 }
273
274 defer dnsWaitGroup.Wait()
275
276 for _, ip := range lookupGooglePublicDNSAddrTests {
277 names, err := LookupAddr(ip)
278 if err != nil {
279 t.Fatal(err)
280 }
281 if len(names) == 0 {
282 t.Error("got no record")
283 }
284 for _, name := range names {
285 if !hasSuffixFold(name, ".google.com.") && !hasSuffixFold(name, ".google.") {
286 t.Errorf("got %q; want a record ending in .google.com. or .google.", name)
287 }
288 }
289 }
290 }
291
292 func TestLookupIPv6LinkLocalAddr(t *testing.T) {
293 if !supportsIPv6() || !*testIPv6 {
294 t.Skip("IPv6 is required")
295 }
296
297 defer dnsWaitGroup.Wait()
298
299 addrs, err := LookupHost("localhost")
300 if err != nil {
301 t.Fatal(err)
302 }
303 found := false
304 for _, addr := range addrs {
305 if addr == "fe80::1%lo0" {
306 found = true
307 break
308 }
309 }
310 if !found {
311 t.Skipf("not supported on %s", runtime.GOOS)
312 }
313 if _, err := LookupAddr("fe80::1%lo0"); err != nil {
314 t.Error(err)
315 }
316 }
317
318 func TestLookupIPv6LinkLocalAddrWithZone(t *testing.T) {
319 if !supportsIPv6() || !*testIPv6 {
320 t.Skip("IPv6 is required")
321 }
322
323 ipaddrs, err := DefaultResolver.LookupIPAddr(context.Background(), "fe80::1%lo0")
324 if err != nil {
325 t.Error(err)
326 }
327 for _, addr := range ipaddrs {
328 if e, a := "lo0", addr.Zone; e != a {
329 t.Errorf("wrong zone: want %q, got %q", e, a)
330 }
331 }
332
333 addrs, err := DefaultResolver.LookupHost(context.Background(), "fe80::1%lo0")
334 if err != nil {
335 t.Error(err)
336 }
337 for _, addr := range addrs {
338 if e, a := "fe80::1%lo0", addr; e != a {
339 t.Errorf("wrong host: want %q got %q", e, a)
340 }
341 }
342 }
343
344 var lookupCNAMETests = []struct {
345 name, cname string
346 }{
347 {"www.iana.org", "icann.org."},
348 {"www.iana.org.", "icann.org."},
349 {"www.google.com", "google.com."},
350 {"google.com", "google.com."},
351 {"cname-to-txt.go4.org", "test-txt-record.go4.org."},
352 }
353
354 func TestLookupCNAME(t *testing.T) {
355 mustHaveExternalNetwork(t)
356 testenv.SkipFlakyNet(t)
357
358 if !supportsIPv4() || !*testIPv4 {
359 t.Skip("IPv4 is required")
360 }
361
362 defer dnsWaitGroup.Wait()
363
364 attempts := 0
365 for i := 0; i < len(lookupCNAMETests); i++ {
366 tt := lookupCNAMETests[i]
367 cname, err := LookupCNAME(tt.name)
368 if err != nil {
369 testenv.SkipFlakyNet(t)
370 if attempts < len(backoffDuration) {
371 dur := backoffDuration[attempts]
372 t.Logf("backoff %v after failure %v\n", dur, err)
373 time.Sleep(dur)
374 attempts++
375 i--
376 continue
377 }
378 t.Fatal(err)
379 }
380 if !hasSuffixFold(cname, tt.cname) {
381 t.Errorf("got %s; want a record containing %s", cname, tt.cname)
382 }
383 }
384 }
385
386 var lookupGoogleHostTests = []struct {
387 name string
388 }{
389 {"google.com"},
390 {"google.com."},
391 }
392
393 func TestLookupGoogleHost(t *testing.T) {
394 mustHaveExternalNetwork(t)
395 testenv.SkipFlakyNet(t)
396
397 if !supportsIPv4() || !*testIPv4 {
398 t.Skip("IPv4 is required")
399 }
400
401 defer dnsWaitGroup.Wait()
402
403 for _, tt := range lookupGoogleHostTests {
404 addrs, err := LookupHost(tt.name)
405 if err != nil {
406 t.Fatal(err)
407 }
408 if len(addrs) == 0 {
409 t.Error("got no record")
410 }
411 for _, addr := range addrs {
412 if ParseIP(addr) == nil {
413 t.Errorf("got %q; want a literal IP address", addr)
414 }
415 }
416 }
417 }
418
419 func TestLookupLongTXT(t *testing.T) {
420 testenv.SkipFlaky(t, 22857)
421 mustHaveExternalNetwork(t)
422
423 defer dnsWaitGroup.Wait()
424
425 txts, err := LookupTXT("golang.rsc.io")
426 if err != nil {
427 t.Fatal(err)
428 }
429 sort.Strings(txts)
430 want := []string{
431 strings.Repeat("abcdefghijklmnopqrstuvwxyABCDEFGHJIKLMNOPQRSTUVWXY", 10),
432 "gophers rule",
433 }
434 if !reflect.DeepEqual(txts, want) {
435 t.Fatalf("LookupTXT golang.rsc.io incorrect\nhave %q\nwant %q", txts, want)
436 }
437 }
438
439 var lookupGoogleIPTests = []struct {
440 name string
441 }{
442 {"google.com"},
443 {"google.com."},
444 }
445
446 func TestLookupGoogleIP(t *testing.T) {
447 mustHaveExternalNetwork(t)
448 testenv.SkipFlakyNet(t)
449
450 if !supportsIPv4() || !*testIPv4 {
451 t.Skip("IPv4 is required")
452 }
453
454 defer dnsWaitGroup.Wait()
455
456 for _, tt := range lookupGoogleIPTests {
457 ips, err := LookupIP(tt.name)
458 if err != nil {
459 t.Fatal(err)
460 }
461 if len(ips) == 0 {
462 t.Error("got no record")
463 }
464 for _, ip := range ips {
465 if ip.To4() == nil && ip.To16() == nil {
466 t.Errorf("got %v; want an IP address", ip)
467 }
468 }
469 }
470 }
471
472 var revAddrTests = []struct {
473 Addr string
474 Reverse string
475 ErrPrefix string
476 }{
477 {"1.2.3.4", "4.3.2.1.in-addr.arpa.", ""},
478 {"245.110.36.114", "114.36.110.245.in-addr.arpa.", ""},
479 {"::ffff:12.34.56.78", "78.56.34.12.in-addr.arpa.", ""},
480 {"::1", "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.", ""},
481 {"1::", "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.0.ip6.arpa.", ""},
482 {"1234:567::89a:bcde", "e.d.c.b.a.9.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.7.6.5.0.4.3.2.1.ip6.arpa.", ""},
483 {"1234:567:fefe:bcbc:adad:9e4a:89a:bcde", "e.d.c.b.a.9.8.0.a.4.e.9.d.a.d.a.c.b.c.b.e.f.e.f.7.6.5.0.4.3.2.1.ip6.arpa.", ""},
484 {"1.2.3", "", "unrecognized address"},
485 {"1.2.3.4.5", "", "unrecognized address"},
486 {"1234:567:bcbca::89a:bcde", "", "unrecognized address"},
487 {"1234:567::bcbc:adad::89a:bcde", "", "unrecognized address"},
488 }
489
490 func TestReverseAddress(t *testing.T) {
491 defer dnsWaitGroup.Wait()
492 for i, tt := range revAddrTests {
493 a, err := reverseaddr(tt.Addr)
494 if len(tt.ErrPrefix) > 0 && err == nil {
495 t.Errorf("#%d: expected %q, got <nil> (error)", i, tt.ErrPrefix)
496 continue
497 }
498 if len(tt.ErrPrefix) == 0 && err != nil {
499 t.Errorf("#%d: expected <nil>, got %q (error)", i, err)
500 }
501 if err != nil && err.(*DNSError).Err != tt.ErrPrefix {
502 t.Errorf("#%d: expected %q, got %q (mismatched error)", i, tt.ErrPrefix, err.(*DNSError).Err)
503 }
504 if a != tt.Reverse {
505 t.Errorf("#%d: expected %q, got %q (reverse address)", i, tt.Reverse, a)
506 }
507 }
508 }
509
510 func TestDNSFlood(t *testing.T) {
511 if !*testDNSFlood {
512 t.Skip("test disabled; use -dnsflood to enable")
513 }
514
515 defer dnsWaitGroup.Wait()
516
517 var N = 5000
518 if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
519
520
521
522
523
524
525
526 N = 500
527 }
528
529 const timeout = 3 * time.Second
530 ctxHalfTimeout, cancel := context.WithTimeout(context.Background(), timeout/2)
531 defer cancel()
532 ctxTimeout, cancel := context.WithTimeout(context.Background(), timeout)
533 defer cancel()
534
535 c := make(chan error, 2*N)
536 for i := 0; i < N; i++ {
537 name := fmt.Sprintf("%d.net-test.golang.org", i)
538 go func() {
539 _, err := DefaultResolver.LookupIPAddr(ctxHalfTimeout, name)
540 c <- err
541 }()
542 go func() {
543 _, err := DefaultResolver.LookupIPAddr(ctxTimeout, name)
544 c <- err
545 }()
546 }
547 qstats := struct {
548 succeeded, failed int
549 timeout, temporary, other int
550 unknown int
551 }{}
552 deadline := time.After(timeout + time.Second)
553 for i := 0; i < 2*N; i++ {
554 select {
555 case <-deadline:
556 t.Fatal("deadline exceeded")
557 case err := <-c:
558 switch err := err.(type) {
559 case nil:
560 qstats.succeeded++
561 case Error:
562 qstats.failed++
563 if err.Timeout() {
564 qstats.timeout++
565 }
566 if err.Temporary() {
567 qstats.temporary++
568 }
569 if !err.Timeout() && !err.Temporary() {
570 qstats.other++
571 }
572 default:
573 qstats.failed++
574 qstats.unknown++
575 }
576 }
577 }
578
579
580
581
582
583
584
585
586 t.Logf("%v succeeded, %v failed (%v timeout, %v temporary, %v other, %v unknown)", qstats.succeeded, qstats.failed, qstats.timeout, qstats.temporary, qstats.other, qstats.unknown)
587 }
588
589 func TestLookupDotsWithLocalSource(t *testing.T) {
590 if !supportsIPv4() || !*testIPv4 {
591 t.Skip("IPv4 is required")
592 }
593
594 mustHaveExternalNetwork(t)
595
596 defer dnsWaitGroup.Wait()
597
598 for i, fn := range []func() func(){forceGoDNS, forceCgoDNS} {
599 fixup := fn()
600 if fixup == nil {
601 continue
602 }
603 names, err := LookupAddr("127.0.0.1")
604 fixup()
605 if err != nil {
606 t.Logf("#%d: %v", i, err)
607 continue
608 }
609 mode := "netgo"
610 if i == 1 {
611 mode = "netcgo"
612 }
613 loop:
614 for i, name := range names {
615 if strings.Index(name, ".") == len(name)-1 {
616 for j := range names {
617 if j == i {
618 continue
619 }
620 if names[j] == name[:len(name)-1] {
621
622
623 continue loop
624 }
625 }
626 t.Errorf("%s: got %s; want %s", mode, name, name[:len(name)-1])
627 } else if strings.Contains(name, ".") && !strings.HasSuffix(name, ".") {
628 t.Errorf("%s: got %s; want name ending with trailing dot", mode, name)
629 }
630 }
631 }
632 }
633
634 func TestLookupDotsWithRemoteSource(t *testing.T) {
635 if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
636 testenv.SkipFlaky(t, 27992)
637 }
638 mustHaveExternalNetwork(t)
639 testenv.SkipFlakyNet(t)
640
641 if !supportsIPv4() || !*testIPv4 {
642 t.Skip("IPv4 is required")
643 }
644
645 if runtime.GOOS == "ios" {
646 t.Skip("no resolv.conf on iOS")
647 }
648
649 defer dnsWaitGroup.Wait()
650
651 if fixup := forceGoDNS(); fixup != nil {
652 testDots(t, "go")
653 fixup()
654 }
655 if fixup := forceCgoDNS(); fixup != nil {
656 testDots(t, "cgo")
657 fixup()
658 }
659 }
660
661 func testDots(t *testing.T, mode string) {
662 names, err := LookupAddr("8.8.8.8")
663 if err != nil {
664 t.Errorf("LookupAddr(8.8.8.8): %v (mode=%v)", err, mode)
665 } else {
666 for _, name := range names {
667 if !hasSuffixFold(name, ".google.com.") && !hasSuffixFold(name, ".google.") {
668 t.Errorf("LookupAddr(8.8.8.8) = %v, want names ending in .google.com or .google with trailing dot (mode=%v)", names, mode)
669 break
670 }
671 }
672 }
673
674 cname, err := LookupCNAME("www.mit.edu")
675 if err != nil {
676 t.Errorf("LookupCNAME(www.mit.edu, mode=%v): %v", mode, err)
677 } else if !strings.HasSuffix(cname, ".") {
678 t.Errorf("LookupCNAME(www.mit.edu) = %v, want cname ending in . with trailing dot (mode=%v)", cname, mode)
679 }
680
681 mxs, err := LookupMX("google.com")
682 if err != nil {
683 t.Errorf("LookupMX(google.com): %v (mode=%v)", err, mode)
684 } else {
685 for _, mx := range mxs {
686 if !hasSuffixFold(mx.Host, ".google.com.") {
687 t.Errorf("LookupMX(google.com) = %v, want names ending in .google.com. with trailing dot (mode=%v)", mxString(mxs), mode)
688 break
689 }
690 }
691 }
692
693 nss, err := LookupNS("google.com")
694 if err != nil {
695 t.Errorf("LookupNS(google.com): %v (mode=%v)", err, mode)
696 } else {
697 for _, ns := range nss {
698 if !hasSuffixFold(ns.Host, ".google.com.") {
699 t.Errorf("LookupNS(google.com) = %v, want names ending in .google.com. with trailing dot (mode=%v)", nsString(nss), mode)
700 break
701 }
702 }
703 }
704
705 cname, srvs, err := LookupSRV("ldap", "tcp", "google.com")
706 if err != nil {
707 t.Errorf("LookupSRV(ldap, tcp, google.com): %v (mode=%v)", err, mode)
708 } else {
709 if !hasSuffixFold(cname, ".google.com.") {
710 t.Errorf("LookupSRV(ldap, tcp, google.com) returned cname=%v, want name ending in .google.com. with trailing dot (mode=%v)", cname, mode)
711 }
712 for _, srv := range srvs {
713 if !hasSuffixFold(srv.Target, ".google.com.") {
714 t.Errorf("LookupSRV(ldap, tcp, google.com) returned addrs=%v, want names ending in .google.com. with trailing dot (mode=%v)", srvString(srvs), mode)
715 break
716 }
717 }
718 }
719 }
720
721 func mxString(mxs []*MX) string {
722 var buf strings.Builder
723 sep := ""
724 fmt.Fprintf(&buf, "[")
725 for _, mx := range mxs {
726 fmt.Fprintf(&buf, "%s%s:%d", sep, mx.Host, mx.Pref)
727 sep = " "
728 }
729 fmt.Fprintf(&buf, "]")
730 return buf.String()
731 }
732
733 func nsString(nss []*NS) string {
734 var buf strings.Builder
735 sep := ""
736 fmt.Fprintf(&buf, "[")
737 for _, ns := range nss {
738 fmt.Fprintf(&buf, "%s%s", sep, ns.Host)
739 sep = " "
740 }
741 fmt.Fprintf(&buf, "]")
742 return buf.String()
743 }
744
745 func srvString(srvs []*SRV) string {
746 var buf strings.Builder
747 sep := ""
748 fmt.Fprintf(&buf, "[")
749 for _, srv := range srvs {
750 fmt.Fprintf(&buf, "%s%s:%d:%d:%d", sep, srv.Target, srv.Port, srv.Priority, srv.Weight)
751 sep = " "
752 }
753 fmt.Fprintf(&buf, "]")
754 return buf.String()
755 }
756
757 func TestLookupPort(t *testing.T) {
758
759
760
761
762
763 type test struct {
764 network string
765 name string
766 port int
767 ok bool
768 }
769 var tests = []test{
770 {"tcp", "0", 0, true},
771 {"udp", "0", 0, true},
772 {"udp", "domain", 53, true},
773
774 {"--badnet--", "zzz", 0, false},
775 {"tcp", "--badport--", 0, false},
776 {"tcp", "-1", 0, false},
777 {"tcp", "65536", 0, false},
778 {"udp", "-1", 0, false},
779 {"udp", "65536", 0, false},
780 {"tcp", "123456789", 0, false},
781
782
783 {"tcp", "", 0, true},
784 {"tcp4", "", 0, true},
785 {"tcp6", "", 0, true},
786 {"udp", "", 0, true},
787 {"udp4", "", 0, true},
788 {"udp6", "", 0, true},
789 }
790
791 switch runtime.GOOS {
792 case "android":
793 if netGoBuildTag {
794 t.Skipf("not supported on %s without cgo; see golang.org/issues/14576", runtime.GOOS)
795 }
796 default:
797 tests = append(tests, test{"tcp", "http", 80, true})
798 }
799
800 for _, tt := range tests {
801 port, err := LookupPort(tt.network, tt.name)
802 if port != tt.port || (err == nil) != tt.ok {
803 t.Errorf("LookupPort(%q, %q) = %d, %v; want %d, error=%t", tt.network, tt.name, port, err, tt.port, !tt.ok)
804 }
805 if err != nil {
806 if perr := parseLookupPortError(err); perr != nil {
807 t.Error(perr)
808 }
809 }
810 }
811 }
812
813
814
815 func TestLookupPort_Minimal(t *testing.T) {
816 type test struct {
817 network string
818 name string
819 port int
820 }
821 var tests = []test{
822 {"tcp", "http", 80},
823 {"tcp", "HTTP", 80},
824 {"tcp", "https", 443},
825 {"tcp", "ssh", 22},
826 {"tcp", "gopher", 70},
827 {"tcp4", "http", 80},
828 {"tcp6", "http", 80},
829 }
830
831 for _, tt := range tests {
832 port, err := LookupPort(tt.network, tt.name)
833 if port != tt.port || err != nil {
834 t.Errorf("LookupPort(%q, %q) = %d, %v; want %d, error=nil", tt.network, tt.name, port, err, tt.port)
835 }
836 }
837 }
838
839 func TestLookupProtocol_Minimal(t *testing.T) {
840 type test struct {
841 name string
842 want int
843 }
844 var tests = []test{
845 {"tcp", 6},
846 {"TcP", 6},
847 {"icmp", 1},
848 {"igmp", 2},
849 {"udp", 17},
850 {"ipv6-icmp", 58},
851 }
852
853 for _, tt := range tests {
854 got, err := lookupProtocol(context.Background(), tt.name)
855 if got != tt.want || err != nil {
856 t.Errorf("LookupProtocol(%q) = %d, %v; want %d, error=nil", tt.name, got, err, tt.want)
857 }
858 }
859
860 }
861
862 func TestLookupNonLDH(t *testing.T) {
863 defer dnsWaitGroup.Wait()
864
865 if fixup := forceGoDNS(); fixup != nil {
866 defer fixup()
867 }
868
869
870
871
872
873 addrs, err := LookupHost("!!!.###.bogus..domain.")
874 if err == nil {
875 t.Fatalf("lookup succeeded: %v", addrs)
876 }
877 if !strings.HasSuffix(err.Error(), errNoSuchHost.Error()) {
878 t.Fatalf("lookup error = %v, want %v", err, errNoSuchHost)
879 }
880 if !err.(*DNSError).IsNotFound {
881 t.Fatalf("lookup error = %v, want true", err.(*DNSError).IsNotFound)
882 }
883 }
884
885 func TestLookupContextCancel(t *testing.T) {
886 mustHaveExternalNetwork(t)
887 testenv.SkipFlakyNet(t)
888
889 origTestHookLookupIP := testHookLookupIP
890 defer func() {
891 dnsWaitGroup.Wait()
892 testHookLookupIP = origTestHookLookupIP
893 }()
894
895 lookupCtx, cancelLookup := context.WithCancel(context.Background())
896 unblockLookup := make(chan struct{})
897
898
899
900
901 testHookLookupIP = func(
902 ctx context.Context,
903 fn func(context.Context, string, string) ([]IPAddr, error),
904 network string,
905 host string,
906 ) ([]IPAddr, error) {
907 select {
908 case <-unblockLookup:
909 default:
910
911
912
913
914
915 t.Logf("starting concurrent LookupIPAddr")
916 dnsWaitGroup.Add(1)
917 go func() {
918 defer dnsWaitGroup.Done()
919 _, err := DefaultResolver.LookupIPAddr(context.Background(), host)
920 if err != nil {
921 t.Error(err)
922 }
923 }()
924 time.Sleep(1 * time.Millisecond)
925 }
926
927 cancelLookup()
928 <-unblockLookup
929
930
931
932
933
934 if err := ctx.Err(); err != nil {
935 t.Logf("testHookLookupIP canceled")
936 return nil, err
937 }
938 t.Logf("testHookLookupIP performing lookup")
939 return fn(ctx, network, host)
940 }
941
942 _, err := DefaultResolver.LookupIPAddr(lookupCtx, "google.com")
943 if dnsErr, ok := err.(*DNSError); !ok || dnsErr.Err != errCanceled.Error() {
944 t.Errorf("unexpected error from canceled, blocked LookupIPAddr: %v", err)
945 }
946 close(unblockLookup)
947 }
948
949
950
951 func TestNilResolverLookup(t *testing.T) {
952 mustHaveExternalNetwork(t)
953 var r *Resolver = nil
954 ctx := context.Background()
955
956
957 r.LookupAddr(ctx, "8.8.8.8")
958 r.LookupCNAME(ctx, "google.com")
959 r.LookupHost(ctx, "google.com")
960 r.LookupIPAddr(ctx, "google.com")
961 r.LookupIP(ctx, "ip", "google.com")
962 r.LookupMX(ctx, "gmail.com")
963 r.LookupNS(ctx, "google.com")
964 r.LookupPort(ctx, "tcp", "smtp")
965 r.LookupSRV(ctx, "service", "proto", "name")
966 r.LookupTXT(ctx, "gmail.com")
967 }
968
969
970
971 func TestLookupHostCancel(t *testing.T) {
972 mustHaveExternalNetwork(t)
973 testenv.SkipFlakyNet(t)
974 t.Parallel()
975
976 const (
977 google = "www.google.com"
978 invalidDomain = "invalid.invalid"
979 n = 600
980 )
981
982 _, err := LookupHost(google)
983 if err != nil {
984 t.Fatal(err)
985 }
986
987 ctx, cancel := context.WithCancel(context.Background())
988 cancel()
989 for i := 0; i < n; i++ {
990 addr, err := DefaultResolver.LookupHost(ctx, invalidDomain)
991 if err == nil {
992 t.Fatalf("LookupHost(%q): returns %v, but should fail", invalidDomain, addr)
993 }
994
995
996
997
998
999
1000
1001
1002
1003 time.Sleep(time.Millisecond * 1)
1004 }
1005
1006 _, err = LookupHost(google)
1007 if err != nil {
1008 t.Fatal(err)
1009 }
1010 }
1011
1012 type lookupCustomResolver struct {
1013 *Resolver
1014 mu sync.RWMutex
1015 dialed bool
1016 }
1017
1018 func (lcr *lookupCustomResolver) dial() func(ctx context.Context, network, address string) (Conn, error) {
1019 return func(ctx context.Context, network, address string) (Conn, error) {
1020 lcr.mu.Lock()
1021 lcr.dialed = true
1022 lcr.mu.Unlock()
1023 return Dial(network, address)
1024 }
1025 }
1026
1027
1028
1029 func TestConcurrentPreferGoResolversDial(t *testing.T) {
1030 switch runtime.GOOS {
1031 case "plan9":
1032
1033
1034 t.Skipf("skip on %v", runtime.GOOS)
1035 }
1036
1037 testenv.MustHaveExternalNetwork(t)
1038 testenv.SkipFlakyNet(t)
1039
1040 defer dnsWaitGroup.Wait()
1041
1042 resolvers := make([]*lookupCustomResolver, 2)
1043 for i := range resolvers {
1044 cs := lookupCustomResolver{Resolver: &Resolver{PreferGo: true}}
1045 cs.Dial = cs.dial()
1046 resolvers[i] = &cs
1047 }
1048
1049 var wg sync.WaitGroup
1050 wg.Add(len(resolvers))
1051 for i, resolver := range resolvers {
1052 go func(r *Resolver, index int) {
1053 defer wg.Done()
1054 _, err := r.LookupIPAddr(context.Background(), "google.com")
1055 if err != nil {
1056 t.Errorf("lookup failed for resolver %d: %q", index, err)
1057 }
1058 }(resolver.Resolver, i)
1059 }
1060 wg.Wait()
1061
1062 if t.Failed() {
1063 t.FailNow()
1064 }
1065
1066 for i, resolver := range resolvers {
1067 if !resolver.dialed {
1068 t.Errorf("custom resolver %d not dialed during lookup", i)
1069 }
1070 }
1071 }
1072
1073 var ipVersionTests = []struct {
1074 network string
1075 version byte
1076 }{
1077 {"tcp", 0},
1078 {"tcp4", '4'},
1079 {"tcp6", '6'},
1080 {"udp", 0},
1081 {"udp4", '4'},
1082 {"udp6", '6'},
1083 {"ip", 0},
1084 {"ip4", '4'},
1085 {"ip6", '6'},
1086 {"ip7", 0},
1087 {"", 0},
1088 }
1089
1090 func TestIPVersion(t *testing.T) {
1091 for _, tt := range ipVersionTests {
1092 if version := ipVersion(tt.network); version != tt.version {
1093 t.Errorf("Family for: %s. Expected: %s, Got: %s", tt.network,
1094 string(tt.version), string(version))
1095 }
1096 }
1097 }
1098
1099
1100
1101 func TestLookupIPAddrPreservesContextValues(t *testing.T) {
1102 origTestHookLookupIP := testHookLookupIP
1103 defer func() { testHookLookupIP = origTestHookLookupIP }()
1104
1105 keyValues := []struct {
1106 key, value any
1107 }{
1108 {"key-1", 12},
1109 {384, "value2"},
1110 {new(float64), 137},
1111 }
1112 ctx := context.Background()
1113 for _, kv := range keyValues {
1114 ctx = context.WithValue(ctx, kv.key, kv.value)
1115 }
1116
1117 wantIPs := []IPAddr{
1118 {IP: IPv4(127, 0, 0, 1)},
1119 {IP: IPv6loopback},
1120 }
1121
1122 checkCtxValues := func(ctx_ context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
1123 for _, kv := range keyValues {
1124 g, w := ctx_.Value(kv.key), kv.value
1125 if !reflect.DeepEqual(g, w) {
1126 t.Errorf("Value lookup:\n\tGot: %v\n\tWant: %v", g, w)
1127 }
1128 }
1129 return wantIPs, nil
1130 }
1131 testHookLookupIP = checkCtxValues
1132
1133 resolvers := []*Resolver{
1134 nil,
1135 new(Resolver),
1136 }
1137
1138 for i, resolver := range resolvers {
1139 gotIPs, err := resolver.LookupIPAddr(ctx, "golang.org")
1140 if err != nil {
1141 t.Errorf("Resolver #%d: unexpected error: %v", i, err)
1142 }
1143 if !reflect.DeepEqual(gotIPs, wantIPs) {
1144 t.Errorf("#%d: mismatched IPAddr results\n\tGot: %v\n\tWant: %v", i, gotIPs, wantIPs)
1145 }
1146 }
1147 }
1148
1149
1150 func TestLookupIPAddrConcurrentCallsForNetworks(t *testing.T) {
1151 origTestHookLookupIP := testHookLookupIP
1152 defer func() { testHookLookupIP = origTestHookLookupIP }()
1153
1154 queries := [][]string{
1155 {"udp", "golang.org"},
1156 {"udp4", "golang.org"},
1157 {"udp6", "golang.org"},
1158 {"udp", "golang.org"},
1159 {"udp", "golang.org"},
1160 }
1161 results := map[[2]string][]IPAddr{
1162 {"udp", "golang.org"}: {
1163 {IP: IPv4(127, 0, 0, 1)},
1164 {IP: IPv6loopback},
1165 },
1166 {"udp4", "golang.org"}: {
1167 {IP: IPv4(127, 0, 0, 1)},
1168 },
1169 {"udp6", "golang.org"}: {
1170 {IP: IPv6loopback},
1171 },
1172 }
1173 calls := int32(0)
1174 waitCh := make(chan struct{})
1175 testHookLookupIP = func(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
1176
1177
1178
1179 if atomic.AddInt32(&calls, 1) == int32(len(results)) {
1180 close(waitCh)
1181 }
1182 select {
1183 case <-waitCh:
1184 case <-ctx.Done():
1185 return nil, ctx.Err()
1186 }
1187 return results[[2]string{network, host}], nil
1188 }
1189
1190 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
1191 defer cancel()
1192 wg := sync.WaitGroup{}
1193 for _, q := range queries {
1194 network := q[0]
1195 host := q[1]
1196 wg.Add(1)
1197 go func() {
1198 defer wg.Done()
1199 gotIPs, err := DefaultResolver.lookupIPAddr(ctx, network, host)
1200 if err != nil {
1201 t.Errorf("lookupIPAddr(%v, %v): unexpected error: %v", network, host, err)
1202 }
1203 wantIPs := results[[2]string{network, host}]
1204 if !reflect.DeepEqual(gotIPs, wantIPs) {
1205 t.Errorf("lookupIPAddr(%v, %v): mismatched IPAddr results\n\tGot: %v\n\tWant: %v", network, host, gotIPs, wantIPs)
1206 }
1207 }()
1208 }
1209 wg.Wait()
1210 }
1211
1212
1213 func TestResolverLookupIPWithEmptyHost(t *testing.T) {
1214 _, err := DefaultResolver.LookupIP(context.Background(), "ip", "")
1215 if err == nil {
1216 t.Fatal("DefaultResolver.LookupIP for empty host success, want no host error")
1217 }
1218 if !strings.HasSuffix(err.Error(), errNoSuchHost.Error()) {
1219 t.Fatalf("lookup error = %v, want %v", err, errNoSuchHost)
1220 }
1221 }
1222
1223 func TestWithUnexpiredValuesPreserved(t *testing.T) {
1224 ctx, cancel := context.WithCancel(context.Background())
1225
1226
1227 key, value := "key-1", 2
1228 ctx = context.WithValue(ctx, key, value)
1229
1230
1231
1232 ctx = withUnexpiredValuesPreserved(ctx)
1233
1234
1235 if g, w := ctx.Value(key), value; g != w {
1236 t.Errorf("Lookup before expiry: Got %v Want %v", g, w)
1237 }
1238
1239
1240 cancel()
1241
1242
1243 if g := ctx.Value(key); g != nil {
1244 t.Errorf("Lookup after expiry: Got %v want nil", g)
1245 }
1246 }
1247
1248
1249 func TestLookupNullByte(t *testing.T) {
1250 testenv.MustHaveExternalNetwork(t)
1251 testenv.SkipFlakyNet(t)
1252 LookupHost("foo\x00bar")
1253 }
1254
1255 func TestResolverLookupIP(t *testing.T) {
1256 testenv.MustHaveExternalNetwork(t)
1257
1258 v4Ok := supportsIPv4() && *testIPv4
1259 v6Ok := supportsIPv6() && *testIPv6
1260
1261 defer dnsWaitGroup.Wait()
1262
1263 for _, impl := range []struct {
1264 name string
1265 fn func() func()
1266 }{
1267 {"go", forceGoDNS},
1268 {"cgo", forceCgoDNS},
1269 } {
1270 t.Run("implementation: "+impl.name, func(t *testing.T) {
1271 fixup := impl.fn()
1272 if fixup == nil {
1273 t.Skip("not supported")
1274 }
1275 defer fixup()
1276
1277 for _, network := range []string{"ip", "ip4", "ip6"} {
1278 t.Run("network: "+network, func(t *testing.T) {
1279 switch {
1280 case network == "ip4" && !v4Ok:
1281 t.Skip("IPv4 is not supported")
1282 case network == "ip6" && !v6Ok:
1283 t.Skip("IPv6 is not supported")
1284 }
1285
1286
1287 const host = "google.com"
1288 ips, err := DefaultResolver.LookupIP(context.Background(), network, host)
1289 if err != nil {
1290 testenv.SkipFlakyNet(t)
1291 t.Fatalf("DefaultResolver.LookupIP(%q, %q): failed with unexpected error: %v", network, host, err)
1292 }
1293
1294 var v4Addrs []netip.Addr
1295 var v6Addrs []netip.Addr
1296 for _, ip := range ips {
1297 if addr, ok := netip.AddrFromSlice(ip); ok {
1298 if addr.Is4() {
1299 v4Addrs = append(v4Addrs, addr)
1300 } else {
1301 v6Addrs = append(v6Addrs, addr)
1302 }
1303 } else {
1304 t.Fatalf("IP=%q is neither IPv4 nor IPv6", ip)
1305 }
1306 }
1307
1308
1309 if network == "ip4" || network == "ip" && v4Ok {
1310 if len(v4Addrs) == 0 {
1311 t.Errorf("DefaultResolver.LookupIP(%q, %q): no IPv4 addresses", network, host)
1312 }
1313 }
1314 if network == "ip6" || network == "ip" && v6Ok {
1315 if len(v6Addrs) == 0 {
1316 t.Errorf("DefaultResolver.LookupIP(%q, %q): no IPv6 addresses", network, host)
1317 }
1318 }
1319
1320
1321 if network == "ip6" && len(v4Addrs) > 0 {
1322 t.Errorf("DefaultResolver.LookupIP(%q, %q): unexpected IPv4 addresses: %v", network, host, v4Addrs)
1323 }
1324 if network == "ip4" && len(v6Addrs) > 0 {
1325 t.Errorf("DefaultResolver.LookupIP(%q, %q): unexpected IPv6 or IPv4-mapped IPv6 addresses: %v", network, host, v6Addrs)
1326 }
1327 })
1328 }
1329 })
1330 }
1331 }
1332
1333
1334 func TestDNSTimeout(t *testing.T) {
1335 origTestHookLookupIP := testHookLookupIP
1336 defer func() { testHookLookupIP = origTestHookLookupIP }()
1337 defer dnsWaitGroup.Wait()
1338
1339 timeoutHookGo := make(chan bool, 1)
1340 timeoutHook := func(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
1341 <-timeoutHookGo
1342 return nil, context.DeadlineExceeded
1343 }
1344 testHookLookupIP = timeoutHook
1345
1346 checkErr := func(err error) {
1347 t.Helper()
1348 if err == nil {
1349 t.Error("expected an error")
1350 } else if dnserr, ok := err.(*DNSError); !ok {
1351 t.Errorf("got error type %T, want %T", err, (*DNSError)(nil))
1352 } else if !dnserr.IsTimeout {
1353 t.Errorf("got error %#v, want IsTimeout == true", dnserr)
1354 } else if isTimeout := dnserr.Timeout(); !isTimeout {
1355 t.Errorf("got err.Timeout() == %t, want true", isTimeout)
1356 }
1357 }
1358
1359
1360 timeoutHookGo <- true
1361 _, err := LookupIP("golang.org")
1362 checkErr(err)
1363
1364
1365 var err1, err2 error
1366 var wg sync.WaitGroup
1367 wg.Add(2)
1368 go func() {
1369 defer wg.Done()
1370 _, err1 = LookupIP("golang1.org")
1371 }()
1372 go func() {
1373 defer wg.Done()
1374 _, err2 = LookupIP("golang1.org")
1375 }()
1376 close(timeoutHookGo)
1377 wg.Wait()
1378 checkErr(err1)
1379 checkErr(err2)
1380
1381
1382 timeoutHookGo = make(chan bool)
1383 ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond)
1384 wg.Add(2)
1385 go func() {
1386 defer wg.Done()
1387 _, err1 = DefaultResolver.LookupIPAddr(ctx, "golang2.org")
1388 }()
1389 go func() {
1390 defer wg.Done()
1391 _, err2 = DefaultResolver.LookupIPAddr(ctx, "golang2.org")
1392 }()
1393 time.Sleep(10 * time.Nanosecond)
1394 close(timeoutHookGo)
1395 wg.Wait()
1396 checkErr(err1)
1397 checkErr(err2)
1398 cancel()
1399 }
1400
1401 func TestLookupNoData(t *testing.T) {
1402 if runtime.GOOS == "plan9" {
1403 t.Skip("not supported on plan9")
1404 }
1405
1406 mustHaveExternalNetwork(t)
1407
1408 testLookupNoData(t, "default resolver")
1409
1410 func() {
1411 defer forceGoDNS()()
1412 testLookupNoData(t, "forced go resolver")
1413 }()
1414
1415 func() {
1416 defer forceCgoDNS()()
1417 testLookupNoData(t, "forced cgo resolver")
1418 }()
1419 }
1420
1421 func testLookupNoData(t *testing.T, prefix string) {
1422 attempts := 0
1423 for {
1424
1425
1426 _, err := LookupHost("golang.rsc.io.")
1427 if err == nil {
1428 t.Errorf("%v: unexpected success", prefix)
1429 return
1430 }
1431
1432 var dnsErr *DNSError
1433 if errors.As(err, &dnsErr) {
1434 succeeded := true
1435 if !dnsErr.IsNotFound {
1436 succeeded = false
1437 t.Logf("%v: IsNotFound is set to false", prefix)
1438 }
1439
1440 if dnsErr.Err != errNoSuchHost.Error() {
1441 succeeded = false
1442 t.Logf("%v: error message is not equal to: %v", prefix, errNoSuchHost.Error())
1443 }
1444
1445 if succeeded {
1446 return
1447 }
1448 }
1449
1450 testenv.SkipFlakyNet(t)
1451 if attempts < len(backoffDuration) {
1452 dur := backoffDuration[attempts]
1453 t.Logf("%v: backoff %v after failure %v\n", prefix, dur, err)
1454 time.Sleep(dur)
1455 attempts++
1456 continue
1457 }
1458
1459 t.Errorf("%v: unexpected error: %v", prefix, err)
1460 return
1461 }
1462 }
1463
1464 func TestLookupPortNotFound(t *testing.T) {
1465 allResolvers(t, func(t *testing.T) {
1466 _, err := LookupPort("udp", "_-unknown-service-")
1467 var dnsErr *DNSError
1468 if !errors.As(err, &dnsErr) || !dnsErr.IsNotFound {
1469 t.Fatalf("unexpected error: %v", err)
1470 }
1471 })
1472 }
1473
1474
1475
1476 var tcpOnlyService = func() string {
1477
1478 if runtime.GOOS == "plan9" {
1479 return "https"
1480 }
1481 return "submissions"
1482 }()
1483
1484 func TestLookupPortDifferentNetwork(t *testing.T) {
1485 allResolvers(t, func(t *testing.T) {
1486 _, err := LookupPort("udp", tcpOnlyService)
1487 var dnsErr *DNSError
1488 if !errors.As(err, &dnsErr) || !dnsErr.IsNotFound {
1489 t.Fatalf("unexpected error: %v", err)
1490 }
1491 })
1492 }
1493
1494 func TestLookupPortEmptyNetworkString(t *testing.T) {
1495 allResolvers(t, func(t *testing.T) {
1496 _, err := LookupPort("", tcpOnlyService)
1497 if err != nil {
1498 t.Fatalf("unexpected error: %v", err)
1499 }
1500 })
1501 }
1502
1503 func TestLookupPortIPNetworkString(t *testing.T) {
1504 allResolvers(t, func(t *testing.T) {
1505 _, err := LookupPort("ip", tcpOnlyService)
1506 if err != nil {
1507 t.Fatalf("unexpected error: %v", err)
1508 }
1509 })
1510 }
1511
1512 func allResolvers(t *testing.T, f func(t *testing.T)) {
1513 t.Run("default resolver", f)
1514 t.Run("forced go resolver", func(t *testing.T) {
1515 if fixup := forceGoDNS(); fixup != nil {
1516 defer fixup()
1517 f(t)
1518 }
1519 })
1520 t.Run("forced cgo resolver", func(t *testing.T) {
1521 if fixup := forceCgoDNS(); fixup != nil {
1522 defer fixup()
1523 f(t)
1524 }
1525 })
1526 }
1527
1528 func TestLookupNoSuchHost(t *testing.T) {
1529 mustHaveExternalNetwork(t)
1530
1531 const testNXDOMAIN = "invalid.invalid."
1532 const testNODATA = "_ldap._tcp.google.com."
1533
1534 tests := []struct {
1535 name string
1536 query func() error
1537 }{
1538 {
1539 name: "LookupCNAME NXDOMAIN",
1540 query: func() error {
1541 _, err := LookupCNAME(testNXDOMAIN)
1542 return err
1543 },
1544 },
1545 {
1546 name: "LookupHost NXDOMAIN",
1547 query: func() error {
1548 _, err := LookupHost(testNXDOMAIN)
1549 return err
1550 },
1551 },
1552 {
1553 name: "LookupHost NODATA",
1554 query: func() error {
1555 _, err := LookupHost(testNODATA)
1556 return err
1557 },
1558 },
1559 {
1560 name: "LookupMX NXDOMAIN",
1561 query: func() error {
1562 _, err := LookupMX(testNXDOMAIN)
1563 return err
1564 },
1565 },
1566 {
1567 name: "LookupMX NODATA",
1568 query: func() error {
1569 _, err := LookupMX(testNODATA)
1570 return err
1571 },
1572 },
1573 {
1574 name: "LookupNS NXDOMAIN",
1575 query: func() error {
1576 _, err := LookupNS(testNXDOMAIN)
1577 return err
1578 },
1579 },
1580 {
1581 name: "LookupNS NODATA",
1582 query: func() error {
1583 _, err := LookupNS(testNODATA)
1584 return err
1585 },
1586 },
1587 {
1588 name: "LookupSRV NXDOMAIN",
1589 query: func() error {
1590 _, _, err := LookupSRV("unknown", "tcp", testNXDOMAIN)
1591 return err
1592 },
1593 },
1594 {
1595 name: "LookupTXT NXDOMAIN",
1596 query: func() error {
1597 _, err := LookupTXT(testNXDOMAIN)
1598 return err
1599 },
1600 },
1601 {
1602 name: "LookupTXT NODATA",
1603 query: func() error {
1604 _, err := LookupTXT(testNODATA)
1605 return err
1606 },
1607 },
1608 }
1609
1610 for _, v := range tests {
1611 t.Run(v.name, func(t *testing.T) {
1612 allResolvers(t, func(t *testing.T) {
1613 attempts := 0
1614 for {
1615 err := v.query()
1616 if err == nil {
1617 t.Errorf("unexpected success")
1618 return
1619 }
1620 if dnsErr, ok := err.(*DNSError); ok {
1621 succeeded := true
1622 if !dnsErr.IsNotFound {
1623 succeeded = false
1624 t.Log("IsNotFound is set to false")
1625 }
1626 if dnsErr.Err != errNoSuchHost.Error() {
1627 succeeded = false
1628 t.Logf("error message is not equal to: %v", errNoSuchHost.Error())
1629 }
1630 if succeeded {
1631 return
1632 }
1633 }
1634 testenv.SkipFlakyNet(t)
1635 if attempts < len(backoffDuration) {
1636 dur := backoffDuration[attempts]
1637 t.Logf("backoff %v after failure %v\n", dur, err)
1638 time.Sleep(dur)
1639 attempts++
1640 continue
1641 }
1642 t.Errorf("unexpected error: %v", err)
1643 return
1644 }
1645 })
1646 })
1647 }
1648 }
1649
View as plain text