Skip to content

Commit 578e281

Browse files
cezarsabradfitz
authored andcommitted
[release-branch.go1.12] 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 #31062 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]> (cherry picked from commit e341bae) Reviewed-on: https://go-review.googlesource.com/c/go/+/170320 Run-TryBot: Andrew Bonventre <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 8acc2ea commit 578e281

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
)
@@ -1093,6 +1094,69 @@ func TestLookupIPAddrPreservesContextValues(t *testing.T) {
10931094
}
10941095
}
10951096

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

0 commit comments

Comments
 (0)