diff --git a/Cargo.lock b/Cargo.lock index 032b26c..eccced0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,6 +89,12 @@ version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + [[package]] name = "async-compression" version = "0.4.18" @@ -189,6 +195,31 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "aws-lc-rs" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c2b7ddaa2c56a367ad27a094ad8ef4faacf8a617c2575acb2ba88949df999ca" +dependencies = [ + "aws-lc-sys", + "paste", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71b2ddd3ada61a305e1d8bb6c005d1eaa7d14d903681edfc400406d523a9b491" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", + "paste", +] + [[package]] name = "axum" version = "0.7.9" @@ -290,6 +321,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56bac90848f6a9393ac03c63c640925c4b7c8ca21654de40d53f55964667c7d8" dependencies = [ + "arc-swap", "bytes", "futures-util", "http", @@ -298,6 +330,9 @@ dependencies = [ "hyper", "hyper-util", "pin-project-lite", + "rustls", + "rustls-pemfile", + "rustls-pki-types", "tokio", "tokio-rustls", "tower 0.4.13", @@ -490,6 +525,15 @@ dependencies = [ "vec_map", ] +[[package]] +name = "cmake" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e24a03c8b52922d68a1589ad61032f2c1aa5a8158d2aa0d93c6e9534944bbad6" +dependencies = [ + "cc", +] + [[package]] name = "codee" version = "0.2.0" @@ -799,6 +843,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "669a445ee724c5c69b1b06fe0b63e70a1c84bc9bb7d9696cd4f4e3ec45050408" +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "either" version = "1.13.0" @@ -922,6 +972,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures" version = "0.3.31" @@ -2674,6 +2730,7 @@ version = "0.23.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" dependencies = [ + "aws-lc-rs", "once_cell", "rustls-pki-types", "rustls-webpki", @@ -2681,6 +2738,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "rustls-pki-types" version = "1.10.1" @@ -2693,6 +2759,7 @@ version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", diff --git a/sparse-server/Cargo.toml b/sparse-server/Cargo.toml index b8843e7..c5d791f 100644 --- a/sparse-server/Cargo.toml +++ b/sparse-server/Cargo.toml @@ -20,7 +20,7 @@ tower-http = { version = "0.5", features = ["fs", "compression-br", "compression wasm-bindgen = "0.2" thiserror = "1" http = "1" -axum-server = { version = "^0.7", features = ["tokio-rustls"], optional = true } +axum-server = { version = "^0.7", features = ["tokio-rustls", "tls-rustls"], optional = true } tracing-subscriber = { version = "0.3", features = ["chrono", "env-filter", "serde", "tracing", "tracing-serde"], optional = true } structopt = { version = "0.3", optional = true } anyhow = "1.0" diff --git a/sparse-server/src/beacon_handler.rs b/sparse-server/src/beacon_handler.rs index 0e78f38..6842e6f 100644 --- a/sparse-server/src/beacon_handler.rs +++ b/sparse-server/src/beacon_handler.rs @@ -3,8 +3,97 @@ use std::{ sync::{Arc, RwLock}, }; +use axum::routing::{get, post, Router}; +use axum_server::{service::MakeService, tls_rustls::RustlsConfig}; +use sqlx::SqlitePool; use tokio::task::JoinHandle; -pub type BeaconListenerHandle = JoinHandle<()>; +pub struct BeaconListenerHandle { + join_handle: JoinHandle<()> +} + +impl BeaconListenerHandle { + pub fn is_finished(&self) -> bool { + self.join_handle.is_finished() + } + + pub fn abort(&self) { + self.join_handle.abort() + } +} pub type BeaconListenerMap = Arc>>; + +pub async fn start_all_listeners(beacon_listener_map: BeaconListenerMap, db: SqlitePool) -> Result<(), crate::error::Error> { + let listener_ids = sqlx::query!("SELECT listener_id FROM beacon_listener") + .fetch_all(&db) + .await?; + + tracing::info!("Starting {} listener(s)...", listener_ids.len()); + + for listener in listener_ids { + start_listener(Arc::clone(&beacon_listener_map), listener.listener_id, db.clone()).await?; + } + + Ok(()) +} + +#[derive(Clone)] +struct ListenerState { + db: SqlitePool +} + +pub async fn start_listener(beacon_listener_map: BeaconListenerMap, listener_id: i64, db: SqlitePool) -> Result<(), crate::error::Error> { + { + let Ok(blm_handle) = beacon_listener_map.read() else { + return Err(crate::error::Error::Generic("Could not acquire write lock on beacon listener map".to_string())); + }; + + if blm_handle.get(&listener_id).is_some() { + return Err(crate::error::Error::Generic("Beacon listener already started".to_string())); + } + } + let listener = sqlx::query!("SELECT * FROM beacon_listener WHERE listener_id = ?", listener_id) + .fetch_one(&db) + .await?; + + let app: Router<()> = Router::new() + .route("/register_beacon", post(|| async {})) + .route("/test", get(|| async { + "hi there" + })) + .with_state(ListenerState { + db + }); + + let hidden_app = Router::new().nest("/hidden_sparse", app); + + let tls_config = RustlsConfig::from_pem( + listener.certificate.as_bytes().to_vec(), + listener.privkey.as_bytes().to_vec() + ).await?; + + let addr = std::net::SocketAddr::from(([0, 0, 0, 0], listener.port as u16)); + + tracing::debug!("Starting listener {}, {}, on port {}", listener_id, listener.domain_name, listener.port); + + let join_handle = tokio::task::spawn(async move { + let res = axum_server::tls_rustls::bind_rustls(addr, tls_config) + .serve(hidden_app.into_make_service()) + .await; + + if let Err(e) = res { + tracing::error!("error running sparse listener: {e:?}"); + } + }); + + let Ok(mut blm_handle) = beacon_listener_map.write() else { + return Err(crate::error::Error::Generic("Could not acquire write lock on beacon listener map".to_string())); + }; + + blm_handle.insert(listener_id, BeaconListenerHandle { + join_handle + }); + + Ok(()) +} diff --git a/sparse-server/src/beacons/listeners.rs b/sparse-server/src/beacons/listeners.rs index 966bad4..bfd2897 100644 --- a/sparse-server/src/beacons/listeners.rs +++ b/sparse-server/src/beacons/listeners.rs @@ -111,7 +111,19 @@ pub async fn remove_listener(listener_id: i64) -> Result<(), ServerFnError> { #[server] pub async fn start_listener(listener_id: i64) -> Result<(), ServerFnError> { - unimplemented!() + let user = user::get_auth_session().await?; + + if user.is_none() { + return Err(ServerFnError::::ServerError("You are not signed in!".to_owned())); + } + + crate::beacon_handler::start_listener( + expect_context(), + listener_id, + expect_context() + ).await?; + + Ok(()) } #[component] @@ -193,7 +205,7 @@ fn DisplayListeners(listeners: Vec) -> impl IntoView { {listener.public_ip.clone()} ":" {listener.port} - ")" + ") " {match listener.active { true => Either::Left(view! { "active!" diff --git a/sparse-server/src/cli.rs b/sparse-server/src/cli.rs index 8923f63..7ad0abe 100644 --- a/sparse-server/src/cli.rs +++ b/sparse-server/src/cli.rs @@ -30,10 +30,6 @@ pub enum Command { /// Address to bind to for the management interface #[structopt(default_value = "127.0.0.1:3000")] management_address: SocketAddrV4, - - /// Public address to bind to for the beacons to call back to - #[structopt(default_value = "127.0.0.1:5000")] - bind_address: SocketAddrV4, }, /// Extract the public key and print it to standard out diff --git a/sparse-server/src/error.rs b/sparse-server/src/error.rs index 12cb6fe..7df4581 100644 --- a/sparse-server/src/error.rs +++ b/sparse-server/src/error.rs @@ -8,6 +8,8 @@ pub enum Error { TokioJoin(tokio::task::JoinError), #[cfg(feature = "ssr")] Pbkdf2(pbkdf2::password_hash::errors::Error), + #[cfg(feature = "ssr")] + Io(std::io::Error), } impl std::fmt::Display for Error { @@ -31,6 +33,10 @@ impl std::fmt::Display for Error { Error::Pbkdf2(err) => { write!(f, "password hash error: {err:?}") } + #[cfg(feature = "ssr")] + Error::Io(err) => { + write!(f, "io error: {err:?}") + } } } } @@ -42,6 +48,8 @@ impl std::error::Error for Error { Error::Sqlx(err) => Some(err), #[cfg(feature = "ssr")] Error::TokioJoin(err) => Some(err), + #[cfg(feature = "ssr")] + Error::Io(err) => Some(err), _ => None, } } @@ -75,3 +83,10 @@ impl From for Error { Self::Pbkdf2(err) } } + +#[cfg(feature = "ssr")] +impl From for Error { + fn from(err: std::io::Error) -> Self { + Self::Io(err) + } +} diff --git a/sparse-server/src/main.rs b/sparse-server/src/main.rs index c31383c..345eef1 100644 --- a/sparse-server/src/main.rs +++ b/sparse-server/src/main.rs @@ -79,9 +79,9 @@ async fn main() -> anyhow::Result { tracing::info!("Done running database migrations!"); match options.command.clone() { - Some(cli::Command::Serve { management_address, bind_address }) => { + Some(cli::Command::Serve { management_address }) => { tracing::info!("Performing requested action, acting as web server"); - webserver::serve_web(management_address, bind_address, pool).await + webserver::serve_web(management_address, pool).await } Some(cli::Command::ExtractPubKey { }) => { Ok(ExitCode::SUCCESS) @@ -95,8 +95,7 @@ async fn main() -> anyhow::Result { tracing::info!("Performing default action of acting as web server"); let default_management_ip = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 3000); - let default_beacon_ip = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 5000); - webserver::serve_web(default_management_ip, default_beacon_ip, pool).await + webserver::serve_web(default_management_ip, pool).await } } } diff --git a/sparse-server/src/users.rs b/sparse-server/src/users.rs index 259fe1d..78bf7ad 100644 --- a/sparse-server/src/users.rs +++ b/sparse-server/src/users.rs @@ -8,7 +8,7 @@ use { crate::db::user }; -fn format_delta(time: chrono::TimeDelta) -> String { +pub fn format_delta(time: chrono::TimeDelta) -> String { let seconds = time.num_seconds(); match seconds { diff --git a/sparse-server/src/webserver.rs b/sparse-server/src/webserver.rs index 4e7679a..2f55052 100644 --- a/sparse-server/src/webserver.rs +++ b/sparse-server/src/webserver.rs @@ -8,7 +8,7 @@ use tokio::signal; use sparse_server::app::*; -pub async fn serve_web(management_address: SocketAddrV4, _bind_address: SocketAddrV4, db: SqlitePool) -> anyhow::Result { +pub async fn serve_web(management_address: SocketAddrV4, db: SqlitePool) -> anyhow::Result { let conf = get_configuration(None).unwrap(); let leptos_options = conf.leptos_options; let routes = generate_route_list(App); @@ -20,6 +20,8 @@ pub async fn serve_web(management_address: SocketAddrV4, _bind_address: SocketAd .br(true) .zstd(true); + crate::beacon_handler::start_all_listeners(std::sync::Arc::clone(&beacon_listeners), db.clone()).await?; + let app = Router::new() .leptos_routes_with_context( &leptos_options,