diff --git a/src/client/http/mod.rs b/src/client/http/mod.rs index bc2aeab42..f3172f261 100644 --- a/src/client/http/mod.rs +++ b/src/client/http/mod.rs @@ -51,7 +51,7 @@ use super::{ response::Response, }; #[cfg(feature = "hickory-dns")] -use crate::dns::hickory::{HickoryDnsResolver, LookupIpStrategy}; +use crate::dns::hickory::HickoryDnsResolver; use crate::{ IntoUrl, Method, Proxy, client::{ @@ -254,9 +254,7 @@ impl ClientBuilder { let mut resolver: Arc = match config.dns_resolver { Some(dns_resolver) => dns_resolver, #[cfg(feature = "hickory-dns")] - None if config.hickory_dns => { - Arc::new(HickoryDnsResolver::new(LookupIpStrategy::Ipv4thenIpv6)?) - } + None if config.hickory_dns => Arc::new(HickoryDnsResolver::new()), None => Arc::new(GaiResolver::new()), }; diff --git a/src/core/client/connect/dns/hickory.rs b/src/core/client/connect/dns/hickory.rs index 0bb2f4929..585d33b80 100644 --- a/src/core/client/connect/dns/hickory.rs +++ b/src/core/client/connect/dns/hickory.rs @@ -1,52 +1,25 @@ //! DNS resolution via the [hickory-resolver](https://github.com/hickory-dns/hickory-dns) crate -use std::{net::SocketAddr, sync::Arc}; +use std::{net::SocketAddr, sync::LazyLock}; use hickory_resolver::{ TokioResolver, - config::{LookupIpStrategy as HickoryLookupIpStrategy, ResolverConfig}, + config::{LookupIpStrategy, ResolverConfig}, lookup_ip::LookupIpIntoIter, name_server::TokioConnectionProvider, }; use super::{Addrs, Name, Resolve, Resolving}; -/// The lookup ip strategy -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] -#[repr(u8)] -pub enum LookupIpStrategy { - /// Only query for A (Ipv4) records - Ipv4Only, - /// Only query for AAAA (Ipv6) records - Ipv6Only, - /// Query for A and AAAA in parallel - #[default] - Ipv4AndIpv6, - /// Query for Ipv6 if that fails, query for Ipv4 - Ipv6thenIpv4, - /// Query for Ipv4 if that fails, query for Ipv6 - Ipv4thenIpv6, -} - -impl LookupIpStrategy { - const fn to_hickory(self) -> HickoryLookupIpStrategy { - match self { - LookupIpStrategy::Ipv4Only => HickoryLookupIpStrategy::Ipv4Only, - LookupIpStrategy::Ipv6Only => HickoryLookupIpStrategy::Ipv6Only, - LookupIpStrategy::Ipv4AndIpv6 => HickoryLookupIpStrategy::Ipv4AndIpv6, - LookupIpStrategy::Ipv6thenIpv4 => HickoryLookupIpStrategy::Ipv6thenIpv4, - LookupIpStrategy::Ipv4thenIpv6 => HickoryLookupIpStrategy::Ipv4thenIpv6, - } - } -} - -/// Wrapper around an `AsyncResolver`, which implements the `Resolve` trait. +/// Wrapper around an [`TokioResolver`], which implements the `Resolve` trait. #[derive(Debug, Clone)] pub struct HickoryDnsResolver { - /// Since we might not have been called in the context of a - /// Tokio Runtime in initialization, so we must delay the actual - /// construction of the resolver. - state: Arc, + /// Shared, lazily-initialized Tokio-based DNS resolver. + /// + /// Backed by [`LazyLock`] to guarantee thread-safe, one-time creation. + /// On initialization, it attempts to load the system's DNS configuration; + /// if unavailable, it falls back to sensible default settings. + resolver: &'static LazyLock, } impl HickoryDnsResolver { @@ -54,29 +27,28 @@ impl HickoryDnsResolver { /// which reads from `/etc/resolve.conf`. The options are /// overriden to look up for both IPv4 and IPv6 addresses /// to work with "happy eyeballs" algorithm. - pub fn new(strategy: S) -> crate::Result - where - S: Into>, - { - let mut resolver = match TokioResolver::builder_tokio() { - Ok(resolver) => resolver, - Err(_err) => { - debug!("error reading DNS system conf: {}", _err); - TokioResolver::builder_with_config( - ResolverConfig::default(), - TokioConnectionProvider::default(), - ) - } - }; - - resolver.options_mut().ip_strategy = strategy - .into() - .map(LookupIpStrategy::to_hickory) - .unwrap_or_default(); + pub fn new() -> HickoryDnsResolver { + static RESOLVER: LazyLock = LazyLock::new(|| { + let mut builder = match TokioResolver::builder_tokio() { + Ok(resolver) => { + debug!("using system DNS configuration"); + resolver + } + Err(_err) => { + debug!("error reading DNS system conf: {}, using defaults", _err); + TokioResolver::builder_with_config( + ResolverConfig::default(), + TokioConnectionProvider::default(), + ) + } + }; + builder.options_mut().ip_strategy = LookupIpStrategy::Ipv4AndIpv6; + builder.build() + }); - Ok(Self { - state: Arc::new(resolver.build()), - }) + HickoryDnsResolver { + resolver: &RESOLVER, + } } } @@ -88,7 +60,7 @@ impl Resolve for HickoryDnsResolver { fn resolve(&self, name: Name) -> Resolving { let resolver = self.clone(); Box::pin(async move { - let lookup = resolver.state.lookup_ip(name.as_str()).await?; + let lookup = resolver.resolver.lookup_ip(name.as_str()).await?; let addrs: Addrs = Box::new(SocketAddrs { iter: lookup.into_iter(), }); diff --git a/src/core/client/connect/dns/mod.rs b/src/core/client/connect/dns/mod.rs index 36c29f0c4..5a227cd92 100644 --- a/src/core/client/connect/dns/mod.rs +++ b/src/core/client/connect/dns/mod.rs @@ -1,9 +1,9 @@ //! DNS resolution -mod gai; +pub(crate) mod gai; #[cfg(feature = "hickory-dns")] -pub mod hickory; -mod resolve; +pub(crate) mod hickory; +pub(crate) mod resolve; pub use resolve::{Addrs, Name, Resolve, Resolving};