zero-trust-rps 0.0.5

Online Multiplayer Rock Paper Scissors
Documentation
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)
    }
}