OSDN Git Service

libgo: Update to weekly.2012-01-20.
[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 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 }