Skip to content

Commit 78cf0e5

Browse files
committed
net: make Dial("tcp", ln.Addr().String()) work even with bad IPv6 config
Some machines can be configured (or came/come configured) in such a state that IPv6 only half works: you can bind on [::]:n but not connect back to it. This implements a fallback such that it's guaranteed that this pattern works: ln, err := Listen("tcp", ":0") ... addr := ln.Addr().String() // "[::]:n" c, err := Dial("tcp", addr) ... which is also now tested. It will first try to dial "[::]:n", as before, but if that dial fails, it will also try "0.0.0.0:n". Fixes #18806 (contains more details) Fixes #20611 (I was going to fix nacl later, but it was easy enough) Change-Id: I1107eb197e902ae8185c781ad1bc4e2bc61d1f4c Reviewed-on: https://go-review.googlesource.com/45088 Reviewed-by: Paul Marks <pmarks@google.com>
1 parent d8a7990 commit 78cf0e5

File tree

3 files changed

+38
-0
lines changed

3 files changed

+38
-0
lines changed

src/net/dial_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,3 +887,21 @@ func TestCancelAfterDial(t *testing.T) {
887887
try()
888888
}
889889
}
890+
891+
// Issue 18806: it should always be possible to net.Dial a
892+
// net.Listener().Addr().String when the listen address was ":n", even
893+
// if the machine has halfway configured IPv6 such that it can bind on
894+
// "::" not connect back to that same address.
895+
func TestDialListenerAddr(t *testing.T) {
896+
ln, err := Listen("tcp", ":0")
897+
if err != nil {
898+
t.Fatal(err)
899+
}
900+
defer ln.Close()
901+
addr := ln.Addr().String()
902+
c, err := Dial("tcp", addr)
903+
if err != nil {
904+
t.Fatalf("for addr %q, dial error: %v", addr, err)
905+
}
906+
c.Close()
907+
}

src/net/ipsock.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,13 @@ func (r *Resolver) internetAddrList(ctx context.Context, net, addr string) (addr
254254
ips = []IPAddr{{IP: ip}}
255255
} else if ip, zone := parseIPv6(host, true); ip != nil {
256256
ips = []IPAddr{{IP: ip, Zone: zone}}
257+
// Issue 18806: if the machine has halfway configured
258+
// IPv6 such that it can bind on "::" (IPv6unspecified)
259+
// but not connect back to that same address, fall
260+
// back to dialing 0.0.0.0.
261+
if ip.Equal(IPv6unspecified) {
262+
ips = append(ips, IPAddr{IP: IPv4zero})
263+
}
257264
} else {
258265
// Try as a DNS name.
259266
ips, err = r.LookupIPAddr(ctx, host)

src/syscall/net_nacl.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,11 @@ func (sa *SockaddrInet4) copy() Sockaddr {
177177

178178
func (sa *SockaddrInet4) key() interface{} { return *sa }
179179

180+
func isIPv4Localhost(sa Sockaddr) bool {
181+
sa4, ok := sa.(*SockaddrInet4)
182+
return ok && sa4.Addr == [4]byte{127, 0, 0, 1}
183+
}
184+
180185
type SockaddrInet6 struct {
181186
Port int
182187
ZoneId uint32
@@ -601,6 +606,14 @@ func (f *netFile) connect(sa Sockaddr) error {
601606
return EISCONN
602607
}
603608
l, ok := net.listener[netAddr{f.proto, f.sotype, sa.key()}]
609+
if !ok {
610+
// If we're dialing 127.0.0.1 but found nothing, try
611+
// 0.0.0.0 also. (Issue 20611)
612+
if isIPv4Localhost(sa) {
613+
sa = &SockaddrInet4{Port: sa.(*SockaddrInet4).Port}
614+
l, ok = net.listener[netAddr{f.proto, f.sotype, sa.key()}]
615+
}
616+
}
604617
if !ok || l.listenerClosed() {
605618
net.Unlock()
606619
return ECONNREFUSED

0 commit comments

Comments
 (0)