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