use hickory_resolver::{
config::{LookupIpStrategy, ResolverConfig, GOOGLE},
net::{runtime::TokioRuntimeProvider, NetError},
TokioResolver,
};
use once_cell::sync::OnceCell;
use std::net::{IpAddr, SocketAddr};
use std::sync::Arc;
use super::{Addrs, Name, Resolve, Resolving};
#[derive(Debug, Default, Clone)]
pub(crate) struct HickoryDnsResolver {
state: Arc<OnceCell<TokioResolver>>,
}
struct SocketAddrs {
iter: std::vec::IntoIter<IpAddr>,
}
impl Resolve for HickoryDnsResolver {
fn resolve(&self, name: Name) -> Resolving {
let resolver = self.clone();
Box::pin(async move {
let resolver = resolver.state.get_or_try_init(new_resolver)?;
let lookup = resolver.lookup_ip(name.as_str()).await?;
let addrs: Addrs = Box::new(SocketAddrs {
iter: lookup.iter().collect::<Vec<_>>().into_iter(),
});
Ok(addrs)
})
}
}
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))
}
}
fn new_resolver() -> Result<TokioResolver, NetError> {
let mut builder = TokioResolver::builder_tokio().unwrap_or_else(|err| {
log::debug!(
"hickory-dns: failed to load system DNS configuration; falling back to Google DNS: {:?}",
err
);
TokioResolver::builder_with_config(
ResolverConfig::udp_and_tcp(&GOOGLE),
TokioRuntimeProvider::default(),
)
});
builder.options_mut().ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
builder.build()
}