1#![doc = include_str!("../README.md")]
2#![deny(clippy::pedantic, missing_docs)]
3#![allow(clippy::module_name_repetitions)]
4#![cfg_attr(docsrs, feature(doc_auto_cfg))]
5
6use std::{
7 future::Future,
8 net::SocketAddr,
9 pin::Pin,
10 sync::Arc,
11 task::{self, Poll},
12};
13
14use hickory_resolver::{
15 error::ResolveError, lookup_ip::LookupIpIntoIter, name_server::ConnectionProvider,
16 AsyncResolver,
17};
18
19use hickory_resolver::{
20 config::{ResolverConfig, ResolverOpts},
21 name_server::TokioConnectionProvider,
22 TokioAsyncResolver,
23};
24use hyper_util::client::legacy::connect::{dns::Name, HttpConnector};
25use tower_service::Service;
26
27pub type TokioHickoryResolver = HickoryResolver<TokioConnectionProvider>;
29
30#[derive(Clone)]
32pub struct HickoryResolver<C: ConnectionProvider> {
33 resolver: Arc<AsyncResolver<C>>,
34}
35
36pub struct SocketAddrs {
38 iter: LookupIpIntoIter,
39}
40
41impl Iterator for SocketAddrs {
42 type Item = SocketAddr;
43
44 fn next(&mut self) -> Option<Self::Item> {
45 self.iter.next().map(|ip_addr| SocketAddr::new(ip_addr, 0))
46 }
47}
48
49fn default_opts() -> ResolverOpts {
52 #[cfg(any(feature = "dnssec-openssl", feature = "dnssec-ring"))]
53 let mut opts = ResolverOpts::default();
54 #[cfg(not(any(feature = "dnssec-openssl", feature = "dnssec-ring")))]
55 let opts = ResolverOpts::default();
56
57 #[cfg(any(feature = "dnssec-openssl", feature = "dnssec-ring"))]
58 {
59 opts.validate = true;
60 }
61
62 opts
63}
64
65impl TokioHickoryResolver {
66 #[must_use]
69 pub fn new() -> Self {
70 Self::default()
71 }
72
73 #[must_use]
76 pub fn google() -> Self {
77 Self::with_config_and_options(ResolverConfig::google(), default_opts())
78 }
79
80 #[must_use]
83 pub fn cloudflare() -> Self {
84 Self::with_config_and_options(ResolverConfig::cloudflare(), default_opts())
85 }
86
87 #[cfg(feature = "dns-over-https-rustls")]
91 #[must_use]
92 pub fn cloudflare_https() -> Self {
93 Self::with_config_and_options(ResolverConfig::cloudflare_https(), default_opts())
94 }
95
96 #[cfg(any(
100 feature = "dns-over-rustls",
101 feature = "dns-over-native-tls",
102 feature = "dns-over-openssl"
103 ))]
104 #[must_use]
105 pub fn cloudflare_tls() -> Self {
106 Self::with_config_and_options(ResolverConfig::cloudflare_tls(), default_opts())
107 }
108
109 #[must_use]
112 pub fn quad9() -> Self {
113 Self::with_config_and_options(ResolverConfig::quad9(), default_opts())
114 }
115
116 #[cfg(feature = "dns-over-https-rustls")]
120 #[must_use]
121 pub fn quad9_https() -> Self {
122 Self::with_config_and_options(ResolverConfig::quad9_https(), default_opts())
123 }
124
125 #[cfg(any(
129 feature = "dns-over-rustls",
130 feature = "dns-over-native-tls",
131 feature = "dns-over-openssl"
132 ))]
133 #[must_use]
134 pub fn quad9_tls() -> Self {
135 Self::with_config_and_options(ResolverConfig::quad9_tls(), default_opts())
136 }
137
138 #[must_use]
142 pub fn with_config_and_options(config: ResolverConfig, options: ResolverOpts) -> Self {
143 Self::from_async_resolver(TokioAsyncResolver::tokio(config, options))
144 }
145
146 #[cfg(feature = "system-config")]
153 pub fn from_system_conf() -> Result<Self, hickory_resolver::error::ResolveError> {
154 Ok(Self::from_async_resolver(
155 TokioAsyncResolver::tokio_from_system_conf()?,
156 ))
157 }
158}
159
160impl Default for TokioHickoryResolver {
161 fn default() -> Self {
162 Self::with_config_and_options(ResolverConfig::default(), default_opts())
163 }
164}
165
166impl<C: ConnectionProvider> HickoryResolver<C> {
167 #[must_use]
169 pub fn from_async_resolver(async_resolver: AsyncResolver<C>) -> Self {
170 let resolver = Arc::new(async_resolver);
171
172 Self { resolver }
173 }
174
175 #[must_use]
177 pub fn into_http_connector(self) -> HickoryHttpConnector<C> {
178 HickoryHttpConnector::new_with_resolver(self)
179 }
180}
181
182impl<C: ConnectionProvider> Service<Name> for HickoryResolver<C> {
183 type Response = SocketAddrs;
184 type Error = ResolveError;
185 #[allow(clippy::type_complexity)]
186 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
187
188 fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
189 Poll::Ready(Ok(()))
190 }
191
192 fn call(&mut self, name: Name) -> Self::Future {
193 let resolver = self.resolver.clone();
194
195 Box::pin(async move {
196 let response = resolver.lookup_ip(name.as_str()).await?;
197 let addresses = response.into_iter();
198
199 Ok(SocketAddrs { iter: addresses })
200 })
201 }
202}
203
204pub type HickoryHttpConnector<C> = HttpConnector<HickoryResolver<C>>;
206
207pub type TokioHickoryHttpConnector = HickoryHttpConnector<TokioConnectionProvider>;