From 60846ed8a33ec95edea33440c7b2e0c10e8c2c4c Mon Sep 17 00:00:00 2001
From: Matt Johnston <matt@ucc.asn.au>
Date: Fri, 19 Aug 2022 18:20:10 +0800
Subject: [PATCH] make serv1 build again with more scopes

---
 async/examples/con1.rs          |  10 +--
 async/examples/serv1.rs         | 111 ++++++++++++++++++++++++++++----
 sshproto/src/async_behaviour.rs |  18 +++---
 sshproto/src/behaviour.rs       |  16 ++---
 sshproto/src/channel.rs         |   2 +-
 sshproto/src/lib.rs             |   1 +
 6 files changed, 121 insertions(+), 37 deletions(-)

diff --git a/async/examples/con1.rs b/async/examples/con1.rs
index 3350507..020635f 100644
--- a/async/examples/con1.rs
+++ b/async/examples/con1.rs
@@ -145,19 +145,15 @@ async fn run(args: &Args) -> Result<()> {
     // Connect to a peer
     let mut stream = TcpStream::connect((args.host.as_str(), args.port)).await?;
 
-    let work = vec![0; 3000];
-    // TODO: better lifetime rather than leaking
-    let work = Box::leak(Box::new(work)).as_mut_slice();
-    let tx = vec![0; 3000];
-    let tx = Box::leak(Box::new(tx)).as_mut_slice();
-
     // app is a Behaviour
     let mut app = door_async::CmdlineClient::new(args.username.as_ref().unwrap());
     for i in &args.identityfile {
         app.add_authkey(read_key(&i).with_context(|| format!("loading key {i}"))?);
     }
 
-    let mut cli = SSHClient::new(work, tx)?;
+    let mut rxbuf = vec![0; 3000];
+    let mut txbuf = vec![0; 3000];
+    let mut cli = SSHClient::new(&mut rxbuf, &mut txbuf)?;
     let mut s = cli.socket();
 
 
diff --git a/async/examples/serv1.rs b/async/examples/serv1.rs
index e8b2156..b94553f 100644
--- a/async/examples/serv1.rs
+++ b/async/examples/serv1.rs
@@ -9,10 +9,13 @@ use pretty_hex::PrettyHex;
 use tokio::net::{TcpStream, TcpListener};
 
 use std::{net::Ipv6Addr, io::Read};
+use std::path::Path;
 
 use door_sshproto::*;
 use door_async::{SSHServer, raw_pty};
 
+use async_trait::async_trait;
+
 use simplelog::*;
 #[derive(argh::FromArgs)]
 /** con1
@@ -29,10 +32,15 @@ struct Args {
     #[argh(option, short='p', default="22")]
     /// port
     port: u16,
+
+    #[argh(option)]
+    /// a path to hostkeys. At most one of each key type.
+    hostkey: Vec<String>,
+
 }
 
 fn parse_args() -> Result<Args> {
-    let mut args: Args = argh::from_env();
+    let args: Args = argh::from_env();
 
     Ok(args)
 }
@@ -52,8 +60,7 @@ fn main() -> Result<()> {
         .map_err(|e| {
             error!("Exit with error: {e:?}");
             e
-        });
-    Ok(())
+        })
 }
 
 fn setup_log(args: &Args) -> Result<()> {
@@ -84,17 +91,97 @@ fn setup_log(args: &Args) -> Result<()> {
     Ok(())
 }
 
-fn run_session<'a, R: Send>(scope: &'a moro::Scope<'a, '_, R>, stream: TcpStream) -> Result<()> {
-    let rxbuf = vec![0; 3000];
-    // TODO: better lifetime rather than leaking
-    let rxbuf = Box::leak(Box::new(rxbuf)).as_mut_slice();
-    let txbuf = vec![0; 3000];
-    let txbuf = Box::leak(Box::new(txbuf)).as_mut_slice();
+fn read_key(p: &str) -> Result<SignKey> {
+    let mut v = vec![];
+    std::fs::File::open(p)?.read_to_end(&mut v)?;
+    SignKey::from_openssh(v).context("parsing openssh key")
+}
+
+struct DemoServer {
+    sess: Option<u32>,
+    keys: Vec<SignKey>
+}
 
-    let mut serv = SSHServer::new(rxbuf, txbuf)?;
+impl DemoServer {
+    fn new(keyfiles: &[String]) -> Result<Self> {
+        let keys = keyfiles.iter().map(|f| {
+            read_key(f).with_context(|| format!("loading key {f}"))
+        }).collect::<Result<Vec<SignKey>>>()?;
 
-    scope.spawn(async {
+        Ok(Self {
+            sess: None,
+            keys,
+        })
+    }
+}
 
+#[async_trait]
+impl AsyncServBehaviour for DemoServer {
+    async fn hostkeys(&mut self) -> BhResult<&[SignKey]> {
+        Ok(&self.keys)
+    }
+
+
+    fn have_auth_password(&self, user: &str) -> bool {
+        true
+    }
+
+    fn have_auth_pubkey(&self, user: &str) -> bool {
+        false
+    }
+
+    async fn auth_password(&mut self, user: &str, password: &str) -> bool {
+        user == "matt" && password == "pw"
+    }
+
+    fn open_session(&mut self, chan: u32) -> ChanOpened {
+        if self.sess.is_some() {
+            ChanOpened::Failure(ChanFail::SSH_OPEN_ADMINISTRATIVELY_PROHIBITED)
+        } else {
+            self.sess = Some(chan);
+            ChanOpened::Success
+        }
+    }
+
+    fn sess_req_shell(&mut self, _chan: u32) -> bool {
+        true
+    }
+
+    fn sess_pty(&mut self, _chan: u32, _pty: &Pty) -> bool {
+        true
+    }
+}
+
+fn run_session<'a, R: Send>(args: &'a Args, scope: &'a moro::Scope<'a, '_, R>, mut stream: TcpStream) -> Result<()> {
+    // app is a Behaviour
+
+    scope.spawn(async move {
+        let mut app = DemoServer::new(&args.hostkey)?;
+        let mut rxbuf = vec![0; 3000];
+        let mut txbuf = vec![0; 3000];
+        let mut serv = SSHServer::new(&mut rxbuf, &mut txbuf)?;
+        let mut s = serv.socket();
+
+        moro::async_scope!(|scope| {
+
+            scope.spawn(tokio::io::copy_bidirectional(&mut stream, &mut s));
+
+            scope.spawn(async {
+                loop {
+                    let ev = serv.progress(&mut app, |ev| {
+                        trace!("progress event {ev:?}");
+                        let e = match ev {
+                            Event::CliAuthed => Some(Event::CliAuthed),
+                            _ => None,
+                        };
+                        Ok(e)
+                    }).await.context("progress loop")?;
+                }
+                #[allow(unreachable_code)]
+                Ok::<_, anyhow::Error>(())
+            });
+            Ok::<_, anyhow::Error>(())
+        }).await
     });
     Ok(())
 
@@ -107,7 +194,7 @@ async fn run(args: &Args) -> Result<()> {
             loop {
                 let (stream, _) = listener.accept().await?;
 
-                run_session(scope, stream)?
+                run_session(args, scope, stream)?
             }
             #[allow(unreachable_code)]
             Ok::<_, anyhow::Error>(())
diff --git a/sshproto/src/async_behaviour.rs b/sshproto/src/async_behaviour.rs
index f7e5f8f..fc8395b 100644
--- a/sshproto/src/async_behaviour.rs
+++ b/sshproto/src/async_behaviour.rs
@@ -117,7 +117,7 @@ pub trait AsyncServBehaviour: Sync+Send {
     // then later request a single key.
     // Also could make it take a closure to call with the key, lets it just
     // be loaded on the stack rather than kept in memory for the whole lifetime.
-    async fn hostkeys(&self) -> BhResult<&[sign::SignKey]>;
+    async fn hostkeys(&mut self) -> BhResult<&[sign::SignKey]>;
 
     // TODO: or return a slice of enums
     fn have_auth_password(&self, user: &str) -> bool;
@@ -126,42 +126,42 @@ pub trait AsyncServBehaviour: Sync+Send {
 
     #[allow(unused)]
     // TODO: change password
-    async fn auth_password(&self, user: &str, password: &str) -> bool {
+    async fn auth_password(&mut self, user: &str, password: &str) -> bool {
         false
     }
 
     /// Returns true if the pubkey can be used to log in.
     /// TODO: allow returning pubkey restriction options
     #[allow(unused)]
-    async fn auth_pubkey(&self, user: &str, pubkey: &sign::SignKey) -> bool {
+    async fn auth_pubkey(&mut self, user: &str, pubkey: &sign::SignKey) -> bool {
         false
     }
 
     /// Returns whether a session can be opened
-    fn open_session(&self, chan: u32) -> channel::ChanOpened;
+    fn open_session(&mut self, chan: u32) -> channel::ChanOpened;
 
     #[allow(unused)]
-    fn open_tcp_forwarded(&self, chan: u32, t: &ForwardedTcpip) -> ChanOpened {
+    fn open_tcp_forwarded(&mut self, chan: u32, t: &ForwardedTcpip) -> ChanOpened {
         ChanOpened::Failure(ChanFail::SSH_OPEN_UNKNOWN_CHANNEL_TYPE)
     }
 
     #[allow(unused)]
-    fn open_tcp_direct(&self, chan: u32, t: &DirectTcpip) -> ChanOpened {
+    fn open_tcp_direct(&mut self, chan: u32, t: &DirectTcpip) -> ChanOpened {
         ChanOpened::Failure(ChanFail::SSH_OPEN_UNKNOWN_CHANNEL_TYPE)
     }
 
     #[allow(unused)]
-    fn sess_req_shell(&self, chan: u32) -> bool {
+    fn sess_req_shell(&mut self, chan: u32) -> bool {
         false
     }
 
     #[allow(unused)]
-    fn sess_req_exec(&self, chan: u32, cmd: &str) -> bool {
+    fn sess_req_exec(&mut self, chan: u32, cmd: &str) -> bool {
         false
     }
 
     #[allow(unused)]
-    fn sess_pty(&self, chan: u32, pty: &Pty) -> bool {
+    fn sess_pty(&mut self, chan: u32, pty: &Pty) -> bool {
         false
     }
 }
diff --git a/sshproto/src/behaviour.rs b/sshproto/src/behaviour.rs
index de627f9..d9861b1 100644
--- a/sshproto/src/behaviour.rs
+++ b/sshproto/src/behaviour.rs
@@ -261,7 +261,7 @@ pub struct ServBehaviour<'a> {
 
 #[cfg(feature = "std")]
 impl<'a> ServBehaviour<'a> {
-    pub(crate) async fn hostkeys(&self) -> BhResult<&[sign::SignKey]> {
+    pub(crate) async fn hostkeys(&mut self) -> BhResult<&[sign::SignKey]> {
         self.inner.hostkeys().await
     }
 
@@ -274,34 +274,34 @@ impl<'a> ServBehaviour<'a> {
 
     // fn authmethods(&self) -> [AuthMethod];
 
-    pub(crate) async fn auth_password(&self, user: &str, password: &str) -> bool {
+    pub(crate) async fn auth_password(&mut self, user: &str, password: &str) -> bool {
         self.inner.auth_password(user, password).await
     }
 
     /// Returns whether a session channel can be opened
-    pub(crate) fn open_session(&self, chan: u32) -> channel::ChanOpened {
+    pub(crate) fn open_session(&mut self, chan: u32) -> channel::ChanOpened {
         self.inner.open_session(chan)
     }
 
-    pub(crate) fn open_tcp_forwarded(&self, chan: u32,
+    pub(crate) fn open_tcp_forwarded(&mut self, chan: u32,
         t: &ForwardedTcpip) -> channel::ChanOpened {
         self.inner.open_tcp_forwarded(chan, t)
     }
 
-    pub(crate) fn open_tcp_direct(&self, chan: u32,
+    pub(crate) fn open_tcp_direct(&mut self, chan: u32,
         t: &DirectTcpip) -> channel::ChanOpened {
         self.inner.open_tcp_direct(chan, t)
     }
 
-    pub(crate) fn sess_req_shell(&self, chan: u32) -> bool {
+    pub(crate) fn sess_req_shell(&mut self, chan: u32) -> bool {
         self.inner.sess_req_shell(chan)
     }
 
-    pub(crate) fn sess_req_exec(&self, chan: u32, cmd: &str) -> bool {
+    pub(crate) fn sess_req_exec(&mut self, chan: u32, cmd: &str) -> bool {
         self.inner.sess_req_exec(chan, cmd)
     }
 
-    pub(crate) fn sess_pty(&self, chan: u32, pty: &Pty) -> bool {
+    pub(crate) fn sess_pty(&mut self, chan: u32, pty: &Pty) -> bool {
         self.inner.sess_pty(chan, pty)
     }
 }
diff --git a/sshproto/src/channel.rs b/sshproto/src/channel.rs
index eac61b8..f116e12 100644
--- a/sshproto/src/channel.rs
+++ b/sshproto/src/channel.rs
@@ -245,7 +245,7 @@ impl Channels {
         let r = match &p.ty {
             ChannelOpenType::Session => {
                 // unwrap: earlier test ensures b.server() succeeds
-                let bserv = b.server().unwrap();
+                let mut bserv = b.server().unwrap();
                 bserv.open_session(ch.recv.num)
             }
             ChannelOpenType::ForwardedTcpip(t) => {
diff --git a/sshproto/src/lib.rs b/sshproto/src/lib.rs
index 5bbcaba..df31681 100644
--- a/sshproto/src/lib.rs
+++ b/sshproto/src/lib.rs
@@ -58,4 +58,5 @@ pub use sign::SignKey;
 pub use packets::PubKey;
 pub use error::{Error,Result};
 pub use channel::{ChanMsg, ChanMsgDetails, ChanEvent, Pty, ChanOpened};
+pub use sshnames::{ChanFail};
 pub use conn::Event;
-- 
GitLab