Skip to content
Snippets Groups Projects
Commit 497720b7 authored by Matt Johnston's avatar Matt Johnston
Browse files

Share embassy common code between std and picow

parent f955b686
Branches
No related merge requests found
# Embassy demos common
The [picow](../picow) and [std](../std) demos share this common code.
Currently not a full crate, just source modules included.
#![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(())
}
......@@ -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",
......
......@@ -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" }
......
......@@ -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(())
}
......@@ -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() {
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment