async_ssh2/
session.rs

1use crate::{
2    agent::Agent, channel::Channel, listener::Listener, sftp::Sftp, util::run_ssh2_fn, Error,
3};
4use smol::Async;
5use ssh2::{
6    self, DisconnectCode, HashType, HostKeyType, KeyboardInteractivePrompt, KnownHosts, MethodType,
7    ScpFileStat,
8};
9#[cfg(unix)]
10use std::os::unix::io::{AsRawFd, RawFd};
11#[cfg(windows)]
12use std::os::windows::io::{AsRawSocket, RawSocket};
13use std::{
14    convert::From,
15    net::TcpStream,
16    path::Path,
17    sync::{Arc, Once},
18};
19
20/// See [`Session`](ssh2::Session).
21pub struct Session {
22    inner: ssh2::Session,
23    stream: Option<Arc<Async<TcpStream>>>,
24}
25
26#[cfg(unix)]
27struct RawFdWrapper(RawFd);
28
29#[cfg(unix)]
30impl AsRawFd for RawFdWrapper {
31    fn as_raw_fd(&self) -> RawFd {
32        self.0
33    }
34}
35
36#[cfg(windows)]
37struct RawSocketWrapper(RawSocket);
38
39#[cfg(windows)]
40impl AsRawSocket for RawSocketWrapper {
41    fn as_raw_socket(&self) -> RawSocket {
42        self.0
43    }
44}
45
46impl Session {
47    /// See [`new`](ssh2::Session::new).
48    pub fn new() -> Result<Session, Error> {
49        let session = ssh2::Session::new()?;
50        static START: Once = Once::new();
51        START.call_once(|| {
52            std::thread::spawn(|| smol::run(futures::future::pending::<()>()));
53        });
54        session.set_blocking(false);
55
56        Ok(Self {
57            inner: session,
58            stream: None,
59        })
60    }
61
62    /// See [`set_banner`](ssh2::Session::set_banner).
63    pub async fn set_banner(&self, banner: &str) -> Result<(), Error> {
64        run_ssh2_fn(self.stream.as_ref().unwrap(), || {
65            self.inner.set_banner(banner)
66        })
67        .await
68    }
69
70    /// See [`set_allow_sigpipe`](ssh2::Session::set_allow_sigpipe).
71    pub fn set_allow_sigpipe(&self, block: bool) {
72        self.inner.set_allow_sigpipe(block)
73    }
74
75    /// See [`set_allow_sigpipe`](ssh2::Session::set_compress).
76    pub fn set_compress(&self, compress: bool) {
77        self.inner.set_compress(compress)
78    }
79
80    /// See [`is_blocking`](ssh2::Session::is_blocking).
81    pub fn is_blocking(&self) -> bool {
82        self.inner.is_blocking()
83    }
84
85    /// See [`set_timeout`](ssh2::Session::set_timeout).
86    pub fn set_timeout(&self, timeout_ms: u32) {
87        self.inner.set_timeout(timeout_ms)
88    }
89
90    /// See [`timeout`](ssh2::Session::timeout).
91    pub fn timeout(&self) -> u32 {
92        self.inner.timeout()
93    }
94
95    /// See [`handshake`](ssh2::Session::handshake).
96    pub async fn handshake(&mut self) -> Result<(), Error> {
97        run_ssh2_fn(self.stream.as_ref().unwrap(), || {
98            self.inner.clone().handshake()
99        })
100        .await
101    }
102
103    /// Sets the tcp stream for the underlying `ssh2` lib.
104    ///
105    /// ```rust,no_run
106    /// use async_ssh2::Session;
107    /// use std::net::TcpStream;
108    /// use smol::Async;
109    ///
110    /// #[tokio::main]
111    /// async fn main() {
112    ///     let stream = Async::<TcpStream>::connect("127.0.0.1:22").await.unwrap();
113    ///     let mut sess = async_ssh2::Session::new().unwrap();
114    ///     sess.set_tcp_stream(stream).unwrap();
115    /// }
116    /// ```
117    pub fn set_tcp_stream(&mut self, stream: Async<TcpStream>) -> Result<(), Error> {
118        #[cfg(unix)]
119        {
120            let raw_fd = RawFdWrapper(stream.as_raw_fd());
121            self.inner.set_tcp_stream(raw_fd);
122        }
123        #[cfg(windows)]
124        {
125            let raw_socket = RawSocketWrapper(stream.as_raw_socket());
126            self.inner.set_tcp_stream(raw_socket);
127        }
128        self.stream = Some(Arc::new(stream));
129        Ok(())
130    }
131
132    /// See [`userauth_password`](ssh2::Session::userauth_password).
133    pub async fn userauth_password(&self, username: &str, password: &str) -> Result<(), Error> {
134        run_ssh2_fn(self.stream.as_ref().unwrap(), || {
135            self.inner.userauth_password(username, password)
136        })
137        .await
138    }
139
140    /// See [`userauth_keyboard_interactive`](ssh2::Session::userauth_keyboard_interactive).
141    pub fn userauth_keyboard_interactive<P: KeyboardInteractivePrompt>(
142        &self,
143        _username: &str,
144        _prompter: &mut P,
145    ) -> Result<(), Error> {
146        unimplemented!();
147    }
148
149    /// See [`userauth_agent`](ssh2::Session::userauth_agent).
150    pub async fn userauth_agent(&self, username: &str) -> Result<(), Error> {
151        let mut agent = self.agent()?;
152        agent.connect().await?;
153        agent.list_identities()?;
154        let identities = agent.identities()?;
155        let identity = match identities.get(0) {
156            Some(identity) => identity,
157            None => return Err(Error::from(ssh2::Error::from_errno(-4))),
158        };
159        agent.userauth(username, &identity).await
160    }
161
162    /// See [`userauth_pubkey_file`](ssh2::Session::userauth_pubkey_file).
163    pub async fn userauth_pubkey_file(
164        &self,
165        username: &str,
166        pubkey: Option<&Path>,
167        privatekey: &Path,
168        passphrase: Option<&str>,
169    ) -> Result<(), Error> {
170        run_ssh2_fn(self.stream.as_ref().unwrap(), || {
171            self.inner
172                .userauth_pubkey_file(username, pubkey, privatekey, passphrase)
173        })
174        .await
175    }
176
177    /// See [`userauth_pubkey_memory`](ssh2::Session::userauth_pubkey_memory).
178    #[cfg(unix)]
179    pub async fn userauth_pubkey_memory(
180        &self,
181        username: &str,
182        pubkeydata: Option<&str>,
183        privatekeydata: &str,
184        passphrase: Option<&str>,
185    ) -> Result<(), Error> {
186        run_ssh2_fn(self.stream.as_ref().unwrap(), || {
187            self.inner
188                .userauth_pubkey_memory(username, pubkeydata, privatekeydata, passphrase)
189        })
190        .await
191    }
192
193    /// See [`userauth_hostbased_file`](ssh2::Session::userauth_hostbased_file).
194    #[allow(missing_docs)]
195    pub async fn userauth_hostbased_file(
196        &self,
197        username: &str,
198        publickey: &Path,
199        privatekey: &Path,
200        passphrase: Option<&str>,
201        hostname: &str,
202        local_username: Option<&str>,
203    ) -> Result<(), Error> {
204        run_ssh2_fn(self.stream.as_ref().unwrap(), || {
205            self.inner.userauth_hostbased_file(
206                username,
207                publickey,
208                privatekey,
209                passphrase,
210                hostname,
211                local_username,
212            )
213        })
214        .await
215    }
216
217    /// See [`authenticated`](ssh2::Session::authenticated).
218    pub fn authenticated(&self) -> bool {
219        self.inner.authenticated()
220    }
221
222    /// See [`auth_methods`](ssh2::Session::auth_methods).
223    pub async fn auth_methods(&self, username: &str) -> Result<&str, Error> {
224        run_ssh2_fn(self.stream.as_ref().unwrap(), || {
225            self.inner.auth_methods(username)
226        })
227        .await
228    }
229
230    /// See [`method_pref`](ssh2::Session::method_pref).
231    pub fn method_pref(&self, method_type: MethodType, prefs: &str) -> Result<(), Error> {
232        self.inner.method_pref(method_type, prefs)?;
233        Ok(())
234    }
235
236    /// See [`methods`](ssh2::Session::methods).
237    pub fn methods(&self, method_type: MethodType) -> Option<&str> {
238        self.inner.methods(method_type)
239    }
240
241    /// See [`supported_algs`](ssh2::Session::supported_algs).
242    pub fn supported_algs(&self, method_type: MethodType) -> Result<Vec<&'static str>, Error> {
243        self.inner.supported_algs(method_type).map_err(From::from)
244    }
245
246    /// See [`agent`](ssh2::Session::agent).
247    pub fn agent(&self) -> Result<Agent, Error> {
248        let agent = self.inner.agent()?;
249        Ok(Agent::new(agent, self.stream.as_ref().unwrap().clone()))
250    }
251
252    /// See [`known_hosts`](ssh2::Session::known_hosts).
253    pub fn known_hosts(&self) -> Result<KnownHosts, Error> {
254        self.inner.known_hosts().map_err(From::from)
255    }
256
257    /// See [`channel_session`](ssh2::Session::channel_session).
258    pub async fn channel_session(&self) -> Result<Channel, Error> {
259        let channel = run_ssh2_fn(self.stream.as_ref().unwrap(), || {
260            self.inner.channel_session()
261        })
262        .await?;
263        Ok(Channel::new(channel, self.stream.as_ref().unwrap().clone()))
264    }
265
266    /// See [`channel_direct_tcpip`](ssh2::Session::channel_direct_tcpip).
267    pub async fn channel_direct_tcpip(
268        &self,
269        host: &str,
270        port: u16,
271        src: Option<(&str, u16)>,
272    ) -> Result<Channel, Error> {
273        let channel = run_ssh2_fn(self.stream.as_ref().unwrap(), || {
274            self.inner.channel_direct_tcpip(host, port, src)
275        })
276        .await?;
277        Ok(Channel::new(channel, self.stream.as_ref().unwrap().clone()))
278    }
279
280    /// See [`channel_forward_listen`](ssh2::Session::channel_forward_listen).
281    pub async fn channel_forward_listen(
282        &self,
283        remote_port: u16,
284        host: Option<&str>,
285        queue_maxsize: Option<u32>,
286    ) -> Result<(Listener, u16), Error> {
287        let (listener, port) = run_ssh2_fn(self.stream.as_ref().unwrap(), || {
288            self.inner
289                .channel_forward_listen(remote_port, host, queue_maxsize)
290        })
291        .await?;
292        Ok((
293            Listener::new(listener, self.stream.as_ref().unwrap().clone()),
294            port,
295        ))
296    }
297
298    /// See [`scp_recv`](ssh2::Session::scp_recv).
299    pub async fn scp_recv(&self, path: &Path) -> Result<(Channel, ScpFileStat), Error> {
300        let (channel, file_stat) =
301            run_ssh2_fn(self.stream.as_ref().unwrap(), || self.inner.scp_recv(path)).await?;
302        Ok((
303            Channel::new(channel, self.stream.as_ref().unwrap().clone()),
304            file_stat,
305        ))
306    }
307
308    /// See [`scp_send`](ssh2::Session::scp_send).
309    pub async fn scp_send(
310        &self,
311        remote_path: &Path,
312        mode: i32,
313        size: u64,
314        times: Option<(u64, u64)>,
315    ) -> Result<Channel, Error> {
316        let channel = run_ssh2_fn(self.stream.as_ref().unwrap(), || {
317            self.inner.scp_send(remote_path, mode, size, times)
318        })
319        .await?;
320        Ok(Channel::new(channel, self.stream.as_ref().unwrap().clone()))
321    }
322
323    /// See [`sftp`](ssh2::Session::sftp).
324    pub async fn sftp(&self) -> Result<Sftp, Error> {
325        let sftp = run_ssh2_fn(self.stream.as_ref().unwrap(), || self.inner.sftp()).await?;
326        Ok(Sftp::new(sftp, self.stream.as_ref().unwrap().clone()))
327    }
328
329    /// See [`channel_open`](ssh2::Session::channel_open).
330    pub async fn channel_open(
331        &self,
332        channel_type: &str,
333        window_size: u32,
334        packet_size: u32,
335        message: Option<&str>,
336    ) -> Result<Channel, Error> {
337        let channel = run_ssh2_fn(self.stream.as_ref().unwrap(), || {
338            self.inner
339                .channel_open(channel_type, window_size, packet_size, message)
340        })
341        .await?;
342        Ok(Channel::new(channel, self.stream.as_ref().unwrap().clone()))
343    }
344
345    /// See [`banner`](ssh2::Session::banner).
346    pub fn banner(&self) -> Option<&str> {
347        self.inner.banner()
348    }
349
350    /// See [`banner_bytes`](ssh2::Session::banner_bytes).
351    pub fn banner_bytes(&self) -> Option<&[u8]> {
352        self.inner.banner_bytes()
353    }
354
355    /// See [`host_key`](ssh2::Session::host_key).
356    pub fn host_key(&self) -> Option<(&[u8], HostKeyType)> {
357        self.inner.host_key()
358    }
359
360    /// See [`host_key_hash`](ssh2::Session::host_key_hash).
361    pub fn host_key_hash(&self, hash: HashType) -> Option<&[u8]> {
362        self.inner.host_key_hash(hash)
363    }
364
365    /// See [`set_keepalive`](ssh2::Session::set_keepalive).
366    pub fn set_keepalive(&self, want_reply: bool, interval: u32) {
367        self.inner.set_keepalive(want_reply, interval)
368    }
369
370    /// See [`keepalive_send`](ssh2::Session::keepalive_send).
371    pub async fn keepalive_send(&self) -> Result<u32, Error> {
372        run_ssh2_fn(self.stream.as_ref().unwrap(), || {
373            self.inner.keepalive_send()
374        })
375        .await
376    }
377
378    /// See [`disconnect`](ssh2::Session::disconnect).
379    pub async fn disconnect(
380        &self,
381        reason: Option<DisconnectCode>,
382        description: &str,
383        lang: Option<&str>,
384    ) -> Result<(), Error> {
385        run_ssh2_fn(self.stream.as_ref().unwrap(), || {
386            self.inner.disconnect(reason, description, lang)
387        })
388        .await
389    }
390}
391
392#[cfg(unix)]
393impl AsRawFd for Session {
394    fn as_raw_fd(&self) -> RawFd {
395        self.inner.as_raw_fd()
396    }
397}
398
399#[cfg(windows)]
400impl AsRawSocket for Session {
401    fn as_raw_socket(&self) -> RawSocket {
402        self.inner.as_raw_socket()
403    }
404}