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 solaris
6
7// DNS client: see RFC 1035.
8// Has to be linked into package net for Dial.
9
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.
15
16package net
17
18import (
19	"context"
20	"errors"
21	"io"
22	"math/rand"
23	"os"
24	"sync"
25	"time"
26)
27
28// A dnsConn represents a DNS transport endpoint.
29type dnsConn interface {
30	io.Closer
31
32	SetDeadline(time.Time) error
33
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)
37}
38
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.
42type dnsPacketConn struct {
43	Conn
44}
45
46func (c *dnsPacketConn) dnsRoundTrip(query *dnsMsg) (*dnsMsg, error) {
47	b, ok := query.Pack()
48	if !ok {
49		return nil, errors.New("cannot marshal DNS message")
50	}
51	if _, err := c.Write(b); err != nil {
52		return nil, err
53	}
54
55	b = make([]byte, 512) // see RFC 1035
56	for {
57		n, err := c.Read(b)
58		if err != nil {
59			return nil, err
60		}
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
67		}
68		return resp, nil
69	}
70}
71
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.
75type dnsStreamConn struct {
76	Conn
77}
78
79func (c *dnsStreamConn) dnsRoundTrip(query *dnsMsg) (*dnsMsg, error) {
80	b, ok := query.Pack()
81	if !ok {
82		return nil, errors.New("cannot marshal DNS message")
83	}
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
88	}
89
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
93	}
94	l = int(b[0])<<8 | int(b[1])
95	if l > len(b) {
96		b = make([]byte, l)
97	}
98	n, err := io.ReadFull(c, b[:l])
99	if err != nil {
100		return nil, err
101	}
102	resp := &dnsMsg{}
103	if !resp.Unpack(b[:n]) {
104		return nil, errors.New("cannot unmarshal DNS message")
105	}
106	if !resp.IsResponseTo(query) {
107		return nil, errors.New("invalid DNS response")
108	}
109	return resp, nil
110}
111
112// exchange sends a query on the connection and hopes for a response.
113func (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,
117		},
118		question: []dnsQuestion{
119			{name, qtype, dnsClassINET},
120		},
121	}
122	for _, network := range []string{"udp", "tcp"} {
123		// TODO(mdempsky): Refactor so defers from UDP-based
124		// exchanges happen before TCP-based exchange.
125
126		ctx, cancel := context.WithDeadline(ctx, time.Now().Add(timeout))
127		defer cancel()
128
129		c, err := r.dial(ctx, network, server)
130		if err != nil {
131			return nil, err
132		}
133		defer c.Close()
134		if d, ok := ctx.Deadline(); ok && !d.IsZero() {
135			c.SetDeadline(d)
136		}
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)
141		}
142		if in.truncated { // see RFC 5966
143			continue
144		}
145		return in, nil
146	}
147	return nil, errors.New("no answer from DNS server")
148}
149
150// Do a lookup for a single name, which must be rooted
151// (otherwise answer will not find the answers).
152func (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))
156
157	for i := 0; i < cfg.attempts; i++ {
158		for j := uint32(0); j < sLen; j++ {
159			server := cfg.servers[(serverOffset+j)%sLen]
160
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,
167				}
168				if nerr, ok := err.(Error); ok && nerr.Timeout() {
169					lastErr.(*DNSError).IsTimeout = true
170				}
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
175				}
176				continue
177			}
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
183			}
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
191			}
192			lastErr = err
193		}
194	}
195	return "", nil, lastErr
196}
197
198// addrRecordList converts and returns a list of IP addresses from DNS
199// address records (both A and AAAA). Other record types are ignored.
200func 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})
210		}
211	}
212	return addrs
213}
214
215// A resolverConfig represents a DNS stub resolver configuration.
216type resolverConfig struct {
217	initOnce sync.Once // guards init of resolverConfig
218
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
223
224	mu        sync.RWMutex // protects dnsConfig
225	dnsConfig *dnsConfig   // parsed resolv.conf structure used in lookups
226}
227
228var resolvConf resolverConfig
229
230// init initializes conf and is only called via conf.initOnce.
231func (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")
237	}
238	conf.lastChecked = time.Now()
239
240	// Prepare ch so that only one update of resolverConfig may
241	// run at once.
242	conf.ch = make(chan struct{}, 1)
243}
244
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".
248func (conf *resolverConfig) tryUpdate(name string) {
249	conf.initOnce.Do(conf.init)
250
251	// Ensure only one update at a time checks resolv.conf.
252	if !conf.tryAcquireSema() {
253		return
254	}
255	defer conf.releaseSema()
256
257	now := time.Now()
258	if conf.lastChecked.After(now.Add(-5 * time.Second)) {
259		return
260	}
261	conf.lastChecked = now
262
263	var mtime time.Time
264	if fi, err := os.Stat(name); err == nil {
265		mtime = fi.ModTime()
266	}
267	if mtime.Equal(conf.dnsConfig.mtime) {
268		return
269	}
270
271	dnsConf := dnsReadConfig(name)
272	conf.mu.Lock()
273	conf.dnsConfig = dnsConf
274	conf.mu.Unlock()
275}
276
277func (conf *resolverConfig) tryAcquireSema() bool {
278	select {
279	case conf.ch <- struct{}{}:
280		return true
281	default:
282		return false
283	}
284}
285
286func (conf *resolverConfig) releaseSema() {
287	<-conf.ch
288}
289
290func (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}
298	}
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
307		}
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
312		}
313	}
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
319	}
320	return
321}
322
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.
327func avoidDNS(name string) bool {
328	if name == "" {
329		return true
330	}
331	if name[len(name)-1] == '.' {
332		name = name[:len(name)-1]
333	}
334	return stringsHasSuffixFold(name, ".onion")
335}
336
337// nameList returns a list of names for sequential DNS queries.
338func (conf *dnsConfig) nameList(name string) []string {
339	if avoidDNS(name) {
340		return nil
341	}
342
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
348	}
349
350	// If name is rooted (trailing dot), try only that name.
351	if rooted {
352		return []string{name}
353	}
354
355	hasNdots := count(name, '.') >= conf.ndots
356	name += "."
357	l++
358
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)
364	}
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)
369		}
370	}
371	// Try unsuffixed, if not tried first above.
372	if !hasNdots {
373		names = append(names, name)
374	}
375	return names
376}
377
378// hostLookupOrder specifies the order of LookupHost lookup strategies.
379// It is basically a simplified representation of nsswitch.conf.
380// "files" means /etc/hosts.
381type hostLookupOrder int
382
383const (
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
390)
391
392var lookupOrderName = map[hostLookupOrder]string{
393	hostLookupCgo:      "cgo",
394	hostLookupFilesDNS: "files,dns",
395	hostLookupDNSFiles: "dns,files",
396	hostLookupFiles:    "files",
397	hostLookupDNS:      "dns",
398}
399
400func (o hostLookupOrder) String() string {
401	if s, ok := lookupOrderName[o]; ok {
402		return s
403	}
404	return "hostLookupOrder=" + itoa(int(o)) + "??"
405}
406
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.
413func (r *Resolver) goLookupHost(ctx context.Context, name string) (addrs []string, err error) {
414	return r.goLookupHostOrder(ctx, name, hostLookupFilesDNS)
415}
416
417func (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
423		}
424	}
425	ips, _, err := r.goLookupIPCNAMEOrder(ctx, name, order)
426	if err != nil {
427		return
428	}
429	addrs = make([]string, 0, len(ips))
430	for _, ip := range ips {
431		addrs = append(addrs, ip.String())
432	}
433	return
434}
435
436// lookup entries from /etc/hosts
437func 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)
443		}
444	}
445	sortByRFC6724(addrs)
446	return
447}
448
449// goLookupIP is the native Go implementation of LookupIP.
450// The libc versions are in cgo_*.go.
451func (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
455}
456
457func (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
462		}
463	}
464	if !isDomainName(name) {
465		// See comment in func lookup above about use of errNoSuchHost.
466		return nil, "", &DNSError{Err: errNoSuchHost.Error(), Name: name}
467	}
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
476	}
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)
486		}
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
498				}
499				continue
500			}
501			addrs = append(addrs, addrRecordList(racer.rrs)...)
502			if cname == "" {
503				cname = racer.cname
504			}
505		}
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
512		}
513		if len(addrs) > 0 {
514			break
515		}
516	}
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
522	}
523	sortByRFC6724(addrs)
524	if len(addrs) == 0 {
525		if order == hostLookupDNSFiles {
526			addrs = goLookupIPFiles(name)
527		}
528		if len(addrs) == 0 && lastErr != nil {
529			return nil, "", lastErr
530		}
531	}
532	return addrs, cname, nil
533}
534
535// goLookupCNAME is the native Go (non-cgo) implementation of LookupCNAME.
536func (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
540}
541
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.
547func (r *Resolver) goLookupPTR(ctx context.Context, addr string) ([]string, error) {
548	names := lookupStaticAddr(addr)
549	if len(names) > 0 {
550		return names, nil
551	}
552	arpa, err := reverseaddr(addr)
553	if err != nil {
554		return nil, err
555	}
556	_, rrs, err := r.lookup(ctx, arpa, dnsTypePTR)
557	if err != nil {
558		return nil, err
559	}
560	ptrs := make([]string, len(rrs))
561	for i, rr := range rrs {
562		ptrs[i] = rr.(*dnsRR_PTR).Ptr
563	}
564	return ptrs, nil
565}
566