github.com/golang-haiku/go-1.4.3@v0.0.0-20190609233734-1f5ae41cc308/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 haiku 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 "runtime" 26 ) 27 28 // A dnsConn represents a DNS transport endpoint. 29 type dnsConn interface { 30 Conn 31 32 // readDNSResponse reads a DNS response message from the DNS 33 // transport endpoint and returns the received DNS response 34 // message. 35 readDNSResponse() (*dnsMsg, error) 36 37 // writeDNSQuery writes a DNS query message to the DNS 38 // connection endpoint. 39 writeDNSQuery(*dnsMsg) error 40 } 41 42 func (c *UDPConn) readDNSResponse() (*dnsMsg, error) { 43 b := make([]byte, 512) // see RFC 1035 44 n, err := c.Read(b) 45 if err != nil { 46 return nil, err 47 } 48 msg := &dnsMsg{} 49 if !msg.Unpack(b[:n]) { 50 return nil, errors.New("cannot unmarshal DNS message") 51 } 52 return msg, nil 53 } 54 55 func (c *UDPConn) writeDNSQuery(msg *dnsMsg) error { 56 b, ok := msg.Pack() 57 if !ok { 58 return errors.New("cannot marshal DNS message") 59 } 60 if _, err := c.Write(b); err != nil { 61 return err 62 } 63 return nil 64 } 65 66 func (c *TCPConn) readDNSResponse() (*dnsMsg, error) { 67 b := make([]byte, 1280) // 1280 is a reasonable initial size for IP over Ethernet, see RFC 4035 68 if _, err := io.ReadFull(c, b[:2]); err != nil { 69 return nil, err 70 } 71 l := int(b[0])<<8 | int(b[1]) 72 if l > len(b) { 73 b = make([]byte, l) 74 } 75 n, err := io.ReadFull(c, b[:l]) 76 if err != nil { 77 return nil, err 78 } 79 msg := &dnsMsg{} 80 if !msg.Unpack(b[:n]) { 81 return nil, errors.New("cannot unmarshal DNS message") 82 } 83 return msg, nil 84 } 85 86 func (c *TCPConn) writeDNSQuery(msg *dnsMsg) error { 87 b, ok := msg.Pack() 88 if !ok { 89 return errors.New("cannot marshal DNS message") 90 } 91 l := uint16(len(b)) 92 b = append([]byte{byte(l >> 8), byte(l)}, b...) 93 if _, err := c.Write(b); err != nil { 94 return err 95 } 96 return nil 97 } 98 99 func (d *Dialer) dialDNS(network, server string) (dnsConn, error) { 100 switch network { 101 case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6": 102 default: 103 return nil, UnknownNetworkError(network) 104 } 105 // Calling Dial here is scary -- we have to be sure not to 106 // dial a name that will require a DNS lookup, or Dial will 107 // call back here to translate it. The DNS config parser has 108 // already checked that all the cfg.servers[i] are IP 109 // addresses, which Dial will use without a DNS lookup. 110 c, err := d.Dial(network, server) 111 if err != nil { 112 return nil, err 113 } 114 switch network { 115 case "tcp", "tcp4", "tcp6": 116 return c.(*TCPConn), nil 117 case "udp", "udp4", "udp6": 118 return c.(*UDPConn), nil 119 } 120 panic("unreachable") 121 } 122 123 // exchange sends a query on the connection and hopes for a response. 124 func exchange(server, name string, qtype uint16, timeout time.Duration) (*dnsMsg, error) { 125 d := Dialer{Timeout: timeout} 126 out := dnsMsg{ 127 dnsMsgHdr: dnsMsgHdr{ 128 recursion_desired: true, 129 }, 130 question: []dnsQuestion{ 131 {name, qtype, dnsClassINET}, 132 }, 133 } 134 for _, network := range []string{"udp", "tcp"} { 135 c, err := d.dialDNS(network, server) 136 if err != nil { 137 return nil, err 138 } 139 defer c.Close() 140 if timeout > 0 { 141 c.SetDeadline(time.Now().Add(timeout)) 142 } 143 out.id = uint16(rand.Int()) ^ uint16(time.Now().UnixNano()) 144 if err := c.writeDNSQuery(&out); err != nil { 145 return nil, err 146 } 147 in, err := c.readDNSResponse() 148 if err != nil { 149 return nil, err 150 } 151 if in.id != out.id { 152 return nil, errors.New("DNS message ID mismatch") 153 } 154 if in.truncated { // see RFC 5966 155 continue 156 } 157 return in, nil 158 } 159 return nil, errors.New("no answer from DNS server") 160 } 161 162 // Do a lookup for a single name, which must be rooted 163 // (otherwise answer will not find the answers). 164 func tryOneName(cfg *dnsConfig, name string, qtype uint16) (string, []dnsRR, error) { 165 if len(cfg.servers) == 0 { 166 return "", nil, &DNSError{Err: "no DNS servers", Name: name} 167 } 168 if len(name) >= 256 { 169 return "", nil, &DNSError{Err: "DNS name too long", Name: name} 170 } 171 timeout := time.Duration(cfg.timeout) * time.Second 172 var lastErr error 173 for i := 0; i < cfg.attempts; i++ { 174 for _, server := range cfg.servers { 175 server = JoinHostPort(server, "53") 176 msg, err := exchange(server, name, qtype, timeout) 177 if err != nil { 178 lastErr = &DNSError{ 179 Err: err.Error(), 180 Name: name, 181 Server: server, 182 } 183 if nerr, ok := err.(Error); ok && nerr.Timeout() { 184 lastErr.(*DNSError).IsTimeout = true 185 } 186 continue 187 } 188 cname, addrs, err := answer(name, server, msg, qtype) 189 if err == nil || err.(*DNSError).Err == noSuchHost { 190 return cname, addrs, err 191 } 192 lastErr = err 193 } 194 } 195 return "", nil, lastErr 196 } 197 198 func convertRR_A(records []dnsRR) []IP { 199 addrs := make([]IP, len(records)) 200 for i, rr := range records { 201 a := rr.(*dnsRR_A).A 202 addrs[i] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a)) 203 } 204 return addrs 205 } 206 207 func convertRR_AAAA(records []dnsRR) []IP { 208 addrs := make([]IP, len(records)) 209 for i, rr := range records { 210 a := make(IP, IPv6len) 211 copy(a, rr.(*dnsRR_AAAA).AAAA[:]) 212 addrs[i] = a 213 } 214 return addrs 215 } 216 217 var cfg struct { 218 ch chan struct{} 219 mu sync.RWMutex // protects dnsConfig and dnserr 220 dnsConfig *dnsConfig 221 dnserr error 222 } 223 var onceLoadConfig sync.Once 224 225 // Assume dns config file is /etc/resolv.conf here 226 func loadDefaultConfig() { 227 if runtime.GOOS != "haiku" { 228 loadConfig("/etc/resolv.conf", 5*time.Second, nil) 229 } else { 230 loadConfig("/boot/system/settings/network/resolv.conf", 5*time.Second, nil) 231 } 232 233 } 234 235 func loadConfig(resolvConfPath string, reloadTime time.Duration, quit <-chan chan struct{}) { 236 var mtime time.Time 237 cfg.ch = make(chan struct{}, 1) 238 if fi, err := os.Stat(resolvConfPath); err != nil { 239 cfg.dnserr = err 240 } else { 241 mtime = fi.ModTime() 242 cfg.dnsConfig, cfg.dnserr = dnsReadConfig(resolvConfPath) 243 } 244 go func() { 245 for { 246 time.Sleep(reloadTime) 247 select { 248 case qresp := <-quit: 249 qresp <- struct{}{} 250 return 251 case <-cfg.ch: 252 } 253 254 // In case of error, we keep the previous config 255 fi, err := os.Stat(resolvConfPath) 256 if err != nil { 257 continue 258 } 259 // If the resolv.conf mtime didn't change, do not reload 260 m := fi.ModTime() 261 if m.Equal(mtime) { 262 continue 263 } 264 mtime = m 265 // In case of error, we keep the previous config 266 ncfg, err := dnsReadConfig(resolvConfPath) 267 if err != nil || len(ncfg.servers) == 0 { 268 continue 269 } 270 cfg.mu.Lock() 271 cfg.dnsConfig = ncfg 272 cfg.dnserr = nil 273 cfg.mu.Unlock() 274 } 275 }() 276 } 277 278 func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error) { 279 if !isDomainName(name) { 280 return name, nil, &DNSError{Err: "invalid domain name", Name: name} 281 } 282 onceLoadConfig.Do(loadDefaultConfig) 283 284 select { 285 case cfg.ch <- struct{}{}: 286 default: 287 } 288 289 cfg.mu.RLock() 290 defer cfg.mu.RUnlock() 291 292 if cfg.dnserr != nil || cfg.dnsConfig == nil { 293 err = cfg.dnserr 294 return 295 } 296 // If name is rooted (trailing dot) or has enough dots, 297 // try it by itself first. 298 rooted := len(name) > 0 && name[len(name)-1] == '.' 299 if rooted || count(name, '.') >= cfg.dnsConfig.ndots { 300 rname := name 301 if !rooted { 302 rname += "." 303 } 304 // Can try as ordinary name. 305 cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype) 306 if rooted || err == nil { 307 return 308 } 309 } 310 311 // Otherwise, try suffixes. 312 for i := 0; i < len(cfg.dnsConfig.search); i++ { 313 rname := name + "." + cfg.dnsConfig.search[i] 314 if rname[len(rname)-1] != '.' { 315 rname += "." 316 } 317 cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype) 318 if err == nil { 319 return 320 } 321 } 322 323 // Last ditch effort: try unsuffixed only if we haven't already, 324 // that is, name is not rooted and has less than ndots dots. 325 if count(name, '.') < cfg.dnsConfig.ndots { 326 cname, addrs, err = tryOneName(cfg.dnsConfig, name+".", qtype) 327 if err == nil { 328 return 329 } 330 } 331 332 if e, ok := err.(*DNSError); ok { 333 // Show original name passed to lookup, not suffixed one. 334 // In general we might have tried many suffixes; showing 335 // just one is misleading. See also golang.org/issue/6324. 336 e.Name = name 337 } 338 return 339 } 340 341 // goLookupHost is the native Go implementation of LookupHost. 342 // Used only if cgoLookupHost refuses to handle the request 343 // (that is, only if cgoLookupHost is the stub in cgo_stub.go). 344 // Normally we let cgo use the C library resolver instead of 345 // depending on our lookup code, so that Go and C get the same 346 // answers. 347 func goLookupHost(name string) (addrs []string, err error) { 348 // Use entries from /etc/hosts if they match. 349 addrs = lookupStaticHost(name) 350 if len(addrs) > 0 { 351 return 352 } 353 ips, err := goLookupIP(name) 354 if err != nil { 355 return 356 } 357 addrs = make([]string, 0, len(ips)) 358 for _, ip := range ips { 359 addrs = append(addrs, ip.String()) 360 } 361 return 362 } 363 364 // goLookupIP is the native Go implementation of LookupIP. 365 // Used only if cgoLookupIP refuses to handle the request 366 // (that is, only if cgoLookupIP is the stub in cgo_stub.go). 367 // Normally we let cgo use the C library resolver instead of 368 // depending on our lookup code, so that Go and C get the same 369 // answers. 370 func goLookupIP(name string) (addrs []IP, err error) { 371 // Use entries from /etc/hosts if possible. 372 haddrs := lookupStaticHost(name) 373 if len(haddrs) > 0 { 374 for _, haddr := range haddrs { 375 if ip := ParseIP(haddr); ip != nil { 376 addrs = append(addrs, ip) 377 } 378 } 379 if len(addrs) > 0 { 380 return 381 } 382 } 383 type racer struct { 384 qtype uint16 385 rrs []dnsRR 386 error 387 } 388 lane := make(chan racer, 1) 389 qtypes := [...]uint16{dnsTypeA, dnsTypeAAAA} 390 for _, qtype := range qtypes { 391 go func(qtype uint16) { 392 _, rrs, err := lookup(name, qtype) 393 lane <- racer{qtype, rrs, err} 394 }(qtype) 395 } 396 var lastErr error 397 for range qtypes { 398 racer := <-lane 399 if racer.error != nil { 400 lastErr = racer.error 401 continue 402 } 403 switch racer.qtype { 404 case dnsTypeA: 405 addrs = append(addrs, convertRR_A(racer.rrs)...) 406 case dnsTypeAAAA: 407 addrs = append(addrs, convertRR_AAAA(racer.rrs)...) 408 } 409 } 410 if len(addrs) == 0 && lastErr != nil { 411 return nil, lastErr 412 } 413 return addrs, nil 414 } 415 416 // goLookupCNAME is the native Go implementation of LookupCNAME. 417 // Used only if cgoLookupCNAME refuses to handle the request 418 // (that is, only if cgoLookupCNAME is the stub in cgo_stub.go). 419 // Normally we let cgo use the C library resolver instead of 420 // depending on our lookup code, so that Go and C get the same 421 // answers. 422 func goLookupCNAME(name string) (cname string, err error) { 423 _, rr, err := lookup(name, dnsTypeCNAME) 424 if err != nil { 425 return 426 } 427 cname = rr[0].(*dnsRR_CNAME).Cname 428 return 429 }