diff --git a/Cargo.lock b/Cargo.lock index 735ace0..54ad2f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -774,6 +774,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "raunch" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a67226789ccd419c55f5c826e04f7d58690b0593364f61bc8ed6e7dbbab49c5" +dependencies = [ + "libc", +] + [[package]] name = "regex" version = "1.10.4" @@ -907,9 +916,12 @@ checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "service-binding" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ef733c6f7034fe1a9c3d812cdef93f2bec735e22b0467f84063de142be42428" +checksum = "e5580e177917150862f941023eb13ff219acc59daefa42b76fa747612723402c" +dependencies = [ + "raunch", +] [[package]] name = "sha1" diff --git a/Cargo.toml b/Cargo.toml index bfbc741..e174643 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,9 +21,9 @@ byteorder = "1.4.3" async-trait = { version = "0.1.77", optional = true } futures = { version = "0.3.30", optional = true } log = { version = "0.4.6", optional = true } -tokio = { version = "1", optional = true, features = ["rt", "net"] } +tokio = { version = "1", optional = true, features = ["rt", "net", "time"] } tokio-util = { version = "0.7.1", optional = true, features = ["codec"] } -service-binding = { version = "^2" } +service-binding = { version = "^2.1" } ssh-encoding = { version = "0.2.0" } ssh-key = { version = "0.6.6", features = ["rsa", "alloc"] } thiserror = "1.0.58" @@ -43,7 +43,7 @@ required-features = ["agent"] env_logger = "0.11.0" rand = "0.8.5" rsa = { version = "0.9.6", features = ["sha2", "sha1"] } -tokio = { version = "1", features = ["macros", "rt-multi-thread", "time"] } +tokio = { version = "1", features = ["macros", "rt-multi-thread"] } sha1 = { version = "0.10.5", default-features = false, features = ["oid"] } testresult = "0.4.0" hex-literal = "0.4.1" diff --git a/examples/ssh-agent-client.rs b/examples/ssh-agent-client.rs index 0230041..45e277d 100644 --- a/examples/ssh-agent-client.rs +++ b/examples/ssh-agent-client.rs @@ -1,35 +1,15 @@ -use ssh_agent_lib::agent::Session; -use ssh_agent_lib::client::Client; -#[cfg(windows)] -use tokio::net::windows::named_pipe::ClientOptions; -#[cfg(unix)] -use tokio::net::UnixStream; +use service_binding::Binding; +use ssh_agent_lib::client::connect; #[tokio::main] async fn main() -> Result<(), Box> { #[cfg(unix)] - let mut client = { - let stream = UnixStream::connect(std::env::var("SSH_AUTH_SOCK")?).await?; - Client::new(stream) - }; - #[cfg(windows)] - let mut client = { - let stream = loop { - // https://docs.rs/windows-sys/latest/windows_sys/Win32/Foundation/constant.ERROR_PIPE_BUSY.html - const ERROR_PIPE_BUSY: u32 = 231u32; - - // correct way to do it taken from - // https://docs.rs/tokio/latest/tokio/net/windows/named_pipe/struct.NamedPipeClient.html - match ClientOptions::new().open(std::env::var("SSH_AUTH_SOCK")?) { - Ok(client) => break client, - Err(e) if e.raw_os_error() == Some(ERROR_PIPE_BUSY as i32) => (), - Err(e) => Err(e)?, - } + let mut client = + connect(Binding::FilePath(std::env::var("SSH_AUTH_SOCK")?.into()).try_into()?).await?; - tokio::time::sleep(std::time::Duration::from_millis(50)).await; - }; - Client::new(stream) - }; + #[cfg(windows)] + let mut client = + connect(Binding::NamedPipe(std::env::var("SSH_AUTH_SOCK")?.into()).try_into()?).await?; eprintln!( "Identities that this agent knows of: {:#?}", diff --git a/src/agent.rs b/src/agent.rs index 7ad8138..f390720 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -80,7 +80,7 @@ impl ListeningSocket for NamedPipeListener { } #[async_trait] -pub trait Session: 'static + Sync + Send + Sized { +pub trait Session: 'static + Sync + Send + Unpin { async fn request_identities(&mut self) -> Result, Box> { Err(Box::new(ProtoError::UnsupportedCommand { command: 11 })) } diff --git a/src/client.rs b/src/client.rs index f34672c..64df32f 100644 --- a/src/client.rs +++ b/src/client.rs @@ -31,6 +31,46 @@ where } } +pub async fn connect( + stream: service_binding::Stream, +) -> Result>, Box> { + match stream { + #[cfg(unix)] + service_binding::Stream::Unix(stream) => { + let stream = tokio::net::UnixStream::from_std(stream)?; + Ok(Box::pin(Client::new(stream))) + } + service_binding::Stream::Tcp(stream) => { + let stream = tokio::net::TcpStream::from_std(stream)?; + Ok(Box::pin(Client::new(stream))) + } + #[cfg(windows)] + service_binding::Stream::NamedPipe(pipe) => { + use tokio::net::windows::named_pipe::ClientOptions; + let stream = loop { + // https://docs.rs/windows-sys/latest/windows_sys/Win32/Foundation/constant.ERROR_PIPE_BUSY.html + const ERROR_PIPE_BUSY: u32 = 231u32; + + // correct way to do it taken from + // https://docs.rs/tokio/latest/tokio/net/windows/named_pipe/struct.NamedPipeClient.html + match ClientOptions::new().open(pipe) { + Ok(client) => break client, + Err(e) if e.raw_os_error() == Some(ERROR_PIPE_BUSY as i32) => (), + Err(e) => Err(e)?, + } + + tokio::time::sleep(std::time::Duration::from_millis(50)).await; + }; + Ok(Box::pin(Client::new(stream))) + } + #[cfg(not(windows))] + service_binding::Stream::NamedPipe(_) => Err(ProtoError::IO(std::io::Error::other( + "Named pipes supported on Windows only", + )) + .into()), + } +} + #[async_trait::async_trait] impl crate::agent::Session for Client where