github.com/d4l3k/go@v0.0.0-20151015000803-65fc379daeda/src/net/dnsclient_unix.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 // +build darwin dragonfly freebsd linux netbsd openbsd solaris 6 7 // DNS client: see RFC 1035. 8 // Has to be linked into package net for Dial. 9 10 // TODO(rsc): 11 // Could potentially handle many outstanding lookups faster. 12 // Could have a small cache. 13 // Random UDP source port (net.Dial should do that for us). 14 // Random request IDs. 15 16 package net 17 18 import ( 19 "errors" 20 "io" 21 "math/rand" 22 "os" 23 "sync" 24 "time" 25 ) 26 27 // A dnsConn represents a DNS transport endpoint. 28 type dnsConn interface { 29 Conn 30 31 // readDNSResponse reads a DNS response message from the DNS 32 // transport endpoint and returns the received DNS response 33 // message. 34 readDNSResponse() (*dnsMsg, error) 35 36 // writeDNSQuery writes a DNS query message to the DNS 37 // connection endpoint. 38 writeDNSQuery(*dnsMsg) error 39 } 40 41 func (c *UDPConn) readDNSResponse() (*dnsMsg, error) { 42 b := make([]byte, 512) // see RFC 1035 43 n, err := c.Read(b) 44 if err != nil { 45 return nil, err 46 } 47 msg := &dnsMsg{} 48 if !msg.Unpack(b[:n]) { 49 return nil, errors.New("cannot unmarshal DNS message") 50 } 51 return msg, nil 52 } 53 54 func (c *UDPConn) writeDNSQuery(msg *dnsMsg) error { 55 b, ok := msg.Pack() 56 if !ok { 57 return errors.New("cannot marshal DNS message") 58 } 59 if _, err := c.Write(b); err != nil { 60 return err 61 } 62 return nil 63 } 64 65 func (c *TCPConn) readDNSResponse() (*dnsMsg, error) { 66 b := make([]byte, 1280) // 1280 is a reasonable initial size for IP over Ethernet, see RFC 4035 67 if _, err := io.ReadFull(c, b[:2]); err != nil { 68 return nil, err 69 } 70 l := int(b[0])<<8 | int(b[1]) 71 if l > len(b) { 72 b = make([]byte, l) 73 } 74 n, err := io.ReadFull(c, b[:l]) 75 if err != nil { 76 return nil, err 77 } 78 msg := &dnsMsg{} 79 if !msg.Unpack(b[:n]) { 80 return nil, errors.New("cannot unmarshal DNS message") 81 } 82 return msg, nil 83 } 84 85 func (c *TCPConn) writeDNSQuery(msg *dnsMsg) error { 86 b, ok := msg.Pack() 87 if !ok { 88 return errors.New("cannot marshal DNS message") 89 } 90 l := uint16(len(b)) 91 b = append([]byte{byte(l >> 8), byte(l)}, b...) 92 if _, err := c.Write(b); err != nil { 93 return err 94 } 95 return nil 96 } 97 98 func (d *Dialer) dialDNS(network, server string) (dnsConn, error) { 99 switch network { 100 case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6": 101 default: 102 return nil, UnknownNetworkError(network) 103 } 104 // Calling Dial here is scary -- we have to be sure not to 105 // dial a name that will require a DNS lookup, or Dial will 106 // call back here to translate it. The DNS config parser has 107 // already checked that all the cfg.servers[i] are IP 108 // addresses, which Dial will use without a DNS lookup. 109 c, err := d.Dial(network, server) 110 if err != nil { 111 return nil, err 112 } 113 switch network { 114 case "tcp", "tcp4", "tcp6": 115 return c.(*TCPConn), nil 116 case "udp", "udp4", "udp6": 117 return c.(*UDPConn), nil 118 } 119 panic("unreachable") 120 } 121 122 // exchange sends a query on the connection and hopes for a response. 123 func exchange(server, name string, qtype uint16, timeout time.Duration) (*dnsMsg, error) { 124 d := Dialer{Timeout: timeout} 125 out := dnsMsg{ 126 dnsMsgHdr: dnsMsgHdr{ 127 recursion_desired: true, 128 }, 129 question: []dnsQuestion{ 130 {name, qtype, dnsClassINET}, 131 }, 132 } 133 for _, network := range []string{"udp", "tcp"} { 134 c, err := d.dialDNS(network, server) 135 if err != nil { 136 return nil, err 137 } 138 defer c.Close() 139 if timeout > 0 { 140 c.SetDeadline(time.Now().Add(timeout)) 141 } 142 out.id = uint16(rand.Int()) ^ uint16(time.Now().UnixNano()) 143 if err := c.writeDNSQuery(&out); err != nil { 144 return nil, err 145 } 146 in, err := c.readDNSResponse() 147 if err != nil { 148 return nil, err 149 } 150 if in.id != out.id { 151 return nil, errors.New("DNS message ID mismatch") 152 } 153 if in.truncated { // see RFC 5966 154 continue 155 } 156 return in, nil 157 } 158 return nil, errors.New("no answer from DNS server") 159 } 160 161 // Do a lookup for a single name, which must be rooted 162 // (otherwise answer will not find the answers). 163 func tryOneName(cfg *dnsConfig, name string, qtype uint16) (string, []dnsRR, error) { 164 if len(cfg.servers) == 0 { 165 return "", nil, &DNSError{Err: "no DNS servers", Name: name} 166 } 167 timeout := time.Duration(cfg.timeout) * time.Second 168 var lastErr error 169 for i := 0; i < cfg.attempts; i++ { 170 for _, server := range cfg.servers { 171 server = JoinHostPort(server, "53") 172 msg, err := exchange(server, name, qtype, timeout) 173 if err != nil { 174 lastErr = &DNSError{ 175 Err: err.Error(), 176 Name: name, 177 Server: server, 178 } 179 if nerr, ok := err.(Error); ok && nerr.Timeout() { 180 lastErr.(*DNSError).IsTimeout = true 181 } 182 continue 183 } 184 cname, rrs, err := answer(name, server, msg, qtype) 185 if err == nil || msg.rcode == dnsRcodeSuccess || msg.rcode == dnsRcodeNameError && msg.recursion_available { 186 return cname, rrs, err 187 } 188 lastErr = err 189 } 190 } 191 return "", nil, lastErr 192 } 193 194 // addrRecordList converts and returns a list of IP addresses from DNS 195 // address records (both A and AAAA). Other record types are ignored. 196 func addrRecordList(rrs []dnsRR) []IPAddr { 197 addrs := make([]IPAddr, 0, 4) 198 for _, rr := range rrs { 199 switch rr := rr.(type) { 200 case *dnsRR_A: 201 addrs = append(addrs, IPAddr{IP: IPv4(byte(rr.A>>24), byte(rr.A>>16), byte(rr.A>>8), byte(rr.A))}) 202 case *dnsRR_AAAA: 203 ip := make(IP, IPv6len) 204 copy(ip, rr.AAAA[:]) 205 addrs = append(addrs, IPAddr{IP: ip}) 206 } 207 } 208 return addrs 209 } 210 211 // A resolverConfig represents a DNS stub resolver configuration. 212 type resolverConfig struct { 213 initOnce sync.Once // guards init of resolverConfig 214 215 // ch is used as a semaphore that only allows one lookup at a 216 // time to recheck resolv.conf. 217 ch chan struct{} // guards lastChecked and modTime 218 lastChecked time.Time // last time resolv.conf was checked 219 modTime time.Time // time of resolv.conf modification 220 221 mu sync.RWMutex // protects dnsConfig 222 dnsConfig *dnsConfig // parsed resolv.conf structure used in lookups 223 } 224 225 var resolvConf resolverConfig 226 227 // init initializes conf and is only called via conf.initOnce. 228 func (conf *resolverConfig) init() { 229 // Set dnsConfig, modTime, and lastChecked so we don't parse 230 // resolv.conf twice the first time. 231 conf.dnsConfig = systemConf().resolv 232 if conf.dnsConfig == nil { 233 conf.dnsConfig = dnsReadConfig("/etc/resolv.conf") 234 } 235 236 if fi, err := os.Stat("/etc/resolv.conf"); err == nil { 237 conf.modTime = fi.ModTime() 238 } 239 conf.lastChecked = time.Now() 240 241 // Prepare ch so that only one update of resolverConfig may 242 // run at once. 243 conf.ch = make(chan struct{}, 1) 244 } 245 246 // tryUpdate tries to update conf with the named resolv.conf file. 247 // The name variable only exists for testing. It is otherwise always 248 // "/etc/resolv.conf". 249 func (conf *resolverConfig) tryUpdate(name string) { 250 conf.initOnce.Do(conf.init) 251 252 // Ensure only one update at a time checks resolv.conf. 253 if !conf.tryAcquireSema() { 254 return 255 } 256 defer conf.releaseSema() 257 258 now := time.Now() 259 if conf.lastChecked.After(now.Add(-5 * time.Second)) { 260 return 261 } 262 conf.lastChecked = now 263 264 if fi, err := os.Stat(name); err == nil { 265 if fi.ModTime().Equal(conf.modTime) { 266 return 267 } 268 conf.modTime = fi.ModTime() 269 } else { 270 // If modTime wasn't set prior, assume nothing has changed. 271 if conf.modTime.IsZero() { 272 return 273 } 274 conf.modTime = time.Time{} 275 } 276 277 dnsConf := dnsReadConfig(name) 278 conf.mu.Lock() 279 conf.dnsConfig = dnsConf 280 conf.mu.Unlock() 281 } 282 283 func (conf *resolverConfig) tryAcquireSema() bool { 284 select { 285 case conf.ch <- struct{}{}: 286 return true 287 default: 288 return false 289 } 290 } 291 292 func (conf *resolverConfig) releaseSema() { 293 <-conf.ch 294 } 295 296 func lookup(name string, qtype uint16) (cname string, rrs []dnsRR, err error) { 297 if !isDomainName(name) { 298 return "", nil, &DNSError{Err: "invalid domain name", Name: name} 299 } 300 resolvConf.tryUpdate("/etc/resolv.conf") 301 resolvConf.mu.RLock() 302 conf := resolvConf.dnsConfig 303 resolvConf.mu.RUnlock() 304 for _, fqdn := range conf.nameList(name) { 305 cname, rrs, err = tryOneName(conf, fqdn, qtype) 306 if err == nil { 307 break 308 } 309 } 310 if err, ok := err.(*DNSError); ok { 311 // Show original name passed to lookup, not suffixed one. 312 // In general we might have tried many suffixes; showing 313 // just one is misleading. See also golang.org/issue/6324. 314 err.Name = name 315 } 316 return 317 } 318 319 // nameList returns a list of names for sequential DNS queries. 320 func (conf *dnsConfig) nameList(name string) []string { 321 // If name is rooted (trailing dot), try only that name. 322 rooted := len(name) > 0 && name[len(name)-1] == '.' 323 if rooted { 324 return []string{name} 325 } 326 // Build list of search choices. 327 names := make([]string, 0, 1+len(conf.search)) 328 // If name has enough dots, try unsuffixed first. 329 if count(name, '.') >= conf.ndots { 330 names = append(names, name+".") 331 } 332 // Try suffixes. 333 for _, suffix := range conf.search { 334 suffixed := name + "." + suffix 335 if suffixed[len(suffixed)-1] != '.' { 336 suffixed += "." 337 } 338 names = append(names, suffixed) 339 } 340 // Try unsuffixed, if not tried first above. 341 if count(name, '.') < conf.ndots { 342 names = append(names, name+".") 343 } 344 return names 345 } 346 347 // hostLookupOrder specifies the order of LookupHost lookup strategies. 348 // It is basically a simplified representation of nsswitch.conf. 349 // "files" means /etc/hosts. 350 type hostLookupOrder int 351 352 const ( 353 // hostLookupCgo means defer to cgo. 354 hostLookupCgo hostLookupOrder = iota 355 hostLookupFilesDNS // files first 356 hostLookupDNSFiles // dns first 357 hostLookupFiles // only files 358 hostLookupDNS // only DNS 359 ) 360 361 var lookupOrderName = map[hostLookupOrder]string{ 362 hostLookupCgo: "cgo", 363 hostLookupFilesDNS: "files,dns", 364 hostLookupDNSFiles: "dns,files", 365 hostLookupFiles: "files", 366 hostLookupDNS: "dns", 367 } 368 369 func (o hostLookupOrder) String() string { 370 if s, ok := lookupOrderName[o]; ok { 371 return s 372 } 373 return "hostLookupOrder=" + itoa(int(o)) + "??" 374 } 375 376 // goLookupHost is the native Go implementation of LookupHost. 377 // Used only if cgoLookupHost refuses to handle the request 378 // (that is, only if cgoLookupHost is the stub in cgo_stub.go). 379 // Normally we let cgo use the C library resolver instead of 380 // depending on our lookup code, so that Go and C get the same 381 // answers. 382 func goLookupHost(name string) (addrs []string, err error) { 383 return goLookupHostOrder(name, hostLookupFilesDNS) 384 } 385 386 func goLookupHostOrder(name string, order hostLookupOrder) (addrs []string, err error) { 387 if order == hostLookupFilesDNS || order == hostLookupFiles { 388 // Use entries from /etc/hosts if they match. 389 addrs = lookupStaticHost(name) 390 if len(addrs) > 0 || order == hostLookupFiles { 391 return 392 } 393 } 394 ips, err := goLookupIPOrder(name, order) 395 if err != nil { 396 return 397 } 398 addrs = make([]string, 0, len(ips)) 399 for _, ip := range ips { 400 addrs = append(addrs, ip.String()) 401 } 402 return 403 } 404 405 // lookup entries from /etc/hosts 406 func goLookupIPFiles(name string) (addrs []IPAddr) { 407 for _, haddr := range lookupStaticHost(name) { 408 haddr, zone := splitHostZone(haddr) 409 if ip := ParseIP(haddr); ip != nil { 410 addr := IPAddr{IP: ip, Zone: zone} 411 addrs = append(addrs, addr) 412 } 413 } 414 sortByRFC6724(addrs) 415 return 416 } 417 418 // goLookupIP is the native Go implementation of LookupIP. 419 // The libc versions are in cgo_*.go. 420 func goLookupIP(name string) (addrs []IPAddr, err error) { 421 return goLookupIPOrder(name, hostLookupFilesDNS) 422 } 423 424 func goLookupIPOrder(name string, order hostLookupOrder) (addrs []IPAddr, err error) { 425 if order == hostLookupFilesDNS || order == hostLookupFiles { 426 addrs = goLookupIPFiles(name) 427 if len(addrs) > 0 || order == hostLookupFiles { 428 return addrs, nil 429 } 430 } 431 if !isDomainName(name) { 432 return nil, &DNSError{Err: "invalid domain name", Name: name} 433 } 434 resolvConf.tryUpdate("/etc/resolv.conf") 435 resolvConf.mu.RLock() 436 conf := resolvConf.dnsConfig 437 resolvConf.mu.RUnlock() 438 type racer struct { 439 rrs []dnsRR 440 error 441 } 442 lane := make(chan racer, 1) 443 qtypes := [...]uint16{dnsTypeA, dnsTypeAAAA} 444 var lastErr error 445 for _, fqdn := range conf.nameList(name) { 446 for _, qtype := range qtypes { 447 go func(qtype uint16) { 448 _, rrs, err := tryOneName(conf, fqdn, qtype) 449 lane <- racer{rrs, err} 450 }(qtype) 451 } 452 for range qtypes { 453 racer := <-lane 454 if racer.error != nil { 455 lastErr = racer.error 456 continue 457 } 458 addrs = append(addrs, addrRecordList(racer.rrs)...) 459 } 460 if len(addrs) > 0 { 461 break 462 } 463 } 464 if lastErr, ok := lastErr.(*DNSError); ok { 465 // Show original name passed to lookup, not suffixed one. 466 // In general we might have tried many suffixes; showing 467 // just one is misleading. See also golang.org/issue/6324. 468 lastErr.Name = name 469 } 470 sortByRFC6724(addrs) 471 if len(addrs) == 0 { 472 if lastErr != nil { 473 return nil, lastErr 474 } 475 if order == hostLookupDNSFiles { 476 addrs = goLookupIPFiles(name) 477 } 478 } 479 return addrs, nil 480 } 481 482 // goLookupCNAME is the native Go implementation of LookupCNAME. 483 // Used only if cgoLookupCNAME refuses to handle the request 484 // (that is, only if cgoLookupCNAME is the stub in cgo_stub.go). 485 // Normally we let cgo use the C library resolver instead of 486 // depending on our lookup code, so that Go and C get the same 487 // answers. 488 func goLookupCNAME(name string) (cname string, err error) { 489 _, rrs, err := lookup(name, dnsTypeCNAME) 490 if err != nil { 491 return 492 } 493 cname = rrs[0].(*dnsRR_CNAME).Cname 494 return 495 } 496 497 // goLookupPTR is the native Go implementation of LookupAddr. 498 // Used only if cgoLookupPTR refuses to handle the request (that is, 499 // only if cgoLookupPTR is the stub in cgo_stub.go). 500 // Normally we let cgo use the C library resolver instead of depending 501 // on our lookup code, so that Go and C get the same answers. 502 func goLookupPTR(addr string) ([]string, error) { 503 names := lookupStaticAddr(addr) 504 if len(names) > 0 { 505 return names, nil 506 } 507 arpa, err := reverseaddr(addr) 508 if err != nil { 509 return nil, err 510 } 511 _, rrs, err := lookup(arpa, dnsTypePTR) 512 if err != nil { 513 return nil, err 514 } 515 ptrs := make([]string, len(rrs)) 516 for i, rr := range rrs { 517 ptrs[i] = rr.(*dnsRR_PTR).Ptr 518 } 519 return ptrs, nil 520 }