sunset-async 0.6.0

Async for Sunset SSH
Documentation
use embedded_io_async::{Read, Write};

use sunset::*;

use crate::*;
use async_sunset::{AsyncSunset, ProgressHolder};

/// An async SSH server instance
///
/// The [`run()`][Self::run] method runs the session to completion. [`progress()`][Self::progress]
/// must be polled, and responses given to the events provided.
///
/// Once the client has opened sessions, those can be retrieved with [`stdio()`][Self::stdio]
/// and [`stdio_stderr()`][Self::stdio_stderr] methods.
///
/// This is async executor agnostic.
#[derive(Debug)]
pub struct SSHServer<'a> {
    sunset: AsyncSunset<'a, sunset::Server>,
}

impl<'a> SSHServer<'a> {
    // May return an error if RNG fails
    pub fn new(inbuf: &'a mut [u8], outbuf: &'a mut [u8]) -> Self {
        let runner = Runner::new_server(inbuf, outbuf);
        let sunset = AsyncSunset::new(runner);
        Self { sunset }
    }

    /// Runs the session to completion.
    ///
    /// `rsock` and `wsock` are the SSH network channel (TCP port 22 or equivalent).
    pub async fn run(
        &self,
        rsock: &mut impl Read,
        wsock: &mut impl Write,
    ) -> Result<()> {
        self.sunset.run(rsock, wsock).await
    }

    /// Returns an event from the SSH session.
    ///
    /// Note that on return `ProgressHolder` holds a mutex over the session,
    /// so most other calls to `SSHServer` will block until the `ProgressHolder`
    /// is dropped.
    pub async fn progress<'g, 'f>(
        &'g self,
        ph: &'f mut ProgressHolder<'g, 'a, sunset::Server>,
    ) -> Result<ServEvent<'f, 'a>> {
        // poll until we get an actual event to return
        match self.sunset.progress(ph).await? {
            Event::Serv(x) => Ok(x),
            Event::None => Ok(ServEvent::PollAgain),
            Event::Progressed => Ok(ServEvent::PollAgain),
            Event::Cli(_) => Error::bug(),
        }
    }

    /// Returns a [`ChanInOut`] representing a channel.
    ///
    /// `ch` is the [`ChanHandle`] returned after accepting a [`ServEvent::OpenSession`] event.
    /// If `stderr` is also needed, use [`stdio_stderr()`](Self::stdio_stderr) instead.
    pub async fn stdio(&self, ch: ChanHandle) -> Result<ChanInOut<'_>> {
        Ok(ChanInOut::new(self.sunset.add_channel(ch).await?))
    }

    /// Retrieve the stdin/stdout/stderr streams.
    ///
    /// See [`stdio()`](Self::stdio).
    pub async fn stdio_stderr(
        &self,
        ch: ChanHandle,
    ) -> Result<(ChanInOut<'_>, ChanOut<'_>)> {
        let io_normal = self.sunset.add_channel(ch).await?;
        let e = ChanOut::new(io_normal.clone_stderr());
        let i = ChanInOut::new(io_normal);
        Ok((i, e))
    }
}

#[cfg(feature = "alloc")]
impl SSHServer<'static> {
    pub fn new_owned() -> Self {
        let runner = Runner::new_server_owned();
        let sunset = AsyncSunset::new(runner);
        Self { sunset }
    }
}

#[cfg(feature = "futures-io")]
impl SSHServer<'_> {
    pub async fn run_futures_io(
        &self,
        rsock: &mut (impl futures_io::AsyncRead + Unpin),
        wsock: &mut (impl futures_io::AsyncWrite + Unpin),
    ) -> Result<()> {
        let mut rsock = embedded_io_adapters::futures_03::FromFutures::new(rsock);
        let mut wsock = embedded_io_adapters::futures_03::FromFutures::new(wsock);
        self.sunset.run(&mut rsock, &mut wsock).await
    }
}

#[cfg(feature = "tokio")]
impl SSHServer<'_> {
    pub async fn run_tokio(
        &self,
        rsock: &mut (impl tokio::io::AsyncRead + Unpin),
        wsock: &mut (impl tokio::io::AsyncWrite + Unpin),
    ) -> Result<()> {
        let mut rsock = embedded_io_adapters::tokio_1::FromTokio::new(rsock);
        let mut wsock = embedded_io_adapters::tokio_1::FromTokio::new(wsock);
        self.sunset.run(&mut rsock, &mut wsock).await
    }
}