Skip to main content

saluki_io/net/dns/
hyper.rs

1use std::{
2    future::Future,
3    net::{IpAddr, SocketAddr},
4    pin::Pin,
5    sync::Arc,
6    task::{Context, Poll},
7};
8
9use hickory_resolver::{net::NetError, TokioResolver};
10use hyper_util::client::legacy::connect::{dns::Name, HttpConnector};
11use saluki_error::{ErrorContext as _, GenericError};
12use tower::Service;
13
14/// An [`HttpConnector`] that uses [`HickoryResolver`].
15pub type HickoryHttpConnector = HttpConnector<HickoryResolver>;
16
17/// A DNS resolver for `hyper` based on `hickory`.
18#[derive(Clone)]
19pub struct HickoryResolver {
20    resolver: Arc<TokioResolver>,
21}
22
23impl HickoryResolver {
24    /// Creates a new [`HickoryResolver`] based on the system's resolver configuration.
25    ///
26    /// # Panics
27    ///
28    /// This will panic if called outside the context of a Tokio runtime.
29    ///
30    /// # Errors
31    ///
32    /// If there is an issue loading the system configuration, an error is returned.
33    pub fn from_system_conf() -> Result<Self, GenericError> {
34        let resolver = TokioResolver::builder_tokio()
35            .error_context("Failed to load the system resolver configuration.")?
36            .build()
37            .error_context("Failed to build the resolver.")?;
38
39        Ok(Self {
40            resolver: Arc::new(resolver),
41        })
42    }
43
44    /// Consumes `self` and creates a new [`HickoryHttpConnector`] with this resolver.
45    pub fn into_http_connector(self) -> HickoryHttpConnector {
46        HickoryHttpConnector::new_with_resolver(self)
47    }
48}
49
50impl Service<Name> for HickoryResolver {
51    type Response = SocketAddrs;
52    type Error = NetError;
53
54    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
55
56    fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
57        Poll::Ready(Ok(()))
58    }
59
60    fn call(&mut self, name: Name) -> Self::Future {
61        let resolver = self.resolver.clone();
62
63        Box::pin(async move {
64            let response = resolver.lookup_ip(name.as_str()).await?;
65            Ok(response.iter().collect())
66        })
67    }
68}
69
70pub struct SocketAddrs(Vec<IpAddr>);
71
72impl Iterator for SocketAddrs {
73    type Item = SocketAddr;
74
75    fn next(&mut self) -> Option<Self::Item> {
76        self.0.pop().map(|ip| SocketAddr::new(ip, 0))
77    }
78}
79
80impl FromIterator<IpAddr> for SocketAddrs {
81    fn from_iter<I: IntoIterator<Item = IpAddr>>(iter: I) -> Self {
82        Self(iter.into_iter().collect())
83    }
84}