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 hyper::{
15 client::{connect::dns::Name, HttpConnector},
16 service::Service,
17};
18use trust_dns_resolver::{
19 config::{ResolverConfig, ResolverOpts},
20 error::ResolveError,
21 lookup_ip::LookupIpIntoIter,
22 TokioAsyncResolver,
23};
24
25#[derive(Clone)]
27pub struct TrustDnsResolver {
28 resolver: Arc<TokioAsyncResolver>,
29}
30
31pub struct SocketAddrs {
33 iter: LookupIpIntoIter,
34}
35
36impl Iterator for SocketAddrs {
37 type Item = SocketAddr;
38
39 fn next(&mut self) -> Option<Self::Item> {
40 self.iter.next().map(|ip_addr| SocketAddr::new(ip_addr, 0))
41 }
42}
43
44fn default_opts() -> ResolverOpts {
47 #[cfg(any(feature = "dnssec-openssl", feature = "dnssec-ring"))]
48 let mut opts = ResolverOpts::default();
49 #[cfg(not(any(feature = "dnssec-openssl", feature = "dnssec-ring")))]
50 let opts = ResolverOpts::default();
51
52 #[cfg(any(feature = "dnssec-openssl", feature = "dnssec-ring"))]
53 {
54 opts.validate = true;
55 }
56
57 opts
58}
59
60impl TrustDnsResolver {
61 #[must_use]
64 pub fn new() -> Self {
65 Self::default()
66 }
67
68 #[must_use]
71 pub fn google() -> Self {
72 Self::with_config_and_options(ResolverConfig::google(), default_opts())
73 }
74
75 #[must_use]
78 pub fn cloudflare() -> Self {
79 Self::with_config_and_options(ResolverConfig::cloudflare(), default_opts())
80 }
81
82 #[cfg(feature = "dns-over-https-rustls")]
86 #[must_use]
87 pub fn cloudflare_https() -> Self {
88 Self::with_config_and_options(ResolverConfig::cloudflare_https(), default_opts())
89 }
90
91 #[cfg(any(
95 feature = "dns-over-rustls",
96 feature = "dns-over-native-tls",
97 feature = "dns-over-openssl"
98 ))]
99 #[must_use]
100 pub fn cloudflare_tls() -> Self {
101 Self::with_config_and_options(ResolverConfig::cloudflare_tls(), default_opts())
102 }
103
104 #[must_use]
107 pub fn quad9() -> Self {
108 Self::with_config_and_options(ResolverConfig::quad9(), default_opts())
109 }
110
111 #[cfg(feature = "dns-over-https-rustls")]
115 #[must_use]
116 pub fn quad9_https() -> Self {
117 Self::with_config_and_options(ResolverConfig::quad9_https(), default_opts())
118 }
119
120 #[cfg(any(
124 feature = "dns-over-rustls",
125 feature = "dns-over-native-tls",
126 feature = "dns-over-openssl"
127 ))]
128 #[must_use]
129 pub fn quad9_tls() -> Self {
130 Self::with_config_and_options(ResolverConfig::quad9_tls(), default_opts())
131 }
132
133 #[allow(clippy::missing_panics_doc)]
137 #[must_use]
138 pub fn with_config_and_options(config: ResolverConfig, options: ResolverOpts) -> Self {
139 let resolver = Arc::new(TokioAsyncResolver::tokio(config, options).unwrap());
142
143 Self { resolver }
144 }
145
146 #[cfg(feature = "system-config")]
149 #[allow(clippy::missing_panics_doc)]
150 #[must_use]
151 pub fn from_system_conf() -> Self {
152 let resolver = Arc::new(TokioAsyncResolver::tokio_from_system_conf().unwrap());
155
156 Self { resolver }
157 }
158
159 #[must_use]
161 pub fn into_http_connector(self) -> TrustDnsHttpConnector {
162 TrustDnsHttpConnector::new_with_resolver(self)
163 }
164
165 #[cfg(feature = "native-tls")]
167 #[must_use]
168 pub fn into_native_tls_https_connector(self) -> NativeTlsHttpsConnector {
169 let mut http_connector = self.into_http_connector();
170 http_connector.enforce_http(false);
171
172 let mut native_https_connector =
173 NativeTlsHttpsConnector::new_with_connector(http_connector);
174
175 #[cfg(feature = "https-only")]
176 native_https_connector.https_only(true);
177
178 #[cfg(not(feature = "https-only"))]
179 native_https_connector.https_only(false);
180
181 native_https_connector
182 }
183
184 #[cfg(feature = "rustls-native")]
186 #[must_use]
187 pub fn into_rustls_native_https_connector(self) -> RustlsHttpsConnector {
188 let mut http_connector = self.into_http_connector();
189 http_connector.enforce_http(false);
190
191 let builder = hyper_rustls::HttpsConnectorBuilder::new().with_native_roots();
192
193 #[cfg(feature = "https-only")]
194 let builder = builder.https_only();
195
196 #[cfg(not(feature = "https-only"))]
197 let builder = builder.https_or_http();
198
199 #[cfg(feature = "rustls-http1")]
200 let builder = builder.enable_http1();
201
202 #[cfg(feature = "rustls-http2")]
203 let builder = builder.enable_http2();
204
205 builder.wrap_connector(http_connector)
206 }
207
208 #[cfg(feature = "rustls-webpki")]
210 #[must_use]
211 pub fn into_rustls_webpki_https_connector(self) -> RustlsHttpsConnector {
212 let mut http_connector = self.into_http_connector();
213 http_connector.enforce_http(false);
214
215 let builder = hyper_rustls::HttpsConnectorBuilder::new().with_webpki_roots();
216
217 #[cfg(feature = "https-only")]
218 let builder = builder.https_only();
219
220 #[cfg(not(feature = "https-only"))]
221 let builder = builder.https_or_http();
222
223 #[cfg(feature = "rustls-http1")]
224 let builder = builder.enable_http1();
225
226 #[cfg(feature = "rustls-http2")]
227 let builder = builder.enable_http2();
228
229 builder.wrap_connector(http_connector)
230 }
231}
232
233impl Default for TrustDnsResolver {
234 fn default() -> Self {
235 Self::with_config_and_options(ResolverConfig::default(), default_opts())
236 }
237}
238
239impl Service<Name> for TrustDnsResolver {
240 type Response = SocketAddrs;
241 type Error = ResolveError;
242 #[allow(clippy::type_complexity)]
243 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
244
245 fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
246 Poll::Ready(Ok(()))
247 }
248
249 fn call(&mut self, name: Name) -> Self::Future {
250 let resolver = self.resolver.clone();
251
252 Box::pin(async move {
253 let response = resolver.lookup_ip(name.as_str()).await?;
254 let addresses = response.into_iter();
255
256 Ok(SocketAddrs { iter: addresses })
257 })
258 }
259}
260
261pub type TrustDnsHttpConnector = HttpConnector<TrustDnsResolver>;
263
264#[cfg(feature = "native-tls")]
266pub type NativeTlsHttpsConnector = hyper_tls::HttpsConnector<TrustDnsHttpConnector>;
267
268#[cfg(any(feature = "rustls-native", feature = "rustls-webpki"))]
270pub type RustlsHttpsConnector = hyper_rustls::HttpsConnector<TrustDnsHttpConnector>;