pub extern crate russh;
use std::{fmt::Debug, net::SocketAddr, sync::Arc};
use russh::server::Handler;
mod channel_server;
mod channel_write;
mod ratatui;
pub use channel_server::{ChannelEvent, ChannelHandler, ChannelServer};
pub use ratatui::{RatatuiApp, RatatuiEnv, RatatuiTerm};
pub trait TailnetServer {
fn new_client(dev: Arc<crate::Device>, addr: SocketAddr) -> Self;
}
impl crate::Device {
pub async fn serve_ssh<H>(
self: Arc<Self>,
config: russh::server::Config,
listen_addr: SocketAddr,
) -> Result<(), crate::Error>
where
H: TailnetServer + Handler + Send + 'static,
H::Error: Debug,
{
let config = Arc::new(config);
let listener = self.tcp_listen(listen_addr).await?;
tracing::info!(%listen_addr, "ssh server listening");
loop {
let conn = listener.accept().await?;
let handler = H::new_client(self.clone(), conn.remote_addr());
let config = config.clone();
tokio::task::spawn(async move {
let sess = match russh::server::run_stream(config, conn, handler).await {
Ok(sess) => sess,
Err(e) => {
tracing::error!(error = ?e, "establishing session");
return;
}
};
match sess.await {
Ok(()) => {}
Err(e) => {
tracing::error!(error = ?e, "running ssh session");
}
}
});
}
}
pub async fn serve_ssh_tui<App>(
self: Arc<Self>,
config: russh::server::Config,
listen_addr: SocketAddr,
) -> Result<(), crate::Error>
where
App: RatatuiApp + Default + Send + 'static,
{
self.serve_ssh::<ChannelServer<RatatuiTerm<App>>>(config, listen_addr)
.await
}
}