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 dnsErr, ok := errors.AsType[*DNSError](err)
1424 if ok {
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 if dnsErr, ok := errors.AsType[*DNSError](err); !ok || !dnsErr.IsNotFound {
1459 t.Fatalf("unexpected error: %v", err)
1460 }
1461 })
1462 }
1463
1464
1465
1466 var tcpOnlyService = func() string {
1467
1468 if runtime.GOOS == "plan9" {
1469 return "https"
1470 }
1471 return "submissions"
1472 }()
1473
1474 func TestLookupPortDifferentNetwork(t *testing.T) {
1475 allResolvers(t, func(t *testing.T) {
1476 _, err := LookupPort("udp", tcpOnlyService)
1477 if dnsErr, ok := errors.AsType[*DNSError](err); !ok || !dnsErr.IsNotFound {
1478 t.Fatalf("unexpected error: %v", err)
1479 }
1480 })
1481 }
1482
1483 func TestLookupPortEmptyNetworkString(t *testing.T) {
1484 allResolvers(t, func(t *testing.T) {
1485 _, err := LookupPort("", tcpOnlyService)
1486 if err != nil {
1487 t.Fatalf("unexpected error: %v", err)
1488 }
1489 })
1490 }
1491
1492 func TestLookupPortIPNetworkString(t *testing.T) {
1493 allResolvers(t, func(t *testing.T) {
1494 _, err := LookupPort("ip", tcpOnlyService)
1495 if err != nil {
1496 t.Fatalf("unexpected error: %v", err)
1497 }
1498 })
1499 }
1500
1501 func TestLookupNoSuchHost(t *testing.T) {
1502 mustHaveExternalNetwork(t)
1503
1504 const testNXDOMAIN = "invalid.invalid."
1505 const testNODATA = "_ldap._tcp.google.com."
1506
1507 tests := []struct {
1508 name string
1509 query func() error
1510 }{
1511 {
1512 name: "LookupCNAME NXDOMAIN",
1513 query: func() error {
1514 _, err := LookupCNAME(testNXDOMAIN)
1515 return err
1516 },
1517 },
1518 {
1519 name: "LookupHost NXDOMAIN",
1520 query: func() error {
1521 _, err := LookupHost(testNXDOMAIN)
1522 return err
1523 },
1524 },
1525 {
1526 name: "LookupHost NODATA",
1527 query: func() error {
1528 _, err := LookupHost(testNODATA)
1529 return err
1530 },
1531 },
1532 {
1533 name: "LookupMX NXDOMAIN",
1534 query: func() error {
1535 _, err := LookupMX(testNXDOMAIN)
1536 return err
1537 },
1538 },
1539 {
1540 name: "LookupMX NODATA",
1541 query: func() error {
1542 _, err := LookupMX(testNODATA)
1543 return err
1544 },
1545 },
1546 {
1547 name: "LookupNS NXDOMAIN",
1548 query: func() error {
1549 _, err := LookupNS(testNXDOMAIN)
1550 return err
1551 },
1552 },
1553 {
1554 name: "LookupNS NODATA",
1555 query: func() error {
1556 _, err := LookupNS(testNODATA)
1557 return err
1558 },
1559 },
1560 {
1561 name: "LookupSRV NXDOMAIN",
1562 query: func() error {
1563 _, _, err := LookupSRV("unknown", "tcp", testNXDOMAIN)
1564 return err
1565 },
1566 },
1567 {
1568 name: "LookupTXT NXDOMAIN",
1569 query: func() error {
1570 _, err := LookupTXT(testNXDOMAIN)
1571 return err
1572 },
1573 },
1574 {
1575 name: "LookupTXT NODATA",
1576 query: func() error {
1577 _, err := LookupTXT(testNODATA)
1578 return err
1579 },
1580 },
1581 }
1582
1583 for _, v := range tests {
1584 t.Run(v.name, func(t *testing.T) {
1585 allResolvers(t, func(t *testing.T) {
1586 attempts := 0
1587 for {
1588 err := v.query()
1589 if err == nil {
1590 t.Errorf("unexpected success")
1591 return
1592 }
1593 if dnsErr, ok := err.(*DNSError); ok {
1594 succeeded := true
1595 if !dnsErr.IsNotFound {
1596 succeeded = false
1597 t.Log("IsNotFound is set to false")
1598 }
1599 if dnsErr.Err != errNoSuchHost.Error() {
1600 succeeded = false
1601 t.Logf("error message is not equal to: %v", errNoSuchHost.Error())
1602 }
1603 if succeeded {
1604 return
1605 }
1606 }
1607 testenv.SkipFlakyNet(t)
1608 if attempts < len(backoffDuration) {
1609 dur := backoffDuration[attempts]
1610 t.Logf("backoff %v after failure %v\n", dur, err)
1611 time.Sleep(dur)
1612 attempts++
1613 continue
1614 }
1615 t.Errorf("unexpected error: %v", err)
1616 return
1617 }
1618 })
1619 })
1620 }
1621 }
1622
1623 func TestDNSErrorUnwrap(t *testing.T) {
1624 if runtime.GOOS == "plan9" {
1625
1626 t.Skip("skipping on plan9")
1627 }
1628 rDeadlineExcceeded := &Resolver{PreferGo: true, Dial: func(ctx context.Context, network, address string) (Conn, error) {
1629 return nil, context.DeadlineExceeded
1630 }}
1631 rCancelled := &Resolver{PreferGo: true, Dial: func(ctx context.Context, network, address string) (Conn, error) {
1632 return nil, context.Canceled
1633 }}
1634
1635 _, err := rDeadlineExcceeded.LookupHost(context.Background(), "test.go.dev")
1636 if !errors.Is(err, context.DeadlineExceeded) {
1637 t.Errorf("errors.Is(err, context.DeadlineExceeded) = false; want = true")
1638 }
1639
1640 _, err = rCancelled.LookupHost(context.Background(), "test.go.dev")
1641 if !errors.Is(err, context.Canceled) {
1642 t.Errorf("errors.Is(err, context.Canceled) = false; want = true")
1643 }
1644
1645 ctx, cancel := context.WithCancel(context.Background())
1646 cancel()
1647 _, err = goResolver.LookupHost(ctx, "text.go.dev")
1648 if !errors.Is(err, context.Canceled) {
1649 t.Errorf("errors.Is(err, context.Canceled) = false; want = true")
1650 }
1651 }
1652
View as plain text