async-dns-lookup 0.1.0

An asynchronous implementation of dns-lookup.
Documentation
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; // copy since Ipv4Addr is Copy
    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)?
}