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