Skip to content

Commit

Permalink
discovery: correctly handle IPv6 addresses from go-discover
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 committed Dec 11, 2024
1 parent 3bc2c07 commit d801869
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 5 deletions.
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 d801869

Please sign in to comment.