1// Copyright 2011 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 aix darwin dragonfly freebsd hurd linux netbsd openbsd solaris
6
7package net
8
9import (
10	"context"
11	"internal/bytealg"
12	"sync"
13	"syscall"
14
15	"golang.org/x/net/dns/dnsmessage"
16)
17
18var onceReadProtocols sync.Once
19
20// readProtocols loads contents of /etc/protocols into protocols map
21// for quick access.
22func readProtocols() {
23	file, err := open("/etc/protocols")
24	if err != nil {
25		return
26	}
27	defer file.close()
28
29	for line, ok := file.readLine(); ok; line, ok = file.readLine() {
30		// tcp    6   TCP    # transmission control protocol
31		if i := bytealg.IndexByteString(line, '#'); i >= 0 {
32			line = line[0:i]
33		}
34		f := getFields(line)
35		if len(f) < 2 {
36			continue
37		}
38		if proto, _, ok := dtoi(f[1]); ok {
39			if _, ok := protocols[f[0]]; !ok {
40				protocols[f[0]] = proto
41			}
42			for _, alias := range f[2:] {
43				if _, ok := protocols[alias]; !ok {
44					protocols[alias] = proto
45				}
46			}
47		}
48	}
49}
50
51// lookupProtocol looks up IP protocol name in /etc/protocols and
52// returns correspondent protocol number.
53func lookupProtocol(_ context.Context, name string) (int, error) {
54	onceReadProtocols.Do(readProtocols)
55	return lookupProtocolMap(name)
56}
57
58func (r *Resolver) dial(ctx context.Context, network, server string) (Conn, error) {
59	// Calling Dial here is scary -- we have to be sure not to
60	// dial a name that will require a DNS lookup, or Dial will
61	// call back here to translate it. The DNS config parser has
62	// already checked that all the cfg.servers are IP
63	// addresses, which Dial will use without a DNS lookup.
64	var c Conn
65	var err error
66	if r != nil && r.Dial != nil {
67		c, err = r.Dial(ctx, network, server)
68	} else {
69		var d Dialer
70		c, err = d.DialContext(ctx, network, server)
71	}
72	if err != nil {
73		return nil, mapErr(err)
74	}
75	return c, nil
76}
77
78func (r *Resolver) lookupHost(ctx context.Context, host string) (addrs []string, err error) {
79	order := systemConf().hostLookupOrder(r, host)
80	if !r.preferGo() && order == hostLookupCgo {
81		if addrs, err, ok := cgoLookupHost(ctx, host); ok {
82			return addrs, err
83		}
84		// cgo not available (or netgo); fall back to Go's DNS resolver
85		order = hostLookupFilesDNS
86	}
87	return r.goLookupHostOrder(ctx, host, order)
88}
89
90func (r *Resolver) lookupIP(ctx context.Context, network, host string) (addrs []IPAddr, err error) {
91	if r.preferGo() {
92		return r.goLookupIP(ctx, host)
93	}
94	order := systemConf().hostLookupOrder(r, host)
95	if order == hostLookupCgo {
96		if addrs, err, ok := cgoLookupIP(ctx, network, host); ok {
97			return addrs, err
98		}
99		// cgo not available (or netgo); fall back to Go's DNS resolver
100		order = hostLookupFilesDNS
101	}
102	ips, _, err := r.goLookupIPCNAMEOrder(ctx, host, order)
103	return ips, err
104}
105
106func (r *Resolver) lookupPort(ctx context.Context, network, service string) (int, error) {
107	if !r.preferGo() && systemConf().canUseCgo() {
108		if port, err, ok := cgoLookupPort(ctx, network, service); ok {
109			if err != nil {
110				// Issue 18213: if cgo fails, first check to see whether we
111				// have the answer baked-in to the net package.
112				if port, err := goLookupPort(network, service); err == nil {
113					return port, nil
114				}
115			}
116			return port, err
117		}
118	}
119	return goLookupPort(network, service)
120}
121
122func (r *Resolver) lookupCNAME(ctx context.Context, name string) (string, error) {
123	if !r.preferGo() && systemConf().canUseCgo() {
124		if cname, err, ok := cgoLookupCNAME(ctx, name); ok {
125			return cname, err
126		}
127	}
128	return r.goLookupCNAME(ctx, name)
129}
130
131func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
132	var target string
133	if service == "" && proto == "" {
134		target = name
135	} else {
136		target = "_" + service + "._" + proto + "." + name
137	}
138	p, server, err := r.lookup(ctx, target, dnsmessage.TypeSRV)
139	if err != nil {
140		return "", nil, err
141	}
142	var srvs []*SRV
143	var cname dnsmessage.Name
144	for {
145		h, err := p.AnswerHeader()
146		if err == dnsmessage.ErrSectionDone {
147			break
148		}
149		if err != nil {
150			return "", nil, &DNSError{
151				Err:    "cannot unmarshal DNS message",
152				Name:   name,
153				Server: server,
154			}
155		}
156		if h.Type != dnsmessage.TypeSRV {
157			if err := p.SkipAnswer(); err != nil {
158				return "", nil, &DNSError{
159					Err:    "cannot unmarshal DNS message",
160					Name:   name,
161					Server: server,
162				}
163			}
164			continue
165		}
166		if cname.Length == 0 && h.Name.Length != 0 {
167			cname = h.Name
168		}
169		srv, err := p.SRVResource()
170		if err != nil {
171			return "", nil, &DNSError{
172				Err:    "cannot unmarshal DNS message",
173				Name:   name,
174				Server: server,
175			}
176		}
177		srvs = append(srvs, &SRV{Target: srv.Target.String(), Port: srv.Port, Priority: srv.Priority, Weight: srv.Weight})
178	}
179	byPriorityWeight(srvs).sort()
180	return cname.String(), srvs, nil
181}
182
183func (r *Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) {
184	p, server, err := r.lookup(ctx, name, dnsmessage.TypeMX)
185	if err != nil {
186		return nil, err
187	}
188	var mxs []*MX
189	for {
190		h, err := p.AnswerHeader()
191		if err == dnsmessage.ErrSectionDone {
192			break
193		}
194		if err != nil {
195			return nil, &DNSError{
196				Err:    "cannot unmarshal DNS message",
197				Name:   name,
198				Server: server,
199			}
200		}
201		if h.Type != dnsmessage.TypeMX {
202			if err := p.SkipAnswer(); err != nil {
203				return nil, &DNSError{
204					Err:    "cannot unmarshal DNS message",
205					Name:   name,
206					Server: server,
207				}
208			}
209			continue
210		}
211		mx, err := p.MXResource()
212		if err != nil {
213			return nil, &DNSError{
214				Err:    "cannot unmarshal DNS message",
215				Name:   name,
216				Server: server,
217			}
218		}
219		mxs = append(mxs, &MX{Host: mx.MX.String(), Pref: mx.Pref})
220
221	}
222	byPref(mxs).sort()
223	return mxs, nil
224}
225
226func (r *Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) {
227	p, server, err := r.lookup(ctx, name, dnsmessage.TypeNS)
228	if err != nil {
229		return nil, err
230	}
231	var nss []*NS
232	for {
233		h, err := p.AnswerHeader()
234		if err == dnsmessage.ErrSectionDone {
235			break
236		}
237		if err != nil {
238			return nil, &DNSError{
239				Err:    "cannot unmarshal DNS message",
240				Name:   name,
241				Server: server,
242			}
243		}
244		if h.Type != dnsmessage.TypeNS {
245			if err := p.SkipAnswer(); err != nil {
246				return nil, &DNSError{
247					Err:    "cannot unmarshal DNS message",
248					Name:   name,
249					Server: server,
250				}
251			}
252			continue
253		}
254		ns, err := p.NSResource()
255		if err != nil {
256			return nil, &DNSError{
257				Err:    "cannot unmarshal DNS message",
258				Name:   name,
259				Server: server,
260			}
261		}
262		nss = append(nss, &NS{Host: ns.NS.String()})
263	}
264	return nss, nil
265}
266
267func (r *Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) {
268	p, server, err := r.lookup(ctx, name, dnsmessage.TypeTXT)
269	if err != nil {
270		return nil, err
271	}
272	var txts []string
273	for {
274		h, err := p.AnswerHeader()
275		if err == dnsmessage.ErrSectionDone {
276			break
277		}
278		if err != nil {
279			return nil, &DNSError{
280				Err:    "cannot unmarshal DNS message",
281				Name:   name,
282				Server: server,
283			}
284		}
285		if h.Type != dnsmessage.TypeTXT {
286			if err := p.SkipAnswer(); err != nil {
287				return nil, &DNSError{
288					Err:    "cannot unmarshal DNS message",
289					Name:   name,
290					Server: server,
291				}
292			}
293			continue
294		}
295		txt, err := p.TXTResource()
296		if err != nil {
297			return nil, &DNSError{
298				Err:    "cannot unmarshal DNS message",
299				Name:   name,
300				Server: server,
301			}
302		}
303		// Multiple strings in one TXT record need to be
304		// concatenated without separator to be consistent
305		// with previous Go resolver.
306		n := 0
307		for _, s := range txt.TXT {
308			n += len(s)
309		}
310		txtJoin := make([]byte, 0, n)
311		for _, s := range txt.TXT {
312			txtJoin = append(txtJoin, s...)
313		}
314		if len(txts) == 0 {
315			txts = make([]string, 0, 1)
316		}
317		txts = append(txts, string(txtJoin))
318	}
319	return txts, nil
320}
321
322func (r *Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error) {
323	if !r.preferGo() && systemConf().canUseCgo() {
324		if ptrs, err, ok := cgoLookupPTR(ctx, addr); ok {
325			return ptrs, err
326		}
327	}
328	return r.goLookupPTR(ctx, addr)
329}
330
331// concurrentThreadsLimit returns the number of threads we permit to
332// run concurrently doing DNS lookups via cgo. A DNS lookup may use a
333// file descriptor so we limit this to less than the number of
334// permitted open files. On some systems, notably Darwin, if
335// getaddrinfo is unable to open a file descriptor it simply returns
336// EAI_NONAME rather than a useful error. Limiting the number of
337// concurrent getaddrinfo calls to less than the permitted number of
338// file descriptors makes that error less likely. We don't bother to
339// apply the same limit to DNS lookups run directly from Go, because
340// there we will return a meaningful "too many open files" error.
341func concurrentThreadsLimit() int {
342	var rlim syscall.Rlimit
343	if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlim); err != nil {
344		return 500
345	}
346	r := int(rlim.Cur)
347	if r > 500 {
348		r = 500
349	} else if r > 30 {
350		r -= 30
351	}
352	return r
353}
354