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