use std::{
fmt,
net::{SocketAddrV4, SocketAddrV6},
num::TryFromIntError,
sync::Arc,
};
#[cfg(all(feature = "rustls", any(feature = "aws-lc-rs", feature = "ring")))]
use rustls::client::WebPkiServerVerifier;
#[cfg(all(feature = "rustls", any(feature = "aws-lc-rs", feature = "ring")))]
use rustls::pki_types::{CertificateDer, PrivateKeyDer};
use thiserror::Error;
#[cfg(feature = "bloom")]
use crate::BloomTokenLog;
#[cfg(not(feature = "bloom"))]
use crate::NoneTokenLog;
#[cfg(all(feature = "rustls", any(feature = "aws-lc-rs", feature = "ring")))]
use crate::crypto::rustls::{QuicServerConfig, configured_provider};
use crate::{
DEFAULT_SUPPORTED_VERSIONS, Duration, MAX_CID_SIZE, RandomConnectionIdGenerator, SystemTime,
TokenLog, TokenMemoryCache, TokenStore, VarInt, VarIntBoundsExceeded,
cid_generator::{ConnectionIdGenerator, HashedConnectionIdGenerator},
crypto::{self, HandshakeTokenKey, HmacKey},
shared::ConnectionId,
};
#[cfg(feature = "qlog")]
mod qlog;
mod transport;
#[cfg(feature = "qlog")]
pub use qlog::{QlogConfig, QlogFactory, QlogFileFactory};
pub use transport::{AckFrequencyConfig, IdleTimeout, MtuDiscoveryConfig, TransportConfig};
#[cfg(doc)]
pub use transport::DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED;
#[derive(Clone)]
pub struct EndpointConfig {
pub(crate) reset_key: Arc<dyn HmacKey>,
pub(crate) max_udp_payload_size: VarInt,
pub(crate) connection_id_generator_factory:
Arc<dyn Fn() -> Box<dyn ConnectionIdGenerator> + Send + Sync>,
pub(crate) supported_versions: Vec<u32>,
pub(crate) grease_quic_bit: bool,
pub(crate) min_reset_interval: Duration,
pub(crate) rng_seed: Option<[u8; 32]>,
}
impl EndpointConfig {
pub fn new(reset_key: Arc<dyn HmacKey>) -> Self {
let cid_factory =
|| -> Box<dyn ConnectionIdGenerator> { Box::<HashedConnectionIdGenerator>::default() };
Self {
reset_key,
max_udp_payload_size: (1500u32 - 28).into(), connection_id_generator_factory: Arc::new(cid_factory),
supported_versions: DEFAULT_SUPPORTED_VERSIONS.to_vec(),
grease_quic_bit: true,
min_reset_interval: Duration::from_millis(20),
rng_seed: None,
}
}
pub fn cid_generator<F: Fn() -> Box<dyn ConnectionIdGenerator> + Send + Sync + 'static>(
&mut self,
factory: F,
) -> &mut Self {
self.connection_id_generator_factory = Arc::new(factory);
self
}
pub fn reset_key(&mut self, key: Arc<dyn HmacKey>) -> &mut Self {
self.reset_key = key;
self
}
pub fn max_udp_payload_size(&mut self, value: u16) -> Result<&mut Self, ConfigError> {
if !(1200..=65_527).contains(&value) {
return Err(ConfigError::OutOfBounds);
}
self.max_udp_payload_size = value.into();
Ok(self)
}
pub fn get_max_udp_payload_size(&self) -> u64 {
self.max_udp_payload_size.into()
}
pub fn supported_versions(&mut self, supported_versions: Vec<u32>) -> &mut Self {
self.supported_versions = supported_versions;
self
}
pub fn grease_quic_bit(&mut self, value: bool) -> &mut Self {
self.grease_quic_bit = value;
self
}
pub fn min_reset_interval(&mut self, value: Duration) -> &mut Self {
self.min_reset_interval = value;
self
}
pub fn rng_seed(&mut self, seed: Option<[u8; 32]>) -> &mut Self {
self.rng_seed = seed;
self
}
}
impl fmt::Debug for EndpointConfig {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("EndpointConfig")
.field("max_udp_payload_size", &self.max_udp_payload_size)
.field("supported_versions", &self.supported_versions)
.field("grease_quic_bit", &self.grease_quic_bit)
.field("rng_seed", &self.rng_seed)
.finish_non_exhaustive()
}
}
#[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
impl Default for EndpointConfig {
fn default() -> Self {
#[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
use aws_lc_rs::hmac;
use rand::Rng;
#[cfg(feature = "ring")]
use ring::hmac;
let mut reset_key = [0; 64];
rand::rng().fill_bytes(&mut reset_key);
Self::new(Arc::new(hmac::Key::new(hmac::HMAC_SHA256, &reset_key)))
}
}
#[derive(Clone)]
pub struct ServerConfig {
pub transport: Arc<TransportConfig>,
pub crypto: Arc<dyn crypto::ServerConfig>,
pub validation_token: ValidationTokenConfig,
pub(crate) token_key: Arc<dyn HandshakeTokenKey>,
pub(crate) retry_token_lifetime: Duration,
pub(crate) migration: bool,
pub(crate) preferred_address_v4: Option<SocketAddrV4>,
pub(crate) preferred_address_v6: Option<SocketAddrV6>,
pub(crate) max_incoming: usize,
pub(crate) incoming_buffer_size: u64,
pub(crate) incoming_buffer_size_total: u64,
pub(crate) time_source: Arc<dyn TimeSource>,
}
impl ServerConfig {
pub fn new(
crypto: Arc<dyn crypto::ServerConfig>,
token_key: Arc<dyn HandshakeTokenKey>,
) -> Self {
Self {
transport: Arc::new(TransportConfig::default()),
crypto,
token_key,
retry_token_lifetime: Duration::from_secs(15),
migration: true,
validation_token: ValidationTokenConfig::default(),
preferred_address_v4: None,
preferred_address_v6: None,
max_incoming: 1 << 16,
incoming_buffer_size: 10 << 20,
incoming_buffer_size_total: 100 << 20,
time_source: Arc::new(StdSystemTime),
}
}
pub fn transport_config(&mut self, transport: Arc<TransportConfig>) -> &mut Self {
self.transport = transport;
self
}
pub fn validation_token_config(
&mut self,
validation_token: ValidationTokenConfig,
) -> &mut Self {
self.validation_token = validation_token;
self
}
pub fn token_key(&mut self, value: Arc<dyn HandshakeTokenKey>) -> &mut Self {
self.token_key = value;
self
}
pub fn retry_token_lifetime(&mut self, value: Duration) -> &mut Self {
self.retry_token_lifetime = value;
self
}
pub fn migration(&mut self, value: bool) -> &mut Self {
self.migration = value;
self
}
pub fn preferred_address_v4(&mut self, address: Option<SocketAddrV4>) -> &mut Self {
self.preferred_address_v4 = address;
self
}
pub fn preferred_address_v6(&mut self, address: Option<SocketAddrV6>) -> &mut Self {
self.preferred_address_v6 = address;
self
}
pub fn max_incoming(&mut self, max_incoming: usize) -> &mut Self {
self.max_incoming = max_incoming;
self
}
pub fn incoming_buffer_size(&mut self, incoming_buffer_size: u64) -> &mut Self {
self.incoming_buffer_size = incoming_buffer_size;
self
}
pub fn incoming_buffer_size_total(&mut self, incoming_buffer_size_total: u64) -> &mut Self {
self.incoming_buffer_size_total = incoming_buffer_size_total;
self
}
pub fn time_source(&mut self, time_source: Arc<dyn TimeSource>) -> &mut Self {
self.time_source = time_source;
self
}
pub(crate) fn has_preferred_address(&self) -> bool {
self.preferred_address_v4.is_some() || self.preferred_address_v6.is_some()
}
}
#[cfg(all(feature = "rustls", any(feature = "aws-lc-rs", feature = "ring")))]
impl ServerConfig {
pub fn with_single_cert(
cert_chain: Vec<CertificateDer<'static>>,
key: PrivateKeyDer<'static>,
) -> Result<Self, rustls::Error> {
Ok(Self::with_crypto(Arc::new(QuicServerConfig::new(
cert_chain, key,
)?)))
}
}
#[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
impl ServerConfig {
pub fn with_crypto(crypto: Arc<dyn crypto::ServerConfig>) -> Self {
use crate::crypto::ring_like::RetryTokenKey;
let retry_token_key = RetryTokenKey::new(&mut rand::rng());
Self::new(crypto, Arc::new(retry_token_key))
}
}
impl fmt::Debug for ServerConfig {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("ServerConfig")
.field("transport", &self.transport)
.field("retry_token_lifetime", &self.retry_token_lifetime)
.field("validation_token", &self.validation_token)
.field("migration", &self.migration)
.field("preferred_address_v4", &self.preferred_address_v4)
.field("preferred_address_v6", &self.preferred_address_v6)
.field("max_incoming", &self.max_incoming)
.field("incoming_buffer_size", &self.incoming_buffer_size)
.field(
"incoming_buffer_size_total",
&self.incoming_buffer_size_total,
)
.finish_non_exhaustive()
}
}
#[derive(Clone)]
pub struct ValidationTokenConfig {
pub(crate) lifetime: Duration,
pub(crate) log: Arc<dyn TokenLog>,
pub(crate) sent: u32,
}
impl ValidationTokenConfig {
pub fn lifetime(&mut self, value: Duration) -> &mut Self {
self.lifetime = value;
self
}
#[allow(rustdoc::redundant_explicit_links)] pub fn log(&mut self, log: Arc<dyn TokenLog>) -> &mut Self {
self.log = log;
self
}
pub fn sent(&mut self, value: u32) -> &mut Self {
self.sent = value;
self
}
}
impl Default for ValidationTokenConfig {
fn default() -> Self {
#[cfg(feature = "bloom")]
let log = Arc::new(BloomTokenLog::default());
#[cfg(not(feature = "bloom"))]
let log = Arc::new(NoneTokenLog);
Self {
lifetime: Duration::from_secs(2 * 7 * 24 * 60 * 60),
log,
sent: if cfg!(feature = "bloom") { 2 } else { 0 },
}
}
}
impl fmt::Debug for ValidationTokenConfig {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("ServerValidationTokenConfig")
.field("lifetime", &self.lifetime)
.field("sent", &self.sent)
.finish_non_exhaustive()
}
}
#[derive(Clone)]
#[non_exhaustive]
pub struct ClientConfig {
pub(crate) transport: Arc<TransportConfig>,
pub(crate) crypto: Arc<dyn crypto::ClientConfig>,
pub(crate) token_store: Arc<dyn TokenStore>,
pub(crate) initial_dst_cid_provider: Arc<dyn Fn() -> ConnectionId + Send + Sync>,
pub(crate) version: u32,
}
impl ClientConfig {
pub fn new(crypto: Arc<dyn crypto::ClientConfig>) -> Self {
Self {
transport: Default::default(),
crypto,
token_store: Arc::new(TokenMemoryCache::default()),
initial_dst_cid_provider: Arc::new(|| {
RandomConnectionIdGenerator::new(MAX_CID_SIZE).generate_cid()
}),
version: 1,
}
}
pub fn initial_dst_cid_provider(
&mut self,
initial_dst_cid_provider: Arc<dyn Fn() -> ConnectionId + Send + Sync>,
) -> &mut Self {
self.initial_dst_cid_provider = initial_dst_cid_provider;
self
}
pub fn transport_config(&mut self, transport: Arc<TransportConfig>) -> &mut Self {
self.transport = transport;
self
}
pub fn token_store(&mut self, store: Arc<dyn TokenStore>) -> &mut Self {
self.token_store = store;
self
}
pub fn version(&mut self, version: u32) -> &mut Self {
self.version = version;
self
}
}
#[cfg(all(feature = "rustls", any(feature = "aws-lc-rs", feature = "ring")))]
impl ClientConfig {
#[cfg(feature = "platform-verifier")]
pub fn try_with_platform_verifier() -> Result<Self, rustls::Error> {
Ok(Self::new(Arc::new(
crypto::rustls::QuicClientConfig::with_platform_verifier()?,
)))
}
pub fn with_root_certificates(
roots: Arc<rustls::RootCertStore>,
) -> Result<Self, rustls::client::VerifierBuilderError> {
Ok(Self::new(Arc::new(crypto::rustls::QuicClientConfig::new(
WebPkiServerVerifier::builder_with_provider(roots, configured_provider()).build()?,
))))
}
}
impl fmt::Debug for ClientConfig {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("ClientConfig")
.field("transport", &self.transport)
.field("version", &self.version)
.finish_non_exhaustive()
}
}
#[derive(Debug, Error, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ConfigError {
#[error("value exceeds supported bounds")]
OutOfBounds,
}
impl From<TryFromIntError> for ConfigError {
fn from(_: TryFromIntError) -> Self {
Self::OutOfBounds
}
}
impl From<VarIntBoundsExceeded> for ConfigError {
fn from(_: VarIntBoundsExceeded) -> Self {
Self::OutOfBounds
}
}
pub trait TimeSource: Send + Sync {
fn now(&self) -> SystemTime;
}
pub struct StdSystemTime;
impl TimeSource for StdSystemTime {
fn now(&self) -> SystemTime {
SystemTime::now()
}
}