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;
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
370#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
371impl ServerConfig {
372 /// Create a server config with the given certificate chain to be presented to clients
373 ///
374 /// Uses a randomized handshake token key.
375 pub fn with_single_cert(
376 cert_chain: Vec<CertificateDer<'static>>,
377 key: PrivateKeyDer<'static>,
378 ) -> Result<Self, rustls::Error> {
379 Ok(Self::with_crypto(Arc::new(QuicServerConfig::new(
380 cert_chain, key,
381 )?)))
382 }
383}
384
385#[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
386impl ServerConfig {
387 /// Create a server config with the given [`crypto::ServerConfig`]
388 ///
389 /// Uses a randomized handshake token key.
390 pub fn with_crypto(crypto: Arc<dyn crypto::ServerConfig>) -> Self {
391 #[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
392 use aws_lc_rs::hkdf;
393 use rand::RngCore;
394 #[cfg(feature = "ring")]
395 use ring::hkdf;
396
397 let rng = &mut rand::rng();
398 let mut master_key = [0u8; 64];
399 rng.fill_bytes(&mut master_key);
400 let master_key = hkdf::Salt::new(hkdf::HKDF_SHA256, &[]).extract(&master_key);
401
402 Self::new(crypto, Arc::new(master_key))
403 }
404}
405
406impl fmt::Debug for ServerConfig {
407 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
408 fmt.debug_struct("ServerConfig")
409 .field("transport", &self.transport)
410 // crypto not debug
411 // token not debug
412 .field("retry_token_lifetime", &self.retry_token_lifetime)
413 .field("validation_token", &self.validation_token)
414 .field("migration", &self.migration)
415 .field("preferred_address_v4", &self.preferred_address_v4)
416 .field("preferred_address_v6", &self.preferred_address_v6)
417 .field("max_incoming", &self.max_incoming)
418 .field("incoming_buffer_size", &self.incoming_buffer_size)
419 .field(
420 "incoming_buffer_size_total",
421 &self.incoming_buffer_size_total,
422 )
423 // system_time_clock not debug
424 .finish_non_exhaustive()
425 }
426}
427
428/// Configuration for sending and handling validation tokens in incoming connections
429///
430/// Default values should be suitable for most internet applications.
431///
432/// ## QUIC Tokens
433///
434/// The QUIC protocol defines a concept of "[address validation][1]". Essentially, one side of a
435/// QUIC connection may appear to be receiving QUIC packets from a particular remote UDP address,
436/// but it will only consider that remote address "validated" once it has convincing evidence that
437/// the address is not being [spoofed][2].
438///
439/// Validation is important primarily because of QUIC's "anti-amplification limit." This limit
440/// prevents a QUIC server from sending a client more than three times the number of bytes it has
441/// received from the client on a given address until that address is validated. This is designed
442/// to mitigate the ability of attackers to use QUIC-based servers as reflectors in [amplification
443/// attacks][3].
444///
445/// A path may become validated in several ways. The server is always considered validated by the
446/// client. The client usually begins in an unvalidated state upon first connecting or migrating,
447/// but then becomes validated through various mechanisms that usually take one network round trip.
448/// However, in some cases, a client which has previously attempted to connect to a server may have
449/// been given a one-time use cryptographically secured "token" that it can send in a subsequent
450/// connection attempt to be validated immediately.
451///
452/// There are two ways these tokens can originate:
453///
454/// - If the server responds to an incoming connection with `retry`, a "retry token" is minted and
455/// sent to the client, which the client immediately uses to attempt to connect again. Retry
456/// tokens operate on short timescales, such as 15 seconds.
457/// - If a client's path within an active connection is validated, the server may send the client
458/// one or more "validation tokens," which the client may store for use in later connections to
459/// the same server. Validation tokens may be valid for much longer lifetimes than retry token.
460///
461/// The usage of validation tokens is most impactful in situations where 0-RTT data is also being
462/// used--in particular, in situations where the server sends the client more than three times more
463/// 0.5-RTT data than it has received 0-RTT data. Since the successful completion of a connection
464/// handshake implicitly causes the client's address to be validated, transmission of 0.5-RTT data
465/// is the main situation where a server might be sending application data to an address that could
466/// be validated by token usage earlier than it would become validated without token usage.
467///
468/// [1]: https://www.rfc-editor.org/rfc/rfc9000.html#section-8
469/// [2]: https://en.wikipedia.org/wiki/IP_address_spoofing
470/// [3]: https://en.wikipedia.org/wiki/Denial-of-service_attack#Amplification
471///
472/// These tokens should not be confused with "stateless reset tokens," which are similarly named
473/// but entirely unrelated.
474#[derive(Clone)]
475pub struct ValidationTokenConfig {
476 pub(crate) lifetime: Duration,
477 pub(crate) log: Arc<dyn TokenLog>,
478 pub(crate) sent: u32,
479}
480
481impl ValidationTokenConfig {
482 /// Duration after an address validation token was issued for which it's considered valid
483 ///
484 /// This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens.
485 ///
486 /// Defaults to 2 weeks.
487 pub fn lifetime(&mut self, value: Duration) -> &mut Self {
488 self.lifetime = value;
489 self
490 }
491
492 #[allow(rustdoc::redundant_explicit_links)] // which links are redundant depends on features
493 /// Set a custom [`TokenLog`]
494 ///
495 /// If the `bloom` feature is enabled (which it is by default), defaults to a default
496 /// [`BloomTokenLog`][crate::BloomTokenLog], which is suitable for most internet applications.
497 ///
498 /// If the `bloom` feature is disabled, defaults to [`NoneTokenLog`][crate::NoneTokenLog],
499 /// which makes the server ignore all address validation tokens (that is, tokens originating
500 /// from NEW_TOKEN frames--retry tokens are not affected).
501 pub fn log(&mut self, log: Arc<dyn TokenLog>) -> &mut Self {
502 self.log = log;
503 self
504 }
505
506 /// Number of address validation tokens sent to a client when its path is validated
507 ///
508 /// This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens.
509 ///
510 /// If the `bloom` feature is enabled (which it is by default), defaults to 2. Otherwise,
511 /// defaults to 0.
512 pub fn sent(&mut self, value: u32) -> &mut Self {
513 self.sent = value;
514 self
515 }
516}
517
518impl Default for ValidationTokenConfig {
519 fn default() -> Self {
520 #[cfg(feature = "bloom")]
521 let log = Arc::new(BloomTokenLog::default());
522 #[cfg(not(feature = "bloom"))]
523 let log = Arc::new(NoneTokenLog);
524 Self {
525 lifetime: Duration::from_secs(2 * 7 * 24 * 60 * 60),
526 log,
527 sent: if cfg!(feature = "bloom") { 2 } else { 0 },
528 }
529 }
530}
531
532impl fmt::Debug for ValidationTokenConfig {
533 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
534 fmt.debug_struct("ServerValidationTokenConfig")
535 .field("lifetime", &self.lifetime)
536 // log not debug
537 .field("sent", &self.sent)
538 .finish_non_exhaustive()
539 }
540}
541
542/// Configuration for outgoing connections
543///
544/// Default values should be suitable for most internet applications.
545#[derive(Clone)]
546#[non_exhaustive]
547pub struct ClientConfig {
548 /// Transport configuration to use
549 pub(crate) transport: Arc<TransportConfig>,
550
551 /// Cryptographic configuration to use
552 pub(crate) crypto: Arc<dyn crypto::ClientConfig>,
553
554 /// Validation token store to use
555 pub(crate) token_store: Arc<dyn TokenStore>,
556
557 /// Provider that populates the destination connection ID of Initial Packets
558 pub(crate) initial_dst_cid_provider: Arc<dyn Fn() -> ConnectionId + Send + Sync>,
559
560 /// QUIC protocol version to use
561 pub(crate) version: u32,
562}
563
564impl ClientConfig {
565 /// Create a default config with a particular cryptographic config
566 pub fn new(crypto: Arc<dyn crypto::ClientConfig>) -> Self {
567 Self {
568 transport: Default::default(),
569 crypto,
570 token_store: Arc::new(TokenMemoryCache::default()),
571 initial_dst_cid_provider: Arc::new(|| {
572 RandomConnectionIdGenerator::new(MAX_CID_SIZE).generate_cid()
573 }),
574 version: 1,
575 }
576 }
577
578 /// Configure how to populate the destination CID of the initial packet when attempting to
579 /// establish a new connection
580 ///
581 /// By default, it's populated with random bytes with reasonable length, so unless you have
582 /// a good reason, you do not need to change it.
583 ///
584 /// When prefer to override the default, please note that the generated connection ID MUST be
585 /// at least 8 bytes long and unpredictable, as per section 7.2 of RFC 9000.
586 pub fn initial_dst_cid_provider(
587 &mut self,
588 initial_dst_cid_provider: Arc<dyn Fn() -> ConnectionId + Send + Sync>,
589 ) -> &mut Self {
590 self.initial_dst_cid_provider = initial_dst_cid_provider;
591 self
592 }
593
594 /// Set a custom [`TransportConfig`]
595 pub fn transport_config(&mut self, transport: Arc<TransportConfig>) -> &mut Self {
596 self.transport = transport;
597 self
598 }
599
600 /// Set a custom [`TokenStore`]
601 ///
602 /// Defaults to [`TokenMemoryCache`], which is suitable for most internet applications.
603 pub fn token_store(&mut self, store: Arc<dyn TokenStore>) -> &mut Self {
604 self.token_store = store;
605 self
606 }
607
608 /// Set the QUIC version to use
609 pub fn version(&mut self, version: u32) -> &mut Self {
610 self.version = version;
611 self
612 }
613}
614
615#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
616impl ClientConfig {
617 /// Create a client configuration that trusts the platform's native roots
618 #[cfg(feature = "platform-verifier")]
619 pub fn with_platform_verifier() -> Self {
620 Self::new(Arc::new(crypto::rustls::QuicClientConfig::new(Arc::new(
621 rustls_platform_verifier::Verifier::new(),
622 ))))
623 }
624
625 /// Create a client configuration that trusts specified trust anchors
626 pub fn with_root_certificates(
627 roots: Arc<rustls::RootCertStore>,
628 ) -> Result<Self, rustls::client::VerifierBuilderError> {
629 Ok(Self::new(Arc::new(crypto::rustls::QuicClientConfig::new(
630 WebPkiServerVerifier::builder_with_provider(roots, configured_provider()).build()?,
631 ))))
632 }
633}
634
635impl fmt::Debug for ClientConfig {
636 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
637 fmt.debug_struct("ClientConfig")
638 .field("transport", &self.transport)
639 // crypto not debug
640 // token_store not debug
641 .field("version", &self.version)
642 .finish_non_exhaustive()
643 }
644}
645
646/// Errors in the configuration of an endpoint
647#[derive(Debug, Error, Clone, PartialEq, Eq)]
648#[non_exhaustive]
649pub enum ConfigError {
650 /// Value exceeds supported bounds
651 #[error("value exceeds supported bounds")]
652 OutOfBounds,
653}
654
655impl From<TryFromIntError> for ConfigError {
656 fn from(_: TryFromIntError) -> Self {
657 Self::OutOfBounds
658 }
659}
660
661impl From<VarIntBoundsExceeded> for ConfigError {
662 fn from(_: VarIntBoundsExceeded) -> Self {
663 Self::OutOfBounds
664 }
665}
666
667/// Object to get current [`SystemTime`]
668///
669/// This exists to allow system time to be mocked in tests, or wherever else desired.
670pub trait TimeSource: Send + Sync {
671 /// Get [`SystemTime::now()`](SystemTime::now) or the mocked equivalent
672 fn now(&self) -> SystemTime;
673}
674
675/// Default implementation of [`TimeSource`]
676///
677/// Implements `now` by calling [`SystemTime::now()`](SystemTime::now).
678pub struct StdSystemTime;
679
680impl TimeSource for StdSystemTime {
681 fn now(&self) -> SystemTime {
682 SystemTime::now()
683 }
684}