Resolve SSH aliases for Dosh UDP host
This commit is contained in:
+84
-11
@@ -98,17 +98,21 @@ async fn main() -> Result<()> {
|
||||
let (cols, rows) = size().unwrap_or((80, 24));
|
||||
|
||||
let started = Instant::now();
|
||||
let target_udp_host = args
|
||||
.dosh_host
|
||||
.clone()
|
||||
.or_else(|| config.dosh_host.clone())
|
||||
.unwrap_or_else(|| {
|
||||
if args.local_auth {
|
||||
"127.0.0.1".to_string()
|
||||
} else {
|
||||
let target_udp_host =
|
||||
if let Some(host) = args.dosh_host.clone().or_else(|| config.dosh_host.clone()) {
|
||||
host
|
||||
} else if args.local_auth {
|
||||
"127.0.0.1".to_string()
|
||||
} else {
|
||||
ssh_config_hostname(&server, ssh_port).unwrap_or_else(|err| {
|
||||
log_debug(
|
||||
args.verbose,
|
||||
2,
|
||||
&format!("dosh could not resolve SSH config hostname, using {server}: {err:#}"),
|
||||
);
|
||||
ssh_destination_host(&server)
|
||||
}
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
let credential = if !args.no_cache {
|
||||
load_cache(&cache_path).ok()
|
||||
@@ -378,6 +382,40 @@ fn ssh_destination_host(server: &str) -> String {
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn ssh_config_hostname(server: &str, ssh_port: Option<u16>) -> Result<String> {
|
||||
let mut command = Command::new("ssh");
|
||||
command.arg("-G");
|
||||
if let Some(ssh_port) = ssh_port {
|
||||
command.arg("-p").arg(ssh_port.to_string());
|
||||
}
|
||||
let output = command
|
||||
.arg(server)
|
||||
.output()
|
||||
.with_context(|| format!("run ssh -G {server}"))?;
|
||||
if !output.status.success() {
|
||||
return Err(anyhow!(
|
||||
"ssh -G failed: {}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
));
|
||||
}
|
||||
let raw = String::from_utf8(output.stdout)?;
|
||||
parse_ssh_config_hostname(&raw).ok_or_else(|| anyhow!("ssh -G did not print hostname"))
|
||||
}
|
||||
|
||||
fn parse_ssh_config_hostname(raw: &str) -> Option<String> {
|
||||
raw.lines().find_map(|line| {
|
||||
let line = line.trim();
|
||||
let (key, value) = line.split_once(char::is_whitespace)?;
|
||||
if key.eq_ignore_ascii_case("hostname") {
|
||||
let value = value.trim();
|
||||
if !value.is_empty() {
|
||||
return Some(value.to_string());
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
fn resolve_addr(host: &str, port: u16) -> Result<SocketAddr> {
|
||||
(host, port)
|
||||
.to_socket_addrs()
|
||||
@@ -407,6 +445,10 @@ async fn bootstrap_attach(
|
||||
let (n, _) = tokio::time::timeout(Duration::from_secs(5), socket.recv_from(&mut buf)).await??;
|
||||
let packet = protocol::decode(&buf[..n])?;
|
||||
if packet.header.kind != PacketKind::AttachOk {
|
||||
if packet.header.kind == PacketKind::AttachReject {
|
||||
let reject: AttachReject = protocol::from_body(&packet.body)?;
|
||||
return Err(anyhow!("attach rejected: {}", reject.reason));
|
||||
}
|
||||
return Err(anyhow!("attach rejected or unexpected response"));
|
||||
}
|
||||
let plain = protocol::decrypt_body(&packet, &bootstrap.session_key, SERVER_TO_CLIENT)?;
|
||||
@@ -471,6 +513,10 @@ async fn try_ticket_attach(
|
||||
tokio::time::timeout(Duration::from_millis(700), socket.recv_from(&mut buf)).await??;
|
||||
let packet = protocol::decode(&buf[..n])?;
|
||||
if packet.header.kind != PacketKind::AttachOk {
|
||||
if packet.header.kind == PacketKind::AttachReject {
|
||||
let reject: AttachReject = protocol::from_body(&packet.body)?;
|
||||
return Err(anyhow!("ticket attach rejected: {}", reject.reason));
|
||||
}
|
||||
return Err(anyhow!("ticket attach rejected"));
|
||||
}
|
||||
let envelope: TicketAttachOkEnvelope = protocol::from_body(&packet.body)?;
|
||||
@@ -533,6 +579,10 @@ async fn try_resume(
|
||||
tokio::time::timeout(Duration::from_millis(700), socket.recv_from(&mut buf)).await??;
|
||||
let packet = protocol::decode(&buf[..n])?;
|
||||
if packet.header.kind != PacketKind::ResumeOk {
|
||||
if packet.header.kind == PacketKind::AttachReject {
|
||||
let reject: AttachReject = protocol::from_body(&packet.body)?;
|
||||
return Err(anyhow!("resume rejected: {}", reject.reason));
|
||||
}
|
||||
return Err(anyhow!("resume rejected"));
|
||||
}
|
||||
let plain = protocol::decrypt_body(&packet, &cached.session_key, SERVER_TO_CLIENT)?;
|
||||
@@ -866,7 +916,7 @@ const TERMINAL_CLEANUP: &[u8] = concat!(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::InputNormalizer;
|
||||
use super::{InputNormalizer, parse_ssh_config_hostname, ssh_destination_host};
|
||||
|
||||
#[test]
|
||||
fn normalizes_ss3_cursor_keys_to_csi() {
|
||||
@@ -890,4 +940,27 @@ mod tests {
|
||||
let mut normalizer = InputNormalizer::default();
|
||||
assert_eq!(normalizer.normalize(b"\x1bOP"), b"\x1bOP");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_ssh_config_hostname() {
|
||||
let raw = "user palav\nhostname palav.dev\nport 22\n";
|
||||
assert_eq!(
|
||||
parse_ssh_config_hostname(raw),
|
||||
Some("palav.dev".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_ssh_config_hostname_case_insensitively() {
|
||||
let raw = "HostName 192.0.2.10\n";
|
||||
assert_eq!(
|
||||
parse_ssh_config_hostname(raw),
|
||||
Some("192.0.2.10".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strips_user_from_fallback_udp_host() {
|
||||
assert_eq!(ssh_destination_host("palav@example.com"), "example.com");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user