@@ -11,14 +11,29 @@ use crate::encoding::{Encoding, Reader};
1111use crate :: key:: { PublicKey , SignatureHash } ;
1212use crate :: { key, protocol, Error , PublicKeyBase64 } ;
1313
14+ pub trait AgentStream : AsyncRead + AsyncWrite { }
15+
16+ impl < S : AsyncRead + AsyncWrite > AgentStream for S { }
17+
1418/// SSH agent client.
15- pub struct AgentClient < S : AsyncRead + AsyncWrite > {
19+ pub struct AgentClient < S : AgentStream > {
1620 stream : S ,
1721 buf : CryptoVec ,
1822}
1923
24+ impl < S : AgentStream + Send + Unpin + ' static > AgentClient < S > {
25+ /// Wraps the internal stream in a Box<dyn _>, allowing different client
26+ /// implementations to have the same type
27+ pub fn dynamic ( self ) -> AgentClient < Box < dyn AgentStream + Send + Unpin > > {
28+ AgentClient {
29+ stream : Box :: new ( self . stream ) ,
30+ buf : self . buf ,
31+ }
32+ }
33+ }
34+
2035// https://tools.ietf.org/html/draft-miller-ssh-agent-00#section-4.1
21- impl < S : AsyncRead + AsyncWrite + Unpin > AgentClient < S > {
36+ impl < S : AgentStream + Unpin > AgentClient < S > {
2237 /// Build a future that connects to an SSH agent via the provided
2338 /// stream (on Unix, usually a Unix-domain socket).
2439 pub fn connect ( stream : S ) -> Self {
@@ -58,24 +73,39 @@ impl AgentClient<tokio::net::UnixStream> {
5873 }
5974}
6075
61- #[ cfg( target_os = "windows" ) ]
76+ #[ cfg( windows) ]
77+ const ERROR_PIPE_BUSY : u32 = 231u32 ;
78+
79+ #[ cfg( windows) ]
6280impl AgentClient < pageant:: PageantStream > {
6381 /// Connect to a running Pageant instance
6482 pub async fn connect_pageant ( ) -> Self {
6583 Self :: connect ( pageant:: PageantStream :: new ( ) )
6684 }
6785}
6886
69- #[ cfg( not( unix) ) ]
70- impl AgentClient < tokio:: net:: TcpStream > {
71- /// Build a future that connects to an SSH agent via the provided
72- /// stream (on Unix, usually a Unix-domain socket).
73- pub async fn connect_env ( ) -> Result < Self , Error > {
74- Err ( Error :: AgentFailure )
87+ #[ cfg( windows) ]
88+ impl AgentClient < tokio:: net:: windows:: named_pipe:: NamedPipeClient > {
89+ /// Connect to an SSH agent via a Windows named pipe
90+ pub async fn connect_named_pipe < P : AsRef < std:: ffi:: OsStr > > ( path : P ) -> Result < Self , Error > {
91+ let stream = loop {
92+ match tokio:: net:: windows:: named_pipe:: ClientOptions :: new ( ) . open ( path. as_ref ( ) ) {
93+ Ok ( client) => break client,
94+ Err ( e) if e. raw_os_error ( ) == Some ( ERROR_PIPE_BUSY as i32 ) => ( ) ,
95+ Err ( e) => return Err ( e. into ( ) ) ,
96+ }
97+
98+ tokio:: time:: sleep ( tokio:: time:: Duration :: from_millis ( 50 ) ) . await ;
99+ } ;
100+
101+ Ok ( AgentClient {
102+ stream,
103+ buf : CryptoVec :: new ( ) ,
104+ } )
75105 }
76106}
77107
78- impl < S : AsyncRead + AsyncWrite + Unpin > AgentClient < S > {
108+ impl < S : AgentStream + Unpin > AgentClient < S > {
79109 async fn read_response ( & mut self ) -> Result < ( ) , Error > {
80110 // Writing the message
81111 self . stream . write_all ( & self . buf ) . await ?;
0 commit comments