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