Skip to content

Commit a5179bd

Browse files
committed
net: don't return IPv4 unspecified addr for Resolve*Addr of [::] or [::]:n
ResolveTCPAddr, ResolveUDPAddr, and ResolveIPAddr return at most one address. When given a name like "golang.org" to resolve that might have more than 1 address, the net package has historically preferred IPv4 addresses, with the assumption that many users don't yet have IPv6 connectivity and randomly selecting between an IPv4 address and an IPv6 address at runtime wouldn't be a good experience for IPv4-only users. In CL 45088 (78cf0e5) I modified the resolution of the unspecified/empty address to internally resolve to both IPv6 "::" and 0.0.0.0 to fix issue #18806. That code has 3 other callers I hadn't considered, though: the Resolve*Addr functions. Since they preferred IPv4, any Resolve*Addr of "[::]:port" or "::" (for ResolveIPAddr) would internally resolve both "::" and 0.0.0.0 and then prefer 0.0.0.0, even though the user was looking up an IPv6 literal. Add tests and fix it, not by undoing the fix to #18806 but by selecting the preference function for Resolve*Addr more explicitly: we still prefer IPv4, but if the address being looked up was an IPv6 literal, prefer IPv6. The tests are skipped on machines without IPv6. Fixes #20911 Change-Id: Ib7036cc43182ae4118cd1390c254e17c04a251a3 Reviewed-on: https://go-review.googlesource.com/47554 Run-TryBot: Brad Fitzpatrick <[email protected]> Reviewed-by: Russ Cox <[email protected]>
1 parent 53d3183 commit a5179bd

File tree

5 files changed

+32
-4
lines changed

5 files changed

+32
-4
lines changed

src/net/iprawsock.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func ResolveIPAddr(network, address string) (*IPAddr, error) {
9191
if err != nil {
9292
return nil, err
9393
}
94-
return addrs.first(isIPv4).(*IPAddr), nil
94+
return addrs.forResolve(network, address).(*IPAddr), nil
9595
}
9696

9797
// IPConn is the implementation of the Conn and PacketConn interfaces

src/net/ipsock.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func supportsIPv4map() bool {
5050
// An addrList represents a list of network endpoint addresses.
5151
type addrList []Addr
5252

53-
// isIPv4 returns true if the Addr contains an IPv4 address.
53+
// isIPv4 reports whether addr contains an IPv4 address.
5454
func isIPv4(addr Addr) bool {
5555
switch addr := addr.(type) {
5656
case *TCPAddr:
@@ -63,6 +63,28 @@ func isIPv4(addr Addr) bool {
6363
return false
6464
}
6565

66+
// isNotIPv4 reports whether addr does not contain an IPv4 address.
67+
func isNotIPv4(addr Addr) bool { return !isIPv4(addr) }
68+
69+
// forResolve returns the most appropriate address in address for
70+
// a call to ResolveTCPAddr, ResolveUDPAddr, or ResolveIPAddr.
71+
// IPv4 is preferred, unless addr contains an IPv6 literal.
72+
func (addrs addrList) forResolve(network, addr string) Addr {
73+
var want6 bool
74+
switch network {
75+
case "ip":
76+
// IPv6 literal (addr does NOT contain a port)
77+
want6 = count(addr, ':') > 0
78+
case "tcp", "udp":
79+
// IPv6 literal. (addr contains a port, so look for '[')
80+
want6 = count(addr, '[') > 0
81+
}
82+
if want6 {
83+
return addrs.first(isNotIPv4)
84+
}
85+
return addrs.first(isIPv4)
86+
}
87+
6688
// first returns the first address which satisfies strategy, or if
6789
// none do, then the first address of any kind.
6890
func (addrs addrList) first(strategy func(Addr) bool) Addr {

src/net/main_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ func setupTestData() {
8989
resolveTCPAddrTests = append(resolveTCPAddrTests, resolveTCPAddrTest{"tcp6", "localhost:3", &TCPAddr{IP: IPv6loopback, Port: 3}, nil})
9090
resolveUDPAddrTests = append(resolveUDPAddrTests, resolveUDPAddrTest{"udp6", "localhost:3", &UDPAddr{IP: IPv6loopback, Port: 3}, nil})
9191
resolveIPAddrTests = append(resolveIPAddrTests, resolveIPAddrTest{"ip6", "localhost", &IPAddr{IP: IPv6loopback}, nil})
92+
93+
// Issue 20911: don't return IPv4 addresses for
94+
// Resolve*Addr calls of the IPv6 unspecified address.
95+
resolveTCPAddrTests = append(resolveTCPAddrTests, resolveTCPAddrTest{"tcp", "[::]:4", &TCPAddr{IP: IPv6unspecified, Port: 4}, nil})
96+
resolveUDPAddrTests = append(resolveUDPAddrTests, resolveUDPAddrTest{"udp", "[::]:4", &UDPAddr{IP: IPv6unspecified, Port: 4}, nil})
97+
resolveIPAddrTests = append(resolveIPAddrTests, resolveIPAddrTest{"ip", "::", &IPAddr{IP: IPv6unspecified}, nil})
9298
}
9399

94100
ifi := loopbackInterface()

src/net/tcpsock.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func ResolveTCPAddr(network, address string) (*TCPAddr, error) {
7777
if err != nil {
7878
return nil, err
7979
}
80-
return addrs.first(isIPv4).(*TCPAddr), nil
80+
return addrs.forResolve(network, address).(*TCPAddr), nil
8181
}
8282

8383
// TCPConn is an implementation of the Conn interface for TCP network

src/net/udpsock.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func ResolveUDPAddr(network, address string) (*UDPAddr, error) {
8080
if err != nil {
8181
return nil, err
8282
}
83-
return addrs.first(isIPv4).(*UDPAddr), nil
83+
return addrs.forResolve(network, address).(*UDPAddr), nil
8484
}
8585

8686
// UDPConn is the implementation of the Conn and PacketConn interfaces

0 commit comments

Comments
 (0)