From c5ecf4bc88c3525c708dfbb6d9e0a870b3e8baaf Mon Sep 17 00:00:00 2001 From: _ <_@_> Date: Sun, 1 Nov 2020 22:07:55 -0600 Subject: [PATCH] Add another layer in the URI to make room for future features --- ptth_handlebars/relay_server_list.html | 2 +- src/bin/file_server.rs | 13 +---- src/lib.rs | 13 ++++- src/relay/mod.rs | 11 +---- src/server/file_server.rs | 27 +++++------ src/server/mod.rs | 66 ++++++++++++++++++-------- todo.md | 2 +- 7 files changed, 76 insertions(+), 58 deletions(-) diff --git a/ptth_handlebars/relay_server_list.html b/ptth_handlebars/relay_server_list.html index 6dd6c4b..ca4fac0 100644 --- a/ptth_handlebars/relay_server_list.html +++ b/ptth_handlebars/relay_server_list.html @@ -26,7 +26,7 @@ {{#if servers}} {{#each servers}}
- {{this.name}} + {{this.name}}
{{/each}} {{else}} diff --git a/src/bin/file_server.rs b/src/bin/file_server.rs index 0c5a8ce..fb3fabf 100644 --- a/src/bin/file_server.rs +++ b/src/bin/file_server.rs @@ -20,6 +20,7 @@ use hyper::{ use ptth::{ http_serde::RequestParts, + prefix_match, server::file_server, }; @@ -33,16 +34,6 @@ fn status_reply > (status: StatusCode, b: B) Response::builder ().status (status).body (b.into ()).unwrap () } -fn prefix_match <'a> (hay: &'a str, needle: &str) -> Option <&'a str> -{ - if hay.starts_with (needle) { - Some (&hay [needle.len ()..]) - } - else { - None - } -} - async fn handle_all (req: Request , state: Arc >) -> Result , Infallible> { @@ -61,7 +52,7 @@ async fn handle_all (req: Request , state: Arc >) _ => return Ok (status_reply (StatusCode::BAD_REQUEST, "Bad request")), }; - let ptth_resp = file_server::serve_all (state.handlebars.clone (), &root, ptth_req).await; + let ptth_resp = file_server::serve_all (&state.handlebars, &root, ptth_req.method, &ptth_req.uri, &ptth_req.headers).await; let mut resp = Response::builder () .status (StatusCode::from (ptth_resp.parts.status_code)); diff --git a/src/lib.rs b/src/lib.rs index fecadc7..369d97b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,17 @@ pub const PTTH_MAGIC_HEADER: &str = "X-PTTH-2LJYXWC4"; pub mod relay; pub mod server; +pub fn prefix_match <'a> (hay: &'a str, needle: &str) -> Option <&'a str> +{ + if hay.starts_with (needle) { + Some (&hay [needle.len ()..]) + } + else { + None + } +} + + #[cfg (test)] mod tests { use std::{ @@ -89,7 +100,7 @@ mod tests { assert_eq! (resp, "Relay is up\n"); - let resp = client.get (&format! ("{}/servers/{}/COPYING", relay_url, server_name)) + let resp = client.get (&format! ("{}/servers/{}/files/COPYING", relay_url, server_name)) .send ().await.unwrap ().bytes ().await.unwrap (); if blake3::hash (&resp) != blake3::Hash::from ([ diff --git a/src/relay/mod.rs b/src/relay/mod.rs index d75f861..a4d3693 100644 --- a/src/relay/mod.rs +++ b/src/relay/mod.rs @@ -33,6 +33,7 @@ use tokio::{ use crate::{ http_serde, + prefix_match, }; /* @@ -291,16 +292,6 @@ async fn handle_http_request ( } } -fn prefix_match <'a> (hay: &'a str, needle: &str) -> Option <&'a str> -{ - if hay.starts_with (needle) { - Some (&hay [needle.len ()..]) - } - else { - None - } -} - async fn handle_all (req: Request , state: Arc ) -> Result , Infallible> { diff --git a/src/server/file_server.rs b/src/server/file_server.rs index 5d57114..7798121 100644 --- a/src/server/file_server.rs +++ b/src/server/file_server.rs @@ -2,11 +2,11 @@ use std::{ cmp::{min, max}, + collections::*, convert::{Infallible, TryInto}, error::Error, io::SeekFrom, path::{Path, PathBuf}, - sync::Arc, }; use handlebars::Handlebars; @@ -99,7 +99,7 @@ async fn read_dir_entry (entry: DirEntry) -> TemplateDirEntry use std::borrow::Cow; async fn serve_dir ( - handlebars: Arc >, + handlebars: &Handlebars <'static>, path: Cow <'_, str>, mut dir: ReadDir ) -> http_serde::Response @@ -226,34 +226,33 @@ async fn serve_error ( } pub async fn serve_all ( - handlebars: Arc >, + handlebars: &Handlebars <'static>, root: &Path, - parts: http_serde::RequestParts + method: http_serde::Method, + uri: &str, + headers: &HashMap >, ) -> http_serde::Response { - println! ("Client requested {}", parts.uri); + println! ("Client requested {}", uri); let mut range_start = None; let mut range_end = None; - for (k, v) in parts.headers.iter () { + if let Some (v) = headers.get ("range") { let v = std::str::from_utf8 (v).unwrap (); - //println! ("{}: {}", k, v); - if k == "range" { - let (start, end) = parse_range_header (v); - range_start = start; - range_end = end; - } + let (start, end) = parse_range_header (v); + range_start = start; + range_end = end; } - let should_send_body = matches! (&parts.method, http_serde::Method::Get); + let should_send_body = matches! (&method, http_serde::Method::Get); use percent_encoding::*; // TODO: There is totally a dir traversal attack in here somewhere - let encoded_path = &parts.uri [1..]; + let encoded_path = &uri [1..]; let path_s = percent_decode (encoded_path.as_bytes ()).decode_utf8 ().unwrap (); let path = Path::new (&*path_s); diff --git a/src/server/mod.rs b/src/server/mod.rs index 3238f5e..8f3acc0 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -15,14 +15,29 @@ use tokio::{ time::delay_for, }; -use crate::http_serde; +use crate::{ + http_serde, + prefix_match, +}; pub mod file_server; +struct ServerState { + opt: Opt, + handlebars: Handlebars <'static>, + client: Client, +} + +fn status_reply (c: http_serde::StatusCode, body: &str) -> http_serde::Response +{ + let mut r = http_serde::Response::default (); + r.status_code (c) + .body_bytes (body.as_bytes ().to_vec ()); + r +} + async fn handle_req_resp <'a> ( - opt: &'a Opt, - handlebars: Arc >, - client: Arc , + state: Arc , req_resp: reqwest::Response ) { //println! ("Step 1"); @@ -40,17 +55,26 @@ async fn handle_req_resp <'a> ( }; for wrapped_req in wrapped_reqs.into_iter () { - let handlebars = handlebars.clone (); - let opt = opt.clone (); - let client = client.clone (); + let state = state.clone (); tokio::spawn (async move { let (req_id, parts) = (wrapped_req.id, wrapped_req.req); - let response = file_server::serve_all (handlebars, &opt.file_server_root, parts).await; + let response = if let Some (uri) = prefix_match (&parts.uri, "/files") { + file_server::serve_all ( + &state.handlebars, + &state.opt.file_server_root, + parts.method, + uri, + &parts.headers + ).await + } + else { + status_reply (http_serde::StatusCode::NotFound, "404 Not Found") + }; - let mut resp_req = client - .post (&format! ("{}/7ZSFUKGV_http_response/{}", opt.relay_url, req_id)) + let mut resp_req = state.client + .post (&format! ("{}/7ZSFUKGV_http_response/{}", state.opt.relay_url, req_id)) .header (crate::PTTH_MAGIC_HEADER, base64::encode (rmp_serde::to_vec (&response.parts).unwrap ())); if let Some (body) = response.body { @@ -89,12 +113,16 @@ pub async fn main (config_file: ConfigFile, opt: Opt) let mut headers = reqwest::header::HeaderMap::new (); headers.insert ("X-ApiKey", config_file.api_key.try_into ().unwrap ()); - // TODO: (FN46S2M2) Combine these Arcs - let client = Arc::new (Client::builder () + let client = Client::builder () .default_headers (headers) - .build ().unwrap ()); - let opt = Arc::new (opt); - let handlebars = Arc::new (file_server::load_templates ()?); + .build ().unwrap (); + let handlebars = file_server::load_templates ()?; + + let state = Arc::new (ServerState { + opt, + handlebars, + client, + }); let mut backoff_delay = 0; @@ -103,7 +131,7 @@ pub async fn main (config_file: ConfigFile, opt: Opt) delay_for (Duration::from_millis (backoff_delay)).await; } - let req_req = client.get (&format! ("{}/7ZSFUKGV_http_listen/{}", opt.relay_url, config_file.name)); + let req_req = state.client.get (&format! ("{}/7ZSFUKGV_http_listen/{}", state.opt.relay_url, config_file.name)); let err_backoff_delay = std::cmp::min (30_000, backoff_delay * 2 + 500); @@ -129,12 +157,10 @@ pub async fn main (config_file: ConfigFile, opt: Opt) // Spawn another task for each request so we can // immediately listen for the next connection - let client = client.clone (); - let opt = opt.clone (); - let handlebars = handlebars.clone (); + let state = state.clone (); tokio::spawn (async move { - handle_req_resp (&opt, handlebars, client, req_resp).await; + handle_req_resp (state, req_resp).await; }); } } diff --git a/todo.md b/todo.md index f97d589..e09fdef 100644 --- a/todo.md +++ b/todo.md @@ -1,4 +1,4 @@ -- FN46S2M2 Combine Arcs +- Server-side hash? - Log / audit log? - Prevent directory traversal attacks