Source file src/pkg/net/dnsclient_unix.go
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 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 "math/rand" 21 "sync" 22 "time" 23 ) 24 25 // Send a request on the connection and hope for a reply. 26 // Up to cfg.attempts attempts. 27 func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, error) { 28 if len(name) >= 256 { 29 return nil, &DNSError{Err: "name too long", Name: name} 30 } 31 out := new(dnsMsg) 32 out.id = uint16(rand.Int()) ^ uint16(time.Now().UnixNano()) 33 out.question = []dnsQuestion{ 34 {name, qtype, dnsClassINET}, 35 } 36 out.recursion_desired = true 37 msg, ok := out.Pack() 38 if !ok { 39 return nil, &DNSError{Err: "internal error - cannot pack message", Name: name} 40 } 41 42 for attempt := 0; attempt < cfg.attempts; attempt++ { 43 n, err := c.Write(msg) 44 if err != nil { 45 return nil, err 46 } 47 48 if cfg.timeout == 0 { 49 c.SetReadDeadline(time.Time{}) 50 } else { 51 c.SetReadDeadline(time.Now().Add(time.Duration(cfg.timeout) * time.Second)) 52 } 53 54 buf := make([]byte, 2000) // More than enough. 55 n, err = c.Read(buf) 56 if err != nil { 57 if e, ok := err.(Error); ok && e.Timeout() { 58 continue 59 } 60 return nil, err 61 } 62 buf = buf[0:n] 63 in := new(dnsMsg) 64 if !in.Unpack(buf) || in.id != out.id { 65 continue 66 } 67 return in, nil 68 } 69 var server string 70 if a := c.RemoteAddr(); a != nil { 71 server = a.String() 72 } 73 return nil, &DNSError{Err: "no answer from server", Name: name, Server: server, IsTimeout: true} 74 } 75 76 // Do a lookup for a single name, which must be rooted 77 // (otherwise answer will not find the answers). 78 func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs []dnsRR, err error) { 79 if len(cfg.servers) == 0 { 80 return "", nil, &DNSError{Err: "no DNS servers", Name: name} 81 } 82 for i := 0; i < len(cfg.servers); i++ { 83 // Calling Dial here is scary -- we have to be sure 84 // not to dial a name that will require a DNS lookup, 85 // or Dial will call back here to translate it. 86 // The DNS config parser has already checked that 87 // all the cfg.servers[i] are IP addresses, which 88 // Dial will use without a DNS lookup. 89 server := cfg.servers[i] + ":53" 90 c, cerr := Dial("udp", server) 91 if cerr != nil { 92 err = cerr 93 continue 94 } 95 msg, merr := exchange(cfg, c, name, qtype) 96 c.Close() 97 if merr != nil { 98 err = merr 99 continue 100 } 101 cname, addrs, err = answer(name, server, msg, qtype) 102 if err == nil || err.(*DNSError).Err == noSuchHost { 103 break 104 } 105 } 106 return 107 } 108 109 func convertRR_A(records []dnsRR) []IP { 110 addrs := make([]IP, len(records)) 111 for i, rr := range records { 112 a := rr.(*dnsRR_A).A 113 addrs[i] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a)) 114 } 115 return addrs 116 } 117 118 func convertRR_AAAA(records []dnsRR) []IP { 119 addrs := make([]IP, len(records)) 120 for i, rr := range records { 121 a := make(IP, IPv6len) 122 copy(a, rr.(*dnsRR_AAAA).AAAA[:]) 123 addrs[i] = a 124 } 125 return addrs 126 } 127 128 var cfg *dnsConfig 129 var dnserr error 130 131 func loadConfig() { cfg, dnserr = dnsReadConfig() } 132 133 var onceLoadConfig sync.Once 134 135 func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error) { 136 if !isDomainName(name) { 137 return name, nil, &DNSError{Err: "invalid domain name", Name: name} 138 } 139 onceLoadConfig.Do(loadConfig) 140 if dnserr != nil || cfg == nil { 141 err = dnserr 142 return 143 } 144 // If name is rooted (trailing dot) or has enough dots, 145 // try it by itself first. 146 rooted := len(name) > 0 && name[len(name)-1] == '.' 147 if rooted || count(name, '.') >= cfg.ndots { 148 rname := name 149 if !rooted { 150 rname += "." 151 } 152 // Can try as ordinary name. 153 cname, addrs, err = tryOneName(cfg, rname, qtype) 154 if err == nil { 155 return 156 } 157 } 158 if rooted { 159 return 160 } 161 162 // Otherwise, try suffixes. 163 for i := 0; i < len(cfg.search); i++ { 164 rname := name + "." + cfg.search[i] 165 if rname[len(rname)-1] != '.' { 166 rname += "." 167 } 168 cname, addrs, err = tryOneName(cfg, rname, qtype) 169 if err == nil { 170 return 171 } 172 } 173 174 // Last ditch effort: try unsuffixed. 175 rname := name 176 if !rooted { 177 rname += "." 178 } 179 cname, addrs, err = tryOneName(cfg, rname, qtype) 180 if err == nil { 181 return 182 } 183 return 184 } 185 186 // goLookupHost is the native Go implementation of LookupHost. 187 // Used only if cgoLookupHost refuses to handle the request 188 // (that is, only if cgoLookupHost is the stub in cgo_stub.go). 189 // Normally we let cgo use the C library resolver instead of 190 // depending on our lookup code, so that Go and C get the same 191 // answers. 192 func goLookupHost(name string) (addrs []string, err error) { 193 // Use entries from /etc/hosts if they match. 194 addrs = lookupStaticHost(name) 195 if len(addrs) > 0 { 196 return 197 } 198 onceLoadConfig.Do(loadConfig) 199 if dnserr != nil || cfg == nil { 200 err = dnserr 201 return 202 } 203 ips, err := goLookupIP(name) 204 if err != nil { 205 return 206 } 207 addrs = make([]string, 0, len(ips)) 208 for _, ip := range ips { 209 addrs = append(addrs, ip.String()) 210 } 211 return 212 } 213 214 // goLookupIP is the native Go implementation of LookupIP. 215 // Used only if cgoLookupIP refuses to handle the request 216 // (that is, only if cgoLookupIP is the stub in cgo_stub.go). 217 // Normally we let cgo use the C library resolver instead of 218 // depending on our lookup code, so that Go and C get the same 219 // answers. 220 func goLookupIP(name string) (addrs []IP, err error) { 221 // Use entries from /etc/hosts if possible. 222 haddrs := lookupStaticHost(name) 223 if len(haddrs) > 0 { 224 for _, haddr := range haddrs { 225 if ip := ParseIP(haddr); ip != nil { 226 addrs = append(addrs, ip) 227 } 228 } 229 if len(addrs) > 0 { 230 return 231 } 232 } 233 onceLoadConfig.Do(loadConfig) 234 if dnserr != nil || cfg == nil { 235 err = dnserr 236 return 237 } 238 var records []dnsRR 239 var cname string 240 cname, records, err = lookup(name, dnsTypeA) 241 if err != nil { 242 return 243 } 244 addrs = convertRR_A(records) 245 if cname != "" { 246 name = cname 247 } 248 _, records, err = lookup(name, dnsTypeAAAA) 249 if err != nil && len(addrs) > 0 { 250 // Ignore error because A lookup succeeded. 251 err = nil 252 } 253 if err != nil { 254 return 255 } 256 addrs = append(addrs, convertRR_AAAA(records)...) 257 return 258 } 259 260 // goLookupCNAME is the native Go implementation of LookupCNAME. 261 // Used only if cgoLookupCNAME refuses to handle the request 262 // (that is, only if cgoLookupCNAME is the stub in cgo_stub.go). 263 // Normally we let cgo use the C library resolver instead of 264 // depending on our lookup code, so that Go and C get the same 265 // answers. 266 func goLookupCNAME(name string) (cname string, err error) { 267 onceLoadConfig.Do(loadConfig) 268 if dnserr != nil || cfg == nil { 269 err = dnserr 270 return 271 } 272 _, rr, err := lookup(name, dnsTypeCNAME) 273 if err != nil { 274 return 275 } 276 cname = rr[0].(*dnsRR_CNAME).Cname 277 return 278 }