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