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