Switched from openssl to Rustls
This commit is contained in:
103
src/cgi.rs
103
src/cgi.rs
@@ -3,10 +3,12 @@ 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 = "cgi")]
|
||||
use tokio::process::Command;
|
||||
|
||||
use tokio_rustls::rustls::ServerConnection;
|
||||
|
||||
#[cfg(feature = "scgi")]
|
||||
use std::net::ToSocketAddrs;
|
||||
@@ -22,39 +24,51 @@ use crate::status::Status;
|
||||
use crate::util;
|
||||
|
||||
#[cfg(any(feature = "cgi", feature = "scgi"))]
|
||||
fn envs(peer_addr: SocketAddr, x509: Option<openssl::x509::X509>, srv: &config::ServerCfg, url: &url::Url) -> HashMap<String, String> {
|
||||
fn envs(
|
||||
peer_addr: SocketAddr,
|
||||
session: &ServerConnection,
|
||||
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_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());
|
||||
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 x509 {
|
||||
Some(x) => {
|
||||
envs.insert("AUTH_TYPE".to_string(), "Certificate".to_string());
|
||||
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)) => {
|
||||
let user = x509
|
||||
.subject()
|
||||
.iter_common_name()
|
||||
.next()
|
||||
.and_then(|cn| cn.as_str().ok())
|
||||
.unwrap();
|
||||
|
||||
let cn = x.subject_name().entries_by_nid(openssl::nid::Nid::COMMONNAME);
|
||||
for c in cn {
|
||||
let cd = match c.data().as_utf8() {
|
||||
Ok(n) => n.to_string(),
|
||||
_ => "".to_string(),
|
||||
};
|
||||
envs.insert("REMOTE_USER".to_string(), cd);
|
||||
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));
|
||||
}
|
||||
|
||||
envs.insert("TLS_CLIENT_HASH".to_string(), util::fingerhex(&x));
|
||||
},
|
||||
None => {},
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
match &srv.server.cgienv {
|
||||
@@ -73,15 +87,15 @@ 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
|
||||
}
|
||||
@@ -92,19 +106,18 @@ pub async fn cgi(
|
||||
path: PathBuf,
|
||||
url: &url::Url,
|
||||
script_name: String,
|
||||
path_info: String
|
||||
path_info: String,
|
||||
) -> Result<(), io::Error> {
|
||||
|
||||
let x509 = con.stream.ssl().peer_certificate();
|
||||
let mut envs = envs(con.peer_addr, x509, &con.srv, &url);
|
||||
let (_, session) = con.stream.get_ref();
|
||||
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) => {
|
||||
std::env::set_current_dir(p)?;
|
||||
},
|
||||
None => {},
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
let cmd = Command::new(path.to_str().unwrap())
|
||||
@@ -113,22 +126,20 @@ pub async fn cgi(
|
||||
.output();
|
||||
|
||||
let cmd = match tokio::time::timeout(tokio::time::Duration::from_secs(5), cmd).await {
|
||||
Ok(c) => {
|
||||
match c {
|
||||
Ok(cc) => cc,
|
||||
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(());
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
logger::logger(con.peer_addr, Status::CGIError, url.as_str());
|
||||
con.send_status(Status::CGIError, None).await?;
|
||||
return Ok(());
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
if !cmd.status.success() {
|
||||
@@ -161,11 +172,15 @@ pub async fn scgi(addr: String, u: url::Url, mut con: conn::Connection) -> Resul
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let x509 = con.stream.ssl().peer_certificate();
|
||||
let envs = envs(con.peer_addr, x509, &con.srv, &u);
|
||||
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!("CONTENT_LENGTH\x00{}\x00SCGI\x001\x00
|
||||
RQUEST_METHOD\x00POST\x00REQUEST_URI\x00{}\x00", len, u.path()));
|
||||
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));
|
||||
}
|
||||
@@ -176,7 +191,11 @@ pub async fn scgi(addr: String, u: url::Url, mut con: conn::Connection) -> Resul
|
||||
|
||||
let mut buf = vec![];
|
||||
if let Err(_) = tokio::time::timeout(
|
||||
tokio::time::Duration::from_secs(5), stream.read_to_end(&mut buf)).await {
|
||||
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(());
|
||||
|
||||
@@ -6,14 +6,16 @@ use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::PathBuf;
|
||||
use url::Url;
|
||||
|
||||
use crate::util;
|
||||
use crate::conn;
|
||||
use crate::status::Status;
|
||||
#[cfg(any(feature = "cgi", feature = "scgi"))]
|
||||
use crate::cgi;
|
||||
use crate::conn;
|
||||
use crate::logger;
|
||||
#[cfg(feature = "proxy")]
|
||||
use crate::revproxy;
|
||||
use crate::status::Status;
|
||||
use crate::util;
|
||||
|
||||
type Result<T=()> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||
type Result<T = ()> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||
|
||||
fn get_mime(path: &PathBuf) -> String {
|
||||
let mut mime = "text/gemini".to_string();
|
||||
@@ -36,8 +38,7 @@ fn get_mime(path: &PathBuf) -> String {
|
||||
async fn get_binary(mut con: conn::Connection, path: PathBuf, meta: String) -> io::Result<()> {
|
||||
let fd = File::open(path)?;
|
||||
let mut reader = BufReader::with_capacity(1024 * 1024, fd);
|
||||
con.send_status(Status::Success, Some(&meta))
|
||||
.await?;
|
||||
con.send_status(Status::Success, Some(&meta)).await?;
|
||||
loop {
|
||||
let len = {
|
||||
let buf = reader.fill_buf()?;
|
||||
@@ -132,33 +133,30 @@ async fn handle_cgi(
|
||||
|
||||
match &con.srv.server.cgipath {
|
||||
Some(c) => {
|
||||
if path.starts_with(c) {
|
||||
if perm.mode() & 0o0111 == 0o0111 {
|
||||
cgi::cgi(con, 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);
|
||||
if path.starts_with(c) {
|
||||
if perm.mode() & 0o0111 == 0o0111 {
|
||||
cgi::cgi(con, 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, path, url, script_name, path_info).await?;
|
||||
return Ok(true);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
// TODO Rewrite this monster.
|
||||
pub async fn handle_connection(
|
||||
mut con: conn::Connection,
|
||||
url: url::Url
|
||||
) -> Result {
|
||||
pub async fn handle_connection(mut con: conn::Connection, url: url::Url) -> Result {
|
||||
let index = match &con.srv.server.index {
|
||||
Some(i) => i.clone(),
|
||||
None => "index.gemini".to_string(),
|
||||
@@ -223,18 +221,17 @@ pub async fn handle_connection(
|
||||
"/" => "/",
|
||||
_ => url.path().trim_end_matches("/"),
|
||||
};
|
||||
match sc.get(u) {
|
||||
Some(r) => {
|
||||
cgi::scgi(r.to_string(), url, con).await?;
|
||||
return Ok(());
|
||||
match sc.get(u) {
|
||||
Some(r) => {
|
||||
cgi::scgi(r.to_string(), url, con).await?;
|
||||
return Ok(());
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
},
|
||||
None => {},
|
||||
None => {}
|
||||
}
|
||||
|
||||
|
||||
let mut path = PathBuf::new();
|
||||
|
||||
if url.path().starts_with("/~") && con.srv.server.usrdir.unwrap_or(false) {
|
||||
@@ -246,7 +243,12 @@ pub async fn handle_connection(
|
||||
path.push("/home/");
|
||||
}
|
||||
if usr.len() == 2 {
|
||||
path.push(format!("{}/{}/{}", usr[0], "public_gemini", util::url_decode(usr[1].as_bytes())));
|
||||
path.push(format!(
|
||||
"{}/{}/{}",
|
||||
usr[0],
|
||||
"public_gemini",
|
||||
util::url_decode(usr[1].as_bytes())
|
||||
));
|
||||
} else {
|
||||
path.push(format!("{}/{}/", usr[0], "public_gemini"));
|
||||
}
|
||||
@@ -304,13 +306,13 @@ pub async fn handle_connection(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if meta.is_file() && perm.mode() & 0o0111 == 0o0111 {
|
||||
if meta.is_file() && perm.mode() & 0o0111 == 0o0111 {
|
||||
logger::logger(con.peer_addr, Status::NotFound, &url.as_str());
|
||||
con.send_status(Status::NotFound, None).await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if perm.mode() & 0o0444 != 0o0444 {
|
||||
if perm.mode() & 0o0444 != 0o0444 {
|
||||
logger::logger(con.peer_addr, Status::NotFound, &url.as_str());
|
||||
con.send_status(Status::NotFound, None).await?;
|
||||
return Ok(());
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
extern crate serde_derive;
|
||||
extern crate toml;
|
||||
use tokio::fs;
|
||||
use std::collections::HashMap;
|
||||
use std::path;
|
||||
use std::env;
|
||||
use crate::lib::errors;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::path;
|
||||
use tokio::fs;
|
||||
|
||||
type Result<T=()> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||
type Result<T = ()> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct Config {
|
||||
@@ -43,7 +43,7 @@ pub struct Server {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ServerCfg {
|
||||
// pub port: u16,
|
||||
// pub port: u16,
|
||||
pub server: Server,
|
||||
}
|
||||
|
||||
@@ -56,7 +56,9 @@ impl Config {
|
||||
if !p.exists() {
|
||||
return Err(Box::new(errors::GemError(
|
||||
"Please run with the path to the config file. \
|
||||
Or create the config as /usr/local/etc/gemserv.conf".to_string())));
|
||||
Or create the config as /usr/local/etc/gemserv.conf"
|
||||
.to_string(),
|
||||
)));
|
||||
}
|
||||
} else {
|
||||
p.push(&args[1]);
|
||||
@@ -67,22 +69,30 @@ impl Config {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Box::new(e)),
|
||||
};
|
||||
|
||||
|
||||
if config.host.is_some() || config.port.is_some() {
|
||||
eprintln!("The host/port keys are depricated in favor \
|
||||
of interface and may be removed in the future.");
|
||||
eprintln!(
|
||||
"The host/port keys are depricated in favor \
|
||||
of interface and may be removed in the future."
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if config.interface.is_some() && (config.host.is_some() || config.port.is_some()) {
|
||||
return Err(Box::new(errors::GemError("You need to specify either host/port or interface".into())));
|
||||
return Err(Box::new(errors::GemError(
|
||||
"You need to specify either host/port or interface".into(),
|
||||
)));
|
||||
} else if config.interface.is_none() && config.host.is_none() && config.port.is_none() {
|
||||
return Err(Box::new(errors::GemError("You need to specify either host/port or interface".into())));
|
||||
return Err(Box::new(errors::GemError(
|
||||
"You need to specify either host/port or interface".into(),
|
||||
)));
|
||||
} else if config.host.is_some() && config.port.is_some() {
|
||||
return Ok(config);
|
||||
} else if config.interface.is_some() {
|
||||
return Ok(config);
|
||||
}
|
||||
return Err(Box::new(errors::GemError("You need to specify either host/port or interface".into())));
|
||||
}
|
||||
return Err(Box::new(errors::GemError(
|
||||
"You need to specify either host/port or interface".into(),
|
||||
)));
|
||||
}
|
||||
pub fn to_map(&self) -> HashMap<String, ServerCfg> {
|
||||
let mut map = HashMap::new();
|
||||
@@ -90,11 +100,11 @@ impl Config {
|
||||
map.insert(
|
||||
srv.hostname.clone(),
|
||||
ServerCfg {
|
||||
// port: self.port.clone(),
|
||||
// port: self.port.clone(),
|
||||
server: srv.clone(),
|
||||
},
|
||||
);
|
||||
}
|
||||
map
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,15 @@ use std::io;
|
||||
use std::marker::Unpin;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::io::AsyncRead;
|
||||
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
||||
use tokio_openssl::SslStream;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_rustls::server::TlsStream;
|
||||
|
||||
use crate::status::Status;
|
||||
|
||||
pub struct Connection {
|
||||
pub stream: SslStream<TcpStream>,
|
||||
pub stream: TlsStream<TcpStream>,
|
||||
pub local_addr: SocketAddr,
|
||||
pub peer_addr: SocketAddr,
|
||||
pub srv: crate::config::ServerCfg,
|
||||
@@ -38,7 +38,11 @@ impl Connection {
|
||||
self.send_raw(b.as_bytes()).await?;
|
||||
}
|
||||
|
||||
futures_util::future::poll_fn(|ctx| std::pin::Pin::new(&mut self.stream).poll_shutdown(ctx)).await.unwrap();
|
||||
futures_util::future::poll_fn(|ctx| {
|
||||
std::pin::Pin::new(&mut self.stream).poll_shutdown(ctx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -49,9 +53,16 @@ impl Connection {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn send_stream<S: AsyncRead + Unpin>(&mut self, reader: &mut S) -> Result<(), io::Error> {
|
||||
pub async fn send_stream<S: AsyncRead + Unpin>(
|
||||
&mut self,
|
||||
reader: &mut S,
|
||||
) -> Result<(), io::Error> {
|
||||
tokio::io::copy(reader, &mut self.stream).await?;
|
||||
futures_util::future::poll_fn(|ctx| std::pin::Pin::new(&mut self.stream).poll_shutdown(ctx)).await.unwrap();
|
||||
futures_util::future::poll_fn(|ctx| {
|
||||
std::pin::Pin::new(&mut self.stream).poll_shutdown(ctx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
pub type Result<T=()> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||
pub type Result<T = ()> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GemError(pub String);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
pub mod util;
|
||||
pub mod status;
|
||||
pub mod conn;
|
||||
pub mod tls;
|
||||
pub mod server;
|
||||
pub mod errors;
|
||||
pub mod server;
|
||||
pub mod status;
|
||||
pub mod tls;
|
||||
pub mod util;
|
||||
|
||||
@@ -1,26 +1,35 @@
|
||||
#![allow(unreachable_code)]
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use openssl::ssl::SslAcceptor;
|
||||
use openssl::error::ErrorStack;
|
||||
use openssl::ssl::NameType;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio_rustls::server::TlsStream;
|
||||
use tokio_rustls::TlsAcceptor;
|
||||
|
||||
//use futures_util::future::TryFutureExt;
|
||||
use url::Url;
|
||||
use std::io;
|
||||
use std::collections::HashMap;
|
||||
use std::future::Future;
|
||||
use std::sync::Arc;
|
||||
use std::io;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use url::Url;
|
||||
|
||||
use crate::config;
|
||||
use crate::conn;
|
||||
use crate::errors::{GemError, Result};
|
||||
use crate::logger;
|
||||
use crate::status::Status;
|
||||
use crate::errors::{GemError, Result};
|
||||
|
||||
pub trait Handler: FnMut(conn::Connection, url::Url) -> Pin<Box<dyn Future<Output = Result> + Send>> + Send + Sync + Copy {}
|
||||
impl<T> Handler for T
|
||||
where T: FnMut(conn::Connection, url::Url) -> Pin<Box<dyn Future<Output = Result> + Send>> + Send + Sync + Copy
|
||||
pub trait Handler:
|
||||
FnMut(conn::Connection, url::Url) -> Pin<Box<dyn Future<Output = Result> + Send>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ Copy
|
||||
{
|
||||
}
|
||||
impl<T> Handler for T where
|
||||
T: FnMut(conn::Connection, url::Url) -> Pin<Box<dyn Future<Output = Result> + Send>>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ Copy
|
||||
{
|
||||
}
|
||||
|
||||
@@ -33,14 +42,15 @@ where
|
||||
|
||||
pub struct Server {
|
||||
pub listener: Vec<TcpListener>,
|
||||
pub acceptor: SslAcceptor,
|
||||
pub acceptor: TlsAcceptor,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
pub async fn bind(addr: Vec<std::net::SocketAddr>,
|
||||
acceptor: fn(config::Config) -> std::result::Result<SslAcceptor, ErrorStack>,
|
||||
cfg: config::Config) -> io::Result<Server>
|
||||
{
|
||||
pub async fn bind(
|
||||
addr: Vec<std::net::SocketAddr>,
|
||||
acceptor: fn(config::Config) -> std::io::Result<TlsAcceptor>,
|
||||
cfg: config::Config,
|
||||
) -> Result<Server> {
|
||||
if addr.len() == 1 {
|
||||
Ok(Server {
|
||||
listener: vec![TcpListener::bind(addr[0].to_owned()).await?],
|
||||
@@ -58,14 +68,18 @@ impl Server {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn serve(self, cmap: HashMap<String, config::ServerCfg>, default: String,
|
||||
handler: impl Handler + 'static + Copy) -> Result
|
||||
{
|
||||
for listen in self.listener {
|
||||
pub async fn serve(
|
||||
self,
|
||||
cmap: HashMap<String, config::ServerCfg>,
|
||||
default: String,
|
||||
handler: impl Handler + 'static + Copy,
|
||||
) -> Result {
|
||||
for listen in self.listener {
|
||||
let cmap = cmap.clone();
|
||||
let default = default.clone();
|
||||
let listen = Arc::new(listen);
|
||||
let acceptor = Arc::new(self.acceptor.clone());
|
||||
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
let (stream, peer_addr) = listen.accept().await?;
|
||||
@@ -75,31 +89,37 @@ impl Server {
|
||||
let default = default.clone();
|
||||
let mut handler = handler.clone();
|
||||
|
||||
let ssl = openssl::ssl::Ssl::new(acceptor.context()).unwrap();
|
||||
let mut stream = tokio_openssl::SslStream::new(ssl, stream).unwrap();
|
||||
|
||||
tokio::spawn(async move {
|
||||
match Pin::new(&mut stream).accept().await {
|
||||
let mut stream = match acceptor.accept(stream).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
log::error!("Error: {}",e);
|
||||
log::error!("Error: {}", e);
|
||||
return Ok(());
|
||||
},
|
||||
}
|
||||
};
|
||||
let (_, sni) = TlsStream::get_mut(&mut stream);
|
||||
let sni = match sni.sni_hostname() {
|
||||
Some(s) => s,
|
||||
None => return Ok(()),
|
||||
};
|
||||
let srv = match stream.ssl().servername(NameType::HOST_NAME) {
|
||||
Some(s) => match cmap.get(s) {
|
||||
Some(ss) => ss,
|
||||
None => cmap.get(&default).unwrap(),
|
||||
},
|
||||
None => cmap.get(&default).unwrap(),
|
||||
}.to_owned();
|
||||
|
||||
let con = conn::Connection { stream, local_addr, peer_addr, srv };
|
||||
let srv = match cmap.get(sni) {
|
||||
Some(h) => h,
|
||||
None => cmap.get(&default).unwrap(),
|
||||
}
|
||||
.to_owned();
|
||||
|
||||
let con = conn::Connection {
|
||||
stream,
|
||||
local_addr,
|
||||
peer_addr,
|
||||
srv,
|
||||
};
|
||||
let (con, url) = match get_request(con).await {
|
||||
Ok((c, u)) => (c, u),
|
||||
Err(_) => return Ok(()) as io::Result<()>,
|
||||
};
|
||||
|
||||
|
||||
match handler(con, url).await {
|
||||
Ok(o) => o,
|
||||
Err(_) => return Ok(()) as io::Result<()>,
|
||||
@@ -111,18 +131,27 @@ impl Server {
|
||||
Ok(()) as io::Result<()>
|
||||
});
|
||||
}
|
||||
tokio::signal::ctrl_c().await.expect("failed to listen for event");
|
||||
tokio::signal::ctrl_c()
|
||||
.await
|
||||
.expect("failed to listen for event");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_request(mut con: conn::Connection) -> Result<(conn::Connection, url::Url)> {
|
||||
let mut buffer = [0; 1024];
|
||||
let len = match tokio::time::timeout(tokio::time::Duration::from_secs(5), con.stream.read(&mut buffer)).await {
|
||||
let len = match tokio::time::timeout(
|
||||
tokio::time::Duration::from_secs(5),
|
||||
con.stream.read(&mut buffer),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(result) => result.unwrap(),
|
||||
Err(e) => {
|
||||
logger::logger(con.peer_addr, Status::BadRequest, "");
|
||||
con.send_status(Status::BadRequest, None).await.map_err(|e| e.to_string())?;
|
||||
con.send_status(Status::BadRequest, None)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
return Err(Box::new(e));
|
||||
}
|
||||
};
|
||||
@@ -130,7 +159,9 @@ pub async fn get_request(mut con: conn::Connection) -> Result<(conn::Connection,
|
||||
Ok(request) => request,
|
||||
Err(e) => {
|
||||
logger::logger(con.peer_addr, Status::BadRequest, "");
|
||||
con.send_status(Status::BadRequest, None).await.map_err(|e| e.to_string())?;
|
||||
con.send_status(Status::BadRequest, None)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
return Err(Box::new(e));
|
||||
}
|
||||
};
|
||||
@@ -149,7 +180,9 @@ pub async fn get_request(mut con: conn::Connection) -> Result<(conn::Connection,
|
||||
Ok(url) => url,
|
||||
Err(e) => {
|
||||
logger::logger(con.peer_addr, Status::BadRequest, &request);
|
||||
con.send_status(Status::BadRequest, None).await.map_err(|e| e.to_string())?;
|
||||
con.send_status(Status::BadRequest, None)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
return Err(Box::new(e));
|
||||
}
|
||||
};
|
||||
@@ -158,10 +191,12 @@ pub async fn get_request(mut con: conn::Connection) -> Result<(conn::Connection,
|
||||
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())?;
|
||||
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() {
|
||||
@@ -169,16 +204,19 @@ pub async fn get_request(mut con: conn::Connection) -> Result<(conn::Connection,
|
||||
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())?;
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
if url.scheme() != "gemini" {
|
||||
logger::logger(con.peer_addr, Status::ProxyRequestRefused, &url.as_str());
|
||||
con.send_status(Status::ProxyRequestRefused, None).await.map_err(|e| e.to_string())?;
|
||||
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))
|
||||
}
|
||||
|
||||
return Ok((con, url));
|
||||
}
|
||||
|
||||
212
src/lib/tls.rs
212
src/lib/tls.rs
@@ -1,69 +1,163 @@
|
||||
extern crate openssl;
|
||||
extern crate tokio_openssl;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::{self, BufReader};
|
||||
use std::sync::Arc;
|
||||
use std::time::SystemTime;
|
||||
|
||||
use openssl::error::ErrorStack;
|
||||
use openssl::ssl::NameType;
|
||||
use openssl::ssl::SniError;
|
||||
use openssl::ssl::SslContextBuilder;
|
||||
use openssl::ssl::SslVersion;
|
||||
use openssl::ssl::SslVerifyMode;
|
||||
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
|
||||
use rustls::client::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier, ServerName};
|
||||
use rustls::internal::msgs::enums::SignatureScheme;
|
||||
use rustls::internal::msgs::handshake::DigitallySignedStruct;
|
||||
use rustls::internal::msgs::handshake::DistinguishedNames;
|
||||
use rustls::server::{ClientCertVerified, ClientCertVerifier, ResolvesServerCertUsingSni};
|
||||
use rustls::sign::{self, CertifiedKey};
|
||||
use rustls::{Certificate, Error, PrivateKey};
|
||||
use rustls_pemfile::{certs, pkcs8_private_keys};
|
||||
use tokio_rustls::rustls;
|
||||
use tokio_rustls::TlsAcceptor;
|
||||
|
||||
use crate::config;
|
||||
|
||||
pub fn acceptor_conf(cfg: config::Config) -> Result<SslAcceptor, ErrorStack> {
|
||||
let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls_server())?;
|
||||
acceptor.set_min_proto_version(Some(SslVersion::TLS1_2))?;
|
||||
let mut map = HashMap::new();
|
||||
let mut num = 1;
|
||||
pub fn tls_acceptor_conf(cfg: config::Config) -> io::Result<TlsAcceptor> {
|
||||
let resolver = load_keypair(cfg)?;
|
||||
let config = rustls::server::ServerConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_client_cert_verifier(Arc::new(GeminiClientAuth))
|
||||
.with_cert_resolver(Arc::new(resolver));
|
||||
let acceptor = TlsAcceptor::from(Arc::new(config));
|
||||
|
||||
Ok(acceptor)
|
||||
}
|
||||
|
||||
pub fn load_certs(path: &String) -> io::Result<Vec<Certificate>> {
|
||||
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<Vec<PrivateKey>> {
|
||||
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())
|
||||
}
|
||||
|
||||
fn load_keypair(cfg: config::Config) -> io::Result<ResolvesServerCertUsingSni> {
|
||||
let mut resolver = rustls::server::ResolvesServerCertUsingSni::new();
|
||||
|
||||
for server in cfg.server.iter() {
|
||||
let mut ctx = SslContextBuilder::new(SslMethod::tls_server())?;
|
||||
ctx.set_verify(SslVerifyMode::NONE);
|
||||
match ctx.set_private_key_file(&server.key, SslFiletype::PEM) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
log::error!("Error: Can't load key file");
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
match ctx.set_certificate_chain_file(&server.cert) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
log::error!("Error: Can't load cert file");
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
let ctx = ctx.build();
|
||||
map.insert(server.hostname.clone(), ctx.clone());
|
||||
if num == 1 {
|
||||
map.insert("default".to_string(), ctx);
|
||||
num += 1;
|
||||
}
|
||||
let key = load_key(&server.key)?.remove(0);
|
||||
let certs = load_certs(&server.cert)?;
|
||||
let signing_key = sign::any_supported_type(&key).expect("error loading key");
|
||||
|
||||
resolver
|
||||
.add(
|
||||
&server.hostname.clone(),
|
||||
CertifiedKey::new(certs, signing_key),
|
||||
)
|
||||
.expect("error loading key");
|
||||
}
|
||||
Ok(resolver)
|
||||
}
|
||||
|
||||
struct GeminiClientAuth;
|
||||
|
||||
impl ClientCertVerifier for GeminiClientAuth {
|
||||
fn client_auth_root_subjects(&self) -> Option<DistinguishedNames> {
|
||||
Some(Vec::new())
|
||||
}
|
||||
|
||||
let ctx_builder = &mut *acceptor;
|
||||
ctx_builder.set_servername_callback(move |ssl, _alert| -> Result<(), SniError> {
|
||||
ssl.set_ssl_context({
|
||||
let hostname = ssl.servername(NameType::HOST_NAME);
|
||||
if let Some(host) = hostname {
|
||||
if let Some(ctx) = map.get(host) {
|
||||
&ctx
|
||||
} else {
|
||||
&map.get(&"default".to_string()).expect("Can't get default")
|
||||
}
|
||||
} else {
|
||||
&map.get(&"default".to_string()).expect("Can't get default")
|
||||
}
|
||||
})
|
||||
.expect("Can't get sni");
|
||||
// for client certs we don't have anything to verify right now?
|
||||
ssl.set_verify_callback(SslVerifyMode::PEER, |_ver, _store| -> bool {
|
||||
return true
|
||||
});
|
||||
fn verify_client_cert(
|
||||
&self,
|
||||
_end_entity: &Certificate,
|
||||
_intermidiates: &[Certificate],
|
||||
_now: SystemTime,
|
||||
) -> Result<ClientCertVerified, Error> {
|
||||
Ok(ClientCertVerified::assertion())
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
fn offer_client_auth(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
Ok(acceptor.build())
|
||||
fn client_auth_mandatory(&self) -> Option<bool> {
|
||||
Some(false)
|
||||
}
|
||||
|
||||
fn verify_tls12_signature(
|
||||
&self,
|
||||
_message: &[u8],
|
||||
_cert: &Certificate,
|
||||
_dss: &DigitallySignedStruct,
|
||||
) -> Result<HandshakeSignatureValid, Error> {
|
||||
Ok(HandshakeSignatureValid::assertion())
|
||||
}
|
||||
|
||||
fn verify_tls13_signature(
|
||||
&self,
|
||||
_message: &[u8],
|
||||
_cert: &Certificate,
|
||||
_dss: &DigitallySignedStruct,
|
||||
) -> Result<HandshakeSignatureValid, Error> {
|
||||
Ok(HandshakeSignatureValid::assertion())
|
||||
}
|
||||
|
||||
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
|
||||
tokio_rustls::rustls::client::WebPkiVerifier::verification_schemes()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GeminiServerAuth;
|
||||
|
||||
impl ServerCertVerifier for GeminiServerAuth {
|
||||
fn verify_server_cert(
|
||||
&self,
|
||||
_end_entity: &Certificate,
|
||||
_intermediates: &[Certificate],
|
||||
_server_name: &ServerName,
|
||||
_scts: &mut dyn Iterator<Item = &[u8]>,
|
||||
_ocsp_response: &[u8],
|
||||
_now: SystemTime,
|
||||
) -> Result<ServerCertVerified, Error> {
|
||||
Ok(ServerCertVerified::assertion())
|
||||
}
|
||||
}
|
||||
// This was pull out of the depths of git.
|
||||
// Rustls won't let self signed certs be used with sni which gemini requires.
|
||||
// At 1.4.2 in https://gemini.circumlunar.space/docs/spec-spec.txt
|
||||
/*
|
||||
pub struct CertResolver {
|
||||
map: HashMap<String, Box<CertifiedKey>>,
|
||||
}
|
||||
|
||||
impl CertResolver {
|
||||
pub fn from_config(cfg: config::Config) -> errors::Result<Self> {
|
||||
let mut map = HashMap::new();
|
||||
|
||||
for server in cfg.server.iter() {
|
||||
let key = load_key(&server.key)?;
|
||||
let certs = load_certs(&server.cert)?;
|
||||
let signing_key = RsaSigningKey::new(&key).unwrap();
|
||||
|
||||
//let signing_key_boxed: Arc<Box<dyn SigningKey>> = Arc::new(Box::new(signing_key));
|
||||
let signing_key_boxed = Arc::new(signing_key);
|
||||
map.insert(
|
||||
server.hostname.clone(),
|
||||
Box::new(CertifiedKey::new(certs, signing_key_boxed)),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(CertResolver { map })
|
||||
}
|
||||
}
|
||||
|
||||
impl ResolvesServerCert for CertResolver {
|
||||
fn resolve(&self, client_hello: ClientHello<'_>) -> Option<Arc<CertifiedKey>> {
|
||||
if let Some(hostname) = client_hello.server_name() {
|
||||
if let Some(cert) = self.map.get(hostname.into()) {
|
||||
let cert_box = Arc::new(cert);
|
||||
return Some(&cert_box);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
use sha2::Digest;
|
||||
use url::form_urlencoded;
|
||||
|
||||
pub fn url_decode(url: &[u8]) -> String {
|
||||
let decoded: String = form_urlencoded::parse(url)
|
||||
.map(|(key, val)| [key, val].concat())
|
||||
.collect();
|
||||
return decoded
|
||||
return decoded;
|
||||
}
|
||||
|
||||
pub fn fingerhex(x509: &openssl::x509::X509) -> String {
|
||||
let finger = match x509.digest(openssl::hash::MessageDigest::sha256()) {
|
||||
Ok(f) => f,
|
||||
_ => return "".to_string(),
|
||||
};
|
||||
pub fn fingerhex(x509: &[u8]) -> String {
|
||||
let mut finger = sha2::Sha256::new();
|
||||
finger.update(&x509);
|
||||
let finger = finger.finalize();
|
||||
let mut hex: String = String::from("SHA256:");
|
||||
for f in finger.as_ref() {
|
||||
for f in finger {
|
||||
hex.push_str(&format!("{:02X}", f));
|
||||
}
|
||||
hex
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
use crate::status;
|
||||
use crate::lib::errors;
|
||||
use crate::status;
|
||||
use log::{info, warn};
|
||||
use std::net::SocketAddr;
|
||||
|
||||
pub fn init(loglev: &Option<String>) -> errors::Result {
|
||||
let loglev = match loglev {
|
||||
None => log::Level::Info,
|
||||
Some(l) => {
|
||||
match l.as_str() {
|
||||
"error" => log::Level::Error,
|
||||
"warn" => log::Level::Warn,
|
||||
"info" => log::Level::Info,
|
||||
_ => {
|
||||
return Err(Box::new(errors::GemError("Incorrect log level in config file.".to_string())));
|
||||
},
|
||||
Some(l) => match l.as_str() {
|
||||
"error" => log::Level::Error,
|
||||
"warn" => log::Level::Warn,
|
||||
"info" => log::Level::Info,
|
||||
_ => {
|
||||
return Err(Box::new(errors::GemError(
|
||||
"Incorrect log level in config file.".to_string(),
|
||||
)));
|
||||
}
|
||||
},
|
||||
};
|
||||
simple_logger::init_with_level(loglev).unwrap();
|
||||
return Ok(())
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn logger(addr: SocketAddr, stat: status::Status, req: &str) {
|
||||
|
||||
64
src/main.rs
64
src/main.rs
@@ -4,19 +4,21 @@ extern crate serde_derive;
|
||||
use std::io;
|
||||
use std::net::ToSocketAddrs;
|
||||
|
||||
mod lib;
|
||||
#[cfg(any(feature = "cgi", feature = "scgi"))]
|
||||
mod cgi;
|
||||
mod config;
|
||||
mod logger;
|
||||
mod revproxy;
|
||||
mod con_handler;
|
||||
mod config;
|
||||
mod lib;
|
||||
mod logger;
|
||||
#[cfg(feature = "proxy")]
|
||||
mod revproxy;
|
||||
|
||||
use lib::util;
|
||||
use lib::conn;
|
||||
use lib::status;
|
||||
use lib::tls;
|
||||
use lib::server;
|
||||
use lib::errors;
|
||||
use lib::server;
|
||||
use lib::status;
|
||||
use lib::tls::{self, tls_acceptor_conf};
|
||||
use lib::util;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> errors::Result {
|
||||
@@ -25,39 +27,51 @@ async fn main() -> errors::Result {
|
||||
Err(e) => {
|
||||
eprintln!("Config error: {}", e);
|
||||
return Ok(());
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
logger::init(&cfg.log)?;
|
||||
|
||||
|
||||
let cmap = cfg.to_map();
|
||||
let default = &cfg.server[0].hostname;
|
||||
println!("Serving {} vhosts", cfg.server.len());
|
||||
|
||||
let mut addr: Vec<std::net::SocketAddr> = Vec::new();
|
||||
if cfg.host.is_some() && cfg.port.is_some() {
|
||||
addr.push(format!("{}:{}", &cfg.host.to_owned().unwrap(), &cfg.port.unwrap())
|
||||
.to_socket_addrs()?.next()
|
||||
.ok_or_else(|| io::Error::from(io::ErrorKind::AddrNotAvailable))?);
|
||||
addr.push(
|
||||
format!("{}:{}", &cfg.host.to_owned().unwrap(), &cfg.port.unwrap())
|
||||
.to_socket_addrs()?
|
||||
.next()
|
||||
.ok_or_else(|| io::Error::from(io::ErrorKind::AddrNotAvailable))?,
|
||||
);
|
||||
} else {
|
||||
match &cfg.interface {
|
||||
Some(i) => {
|
||||
for iface in i {
|
||||
addr.push(iface
|
||||
.to_socket_addrs()?.next()
|
||||
.ok_or_else(|| io::Error::from(io::ErrorKind::AddrNotAvailable))?);
|
||||
}
|
||||
},
|
||||
None => {},
|
||||
addr.push(
|
||||
iface
|
||||
.to_socket_addrs()?
|
||||
.next()
|
||||
.ok_or_else(|| io::Error::from(io::ErrorKind::AddrNotAvailable))?,
|
||||
);
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
addr.sort_by(|a, b| a.port().cmp(&b.port()));
|
||||
addr.dedup();
|
||||
|
||||
let server = server::Server::bind(addr, tls::acceptor_conf, cfg.clone()).await?;
|
||||
if let Err(e) = server.serve(cmap, default.to_string(), server::force_boxed(con_handler::handle_connection)).await {
|
||||
return Err(e)
|
||||
let server = server::Server::bind(addr, tls_acceptor_conf, cfg.clone()).await?;
|
||||
if let Err(e) = server
|
||||
.serve(
|
||||
cmap,
|
||||
default.to_string(),
|
||||
server::force_boxed(con_handler::handle_connection),
|
||||
)
|
||||
.await
|
||||
{
|
||||
return Err(e);
|
||||
};
|
||||
return Ok(())
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
#![cfg(feature = "proxy")]
|
||||
use openssl::ssl::{SslConnector, SslMethod};
|
||||
use std::convert::TryFrom;
|
||||
use std::io;
|
||||
use std::net::ToSocketAddrs;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
use tokio_rustls::rustls;
|
||||
use tokio_rustls::TlsConnector;
|
||||
|
||||
use crate::conn;
|
||||
use crate::logger;
|
||||
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();
|
||||
@@ -22,32 +26,25 @@ pub async fn proxy(addr: String, u: url::Url, mut con: conn::Connection) -> Resu
|
||||
con.send_status(Status::NotFound, None).await?;
|
||||
return Ok(());
|
||||
}
|
||||
let domain = &addr;
|
||||
let addr = addr
|
||||
.to_socket_addrs()?
|
||||
.next()
|
||||
.ok_or_else(|| io::Error::from(io::ErrorKind::AddrNotAvailable))?;
|
||||
|
||||
let mut connector = SslConnector::builder(SslMethod::tls()).unwrap();
|
||||
connector.set_verify(openssl::ssl::SslVerifyMode::NONE);
|
||||
let config = connector.build().configure().unwrap().into_ssl("localhost").unwrap();
|
||||
let config = rustls::ClientConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_custom_certificate_verifier(Arc::new(tls::GeminiServerAuth))
|
||||
.with_no_client_auth();
|
||||
let connector = TlsConnector::from(Arc::new(config));
|
||||
|
||||
let stream = TcpStream::connect(&addr).await?;
|
||||
|
||||
let domain = rustls::ServerName::try_from(domain.as_str())
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid dnsname"))?;
|
||||
|
||||
let mut stream = connector.connect(domain, stream).await?;
|
||||
|
||||
let stream = match TcpStream::connect(&addr).await {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
logger::logger(con.peer_addr, Status::ProxyError, u.as_str());
|
||||
con.send_status(Status::ProxyError, None).await?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let mut stream = tokio_openssl::SslStream::new(config, stream).unwrap();
|
||||
match Pin::new(&mut stream).connect().await {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
logger::logger(con.peer_addr, Status::ProxyError, u.as_str());
|
||||
con.send_status(Status::ProxyError, None).await?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
stream.write_all(p[1].as_bytes()).await?;
|
||||
stream.flush().await?;
|
||||
|
||||
@@ -58,32 +55,25 @@ pub async fn proxy(addr: String, u: url::Url, mut con: conn::Connection) -> Resu
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn proxy_all(addr: &str, u: url::Url, mut con: conn::Connection) -> Result<(), io::Error> {
|
||||
let mut connector = SslConnector::builder(SslMethod::tls()).unwrap();
|
||||
connector.set_verify(openssl::ssl::SslVerifyMode::NONE);
|
||||
pub async fn proxy_all(
|
||||
addr: &str,
|
||||
u: url::Url,
|
||||
mut con: conn::Connection,
|
||||
) -> Result<(), io::Error> {
|
||||
let domain = addr.splitn(2, ':').next().unwrap();
|
||||
let config = connector.build().configure().unwrap().into_ssl(domain).unwrap();
|
||||
|
||||
// TCP handshake
|
||||
let stream = match TcpStream::connect(&addr).await {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
logger::logger(con.peer_addr, Status::ProxyError, u.as_str());
|
||||
con.send_status(Status::ProxyError, None).await?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let config = rustls::ClientConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_custom_certificate_verifier(Arc::new(tls::GeminiServerAuth))
|
||||
.with_no_client_auth();
|
||||
let connector = TlsConnector::from(Arc::new(config));
|
||||
|
||||
// TLS handshake with SNI
|
||||
let mut stream = tokio_openssl::SslStream::new(config, stream).unwrap();
|
||||
match Pin::new(&mut stream).connect().await {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
logger::logger(con.peer_addr, Status::ProxyError, u.as_str());
|
||||
con.send_status(Status::ProxyError, None).await?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let stream = TcpStream::connect(&addr).await?;
|
||||
|
||||
let domain = rustls::ServerName::try_from(domain)
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid dnsname"))?;
|
||||
|
||||
let mut stream = connector.connect(domain, stream).await?;
|
||||
|
||||
// send request: URL + CRLF
|
||||
stream.write_all(u.as_ref().as_bytes()).await?;
|
||||
|
||||
Reference in New Issue
Block a user