Source file src/net/lookup_test.go

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

View as plain text