nscd_lookup/
reqwest.rs

1//! Compatibility with [`reqwest`].
2
3use std::net::SocketAddr;
4use std::sync::{Arc, OnceLock};
5
6use ouroboros::self_referencing;
7use reqwest::dns::{Addrs, Name, Resolve, Resolving};
8
9use crate::IpAddrIterator;
10use crate::protocol::interpret_data;
11use crate::tokio::fill_buf;
12
13/// A [`dns_resolver`][reqwest::ClientBuilder::dns_resolver] for [`reqwest::Client`] that uses nscd.
14pub fn resolver() -> Arc<Resolver> {
15    static RESOLVER: OnceLock<Arc<Resolver>> = OnceLock::new();
16
17    Arc::clone(RESOLVER.get_or_init(|| Arc::new(Resolver)))
18}
19
20/// An empty struct that implements [`Resolve`]; use [`resolver()`] instead.
21#[derive(Debug, Clone, Copy, Default)]
22pub struct Resolver;
23
24impl Resolve for Resolver {
25    #[inline]
26    fn resolve(&self, name: Name) -> Resolving {
27        Box::pin(resolve(name))
28    }
29}
30
31async fn resolve(name: Name) -> Result<Addrs, Box<dyn std::error::Error + Send + Sync>> {
32    let mut buf = Vec::new();
33    if let Some(resp) = fill_buf(name.as_str().as_bytes(), &mut buf).await? {
34        let addrs = ResolvedAddrs::try_new(buf, |buf| interpret_data(&resp, buf))?;
35        Ok(Box::new(addrs))
36    } else {
37        Err(Box::new(NoResults(name)))
38    }
39}
40
41#[self_referencing]
42struct ResolvedAddrs {
43    buf: Vec<u8>,
44    #[borrows(buf)]
45    #[covariant]
46    iter: IpAddrIterator<'this>,
47}
48
49impl Iterator for ResolvedAddrs {
50    type Item = SocketAddr;
51
52    fn next(&mut self) -> Option<Self::Item> {
53        self.with_iter_mut(|iter| Some(SocketAddr::new(iter.next()?, 0)))
54    }
55}
56
57/// No IP addresses found for {0:?}
58#[derive(Debug, thiserror::Error, displaydoc::Display)]
59struct NoResults(Name);