github.com/m10x/go/src@v0.0.0-20220112094212-ba61592315da/net/lookup_test.go (about)

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