Skip to main content

saluki_io/net/dns/
host.rs

1//! Resolution of hosts to IP addresses.
2
3use std::net::IpAddr;
4
5use snafu::{ResultExt as _, Snafu};
6
7/// An error produced while resolving a host to an IP address.
8#[derive(Debug, Snafu)]
9#[snafu(context(suffix(false)))]
10pub enum ResolveHostError {
11    /// The host lookup failed.
12    #[snafu(display("failed to look up host '{}': {}", host, source))]
13    LookupFailed {
14        /// Host that was looked up.
15        host: String,
16
17        /// Source of the error.
18        source: std::io::Error,
19    },
20
21    /// The host lookup succeeded but returned no addresses.
22    #[snafu(display("host '{}' resolved to zero IP addresses", host))]
23    NoAddresses {
24        /// Host that was looked up.
25        host: String,
26    },
27}
28
29/// Resolves a host to an IP address.
30///
31/// `host` can be an IP literal, which is parsed without a DNS lookup, or a hostname, which is resolved using the system
32/// resolver. When a hostname resolves to several addresses, the first one is returned.
33///
34/// # Errors
35///
36/// If the lookup fails, [`ResolveHostError::LookupFailed`] is returned. If the lookup succeeds but returns no
37/// addresses, [`ResolveHostError::NoAddresses`] is returned.
38pub async fn resolve_host(host: &str) -> Result<IpAddr, ResolveHostError> {
39    let mut addrs = tokio::net::lookup_host((host, 0u16))
40        .await
41        .context(LookupFailed { host })?;
42    addrs
43        .next()
44        .map(|sa| sa.ip())
45        .ok_or_else(|| ResolveHostError::NoAddresses { host: host.to_string() })
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51
52    #[tokio::test]
53    async fn resolves_ip_literals_without_lookup() {
54        assert_eq!(resolve_host("127.0.0.1").await.unwrap(), IpAddr::from([127, 0, 0, 1]));
55        assert_eq!(
56            resolve_host("192.168.1.50").await.unwrap(),
57            IpAddr::from([192, 168, 1, 50])
58        );
59        assert_eq!(
60            resolve_host("::1").await.unwrap(),
61            IpAddr::from(std::net::Ipv6Addr::LOCALHOST)
62        );
63    }
64}