diff --git a/src/net/iprawsock.go b/src/net/iprawsock.go index 00f59631a2a57..c4b54f00c4e6e 100644 --- a/src/net/iprawsock.go +++ b/src/net/iprawsock.go @@ -91,7 +91,7 @@ func ResolveIPAddr(network, address string) (*IPAddr, error) { if err != nil { return nil, err } - return addrs.first(isIPv4).(*IPAddr), nil + return addrs.forResolve(network, address).(*IPAddr), nil } // IPConn is the implementation of the Conn and PacketConn interfaces diff --git a/src/net/ipsock.go b/src/net/ipsock.go index 7dafcaf630093..947bdf34897e7 100644 --- a/src/net/ipsock.go +++ b/src/net/ipsock.go @@ -50,7 +50,7 @@ func supportsIPv4map() bool { // An addrList represents a list of network endpoint addresses. type addrList []Addr -// isIPv4 returns true if the Addr contains an IPv4 address. +// isIPv4 reports whether addr contains an IPv4 address. func isIPv4(addr Addr) bool { switch addr := addr.(type) { case *TCPAddr: @@ -63,6 +63,28 @@ func isIPv4(addr Addr) bool { return false } +// isNotIPv4 reports whether addr does not contain an IPv4 address. +func isNotIPv4(addr Addr) bool { return !isIPv4(addr) } + +// forResolve returns the most appropriate address in address for +// a call to ResolveTCPAddr, ResolveUDPAddr, or ResolveIPAddr. +// IPv4 is preferred, unless addr contains an IPv6 literal. +func (addrs addrList) forResolve(network, addr string) Addr { + var want6 bool + switch network { + case "ip": + // IPv6 literal (addr does NOT contain a port) + want6 = count(addr, ':') > 0 + case "tcp", "udp": + // IPv6 literal. (addr contains a port, so look for '[') + want6 = count(addr, '[') > 0 + } + if want6 { + return addrs.first(isNotIPv4) + } + return addrs.first(isIPv4) +} + // first returns the first address which satisfies strategy, or if // none do, then the first address of any kind. func (addrs addrList) first(strategy func(Addr) bool) Addr { diff --git a/src/net/main_test.go b/src/net/main_test.go index bbf32cfcd945c..3e7a85ad2d9fc 100644 --- a/src/net/main_test.go +++ b/src/net/main_test.go @@ -89,6 +89,12 @@ func setupTestData() { resolveTCPAddrTests = append(resolveTCPAddrTests, resolveTCPAddrTest{"tcp6", "localhost:3", &TCPAddr{IP: IPv6loopback, Port: 3}, nil}) resolveUDPAddrTests = append(resolveUDPAddrTests, resolveUDPAddrTest{"udp6", "localhost:3", &UDPAddr{IP: IPv6loopback, Port: 3}, nil}) resolveIPAddrTests = append(resolveIPAddrTests, resolveIPAddrTest{"ip6", "localhost", &IPAddr{IP: IPv6loopback}, nil}) + + // Issue 20911: don't return IPv4 addresses for + // Resolve*Addr calls of the IPv6 unspecified address. + resolveTCPAddrTests = append(resolveTCPAddrTests, resolveTCPAddrTest{"tcp", "[::]:4", &TCPAddr{IP: IPv6unspecified, Port: 4}, nil}) + resolveUDPAddrTests = append(resolveUDPAddrTests, resolveUDPAddrTest{"udp", "[::]:4", &UDPAddr{IP: IPv6unspecified, Port: 4}, nil}) + resolveIPAddrTests = append(resolveIPAddrTests, resolveIPAddrTest{"ip", "::", &IPAddr{IP: IPv6unspecified}, nil}) } ifi := loopbackInterface() diff --git a/src/net/tcpsock.go b/src/net/tcpsock.go index 74878fc614a6c..e957aa3005a17 100644 --- a/src/net/tcpsock.go +++ b/src/net/tcpsock.go @@ -77,7 +77,7 @@ func ResolveTCPAddr(network, address string) (*TCPAddr, error) { if err != nil { return nil, err } - return addrs.first(isIPv4).(*TCPAddr), nil + return addrs.forResolve(network, address).(*TCPAddr), nil } // TCPConn is an implementation of the Conn interface for TCP network diff --git a/src/net/udpsock.go b/src/net/udpsock.go index 28b6906c5c91b..2c0f74fdabdd2 100644 --- a/src/net/udpsock.go +++ b/src/net/udpsock.go @@ -80,7 +80,7 @@ func ResolveUDPAddr(network, address string) (*UDPAddr, error) { if err != nil { return nil, err } - return addrs.first(isIPv4).(*UDPAddr), nil + return addrs.forResolve(network, address).(*UDPAddr), nil } // UDPConn is the implementation of the Conn and PacketConn interfaces