github.com/m10x/go/src@v0.0.0-20220112094212-ba61592315da/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 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 // The Lookup APIs use various sources such as local database, DNS or 41 // mDNS, and may use platform-dependent DNS stub resolver if possible. 42 // The APIs accept any of forms for a query; host name in various 43 // encodings, UTF-8 encoded net name, domain name, FQDN or absolute 44 // FQDN, but the result would be one of the forms and it depends on 45 // the circumstances. 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 // non-standard back door 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 // On Darwin this test consumes kernel threads much 519 // than other platforms for some reason. 520 // When we monitor the number of allocated Ms by 521 // observing on runtime.newm calls, we can see that it 522 // easily reaches the per process ceiling 523 // kern.num_threads when CGO_ENABLED=1 and 524 // GODEBUG=netdns=go. 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 // A high volume of DNS queries for sub-domain of golang.org 579 // would be coordinated by authoritative or recursive server, 580 // or stub resolver which implements query-response rate 581 // limitation, so we can expect some query successes and more 582 // failures including timeout, temporary and other here. 583 // As a rule, unknown must not be shown but it might possibly 584 // happen due to issue 4856 for now. 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 { // "localhost" not "localhost." 615 for j := range names { 616 if j == i { 617 continue 618 } 619 if names[j] == name[:len(name)-1] { 620 // It's OK if we find the name without the dot, 621 // as some systems say 127.0.0.1 localhost localhost. 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, ".") { // "localhost.localdomain." not "localhost.localdomain" 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") // Google dns server 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 // See https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml 758 // 759 // Please be careful about adding new test cases. 760 // There are platforms which have incomplete mappings for 761 // restricted resource access and security reasons. 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 // Issue 13610: LookupPort("tcp", "") 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 // Like TestLookupPort but with minimal tests that should always pass 813 // because the answers are baked-in to the net package. 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}, // case shouldn't matter 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}, // case shouldn't matter 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 // "LDH" stands for letters, digits, and hyphens and is the usual 869 // description of standard DNS names. 870 // This test is checking that other kinds of names are reported 871 // as not found, not reported as invalid names. 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 defer dnsWaitGroup.Wait() 887 888 ctx, ctxCancel := context.WithCancel(context.Background()) 889 ctxCancel() 890 _, err := DefaultResolver.LookupIPAddr(ctx, "google.com") 891 if err.(*DNSError).Err != errCanceled.Error() { 892 testenv.SkipFlakyNet(t) 893 t.Fatal(err) 894 } 895 ctx = context.Background() 896 _, err = DefaultResolver.LookupIPAddr(ctx, "google.com") 897 if err != nil { 898 testenv.SkipFlakyNet(t) 899 t.Fatal(err) 900 } 901 } 902 903 // Issue 24330: treat the nil *Resolver like a zero value. Verify nothing 904 // crashes if nil is used. 905 func TestNilResolverLookup(t *testing.T) { 906 mustHaveExternalNetwork(t) 907 var r *Resolver = nil 908 ctx := context.Background() 909 910 // Don't care about the results, just that nothing panics: 911 r.LookupAddr(ctx, "8.8.8.8") 912 r.LookupCNAME(ctx, "google.com") 913 r.LookupHost(ctx, "google.com") 914 r.LookupIPAddr(ctx, "google.com") 915 r.LookupIP(ctx, "ip", "google.com") 916 r.LookupMX(ctx, "gmail.com") 917 r.LookupNS(ctx, "google.com") 918 r.LookupPort(ctx, "tcp", "smtp") 919 r.LookupSRV(ctx, "service", "proto", "name") 920 r.LookupTXT(ctx, "gmail.com") 921 } 922 923 // TestLookupHostCancel verifies that lookup works even after many 924 // canceled lookups (see golang.org/issue/24178 for details). 925 func TestLookupHostCancel(t *testing.T) { 926 mustHaveExternalNetwork(t) 927 testenv.SkipFlakyNet(t) 928 t.Parallel() // Executes 600ms worth of sequential sleeps. 929 930 const ( 931 google = "www.google.com" 932 invalidDomain = "invalid.invalid" // RFC 2606 reserves .invalid 933 n = 600 // this needs to be larger than threadLimit size 934 ) 935 936 _, err := LookupHost(google) 937 if err != nil { 938 t.Fatal(err) 939 } 940 941 ctx, cancel := context.WithCancel(context.Background()) 942 cancel() 943 for i := 0; i < n; i++ { 944 addr, err := DefaultResolver.LookupHost(ctx, invalidDomain) 945 if err == nil { 946 t.Fatalf("LookupHost(%q): returns %v, but should fail", invalidDomain, addr) 947 } 948 949 // Don't verify what the actual error is. 950 // We know that it must be non-nil because the domain is invalid, 951 // but we don't have any guarantee that LookupHost actually bothers 952 // to check for cancellation on the fast path. 953 // (For example, it could use a local cache to avoid blocking entirely.) 954 955 // The lookup may deduplicate in-flight requests, so give it time to settle 956 // in between. 957 time.Sleep(time.Millisecond * 1) 958 } 959 960 _, err = LookupHost(google) 961 if err != nil { 962 t.Fatal(err) 963 } 964 } 965 966 type lookupCustomResolver struct { 967 *Resolver 968 mu sync.RWMutex 969 dialed bool 970 } 971 972 func (lcr *lookupCustomResolver) dial() func(ctx context.Context, network, address string) (Conn, error) { 973 return func(ctx context.Context, network, address string) (Conn, error) { 974 lcr.mu.Lock() 975 lcr.dialed = true 976 lcr.mu.Unlock() 977 return Dial(network, address) 978 } 979 } 980 981 // TestConcurrentPreferGoResolversDial tests that multiple resolvers with the 982 // PreferGo option used concurrently are all dialed properly. 983 func TestConcurrentPreferGoResolversDial(t *testing.T) { 984 // The windows and plan9 implementation of the resolver does not use 985 // the Dial function. 986 switch runtime.GOOS { 987 case "windows", "plan9": 988 t.Skipf("skip on %v", runtime.GOOS) 989 } 990 991 testenv.MustHaveExternalNetwork(t) 992 testenv.SkipFlakyNet(t) 993 994 defer dnsWaitGroup.Wait() 995 996 resolvers := make([]*lookupCustomResolver, 2) 997 for i := range resolvers { 998 cs := lookupCustomResolver{Resolver: &Resolver{PreferGo: true}} 999 cs.Dial = cs.dial() 1000 resolvers[i] = &cs 1001 } 1002 1003 var wg sync.WaitGroup 1004 wg.Add(len(resolvers)) 1005 for i, resolver := range resolvers { 1006 go func(r *Resolver, index int) { 1007 defer wg.Done() 1008 _, err := r.LookupIPAddr(context.Background(), "google.com") 1009 if err != nil { 1010 t.Errorf("lookup failed for resolver %d: %q", index, err) 1011 } 1012 }(resolver.Resolver, i) 1013 } 1014 wg.Wait() 1015 1016 if t.Failed() { 1017 t.FailNow() 1018 } 1019 1020 for i, resolver := range resolvers { 1021 if !resolver.dialed { 1022 t.Errorf("custom resolver %d not dialed during lookup", i) 1023 } 1024 } 1025 } 1026 1027 var ipVersionTests = []struct { 1028 network string 1029 version byte 1030 }{ 1031 {"tcp", 0}, 1032 {"tcp4", '4'}, 1033 {"tcp6", '6'}, 1034 {"udp", 0}, 1035 {"udp4", '4'}, 1036 {"udp6", '6'}, 1037 {"ip", 0}, 1038 {"ip4", '4'}, 1039 {"ip6", '6'}, 1040 {"ip7", 0}, 1041 {"", 0}, 1042 } 1043 1044 func TestIPVersion(t *testing.T) { 1045 for _, tt := range ipVersionTests { 1046 if version := ipVersion(tt.network); version != tt.version { 1047 t.Errorf("Family for: %s. Expected: %s, Got: %s", tt.network, 1048 string(tt.version), string(version)) 1049 } 1050 } 1051 } 1052 1053 // Issue 28600: The context that is used to lookup ips should always 1054 // preserve the values from the context that was passed into LookupIPAddr. 1055 func TestLookupIPAddrPreservesContextValues(t *testing.T) { 1056 origTestHookLookupIP := testHookLookupIP 1057 defer func() { testHookLookupIP = origTestHookLookupIP }() 1058 1059 keyValues := []struct { 1060 key, value any 1061 }{ 1062 {"key-1", 12}, 1063 {384, "value2"}, 1064 {new(float64), 137}, 1065 } 1066 ctx := context.Background() 1067 for _, kv := range keyValues { 1068 ctx = context.WithValue(ctx, kv.key, kv.value) 1069 } 1070 1071 wantIPs := []IPAddr{ 1072 {IP: IPv4(127, 0, 0, 1)}, 1073 {IP: IPv6loopback}, 1074 } 1075 1076 checkCtxValues := func(ctx_ context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) { 1077 for _, kv := range keyValues { 1078 g, w := ctx_.Value(kv.key), kv.value 1079 if !reflect.DeepEqual(g, w) { 1080 t.Errorf("Value lookup:\n\tGot: %v\n\tWant: %v", g, w) 1081 } 1082 } 1083 return wantIPs, nil 1084 } 1085 testHookLookupIP = checkCtxValues 1086 1087 resolvers := []*Resolver{ 1088 nil, 1089 new(Resolver), 1090 } 1091 1092 for i, resolver := range resolvers { 1093 gotIPs, err := resolver.LookupIPAddr(ctx, "golang.org") 1094 if err != nil { 1095 t.Errorf("Resolver #%d: unexpected error: %v", i, err) 1096 } 1097 if !reflect.DeepEqual(gotIPs, wantIPs) { 1098 t.Errorf("#%d: mismatched IPAddr results\n\tGot: %v\n\tWant: %v", i, gotIPs, wantIPs) 1099 } 1100 } 1101 } 1102 1103 // Issue 30521: The lookup group should call the resolver for each network. 1104 func TestLookupIPAddrConcurrentCallsForNetworks(t *testing.T) { 1105 origTestHookLookupIP := testHookLookupIP 1106 defer func() { testHookLookupIP = origTestHookLookupIP }() 1107 1108 queries := [][]string{ 1109 {"udp", "golang.org"}, 1110 {"udp4", "golang.org"}, 1111 {"udp6", "golang.org"}, 1112 {"udp", "golang.org"}, 1113 {"udp", "golang.org"}, 1114 } 1115 results := map[[2]string][]IPAddr{ 1116 {"udp", "golang.org"}: { 1117 {IP: IPv4(127, 0, 0, 1)}, 1118 {IP: IPv6loopback}, 1119 }, 1120 {"udp4", "golang.org"}: { 1121 {IP: IPv4(127, 0, 0, 1)}, 1122 }, 1123 {"udp6", "golang.org"}: { 1124 {IP: IPv6loopback}, 1125 }, 1126 } 1127 calls := int32(0) 1128 waitCh := make(chan struct{}) 1129 testHookLookupIP = func(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) { 1130 // We'll block until this is called one time for each different 1131 // expected result. This will ensure that the lookup group would wait 1132 // for the existing call if it was to be reused. 1133 if atomic.AddInt32(&calls, 1) == int32(len(results)) { 1134 close(waitCh) 1135 } 1136 select { 1137 case <-waitCh: 1138 case <-ctx.Done(): 1139 return nil, ctx.Err() 1140 } 1141 return results[[2]string{network, host}], nil 1142 } 1143 1144 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 1145 defer cancel() 1146 wg := sync.WaitGroup{} 1147 for _, q := range queries { 1148 network := q[0] 1149 host := q[1] 1150 wg.Add(1) 1151 go func() { 1152 defer wg.Done() 1153 gotIPs, err := DefaultResolver.lookupIPAddr(ctx, network, host) 1154 if err != nil { 1155 t.Errorf("lookupIPAddr(%v, %v): unexpected error: %v", network, host, err) 1156 } 1157 wantIPs := results[[2]string{network, host}] 1158 if !reflect.DeepEqual(gotIPs, wantIPs) { 1159 t.Errorf("lookupIPAddr(%v, %v): mismatched IPAddr results\n\tGot: %v\n\tWant: %v", network, host, gotIPs, wantIPs) 1160 } 1161 }() 1162 } 1163 wg.Wait() 1164 } 1165 1166 func TestWithUnexpiredValuesPreserved(t *testing.T) { 1167 ctx, cancel := context.WithCancel(context.Background()) 1168 1169 // Insert a value into it. 1170 key, value := "key-1", 2 1171 ctx = context.WithValue(ctx, key, value) 1172 1173 // Now use the "values preserving context" like 1174 // we would for LookupIPAddr. See Issue 28600. 1175 ctx = withUnexpiredValuesPreserved(ctx) 1176 1177 // Lookup before expiry. 1178 if g, w := ctx.Value(key), value; g != w { 1179 t.Errorf("Lookup before expiry: Got %v Want %v", g, w) 1180 } 1181 1182 // Cancel the context. 1183 cancel() 1184 1185 // Lookup after expiry should return nil 1186 if g := ctx.Value(key); g != nil { 1187 t.Errorf("Lookup after expiry: Got %v want nil", g) 1188 } 1189 } 1190 1191 // Issue 31597: don't panic on null byte in name 1192 func TestLookupNullByte(t *testing.T) { 1193 testenv.MustHaveExternalNetwork(t) 1194 testenv.SkipFlakyNet(t) 1195 LookupHost("foo\x00bar") // check that it doesn't panic; it used to on Windows 1196 } 1197 1198 func TestResolverLookupIP(t *testing.T) { 1199 testenv.MustHaveExternalNetwork(t) 1200 1201 v4Ok := supportsIPv4() && *testIPv4 1202 v6Ok := supportsIPv6() && *testIPv6 1203 1204 defer dnsWaitGroup.Wait() 1205 1206 for _, impl := range []struct { 1207 name string 1208 fn func() func() 1209 }{ 1210 {"go", forceGoDNS}, 1211 {"cgo", forceCgoDNS}, 1212 } { 1213 t.Run("implementation: "+impl.name, func(t *testing.T) { 1214 fixup := impl.fn() 1215 if fixup == nil { 1216 t.Skip("not supported") 1217 } 1218 defer fixup() 1219 1220 for _, network := range []string{"ip", "ip4", "ip6"} { 1221 t.Run("network: "+network, func(t *testing.T) { 1222 switch { 1223 case network == "ip4" && !v4Ok: 1224 t.Skip("IPv4 is not supported") 1225 case network == "ip6" && !v6Ok: 1226 t.Skip("IPv6 is not supported") 1227 } 1228 1229 // google.com has both A and AAAA records. 1230 const host = "google.com" 1231 ips, err := DefaultResolver.LookupIP(context.Background(), network, host) 1232 if err != nil { 1233 testenv.SkipFlakyNet(t) 1234 t.Fatalf("DefaultResolver.LookupIP(%q, %q): failed with unexpected error: %v", network, host, err) 1235 } 1236 1237 var v4Addrs []IP 1238 var v6Addrs []IP 1239 for _, ip := range ips { 1240 switch { 1241 case ip.To4() != nil: 1242 // We need to skip the test below because To16 will 1243 // convent an IPv4 address to an IPv4-mapped IPv6 1244 // address. 1245 v4Addrs = append(v4Addrs, ip) 1246 case ip.To16() != nil: 1247 v6Addrs = append(v6Addrs, ip) 1248 default: 1249 t.Fatalf("IP=%q is neither IPv4 nor IPv6", ip) 1250 } 1251 } 1252 1253 // Check that we got the expected addresses. 1254 if network == "ip4" || network == "ip" && v4Ok { 1255 if len(v4Addrs) == 0 { 1256 t.Errorf("DefaultResolver.LookupIP(%q, %q): no IPv4 addresses", network, host) 1257 } 1258 } 1259 if network == "ip6" || network == "ip" && v6Ok { 1260 if len(v6Addrs) == 0 { 1261 t.Errorf("DefaultResolver.LookupIP(%q, %q): no IPv6 addresses", network, host) 1262 } 1263 } 1264 1265 // Check that we didn't get any unexpected addresses. 1266 if network == "ip6" && len(v4Addrs) > 0 { 1267 t.Errorf("DefaultResolver.LookupIP(%q, %q): unexpected IPv4 addresses: %v", network, host, v4Addrs) 1268 } 1269 if network == "ip4" && len(v6Addrs) > 0 { 1270 t.Errorf("DefaultResolver.LookupIP(%q, %q): unexpected IPv6 addresses: %v", network, host, v6Addrs) 1271 } 1272 }) 1273 } 1274 }) 1275 } 1276 } 1277 1278 // A context timeout should still return a DNSError. 1279 func TestDNSTimeout(t *testing.T) { 1280 origTestHookLookupIP := testHookLookupIP 1281 defer func() { testHookLookupIP = origTestHookLookupIP }() 1282 defer dnsWaitGroup.Wait() 1283 1284 timeoutHookGo := make(chan bool, 1) 1285 timeoutHook := func(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) { 1286 <-timeoutHookGo 1287 return nil, context.DeadlineExceeded 1288 } 1289 testHookLookupIP = timeoutHook 1290 1291 checkErr := func(err error) { 1292 t.Helper() 1293 if err == nil { 1294 t.Error("expected an error") 1295 } else if dnserr, ok := err.(*DNSError); !ok { 1296 t.Errorf("got error type %T, want %T", err, (*DNSError)(nil)) 1297 } else if !dnserr.IsTimeout { 1298 t.Errorf("got error %#v, want IsTimeout == true", dnserr) 1299 } else if isTimeout := dnserr.Timeout(); !isTimeout { 1300 t.Errorf("got err.Timeout() == %t, want true", isTimeout) 1301 } 1302 } 1303 1304 // Single lookup. 1305 timeoutHookGo <- true 1306 _, err := LookupIP("golang.org") 1307 checkErr(err) 1308 1309 // Double lookup. 1310 var err1, err2 error 1311 var wg sync.WaitGroup 1312 wg.Add(2) 1313 go func() { 1314 defer wg.Done() 1315 _, err1 = LookupIP("golang1.org") 1316 }() 1317 go func() { 1318 defer wg.Done() 1319 _, err2 = LookupIP("golang1.org") 1320 }() 1321 close(timeoutHookGo) 1322 wg.Wait() 1323 checkErr(err1) 1324 checkErr(err2) 1325 1326 // Double lookup with context. 1327 timeoutHookGo = make(chan bool) 1328 ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond) 1329 wg.Add(2) 1330 go func() { 1331 defer wg.Done() 1332 _, err1 = DefaultResolver.LookupIPAddr(ctx, "golang2.org") 1333 }() 1334 go func() { 1335 defer wg.Done() 1336 _, err2 = DefaultResolver.LookupIPAddr(ctx, "golang2.org") 1337 }() 1338 time.Sleep(10 * time.Nanosecond) 1339 close(timeoutHookGo) 1340 wg.Wait() 1341 checkErr(err1) 1342 checkErr(err2) 1343 cancel() 1344 }