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
14#[cfg(feature = "tokio")]
15use hickory_resolver::{
16 config::ResolverConfig, config::ResolverOpts, name_server::TokioConnectionProvider,
17 proto::runtime::TokioRuntimeProvider, TokioResolver,
18};
19use hickory_resolver::{
20 lookup_ip::LookupIpIntoIter, name_server::ConnectionProvider, ResolveError, Resolver,
21};
22use hyper_util::client::legacy::connect::{dns::Name, HttpConnector};
23use tower_service::Service;
24
25#[cfg(feature = "tokio")]
27pub type TokioHickoryResolver = HickoryResolver<TokioConnectionProvider>;
28
29#[derive(Clone)]
31pub struct HickoryResolver<C: ConnectionProvider> {
32 resolver: Arc<Resolver<C>>,
33}
34
35pub struct SocketAddrs {
37 iter: LookupIpIntoIter,
38}
39
40impl Iterator for SocketAddrs {
41 type Item = SocketAddr;
42
43 fn next(&mut self) -> Option<Self::Item> {
44 self.iter.next().map(|ip_addr| SocketAddr::new(ip_addr, 0))
45 }
46}
47
48#[cfg(feature = "tokio")]
51fn default_opts() -> ResolverOpts {
52 #[cfg(any(feature = "dnssec-aws-lc-rs", feature = "dnssec-ring"))]
53 let mut opts = ResolverOpts::default();
54 #[cfg(not(any(feature = "dnssec-aws-lc-rs", feature = "dnssec-ring")))]
55 let opts = ResolverOpts::default();
56
57 #[cfg(any(feature = "dnssec-aws-lc-rs", feature = "dnssec-ring"))]
58 {
59 opts.validate = true;
60 }
61
62 opts
63}
64
65#[cfg(feature = "tokio")]
66impl TokioHickoryResolver {
67 #[must_use]
70 pub fn new() -> Self {
71 Self::default()
72 }
73
74 #[must_use]
77 pub fn google() -> Self {
78 Self::with_config_and_options(ResolverConfig::google(), default_opts())
79 }
80
81 #[must_use]
84 pub fn cloudflare() -> Self {
85 Self::with_config_and_options(ResolverConfig::cloudflare(), default_opts())
86 }
87
88 #[cfg(any(feature = "https-aws-lc-rs", feature = "https-ring"))]
92 #[must_use]
93 pub fn cloudflare_https() -> Self {
94 Self::with_config_and_options(ResolverConfig::cloudflare_https(), default_opts())
95 }
96
97 #[cfg(any(feature = "tls-aws-lc-rs", feature = "tls-ring",))]
101 #[must_use]
102 pub fn cloudflare_tls() -> Self {
103 Self::with_config_and_options(ResolverConfig::cloudflare_tls(), default_opts())
104 }
105
106 #[must_use]
109 pub fn quad9() -> Self {
110 Self::with_config_and_options(ResolverConfig::quad9(), default_opts())
111 }
112
113 #[cfg(any(feature = "https-aws-lc-rs", feature = "https-ring"))]
117 #[must_use]
118 pub fn quad9_https() -> Self {
119 Self::with_config_and_options(ResolverConfig::quad9_https(), default_opts())
120 }
121
122 #[cfg(any(feature = "tls-aws-lc-rs", feature = "tls-ring",))]
126 #[must_use]
127 pub fn quad9_tls() -> Self {
128 Self::with_config_and_options(ResolverConfig::quad9_tls(), default_opts())
129 }
130
131 #[must_use]
135 pub fn with_config_and_options(config: ResolverConfig, options: ResolverOpts) -> Self {
136 Self::from_resolver(
137 TokioResolver::builder_with_config(
138 config,
139 TokioConnectionProvider::new(TokioRuntimeProvider::new()),
140 )
141 .with_options(options)
142 .build(),
143 )
144 }
145
146 #[cfg(feature = "system-config")]
153 pub fn from_system_conf() -> Result<Self, ResolveError> {
154 Ok(Self::from_resolver(TokioResolver::builder_tokio()?.build()))
155 }
156}
157
158#[cfg(feature = "tokio")]
159impl Default for TokioHickoryResolver {
160 fn default() -> Self {
161 Self::with_config_and_options(ResolverConfig::default(), default_opts())
162 }
163}
164
165impl<C: ConnectionProvider> HickoryResolver<C> {
166 #[must_use]
168 pub fn from_resolver(resolver: Resolver<C>) -> Self {
169 let resolver = Arc::new(resolver);
170
171 Self { resolver }
172 }
173
174 #[must_use]
176 pub fn into_http_connector(self) -> HickoryHttpConnector<C> {
177 HickoryHttpConnector::new_with_resolver(self)
178 }
179}
180
181impl<C: ConnectionProvider> Service<Name> for HickoryResolver<C> {
182 type Response = SocketAddrs;
183 type Error = ResolveError;
184 #[allow(clippy::type_complexity)]
185 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
186
187 fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
188 Poll::Ready(Ok(()))
189 }
190
191 fn call(&mut self, name: Name) -> Self::Future {
192 let resolver = self.resolver.clone();
193
194 Box::pin(async move {
195 let response = resolver.lookup_ip(name.as_str()).await?;
196 let addresses = response.into_iter();
197
198 Ok(SocketAddrs { iter: addresses })
199 })
200 }
201}
202
203pub type HickoryHttpConnector<C> = HttpConnector<HickoryResolver<C>>;
205
206#[cfg(feature = "tokio")]
208pub type TokioHickoryHttpConnector = HickoryHttpConnector<TokioConnectionProvider>;