diff --git a/.build.yml b/.build.yml index 4f9d7c5..5a000ee 100644 --- a/.build.yml +++ b/.build.yml @@ -2,10 +2,9 @@ image: alpine/edge packages: - rust - cargo - - libressl-dev sources: - https://git.sr.ht/~int80h/gemserv tasks: - build: | cd gemserv/ - cargo build --release \ No newline at end of file + cargo build diff --git a/Cargo.lock b/Cargo.lock index f2bf774..f8fe1c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -272,9 +272,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.108" +version = "0.2.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119" +checksum = "f98a04dce437184842841303488f70d0188c5f51437d2a834dc097eafa909a01" [[package]] name = "lock_api" diff --git a/src/cgi.rs b/src/cgi.rs index 15407eb..c911dcd 100644 --- a/src/cgi.rs +++ b/src/cgi.rs @@ -54,8 +54,7 @@ fn envs( if let Some(cert) = session.peer_certificates() { let cert = tokio_rustls::rustls::Certificate::as_ref(&cert[0]); - match x509_parser::parse_x509_certificate(cert) { - Ok((_, x509)) => { + if let Ok((_, x509)) = x509_parser::parse_x509_certificate(cert) { let user = x509 .subject() .iter_common_name() @@ -65,9 +64,7 @@ fn envs( envs.insert("AUTH_TYPE".to_string(), "Certificate".to_string()); envs.insert("REMOTE_USER".to_string(), user.to_string()); - envs.insert("TLS_CLIENT_HASH".to_string(), util::fingerhex(&cert)); - } - Err(_) => {} + envs.insert("TLS_CLIENT_HASH".to_string(), util::fingerhex(cert)); } } @@ -109,15 +106,12 @@ pub async fn cgi( path_info: String, ) -> Result<(), io::Error> { let (_, session) = con.stream.get_ref(); - let mut envs = envs(con.peer_addr, session, &con.srv, &url); + let mut envs = envs(con.peer_addr, session, &con.srv, url); envs.insert("SCRIPT_NAME".into(), script_name); envs.insert("PATH_INFO".into(), path_info); - match path.parent() { - Some(p) => { + if let Some(p) = path.parent() { std::env::set_current_dir(p)?; - } - None => {} } let cmd = Command::new(path.to_str().unwrap()) @@ -154,7 +148,7 @@ pub async fn cgi( } con.send_raw(&cmd).await?; - return Ok(()); + Ok(()) } #[cfg(feature = "scgi")] @@ -175,12 +169,12 @@ pub async fn scgi(addr: String, u: url::Url, mut con: conn::Connection) -> Resul let (_, session) = con.stream.get_ref(); let envs = envs(con.peer_addr, session, &con.srv, &u); let len = 0usize; - let mut byt = String::from(format!( + let mut byt = format!( "CONTENT_LENGTH\x00{}\x00SCGI\x001\x00 RQUEST_METHOD\x00POST\x00REQUEST_URI\x00{}\x00", len, u.path() - )); + ); for (k, v) in envs.iter() { byt.push_str(&format!("{}\x00{}\x00", k, v)); } diff --git a/src/con_handler.rs b/src/con_handler.rs index c692f18..ac2dfbb 100644 --- a/src/con_handler.rs +++ b/src/con_handler.rs @@ -1,9 +1,8 @@ -use new_mime_guess; use std::fs; use std::fs::File; use std::io::{self, BufRead, BufReader}; use std::os::unix::fs::PermissionsExt; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use url::Url; #[cfg(any(feature = "cgi", feature = "scgi"))] @@ -17,7 +16,7 @@ use crate::util; type Result = std::result::Result>; -fn get_mime(path: &PathBuf) -> String { +fn get_mime(path: &Path) -> String { let mut mime = "text/gemini".to_string(); if path.is_dir() { return mime; @@ -32,7 +31,7 @@ fn get_mime(path: &PathBuf) -> String { None => "text/plain".to_string(), }; - return mime; + mime } async fn get_binary(mut con: conn::Connection, path: PathBuf, meta: String) -> io::Result<()> { @@ -63,29 +62,27 @@ async fn get_content(path: PathBuf, u: &url::Url) -> Result { let mut files: Vec = Vec::new(); // needs work - for file in fs::read_dir(&path)? { - if let Ok(file) = file { - let m = file.metadata()?; - let perm = m.permissions(); - if perm.mode() & 0o0444 != 0o0444 { - continue; - } - let file = file.path(); - let p = file.strip_prefix(&path).unwrap(); - let ps = match p.to_str() { - Some(s) => s, - None => continue, - }; - let ep = match u.join(ps) { - Ok(p) => p, - _ => continue, - }; - if m.is_dir() { - dirs.push(format!("=> {}/ {}/\r\n", ep, p.display())); - } else { - files.push(format!("=> {} {}\r\n", ep, p.display())); - } + for file in (fs::read_dir(&path)?).flatten() { + let m = file.metadata()?; + let perm = m.permissions(); + if perm.mode() & 0o0444 != 0o0444 { + continue; } + let file = file.path(); + let p = file.strip_prefix(&path).unwrap(); + let ps = match p.to_str() { + Some(s) => s, + None => continue, + }; + let ep = match u.join(ps) { + Ok(p) => p, + _ => continue, + }; + if m.is_dir() { + dirs.push(format!("=> {}/ {}/\r\n", ep, p.display())); + } else { + files.push(format!("=> {} {}\r\n", ep, p.display())); + } } dirs.sort(); @@ -101,7 +98,7 @@ async fn get_content(path: PathBuf, u: &url::Url) -> Result { list.push_str(&file); } - return Ok(list); + Ok(list) } // Handle CGI and return Ok(true), or indicate this request wasn't for CGI with Ok(false) @@ -166,15 +163,12 @@ pub async fn handle_connection(mut con: conn::Connection, url: url::Url) -> Resu Some(re) => { let u = match url.path() { "/" => "/", - _ => url.path().trim_end_matches("/"), + _ => url.path().trim_end_matches('/'), }; - match re.get(u) { - Some(r) => { - logger::logger(con.peer_addr, Status::RedirectTemporary, &url.as_str()); + if let Some(r) = re.get(u) { + logger::logger(con.peer_addr, Status::RedirectTemporary, url.as_str()); con.send_status(Status::RedirectTemporary, Some(r)).await?; return Ok(()); - } - None => {} } } None => {} @@ -202,12 +196,9 @@ pub async fn handle_connection(mut con: conn::Connection, url: url::Url) -> Resu #[cfg(feature = "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) => { + Some(s) => if let Some(p) = pr.get(s[0]) { revproxy::proxy(p.to_string(), url, con).await?; return Ok(()); - } - None => {} }, None => {} }, @@ -219,14 +210,11 @@ pub async fn handle_connection(mut con: conn::Connection, url: url::Url) -> Resu Some(sc) => { let u = match url.path() { "/" => "/", - _ => url.path().trim_end_matches("/"), + _ => url.path().trim_end_matches('/'), }; - match sc.get(u) { - Some(r) => { + if let Some(r) = sc.get(u) { cgi::scgi(r.to_string(), url, con).await?; return Ok(()); - } - None => {} } } None => {} @@ -236,7 +224,7 @@ pub async fn handle_connection(mut con: conn::Connection, url: url::Url) -> Resu 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(); + let usr: Vec<&str> = usr.splitn(2, '/').collect(); if cfg!(target_os = "macos") { path.push("/Users/"); } else { @@ -255,7 +243,7 @@ pub async fn handle_connection(mut con: conn::Connection, url: url::Url) -> Resu } else { path.push(&con.srv.server.dir); if url.path() != "" || url.path() != "/" { - let decoded = util::url_decode(url.path().trim_start_matches("/").as_bytes()); + let decoded = util::url_decode(url.path().trim_start_matches('/').as_bytes()); path.push(decoded); } } @@ -263,11 +251,11 @@ pub async fn handle_connection(mut con: conn::Connection, url: url::Url) -> Resu if !path.exists() { // See if it's a subpath of a CGI script before returning NotFound #[cfg(feature = "cgi")] - if handle_cgi(&mut con, &url.as_str(), &url, &path).await? { + if handle_cgi(&mut con, url.as_str(), &url, &path).await? { return Ok(()); } - logger::logger(con.peer_addr, Status::NotFound, &url.as_str()); + logger::logger(con.peer_addr, Status::NotFound, url.as_str()); con.send_status(Status::NotFound, None).await?; return Ok(()); } @@ -278,8 +266,8 @@ pub async fn handle_connection(mut con: conn::Connection, url: url::Url) -> Resu // TODO fix me // This block is terrible if meta.is_dir() { - if !url.path().ends_with("/") { - logger::logger(con.peer_addr, Status::RedirectPermanent, &url.as_str()); + if !url.path().ends_with('/') { + logger::logger(con.peer_addr, Status::RedirectPermanent, url.as_str()); con.send_status( Status::RedirectPermanent, Some(format!("{}/", url).as_str()), @@ -302,18 +290,18 @@ pub async fn handle_connection(mut con: conn::Connection, url: url::Url) -> Resu } #[cfg(feature = "cgi")] - if handle_cgi(&mut con, &url.as_str(), &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, &url.as_str()); + 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, &url.as_str()); + logger::logger(con.peer_addr, Status::NotFound, url.as_str()); con.send_status(Status::NotFound, None).await?; return Ok(()); } @@ -323,14 +311,14 @@ pub async fn handle_connection(mut con: conn::Connection, url: url::Url) -> Resu mime += &("; lang=".to_string() + &con.srv.server.lang.to_owned().unwrap()); } if !mime.starts_with("text/") { - logger::logger(con.peer_addr, Status::Success, &url.as_str()); + 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?; con.send_body(Status::Success, Some(&mime), Some(content)) .await?; - logger::logger(con.peer_addr, Status::Success, &url.as_str()); + logger::logger(con.peer_addr, Status::Success, url.as_str()); Ok(()) } diff --git a/src/config.rs b/src/config.rs index 7f83119..3ccb1ad 100644 --- a/src/config.rs +++ b/src/config.rs @@ -90,9 +90,9 @@ impl Config { } else if config.interface.is_some() { return Ok(config); } - return Err(Box::new(errors::GemError( + Err(Box::new(errors::GemError( "You need to specify either host/port or interface".into(), - ))); + ))) } pub fn to_map(&self) -> HashMap { let mut map = HashMap::new(); diff --git a/src/lib/conn.rs b/src/lib/conn.rs index 3ea90b0..39aa88c 100644 --- a/src/lib/conn.rs +++ b/src/lib/conn.rs @@ -30,7 +30,7 @@ impl Connection { ) -> Result<(), io::Error> { let meta = match meta { Some(m) => m, - None => &stat.to_str(), + None => stat.to_str(), }; self.send_raw(format!("{} {}\r\n", stat as u8, meta).as_bytes()) .await?; diff --git a/src/lib/server.rs b/src/lib/server.rs index d25de77..00a3121 100644 --- a/src/lib/server.rs +++ b/src/lib/server.rs @@ -169,9 +169,9 @@ pub async fn get_request(mut con: conn::Connection) -> Result<(conn::Connection, request = request.replacen("//", "gemini://", 1); } - if request.ends_with("\n") { + if request.ends_with('\n') { request.pop(); - if request.ends_with("\r") { + if request.ends_with('\r') { request.pop(); } } @@ -187,36 +187,30 @@ pub async fn get_request(mut con: conn::Connection) -> Result<(conn::Connection, } }; - match url.host_str() { - Some(h) => { - if con.srv.server.hostname.as_str() != h.to_lowercase() { - 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(Box::new(GemError("Wrong host".into()))); - } + if let Some(h) = url.host_str() { + if con.srv.server.hostname.as_str() != h.to_lowercase() { + 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(Box::new(GemError("Wrong host".into()))); } - None => {} } - match url.port() { - Some(p) => { - if p != con.local_addr.port() { - logger::logger(con.peer_addr, Status::ProxyRequestRefused, &url.as_str()); - con.send_status(Status::ProxyRequestRefused, None) - .await - .map_err(|e| e.to_string())?; - } + if let Some(p) = url.port() { + if p != con.local_addr.port() { + logger::logger(con.peer_addr, Status::ProxyRequestRefused, url.as_str()); + con.send_status(Status::ProxyRequestRefused, None) + .await + .map_err(|e| e.to_string())?; } - None => {} } if url.scheme() != "gemini" { - logger::logger(con.peer_addr, Status::ProxyRequestRefused, &url.as_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(Box::new(GemError("scheme not gemini".into()))); } - return Ok((con, url)); + Ok((con, url)) } diff --git a/src/lib/status.rs b/src/lib/status.rs index 9315c33..3a8a03c 100644 --- a/src/lib/status.rs +++ b/src/lib/status.rs @@ -29,7 +29,7 @@ pub enum Status { impl Status { pub fn to_str(&self) -> &str { - let meta = match self { + match self { Status::Input => "Input", Status::Success => "Success", Status::SuccessEndOfSession => "Success End Of Session", @@ -51,8 +51,7 @@ impl Status { Status::CertificateNotAccepted => "Certificate Not Accepted", Status::FutureCertificateRejected => "Future Certificate Rejected", Status::ExpiredCertificateRejected => "Expired Certificate Rejected", - }; - return meta; + } } } diff --git a/src/lib/tls.rs b/src/lib/tls.rs index 6df53a3..1108d59 100644 --- a/src/lib/tls.rs +++ b/src/lib/tls.rs @@ -27,13 +27,13 @@ pub fn tls_acceptor_conf(cfg: config::Config) -> io::Result { Ok(acceptor) } -pub fn load_certs(path: &String) -> io::Result> { +pub fn load_certs(path: &str) -> io::Result> { certs(&mut BufReader::new(File::open(path)?)) .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid cert")) .map(|mut certs| certs.drain(..).map(Certificate).collect()) } -fn load_key(path: &String) -> io::Result> { +fn load_key(path: &str) -> io::Result> { pkcs8_private_keys(&mut std::io::BufReader::new(std::fs::File::open(path)?)) .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid key")) .map(|mut keys| keys.drain(..).map(PrivateKey).collect()) diff --git a/src/lib/util.rs b/src/lib/util.rs index fd1463d..fa21575 100644 --- a/src/lib/util.rs +++ b/src/lib/util.rs @@ -5,8 +5,9 @@ pub fn url_decode(url: &[u8]) -> String { let decoded: String = form_urlencoded::parse(url) .map(|(key, val)| [key, val].concat()) .collect(); - return decoded; + decoded } + pub fn fingerhex(x509: &[u8]) -> String { let mut finger = sha2::Sha256::new(); finger.update(&x509); diff --git a/src/logger.rs b/src/logger.rs index f8cb652..e1578f3 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -18,7 +18,7 @@ pub fn init(loglev: &Option) -> errors::Result { }, }; simple_logger::init_with_level(loglev).unwrap(); - return Ok(()); + Ok(()) } pub fn logger(addr: SocketAddr, stat: status::Status, req: &str) { diff --git a/src/main.rs b/src/main.rs index da82285..ece75c0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -73,5 +73,5 @@ async fn main() -> errors::Result { { return Err(e); }; - return Ok(()); + Ok(()) } diff --git a/src/revproxy.rs b/src/revproxy.rs index 5396cd8..4e44515 100644 --- a/src/revproxy.rs +++ b/src/revproxy.rs @@ -15,13 +15,13 @@ use crate::status::Status; use crate::tls; pub async fn proxy(addr: String, u: url::Url, mut con: conn::Connection) -> Result<(), io::Error> { - let p: Vec<&str> = u.path().trim_start_matches("/").splitn(2, "/").collect(); + let p: Vec<&str> = u.path().trim_start_matches('/').splitn(2, '/').collect(); if p.len() == 1 { logger::logger(con.peer_addr, Status::NotFound, u.as_str()); con.send_status(Status::NotFound, None).await?; return Ok(()); } - if p[1] == "" || p[1] == "/" { + if p[1].is_empty() || p[1] == "/" { logger::logger(con.peer_addr, Status::NotFound, u.as_str()); con.send_status(Status::NotFound, None).await?; return Ok(());