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(feature = "rustls-aws-lc-rs")]
16use rustls::client::WebPkiServerVerifier;
17#[cfg(feature = "rustls-aws-lc-rs")]
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(feature = "rustls-aws-lc-rs")]
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, buffer_defaults,
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(feature = "aws-lc-rs")]
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(feature = "aws-lc-rs")]
335impl Default for EndpointConfig {
336 fn default() -> Self {
337 use aws_lc_rs::hmac;
338 use rand::RngCore;
339
340 let mut reset_key = [0; 64];
341 rand::thread_rng().fill_bytes(&mut reset_key);
342
343 Self::new(Arc::new(hmac::Key::new(hmac::HMAC_SHA256, &reset_key)))
344 }
345}
346
347/// Parameters governing incoming connections
348///
349/// Default values should be suitable for most internet applications.
350#[derive(Clone)]
351pub struct ServerConfig {
352 /// Transport configuration to use for incoming connections
353 pub transport: Arc<TransportConfig>,
354
355 /// TLS configuration used for incoming connections
356 ///
357 /// Must be set to use TLS 1.3 only.
358 pub crypto: Arc<dyn crypto::ServerConfig>,
359
360 /// Configuration for sending and handling validation tokens
361 pub validation_token: ValidationTokenConfig,
362
363 /// Used to generate one-time AEAD keys to protect handshake tokens
364 pub(crate) token_key: Arc<dyn HandshakeTokenKey>,
365
366 /// Key material for Token v2 address-validation tokens. When present, NEW_TOKEN frames will
367 /// emit AES-GCM protected tokens bound to peer identity and connection ID.
368 pub(crate) token_v2_key: Option<crate::token_v2::TokenKey>,
369
370 /// Duration after a retry token was issued for which it's considered valid
371 pub(crate) retry_token_lifetime: Duration,
372
373 /// Whether to allow clients to migrate to new addresses
374 ///
375 /// Improves behavior for clients that move between different internet connections or suffer NAT
376 /// rebinding. Enabled by default.
377 pub(crate) migration: bool,
378
379 pub(crate) preferred_address_v4: Option<SocketAddrV4>,
380 pub(crate) preferred_address_v6: Option<SocketAddrV6>,
381
382 pub(crate) max_incoming: usize,
383 pub(crate) incoming_buffer_size: u64,
384 pub(crate) incoming_buffer_size_total: u64,
385
386 pub(crate) time_source: Arc<dyn TimeSource>,
387}
388
389impl ServerConfig {
390 /// Create a default config with a particular handshake token key
391 pub fn new(
392 crypto: Arc<dyn crypto::ServerConfig>,
393 token_key: Arc<dyn HandshakeTokenKey>,
394 ) -> Self {
395 Self {
396 transport: Arc::new(TransportConfig::default()),
397 crypto,
398
399 token_key,
400 token_v2_key: None,
401 retry_token_lifetime: Duration::from_secs(15),
402
403 migration: true,
404
405 validation_token: ValidationTokenConfig::default(),
406
407 preferred_address_v4: None,
408 preferred_address_v6: None,
409
410 max_incoming: 1 << 16,
411 incoming_buffer_size: 10 << 20,
412 incoming_buffer_size_total: 100 << 20,
413
414 time_source: Arc::new(StdSystemTime),
415 }
416 }
417
418 /// Set a custom [`TransportConfig`]
419 pub fn transport_config(&mut self, transport: Arc<TransportConfig>) -> &mut Self {
420 self.transport = transport;
421 self
422 }
423
424 /// Set a custom [`ValidationTokenConfig`]
425 pub fn validation_token_config(
426 &mut self,
427 validation_token: ValidationTokenConfig,
428 ) -> &mut Self {
429 self.validation_token = validation_token;
430 self
431 }
432
433 /// Private key used to authenticate data included in handshake tokens
434 pub fn token_key(&mut self, value: Arc<dyn HandshakeTokenKey>) -> &mut Self {
435 self.token_key = value;
436 self
437 }
438
439 /// Configure the key used for Token v2 address-validation tokens. When unset, the server will
440 /// continue issuing legacy validation tokens.
441 pub fn token_v2_key(&mut self, key: Option<crate::token_v2::TokenKey>) -> &mut Self {
442 self.token_v2_key = key;
443 self
444 }
445
446 /// Duration after a retry token was issued for which it's considered valid
447 ///
448 /// Defaults to 15 seconds.
449 pub fn retry_token_lifetime(&mut self, value: Duration) -> &mut Self {
450 self.retry_token_lifetime = value;
451 self
452 }
453
454 /// Whether to allow clients to migrate to new addresses
455 ///
456 /// Improves behavior for clients that move between different internet connections or suffer NAT
457 /// rebinding. Enabled by default.
458 pub fn migration(&mut self, value: bool) -> &mut Self {
459 self.migration = value;
460 self
461 }
462
463 /// The preferred IPv4 address that will be communicated to clients during handshaking
464 ///
465 /// If the client is able to reach this address, it will switch to it.
466 pub fn preferred_address_v4(&mut self, address: Option<SocketAddrV4>) -> &mut Self {
467 self.preferred_address_v4 = address;
468 self
469 }
470
471 /// The preferred IPv6 address that will be communicated to clients during handshaking
472 ///
473 /// If the client is able to reach this address, it will switch to it.
474 pub fn preferred_address_v6(&mut self, address: Option<SocketAddrV6>) -> &mut Self {
475 self.preferred_address_v6 = address;
476 self
477 }
478
479 /// Maximum number of [`Incoming`][crate::Incoming] to allow to exist at a time
480 ///
481 /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt
482 /// is received and stops existing when the application either accepts it or otherwise disposes
483 /// of it. While this limit is reached, new incoming connection attempts are immediately
484 /// refused. Larger values have greater worst-case memory consumption, but accommodate greater
485 /// application latency in handling incoming connection attempts.
486 ///
487 /// The default value is set to 65536. With a typical Ethernet MTU of 1500 bytes, this limits
488 /// memory consumption from this to under 100 MiB--a generous amount that still prevents memory
489 /// exhaustion in most contexts.
490 pub fn max_incoming(&mut self, max_incoming: usize) -> &mut Self {
491 self.max_incoming = max_incoming;
492 self
493 }
494
495 /// Maximum number of received bytes to buffer for each [`Incoming`][crate::Incoming]
496 ///
497 /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt
498 /// is received and stops existing when the application either accepts it or otherwise disposes
499 /// of it. This limit governs only packets received within that period, and does not include
500 /// the first packet. Packets received in excess of this limit are dropped, which may cause
501 /// 0-RTT or handshake data to have to be retransmitted.
502 ///
503 /// The default value is set to 10 MiB--an amount such that in most situations a client would
504 /// not transmit that much 0-RTT data faster than the server handles the corresponding
505 /// [`Incoming`][crate::Incoming].
506 pub fn incoming_buffer_size(&mut self, incoming_buffer_size: u64) -> &mut Self {
507 self.incoming_buffer_size = incoming_buffer_size;
508 self
509 }
510
511 /// Maximum number of received bytes to buffer for all [`Incoming`][crate::Incoming]
512 /// collectively
513 ///
514 /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt
515 /// is received and stops existing when the application either accepts it or otherwise disposes
516 /// of it. This limit governs only packets received within that period, and does not include
517 /// the first packet. Packets received in excess of this limit are dropped, which may cause
518 /// 0-RTT or handshake data to have to be retransmitted.
519 ///
520 /// The default value is set to 100 MiB--a generous amount that still prevents memory
521 /// exhaustion in most contexts.
522 pub fn incoming_buffer_size_total(&mut self, incoming_buffer_size_total: u64) -> &mut Self {
523 self.incoming_buffer_size_total = incoming_buffer_size_total;
524 self
525 }
526
527 /// Object to get current [`SystemTime`]
528 ///
529 /// This exists to allow system time to be mocked in tests, or wherever else desired.
530 ///
531 /// Defaults to [`StdSystemTime`], which simply calls [`SystemTime::now()`](SystemTime::now).
532 pub fn time_source(&mut self, time_source: Arc<dyn TimeSource>) -> &mut Self {
533 self.time_source = time_source;
534 self
535 }
536
537 pub(crate) fn has_preferred_address(&self) -> bool {
538 self.preferred_address_v4.is_some() || self.preferred_address_v6.is_some()
539 }
540}
541
542#[cfg(feature = "rustls-aws-lc-rs")]
543impl ServerConfig {
544 /// Create a server config with the given certificate chain to be presented to clients
545 ///
546 /// Uses a randomized handshake token key.
547 pub fn with_single_cert(
548 cert_chain: Vec<CertificateDer<'static>>,
549 key: PrivateKeyDer<'static>,
550 ) -> Result<Self, rustls::Error> {
551 Ok(Self::with_crypto(Arc::new(QuicServerConfig::new(
552 cert_chain, key,
553 )?)))
554 }
555}
556
557#[cfg(feature = "aws-lc-rs")]
558impl ServerConfig {
559 /// Create a server config with the given [`crypto::ServerConfig`]
560 ///
561 /// Uses a randomized handshake token key.
562 pub fn with_crypto(crypto: Arc<dyn crypto::ServerConfig>) -> Self {
563 use aws_lc_rs::hkdf;
564 use rand::RngCore;
565
566 let rng = &mut rand::thread_rng();
567 let mut master_key = [0u8; 64];
568 rng.fill_bytes(&mut master_key);
569 let master_key = hkdf::Salt::new(hkdf::HKDF_SHA256, &[]).extract(&master_key);
570
571 Self::new(crypto, Arc::new(master_key))
572 }
573}
574
575impl fmt::Debug for ServerConfig {
576 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
577 fmt.debug_struct("ServerConfig")
578 .field("transport", &self.transport)
579 // crypto not debug
580 // token not debug
581 .field("retry_token_lifetime", &self.retry_token_lifetime)
582 .field("validation_token", &self.validation_token)
583 .field("migration", &self.migration)
584 .field("preferred_address_v4", &self.preferred_address_v4)
585 .field("preferred_address_v6", &self.preferred_address_v6)
586 .field("max_incoming", &self.max_incoming)
587 .field("incoming_buffer_size", &self.incoming_buffer_size)
588 .field(
589 "incoming_buffer_size_total",
590 &self.incoming_buffer_size_total,
591 )
592 // system_time_clock not debug
593 .finish_non_exhaustive()
594 }
595}
596
597/// Configuration for sending and handling validation tokens in incoming connections
598///
599/// Default values should be suitable for most internet applications.
600///
601/// ## QUIC Tokens
602///
603/// The QUIC protocol defines a concept of "[address validation][1]". Essentially, one side of a
604/// QUIC connection may appear to be receiving QUIC packets from a particular remote UDP address,
605/// but it will only consider that remote address "validated" once it has convincing evidence that
606/// the address is not being [spoofed][2].
607///
608/// Validation is important primarily because of QUIC's "anti-amplification limit." This limit
609/// prevents a QUIC server from sending a client more than three times the number of bytes it has
610/// received from the client on a given address until that address is validated. This is designed
611/// to mitigate the ability of attackers to use QUIC-based servers as reflectors in [amplification
612/// attacks][3].
613///
614/// A path may become validated in several ways. The server is always considered validated by the
615/// client. The client usually begins in an unvalidated state upon first connecting or migrating,
616/// but then becomes validated through various mechanisms that usually take one network round trip.
617/// However, in some cases, a client which has previously attempted to connect to a server may have
618/// been given a one-time use cryptographically secured "token" that it can send in a subsequent
619/// connection attempt to be validated immediately.
620///
621/// There are two ways these tokens can originate:
622///
623/// - If the server responds to an incoming connection with `retry`, a "retry token" is minted and
624/// sent to the client, which the client immediately uses to attempt to connect again. Retry
625/// tokens operate on short timescales, such as 15 seconds.
626/// - If a client's path within an active connection is validated, the server may send the client
627/// one or more "validation tokens," which the client may store for use in later connections to
628/// the same server. Validation tokens may be valid for much longer lifetimes than retry token.
629///
630/// The usage of validation tokens is most impactful in situations where 0-RTT data is also being
631/// used--in particular, in situations where the server sends the client more than three times more
632/// 0.5-RTT data than it has received 0-RTT data. Since the successful completion of a connection
633/// handshake implicitly causes the client's address to be validated, transmission of 0.5-RTT data
634/// is the main situation where a server might be sending application data to an address that could
635/// be validated by token usage earlier than it would become validated without token usage.
636///
637/// [1]: https://www.rfc-editor.org/rfc/rfc9000.html#section-8
638/// [2]: https://en.wikipedia.org/wiki/IP_address_spoofing
639/// [3]: https://en.wikipedia.org/wiki/Denial-of-service_attack#Amplification
640///
641/// These tokens should not be confused with "stateless reset tokens," which are similarly named
642/// but entirely unrelated.
643#[derive(Clone)]
644pub struct ValidationTokenConfig {
645 pub(crate) lifetime: Duration,
646 pub(crate) log: Arc<dyn TokenLog>,
647 pub(crate) sent: u32,
648}
649
650impl ValidationTokenConfig {
651 /// Duration after an address validation token was issued for which it's considered valid
652 ///
653 /// This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens.
654 ///
655 /// Defaults to 2 weeks.
656 pub fn lifetime(&mut self, value: Duration) -> &mut Self {
657 self.lifetime = value;
658 self
659 }
660
661 #[allow(rustdoc::redundant_explicit_links)] // which links are redundant depends on features
662 /// Set a custom token log
663 ///
664 /// If the `bloom` feature is enabled (which it is by default), defaults to a bloom
665 /// token log, which is suitable for most internet applications.
666 ///
667 /// If the `bloom` feature is disabled, defaults to a none token log,
668 /// which makes the server ignore all address validation tokens (that is, tokens originating
669 /// from NEW_TOKEN frames--retry tokens are not affected).
670 pub fn log(&mut self, log: Arc<dyn TokenLog>) -> &mut Self {
671 self.log = log;
672 self
673 }
674
675 /// Number of address validation tokens sent to a client when its path is validated
676 ///
677 /// This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens.
678 ///
679 /// If the `bloom` feature is enabled (which it is by default), defaults to 2. Otherwise,
680 /// defaults to 0.
681 pub fn sent(&mut self, value: u32) -> &mut Self {
682 self.sent = value;
683 self
684 }
685}
686
687impl Default for ValidationTokenConfig {
688 fn default() -> Self {
689 let log = Arc::new(NoneTokenLog);
690 Self {
691 lifetime: Duration::from_secs(2 * 7 * 24 * 60 * 60),
692 log,
693 sent: if cfg!(feature = "bloom") { 2 } else { 0 },
694 }
695 }
696}
697
698impl fmt::Debug for ValidationTokenConfig {
699 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
700 fmt.debug_struct("ServerValidationTokenConfig")
701 .field("lifetime", &self.lifetime)
702 // log not debug
703 .field("sent", &self.sent)
704 .finish_non_exhaustive()
705 }
706}
707
708/// Configuration for outgoing connections
709///
710/// Default values should be suitable for most internet applications.
711#[derive(Clone)]
712#[non_exhaustive]
713pub struct ClientConfig {
714 /// Transport configuration to use
715 pub(crate) transport: Arc<TransportConfig>,
716
717 /// Cryptographic configuration to use
718 pub(crate) crypto: Arc<dyn crypto::ClientConfig>,
719
720 /// Validation token store to use
721 pub(crate) token_store: Arc<dyn TokenStore>,
722
723 /// Provider that populates the destination connection ID of Initial Packets
724 pub(crate) initial_dst_cid_provider: Arc<dyn Fn() -> ConnectionId + Send + Sync>,
725
726 /// QUIC protocol version to use
727 pub(crate) version: u32,
728}
729
730impl ClientConfig {
731 /// Create a default config with a particular cryptographic config
732 pub fn new(crypto: Arc<dyn crypto::ClientConfig>) -> Self {
733 Self {
734 transport: Default::default(),
735 crypto,
736 token_store: Arc::new(TokenMemoryCache::default()),
737 initial_dst_cid_provider: Arc::new(|| {
738 RandomConnectionIdGenerator::new(MAX_CID_SIZE).generate_cid()
739 }),
740 version: 1,
741 }
742 }
743
744 /// Configure how to populate the destination CID of the initial packet when attempting to
745 /// establish a new connection
746 ///
747 /// By default, it's populated with random bytes with reasonable length, so unless you have
748 /// a good reason, you do not need to change it.
749 ///
750 /// When prefer to override the default, please note that the generated connection ID MUST be
751 /// at least 8 bytes long and unpredictable, as per section 7.2 of RFC 9000.
752 pub fn initial_dst_cid_provider(
753 &mut self,
754 initial_dst_cid_provider: Arc<dyn Fn() -> ConnectionId + Send + Sync>,
755 ) -> &mut Self {
756 self.initial_dst_cid_provider = initial_dst_cid_provider;
757 self
758 }
759
760 /// Set a custom [`TransportConfig`]
761 pub fn transport_config(&mut self, transport: Arc<TransportConfig>) -> &mut Self {
762 self.transport = transport;
763 self
764 }
765
766 /// Set a custom token store
767 ///
768 /// Defaults to a memory cache, which is suitable for most internet applications.
769 pub fn token_store(&mut self, store: Arc<dyn TokenStore>) -> &mut Self {
770 self.token_store = store;
771 self
772 }
773
774 /// Set the QUIC version to use
775 pub fn version(&mut self, version: u32) -> &mut Self {
776 self.version = version;
777 self
778 }
779}
780
781#[cfg(feature = "rustls-aws-lc-rs")]
782impl ClientConfig {
783 /// Create a client configuration that trusts the platform's native roots
784 #[cfg(feature = "platform-verifier")]
785 pub fn try_with_platform_verifier() -> Result<Self, rustls::Error> {
786 Ok(Self::new(Arc::new(
787 crypto::rustls::QuicClientConfig::with_platform_verifier()?,
788 )))
789 }
790
791 /// Create a client configuration that trusts specified trust anchors
792 pub fn with_root_certificates(
793 roots: Arc<rustls::RootCertStore>,
794 ) -> Result<Self, rustls::Error> {
795 Ok(Self::new(Arc::new(crypto::rustls::QuicClientConfig::new(
796 WebPkiServerVerifier::builder_with_provider(roots, configured_provider())
797 .build()
798 .map_err(|e| rustls::Error::General(e.to_string()))?,
799 )?)))
800 }
801}
802
803impl fmt::Debug for ClientConfig {
804 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
805 fmt.debug_struct("ClientConfig")
806 .field("transport", &self.transport)
807 // crypto not debug
808 // token_store not debug
809 .field("version", &self.version)
810 .finish_non_exhaustive()
811 }
812}
813
814/// Errors in the configuration of an endpoint
815#[derive(Debug, Error, Clone, PartialEq, Eq)]
816#[non_exhaustive]
817pub enum ConfigError {
818 /// Value exceeds supported bounds
819 #[error("value exceeds supported bounds")]
820 OutOfBounds,
821}
822
823impl From<TryFromIntError> for ConfigError {
824 fn from(_: TryFromIntError) -> Self {
825 Self::OutOfBounds
826 }
827}
828
829impl From<VarIntBoundsExceeded> for ConfigError {
830 fn from(_: VarIntBoundsExceeded) -> Self {
831 Self::OutOfBounds
832 }
833}
834
835/// Object to get current [`SystemTime`]
836///
837/// This exists to allow system time to be mocked in tests, or wherever else desired.
838pub trait TimeSource: Send + Sync {
839 /// Get [`SystemTime::now()`](SystemTime::now) or the mocked equivalent
840 fn now(&self) -> SystemTime;
841}
842
843/// Default implementation of [`TimeSource`]
844///
845/// Implements `now` by calling [`SystemTime::now()`](SystemTime::now).
846pub struct StdSystemTime;
847
848impl TimeSource for StdSystemTime {
849 fn now(&self) -> SystemTime {
850 SystemTime::now()
851 }
852}