async_dns/
lib.rs

1//! Asynchronous DNS lookups.
2//!
3//! This crate provides asynchronous DNS lookups. It uses the following mechanisms
4//! to resolve hostnames:
5//!
6//! - On `cfg(unix)`, it uses a custom implementation based on [`async-fs`] for reading
7//!   files, [`async-io`] for communication with the server, and [`dns-protocol`] for the
8//!   protocol implementation.
9//! - On `cfg(windows)`, it uses the [`DnsQueryEx`] function to make asynchronous queries.
10//!
11//! [`DnsQueryEx`]: https://docs.microsoft.com/en-us/windows/win32/api/windns/nf-windns-dnsqueryex
12//! [`async-fs`]: https://crates.io/crates/async-fs
13//! [`async-io`]: https://crates.io/crates/async-io
14//! [`dns-protocol`]: https://crates.io/crates/dns-protocol
15
16// Non-windows platforms use no unsafe code.
17#![cfg_attr(not(windows), forbid(unsafe_code))]
18#![forbid(missing_docs, future_incompatible)]
19
20cfg_if::cfg_if! {
21    if #[cfg(unix)] {
22        mod unix;
23        use unix as sys;
24    } else if #[cfg(windows)] {
25        mod windows;
26        use windows as sys;
27    } else {
28        compile_error! {
29            "async-dns does not support this platform"
30        }
31    }
32}
33
34use std::io;
35use std::iter::FusedIterator;
36use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
37
38/// Preform a DNS lookup, retrieving the IP addresses and other necessary information.
39pub async fn lookup(name: &str) -> io::Result<impl Iterator<Item = AddressInfo>> {
40    // Try to parse the name as an IP address.
41    if let Ok(ip) = name.parse::<Ipv4Addr>() {
42        return Ok(OneOrMany::One(Some(AddressInfo {
43            ip_address: ip.into(),
44        })));
45    }
46
47    if let Ok(ip) = name.parse::<Ipv6Addr>() {
48        return Ok(OneOrMany::One(Some(AddressInfo {
49            ip_address: ip.into(),
50        })));
51    }
52
53    // Perform the actual DNS lookup.
54    sys::lookup(name)
55        .await
56        .map(|v| OneOrMany::Many(v.into_iter()))
57}
58
59/// Information about an address.
60#[non_exhaustive]
61pub struct AddressInfo {
62    /// The IP address of the system.
63    pub ip_address: IpAddr,
64}
65
66/// Either an iterator or a single value.
67enum OneOrMany<I> {
68    One(Option<AddressInfo>),
69    Many(I),
70}
71
72impl<I: Iterator<Item = AddressInfo>> Iterator for OneOrMany<I> {
73    type Item = AddressInfo;
74
75    fn next(&mut self) -> Option<Self::Item> {
76        match self {
77            OneOrMany::One(v) => v.take(),
78            OneOrMany::Many(v) => v.next(),
79        }
80    }
81
82    fn size_hint(&self) -> (usize, Option<usize>) {
83        match self {
84            OneOrMany::One(v) => (v.is_some() as usize, Some(v.is_some() as usize)),
85            OneOrMany::Many(v) => v.size_hint(),
86        }
87    }
88
89    fn fold<B, F>(self, init: B, mut f: F) -> B
90    where
91        Self: Sized,
92        F: FnMut(B, Self::Item) -> B,
93    {
94        match self {
95            OneOrMany::One(v) => {
96                if let Some(v) = v {
97                    f(init, v)
98                } else {
99                    init
100                }
101            }
102            OneOrMany::Many(v) => v.fold(init, f),
103        }
104    }
105}
106
107impl<I: FusedIterator<Item = AddressInfo>> FusedIterator for OneOrMany<I> {}
108
109impl<I: ExactSizeIterator<Item = AddressInfo>> ExactSizeIterator for OneOrMany<I> {}
110
111impl<I: DoubleEndedIterator<Item = AddressInfo>> DoubleEndedIterator for OneOrMany<I> {
112    fn next_back(&mut self) -> Option<Self::Item> {
113        match self {
114            OneOrMany::One(v) => v.take(),
115            OneOrMany::Many(v) => v.next_back(),
116        }
117    }
118}
119
120fn _assert_threadsafe() {
121    fn _assertion<F: Send + Sync>(_: F) {}
122    _assertion(lookup("foobar"));
123}