use std::{io, net::SocketAddr, vec};
use dns_lookup::AddrInfoHints;
use hyper_util::client::legacy::connect::dns::Name;
use tokio::task::JoinHandle;
use crate::background;
#[derive(Debug, Default, Clone)]
#[cfg_attr(feature = "builder", derive(derive_builder::Builder))]
pub struct System {
pub addr_info_hints: Option<AddrInfoHints>,
pub service: Option<String>,
}
impl background::Resolve for System {
type Iter = vec::IntoIter<SocketAddr>;
fn resolve(&mut self, name: Name) -> JoinHandle<io::Result<Self::Iter>> {
let addr_info_hints = self.addr_info_hints;
let service = self.service.clone();
tokio::task::spawn_blocking(move || {
debug!("resolving host={:?} service={:?}", name, service);
let iter =
dns_lookup::getaddrinfo(Some(name.as_str()), service.as_deref(), addr_info_hints)?;
let list = iter
.map(|result| result.map(|addr_info| addr_info.sockaddr))
.collect::<Result<Vec<_>, _>>()?;
Ok(list.into_iter())
})
}
}
pub type Resolver = background::Resolver<System>;
impl System {
pub fn resolver(self) -> Resolver {
Resolver::new(self)
}
}
impl From<System> for Resolver {
fn from(system: System) -> Self {
system.resolver()
}
}
#[cfg(test)]
mod tests {
use std::{
net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6},
str::FromStr,
};
use tower_service::Service;
use super::*;
#[tokio::test]
async fn test_resolve_ipv4() {
let mut resolver = background::Resolver::new(System {
addr_info_hints: Some(AddrInfoHints {
address: dns_lookup::AddrFamily::Inet.into(),
..Default::default()
}),
service: None,
});
let addrs: Vec<_> = resolver
.call(Name::from_str("localhost").unwrap())
.await
.unwrap()
.collect();
let localhost = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 0));
assert!(!addrs.is_empty());
for addr in addrs {
assert_eq!(addr, localhost);
}
}
#[tokio::test]
async fn test_resolve_ipv6() {
let mut resolver = background::Resolver::new(System {
addr_info_hints: Some(AddrInfoHints {
address: dns_lookup::AddrFamily::Inet6.into(),
..Default::default()
}),
service: None,
});
let addrs: Vec<_> = resolver
.call(Name::from_str("localhost").unwrap())
.await
.unwrap()
.collect();
let localhost = SocketAddr::V6(SocketAddrV6::new(
Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1),
0,
0,
0,
));
assert!(!addrs.is_empty());
for addr in addrs {
assert_eq!(addr, localhost);
}
}
}