libgo: update to go1.9
[official-gcc.git] / libgo / go / net / dnsclient_unix.go
blobff6a4f69dcda04ee83121ed60da6f0fefa2ba747
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.
5 // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
7 // DNS client: see RFC 1035.
8 // Has to be linked into package net for Dial.
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.
16 package net
18 import (
19 "context"
20 "errors"
21 "io"
22 "math/rand"
23 "os"
24 "sync"
25 "time"
28 // A dnsConn represents a DNS transport endpoint.
29 type dnsConn interface {
30 io.Closer
32 SetDeadline(time.Time) error
34 // dnsRoundTrip executes a single DNS transaction, returning a
35 // DNS response message for the provided DNS query message.
36 dnsRoundTrip(query *dnsMsg) (*dnsMsg, error)
39 // dnsPacketConn implements the dnsConn interface for RFC 1035's
40 // "UDP usage" transport mechanism. Conn is a packet-oriented connection,
41 // such as a *UDPConn.
42 type dnsPacketConn struct {
43 Conn
46 func (c *dnsPacketConn) dnsRoundTrip(query *dnsMsg) (*dnsMsg, error) {
47 b, ok := query.Pack()
48 if !ok {
49 return nil, errors.New("cannot marshal DNS message")
51 if _, err := c.Write(b); err != nil {
52 return nil, err
55 b = make([]byte, 512) // see RFC 1035
56 for {
57 n, err := c.Read(b)
58 if err != nil {
59 return nil, err
61 resp := &dnsMsg{}
62 if !resp.Unpack(b[:n]) || !resp.IsResponseTo(query) {
63 // Ignore invalid responses as they may be malicious
64 // forgery attempts. Instead continue waiting until
65 // timeout. See golang.org/issue/13281.
66 continue
68 return resp, nil
72 // dnsStreamConn implements the dnsConn interface for RFC 1035's
73 // "TCP usage" transport mechanism. Conn is a stream-oriented connection,
74 // such as a *TCPConn.
75 type dnsStreamConn struct {
76 Conn
79 func (c *dnsStreamConn) dnsRoundTrip(query *dnsMsg) (*dnsMsg, error) {
80 b, ok := query.Pack()
81 if !ok {
82 return nil, errors.New("cannot marshal DNS message")
84 l := len(b)
85 b = append([]byte{byte(l >> 8), byte(l)}, b...)
86 if _, err := c.Write(b); err != nil {
87 return nil, err
90 b = make([]byte, 1280) // 1280 is a reasonable initial size for IP over Ethernet, see RFC 4035
91 if _, err := io.ReadFull(c, b[:2]); err != nil {
92 return nil, err
94 l = int(b[0])<<8 | int(b[1])
95 if l > len(b) {
96 b = make([]byte, l)
98 n, err := io.ReadFull(c, b[:l])
99 if err != nil {
100 return nil, err
102 resp := &dnsMsg{}
103 if !resp.Unpack(b[:n]) {
104 return nil, errors.New("cannot unmarshal DNS message")
106 if !resp.IsResponseTo(query) {
107 return nil, errors.New("invalid DNS response")
109 return resp, nil
112 // exchange sends a query on the connection and hopes for a response.
113 func (r *Resolver) exchange(ctx context.Context, server, name string, qtype uint16, timeout time.Duration) (*dnsMsg, error) {
114 out := dnsMsg{
115 dnsMsgHdr: dnsMsgHdr{
116 recursion_desired: true,
118 question: []dnsQuestion{
119 {name, qtype, dnsClassINET},
122 for _, network := range []string{"udp", "tcp"} {
123 // TODO(mdempsky): Refactor so defers from UDP-based
124 // exchanges happen before TCP-based exchange.
126 ctx, cancel := context.WithDeadline(ctx, time.Now().Add(timeout))
127 defer cancel()
129 c, err := r.dial(ctx, network, server)
130 if err != nil {
131 return nil, err
133 defer c.Close()
134 if d, ok := ctx.Deadline(); ok && !d.IsZero() {
135 c.SetDeadline(d)
137 out.id = uint16(rand.Int()) ^ uint16(time.Now().UnixNano())
138 in, err := c.dnsRoundTrip(&out)
139 if err != nil {
140 return nil, mapErr(err)
142 if in.truncated { // see RFC 5966
143 continue
145 return in, nil
147 return nil, errors.New("no answer from DNS server")
150 // Do a lookup for a single name, which must be rooted
151 // (otherwise answer will not find the answers).
152 func (r *Resolver) tryOneName(ctx context.Context, cfg *dnsConfig, name string, qtype uint16) (string, []dnsRR, error) {
153 var lastErr error
154 serverOffset := cfg.serverOffset()
155 sLen := uint32(len(cfg.servers))
157 for i := 0; i < cfg.attempts; i++ {
158 for j := uint32(0); j < sLen; j++ {
159 server := cfg.servers[(serverOffset+j)%sLen]
161 msg, err := r.exchange(ctx, server, name, qtype, cfg.timeout)
162 if err != nil {
163 lastErr = &DNSError{
164 Err: err.Error(),
165 Name: name,
166 Server: server,
168 if nerr, ok := err.(Error); ok && nerr.Timeout() {
169 lastErr.(*DNSError).IsTimeout = true
171 // Set IsTemporary for socket-level errors. Note that this flag
172 // may also be used to indicate a SERVFAIL response.
173 if _, ok := err.(*OpError); ok {
174 lastErr.(*DNSError).IsTemporary = true
176 continue
178 // libresolv continues to the next server when it receives
179 // an invalid referral response. See golang.org/issue/15434.
180 if msg.rcode == dnsRcodeSuccess && !msg.authoritative && !msg.recursion_available && len(msg.answer) == 0 && len(msg.extra) == 0 {
181 lastErr = &DNSError{Err: "lame referral", Name: name, Server: server}
182 continue
184 cname, rrs, err := answer(name, server, msg, qtype)
185 // If answer errored for rcodes dnsRcodeSuccess or dnsRcodeNameError,
186 // it means the response in msg was not useful and trying another
187 // server probably won't help. Return now in those cases.
188 // TODO: indicate this in a more obvious way, such as a field on DNSError?
189 if err == nil || msg.rcode == dnsRcodeSuccess || msg.rcode == dnsRcodeNameError {
190 return cname, rrs, err
192 lastErr = err
195 return "", nil, lastErr
198 // addrRecordList converts and returns a list of IP addresses from DNS
199 // address records (both A and AAAA). Other record types are ignored.
200 func addrRecordList(rrs []dnsRR) []IPAddr {
201 addrs := make([]IPAddr, 0, 4)
202 for _, rr := range rrs {
203 switch rr := rr.(type) {
204 case *dnsRR_A:
205 addrs = append(addrs, IPAddr{IP: IPv4(byte(rr.A>>24), byte(rr.A>>16), byte(rr.A>>8), byte(rr.A))})
206 case *dnsRR_AAAA:
207 ip := make(IP, IPv6len)
208 copy(ip, rr.AAAA[:])
209 addrs = append(addrs, IPAddr{IP: ip})
212 return addrs
215 // A resolverConfig represents a DNS stub resolver configuration.
216 type resolverConfig struct {
217 initOnce sync.Once // guards init of resolverConfig
219 // ch is used as a semaphore that only allows one lookup at a
220 // time to recheck resolv.conf.
221 ch chan struct{} // guards lastChecked and modTime
222 lastChecked time.Time // last time resolv.conf was checked
224 mu sync.RWMutex // protects dnsConfig
225 dnsConfig *dnsConfig // parsed resolv.conf structure used in lookups
228 var resolvConf resolverConfig
230 // init initializes conf and is only called via conf.initOnce.
231 func (conf *resolverConfig) init() {
232 // Set dnsConfig and lastChecked so we don't parse
233 // resolv.conf twice the first time.
234 conf.dnsConfig = systemConf().resolv
235 if conf.dnsConfig == nil {
236 conf.dnsConfig = dnsReadConfig("/etc/resolv.conf")
238 conf.lastChecked = time.Now()
240 // Prepare ch so that only one update of resolverConfig may
241 // run at once.
242 conf.ch = make(chan struct{}, 1)
245 // tryUpdate tries to update conf with the named resolv.conf file.
246 // The name variable only exists for testing. It is otherwise always
247 // "/etc/resolv.conf".
248 func (conf *resolverConfig) tryUpdate(name string) {
249 conf.initOnce.Do(conf.init)
251 // Ensure only one update at a time checks resolv.conf.
252 if !conf.tryAcquireSema() {
253 return
255 defer conf.releaseSema()
257 now := time.Now()
258 if conf.lastChecked.After(now.Add(-5 * time.Second)) {
259 return
261 conf.lastChecked = now
263 var mtime time.Time
264 if fi, err := os.Stat(name); err == nil {
265 mtime = fi.ModTime()
267 if mtime.Equal(conf.dnsConfig.mtime) {
268 return
271 dnsConf := dnsReadConfig(name)
272 conf.mu.Lock()
273 conf.dnsConfig = dnsConf
274 conf.mu.Unlock()
277 func (conf *resolverConfig) tryAcquireSema() bool {
278 select {
279 case conf.ch <- struct{}{}:
280 return true
281 default:
282 return false
286 func (conf *resolverConfig) releaseSema() {
287 <-conf.ch
290 func (r *Resolver) lookup(ctx context.Context, name string, qtype uint16) (cname string, rrs []dnsRR, err error) {
291 if !isDomainName(name) {
292 // We used to use "invalid domain name" as the error,
293 // but that is a detail of the specific lookup mechanism.
294 // Other lookups might allow broader name syntax
295 // (for example Multicast DNS allows UTF-8; see RFC 6762).
296 // For consistency with libc resolvers, report no such host.
297 return "", nil, &DNSError{Err: errNoSuchHost.Error(), Name: name}
299 resolvConf.tryUpdate("/etc/resolv.conf")
300 resolvConf.mu.RLock()
301 conf := resolvConf.dnsConfig
302 resolvConf.mu.RUnlock()
303 for _, fqdn := range conf.nameList(name) {
304 cname, rrs, err = r.tryOneName(ctx, conf, fqdn, qtype)
305 if err == nil {
306 break
308 if nerr, ok := err.(Error); ok && nerr.Temporary() && r.StrictErrors {
309 // If we hit a temporary error with StrictErrors enabled,
310 // stop immediately instead of trying more names.
311 break
314 if err, ok := err.(*DNSError); ok {
315 // Show original name passed to lookup, not suffixed one.
316 // In general we might have tried many suffixes; showing
317 // just one is misleading. See also golang.org/issue/6324.
318 err.Name = name
320 return
323 // avoidDNS reports whether this is a hostname for which we should not
324 // use DNS. Currently this includes only .onion, per RFC 7686. See
325 // golang.org/issue/13705. Does not cover .local names (RFC 6762),
326 // see golang.org/issue/16739.
327 func avoidDNS(name string) bool {
328 if name == "" {
329 return true
331 if name[len(name)-1] == '.' {
332 name = name[:len(name)-1]
334 return stringsHasSuffixFold(name, ".onion")
337 // nameList returns a list of names for sequential DNS queries.
338 func (conf *dnsConfig) nameList(name string) []string {
339 if avoidDNS(name) {
340 return nil
343 // Check name length (see isDomainName).
344 l := len(name)
345 rooted := l > 0 && name[l-1] == '.'
346 if l > 254 || l == 254 && rooted {
347 return nil
350 // If name is rooted (trailing dot), try only that name.
351 if rooted {
352 return []string{name}
355 hasNdots := count(name, '.') >= conf.ndots
356 name += "."
359 // Build list of search choices.
360 names := make([]string, 0, 1+len(conf.search))
361 // If name has enough dots, try unsuffixed first.
362 if hasNdots {
363 names = append(names, name)
365 // Try suffixes that are not too long (see isDomainName).
366 for _, suffix := range conf.search {
367 if l+len(suffix) <= 254 {
368 names = append(names, name+suffix)
371 // Try unsuffixed, if not tried first above.
372 if !hasNdots {
373 names = append(names, name)
375 return names
378 // hostLookupOrder specifies the order of LookupHost lookup strategies.
379 // It is basically a simplified representation of nsswitch.conf.
380 // "files" means /etc/hosts.
381 type hostLookupOrder int
383 const (
384 // hostLookupCgo means defer to cgo.
385 hostLookupCgo hostLookupOrder = iota
386 hostLookupFilesDNS // files first
387 hostLookupDNSFiles // dns first
388 hostLookupFiles // only files
389 hostLookupDNS // only DNS
392 var lookupOrderName = map[hostLookupOrder]string{
393 hostLookupCgo: "cgo",
394 hostLookupFilesDNS: "files,dns",
395 hostLookupDNSFiles: "dns,files",
396 hostLookupFiles: "files",
397 hostLookupDNS: "dns",
400 func (o hostLookupOrder) String() string {
401 if s, ok := lookupOrderName[o]; ok {
402 return s
404 return "hostLookupOrder=" + itoa(int(o)) + "??"
407 // goLookupHost is the native Go implementation of LookupHost.
408 // Used only if cgoLookupHost refuses to handle the request
409 // (that is, only if cgoLookupHost is the stub in cgo_stub.go).
410 // Normally we let cgo use the C library resolver instead of
411 // depending on our lookup code, so that Go and C get the same
412 // answers.
413 func (r *Resolver) goLookupHost(ctx context.Context, name string) (addrs []string, err error) {
414 return r.goLookupHostOrder(ctx, name, hostLookupFilesDNS)
417 func (r *Resolver) goLookupHostOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []string, err error) {
418 if order == hostLookupFilesDNS || order == hostLookupFiles {
419 // Use entries from /etc/hosts if they match.
420 addrs = lookupStaticHost(name)
421 if len(addrs) > 0 || order == hostLookupFiles {
422 return
425 ips, _, err := r.goLookupIPCNAMEOrder(ctx, name, order)
426 if err != nil {
427 return
429 addrs = make([]string, 0, len(ips))
430 for _, ip := range ips {
431 addrs = append(addrs, ip.String())
433 return
436 // lookup entries from /etc/hosts
437 func goLookupIPFiles(name string) (addrs []IPAddr) {
438 for _, haddr := range lookupStaticHost(name) {
439 haddr, zone := splitHostZone(haddr)
440 if ip := ParseIP(haddr); ip != nil {
441 addr := IPAddr{IP: ip, Zone: zone}
442 addrs = append(addrs, addr)
445 sortByRFC6724(addrs)
446 return
449 // goLookupIP is the native Go implementation of LookupIP.
450 // The libc versions are in cgo_*.go.
451 func (r *Resolver) goLookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
452 order := systemConf().hostLookupOrder(host)
453 addrs, _, err = r.goLookupIPCNAMEOrder(ctx, host, order)
454 return
457 func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []IPAddr, cname string, err error) {
458 if order == hostLookupFilesDNS || order == hostLookupFiles {
459 addrs = goLookupIPFiles(name)
460 if len(addrs) > 0 || order == hostLookupFiles {
461 return addrs, name, nil
464 if !isDomainName(name) {
465 // See comment in func lookup above about use of errNoSuchHost.
466 return nil, "", &DNSError{Err: errNoSuchHost.Error(), Name: name}
468 resolvConf.tryUpdate("/etc/resolv.conf")
469 resolvConf.mu.RLock()
470 conf := resolvConf.dnsConfig
471 resolvConf.mu.RUnlock()
472 type racer struct {
473 cname string
474 rrs []dnsRR
475 error
477 lane := make(chan racer, 1)
478 qtypes := [...]uint16{dnsTypeA, dnsTypeAAAA}
479 var lastErr error
480 for _, fqdn := range conf.nameList(name) {
481 for _, qtype := range qtypes {
482 go func(qtype uint16) {
483 cname, rrs, err := r.tryOneName(ctx, conf, fqdn, qtype)
484 lane <- racer{cname, rrs, err}
485 }(qtype)
487 hitStrictError := false
488 for range qtypes {
489 racer := <-lane
490 if racer.error != nil {
491 if nerr, ok := racer.error.(Error); ok && nerr.Temporary() && r.StrictErrors {
492 // This error will abort the nameList loop.
493 hitStrictError = true
494 lastErr = racer.error
495 } else if lastErr == nil || fqdn == name+"." {
496 // Prefer error for original name.
497 lastErr = racer.error
499 continue
501 addrs = append(addrs, addrRecordList(racer.rrs)...)
502 if cname == "" {
503 cname = racer.cname
506 if hitStrictError {
507 // If either family hit an error with StrictErrors enabled,
508 // discard all addresses. This ensures that network flakiness
509 // cannot turn a dualstack hostname IPv4/IPv6-only.
510 addrs = nil
511 break
513 if len(addrs) > 0 {
514 break
517 if lastErr, ok := lastErr.(*DNSError); ok {
518 // Show original name passed to lookup, not suffixed one.
519 // In general we might have tried many suffixes; showing
520 // just one is misleading. See also golang.org/issue/6324.
521 lastErr.Name = name
523 sortByRFC6724(addrs)
524 if len(addrs) == 0 {
525 if order == hostLookupDNSFiles {
526 addrs = goLookupIPFiles(name)
528 if len(addrs) == 0 && lastErr != nil {
529 return nil, "", lastErr
532 return addrs, cname, nil
535 // goLookupCNAME is the native Go (non-cgo) implementation of LookupCNAME.
536 func (r *Resolver) goLookupCNAME(ctx context.Context, host string) (cname string, err error) {
537 order := systemConf().hostLookupOrder(host)
538 _, cname, err = r.goLookupIPCNAMEOrder(ctx, host, order)
539 return
542 // goLookupPTR is the native Go implementation of LookupAddr.
543 // Used only if cgoLookupPTR refuses to handle the request (that is,
544 // only if cgoLookupPTR is the stub in cgo_stub.go).
545 // Normally we let cgo use the C library resolver instead of depending
546 // on our lookup code, so that Go and C get the same answers.
547 func (r *Resolver) goLookupPTR(ctx context.Context, addr string) ([]string, error) {
548 names := lookupStaticAddr(addr)
549 if len(names) > 0 {
550 return names, nil
552 arpa, err := reverseaddr(addr)
553 if err != nil {
554 return nil, err
556 _, rrs, err := r.lookup(ctx, arpa, dnsTypePTR)
557 if err != nil {
558 return nil, err
560 ptrs := make([]string, len(rrs))
561 for i, rr := range rrs {
562 ptrs[i] = rr.(*dnsRR_PTR).Ptr
564 return ptrs, nil