...
Run Format

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

View as plain text