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.golang.org", "golang.org."},
339 {"www.golang.org.", "golang.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 {"tcp", "bad\x00port", 0, false},
773
774
775 {"tcp", "", 0, true},
776 {"tcp4", "", 0, true},
777 {"tcp6", "", 0, true},
778 {"udp", "", 0, true},
779 {"udp4", "", 0, true},
780 {"udp6", "", 0, true},
781 }
782
783 switch runtime.GOOS {
784 case "android":
785 if netGoBuildTag {
786 t.Skipf("not supported on %s without cgo; see golang.org/issues/14576", runtime.GOOS)
787 }
788 default:
789 tests = append(tests, test{"tcp", "http", 80, true})
790 }
791
792 for _, tt := range tests {
793 port, err := LookupPort(tt.network, tt.name)
794 if port != tt.port || (err == nil) != tt.ok {
795 t.Errorf("LookupPort(%q, %q) = %d, %v; want %d, error=%t", tt.network, tt.name, port, err, tt.port, !tt.ok)
796 }
797 if err != nil {
798 if perr := parseLookupPortError(err); perr != nil {
799 t.Error(perr)
800 }
801 }
802 }
803 }
804
805
806
807 func TestLookupPort_Minimal(t *testing.T) {
808 type test struct {
809 network string
810 name string
811 port int
812 }
813 var tests = []test{
814 {"tcp", "http", 80},
815 {"tcp", "HTTP", 80},
816 {"tcp", "https", 443},
817 {"tcp", "ssh", 22},
818 {"tcp", "gopher", 70},
819 {"tcp4", "http", 80},
820 {"tcp6", "http", 80},
821 }
822
823 for _, tt := range tests {
824 port, err := LookupPort(tt.network, tt.name)
825 if port != tt.port || err != nil {
826 t.Errorf("LookupPort(%q, %q) = %d, %v; want %d, error=nil", tt.network, tt.name, port, err, tt.port)
827 }
828 }
829 }
830
831 func TestLookupProtocol_Minimal(t *testing.T) {
832 type test struct {
833 name string
834 want int
835 }
836 var tests = []test{
837 {"tcp", 6},
838 {"TcP", 6},
839 {"icmp", 1},
840 {"igmp", 2},
841 {"udp", 17},
842 {"ipv6-icmp", 58},
843 }
844
845 for _, tt := range tests {
846 got, err := lookupProtocol(context.Background(), tt.name)
847 if got != tt.want || err != nil {
848 t.Errorf("LookupProtocol(%q) = %d, %v; want %d, error=nil", tt.name, got, err, tt.want)
849 }
850 }
851
852 }
853
854 func TestLookupNonLDH(t *testing.T) {
855 defer dnsWaitGroup.Wait()
856
857 if fixup := forceGoDNS(); fixup != nil {
858 defer fixup()
859 }
860
861
862
863
864
865 addrs, err := LookupHost("!!!.###.bogus..domain.")
866 if err == nil {
867 t.Fatalf("lookup succeeded: %v", addrs)
868 }
869 if !strings.HasSuffix(err.Error(), errNoSuchHost.Error()) {
870 t.Fatalf("lookup error = %v, want %v", err, errNoSuchHost)
871 }
872 if !err.(*DNSError).IsNotFound {
873 t.Fatalf("lookup error = %v, want true", err.(*DNSError).IsNotFound)
874 }
875 }
876
877 func TestLookupContextCancel(t *testing.T) {
878 mustHaveExternalNetwork(t)
879 testenv.SkipFlakyNet(t)
880
881 origTestHookLookupIP := testHookLookupIP
882 defer func() {
883 dnsWaitGroup.Wait()
884 testHookLookupIP = origTestHookLookupIP
885 }()
886
887 lookupCtx, cancelLookup := context.WithCancel(context.Background())
888 unblockLookup := make(chan struct{})
889
890
891
892
893 testHookLookupIP = func(
894 ctx context.Context,
895 fn func(context.Context, string, string) ([]IPAddr, error),
896 network string,
897 host string,
898 ) ([]IPAddr, error) {
899 select {
900 case <-unblockLookup:
901 default:
902
903
904
905
906
907 t.Logf("starting concurrent LookupIPAddr")
908 dnsWaitGroup.Add(1)
909 go func() {
910 defer dnsWaitGroup.Done()
911 _, err := DefaultResolver.LookupIPAddr(context.Background(), host)
912 if err != nil {
913 t.Error(err)
914 }
915 }()
916 time.Sleep(1 * time.Millisecond)
917 }
918
919 cancelLookup()
920 <-unblockLookup
921
922
923
924
925
926 if err := ctx.Err(); err != nil {
927 t.Logf("testHookLookupIP canceled")
928 return nil, err
929 }
930 t.Logf("testHookLookupIP performing lookup")
931 return fn(ctx, network, host)
932 }
933
934 _, err := DefaultResolver.LookupIPAddr(lookupCtx, "google.com")
935 if dnsErr, ok := err.(*DNSError); !ok || dnsErr.Err != errCanceled.Error() {
936 t.Errorf("unexpected error from canceled, blocked LookupIPAddr: %v", err)
937 }
938 close(unblockLookup)
939 }
940
941
942
943 func TestNilResolverLookup(t *testing.T) {
944 mustHaveExternalNetwork(t)
945 var r *Resolver = nil
946 ctx := context.Background()
947
948
949 r.LookupAddr(ctx, "8.8.8.8")
950 r.LookupCNAME(ctx, "google.com")
951 r.LookupHost(ctx, "google.com")
952 r.LookupIPAddr(ctx, "google.com")
953 r.LookupIP(ctx, "ip", "google.com")
954 r.LookupMX(ctx, "gmail.com")
955 r.LookupNS(ctx, "google.com")
956 r.LookupPort(ctx, "tcp", "smtp")
957 r.LookupSRV(ctx, "service", "proto", "name")
958 r.LookupTXT(ctx, "gmail.com")
959 }
960
961
962
963 func TestLookupHostCancel(t *testing.T) {
964 mustHaveExternalNetwork(t)
965 testenv.SkipFlakyNet(t)
966 t.Parallel()
967
968 const (
969 google = "www.google.com"
970 invalidDomain = "invalid.invalid"
971 n = 600
972 )
973
974 _, err := LookupHost(google)
975 if err != nil {
976 t.Fatal(err)
977 }
978
979 ctx, cancel := context.WithCancel(context.Background())
980 cancel()
981 for i := 0; i < n; i++ {
982 addr, err := DefaultResolver.LookupHost(ctx, invalidDomain)
983 if err == nil {
984 t.Fatalf("LookupHost(%q): returns %v, but should fail", invalidDomain, addr)
985 }
986
987
988
989
990
991
992
993
994
995 time.Sleep(time.Millisecond * 1)
996 }
997
998 _, err = LookupHost(google)
999 if err != nil {
1000 t.Fatal(err)
1001 }
1002 }
1003
1004 type lookupCustomResolver struct {
1005 *Resolver
1006 mu sync.RWMutex
1007 dialed bool
1008 }
1009
1010 func (lcr *lookupCustomResolver) dial() func(ctx context.Context, network, address string) (Conn, error) {
1011 return func(ctx context.Context, network, address string) (Conn, error) {
1012 lcr.mu.Lock()
1013 lcr.dialed = true
1014 lcr.mu.Unlock()
1015 return Dial(network, address)
1016 }
1017 }
1018
1019
1020
1021 func TestConcurrentPreferGoResolversDial(t *testing.T) {
1022 switch runtime.GOOS {
1023 case "plan9":
1024
1025
1026 t.Skipf("skip on %v", runtime.GOOS)
1027 }
1028
1029 testenv.MustHaveExternalNetwork(t)
1030 testenv.SkipFlakyNet(t)
1031
1032 defer dnsWaitGroup.Wait()
1033
1034 resolvers := make([]*lookupCustomResolver, 2)
1035 for i := range resolvers {
1036 cs := lookupCustomResolver{Resolver: &Resolver{PreferGo: true}}
1037 cs.Dial = cs.dial()
1038 resolvers[i] = &cs
1039 }
1040
1041 var wg sync.WaitGroup
1042 wg.Add(len(resolvers))
1043 for i, resolver := range resolvers {
1044 go func(r *Resolver, index int) {
1045 defer wg.Done()
1046 _, err := r.LookupIPAddr(context.Background(), "google.com")
1047 if err != nil {
1048 t.Errorf("lookup failed for resolver %d: %q", index, err)
1049 }
1050 }(resolver.Resolver, i)
1051 }
1052 wg.Wait()
1053
1054 if t.Failed() {
1055 t.FailNow()
1056 }
1057
1058 for i, resolver := range resolvers {
1059 if !resolver.dialed {
1060 t.Errorf("custom resolver %d not dialed during lookup", i)
1061 }
1062 }
1063 }
1064
1065 var ipVersionTests = []struct {
1066 network string
1067 version byte
1068 }{
1069 {"tcp", 0},
1070 {"tcp4", '4'},
1071 {"tcp6", '6'},
1072 {"udp", 0},
1073 {"udp4", '4'},
1074 {"udp6", '6'},
1075 {"ip", 0},
1076 {"ip4", '4'},
1077 {"ip6", '6'},
1078 {"ip7", 0},
1079 {"", 0},
1080 }
1081
1082 func TestIPVersion(t *testing.T) {
1083 for _, tt := range ipVersionTests {
1084 if version := ipVersion(tt.network); version != tt.version {
1085 t.Errorf("Family for: %s. Expected: %s, Got: %s", tt.network,
1086 string(tt.version), string(version))
1087 }
1088 }
1089 }
1090
1091
1092
1093 func TestLookupIPAddrPreservesContextValues(t *testing.T) {
1094 origTestHookLookupIP := testHookLookupIP
1095 defer func() { testHookLookupIP = origTestHookLookupIP }()
1096
1097 keyValues := []struct {
1098 key, value any
1099 }{
1100 {"key-1", 12},
1101 {384, "value2"},
1102 {new(float64), 137},
1103 }
1104 ctx := context.Background()
1105 for _, kv := range keyValues {
1106 ctx = context.WithValue(ctx, kv.key, kv.value)
1107 }
1108
1109 wantIPs := []IPAddr{
1110 {IP: IPv4(127, 0, 0, 1)},
1111 {IP: IPv6loopback},
1112 }
1113
1114 checkCtxValues := func(ctx_ context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
1115 for _, kv := range keyValues {
1116 g, w := ctx_.Value(kv.key), kv.value
1117 if !reflect.DeepEqual(g, w) {
1118 t.Errorf("Value lookup:\n\tGot: %v\n\tWant: %v", g, w)
1119 }
1120 }
1121 return wantIPs, nil
1122 }
1123 testHookLookupIP = checkCtxValues
1124
1125 resolvers := []*Resolver{
1126 nil,
1127 new(Resolver),
1128 }
1129
1130 for i, resolver := range resolvers {
1131 gotIPs, err := resolver.LookupIPAddr(ctx, "golang.org")
1132 if err != nil {
1133 t.Errorf("Resolver #%d: unexpected error: %v", i, err)
1134 }
1135 if !reflect.DeepEqual(gotIPs, wantIPs) {
1136 t.Errorf("#%d: mismatched IPAddr results\n\tGot: %v\n\tWant: %v", i, gotIPs, wantIPs)
1137 }
1138 }
1139 }
1140
1141
1142 func TestLookupIPAddrConcurrentCallsForNetworks(t *testing.T) {
1143 origTestHookLookupIP := testHookLookupIP
1144 defer func() { testHookLookupIP = origTestHookLookupIP }()
1145
1146 queries := [][]string{
1147 {"udp", "golang.org"},
1148 {"udp4", "golang.org"},
1149 {"udp6", "golang.org"},
1150 {"udp", "golang.org"},
1151 {"udp", "golang.org"},
1152 }
1153 results := map[[2]string][]IPAddr{
1154 {"udp", "golang.org"}: {
1155 {IP: IPv4(127, 0, 0, 1)},
1156 {IP: IPv6loopback},
1157 },
1158 {"udp4", "golang.org"}: {
1159 {IP: IPv4(127, 0, 0, 1)},
1160 },
1161 {"udp6", "golang.org"}: {
1162 {IP: IPv6loopback},
1163 },
1164 }
1165 calls := int32(0)
1166 waitCh := make(chan struct{})
1167 testHookLookupIP = func(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
1168
1169
1170
1171 if atomic.AddInt32(&calls, 1) == int32(len(results)) {
1172 close(waitCh)
1173 }
1174 select {
1175 case <-waitCh:
1176 case <-ctx.Done():
1177 return nil, ctx.Err()
1178 }
1179 return results[[2]string{network, host}], nil
1180 }
1181
1182 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
1183 defer cancel()
1184 wg := sync.WaitGroup{}
1185 for _, q := range queries {
1186 network := q[0]
1187 host := q[1]
1188 wg.Add(1)
1189 go func() {
1190 defer wg.Done()
1191 gotIPs, err := DefaultResolver.lookupIPAddr(ctx, network, host)
1192 if err != nil {
1193 t.Errorf("lookupIPAddr(%v, %v): unexpected error: %v", network, host, err)
1194 }
1195 wantIPs := results[[2]string{network, host}]
1196 if !reflect.DeepEqual(gotIPs, wantIPs) {
1197 t.Errorf("lookupIPAddr(%v, %v): mismatched IPAddr results\n\tGot: %v\n\tWant: %v", network, host, gotIPs, wantIPs)
1198 }
1199 }()
1200 }
1201 wg.Wait()
1202 }
1203
1204
1205 func TestResolverLookupIPWithEmptyHost(t *testing.T) {
1206 _, err := DefaultResolver.LookupIP(context.Background(), "ip", "")
1207 if err == nil {
1208 t.Fatal("DefaultResolver.LookupIP for empty host success, want no host error")
1209 }
1210 if !strings.HasSuffix(err.Error(), errNoSuchHost.Error()) {
1211 t.Fatalf("lookup error = %v, want %v", err, errNoSuchHost)
1212 }
1213 }
1214
1215 func TestWithUnexpiredValuesPreserved(t *testing.T) {
1216 ctx, cancel := context.WithCancel(context.Background())
1217
1218
1219 key, value := "key-1", 2
1220 ctx = context.WithValue(ctx, key, value)
1221
1222
1223
1224 ctx = withUnexpiredValuesPreserved(ctx)
1225
1226
1227 if g, w := ctx.Value(key), value; g != w {
1228 t.Errorf("Lookup before expiry: Got %v Want %v", g, w)
1229 }
1230
1231
1232 cancel()
1233
1234
1235 if g := ctx.Value(key); g != nil {
1236 t.Errorf("Lookup after expiry: Got %v want nil", g)
1237 }
1238 }
1239
1240
1241 func TestLookupNullByte(t *testing.T) {
1242 testenv.MustHaveExternalNetwork(t)
1243 testenv.SkipFlakyNet(t)
1244 LookupHost("foo\x00bar")
1245 }
1246
1247 func TestResolverLookupIP(t *testing.T) {
1248 testenv.MustHaveExternalNetwork(t)
1249
1250 v4Ok := supportsIPv4() && *testIPv4
1251 v6Ok := supportsIPv6() && *testIPv6
1252
1253 defer dnsWaitGroup.Wait()
1254
1255 for _, impl := range []struct {
1256 name string
1257 fn func() func()
1258 }{
1259 {"go", forceGoDNS},
1260 {"cgo", forceCgoDNS},
1261 } {
1262 t.Run("implementation: "+impl.name, func(t *testing.T) {
1263 fixup := impl.fn()
1264 if fixup == nil {
1265 t.Skip("not supported")
1266 }
1267 defer fixup()
1268
1269 for _, network := range []string{"ip", "ip4", "ip6"} {
1270 t.Run("network: "+network, func(t *testing.T) {
1271 switch {
1272 case network == "ip4" && !v4Ok:
1273 t.Skip("IPv4 is not supported")
1274 case network == "ip6" && !v6Ok:
1275 t.Skip("IPv6 is not supported")
1276 }
1277
1278
1279 const host = "google.com"
1280 ips, err := DefaultResolver.LookupIP(context.Background(), network, host)
1281 if err != nil {
1282 testenv.SkipFlakyNet(t)
1283 t.Fatalf("DefaultResolver.LookupIP(%q, %q): failed with unexpected error: %v", network, host, err)
1284 }
1285
1286 var v4Addrs []netip.Addr
1287 var v6Addrs []netip.Addr
1288 for _, ip := range ips {
1289 if addr, ok := netip.AddrFromSlice(ip); ok {
1290 if addr.Is4() {
1291 v4Addrs = append(v4Addrs, addr)
1292 } else {
1293 v6Addrs = append(v6Addrs, addr)
1294 }
1295 } else {
1296 t.Fatalf("IP=%q is neither IPv4 nor IPv6", ip)
1297 }
1298 }
1299
1300
1301 if network == "ip4" || network == "ip" && v4Ok {
1302 if len(v4Addrs) == 0 {
1303 t.Errorf("DefaultResolver.LookupIP(%q, %q): no IPv4 addresses", network, host)
1304 }
1305 }
1306 if network == "ip6" || network == "ip" && v6Ok {
1307 if len(v6Addrs) == 0 {
1308 t.Errorf("DefaultResolver.LookupIP(%q, %q): no IPv6 addresses", network, host)
1309 }
1310 }
1311
1312
1313 if network == "ip6" && len(v4Addrs) > 0 {
1314 t.Errorf("DefaultResolver.LookupIP(%q, %q): unexpected IPv4 addresses: %v", network, host, v4Addrs)
1315 }
1316 if network == "ip4" && len(v6Addrs) > 0 {
1317 t.Errorf("DefaultResolver.LookupIP(%q, %q): unexpected IPv6 or IPv4-mapped IPv6 addresses: %v", network, host, v6Addrs)
1318 }
1319 })
1320 }
1321 })
1322 }
1323 }
1324
1325
1326 func TestDNSTimeout(t *testing.T) {
1327 origTestHookLookupIP := testHookLookupIP
1328 defer func() { testHookLookupIP = origTestHookLookupIP }()
1329 defer dnsWaitGroup.Wait()
1330
1331 timeoutHookGo := make(chan bool, 1)
1332 timeoutHook := func(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
1333 <-timeoutHookGo
1334 return nil, context.DeadlineExceeded
1335 }
1336 testHookLookupIP = timeoutHook
1337
1338 checkErr := func(err error) {
1339 t.Helper()
1340 if err == nil {
1341 t.Error("expected an error")
1342 } else if dnserr, ok := err.(*DNSError); !ok {
1343 t.Errorf("got error type %T, want %T", err, (*DNSError)(nil))
1344 } else if !dnserr.IsTimeout {
1345 t.Errorf("got error %#v, want IsTimeout == true", dnserr)
1346 } else if isTimeout := dnserr.Timeout(); !isTimeout {
1347 t.Errorf("got err.Timeout() == %t, want true", isTimeout)
1348 }
1349 }
1350
1351
1352 timeoutHookGo <- true
1353 _, err := LookupIP("golang.org")
1354 checkErr(err)
1355
1356
1357 var err1, err2 error
1358 var wg sync.WaitGroup
1359 wg.Add(2)
1360 go func() {
1361 defer wg.Done()
1362 _, err1 = LookupIP("golang1.org")
1363 }()
1364 go func() {
1365 defer wg.Done()
1366 _, err2 = LookupIP("golang1.org")
1367 }()
1368 close(timeoutHookGo)
1369 wg.Wait()
1370 checkErr(err1)
1371 checkErr(err2)
1372
1373
1374 timeoutHookGo = make(chan bool)
1375 ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond)
1376 wg.Add(2)
1377 go func() {
1378 defer wg.Done()
1379 _, err1 = DefaultResolver.LookupIPAddr(ctx, "golang2.org")
1380 }()
1381 go func() {
1382 defer wg.Done()
1383 _, err2 = DefaultResolver.LookupIPAddr(ctx, "golang2.org")
1384 }()
1385 time.Sleep(10 * time.Nanosecond)
1386 close(timeoutHookGo)
1387 wg.Wait()
1388 checkErr(err1)
1389 checkErr(err2)
1390 cancel()
1391 }
1392
1393 func TestLookupNoData(t *testing.T) {
1394 if runtime.GOOS == "plan9" {
1395 t.Skip("not supported on plan9")
1396 }
1397
1398 mustHaveExternalNetwork(t)
1399
1400 testLookupNoData(t, "default resolver")
1401
1402 func() {
1403 defer forceGoDNS()()
1404 testLookupNoData(t, "forced go resolver")
1405 }()
1406
1407 func() {
1408 defer forceCgoDNS()()
1409 testLookupNoData(t, "forced cgo resolver")
1410 }()
1411 }
1412
1413 func testLookupNoData(t *testing.T, prefix string) {
1414 attempts := 0
1415 for {
1416
1417
1418 _, err := LookupHost("golang.rsc.io.")
1419 if err == nil {
1420 t.Errorf("%v: unexpected success", prefix)
1421 return
1422 }
1423
1424 var dnsErr *DNSError
1425 if errors.As(err, &dnsErr) {
1426 succeeded := true
1427 if !dnsErr.IsNotFound {
1428 succeeded = false
1429 t.Logf("%v: IsNotFound is set to false", prefix)
1430 }
1431
1432 if dnsErr.Err != errNoSuchHost.Error() {
1433 succeeded = false
1434 t.Logf("%v: error message is not equal to: %v", prefix, errNoSuchHost.Error())
1435 }
1436
1437 if succeeded {
1438 return
1439 }
1440 }
1441
1442 testenv.SkipFlakyNet(t)
1443 if attempts < len(backoffDuration) {
1444 dur := backoffDuration[attempts]
1445 t.Logf("%v: backoff %v after failure %v\n", prefix, dur, err)
1446 time.Sleep(dur)
1447 attempts++
1448 continue
1449 }
1450
1451 t.Errorf("%v: unexpected error: %v", prefix, err)
1452 return
1453 }
1454 }
1455
1456 func TestLookupPortNotFound(t *testing.T) {
1457 allResolvers(t, func(t *testing.T) {
1458 _, err := LookupPort("udp", "_-unknown-service-")
1459 var dnsErr *DNSError
1460 if !errors.As(err, &dnsErr) || !dnsErr.IsNotFound {
1461 t.Fatalf("unexpected error: %v", err)
1462 }
1463 })
1464 }
1465
1466
1467
1468 var tcpOnlyService = func() string {
1469
1470 if runtime.GOOS == "plan9" {
1471 return "https"
1472 }
1473 return "submissions"
1474 }()
1475
1476 func TestLookupPortDifferentNetwork(t *testing.T) {
1477 allResolvers(t, func(t *testing.T) {
1478 _, err := LookupPort("udp", tcpOnlyService)
1479 var dnsErr *DNSError
1480 if !errors.As(err, &dnsErr) || !dnsErr.IsNotFound {
1481 t.Fatalf("unexpected error: %v", err)
1482 }
1483 })
1484 }
1485
1486 func TestLookupPortEmptyNetworkString(t *testing.T) {
1487 allResolvers(t, func(t *testing.T) {
1488 _, err := LookupPort("", tcpOnlyService)
1489 if err != nil {
1490 t.Fatalf("unexpected error: %v", err)
1491 }
1492 })
1493 }
1494
1495 func TestLookupPortIPNetworkString(t *testing.T) {
1496 allResolvers(t, func(t *testing.T) {
1497 _, err := LookupPort("ip", tcpOnlyService)
1498 if err != nil {
1499 t.Fatalf("unexpected error: %v", err)
1500 }
1501 })
1502 }
1503
1504 func TestLookupNoSuchHost(t *testing.T) {
1505 mustHaveExternalNetwork(t)
1506
1507 const testNXDOMAIN = "invalid.invalid."
1508 const testNODATA = "_ldap._tcp.google.com."
1509
1510 tests := []struct {
1511 name string
1512 query func() error
1513 }{
1514 {
1515 name: "LookupCNAME NXDOMAIN",
1516 query: func() error {
1517 _, err := LookupCNAME(testNXDOMAIN)
1518 return err
1519 },
1520 },
1521 {
1522 name: "LookupHost NXDOMAIN",
1523 query: func() error {
1524 _, err := LookupHost(testNXDOMAIN)
1525 return err
1526 },
1527 },
1528 {
1529 name: "LookupHost NODATA",
1530 query: func() error {
1531 _, err := LookupHost(testNODATA)
1532 return err
1533 },
1534 },
1535 {
1536 name: "LookupMX NXDOMAIN",
1537 query: func() error {
1538 _, err := LookupMX(testNXDOMAIN)
1539 return err
1540 },
1541 },
1542 {
1543 name: "LookupMX NODATA",
1544 query: func() error {
1545 _, err := LookupMX(testNODATA)
1546 return err
1547 },
1548 },
1549 {
1550 name: "LookupNS NXDOMAIN",
1551 query: func() error {
1552 _, err := LookupNS(testNXDOMAIN)
1553 return err
1554 },
1555 },
1556 {
1557 name: "LookupNS NODATA",
1558 query: func() error {
1559 _, err := LookupNS(testNODATA)
1560 return err
1561 },
1562 },
1563 {
1564 name: "LookupSRV NXDOMAIN",
1565 query: func() error {
1566 _, _, err := LookupSRV("unknown", "tcp", testNXDOMAIN)
1567 return err
1568 },
1569 },
1570 {
1571 name: "LookupTXT NXDOMAIN",
1572 query: func() error {
1573 _, err := LookupTXT(testNXDOMAIN)
1574 return err
1575 },
1576 },
1577 {
1578 name: "LookupTXT NODATA",
1579 query: func() error {
1580 _, err := LookupTXT(testNODATA)
1581 return err
1582 },
1583 },
1584 }
1585
1586 for _, v := range tests {
1587 t.Run(v.name, func(t *testing.T) {
1588 allResolvers(t, func(t *testing.T) {
1589 attempts := 0
1590 for {
1591 err := v.query()
1592 if err == nil {
1593 t.Errorf("unexpected success")
1594 return
1595 }
1596 if dnsErr, ok := err.(*DNSError); ok {
1597 succeeded := true
1598 if !dnsErr.IsNotFound {
1599 succeeded = false
1600 t.Log("IsNotFound is set to false")
1601 }
1602 if dnsErr.Err != errNoSuchHost.Error() {
1603 succeeded = false
1604 t.Logf("error message is not equal to: %v", errNoSuchHost.Error())
1605 }
1606 if succeeded {
1607 return
1608 }
1609 }
1610 testenv.SkipFlakyNet(t)
1611 if attempts < len(backoffDuration) {
1612 dur := backoffDuration[attempts]
1613 t.Logf("backoff %v after failure %v\n", dur, err)
1614 time.Sleep(dur)
1615 attempts++
1616 continue
1617 }
1618 t.Errorf("unexpected error: %v", err)
1619 return
1620 }
1621 })
1622 })
1623 }
1624 }
1625
1626 func TestDNSErrorUnwrap(t *testing.T) {
1627 if runtime.GOOS == "plan9" {
1628
1629 t.Skip("skipping on plan9")
1630 }
1631 rDeadlineExcceeded := &Resolver{PreferGo: true, Dial: func(ctx context.Context, network, address string) (Conn, error) {
1632 return nil, context.DeadlineExceeded
1633 }}
1634 rCancelled := &Resolver{PreferGo: true, Dial: func(ctx context.Context, network, address string) (Conn, error) {
1635 return nil, context.Canceled
1636 }}
1637
1638 _, err := rDeadlineExcceeded.LookupHost(context.Background(), "test.go.dev")
1639 if !errors.Is(err, context.DeadlineExceeded) {
1640 t.Errorf("errors.Is(err, context.DeadlineExceeded) = false; want = true")
1641 }
1642
1643 _, err = rCancelled.LookupHost(context.Background(), "test.go.dev")
1644 if !errors.Is(err, context.Canceled) {
1645 t.Errorf("errors.Is(err, context.Canceled) = false; want = true")
1646 }
1647
1648 ctx, cancel := context.WithCancel(context.Background())
1649 cancel()
1650 _, err = goResolver.LookupHost(ctx, "text.go.dev")
1651 if !errors.Is(err, context.Canceled) {
1652 t.Errorf("errors.Is(err, context.Canceled) = false; want = true")
1653 }
1654 }
1655
View as plain text