hyper_system_resolver/
system.rs

1//! The system resolver implementation.
2
3use std::{io, net::SocketAddr, vec};
4
5use dns_lookup::AddrInfoHints;
6use hyper_util::client::legacy::connect::dns::Name;
7use tokio::task::JoinHandle;
8
9use crate::background;
10
11/// [`System`] encapsulates logic to perform the name resolution in
12/// the background using system resolution mechanisms.
13///
14/// Uses [`dns_lookup::getaddrinfo`] in a [`tokio::task::spawn_blocking`] to
15/// perform the resolution.
16#[derive(Debug, Builder, Default, Clone)]
17pub struct System {
18    /// The hints to give the the system resolver when performing the
19    /// resolution.
20    ///
21    /// Passing [`None`] is not equivalent to passing [`Some`] value filled with
22    /// zeroes, as underlying systems typically have some non-trivial defaults
23    /// when hint is omitted.
24    pub addr_info_hints: Option<AddrInfoHints>,
25
26    /// The name of the service to resolve.
27    /// If set to [`None`], the network address of the node is resolved.
28    /// If set to [`Some`], the the requested service address is resolved.
29    /// This can be either a descriptive name or a numeric representation
30    /// suitable for use with the address family or families.
31    /// If the specified address family is AF_INET,  AF_INET6, or AF_UNSPEC,
32    /// the service can be specified as a string specifying a decimal port
33    /// number.
34    pub service: Option<String>,
35}
36
37impl background::Resolve for System {
38    type Iter = vec::IntoIter<SocketAddr>;
39
40    fn resolve(&mut self, name: Name) -> JoinHandle<io::Result<Self::Iter>> {
41        let addr_info_hints = self.addr_info_hints;
42        let service = self.service.clone();
43        tokio::task::spawn_blocking(move || {
44            debug!("resolving host={:?} service={:?}", name, service);
45
46            let iter =
47                dns_lookup::getaddrinfo(Some(name.as_str()), service.as_deref(), addr_info_hints)?;
48            let list = iter
49                .map(|result| result.map(|addr_info| addr_info.sockaddr))
50                .collect::<Result<Vec<_>, _>>()?;
51            Ok(list.into_iter())
52        })
53    }
54}
55
56/// System resolver compatible with [`hyper`].
57pub type Resolver = background::Resolver<System>;
58
59impl System {
60    /// Use this [`System`] to create a new [`hyper`]-compatible [`Resolver`].
61    pub fn resolver(self) -> Resolver {
62        Resolver::new(self)
63    }
64}
65
66impl From<System> for Resolver {
67    fn from(system: System) -> Self {
68        system.resolver()
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use std::{
75        net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6},
76        str::FromStr,
77    };
78
79    use tower_service::Service;
80
81    use super::*;
82
83    #[tokio::test]
84    async fn test_resolve_ipv4() {
85        let mut resolver = background::Resolver::new(System {
86            addr_info_hints: Some(AddrInfoHints {
87                address: dns_lookup::AddrFamily::Inet.into(),
88                ..Default::default()
89            }),
90            service: None,
91        });
92
93        let addrs: Vec<_> = resolver
94            .call(Name::from_str("localhost").unwrap())
95            .await
96            .unwrap()
97            .collect();
98
99        let localhost = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 0));
100
101        assert!(!addrs.is_empty());
102        for addr in addrs {
103            assert_eq!(addr, localhost);
104        }
105    }
106
107    #[tokio::test]
108    async fn test_resolve_ipv6() {
109        let mut resolver = background::Resolver::new(System {
110            addr_info_hints: Some(AddrInfoHints {
111                address: dns_lookup::AddrFamily::Inet6.into(),
112                ..Default::default()
113            }),
114            service: None,
115        });
116
117        let addrs: Vec<_> = resolver
118            .call(Name::from_str("localhost").unwrap())
119            .await
120            .unwrap()
121            .collect();
122
123        let localhost = SocketAddr::V6(SocketAddrV6::new(
124            Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1),
125            0,
126            0,
127            0,
128        ));
129
130        assert!(!addrs.is_empty());
131        for addr in addrs {
132            assert_eq!(addr, localhost);
133        }
134    }
135}