]> Cypherpunks repositories - gostls13.git/commitdiff
net: make Dial("tcp", ln.Addr().String()) work even with bad IPv6 config
authorBrad Fitzpatrick <bradfitz@golang.org>
Wed, 7 Jun 2017 21:01:48 +0000 (21:01 +0000)
committerBrad Fitzpatrick <bradfitz@golang.org>
Thu, 8 Jun 2017 22:20:17 +0000 (22:20 +0000)
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>
src/net/dial_test.go
src/net/ipsock.go
src/syscall/net_nacl.go

index a49a9d742603bfb7085773a16988b8fdc5995c87..59a016a0c5fd55530d9a07b55cd4f304a4d1c689 100644 (file)
@@ -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()
+}
index ade6eab62a4c90e5b7fdfaca91447eaa79426342..6049692d376fd97c4bba41d7ea9bcfeccbf8e17b 100644 (file)
@@ -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)
index 9dc5d0ca0b65b591a45ac63869124622b0499798..b019cbff873c32af1c06620529c67be9c7de5642 100644 (file)
@@ -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