|
|
|
@ -1,26 +1,43 @@
|
|
|
|
|
#[macro_use] extern crate log;
|
|
|
|
|
|
|
|
|
|
use std::{
|
|
|
|
|
convert::TryFrom,
|
|
|
|
|
io::BufReader,
|
|
|
|
|
sync::Arc,
|
|
|
|
|
path::PathBuf,
|
|
|
|
|
time::Duration,
|
|
|
|
|
};
|
|
|
|
|
#[cfg(feature = "ratelimiting")]
|
|
|
|
|
use std::net::IpAddr;
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
use std::{
|
|
|
|
|
convert::TryFrom,
|
|
|
|
|
path::PathBuf,
|
|
|
|
|
};
|
|
|
|
|
#[cfg(feature = "scgi_srv")]
|
|
|
|
|
use std::{
|
|
|
|
|
collections::HashMap,
|
|
|
|
|
net::SocketAddr,
|
|
|
|
|
str::FromStr,
|
|
|
|
|
};
|
|
|
|
|
use tokio::{
|
|
|
|
|
io,
|
|
|
|
|
io::BufReader,
|
|
|
|
|
net::TcpListener,
|
|
|
|
|
net::ToSocketAddrs,
|
|
|
|
|
prelude::*,
|
|
|
|
|
io::{self, BufStream},
|
|
|
|
|
net::{TcpStream, ToSocketAddrs},
|
|
|
|
|
};
|
|
|
|
|
#[cfg(feature = "scgi_srv")]
|
|
|
|
|
use tokio::net::UnixListener;
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
use tokio::{
|
|
|
|
|
time::timeout,
|
|
|
|
|
net::TcpStream,
|
|
|
|
|
};
|
|
|
|
|
#[cfg(feature = "ratelimiting")]
|
|
|
|
|
use tokio::time::interval;
|
|
|
|
|
use tokio::net::TcpListener;
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
use rustls::ClientCertVerifier;
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
use rustls::internal::msgs::handshake::DigitallySignedStruct;
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
use tokio_rustls::{rustls, TlsAcceptor};
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
use rustls::*;
|
|
|
|
|
use anyhow::*;
|
|
|
|
|
use crate::util::opt_timeout;
|
|
|
|
@ -54,6 +71,7 @@ use handling::Handler;
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
struct ServerInner {
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
tls_acceptor: TlsAcceptor,
|
|
|
|
|
routes: Arc<RoutingNode<Handler>>,
|
|
|
|
|
timeout: Duration,
|
|
|
|
@ -65,7 +83,7 @@ struct ServerInner {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ServerInner {
|
|
|
|
|
async fn serve(self, listener: TcpListener) -> Result<()> {
|
|
|
|
|
async fn serve_ip(self, listener: TcpListener) -> Result<()> {
|
|
|
|
|
#[cfg(feature = "ratelimiting")]
|
|
|
|
|
tokio::spawn(prune_ratelimit_log(self.rate_limits.clone()));
|
|
|
|
|
|
|
|
|
@ -82,48 +100,110 @@ impl ServerInner {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn serve_client(self, stream: TcpStream) -> Result<()> {
|
|
|
|
|
#[cfg(feature="ratelimiting")]
|
|
|
|
|
let peer_addr = stream.peer_addr()?.ip();
|
|
|
|
|
#[cfg(feature = "scgi_srv")]
|
|
|
|
|
// Yeah it's code duplication, but I can't find a way around it, so this is what we're
|
|
|
|
|
// getting for now
|
|
|
|
|
async fn serve_unix(self, listener: UnixListener) -> Result<()> {
|
|
|
|
|
#[cfg(feature = "ratelimiting")]
|
|
|
|
|
tokio::spawn(prune_ratelimit_log(self.rate_limits.clone()));
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
let (stream, _addr) = listener.accept().await
|
|
|
|
|
.context("Failed to accept client")?;
|
|
|
|
|
let this = self.clone();
|
|
|
|
|
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
if let Err(err) = this.serve_client(stream).await {
|
|
|
|
|
error!("{:?}", err);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn serve_client(
|
|
|
|
|
&self,
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
stream: TcpStream,
|
|
|
|
|
#[cfg(feature = "scgi_srv")]
|
|
|
|
|
stream: impl AsyncWrite + AsyncRead + Unpin,
|
|
|
|
|
) -> Result<()> {
|
|
|
|
|
let fut_accept_request = async {
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
let stream = self.tls_acceptor.accept(stream).await
|
|
|
|
|
.context("Failed to establish TLS session")?;
|
|
|
|
|
let mut stream = BufStream::new(stream);
|
|
|
|
|
let mut stream = BufReader::new(stream);
|
|
|
|
|
|
|
|
|
|
#[cfg(feature="user_management")]
|
|
|
|
|
let request = self.receive_request(&mut stream).await
|
|
|
|
|
.context("Failed to receive request")?;
|
|
|
|
|
#[cfg(not(feature="user_management"))]
|
|
|
|
|
let request = Self::receive_request(&mut stream).await
|
|
|
|
|
.context("Failed to receive request")?;
|
|
|
|
|
|
|
|
|
|
Result::<_, anyhow::Error>::Ok((request, stream))
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Use a timeout for interacting with the client
|
|
|
|
|
let fut_accept_request = timeout(self.timeout, fut_accept_request);
|
|
|
|
|
let (mut request, mut stream) = fut_accept_request.await
|
|
|
|
|
.context("Client timed out while waiting for response")??;
|
|
|
|
|
|
|
|
|
|
#[cfg(feature="ratelimiting")]
|
|
|
|
|
// Wait for the request to be parsed
|
|
|
|
|
let (mut request, mut stream) = {
|
|
|
|
|
#[cfg(feature = "gemini_srv")] {
|
|
|
|
|
// Use a timeout for interacting with the client
|
|
|
|
|
let fut_accept_request = timeout(self.timeout, fut_accept_request);
|
|
|
|
|
fut_accept_request.await
|
|
|
|
|
.context("Client timed out while waiting for response")??
|
|
|
|
|
}
|
|
|
|
|
#[cfg(feature = "scgi_srv")]
|
|
|
|
|
fut_accept_request.await?
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Determine the remote client's IP address for logging and ratelimiting
|
|
|
|
|
let peer_addr = {
|
|
|
|
|
#[cfg(feature = "gemini_srv")] {
|
|
|
|
|
stream.get_ref()
|
|
|
|
|
.get_ref()
|
|
|
|
|
.0
|
|
|
|
|
.peer_addr()?
|
|
|
|
|
.ip()
|
|
|
|
|
}
|
|
|
|
|
#[cfg(feature = "scgi_srv")] {
|
|
|
|
|
SocketAddr::from_str(
|
|
|
|
|
request.headers()
|
|
|
|
|
.get("REMOTE_ADDR")
|
|
|
|
|
.ok_or(ParseError::Malformed("REMOTE_ADDR header not received"))?
|
|
|
|
|
.as_str()
|
|
|
|
|
).context("Received malformed IP address from upstream")?
|
|
|
|
|
.ip()
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "ratelimiting")]
|
|
|
|
|
// Perform ratelimiting checks
|
|
|
|
|
if let Some(resp) = self.check_rate_limits(peer_addr, &request) {
|
|
|
|
|
|
|
|
|
|
// Log warning
|
|
|
|
|
warn!(
|
|
|
|
|
"Client from {} requesting {} was turned away by ratelimiting",
|
|
|
|
|
peer_addr,
|
|
|
|
|
request.uri()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Send error response
|
|
|
|
|
self.send_response(resp, &mut stream).await
|
|
|
|
|
.context("Failed to send response")?;
|
|
|
|
|
|
|
|
|
|
// Exit
|
|
|
|
|
return Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
debug!("Client requested: {}", request.uri());
|
|
|
|
|
info!("{} requested: {}", peer_addr, request.uri());
|
|
|
|
|
|
|
|
|
|
// Identify the client certificate from the tls stream. This is the first
|
|
|
|
|
// certificate in the certificate chain.
|
|
|
|
|
let client_cert = stream.get_ref()
|
|
|
|
|
.get_ref()
|
|
|
|
|
.1
|
|
|
|
|
.get_peer_certificates()
|
|
|
|
|
.and_then(|mut v| if v.is_empty() {None} else {Some(v.remove(0))});
|
|
|
|
|
|
|
|
|
|
request.set_cert(client_cert);
|
|
|
|
|
#[cfg(feature = "gemini_srv")] { // This is done earlier for `scgi_srv`
|
|
|
|
|
let client_cert = stream.get_ref()
|
|
|
|
|
.get_ref()
|
|
|
|
|
.1
|
|
|
|
|
.get_peer_certificates()
|
|
|
|
|
.and_then(|mut v| if v.is_empty() {None} else {Some(v.remove(0))});
|
|
|
|
|
|
|
|
|
|
request.set_cert(client_cert);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let response = if let Some((trailing, handler)) = self.routes.match_request(&request) {
|
|
|
|
|
request.set_trailing(trailing);
|
|
|
|
@ -197,13 +277,13 @@ impl ServerInner {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
async fn receive_request(
|
|
|
|
|
#[cfg(feature="user_management")]
|
|
|
|
|
&self,
|
|
|
|
|
stream: &mut (impl AsyncBufRead + Unpin)
|
|
|
|
|
stream: &mut (impl AsyncBufRead + Unpin),
|
|
|
|
|
) -> Result<Request> {
|
|
|
|
|
let limit = REQUEST_URI_MAX_LEN + "\r\n".len();
|
|
|
|
|
let mut stream = stream.take(limit as u64);
|
|
|
|
|
const HEADER_LIMIT: usize = REQUEST_URI_MAX_LEN + "\r\n".len();
|
|
|
|
|
let mut stream = stream.take(HEADER_LIMIT as u64);
|
|
|
|
|
let mut uri = Vec::new();
|
|
|
|
|
|
|
|
|
|
stream.read_until(b'\n', &mut uri).await?;
|
|
|
|
@ -223,23 +303,123 @@ impl ServerInner {
|
|
|
|
|
let uri = URIReference::try_from(&*uri)
|
|
|
|
|
.context("Request URI is invalid")?
|
|
|
|
|
.into_owned();
|
|
|
|
|
let request = Request::from_uri(
|
|
|
|
|
|
|
|
|
|
Request::new(
|
|
|
|
|
uri,
|
|
|
|
|
#[cfg(feature="user_management")]
|
|
|
|
|
self.manager.clone(),
|
|
|
|
|
) .context("Failed to create request from URI")?;
|
|
|
|
|
).context("Failed to create request from URI")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "scgi_srv")]
|
|
|
|
|
async fn receive_request(
|
|
|
|
|
&self,
|
|
|
|
|
stream: &mut (impl AsyncBufRead + Unpin),
|
|
|
|
|
) -> Result<Request> {
|
|
|
|
|
let mut buff = Vec::with_capacity(4);
|
|
|
|
|
|
|
|
|
|
#[allow(clippy::char_lit_as_u8)]
|
|
|
|
|
// Read the length of the header netstring (e.g. "120:")
|
|
|
|
|
stream.read_until(':' as u8, &mut buff).await?;
|
|
|
|
|
|
|
|
|
|
buff.pop(); // Remove the trailing ':'
|
|
|
|
|
let len = std::str::from_utf8(&*buff)
|
|
|
|
|
.ok()
|
|
|
|
|
.and_then(|s| usize::from_str(s).ok())
|
|
|
|
|
.ok_or(ParseError::Malformed("netstring length"))?;
|
|
|
|
|
|
|
|
|
|
// Read in the headers
|
|
|
|
|
buff.clear();
|
|
|
|
|
buff.resize(len + 1, 0);
|
|
|
|
|
stream.read_exact(buff.as_mut()).await?;
|
|
|
|
|
buff.truncate(len - 1); // Remove the final \x00,
|
|
|
|
|
|
|
|
|
|
// Parse the headers
|
|
|
|
|
let (maybe_trailing, headers) = buff.split(|b| *b == 0) // Headers are null delimiited
|
|
|
|
|
.map(|bytes| // Convert to an &str
|
|
|
|
|
std::str::from_utf8(bytes)
|
|
|
|
|
.map_err(|_| ParseError::Malformed("scgi headers"))
|
|
|
|
|
.map(str::trim)
|
|
|
|
|
)
|
|
|
|
|
.try_fold( // Turn the array of [header, value, header, ...] into a map
|
|
|
|
|
(Option::<&str>::None, HashMap::<String, String>::with_capacity(16)),
|
|
|
|
|
|(last_header, mut headers), s| {
|
|
|
|
|
s.map(|text| {
|
|
|
|
|
match last_header {
|
|
|
|
|
None => (Some(text), headers),
|
|
|
|
|
Some(header) => {
|
|
|
|
|
headers.insert(header.to_string(), text.to_string());
|
|
|
|
|
(None, headers)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
// If there's not the same number of headers as values, that's a problem
|
|
|
|
|
if maybe_trailing.is_some() {
|
|
|
|
|
bail!(ParseError::Malformed("trailing header"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check the content length info
|
|
|
|
|
let cont_len_val = headers.get("CONTENT_LENGTH")
|
|
|
|
|
.ok_or(ParseError::Malformed("No content length header!"))?;
|
|
|
|
|
let cont_len = usize::from_str(cont_len_val)
|
|
|
|
|
.map_err(|_| ParseError::Malformed("Malformed content length"))?;
|
|
|
|
|
if cont_len > 0 {
|
|
|
|
|
bail!(ParseError::Malformed("Gemini SCGI requests should not have a body"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Spec requires setting an SCGI header to one
|
|
|
|
|
if *headers.get("SCGI").ok_or(ParseError::Malformed("No SCGI header"))? != "1" {
|
|
|
|
|
bail!(ParseError::Malformed("SCGI header not set to \"1\""));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(request)
|
|
|
|
|
trace!("Headers received: {:?}", headers);
|
|
|
|
|
|
|
|
|
|
Ok(Request::new(headers)?)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct Server<A> {
|
|
|
|
|
addr: A,
|
|
|
|
|
cert_path: PathBuf,
|
|
|
|
|
key_path: PathBuf,
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
#[cfg(feature = "scgi_srv")]
|
|
|
|
|
enum ParseError {
|
|
|
|
|
IO(io::Error),
|
|
|
|
|
Malformed(&'static str),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "scgi_srv")]
|
|
|
|
|
impl From<io::Error> for ParseError {
|
|
|
|
|
fn from(e: io::Error) -> Self {
|
|
|
|
|
Self::IO(e)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "scgi_srv")]
|
|
|
|
|
impl std::fmt::Display for ParseError {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
|
match self {
|
|
|
|
|
Self::IO(e) => write!(f, "IO Error while parsing and responding SCGI: {}", e),
|
|
|
|
|
Self::Malformed(e) => write!(f, "SCGI request malformed at {}", e),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "scgi_srv")]
|
|
|
|
|
impl std::error::Error for ParseError {
|
|
|
|
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
|
|
|
if let Self::IO(e) = self { Some(e) } else { None }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct Server {
|
|
|
|
|
timeout: Duration,
|
|
|
|
|
complex_body_timeout_override: Option<Duration>,
|
|
|
|
|
routes: RoutingNode<Handler>,
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
cert_path: PathBuf,
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
key_path: PathBuf,
|
|
|
|
|
#[cfg(feature="ratelimiting")]
|
|
|
|
|
rate_limits: RoutingNode<RateLimiter<IpAddr>>,
|
|
|
|
|
#[cfg(feature="user_management")]
|
|
|
|
@ -250,15 +430,16 @@ pub struct Server<A> {
|
|
|
|
|
certgen_mode: CertGenMode,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<A: ToSocketAddrs> Server<A> {
|
|
|
|
|
pub fn bind(addr: A) -> Self {
|
|
|
|
|
impl Server {
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
addr,
|
|
|
|
|
timeout: Duration::from_secs(1),
|
|
|
|
|
complex_body_timeout_override: Some(Duration::from_secs(30)),
|
|
|
|
|
routes: RoutingNode::default(),
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
cert_path: PathBuf::from("cert/cert.pem"),
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
key_path: PathBuf::from("cert/key.pem"),
|
|
|
|
|
routes: RoutingNode::default(),
|
|
|
|
|
#[cfg(feature="ratelimiting")]
|
|
|
|
|
rate_limits: RoutingNode::default(),
|
|
|
|
|
#[cfg(feature="user_management")]
|
|
|
|
@ -309,6 +490,7 @@ impl<A: ToSocketAddrs> Server<A> {
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
/// Sets the directory that kochab should look for TLS certs and keys into
|
|
|
|
|
///
|
|
|
|
|
/// Northstar will look for files called `cert.pem` and `key.pem` in the provided
|
|
|
|
@ -324,6 +506,7 @@ impl<A: ToSocketAddrs> Server<A> {
|
|
|
|
|
.set_key(dir.join("key.pem"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
/// Set the path to the TLS certificate kochab will use
|
|
|
|
|
///
|
|
|
|
|
/// This defaults to `cert/cert.pem`.
|
|
|
|
@ -335,6 +518,7 @@ impl<A: ToSocketAddrs> Server<A> {
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
/// Set the path to the ertificate key kochab will use
|
|
|
|
|
///
|
|
|
|
|
/// This defaults to `cert/key.pem`.
|
|
|
|
@ -436,7 +620,8 @@ impl<A: ToSocketAddrs> Server<A> {
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn serve(mut self) -> Result<()> {
|
|
|
|
|
fn build(mut self) -> Result<ServerInner> {
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
let config = tls_config(
|
|
|
|
|
&self.cert_path,
|
|
|
|
|
&self.key_path,
|
|
|
|
@ -444,28 +629,51 @@ impl<A: ToSocketAddrs> Server<A> {
|
|
|
|
|
self.certgen_mode
|
|
|
|
|
).context("Failed to create TLS config")?;
|
|
|
|
|
|
|
|
|
|
let listener = TcpListener::bind(self.addr).await
|
|
|
|
|
.context("Failed to create socket")?;
|
|
|
|
|
|
|
|
|
|
self.routes.shrink();
|
|
|
|
|
|
|
|
|
|
#[cfg(feature="user_management")]
|
|
|
|
|
let data_dir = self.data_dir;
|
|
|
|
|
|
|
|
|
|
let server = ServerInner {
|
|
|
|
|
tls_acceptor: TlsAcceptor::from(config),
|
|
|
|
|
Ok(ServerInner {
|
|
|
|
|
routes: Arc::new(self.routes),
|
|
|
|
|
timeout: self.timeout,
|
|
|
|
|
complex_timeout: self.complex_body_timeout_override,
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
tls_acceptor: TlsAcceptor::from(config),
|
|
|
|
|
#[cfg(feature="ratelimiting")]
|
|
|
|
|
rate_limits: Arc::new(self.rate_limits),
|
|
|
|
|
#[cfg(feature="user_management")]
|
|
|
|
|
manager: UserManager::new(
|
|
|
|
|
self.database.unwrap_or_else(move|| sled::open(data_dir).unwrap())
|
|
|
|
|
)?,
|
|
|
|
|
};
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
server.serve(listener).await
|
|
|
|
|
/// Start serving requests on a given bound address & port
|
|
|
|
|
///
|
|
|
|
|
/// `addr` can be anything `tokio` can parse, including just a string like
|
|
|
|
|
/// "localhost:1965"
|
|
|
|
|
pub async fn serve_ip(self, addr: impl ToSocketAddrs) -> Result<()> {
|
|
|
|
|
let server = self.build()?;
|
|
|
|
|
let socket = TcpListener::bind(addr).await?;
|
|
|
|
|
server.serve_ip(socket).await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "scgi_srv")]
|
|
|
|
|
/// Start serving requests on a given unix socket
|
|
|
|
|
///
|
|
|
|
|
/// Requires an address in the form of a path to bind to. This is only available when
|
|
|
|
|
/// in `scgi_srv` mode.
|
|
|
|
|
pub async fn serve_unix(self, addr: impl AsRef<std::path::Path>) -> Result<()> {
|
|
|
|
|
let server = self.build()?;
|
|
|
|
|
let socket = UnixListener::bind(addr)?;
|
|
|
|
|
server.serve_unix(socket).await
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for Server {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self::new()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -512,6 +720,7 @@ async fn prune_ratelimit_log(rate_limits: Arc<RoutingNode<RateLimiter<IpAddr>>>)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
fn tls_config(
|
|
|
|
|
cert_path: &PathBuf,
|
|
|
|
|
key_path: &PathBuf,
|
|
|
|
@ -535,20 +744,22 @@ fn tls_config(
|
|
|
|
|
Ok(config.into())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
fn load_cert_chain(cert_path: &PathBuf) -> Result<Vec<Certificate>> {
|
|
|
|
|
let certs = std::fs::File::open(cert_path)
|
|
|
|
|
.with_context(|| format!("Failed to open `{:?}`", cert_path))?;
|
|
|
|
|
let mut certs = BufReader::new(certs);
|
|
|
|
|
let mut certs = std::io::BufReader::new(certs);
|
|
|
|
|
let certs = rustls::internal::pemfile::certs(&mut certs)
|
|
|
|
|
.map_err(|_| anyhow!("failed to load certs `{:?}`", cert_path))?;
|
|
|
|
|
|
|
|
|
|
Ok(certs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
fn load_key(key_path: &PathBuf) -> Result<PrivateKey> {
|
|
|
|
|
let keys = std::fs::File::open(key_path)
|
|
|
|
|
.with_context(|| format!("Failed to open `{:?}`", key_path))?;
|
|
|
|
|
let mut keys = BufReader::new(keys);
|
|
|
|
|
let mut keys = std::io::BufReader::new(keys);
|
|
|
|
|
let mut keys = rustls::internal::pemfile::pkcs8_private_keys(&mut keys)
|
|
|
|
|
.map_err(|_| anyhow!("failed to load key `{:?}`", key_path))?;
|
|
|
|
|
|
|
|
|
@ -559,11 +770,14 @@ fn load_key(key_path: &PathBuf) -> Result<PrivateKey> {
|
|
|
|
|
Ok(key)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
/// A client cert verifier that accepts all connections
|
|
|
|
|
///
|
|
|
|
|
/// Unfortunately, rustls doesn't provide a ClientCertVerifier that accepts self-signed
|
|
|
|
|
/// certificates, so we need to implement this ourselves.
|
|
|
|
|
struct AllowAnonOrSelfsignedClient { }
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
impl AllowAnonOrSelfsignedClient {
|
|
|
|
|
|
|
|
|
|
/// Create a new verifier
|
|
|
|
@ -573,6 +787,7 @@ impl AllowAnonOrSelfsignedClient {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "gemini_srv")]
|
|
|
|
|
impl ClientCertVerifier for AllowAnonOrSelfsignedClient {
|
|
|
|
|
|
|
|
|
|
fn client_auth_root_subjects(
|
|
|
|
|