Skip to content

Commit 2bc5f12

Browse files
committed
net: add Resolver type, Dialer.Resolver, and DefaultResolver
The new Resolver type (a struct) has 9 Lookup methods, all taking a context.Context. There's now a new DefaultResolver global, like http's DefaultTransport and DefaultClient. net.Dialer now has an optional Resolver field to set the Resolver. This also does finishes some resolver cleanup internally, deleting lookupIPMerge and renaming lookupIPContext into Resolver.LookupIPAddr. The Resolver currently doesn't let you tweak much, but it's a struct specifically so we can add knobs in the future. Currently I just added a bool to force the pure Go resolver. In the future we could let people provide an interface to implement the methods, or add a Timeout time.Duration, which would wrap all provided contexts in a context.WithTimeout. Fixes #16672 Change-Id: I7ba1f886704f06def7b6b5c4da9809db51bc1495 Reviewed-on: https://go-review.googlesource.com/29440 Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent ea143c2 commit 2bc5f12

11 files changed

+170
-82
lines changed

src/net/dial.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ type Dialer struct {
5959
// that do not support keep-alives ignore this field.
6060
KeepAlive time.Duration
6161

62+
// Resolver optionally specifies an alternate resolver to use.
63+
Resolver *Resolver
64+
6265
// Cancel is an optional channel whose closure indicates that
6366
// the dial should be canceled. Not all types of dials support
6467
// cancelation.
@@ -92,6 +95,13 @@ func (d *Dialer) deadline(ctx context.Context, now time.Time) (earliest time.Tim
9295
return minNonzeroTime(earliest, d.Deadline)
9396
}
9497

98+
func (d *Dialer) resolver() *Resolver {
99+
if d.Resolver != nil {
100+
return d.Resolver
101+
}
102+
return DefaultResolver
103+
}
104+
95105
// partialDeadline returns the deadline to use for a single address,
96106
// when multiple addresses are pending.
97107
func partialDeadline(now, deadline time.Time, addrsRemaining int) (time.Time, error) {
@@ -156,7 +166,7 @@ func parseNetwork(ctx context.Context, net string) (afnet string, proto int, err
156166
// resolverAddrList resolves addr using hint and returns a list of
157167
// addresses. The result contains at least one address when error is
158168
// nil.
159-
func resolveAddrList(ctx context.Context, op, network, addr string, hint Addr) (addrList, error) {
169+
func (r *Resolver) resolveAddrList(ctx context.Context, op, network, addr string, hint Addr) (addrList, error) {
160170
afnet, _, err := parseNetwork(ctx, network)
161171
if err != nil {
162172
return nil, err
@@ -166,7 +176,6 @@ func resolveAddrList(ctx context.Context, op, network, addr string, hint Addr) (
166176
}
167177
switch afnet {
168178
case "unix", "unixgram", "unixpacket":
169-
// TODO(bradfitz): push down context
170179
addr, err := ResolveUnixAddr(afnet, addr)
171180
if err != nil {
172181
return nil, err
@@ -176,7 +185,7 @@ func resolveAddrList(ctx context.Context, op, network, addr string, hint Addr) (
176185
}
177186
return addrList{addr}, nil
178187
}
179-
addrs, err := internetAddrList(ctx, afnet, addr)
188+
addrs, err := r.internetAddrList(ctx, afnet, addr)
180189
if err != nil || op != "dial" || hint == nil {
181190
return addrs, err
182191
}
@@ -326,7 +335,7 @@ func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn
326335
resolveCtx = context.WithValue(resolveCtx, nettrace.TraceKey{}, &shadow)
327336
}
328337

329-
addrs, err := resolveAddrList(resolveCtx, "dial", network, address, d.LocalAddr)
338+
addrs, err := d.resolver().resolveAddrList(resolveCtx, "dial", network, address, d.LocalAddr)
330339
if err != nil {
331340
return nil, &OpError{Op: "dial", Net: network, Source: nil, Addr: nil, Err: err}
332341
}
@@ -525,7 +534,7 @@ func dialSingle(ctx context.Context, dp *dialParam, ra Addr) (c Conn, err error)
525534
// instead of just the interface with the given host address.
526535
// See Dial for more details about address syntax.
527536
func Listen(net, laddr string) (Listener, error) {
528-
addrs, err := resolveAddrList(context.Background(), "listen", net, laddr, nil)
537+
addrs, err := DefaultResolver.resolveAddrList(context.Background(), "listen", net, laddr, nil)
529538
if err != nil {
530539
return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: nil, Err: err}
531540
}
@@ -552,7 +561,7 @@ func Listen(net, laddr string) (Listener, error) {
552561
// instead of just the interface with the given host address.
553562
// See Dial for the syntax of laddr.
554563
func ListenPacket(net, laddr string) (PacketConn, error) {
555-
addrs, err := resolveAddrList(context.Background(), "listen", net, laddr, nil)
564+
addrs, err := DefaultResolver.resolveAddrList(context.Background(), "listen", net, laddr, nil)
556565
if err != nil {
557566
return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: nil, Err: err}
558567
}

src/net/dnsclient_unix.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -456,8 +456,9 @@ func goLookupIPFiles(name string) (addrs []IPAddr) {
456456

457457
// goLookupIP is the native Go implementation of LookupIP.
458458
// The libc versions are in cgo_*.go.
459-
func goLookupIP(ctx context.Context, name string) (addrs []IPAddr, err error) {
460-
return goLookupIPOrder(ctx, name, hostLookupFilesDNS)
459+
func goLookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
460+
order := systemConf().hostLookupOrder(host)
461+
return goLookupIPOrder(ctx, host, order)
461462
}
462463

463464
func goLookupIPOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []IPAddr, err error) {

src/net/iprawsock.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func ResolveIPAddr(net, addr string) (*IPAddr, error) {
6565
default:
6666
return nil, UnknownNetworkError(net)
6767
}
68-
addrs, err := internetAddrList(context.Background(), afnet, addr)
68+
addrs, err := DefaultResolver.internetAddrList(context.Background(), afnet, addr)
6969
if err != nil {
7070
return nil, err
7171
}

src/net/ipsock.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ func JoinHostPort(host, port string) string {
190190
// address or a DNS name, and returns a list of internet protocol
191191
// family addresses. The result contains at least one address when
192192
// error is nil.
193-
func internetAddrList(ctx context.Context, net, addr string) (addrList, error) {
193+
func (r *Resolver) internetAddrList(ctx context.Context, net, addr string) (addrList, error) {
194194
var (
195195
err error
196196
host, port string
@@ -202,7 +202,7 @@ func internetAddrList(ctx context.Context, net, addr string) (addrList, error) {
202202
if host, port, err = SplitHostPort(addr); err != nil {
203203
return nil, err
204204
}
205-
if portnum, err = LookupPort(net, port); err != nil {
205+
if portnum, err = r.LookupPort(ctx, net, port); err != nil {
206206
return nil, err
207207
}
208208
}
@@ -238,7 +238,7 @@ func internetAddrList(ctx context.Context, net, addr string) (addrList, error) {
238238
return addrList{inetaddr(IPAddr{IP: ip, Zone: zone})}, nil
239239
}
240240
// Try as a DNS name.
241-
ips, err := lookupIPContext(ctx, host)
241+
ips, err := r.LookupIPAddr(ctx, host)
242242
if err != nil {
243243
return nil, err
244244
}

src/net/lookup.go

Lines changed: 131 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -84,94 +84,83 @@ func lookupPortMap(network, service string) (port int, error error) {
8484
return 0, &AddrError{Err: "unknown port", Addr: network + "/" + service}
8585
}
8686

87+
// DefaultResolver is the resolver used by the package-level Lookup
88+
// functions and by Dialers without a specified Resolver.
89+
var DefaultResolver = &Resolver{}
90+
91+
// A Resolver looks up names and numbers.
92+
//
93+
// A nil *Resolver is equivalent to a zero Resolver.
94+
type Resolver struct {
95+
// PreferGo controls whether Go's built-in DNS resolver is preferred
96+
// on platforms where it's available. It is equivalent to setting
97+
// GODEBUG=netdns=go, but scoped to just this resolver.
98+
PreferGo bool
99+
100+
// TODO(bradfitz): optional interface impl override hook
101+
// TODO(bradfitz): Timeout time.Duration?
102+
}
103+
104+
func (r *Resolver) lookupIPFunc() func(context.Context, string) ([]IPAddr, error) {
105+
if r != nil && r.PreferGo {
106+
return goLookupIP
107+
}
108+
return lookupIP
109+
}
110+
87111
// LookupHost looks up the given host using the local resolver.
88-
// It returns an array of that host's addresses.
112+
// It returns a slice of that host's addresses.
89113
func LookupHost(host string) (addrs []string, err error) {
90-
// Make sure that no matter what we do later, host=="" is rejected.
91-
// ParseIP, for example, does accept empty strings.
92-
if host == "" {
93-
return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host}
94-
}
95-
if ip := ParseIP(host); ip != nil {
96-
return []string{host}, nil
97-
}
98-
return lookupHost(context.Background(), host)
114+
return DefaultResolver.LookupHost(context.Background(), host)
99115
}
100116

101-
// LookupIP looks up host using the local resolver.
102-
// It returns an array of that host's IPv4 and IPv6 addresses.
103-
func LookupIP(host string) (ips []IP, err error) {
117+
// LookupHost looks up the given host using the local resolver.
118+
// It returns a slice of that host's addresses.
119+
func (r *Resolver) LookupHost(ctx context.Context, host string) (addrs []string, err error) {
104120
// Make sure that no matter what we do later, host=="" is rejected.
105121
// ParseIP, for example, does accept empty strings.
106122
if host == "" {
107123
return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host}
108124
}
109125
if ip := ParseIP(host); ip != nil {
110-
return []IP{ip}, nil
111-
}
112-
addrs, err := lookupIPMerge(context.Background(), host)
113-
if err != nil {
114-
return
115-
}
116-
ips = make([]IP, len(addrs))
117-
for i, addr := range addrs {
118-
ips[i] = addr.IP
126+
return []string{host}, nil
119127
}
120-
return
128+
return lookupHost(ctx, host)
121129
}
122130

123-
var lookupGroup singleflight.Group
124-
125-
// lookupIPMerge wraps lookupIP, but makes sure that for any given
126-
// host, only one lookup is in-flight at a time. The returned memory
127-
// is always owned by the caller.
128-
func lookupIPMerge(ctx context.Context, host string) (addrs []IPAddr, err error) {
129-
addrsi, err, shared := lookupGroup.Do(host, func() (interface{}, error) {
130-
return testHookLookupIP(ctx, lookupIP, host)
131-
})
132-
return lookupIPReturn(addrsi, err, shared)
133-
}
134-
135-
// lookupIPReturn turns the return values from singleflight.Do into
136-
// the return values from LookupIP.
137-
func lookupIPReturn(addrsi interface{}, err error, shared bool) ([]IPAddr, error) {
131+
// LookupIP looks up host using the local resolver.
132+
// It returns a slice of that host's IPv4 and IPv6 addresses.
133+
func LookupIP(host string) ([]IP, error) {
134+
addrs, err := DefaultResolver.LookupIPAddr(context.Background(), host)
138135
if err != nil {
139136
return nil, err
140137
}
141-
addrs := addrsi.([]IPAddr)
142-
if shared {
143-
clone := make([]IPAddr, len(addrs))
144-
copy(clone, addrs)
145-
addrs = clone
138+
ips := make([]IP, len(addrs))
139+
for i, ia := range addrs {
140+
ips[i] = ia.IP
146141
}
147-
return addrs, nil
142+
return ips, nil
148143
}
149144

150-
// ipAddrsEface returns an empty interface slice of addrs.
151-
func ipAddrsEface(addrs []IPAddr) []interface{} {
152-
s := make([]interface{}, len(addrs))
153-
for i, v := range addrs {
154-
s[i] = v
145+
// LookupIPAddr looks up host using the local resolver.
146+
// It returns a slice of that host's IPv4 and IPv6 addresses.
147+
func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, error) {
148+
// Make sure that no matter what we do later, host=="" is rejected.
149+
// ParseIP, for example, does accept empty strings.
150+
if host == "" {
151+
return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host}
152+
}
153+
if ip := ParseIP(host); ip != nil {
154+
return []IPAddr{{IP: ip}}, nil
155155
}
156-
return s
157-
}
158-
159-
// lookupIPContext looks up a hostname with a context.
160-
//
161-
// TODO(bradfitz): rename this function. All the other
162-
// build-tag-specific lookupIP funcs also take a context now, so this
163-
// name is no longer great. Maybe make this lookupIPMerge and ditch
164-
// the other one, making its callers call this instead with a
165-
// context.Background().
166-
func lookupIPContext(ctx context.Context, host string) (addrs []IPAddr, err error) {
167156
trace, _ := ctx.Value(nettrace.TraceKey{}).(*nettrace.Trace)
168157
if trace != nil && trace.DNSStart != nil {
169158
trace.DNSStart(host)
170159
}
171160
// The underlying resolver func is lookupIP by default but it
172161
// can be overridden by tests. This is needed by net/http, so it
173162
// uses a context key instead of unexported variables.
174-
resolverFunc := lookupIP
163+
resolverFunc := r.lookupIPFunc()
175164
if alt, _ := ctx.Value(nettrace.LookupIPAltResolverKey{}).(func(context.Context, string) ([]IPAddr, error)); alt != nil {
176165
resolverFunc = alt
177166
}
@@ -201,11 +190,46 @@ func lookupIPContext(ctx context.Context, host string) (addrs []IPAddr, err erro
201190
}
202191
}
203192

193+
// lookupGroup merges LookupIPAddr calls together for lookups
194+
// for the same host. The lookupGroup key is is the LookupIPAddr.host
195+
// argument.
196+
// The return values are ([]IPAddr, error).
197+
var lookupGroup singleflight.Group
198+
199+
// lookupIPReturn turns the return values from singleflight.Do into
200+
// the return values from LookupIP.
201+
func lookupIPReturn(addrsi interface{}, err error, shared bool) ([]IPAddr, error) {
202+
if err != nil {
203+
return nil, err
204+
}
205+
addrs := addrsi.([]IPAddr)
206+
if shared {
207+
clone := make([]IPAddr, len(addrs))
208+
copy(clone, addrs)
209+
addrs = clone
210+
}
211+
return addrs, nil
212+
}
213+
214+
// ipAddrsEface returns an empty interface slice of addrs.
215+
func ipAddrsEface(addrs []IPAddr) []interface{} {
216+
s := make([]interface{}, len(addrs))
217+
for i, v := range addrs {
218+
s[i] = v
219+
}
220+
return s
221+
}
222+
204223
// LookupPort looks up the port for the given network and service.
205224
func LookupPort(network, service string) (port int, err error) {
225+
return DefaultResolver.LookupPort(context.Background(), network, service)
226+
}
227+
228+
// LookupPort looks up the port for the given network and service.
229+
func (r *Resolver) LookupPort(ctx context.Context, network, service string) (port int, err error) {
206230
port, needsLookup := parsePort(service)
207231
if needsLookup {
208-
port, err = lookupPort(context.Background(), network, service)
232+
port, err = lookupPort(ctx, network, service)
209233
if err != nil {
210234
return 0, err
211235
}
@@ -224,6 +248,14 @@ func LookupCNAME(name string) (cname string, err error) {
224248
return lookupCNAME(context.Background(), name)
225249
}
226250

251+
// LookupCNAME returns the canonical DNS host for the given name.
252+
// Callers that do not care about the canonical name can call
253+
// LookupHost or LookupIP directly; both take care of resolving
254+
// the canonical name as part of the lookup.
255+
func (r *Resolver) LookupCNAME(ctx context.Context, name string) (cname string, err error) {
256+
return lookupCNAME(ctx, name)
257+
}
258+
227259
// LookupSRV tries to resolve an SRV query of the given service,
228260
// protocol, and domain name. The proto is "tcp" or "udp".
229261
// The returned records are sorted by priority and randomized
@@ -237,23 +269,57 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err
237269
return lookupSRV(context.Background(), service, proto, name)
238270
}
239271

272+
// LookupSRV tries to resolve an SRV query of the given service,
273+
// protocol, and domain name. The proto is "tcp" or "udp".
274+
// The returned records are sorted by priority and randomized
275+
// by weight within a priority.
276+
//
277+
// LookupSRV constructs the DNS name to look up following RFC 2782.
278+
// That is, it looks up _service._proto.name. To accommodate services
279+
// publishing SRV records under non-standard names, if both service
280+
// and proto are empty strings, LookupSRV looks up name directly.
281+
func (r *Resolver) LookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) {
282+
return lookupSRV(ctx, service, proto, name)
283+
}
284+
240285
// LookupMX returns the DNS MX records for the given domain name sorted by preference.
241-
func LookupMX(name string) (mxs []*MX, err error) {
286+
func LookupMX(name string) ([]*MX, error) {
242287
return lookupMX(context.Background(), name)
243288
}
244289

290+
// LookupMX returns the DNS MX records for the given domain name sorted by preference.
291+
func (r *Resolver) LookupMX(ctx context.Context, name string) ([]*MX, error) {
292+
return lookupMX(ctx, name)
293+
}
294+
245295
// LookupNS returns the DNS NS records for the given domain name.
246-
func LookupNS(name string) (nss []*NS, err error) {
296+
func LookupNS(name string) ([]*NS, error) {
247297
return lookupNS(context.Background(), name)
248298
}
249299

300+
// LookupNS returns the DNS NS records for the given domain name.
301+
func (r *Resolver) LookupNS(ctx context.Context, name string) ([]*NS, error) {
302+
return lookupNS(ctx, name)
303+
}
304+
250305
// LookupTXT returns the DNS TXT records for the given domain name.
251-
func LookupTXT(name string) (txts []string, err error) {
306+
func LookupTXT(name string) ([]string, error) {
252307
return lookupTXT(context.Background(), name)
253308
}
254309

310+
// LookupTXT returns the DNS TXT records for the given domain name.
311+
func (r *Resolver) LookupTXT(ctx context.Context, name string) ([]string, error) {
312+
return lookupTXT(ctx, name)
313+
}
314+
255315
// LookupAddr performs a reverse lookup for the given address, returning a list
256316
// of names mapping to that address.
257317
func LookupAddr(addr string) (names []string, err error) {
258318
return lookupAddr(context.Background(), addr)
259319
}
320+
321+
// LookupAddr performs a reverse lookup for the given address, returning a list
322+
// of names mapping to that address.
323+
func (r *Resolver) LookupAddr(ctx context.Context, addr string) (names []string, err error) {
324+
return lookupAddr(ctx, addr)
325+
}

0 commit comments

Comments
 (0)