ant_quic/config/
mod.rs

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