github.com/spotify/syslog-redirector-golang@v0.0.0-20140320174030-4859f03d829a/src/pkg/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 6 7 // DNS client: see RFC 1035. 8 // Has to be linked into package net for Dial. 9 10 // TODO(rsc): 11 // Check periodically whether /etc/resolv.conf has changed. 12 // Could potentially handle many outstanding lookups faster. 13 // Could have a small cache. 14 // Random UDP source port (net.Dial should do that for us). 15 // Random request IDs. 16 17 package net 18 19 import ( 20 "io" 21 "math/rand" 22 "sync" 23 "time" 24 ) 25 26 // Send a request on the connection and hope for a reply. 27 // Up to cfg.attempts attempts. 28 func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, error) { 29 _, useTCP := c.(*TCPConn) 30 if len(name) >= 256 { 31 return nil, &DNSError{Err: "name too long", Name: name} 32 } 33 out := new(dnsMsg) 34 out.id = uint16(rand.Int()) ^ uint16(time.Now().UnixNano()) 35 out.question = []dnsQuestion{ 36 {name, qtype, dnsClassINET}, 37 } 38 out.recursion_desired = true 39 msg, ok := out.Pack() 40 if !ok { 41 return nil, &DNSError{Err: "internal error - cannot pack message", Name: name} 42 } 43 if useTCP { 44 mlen := uint16(len(msg)) 45 msg = append([]byte{byte(mlen >> 8), byte(mlen)}, msg...) 46 } 47 for attempt := 0; attempt < cfg.attempts; attempt++ { 48 n, err := c.Write(msg) 49 if err != nil { 50 return nil, err 51 } 52 53 if cfg.timeout == 0 { 54 c.SetReadDeadline(noDeadline) 55 } else { 56 c.SetReadDeadline(time.Now().Add(time.Duration(cfg.timeout) * time.Second)) 57 } 58 buf := make([]byte, 2000) 59 if useTCP { 60 n, err = io.ReadFull(c, buf[:2]) 61 if err != nil { 62 if e, ok := err.(Error); ok && e.Timeout() { 63 continue 64 } 65 } 66 mlen := int(buf[0])<<8 | int(buf[1]) 67 if mlen > len(buf) { 68 buf = make([]byte, mlen) 69 } 70 n, err = io.ReadFull(c, buf[:mlen]) 71 } else { 72 n, err = c.Read(buf) 73 } 74 if err != nil { 75 if e, ok := err.(Error); ok && e.Timeout() { 76 continue 77 } 78 return nil, err 79 } 80 buf = buf[:n] 81 in := new(dnsMsg) 82 if !in.Unpack(buf) || in.id != out.id { 83 continue 84 } 85 return in, nil 86 } 87 var server string 88 if a := c.RemoteAddr(); a != nil { 89 server = a.String() 90 } 91 return nil, &DNSError{Err: "no answer from server", Name: name, Server: server, IsTimeout: true} 92 } 93 94 // Do a lookup for a single name, which must be rooted 95 // (otherwise answer will not find the answers). 96 func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs []dnsRR, err error) { 97 if len(cfg.servers) == 0 { 98 return "", nil, &DNSError{Err: "no DNS servers", Name: name} 99 } 100 for i := 0; i < len(cfg.servers); i++ { 101 // Calling Dial here is scary -- we have to be sure 102 // not to dial a name that will require a DNS lookup, 103 // or Dial will call back here to translate it. 104 // The DNS config parser has already checked that 105 // all the cfg.servers[i] are IP addresses, which 106 // Dial will use without a DNS lookup. 107 server := cfg.servers[i] + ":53" 108 c, cerr := Dial("udp", server) 109 if cerr != nil { 110 err = cerr 111 continue 112 } 113 msg, merr := exchange(cfg, c, name, qtype) 114 c.Close() 115 if merr != nil { 116 err = merr 117 continue 118 } 119 if msg.truncated { // see RFC 5966 120 c, cerr = Dial("tcp", server) 121 if cerr != nil { 122 err = cerr 123 continue 124 } 125 msg, merr = exchange(cfg, c, name, qtype) 126 c.Close() 127 if merr != nil { 128 err = merr 129 continue 130 } 131 } 132 cname, addrs, err = answer(name, server, msg, qtype) 133 if err == nil || err.(*DNSError).Err == noSuchHost { 134 break 135 } 136 } 137 return 138 } 139 140 func convertRR_A(records []dnsRR) []IP { 141 addrs := make([]IP, len(records)) 142 for i, rr := range records { 143 a := rr.(*dnsRR_A).A 144 addrs[i] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a)) 145 } 146 return addrs 147 } 148 149 func convertRR_AAAA(records []dnsRR) []IP { 150 addrs := make([]IP, len(records)) 151 for i, rr := range records { 152 a := make(IP, IPv6len) 153 copy(a, rr.(*dnsRR_AAAA).AAAA[:]) 154 addrs[i] = a 155 } 156 return addrs 157 } 158 159 var cfg *dnsConfig 160 var dnserr error 161 162 func loadConfig() { cfg, dnserr = dnsReadConfig() } 163 164 var onceLoadConfig sync.Once 165 166 func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error) { 167 if !isDomainName(name) { 168 return name, nil, &DNSError{Err: "invalid domain name", Name: name} 169 } 170 onceLoadConfig.Do(loadConfig) 171 if dnserr != nil || cfg == nil { 172 err = dnserr 173 return 174 } 175 // If name is rooted (trailing dot) or has enough dots, 176 // try it by itself first. 177 rooted := len(name) > 0 && name[len(name)-1] == '.' 178 if rooted || count(name, '.') >= cfg.ndots { 179 rname := name 180 if !rooted { 181 rname += "." 182 } 183 // Can try as ordinary name. 184 cname, addrs, err = tryOneName(cfg, rname, qtype) 185 if err == nil { 186 return 187 } 188 } 189 if rooted { 190 return 191 } 192 193 // Otherwise, try suffixes. 194 for i := 0; i < len(cfg.search); i++ { 195 rname := name + "." + cfg.search[i] 196 if rname[len(rname)-1] != '.' { 197 rname += "." 198 } 199 cname, addrs, err = tryOneName(cfg, rname, qtype) 200 if err == nil { 201 return 202 } 203 } 204 205 // Last ditch effort: try unsuffixed. 206 rname := name 207 if !rooted { 208 rname += "." 209 } 210 cname, addrs, err = tryOneName(cfg, rname, qtype) 211 if err == nil { 212 return 213 } 214 if e, ok := err.(*DNSError); ok { 215 // Show original name passed to lookup, not suffixed one. 216 // In general we might have tried many suffixes; showing 217 // just one is misleading. See also golang.org/issue/6324. 218 e.Name = name 219 } 220 return 221 } 222 223 // goLookupHost is the native Go implementation of LookupHost. 224 // Used only if cgoLookupHost refuses to handle the request 225 // (that is, only if cgoLookupHost is the stub in cgo_stub.go). 226 // Normally we let cgo use the C library resolver instead of 227 // depending on our lookup code, so that Go and C get the same 228 // answers. 229 func goLookupHost(name string) (addrs []string, err error) { 230 // Use entries from /etc/hosts if they match. 231 addrs = lookupStaticHost(name) 232 if len(addrs) > 0 { 233 return 234 } 235 onceLoadConfig.Do(loadConfig) 236 if dnserr != nil || cfg == nil { 237 err = dnserr 238 return 239 } 240 ips, err := goLookupIP(name) 241 if err != nil { 242 return 243 } 244 addrs = make([]string, 0, len(ips)) 245 for _, ip := range ips { 246 addrs = append(addrs, ip.String()) 247 } 248 return 249 } 250 251 // goLookupIP is the native Go implementation of LookupIP. 252 // Used only if cgoLookupIP refuses to handle the request 253 // (that is, only if cgoLookupIP is the stub in cgo_stub.go). 254 // Normally we let cgo use the C library resolver instead of 255 // depending on our lookup code, so that Go and C get the same 256 // answers. 257 func goLookupIP(name string) (addrs []IP, err error) { 258 // Use entries from /etc/hosts if possible. 259 haddrs := lookupStaticHost(name) 260 if len(haddrs) > 0 { 261 for _, haddr := range haddrs { 262 if ip := ParseIP(haddr); ip != nil { 263 addrs = append(addrs, ip) 264 } 265 } 266 if len(addrs) > 0 { 267 return 268 } 269 } 270 onceLoadConfig.Do(loadConfig) 271 if dnserr != nil || cfg == nil { 272 err = dnserr 273 return 274 } 275 var records []dnsRR 276 var cname string 277 var err4, err6 error 278 cname, records, err4 = lookup(name, dnsTypeA) 279 addrs = convertRR_A(records) 280 if cname != "" { 281 name = cname 282 } 283 _, records, err6 = lookup(name, dnsTypeAAAA) 284 if err4 != nil && err6 == nil { 285 // Ignore A error because AAAA lookup succeeded. 286 err4 = nil 287 } 288 if err6 != nil && len(addrs) > 0 { 289 // Ignore AAAA error because A lookup succeeded. 290 err6 = nil 291 } 292 if err4 != nil { 293 return nil, err4 294 } 295 if err6 != nil { 296 return nil, err6 297 } 298 299 addrs = append(addrs, convertRR_AAAA(records)...) 300 return addrs, nil 301 } 302 303 // goLookupCNAME is the native Go implementation of LookupCNAME. 304 // Used only if cgoLookupCNAME refuses to handle the request 305 // (that is, only if cgoLookupCNAME is the stub in cgo_stub.go). 306 // Normally we let cgo use the C library resolver instead of 307 // depending on our lookup code, so that Go and C get the same 308 // answers. 309 func goLookupCNAME(name string) (cname string, err error) { 310 onceLoadConfig.Do(loadConfig) 311 if dnserr != nil || cfg == nil { 312 err = dnserr 313 return 314 } 315 _, rr, err := lookup(name, dnsTypeCNAME) 316 if err != nil { 317 return 318 } 319 cname = rr[0].(*dnsRR_CNAME).Cname 320 return 321 }