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