Skip to content

Commit e341bae

Browse files
cezarsaianlancetaylor
authored andcommitted
net: use network and host as singleflight key during lookupIP
In CL 120215 the cgo resolver was changed to have different logic based on the network being queried. However, the singleflight cache key wasn't updated to also include the network. This way it was possible for concurrent queries to return the result for the wrong network. This CL changes the key to include both network and host, fixing the problem. Fixes #30521 Change-Id: I8b41b0ce1d9a02d18876c43e347654312eba22fc Reviewed-on: https://go-review.googlesource.com/c/go/+/166037 Reviewed-by: Brad Fitzpatrick <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]>
1 parent 6a9da69 commit e341bae

File tree

2 files changed

+67
-2
lines changed

2 files changed

+67
-2
lines changed

src/net/lookup.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,9 @@ func (r *Resolver) lookupIPAddr(ctx context.Context, network, host string) ([]IP
262262
// only the values in context. See Issue 28600.
263263
lookupGroupCtx, lookupGroupCancel := context.WithCancel(withUnexpiredValuesPreserved(ctx))
264264

265+
lookupKey := network + "\000" + host
265266
dnsWaitGroup.Add(1)
266-
ch, called := r.getLookupGroup().DoChan(host, func() (interface{}, error) {
267+
ch, called := r.getLookupGroup().DoChan(lookupKey, func() (interface{}, error) {
267268
defer dnsWaitGroup.Done()
268269
return testHookLookupIP(lookupGroupCtx, resolverFunc, network, host)
269270
})
@@ -280,7 +281,7 @@ func (r *Resolver) lookupIPAddr(ctx context.Context, network, host string) ([]IP
280281
// let the lookup continue uncanceled, and let later
281282
// lookups with the same key share the result.
282283
// See issues 8602, 20703, 22724.
283-
if r.getLookupGroup().ForgetUnshared(host) {
284+
if r.getLookupGroup().ForgetUnshared(lookupKey) {
284285
lookupGroupCancel()
285286
} else {
286287
go func() {

src/net/lookup_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"sort"
1717
"strings"
1818
"sync"
19+
"sync/atomic"
1920
"testing"
2021
"time"
2122
)
@@ -1096,6 +1097,69 @@ func TestLookupIPAddrPreservesContextValues(t *testing.T) {
10961097
}
10971098
}
10981099

1100+
// Issue 30521: The lookup group should call the resolver for each network.
1101+
func TestLookupIPAddrConcurrentCallsForNetworks(t *testing.T) {
1102+
origTestHookLookupIP := testHookLookupIP
1103+
defer func() { testHookLookupIP = origTestHookLookupIP }()
1104+
1105+
queries := [][]string{
1106+
{"udp", "golang.org"},
1107+
{"udp4", "golang.org"},
1108+
{"udp6", "golang.org"},
1109+
{"udp", "golang.org"},
1110+
{"udp", "golang.org"},
1111+
}
1112+
results := map[[2]string][]IPAddr{
1113+
{"udp", "golang.org"}: {
1114+
{IP: IPv4(127, 0, 0, 1)},
1115+
{IP: IPv6loopback},
1116+
},
1117+
{"udp4", "golang.org"}: {
1118+
{IP: IPv4(127, 0, 0, 1)},
1119+
},
1120+
{"udp6", "golang.org"}: {
1121+
{IP: IPv6loopback},
1122+
},
1123+
}
1124+
calls := int32(0)
1125+
waitCh := make(chan struct{})
1126+
testHookLookupIP = func(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
1127+
// We'll block until this is called one time for each different
1128+
// expected result. This will ensure that the lookup group would wait
1129+
// for the existing call if it was to be reused.
1130+
if atomic.AddInt32(&calls, 1) == int32(len(results)) {
1131+
close(waitCh)
1132+
}
1133+
select {
1134+
case <-waitCh:
1135+
case <-ctx.Done():
1136+
return nil, ctx.Err()
1137+
}
1138+
return results[[2]string{network, host}], nil
1139+
}
1140+
1141+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
1142+
defer cancel()
1143+
wg := sync.WaitGroup{}
1144+
for _, q := range queries {
1145+
network := q[0]
1146+
host := q[1]
1147+
wg.Add(1)
1148+
go func() {
1149+
defer wg.Done()
1150+
gotIPs, err := DefaultResolver.lookupIPAddr(ctx, network, host)
1151+
if err != nil {
1152+
t.Errorf("lookupIPAddr(%v, %v): unexpected error: %v", network, host, err)
1153+
}
1154+
wantIPs := results[[2]string{network, host}]
1155+
if !reflect.DeepEqual(gotIPs, wantIPs) {
1156+
t.Errorf("lookupIPAddr(%v, %v): mismatched IPAddr results\n\tGot: %v\n\tWant: %v", network, host, gotIPs, wantIPs)
1157+
}
1158+
}()
1159+
}
1160+
wg.Wait()
1161+
}
1162+
10991163
func TestWithUnexpiredValuesPreserved(t *testing.T) {
11001164
ctx, cancel := context.WithCancel(context.Background())
11011165

0 commit comments

Comments
 (0)