use std::{
io,
net::{IpAddr, Ipv4Addr, SocketAddr},
};
use tokio::{
runtime::Builder,
sync::{
mpsc::{self, Sender},
oneshot,
},
task::LocalSet,
};
#[derive(Debug)]
pub struct Request(Ipv4Addr, oneshot::Sender<Result<String, std::io::Error>>);
#[derive(Clone)]
pub struct AsyncDnsResolver {
request_sender: Sender<Request>,
}
impl Default for AsyncDnsResolver {
fn default() -> Self {
Self::new()
}
}
impl AsyncDnsResolver {
pub fn new() -> Self {
let (request_sender, mut request_receiver) = mpsc::channel::<Request>(1);
let runtime = Builder::new_current_thread().enable_all().build().unwrap();
std::thread::spawn(move || {
let local = LocalSet::new();
local.spawn_local(async move {
while let Some(Request(ipv4, oneshot_sender)) = request_receiver.recv().await {
tokio::task::spawn_local(async move {
let response = reverse_lookup_async(&ipv4).await;
if let Err(res) = oneshot_sender.send(response) {
log::warn!("Warning! The receiver has been dropped. {:?}", res);
}
});
}
});
runtime.block_on(local);
});
Self { request_sender }
}
pub async fn reverse_lookup(&self, ipv4: Ipv4Addr) -> Result<String, std::io::Error> {
let (oneshot_sender, oneshot_receiver) =
oneshot::channel::<Result<String, std::io::Error>>();
self.request_sender
.send(Request(ipv4, oneshot_sender))
.await
.map_err(io::Error::other)?;
oneshot_receiver.await.map_err(io::Error::other)?
}
}
async fn reverse_lookup_async(ip: &Ipv4Addr) -> Result<String, io::Error> {
let ip = *ip; tokio::task::spawn_blocking(move || {
let sa = SocketAddr::new(IpAddr::V4(ip), 0);
let host = dns_lookup::getnameinfo(&sa, 0)?;
Ok::<_, io::Error>(host.0)
})
.await
.map_err(io::Error::other)?
}