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