1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-22 22:42:48 +03:00

ssh: do our own proxycommand handling

Adapt the code we were using for libssh2 and use it for both
libssh and libssh2 so that we have consistent proxy command
support on both unix and windows, and both versions of the
ssh libraries.

This may help resolve the linked issue on windows, or at least,
uncover the next layer of problem(s).

refs: https://github.com/wez/wezterm/issues/3083
This commit is contained in:
Wez Furlong 2023-02-09 09:08:46 -07:00
parent 508ea437df
commit a5b449f364
No known key found for this signature in database
GPG Key ID: 7A7F66A31EC9B387

View File

@ -202,25 +202,22 @@ impl SessionInner {
sess.set_option(libssh_rs::SshOption::BindAddress(bind_addr.to_string()))?;
}
if let Some(cmd) = self.config.get("proxycommand") {
sess.set_option(libssh_rs::SshOption::ProxyCommand(Some(cmd.to_string())))?;
} else {
let sock = self.connect_to_host(&hostname, port, verbose)?;
let raw = {
#[cfg(unix)]
{
use std::os::unix::io::IntoRawFd;
sock.into_raw_fd()
}
#[cfg(windows)]
{
use std::os::windows::io::IntoRawSocket;
sock.into_raw_socket()
}
};
let sock =
self.connect_to_host(&hostname, port, verbose, self.config.get("proxycommand"))?;
let raw = {
#[cfg(unix)]
{
use std::os::unix::io::IntoRawFd;
sock.into_raw_fd()
}
#[cfg(windows)]
{
use std::os::windows::io::IntoRawSocket;
sock.into_raw_socket()
}
};
sess.set_option(libssh_rs::SshOption::Socket(raw))?;
}
sess.set_option(libssh_rs::SshOption::Socket(raw))?;
sess.connect()
.with_context(|| format!("Connecting to {hostname}:{port}"))?;
@ -281,46 +278,8 @@ impl SessionInner {
))))
.context("notifying user of banner")?;
let sock: Socket = if let Some(proxy_command) =
self.config.get("proxycommand").and_then(|c| {
if !c.is_empty() && c != "none" {
Some(c)
} else {
None
}
}) {
let mut cmd;
if cfg!(windows) {
let comspec = std::env::var("COMSPEC").unwrap_or_else(|_| "cmd".to_string());
cmd = std::process::Command::new(comspec);
cmd.args(&["/c", proxy_command]);
} else {
cmd = std::process::Command::new("sh");
cmd.args(&["-c", &format!("exec {}", proxy_command)]);
}
let (a, b) = socketpair()?;
cmd.stdin(b.as_stdio()?);
cmd.stdout(b.as_stdio()?);
cmd.stderr(std::process::Stdio::inherit());
let _child = cmd
.spawn()
.with_context(|| format!("spawning ProxyCommand {}", proxy_command))?;
#[cfg(unix)]
unsafe {
use std::os::unix::io::{FromRawFd, IntoRawFd};
Socket::from_raw_fd(a.into_raw_fd())
}
#[cfg(windows)]
unsafe {
use std::os::windows::io::{FromRawSocket, IntoRawSocket};
Socket::from_raw_socket(a.into_raw_socket())
}
} else {
self.connect_to_host(&hostname, port, verbose)?
};
let sock =
self.connect_to_host(&hostname, port, verbose, self.config.get("proxycommand"))?;
let mut sess = ssh2::Session::new()?;
if verbose {
@ -353,8 +312,52 @@ impl SessionInner {
/// Explicitly and directly connect to the requested host because
/// neither libssh no libssh2 respect addressfamily, so we must
/// handle it for ourselves
fn connect_to_host(&self, hostname: &str, port: u16, verbose: bool) -> anyhow::Result<Socket> {
/// handle it for ourselves.
/// If proxy_command is set, then we execute that process for ourselves
/// too, as proxy commands are not supported by libssh2 and are not supported
/// on Windows in libssh.
fn connect_to_host(
&self,
hostname: &str,
port: u16,
verbose: bool,
proxy_command: Option<&String>,
) -> anyhow::Result<Socket> {
match proxy_command.map(|s| s.as_str()) {
Some("none") | None => {}
Some(proxy_command) => {
let mut cmd;
if cfg!(windows) {
let comspec = std::env::var("COMSPEC").unwrap_or_else(|_| "cmd".to_string());
cmd = std::process::Command::new(comspec);
cmd.args(&["/c", proxy_command]);
} else {
cmd = std::process::Command::new("sh");
cmd.args(&["-c", &format!("exec {}", proxy_command)]);
}
let (a, b) = socketpair()?;
cmd.stdin(b.as_stdio()?);
cmd.stdout(b.as_stdio()?);
cmd.stderr(std::process::Stdio::inherit());
let _child = cmd
.spawn()
.with_context(|| format!("spawning ProxyCommand {}", proxy_command))?;
#[cfg(unix)]
unsafe {
use std::os::unix::io::{FromRawFd, IntoRawFd};
return Ok(Socket::from_raw_fd(a.into_raw_fd()));
}
#[cfg(windows)]
unsafe {
use std::os::windows::io::{FromRawSocket, IntoRawSocket};
return Ok(Socket::from_raw_socket(a.into_raw_socket()));
}
}
}
let addr = (hostname, port)
.to_socket_addrs()?
.filter(|addr| self.filter_sock_addr(addr))