From 21094bd0bf40091e72b0b2d2749fccf865eab6e4 Mon Sep 17 00:00:00 2001 From: Matt Johnston <matt@ucc.asn.au> Date: Tue, 9 Aug 2022 23:38:31 +0800 Subject: [PATCH] Some doc improvements and tidying con1 --- async/examples/con1.rs | 46 ++++++++++++++++----------------- async/src/client.rs | 4 +-- sshproto/src/behaviour.rs | 5 ++-- sshproto/src/block_behaviour.rs | 8 ++++++ sshproto/src/error.rs | 2 +- sshproto/src/packets.rs | 6 +++-- sshproto/src/sshnames.rs | 15 ++++++++--- sshproto/src/sshwire.rs | 18 +++++++------ 8 files changed, 61 insertions(+), 43 deletions(-) diff --git a/async/examples/con1.rs b/async/examples/con1.rs index b1e3b88..3350507 100644 --- a/async/examples/con1.rs +++ b/async/examples/con1.rs @@ -136,6 +136,12 @@ async fn run(args: &Args) -> Result<()> { info!("running main"); trace!("tracing main"); + let (cmd, wantpty) = if args.cmd.is_empty() { + (None, true) + } else { + (Some(args.cmd.join(" ")), false) + }; + // Connect to a peer let mut stream = TcpStream::connect((args.host.as_str(), args.port)).await?; @@ -145,21 +151,22 @@ async fn run(args: &Args) -> Result<()> { let tx = vec![0; 3000]; let tx = Box::leak(Box::new(tx)).as_mut_slice(); - // cli is a Behaviour - let mut cli = door_async::CmdlineClient::new(args.username.as_ref().unwrap()); + // app is a Behaviour + let mut app = door_async::CmdlineClient::new(args.username.as_ref().unwrap()); for i in &args.identityfile { - cli.add_authkey(read_key(&i).with_context(|| format!("loading key {i}"))?); + app.add_authkey(read_key(&i).with_context(|| format!("loading key {i}"))?); } - let mut door = SSHClient::new(work, tx)?; - let mut s = door.socket(); + let mut cli = SSHClient::new(work, tx)?; + let mut s = cli.socket(); + moro::async_scope!(|scope| { scope.spawn(tokio::io::copy_bidirectional(&mut stream, &mut s)); scope.spawn(async { loop { - let ev = door.progress(&mut cli, |ev| { + let ev = cli.progress(&mut app, |ev| { trace!("progress event {ev:?}"); let e = match ev { Event::CliAuthed => Some(Event::CliAuthed), @@ -172,41 +179,34 @@ async fn run(args: &Args) -> Result<()> { Some(Event::CliAuthed) => { let mut raw_pty_guard = None; info!("Opening a new session channel"); - let (cmd, pty) = if args.cmd.is_empty() { - (None, true) - } else { - (Some(args.cmd.join(" ")), false) - }; - let (mut io, mut err) = if pty { + let (mut io, mut errpair) = if wantpty { raw_pty_guard = Some(raw_pty()?); - let io = door.open_client_session_pty(cmd.as_deref()).await + let io = cli.open_session_pty(cmd.as_deref()).await .context("Opening session")?; (io, None) } else { - let (io, err) = door.open_client_session_nopty(cmd.as_deref()).await + let (io, err) = cli.open_session_nopty(cmd.as_deref()).await .context("Opening session")?; - (io, Some(err)) + let errpair = (err, door_async::stderr()?); + (io, Some(errpair)) }; + let mut i = door_async::stdin()?; let mut o = door_async::stdout()?; - let mut e = if err.is_some() { - Some(door_async::stderr()?) - } else { - None - }; let mut io2 = io.clone(); scope.spawn(async move { moro::async_scope!(|scope| { scope.spawn(tokio::io::copy(&mut io, &mut o)); scope.spawn(tokio::io::copy(&mut i, &mut io2)); - if let Some(ref mut err) = err { - scope.spawn(tokio::io::copy(err, e.as_mut().unwrap())); + if let Some(ref mut ep) = errpair { + let (err, e) = ep; + scope.spawn(tokio::io::copy(err, e)); } }).await; drop(raw_pty_guard); Ok::<_, anyhow::Error>(()) }); - // TODO: handle channel completion + // TODO: handle channel completion or open failure } Some(_) => unreachable!(), None => {}, diff --git a/async/src/client.rs b/async/src/client.rs index 6303029..d84a64c 100644 --- a/async/src/client.rs +++ b/async/src/client.rs @@ -58,7 +58,7 @@ impl<'a> SSHClient<'a> { // TODO: return a Channel object that gives events like WinChange or exit status // TODO: move to SimpleClient or something? - pub async fn open_client_session_nopty(&mut self, exec: Option<&str>) + pub async fn open_session_nopty(&mut self, exec: Option<&str>) -> Result<(ChanInOut<'a>, ChanExtIn<'a>)> { let chan = self.door.with_runner(|runner| { runner.open_client_session(exec, None) @@ -69,7 +69,7 @@ impl<'a> SSHClient<'a> { Ok((cstd, cerr)) } - pub async fn open_client_session_pty(&mut self, exec: Option<&str>) + pub async fn open_session_pty(&mut self, exec: Option<&str>) -> Result<ChanInOut<'a>> { // XXX error handling diff --git a/sshproto/src/behaviour.rs b/sshproto/src/behaviour.rs index ec34231..3f4336f 100644 --- a/sshproto/src/behaviour.rs +++ b/sshproto/src/behaviour.rs @@ -20,6 +20,7 @@ use conn::RespPackets; use sshwire::TextString; // TODO: "Bh" is an ugly abbreviation. Naming is hard. +// How about SSHApp instead? CliApp, ServApp? // TODO: probably want a special Result here. They probably all want // Result, it can return an error or other options like Disconnect? @@ -30,9 +31,6 @@ pub enum BhError { Fail, } -#[cfg(feature = "tokio-queue")] -pub type ReplyChannel = bhtokio::ReplyChannel; - // TODO: once async functions in traits work with no_std, this can all be reworked // to probably have Behaviour as a trait not a struct. // Tracking Issue for static async fn in traits @@ -70,6 +68,7 @@ impl<'a> Behaviour<'a> { pub(crate) fn client(&mut self) -> Result<CliBehaviour> { self.inner.client() } + pub(crate) fn server(&mut self) -> Result<ServBehaviour> { self.inner.server() } diff --git a/sshproto/src/block_behaviour.rs b/sshproto/src/block_behaviour.rs index 86240f1..99d0271 100644 --- a/sshproto/src/block_behaviour.rs +++ b/sshproto/src/block_behaviour.rs @@ -87,9 +87,17 @@ pub trait BlockCliBehaviour { pub trait BlockServBehaviour { fn hostkeys(&self) -> BhResult<&[&sign::SignKey]>; + fn have_auth_password(&self, username: &str) -> bool; + fn have_auth_pubkey(&self, username: &str) -> bool; + // fn authmethods(&self) -> [AuthMethod]; fn auth_password(&self, user: &str, password: &str) -> bool; + /// Returns whether a session channel can be opened + fn open_session(&self) -> BhResult<bool>; + + fn open_tcp_forwarded(&self, ) -> BhResult<bool>; + fn open_tcp_direct(&self) -> BhResult<bool>; } diff --git a/sshproto/src/error.rs b/sshproto/src/error.rs index 4217610..4b71979 100644 --- a/sshproto/src/error.rs +++ b/sshproto/src/error.rs @@ -23,7 +23,7 @@ pub enum Error { /// Input buffer ran out RanOut, - /// Not a UTF8 string + /// Not a UTF-8 string BadString, /// Not a valid SSH ASCII string diff --git a/sshproto/src/packets.rs b/sshproto/src/packets.rs index 89e2405..1cba608 100644 --- a/sshproto/src/packets.rs +++ b/sshproto/src/packets.rs @@ -1,5 +1,7 @@ -//! SSH protocol packets. A [`Packet`] can be encoded/decoded to the -//! SSH Binary Packet Protocol using [`crate::sshwire`]. +//! SSH protocol packets. +//! +//! A [`Packet`] can be encoded/decoded to the +//! SSH Binary Packet Protocol using [`sshwire`]. use core::borrow::BorrowMut; use core::cell::Cell; diff --git a/sshproto/src/sshnames.rs b/sshproto/src/sshnames.rs index 38296bc..992a6e4 100644 --- a/sshproto/src/sshnames.rs +++ b/sshproto/src/sshnames.rs @@ -1,8 +1,9 @@ -//! Named SSH algorithms, methods and extensions. This module also serves as -//! an index of SSH specifications. - +//! Named SSH algorithms, methods and extensions. +//! //! Some identifiers are also listed directly in `packet.rs` derive attributes. -//! Packet numbers are listed in `packet.rs`. +//! Packet numbers are listed in `packets.rs`. +//! +//! This module also serves as index of SSH specifications. /// [RFC8731](https://tools.ietf.org/html/rfc8731) pub const SSH_NAME_CURVE25519: &str = "curve25519-sha256"; @@ -52,3 +53,9 @@ pub const SSH_AUTHMETHOD_INTERACTIVE: &str = "keyboard-interactive"; /// [RFC4254](https://tools.ietf.org/html/rfc4254) pub const SSH_EXTENDED_DATA_STDERR: u32 = 1; + +/// [RFC4254](https://tools.ietf.org/html/rfc4254) +pub const SSH_OPEN_ADMINISTRATIVELY_PROHIBITED: u32 = 1; +pub const SSH_OPEN_CONNECT_FAILED: u32 = 2; +pub const SSH_OPEN_UNKNOWN_CHANNEL_TYPE: u32 = 3; +pub const SSH_OPEN_RESOURCE_SHORTAGE: u32 = 4; diff --git a/sshproto/src/sshwire.rs b/sshproto/src/sshwire.rs index e24e0cd..421f885 100644 --- a/sshproto/src/sshwire.rs +++ b/sshproto/src/sshwire.rs @@ -58,6 +58,7 @@ pub trait SSHDecodeEnum<'de>: Sized { } /// A subset of [`Error`] for `SSHEncode` and `SSHDecode`. +/// /// Compiled code size is very sensitive to the size of this /// enum so we avoid unused elements. #[derive(Debug)] @@ -239,7 +240,7 @@ pub fn hash_mpint(hash_ctx: &mut dyn digest::DynDigest, m: &[u8]) { /////////////////////////////////////////////// -/// A SSH style binary string. Serialized as 32 bit length followed by the bytes +/// A SSH style binary string. Serialized as `u32` length followed by the bytes /// of the slice. /// Application API #[derive(Clone,PartialEq)] @@ -275,12 +276,13 @@ impl<'de> SSHDecode<'de> for BinString<'de> { } /// A text string that may be presented to a user or used -/// for things such as a password, username, exec command, tcp hostname, etc. +/// for things such as a password, username, exec command, TCP hostname, etc. +/// /// The SSH protocol defines it to be UTF-8, though -/// in some applications it could be treated as ascii-only. +/// in some applications it could be treated as ASCII-only. /// The library treats it as an opaque `&[u8]`, leaving -/// decoding to the `Behaviour`. - +/// decoding to the [`Behaviour`]. +/// /// Note that SSH protocol identifiers in `Packet` etc /// are `&str` rather than `TextString`, and always defined as ASCII. /// Application API @@ -288,8 +290,8 @@ impl<'de> SSHDecode<'de> for BinString<'de> { pub struct TextString<'a>(pub &'a [u8]); impl<'a> TextString<'a> { - /// Returns the utf8 decoded string, using [`core::str::from_utf8`] - /// Don't call this if you are avoiding including utf8 routines in + /// Returns the UTF-8 decoded string, using [`core::str::from_utf8`] + /// Don't call this if you are avoiding including UTF-8 routines in /// the binary. pub fn as_str(&self) -> Result<&'a str> { core::str::from_utf8(self.0).map_err(|_| Error::BadString) @@ -484,7 +486,7 @@ impl<'de> SSHDecode<'de> for u32 { } } -/// Decodes a SSH name string. Must be ascii +/// Decodes a SSH name string. Must be ASCII /// without control characters. RFC4251 section 6. pub fn try_as_ascii<'a>(t: &'a [u8]) -> WireResult<&'a AsciiStr> { let n = t.as_ascii_str().map_err(|_| WireError::BadName)?; -- GitLab