Commit 78cf0e56 authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

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/45088Reviewed-by: default avatarPaul Marks <pmarks@google.com>
parent d8a7990f
......@@ -887,3 +887,21 @@ func TestCancelAfterDial(t *testing.T) {
try()
}
}
// Issue 18806: it should always be possible to net.Dial a
// net.Listener().Addr().String when the listen address was ":n", even
// if the machine has halfway configured IPv6 such that it can bind on
// "::" not connect back to that same address.
func TestDialListenerAddr(t *testing.T) {
ln, err := Listen("tcp", ":0")
if err != nil {
t.Fatal(err)
}
defer ln.Close()
addr := ln.Addr().String()
c, err := Dial("tcp", addr)
if err != nil {
t.Fatalf("for addr %q, dial error: %v", addr, err)
}
c.Close()
}
......@@ -254,6 +254,13 @@ func (r *Resolver) internetAddrList(ctx context.Context, net, addr string) (addr
ips = []IPAddr{{IP: ip}}
} else if ip, zone := parseIPv6(host, true); ip != nil {
ips = []IPAddr{{IP: ip, Zone: zone}}
// Issue 18806: if the machine has halfway configured
// IPv6 such that it can bind on "::" (IPv6unspecified)
// but not connect back to that same address, fall
// back to dialing 0.0.0.0.
if ip.Equal(IPv6unspecified) {
ips = append(ips, IPAddr{IP: IPv4zero})
}
} else {
// Try as a DNS name.
ips, err = r.LookupIPAddr(ctx, host)
......
......@@ -177,6 +177,11 @@ func (sa *SockaddrInet4) copy() Sockaddr {
func (sa *SockaddrInet4) key() interface{} { return *sa }
func isIPv4Localhost(sa Sockaddr) bool {
sa4, ok := sa.(*SockaddrInet4)
return ok && sa4.Addr == [4]byte{127, 0, 0, 1}
}
type SockaddrInet6 struct {
Port int
ZoneId uint32
......@@ -601,6 +606,14 @@ func (f *netFile) connect(sa Sockaddr) error {
return EISCONN
}
l, ok := net.listener[netAddr{f.proto, f.sotype, sa.key()}]
if !ok {
// If we're dialing 127.0.0.1 but found nothing, try
// 0.0.0.0 also. (Issue 20611)
if isIPv4Localhost(sa) {
sa = &SockaddrInet4{Port: sa.(*SockaddrInet4).Port}
l, ok = net.listener[netAddr{f.proto, f.sotype, sa.key()}]
}
}
if !ok || l.listenerClosed() {
net.Unlock()
return ECONNREFUSED
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment