11//! DNS resolution via the [hickory-resolver](https://github.com/hickory-dns/hickory-dns) crate
22
3- use std:: { net:: SocketAddr , sync:: Arc } ;
3+ use std:: { net:: SocketAddr , sync:: LazyLock } ;
44
55use hickory_resolver:: {
66 TokioResolver ,
7- config:: { LookupIpStrategy as HickoryLookupIpStrategy , ResolverConfig } ,
7+ config:: { LookupIpStrategy , ResolverConfig } ,
88 lookup_ip:: LookupIpIntoIter ,
99 name_server:: TokioConnectionProvider ,
1010} ;
1111
1212use super :: { Addrs , Name , Resolve , Resolving } ;
1313
14- /// The lookup ip strategy
15- #[ derive( Debug , Default , Clone , Copy , PartialEq , Eq , Hash ) ]
16- #[ repr( u8 ) ]
17- pub enum LookupIpStrategy {
18- /// Only query for A (Ipv4) records
19- Ipv4Only ,
20- /// Only query for AAAA (Ipv6) records
21- Ipv6Only ,
22- /// Query for A and AAAA in parallel
23- #[ default]
24- Ipv4AndIpv6 ,
25- /// Query for Ipv6 if that fails, query for Ipv4
26- Ipv6thenIpv4 ,
27- /// Query for Ipv4 if that fails, query for Ipv6
28- Ipv4thenIpv6 ,
29- }
30-
31- impl LookupIpStrategy {
32- const fn to_hickory ( self ) -> HickoryLookupIpStrategy {
33- match self {
34- LookupIpStrategy :: Ipv4Only => HickoryLookupIpStrategy :: Ipv4Only ,
35- LookupIpStrategy :: Ipv6Only => HickoryLookupIpStrategy :: Ipv6Only ,
36- LookupIpStrategy :: Ipv4AndIpv6 => HickoryLookupIpStrategy :: Ipv4AndIpv6 ,
37- LookupIpStrategy :: Ipv6thenIpv4 => HickoryLookupIpStrategy :: Ipv6thenIpv4 ,
38- LookupIpStrategy :: Ipv4thenIpv6 => HickoryLookupIpStrategy :: Ipv4thenIpv6 ,
39- }
40- }
41- }
42-
43- /// Wrapper around an `AsyncResolver`, which implements the `Resolve` trait.
14+ /// Wrapper around an [`TokioResolver`], which implements the `Resolve` trait.
4415#[ derive( Debug , Clone ) ]
4516pub struct HickoryDnsResolver {
46- /// Since we might not have been called in the context of a
47- /// Tokio Runtime in initialization, so we must delay the actual
48- /// construction of the resolver.
49- state : Arc < TokioResolver > ,
17+ /// Shared, lazily-initialized Tokio-based DNS resolver.
18+ ///
19+ /// Backed by [`LazyLock`] to guarantee thread-safe, one-time creation.
20+ /// On initialization, it attempts to load the system's DNS configuration;
21+ /// if unavailable, it falls back to sensible default settings.
22+ resolver : & ' static LazyLock < TokioResolver > ,
5023}
5124
5225impl HickoryDnsResolver {
5326 /// Create a new resolver with the default configuration,
5427 /// which reads from `/etc/resolve.conf`. The options are
5528 /// overriden to look up for both IPv4 and IPv6 addresses
5629 /// to work with "happy eyeballs" algorithm.
57- pub fn new < S > ( strategy : S ) -> crate :: Result < Self >
58- where
59- S : Into < Option < LookupIpStrategy > > ,
60- {
61- let mut resolver = match TokioResolver :: builder_tokio ( ) {
62- Ok ( resolver) => resolver,
63- Err ( _err) => {
64- debug ! ( "error reading DNS system conf: {}" , _err) ;
65- TokioResolver :: builder_with_config (
66- ResolverConfig :: default ( ) ,
67- TokioConnectionProvider :: default ( ) ,
68- )
69- }
70- } ;
71-
72- resolver. options_mut ( ) . ip_strategy = strategy
73- . into ( )
74- . map ( LookupIpStrategy :: to_hickory)
75- . unwrap_or_default ( ) ;
30+ pub fn new ( ) -> HickoryDnsResolver {
31+ static RESOLVER : LazyLock < TokioResolver > = LazyLock :: new ( || {
32+ let mut builder = match TokioResolver :: builder_tokio ( ) {
33+ Ok ( resolver) => {
34+ debug ! ( "using system DNS configuration" ) ;
35+ resolver
36+ }
37+ Err ( _err) => {
38+ debug ! ( "error reading DNS system conf: {}, using defaults" , _err) ;
39+ TokioResolver :: builder_with_config (
40+ ResolverConfig :: default ( ) ,
41+ TokioConnectionProvider :: default ( ) ,
42+ )
43+ }
44+ } ;
45+ builder. options_mut ( ) . ip_strategy = LookupIpStrategy :: Ipv4AndIpv6 ;
46+ builder. build ( )
47+ } ) ;
7648
77- Ok ( Self {
78- state : Arc :: new ( resolver . build ( ) ) ,
79- } )
49+ HickoryDnsResolver {
50+ resolver : & RESOLVER ,
51+ }
8052 }
8153}
8254
@@ -88,7 +60,7 @@ impl Resolve for HickoryDnsResolver {
8860 fn resolve ( & self , name : Name ) -> Resolving {
8961 let resolver = self . clone ( ) ;
9062 Box :: pin ( async move {
91- let lookup = resolver. state . lookup_ip ( name. as_str ( ) ) . await ?;
63+ let lookup = resolver. resolver . lookup_ip ( name. as_str ( ) ) . await ?;
9264 let addrs: Addrs = Box :: new ( SocketAddrs {
9365 iter : lookup. into_iter ( ) ,
9466 } ) ;
0 commit comments