From 53bc57051ded45c53a862ca1e0b347b3bf520f06 Mon Sep 17 00:00:00 2001 From: Matt Johnston <matt@ucc.asn.au> Date: Mon, 27 Jun 2022 23:30:31 +0800 Subject: [PATCH] misisng files --- async/src/cmdline_client.rs | 134 ++++++++++++++++++++++++++++++++++++ async/src/pty.rs | 98 ++++++++++++++++++++++++++ 2 files changed, 232 insertions(+) create mode 100644 async/src/cmdline_client.rs create mode 100644 async/src/pty.rs diff --git a/async/src/cmdline_client.rs b/async/src/cmdline_client.rs new file mode 100644 index 0000000..26d395a --- /dev/null +++ b/async/src/cmdline_client.rs @@ -0,0 +1,134 @@ +#[allow(unused_imports)] +use log::{debug, error, info, log, trace, warn}; + +use core::str::FromStr; + +use door::SignKey; +use door_sshproto as door; +use door_sshproto::{BhError, BhResult}; +use door_sshproto::{ChanMsg, ChanMsgDetails, Error, RespPackets, Result, Runner}; + +use std::collections::VecDeque; + +use async_trait::async_trait; + +/// Command line interface SSH client behaviour +pub struct CmdlineClient { + auth_done: bool, + main_ch: Option<u32>, + authkeys: VecDeque<SignKey>, + username: String, +} + +impl CmdlineClient { + pub fn new(username: &impl AsRef<str>) -> Self { + CmdlineClient { + auth_done: false, + main_ch: None, + authkeys: VecDeque::new(), + username: username.as_ref().into(), + } + } + + pub fn add_authkey(&mut self, k: SignKey) { + self.authkeys.push_back(k) + } +} + +// #[async_trait(?Send)] +#[async_trait] +impl door::AsyncCliBehaviour for CmdlineClient { + async fn username(&mut self) -> BhResult<door::ResponseString> { + door::ResponseString::from_str(&self.username).map_err(|_| BhError::Fail) + } + + async fn valid_hostkey(&mut self, key: &door::PubKey) -> BhResult<bool> { + trace!("valid_hostkey for {key:?}"); + Ok(true) + } + + async fn next_authkey(&mut self) -> BhResult<Option<door::SignKey>> { + Ok(self.authkeys.pop_front()) + } + + async fn auth_password( + &mut self, + pwbuf: &mut door::ResponseString, + ) -> BhResult<bool> { + let pw = + rpassword::prompt_password(format!("password for {}: ", self.username)) + .map_err(|e| { + warn!("read_password failed {e:}"); + BhError::Fail + })?; + if pwbuf.push_str(&pw).is_err() { + Err(BhError::Fail) + } else { + Ok(true) + } + } + + async fn authenticated(&mut self) { + info!("Authentication succeeded"); + self.auth_done = true; + } + // } + + // impl door::BlockCliBehaviour for CmdlineClient { + // fn chan_handler<'f>(&mut self, resp: &mut RespPackets, chan_msg: ChanMsg<'f>) -> Result<()> { + // if Some(chan_msg.num) != self.main_ch { + // return Err(Error::SSHProtoError) + // } + + // match chan_msg.msg { + // ChanMsgDetails::Data(buf) => { + // let _ = std::io::stdout().write_all(buf); + // }, + // ChanMsgDetails::ExtData{..} => { + // } + // ChanMsgDetails::Req{..} => { + // } + // _ => {} + // } + // Ok(()) + // } + + // fn progress(&mut self, runner: &mut Runner) -> Result<()> { + // if self.auth_done { + // if self.main_ch.is_none() { + // let ch = runner.open_client_session(Some("cowsay it works"), false)?; + // self.main_ch = Some(ch); + // } + // } + // Ok(()) + // } + + // fn username(&mut self) -> BhResult<door::ResponseString> { + // // TODO unwrap + // let mut p = door::ResponseString::new(); + // p.push_str("matt").unwrap(); + // Ok(p) + // } + + // fn valid_hostkey(&mut self, key: &door::PubKey) -> BhResult<bool> { + // trace!("valid_hostkey for {key:?}"); + // Ok(true) + // } + + // fn auth_password(&mut self, pwbuf: &mut door::ResponseString) -> BhResult<bool> { + // let pw = rpassword::prompt_password("password: ").map_err(|e| { + // warn!("read_password failed {e:}"); + // BhError::Fail + // })?; + // if pwbuf.push_str(&pw).is_err() { + // Err(BhError::Fail) + // } else { + // Ok(true) + // } + // } + + // fn authenticated(&mut self) { + // info!("Authentication succeeded"); + // self.auth_done = true; + // } +} diff --git a/async/src/pty.rs b/async/src/pty.rs new file mode 100644 index 0000000..9bb12b2 --- /dev/null +++ b/async/src/pty.rs @@ -0,0 +1,98 @@ +#[allow(unused_imports)] +use log::{debug, error, info, log, trace, warn}; + +use std::io::Error as IoError; + +use libc::{ioctl, winsize, termios, tcgetattr, tcsetattr }; + +use door_sshproto as door; +use door::{Behaviour, AsyncCliBehaviour, Runner, Result, Pty}; +use door::config::*; + + +pub fn current_pty() -> Result<Pty, IoError> { + let mut term = heapless::String::<MAX_TERM>::new(); + let t = std::env::var("TERM").unwrap_or(DEFAULT_TERM.into()); + // XXX error + term.push_str(&t).expect("TERM fits buffer"); + + let mut ws = winsize { ws_row: 0, ws_col: 0, ws_xpixel: 0, ws_ypixel: 0 }; + let r = unsafe { ioctl(libc::STDIN_FILENO, libc::TIOCGWINSZ, &mut ws) }; + if r != 0 { + return Err(IoError::last_os_error()) + } + + info!("rows {} cols {}", ws.ws_row, ws.ws_col); + + Ok(Pty { + term, + rows: ws.ws_row as u32, + cols: ws.ws_col as u32, + width: ws.ws_xpixel as u32, + height: ws.ws_ypixel as u32, + modes: heapless::Vec::new(), + }) + +} + +/// Puts stdin/stdout into raw mode. This assumes that stdin/stdout +/// share a common file descriptor, as is the case with a pty. +/// The returned `RawPtyGuard` reverts to previous terminal settings +/// when it is dropped. +pub fn raw_pty() -> Result<RawPtyGuard, IoError> { + RawPtyGuard::new() +} + +pub struct RawPtyGuard { + saved: termios, +} + +impl RawPtyGuard { + fn new() -> Result<Self, IoError> { + let mut saved: termios = unsafe { core::mem::zeroed() }; + let r = unsafe { tcgetattr(libc::STDIN_FILENO, &mut saved) }; + if r != 0 { + return Err(IoError::last_os_error()) + } + + Self::set_raw(&saved)?; + + Ok(Self { + saved, + }) + } + + fn set_raw(current: &termios) -> Result<(), IoError> { + use libc::*; + let mut raw = current.clone(); + + raw.c_iflag |= IGNPAR; + // We could also set IUCLC but it isn't in posix + raw.c_iflag &= !(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); + raw.c_lflag &= !(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); + raw.c_oflag &= !OPOST; + + let r = unsafe { tcsetattr(libc::STDIN_FILENO, TCSADRAIN, &raw) }; + if r != 0 { + return Err(IoError::last_os_error()) + } + + info!("set_raw"); + + Ok(()) + + } +} + +impl Drop for RawPtyGuard { + fn drop(&mut self) { + + let r = unsafe { tcsetattr(libc::STDIN_FILENO, libc::TCSADRAIN, &self.saved) }; + if r != 0 { + let e = IoError::last_os_error(); + warn!("Failed restoring TTY: {e}"); + } else { + info!("Restored TTY"); + } + } +} -- GitLab