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