use std::net::{SocketAddr, UdpSocket};
use quinn::{default_runtime, Endpoint, EndpointConfig, ServerConfig};
use rustls_pki_types::{pem::PemObject, CertificateDer, PrivateKeyDer};
use crate::common::{
connection::get_default_transport_config,
constants::{LOCALHOST4, LOCALHOST6, ZERO_ADDR4, ZERO_ADDR6},
result::DynResult,
tls_debug::get_localhost_keys,
};
use super::ServerOptions;
pub fn read_certs_from_file(
options: &ServerOptions,
) -> DynResult<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)> {
if options.domain == "localhost"
&& options.public_pem.is_none()
&& options.private_pem.is_none()
{
return Ok(get_localhost_keys());
}
let certs = CertificateDer::pem_file_iter(
options
.public_pem
.as_ref()
.ok_or("public_pem argument is required if domain isn't localhost")?,
)?
.collect::<Result<Vec<_>, _>>()?;
log::trace!("Read {:?}", options.public_pem.as_ref());
let key = PrivateKeyDer::from_pem_file(
options
.private_pem
.as_ref()
.ok_or("private_pem argument is required if domain isn't localhost")?,
)?;
log::trace!("Read {:?}", options.private_pem.as_ref());
Ok((certs, key))
}
pub fn create_server_config(options: &ServerOptions) -> DynResult<ServerConfig> {
let (cert_chain, key) = read_certs_from_file(options)?;
let mut server_config = ServerConfig::with_single_cert(cert_chain, key)?;
server_config.transport_config(get_default_transport_config());
Ok(server_config)
}
pub async fn listen(options: &ServerOptions) -> DynResult<Vec<Endpoint>> {
let addresses = if options.public {
vec![ZERO_ADDR6, ZERO_ADDR4]
} else {
vec![LOCALHOST6, LOCALHOST4]
};
let server_config = create_server_config(options)?;
let mut endpoints = vec![];
let mut udp_err: Option<std::io::Error> = None;
for addr in addresses {
let addr = SocketAddr::new(addr, options.port);
match UdpSocket::bind(addr) {
Ok(udp_sock) => endpoints.push(Endpoint::new(
EndpointConfig::default(),
Some(server_config.clone()),
udp_sock,
default_runtime().unwrap(),
)?),
Err(err) => udp_err = Some(err),
}
}
if endpoints.is_empty() {
match udp_err {
Some(err) => Err(err.into()),
None => Err("Could not listen".into()),
}
} else {
Ok(endpoints)
}
}