From bc5f721de8996b48550b5069f5592caf2968e822 Mon Sep 17 00:00:00 2001 From: fx Date: Mon, 9 Oct 2023 13:07:54 +0200 Subject: added wol func and bad auth --- src/auth.rs | 41 ++++++++++++++++++++++++++++++++++++-- src/config.rs | 11 +++++++++++ src/main.rs | 3 ++- src/routes/start.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++--------- src/wol.rs | 29 +++++++++++++++++++++++++++ 5 files changed, 129 insertions(+), 12 deletions(-) create mode 100644 src/wol.rs (limited to 'src') diff --git a/src/auth.rs b/src/auth.rs index b1ad76d..b7693a0 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -1,3 +1,40 @@ -pub fn auth(secret: &str) -> bool { - secret == "aaa" +use std::error::Error; +use axum::headers::HeaderValue; +use axum::http::StatusCode; +use tracing::error; +use crate::auth::AuthError::{MissingSecret, ServerError, WrongSecret}; +use crate::config::SETTINGS; + +pub fn auth(secret: Option<&HeaderValue>) -> Result { + if let Some(value) = secret { + let key = SETTINGS + .get_string("apikey") + .map_err(|err| ServerError(Box::new(err)))?; + if value.to_str().map_err(|err| ServerError(Box::new(err)))? == key.as_str() { + Ok(true) + } else { + Err(WrongSecret) + } + } else { + Err(MissingSecret) + } } + +pub enum AuthError { + WrongSecret, + MissingSecret, + ServerError(Box), +} + +impl AuthError { + pub fn get(self) -> (StatusCode, &'static str) { + match self { + AuthError::WrongSecret => (StatusCode::UNAUTHORIZED, "Wrong credentials"), + AuthError::MissingSecret => (StatusCode::BAD_REQUEST, "Missing credentials"), + AuthError::ServerError(err) => { + error!("server error: {}", err.to_string()); + (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") + }, + } + } +} \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index e69de29..4c79810 100644 --- a/src/config.rs +++ b/src/config.rs @@ -0,0 +1,11 @@ +use config::Config; +use once_cell::sync::Lazy; + +pub static SETTINGS: Lazy = Lazy::new(setup); + +fn setup() -> Config { + Config::builder() + .add_source(config::Environment::with_prefix("WEBOL").separator("_")) + .build() + .unwrap() +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 60f2214..0fe170d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,11 +5,12 @@ use tracing_subscriber::{EnvFilter, fmt::{self, time::LocalTime}, prelude::*}; use crate::routes::start::start; mod auth; +mod config; mod routes; +mod wol; #[tokio::main] async fn main() { - unsafe { local_offset::set_soundness(local_offset::Soundness::Unsound); } let time_format = time::macros::format_description!("[year]-[month]-[day] [hour]:[minute]:[second]"); diff --git a/src/routes/start.rs b/src/routes/start.rs index cda6352..e7d7e0e 100644 --- a/src/routes/start.rs +++ b/src/routes/start.rs @@ -1,18 +1,33 @@ use axum::headers::HeaderMap; +use axum::http::StatusCode; use axum::Json; +use axum::response::{IntoResponse, Response}; use serde::{Deserialize, Serialize}; +use std::error::Error; use serde_json::{json, Value}; -use crate::auth::auth; +use tracing::error; +use crate::auth::{auth, AuthError}; +use crate::config::SETTINGS; +use crate::wol::{create_buffer, send_packet}; -pub async fn start(headers: HeaderMap, Json(payload): Json) -> Json { - let mut res = StartResponse { id: payload.id, boot: false }; - if let Some(secret) = headers.get("authorization") { - if !auth(secret.to_str().unwrap()) { Json(json!(res)) } else { - res.boot = true; - Json(json!(res)) - } +pub async fn start(headers: HeaderMap, Json(payload): Json) -> Result, StartError> { + let secret = headers.get("authorization"); + if auth(secret).map_err(StartError::Auth)? { + let bind_addr = SETTINGS + .get_string("bindaddr") + .map_err(|err| StartError::Server(Box::new(err)))?; + let broadcast_addr = SETTINGS + .get_string("broadcastaddr") + .map_err(|err| StartError::Server(Box::new(err)))?; + let _ = send_packet( + &bind_addr.parse().map_err(|err| StartError::Server(Box::new(err)))?, + &broadcast_addr.parse().map_err(|err| StartError::Server(Box::new(err)))?, + // TODO: MAC saved in DB + create_buffer(std::env::var("MAC").unwrap().as_str()).map_err(|err| StartError::Server(Box::new(err)))? + ).map_err(|err| StartError::Server(Box::new(err))); + Ok(Json(json!(StartResponse { id: payload.id, boot: true }))) } else { - Json(json!(res)) + Err(StartError::Generic) } } @@ -27,3 +42,27 @@ struct StartResponse { id: String, boot: bool, } + +pub enum StartError { + Auth(AuthError), + Generic, + Server(Box), +} + +impl IntoResponse for StartError { + fn into_response(self) -> Response { + let (status, error_message) = match self { + StartError::Auth(err) => err.get(), + StartError::Generic => (StatusCode::INTERNAL_SERVER_ERROR, ""), + StartError::Server(err) => { + error!("server error: {}", err.to_string()); + (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") + }, + + }; + let body = Json(json!({ + "error": error_message, + })); + (status, body).into_response() + } +} \ No newline at end of file diff --git a/src/wol.rs b/src/wol.rs new file mode 100644 index 0000000..80b66cd --- /dev/null +++ b/src/wol.rs @@ -0,0 +1,29 @@ +use std::net::{SocketAddr, UdpSocket}; +use std::num::ParseIntError; + +/// Creates the magic packet from a mac address +/// +/// # Panics +/// +/// Panics if `mac_addr` is an invalid mac +pub fn create_buffer(mac_addr: &str) -> Result, ParseIntError> { + let mut mac = Vec::new(); + let sp = mac_addr.split(':'); + for f in sp { + mac.push(u8::from_str_radix(f, 16)?); + }; + let mut buf = vec![255; 6]; + for _ in 0..16 { + for i in &mac { + buf.push(*i); + } + } + Ok(buf) +} + +/// Sends a buffer on UDP broadcast +pub fn send_packet(bind_addr: &SocketAddr, broadcast_addr: &SocketAddr, buffer: Vec) -> Result { + let socket = UdpSocket::bind(bind_addr)?; + socket.set_broadcast(true)?; + socket.send_to(&buffer, broadcast_addr) +} \ No newline at end of file -- cgit v1.2.3