Home | History | Annotate | Download | only in net
      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 package net
      6 
      7 import (
      8 	"bytes"
      9 	"context"
     10 	"fmt"
     11 	"internal/testenv"
     12 	"reflect"
     13 	"runtime"
     14 	"sort"
     15 	"strings"
     16 	"testing"
     17 	"time"
     18 )
     19 
     20 func lookupLocalhost(ctx context.Context, fn func(context.Context, string) ([]IPAddr, error), host string) ([]IPAddr, error) {
     21 	switch host {
     22 	case "localhost":
     23 		return []IPAddr{
     24 			{IP: IPv4(127, 0, 0, 1)},
     25 			{IP: IPv6loopback},
     26 		}, nil
     27 	default:
     28 		return fn(ctx, host)
     29 	}
     30 }
     31 
     32 // The Lookup APIs use various sources such as local database, DNS or
     33 // mDNS, and may use platform-dependent DNS stub resolver if possible.
     34 // The APIs accept any of forms for a query; host name in various
     35 // encodings, UTF-8 encoded net name, domain name, FQDN or absolute
     36 // FQDN, but the result would be one of the forms and it depends on
     37 // the circumstances.
     38 
     39 var lookupGoogleSRVTests = []struct {
     40 	service, proto, name string
     41 	cname, target        string
     42 }{
     43 	{
     44 		"xmpp-server", "tcp", "google.com",
     45 		"google.com.", "google.com.",
     46 	},
     47 	{
     48 		"xmpp-server", "tcp", "google.com.",
     49 		"google.com.", "google.com.",
     50 	},
     51 
     52 	// non-standard back door
     53 	{
     54 		"", "", "_xmpp-server._tcp.google.com",
     55 		"google.com.", "google.com.",
     56 	},
     57 	{
     58 		"", "", "_xmpp-server._tcp.google.com.",
     59 		"google.com.", "google.com.",
     60 	},
     61 }
     62 
     63 func TestLookupGoogleSRV(t *testing.T) {
     64 	if testenv.Builder() == "" {
     65 		testenv.MustHaveExternalNetwork(t)
     66 	}
     67 
     68 	if !supportsIPv4() || !*testIPv4 {
     69 		t.Skip("IPv4 is required")
     70 	}
     71 
     72 	for _, tt := range lookupGoogleSRVTests {
     73 		cname, srvs, err := LookupSRV(tt.service, tt.proto, tt.name)
     74 		if err != nil {
     75 			testenv.SkipFlakyNet(t)
     76 			t.Fatal(err)
     77 		}
     78 		if len(srvs) == 0 {
     79 			t.Error("got no record")
     80 		}
     81 		if !strings.HasSuffix(cname, tt.cname) {
     82 			t.Errorf("got %s; want %s", cname, tt.cname)
     83 		}
     84 		for _, srv := range srvs {
     85 			if !strings.HasSuffix(srv.Target, tt.target) {
     86 				t.Errorf("got %v; want a record containing %s", srv, tt.target)
     87 			}
     88 		}
     89 	}
     90 }
     91 
     92 var lookupGmailMXTests = []struct {
     93 	name, host string
     94 }{
     95 	{"gmail.com", "google.com."},
     96 	{"gmail.com.", "google.com."},
     97 }
     98 
     99 func TestLookupGmailMX(t *testing.T) {
    100 	if testenv.Builder() == "" {
    101 		testenv.MustHaveExternalNetwork(t)
    102 	}
    103 
    104 	if !supportsIPv4() || !*testIPv4 {
    105 		t.Skip("IPv4 is required")
    106 	}
    107 
    108 	defer dnsWaitGroup.Wait()
    109 
    110 	for _, tt := range lookupGmailMXTests {
    111 		mxs, err := LookupMX(tt.name)
    112 		if err != nil {
    113 			t.Fatal(err)
    114 		}
    115 		if len(mxs) == 0 {
    116 			t.Error("got no record")
    117 		}
    118 		for _, mx := range mxs {
    119 			if !strings.HasSuffix(mx.Host, tt.host) {
    120 				t.Errorf("got %v; want a record containing %s", mx, tt.host)
    121 			}
    122 		}
    123 	}
    124 }
    125 
    126 var lookupGmailNSTests = []struct {
    127 	name, host string
    128 }{
    129 	{"gmail.com", "google.com."},
    130 	{"gmail.com.", "google.com."},
    131 }
    132 
    133 func TestLookupGmailNS(t *testing.T) {
    134 	if testenv.Builder() == "" {
    135 		testenv.MustHaveExternalNetwork(t)
    136 	}
    137 
    138 	if !supportsIPv4() || !*testIPv4 {
    139 		t.Skip("IPv4 is required")
    140 	}
    141 
    142 	defer dnsWaitGroup.Wait()
    143 
    144 	for _, tt := range lookupGmailNSTests {
    145 		nss, err := LookupNS(tt.name)
    146 		if err != nil {
    147 			testenv.SkipFlakyNet(t)
    148 			t.Fatal(err)
    149 		}
    150 		if len(nss) == 0 {
    151 			t.Error("got no record")
    152 		}
    153 		for _, ns := range nss {
    154 			if !strings.HasSuffix(ns.Host, tt.host) {
    155 				t.Errorf("got %v; want a record containing %s", ns, tt.host)
    156 			}
    157 		}
    158 	}
    159 }
    160 
    161 var lookupGmailTXTTests = []struct {
    162 	name, txt, host string
    163 }{
    164 	{"gmail.com", "spf", "google.com"},
    165 	{"gmail.com.", "spf", "google.com"},
    166 }
    167 
    168 func TestLookupGmailTXT(t *testing.T) {
    169 	if testenv.Builder() == "" {
    170 		testenv.MustHaveExternalNetwork(t)
    171 	}
    172 
    173 	if !supportsIPv4() || !*testIPv4 {
    174 		t.Skip("IPv4 is required")
    175 	}
    176 
    177 	defer dnsWaitGroup.Wait()
    178 
    179 	for _, tt := range lookupGmailTXTTests {
    180 		txts, err := LookupTXT(tt.name)
    181 		if err != nil {
    182 			t.Fatal(err)
    183 		}
    184 		if len(txts) == 0 {
    185 			t.Error("got no record")
    186 		}
    187 		for _, txt := range txts {
    188 			if !strings.Contains(txt, tt.txt) || (!strings.HasSuffix(txt, tt.host) && !strings.HasSuffix(txt, tt.host+".")) {
    189 				t.Errorf("got %s; want a record containing %s, %s", txt, tt.txt, tt.host)
    190 			}
    191 		}
    192 	}
    193 }
    194 
    195 var lookupGooglePublicDNSAddrTests = []struct {
    196 	addr, name string
    197 }{
    198 	{"8.8.8.8", ".google.com."},
    199 	{"8.8.4.4", ".google.com."},
    200 
    201 	{"2001:4860:4860::8888", ".google.com."},
    202 	{"2001:4860:4860::8844", ".google.com."},
    203 }
    204 
    205 func TestLookupGooglePublicDNSAddr(t *testing.T) {
    206 	if testenv.Builder() == "" {
    207 		testenv.MustHaveExternalNetwork(t)
    208 	}
    209 
    210 	if !supportsIPv4() || !supportsIPv6() || !*testIPv4 || !*testIPv6 {
    211 		t.Skip("both IPv4 and IPv6 are required")
    212 	}
    213 
    214 	defer dnsWaitGroup.Wait()
    215 
    216 	for _, tt := range lookupGooglePublicDNSAddrTests {
    217 		names, err := LookupAddr(tt.addr)
    218 		if err != nil {
    219 			t.Fatal(err)
    220 		}
    221 		if len(names) == 0 {
    222 			t.Error("got no record")
    223 		}
    224 		for _, name := range names {
    225 			if !strings.HasSuffix(name, tt.name) {
    226 				t.Errorf("got %s; want a record containing %s", name, tt.name)
    227 			}
    228 		}
    229 	}
    230 }
    231 
    232 func TestLookupIPv6LinkLocalAddr(t *testing.T) {
    233 	if !supportsIPv6() || !*testIPv6 {
    234 		t.Skip("IPv6 is required")
    235 	}
    236 
    237 	defer dnsWaitGroup.Wait()
    238 
    239 	addrs, err := LookupHost("localhost")
    240 	if err != nil {
    241 		t.Fatal(err)
    242 	}
    243 	found := false
    244 	for _, addr := range addrs {
    245 		if addr == "fe80::1%lo0" {
    246 			found = true
    247 			break
    248 		}
    249 	}
    250 	if !found {
    251 		t.Skipf("not supported on %s", runtime.GOOS)
    252 	}
    253 	if _, err := LookupAddr("fe80::1%lo0"); err != nil {
    254 		t.Error(err)
    255 	}
    256 }
    257 
    258 var lookupCNAMETests = []struct {
    259 	name, cname string
    260 }{
    261 	{"www.iana.org", "icann.org."},
    262 	{"www.iana.org.", "icann.org."},
    263 	{"www.google.com", "google.com."},
    264 }
    265 
    266 func TestLookupCNAME(t *testing.T) {
    267 	if testenv.Builder() == "" {
    268 		testenv.MustHaveExternalNetwork(t)
    269 	}
    270 
    271 	if !supportsIPv4() || !*testIPv4 {
    272 		t.Skip("IPv4 is required")
    273 	}
    274 
    275 	defer dnsWaitGroup.Wait()
    276 
    277 	for _, tt := range lookupCNAMETests {
    278 		cname, err := LookupCNAME(tt.name)
    279 		if err != nil {
    280 			t.Fatal(err)
    281 		}
    282 		if !strings.HasSuffix(cname, tt.cname) {
    283 			t.Errorf("got %s; want a record containing %s", cname, tt.cname)
    284 		}
    285 	}
    286 }
    287 
    288 var lookupGoogleHostTests = []struct {
    289 	name string
    290 }{
    291 	{"google.com"},
    292 	{"google.com."},
    293 }
    294 
    295 func TestLookupGoogleHost(t *testing.T) {
    296 	if testenv.Builder() == "" {
    297 		testenv.MustHaveExternalNetwork(t)
    298 	}
    299 
    300 	if !supportsIPv4() || !*testIPv4 {
    301 		t.Skip("IPv4 is required")
    302 	}
    303 
    304 	defer dnsWaitGroup.Wait()
    305 
    306 	for _, tt := range lookupGoogleHostTests {
    307 		addrs, err := LookupHost(tt.name)
    308 		if err != nil {
    309 			t.Fatal(err)
    310 		}
    311 		if len(addrs) == 0 {
    312 			t.Error("got no record")
    313 		}
    314 		for _, addr := range addrs {
    315 			if ParseIP(addr) == nil {
    316 				t.Errorf("got %q; want a literal IP address", addr)
    317 			}
    318 		}
    319 	}
    320 }
    321 
    322 func TestLookupLongTXT(t *testing.T) {
    323 	if runtime.GOOS == "plan9" {
    324 		t.Skip("skipping on plan9; see https://golang.org/issue/22857")
    325 	}
    326 	if testenv.Builder() == "" {
    327 		testenv.MustHaveExternalNetwork(t)
    328 	}
    329 
    330 	defer dnsWaitGroup.Wait()
    331 
    332 	txts, err := LookupTXT("golang.rsc.io")
    333 	if err != nil {
    334 		t.Fatal(err)
    335 	}
    336 	sort.Strings(txts)
    337 	want := []string{
    338 		strings.Repeat("abcdefghijklmnopqrstuvwxyABCDEFGHJIKLMNOPQRSTUVWXY", 10),
    339 		"gophers rule",
    340 	}
    341 	if !reflect.DeepEqual(txts, want) {
    342 		t.Fatalf("LookupTXT golang.rsc.io incorrect\nhave %q\nwant %q", txts, want)
    343 	}
    344 }
    345 
    346 var lookupGoogleIPTests = []struct {
    347 	name string
    348 }{
    349 	{"google.com"},
    350 	{"google.com."},
    351 }
    352 
    353 func TestLookupGoogleIP(t *testing.T) {
    354 	if testenv.Builder() == "" {
    355 		testenv.MustHaveExternalNetwork(t)
    356 	}
    357 
    358 	if !supportsIPv4() || !*testIPv4 {
    359 		t.Skip("IPv4 is required")
    360 	}
    361 
    362 	defer dnsWaitGroup.Wait()
    363 
    364 	for _, tt := range lookupGoogleIPTests {
    365 		ips, err := LookupIP(tt.name)
    366 		if err != nil {
    367 			t.Fatal(err)
    368 		}
    369 		if len(ips) == 0 {
    370 			t.Error("got no record")
    371 		}
    372 		for _, ip := range ips {
    373 			if ip.To4() == nil && ip.To16() == nil {
    374 				t.Errorf("got %v; want an IP address", ip)
    375 			}
    376 		}
    377 	}
    378 }
    379 
    380 var revAddrTests = []struct {
    381 	Addr      string
    382 	Reverse   string
    383 	ErrPrefix string
    384 }{
    385 	{"1.2.3.4", "4.3.2.1.in-addr.arpa.", ""},
    386 	{"245.110.36.114", "114.36.110.245.in-addr.arpa.", ""},
    387 	{"::ffff:12.34.56.78", "78.56.34.12.in-addr.arpa.", ""},
    388 	{"::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.", ""},
    389 	{"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.", ""},
    390 	{"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.", ""},
    391 	{"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.", ""},
    392 	{"1.2.3", "", "unrecognized address"},
    393 	{"1.2.3.4.5", "", "unrecognized address"},
    394 	{"1234:567:bcbca::89a:bcde", "", "unrecognized address"},
    395 	{"1234:567::bcbc:adad::89a:bcde", "", "unrecognized address"},
    396 }
    397 
    398 func TestReverseAddress(t *testing.T) {
    399 	defer dnsWaitGroup.Wait()
    400 	for i, tt := range revAddrTests {
    401 		a, err := reverseaddr(tt.Addr)
    402 		if len(tt.ErrPrefix) > 0 && err == nil {
    403 			t.Errorf("#%d: expected %q, got <nil> (error)", i, tt.ErrPrefix)
    404 			continue
    405 		}
    406 		if len(tt.ErrPrefix) == 0 && err != nil {
    407 			t.Errorf("#%d: expected <nil>, got %q (error)", i, err)
    408 		}
    409 		if err != nil && err.(*DNSError).Err != tt.ErrPrefix {
    410 			t.Errorf("#%d: expected %q, got %q (mismatched error)", i, tt.ErrPrefix, err.(*DNSError).Err)
    411 		}
    412 		if a != tt.Reverse {
    413 			t.Errorf("#%d: expected %q, got %q (reverse address)", i, tt.Reverse, a)
    414 		}
    415 	}
    416 }
    417 
    418 func TestDNSFlood(t *testing.T) {
    419 	if !*testDNSFlood {
    420 		t.Skip("test disabled; use -dnsflood to enable")
    421 	}
    422 
    423 	defer dnsWaitGroup.Wait()
    424 
    425 	var N = 5000
    426 	if runtime.GOOS == "darwin" {
    427 		// On Darwin this test consumes kernel threads much
    428 		// than other platforms for some reason.
    429 		// When we monitor the number of allocated Ms by
    430 		// observing on runtime.newm calls, we can see that it
    431 		// easily reaches the per process ceiling
    432 		// kern.num_threads when CGO_ENABLED=1 and
    433 		// GODEBUG=netdns=go.
    434 		N = 500
    435 	}
    436 
    437 	const timeout = 3 * time.Second
    438 	ctxHalfTimeout, cancel := context.WithTimeout(context.Background(), timeout/2)
    439 	defer cancel()
    440 	ctxTimeout, cancel := context.WithTimeout(context.Background(), timeout)
    441 	defer cancel()
    442 
    443 	c := make(chan error, 2*N)
    444 	for i := 0; i < N; i++ {
    445 		name := fmt.Sprintf("%d.net-test.golang.org", i)
    446 		go func() {
    447 			_, err := DefaultResolver.LookupIPAddr(ctxHalfTimeout, name)
    448 			c <- err
    449 		}()
    450 		go func() {
    451 			_, err := DefaultResolver.LookupIPAddr(ctxTimeout, name)
    452 			c <- err
    453 		}()
    454 	}
    455 	qstats := struct {
    456 		succeeded, failed         int
    457 		timeout, temporary, other int
    458 		unknown                   int
    459 	}{}
    460 	deadline := time.After(timeout + time.Second)
    461 	for i := 0; i < 2*N; i++ {
    462 		select {
    463 		case <-deadline:
    464 			t.Fatal("deadline exceeded")
    465 		case err := <-c:
    466 			switch err := err.(type) {
    467 			case nil:
    468 				qstats.succeeded++
    469 			case Error:
    470 				qstats.failed++
    471 				if err.Timeout() {
    472 					qstats.timeout++
    473 				}
    474 				if err.Temporary() {
    475 					qstats.temporary++
    476 				}
    477 				if !err.Timeout() && !err.Temporary() {
    478 					qstats.other++
    479 				}
    480 			default:
    481 				qstats.failed++
    482 				qstats.unknown++
    483 			}
    484 		}
    485 	}
    486 
    487 	// A high volume of DNS queries for sub-domain of golang.org
    488 	// would be coordinated by authoritative or recursive server,
    489 	// or stub resolver which implements query-response rate
    490 	// limitation, so we can expect some query successes and more
    491 	// failures including timeout, temporary and other here.
    492 	// As a rule, unknown must not be shown but it might possibly
    493 	// happen due to issue 4856 for now.
    494 	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)
    495 }
    496 
    497 func TestLookupDotsWithLocalSource(t *testing.T) {
    498 	if !supportsIPv4() || !*testIPv4 {
    499 		t.Skip("IPv4 is required")
    500 	}
    501 
    502 	if testenv.Builder() == "" {
    503 		testenv.MustHaveExternalNetwork(t)
    504 	}
    505 
    506 	defer dnsWaitGroup.Wait()
    507 
    508 	for i, fn := range []func() func(){forceGoDNS, forceCgoDNS} {
    509 		fixup := fn()
    510 		if fixup == nil {
    511 			continue
    512 		}
    513 		names, err := LookupAddr("127.0.0.1")
    514 		fixup()
    515 		if err != nil {
    516 			t.Logf("#%d: %v", i, err)
    517 			continue
    518 		}
    519 		mode := "netgo"
    520 		if i == 1 {
    521 			mode = "netcgo"
    522 		}
    523 	loop:
    524 		for i, name := range names {
    525 			if strings.Index(name, ".") == len(name)-1 { // "localhost" not "localhost."
    526 				for j := range names {
    527 					if j == i {
    528 						continue
    529 					}
    530 					if names[j] == name[:len(name)-1] {
    531 						// It's OK if we find the name without the dot,
    532 						// as some systems say 127.0.0.1 localhost localhost.
    533 						continue loop
    534 					}
    535 				}
    536 				t.Errorf("%s: got %s; want %s", mode, name, name[:len(name)-1])
    537 			} else if strings.Contains(name, ".") && !strings.HasSuffix(name, ".") { // "localhost.localdomain." not "localhost.localdomain"
    538 				t.Errorf("%s: got %s; want name ending with trailing dot", mode, name)
    539 			}
    540 		}
    541 	}
    542 }
    543 
    544 func TestLookupDotsWithRemoteSource(t *testing.T) {
    545 	if testenv.Builder() == "" {
    546 		testenv.MustHaveExternalNetwork(t)
    547 	}
    548 
    549 	if !supportsIPv4() || !*testIPv4 {
    550 		t.Skip("IPv4 is required")
    551 	}
    552 
    553 	defer dnsWaitGroup.Wait()
    554 
    555 	if fixup := forceGoDNS(); fixup != nil {
    556 		testDots(t, "go")
    557 		fixup()
    558 	}
    559 	if fixup := forceCgoDNS(); fixup != nil {
    560 		testDots(t, "cgo")
    561 		fixup()
    562 	}
    563 }
    564 
    565 func testDots(t *testing.T, mode string) {
    566 	names, err := LookupAddr("8.8.8.8") // Google dns server
    567 	if err != nil {
    568 		testenv.SkipFlakyNet(t)
    569 		t.Errorf("LookupAddr(8.8.8.8): %v (mode=%v)", err, mode)
    570 	} else {
    571 		for _, name := range names {
    572 			if !strings.HasSuffix(name, ".google.com.") {
    573 				t.Errorf("LookupAddr(8.8.8.8) = %v, want names ending in .google.com. with trailing dot (mode=%v)", names, mode)
    574 				break
    575 			}
    576 		}
    577 	}
    578 
    579 	cname, err := LookupCNAME("www.mit.edu")
    580 	if err != nil {
    581 		testenv.SkipFlakyNet(t)
    582 		t.Errorf("LookupCNAME(www.mit.edu, mode=%v): %v", mode, err)
    583 	} else if !strings.HasSuffix(cname, ".") {
    584 		t.Errorf("LookupCNAME(www.mit.edu) = %v, want cname ending in . with trailing dot (mode=%v)", cname, mode)
    585 	}
    586 
    587 	mxs, err := LookupMX("google.com")
    588 	if err != nil {
    589 		testenv.SkipFlakyNet(t)
    590 		t.Errorf("LookupMX(google.com): %v (mode=%v)", err, mode)
    591 	} else {
    592 		for _, mx := range mxs {
    593 			if !strings.HasSuffix(mx.Host, ".google.com.") {
    594 				t.Errorf("LookupMX(google.com) = %v, want names ending in .google.com. with trailing dot (mode=%v)", mxString(mxs), mode)
    595 				break
    596 			}
    597 		}
    598 	}
    599 
    600 	nss, err := LookupNS("google.com")
    601 	if err != nil {
    602 		testenv.SkipFlakyNet(t)
    603 		t.Errorf("LookupNS(google.com): %v (mode=%v)", err, mode)
    604 	} else {
    605 		for _, ns := range nss {
    606 			if !strings.HasSuffix(ns.Host, ".google.com.") {
    607 				t.Errorf("LookupNS(google.com) = %v, want names ending in .google.com. with trailing dot (mode=%v)", nsString(nss), mode)
    608 				break
    609 			}
    610 		}
    611 	}
    612 
    613 	cname, srvs, err := LookupSRV("xmpp-server", "tcp", "google.com")
    614 	if err != nil {
    615 		testenv.SkipFlakyNet(t)
    616 		t.Errorf("LookupSRV(xmpp-server, tcp, google.com): %v (mode=%v)", err, mode)
    617 	} else {
    618 		if !strings.HasSuffix(cname, ".google.com.") {
    619 			t.Errorf("LookupSRV(xmpp-server, tcp, google.com) returned cname=%v, want name ending in .google.com. with trailing dot (mode=%v)", cname, mode)
    620 		}
    621 		for _, srv := range srvs {
    622 			if !strings.HasSuffix(srv.Target, ".google.com.") {
    623 				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)
    624 				break
    625 			}
    626 		}
    627 	}
    628 }
    629 
    630 func mxString(mxs []*MX) string {
    631 	var buf bytes.Buffer
    632 	sep := ""
    633 	fmt.Fprintf(&buf, "[")
    634 	for _, mx := range mxs {
    635 		fmt.Fprintf(&buf, "%s%s:%d", sep, mx.Host, mx.Pref)
    636 		sep = " "
    637 	}
    638 	fmt.Fprintf(&buf, "]")
    639 	return buf.String()
    640 }
    641 
    642 func nsString(nss []*NS) string {
    643 	var buf bytes.Buffer
    644 	sep := ""
    645 	fmt.Fprintf(&buf, "[")
    646 	for _, ns := range nss {
    647 		fmt.Fprintf(&buf, "%s%s", sep, ns.Host)
    648 		sep = " "
    649 	}
    650 	fmt.Fprintf(&buf, "]")
    651 	return buf.String()
    652 }
    653 
    654 func srvString(srvs []*SRV) string {
    655 	var buf bytes.Buffer
    656 	sep := ""
    657 	fmt.Fprintf(&buf, "[")
    658 	for _, srv := range srvs {
    659 		fmt.Fprintf(&buf, "%s%s:%d:%d:%d", sep, srv.Target, srv.Port, srv.Priority, srv.Weight)
    660 		sep = " "
    661 	}
    662 	fmt.Fprintf(&buf, "]")
    663 	return buf.String()
    664 }
    665 
    666 func TestLookupPort(t *testing.T) {
    667 	// See http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml
    668 	//
    669 	// Please be careful about adding new test cases.
    670 	// There are platforms having incomplete mappings for
    671 	// restricted resource access and security reasons.
    672 	type test struct {
    673 		network string
    674 		name    string
    675 		port    int
    676 		ok      bool
    677 	}
    678 	var tests = []test{
    679 		{"tcp", "0", 0, true},
    680 		{"udp", "0", 0, true},
    681 		{"udp", "domain", 53, true},
    682 
    683 		{"--badnet--", "zzz", 0, false},
    684 		{"tcp", "--badport--", 0, false},
    685 		{"tcp", "-1", 0, false},
    686 		{"tcp", "65536", 0, false},
    687 		{"udp", "-1", 0, false},
    688 		{"udp", "65536", 0, false},
    689 		{"tcp", "123456789", 0, false},
    690 
    691 		// Issue 13610: LookupPort("tcp", "")
    692 		{"tcp", "", 0, true},
    693 		{"tcp4", "", 0, true},
    694 		{"tcp6", "", 0, true},
    695 		{"udp", "", 0, true},
    696 		{"udp4", "", 0, true},
    697 		{"udp6", "", 0, true},
    698 	}
    699 
    700 	switch runtime.GOOS {
    701 	case "android":
    702 		if netGo {
    703 			t.Skipf("not supported on %s without cgo; see golang.org/issues/14576", runtime.GOOS)
    704 		}
    705 	default:
    706 		tests = append(tests, test{"tcp", "http", 80, true})
    707 	}
    708 
    709 	for _, tt := range tests {
    710 		port, err := LookupPort(tt.network, tt.name)
    711 		if port != tt.port || (err == nil) != tt.ok {
    712 			t.Errorf("LookupPort(%q, %q) = %d, %v; want %d, error=%t", tt.network, tt.name, port, err, tt.port, !tt.ok)
    713 		}
    714 		if err != nil {
    715 			if perr := parseLookupPortError(err); perr != nil {
    716 				t.Error(perr)
    717 			}
    718 		}
    719 	}
    720 }
    721 
    722 // Like TestLookupPort but with minimal tests that should always pass
    723 // because the answers are baked-in to the net package.
    724 func TestLookupPort_Minimal(t *testing.T) {
    725 	type test struct {
    726 		network string
    727 		name    string
    728 		port    int
    729 	}
    730 	var tests = []test{
    731 		{"tcp", "http", 80},
    732 		{"tcp", "HTTP", 80}, // case shouldn't matter
    733 		{"tcp", "https", 443},
    734 		{"tcp", "ssh", 22},
    735 		{"tcp", "gopher", 70},
    736 		{"tcp4", "http", 80},
    737 		{"tcp6", "http", 80},
    738 	}
    739 
    740 	for _, tt := range tests {
    741 		port, err := LookupPort(tt.network, tt.name)
    742 		if port != tt.port || err != nil {
    743 			t.Errorf("LookupPort(%q, %q) = %d, %v; want %d, error=nil", tt.network, tt.name, port, err, tt.port)
    744 		}
    745 	}
    746 }
    747 
    748 func TestLookupProtocol_Minimal(t *testing.T) {
    749 	type test struct {
    750 		name string
    751 		want int
    752 	}
    753 	var tests = []test{
    754 		{"tcp", 6},
    755 		{"TcP", 6}, // case shouldn't matter
    756 		{"icmp", 1},
    757 		{"igmp", 2},
    758 		{"udp", 17},
    759 		{"ipv6-icmp", 58},
    760 	}
    761 
    762 	for _, tt := range tests {
    763 		got, err := lookupProtocol(context.Background(), tt.name)
    764 		if got != tt.want || err != nil {
    765 			t.Errorf("LookupProtocol(%q) = %d, %v; want %d, error=nil", tt.name, got, err, tt.want)
    766 		}
    767 	}
    768 
    769 }
    770 
    771 func TestLookupNonLDH(t *testing.T) {
    772 	if runtime.GOOS == "nacl" {
    773 		t.Skip("skip on nacl")
    774 	}
    775 
    776 	defer dnsWaitGroup.Wait()
    777 
    778 	if fixup := forceGoDNS(); fixup != nil {
    779 		defer fixup()
    780 	}
    781 
    782 	// "LDH" stands for letters, digits, and hyphens and is the usual
    783 	// description of standard DNS names.
    784 	// This test is checking that other kinds of names are reported
    785 	// as not found, not reported as invalid names.
    786 	addrs, err := LookupHost("!!!.###.bogus..domain.")
    787 	if err == nil {
    788 		t.Fatalf("lookup succeeded: %v", addrs)
    789 	}
    790 	if !strings.HasSuffix(err.Error(), errNoSuchHost.Error()) {
    791 		t.Fatalf("lookup error = %v, want %v", err, errNoSuchHost)
    792 	}
    793 }
    794