wtransport/
config.rs

1//!
2//! This module defines configurations for the WebTransport server and client.
3//!
4//! It provides builders for creating server and client configurations with various options.
5//!
6//! The module includes:
7//! - [`ServerConfig`]: Configuration for the WebTransport server.
8//! - [`ClientConfig`]: Configuration for the WebTransport client.
9//!
10//! Example for creating a server configuration:
11//!
12//! ```no_run
13//! # async fn run() -> anyhow::Result<()> {
14//! use wtransport::Identity;
15//! use wtransport::ServerConfig;
16//!
17//! let server_config = ServerConfig::builder()
18//!     .with_bind_default(443)
19//!     .with_identity(Identity::load_pemfiles("cert.pem", "key.pem").await?)
20//!     .build();
21//!
22//! # Ok(())
23//! # }
24//! ```
25//!
26//! Example for creating a client configuration:
27//!
28//! ```no_run
29//! use wtransport::ClientConfig;
30//!
31//! let client_config = ClientConfig::builder()
32//!     .with_bind_default()
33//!     .with_native_certs()
34//!     .build();
35//! ```
36
37use crate::tls::build_native_cert_store;
38use crate::tls::Identity;
39use socket2::Domain as SocketDomain;
40use socket2::Protocol as SocketProtocol;
41use socket2::Socket;
42use socket2::Type as SocketType;
43use std::fmt::Debug;
44use std::fmt::Display;
45use std::future::Future;
46use std::net::IpAddr;
47use std::net::Ipv4Addr;
48use std::net::Ipv6Addr;
49use std::net::SocketAddr;
50use std::net::SocketAddrV4;
51use std::net::SocketAddrV6;
52use std::net::UdpSocket;
53use std::pin::Pin;
54use std::sync::Arc;
55use std::time::Duration;
56
57/// Alias of [`crate::tls::rustls::ServerConfig`].
58pub type TlsServerConfig = rustls::ServerConfig;
59
60/// Alias of [`crate::tls::rustls::ClientConfig`].
61pub type TlsClientConfig = rustls::ClientConfig;
62
63/// Alias of [`crate::quinn::TransportConfig`].
64#[cfg(feature = "quinn")]
65#[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
66pub type QuicTransportConfig = quinn::TransportConfig;
67
68/// Alias of [`crate::quinn::ServerConfig`].
69#[cfg(feature = "quinn")]
70#[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
71pub type QuicServerConfig = quinn::ServerConfig;
72
73/// Alias of [`crate::quinn::ClientConfig`].
74#[cfg(feature = "quinn")]
75#[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
76pub type QuicClientConfig = quinn::ClientConfig;
77
78/// Configuration for IP address socket bind.
79#[derive(Debug, Copy, Clone)]
80pub enum IpBindConfig {
81    /// Bind to LOCALHOST IPv4 address (no IPv6).
82    LocalV4,
83
84    /// Bind to LOCALHOST IPv6 address (no IPv4).
85    LocalV6,
86
87    /// Bind to LOCALHOST both IPv4 and IPv6 address (dual stack, if supported).
88    LocalDual,
89
90    /// Bind to `INADDR_ANY` IPv4 address (no IPv6).
91    InAddrAnyV4,
92
93    /// Bind to `INADDR_ANY` IPv6 address (no IPv4).
94    InAddrAnyV6,
95
96    /// Bind to `INADDR_ANY` both IPv4 and IPv6 address (dual stack, if supported).
97    InAddrAnyDual,
98}
99
100impl IpBindConfig {
101    fn into_ip(self) -> IpAddr {
102        match self {
103            IpBindConfig::LocalV4 => Ipv4Addr::LOCALHOST.into(),
104            IpBindConfig::LocalV6 => Ipv6Addr::LOCALHOST.into(),
105            IpBindConfig::LocalDual => Ipv6Addr::LOCALHOST.into(),
106            IpBindConfig::InAddrAnyV4 => Ipv4Addr::UNSPECIFIED.into(),
107            IpBindConfig::InAddrAnyV6 => Ipv6Addr::UNSPECIFIED.into(),
108            IpBindConfig::InAddrAnyDual => Ipv6Addr::UNSPECIFIED.into(),
109        }
110    }
111
112    fn into_dual_stack_config(self) -> Ipv6DualStackConfig {
113        match self {
114            IpBindConfig::LocalV4 | IpBindConfig::InAddrAnyV4 => Ipv6DualStackConfig::OsDefault,
115            IpBindConfig::LocalV6 | IpBindConfig::InAddrAnyV6 => Ipv6DualStackConfig::Deny,
116            IpBindConfig::LocalDual | IpBindConfig::InAddrAnyDual => Ipv6DualStackConfig::Allow,
117        }
118    }
119}
120
121/// Configuration for IPv6 dual stack.
122#[derive(Debug, Copy, Clone)]
123pub enum Ipv6DualStackConfig {
124    /// Do not configure dual stack. Use OS's default.
125    OsDefault,
126
127    /// Deny dual stack. This is equivalent to `IPV6_V6ONLY`.
128    ///
129    /// Socket will only bind for IPv6 (IPv4 port will still be available).
130    Deny,
131
132    /// Allow dual stack.
133    ///
134    /// Please note that not all configurations/platforms support dual stack.
135    Allow,
136}
137
138/// Invalid idle timeout.
139pub struct InvalidIdleTimeout;
140
141/// Server configuration.
142///
143/// You can create an instance of `ServerConfig` using its builder pattern by calling
144/// the [`builder()`](Self::builder) method.
145/// Once you have an instance, you can further customize it by chaining method calls
146/// to set various configuration options.
147///
148/// ## Configuration Builder States
149///
150/// The configuration process follows a *state-based builder pattern*, where the server
151/// configuration progresses through *3* states.
152///
153/// ### 1. `WantsBindAddress`
154///
155/// The caller must supply a binding address for the server. This is where to specify
156/// the listening port of the server.
157/// The following options are mutually exclusive:
158///
159///   - [`with_bind_default`](ServerConfigBuilder::with_bind_default): the simplest
160///     configuration where only the port will be specified.
161///   - [`with_bind_config`](ServerConfigBuilder::with_bind_config): configures
162///     binding to an address determined by a configuration preset.
163///   - [`with_bind_address`](ServerConfigBuilder::with_bind_address): configures
164///     binding to a custom-specified socket address.
165///   - [`with_bind_address_v6`](ServerConfigBuilder::with_bind_address_v6): configures
166///     binding to a custom-specified socket address for *IPv6*, along with the [dual stack
167///     configuration](Ipv6DualStackConfig).
168///   - [`with_bind_socket`](ServerConfigBuilder::with_bind_socket): configures
169///     binding directly to a custom-specified socket.
170///
171/// Only one of these options can be selected during the client configuration process.
172///
173/// #### Examples:
174///
175/// ```
176/// use wtransport::ServerConfig;
177///
178/// // Configuration for accepting incoming connection on port 443
179/// ServerConfig::builder().with_bind_default(443);
180/// ```
181///
182/// ### 2. `WantsIdentity`
183///
184/// The caller must supply a TLS identity for the server.
185///
186/// - [`with_identity`](ServerConfigBuilder::with_identity): configures
187///   a TLS [`Identity`] for the server.
188/// - [`with_custom_tls`](ServerConfigBuilder::with_custom_tls): sets the TLS
189///   server configuration manually.
190/// - [`with_custom_transport`](ServerConfigBuilder::with_custom_transport): sets the QUIC
191///   transport configuration manually (using default TLS).
192/// - [`with_custom_tls_and_transport`](ServerConfigBuilder::with_custom_tls_and_transport): sets both
193///   a custom TLS and QUIC transport configuration.
194/// - [`build_with_quic_config`](ServerConfigBuilder::build_with_quic_config): directly builds
195///   [`ServerConfig`] providing both TLS and QUIC transport configuration given by
196///   [`quic_config`](QuicServerConfig).
197///
198/// #### Examples:
199/// ```
200/// # use anyhow::Result;
201/// use wtransport::Identity;
202/// use wtransport::ServerConfig;
203///
204/// # async fn run() -> Result<()> {
205/// ServerConfig::builder()
206///     .with_bind_default(443)
207///     .with_identity(Identity::load_pemfiles("cert.pem", "key.pem").await?);
208/// # Ok(())
209/// # }
210/// ```
211///
212/// ### 3. `WantsTransportConfigServer`
213///
214/// The caller can supply *additional* transport configurations.
215/// Multiple options can be given at this stage. Once the configuration is completed, it is possible
216/// to finalize with the method [`build()`](ServerConfigBuilder::build).
217///
218/// All these options can be omitted in the configuration; default values will be used.
219///
220/// - [`max_idle_timeout`](ServerConfigBuilder::max_idle_timeout)
221/// - [`keep_alive_interval`](ServerConfigBuilder::keep_alive_interval)
222/// - [`allow_migration`](ServerConfigBuilder::allow_migration)
223///
224/// #### Examples:
225/// ```
226/// # use anyhow::Result;
227/// use wtransport::ServerConfig;
228/// use wtransport::Identity;
229/// use std::time::Duration;
230///
231/// # async fn run() -> Result<()> {
232/// let server_config = ServerConfig::builder()
233///     .with_bind_default(443)
234///     .with_identity(Identity::load_pemfiles("cert.pem", "key.pem").await?)
235///     .keep_alive_interval(Some(Duration::from_secs(3)))
236///     .build();
237/// # Ok(())
238/// # }
239#[derive(Debug)]
240pub struct ServerConfig {
241    pub(crate) bind_address_config: BindAddressConfig,
242    pub(crate) endpoint_config: quinn::EndpointConfig,
243    pub(crate) quic_config: quinn::ServerConfig,
244}
245
246impl ServerConfig {
247    /// Creates a builder to build up the server configuration.
248    ///
249    /// For more information, see the [`ServerConfigBuilder`] documentation.
250    pub fn builder() -> ServerConfigBuilder<states::WantsBindAddress> {
251        ServerConfigBuilder::default()
252    }
253
254    /// Returns a reference to the inner QUIC endpoint configuration.
255    #[cfg(feature = "quinn")]
256    #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
257    pub fn quic_endpoint_config(&self) -> &quinn::EndpointConfig {
258        &self.endpoint_config
259    }
260
261    /// Returns a mutable reference to the inner QUIC endpoint configuration.
262    #[cfg(feature = "quinn")]
263    #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
264    pub fn quic_endpoint_config_mut(&mut self) -> &mut quinn::EndpointConfig {
265        &mut self.endpoint_config
266    }
267
268    /// Returns a reference to the inner QUIC configuration.
269    #[cfg(feature = "quinn")]
270    #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
271    pub fn quic_config(&self) -> &quinn::ServerConfig {
272        &self.quic_config
273    }
274
275    /// Returns a mutable reference to the inner QUIC configuration.
276    #[cfg(feature = "quinn")]
277    #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
278    pub fn quic_config_mut(&mut self) -> &mut quinn::ServerConfig {
279        &mut self.quic_config
280    }
281}
282
283/// Server builder configuration.
284///
285/// The builder might have different state at compile time.
286///
287/// # Examples:
288/// ```no_run
289/// # async fn run() -> anyhow::Result<()> {
290/// # use std::net::Ipv4Addr;
291/// # use std::net::SocketAddr;
292/// # use wtransport::Identity;
293/// # use wtransport::ServerConfig;
294/// let config = ServerConfig::builder()
295///     .with_bind_default(4433)
296///     .with_identity(Identity::load_pemfiles("cert.pem", "key.pem").await?);
297/// # Ok(())
298/// # }
299/// ```
300#[must_use]
301pub struct ServerConfigBuilder<State>(State);
302
303impl ServerConfigBuilder<states::WantsBindAddress> {
304    /// Configures for accepting incoming connections binding ANY IP (allowing IP dual-stack).
305    ///
306    /// `listening_port` is the port where the server will accept incoming connections.
307    ///
308    /// This is equivalent to: [`Self::with_bind_config`] with [`IpBindConfig::InAddrAnyDual`].
309    pub fn with_bind_default(
310        self,
311        listening_port: u16,
312    ) -> ServerConfigBuilder<states::WantsIdentity> {
313        self.with_bind_config(IpBindConfig::InAddrAnyDual, listening_port)
314    }
315
316    /// Sets the binding (local) socket address with a specific [`IpBindConfig`].
317    ///
318    /// `listening_port` is the port where the server will accept incoming connections.
319    pub fn with_bind_config(
320        self,
321        ip_bind_config: IpBindConfig,
322        listening_port: u16,
323    ) -> ServerConfigBuilder<states::WantsIdentity> {
324        let ip_address: IpAddr = ip_bind_config.into_ip();
325
326        match ip_address {
327            IpAddr::V4(ip) => self.with_bind_address(SocketAddr::new(ip.into(), listening_port)),
328            IpAddr::V6(ip) => self.with_bind_address_v6(
329                SocketAddrV6::new(ip, listening_port, 0, 0),
330                ip_bind_config.into_dual_stack_config(),
331            ),
332        }
333    }
334
335    /// Sets the binding (local) socket address for the endpoint.
336    pub fn with_bind_address(
337        self,
338        address: SocketAddr,
339    ) -> ServerConfigBuilder<states::WantsIdentity> {
340        ServerConfigBuilder(states::WantsIdentity {
341            bind_address_config: BindAddressConfig::from(address),
342        })
343    }
344
345    /// Sets the binding (local) socket address for the endpoint with Ipv6 address.
346    ///
347    /// `dual_stack_config` allows/denies dual stack port binding.
348    pub fn with_bind_address_v6(
349        self,
350        address: SocketAddrV6,
351        dual_stack_config: Ipv6DualStackConfig,
352    ) -> ServerConfigBuilder<states::WantsIdentity> {
353        ServerConfigBuilder(states::WantsIdentity {
354            bind_address_config: BindAddressConfig::AddressV6(address, dual_stack_config),
355        })
356    }
357
358    /// Configures the server to bind to a pre-existing [`UdpSocket`].
359    ///
360    /// This allows the server to use an already created socket, which may be beneficial
361    /// for scenarios where socket reuse or specific socket configuration is needed.
362    pub fn with_bind_socket(self, socket: UdpSocket) -> ServerConfigBuilder<states::WantsIdentity> {
363        ServerConfigBuilder(states::WantsIdentity {
364            bind_address_config: BindAddressConfig::Socket(socket),
365        })
366    }
367}
368
369impl ServerConfigBuilder<states::WantsIdentity> {
370    /// Configures TLS with safe defaults and a TLS [`Identity`].
371    ///
372    /// # Example
373    /// ```no_run
374    /// use wtransport::Identity;
375    /// use wtransport::ServerConfig;
376    /// # use anyhow::Result;
377    ///
378    /// # async fn run() -> Result<()> {
379    /// let identity = Identity::load_pemfiles("cert.pem", "key.pem").await?;
380    ///
381    /// let server_config = ServerConfig::builder()
382    ///     .with_bind_default(4433)
383    ///     .with_identity(identity)
384    ///     .build();
385    /// # Ok(())
386    /// # }
387    /// ```
388    pub fn with_identity(
389        self,
390        identity: Identity,
391    ) -> ServerConfigBuilder<states::WantsTransportConfigServer> {
392        use crate::tls::server::build_default_tls_config;
393
394        let tls_config = build_default_tls_config(identity);
395        let quic_endpoint_config = quinn::EndpointConfig::default();
396        let quic_transport_config = quinn::TransportConfig::default();
397
398        self.with(tls_config, quic_endpoint_config, quic_transport_config)
399    }
400
401    /// Allows for manual configuration of a custom TLS setup using a provided
402    /// [`rustls::ServerConfig`], which must support
403    /// [`rustls::CipherSuite::TLS13_AES_128_GCM_SHA256`]. A suitable configuration
404    /// can be obtained using the `ring` crypto provider with a set of versions containing
405    /// [`rustls::version::TLS13`].
406    ///
407    /// This method is provided for advanced users who need fine-grained control over the
408    /// TLS configuration. It allows you to pass a preconfigured [`rustls::ServerConfig`]
409    /// instance to customize the TLS settings according to your specific requirements.
410    ///
411    /// Generally, it is recommended to use the [`with_identity`](Self::with_identity) method
412    /// to configure TLS with safe defaults and an TLS [`Identity`].
413    ///
414    /// # Example
415    ///
416    /// ```no_run
417    /// use wtransport::tls::rustls;
418    /// use wtransport::ServerConfig;
419    ///
420    /// // Create a custom rustls::ServerConfig with specific TLS settings
421    /// let custom_tls_config = rustls::ServerConfig::builder();
422    /// // Customize TLS settings here...
423    /// # let custom_tls_config = custom_tls_config
424    /// #          .with_no_client_auth()
425    /// #          .with_single_cert(todo!(), todo!()).unwrap();
426    ///
427    /// // Create a ServerConfigBuilder with the custom TLS configuration
428    /// let server_config = ServerConfig::builder()
429    ///     .with_bind_default(4433)
430    ///     .with_custom_tls(custom_tls_config)
431    ///     .build();
432    /// ```
433    pub fn with_custom_tls(
434        self,
435        tls_config: TlsServerConfig,
436    ) -> ServerConfigBuilder<states::WantsTransportConfigServer> {
437        let quic_endpoint_config = quinn::EndpointConfig::default();
438        let quic_transport_config = quinn::TransportConfig::default();
439
440        self.with(tls_config, quic_endpoint_config, quic_transport_config)
441    }
442
443    /// Configures the server with a custom QUIC transport configuration and a default TLS setup
444    /// using the provided [`Identity`].
445    ///
446    /// This method is useful for scenarios where you need to customize the transport settings
447    /// while relying on a default TLS configuration built from an [`Identity`]. It gives you
448    /// control over the transport layer while maintaining safe and standard TLS settings.
449    ///
450    /// **See**: [`with_identity`](Self::with_identity)
451    /// for a simpler configuration option that does not require custom transport settings.
452    ///
453    /// # Parameters
454    ///
455    /// - `identity`: A reference to an [`Identity`] that contains the server's certificate and
456    ///   private key. This will be used to generate the default TLS configuration.
457    /// - `quic_transport_config`: A custom [`QuicTransportConfig`] instance that allows you to specify
458    ///   various QUIC transport-layer settings according to your requirements.
459    ///
460    /// # Example
461    ///
462    /// ```
463    /// use wtransport::config::QuicTransportConfig;
464    /// use wtransport::Identity;
465    /// use wtransport::ServerConfig;
466    ///
467    /// // Generate a server identity (self signed certificate and private key)
468    /// let identity = Identity::self_signed(["localhost", "127.0.0.1", "::1"]).unwrap();
469    ///
470    /// // Create a custom QuicTransportConfig with specific settings
471    /// let mut custom_transport_config = QuicTransportConfig::default();
472    /// custom_transport_config.datagram_send_buffer_size(1024);
473    ///
474    /// // Create a ServerConfigBuilder with the custom transport configuration and default TLS settings
475    /// let server_config = ServerConfig::builder()
476    ///     .with_bind_default(4433)
477    ///     .with_custom_transport(identity, custom_transport_config)
478    ///     .build();
479    /// ```
480    #[cfg(feature = "quinn")]
481    #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
482    pub fn with_custom_transport(
483        self,
484        identity: Identity,
485        quic_transport_config: QuicTransportConfig,
486    ) -> ServerConfigBuilder<states::WantsTransportConfigServer> {
487        use crate::tls::server::build_default_tls_config;
488
489        let tls_config = build_default_tls_config(identity);
490        let quic_endpoint_config = quinn::EndpointConfig::default();
491
492        self.with(tls_config, quic_endpoint_config, quic_transport_config)
493    }
494
495    /// Configures the server with both a custom TLS configuration and a custom QUIC transport
496    /// configuration.
497    ///
498    /// This method is designed for advanced users who require full control over both the TLS
499    /// and transport settings. It allows you to pass a preconfigured [`TlsServerConfig`] and
500    /// a custom [`QuicTransportConfig`] to fine-tune both layers of the server configuration.
501    ///
502    /// # Parameters
503    ///
504    /// - `tls_config`: A custom [`TlsServerConfig`] instance that allows you to specify
505    ///   detailed TLS settings, such as ciphersuites, certificate verification, and more. It must
506    ///   support TLS 1.3 (see the documentation of [`Self::with_custom_tls`]).
507    /// - `quic_transport_config`: A custom [`QuicTransportConfig`] instance that allows you to specify
508    ///   various QUIC transport-layer settings according to your requirements.
509    #[cfg(feature = "quinn")]
510    #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
511    pub fn with_custom_tls_and_transport(
512        self,
513        tls_config: TlsServerConfig,
514        quic_transport_config: QuicTransportConfig,
515    ) -> ServerConfigBuilder<states::WantsTransportConfigServer> {
516        let quic_endpoint_config = quinn::EndpointConfig::default();
517        self.with(tls_config, quic_endpoint_config, quic_transport_config)
518    }
519
520    /// Directly builds [`ServerConfig`] skipping TLS and transport configuration.
521    ///
522    /// Both TLS and transport configuration is given by [`quic_config`](QuicServerConfig).
523    #[cfg(feature = "quinn")]
524    #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
525    pub fn build_with_quic_config(self, quic_config: QuicServerConfig) -> ServerConfig {
526        ServerConfig {
527            bind_address_config: self.0.bind_address_config,
528            endpoint_config: quinn::EndpointConfig::default(),
529            quic_config,
530        }
531    }
532
533    fn with(
534        self,
535        tls_config: TlsServerConfig,
536        endpoint_config: quinn::EndpointConfig,
537        transport_config: quinn::TransportConfig,
538    ) -> ServerConfigBuilder<states::WantsTransportConfigServer> {
539        ServerConfigBuilder(states::WantsTransportConfigServer {
540            bind_address_config: self.0.bind_address_config,
541            tls_config,
542            endpoint_config,
543            transport_config,
544            migration: true,
545        })
546    }
547}
548
549impl ServerConfigBuilder<states::WantsTransportConfigServer> {
550    /// Completes configuration process.
551    ///
552    /// # Panics
553    ///
554    /// See the documentation of [`Self::with_custom_tls`] for the TLS 1.3 requirement.
555    #[must_use]
556    pub fn build(self) -> ServerConfig {
557        let crypto: Arc<quinn::crypto::rustls::QuicServerConfig> = Arc::new(
558            quinn::crypto::rustls::QuicServerConfig::try_from(self.0.tls_config)
559                .expect("CipherSuite::TLS13_AES_128_GCM_SHA256 missing"),
560        );
561
562        let mut quic_config = quinn::ServerConfig::with_crypto(crypto);
563
564        quic_config.transport_config(Arc::new(self.0.transport_config));
565        quic_config.migration(self.0.migration);
566
567        ServerConfig {
568            bind_address_config: self.0.bind_address_config,
569            endpoint_config: self.0.endpoint_config,
570            quic_config,
571        }
572    }
573
574    /// Maximum duration of inactivity to accept before timing out the connection.
575    ///
576    /// The true idle timeout is the minimum of this and the peer's own max idle timeout. `None`
577    /// represents an infinite timeout.
578    ///
579    /// **WARNING**: If a peer or its network path malfunctions or acts maliciously, an infinite
580    /// idle timeout can result in permanently hung futures!
581    pub fn max_idle_timeout(
582        mut self,
583        idle_timeout: Option<Duration>,
584    ) -> Result<Self, InvalidIdleTimeout> {
585        let idle_timeout = idle_timeout
586            .map(quinn::IdleTimeout::try_from)
587            .transpose()
588            .map_err(|_| InvalidIdleTimeout)?;
589
590        self.0.transport_config.max_idle_timeout(idle_timeout);
591
592        Ok(self)
593    }
594
595    /// Period of inactivity before sending a keep-alive packet
596    ///
597    /// Keep-alive packets prevent an inactive but otherwise healthy connection from timing out.
598    ///
599    /// `None` to disable, which is the default. Only one side of any given connection needs keep-alive
600    /// enabled for the connection to be preserved. Must be set lower than the
601    /// [`max_idle_timeout`](Self::max_idle_timeout) of both peers to be effective.
602    pub fn keep_alive_interval(mut self, interval: Option<Duration>) -> Self {
603        self.0.transport_config.keep_alive_interval(interval);
604        self
605    }
606
607    /// Whether to allow clients to migrate to new addresses.
608    ///
609    /// Improves behavior for clients that move between different internet connections or suffer NAT
610    /// rebinding. Enabled by default.
611    pub fn allow_migration(mut self, value: bool) -> Self {
612        self.0.migration = value;
613        self
614    }
615}
616
617/// Client configuration.
618///
619///
620/// You can create an instance of `ClientConfig` using its builder pattern by calling
621/// the [`builder()`](Self::builder) method.
622/// Once you have an instance, you can further customize it by chaining method calls
623/// to set various configuration options.
624///
625/// ## Configuration Builder States
626///
627/// The configuration process follows a *state-based builder pattern*, where the client
628/// configuration progresses through *3* states.
629///
630/// ### 1. `WantsBindAddress`
631///
632/// The caller must supply a binding address for the client.
633/// The following options are mutually exclusive:
634///
635///   - [`with_bind_default`](ClientConfigBuilder::with_bind_default): configures to use
636///     the default bind address. This is generally the *default* choice for a client.
637///   - [`with_bind_config`](ClientConfigBuilder::with_bind_config): configures
638///     binding to an address determined by a configuration preset.
639///   - [`with_bind_address`](ClientConfigBuilder::with_bind_address): configures
640///     binding to a custom-specified socket address.
641///   - [`with_bind_address_v6`](ClientConfigBuilder::with_bind_address_v6): configures
642///     binding to a custom-specified socket address for *IPv6*, along with the [dual stack
643///     configuration](Ipv6DualStackConfig).
644///   - [`with_bind_socket`](ClientConfigBuilder::with_bind_socket): configures
645///     binding directly to a custom-specified socket.
646///
647/// Only one of these options can be selected during the client configuration process.
648///
649/// #### Examples:
650///
651/// ```
652/// use wtransport::ClientConfig;
653///
654/// ClientConfig::builder().with_bind_default();
655/// ```
656///
657/// ### 2. `WantsRootStore`
658///
659/// The caller must supply a TLS root store configuration for server certificate validation.
660/// The following options are mutually exclusive:
661///
662/// - [`with_native_certs`](ClientConfigBuilder::with_native_certs): configures to use
663///   root certificates found in the platform's native certificate store. This is the *default*
664///   configuration as it uses root store installed on the current machine.
665/// - [`with_server_certificate_hashes`][cert_hashes]: configures the client to accept
666///   *some* certificates mapped to hashes. This can be used to connect to self signed
667///   certificates securely, where the hash of the certificate is shared in advance
668///   through some other mechanism (such as an invite link).
669/// - (**insecure**) [`with_no_cert_validation`](ClientConfigBuilder::with_no_cert_validation):
670///   configure to skip server certificate validation. This might be handy for testing purpose
671///   to accept *self-signed* certificate, but you should almost always prefer
672///   [`with_server_certificate_hashes`][cert_hashes] for that use case.
673/// - [`with_custom_tls`](ClientConfigBuilder::with_custom_tls): sets the TLS client
674///   configuration manually.
675/// - [`with_custom_transport`](ClientConfigBuilder::with_custom_transport): sets the QUIC
676///   transport configuration manually (using default TLS).
677/// - [`with_custom_tls_and_transport`](ClientConfigBuilder::with_custom_tls_and_transport): sets both
678///   a custom TLS and QUIC transport configuration.
679/// - [`build_with_quic_config`](ClientConfigBuilder::build_with_quic_config): directly builds
680///   [`ClientConfig`] providing both TLS and QUIC transport configuration given by
681///   [`quic_config`](QuicClientConfig).
682///
683/// Only one of these options can be selected during the client configuration process.
684///
685/// [cert_hashes]: ClientConfigBuilder::with_server_certificate_hashes
686///
687/// #### Examples:
688/// ```
689/// use wtransport::ClientConfig;
690///
691/// ClientConfig::builder()
692///     .with_bind_default()
693///     .with_native_certs();
694/// ```
695///
696/// ### 3. `WantsTransportConfigClient`
697///
698/// The caller can supply *additional* transport configurations.
699/// Multiple options can be given at this stage. Once the configuration is completed, it is possible
700/// to finalize with the method [`build()`](ClientConfigBuilder::build).
701///
702/// All these options can be omitted in the configuration; default values will be used.
703///
704/// - [`max_idle_timeout`](ClientConfigBuilder::max_idle_timeout)
705/// - [`keep_alive_interval`](ClientConfigBuilder::keep_alive_interval)
706/// - [`dns_resolver`](ClientConfigBuilder::dns_resolver)
707///
708/// #### Examples:
709/// ```
710/// use std::time::Duration;
711/// use wtransport::ClientConfig;
712///
713/// let client_config = ClientConfig::builder()
714///     .with_bind_default()
715///     .with_native_certs()
716///     .max_idle_timeout(Some(Duration::from_secs(30)))
717///     .unwrap()
718///     .keep_alive_interval(Some(Duration::from_secs(3)))
719///     .build();
720/// ```
721#[derive(Debug)]
722pub struct ClientConfig {
723    pub(crate) bind_address_config: BindAddressConfig,
724    pub(crate) endpoint_config: quinn::EndpointConfig,
725    pub(crate) quic_config: quinn::ClientConfig,
726    pub(crate) dns_resolver: Arc<dyn DnsResolver + Send + Sync>,
727}
728
729impl ClientConfig {
730    /// Creates a builder to build up the client configuration.
731    ///
732    /// For more information, see the [`ClientConfigBuilder`] documentation.
733    pub fn builder() -> ClientConfigBuilder<states::WantsBindAddress> {
734        ClientConfigBuilder::default()
735    }
736
737    /// Allows setting a custom [`DnsResolver`] for this configuration.
738    ///
739    /// Default resolver is [`TokioDnsResolver`].
740    pub fn set_dns_resolver<R>(&mut self, dns_resolver: R)
741    where
742        R: DnsResolver + Send + Sync + 'static,
743    {
744        self.dns_resolver = Arc::new(dns_resolver);
745    }
746
747    /// Returns a reference to the inner QUIC endpoint configuration.
748    #[cfg(feature = "quinn")]
749    #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
750    pub fn quic_endpoint_config(&self) -> &quinn::EndpointConfig {
751        &self.endpoint_config
752    }
753
754    /// Returns a mutable reference to the inner QUIC endpoint configuration.
755    #[cfg(feature = "quinn")]
756    #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
757    pub fn quic_endpoint_config_mut(&mut self) -> &mut quinn::EndpointConfig {
758        &mut self.endpoint_config
759    }
760
761    /// Returns a reference to the inner QUIC configuration.
762    #[cfg(feature = "quinn")]
763    #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
764    pub fn quic_config(&self) -> &quinn::ClientConfig {
765        &self.quic_config
766    }
767
768    /// Returns a mutable reference to the inner QUIC configuration.
769    #[cfg(feature = "quinn")]
770    #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
771    pub fn quic_config_mut(&mut self) -> &mut quinn::ClientConfig {
772        &mut self.quic_config
773    }
774}
775
776impl Default for ClientConfig {
777    fn default() -> Self {
778        ClientConfig::builder()
779            .with_bind_default()
780            .with_native_certs()
781            .build()
782    }
783}
784
785/// Client builder configuration.
786///
787/// The builder might have different state at compile time.
788///
789/// # Example
790/// ```no_run
791/// # use std::net::Ipv4Addr;
792/// # use std::net::SocketAddr;
793/// # use wtransport::ClientConfig;
794/// let config = ClientConfig::builder().with_bind_default();
795/// ```
796#[must_use]
797pub struct ClientConfigBuilder<State>(State);
798
799impl ClientConfigBuilder<states::WantsBindAddress> {
800    /// Configures for connecting binding ANY IP (allowing IP dual-stack).
801    ///
802    /// Bind port will be randomly picked.
803    ///
804    /// This is equivalent to: [`Self::with_bind_config`] with [`IpBindConfig::InAddrAnyDual`].
805    pub fn with_bind_default(self) -> ClientConfigBuilder<states::WantsRootStore> {
806        self.with_bind_config(IpBindConfig::InAddrAnyDual)
807    }
808
809    /// Sets the binding (local) socket address with a specific [`IpBindConfig`].
810    ///
811    /// Bind port will be randomly picked.
812    pub fn with_bind_config(
813        self,
814        ip_bind_config: IpBindConfig,
815    ) -> ClientConfigBuilder<states::WantsRootStore> {
816        let ip_address: IpAddr = ip_bind_config.into_ip();
817
818        match ip_address {
819            IpAddr::V4(ip) => self.with_bind_address(SocketAddr::new(ip.into(), 0)),
820            IpAddr::V6(ip) => self.with_bind_address_v6(
821                SocketAddrV6::new(ip, 0, 0, 0),
822                ip_bind_config.into_dual_stack_config(),
823            ),
824        }
825    }
826
827    /// Sets the binding (local) socket address for the endpoint.
828    pub fn with_bind_address(
829        self,
830        address: SocketAddr,
831    ) -> ClientConfigBuilder<states::WantsRootStore> {
832        ClientConfigBuilder(states::WantsRootStore {
833            bind_address_config: BindAddressConfig::from(address),
834        })
835    }
836
837    /// Sets the binding (local) socket address for the endpoint.
838    ///
839    /// `dual_stack_config` allows/denies dual stack port binding.
840    pub fn with_bind_address_v6(
841        self,
842        address: SocketAddrV6,
843        dual_stack_config: Ipv6DualStackConfig,
844    ) -> ClientConfigBuilder<states::WantsRootStore> {
845        ClientConfigBuilder(states::WantsRootStore {
846            bind_address_config: BindAddressConfig::AddressV6(address, dual_stack_config),
847        })
848    }
849
850    /// Configures the client to bind to a pre-existing [`UdpSocket`].
851    ///
852    /// This allows the client to use an already created socket, which can be useful in cases
853    /// where socket reuse or specific socket configurations are necessary.
854    pub fn with_bind_socket(
855        self,
856        socket: UdpSocket,
857    ) -> ClientConfigBuilder<states::WantsRootStore> {
858        ClientConfigBuilder(states::WantsRootStore {
859            bind_address_config: BindAddressConfig::Socket(socket),
860        })
861    }
862}
863
864impl ClientConfigBuilder<states::WantsRootStore> {
865    /// Configures the client to use native (local) root certificates for server validation.
866    ///
867    /// This method loads trusted root certificates from the system's certificate store,
868    /// ensuring that your client can trust certificates signed by well-known authorities.
869    ///
870    /// It configures safe default TLS configuration.
871    pub fn with_native_certs(self) -> ClientConfigBuilder<states::WantsTransportConfigClient> {
872        use crate::tls::client::build_default_tls_config;
873
874        let tls_config = build_default_tls_config(Arc::new(build_native_cert_store()), None);
875        let endpoint_config = quinn::EndpointConfig::default();
876        let transport_config = quinn::TransportConfig::default();
877
878        self.with(tls_config, endpoint_config, transport_config)
879    }
880
881    /// Configures the client to skip server certificate validation, potentially
882    /// compromising security.
883    ///
884    /// This method is intended for advanced users and should be used with caution. It
885    /// configures the client to bypass server certificate validation during the TLS
886    /// handshake, effectively trusting any server certificate presented, even if it is
887    /// not signed by a trusted certificate authority (CA). Using this method can expose
888    /// your application to security risks.
889    ///
890    /// # Safety Note
891    ///
892    /// Using [`with_no_cert_validation`] should only be considered when you have a
893    /// specific need to disable certificate validation. In most cases, it is strongly
894    /// recommended to validate server certificates using trusted root certificates
895    /// (e.g., [`with_native_certs`]) to ensure secure communication.
896    ///
897    /// However, this method can be useful in testing environments or situations where
898    /// you intentionally want to skip certificate validation for specific use cases.
899    ///
900    /// [`with_native_certs`]: #method.with_native_certs
901    /// [`with_no_cert_validation`]: #method.with_no_cert_validation
902    #[cfg(feature = "dangerous-configuration")]
903    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-configuration")))]
904    pub fn with_no_cert_validation(
905        self,
906    ) -> ClientConfigBuilder<states::WantsTransportConfigClient> {
907        use crate::tls::client::build_default_tls_config;
908        use crate::tls::client::NoServerVerification;
909        use rustls::RootCertStore;
910
911        let tls_config = build_default_tls_config(
912            Arc::new(RootCertStore::empty()),
913            Some(Arc::new(NoServerVerification::new())),
914        );
915
916        let endpoint_config = quinn::EndpointConfig::default();
917        let transport_config = quinn::TransportConfig::default();
918
919        self.with(tls_config, endpoint_config, transport_config)
920    }
921
922    /// Configures the client to skip *some* server certificates validation.
923    ///
924    /// This method configures the client to accept server certificates
925    /// whose digests match the specified *SHA-256* hashes and fulfill
926    /// some additional constraints (*see notes below*).
927    ///
928    /// This is useful for scenarios where clients need to accept known
929    /// self-signed certificates or certificates from non-standard authorities.
930    ///
931    /// This method configuration is similar to the
932    /// [browser W3C WebTransport API](https://www.w3.org/TR/webtransport/#dom-webtransportoptions-servercertificatehashes).
933    ///
934    /// # Notes
935    ///
936    /// - The current time MUST be within the validity period of the certificate.
937    /// - The total length of the validity period MUST NOT exceed *two* weeks.
938    /// - Only certificates for which the public key algorithm is *ECDSA* with the *secp256r1* are accepted.
939    pub fn with_server_certificate_hashes<I>(
940        self,
941        hashes: I,
942    ) -> ClientConfigBuilder<states::WantsTransportConfigClient>
943    where
944        I: IntoIterator<Item = crate::tls::Sha256Digest>,
945    {
946        use crate::tls::client::build_default_tls_config;
947        use crate::tls::client::ServerHashVerification;
948        use rustls::RootCertStore;
949
950        let tls_config = build_default_tls_config(
951            Arc::new(RootCertStore::empty()),
952            Some(Arc::new(ServerHashVerification::new(hashes))),
953        );
954
955        let endpoint_config = quinn::EndpointConfig::default();
956        let transport_config = quinn::TransportConfig::default();
957
958        self.with(tls_config, endpoint_config, transport_config)
959    }
960
961    /// Allows for manual configuration of a custom TLS setup using a provided
962    /// [`rustls::ClientConfig`], which must support
963    /// [`rustls::CipherSuite::TLS13_AES_128_GCM_SHA256`]. A suitable configuration
964    /// can be obtained using the `ring` crypto provider with a set of versions containing
965    /// [`rustls::version::TLS13`].
966    ///
967    /// This method is provided for advanced users who need fine-grained control over the
968    /// TLS configuration. It allows you to pass a preconfigured [`rustls::ClientConfig`]
969    /// instance to customize the TLS settings according to your specific requirements.
970    ///
971    /// For most use cases, it is recommended to use the [`with_native_certs`](Self::with_native_certs)
972    /// method to configure TLS with safe defaults.
973    pub fn with_custom_tls(
974        self,
975        tls_config: TlsClientConfig,
976    ) -> ClientConfigBuilder<states::WantsTransportConfigClient> {
977        let endpoint_config = quinn::EndpointConfig::default();
978        let transport_config = quinn::TransportConfig::default();
979
980        self.with(tls_config, endpoint_config, transport_config)
981    }
982
983    /// Similar to [`with_native_certs`](Self::with_native_certs), but it allows specifying a custom
984    /// QUIC transport configuration.
985    ///
986    /// # Parameters
987    ///
988    /// - `quic_transport_config`: A custom [`QuicTransportConfig`] instance that allows you to specify
989    ///   various QUIC transport-layer settings according to your requirements.
990    ///
991    /// # Example
992    ///
993    /// ```
994    /// use wtransport::config::QuicTransportConfig;
995    /// use wtransport::ClientConfig;
996    ///
997    /// // Create a custom QuicTransportConfig with specific settings
998    /// let mut custom_transport_config = QuicTransportConfig::default();
999    /// custom_transport_config.datagram_send_buffer_size(1024);
1000    ///
1001    /// // Create a ClientConfigBuilder with the custom transport configuration and default TLS settings
1002    /// let client_config = ClientConfig::builder()
1003    ///     .with_bind_default()
1004    ///     .with_custom_transport(custom_transport_config)
1005    ///     .build();
1006    /// ```
1007    #[cfg(feature = "quinn")]
1008    #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
1009    pub fn with_custom_transport(
1010        self,
1011        quic_transport_config: QuicTransportConfig,
1012    ) -> ClientConfigBuilder<states::WantsTransportConfigClient> {
1013        use crate::tls::client::build_default_tls_config;
1014
1015        let tls_config = build_default_tls_config(Arc::new(build_native_cert_store()), None);
1016        let quic_endpoint_config = quinn::EndpointConfig::default();
1017
1018        self.with(tls_config, quic_endpoint_config, quic_transport_config)
1019    }
1020
1021    /// Configures the client with both a custom TLS configuration and a custom QUIC transport
1022    /// configuration.
1023    ///
1024    /// This method is designed for advanced users who require full control over both the TLS
1025    /// and transport settings. It allows you to pass a preconfigured [`TlsClientConfig`] and
1026    /// a custom [`QuicTransportConfig`] to fine-tune both layers of the server configuration.
1027    ///
1028    /// # Parameters
1029    ///
1030    /// - `tls_config`: A custom [`TlsClientConfig`] instance that allows you to specify
1031    ///   detailed TLS settings, such as ciphersuites, certificate verification, and more. It must
1032    ///   support TLS 1.3 (see the documentation of [`Self::with_custom_tls`]).
1033    /// - `quic_transport_config`: A custom [`QuicTransportConfig`] instance that allows you to specify
1034    ///   various QUIC transport-layer settings according to your requirements.
1035    #[cfg(feature = "quinn")]
1036    #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
1037    pub fn with_custom_tls_and_transport(
1038        self,
1039        tls_config: TlsClientConfig,
1040        quic_transport_config: QuicTransportConfig,
1041    ) -> ClientConfigBuilder<states::WantsTransportConfigClient> {
1042        let quic_endpoint_config = quinn::EndpointConfig::default();
1043        self.with(tls_config, quic_endpoint_config, quic_transport_config)
1044    }
1045
1046    /// Directly builds [`ClientConfig`] skipping TLS and transport configuration.
1047    ///
1048    /// Both TLS and transport configuration is given by [`quic_config`](QuicClientConfig).
1049    #[cfg(feature = "quinn")]
1050    #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
1051    pub fn build_with_quic_config(self, quic_config: QuicClientConfig) -> ClientConfig {
1052        ClientConfig {
1053            bind_address_config: self.0.bind_address_config,
1054            endpoint_config: quinn::EndpointConfig::default(),
1055            quic_config,
1056            dns_resolver: Arc::<TokioDnsResolver>::default(),
1057        }
1058    }
1059
1060    fn with(
1061        self,
1062        tls_config: TlsClientConfig,
1063        endpoint_config: quinn::EndpointConfig,
1064        transport_config: quinn::TransportConfig,
1065    ) -> ClientConfigBuilder<states::WantsTransportConfigClient> {
1066        ClientConfigBuilder(states::WantsTransportConfigClient {
1067            bind_address_config: self.0.bind_address_config,
1068            tls_config,
1069            endpoint_config,
1070            transport_config,
1071            dns_resolver: Arc::<TokioDnsResolver>::default(),
1072        })
1073    }
1074}
1075
1076impl ClientConfigBuilder<states::WantsTransportConfigClient> {
1077    /// Completes configuration process.
1078    ///
1079    /// # Panics
1080    ///
1081    /// See the documentation of [`Self::with_custom_tls`] for the TLS 1.3 requirement.
1082    #[must_use]
1083    pub fn build(self) -> ClientConfig {
1084        let crypto = quinn::crypto::rustls::QuicClientConfig::try_from(self.0.tls_config)
1085            .expect("CipherSuite::TLS13_AES_128_GCM_SHA256 missing");
1086
1087        let mut quic_config = quinn::ClientConfig::new(Arc::new(crypto));
1088        quic_config.transport_config(Arc::new(self.0.transport_config));
1089
1090        ClientConfig {
1091            bind_address_config: self.0.bind_address_config,
1092            endpoint_config: self.0.endpoint_config,
1093            quic_config,
1094            dns_resolver: self.0.dns_resolver,
1095        }
1096    }
1097
1098    /// Maximum duration of inactivity to accept before timing out the connection.
1099    ///
1100    /// The true idle timeout is the minimum of this and the peer's own max idle timeout. `None`
1101    /// represents an infinite timeout.
1102    ///
1103    /// **WARNING**: If a peer or its network path malfunctions or acts maliciously, an infinite
1104    /// idle timeout can result in permanently hung futures!
1105    pub fn max_idle_timeout(
1106        mut self,
1107        idle_timeout: Option<Duration>,
1108    ) -> Result<Self, InvalidIdleTimeout> {
1109        let idle_timeout = idle_timeout
1110            .map(quinn::IdleTimeout::try_from)
1111            .transpose()
1112            .map_err(|_| InvalidIdleTimeout)?;
1113
1114        self.0.transport_config.max_idle_timeout(idle_timeout);
1115
1116        Ok(self)
1117    }
1118
1119    /// Period of inactivity before sending a keep-alive packet
1120    ///
1121    /// Keep-alive packets prevent an inactive but otherwise healthy connection from timing out.
1122    ///
1123    /// `None` to disable, which is the default. Only one side of any given connection needs keep-alive
1124    /// enabled for the connection to be preserved. Must be set lower than the
1125    /// [`max_idle_timeout`](Self::max_idle_timeout) of both peers to be effective.
1126    pub fn keep_alive_interval(mut self, interval: Option<Duration>) -> Self {
1127        self.0.transport_config.keep_alive_interval(interval);
1128        self
1129    }
1130
1131    /// Sets the *DNS* resolver used during [`Endpoint::connect`](crate::Endpoint::connect).
1132    ///
1133    /// Default configuration uses [`TokioDnsResolver`].
1134    pub fn dns_resolver<R>(mut self, dns_resolver: R) -> Self
1135    where
1136        R: DnsResolver + Send + Sync + 'static,
1137    {
1138        self.0.dns_resolver = Arc::new(dns_resolver);
1139        self
1140    }
1141}
1142
1143impl Default for ServerConfigBuilder<states::WantsBindAddress> {
1144    fn default() -> Self {
1145        Self(states::WantsBindAddress {})
1146    }
1147}
1148
1149impl Default for ClientConfigBuilder<states::WantsBindAddress> {
1150    fn default() -> Self {
1151        Self(states::WantsBindAddress {})
1152    }
1153}
1154
1155#[derive(Debug)]
1156pub(crate) enum BindAddressConfig {
1157    AddressV4(SocketAddrV4),
1158    AddressV6(SocketAddrV6, Ipv6DualStackConfig),
1159    Socket(UdpSocket),
1160}
1161
1162impl BindAddressConfig {
1163    pub(crate) fn bind_socket(self) -> std::io::Result<UdpSocket> {
1164        let (bind_address, dual_stack_config) = match self {
1165            BindAddressConfig::AddressV4(address) => {
1166                (SocketAddr::from(address), Ipv6DualStackConfig::OsDefault)
1167            }
1168            BindAddressConfig::AddressV6(address, ipv6_dual_stack_config) => {
1169                (SocketAddr::from(address), ipv6_dual_stack_config)
1170            }
1171            BindAddressConfig::Socket(socket) => {
1172                return Ok(socket);
1173            }
1174        };
1175
1176        let domain = match bind_address {
1177            SocketAddr::V4(_) => SocketDomain::IPV4,
1178            SocketAddr::V6(_) => SocketDomain::IPV6,
1179        };
1180
1181        let socket = Socket::new(domain, SocketType::DGRAM, Some(SocketProtocol::UDP))?;
1182
1183        match dual_stack_config {
1184            Ipv6DualStackConfig::OsDefault => {}
1185            Ipv6DualStackConfig::Deny => socket.set_only_v6(true)?,
1186            Ipv6DualStackConfig::Allow => socket.set_only_v6(false)?,
1187        }
1188
1189        socket.bind(&bind_address.into())?;
1190
1191        Ok(UdpSocket::from(socket))
1192    }
1193}
1194
1195impl From<SocketAddr> for BindAddressConfig {
1196    fn from(value: SocketAddr) -> Self {
1197        match value {
1198            SocketAddr::V4(address) => BindAddressConfig::AddressV4(address),
1199            SocketAddr::V6(address) => {
1200                BindAddressConfig::AddressV6(address, Ipv6DualStackConfig::OsDefault)
1201            }
1202        }
1203    }
1204}
1205
1206/// State-types for client/server builder.
1207pub mod states {
1208    use super::*;
1209
1210    /// Config builder state where the caller must supply binding address.
1211    pub struct WantsBindAddress {}
1212
1213    /// Config builder state where the caller must supply TLS certificate.
1214    pub struct WantsIdentity {
1215        pub(super) bind_address_config: BindAddressConfig,
1216    }
1217
1218    /// Config builder state where the caller must supply TLS root store.
1219    pub struct WantsRootStore {
1220        pub(super) bind_address_config: BindAddressConfig,
1221    }
1222
1223    /// Config builder state where transport properties can be set.
1224    pub struct WantsTransportConfigServer {
1225        pub(super) bind_address_config: BindAddressConfig,
1226        pub(super) tls_config: TlsServerConfig,
1227        pub(super) endpoint_config: quinn::EndpointConfig,
1228        pub(super) transport_config: quinn::TransportConfig,
1229        pub(super) migration: bool,
1230    }
1231
1232    /// Config builder state where transport properties can be set.
1233    pub struct WantsTransportConfigClient {
1234        pub(super) bind_address_config: BindAddressConfig,
1235        pub(super) tls_config: TlsClientConfig,
1236        pub(super) endpoint_config: quinn::EndpointConfig,
1237        pub(super) transport_config: quinn::TransportConfig,
1238        pub(super) dns_resolver: Arc<dyn DnsResolver + Send + Sync>,
1239    }
1240}
1241
1242/// Future resolving domain name.
1243///
1244/// See [`DnsResolver::resolve`].
1245pub trait DnsLookupFuture: Future<Output = std::io::Result<Option<SocketAddr>>> + Send {}
1246
1247impl<F> DnsLookupFuture for F where F: Future<Output = std::io::Result<Option<SocketAddr>>> + Send {}
1248
1249/// A trait for asynchronously resolving domain names to IP addresses using DNS.
1250pub trait DnsResolver: Debug {
1251    /// Resolves a domain name to one IP address.
1252    fn resolve(&self, host: &str) -> Pin<Box<dyn DnsLookupFuture>>;
1253}
1254
1255/// A DNS resolver implementation using the *Tokio* asynchronous runtime.
1256///
1257/// Internally, it uses [`tokio::net::lookup_host`].
1258#[derive(Default)]
1259pub struct TokioDnsResolver;
1260
1261impl DnsResolver for TokioDnsResolver {
1262    fn resolve(&self, host: &str) -> Pin<Box<dyn DnsLookupFuture>> {
1263        let host = host.to_string();
1264
1265        Box::pin(async move { Ok(tokio::net::lookup_host(host).await?.next()) })
1266    }
1267}
1268
1269impl Debug for TokioDnsResolver {
1270    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1271        f.debug_struct("TokioDnsResolver").finish()
1272    }
1273}
1274
1275impl std::error::Error for InvalidIdleTimeout {}
1276
1277impl Debug for InvalidIdleTimeout {
1278    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1279        f.write_str("idle timeout value configuration is invalid")
1280    }
1281}
1282
1283impl Display for InvalidIdleTimeout {
1284    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1285        Debug::fmt(self, f)
1286    }
1287}