c_ares_resolver/
resolver.rs

1use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
2use std::sync::{Arc, Mutex};
3
4use crate::error::Error;
5use crate::eventloop::{EventLoop, EventLoopStopper};
6
7#[cfg(cares1_24)]
8use c_ares::AresString;
9
10#[cfg(cares1_29)]
11use c_ares::{ServerFailoverOptions, ServerStateFlags};
12
13/// Used to configure the behaviour of the resolver.
14#[derive(Default)]
15pub struct Options {
16    inner: c_ares::Options,
17}
18
19impl Options {
20    /// Returns a fresh `Options`, on which no values are set.
21    pub fn new() -> Self {
22        Self::default()
23    }
24
25    /// Set flags controlling the behaviour of the resolver.
26    pub fn set_flags(&mut self, flags: c_ares::Flags) -> &mut Self {
27        self.inner.set_flags(flags);
28        self
29    }
30
31    /// Set the number of milliseconds each name server is given to respond to a query on the first
32    /// try.  (After the first try, the timeout algorithm becomes more complicated, but scales
33    /// linearly with the value of timeout).  The default is 5000ms.
34    pub fn set_timeout(&mut self, ms: u32) -> &mut Self {
35        self.inner.set_timeout(ms);
36        self
37    }
38
39    /// Set the number of tries the resolver will try contacting each name server before giving up.
40    /// The default is four tries.
41    pub fn set_tries(&mut self, tries: u32) -> &mut Self {
42        self.inner.set_tries(tries);
43        self
44    }
45
46    /// Set the number of dots which must be present in a domain name for it to be queried for "as
47    /// is" prior to querying for it with the default domain extensions appended.  The default
48    /// value is 1 unless set otherwise by resolv.conf or the RES_OPTIONS environment variable.
49    pub fn set_ndots(&mut self, ndots: u32) -> &mut Self {
50        self.inner.set_ndots(ndots);
51        self
52    }
53
54    /// Set the UDP port to use for queries.  The default value is 53, the standard name service
55    /// port.
56    pub fn set_udp_port(&mut self, udp_port: u16) -> &mut Self {
57        self.inner.set_udp_port(udp_port);
58        self
59    }
60
61    /// Set the TCP port to use for queries.  The default value is 53, the standard name service
62    /// port.
63    pub fn set_tcp_port(&mut self, tcp_port: u16) -> &mut Self {
64        self.inner.set_tcp_port(tcp_port);
65        self
66    }
67
68    /// Set the domains to search, instead of the domains specified in resolv.conf or the domain
69    /// derived from the kernel hostname variable.
70    pub fn set_domains(&mut self, domains: &[&str]) -> &mut Self {
71        self.inner.set_domains(domains);
72        self
73    }
74
75    /// Set the lookups to perform for host queries. `lookups` should be set to a string of the
76    /// characters "b" or "f", where "b" indicates a DNS lookup and "f" indicates a lookup in the
77    /// hosts file.
78    pub fn set_lookups(&mut self, lookups: &str) -> &mut Self {
79        self.inner.set_lookups(lookups);
80        self
81    }
82
83    /// Set the socket send buffer size.
84    pub fn set_sock_send_buffer_size(&mut self, size: u32) -> &mut Self {
85        self.inner.set_sock_send_buffer_size(size);
86        self
87    }
88
89    /// Set the socket receive buffer size.
90    pub fn set_sock_receive_buffer_size(&mut self, size: u32) -> &mut Self {
91        self.inner.set_sock_receive_buffer_size(size);
92        self
93    }
94
95    /// Configure round robin selection of nameservers.
96    pub fn set_rotate(&mut self) -> &mut Self {
97        self.inner.set_rotate();
98        self
99    }
100
101    /// Prevent round robin selection of nameservers.
102    pub fn set_no_rotate(&mut self) -> &mut Self {
103        self.inner.set_no_rotate();
104        self
105    }
106
107    /// Set the EDNS packet size.
108    pub fn set_ednspsz(&mut self, size: u32) -> &mut Self {
109        self.inner.set_ednspsz(size);
110        self
111    }
112
113    /// Set the path to use for reading the resolv.conf file.  The `resolvconf_path` should be set
114    /// to a path string, and will be honoured on *nix like systems.  The default is
115    /// /etc/resolv.conf.
116    pub fn set_resolvconf_path(&mut self, resolvconf_path: &str) -> &mut Self {
117        self.inner.set_resolvconf_path(resolvconf_path);
118        self
119    }
120
121    /// Set the path to use for reading the hosts file.  The `hosts_path` should be set to a path
122    /// string, and will be honoured on *nix like systems.  The default is /etc/hosts.
123    #[cfg(cares1_19)]
124    pub fn set_hosts_path(&mut self, hosts_path: &str) -> &mut Self {
125        self.inner.set_hosts_path(hosts_path);
126        self
127    }
128
129    /// Set the maximum number of udp queries that can be sent on a single ephemeral port to a
130    /// given DNS server before a new ephemeral port is assigned.  Any value of 0 or less will be
131    /// considered unlimited, and is the default.
132    #[cfg(cares1_20)]
133    pub fn set_udp_max_queries(&mut self, udp_max_queries: i32) -> &mut Self {
134        self.inner.set_udp_max_queries(udp_max_queries);
135        self
136    }
137
138    /// Set the upper bound for timeout between sequential retry attempts, in milliseconds.  When
139    /// retrying queries, the timeout is increased from the requested timeout parameter, this caps
140    /// the value.
141    #[cfg(cares1_22)]
142    pub fn set_max_timeout(&mut self, max_timeout: i32) -> &mut Self {
143        self.inner.set_max_timeout(max_timeout);
144        self
145    }
146
147    /// Enable the built-in query cache.  Will cache queries based on the returned TTL in the DNS
148    /// message.  Only fully successful and NXDOMAIN query results will be cached.
149    ///
150    /// The provided value is the maximum number of seconds a query result may be cached; this will
151    /// override a larger TTL in the response message. This must be a non-zero value otherwise the
152    /// cache will be disabled.
153    #[cfg(cares1_23)]
154    pub fn set_query_cache_max_ttl(&mut self, qcache_max_ttl: u32) -> &mut Self {
155        self.inner.set_query_cache_max_ttl(qcache_max_ttl);
156        self
157    }
158
159    /// Set server failover options.
160    ///
161    /// When a DNS server fails to respond to a query, c-ares will deprioritize the server.  On
162    /// subsequent queries, servers with fewer consecutive failures will be selected in preference.
163    /// However, in order to detect when such a server has recovered, c-ares will occasionally
164    /// retry failed servers.  [`c_ares::ServerFailoverOptions`] contains options to control this
165    /// behaviour.
166    ///
167    /// If this option is not specified then c-ares will use a retry chance of 10% and a minimum
168    /// delay of 5 seconds.
169    #[cfg(cares1_29)]
170    pub fn set_server_failover_options(
171        &mut self,
172        server_failover_options: &ServerFailoverOptions,
173    ) -> &mut Self {
174        self.inner
175            .set_server_failover_options(server_failover_options);
176        self
177    }
178}
179
180/// An asynchronous DNS resolver, which returns results via callbacks.
181///
182/// Note that dropping the resolver will cause all outstanding requests to fail with result
183/// `c_ares::Error::EDESTRUCTION`.
184pub struct Resolver {
185    ares_channel: Arc<Mutex<c_ares::Channel>>,
186    _event_loop_stopper: EventLoopStopper,
187}
188
189impl Resolver {
190    /// Create a new `Resolver`, using default `Options`.
191    pub fn new() -> Result<Self, Error> {
192        let options = Options::default();
193        Self::with_options(options)
194    }
195
196    /// Create a new `Resolver`, with the given `Options`.
197    pub fn with_options(options: Options) -> Result<Self, Error> {
198        // Create and run the event loop.
199        let event_loop = EventLoop::new(options.inner)?;
200        let channel = Arc::clone(&event_loop.ares_channel);
201        let stopper = event_loop.run();
202
203        // Return the Resolver.
204        let resolver = Self {
205            ares_channel: channel,
206            _event_loop_stopper: stopper,
207        };
208        Ok(resolver)
209    }
210
211    /// Reinitialize a channel from system configuration.
212    #[cfg(cares1_22)]
213    pub fn reinit(&self) -> c_ares::Result<&Self> {
214        self.ares_channel.lock().unwrap().reinit()?;
215        Ok(self)
216    }
217
218    /// Set the list of servers to contact, instead of the servers specified in resolv.conf or the
219    /// local named.
220    ///
221    /// String format is `host[:port]`.  IPv6 addresses with ports require square brackets eg
222    /// `[2001:4860:4860::8888]:53`.
223    pub fn set_servers(&self, servers: &[&str]) -> c_ares::Result<&Self> {
224        self.ares_channel.lock().unwrap().set_servers(servers)?;
225        Ok(self)
226    }
227
228    /// Retrieves the list of servers in comma delimited format.
229    #[cfg(cares1_24)]
230    pub fn get_servers(&self) -> AresString {
231        self.ares_channel.lock().unwrap().get_servers()
232    }
233
234    /// Set the local IPv4 address from which to make queries.
235    pub fn set_local_ipv4(&self, ipv4: Ipv4Addr) -> &Self {
236        self.ares_channel.lock().unwrap().set_local_ipv4(ipv4);
237        self
238    }
239
240    /// Set the local IPv6 address from which to make queries.
241    pub fn set_local_ipv6(&self, ipv6: &Ipv6Addr) -> &Self {
242        self.ares_channel.lock().unwrap().set_local_ipv6(ipv6);
243        self
244    }
245
246    /// Set the local device from which to make queries.
247    pub fn set_local_device(&self, device: &str) -> &Self {
248        self.ares_channel.lock().unwrap().set_local_device(device);
249        self
250    }
251
252    /// Initializes an address sortlist configuration, so that addresses returned by
253    /// `get_host_by_name()` are sorted according to the sortlist.
254    ///
255    /// Each element of the sortlist holds an IP-address/netmask pair. The netmask is optional but
256    /// follows the address after a slash if present. For example: "130.155.160.0/255.255.240.0",
257    /// or "130.155.0.0".
258    pub fn set_sortlist(&self, sortlist: &[&str]) -> c_ares::Result<&Self> {
259        self.ares_channel.lock().unwrap().set_sortlist(sortlist)?;
260        Ok(self)
261    }
262
263    /// Set a callback function to be invoked whenever a query on the channel completes.
264    ///
265    /// `callback(server, success, flags)` will be called when a query completes.
266    ///
267    /// - `server` indicates the DNS server that was used for the query.
268    /// - `success` indicates whether the query succeeded or not.
269    /// - `flags` is a bitmask of flags describing various aspects of the query.
270    #[cfg(cares1_29)]
271    pub fn set_server_state_callback<F>(&self, callback: F) -> &Self
272    where
273        F: FnMut(&str, bool, ServerStateFlags) + Send + 'static,
274    {
275        self.ares_channel
276            .lock()
277            .unwrap()
278            .set_server_state_callback(callback);
279        self
280    }
281
282    /// Look up the A records associated with `name`.
283    ///
284    /// On completion, `handler` is called with the result.
285    pub fn query_a<F>(&self, name: &str, handler: F)
286    where
287        F: FnOnce(c_ares::Result<c_ares::AResults>) + Send + 'static,
288    {
289        self.ares_channel.lock().unwrap().query_a(name, handler)
290    }
291
292    /// Search for the A records associated with `name`.
293    ///
294    /// On completion, `handler` is called with the result.
295    pub fn search_a<F>(&self, name: &str, handler: F)
296    where
297        F: FnOnce(c_ares::Result<c_ares::AResults>) + Send + 'static,
298    {
299        self.ares_channel.lock().unwrap().search_a(name, handler)
300    }
301
302    /// Look up the AAAA records associated with `name`.
303    ///
304    /// On completion, `handler` is called with the result.
305    pub fn query_aaaa<F>(&self, name: &str, handler: F)
306    where
307        F: FnOnce(c_ares::Result<c_ares::AAAAResults>) + Send + 'static,
308    {
309        self.ares_channel.lock().unwrap().query_aaaa(name, handler)
310    }
311
312    /// Search for the AAAA records associated with `name`.
313    ///
314    /// On completion, `handler` is called with the result.
315    pub fn search_aaaa<F>(&self, name: &str, handler: F)
316    where
317        F: FnOnce(c_ares::Result<c_ares::AAAAResults>) + Send + 'static,
318    {
319        self.ares_channel.lock().unwrap().search_aaaa(name, handler)
320    }
321
322    /// Look up the CAA records associated with `name`.
323    ///
324    /// On completion, `handler` is called with the result.
325    pub fn query_caa<F>(&self, name: &str, handler: F)
326    where
327        F: FnOnce(c_ares::Result<c_ares::CAAResults>) + Send + 'static,
328    {
329        self.ares_channel.lock().unwrap().query_caa(name, handler)
330    }
331
332    /// Search for the CAA records associated with `name`.
333    ///
334    /// On completion, `handler` is called with the result.
335    pub fn search_caa<F>(&self, name: &str, handler: F)
336    where
337        F: FnOnce(c_ares::Result<c_ares::CAAResults>) + Send + 'static,
338    {
339        self.ares_channel.lock().unwrap().search_caa(name, handler)
340    }
341
342    /// Look up the CNAME records associated with `name`.
343    ///
344    /// On completion, `handler` is called with the result.
345    pub fn query_cname<F>(&self, name: &str, handler: F)
346    where
347        F: FnOnce(c_ares::Result<c_ares::CNameResults>) + Send + 'static,
348    {
349        self.ares_channel.lock().unwrap().query_cname(name, handler)
350    }
351
352    /// Search for the CNAME records associated with `name`.
353    ///
354    /// On completion, `handler` is called with the result.
355    pub fn search_cname<F>(&self, name: &str, handler: F)
356    where
357        F: FnOnce(c_ares::Result<c_ares::CNameResults>) + Send + 'static,
358    {
359        self.ares_channel
360            .lock()
361            .unwrap()
362            .search_cname(name, handler)
363    }
364
365    /// Look up the MX records associated with `name`.
366    ///
367    /// On completion, `handler` is called with the result.
368    pub fn query_mx<F>(&self, name: &str, handler: F)
369    where
370        F: FnOnce(c_ares::Result<c_ares::MXResults>) + Send + 'static,
371    {
372        self.ares_channel.lock().unwrap().query_mx(name, handler)
373    }
374
375    /// Search for the MX records associated with `name`.
376    ///
377    /// On completion, `handler` is called with the result.
378    pub fn search_mx<F>(&self, name: &str, handler: F)
379    where
380        F: FnOnce(c_ares::Result<c_ares::MXResults>) + Send + 'static,
381    {
382        self.ares_channel.lock().unwrap().search_mx(name, handler)
383    }
384
385    /// Look up the NAPTR records associated with `name`.
386    ///
387    /// On completion, `handler` is called with the result.
388    pub fn query_naptr<F>(&self, name: &str, handler: F)
389    where
390        F: FnOnce(c_ares::Result<c_ares::NAPTRResults>) + Send + 'static,
391    {
392        self.ares_channel.lock().unwrap().query_naptr(name, handler)
393    }
394
395    /// Search for the NAPTR records associated with `name`.
396    ///
397    /// On completion, `handler` is called with the result.
398    pub fn search_naptr<F>(&self, name: &str, handler: F)
399    where
400        F: FnOnce(c_ares::Result<c_ares::NAPTRResults>) + Send + 'static,
401    {
402        self.ares_channel
403            .lock()
404            .unwrap()
405            .search_naptr(name, handler)
406    }
407
408    /// Look up the NS records associated with `name`.
409    ///
410    /// On completion, `handler` is called with the result.
411    pub fn query_ns<F>(&self, name: &str, handler: F)
412    where
413        F: FnOnce(c_ares::Result<c_ares::NSResults>) + Send + 'static,
414    {
415        self.ares_channel.lock().unwrap().query_ns(name, handler)
416    }
417
418    /// Search for the NS records associated with `name`.
419    ///
420    /// On completion, `handler` is called with the result.
421    pub fn search_ns<F>(&self, name: &str, handler: F)
422    where
423        F: FnOnce(c_ares::Result<c_ares::NSResults>) + Send + 'static,
424    {
425        self.ares_channel.lock().unwrap().search_ns(name, handler)
426    }
427
428    /// Look up the PTR records associated with `name`.
429    ///
430    /// On completion, `handler` is called with the result.
431    pub fn query_ptr<F>(&self, name: &str, handler: F)
432    where
433        F: FnOnce(c_ares::Result<c_ares::PTRResults>) + Send + 'static,
434    {
435        self.ares_channel.lock().unwrap().query_ptr(name, handler)
436    }
437
438    /// Search for the PTR records associated with `name`.
439    ///
440    /// On completion, `handler` is called with the result.
441    pub fn search_ptr<F>(&self, name: &str, handler: F)
442    where
443        F: FnOnce(c_ares::Result<c_ares::PTRResults>) + Send + 'static,
444    {
445        self.ares_channel.lock().unwrap().search_ptr(name, handler)
446    }
447
448    /// Look up the SOA record associated with `name`.
449    ///
450    /// On completion, `handler` is called with the result.
451    pub fn query_soa<F>(&self, name: &str, handler: F)
452    where
453        F: FnOnce(c_ares::Result<c_ares::SOAResult>) + Send + 'static,
454    {
455        self.ares_channel.lock().unwrap().query_soa(name, handler)
456    }
457
458    /// Search for the SOA record associated with `name`.
459    ///
460    /// On completion, `handler` is called with the result.
461    pub fn search_soa<F>(&self, name: &str, handler: F)
462    where
463        F: FnOnce(c_ares::Result<c_ares::SOAResult>) + Send + 'static,
464    {
465        self.ares_channel.lock().unwrap().search_soa(name, handler)
466    }
467
468    /// Look up the SRV records associated with `name`.
469    ///
470    /// On completion, `handler` is called with the result.
471    pub fn query_srv<F>(&self, name: &str, handler: F)
472    where
473        F: FnOnce(c_ares::Result<c_ares::SRVResults>) + Send + 'static,
474    {
475        self.ares_channel.lock().unwrap().query_srv(name, handler)
476    }
477
478    /// Search for the SRV records associated with `name`.
479    ///
480    /// On completion, `handler` is called with the result.
481    pub fn search_srv<F>(&self, name: &str, handler: F)
482    where
483        F: FnOnce(c_ares::Result<c_ares::SRVResults>) + Send + 'static,
484    {
485        self.ares_channel.lock().unwrap().search_srv(name, handler)
486    }
487
488    /// Look up the TXT records associated with `name`.
489    ///
490    /// On completion, `handler` is called with the result.
491    pub fn query_txt<F>(&self, name: &str, handler: F)
492    where
493        F: FnOnce(c_ares::Result<c_ares::TXTResults>) + Send + 'static,
494    {
495        self.ares_channel.lock().unwrap().query_txt(name, handler)
496    }
497
498    /// Search for the TXT records associated with `name`.
499    ///
500    /// On completion, `handler` is called with the result.
501    pub fn search_txt<F>(&self, name: &str, handler: F)
502    where
503        F: FnOnce(c_ares::Result<c_ares::TXTResults>) + Send + 'static,
504    {
505        self.ares_channel.lock().unwrap().search_txt(name, handler)
506    }
507
508    /// Look up the URI records associated with `name`.
509    ///
510    /// On completion, `handler` is called with the result.
511    pub fn query_uri<F>(&self, name: &str, handler: F)
512    where
513        F: FnOnce(c_ares::Result<c_ares::URIResults>) + Send + 'static,
514    {
515        self.ares_channel.lock().unwrap().query_uri(name, handler)
516    }
517
518    /// Search for the URI records associated with `name`.
519    ///
520    /// On completion, `handler` is called with the result.
521    pub fn search_uri<F>(&self, name: &str, handler: F)
522    where
523        F: FnOnce(c_ares::Result<c_ares::URIResults>) + Send + 'static,
524    {
525        self.ares_channel.lock().unwrap().search_uri(name, handler)
526    }
527
528    /// Perform a host query by address.
529    ///
530    /// On completion, `handler` is called with the result.
531    pub fn get_host_by_address<F>(&self, address: &IpAddr, handler: F)
532    where
533        F: FnOnce(c_ares::Result<c_ares::HostResults>) + Send + 'static,
534    {
535        self.ares_channel
536            .lock()
537            .unwrap()
538            .get_host_by_address(address, handler)
539    }
540
541    /// Perform a host query by name.
542    ///
543    /// On completion, `handler` is called with the result.
544    pub fn get_host_by_name<F>(&self, name: &str, family: c_ares::AddressFamily, handler: F)
545    where
546        F: FnOnce(c_ares::Result<c_ares::HostResults>) + Send + 'static,
547    {
548        self.ares_channel
549            .lock()
550            .unwrap()
551            .get_host_by_name(name, family, handler);
552    }
553
554    /// Address-to-nodename translation in protocol-independent manner.
555    ///
556    /// On completion, `handler` is called with the result.
557    pub fn get_name_info<F>(&self, address: &SocketAddr, flags: c_ares::NIFlags, handler: F)
558    where
559        F: FnOnce(c_ares::Result<c_ares::NameInfoResult>) + Send + 'static,
560    {
561        self.ares_channel
562            .lock()
563            .unwrap()
564            .get_name_info(address, flags, handler)
565    }
566
567    /// Initiate a single-question DNS query for `name`.  The class and type of the query are per
568    /// the provided parameters, taking values as defined in `arpa/nameser.h`.
569    ///
570    /// On completion, `handler` is called with the result.
571    ///
572    /// This method is provided so that users can query DNS types for which `c-ares` does not
573    /// provide a parser; or in case a third-party parser is preferred.  Usually, if a suitable
574    /// `query_xxx()` is available, that should be used.
575    pub fn query<F>(&self, name: &str, dns_class: u16, query_type: u16, handler: F)
576    where
577        F: FnOnce(c_ares::Result<&[u8]>) + Send + 'static,
578    {
579        self.ares_channel
580            .lock()
581            .unwrap()
582            .query(name, dns_class, query_type, handler);
583    }
584
585    /// Initiate a series of single-question DNS queries for `name`.  The class and type of the
586    /// query are per the provided parameters, taking values as defined in `arpa/nameser.h`.
587    ///
588    /// On completion, `handler` is called with the result.
589    ///
590    /// This method is provided so that users can search DNS types for which `c-ares` does not
591    /// provide a parser; or in case a third-party parser is preferred.  Usually, if a suitable
592    /// `search_xxx()` is available, that should be used.
593    pub fn search<F>(&self, name: &str, dns_class: u16, query_type: u16, handler: F)
594    where
595        F: FnOnce(c_ares::Result<&[u8]>) + Send + 'static,
596    {
597        self.ares_channel
598            .lock()
599            .unwrap()
600            .search(name, dns_class, query_type, handler);
601    }
602
603    /// Cancel all requests made on this `Resolver`.
604    pub fn cancel(&self) {
605        self.ares_channel.lock().unwrap().cancel();
606    }
607}