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