github.com/roboticscm/goman@v0.0.0-20210203095141-87c07b4a0a55/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 if len(name) >= 256 { 168 return "", nil, &DNSError{Err: "DNS name too long", Name: name} 169 } 170 timeout := time.Duration(cfg.timeout) * time.Second 171 var lastErr error 172 for i := 0; i < cfg.attempts; i++ { 173 for _, server := range cfg.servers { 174 server = JoinHostPort(server, "53") 175 msg, err := exchange(server, name, qtype, timeout) 176 if err != nil { 177 lastErr = &DNSError{ 178 Err: err.Error(), 179 Name: name, 180 Server: server, 181 } 182 if nerr, ok := err.(Error); ok && nerr.Timeout() { 183 lastErr.(*DNSError).IsTimeout = true 184 } 185 continue 186 } 187 cname, addrs, err := answer(name, server, msg, qtype) 188 if err == nil || err.(*DNSError).Err == noSuchHost { 189 return cname, addrs, err 190 } 191 lastErr = err 192 } 193 } 194 return "", nil, lastErr 195 } 196 197 func convertRR_A(records []dnsRR) []IP { 198 addrs := make([]IP, len(records)) 199 for i, rr := range records { 200 a := rr.(*dnsRR_A).A 201 addrs[i] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a)) 202 } 203 return addrs 204 } 205 206 func convertRR_AAAA(records []dnsRR) []IP { 207 addrs := make([]IP, len(records)) 208 for i, rr := range records { 209 a := make(IP, IPv6len) 210 copy(a, rr.(*dnsRR_AAAA).AAAA[:]) 211 addrs[i] = a 212 } 213 return addrs 214 } 215 216 var cfg struct { 217 ch chan struct{} 218 mu sync.RWMutex // protects dnsConfig and dnserr 219 dnsConfig *dnsConfig 220 dnserr error 221 } 222 var onceLoadConfig sync.Once 223 224 // Assume dns config file is /etc/resolv.conf here 225 func loadDefaultConfig() { 226 loadConfig("/etc/resolv.conf", 5*time.Second, nil) 227 } 228 229 func loadConfig(resolvConfPath string, reloadTime time.Duration, quit <-chan chan struct{}) { 230 var mtime time.Time 231 cfg.ch = make(chan struct{}, 1) 232 if fi, err := os.Stat(resolvConfPath); err != nil { 233 cfg.dnserr = err 234 } else { 235 mtime = fi.ModTime() 236 cfg.dnsConfig, cfg.dnserr = dnsReadConfig(resolvConfPath) 237 } 238 go func() { 239 for { 240 time.Sleep(reloadTime) 241 select { 242 case qresp := <-quit: 243 qresp <- struct{}{} 244 return 245 case <-cfg.ch: 246 } 247 248 // In case of error, we keep the previous config 249 fi, err := os.Stat(resolvConfPath) 250 if err != nil { 251 continue 252 } 253 // If the resolv.conf mtime didn't change, do not reload 254 m := fi.ModTime() 255 if m.Equal(mtime) { 256 continue 257 } 258 mtime = m 259 // In case of error, we keep the previous config 260 ncfg, err := dnsReadConfig(resolvConfPath) 261 if err != nil || len(ncfg.servers) == 0 { 262 continue 263 } 264 cfg.mu.Lock() 265 cfg.dnsConfig = ncfg 266 cfg.dnserr = nil 267 cfg.mu.Unlock() 268 } 269 }() 270 } 271 272 func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error) { 273 if !isDomainName(name) { 274 return name, nil, &DNSError{Err: "invalid domain name", Name: name} 275 } 276 onceLoadConfig.Do(loadDefaultConfig) 277 278 select { 279 case cfg.ch <- struct{}{}: 280 default: 281 } 282 283 cfg.mu.RLock() 284 defer cfg.mu.RUnlock() 285 286 if cfg.dnserr != nil || cfg.dnsConfig == nil { 287 err = cfg.dnserr 288 return 289 } 290 // If name is rooted (trailing dot) or has enough dots, 291 // try it by itself first. 292 rooted := len(name) > 0 && name[len(name)-1] == '.' 293 if rooted || count(name, '.') >= cfg.dnsConfig.ndots { 294 rname := name 295 if !rooted { 296 rname += "." 297 } 298 // Can try as ordinary name. 299 cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype) 300 if rooted || err == nil { 301 return 302 } 303 } 304 305 // Otherwise, try suffixes. 306 for i := 0; i < len(cfg.dnsConfig.search); i++ { 307 rname := name + "." + cfg.dnsConfig.search[i] 308 if rname[len(rname)-1] != '.' { 309 rname += "." 310 } 311 cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype) 312 if err == nil { 313 return 314 } 315 } 316 317 // Last ditch effort: try unsuffixed only if we haven't already, 318 // that is, name is not rooted and has less than ndots dots. 319 if count(name, '.') < cfg.dnsConfig.ndots { 320 cname, addrs, err = tryOneName(cfg.dnsConfig, name+".", qtype) 321 if err == nil { 322 return 323 } 324 } 325 326 if e, ok := err.(*DNSError); ok { 327 // Show original name passed to lookup, not suffixed one. 328 // In general we might have tried many suffixes; showing 329 // just one is misleading. See also golang.org/issue/6324. 330 e.Name = name 331 } 332 return 333 } 334 335 // goLookupHost is the native Go implementation of LookupHost. 336 // Used only if cgoLookupHost refuses to handle the request 337 // (that is, only if cgoLookupHost is the stub in cgo_stub.go). 338 // Normally we let cgo use the C library resolver instead of 339 // depending on our lookup code, so that Go and C get the same 340 // answers. 341 func goLookupHost(name string) (addrs []string, err error) { 342 // Use entries from /etc/hosts if they match. 343 addrs = lookupStaticHost(name) 344 if len(addrs) > 0 { 345 return 346 } 347 ips, err := goLookupIP(name) 348 if err != nil { 349 return 350 } 351 addrs = make([]string, 0, len(ips)) 352 for _, ip := range ips { 353 addrs = append(addrs, ip.String()) 354 } 355 return 356 } 357 358 // goLookupIP is the native Go implementation of LookupIP. 359 // Used only if cgoLookupIP refuses to handle the request 360 // (that is, only if cgoLookupIP is the stub in cgo_stub.go). 361 // Normally we let cgo use the C library resolver instead of 362 // depending on our lookup code, so that Go and C get the same 363 // answers. 364 func goLookupIP(name string) (addrs []IP, err error) { 365 // Use entries from /etc/hosts if possible. 366 haddrs := lookupStaticHost(name) 367 if len(haddrs) > 0 { 368 for _, haddr := range haddrs { 369 if ip := ParseIP(haddr); ip != nil { 370 addrs = append(addrs, ip) 371 } 372 } 373 if len(addrs) > 0 { 374 return 375 } 376 } 377 type racer struct { 378 qtype uint16 379 rrs []dnsRR 380 error 381 } 382 lane := make(chan racer, 1) 383 qtypes := [...]uint16{dnsTypeA, dnsTypeAAAA} 384 for _, qtype := range qtypes { 385 go func(qtype uint16) { 386 _, rrs, err := lookup(name, qtype) 387 lane <- racer{qtype, rrs, err} 388 }(qtype) 389 } 390 var lastErr error 391 for range qtypes { 392 racer := <-lane 393 if racer.error != nil { 394 lastErr = racer.error 395 continue 396 } 397 switch racer.qtype { 398 case dnsTypeA: 399 addrs = append(addrs, convertRR_A(racer.rrs)...) 400 case dnsTypeAAAA: 401 addrs = append(addrs, convertRR_AAAA(racer.rrs)...) 402 } 403 } 404 if len(addrs) == 0 && lastErr != nil { 405 return nil, lastErr 406 } 407 return addrs, nil 408 } 409 410 // goLookupCNAME is the native Go implementation of LookupCNAME. 411 // Used only if cgoLookupCNAME refuses to handle the request 412 // (that is, only if cgoLookupCNAME is the stub in cgo_stub.go). 413 // Normally we let cgo use the C library resolver instead of 414 // depending on our lookup code, so that Go and C get the same 415 // answers. 416 func goLookupCNAME(name string) (cname string, err error) { 417 _, rr, err := lookup(name, dnsTypeCNAME) 418 if err != nil { 419 return 420 } 421 cname = rr[0].(*dnsRR_CNAME).Cname 422 return 423 }