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