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