#![doc = include_str!("../README.md")]
#![deny(clippy::pedantic, missing_docs)]
#![allow(clippy::module_name_repetitions)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
use std::{
future::Future,
net::SocketAddr,
pin::Pin,
sync::Arc,
task::{self, Poll},
};
use hyper::{
client::{connect::dns::Name, HttpConnector},
service::Service,
};
use trust_dns_resolver::{
config::{ResolverConfig, ResolverOpts},
error::ResolveError,
lookup_ip::LookupIpIntoIter,
TokioAsyncResolver,
};
#[derive(Clone)]
pub struct TrustDnsResolver {
resolver: Arc<TokioAsyncResolver>,
}
pub struct SocketAddrs {
iter: LookupIpIntoIter,
}
impl Iterator for SocketAddrs {
type Item = SocketAddr;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|ip_addr| SocketAddr::new(ip_addr, 0))
}
}
impl TrustDnsResolver {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn google() -> Self {
Self::with_config_and_options(ResolverConfig::google(), ResolverOpts::default())
}
#[must_use]
pub fn cloudflare() -> Self {
Self::with_config_and_options(ResolverConfig::cloudflare(), ResolverOpts::default())
}
#[cfg(feature = "dns-over-https")]
#[must_use]
pub fn cloudflare_https() -> Self {
Self::with_config_and_options(ResolverConfig::cloudflare_https(), ResolverOpts::default())
}
#[cfg(feature = "dns-over-tls")]
#[must_use]
pub fn cloudflare_tls() -> Self {
Self::with_config_and_options(ResolverConfig::cloudflare_tls(), ResolverOpts::default())
}
#[must_use]
pub fn quad9() -> Self {
Self::with_config_and_options(ResolverConfig::quad9(), ResolverOpts::default())
}
#[cfg(feature = "dns-over-https")]
#[must_use]
pub fn quad9_https() -> Self {
Self::with_config_and_options(ResolverConfig::quad9_https(), ResolverOpts::default())
}
#[cfg(feature = "dns-over-tls")]
#[must_use]
pub fn quad9_tls() -> Self {
Self::with_config_and_options(ResolverConfig::quad9_tls(), ResolverOpts::default())
}
#[allow(clippy::missing_panics_doc)]
#[must_use]
pub fn with_config_and_options(config: ResolverConfig, options: ResolverOpts) -> Self {
let resolver = Arc::new(TokioAsyncResolver::tokio(config, options).unwrap());
Self { resolver }
}
#[cfg(feature = "system-config")]
#[allow(clippy::missing_panics_doc)]
#[must_use]
pub fn from_system_conf() -> Self {
let resolver = Arc::new(TokioAsyncResolver::tokio_from_system_conf().unwrap());
Self { resolver }
}
#[must_use]
pub fn into_http_connector(self) -> TrustDnsHttpConnector {
TrustDnsHttpConnector::new_with_resolver(self)
}
#[cfg(feature = "native-tls")]
#[must_use]
pub fn into_native_tls_https_connector(self) -> NativeTlsHttpsConnector {
let mut http_connector = self.into_http_connector();
http_connector.enforce_http(false);
let mut native_https_connector =
NativeTlsHttpsConnector::new_with_connector(http_connector);
#[cfg(feature = "https-only")]
native_https_connector.https_only(true);
#[cfg(not(feature = "https-only"))]
https_connector.https_only(false);
native_https_connector
}
#[cfg(feature = "rustls-native")]
#[must_use]
pub fn into_rustls_native_https_connector(self) -> RustlsHttpsConnector {
let mut http_connector = self.into_http_connector();
http_connector.enforce_http(false);
let builder = hyper_rustls::HttpsConnectorBuilder::new().with_native_roots();
#[cfg(feature = "https-only")]
let builder = builder.https_only();
#[cfg(not(feature = "https-only"))]
let builder = builder.https_or_http();
#[cfg(feature = "rustls-http1")]
let builder = builder.enable_http1();
#[cfg(feature = "rustls-http2")]
let builder = builder.enable_http2();
builder.wrap_connector(http_connector)
}
#[cfg(feature = "rustls-webpki")]
#[must_use]
pub fn into_rustls_webpki_https_connector(self) -> RustlsHttpsConnector {
let mut http_connector = self.into_http_connector();
http_connector.enforce_http(false);
let builder = hyper_rustls::HttpsConnectorBuilder::new().with_webpki_roots();
#[cfg(feature = "https-only")]
let builder = builder.https_only();
#[cfg(not(feature = "https-only"))]
let builder = builder.https_or_http();
#[cfg(feature = "rustls-http1")]
let builder = builder.enable_http1();
#[cfg(feature = "rustls-http2")]
let builder = builder.enable_http2();
builder.wrap_connector(http_connector)
}
}
impl Default for TrustDnsResolver {
fn default() -> Self {
Self::with_config_and_options(ResolverConfig::default(), ResolverOpts::default())
}
}
impl Service<Name> for TrustDnsResolver {
type Response = SocketAddrs;
type Error = ResolveError;
#[allow(clippy::type_complexity)]
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, name: Name) -> Self::Future {
let resolver = self.resolver.clone();
Box::pin(async move {
let response = resolver.lookup_ip(name.as_str()).await?;
let addresses = response.into_iter();
Ok(SocketAddrs { iter: addresses })
})
}
}
pub type TrustDnsHttpConnector = HttpConnector<TrustDnsResolver>;
#[cfg(feature = "native-tls")]
pub type NativeTlsHttpsConnector = hyper_tls::HttpsConnector<TrustDnsHttpConnector>;
#[cfg(any(feature = "rustls-native", feature = "rustls-webpki"))]
pub type RustlsHttpsConnector = hyper_rustls::HttpsConnector<TrustDnsHttpConnector>;