github.com/code-reading/golang@v0.0.0-20220303082512-ba5bc0e589a3/go/src/net/lookup_test.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build !js 6 // +build !js 7 8 package net 9 10 import ( 11 "bytes" 12 "context" 13 "fmt" 14 "internal/testenv" 15 "reflect" 16 "runtime" 17 "sort" 18 "strings" 19 "sync" 20 "sync/atomic" 21 "testing" 22 "time" 23 ) 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 // The Lookup APIs use various sources such as local database, DNS or 42 // mDNS, and may use platform-dependent DNS stub resolver if possible. 43 // The APIs accept any of forms for a query; host name in various 44 // encodings, UTF-8 encoded net name, domain name, FQDN or absolute 45 // FQDN, but the result would be one of the forms and it depends on 46 // the circumstances. 47 48 var lookupGoogleSRVTests = []struct { 49 service, proto, name string 50 cname, target string 51 }{ 52 { 53 "xmpp-server", "tcp", "google.com", 54 "google.com.", "google.com.", 55 }, 56 { 57 "xmpp-server", "tcp", "google.com.", 58 "google.com.", "google.com.", 59 }, 60 61 // non-standard back door 62 { 63 "", "", "_xmpp-server._tcp.google.com", 64 "google.com.", "google.com.", 65 }, 66 { 67 "", "", "_xmpp-server._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 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 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 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 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 found := false 250 for _, txt := range txts { 251 if strings.Contains(txt, tt.txt) && (strings.HasSuffix(txt, tt.host) || strings.HasSuffix(txt, tt.host+".")) { 252 found = true 253 break 254 } 255 } 256 if !found { 257 t.Errorf("got %v; want a record containing %s, %s", txts, tt.txt, tt.host) 258 } 259 } 260 } 261 262 var lookupGooglePublicDNSAddrTests = []string{ 263 "8.8.8.8", 264 "8.8.4.4", 265 "2001:4860:4860::8888", 266 "2001:4860:4860::8844", 267 } 268 269 func TestLookupGooglePublicDNSAddr(t *testing.T) { 270 mustHaveExternalNetwork(t) 271 272 if !supportsIPv4() || !supportsIPv6() || !*testIPv4 || !*testIPv6 { 273 t.Skip("both IPv4 and IPv6 are required") 274 } 275 276 defer dnsWaitGroup.Wait() 277 278 for _, ip := range lookupGooglePublicDNSAddrTests { 279 names, err := LookupAddr(ip) 280 if err != nil { 281 t.Fatal(err) 282 } 283 if len(names) == 0 { 284 t.Error("got no record") 285 } 286 for _, name := range names { 287 if !hasSuffixFold(name, ".google.com.") && !hasSuffixFold(name, ".google.") { 288 t.Errorf("got %q; want a record ending in .google.com. or .google.", name) 289 } 290 } 291 } 292 } 293 294 func TestLookupIPv6LinkLocalAddr(t *testing.T) { 295 if !supportsIPv6() || !*testIPv6 { 296 t.Skip("IPv6 is required") 297 } 298 299 defer dnsWaitGroup.Wait() 300 301 addrs, err := LookupHost("localhost") 302 if err != nil { 303 t.Fatal(err) 304 } 305 found := false 306 for _, addr := range addrs { 307 if addr == "fe80::1%lo0" { 308 found = true 309 break 310 } 311 } 312 if !found { 313 t.Skipf("not supported on %s", runtime.GOOS) 314 } 315 if _, err := LookupAddr("fe80::1%lo0"); err != nil { 316 t.Error(err) 317 } 318 } 319 320 func TestLookupIPv6LinkLocalAddrWithZone(t *testing.T) { 321 if !supportsIPv6() || !*testIPv6 { 322 t.Skip("IPv6 is required") 323 } 324 325 ipaddrs, err := DefaultResolver.LookupIPAddr(context.Background(), "fe80::1%lo0") 326 if err != nil { 327 t.Error(err) 328 } 329 for _, addr := range ipaddrs { 330 if e, a := "lo0", addr.Zone; e != a { 331 t.Errorf("wrong zone: want %q, got %q", e, a) 332 } 333 } 334 335 addrs, err := DefaultResolver.LookupHost(context.Background(), "fe80::1%lo0") 336 if err != nil { 337 t.Error(err) 338 } 339 for _, addr := range addrs { 340 if e, a := "fe80::1%lo0", addr; e != a { 341 t.Errorf("wrong host: want %q got %q", e, a) 342 } 343 } 344 } 345 346 var lookupCNAMETests = []struct { 347 name, cname string 348 }{ 349 {"www.iana.org", "icann.org."}, 350 {"www.iana.org.", "icann.org."}, 351 {"www.google.com", "google.com."}, 352 } 353 354 func TestLookupCNAME(t *testing.T) { 355 mustHaveExternalNetwork(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 395 if !supportsIPv4() || !*testIPv4 { 396 t.Skip("IPv4 is required") 397 } 398 399 defer dnsWaitGroup.Wait() 400 401 for _, tt := range lookupGoogleHostTests { 402 addrs, err := LookupHost(tt.name) 403 if err != nil { 404 t.Fatal(err) 405 } 406 if len(addrs) == 0 { 407 t.Error("got no record") 408 } 409 for _, addr := range addrs { 410 if ParseIP(addr) == nil { 411 t.Errorf("got %q; want a literal IP address", addr) 412 } 413 } 414 } 415 } 416 417 func TestLookupLongTXT(t *testing.T) { 418 testenv.SkipFlaky(t, 22857) 419 mustHaveExternalNetwork(t) 420 421 defer dnsWaitGroup.Wait() 422 423 txts, err := LookupTXT("golang.rsc.io") 424 if err != nil { 425 t.Fatal(err) 426 } 427 sort.Strings(txts) 428 want := []string{ 429 strings.Repeat("abcdefghijklmnopqrstuvwxyABCDEFGHJIKLMNOPQRSTUVWXY", 10), 430 "gophers rule", 431 } 432 if !reflect.DeepEqual(txts, want) { 433 t.Fatalf("LookupTXT golang.rsc.io incorrect\nhave %q\nwant %q", txts, want) 434 } 435 } 436 437 var lookupGoogleIPTests = []struct { 438 name string 439 }{ 440 {"google.com"}, 441 {"google.com."}, 442 } 443 444 func TestLookupGoogleIP(t *testing.T) { 445 mustHaveExternalNetwork(t) 446 447 if !supportsIPv4() || !*testIPv4 { 448 t.Skip("IPv4 is required") 449 } 450 451 defer dnsWaitGroup.Wait() 452 453 for _, tt := range lookupGoogleIPTests { 454 ips, err := LookupIP(tt.name) 455 if err != nil { 456 t.Fatal(err) 457 } 458 if len(ips) == 0 { 459 t.Error("got no record") 460 } 461 for _, ip := range ips { 462 if ip.To4() == nil && ip.To16() == nil { 463 t.Errorf("got %v; want an IP address", ip) 464 } 465 } 466 } 467 } 468 469 var revAddrTests = []struct { 470 Addr string 471 Reverse string 472 ErrPrefix string 473 }{ 474 {"1.2.3.4", "4.3.2.1.in-addr.arpa.", ""}, 475 {"245.110.36.114", "114.36.110.245.in-addr.arpa.", ""}, 476 {"::ffff:12.34.56.78", "78.56.34.12.in-addr.arpa.", ""}, 477 {"::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.", ""}, 478 {"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.", ""}, 479 {"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.", ""}, 480 {"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.", ""}, 481 {"1.2.3", "", "unrecognized address"}, 482 {"1.2.3.4.5", "", "unrecognized address"}, 483 {"1234:567:bcbca::89a:bcde", "", "unrecognized address"}, 484 {"1234:567::bcbc:adad::89a:bcde", "", "unrecognized address"}, 485 } 486 487 func TestReverseAddress(t *testing.T) { 488 defer dnsWaitGroup.Wait() 489 for i, tt := range revAddrTests { 490 a, err := reverseaddr(tt.Addr) 491 if len(tt.ErrPrefix) > 0 && err == nil { 492 t.Errorf("#%d: expected %q, got <nil> (error)", i, tt.ErrPrefix) 493 continue 494 } 495 if len(tt.ErrPrefix) == 0 && err != nil { 496 t.Errorf("#%d: expected <nil>, got %q (error)", i, err) 497 } 498 if err != nil && err.(*DNSError).Err != tt.ErrPrefix { 499 t.Errorf("#%d: expected %q, got %q (mismatched error)", i, tt.ErrPrefix, err.(*DNSError).Err) 500 } 501 if a != tt.Reverse { 502 t.Errorf("#%d: expected %q, got %q (reverse address)", i, tt.Reverse, a) 503 } 504 } 505 } 506 507 func TestDNSFlood(t *testing.T) { 508 if !*testDNSFlood { 509 t.Skip("test disabled; use -dnsflood to enable") 510 } 511 512 defer dnsWaitGroup.Wait() 513 514 var N = 5000 515 if runtime.GOOS == "darwin" || runtime.GOOS == "ios" { 516 // On Darwin this test consumes kernel threads much 517 // than other platforms for some reason. 518 // When we monitor the number of allocated Ms by 519 // observing on runtime.newm calls, we can see that it 520 // easily reaches the per process ceiling 521 // kern.num_threads when CGO_ENABLED=1 and 522 // GODEBUG=netdns=go. 523 N = 500 524 } 525 526 const timeout = 3 * time.Second 527 ctxHalfTimeout, cancel := context.WithTimeout(context.Background(), timeout/2) 528 defer cancel() 529 ctxTimeout, cancel := context.WithTimeout(context.Background(), timeout) 530 defer cancel() 531 532 c := make(chan error, 2*N) 533 for i := 0; i < N; i++ { 534 name := fmt.Sprintf("%d.net-test.golang.org", i) 535 go func() { 536 _, err := DefaultResolver.LookupIPAddr(ctxHalfTimeout, name) 537 c <- err 538 }() 539 go func() { 540 _, err := DefaultResolver.LookupIPAddr(ctxTimeout, name) 541 c <- err 542 }() 543 } 544 qstats := struct { 545 succeeded, failed int 546 timeout, temporary, other int 547 unknown int 548 }{} 549 deadline := time.After(timeout + time.Second) 550 for i := 0; i < 2*N; i++ { 551 select { 552 case <-deadline: 553 t.Fatal("deadline exceeded") 554 case err := <-c: 555 switch err := err.(type) { 556 case nil: 557 qstats.succeeded++ 558 case Error: 559 qstats.failed++ 560 if err.Timeout() { 561 qstats.timeout++ 562 } 563 if err.Temporary() { 564 qstats.temporary++ 565 } 566 if !err.Timeout() && !err.Temporary() { 567 qstats.other++ 568 } 569 default: 570 qstats.failed++ 571 qstats.unknown++ 572 } 573 } 574 } 575 576 // A high volume of DNS queries for sub-domain of golang.org 577 // would be coordinated by authoritative or recursive server, 578 // or stub resolver which implements query-response rate 579 // limitation, so we can expect some query successes and more 580 // failures including timeout, temporary and other here. 581 // As a rule, unknown must not be shown but it might possibly 582 // happen due to issue 4856 for now. 583 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) 584 } 585 586 func TestLookupDotsWithLocalSource(t *testing.T) { 587 if !supportsIPv4() || !*testIPv4 { 588 t.Skip("IPv4 is required") 589 } 590 591 mustHaveExternalNetwork(t) 592 593 defer dnsWaitGroup.Wait() 594 595 for i, fn := range []func() func(){forceGoDNS, forceCgoDNS} { 596 fixup := fn() 597 if fixup == nil { 598 continue 599 } 600 names, err := LookupAddr("127.0.0.1") 601 fixup() 602 if err != nil { 603 t.Logf("#%d: %v", i, err) 604 continue 605 } 606 mode := "netgo" 607 if i == 1 { 608 mode = "netcgo" 609 } 610 loop: 611 for i, name := range names { 612 if strings.Index(name, ".") == len(name)-1 { // "localhost" not "localhost." 613 for j := range names { 614 if j == i { 615 continue 616 } 617 if names[j] == name[:len(name)-1] { 618 // It's OK if we find the name without the dot, 619 // as some systems say 127.0.0.1 localhost localhost. 620 continue loop 621 } 622 } 623 t.Errorf("%s: got %s; want %s", mode, name, name[:len(name)-1]) 624 } else if strings.Contains(name, ".") && !strings.HasSuffix(name, ".") { // "localhost.localdomain." not "localhost.localdomain" 625 t.Errorf("%s: got %s; want name ending with trailing dot", mode, name) 626 } 627 } 628 } 629 } 630 631 func TestLookupDotsWithRemoteSource(t *testing.T) { 632 if runtime.GOOS == "darwin" || runtime.GOOS == "ios" { 633 testenv.SkipFlaky(t, 27992) 634 } 635 mustHaveExternalNetwork(t) 636 637 if !supportsIPv4() || !*testIPv4 { 638 t.Skip("IPv4 is required") 639 } 640 641 if iOS() { 642 t.Skip("no resolv.conf on iOS") 643 } 644 645 defer dnsWaitGroup.Wait() 646 647 if fixup := forceGoDNS(); fixup != nil { 648 testDots(t, "go") 649 fixup() 650 } 651 if fixup := forceCgoDNS(); fixup != nil { 652 testDots(t, "cgo") 653 fixup() 654 } 655 } 656 657 func testDots(t *testing.T, mode string) { 658 names, err := LookupAddr("8.8.8.8") // Google dns server 659 if err != nil { 660 testenv.SkipFlakyNet(t) 661 t.Errorf("LookupAddr(8.8.8.8): %v (mode=%v)", err, mode) 662 } else { 663 for _, name := range names { 664 if !hasSuffixFold(name, ".google.com.") && !hasSuffixFold(name, ".google.") { 665 t.Errorf("LookupAddr(8.8.8.8) = %v, want names ending in .google.com or .google with trailing dot (mode=%v)", names, mode) 666 break 667 } 668 } 669 } 670 671 cname, err := LookupCNAME("www.mit.edu") 672 if err != nil { 673 testenv.SkipFlakyNet(t) 674 t.Errorf("LookupCNAME(www.mit.edu, mode=%v): %v", mode, err) 675 } else if !strings.HasSuffix(cname, ".") { 676 t.Errorf("LookupCNAME(www.mit.edu) = %v, want cname ending in . with trailing dot (mode=%v)", cname, mode) 677 } 678 679 mxs, err := LookupMX("google.com") 680 if err != nil { 681 testenv.SkipFlakyNet(t) 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 testenv.SkipFlakyNet(t) 695 t.Errorf("LookupNS(google.com): %v (mode=%v)", err, mode) 696 } else { 697 for _, ns := range nss { 698 if !hasSuffixFold(ns.Host, ".google.com.") { 699 t.Errorf("LookupNS(google.com) = %v, want names ending in .google.com. with trailing dot (mode=%v)", nsString(nss), mode) 700 break 701 } 702 } 703 } 704 705 cname, srvs, err := LookupSRV("xmpp-server", "tcp", "google.com") 706 if err != nil { 707 testenv.SkipFlakyNet(t) 708 t.Errorf("LookupSRV(xmpp-server, tcp, google.com): %v (mode=%v)", err, mode) 709 } else { 710 if !hasSuffixFold(cname, ".google.com.") { 711 t.Errorf("LookupSRV(xmpp-server, tcp, google.com) returned cname=%v, want name ending in .google.com. with trailing dot (mode=%v)", cname, mode) 712 } 713 for _, srv := range srvs { 714 if !hasSuffixFold(srv.Target, ".google.com.") { 715 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) 716 break 717 } 718 } 719 } 720 } 721 722 func mxString(mxs []*MX) string { 723 var buf bytes.Buffer 724 sep := "" 725 fmt.Fprintf(&buf, "[") 726 for _, mx := range mxs { 727 fmt.Fprintf(&buf, "%s%s:%d", sep, mx.Host, mx.Pref) 728 sep = " " 729 } 730 fmt.Fprintf(&buf, "]") 731 return buf.String() 732 } 733 734 func nsString(nss []*NS) string { 735 var buf bytes.Buffer 736 sep := "" 737 fmt.Fprintf(&buf, "[") 738 for _, ns := range nss { 739 fmt.Fprintf(&buf, "%s%s", sep, ns.Host) 740 sep = " " 741 } 742 fmt.Fprintf(&buf, "]") 743 return buf.String() 744 } 745 746 func srvString(srvs []*SRV) string { 747 var buf bytes.Buffer 748 sep := "" 749 fmt.Fprintf(&buf, "[") 750 for _, srv := range srvs { 751 fmt.Fprintf(&buf, "%s%s:%d:%d:%d", sep, srv.Target, srv.Port, srv.Priority, srv.Weight) 752 sep = " " 753 } 754 fmt.Fprintf(&buf, "]") 755 return buf.String() 756 } 757 758 func TestLookupPort(t *testing.T) { 759 // See https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml 760 // 761 // Please be careful about adding new test cases. 762 // There are platforms which have incomplete mappings for 763 // restricted resource access and security reasons. 764 type test struct { 765 network string 766 name string 767 port int 768 ok bool 769 } 770 var tests = []test{ 771 {"tcp", "0", 0, true}, 772 {"udp", "0", 0, true}, 773 {"udp", "domain", 53, true}, 774 775 {"--badnet--", "zzz", 0, false}, 776 {"tcp", "--badport--", 0, false}, 777 {"tcp", "-1", 0, false}, 778 {"tcp", "65536", 0, false}, 779 {"udp", "-1", 0, false}, 780 {"udp", "65536", 0, false}, 781 {"tcp", "123456789", 0, false}, 782 783 // Issue 13610: LookupPort("tcp", "") 784 {"tcp", "", 0, true}, 785 {"tcp4", "", 0, true}, 786 {"tcp6", "", 0, true}, 787 {"udp", "", 0, true}, 788 {"udp4", "", 0, true}, 789 {"udp6", "", 0, true}, 790 } 791 792 switch runtime.GOOS { 793 case "android": 794 if netGo { 795 t.Skipf("not supported on %s without cgo; see golang.org/issues/14576", runtime.GOOS) 796 } 797 default: 798 tests = append(tests, test{"tcp", "http", 80, true}) 799 } 800 801 for _, tt := range tests { 802 port, err := LookupPort(tt.network, tt.name) 803 if port != tt.port || (err == nil) != tt.ok { 804 t.Errorf("LookupPort(%q, %q) = %d, %v; want %d, error=%t", tt.network, tt.name, port, err, tt.port, !tt.ok) 805 } 806 if err != nil { 807 if perr := parseLookupPortError(err); perr != nil { 808 t.Error(perr) 809 } 810 } 811 } 812 } 813 814 // Like TestLookupPort but with minimal tests that should always pass 815 // because the answers are baked-in to the net package. 816 func TestLookupPort_Minimal(t *testing.T) { 817 type test struct { 818 network string 819 name string 820 port int 821 } 822 var tests = []test{ 823 {"tcp", "http", 80}, 824 {"tcp", "HTTP", 80}, // case shouldn't matter 825 {"tcp", "https", 443}, 826 {"tcp", "ssh", 22}, 827 {"tcp", "gopher", 70}, 828 {"tcp4", "http", 80}, 829 {"tcp6", "http", 80}, 830 } 831 832 for _, tt := range tests { 833 port, err := LookupPort(tt.network, tt.name) 834 if port != tt.port || err != nil { 835 t.Errorf("LookupPort(%q, %q) = %d, %v; want %d, error=nil", tt.network, tt.name, port, err, tt.port) 836 } 837 } 838 } 839 840 func TestLookupProtocol_Minimal(t *testing.T) { 841 type test struct { 842 name string 843 want int 844 } 845 var tests = []test{ 846 {"tcp", 6}, 847 {"TcP", 6}, // case shouldn't matter 848 {"icmp", 1}, 849 {"igmp", 2}, 850 {"udp", 17}, 851 {"ipv6-icmp", 58}, 852 } 853 854 for _, tt := range tests { 855 got, err := lookupProtocol(context.Background(), tt.name) 856 if got != tt.want || err != nil { 857 t.Errorf("LookupProtocol(%q) = %d, %v; want %d, error=nil", tt.name, got, err, tt.want) 858 } 859 } 860 861 } 862 863 func TestLookupNonLDH(t *testing.T) { 864 defer dnsWaitGroup.Wait() 865 866 if fixup := forceGoDNS(); fixup != nil { 867 defer fixup() 868 } 869 870 // "LDH" stands for letters, digits, and hyphens and is the usual 871 // description of standard DNS names. 872 // This test is checking that other kinds of names are reported 873 // as not found, not reported as invalid names. 874 addrs, err := LookupHost("!!!.###.bogus..domain.") 875 if err == nil { 876 t.Fatalf("lookup succeeded: %v", addrs) 877 } 878 if !strings.HasSuffix(err.Error(), errNoSuchHost.Error()) { 879 t.Fatalf("lookup error = %v, want %v", err, errNoSuchHost) 880 } 881 if !err.(*DNSError).IsNotFound { 882 t.Fatalf("lookup error = %v, want true", err.(*DNSError).IsNotFound) 883 } 884 } 885 886 func TestLookupContextCancel(t *testing.T) { 887 mustHaveExternalNetwork(t) 888 defer dnsWaitGroup.Wait() 889 890 ctx, ctxCancel := context.WithCancel(context.Background()) 891 ctxCancel() 892 _, err := DefaultResolver.LookupIPAddr(ctx, "google.com") 893 if err != errCanceled { 894 testenv.SkipFlakyNet(t) 895 t.Fatal(err) 896 } 897 ctx = context.Background() 898 _, err = DefaultResolver.LookupIPAddr(ctx, "google.com") 899 if err != nil { 900 testenv.SkipFlakyNet(t) 901 t.Fatal(err) 902 } 903 } 904 905 // Issue 24330: treat the nil *Resolver like a zero value. Verify nothing 906 // crashes if nil is used. 907 func TestNilResolverLookup(t *testing.T) { 908 mustHaveExternalNetwork(t) 909 var r *Resolver = nil 910 ctx := context.Background() 911 912 // Don't care about the results, just that nothing panics: 913 r.LookupAddr(ctx, "8.8.8.8") 914 r.LookupCNAME(ctx, "google.com") 915 r.LookupHost(ctx, "google.com") 916 r.LookupIPAddr(ctx, "google.com") 917 r.LookupIP(ctx, "ip", "google.com") 918 r.LookupMX(ctx, "gmail.com") 919 r.LookupNS(ctx, "google.com") 920 r.LookupPort(ctx, "tcp", "smtp") 921 r.LookupSRV(ctx, "service", "proto", "name") 922 r.LookupTXT(ctx, "gmail.com") 923 } 924 925 // TestLookupHostCancel verifies that lookup works even after many 926 // canceled lookups (see golang.org/issue/24178 for details). 927 func TestLookupHostCancel(t *testing.T) { 928 mustHaveExternalNetwork(t) 929 const ( 930 google = "www.google.com" 931 invalidDomain = "invalid.invalid" // RFC 2606 reserves .invalid 932 n = 600 // this needs to be larger than threadLimit size 933 ) 934 935 _, err := LookupHost(google) 936 if err != nil { 937 t.Fatal(err) 938 } 939 940 ctx, cancel := context.WithCancel(context.Background()) 941 cancel() 942 for i := 0; i < n; i++ { 943 addr, err := DefaultResolver.LookupHost(ctx, invalidDomain) 944 if err == nil { 945 t.Fatalf("LookupHost(%q): returns %v, but should fail", invalidDomain, addr) 946 } 947 if !strings.Contains(err.Error(), "canceled") { 948 t.Fatalf("LookupHost(%q): failed with unexpected error: %v", invalidDomain, err) 949 } 950 time.Sleep(time.Millisecond * 1) 951 } 952 953 _, err = LookupHost(google) 954 if err != nil { 955 t.Fatal(err) 956 } 957 } 958 959 type lookupCustomResolver struct { 960 *Resolver 961 mu sync.RWMutex 962 dialed bool 963 } 964 965 func (lcr *lookupCustomResolver) dial() func(ctx context.Context, network, address string) (Conn, error) { 966 return func(ctx context.Context, network, address string) (Conn, error) { 967 lcr.mu.Lock() 968 lcr.dialed = true 969 lcr.mu.Unlock() 970 return Dial(network, address) 971 } 972 } 973 974 // TestConcurrentPreferGoResolversDial tests that multiple resolvers with the 975 // PreferGo option used concurrently are all dialed properly. 976 func TestConcurrentPreferGoResolversDial(t *testing.T) { 977 // The windows and plan9 implementation of the resolver does not use 978 // the Dial function. 979 switch runtime.GOOS { 980 case "windows", "plan9": 981 t.Skipf("skip on %v", runtime.GOOS) 982 } 983 984 testenv.MustHaveExternalNetwork(t) 985 testenv.SkipFlakyNet(t) 986 987 defer dnsWaitGroup.Wait() 988 989 resolvers := make([]*lookupCustomResolver, 2) 990 for i := range resolvers { 991 cs := lookupCustomResolver{Resolver: &Resolver{PreferGo: true}} 992 cs.Dial = cs.dial() 993 resolvers[i] = &cs 994 } 995 996 var wg sync.WaitGroup 997 wg.Add(len(resolvers)) 998 for i, resolver := range resolvers { 999 go func(r *Resolver, index int) { 1000 defer wg.Done() 1001 _, err := r.LookupIPAddr(context.Background(), "google.com") 1002 if err != nil { 1003 t.Errorf("lookup failed for resolver %d: %q", index, err) 1004 } 1005 }(resolver.Resolver, i) 1006 } 1007 wg.Wait() 1008 1009 if t.Failed() { 1010 t.FailNow() 1011 } 1012 1013 for i, resolver := range resolvers { 1014 if !resolver.dialed { 1015 t.Errorf("custom resolver %d not dialed during lookup", i) 1016 } 1017 } 1018 } 1019 1020 var ipVersionTests = []struct { 1021 network string 1022 version byte 1023 }{ 1024 {"tcp", 0}, 1025 {"tcp4", '4'}, 1026 {"tcp6", '6'}, 1027 {"udp", 0}, 1028 {"udp4", '4'}, 1029 {"udp6", '6'}, 1030 {"ip", 0}, 1031 {"ip4", '4'}, 1032 {"ip6", '6'}, 1033 {"ip7", 0}, 1034 {"", 0}, 1035 } 1036 1037 func TestIPVersion(t *testing.T) { 1038 for _, tt := range ipVersionTests { 1039 if version := ipVersion(tt.network); version != tt.version { 1040 t.Errorf("Family for: %s. Expected: %s, Got: %s", tt.network, 1041 string(tt.version), string(version)) 1042 } 1043 } 1044 } 1045 1046 // Issue 28600: The context that is used to lookup ips should always 1047 // preserve the values from the context that was passed into LookupIPAddr. 1048 func TestLookupIPAddrPreservesContextValues(t *testing.T) { 1049 origTestHookLookupIP := testHookLookupIP 1050 defer func() { testHookLookupIP = origTestHookLookupIP }() 1051 1052 keyValues := []struct { 1053 key, value interface{} 1054 }{ 1055 {"key-1", 12}, 1056 {384, "value2"}, 1057 {new(float64), 137}, 1058 } 1059 ctx := context.Background() 1060 for _, kv := range keyValues { 1061 ctx = context.WithValue(ctx, kv.key, kv.value) 1062 } 1063 1064 wantIPs := []IPAddr{ 1065 {IP: IPv4(127, 0, 0, 1)}, 1066 {IP: IPv6loopback}, 1067 } 1068 1069 checkCtxValues := func(ctx_ context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) { 1070 for _, kv := range keyValues { 1071 g, w := ctx_.Value(kv.key), kv.value 1072 if !reflect.DeepEqual(g, w) { 1073 t.Errorf("Value lookup:\n\tGot: %v\n\tWant: %v", g, w) 1074 } 1075 } 1076 return wantIPs, nil 1077 } 1078 testHookLookupIP = checkCtxValues 1079 1080 resolvers := []*Resolver{ 1081 nil, 1082 new(Resolver), 1083 } 1084 1085 for i, resolver := range resolvers { 1086 gotIPs, err := resolver.LookupIPAddr(ctx, "golang.org") 1087 if err != nil { 1088 t.Errorf("Resolver #%d: unexpected error: %v", i, err) 1089 } 1090 if !reflect.DeepEqual(gotIPs, wantIPs) { 1091 t.Errorf("#%d: mismatched IPAddr results\n\tGot: %v\n\tWant: %v", i, gotIPs, wantIPs) 1092 } 1093 } 1094 } 1095 1096 // Issue 30521: The lookup group should call the resolver for each network. 1097 func TestLookupIPAddrConcurrentCallsForNetworks(t *testing.T) { 1098 origTestHookLookupIP := testHookLookupIP 1099 defer func() { testHookLookupIP = origTestHookLookupIP }() 1100 1101 queries := [][]string{ 1102 {"udp", "golang.org"}, 1103 {"udp4", "golang.org"}, 1104 {"udp6", "golang.org"}, 1105 {"udp", "golang.org"}, 1106 {"udp", "golang.org"}, 1107 } 1108 results := map[[2]string][]IPAddr{ 1109 {"udp", "golang.org"}: { 1110 {IP: IPv4(127, 0, 0, 1)}, 1111 {IP: IPv6loopback}, 1112 }, 1113 {"udp4", "golang.org"}: { 1114 {IP: IPv4(127, 0, 0, 1)}, 1115 }, 1116 {"udp6", "golang.org"}: { 1117 {IP: IPv6loopback}, 1118 }, 1119 } 1120 calls := int32(0) 1121 waitCh := make(chan struct{}) 1122 testHookLookupIP = func(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) { 1123 // We'll block until this is called one time for each different 1124 // expected result. This will ensure that the lookup group would wait 1125 // for the existing call if it was to be reused. 1126 if atomic.AddInt32(&calls, 1) == int32(len(results)) { 1127 close(waitCh) 1128 } 1129 select { 1130 case <-waitCh: 1131 case <-ctx.Done(): 1132 return nil, ctx.Err() 1133 } 1134 return results[[2]string{network, host}], nil 1135 } 1136 1137 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 1138 defer cancel() 1139 wg := sync.WaitGroup{} 1140 for _, q := range queries { 1141 network := q[0] 1142 host := q[1] 1143 wg.Add(1) 1144 go func() { 1145 defer wg.Done() 1146 gotIPs, err := DefaultResolver.lookupIPAddr(ctx, network, host) 1147 if err != nil { 1148 t.Errorf("lookupIPAddr(%v, %v): unexpected error: %v", network, host, err) 1149 } 1150 wantIPs := results[[2]string{network, host}] 1151 if !reflect.DeepEqual(gotIPs, wantIPs) { 1152 t.Errorf("lookupIPAddr(%v, %v): mismatched IPAddr results\n\tGot: %v\n\tWant: %v", network, host, gotIPs, wantIPs) 1153 } 1154 }() 1155 } 1156 wg.Wait() 1157 } 1158 1159 func TestWithUnexpiredValuesPreserved(t *testing.T) { 1160 ctx, cancel := context.WithCancel(context.Background()) 1161 1162 // Insert a value into it. 1163 key, value := "key-1", 2 1164 ctx = context.WithValue(ctx, key, value) 1165 1166 // Now use the "values preserving context" like 1167 // we would for LookupIPAddr. See Issue 28600. 1168 ctx = withUnexpiredValuesPreserved(ctx) 1169 1170 // Lookup before expiry. 1171 if g, w := ctx.Value(key), value; g != w { 1172 t.Errorf("Lookup before expiry: Got %v Want %v", g, w) 1173 } 1174 1175 // Cancel the context. 1176 cancel() 1177 1178 // Lookup after expiry should return nil 1179 if g := ctx.Value(key); g != nil { 1180 t.Errorf("Lookup after expiry: Got %v want nil", g) 1181 } 1182 } 1183 1184 // Issue 31597: don't panic on null byte in name 1185 func TestLookupNullByte(t *testing.T) { 1186 testenv.MustHaveExternalNetwork(t) 1187 testenv.SkipFlakyNet(t) 1188 LookupHost("foo\x00bar") // check that it doesn't panic; it used to on Windows 1189 } 1190 1191 func TestResolverLookupIP(t *testing.T) { 1192 testenv.MustHaveExternalNetwork(t) 1193 1194 v4Ok := supportsIPv4() && *testIPv4 1195 v6Ok := supportsIPv6() && *testIPv6 1196 1197 defer dnsWaitGroup.Wait() 1198 1199 for _, impl := range []struct { 1200 name string 1201 fn func() func() 1202 }{ 1203 {"go", forceGoDNS}, 1204 {"cgo", forceCgoDNS}, 1205 } { 1206 t.Run("implementation: "+impl.name, func(t *testing.T) { 1207 fixup := impl.fn() 1208 if fixup == nil { 1209 t.Skip("not supported") 1210 } 1211 defer fixup() 1212 1213 for _, network := range []string{"ip", "ip4", "ip6"} { 1214 t.Run("network: "+network, func(t *testing.T) { 1215 switch { 1216 case network == "ip4" && !v4Ok: 1217 t.Skip("IPv4 is not supported") 1218 case network == "ip6" && !v6Ok: 1219 t.Skip("IPv6 is not supported") 1220 } 1221 1222 // google.com has both A and AAAA records. 1223 const host = "google.com" 1224 ips, err := DefaultResolver.LookupIP(context.Background(), network, host) 1225 if err != nil { 1226 testenv.SkipFlakyNet(t) 1227 t.Fatalf("DefaultResolver.LookupIP(%q, %q): failed with unexpected error: %v", network, host, err) 1228 } 1229 1230 var v4Addrs []IP 1231 var v6Addrs []IP 1232 for _, ip := range ips { 1233 switch { 1234 case ip.To4() != nil: 1235 // We need to skip the test below because To16 will 1236 // convent an IPv4 address to an IPv4-mapped IPv6 1237 // address. 1238 v4Addrs = append(v4Addrs, ip) 1239 case ip.To16() != nil: 1240 v6Addrs = append(v6Addrs, ip) 1241 default: 1242 t.Fatalf("IP=%q is neither IPv4 nor IPv6", ip) 1243 } 1244 } 1245 1246 // Check that we got the expected addresses. 1247 if network == "ip4" || network == "ip" && v4Ok { 1248 if len(v4Addrs) == 0 { 1249 t.Errorf("DefaultResolver.LookupIP(%q, %q): no IPv4 addresses", network, host) 1250 } 1251 } 1252 if network == "ip6" || network == "ip" && v6Ok { 1253 if len(v6Addrs) == 0 { 1254 t.Errorf("DefaultResolver.LookupIP(%q, %q): no IPv6 addresses", network, host) 1255 } 1256 } 1257 1258 // Check that we didn't get any unexpected addresses. 1259 if network == "ip6" && len(v4Addrs) > 0 { 1260 t.Errorf("DefaultResolver.LookupIP(%q, %q): unexpected IPv4 addresses: %v", network, host, v4Addrs) 1261 } 1262 if network == "ip4" && len(v6Addrs) > 0 { 1263 t.Errorf("DefaultResolver.LookupIP(%q, %q): unexpected IPv6 addresses: %v", network, host, v6Addrs) 1264 } 1265 }) 1266 } 1267 }) 1268 } 1269 }