use std::{net::SocketAddr, sync::Arc}; use anyhow::{Context, Result}; use quinn::{Endpoint, ServerConfig, TransportConfig, crypto::rustls::QuicServerConfig}; use rustls::pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer}; use crate::RelayConfig; const RELAY_ALPN: &[u8] = b"lanparty-l2/1"; const DATAGRAM_BUFFER_BYTES: usize = 4 * 1024 * 1024; #[derive(Debug)] pub struct RelayServer { endpoint: Endpoint, } impl RelayServer { pub fn bind(config: &RelayConfig) -> Result { let server_config = development_server_config()?; let endpoint = Endpoint::server(server_config, config.listen().socket_addr()) .context("failed to bind QUIC relay endpoint")?; Ok(Self { endpoint }) } pub fn local_addr(&self) -> Result { self.endpoint .local_addr() .context("failed to read relay local address") } pub async fn wait_for_shutdown(self) -> Result<()> { tokio::signal::ctrl_c() .await .context("failed to wait for Ctrl-C")?; self.shutdown("relay shutting down").await; Ok(()) } pub async fn shutdown(self, reason: &str) { self.endpoint.close(0_u32.into(), reason.as_bytes()); self.endpoint.wait_idle().await; } } fn development_server_config() -> Result { let certified_key = rcgen::generate_simple_self_signed(vec!["lanparty-relay.local".into()]) .context("failed to generate development relay certificate")?; let cert_chain = vec![certified_key.cert.der().clone()]; let private_key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( certified_key.signing_key.serialize_der(), )); let mut tls_config = rustls::ServerConfig::builder() .with_no_client_auth() .with_single_cert(cert_chain, private_key) .context("failed to build relay TLS config")?; tls_config.alpn_protocols = vec![RELAY_ALPN.to_vec()]; let mut server_config = ServerConfig::with_crypto(Arc::new( QuicServerConfig::try_from(tls_config).context("failed to build QUIC TLS config")?, )); let mut transport = TransportConfig::default(); transport.datagram_receive_buffer_size(Some(DATAGRAM_BUFFER_BYTES)); transport.datagram_send_buffer_size(DATAGRAM_BUFFER_BYTES); server_config.transport_config(Arc::new(transport)); Ok(server_config) } #[cfg(test)] mod tests { use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use crate::{DEFAULT_MAX_CLIENTS_PER_ROOM, ListenEndpoint}; use super::*; #[tokio::test] async fn binds_quic_endpoint_on_configured_address() { let config = RelayConfig::new( ListenEndpoint::new(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0)), DEFAULT_MAX_CLIENTS_PER_ROOM, ) .unwrap(); let server = RelayServer::bind(&config).unwrap(); let local_addr = server.local_addr().unwrap(); assert_eq!(local_addr.ip(), IpAddr::V4(Ipv4Addr::LOCALHOST)); assert_ne!(local_addr.port(), 0); server.shutdown("test complete").await; } }