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