Skip to content

Commit

Permalink
discovery: correctly handle IPv6 addresses from go-discover (#24649)
Browse files Browse the repository at this point in the history
Nomad sets a default port when resolving server addresses that don't have
one. When we get a "bare" IPv6 address without a port, we end up with an
unexpected error "too many colons in address" when we try to split the address
and host, because the standard library function expects IPv6 addresses to be
wrapped in brackets as recommended by RFC5952. User-configured addresses avoid
this problem by accepting IP address and port as separate configuration values,
but go-discover emits "bare" IPv6 addresses without a port in IPv6 environments.

Fix this by adding brackets to IPv6 addresses when we get the "too many colons"
error from the stdlib. This will still give erroneous results if the address
includes the port but is missing brackets, but there's no way to unambiguously
parse that address.

Ref: https://www.rfc-editor.org/rfc/rfc5952
Fixes: #24608
  • Loading branch information
tgross authored Dec 17, 2024
1 parent a4ac202 commit 30e57c3
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 5 deletions.
3 changes: 3 additions & 0 deletions .changelog/24649.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
discovery: Fixed a bug where IPv6 addresses would not be accepted from cloud autojoin
```
20 changes: 18 additions & 2 deletions client/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -445,14 +445,30 @@ func (c *Client) handleStreamingConn(conn net.Conn) {
// net.Addr or an error.
func resolveServer(s string) (net.Addr, error) {
const defaultClientPort = "4647" // default client RPC port

host, port, err := net.SplitHostPort(s)
if err != nil {
if strings.Contains(err.Error(), "missing port") {
switch {
case strings.Contains(err.Error(), "missing port"):
// with IPv6 addresses the `host` variable will have brackets
// removed, so send the original value thru again with only the
// correct port suffix
return resolveServer(s + ":" + defaultClientPort)
} else {
case strings.Contains(err.Error(), "too many colons"):
// note: we expect IPv6 address strings to be RFC5952 compliant to
// disambiguate port numbers from the address. Because the port number
// is typically 4 decimal digits, the same size as an IPv6 address
// segment, there's no way to disambiguate this. See
// https://www.rfc-editor.org/rfc/rfc5952
ip := net.ParseIP(s)
if ip.To4() == nil && ip.To16() != nil {
if !strings.HasPrefix(s, "[") {
return resolveServer("[" + s + "]:" + defaultClientPort)
}
}
return nil, err

default:
return nil, err
}
} else if port == "" {
Expand Down
12 changes: 9 additions & 3 deletions client/rpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,15 @@ func Test_resolveServer(t *testing.T) {
expectErr string
}{
{
name: "ipv6 no brackets",
addr: "2001:db8::1",
expectErr: "address 2001:db8::1: too many colons in address",
name: "ipv6 no brackets",
addr: "2001:db8::1",
expect: "[2001:db8::1]:4647",
},
{
// expected bad result
name: "ambiguous ipv6 no brackets with port",
addr: "2001:db8::1:4647",
expect: "[2001:db8::1:4647]:4647",
},
{
name: "ipv6 no port",
Expand Down

0 comments on commit 30e57c3

Please sign in to comment.