sunset_async/
server.rs

1use embedded_io_async::{Read, Write};
2
3use sunset::*;
4
5use crate::*;
6use async_sunset::{AsyncSunset, ProgressHolder};
7
8/// An async SSH server instance
9///
10/// The [`run()`][Self::run] method runs the session to completion. [`progress()`][Self::progress]
11/// must be polled, and responses given to the events provided.
12///
13/// Once the client has opened sessions, those can be retrieved with [`stdio()`][Self::stdio]
14/// and [`stdio_stderr()`][Self::stdio_stderr] methods.
15///
16/// This is async executor agnostic.
17#[derive(Debug)]
18pub struct SSHServer<'a> {
19    sunset: AsyncSunset<'a, sunset::Server>,
20}
21
22impl<'a> SSHServer<'a> {
23    // May return an error if RNG fails
24    pub fn new(inbuf: &'a mut [u8], outbuf: &'a mut [u8]) -> Self {
25        let runner = Runner::new_server(inbuf, outbuf);
26        let sunset = AsyncSunset::new(runner);
27        Self { sunset }
28    }
29
30    /// Runs the session to completion.
31    ///
32    /// `rsock` and `wsock` are the SSH network channel (TCP port 22 or equivalent).
33    pub async fn run(
34        &self,
35        rsock: &mut impl Read,
36        wsock: &mut impl Write,
37    ) -> Result<()> {
38        self.sunset.run(rsock, wsock).await
39    }
40
41    /// Returns an event from the SSH session.
42    ///
43    /// Note that on return `ProgressHolder` holds a mutex over the session,
44    /// so most other calls to `SSHServer` will block until the `ProgressHolder`
45    /// is dropped.
46    pub async fn progress<'g, 'f>(
47        &'g self,
48        ph: &'f mut ProgressHolder<'g, 'a, sunset::Server>,
49    ) -> Result<ServEvent<'f, 'a>> {
50        // poll until we get an actual event to return
51        match self.sunset.progress(ph).await? {
52            Event::Serv(x) => Ok(x),
53            Event::None => Ok(ServEvent::PollAgain),
54            Event::Progressed => Ok(ServEvent::PollAgain),
55            Event::Cli(_) => Err(Error::bug()),
56        }
57    }
58
59    /// Returns a [`ChanInOut`] representing a channel.
60    ///
61    /// For a shell this is stdin/stdout, for other channel types it is the only
62    /// data type.
63    /// `ch` is the [`ChanHandle`] returned after accepting a [`ServEvent::OpenSession`] event.
64    pub async fn stdio(&self, ch: ChanHandle) -> Result<ChanInOut<'_>> {
65        let num = ch.num();
66        self.sunset.add_channel(ch, 1).await?;
67        Ok(ChanInOut::new(num, ChanData::Normal, &self.sunset))
68    }
69
70    /// Retrieve the stdin/stdout/stderr streams.
71    ///
72    /// If stderr is not required, use [`stdio()`][Self::stdio] instead to avoid needing to poll
73    /// the returned stderr.
74    /// The session will block until the streams are drained (they use the session buffer),
75    /// so they must be drained if used.
76    pub async fn stdio_stderr(
77        &self,
78        ch: ChanHandle,
79    ) -> Result<(ChanInOut<'_>, ChanOut<'_>)> {
80        let num = ch.num();
81        self.sunset.add_channel(ch, 2).await?;
82        let i = ChanInOut::new(num, ChanData::Normal, &self.sunset);
83        let e = ChanOut::new(num, ChanData::Stderr, &self.sunset);
84        Ok((i, e))
85    }
86
87    // TODO: add stdio_stderr()
88}