Home | History | Annotate | Download | only in 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 package net
      6 
      7 import (
      8 	"bytes"
      9 	"fmt"
     10 	"strings"
     11 	"testing"
     12 	"time"
     13 )
     14 
     15 func lookupLocalhost(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, error) {
     16 	switch host {
     17 	case "localhost":
     18 		return []IPAddr{
     19 			{IP: IPv4(127, 0, 0, 1)},
     20 			{IP: IPv6loopback},
     21 		}, nil
     22 	default:
     23 		return fn(host)
     24 	}
     25 }
     26 
     27 // The Lookup APIs use various sources such as local database, DNS or
     28 // mDNS, and may use platform-dependent DNS stub resolver if possible.
     29 // The APIs accept any of forms for a query; host name in various
     30 // encodings, UTF-8 encoded net name, domain name, FQDN or absolute
     31 // FQDN, but the result would be one of the forms and it depends on
     32 // the circumstances.
     33 
     34 var lookupGoogleSRVTests = []struct {
     35 	service, proto, name string
     36 	cname, target        string
     37 }{
     38 	{
     39 		"xmpp-server", "tcp", "google.com",
     40 		"google.com", "google.com",
     41 	},
     42 	{
     43 		"xmpp-server", "tcp", "google.com.",
     44 		"google.com", "google.com",
     45 	},
     46 
     47 	// non-standard back door
     48 	{
     49 		"", "", "_xmpp-server._tcp.google.com",
     50 		"google.com", "google.com",
     51 	},
     52 	{
     53 		"", "", "_xmpp-server._tcp.google.com.",
     54 		"google.com", "google.com",
     55 	},
     56 }
     57 
     58 func TestLookupGoogleSRV(t *testing.T) {
     59 	if testing.Short() || !*testExternal {
     60 		t.Skip("avoid external network")
     61 	}
     62 	if !supportsIPv4 || !*testIPv4 {
     63 		t.Skip("IPv4 is required")
     64 	}
     65 
     66 	for _, tt := range lookupGoogleSRVTests {
     67 		cname, srvs, err := LookupSRV(tt.service, tt.proto, tt.name)
     68 		if err != nil {
     69 			t.Fatal(err)
     70 		}
     71 		if len(srvs) == 0 {
     72 			t.Error("got no record")
     73 		}
     74 		if !strings.HasSuffix(cname, tt.cname) && !strings.HasSuffix(cname, tt.cname+".") {
     75 			t.Errorf("got %s; want %s", cname, tt.cname)
     76 		}
     77 		for _, srv := range srvs {
     78 			if !strings.HasSuffix(srv.Target, tt.target) && !strings.HasSuffix(srv.Target, tt.target+".") {
     79 				t.Errorf("got %v; want a record containing %s", srv, tt.target)
     80 			}
     81 		}
     82 	}
     83 }
     84 
     85 var lookupGmailMXTests = []struct {
     86 	name, host string
     87 }{
     88 	{"gmail.com", "google.com"},
     89 	{"gmail.com.", "google.com"},
     90 }
     91 
     92 func TestLookupGmailMX(t *testing.T) {
     93 	if testing.Short() || !*testExternal {
     94 		t.Skip("avoid external network")
     95 	}
     96 	if !supportsIPv4 || !*testIPv4 {
     97 		t.Skip("IPv4 is required")
     98 	}
     99 
    100 	for _, tt := range lookupGmailMXTests {
    101 		mxs, err := LookupMX(tt.name)
    102 		if err != nil {
    103 			t.Fatal(err)
    104 		}
    105 		if len(mxs) == 0 {
    106 			t.Error("got no record")
    107 		}
    108 		for _, mx := range mxs {
    109 			if !strings.HasSuffix(mx.Host, tt.host) && !strings.HasSuffix(mx.Host, tt.host+".") {
    110 				t.Errorf("got %v; want a record containing %s", mx, tt.host)
    111 			}
    112 		}
    113 	}
    114 }
    115 
    116 var lookupGmailNSTests = []struct {
    117 	name, host string
    118 }{
    119 	{"gmail.com", "google.com"},
    120 	{"gmail.com.", "google.com"},
    121 }
    122 
    123 func TestLookupGmailNS(t *testing.T) {
    124 	if testing.Short() || !*testExternal {
    125 		t.Skip("avoid external network")
    126 	}
    127 	if !supportsIPv4 || !*testIPv4 {
    128 		t.Skip("IPv4 is required")
    129 	}
    130 
    131 	for _, tt := range lookupGmailNSTests {
    132 		nss, err := LookupNS(tt.name)
    133 		if err != nil {
    134 			t.Fatal(err)
    135 		}
    136 		if len(nss) == 0 {
    137 			t.Error("got no record")
    138 		}
    139 		for _, ns := range nss {
    140 			if !strings.HasSuffix(ns.Host, tt.host) && !strings.HasSuffix(ns.Host, tt.host+".") {
    141 				t.Errorf("got %v; want a record containing %s", ns, tt.host)
    142 			}
    143 		}
    144 	}
    145 }
    146 
    147 var lookupGmailTXTTests = []struct {
    148 	name, txt, host string
    149 }{
    150 	{"gmail.com", "spf", "google.com"},
    151 	{"gmail.com.", "spf", "google.com"},
    152 }
    153 
    154 func TestLookupGmailTXT(t *testing.T) {
    155 	if testing.Short() || !*testExternal {
    156 		t.Skip("avoid external network")
    157 	}
    158 	if !supportsIPv4 || !*testIPv4 {
    159 		t.Skip("IPv4 is required")
    160 	}
    161 
    162 	for _, tt := range lookupGmailTXTTests {
    163 		txts, err := LookupTXT(tt.name)
    164 		if err != nil {
    165 			t.Fatal(err)
    166 		}
    167 		if len(txts) == 0 {
    168 			t.Error("got no record")
    169 		}
    170 		for _, txt := range txts {
    171 			if !strings.Contains(txt, tt.txt) || (!strings.HasSuffix(txt, tt.host) && !strings.HasSuffix(txt, tt.host+".")) {
    172 				t.Errorf("got %s; want a record containing %s, %s", txt, tt.txt, tt.host)
    173 			}
    174 		}
    175 	}
    176 }
    177 
    178 var lookupGooglePublicDNSAddrTests = []struct {
    179 	addr, name string
    180 }{
    181 	{"8.8.8.8", ".google.com"},
    182 	{"8.8.4.4", ".google.com"},
    183 	{"2001:4860:4860::8888", ".google.com"},
    184 	{"2001:4860:4860::8844", ".google.com"},
    185 }
    186 
    187 func TestLookupGooglePublicDNSAddr(t *testing.T) {
    188 	if testing.Short() || !*testExternal {
    189 		t.Skip("avoid external network")
    190 	}
    191 	if !supportsIPv4 || !supportsIPv6 || !*testIPv4 || !*testIPv6 {
    192 		t.Skip("both IPv4 and IPv6 are required")
    193 	}
    194 
    195 	for _, tt := range lookupGooglePublicDNSAddrTests {
    196 		names, err := LookupAddr(tt.addr)
    197 		if err != nil {
    198 			t.Fatal(err)
    199 		}
    200 		if len(names) == 0 {
    201 			t.Error("got no record")
    202 		}
    203 		for _, name := range names {
    204 			if !strings.HasSuffix(name, tt.name) && !strings.HasSuffix(name, tt.name+".") {
    205 				t.Errorf("got %s; want a record containing %s", name, tt.name)
    206 			}
    207 		}
    208 	}
    209 }
    210 
    211 var lookupIANACNAMETests = []struct {
    212 	name, cname string
    213 }{
    214 	{"www.iana.org", "icann.org"},
    215 	{"www.iana.org.", "icann.org"},
    216 }
    217 
    218 func TestLookupIANACNAME(t *testing.T) {
    219 	if testing.Short() || !*testExternal {
    220 		t.Skip("avoid external network")
    221 	}
    222 	if !supportsIPv4 || !*testIPv4 {
    223 		t.Skip("IPv4 is required")
    224 	}
    225 
    226 	for _, tt := range lookupIANACNAMETests {
    227 		cname, err := LookupCNAME(tt.name)
    228 		if err != nil {
    229 			t.Fatal(err)
    230 		}
    231 		if !strings.HasSuffix(cname, tt.cname) && !strings.HasSuffix(cname, tt.cname+".") {
    232 			t.Errorf("got %s; want a record containing %s", cname, tt.cname)
    233 		}
    234 	}
    235 }
    236 
    237 var lookupGoogleHostTests = []struct {
    238 	name string
    239 }{
    240 	{"google.com"},
    241 	{"google.com."},
    242 }
    243 
    244 func TestLookupGoogleHost(t *testing.T) {
    245 	if testing.Short() || !*testExternal {
    246 		t.Skip("avoid external network")
    247 	}
    248 	if !supportsIPv4 || !*testIPv4 {
    249 		t.Skip("IPv4 is required")
    250 	}
    251 
    252 	for _, tt := range lookupGoogleHostTests {
    253 		addrs, err := LookupHost(tt.name)
    254 		if err != nil {
    255 			t.Fatal(err)
    256 		}
    257 		if len(addrs) == 0 {
    258 			t.Error("got no record")
    259 		}
    260 		for _, addr := range addrs {
    261 			if ParseIP(addr) == nil {
    262 				t.Errorf("got %q; want a literal IP address", addr)
    263 			}
    264 		}
    265 	}
    266 }
    267 
    268 var lookupGoogleIPTests = []struct {
    269 	name string
    270 }{
    271 	{"google.com"},
    272 	{"google.com."},
    273 }
    274 
    275 func TestLookupGoogleIP(t *testing.T) {
    276 	if testing.Short() || !*testExternal {
    277 		t.Skip("avoid external network")
    278 	}
    279 	if !supportsIPv4 || !*testIPv4 {
    280 		t.Skip("IPv4 is required")
    281 	}
    282 
    283 	for _, tt := range lookupGoogleIPTests {
    284 		ips, err := LookupIP(tt.name)
    285 		if err != nil {
    286 			t.Fatal(err)
    287 		}
    288 		if len(ips) == 0 {
    289 			t.Error("got no record")
    290 		}
    291 		for _, ip := range ips {
    292 			if ip.To4() == nil && ip.To16() == nil {
    293 				t.Errorf("got %v; want an IP address", ip)
    294 			}
    295 		}
    296 	}
    297 }
    298 
    299 var revAddrTests = []struct {
    300 	Addr      string
    301 	Reverse   string
    302 	ErrPrefix string
    303 }{
    304 	{"1.2.3.4", "4.3.2.1.in-addr.arpa.", ""},
    305 	{"245.110.36.114", "114.36.110.245.in-addr.arpa.", ""},
    306 	{"::ffff:12.34.56.78", "78.56.34.12.in-addr.arpa.", ""},
    307 	{"::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.", ""},
    308 	{"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.", ""},
    309 	{"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.", ""},
    310 	{"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.", ""},
    311 	{"1.2.3", "", "unrecognized address"},
    312 	{"1.2.3.4.5", "", "unrecognized address"},
    313 	{"1234:567:bcbca::89a:bcde", "", "unrecognized address"},
    314 	{"1234:567::bcbc:adad::89a:bcde", "", "unrecognized address"},
    315 }
    316 
    317 func TestReverseAddress(t *testing.T) {
    318 	for i, tt := range revAddrTests {
    319 		a, err := reverseaddr(tt.Addr)
    320 		if len(tt.ErrPrefix) > 0 && err == nil {
    321 			t.Errorf("#%d: expected %q, got <nil> (error)", i, tt.ErrPrefix)
    322 			continue
    323 		}
    324 		if len(tt.ErrPrefix) == 0 && err != nil {
    325 			t.Errorf("#%d: expected <nil>, got %q (error)", i, err)
    326 		}
    327 		if err != nil && err.(*DNSError).Err != tt.ErrPrefix {
    328 			t.Errorf("#%d: expected %q, got %q (mismatched error)", i, tt.ErrPrefix, err.(*DNSError).Err)
    329 		}
    330 		if a != tt.Reverse {
    331 			t.Errorf("#%d: expected %q, got %q (reverse address)", i, tt.Reverse, a)
    332 		}
    333 	}
    334 }
    335 
    336 func TestLookupIPDeadline(t *testing.T) {
    337 	if !*testDNSFlood {
    338 		t.Skip("test disabled; use -dnsflood to enable")
    339 	}
    340 
    341 	const N = 5000
    342 	const timeout = 3 * time.Second
    343 	c := make(chan error, 2*N)
    344 	for i := 0; i < N; i++ {
    345 		name := fmt.Sprintf("%d.net-test.golang.org", i)
    346 		go func() {
    347 			_, err := lookupIPDeadline(name, time.Now().Add(timeout/2))
    348 			c <- err
    349 		}()
    350 		go func() {
    351 			_, err := lookupIPDeadline(name, time.Now().Add(timeout))
    352 			c <- err
    353 		}()
    354 	}
    355 	qstats := struct {
    356 		succeeded, failed         int
    357 		timeout, temporary, other int
    358 		unknown                   int
    359 	}{}
    360 	deadline := time.After(timeout + time.Second)
    361 	for i := 0; i < 2*N; i++ {
    362 		select {
    363 		case <-deadline:
    364 			t.Fatal("deadline exceeded")
    365 		case err := <-c:
    366 			switch err := err.(type) {
    367 			case nil:
    368 				qstats.succeeded++
    369 			case Error:
    370 				qstats.failed++
    371 				if err.Timeout() {
    372 					qstats.timeout++
    373 				}
    374 				if err.Temporary() {
    375 					qstats.temporary++
    376 				}
    377 				if !err.Timeout() && !err.Temporary() {
    378 					qstats.other++
    379 				}
    380 			default:
    381 				qstats.failed++
    382 				qstats.unknown++
    383 			}
    384 		}
    385 	}
    386 
    387 	// A high volume of DNS queries for sub-domain of golang.org
    388 	// would be coordinated by authoritative or recursive server,
    389 	// or stub resolver which implements query-response rate
    390 	// limitation, so we can expect some query successes and more
    391 	// failures including timeout, temporary and other here.
    392 	// As a rule, unknown must not be shown but it might possibly
    393 	// happen due to issue 4856 for now.
    394 	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)
    395 }
    396 
    397 func TestLookupDots(t *testing.T) {
    398 	if testing.Short() || !*testExternal {
    399 		t.Skipf("skipping external network test")
    400 	}
    401 
    402 	fixup := forceGoDNS()
    403 	defer fixup()
    404 	testDots(t, "go")
    405 
    406 	if forceCgoDNS() {
    407 		testDots(t, "cgo")
    408 	}
    409 }
    410 
    411 func testDots(t *testing.T, mode string) {
    412 	names, err := LookupAddr("8.8.8.8") // Google dns server
    413 	if err != nil {
    414 		t.Errorf("LookupAddr(8.8.8.8): %v (mode=%v)", err, mode)
    415 	} else {
    416 		for _, name := range names {
    417 			if !strings.HasSuffix(name, ".google.com.") {
    418 				t.Errorf("LookupAddr(8.8.8.8) = %v, want names ending in .google.com. with trailing dot (mode=%v)", names, mode)
    419 				break
    420 			}
    421 		}
    422 	}
    423 
    424 	cname, err := LookupCNAME("www.mit.edu")
    425 	if err != nil || !strings.HasSuffix(cname, ".") {
    426 		t.Errorf("LookupCNAME(www.mit.edu) = %v, %v, want cname ending in . with trailing dot (mode=%v)", cname, err, mode)
    427 	}
    428 
    429 	mxs, err := LookupMX("google.com")
    430 	if err != nil {
    431 		t.Errorf("LookupMX(google.com): %v (mode=%v)", err, mode)
    432 	} else {
    433 		for _, mx := range mxs {
    434 			if !strings.HasSuffix(mx.Host, ".google.com.") {
    435 				t.Errorf("LookupMX(google.com) = %v, want names ending in .google.com. with trailing dot (mode=%v)", mxString(mxs), mode)
    436 				break
    437 			}
    438 		}
    439 	}
    440 
    441 	nss, err := LookupNS("google.com")
    442 	if err != nil {
    443 		t.Errorf("LookupNS(google.com): %v (mode=%v)", err, mode)
    444 	} else {
    445 		for _, ns := range nss {
    446 			if !strings.HasSuffix(ns.Host, ".google.com.") {
    447 				t.Errorf("LookupNS(google.com) = %v, want names ending in .google.com. with trailing dot (mode=%v)", nsString(nss), mode)
    448 				break
    449 			}
    450 		}
    451 	}
    452 
    453 	cname, srvs, err := LookupSRV("xmpp-server", "tcp", "google.com")
    454 	if err != nil {
    455 		t.Errorf("LookupSRV(xmpp-server, tcp, google.com): %v (mode=%v)", err, mode)
    456 	} else {
    457 		if !strings.HasSuffix(cname, ".google.com.") {
    458 			t.Errorf("LookupSRV(xmpp-server, tcp, google.com) returned cname=%v, want name ending in .google.com. with trailing dot (mode=%v)", cname, mode)
    459 		}
    460 		for _, srv := range srvs {
    461 			if !strings.HasSuffix(srv.Target, ".google.com.") {
    462 				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)
    463 				break
    464 			}
    465 		}
    466 	}
    467 }
    468 
    469 func mxString(mxs []*MX) string {
    470 	var buf bytes.Buffer
    471 	sep := ""
    472 	fmt.Fprintf(&buf, "[")
    473 	for _, mx := range mxs {
    474 		fmt.Fprintf(&buf, "%s%s:%d", sep, mx.Host, mx.Pref)
    475 		sep = " "
    476 	}
    477 	fmt.Fprintf(&buf, "]")
    478 	return buf.String()
    479 }
    480 
    481 func nsString(nss []*NS) string {
    482 	var buf bytes.Buffer
    483 	sep := ""
    484 	fmt.Fprintf(&buf, "[")
    485 	for _, ns := range nss {
    486 		fmt.Fprintf(&buf, "%s%s", sep, ns.Host)
    487 		sep = " "
    488 	}
    489 	fmt.Fprintf(&buf, "]")
    490 	return buf.String()
    491 }
    492 
    493 func srvString(srvs []*SRV) string {
    494 	var buf bytes.Buffer
    495 	sep := ""
    496 	fmt.Fprintf(&buf, "[")
    497 	for _, srv := range srvs {
    498 		fmt.Fprintf(&buf, "%s%s:%d:%d:%d", sep, srv.Target, srv.Port, srv.Priority, srv.Weight)
    499 		sep = " "
    500 	}
    501 	fmt.Fprintf(&buf, "]")
    502 	return buf.String()
    503 }
    504