chipsenkbeil/distant

distant incorrectly determines that hypens may not be used in hostnames

westernwontons opened this issue · 9 comments

I have a distant server running on a remote machine, where the user is named 'ubuntu-user'. When I attempt to make the connection with the following command:

distant client launch ubuntu-user@123.456.78.99:22

I get the following error:

error: Invalid value "ubuntu-user@123.456.78.99:22" for '<DESTINATION>': Hostname label can only be a-zA-Z0-9 or hyphen ('-')

For more information try --help


Caused by:
    Hostname label can only be a-zA-Z0-9 or hyphen ('-')

Which version of distant are you running?

Latest from master, I cloned the project and ran cargo install --path .

If you're open, I would be happy to contribute. I discovered this project yesterday and I really love the idea, plus it could be immensely useful for me because I have to manage a bunch of projects. It's always a pain to set everything up on a fresh system.

@westernwontons yeah, I'm more than happy to have contributions! Feel free to take a look and submit a pull request if you figure it out.

You may want to wait for #200 to merge in. I'll be doing that as soon as the tests pass on the CI.

The logic where validation is done should be here:

return Err(HostParseError::InvalidLabel);

Specifically, you can see the error message here:

Self::InvalidLabel => "Hostname label can only be a-zA-Z0-9 or hyphen ('-')",

Oh, and it could be that more than the host is being read into the destination parser, which can be found here:

pub fn parse(s: &str) -> Result<Destination, &'static str> {
let (s, scheme) = maybe(parse_scheme)(s)?;
let (s, username_password) = maybe(parse_username_password)(s)?;
// NOTE: We can have a host or host/port in a couple of different ways
//
// 1. IPv4 - 127.0.0.1 or 127.0.0.1:1234
// 2. IPv6 - ::1 or [::1]:1234
// 3. Name - localhost or localhost:1234
//
// To determine path to take, we count the colons. If there is more than 1, we can assume IPv6
// is involved and can try to parse entirely as IPv6, or if that fails split off one colon and
// try a second time. Otherwise, we assume that it is not IPv6 and can parse with a single
// colon at most for a port.
let colon_cnt = s.chars().filter(|c| *c == ':').count();
let (s, host) = if colon_cnt > 1 {
// Either the host is [{}] with a port following, or the host is everything
either(
delimited(
parse_char('['),
parse_and_then(parse_until(|c| c == ']'), parse_host),
parse_char(']'),
),
parse_host,
)(s)?
} else {
parse_and_then(parse_until(|c| c == ':'), parse_host)(s)?
};
let (s, port) = maybe(prefixed(parse_char(':'), parse_port))(s)?;
if !s.is_empty() {
return Err("Str has more characters after destination");
}
Ok(Destination {
scheme: scheme.map(ToString::to_string),
username: username_password
.as_ref()
.and_then(|up| up.0)
.map(ToString::to_string),
password: username_password
.as_ref()
.and_then(|up| up.1)
.map(ToString::to_string),
host,
port,
})
}

After some tracking I've found that the problem seems to be here:

let (auth, username) = maybe(parse_until(|c| !c.is_alphanumeric()))(auth)?;

The check when calling is_alphanumeric fails for hypens. I added a test with the username some-user. The username variable on line 72 only contained some, because parse_until stops when the closure returns false.

If it's alright with you I could whip up a PR with some tests that would allow hypens

@westernwontons good find! Go for it!

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues. If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.