From 4fb31bec38be0cc0cf0780744c6f1b4036b1ff3f Mon Sep 17 00:00:00 2001 From: int 80h Date: Sun, 21 Nov 2021 15:04:26 -0500 Subject: [PATCH] Small amount of refactoring --- Cargo.lock | 18 ++++---- Cargo.toml | 4 +- src/cgi.rs | 7 ++- src/config.rs | 2 +- src/conn.rs | 1 + src/main.rs | 120 +++++++++++++++++++++++++++----------------------- 6 files changed, 80 insertions(+), 72 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1b51e9b..2dfa87f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,7 +140,7 @@ dependencies = [ [[package]] name = "gemserv" -version = "0.4.7" +version = "0.4.8" dependencies = [ "futures-util", "log", @@ -193,9 +193,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.107" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" +checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119" [[package]] name = "lock_api" @@ -326,9 +326,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.70" +version = "0.9.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6517987b3f8226b5da3661dad65ff7f300cc59fb5ea8333ca191fc65fde3edf" +checksum = "7df13d165e607909b363a4757a6f133f8a818a74e9d3a98d09c6128e15fa4c73" dependencies = [ "autocfg", "cc", @@ -521,9 +521,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "588b2d10a336da58d877567cd8fb8a14b463e2104910f8132cd054b4b96e29ee" +checksum = "70e992e41e0d2fb9f755b37446f20900f64446ef54874f40a60c78f021ac6144" dependencies = [ "autocfg", "bytes", @@ -541,9 +541,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "114383b041aa6212c579467afa0075fbbdd0718de036100bc0ba7961d8cb9095" +checksum = "c9efc1aba077437943f7515666aa2b882dfabfbfdf89c819ea75a8d6e9eaba5e" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 34d35f0..4203ccd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gemserv" -version = "0.4.7" +version = "0.4.8" authors = ["int 80h "] edition = "2018" description = "A gemini server" @@ -12,7 +12,7 @@ license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tokio = { version = "1.13.0", features = [ "full" ] } +tokio = { version = "1.14", features = [ "full" ] } openssl = "0.10.38" tokio-openssl = "0.6.3" futures-util = "0.3.17" diff --git a/src/cgi.rs b/src/cgi.rs index 1605d53..1977f31 100644 --- a/src/cgi.rs +++ b/src/cgi.rs @@ -89,7 +89,6 @@ fn check(byt: u8, peer_addr: SocketAddr, u: &url::Url) -> bool { #[cfg(feature = "cgi")] pub async fn cgi( con: &mut conn::Connection, - srv: &config::ServerCfg, path: PathBuf, url: &url::Url, script_name: String, @@ -97,7 +96,7 @@ pub async fn cgi( ) -> Result<(), io::Error> { let x509 = con.stream.ssl().peer_certificate(); - let mut envs = envs(con.peer_addr, x509, srv, &url); + let mut envs = envs(con.peer_addr, x509, &con.srv, &url); envs.insert("SCRIPT_NAME".into(), script_name); envs.insert("PATH_INFO".into(), path_info); @@ -148,7 +147,7 @@ pub async fn cgi( } #[cfg(feature = "scgi")] -pub async fn scgi(addr: String, u: url::Url, mut con: conn::Connection, srv: &config::ServerCfg) -> Result<(), io::Error> { +pub async fn scgi(addr: String, u: url::Url, mut con: conn::Connection) -> Result<(), io::Error> { let addr = addr .to_socket_addrs()? .next() @@ -163,7 +162,7 @@ pub async fn scgi(addr: String, u: url::Url, mut con: conn::Connection, srv: &co } }; let x509 = con.stream.ssl().peer_certificate(); - let envs = envs(con.peer_addr, x509, srv, &u); + let envs = envs(con.peer_addr, x509, &con.srv, &u); let len = 0usize; let mut byt = String::from(format!("CONTENT_LENGTH\x00{}\x00SCGI\x001\x00 RQUEST_METHOD\x00POST\x00REQUEST_URI\x00{}\x00", len, u.path())); diff --git a/src/config.rs b/src/config.rs index 29c70db..8b0ade2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -64,4 +64,4 @@ impl Config { } map } -} +} \ No newline at end of file diff --git a/src/conn.rs b/src/conn.rs index a6fa246..72dbfe0 100644 --- a/src/conn.rs +++ b/src/conn.rs @@ -12,6 +12,7 @@ use crate::status::Status; pub struct Connection { pub stream: SslStream, pub peer_addr: SocketAddr, + pub srv: crate::config::ServerCfg, } impl Connection { diff --git a/src/main.rs b/src/main.rs index 574a3c9..ee42fab 100644 --- a/src/main.rs +++ b/src/main.rs @@ -64,7 +64,7 @@ async fn get_binary(mut con: conn::Connection, path: PathBuf, meta: String) -> i Ok(()) } -async fn get_content(path: PathBuf, u: url::Url) -> Result { +async fn get_content(path: PathBuf, u: &url::Url) -> Result { let meta = tokio::fs::metadata(&path).await?; if meta.is_file() { return Ok(tokio::fs::read_to_string(path).await?); @@ -119,12 +119,11 @@ async fn get_content(path: PathBuf, u: url::Url) -> Result { #[cfg(feature = "cgi")] async fn handle_cgi( con: &mut conn::Connection, - srv: &config::ServerCfg, request: &str, url: &Url, full_path: &PathBuf, ) -> Result { - if srv.server.cgi.unwrap_or(false) { + if con.srv.server.cgi.unwrap_or(false) { let mut path = full_path.clone(); let mut segments = url.path_segments().unwrap(); let mut path_info = "".to_string(); @@ -143,11 +142,11 @@ async fn handle_cgi( let meta = tokio::fs::metadata(&path).await?; let perm = meta.permissions(); - match &srv.server.cgipath { + match &con.srv.server.cgipath { Some(c) => { if path.starts_with(c) { if perm.mode() & 0o0111 == 0o0111 { - cgi::cgi(con, srv, path, url, script_name, path_info).await?; + cgi::cgi(con, path, url, script_name, path_info).await?; return Ok(true); } else { logger::logger(con.peer_addr, Status::CGIError, request); @@ -158,7 +157,7 @@ async fn handle_cgi( }, None => { if meta.is_file() && perm.mode() & 0o0111 == 0o0111 { - cgi::cgi(con, srv, path, url, script_name, path_info).await?; + cgi::cgi(con, path, url, script_name, path_info).await?; return Ok(true); } }, @@ -167,30 +166,22 @@ async fn handle_cgi( Ok(false) } -// TODO Rewrite this monster. -async fn handle_connection( - mut con: conn::Connection, - srv: &config::ServerCfg, -) -> Result<(), io::Error> { - let index = match &srv.server.index { - Some(i) => i.clone(), - None => "index.gemini".to_string(), - }; +async fn get_request(mut con: conn::Connection) -> Result<(conn::Connection, url::Url), String> { let mut buffer = [0; 1024]; let len = match tokio::time::timeout(tokio::time::Duration::from_secs(5), con.stream.read(&mut buffer)).await { Ok(result) => result.unwrap(), - Err(_) => { + Err(e) => { logger::logger(con.peer_addr, Status::BadRequest, ""); - con.send_status(Status::BadRequest, None).await?; - return Ok(()); + con.send_status(Status::BadRequest, None).await.map_err(|e| e.to_string())?; + return Err(e.to_string()); } }; let mut request = match String::from_utf8(buffer[..len].to_vec()) { Ok(request) => request, - Err(_) => { + Err(e) => { logger::logger(con.peer_addr, Status::BadRequest, ""); - con.send_status(Status::BadRequest, None).await?; - return Ok(()); + con.send_status(Status::BadRequest, None).await.map_err(|e| e.to_string())?; + return Err(e.to_string()); } }; if request.starts_with("//") { @@ -208,35 +199,48 @@ async fn handle_connection( Ok(url) => url, Err(_) => { logger::logger(con.peer_addr, Status::BadRequest, &request); - con.send_status(Status::BadRequest, None).await?; - return Ok(()); + con.send_status(Status::BadRequest, None).await.map_err(|e| e.to_string())?; + return Err("Bad url".to_string()); } }; - if Some(srv.server.hostname.as_str()) != url.host_str() { - logger::logger(con.peer_addr, Status::ProxyRequestRefused, &request); - con.send_status(Status::ProxyRequestRefused, None).await?; - return Ok(()); + if Some(con.srv.server.hostname.as_str()) != url.host_str() { + logger::logger(con.peer_addr, Status::ProxyRequestRefused, &url.as_str()); + con.send_status(Status::ProxyRequestRefused, None).await.map_err(|e| e.to_string())?; + return Err("Wrong host".to_string()); } match url.port() { Some(p) => { - if p != srv.port { - logger::logger(con.peer_addr, Status::ProxyRequestRefused, &request); + if p != con.srv.port { + logger::logger(con.peer_addr, Status::ProxyRequestRefused, &url.as_str()); con.send_status(status::Status::ProxyRequestRefused, None) - .await?; + .await.map_err(|e| e.to_string())?; } } None => {} } if url.scheme() != "gemini" { - logger::logger(con.peer_addr, Status::ProxyRequestRefused, &request); - con.send_status(Status::ProxyRequestRefused, None).await?; - return Ok(()); + logger::logger(con.peer_addr, Status::ProxyRequestRefused, &url.as_str()); + con.send_status(Status::ProxyRequestRefused, None).await.map_err(|e| e.to_string())?; + return Err("scheme not gemini".to_string()); } + + return Ok((con, url)) +} - match &srv.server.redirect { +// TODO Rewrite this monster. +async fn handle_connection( + mut con: conn::Connection, + url: url::Url +) -> Result<(), io::Error> { + let index = match &con.srv.server.index { + Some(i) => i.clone(), + None => "index.gemini".to_string(), + }; + + match &con.srv.server.redirect.to_owned() { Some(re) => { let u = match url.path() { "/" => "/", @@ -244,7 +248,7 @@ async fn handle_connection( }; match re.get(u) { Some(r) => { - logger::logger(con.peer_addr, Status::RedirectTemporary, &request); + logger::logger(con.peer_addr, Status::RedirectTemporary, &url.as_str()); con.send_status(Status::RedirectTemporary, Some(r)).await?; return Ok(()); } @@ -255,7 +259,7 @@ async fn handle_connection( } #[cfg(feature = "proxy")] - if let Some(pr) = &srv.server.proxy_all { + if let Some(pr) = con.srv.server.proxy_all.to_owned() { let host_port: Vec<&str> = pr.splitn(2, ':').collect(); let host = host_port[0]; let port: Option; @@ -269,12 +273,12 @@ async fn handle_connection( upstream_url.set_host(Some(host)).unwrap(); upstream_url.set_port(port).unwrap(); - revproxy::proxy_all(pr, upstream_url, con).await?; + revproxy::proxy_all(pr.as_str(), upstream_url, con).await?; return Ok(()); } #[cfg(feature = "proxy")] - match &srv.server.proxy { + match &con.srv.server.proxy { Some(pr) => match url.path_segments().map(|c| c.collect::>()) { Some(s) => match pr.get(s[0]) { Some(p) => { @@ -289,7 +293,7 @@ async fn handle_connection( } #[cfg(feature = "scgi")] - match &srv.server.scgi { + match &con.srv.server.scgi { Some(sc) => { let u = match url.path() { "/" => "/", @@ -297,7 +301,7 @@ async fn handle_connection( }; match sc.get(u) { Some(r) => { - cgi::scgi(r.to_string(), url, con, srv).await?; + cgi::scgi(r.to_string(), url, con).await?; return Ok(()); } None => {} @@ -309,7 +313,7 @@ async fn handle_connection( let mut path = PathBuf::new(); - if url.path().starts_with("/~") && srv.server.usrdir.unwrap_or(false) { + if url.path().starts_with("/~") && con.srv.server.usrdir.unwrap_or(false) { let usr = url.path().trim_start_matches("/~"); let usr: Vec<&str> = usr.splitn(2, "/").collect(); if cfg!(target_os = "macos") { @@ -323,7 +327,7 @@ async fn handle_connection( path.push(format!("{}/{}/", usr[0], "public_gemini")); } } else { - path.push(&srv.server.dir); + path.push(&con.srv.server.dir); if url.path() != "" || url.path() != "/" { let decoded = util::url_decode(url.path().trim_start_matches("/").as_bytes()); path.push(decoded); @@ -333,11 +337,11 @@ async fn handle_connection( if !path.exists() { // See if it's a subpath of a CGI script before returning NotFound #[cfg(feature = "cgi")] - if handle_cgi(&mut con, srv, &request, &url, &path).await? { + if handle_cgi(&mut con, &url.as_str(), &url, &path).await? { return Ok(()); } - logger::logger(con.peer_addr, Status::NotFound, &request); + logger::logger(con.peer_addr, Status::NotFound, &url.as_str()); con.send_status(Status::NotFound, None).await?; return Ok(()); } @@ -349,7 +353,7 @@ async fn handle_connection( // This block is terrible if meta.is_dir() { if !url.path().ends_with("/") { - logger::logger(con.peer_addr, Status::RedirectPermanent, &request); + logger::logger(con.peer_addr, Status::RedirectPermanent, &url.as_str()); con.send_status( Status::RedirectPermanent, Some(format!("{}/", url).as_str()), @@ -372,35 +376,35 @@ async fn handle_connection( } #[cfg(feature = "cgi")] - if handle_cgi(&mut con, srv, &request, &url, &path).await? { + if handle_cgi(&mut con, &url.as_str(), &url, &path).await? { return Ok(()); } if meta.is_file() && perm.mode() & 0o0111 == 0o0111 { - logger::logger(con.peer_addr, Status::NotFound, &request); + logger::logger(con.peer_addr, Status::NotFound, &url.as_str()); con.send_status(Status::NotFound, None).await?; return Ok(()); } if perm.mode() & 0o0444 != 0o0444 { - logger::logger(con.peer_addr, Status::NotFound, &request); + logger::logger(con.peer_addr, Status::NotFound, &url.as_str()); con.send_status(Status::NotFound, None).await?; return Ok(()); } let mut mime = get_mime(&path); - if mime == "text/gemini" && srv.server.lang.is_some() { - mime += &("; lang=".to_string() + &srv.server.lang.to_owned().unwrap()); + if mime == "text/gemini" && con.srv.server.lang.is_some() { + mime += &("; lang=".to_string() + &con.srv.server.lang.to_owned().unwrap()); } if !mime.starts_with("text/") { - logger::logger(con.peer_addr, Status::Success, &request); + logger::logger(con.peer_addr, Status::Success, &url.as_str()); get_binary(con, path, mime).await?; return Ok(()); } - let content = get_content(path, url).await?; + let content = get_content(path, &url).await?; con.send_body(status::Status::Success, Some(&mime), Some(content)) .await?; - logger::logger(con.peer_addr, Status::Success, &request); + logger::logger(con.peer_addr, Status::Success, &url.as_str()); Ok(()) } @@ -484,10 +488,14 @@ fn main() -> io::Result<()> { None => cmap.get(&default).unwrap(), }, None => cmap.get(&default).unwrap(), - }; + }.to_owned(); - let con = conn::Connection { stream, peer_addr }; - handle_connection(con, srv).await?; + let con = conn::Connection { stream, peer_addr, srv }; + let (con, url) = match get_request(con).await { + Ok((c, u)) => (c, u), + Err(_) => return Ok(()) as io::Result<()>, + }; + handle_connection(con, url).await?; Ok(()) as io::Result<()> }; -- 2.34.2