domain/resolv/lookup/
host.rs

1//! Looking up host names.
2
3use crate::base::iana::Rtype;
4use crate::base::message::RecordIter;
5use crate::base::name::{ParsedDname, ToDname, ToRelativeDname};
6use crate::rdata::{Aaaa, A};
7use crate::resolv::resolver::{Resolver, SearchNames};
8use octseq::octets::Octets;
9use std::io;
10use std::net::{IpAddr, SocketAddr, ToSocketAddrs};
11
12//------------ lookup_host ---------------------------------------------------
13
14/// Creates a future that resolves a host name into its IP addresses.
15///
16/// The future will use the resolver given in `resolv` to query the
17/// DNS for the IPv4 and IPv6 addresses associated with `name`.
18///
19/// The value returned upon success can be turned into an iterator over
20/// IP addresses or even socket addresses. Since the lookup may determine that
21/// the host name is in fact an alias for another name, the value will also
22/// return the canonical name.
23pub async fn lookup_host<R: Resolver>(
24    resolver: &R,
25    qname: impl ToDname,
26) -> Result<FoundHosts<R>, io::Error> {
27    let (a, aaaa) = tokio::join!(
28        resolver.query((&qname, Rtype::A)),
29        resolver.query((&qname, Rtype::Aaaa)),
30    );
31    FoundHosts::new(aaaa, a)
32}
33
34//------------ search_host ---------------------------------------------------
35
36pub async fn search_host<R: Resolver + SearchNames>(
37    resolver: &R,
38    qname: impl ToRelativeDname,
39) -> Result<FoundHosts<R>, io::Error> {
40    for suffix in resolver.search_iter() {
41        if let Ok(name) = (&qname).chain(suffix) {
42            if let Ok(answer) = lookup_host(resolver, name).await {
43                if !answer.is_empty() {
44                    return Ok(answer);
45                }
46            }
47        }
48    }
49    lookup_host(resolver, qname.chain_root()).await
50}
51
52//------------ FoundHosts ----------------------------------------------------
53
54/// The value returned by a successful host lookup.
55///
56/// You can use the `iter()` method to get an iterator over the IP addresses
57/// or `port_iter()` to get an iterator over socket addresses with the given
58/// port.
59///
60/// The `canonical_name()` method returns the canonical name of the host for
61/// which the addresses were found.
62#[derive(Debug)]
63pub struct FoundHosts<R: Resolver> {
64    /// The answer to the AAAA query.
65    aaaa: Result<R::Answer, io::Error>,
66
67    /// The answer to the A query.
68    a: Result<R::Answer, io::Error>,
69}
70
71impl<R: Resolver> FoundHosts<R> {
72    pub fn new(
73        aaaa: Result<R::Answer, io::Error>,
74        a: Result<R::Answer, io::Error>,
75    ) -> Result<Self, io::Error> {
76        if aaaa.is_err() && a.is_err() {
77            match aaaa {
78                Err(err) => return Err(err),
79                _ => unreachable!(),
80            }
81        }
82
83        Ok(FoundHosts { aaaa, a })
84    }
85
86    pub fn is_empty(&self) -> bool {
87        if let Ok(ref aaaa) = self.aaaa {
88            if aaaa.as_ref().header_counts().ancount() > 0 {
89                return false;
90            }
91        }
92        if let Ok(ref a) = self.a {
93            if a.as_ref().header_counts().ancount() > 0 {
94                return false;
95            }
96        }
97        true
98    }
99
100    /// Returns a reference to one of the answers.
101    fn answer(&self) -> &R::Answer {
102        match self.aaaa.as_ref() {
103            Ok(answer) => answer,
104            Err(_) => self.a.as_ref().unwrap(),
105        }
106    }
107}
108
109impl<R: Resolver> FoundHosts<R>
110where
111    R::Octets: Octets,
112{
113    pub fn qname(&self) -> ParsedDname<<R::Octets as Octets>::Range<'_>> {
114        self.answer()
115            .as_ref()
116            .first_question()
117            .unwrap()
118            .into_qname()
119    }
120
121    /// Returns a reference to the canonical name for the host.
122    ///
123    /// # Notes
124    ///
125    /// This method expects the canonical name to be same in both A/AAAA
126    /// responses, if it isn't, it's going to return a canonical name for
127    /// one of them.
128    pub fn canonical_name(
129        &self,
130    ) -> ParsedDname<<R::Octets as Octets>::Range<'_>> {
131        self.answer().as_ref().canonical_name().unwrap()
132    }
133
134    /// Returns an iterator over the IP addresses returned by the lookup.
135    pub fn iter(&self) -> FoundHostsIter {
136        FoundHostsIter {
137            aaaa_name: self
138                .aaaa
139                .as_ref()
140                .ok()
141                .and_then(|msg| msg.as_ref().for_slice().canonical_name()),
142            a_name: self
143                .a
144                .as_ref()
145                .ok()
146                .and_then(|msg| msg.as_ref().for_slice().canonical_name()),
147            aaaa: {
148                self.aaaa
149                    .as_ref()
150                    .ok()
151                    .and_then(|msg| msg.as_ref().for_slice().answer().ok())
152                    .map(|answer| answer.limit_to::<Aaaa>())
153            },
154            a: {
155                self.a
156                    .as_ref()
157                    .ok()
158                    .and_then(|msg| msg.as_ref().for_slice().answer().ok())
159                    .map(|answer| answer.limit_to::<A>())
160            },
161        }
162    }
163
164    /// Returns an iterator over socket addresses gained from the lookup.
165    ///
166    /// The socket addresses are gained by combining the IP addresses with
167    /// `port`. The returned iterator implements `ToSocketAddrs` and thus
168    /// can be used where `std::net` wants addresses right away.
169    pub fn port_iter(&self, port: u16) -> FoundHostsSocketIter {
170        FoundHostsSocketIter {
171            iter: self.iter(),
172            port,
173        }
174    }
175}
176
177//------------ FoundHostsIter ------------------------------------------------
178
179/// An iterator over the IP addresses returned by a host lookup.
180#[derive(Clone)]
181pub struct FoundHostsIter<'a> {
182    aaaa_name: Option<ParsedDname<&'a [u8]>>,
183    a_name: Option<ParsedDname<&'a [u8]>>,
184    aaaa: Option<RecordIter<'a, [u8], Aaaa>>,
185    a: Option<RecordIter<'a, [u8], A>>,
186}
187
188impl<'a> Iterator for FoundHostsIter<'a> {
189    type Item = IpAddr;
190
191    fn next(&mut self) -> Option<IpAddr> {
192        while let Some(res) = self.aaaa.as_mut().and_then(Iterator::next) {
193            if let Ok(record) = res {
194                if Some(record.owner()) == self.aaaa_name.as_ref() {
195                    return Some(record.data().addr().into());
196                }
197            }
198        }
199        while let Some(res) = self.a.as_mut().and_then(Iterator::next) {
200            if let Ok(record) = res {
201                if Some(record.owner()) == self.a_name.as_ref() {
202                    return Some(record.data().addr().into());
203                }
204            }
205        }
206        None
207    }
208}
209
210//------------ FoundHostsSocketIter ------------------------------------------
211
212/// An iterator over socket addresses derived from a host lookup.
213#[derive(Clone)]
214pub struct FoundHostsSocketIter<'a> {
215    iter: FoundHostsIter<'a>,
216    port: u16,
217}
218
219impl<'a> Iterator for FoundHostsSocketIter<'a> {
220    type Item = SocketAddr;
221
222    fn next(&mut self) -> Option<SocketAddr> {
223        self.iter
224            .next()
225            .map(|addr| SocketAddr::new(addr, self.port))
226    }
227}
228
229impl<'a> ToSocketAddrs for FoundHostsSocketIter<'a> {
230    type Iter = Self;
231
232    fn to_socket_addrs(&self) -> io::Result<Self> {
233        Ok(self.clone())
234    }
235}