Skip to content

Commit c659be4

Browse files
ekalininbradfitz
authored andcommitted
net: make cgo resolver work more accurately with network parameter
Unlike the go resolver, the existing cgo resolver exchanges both DNS A and AAAA RR queries unconditionally and causes unreasonable connection setup latencies to applications using the cgo resolver. This change adds new argument (`network`) in all functions through the series of calls: from Resolver.internetAddrList to cgoLookupIPCNAME. Benefit: no redundant DNS calls if certain IP version is used IPv4/IPv6 (no `AAAA` DNS requests if used tcp4, udp4, ip4 network. And vice versa: no `A` DNS requests if used tcp6, udp6, ip6 network) Fixes #25947 Change-Id: I39edbd726d82d6133fdada4d06cd90d401e7e669 Reviewed-on: https://go-review.googlesource.com/c/120215 Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent fc4f2e5 commit c659be4

15 files changed

+110
-44
lines changed

src/net/cgo_stub.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func cgoLookupPort(ctx context.Context, network, service string) (port int, err
2424
return 0, nil, false
2525
}
2626

27-
func cgoLookupIP(ctx context.Context, name string) (addrs []IPAddr, err error, completed bool) {
27+
func cgoLookupIP(ctx context.Context, network, name string) (addrs []IPAddr, err error, completed bool) {
2828
return nil, nil, false
2929
}
3030

src/net/cgo_unix.go

+21-16
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ type reverseLookupResult struct {
4949
}
5050

5151
func cgoLookupHost(ctx context.Context, name string) (hosts []string, err error, completed bool) {
52-
addrs, err, completed := cgoLookupIP(ctx, name)
52+
addrs, err, completed := cgoLookupIP(ctx, "ip", name)
5353
for _, addr := range addrs {
5454
hosts = append(hosts, addr.String())
5555
}
@@ -69,13 +69,11 @@ func cgoLookupPort(ctx context.Context, network, service string) (port int, err
6969
default:
7070
return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}, true
7171
}
72-
if len(network) >= 4 {
73-
switch network[3] {
74-
case '4':
75-
hints.ai_family = C.AF_INET
76-
case '6':
77-
hints.ai_family = C.AF_INET6
78-
}
72+
switch ipVersion(network) {
73+
case '4':
74+
hints.ai_family = C.AF_INET
75+
case '6':
76+
hints.ai_family = C.AF_INET6
7977
}
8078
if ctx.Done() == nil {
8179
port, err := cgoLookupServicePort(&hints, network, service)
@@ -135,13 +133,20 @@ func cgoPortLookup(result chan<- portLookupResult, hints *C.struct_addrinfo, net
135133
result <- portLookupResult{port, err}
136134
}
137135

138-
func cgoLookupIPCNAME(name string) (addrs []IPAddr, cname string, err error) {
136+
func cgoLookupIPCNAME(network, name string) (addrs []IPAddr, cname string, err error) {
139137
acquireThread()
140138
defer releaseThread()
141139

142140
var hints C.struct_addrinfo
143141
hints.ai_flags = cgoAddrInfoFlags
144142
hints.ai_socktype = C.SOCK_STREAM
143+
hints.ai_family = C.AF_UNSPEC
144+
switch ipVersion(network) {
145+
case '4':
146+
hints.ai_family = C.AF_INET
147+
case '6':
148+
hints.ai_family = C.AF_INET6
149+
}
145150

146151
h := make([]byte, len(name)+1)
147152
copy(h, name)
@@ -197,18 +202,18 @@ func cgoLookupIPCNAME(name string) (addrs []IPAddr, cname string, err error) {
197202
return addrs, cname, nil
198203
}
199204

200-
func cgoIPLookup(result chan<- ipLookupResult, name string) {
201-
addrs, cname, err := cgoLookupIPCNAME(name)
205+
func cgoIPLookup(result chan<- ipLookupResult, network, name string) {
206+
addrs, cname, err := cgoLookupIPCNAME(network, name)
202207
result <- ipLookupResult{addrs, cname, err}
203208
}
204209

205-
func cgoLookupIP(ctx context.Context, name string) (addrs []IPAddr, err error, completed bool) {
210+
func cgoLookupIP(ctx context.Context, network, name string) (addrs []IPAddr, err error, completed bool) {
206211
if ctx.Done() == nil {
207-
addrs, _, err = cgoLookupIPCNAME(name)
212+
addrs, _, err = cgoLookupIPCNAME(network, name)
208213
return addrs, err, true
209214
}
210215
result := make(chan ipLookupResult, 1)
211-
go cgoIPLookup(result, name)
216+
go cgoIPLookup(result, network, name)
212217
select {
213218
case r := <-result:
214219
return r.addrs, r.err, true
@@ -219,11 +224,11 @@ func cgoLookupIP(ctx context.Context, name string) (addrs []IPAddr, err error, c
219224

220225
func cgoLookupCNAME(ctx context.Context, name string) (cname string, err error, completed bool) {
221226
if ctx.Done() == nil {
222-
_, cname, err = cgoLookupIPCNAME(name)
227+
_, cname, err = cgoLookupIPCNAME("ip", name)
223228
return cname, err, true
224229
}
225230
result := make(chan ipLookupResult, 1)
226-
go cgoIPLookup(result, name)
231+
go cgoIPLookup(result, "ip", name)
227232
select {
228233
case r := <-result:
229234
return r.cname, r.err, true

src/net/cgo_unix_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515
func TestCgoLookupIP(t *testing.T) {
1616
defer dnsWaitGroup.Wait()
1717
ctx := context.Background()
18-
_, err, ok := cgoLookupIP(ctx, "localhost")
18+
_, err, ok := cgoLookupIP(ctx, "ip", "localhost")
1919
if !ok {
2020
t.Errorf("cgoLookupIP must not be a placeholder")
2121
}
@@ -28,7 +28,7 @@ func TestCgoLookupIPWithCancel(t *testing.T) {
2828
defer dnsWaitGroup.Wait()
2929
ctx, cancel := context.WithCancel(context.Background())
3030
defer cancel()
31-
_, err, ok := cgoLookupIP(ctx, "localhost")
31+
_, err, ok := cgoLookupIP(ctx, "ip", "localhost")
3232
if !ok {
3333
t.Errorf("cgoLookupIP must not be a placeholder")
3434
}

src/net/dial_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ func TestDialParallel(t *testing.T) {
346346
}
347347
}
348348

349-
func lookupSlowFast(ctx context.Context, fn func(context.Context, string) ([]IPAddr, error), host string) ([]IPAddr, error) {
349+
func lookupSlowFast(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
350350
switch host {
351351
case "slow6loopback4":
352352
// Returns a slow IPv6 address, and a local IPv4 address.
@@ -355,7 +355,7 @@ func lookupSlowFast(ctx context.Context, fn func(context.Context, string) ([]IPA
355355
{IP: ParseIP("127.0.0.1")},
356356
}, nil
357357
default:
358-
return fn(ctx, host)
358+
return fn(ctx, network, host)
359359
}
360360
}
361361

src/net/error_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ func TestDialError(t *testing.T) {
144144

145145
origTestHookLookupIP := testHookLookupIP
146146
defer func() { testHookLookupIP = origTestHookLookupIP }()
147-
testHookLookupIP = func(ctx context.Context, fn func(context.Context, string) ([]IPAddr, error), host string) ([]IPAddr, error) {
147+
testHookLookupIP = func(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
148148
return nil, &DNSError{Err: "dial error test", Name: "name", Server: "server", IsTimeout: true}
149149
}
150150
sw.Set(socktest.FilterConnect, func(so *socktest.Status) (socktest.AfterFilter, error) {
@@ -293,7 +293,7 @@ func TestListenError(t *testing.T) {
293293

294294
origTestHookLookupIP := testHookLookupIP
295295
defer func() { testHookLookupIP = origTestHookLookupIP }()
296-
testHookLookupIP = func(_ context.Context, fn func(context.Context, string) ([]IPAddr, error), host string) ([]IPAddr, error) {
296+
testHookLookupIP = func(_ context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
297297
return nil, &DNSError{Err: "listen error test", Name: "name", Server: "server", IsTimeout: true}
298298
}
299299
sw.Set(socktest.FilterListen, func(so *socktest.Status) (socktest.AfterFilter, error) {
@@ -353,7 +353,7 @@ func TestListenPacketError(t *testing.T) {
353353

354354
origTestHookLookupIP := testHookLookupIP
355355
defer func() { testHookLookupIP = origTestHookLookupIP }()
356-
testHookLookupIP = func(_ context.Context, fn func(context.Context, string) ([]IPAddr, error), host string) ([]IPAddr, error) {
356+
testHookLookupIP = func(_ context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
357357
return nil, &DNSError{Err: "listen error test", Name: "name", Server: "server", IsTimeout: true}
358358
}
359359

src/net/hook.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ var (
1313
testHookHostsPath = "/etc/hosts"
1414
testHookLookupIP = func(
1515
ctx context.Context,
16-
fn func(context.Context, string) ([]IPAddr, error),
16+
fn func(context.Context, string, string) ([]IPAddr, error),
17+
network string,
1718
host string,
1819
) ([]IPAddr, error) {
19-
return fn(ctx, host)
20+
return fn(ctx, network, host)
2021
}
2122
testHookSetKeepAlive = func() {}
2223
)

src/net/http/transport_test.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -3825,9 +3825,9 @@ func testTransportEventTrace(t *testing.T, h2 bool, noHooks bool) {
38253825
}
38263826

38273827
// Install a fake DNS server.
3828-
ctx := context.WithValue(context.Background(), nettrace.LookupIPAltResolverKey{}, func(ctx context.Context, host string) ([]net.IPAddr, error) {
3828+
ctx := context.WithValue(context.Background(), nettrace.LookupIPAltResolverKey{}, func(ctx context.Context, network, host string) ([]net.IPAddr, error) {
38293829
if host != "dns-is-faked.golang" {
3830-
t.Errorf("unexpected DNS host lookup for %q", host)
3830+
t.Errorf("unexpected DNS host lookup for %q/%q", network, host)
38313831
return nil, nil
38323832
}
38333833
return []net.IPAddr{{IP: net.ParseIP(ip)}}, nil
@@ -4176,7 +4176,7 @@ func TestTransportMaxIdleConns(t *testing.T) {
41764176
if err != nil {
41774177
t.Fatal(err)
41784178
}
4179-
ctx := context.WithValue(context.Background(), nettrace.LookupIPAltResolverKey{}, func(ctx context.Context, host string) ([]net.IPAddr, error) {
4179+
ctx := context.WithValue(context.Background(), nettrace.LookupIPAltResolverKey{}, func(ctx context.Context, _, host string) ([]net.IPAddr, error) {
41804180
return []net.IPAddr{{IP: net.ParseIP(ip)}}, nil
41814181
})
41824182

@@ -4416,9 +4416,9 @@ func testTransportIDNA(t *testing.T, h2 bool) {
44164416
}
44174417

44184418
// Install a fake DNS server.
4419-
ctx := context.WithValue(context.Background(), nettrace.LookupIPAltResolverKey{}, func(ctx context.Context, host string) ([]net.IPAddr, error) {
4419+
ctx := context.WithValue(context.Background(), nettrace.LookupIPAltResolverKey{}, func(ctx context.Context, network, host string) ([]net.IPAddr, error) {
44204420
if host != punyDomain {
4421-
t.Errorf("got DNS host lookup for %q; want %q", host, punyDomain)
4421+
t.Errorf("got DNS host lookup for %q/%q; want %q", network, host, punyDomain)
44224422
return nil, nil
44234423
}
44244424
return []net.IPAddr{{IP: net.ParseIP(ip)}}, nil

src/net/ipsock.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ func (r *Resolver) internetAddrList(ctx context.Context, net, addr string) (addr
277277
}
278278

279279
// Try as a literal IP address, then as a DNS name.
280-
ips, err := r.LookupIPAddr(ctx, host)
280+
ips, err := r.lookupIPAddr(ctx, net, host)
281281
if err != nil {
282282
return nil, err
283283
}

src/net/lookup.go

+28-2
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,19 @@ func lookupPortMap(network, service string) (port int, error error) {
9797
return 0, &AddrError{Err: "unknown port", Addr: network + "/" + service}
9898
}
9999

100+
// ipVersion returns the provided network's IP version: '4', '6' or 0
101+
// if network does not end in a '4' or '6' byte.
102+
func ipVersion(network string) byte {
103+
if network == "" {
104+
return 0
105+
}
106+
n := network[len(network)-1]
107+
if n != '4' && n != '6' {
108+
n = 0
109+
}
110+
return n
111+
}
112+
100113
// DefaultResolver is the resolver used by the package-level Lookup
101114
// functions and by Dialers without a specified Resolver.
102115
var DefaultResolver = &Resolver{}
@@ -189,6 +202,12 @@ func LookupIP(host string) ([]IP, error) {
189202
// LookupIPAddr looks up host using the local resolver.
190203
// It returns a slice of that host's IPv4 and IPv6 addresses.
191204
func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, error) {
205+
return r.lookupIPAddr(ctx, "ip", host)
206+
}
207+
208+
// lookupIPAddr looks up host using the local resolver and particular network.
209+
// It returns a slice of that host's IPv4 and IPv6 addresses.
210+
func (r *Resolver) lookupIPAddr(ctx context.Context, network, host string) ([]IPAddr, error) {
192211
// Make sure that no matter what we do later, host=="" is rejected.
193212
// parseIP, for example, does accept empty strings.
194213
if host == "" {
@@ -205,7 +224,7 @@ func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, err
205224
// can be overridden by tests. This is needed by net/http, so it
206225
// uses a context key instead of unexported variables.
207226
resolverFunc := r.lookupIP
208-
if alt, _ := ctx.Value(nettrace.LookupIPAltResolverKey{}).(func(context.Context, string) ([]IPAddr, error)); alt != nil {
227+
if alt, _ := ctx.Value(nettrace.LookupIPAltResolverKey{}).(func(context.Context, string, string) ([]IPAddr, error)); alt != nil {
209228
resolverFunc = alt
210229
}
211230

@@ -218,7 +237,7 @@ func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, err
218237
dnsWaitGroup.Add(1)
219238
ch, called := r.getLookupGroup().DoChan(host, func() (interface{}, error) {
220239
defer dnsWaitGroup.Done()
221-
return testHookLookupIP(lookupGroupCtx, resolverFunc, host)
240+
return testHookLookupIP(lookupGroupCtx, resolverFunc, network, host)
222241
})
223242
if !called {
224243
dnsWaitGroup.Done()
@@ -289,6 +308,13 @@ func LookupPort(network, service string) (port int, err error) {
289308
func (r *Resolver) LookupPort(ctx context.Context, network, service string) (port int, err error) {
290309
port, needsLookup := parsePort(service)
291310
if needsLookup {
311+
switch network {
312+
case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6":
313+
case "": // a hint wildcard for Go 1.0 undocumented behavior
314+
network = "ip"
315+
default:
316+
return 0, &AddrError{Err: "unknown network", Addr: network}
317+
}
292318
port, err = r.lookupPort(ctx, network, service)
293319
if err != nil {
294320
return 0, err

src/net/lookup_fake.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func (*Resolver) lookupHost(ctx context.Context, host string) (addrs []string, e
1919
return nil, syscall.ENOPROTOOPT
2020
}
2121

22-
func (*Resolver) lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
22+
func (*Resolver) lookupIP(ctx context.Context, network, host string) (addrs []IPAddr, err error) {
2323
return nil, syscall.ENOPROTOOPT
2424
}
2525

src/net/lookup_plan9.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ loop:
176176
return
177177
}
178178

179-
func (r *Resolver) lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
179+
func (r *Resolver) lookupIP(ctx context.Context, _, host string) (addrs []IPAddr, err error) {
180180
lits, err := r.lookupHost(ctx, host)
181181
if err != nil {
182182
return

src/net/lookup_test.go

+28-2
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@ import (
2020
"time"
2121
)
2222

23-
func lookupLocalhost(ctx context.Context, fn func(context.Context, string) ([]IPAddr, error), host string) ([]IPAddr, error) {
23+
func lookupLocalhost(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
2424
switch host {
2525
case "localhost":
2626
return []IPAddr{
2727
{IP: IPv4(127, 0, 0, 1)},
2828
{IP: IPv6loopback},
2929
}, nil
3030
default:
31-
return fn(ctx, host)
31+
return fn(ctx, network, host)
3232
}
3333
}
3434

@@ -1008,3 +1008,29 @@ func TestConcurrentPreferGoResolversDial(t *testing.T) {
10081008
}
10091009
}
10101010
}
1011+
1012+
var ipVersionTests = []struct {
1013+
network string
1014+
version byte
1015+
}{
1016+
{"tcp", 0},
1017+
{"tcp4", '4'},
1018+
{"tcp6", '6'},
1019+
{"udp", 0},
1020+
{"udp4", '4'},
1021+
{"udp6", '6'},
1022+
{"ip", 0},
1023+
{"ip4", '4'},
1024+
{"ip6", '6'},
1025+
{"ip7", 0},
1026+
{"", 0},
1027+
}
1028+
1029+
func TestIPVersion(t *testing.T) {
1030+
for _, tt := range ipVersionTests {
1031+
if version := ipVersion(tt.network); version != tt.version {
1032+
t.Errorf("Family for: %s. Expected: %s, Got: %s", tt.network,
1033+
string(tt.version), string(version))
1034+
}
1035+
}
1036+
}

src/net/lookup_unix.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,13 @@ func (r *Resolver) lookupHost(ctx context.Context, host string) (addrs []string,
8787
return r.goLookupHostOrder(ctx, host, order)
8888
}
8989

90-
func (r *Resolver) lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
90+
func (r *Resolver) lookupIP(ctx context.Context, network, host string) (addrs []IPAddr, err error) {
9191
if r.preferGo() {
9292
return r.goLookupIP(ctx, host)
9393
}
9494
order := systemConf().hostLookupOrder(r, host)
9595
if order == hostLookupCgo {
96-
if addrs, err, ok := cgoLookupIP(ctx, host); ok {
96+
if addrs, err, ok := cgoLookupIP(ctx, network, host); ok {
9797
return addrs, err
9898
}
9999
// cgo not available (or netgo); fall back to Go's DNS resolver

src/net/lookup_windows.go

+11-3
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func lookupProtocol(ctx context.Context, name string) (int, error) {
6565
}
6666

6767
func (r *Resolver) lookupHost(ctx context.Context, name string) ([]string, error) {
68-
ips, err := r.lookupIP(ctx, name)
68+
ips, err := r.lookupIP(ctx, "ip", name)
6969
if err != nil {
7070
return nil, err
7171
}
@@ -76,14 +76,22 @@ func (r *Resolver) lookupHost(ctx context.Context, name string) ([]string, error
7676
return addrs, nil
7777
}
7878

79-
func (r *Resolver) lookupIP(ctx context.Context, name string) ([]IPAddr, error) {
79+
func (r *Resolver) lookupIP(ctx context.Context, network, name string) ([]IPAddr, error) {
8080
// TODO(bradfitz,brainman): use ctx more. See TODO below.
8181

82+
var family int32 = syscall.AF_UNSPEC
83+
switch ipVersion(network) {
84+
case '4':
85+
family = syscall.AF_INET
86+
case '6':
87+
family = syscall.AF_INET6
88+
}
89+
8290
getaddr := func() ([]IPAddr, error) {
8391
acquireThread()
8492
defer releaseThread()
8593
hints := syscall.AddrinfoW{
86-
Family: syscall.AF_UNSPEC,
94+
Family: family,
8795
Socktype: syscall.SOCK_STREAM,
8896
Protocol: syscall.IPPROTO_IP,
8997
}

0 commit comments

Comments
 (0)