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