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