Skip to main content

sunset_async/
client.rs

1use embedded_io_async::{Read, Write};
2
3use sunset::*;
4
5use crate::*;
6use async_channel::{ChanIn, ChanInOut};
7use async_sunset::{AsyncSunset, ProgressHolder};
8
9/// An async SSH client instance
10///
11/// The [`run()`][Self::run] method runs the session to completion. [`progress()`][Self::progress]
12/// must be polled, and responses given to the events provided.
13///
14/// Once authentication has completed (`progress()` returns [`CliEvent::Authenticated`]), the application
15/// may open remote channels with [`open_session_pty()`][Self::open_session_pty] etc.
16///
17/// This is async executor agnostic.
18pub struct SSHClient<'a> {
19    sunset: AsyncSunset<'a, sunset::Client>,
20}
21
22impl<'a> SSHClient<'a> {
23    pub fn new(inbuf: &'a mut [u8], outbuf: &'a mut [u8]) -> Self {
24        let runner = Runner::new_client(inbuf, outbuf);
25        let sunset = AsyncSunset::new(runner);
26        Self { sunset }
27    }
28
29    /// Runs the session to completion.
30    ///
31    /// `rsock` and `wsock` are the SSH network channel (TCP port 22 or equivalent).
32    pub async fn run(
33        &self,
34        rsock: &mut impl Read,
35        wsock: &mut impl Write,
36    ) -> Result<()> {
37        self.sunset.run(rsock, wsock).await
38    }
39
40    /// Returns an event from the SSH session.
41    ///
42    /// Note that on return `ProgressHolder` holds a mutex over the session,
43    /// so other calls to `SSHClient` may block until the `ProgressHolder`
44    /// is dropped.
45    pub async fn progress<'g, 'f>(
46        &'g self,
47        ph: &'f mut ProgressHolder<'g, 'a, sunset::Client>,
48    ) -> Result<CliEvent<'f, 'a>> {
49        match self.sunset.progress(ph).await? {
50            Event::Cli(x) => Ok(x),
51            Event::None => Ok(CliEvent::PollAgain),
52            Event::Progressed => Ok(CliEvent::PollAgain),
53            _ => Error::bug(),
54        }
55    }
56
57    pub async fn open_session_nopty(&self) -> Result<(ChanInOut<'_>, ChanIn<'_>)> {
58        let ch =
59            self.sunset.with_runner(|runner| runner.open_client_session()).await?;
60
61        let io_normal = self.sunset.add_channel(ch).await?;
62        let e = ChanIn::new(io_normal.clone_stderr());
63        let i = ChanInOut::new(io_normal);
64        Ok((i, e))
65    }
66
67    pub async fn open_session_pty(&self) -> Result<ChanInOut<'_>> {
68        let ch =
69            self.sunset.with_runner(|runner| runner.open_client_session()).await?;
70
71        Ok(ChanInOut::new(self.sunset.add_channel(ch).await?))
72    }
73}
74
75#[cfg(feature = "alloc")]
76impl SSHClient<'static> {
77    pub fn new_owned() -> Self {
78        let runner = Runner::<'static, _>::new_client_owned();
79        let sunset = AsyncSunset::new(runner);
80        Self { sunset }
81    }
82}
83
84#[cfg(feature = "futures-io")]
85impl SSHClient<'_> {
86    pub async fn run_futures_io(
87        &self,
88        rsock: &mut (impl futures_io::AsyncRead + Unpin),
89        wsock: &mut (impl futures_io::AsyncWrite + Unpin),
90    ) -> Result<()> {
91        let mut rsock = embedded_io_adapters::futures_03::FromFutures::new(rsock);
92        let mut wsock = embedded_io_adapters::futures_03::FromFutures::new(wsock);
93        self.sunset.run(&mut rsock, &mut wsock).await
94    }
95}
96
97#[cfg(feature = "tokio")]
98impl SSHClient<'_> {
99    pub async fn run_tokio(
100        &self,
101        rsock: &mut (impl tokio::io::AsyncRead + Unpin),
102        wsock: &mut (impl tokio::io::AsyncWrite + Unpin),
103    ) -> Result<()> {
104        let mut rsock = embedded_io_adapters::tokio_1::FromTokio::new(rsock);
105        let mut wsock = embedded_io_adapters::tokio_1::FromTokio::new(wsock);
106        self.sunset.run(&mut rsock, &mut wsock).await
107    }
108}