Skip to content

fix: IPv6 address literals are unusable with -H, rejected by the hostlist expander #251

Description

@inureyes

Problem / Background

An IPv6 address literal cannot be passed to -H/--hosts in any form. Verified against main at commit 56abe143 with a debug build:

-H value Result
[::1] Error: Failed to expand host expression: [::1] / unmatched closing bracket in '[::1]'
[::1]:22 Error: Failed to expand host expression: [::1]:22
user@[::1] Error: Failed to expand host expression: user@[::1]
user@[::1]:22 Error: Failed to expand host expression: user@[::1]:22
::1 (unbracketed) Reaches the connect path but fails resolution: Invalid address was provided: failed to lookup address information: nodename nor servname provided, or not known
localhost, ip6-localhost (hostnames) Work correctly, resolution succeeds

The bracketed forms die in the hostlist expander before any connection is attempted. The unbracketed form dies in to_socket_addrs() because a bare ::1 cannot be parsed as host:port. Hostnames that resolve to IPv6 are unaffected and work fine.

Root cause

The hostlist expander uses [ and ] as the delimiters for its range expression syntax (node[1-3]), so a bracketed IPv6 literal collides with that syntax.

src/hostlist/parser.rs already has partial IPv6 awareness, but it is incomplete. In parse_host_pattern, the '[' arm detects a possible IPv6 literal via is_ipv6_start and takes a fast path that pushes [ into current_literal and continues, but it never sets bracket_depth. When the matching ] is reached, the ']' arm sees bracket_depth == 0 and returns HostlistError::UnmatchedBracket, which is exactly the observed unmatched closing bracket in '[::1]'. The closing ] is also never appended to the literal, so even if the depth check were bypassed the reassembled host string would be malformed.

A second gap: is_ipv6_start returns true only when the character right after [ is :. A literal such as [2001:db8::1] starts with 2, so it is routed into parse_range_expression and fails with a different, equally unhelpful error.

Also investigate whether the config-file host path (clusters: entries in YAML, expanded in src/app/nodes.rs) accepts IPv6 literals even though -H does not, since the two may take different code paths.

Why this matters now

PR #247 (issue #246) made -4/-6 functional. Before that they parsed and did nothing, so there was no reason to reach for an IPv6 literal on the command line. Now bssh -6 -H '[::1]' ping is a natural thing to type, and it fails with unmatched closing bracket, which gives no hint that the bracket syntax is the problem or what the working alternative is.

This is pre-existing and not a regression from #247. Confirmed: neither merge commit b79ab196 (#247) nor 56abe143 (#250) touched anything under src/hostlist/.

Proposed Solution

Make the hostlist parser recognize a bracketed IPv6 literal as a single literal segment rather than a range expression, carry the closing bracket into the literal, and keep the resulting host string in a form the connect path can hand to address resolution with an explicit port. Extend is_ipv6_start past the leading-colon heuristic so it also accepts literals beginning with a hex digit, and define an unambiguous rule for separating an IPv6 literal from a range expression.

Acceptance Criteria

  • -H accepts IPv6 literals in the standard bracketed forms: [::1], [::1]:2222, user@[::1], user@[::1]:2222.
  • The bracketed IPv6 form is disambiguated from the node[1-3] hostlist range syntax, with the disambiguation rule stated in the code and in the docs. Decide and document what happens for genuinely ambiguous input.
  • A bare unbracketed ::1 either works or fails with a message that names the bracket requirement, instead of the current generic resolution error.
  • Existing node[1-3] range expansion keeps working unchanged, covered by a regression test.
  • Tests covering each row of the table above.
  • Verify whether the config-file host path has the same limitation, and fix it there too if so.
  • docs/man/bssh.1 documents the accepted IPv6 literal syntax for -H.
  • The fix is integrated into the real code flow that -H and the config host path use, not added as a standalone helper.

Technical Considerations

Relevant code:

  • src/hostlist/parser.rs: parse_host_pattern, is_ipv6_start, parse_range_expression
  • src/hostlist/error.rs: UnmatchedBracket, UnclosedBracket, NestedBrackets, EmptyBracket
  • src/main.rs and src/app/nodes.rs: the two call sites that wrap expander failures in Failed to expand host expression

The user@host:port split has to run against the bracketed form without treating the colons inside the brackets as the port separator. Only a colon after the closing ] is a port separator.

Context: #246, #247.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions