OSDN Git Service

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