@@ -84,94 +84,83 @@ func lookupPortMap(network, service string) (port int, error error) {
84
84
return 0 , & AddrError {Err : "unknown port" , Addr : network + "/" + service }
85
85
}
86
86
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
+
87
111
// 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.
89
113
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 )
99
115
}
100
116
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 ) {
104
120
// Make sure that no matter what we do later, host=="" is rejected.
105
121
// ParseIP, for example, does accept empty strings.
106
122
if host == "" {
107
123
return nil , & DNSError {Err : errNoSuchHost .Error (), Name : host }
108
124
}
109
125
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
119
127
}
120
- return
128
+ return lookupHost ( ctx , host )
121
129
}
122
130
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 )
138
135
if err != nil {
139
136
return nil , err
140
137
}
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
146
141
}
147
- return addrs , nil
142
+ return ips , nil
148
143
}
149
144
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
155
155
}
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 ) {
167
156
trace , _ := ctx .Value (nettrace.TraceKey {}).(* nettrace.Trace )
168
157
if trace != nil && trace .DNSStart != nil {
169
158
trace .DNSStart (host )
170
159
}
171
160
// The underlying resolver func is lookupIP by default but it
172
161
// can be overridden by tests. This is needed by net/http, so it
173
162
// uses a context key instead of unexported variables.
174
- resolverFunc := lookupIP
163
+ resolverFunc := r . lookupIPFunc ()
175
164
if alt , _ := ctx .Value (nettrace.LookupIPAltResolverKey {}).(func (context.Context , string ) ([]IPAddr , error )); alt != nil {
176
165
resolverFunc = alt
177
166
}
@@ -201,11 +190,46 @@ func lookupIPContext(ctx context.Context, host string) (addrs []IPAddr, err erro
201
190
}
202
191
}
203
192
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
+
204
223
// LookupPort looks up the port for the given network and service.
205
224
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 ) {
206
230
port , needsLookup := parsePort (service )
207
231
if needsLookup {
208
- port , err = lookupPort (context . Background () , network , service )
232
+ port , err = lookupPort (ctx , network , service )
209
233
if err != nil {
210
234
return 0 , err
211
235
}
@@ -224,6 +248,14 @@ func LookupCNAME(name string) (cname string, err error) {
224
248
return lookupCNAME (context .Background (), name )
225
249
}
226
250
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
+
227
259
// LookupSRV tries to resolve an SRV query of the given service,
228
260
// protocol, and domain name. The proto is "tcp" or "udp".
229
261
// 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
237
269
return lookupSRV (context .Background (), service , proto , name )
238
270
}
239
271
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
+
240
285
// 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 ) {
242
287
return lookupMX (context .Background (), name )
243
288
}
244
289
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
+
245
295
// 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 ) {
247
297
return lookupNS (context .Background (), name )
248
298
}
249
299
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
+
250
305
// 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 ) {
252
307
return lookupTXT (context .Background (), name )
253
308
}
254
309
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
+
255
315
// LookupAddr performs a reverse lookup for the given address, returning a list
256
316
// of names mapping to that address.
257
317
func LookupAddr (addr string ) (names []string , err error ) {
258
318
return lookupAddr (context .Background (), addr )
259
319
}
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