//! DNS resolution via the [hickory-resolver](https://github.com/hickory-dns/hickory-dns) crate use hickory_resolver::{lookup_ip::LookupIpIntoIter, system_conf, TokioAsyncResolver}; use hyper::client::connect::dns::Name; use once_cell::sync::OnceCell; use std::io; use std::net::SocketAddr; use std::sync::Arc; use super::{Addrs, Resolve, Resolving}; /// Wrapper around an `AsyncResolver`, which implements the `Resolve` trait. #[derive(Debug, Default, Clone)] pub(crate) struct HickoryDnsResolver { /// Since we might not have been called in the context of a /// Tokio Runtime in initialization, so we must delay the actual /// construction of the resolver. state: Arc>, } struct SocketAddrs { iter: LookupIpIntoIter, } 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.into_iter(), }); Ok(addrs) }) } } impl Iterator for SocketAddrs { type Item = SocketAddr; fn next(&mut self) -> Option { self.iter.next().map(|ip_addr| SocketAddr::new(ip_addr, 0)) } } /// Create a new resolver with the default configuration, /// which reads from `/etc/resolve.conf`. fn new_resolver() -> io::Result { let (config, opts) = system_conf::read_system_conf().map_err(|e| { io::Error::new( io::ErrorKind::Other, format!("error reading DNS system conf: {e}"), ) })?; Ok(TokioAsyncResolver::tokio(config, opts)) }