dns_lookup/
lookup.rs

1use std::io;
2use std::net::IpAddr;
3use std::str;
4
5#[cfg(unix)]
6use libc::{NI_NAMEREQD, NI_NUMERICSERV, SOCK_STREAM};
7
8#[cfg(windows)]
9use windows_sys::Win32::Networking::WinSock::{NI_NAMEREQD, NI_NUMERICSERV, SOCK_STREAM};
10
11use crate::addrinfo::{getaddrinfo, AddrInfoHints};
12use crate::nameinfo::getnameinfo;
13
14/// Lookup the address for a given hostname via DNS.
15///
16/// Returns an iterator of IP Addresses, or an `io::Error` on failure.
17pub fn lookup_host(host: &str) -> io::Result<Vec<IpAddr>> {
18    let hints = AddrInfoHints {
19        socktype: SOCK_STREAM as i32,
20        ..AddrInfoHints::default()
21    };
22
23    match getaddrinfo(Some(host), None, Some(hints)) {
24        Ok(addrs) => {
25            let addrs: io::Result<Vec<_>> = addrs.map(|r| r.map(|a| a.sockaddr.ip())).collect();
26            addrs
27        }
28        Err(e) => {
29            reload_dns_nameserver();
30            Err(e)?
31        }
32    }
33}
34
35/// Lookup the hostname of a given IP Address via DNS.
36///
37/// Returns the hostname as a String, or an `io::Error` on failure or if the hostname cannot be determined.
38pub fn lookup_addr(addr: &IpAddr) -> io::Result<String> {
39    let sock = (*addr, 0).into();
40    match getnameinfo(&sock, (NI_NUMERICSERV | NI_NAMEREQD) as i32) {
41        Ok((name, _)) => Ok(name),
42        Err(e) => {
43            reload_dns_nameserver();
44            Err(e)?
45        }
46    }
47}
48
49// The lookup failure could be caused by using a stale /etc/resolv.conf.
50// See https://github.com/rust-lang/rust/issues/41570.
51// We therefore force a reload of the nameserver information.
52// MacOS and IOS don't seem to have this problem.
53fn reload_dns_nameserver() {
54    cfg_if::cfg_if! {
55      if #[cfg(target_os = "macos")] {
56      } else if #[cfg(target_os = "ios")] {
57      } else if #[cfg(target_os = "tvos")] {
58      } else if #[cfg(unix)] {
59        use libc;
60        unsafe {
61          libc::res_init();
62        }
63      }
64    }
65}
66
67#[test]
68fn test_localhost() {
69    let ips = lookup_host("localhost").unwrap();
70    assert!(ips.contains(&IpAddr::V4("127.0.0.1".parse().unwrap())));
71    assert!(!ips.contains(&IpAddr::V4("10.0.0.1".parse().unwrap())));
72}
73
74#[cfg(unix)]
75#[test]
76fn test_rev_localhost() {
77    let name = lookup_addr(&IpAddr::V4("127.0.0.1".parse().unwrap()));
78    assert_eq!(name.unwrap(), "localhost");
79}
80
81#[cfg(windows)]
82#[test]
83fn test_hostname() {
84    // Get machine's hostname.
85    let hostname = crate::hostname::get_hostname().unwrap();
86
87    // Do reverse lookup of 127.0.0.1.
88    let rev_name = lookup_addr(&IpAddr::V4("127.0.0.1".parse().unwrap()));
89
90    assert_eq!(rev_name.unwrap(), hostname);
91}