From 497720b794620ea1e0801fd3380d142fa4eba32e Mon Sep 17 00:00:00 2001 From: Matt Johnston <matt@ucc.asn.au> Date: Tue, 22 Nov 2022 23:38:13 +0800 Subject: [PATCH] Share embassy common code between std and picow --- embassy/demos/common/README.md | 4 + embassy/demos/common/common.rs | 183 ++++++++++++++++++ .../demos/{std/src => common}/demo_menu.rs | 0 embassy/demos/picow/Cargo.lock | 7 + embassy/demos/picow/Cargo.toml | 6 + embassy/demos/picow/src/main.rs | 153 ++------------- embassy/demos/std/src/main.rs | 158 ++------------- 7 files changed, 222 insertions(+), 289 deletions(-) create mode 100644 embassy/demos/common/README.md create mode 100644 embassy/demos/common/common.rs rename embassy/demos/{std/src => common}/demo_menu.rs (100%) diff --git a/embassy/demos/common/README.md b/embassy/demos/common/README.md new file mode 100644 index 0000000..225de75 --- /dev/null +++ b/embassy/demos/common/README.md @@ -0,0 +1,4 @@ +# Embassy demos common + +The [picow](../picow) and [std](../std) demos share this common code. +Currently not a full crate, just source modules included. diff --git a/embassy/demos/common/common.rs b/embassy/demos/common/common.rs new file mode 100644 index 0000000..482eb77 --- /dev/null +++ b/embassy/demos/common/common.rs @@ -0,0 +1,183 @@ +#![feature(type_alias_impl_trait)] + +#[allow(unused_imports)] +#[cfg(not(feature = "defmt"))] +use { + log::{debug, error, info, log, trace, warn}, +}; + +#[cfg(feature = "defmt")] +use defmt::{debug, info, warn, panic, error, trace}; + +use core::future::Future; + +use embassy_executor::{Spawner, Executor}; +use embassy_sync::mutex::Mutex; +use embassy_sync::blocking_mutex::raw::{NoopRawMutex, CriticalSectionRawMutex}; +use embassy_sync::signal::Signal; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Stack, Device, StackResources, ConfigStrategy}; +use embassy_futures::join::join; +use static_cell::StaticCell; + +use menu::Runner as MenuRunner; +use menu::Menu; + +use sunset::*; +use sunset::error::TrapBug; +use sunset_embassy::SSHServer; + +mod demo_menu; + +#[macro_export] +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell<T> = StaticCell::new(); + STATIC_CELL.init_with(move || $val) + }}; +} + +pub struct SSHConfig { + keys: [SignKey; 1], +} + +impl SSHConfig { + pub fn new() -> Result<Self> { + let keys = [SignKey::generate(KeyType::Ed25519)?]; + Ok(Self { + keys + }) + } +} + +// main entry point +pub async fn listener<D: Device>(stack: &'static Stack<D>, config: &SSHConfig) -> ! { + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + + loop { + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + + info!("Listening on TCP:22..."); + if let Err(e) = socket.accept(22).await { + warn!("accept error: {:?}", e); + continue; + } + + let r = session(&mut socket, &config).await; + if let Err(_e) = r { + // warn!("Ended with error: {:?}", e); + warn!("Ended with error"); + } + } +} + +struct DemoServer<'a> { + config: &'a SSHConfig, + + sess: Option<u32>, + shell_started: bool, + + shell: &'a DemoShell, +} + +impl<'a> DemoServer<'a> { + fn new(shell: &'a DemoShell, config: &'a SSHConfig) -> Result<Self> { + + Ok(Self { + sess: None, + config, + shell_started: false, + shell, + }) + } +} + +impl<'a> ServBehaviour for DemoServer<'a> { + fn hostkeys(&mut self) -> BhResult<&[SignKey]> { + Ok(&self.config.keys) + } + + fn auth_unchallenged(&mut self, username: TextString) -> bool { + info!("Allowing auth for user {}", username.as_str().unwrap_or("bad")); + true + } + + 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_shell(&mut self, chan: u32) -> bool { + let r = !self.shell_started && self.sess == Some(chan); + self.shell_started = true; + self.shell.notify.signal(chan); + trace!("req want shell"); + r + } + + fn sess_pty(&mut self, chan: u32, _pty: &Pty) -> bool { + self.sess == Some(chan) + } +} + + +#[derive(Default)] +struct DemoShell { + notify: Signal<CriticalSectionRawMutex, u32>, +} + +impl DemoShell { + async fn run<'f>(&self, serv: &SSHServer<'f>) -> Result<()> + { + let session = async { + // wait for a shell to start + let chan = self.notify.wait().await; + + let mut menu_buf = [0u8; 64]; + let menu_out = demo_menu::Output::default(); + + let mut menu = MenuRunner::new(&demo_menu::ROOT_MENU, &mut menu_buf, menu_out); + + loop { + let mut b = [0u8; 20]; + let lr = serv.read_channel_stdin(chan, &mut b).await?; + let b = &mut b[..lr]; + for c in b.iter() { + menu.input_byte(*c); + menu.context.flush(serv, chan).await?; + } + } + Ok(()) + }; + + session.await + } +} + + +async fn session(socket: &mut TcpSocket<'_>, config: &SSHConfig) -> sunset::Result<()> { + let shell = DemoShell::default(); + let app = DemoServer::new(&shell, config)?; + + let mut ssh_rxbuf = [0; 2000]; + let mut ssh_txbuf = [0; 2000]; + let serv = SSHServer::new(&mut ssh_rxbuf, &mut ssh_txbuf)?; + let serv = &serv; + + let app = Mutex::<NoopRawMutex, _>::new(app); + + let session = shell.run(serv); + + let app = &app as &Mutex::<NoopRawMutex, dyn ServBehaviour>; + let run = serv.run(socket, app); + + join(run, session).await; + + Ok(()) +} diff --git a/embassy/demos/std/src/demo_menu.rs b/embassy/demos/common/demo_menu.rs similarity index 100% rename from embassy/demos/std/src/demo_menu.rs rename to embassy/demos/common/demo_menu.rs diff --git a/embassy/demos/picow/Cargo.lock b/embassy/demos/picow/Cargo.lock index 6ead602..4932431 100644 --- a/embassy/demos/picow/Cargo.lock +++ b/embassy/demos/picow/Cargo.lock @@ -872,6 +872,12 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "menu" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b03d7f798bfe97329ad6df937951142eec93886b37d87010502dd25e8cc75fd5" + [[package]] name = "nb" version = "0.1.3" @@ -1341,6 +1347,7 @@ dependencies = [ "futures", "getrandom", "heapless", + "menu", "panic-probe", "pin-utils", "rand", diff --git a/embassy/demos/picow/Cargo.toml b/embassy/demos/picow/Cargo.toml index 367297c..9dcb997 100644 --- a/embassy/demos/picow/Cargo.toml +++ b/embassy/demos/picow/Cargo.toml @@ -36,12 +36,18 @@ sunset = { path = "../../.." } getrandom = { version = "0.2", default-features = false, features = ["custom"]} pin-utils = "0.1" +menu = "0.3" + caprand = { path = "../../../../caprand" } critical-section = "1.1" rand = { version = "0.8", default-features = false, features = ["getrandom"] } sha2 = { version = "0.10", default-features = false } +[features] +default = ["defmt"] +defmt = [] + [patch.crates-io] embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "e7fdd500d8354a03fcd105c8298cf7b4798a4107" } embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "e7fdd500d8354a03fcd105c8298cf7b4798a4107" } diff --git a/embassy/demos/picow/src/main.rs b/embassy/demos/picow/src/main.rs index 95aa07a..e13f1bc 100644 --- a/embassy/demos/picow/src/main.rs +++ b/embassy/demos/picow/src/main.rs @@ -20,14 +20,6 @@ use embedded_io::asynch::{Read, Write}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; -use core::str::FromStr; -use core::cell::RefCell; -use core::num::NonZeroU32; -use futures::{task::noop_waker_ref,pending}; -use core::task::{Context,Poll,Waker,RawWaker,RawWakerVTable}; -use pin_utils::{pin_mut,unsafe_pinned}; -use core::pin::Pin; - use rand::rngs::OsRng; use rand::RngCore; @@ -35,16 +27,12 @@ use sunset::*; use sunset_embassy::SSHServer; mod wifi; +#[path = "../../common/common.rs"] +mod demo_common; -const NUM_LISTENERS: usize = 4; +use demo_common::SSHConfig; -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell<T> = StaticCell::new(); - STATIC_CELL.init_with(move || $val) - }}; -} +const NUM_LISTENERS: usize = 4; #[embassy_executor::task] async fn net_task(stack: &'static Stack<cyw43::NetDevice<'static>>) -> ! { @@ -111,139 +99,20 @@ async fn main(spawner: Spawner) { seed )); + let config = &*singleton!( + demo_common::SSHConfig::new().unwrap() + ); + unwrap!(spawner.spawn(net_task(stack))); for _ in 0..NUM_LISTENERS { - spawner.spawn(listener(stack)).unwrap(); + spawner.spawn(listener(stack, &config)).unwrap(); } } // TODO: pool_size should be NUM_LISTENERS but needs a literal #[embassy_executor::task(pool_size = 4)] -async fn listener(stack: &'static Stack<cyw43::NetDevice<'static>>) -> ! { - let mut rx_buffer = [0; 4096]; - let mut tx_buffer = [0; 4096]; - - loop { - let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); - - info!("Listening on TCP:22..."); - if let Err(e) = socket.accept(22).await { - warn!("accept error: {:?}", e); - continue; - } - - let r = session(&mut socket).await; - if let Err(_e) = r { - // warn!("Ended with error: {:?}", e); - warn!("Ended with error"); - } - } -} - -struct DemoServer { - keys: [SignKey; 1], - - sess: Option<u32>, - want_shell: bool, - shell_started: bool, - - notify: Signal<CriticalSectionRawMutex, ()>, -} - -impl DemoServer { - fn new() -> Result<Self> { - let keys = [SignKey::generate(KeyType::Ed25519)?]; - - Ok(Self { - sess: None, - keys, - want_shell: false, - shell_started: false, - notify: Signal::new(), - }) - } -} - -impl ServBehaviour for DemoServer { - fn hostkeys(&mut self) -> BhResult<&[SignKey]> { - Ok(&self.keys) - } - - - fn have_auth_password(&self, user: TextString) -> bool { - true - } - - fn have_auth_pubkey(&self, user: TextString) -> bool { - true - } - - fn auth_unchallenged(&mut self, username: TextString) -> bool { - let u = username.as_str().unwrap_or("mystery user"); - info!("Allowing auth for user {}", u); - true - } - - fn auth_password(&mut self, user: TextString, password: TextString) -> bool { - user.as_str().unwrap_or("") == "matt" && password.as_str().unwrap_or("") == "pw" - } - - // fn auth_pubkey(&mut self, user: TextString, pubkey: &PubKey) -> bool { - // if user.as_str().unwrap_or("") != "matt" { - // return false - // } - - // // key is tested1 - // pubkey.matches_openssh("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMkNdReJERy1rPGqdfTN73TnayPR+lTNhdZvOgkAOs5x") - // .unwrap_or_else(|e| { - // warn!("Failed loading openssh key: {e}"); - // false - // }) - // } - - 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_shell(&mut self, chan: u32) -> bool { - let r = !self.want_shell && self.sess == Some(chan); - self.want_shell = true; - trace!("req want shell"); - r - } - - fn sess_pty(&mut self, chan: u32, _pty: &Pty) -> bool { - self.sess == Some(chan) - } +async fn listener(stack: &'static Stack<cyw43::NetDevice<'static>>, config: &'static SSHConfig) -> ! { + demo_common::listener(stack, config).await } -async fn session(socket: &mut TcpSocket<'_>) -> sunset::Result<()> { - let mut app = DemoServer::new()?; - - let mut ssh_rxbuf = [0; 2000]; - let mut ssh_txbuf = [0; 2000]; - let serv = SSHServer::new(&mut ssh_rxbuf, &mut ssh_txbuf, &mut app)?; - let serv = &serv; - - let app = Mutex::<NoopRawMutex, _>::new(app); - let app = &app as &Mutex::<NoopRawMutex, dyn ServBehaviour>; - - let run = serv.run(socket, app); - - let session = async { - loop { - } - }; - - // TODO: handle results - join(run, session).await; - - Ok(()) -} diff --git a/embassy/demos/std/src/main.rs b/embassy/demos/std/src/main.rs index deac6e9..5f5d75a 100644 --- a/embassy/demos/std/src/main.rs +++ b/embassy/demos/std/src/main.rs @@ -18,18 +18,9 @@ use embassy_futures::join::join; use embedded_io::asynch::{Read, Write}; use static_cell::StaticCell; -use core::str::FromStr; -use core::cell::RefCell; -use core::num::NonZeroU32; -use futures::{task::noop_waker_ref,pending}; -use core::task::{Context,Poll,Waker,RawWaker,RawWakerVTable}; - use rand::rngs::OsRng; use rand::RngCore; -use menu::Runner as MenuRunner; -use menu::Menu; - use sunset::*; use sunset::error::TrapBug; use sunset_embassy::SSHServer; @@ -37,17 +28,12 @@ use sunset_embassy::SSHServer; use crate::tuntap::TunTapDevice; mod tuntap; -mod demo_menu; +#[path = "../../common/common.rs"] +mod demo_common; -const NUM_LISTENERS: usize = 4; +use demo_common::SSHConfig; -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell<T> = StaticCell::new(); - STATIC_CELL.init_with(move || $val) - }}; -} +const NUM_LISTENERS: usize = 4; #[embassy_executor::task] async fn net_task(stack: &'static Stack<TunTapDevice>) -> ! { @@ -75,148 +61,26 @@ async fn main_task(spawner: Spawner) { seed )); + let config = &*singleton!( + demo_common::SSHConfig::new().unwrap() + ); + // Launch network task spawner.spawn(net_task(stack)).unwrap(); for _ in 0..NUM_LISTENERS { - spawner.spawn(listener(stack)).unwrap(); + spawner.spawn(listener(stack, &config)).unwrap(); } } // TODO: pool_size should be NUM_LISTENERS but needs a literal #[embassy_executor::task(pool_size = 4)] -async fn listener(stack: &'static Stack<TunTapDevice>) -> ! { - let mut rx_buffer = [0; 4096]; - let mut tx_buffer = [0; 4096]; - - loop { - let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - - info!("Listening on TCP:22..."); - if let Err(e) = socket.accept(22).await { - warn!("accept error: {:?}", e); - continue; - } - - let r = session(&mut socket).await; - if let Err(_e) = r { - // warn!("Ended with error: {:?}", e); - warn!("Ended with error"); - } - } -} - -struct DemoServer<'a> { - keys: [SignKey; 1], - - sess: Option<u32>, - shell_started: bool, - - shell: &'a DemoShell, -} - -impl<'a> DemoServer<'a> { - fn new(shell: &'a DemoShell) -> Result<Self> { - - let keys = [SignKey::generate(KeyType::Ed25519)?]; - - Ok(Self { - sess: None, - keys, - shell_started: false, - shell, - }) - } -} - -impl<'a> ServBehaviour for DemoServer<'a> { - fn hostkeys(&mut self) -> BhResult<&[SignKey]> { - Ok(&self.keys) - } - - fn auth_unchallenged(&mut self, username: TextString) -> bool { - info!("Allowing auth for user {:?}", username.as_str()); - true - } - - 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_shell(&mut self, chan: u32) -> bool { - let r = !self.shell_started && self.sess == Some(chan); - self.shell_started = true; - self.shell.notify.signal(chan); - trace!("req want shell"); - r - } - - fn sess_pty(&mut self, chan: u32, _pty: &Pty) -> bool { - self.sess == Some(chan) - } -} - - -#[derive(Default)] -struct DemoShell { - notify: Signal<CriticalSectionRawMutex, u32>, -} +async fn listener(stack: &'static Stack<TunTapDevice>, config: &'static SSHConfig) -> ! { -impl DemoShell { - async fn run<'f>(&self, serv: &SSHServer<'f>) -> Result<()> - { - let session = async { - // wait for a shell to start - let chan = self.notify.wait().await; - - let mut menu_buf = [0u8; 64]; - let mut menu_out = demo_menu::Output::default(); - - let mut menu = MenuRunner::new(&demo_menu::ROOT_MENU, &mut menu_buf, menu_out); - - loop { - let mut b = [0u8; 20]; - let lr = serv.read_channel_stdin(chan, &mut b).await?; - let b = &mut b[..lr]; - for c in b.iter() { - menu.input_byte(*c); - menu.context.flush(serv, chan).await?; - } - } - Ok(()) - }; - - session.await - } + demo_common::listener(stack, config).await } -async fn session(socket: &mut TcpSocket<'_>) -> sunset::Result<()> { - let shell = DemoShell::default(); - let app = DemoServer::new(&shell)?; - - let mut ssh_rxbuf = [0; 2000]; - let mut ssh_txbuf = [0; 2000]; - let serv = SSHServer::new(&mut ssh_rxbuf, &mut ssh_txbuf)?; - let serv = &serv; - - let app = Mutex::<NoopRawMutex, _>::new(app); - - let session = shell.run(serv); - - let app = &app as &Mutex::<NoopRawMutex, dyn ServBehaviour>; - let run = serv.run(socket, app); - - join(run, session).await; - - Ok(()) -} - static EXECUTOR: StaticCell<Executor> = StaticCell::new(); fn main() { -- GitLab