#![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},
};
#[cfg(feature = "tokio")]
use hickory_resolver::{
config::ResolverConfig, config::ResolverOpts, name_server::TokioConnectionProvider,
proto::runtime::TokioRuntimeProvider, TokioResolver,
};
use hickory_resolver::{
lookup_ip::LookupIpIntoIter, name_server::ConnectionProvider, ResolveError, Resolver,
};
use hyper_util::client::legacy::connect::{dns::Name, HttpConnector};
use tower_service::Service;
#[cfg(feature = "tokio")]
pub type TokioHickoryResolver = HickoryResolver<TokioConnectionProvider>;
#[derive(Clone)]
pub struct HickoryResolver<C: ConnectionProvider> {
resolver: Arc<Resolver<C>>,
}
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))
}
}
#[cfg(feature = "tokio")]
fn default_opts() -> ResolverOpts {
#[cfg(any(feature = "dnssec-aws-lc-rs", feature = "dnssec-ring"))]
let mut opts = ResolverOpts::default();
#[cfg(not(any(feature = "dnssec-aws-lc-rs", feature = "dnssec-ring")))]
let opts = ResolverOpts::default();
#[cfg(any(feature = "dnssec-aws-lc-rs", feature = "dnssec-ring"))]
{
opts.validate = true;
}
opts
}
#[cfg(feature = "tokio")]
impl TokioHickoryResolver {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn google() -> Self {
Self::with_config_and_options(ResolverConfig::google(), default_opts())
}
#[must_use]
pub fn cloudflare() -> Self {
Self::with_config_and_options(ResolverConfig::cloudflare(), default_opts())
}
#[cfg(any(feature = "https-aws-lc-rs", feature = "https-ring"))]
#[must_use]
pub fn cloudflare_https() -> Self {
Self::with_config_and_options(ResolverConfig::cloudflare_https(), default_opts())
}
#[cfg(any(feature = "tls-aws-lc-rs", feature = "tls-ring",))]
#[must_use]
pub fn cloudflare_tls() -> Self {
Self::with_config_and_options(ResolverConfig::cloudflare_tls(), default_opts())
}
#[must_use]
pub fn quad9() -> Self {
Self::with_config_and_options(ResolverConfig::quad9(), default_opts())
}
#[cfg(any(feature = "https-aws-lc-rs", feature = "https-ring"))]
#[must_use]
pub fn quad9_https() -> Self {
Self::with_config_and_options(ResolverConfig::quad9_https(), default_opts())
}
#[cfg(any(feature = "tls-aws-lc-rs", feature = "tls-ring",))]
#[must_use]
pub fn quad9_tls() -> Self {
Self::with_config_and_options(ResolverConfig::quad9_tls(), default_opts())
}
#[must_use]
pub fn with_config_and_options(config: ResolverConfig, options: ResolverOpts) -> Self {
Self::from_resolver(
TokioResolver::builder_with_config(
config,
TokioConnectionProvider::new(TokioRuntimeProvider::new()),
)
.with_options(options)
.build(),
)
}
#[cfg(feature = "system-config")]
pub fn from_system_conf() -> Result<Self, ResolveError> {
Ok(Self::from_resolver(TokioResolver::builder_tokio()?.build()))
}
}
#[cfg(feature = "tokio")]
impl Default for TokioHickoryResolver {
fn default() -> Self {
Self::with_config_and_options(ResolverConfig::default(), default_opts())
}
}
impl<C: ConnectionProvider> HickoryResolver<C> {
#[must_use]
pub fn from_resolver(resolver: Resolver<C>) -> Self {
let resolver = Arc::new(resolver);
Self { resolver }
}
#[must_use]
pub fn into_http_connector(self) -> HickoryHttpConnector<C> {
HickoryHttpConnector::new_with_resolver(self)
}
}
impl<C: ConnectionProvider> Service<Name> for HickoryResolver<C> {
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 HickoryHttpConnector<C> = HttpConnector<HickoryResolver<C>>;
#[cfg(feature = "tokio")]
pub type TokioHickoryHttpConnector = HickoryHttpConnector<TokioConnectionProvider>;