169 lines
5.1 KiB
Rust
169 lines
5.1 KiB
Rust
#![cfg(any(feature = "cgi", feature = "scgi"))]
|
|
use std::collections::HashMap;
|
|
use std::io;
|
|
use std::net::SocketAddr;
|
|
|
|
#[cfg(feature = "cgi")]
|
|
use tokio::process::Command;
|
|
#[cfg(feature = "cgi")]
|
|
use std::path::PathBuf;
|
|
|
|
#[cfg(feature = "scgi")]
|
|
use std::net::ToSocketAddrs;
|
|
#[cfg(feature = "scgi")]
|
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|
#[cfg(feature = "scgi")]
|
|
use tokio::net::TcpStream;
|
|
|
|
use crate::config;
|
|
use crate::conn;
|
|
use crate::logger;
|
|
use crate::status::Status;
|
|
|
|
#[cfg(any(feature = "cgi", feature = "scgi"))]
|
|
fn envs(peer_addr: SocketAddr, srv: &config::ServerCfg, url: &url::Url) -> HashMap<String, String> {
|
|
let mut envs = HashMap::new();
|
|
envs.insert("GATEWAY_INTERFACE".to_string(), "CGI/1.1".to_string());
|
|
envs.insert("GEMINI_URL".to_string(), url.to_string());
|
|
envs.insert("SERVER_NAME".to_string(), url.host_str().unwrap().to_string());
|
|
envs.insert("SERVER_PROTOCOL".to_string(), "GEMINI".to_string());
|
|
let addr = peer_addr.ip().to_string();
|
|
envs.insert("REMOTE_ADDR".to_string(), addr.clone());
|
|
envs.insert("REMOTE_HOST".to_string(), addr);
|
|
let port = peer_addr.port().to_string();
|
|
envs.insert("REMOTE_PORT".to_string(), port);
|
|
envs.insert("SERVER_SOFTWARE".to_string(), env!("CARGO_PKG_NAME").to_string());
|
|
|
|
if let Some(q) = url.query() {
|
|
envs.insert("QUERY_STRING".to_string(), q.to_string());
|
|
}
|
|
|
|
match &srv.server.cgienv {
|
|
Some(c) => {
|
|
for (k, v) in c.iter() {
|
|
envs.insert(k.clone(), v.clone());
|
|
}
|
|
}
|
|
None => {}
|
|
}
|
|
envs
|
|
}
|
|
|
|
#[cfg(any(feature = "cgi", feature = "scgi"))]
|
|
fn check(byt: u8, peer_addr: SocketAddr, u: url::Url) -> bool {
|
|
match byt {
|
|
49 => {
|
|
logger::logger(peer_addr, Status::Input, u.as_str());
|
|
},
|
|
50 => {
|
|
logger::logger(peer_addr, Status::Success, u.as_str());
|
|
},
|
|
51..=54 => {}
|
|
_ => {
|
|
logger::logger(peer_addr, Status::CGIError, u.as_str());
|
|
return false;
|
|
},
|
|
}
|
|
true
|
|
}
|
|
|
|
#[cfg(feature = "cgi")]
|
|
pub async fn cgi(
|
|
mut con: conn::Connection,
|
|
srv: &config::ServerCfg,
|
|
path: PathBuf,
|
|
url: url::Url,
|
|
) -> 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());
|
|
|
|
match path.parent() {
|
|
Some(p) => {
|
|
std::env::set_current_dir(p)?;
|
|
},
|
|
None => {},
|
|
}
|
|
|
|
let cmd = Command::new(path.to_str().unwrap())
|
|
.env_clear()
|
|
.envs(&envs)
|
|
.output();
|
|
|
|
let cmd = match tokio::time::timeout(tokio::time::Duration::from_secs(5), cmd).await {
|
|
Ok(c) => {
|
|
match c {
|
|
Ok(cc) => cc,
|
|
|
|
Err(_) => {
|
|
logger::logger(con.peer_addr, Status::CGIError, url.as_str());
|
|
con.send_status(Status::CGIError, None).await?;
|
|
return Ok(());
|
|
},
|
|
}
|
|
},
|
|
Err(_) => {
|
|
logger::logger(con.peer_addr, Status::CGIError, url.as_str());
|
|
con.send_status(Status::CGIError, None).await?;
|
|
return Ok(());
|
|
},
|
|
};
|
|
|
|
if !cmd.status.success() {
|
|
logger::logger(con.peer_addr, Status::CGIError, url.as_str());
|
|
con.send_status(Status::CGIError, None).await?;
|
|
return Ok(());
|
|
}
|
|
let cmd = String::from_utf8(cmd.stdout).unwrap();
|
|
if !check(cmd.as_bytes()[0], con.peer_addr, url) {
|
|
con.send_status(Status::CGIError, None).await?;
|
|
return Ok(());
|
|
}
|
|
|
|
con.send_raw(cmd.as_bytes()).await?;
|
|
return Ok(());
|
|
}
|
|
|
|
#[cfg(feature = "scgi")]
|
|
pub async fn scgi(addr: String, u: url::Url, mut con: conn::Connection, srv: &config::ServerCfg) -> Result<(), io::Error> {
|
|
let addr = addr
|
|
.to_socket_addrs()?
|
|
.next()
|
|
.ok_or_else(|| io::Error::from(io::ErrorKind::AddrNotAvailable))?;
|
|
|
|
let mut stream = match TcpStream::connect(&addr).await {
|
|
Ok(s) => s,
|
|
Err(_) => {
|
|
logger::logger(con.peer_addr, Status::CGIError, u.as_str());
|
|
con.send_status(Status::CGIError, None).await?;
|
|
return Ok(());
|
|
}
|
|
};
|
|
let envs = envs(con.peer_addr, 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()));
|
|
for (k, v) in envs.iter() {
|
|
byt.push_str(&format!("{}\x00{}\x00", k, v));
|
|
}
|
|
byt = byt.len().to_string() + ":" + &byt + ",";
|
|
|
|
stream.write_all(byt.as_bytes()).await?;
|
|
stream.flush().await?;
|
|
|
|
let mut buf = vec![];
|
|
if let Err(_) = tokio::time::timeout(
|
|
tokio::time::Duration::from_secs(5), stream.read_to_end(&mut buf)).await {
|
|
logger::logger(con.peer_addr, Status::CGIError, u.as_str());
|
|
con.send_status(Status::CGIError, None).await?;
|
|
return Ok(());
|
|
}
|
|
let req = String::from_utf8_lossy(&buf[..]);
|
|
if !check(req.as_bytes()[0], con.peer_addr, u) {
|
|
con.send_status(Status::CGIError, None).await?;
|
|
return Ok(());
|
|
}
|
|
|
|
con.send_raw(req.as_bytes()).await?;
|
|
Ok(())
|
|
}
|