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