diff --git a/src/cgi.rs b/src/cgi.rs index e8b7dd8..f4175fa 100644 --- a/src/cgi.rs +++ b/src/cgi.rs @@ -50,7 +50,7 @@ fn envs(peer_addr: SocketAddr, srv: &config::ServerCfg, url: &url::Url) -> HashM } #[cfg(any(feature = "cgi", feature = "scgi"))] -fn check(byt: u8, peer_addr: SocketAddr, u: url::Url) -> bool { +fn check(byt: u8, peer_addr: SocketAddr, u: &url::Url) -> bool { match byt { 49 => { logger::logger(peer_addr, Status::Input, u.as_str()); @@ -69,13 +69,16 @@ fn check(byt: u8, peer_addr: SocketAddr, u: url::Url) -> bool { #[cfg(feature = "cgi")] pub async fn cgi( - mut con: conn::Connection, + con: &mut conn::Connection, srv: &config::ServerCfg, path: PathBuf, - url: url::Url, + url: &url::Url, + script_name: String, + path_info: String ) -> Result<(), io::Error> { let mut envs = envs(con.peer_addr, srv, &url); - envs.insert("SCRIPT_NAME".to_string(), path.file_name().unwrap().to_str().unwrap().to_string()); + envs.insert("SCRIPT_NAME".into(), script_name); + envs.insert("PATH_INFO".into(), path_info); match path.parent() { Some(p) => { @@ -88,7 +91,7 @@ pub async fn cgi( .env_clear() .envs(&envs) .output(); - + let cmd = match tokio::time::timeout(tokio::time::Duration::from_secs(5), cmd).await { Ok(c) => { match c { @@ -118,7 +121,7 @@ pub async fn cgi( con.send_status(Status::CGIError, None).await?; return Ok(()); } - + con.send_raw(cmd.as_bytes()).await?; return Ok(()); } @@ -146,7 +149,7 @@ pub async fn scgi(addr: String, u: url::Url, mut con: conn::Connection, srv: &co byt.push_str(&format!("{}\x00{}\x00", k, v)); } byt = byt.len().to_string() + ":" + &byt + ","; - + stream.write_all(byt.as_bytes()).await?; stream.flush().await?; @@ -158,7 +161,7 @@ pub async fn scgi(addr: String, u: url::Url, mut con: conn::Connection, srv: &co return Ok(()); } let req = String::from_utf8_lossy(&buf[..]); - if !check(req.as_bytes()[0], con.peer_addr, u) { + if !check(req.as_bytes()[0], con.peer_addr, &u) { con.send_status(Status::CGIError, None).await?; return Ok(()); } diff --git a/src/main.rs b/src/main.rs index 813cde7..6363147 100644 --- a/src/main.rs +++ b/src/main.rs @@ -105,6 +105,58 @@ async fn get_content(path: PathBuf, u: url::Url) -> Result { return Ok(list); } +// Handle CGI and return Ok(true), or indicate this request wasn't for CGI with Ok(false) +#[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) { + let mut path = full_path.clone(); + let mut segments = url.path_segments().unwrap(); + let mut path_info = "".to_string(); + + // Find an ancestor url that matches a file + while !path.exists() { + if let Some(segment) = segments.next_back() { + path.pop(); + path_info = format!("/{}{}", &segment, path_info); + } else { + return Ok(false); + } + } + let script_name = format!("/{}", segments.collect::>().join("/")); + + let meta = tokio::fs::metadata(&path).await?; + let perm = meta.permissions(); + + match &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?; + return Ok(true); + } else { + logger::logger(con.peer_addr, Status::CGIError, request); + con.send_status(Status::CGIError, None).await?; + return Ok(true); + } + } + }, + None => { + if meta.is_file() && perm.mode() & 0o0111 == 0o0111 { + cgi::cgi(con, srv, path, url, script_name, path_info).await?; + return Ok(true); + } + }, + } + } + Ok(false) +} + // TODO Rewrite this monster. async fn handle_connection( mut con: conn::Connection, @@ -237,6 +289,12 @@ 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? { + return Ok(()); + } + logger::logger(con.peer_addr, Status::NotFound, &request); con.send_status(Status::NotFound, None).await?; return Ok(()); @@ -272,28 +330,10 @@ async fn handle_connection( } #[cfg(feature = "cgi")] - if srv.server.cgi.unwrap_or(false) { - match &srv.server.cgipath { - Some(c) => { - if c.trim_end_matches("/") == path.parent().unwrap().to_str().unwrap() { - if perm.mode() & 0o0111 == 0o0111 { - cgi::cgi(con, srv, path, url).await?; - return Ok(()); - } else { - logger::logger(con.peer_addr, Status::CGIError, &request); - con.send_status(Status::CGIError, None).await?; - return Ok(()); - } - } - }, - None => { - if meta.is_file() && perm.mode() & 0o0111 == 0o0111 { - cgi::cgi(con, srv, path, url).await?; - return Ok(()); - } - }, - } + if handle_cgi(&mut con, srv, &request, &url, &path).await? { + return Ok(()); } + if meta.is_file() && perm.mode() & 0o0111 == 0o0111 { logger::logger(con.peer_addr, Status::NotFound, &request); con.send_status(Status::NotFound, None).await?;