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