use std::sync::Arc;
use async_trait::async_trait;
use parking_lot::Mutex;
use russh::server::{Auth, Handler, Handle, Msg, Session};
use russh::{Channel, ChannelId, CryptoVec};
use russh_keys::PublicKey;
use tokio::sync::mpsc;
use crate::terminal::{InputParser, SshSessionHandle};
async fn forward_output(
handle: Handle,
channel_id: ChannelId,
mut output_rx: mpsc::UnboundedReceiver<Vec<u8>>,
) {
while let Some(data) = output_rx.recv().await {
if !data.is_empty() {
if handle.data(channel_id, CryptoVec::from(data)).await.is_err() {
break;
}
}
}
let _ = handle.eof(channel_id).await;
let _ = handle.close(channel_id).await;
}
pub struct TuiSession {
pub channel_id: ChannelId,
pub handle: SshSessionHandle,
pub output_rx: Option<mpsc::UnboundedReceiver<Vec<u8>>>,
pub backend: Option<crate::terminal::SshBackend>,
}
pub struct TuiHandler<F>
where
F: FnOnce(Box<dyn crate::terminal::Backend>) + Send + 'static,
{
session: Option<TuiSession>,
app_factory: Option<F>,
peer_addr: Option<std::net::SocketAddr>,
}
impl<F> TuiHandler<F>
where
F: FnOnce(Box<dyn crate::terminal::Backend>) + Send + 'static,
{
pub fn new(app_factory: F, peer_addr: Option<std::net::SocketAddr>) -> Self {
Self {
session: None,
app_factory: Some(app_factory),
peer_addr,
}
}
async fn start_tui(&mut self, channel_id: ChannelId, session: &mut Session) -> Result<(), russh::Error> {
if let Some(ref mut s) = self.session {
if s.channel_id == channel_id {
if let Some(output_rx) = s.output_rx.take() {
let handle = session.handle();
tokio::spawn(async move {
forward_output(handle, channel_id, output_rx).await;
});
}
if let Some(backend) = s.backend.take() {
if let Some(factory) = self.app_factory.take() {
tokio::task::spawn_blocking(move || {
factory(Box::new(backend));
});
}
}
}
}
Ok(())
}
}
#[async_trait]
impl<F> Handler for TuiHandler<F>
where
F: FnOnce(Box<dyn crate::terminal::Backend>) + Send + 'static,
{
type Error = russh::Error;
async fn auth_password(
&mut self,
user: &str,
_password: &str,
) -> Result<Auth, Self::Error> {
log::info!("Password auth attempt from {:?} for user '{}'", self.peer_addr, user);
Ok(Auth::Accept)
}
async fn auth_publickey(
&mut self,
user: &str,
_key: &PublicKey,
) -> Result<Auth, Self::Error> {
log::info!("Pubkey auth attempt from {:?} for user '{}'", self.peer_addr, user);
Ok(Auth::Accept)
}
async fn channel_open_session(
&mut self,
channel: Channel<Msg>,
_session: &mut Session,
) -> Result<bool, Self::Error> {
log::debug!("Channel open session request");
let (event_tx, event_rx) = mpsc::unbounded_channel();
let (output_tx, output_rx) = mpsc::unbounded_channel();
let size = Arc::new(Mutex::new((80u16, 24u16)));
let backend = crate::terminal::SshBackend::new(
output_tx,
event_rx,
Arc::clone(&size),
);
let (_dummy_tx, dummy_rx) = mpsc::unbounded_channel();
let handle = SshSessionHandle {
event_tx,
output_rx: dummy_rx,
size,
input_parser: InputParser::new(),
};
self.session = Some(TuiSession {
channel_id: channel.id(),
handle,
output_rx: Some(output_rx),
backend: Some(backend),
});
Ok(true)
}
async fn pty_request(
&mut self,
channel_id: ChannelId,
term: &str,
col_width: u32,
row_height: u32,
_pix_width: u32,
_pix_height: u32,
_modes: &[(russh::Pty, u32)],
session: &mut Session,
) -> Result<(), Self::Error> {
log::debug!(
"PTY request: {}x{} term={}",
col_width, row_height, term
);
if let Some(ref mut s) = self.session {
if s.channel_id == channel_id {
s.handle.resize(col_width as u16, row_height as u16);
}
}
session.channel_success(channel_id)?;
Ok(())
}
async fn shell_request(
&mut self,
channel_id: ChannelId,
session: &mut Session,
) -> Result<(), Self::Error> {
self.start_tui(channel_id, session).await?;
session.channel_success(channel_id)?;
Ok(())
}
async fn exec_request(
&mut self,
channel_id: ChannelId,
_data: &[u8],
session: &mut Session,
) -> Result<(), Self::Error> {
self.start_tui(channel_id, session).await?;
session.channel_success(channel_id)?;
Ok(())
}
async fn window_change_request(
&mut self,
channel_id: ChannelId,
col_width: u32,
row_height: u32,
_pix_width: u32,
_pix_height: u32,
_session: &mut Session,
) -> Result<(), Self::Error> {
log::debug!("Window change: {}x{}", col_width, row_height);
if let Some(ref mut s) = self.session {
if s.channel_id == channel_id {
s.handle.resize(col_width as u16, row_height as u16);
}
}
Ok(())
}
async fn data(
&mut self,
channel_id: ChannelId,
data: &[u8],
_session: &mut Session,
) -> Result<(), Self::Error> {
if let Some(ref mut s) = self.session {
if s.channel_id == channel_id {
s.handle.process_input(data);
}
}
Ok(())
}
async fn channel_close(
&mut self,
channel_id: ChannelId,
_session: &mut Session,
) -> Result<(), Self::Error> {
if let Some(ref s) = self.session {
if s.channel_id == channel_id {
log::info!("Channel closed for {:?}", self.peer_addr);
self.session = None;
}
}
Ok(())
}
async fn channel_eof(
&mut self,
channel_id: ChannelId,
session: &mut Session,
) -> Result<(), Self::Error> {
log::debug!("Channel EOF on {:?}", channel_id);
if let Some(ref s) = self.session {
if s.channel_id == channel_id {
session.close(channel_id)?;
}
}
Ok(())
}
}
pub struct SimpleTuiHandler {
session: Option<TuiSession>,
peer_addr: Option<std::net::SocketAddr>,
}
impl SimpleTuiHandler {
pub fn new(peer_addr: Option<std::net::SocketAddr>) -> Self {
Self {
session: None,
peer_addr,
}
}
pub fn session(&self) -> Option<&TuiSession> {
self.session.as_ref()
}
pub fn session_mut(&mut self) -> Option<&mut TuiSession> {
self.session.as_mut()
}
}