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 darwin dragonfly freebsd linux netbsd openbsd solaris
6
7package net
8
9import (
10	"context"
11	"sync"
12)
13
14var onceReadProtocols sync.Once
15
16// readProtocols loads contents of /etc/protocols into protocols map
17// for quick access.
18func readProtocols() {
19	file, err := open("/etc/protocols")
20	if err != nil {
21		return
22	}
23	defer file.close()
24
25	for line, ok := file.readLine(); ok; line, ok = file.readLine() {
26		// tcp    6   TCP    # transmission control protocol
27		if i := byteIndex(line, '#'); i >= 0 {
28			line = line[0:i]
29		}
30		f := getFields(line)
31		if len(f) < 2 {
32			continue
33		}
34		if proto, _, ok := dtoi(f[1]); ok {
35			if _, ok := protocols[f[0]]; !ok {
36				protocols[f[0]] = proto
37			}
38			for _, alias := range f[2:] {
39				if _, ok := protocols[alias]; !ok {
40					protocols[alias] = proto
41				}
42			}
43		}
44	}
45}
46
47// lookupProtocol looks up IP protocol name in /etc/protocols and
48// returns correspondent protocol number.
49func lookupProtocol(_ context.Context, name string) (int, error) {
50	onceReadProtocols.Do(readProtocols)
51	return lookupProtocolMap(name)
52}
53
54func (r *Resolver) dial(ctx context.Context, network, server string) (dnsConn, error) {
55	// Calling Dial here is scary -- we have to be sure not to
56	// dial a name that will require a DNS lookup, or Dial will
57	// call back here to translate it. The DNS config parser has
58	// already checked that all the cfg.servers are IP
59	// addresses, which Dial will use without a DNS lookup.
60	var c Conn
61	var err error
62	if r.Dial != nil {
63		c, err = r.Dial(ctx, network, server)
64	} else {
65		var d Dialer
66		c, err = d.DialContext(ctx, network, server)
67	}
68	if err != nil {
69		return nil, mapErr(err)
70	}
71	if _, ok := c.(PacketConn); ok {
72		return &dnsPacketConn{c}, nil
73	}
74	return &dnsStreamConn{c}, nil
75}
76
77func (r *Resolver) lookupHost(ctx context.Context, host string) (addrs []string, err error) {
78	order := systemConf().hostLookupOrder(host)
79	if !r.PreferGo && order == hostLookupCgo {
80		if addrs, err, ok := cgoLookupHost(ctx, host); ok {
81			return addrs, err
82		}
83		// cgo not available (or netgo); fall back to Go's DNS resolver
84		order = hostLookupFilesDNS
85	}
86	return r.goLookupHostOrder(ctx, host, order)
87}
88
89func (r *Resolver) lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
90	if r.PreferGo {
91		return r.goLookupIP(ctx, host)
92	}
93	order := systemConf().hostLookupOrder(host)
94	if order == hostLookupCgo {
95		if addrs, err, ok := cgoLookupIP(ctx, host); ok {
96			return addrs, err
97		}
98		// cgo not available (or netgo); fall back to Go's DNS resolver
99		order = hostLookupFilesDNS
100	}
101	addrs, _, err = r.goLookupIPCNAMEOrder(ctx, host, order)
102	return
103}
104
105func (r *Resolver) lookupPort(ctx context.Context, network, service string) (int, error) {
106	if !r.PreferGo && systemConf().canUseCgo() {
107		if port, err, ok := cgoLookupPort(ctx, network, service); ok {
108			if err != nil {
109				// Issue 18213: if cgo fails, first check to see whether we
110				// have the answer baked-in to the net package.
111				if port, err := goLookupPort(network, service); err == nil {
112					return port, nil
113				}
114			}
115			return port, err
116		}
117	}
118	return goLookupPort(network, service)
119}
120
121func (r *Resolver) lookupCNAME(ctx context.Context, name string) (string, error) {
122	if !r.PreferGo && systemConf().canUseCgo() {
123		if cname, err, ok := cgoLookupCNAME(ctx, name); ok {
124			return cname, err
125		}
126	}
127	return r.goLookupCNAME(ctx, name)
128}
129
130func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
131	var target string
132	if service == "" && proto == "" {
133		target = name
134	} else {
135		target = "_" + service + "._" + proto + "." + name
136	}
137	cname, rrs, err := r.lookup(ctx, target, dnsTypeSRV)
138	if err != nil {
139		return "", nil, err
140	}
141	srvs := make([]*SRV, len(rrs))
142	for i, rr := range rrs {
143		rr := rr.(*dnsRR_SRV)
144		srvs[i] = &SRV{Target: rr.Target, Port: rr.Port, Priority: rr.Priority, Weight: rr.Weight}
145	}
146	byPriorityWeight(srvs).sort()
147	return cname, srvs, nil
148}
149
150func (r *Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) {
151	_, rrs, err := r.lookup(ctx, name, dnsTypeMX)
152	if err != nil {
153		return nil, err
154	}
155	mxs := make([]*MX, len(rrs))
156	for i, rr := range rrs {
157		rr := rr.(*dnsRR_MX)
158		mxs[i] = &MX{Host: rr.Mx, Pref: rr.Pref}
159	}
160	byPref(mxs).sort()
161	return mxs, nil
162}
163
164func (r *Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) {
165	_, rrs, err := r.lookup(ctx, name, dnsTypeNS)
166	if err != nil {
167		return nil, err
168	}
169	nss := make([]*NS, len(rrs))
170	for i, rr := range rrs {
171		nss[i] = &NS{Host: rr.(*dnsRR_NS).Ns}
172	}
173	return nss, nil
174}
175
176func (r *Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) {
177	_, rrs, err := r.lookup(ctx, name, dnsTypeTXT)
178	if err != nil {
179		return nil, err
180	}
181	txts := make([]string, len(rrs))
182	for i, rr := range rrs {
183		txts[i] = rr.(*dnsRR_TXT).Txt
184	}
185	return txts, nil
186}
187
188func (r *Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error) {
189	if !r.PreferGo && systemConf().canUseCgo() {
190		if ptrs, err, ok := cgoLookupPTR(ctx, addr); ok {
191			return ptrs, err
192		}
193	}
194	return r.goLookupPTR(ctx, addr)
195}
196