Source file
src/net/lookup_test.go
Documentation: net
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
356 if !supportsIPv4() || !*testIPv4 {
357 t.Skip("IPv4 is required")
358 }
359
360 defer dnsWaitGroup.Wait()
361
362 attempts := 0
363 for i := 0; i < len(lookupCNAMETests); i++ {
364 tt := lookupCNAMETests[i]
365 cname, err := LookupCNAME(tt.name)
366 if err != nil {
367 testenv.SkipFlakyNet(t)
368 if attempts < len(backoffDuration) {
369 dur := backoffDuration[attempts]
370 t.Logf("backoff %v after failure %v\n", dur, err)
371 time.Sleep(dur)
372 attempts++
373 i--
374 continue
375 }
376 t.Fatal(err)
377 }
378 if !hasSuffixFold(cname, tt.cname) {
379 t.Errorf("got %s; want a record containing %s", cname, tt.cname)
380 }
381 }
382 }
383
384 var lookupGoogleHostTests = []struct {
385 name string
386 }{
387 {"google.com"},
388 {"google.com."},
389 }
390
391 func TestLookupGoogleHost(t *testing.T) {
392 mustHaveExternalNetwork(t)
393
394 if !supportsIPv4() || !*testIPv4 {
395 t.Skip("IPv4 is required")
396 }
397
398 defer dnsWaitGroup.Wait()
399
400 for _, tt := range lookupGoogleHostTests {
401 addrs, err := LookupHost(tt.name)
402 if err != nil {
403 t.Fatal(err)
404 }
405 if len(addrs) == 0 {
406 t.Error("got no record")
407 }
408 for _, addr := range addrs {
409 if ParseIP(addr) == nil {
410 t.Errorf("got %q; want a literal IP address", addr)
411 }
412 }
413 }
414 }
415
416 func TestLookupLongTXT(t *testing.T) {
417 testenv.SkipFlaky(t, 22857)
418 mustHaveExternalNetwork(t)
419
420 defer dnsWaitGroup.Wait()
421
422 txts, err := LookupTXT("golang.rsc.io")
423 if err != nil {
424 t.Fatal(err)
425 }
426 sort.Strings(txts)
427 want := []string{
428 strings.Repeat("abcdefghijklmnopqrstuvwxyABCDEFGHJIKLMNOPQRSTUVWXY", 10),
429 "gophers rule",
430 }
431 if !reflect.DeepEqual(txts, want) {
432 t.Fatalf("LookupTXT golang.rsc.io incorrect\nhave %q\nwant %q", txts, want)
433 }
434 }
435
436 var lookupGoogleIPTests = []struct {
437 name string
438 }{
439 {"google.com"},
440 {"google.com."},
441 }
442
443 func TestLookupGoogleIP(t *testing.T) {
444 mustHaveExternalNetwork(t)
445
446 if !supportsIPv4() || !*testIPv4 {
447 t.Skip("IPv4 is required")
448 }
449
450 defer dnsWaitGroup.Wait()
451
452 for _, tt := range lookupGoogleIPTests {
453 ips, err := LookupIP(tt.name)
454 if err != nil {
455 t.Fatal(err)
456 }
457 if len(ips) == 0 {
458 t.Error("got no record")
459 }
460 for _, ip := range ips {
461 if ip.To4() == nil && ip.To16() == nil {
462 t.Errorf("got %v; want an IP address", ip)
463 }
464 }
465 }
466 }
467
468 var revAddrTests = []struct {
469 Addr string
470 Reverse string
471 ErrPrefix string
472 }{
473 {"1.2.3.4", "4.3.2.1.in-addr.arpa.", ""},
474 {"245.110.36.114", "114.36.110.245.in-addr.arpa.", ""},
475 {"::ffff:12.34.56.78", "78.56.34.12.in-addr.arpa.", ""},
476 {"::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.", ""},
477 {"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.", ""},
478 {"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.", ""},
479 {"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.", ""},
480 {"1.2.3", "", "unrecognized address"},
481 {"1.2.3.4.5", "", "unrecognized address"},
482 {"1234:567:bcbca::89a:bcde", "", "unrecognized address"},
483 {"1234:567::bcbc:adad::89a:bcde", "", "unrecognized address"},
484 }
485
486 func TestReverseAddress(t *testing.T) {
487 defer dnsWaitGroup.Wait()
488 for i, tt := range revAddrTests {
489 a, err := reverseaddr(tt.Addr)
490 if len(tt.ErrPrefix) > 0 && err == nil {
491 t.Errorf("#%d: expected %q, got <nil> (error)", i, tt.ErrPrefix)
492 continue
493 }
494 if len(tt.ErrPrefix) == 0 && err != nil {
495 t.Errorf("#%d: expected <nil>, got %q (error)", i, err)
496 }
497 if err != nil && err.(*DNSError).Err != tt.ErrPrefix {
498 t.Errorf("#%d: expected %q, got %q (mismatched error)", i, tt.ErrPrefix, err.(*DNSError).Err)
499 }
500 if a != tt.Reverse {
501 t.Errorf("#%d: expected %q, got %q (reverse address)", i, tt.Reverse, a)
502 }
503 }
504 }
505
506 func TestDNSFlood(t *testing.T) {
507 if !*testDNSFlood {
508 t.Skip("test disabled; use -dnsflood to enable")
509 }
510
511 defer dnsWaitGroup.Wait()
512
513 var N = 5000
514 if runtime.GOOS == "darwin" {
515
516
517
518
519
520
521
522 N = 500
523 }
524
525 const timeout = 3 * time.Second
526 ctxHalfTimeout, cancel := context.WithTimeout(context.Background(), timeout/2)
527 defer cancel()
528 ctxTimeout, cancel := context.WithTimeout(context.Background(), timeout)
529 defer cancel()
530
531 c := make(chan error, 2*N)
532 for i := 0; i < N; i++ {
533 name := fmt.Sprintf("%d.net-test.golang.org", i)
534 go func() {
535 _, err := DefaultResolver.LookupIPAddr(ctxHalfTimeout, name)
536 c <- err
537 }()
538 go func() {
539 _, err := DefaultResolver.LookupIPAddr(ctxTimeout, name)
540 c <- err
541 }()
542 }
543 qstats := struct {
544 succeeded, failed int
545 timeout, temporary, other int
546 unknown int
547 }{}
548 deadline := time.After(timeout + time.Second)
549 for i := 0; i < 2*N; i++ {
550 select {
551 case <-deadline:
552 t.Fatal("deadline exceeded")
553 case err := <-c:
554 switch err := err.(type) {
555 case nil:
556 qstats.succeeded++
557 case Error:
558 qstats.failed++
559 if err.Timeout() {
560 qstats.timeout++
561 }
562 if err.Temporary() {
563 qstats.temporary++
564 }
565 if !err.Timeout() && !err.Temporary() {
566 qstats.other++
567 }
568 default:
569 qstats.failed++
570 qstats.unknown++
571 }
572 }
573 }
574
575
576
577
578
579
580
581
582 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)
583 }
584
585 func TestLookupDotsWithLocalSource(t *testing.T) {
586 if !supportsIPv4() || !*testIPv4 {
587 t.Skip("IPv4 is required")
588 }
589
590 mustHaveExternalNetwork(t)
591
592 defer dnsWaitGroup.Wait()
593
594 for i, fn := range []func() func(){forceGoDNS, forceCgoDNS} {
595 fixup := fn()
596 if fixup == nil {
597 continue
598 }
599 names, err := LookupAddr("127.0.0.1")
600 fixup()
601 if err != nil {
602 t.Logf("#%d: %v", i, err)
603 continue
604 }
605 mode := "netgo"
606 if i == 1 {
607 mode = "netcgo"
608 }
609 loop:
610 for i, name := range names {
611 if strings.Index(name, ".") == len(name)-1 {
612 for j := range names {
613 if j == i {
614 continue
615 }
616 if names[j] == name[:len(name)-1] {
617
618
619 continue loop
620 }
621 }
622 t.Errorf("%s: got %s; want %s", mode, name, name[:len(name)-1])
623 } else if strings.Contains(name, ".") && !strings.HasSuffix(name, ".") {
624 t.Errorf("%s: got %s; want name ending with trailing dot", mode, name)
625 }
626 }
627 }
628 }
629
630 func TestLookupDotsWithRemoteSource(t *testing.T) {
631 if runtime.GOOS == "darwin" {
632 testenv.SkipFlaky(t, 27992)
633 }
634 mustHaveExternalNetwork(t)
635
636 if !supportsIPv4() || !*testIPv4 {
637 t.Skip("IPv4 is required")
638 }
639
640 if iOS() {
641 t.Skip("no resolv.conf on iOS")
642 }
643
644 defer dnsWaitGroup.Wait()
645
646 if fixup := forceGoDNS(); fixup != nil {
647 testDots(t, "go")
648 fixup()
649 }
650 if fixup := forceCgoDNS(); fixup != nil {
651 testDots(t, "cgo")
652 fixup()
653 }
654 }
655
656 func testDots(t *testing.T, mode string) {
657 names, err := LookupAddr("8.8.8.8")
658 if err != nil {
659 testenv.SkipFlakyNet(t)
660 t.Errorf("LookupAddr(8.8.8.8): %v (mode=%v)", err, mode)
661 } else {
662 for _, name := range names {
663 if !hasSuffixFold(name, ".google.com.") && !hasSuffixFold(name, ".google.") {
664 t.Errorf("LookupAddr(8.8.8.8) = %v, want names ending in .google.com or .google with trailing dot (mode=%v)", names, mode)
665 break
666 }
667 }
668 }
669
670 cname, err := LookupCNAME("www.mit.edu")
671 if err != nil {
672 testenv.SkipFlakyNet(t)
673 t.Errorf("LookupCNAME(www.mit.edu, mode=%v): %v", mode, err)
674 } else if !strings.HasSuffix(cname, ".") {
675 t.Errorf("LookupCNAME(www.mit.edu) = %v, want cname ending in . with trailing dot (mode=%v)", cname, mode)
676 }
677
678 mxs, err := LookupMX("google.com")
679 if err != nil {
680 testenv.SkipFlakyNet(t)
681 t.Errorf("LookupMX(google.com): %v (mode=%v)", err, mode)
682 } else {
683 for _, mx := range mxs {
684 if !hasSuffixFold(mx.Host, ".google.com.") {
685 t.Errorf("LookupMX(google.com) = %v, want names ending in .google.com. with trailing dot (mode=%v)", mxString(mxs), mode)
686 break
687 }
688 }
689 }
690
691 nss, err := LookupNS("google.com")
692 if err != nil {
693 testenv.SkipFlakyNet(t)
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 testenv.SkipFlakyNet(t)
707 t.Errorf("LookupSRV(xmpp-server, tcp, google.com): %v (mode=%v)", err, mode)
708 } else {
709 if !hasSuffixFold(cname, ".google.com.") {
710 t.Errorf("LookupSRV(xmpp-server, tcp, google.com) returned cname=%v, want name ending in .google.com. with trailing dot (mode=%v)", cname, mode)
711 }
712 for _, srv := range srvs {
713 if !hasSuffixFold(srv.Target, ".google.com.") {
714 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)
715 break
716 }
717 }
718 }
719 }
720
721 func mxString(mxs []*MX) string {
722 var buf bytes.Buffer
723 sep := ""
724 fmt.Fprintf(&buf, "[")
725 for _, mx := range mxs {
726 fmt.Fprintf(&buf, "%s%s:%d", sep, mx.Host, mx.Pref)
727 sep = " "
728 }
729 fmt.Fprintf(&buf, "]")
730 return buf.String()
731 }
732
733 func nsString(nss []*NS) string {
734 var buf bytes.Buffer
735 sep := ""
736 fmt.Fprintf(&buf, "[")
737 for _, ns := range nss {
738 fmt.Fprintf(&buf, "%s%s", sep, ns.Host)
739 sep = " "
740 }
741 fmt.Fprintf(&buf, "]")
742 return buf.String()
743 }
744
745 func srvString(srvs []*SRV) string {
746 var buf bytes.Buffer
747 sep := ""
748 fmt.Fprintf(&buf, "[")
749 for _, srv := range srvs {
750 fmt.Fprintf(&buf, "%s%s:%d:%d:%d", sep, srv.Target, srv.Port, srv.Priority, srv.Weight)
751 sep = " "
752 }
753 fmt.Fprintf(&buf, "]")
754 return buf.String()
755 }
756
757 func TestLookupPort(t *testing.T) {
758
759
760
761
762
763 type test struct {
764 network string
765 name string
766 port int
767 ok bool
768 }
769 var tests = []test{
770 {"tcp", "0", 0, true},
771 {"udp", "0", 0, true},
772 {"udp", "domain", 53, true},
773
774 {"--badnet--", "zzz", 0, false},
775 {"tcp", "--badport--", 0, false},
776 {"tcp", "-1", 0, false},
777 {"tcp", "65536", 0, false},
778 {"udp", "-1", 0, false},
779 {"udp", "65536", 0, false},
780 {"tcp", "123456789", 0, false},
781
782
783 {"tcp", "", 0, true},
784 {"tcp4", "", 0, true},
785 {"tcp6", "", 0, true},
786 {"udp", "", 0, true},
787 {"udp4", "", 0, true},
788 {"udp6", "", 0, true},
789 }
790
791 switch runtime.GOOS {
792 case "android":
793 if netGo {
794 t.Skipf("not supported on %s without cgo; see golang.org/issues/14576", runtime.GOOS)
795 }
796 default:
797 tests = append(tests, test{"tcp", "http", 80, true})
798 }
799
800 for _, tt := range tests {
801 port, err := LookupPort(tt.network, tt.name)
802 if port != tt.port || (err == nil) != tt.ok {
803 t.Errorf("LookupPort(%q, %q) = %d, %v; want %d, error=%t", tt.network, tt.name, port, err, tt.port, !tt.ok)
804 }
805 if err != nil {
806 if perr := parseLookupPortError(err); perr != nil {
807 t.Error(perr)
808 }
809 }
810 }
811 }
812
813
814
815 func TestLookupPort_Minimal(t *testing.T) {
816 type test struct {
817 network string
818 name string
819 port int
820 }
821 var tests = []test{
822 {"tcp", "http", 80},
823 {"tcp", "HTTP", 80},
824 {"tcp", "https", 443},
825 {"tcp", "ssh", 22},
826 {"tcp", "gopher", 70},
827 {"tcp4", "http", 80},
828 {"tcp6", "http", 80},
829 }
830
831 for _, tt := range tests {
832 port, err := LookupPort(tt.network, tt.name)
833 if port != tt.port || err != nil {
834 t.Errorf("LookupPort(%q, %q) = %d, %v; want %d, error=nil", tt.network, tt.name, port, err, tt.port)
835 }
836 }
837 }
838
839 func TestLookupProtocol_Minimal(t *testing.T) {
840 type test struct {
841 name string
842 want int
843 }
844 var tests = []test{
845 {"tcp", 6},
846 {"TcP", 6},
847 {"icmp", 1},
848 {"igmp", 2},
849 {"udp", 17},
850 {"ipv6-icmp", 58},
851 }
852
853 for _, tt := range tests {
854 got, err := lookupProtocol(context.Background(), tt.name)
855 if got != tt.want || err != nil {
856 t.Errorf("LookupProtocol(%q) = %d, %v; want %d, error=nil", tt.name, got, err, tt.want)
857 }
858 }
859
860 }
861
862 func TestLookupNonLDH(t *testing.T) {
863 defer dnsWaitGroup.Wait()
864
865 if fixup := forceGoDNS(); fixup != nil {
866 defer fixup()
867 }
868
869
870
871
872
873 addrs, err := LookupHost("!!!.###.bogus..domain.")
874 if err == nil {
875 t.Fatalf("lookup succeeded: %v", addrs)
876 }
877 if !strings.HasSuffix(err.Error(), errNoSuchHost.Error()) {
878 t.Fatalf("lookup error = %v, want %v", err, errNoSuchHost)
879 }
880 if !err.(*DNSError).IsNotFound {
881 t.Fatalf("lookup error = %v, want true", err.(*DNSError).IsNotFound)
882 }
883 }
884
885 func TestLookupContextCancel(t *testing.T) {
886 mustHaveExternalNetwork(t)
887 defer dnsWaitGroup.Wait()
888
889 ctx, ctxCancel := context.WithCancel(context.Background())
890 ctxCancel()
891 _, err := DefaultResolver.LookupIPAddr(ctx, "google.com")
892 if err != errCanceled {
893 testenv.SkipFlakyNet(t)
894 t.Fatal(err)
895 }
896 ctx = context.Background()
897 _, err = DefaultResolver.LookupIPAddr(ctx, "google.com")
898 if err != nil {
899 testenv.SkipFlakyNet(t)
900 t.Fatal(err)
901 }
902 }
903
904
905
906 func TestNilResolverLookup(t *testing.T) {
907 mustHaveExternalNetwork(t)
908 var r *Resolver = nil
909 ctx := context.Background()
910
911
912 r.LookupAddr(ctx, "8.8.8.8")
913 r.LookupCNAME(ctx, "google.com")
914 r.LookupHost(ctx, "google.com")
915 r.LookupIPAddr(ctx, "google.com")
916 r.LookupIP(ctx, "ip", "google.com")
917 r.LookupMX(ctx, "gmail.com")
918 r.LookupNS(ctx, "google.com")
919 r.LookupPort(ctx, "tcp", "smtp")
920 r.LookupSRV(ctx, "service", "proto", "name")
921 r.LookupTXT(ctx, "gmail.com")
922 }
923
924
925
926 func TestLookupHostCancel(t *testing.T) {
927 mustHaveExternalNetwork(t)
928 const (
929 google = "www.google.com"
930 invalidDomain = "invalid.invalid"
931 n = 600
932 )
933
934 _, err := LookupHost(google)
935 if err != nil {
936 t.Fatal(err)
937 }
938
939 ctx, cancel := context.WithCancel(context.Background())
940 cancel()
941 for i := 0; i < n; i++ {
942 addr, err := DefaultResolver.LookupHost(ctx, invalidDomain)
943 if err == nil {
944 t.Fatalf("LookupHost(%q): returns %v, but should fail", invalidDomain, addr)
945 }
946 if !strings.Contains(err.Error(), "canceled") {
947 t.Fatalf("LookupHost(%q): failed with unexpected error: %v", invalidDomain, err)
948 }
949 time.Sleep(time.Millisecond * 1)
950 }
951
952 _, err = LookupHost(google)
953 if err != nil {
954 t.Fatal(err)
955 }
956 }
957
958 type lookupCustomResolver struct {
959 *Resolver
960 mu sync.RWMutex
961 dialed bool
962 }
963
964 func (lcr *lookupCustomResolver) dial() func(ctx context.Context, network, address string) (Conn, error) {
965 return func(ctx context.Context, network, address string) (Conn, error) {
966 lcr.mu.Lock()
967 lcr.dialed = true
968 lcr.mu.Unlock()
969 return Dial(network, address)
970 }
971 }
972
973
974
975 func TestConcurrentPreferGoResolversDial(t *testing.T) {
976
977
978 switch runtime.GOOS {
979 case "windows", "plan9":
980 t.Skipf("skip on %v", runtime.GOOS)
981 }
982
983 testenv.MustHaveExternalNetwork(t)
984 testenv.SkipFlakyNet(t)
985
986 defer dnsWaitGroup.Wait()
987
988 resolvers := make([]*lookupCustomResolver, 2)
989 for i := range resolvers {
990 cs := lookupCustomResolver{Resolver: &Resolver{PreferGo: true}}
991 cs.Dial = cs.dial()
992 resolvers[i] = &cs
993 }
994
995 var wg sync.WaitGroup
996 wg.Add(len(resolvers))
997 for i, resolver := range resolvers {
998 go func(r *Resolver, index int) {
999 defer wg.Done()
1000 _, err := r.LookupIPAddr(context.Background(), "google.com")
1001 if err != nil {
1002 t.Errorf("lookup failed for resolver %d: %q", index, err)
1003 }
1004 }(resolver.Resolver, i)
1005 }
1006 wg.Wait()
1007
1008 if t.Failed() {
1009 t.FailNow()
1010 }
1011
1012 for i, resolver := range resolvers {
1013 if !resolver.dialed {
1014 t.Errorf("custom resolver %d not dialed during lookup", i)
1015 }
1016 }
1017 }
1018
1019 var ipVersionTests = []struct {
1020 network string
1021 version byte
1022 }{
1023 {"tcp", 0},
1024 {"tcp4", '4'},
1025 {"tcp6", '6'},
1026 {"udp", 0},
1027 {"udp4", '4'},
1028 {"udp6", '6'},
1029 {"ip", 0},
1030 {"ip4", '4'},
1031 {"ip6", '6'},
1032 {"ip7", 0},
1033 {"", 0},
1034 }
1035
1036 func TestIPVersion(t *testing.T) {
1037 for _, tt := range ipVersionTests {
1038 if version := ipVersion(tt.network); version != tt.version {
1039 t.Errorf("Family for: %s. Expected: %s, Got: %s", tt.network,
1040 string(tt.version), string(version))
1041 }
1042 }
1043 }
1044
1045
1046
1047 func TestLookupIPAddrPreservesContextValues(t *testing.T) {
1048 origTestHookLookupIP := testHookLookupIP
1049 defer func() { testHookLookupIP = origTestHookLookupIP }()
1050
1051 keyValues := []struct {
1052 key, value interface{}
1053 }{
1054 {"key-1", 12},
1055 {384, "value2"},
1056 {new(float64), 137},
1057 }
1058 ctx := context.Background()
1059 for _, kv := range keyValues {
1060 ctx = context.WithValue(ctx, kv.key, kv.value)
1061 }
1062
1063 wantIPs := []IPAddr{
1064 {IP: IPv4(127, 0, 0, 1)},
1065 {IP: IPv6loopback},
1066 }
1067
1068 checkCtxValues := func(ctx_ context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
1069 for _, kv := range keyValues {
1070 g, w := ctx_.Value(kv.key), kv.value
1071 if !reflect.DeepEqual(g, w) {
1072 t.Errorf("Value lookup:\n\tGot: %v\n\tWant: %v", g, w)
1073 }
1074 }
1075 return wantIPs, nil
1076 }
1077 testHookLookupIP = checkCtxValues
1078
1079 resolvers := []*Resolver{
1080 nil,
1081 new(Resolver),
1082 }
1083
1084 for i, resolver := range resolvers {
1085 gotIPs, err := resolver.LookupIPAddr(ctx, "golang.org")
1086 if err != nil {
1087 t.Errorf("Resolver #%d: unexpected error: %v", i, err)
1088 }
1089 if !reflect.DeepEqual(gotIPs, wantIPs) {
1090 t.Errorf("#%d: mismatched IPAddr results\n\tGot: %v\n\tWant: %v", i, gotIPs, wantIPs)
1091 }
1092 }
1093 }
1094
1095
1096 func TestLookupIPAddrConcurrentCallsForNetworks(t *testing.T) {
1097 origTestHookLookupIP := testHookLookupIP
1098 defer func() { testHookLookupIP = origTestHookLookupIP }()
1099
1100 queries := [][]string{
1101 {"udp", "golang.org"},
1102 {"udp4", "golang.org"},
1103 {"udp6", "golang.org"},
1104 {"udp", "golang.org"},
1105 {"udp", "golang.org"},
1106 }
1107 results := map[[2]string][]IPAddr{
1108 {"udp", "golang.org"}: {
1109 {IP: IPv4(127, 0, 0, 1)},
1110 {IP: IPv6loopback},
1111 },
1112 {"udp4", "golang.org"}: {
1113 {IP: IPv4(127, 0, 0, 1)},
1114 },
1115 {"udp6", "golang.org"}: {
1116 {IP: IPv6loopback},
1117 },
1118 }
1119 calls := int32(0)
1120 waitCh := make(chan struct{})
1121 testHookLookupIP = func(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
1122
1123
1124
1125 if atomic.AddInt32(&calls, 1) == int32(len(results)) {
1126 close(waitCh)
1127 }
1128 select {
1129 case <-waitCh:
1130 case <-ctx.Done():
1131 return nil, ctx.Err()
1132 }
1133 return results[[2]string{network, host}], nil
1134 }
1135
1136 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
1137 defer cancel()
1138 wg := sync.WaitGroup{}
1139 for _, q := range queries {
1140 network := q[0]
1141 host := q[1]
1142 wg.Add(1)
1143 go func() {
1144 defer wg.Done()
1145 gotIPs, err := DefaultResolver.lookupIPAddr(ctx, network, host)
1146 if err != nil {
1147 t.Errorf("lookupIPAddr(%v, %v): unexpected error: %v", network, host, err)
1148 }
1149 wantIPs := results[[2]string{network, host}]
1150 if !reflect.DeepEqual(gotIPs, wantIPs) {
1151 t.Errorf("lookupIPAddr(%v, %v): mismatched IPAddr results\n\tGot: %v\n\tWant: %v", network, host, gotIPs, wantIPs)
1152 }
1153 }()
1154 }
1155 wg.Wait()
1156 }
1157
1158 func TestWithUnexpiredValuesPreserved(t *testing.T) {
1159 ctx, cancel := context.WithCancel(context.Background())
1160
1161
1162 key, value := "key-1", 2
1163 ctx = context.WithValue(ctx, key, value)
1164
1165
1166
1167 ctx = withUnexpiredValuesPreserved(ctx)
1168
1169
1170 if g, w := ctx.Value(key), value; g != w {
1171 t.Errorf("Lookup before expiry: Got %v Want %v", g, w)
1172 }
1173
1174
1175 cancel()
1176
1177
1178 if g := ctx.Value(key); g != nil {
1179 t.Errorf("Lookup after expiry: Got %v want nil", g)
1180 }
1181 }
1182
1183
1184 func TestLookupNullByte(t *testing.T) {
1185 testenv.MustHaveExternalNetwork(t)
1186 testenv.SkipFlakyNet(t)
1187 LookupHost("foo\x00bar")
1188 }
1189
1190 func TestResolverLookupIP(t *testing.T) {
1191 testenv.MustHaveExternalNetwork(t)
1192
1193 v4Ok := supportsIPv4() && *testIPv4
1194 v6Ok := supportsIPv6() && *testIPv6
1195
1196 defer dnsWaitGroup.Wait()
1197
1198 for _, impl := range []struct {
1199 name string
1200 fn func() func()
1201 }{
1202 {"go", forceGoDNS},
1203 {"cgo", forceCgoDNS},
1204 } {
1205 t.Run("implementation: "+impl.name, func(t *testing.T) {
1206 fixup := impl.fn()
1207 if fixup == nil {
1208 t.Skip("not supported")
1209 }
1210 defer fixup()
1211
1212 for _, network := range []string{"ip", "ip4", "ip6"} {
1213 t.Run("network: "+network, func(t *testing.T) {
1214 switch {
1215 case network == "ip4" && !v4Ok:
1216 t.Skip("IPv4 is not supported")
1217 case network == "ip6" && !v6Ok:
1218 t.Skip("IPv6 is not supported")
1219 }
1220
1221
1222 const host = "google.com"
1223 ips, err := DefaultResolver.LookupIP(context.Background(), network, host)
1224 if err != nil {
1225 testenv.SkipFlakyNet(t)
1226 t.Fatalf("DefaultResolver.LookupIP(%q, %q): failed with unexpected error: %v", network, host, err)
1227 }
1228
1229 var v4Addrs []IP
1230 var v6Addrs []IP
1231 for _, ip := range ips {
1232 switch {
1233 case ip.To4() != nil:
1234
1235
1236
1237 v4Addrs = append(v4Addrs, ip)
1238 case ip.To16() != nil:
1239 v6Addrs = append(v6Addrs, ip)
1240 default:
1241 t.Fatalf("IP=%q is neither IPv4 nor IPv6", ip)
1242 }
1243 }
1244
1245
1246 if network == "ip4" || network == "ip" && v4Ok {
1247 if len(v4Addrs) == 0 {
1248 t.Errorf("DefaultResolver.LookupIP(%q, %q): no IPv4 addresses", network, host)
1249 }
1250 }
1251 if network == "ip6" || network == "ip" && v6Ok {
1252 if len(v6Addrs) == 0 {
1253 t.Errorf("DefaultResolver.LookupIP(%q, %q): no IPv6 addresses", network, host)
1254 }
1255 }
1256
1257
1258 if network == "ip6" && len(v4Addrs) > 0 {
1259 t.Errorf("DefaultResolver.LookupIP(%q, %q): unexpected IPv4 addresses: %v", network, host, v4Addrs)
1260 }
1261 if network == "ip4" && len(v6Addrs) > 0 {
1262 t.Errorf("DefaultResolver.LookupIP(%q, %q): unexpected IPv6 addresses: %v", network, host, v6Addrs)
1263 }
1264 })
1265 }
1266 })
1267 }
1268 }
1269
View as plain text