ant_quic/config/
mod.rs

1use std::{
2    fmt,
3    net::{SocketAddrV4, SocketAddrV6},
4    num::TryFromIntError,
5    sync::Arc,
6};
7
8#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
9use rustls::client::WebPkiServerVerifier;
10#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
11use rustls::pki_types::{CertificateDer, PrivateKeyDer};
12use thiserror::Error;
13
14#[cfg(feature = "bloom")]
15use crate::NoneTokenLog;
16#[cfg(not(feature = "bloom"))]
17use crate::NoneTokenLog;
18#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
19use crate::crypto::rustls::{QuicServerConfig, configured_provider};
20use crate::{
21    DEFAULT_SUPPORTED_VERSIONS, Duration, MAX_CID_SIZE, RandomConnectionIdGenerator, SystemTime,
22    TokenLog, TokenMemoryCache, TokenStore, VarInt, VarIntBoundsExceeded,
23    cid_generator::{ConnectionIdGenerator, HashedConnectionIdGenerator},
24    crypto::{self, HandshakeTokenKey, HmacKey},
25    shared::ConnectionId,
26};
27
28mod transport;
29pub use transport::{AckFrequencyConfig, IdleTimeout, MtuDiscoveryConfig, TransportConfig};
30
31pub mod nat_timeouts;
32pub mod timeouts;
33
34// Production-ready configuration validation
35pub(crate) mod validation;
36
37/// Global configuration for the endpoint, affecting all connections
38///
39/// Default values should be suitable for most internet applications.
40#[derive(Clone)]
41pub struct EndpointConfig {
42    pub(crate) reset_key: Arc<dyn HmacKey>,
43    pub(crate) max_udp_payload_size: VarInt,
44    /// CID generator factory
45    ///
46    /// Create a cid generator for local cid in Endpoint struct
47    pub(crate) connection_id_generator_factory:
48        Arc<dyn Fn() -> Box<dyn ConnectionIdGenerator> + Send + Sync>,
49    pub(crate) supported_versions: Vec<u32>,
50    pub(crate) grease_quic_bit: bool,
51    /// Minimum interval between outgoing stateless reset packets
52    pub(crate) min_reset_interval: Duration,
53    /// Optional seed to be used internally for random number generation
54    pub(crate) rng_seed: Option<[u8; 32]>,
55    /// Address discovery configuration
56    /// Since transport parameters use an enum, we store settings separately here
57    pub(crate) address_discovery_enabled: bool,
58    pub(crate) address_discovery_max_rate: u8,
59    pub(crate) address_discovery_observe_all: bool,
60    /// Post-Quantum Cryptography configuration
61    #[cfg(feature = "pqc")]
62    pub(crate) pqc_config: Option<crate::crypto::pqc::PqcConfig>,
63}
64
65impl EndpointConfig {
66    /// Create a default config with a particular `reset_key`
67    pub fn new(reset_key: Arc<dyn HmacKey>) -> Self {
68        let cid_factory =
69            || -> Box<dyn ConnectionIdGenerator> { Box::<HashedConnectionIdGenerator>::default() };
70        Self {
71            reset_key,
72            max_udp_payload_size: (1500u32 - 28).into(), // Ethernet MTU minus IP + UDP headers
73            connection_id_generator_factory: Arc::new(cid_factory),
74            supported_versions: DEFAULT_SUPPORTED_VERSIONS.to_vec(),
75            grease_quic_bit: true,
76            min_reset_interval: Duration::from_millis(20),
77            rng_seed: None,
78            address_discovery_enabled: true,
79            address_discovery_max_rate: 10,
80            address_discovery_observe_all: false,
81            #[cfg(feature = "pqc")]
82            pqc_config: None,
83        }
84    }
85
86    /// Supply a custom connection ID generator factory
87    ///
88    /// Called once by each `Endpoint` constructed from this configuration to obtain the CID
89    /// generator which will be used to generate the CIDs used for incoming packets on all
90    /// connections involving that  `Endpoint`. A custom CID generator allows applications to embed
91    /// information in local connection IDs, e.g. to support stateless packet-level load balancers.
92    ///
93    /// Defaults to [`HashedConnectionIdGenerator`].
94    pub fn cid_generator<F: Fn() -> Box<dyn ConnectionIdGenerator> + Send + Sync + 'static>(
95        &mut self,
96        factory: F,
97    ) -> &mut Self {
98        self.connection_id_generator_factory = Arc::new(factory);
99        self
100    }
101
102    /// Private key used to send authenticated connection resets to peers who were
103    /// communicating with a previous instance of this endpoint.
104    pub fn reset_key(&mut self, key: Arc<dyn HmacKey>) -> &mut Self {
105        self.reset_key = key;
106        self
107    }
108
109    /// Maximum UDP payload size accepted from peers (excluding UDP and IP overhead).
110    ///
111    /// Must be greater or equal than 1200.
112    ///
113    /// Defaults to 1472, which is the largest UDP payload that can be transmitted in the typical
114    /// 1500 byte Ethernet MTU. Deployments on links with larger MTUs (e.g. loopback or Ethernet
115    /// with jumbo frames) can raise this to improve performance at the cost of a linear increase in
116    /// datagram receive buffer size.
117    pub fn max_udp_payload_size(&mut self, value: u16) -> Result<&mut Self, ConfigError> {
118        if !(1200..=65_527).contains(&value) {
119            return Err(ConfigError::OutOfBounds);
120        }
121
122        self.max_udp_payload_size = value.into();
123        Ok(self)
124    }
125
126    /// Get the current value of [`max_udp_payload_size`](Self::max_udp_payload_size)
127    //
128    // While most parameters don't need to be readable, this must be exposed to allow higher-level
129    // layers, e.g. the `quinn` crate, to determine how large a receive buffer to allocate to
130    // support an externally-defined `EndpointConfig`.
131    //
132    // While `get_` accessors are typically unidiomatic in Rust, we favor concision for setters,
133    // which will be used far more heavily.
134    pub fn get_max_udp_payload_size(&self) -> u64 {
135        self.max_udp_payload_size.into()
136    }
137
138    /// Override supported QUIC versions
139    pub fn supported_versions(&mut self, supported_versions: Vec<u32>) -> &mut Self {
140        self.supported_versions = supported_versions;
141        self
142    }
143
144    /// Whether to accept QUIC packets containing any value for the fixed bit
145    ///
146    /// Enabled by default. Helps protect against protocol ossification and makes traffic less
147    /// identifiable to observers. Disable if helping observers identify this traffic as QUIC is
148    /// desired.
149    pub fn grease_quic_bit(&mut self, value: bool) -> &mut Self {
150        self.grease_quic_bit = value;
151        self
152    }
153
154    /// Minimum interval between outgoing stateless reset packets
155    ///
156    /// Defaults to 20ms. Limits the impact of attacks which flood an endpoint with garbage packets,
157    /// e.g. [ISAKMP/IKE amplification]. Larger values provide a stronger defense, but may delay
158    /// detection of some error conditions by clients. Using a [`ConnectionIdGenerator`] with a low
159    /// rate of false positives in [`validate`](ConnectionIdGenerator::validate) reduces the risk
160    /// incurred by a small minimum reset interval.
161    ///
162    /// [ISAKMP/IKE
163    /// amplification]: https://bughunters.google.com/blog/5960150648750080/preventing-cross-service-udp-loops-in-quic#isakmp-ike-amplification-vs-quic
164    pub fn min_reset_interval(&mut self, value: Duration) -> &mut Self {
165        self.min_reset_interval = value;
166        self
167    }
168
169    /// Optional seed to be used internally for random number generation
170    ///
171    /// By default, quinn will initialize an endpoint's rng using a platform entropy source.
172    /// However, you can seed the rng yourself through this method (e.g. if you need to run quinn
173    /// deterministically or if you are using quinn in an environment that doesn't have a source of
174    /// entropy available).
175    pub fn rng_seed(&mut self, seed: Option<[u8; 32]>) -> &mut Self {
176        self.rng_seed = seed;
177        self
178    }
179
180    /// Check if address discovery is enabled
181    ///
182    /// Checks environment variables first, then falls back to configuration
183    pub fn address_discovery_enabled(&self) -> bool {
184        // Check environment variable override
185        if let Ok(val) = std::env::var("ANT_QUIC_ADDRESS_DISCOVERY_ENABLED") {
186            return val.to_lowercase() == "true" || val == "1";
187        }
188        self.address_discovery_enabled
189    }
190
191    /// Set whether address discovery is enabled
192    pub fn set_address_discovery_enabled(&mut self, enabled: bool) -> &mut Self {
193        self.address_discovery_enabled = enabled;
194        self
195    }
196
197    /// Get the maximum observation rate
198    ///
199    /// Checks environment variables first, then falls back to configuration
200    pub fn max_observation_rate(&self) -> u8 {
201        // Check environment variable override
202        if let Ok(val) = std::env::var("ANT_QUIC_MAX_OBSERVATION_RATE") {
203            if let Ok(rate) = val.parse::<u8>() {
204                return rate.min(63); // Cap at protocol maximum
205            }
206        }
207        self.address_discovery_max_rate
208    }
209
210    /// Set the maximum observation rate (0-63 per second)
211    pub fn set_max_observation_rate(&mut self, rate: u8) -> &mut Self {
212        self.address_discovery_max_rate = rate.min(63);
213        self
214    }
215
216    /// Check if all paths should be observed
217    pub fn observe_all_paths(&self) -> bool {
218        self.address_discovery_observe_all
219    }
220
221    /// Set whether to observe all paths or just active ones
222    pub fn set_observe_all_paths(&mut self, observe_all: bool) -> &mut Self {
223        self.address_discovery_observe_all = observe_all;
224        self
225    }
226
227    /// Builder method for enabling address discovery
228    pub fn address_discovery(mut self, enabled: bool) -> Self {
229        self.address_discovery_enabled = enabled;
230        self
231    }
232
233    /// Builder method for setting observation rate
234    pub fn observation_rate(mut self, rate: u8) -> Self {
235        self.address_discovery_max_rate = rate.min(63);
236        self
237    }
238
239    /// Builder method for observing all paths
240    pub fn with_observe_all_paths(mut self, observe_all: bool) -> Self {
241        self.address_discovery_observe_all = observe_all;
242        self
243    }
244
245    /// Check if address discovery feature is available
246    ///
247    /// Always returns true as address discovery is a core feature
248    pub fn address_discovery_available(&self) -> bool {
249        true
250    }
251
252    /// Set Post-Quantum Cryptography configuration
253    ///
254    /// This configures PQC behavior including algorithm selection, operation modes,
255    /// and performance tuning parameters.
256    #[cfg(feature = "pqc")]
257    pub fn pqc_config(&mut self, config: crate::crypto::pqc::PqcConfig) -> &mut Self {
258        self.pqc_config = Some(config);
259        self
260    }
261}
262
263impl fmt::Debug for EndpointConfig {
264    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
265        fmt.debug_struct("EndpointConfig")
266            // reset_key not debug
267            .field("max_udp_payload_size", &self.max_udp_payload_size)
268            // cid_generator_factory not debug
269            .field("supported_versions", &self.supported_versions)
270            .field("grease_quic_bit", &self.grease_quic_bit)
271            .field("rng_seed", &self.rng_seed)
272            .field("address_discovery_enabled", &self.address_discovery_enabled)
273            .field(
274                "address_discovery_max_rate",
275                &self.address_discovery_max_rate,
276            )
277            .field(
278                "address_discovery_observe_all",
279                &self.address_discovery_observe_all,
280            )
281            .finish_non_exhaustive()
282    }
283}
284
285#[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
286impl Default for EndpointConfig {
287    fn default() -> Self {
288        #[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
289        use aws_lc_rs::hmac;
290        use rand::RngCore;
291        #[cfg(feature = "ring")]
292        use ring::hmac;
293
294        let mut reset_key = [0; 64];
295        rand::thread_rng().fill_bytes(&mut reset_key);
296
297        Self::new(Arc::new(hmac::Key::new(hmac::HMAC_SHA256, &reset_key)))
298    }
299}
300
301/// Parameters governing incoming connections
302///
303/// Default values should be suitable for most internet applications.
304#[derive(Clone)]
305pub struct ServerConfig {
306    /// Transport configuration to use for incoming connections
307    pub transport: Arc<TransportConfig>,
308
309    /// TLS configuration used for incoming connections
310    ///
311    /// Must be set to use TLS 1.3 only.
312    pub crypto: Arc<dyn crypto::ServerConfig>,
313
314    /// Configuration for sending and handling validation tokens
315    pub validation_token: ValidationTokenConfig,
316
317    /// Used to generate one-time AEAD keys to protect handshake tokens
318    pub(crate) token_key: Arc<dyn HandshakeTokenKey>,
319
320    /// Duration after a retry token was issued for which it's considered valid
321    pub(crate) retry_token_lifetime: Duration,
322
323    /// Whether to allow clients to migrate to new addresses
324    ///
325    /// Improves behavior for clients that move between different internet connections or suffer NAT
326    /// rebinding. Enabled by default.
327    pub(crate) migration: bool,
328
329    pub(crate) preferred_address_v4: Option<SocketAddrV4>,
330    pub(crate) preferred_address_v6: Option<SocketAddrV6>,
331
332    pub(crate) max_incoming: usize,
333    pub(crate) incoming_buffer_size: u64,
334    pub(crate) incoming_buffer_size_total: u64,
335
336    pub(crate) time_source: Arc<dyn TimeSource>,
337}
338
339impl ServerConfig {
340    /// Create a default config with a particular handshake token key
341    pub fn new(
342        crypto: Arc<dyn crypto::ServerConfig>,
343        token_key: Arc<dyn HandshakeTokenKey>,
344    ) -> Self {
345        Self {
346            transport: Arc::new(TransportConfig::default()),
347            crypto,
348
349            token_key,
350            retry_token_lifetime: Duration::from_secs(15),
351
352            migration: true,
353
354            validation_token: ValidationTokenConfig::default(),
355
356            preferred_address_v4: None,
357            preferred_address_v6: None,
358
359            max_incoming: 1 << 16,
360            incoming_buffer_size: 10 << 20,
361            incoming_buffer_size_total: 100 << 20,
362
363            time_source: Arc::new(StdSystemTime),
364        }
365    }
366
367    /// Set a custom [`TransportConfig`]
368    pub fn transport_config(&mut self, transport: Arc<TransportConfig>) -> &mut Self {
369        self.transport = transport;
370        self
371    }
372
373    /// Set a custom [`ValidationTokenConfig`]
374    pub fn validation_token_config(
375        &mut self,
376        validation_token: ValidationTokenConfig,
377    ) -> &mut Self {
378        self.validation_token = validation_token;
379        self
380    }
381
382    /// Private key used to authenticate data included in handshake tokens
383    pub fn token_key(&mut self, value: Arc<dyn HandshakeTokenKey>) -> &mut Self {
384        self.token_key = value;
385        self
386    }
387
388    /// Duration after a retry token was issued for which it's considered valid
389    ///
390    /// Defaults to 15 seconds.
391    pub fn retry_token_lifetime(&mut self, value: Duration) -> &mut Self {
392        self.retry_token_lifetime = value;
393        self
394    }
395
396    /// Whether to allow clients to migrate to new addresses
397    ///
398    /// Improves behavior for clients that move between different internet connections or suffer NAT
399    /// rebinding. Enabled by default.
400    pub fn migration(&mut self, value: bool) -> &mut Self {
401        self.migration = value;
402        self
403    }
404
405    /// The preferred IPv4 address that will be communicated to clients during handshaking
406    ///
407    /// If the client is able to reach this address, it will switch to it.
408    pub fn preferred_address_v4(&mut self, address: Option<SocketAddrV4>) -> &mut Self {
409        self.preferred_address_v4 = address;
410        self
411    }
412
413    /// The preferred IPv6 address that will be communicated to clients during handshaking
414    ///
415    /// If the client is able to reach this address, it will switch to it.
416    pub fn preferred_address_v6(&mut self, address: Option<SocketAddrV6>) -> &mut Self {
417        self.preferred_address_v6 = address;
418        self
419    }
420
421    /// Maximum number of [`Incoming`][crate::Incoming] to allow to exist at a time
422    ///
423    /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt
424    /// is received and stops existing when the application either accepts it or otherwise disposes
425    /// of it. While this limit is reached, new incoming connection attempts are immediately
426    /// refused. Larger values have greater worst-case memory consumption, but accommodate greater
427    /// application latency in handling incoming connection attempts.
428    ///
429    /// The default value is set to 65536. With a typical Ethernet MTU of 1500 bytes, this limits
430    /// memory consumption from this to under 100 MiB--a generous amount that still prevents memory
431    /// exhaustion in most contexts.
432    pub fn max_incoming(&mut self, max_incoming: usize) -> &mut Self {
433        self.max_incoming = max_incoming;
434        self
435    }
436
437    /// Maximum number of received bytes to buffer for each [`Incoming`][crate::Incoming]
438    ///
439    /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt
440    /// is received and stops existing when the application either accepts it or otherwise disposes
441    /// of it. This limit governs only packets received within that period, and does not include
442    /// the first packet. Packets received in excess of this limit are dropped, which may cause
443    /// 0-RTT or handshake data to have to be retransmitted.
444    ///
445    /// The default value is set to 10 MiB--an amount such that in most situations a client would
446    /// not transmit that much 0-RTT data faster than the server handles the corresponding
447    /// [`Incoming`][crate::Incoming].
448    pub fn incoming_buffer_size(&mut self, incoming_buffer_size: u64) -> &mut Self {
449        self.incoming_buffer_size = incoming_buffer_size;
450        self
451    }
452
453    /// Maximum number of received bytes to buffer for all [`Incoming`][crate::Incoming]
454    /// collectively
455    ///
456    /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt
457    /// is received and stops existing when the application either accepts it or otherwise disposes
458    /// of it. This limit governs only packets received within that period, and does not include
459    /// the first packet. Packets received in excess of this limit are dropped, which may cause
460    /// 0-RTT or handshake data to have to be retransmitted.
461    ///
462    /// The default value is set to 100 MiB--a generous amount that still prevents memory
463    /// exhaustion in most contexts.
464    pub fn incoming_buffer_size_total(&mut self, incoming_buffer_size_total: u64) -> &mut Self {
465        self.incoming_buffer_size_total = incoming_buffer_size_total;
466        self
467    }
468
469    /// Object to get current [`SystemTime`]
470    ///
471    /// This exists to allow system time to be mocked in tests, or wherever else desired.
472    ///
473    /// Defaults to [`StdSystemTime`], which simply calls [`SystemTime::now()`](SystemTime::now).
474    pub fn time_source(&mut self, time_source: Arc<dyn TimeSource>) -> &mut Self {
475        self.time_source = time_source;
476        self
477    }
478
479    pub(crate) fn has_preferred_address(&self) -> bool {
480        self.preferred_address_v4.is_some() || self.preferred_address_v6.is_some()
481    }
482}
483
484#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
485impl ServerConfig {
486    /// Create a server config with the given certificate chain to be presented to clients
487    ///
488    /// Uses a randomized handshake token key.
489    pub fn with_single_cert(
490        cert_chain: Vec<CertificateDer<'static>>,
491        key: PrivateKeyDer<'static>,
492    ) -> Result<Self, rustls::Error> {
493        Ok(Self::with_crypto(Arc::new(QuicServerConfig::new(
494            cert_chain, key,
495        )?)))
496    }
497}
498
499#[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
500impl ServerConfig {
501    /// Create a server config with the given [`crypto::ServerConfig`]
502    ///
503    /// Uses a randomized handshake token key.
504    pub fn with_crypto(crypto: Arc<dyn crypto::ServerConfig>) -> Self {
505        #[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
506        use aws_lc_rs::hkdf;
507        use rand::RngCore;
508        #[cfg(feature = "ring")]
509        use ring::hkdf;
510
511        let rng = &mut rand::thread_rng();
512        let mut master_key = [0u8; 64];
513        rng.fill_bytes(&mut master_key);
514        let master_key = hkdf::Salt::new(hkdf::HKDF_SHA256, &[]).extract(&master_key);
515
516        Self::new(crypto, Arc::new(master_key))
517    }
518}
519
520impl fmt::Debug for ServerConfig {
521    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
522        fmt.debug_struct("ServerConfig")
523            .field("transport", &self.transport)
524            // crypto not debug
525            // token not debug
526            .field("retry_token_lifetime", &self.retry_token_lifetime)
527            .field("validation_token", &self.validation_token)
528            .field("migration", &self.migration)
529            .field("preferred_address_v4", &self.preferred_address_v4)
530            .field("preferred_address_v6", &self.preferred_address_v6)
531            .field("max_incoming", &self.max_incoming)
532            .field("incoming_buffer_size", &self.incoming_buffer_size)
533            .field(
534                "incoming_buffer_size_total",
535                &self.incoming_buffer_size_total,
536            )
537            // system_time_clock not debug
538            .finish_non_exhaustive()
539    }
540}
541
542/// Configuration for sending and handling validation tokens in incoming connections
543///
544/// Default values should be suitable for most internet applications.
545///
546/// ## QUIC Tokens
547///
548/// The QUIC protocol defines a concept of "[address validation][1]". Essentially, one side of a
549/// QUIC connection may appear to be receiving QUIC packets from a particular remote UDP address,
550/// but it will only consider that remote address "validated" once it has convincing evidence that
551/// the address is not being [spoofed][2].
552///
553/// Validation is important primarily because of QUIC's "anti-amplification limit." This limit
554/// prevents a QUIC server from sending a client more than three times the number of bytes it has
555/// received from the client on a given address until that address is validated. This is designed
556/// to mitigate the ability of attackers to use QUIC-based servers as reflectors in [amplification
557/// attacks][3].
558///
559/// A path may become validated in several ways. The server is always considered validated by the
560/// client. The client usually begins in an unvalidated state upon first connecting or migrating,
561/// but then becomes validated through various mechanisms that usually take one network round trip.
562/// However, in some cases, a client which has previously attempted to connect to a server may have
563/// been given a one-time use cryptographically secured "token" that it can send in a subsequent
564/// connection attempt to be validated immediately.
565///
566/// There are two ways these tokens can originate:
567///
568/// - If the server responds to an incoming connection with `retry`, a "retry token" is minted and
569///   sent to the client, which the client immediately uses to attempt to connect again. Retry
570///   tokens operate on short timescales, such as 15 seconds.
571/// - If a client's path within an active connection is validated, the server may send the client
572///   one or more "validation tokens," which the client may store for use in later connections to
573///   the same server. Validation tokens may be valid for much longer lifetimes than retry token.
574///
575/// The usage of validation tokens is most impactful in situations where 0-RTT data is also being
576/// used--in particular, in situations where the server sends the client more than three times more
577/// 0.5-RTT data than it has received 0-RTT data. Since the successful completion of a connection
578/// handshake implicitly causes the client's address to be validated, transmission of 0.5-RTT data
579/// is the main situation where a server might be sending application data to an address that could
580/// be validated by token usage earlier than it would become validated without token usage.
581///
582/// [1]: https://www.rfc-editor.org/rfc/rfc9000.html#section-8
583/// [2]: https://en.wikipedia.org/wiki/IP_address_spoofing
584/// [3]: https://en.wikipedia.org/wiki/Denial-of-service_attack#Amplification
585///
586/// These tokens should not be confused with "stateless reset tokens," which are similarly named
587/// but entirely unrelated.
588#[derive(Clone)]
589pub struct ValidationTokenConfig {
590    pub(crate) lifetime: Duration,
591    pub(crate) log: Arc<dyn TokenLog>,
592    pub(crate) sent: u32,
593}
594
595impl ValidationTokenConfig {
596    /// Duration after an address validation token was issued for which it's considered valid
597    ///
598    /// This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens.
599    ///
600    /// Defaults to 2 weeks.
601    pub fn lifetime(&mut self, value: Duration) -> &mut Self {
602        self.lifetime = value;
603        self
604    }
605
606    #[allow(rustdoc::redundant_explicit_links)] // which links are redundant depends on features
607    /// Set a custom token log
608    ///
609    /// If the `bloom` feature is enabled (which it is by default), defaults to a bloom
610    /// token log, which is suitable for most internet applications.
611    ///
612    /// If the `bloom` feature is disabled, defaults to a none token log,
613    /// which makes the server ignore all address validation tokens (that is, tokens originating
614    /// from NEW_TOKEN frames--retry tokens are not affected).
615    pub fn log(&mut self, log: Arc<dyn TokenLog>) -> &mut Self {
616        self.log = log;
617        self
618    }
619
620    /// Number of address validation tokens sent to a client when its path is validated
621    ///
622    /// This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens.
623    ///
624    /// If the `bloom` feature is enabled (which it is by default), defaults to 2. Otherwise,
625    /// defaults to 0.
626    pub fn sent(&mut self, value: u32) -> &mut Self {
627        self.sent = value;
628        self
629    }
630}
631
632impl Default for ValidationTokenConfig {
633    fn default() -> Self {
634        let log = Arc::new(NoneTokenLog);
635        Self {
636            lifetime: Duration::from_secs(2 * 7 * 24 * 60 * 60),
637            log,
638            sent: if cfg!(feature = "bloom") { 2 } else { 0 },
639        }
640    }
641}
642
643impl fmt::Debug for ValidationTokenConfig {
644    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
645        fmt.debug_struct("ServerValidationTokenConfig")
646            .field("lifetime", &self.lifetime)
647            // log not debug
648            .field("sent", &self.sent)
649            .finish_non_exhaustive()
650    }
651}
652
653/// Configuration for outgoing connections
654///
655/// Default values should be suitable for most internet applications.
656#[derive(Clone)]
657#[non_exhaustive]
658pub struct ClientConfig {
659    /// Transport configuration to use
660    pub(crate) transport: Arc<TransportConfig>,
661
662    /// Cryptographic configuration to use
663    pub(crate) crypto: Arc<dyn crypto::ClientConfig>,
664
665    /// Validation token store to use
666    pub(crate) token_store: Arc<dyn TokenStore>,
667
668    /// Provider that populates the destination connection ID of Initial Packets
669    pub(crate) initial_dst_cid_provider: Arc<dyn Fn() -> ConnectionId + Send + Sync>,
670
671    /// QUIC protocol version to use
672    pub(crate) version: u32,
673}
674
675impl ClientConfig {
676    /// Create a default config with a particular cryptographic config
677    pub fn new(crypto: Arc<dyn crypto::ClientConfig>) -> Self {
678        Self {
679            transport: Default::default(),
680            crypto,
681            token_store: Arc::new(TokenMemoryCache::default()),
682            initial_dst_cid_provider: Arc::new(|| {
683                RandomConnectionIdGenerator::new(MAX_CID_SIZE).generate_cid()
684            }),
685            version: 1,
686        }
687    }
688
689    /// Configure how to populate the destination CID of the initial packet when attempting to
690    /// establish a new connection
691    ///
692    /// By default, it's populated with random bytes with reasonable length, so unless you have
693    /// a good reason, you do not need to change it.
694    ///
695    /// When prefer to override the default, please note that the generated connection ID MUST be
696    /// at least 8 bytes long and unpredictable, as per section 7.2 of RFC 9000.
697    pub fn initial_dst_cid_provider(
698        &mut self,
699        initial_dst_cid_provider: Arc<dyn Fn() -> ConnectionId + Send + Sync>,
700    ) -> &mut Self {
701        self.initial_dst_cid_provider = initial_dst_cid_provider;
702        self
703    }
704
705    /// Set a custom [`TransportConfig`]
706    pub fn transport_config(&mut self, transport: Arc<TransportConfig>) -> &mut Self {
707        self.transport = transport;
708        self
709    }
710
711    /// Set a custom token store
712    ///
713    /// Defaults to a memory cache, which is suitable for most internet applications.
714    pub fn token_store(&mut self, store: Arc<dyn TokenStore>) -> &mut Self {
715        self.token_store = store;
716        self
717    }
718
719    /// Set the QUIC version to use
720    pub fn version(&mut self, version: u32) -> &mut Self {
721        self.version = version;
722        self
723    }
724}
725
726#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
727impl ClientConfig {
728    /// Create a client configuration that trusts the platform's native roots
729    #[deprecated(since = "0.11.13", note = "use `try_with_platform_verifier()` instead")]
730    #[cfg(feature = "platform-verifier")]
731    pub fn with_platform_verifier() -> Self {
732        Self::try_with_platform_verifier().expect("use try_with_platform_verifier() instead")
733    }
734
735    /// Create a client configuration that trusts the platform's native roots
736    #[cfg(feature = "platform-verifier")]
737    pub fn try_with_platform_verifier() -> Result<Self, rustls::Error> {
738        Ok(Self::new(Arc::new(
739            crypto::rustls::QuicClientConfig::with_platform_verifier()?,
740        )))
741    }
742
743    /// Create a client configuration that trusts specified trust anchors
744    pub fn with_root_certificates(
745        roots: Arc<rustls::RootCertStore>,
746    ) -> Result<Self, rustls::client::VerifierBuilderError> {
747        Ok(Self::new(Arc::new(crypto::rustls::QuicClientConfig::new(
748            WebPkiServerVerifier::builder_with_provider(roots, configured_provider()).build()?,
749        ))))
750    }
751}
752
753impl fmt::Debug for ClientConfig {
754    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
755        fmt.debug_struct("ClientConfig")
756            .field("transport", &self.transport)
757            // crypto not debug
758            // token_store not debug
759            .field("version", &self.version)
760            .finish_non_exhaustive()
761    }
762}
763
764/// Errors in the configuration of an endpoint
765#[derive(Debug, Error, Clone, PartialEq, Eq)]
766#[non_exhaustive]
767pub enum ConfigError {
768    /// Value exceeds supported bounds
769    #[error("value exceeds supported bounds")]
770    OutOfBounds,
771}
772
773impl From<TryFromIntError> for ConfigError {
774    fn from(_: TryFromIntError) -> Self {
775        Self::OutOfBounds
776    }
777}
778
779impl From<VarIntBoundsExceeded> for ConfigError {
780    fn from(_: VarIntBoundsExceeded) -> Self {
781        Self::OutOfBounds
782    }
783}
784
785/// Object to get current [`SystemTime`]
786///
787/// This exists to allow system time to be mocked in tests, or wherever else desired.
788pub trait TimeSource: Send + Sync {
789    /// Get [`SystemTime::now()`](SystemTime::now) or the mocked equivalent
790    fn now(&self) -> SystemTime;
791}
792
793/// Default implementation of [`TimeSource`]
794///
795/// Implements `now` by calling [`SystemTime::now()`](SystemTime::now).
796pub struct StdSystemTime;
797
798impl TimeSource for StdSystemTime {
799    fn now(&self) -> SystemTime {
800        SystemTime::now()
801    }
802}