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