hyper_system_resolver/
system.rs

1//! The system resolver implementation.
2
3use std::{io, net::SocketAddr, vec};
4
5use dns_lookup::AddrInfoHints;
6use hyper::client::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 = dns_lookup::getaddrinfo(Some(name.as_str()), service.as_deref(), addr_info_hints)?;
47            let list = iter
48                .map(|result| result.map(|addr_info| addr_info.sockaddr))
49                .collect::<Result<Vec<_>, _>>()?;
50            Ok(list.into_iter())
51        })
52    }
53}
54
55/// System resolver compatible with [`hyper`].
56pub type Resolver = background::Resolver<System>;
57
58impl System {
59    /// Use this [`System`] to create a new [`hyper`]-compatible [`Resolver`].
60    pub fn resolver(self) -> Resolver {
61        Resolver::new(self)
62    }
63}
64
65impl From<System> for Resolver {
66    fn from(system: System) -> Self {
67        system.resolver()
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use std::{
74        net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6},
75        str::FromStr,
76    };
77
78    use tower_service::Service;
79
80    use super::*;
81
82    #[tokio::test]
83    async fn test_resolve_ipv4() {
84        let mut resolver = background::Resolver::new(System {
85            addr_info_hints: Some(
86                AddrInfoHints {
87                    address: dns_lookup::AddrFamily::Inet.into(),
88                    ..Default::default()
89                }
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}