use crate::errors::{new_io_error, Result};
use hickory_resolver::config::{NameServerConfig, NameServerConfigGroup, ResolverConfig};
use hickory_resolver::name_server::TokioConnectionProvider;
use hickory_resolver::proto::xfer::Protocol;
use hickory_resolver::TokioResolver;
use std::net::SocketAddr;
use std::sync::Arc;
#[derive(Clone)]
pub struct DnsResolver {
inner: Arc<TokioResolver>,
}
impl DnsResolver {
pub fn new(dns_servers: Vec<SocketAddr>) -> Result<Self> {
if dns_servers.is_empty() {
return Err(new_io_error(
std::io::ErrorKind::InvalidInput,
"DNS servers list cannot be empty",
));
}
let name_servers: Vec<NameServerConfig> = dns_servers
.into_iter()
.map(|addr| NameServerConfig::new(addr, Protocol::Udp))
.collect();
let name_server_group = NameServerConfigGroup::from(name_servers);
let config = ResolverConfig::from_parts(None, vec![], name_server_group);
let resolver =
TokioResolver::builder_with_config(config, TokioConnectionProvider::default()).build();
Ok(Self {
inner: Arc::new(resolver),
})
}
pub fn system() -> Result<Self> {
let resolver = TokioResolver::builder_tokio()
.map_err(|e| new_io_error(std::io::ErrorKind::Other, &e.to_string()))?
.build();
Ok(Self {
inner: Arc::new(resolver),
})
}
pub async fn resolve(&self, host: &str, port: u16) -> Result<Vec<SocketAddr>> {
let lookup = self
.inner
.lookup_ip(host)
.await
.map_err(|e| new_io_error(std::io::ErrorKind::Other, &e.to_string()))?;
let addrs: Vec<SocketAddr> = lookup.iter().map(|ip| SocketAddr::new(ip, port)).collect();
if addrs.is_empty() {
return Err(new_io_error(
std::io::ErrorKind::NotFound,
&format!("no addresses found for host: {}", host),
));
}
Ok(addrs)
}
pub async fn resolve_one(&self, host: &str, port: u16) -> Result<SocketAddr> {
let addrs = self.resolve(host, port).await?;
addrs.first().copied().ok_or_else(|| {
new_io_error(
std::io::ErrorKind::NotFound,
&format!("no addresses found for host: {}", host),
)
})
}
}
impl std::fmt::Debug for DnsResolver {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DnsResolver").finish()
}
}