Skip to main content

c_ares_resolver/
resolver.rs

1use std::fmt;
2use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
3use std::sync::{Arc, Mutex};
4
5use crate::error::Error;
6use crate::eventloop::{EventLoop, EventLoopStopper};
7
8#[cfg(cares1_29)]
9use c_ares::{ServerFailoverOptions, ServerStateFlags};
10
11/// Used to configure the behaviour of the resolver.
12#[derive(Default)]
13pub struct Options {
14    inner: c_ares::Options,
15}
16
17impl fmt::Debug for Options {
18    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19        f.debug_struct("Options").finish_non_exhaustive()
20    }
21}
22
23impl Options {
24    /// Returns a fresh `Options`, on which no values are set.
25    ///
26    /// # Examples
27    ///
28    /// ```
29    /// use std::time::Duration;
30    /// use c_ares_resolver::Options;
31    ///
32    /// let mut options = Options::new();
33    /// options.set_timeout(Duration::from_secs(5))
34    ///        .set_tries(3);
35    /// let resolver = c_ares_resolver::FutureResolver::with_options(options).unwrap();
36    /// ```
37    pub fn new() -> Self {
38        Self::default()
39    }
40
41    /// Set flags controlling the behaviour of the resolver.
42    pub fn set_flags(&mut self, flags: c_ares::Flags) -> &mut Self {
43        self.inner.set_flags(flags);
44        self
45    }
46
47    /// Set the number of milliseconds each name server is given to respond to a query on the first
48    /// try.  (After the first try, the timeout algorithm becomes more complicated, but scales
49    /// linearly with the value of timeout).  The default is 5000ms.
50    pub fn set_timeout(&mut self, timeout: std::time::Duration) -> &mut Self {
51        self.inner.set_timeout(timeout);
52        self
53    }
54
55    /// Set the number of tries the resolver will try contacting each name server before giving up.
56    /// The default is four tries.
57    pub fn set_tries(&mut self, tries: u32) -> &mut Self {
58        self.inner.set_tries(tries);
59        self
60    }
61
62    /// Set the number of dots which must be present in a domain name for it to be queried for "as
63    /// is" prior to querying for it with the default domain extensions appended.  The default
64    /// value is 1 unless set otherwise by resolv.conf or the `RES_OPTIONS` environment variable.
65    pub fn set_ndots(&mut self, ndots: u32) -> &mut Self {
66        self.inner.set_ndots(ndots);
67        self
68    }
69
70    /// Set the UDP port to use for queries.  The default value is 53, the standard name service
71    /// port.
72    pub fn set_udp_port(&mut self, udp_port: u16) -> &mut Self {
73        self.inner.set_udp_port(udp_port);
74        self
75    }
76
77    /// Set the TCP port to use for queries.  The default value is 53, the standard name service
78    /// port.
79    pub fn set_tcp_port(&mut self, tcp_port: u16) -> &mut Self {
80        self.inner.set_tcp_port(tcp_port);
81        self
82    }
83
84    /// Set the domains to search, instead of the domains specified in resolv.conf or the domain
85    /// derived from the kernel hostname variable.
86    pub fn set_domains<I, S>(&mut self, domains: I) -> c_ares::Result<&mut Self>
87    where
88        I: IntoIterator<Item = S>,
89        S: AsRef<str>,
90    {
91        self.inner.set_domains(domains)?;
92        Ok(self)
93    }
94
95    /// Set the lookups to perform for host queries. `lookups` should be set to a string of the
96    /// characters "b" or "f", where "b" indicates a DNS lookup and "f" indicates a lookup in the
97    /// hosts file.
98    pub fn set_lookups(&mut self, lookups: &str) -> c_ares::Result<&mut Self> {
99        self.inner.set_lookups(lookups)?;
100        Ok(self)
101    }
102
103    /// Set the socket send buffer size.
104    pub fn set_sock_send_buffer_size(&mut self, size: u32) -> &mut Self {
105        self.inner.set_sock_send_buffer_size(size);
106        self
107    }
108
109    /// Set the socket receive buffer size.
110    pub fn set_sock_receive_buffer_size(&mut self, size: u32) -> &mut Self {
111        self.inner.set_sock_receive_buffer_size(size);
112        self
113    }
114
115    /// Configure round robin selection of nameservers.
116    pub fn set_rotate(&mut self) -> &mut Self {
117        self.inner.set_rotate();
118        self
119    }
120
121    /// Prevent round robin selection of nameservers.
122    pub fn set_no_rotate(&mut self) -> &mut Self {
123        self.inner.set_no_rotate();
124        self
125    }
126
127    /// Set the EDNS packet size.
128    pub fn set_ednspsz(&mut self, size: u32) -> &mut Self {
129        self.inner.set_ednspsz(size);
130        self
131    }
132
133    /// Set the path to use for reading the resolv.conf file.  The `resolvconf_path` should be set
134    /// to a path string, and will be honoured on *nix like systems.  The default is
135    /// /etc/resolv.conf.
136    pub fn set_resolvconf_path(&mut self, resolvconf_path: &str) -> c_ares::Result<&mut Self> {
137        self.inner.set_resolvconf_path(resolvconf_path)?;
138        Ok(self)
139    }
140
141    /// Set the path to use for reading the hosts file.  The `hosts_path` should be set to a path
142    /// string, and will be honoured on *nix like systems.  The default is /etc/hosts.
143    #[cfg(cares1_19)]
144    pub fn set_hosts_path(&mut self, hosts_path: &str) -> c_ares::Result<&mut Self> {
145        self.inner.set_hosts_path(hosts_path)?;
146        Ok(self)
147    }
148
149    /// Set the maximum number of UDP queries that can be sent on a single ephemeral port to a
150    /// given DNS server before a new ephemeral port is assigned.
151    ///
152    /// Pass `None` for unlimited (the default).
153    #[cfg(cares1_20)]
154    pub fn set_udp_max_queries(&mut self, udp_max_queries: Option<u32>) -> &mut Self {
155        self.inner.set_udp_max_queries(udp_max_queries);
156        self
157    }
158
159    /// Set the upper bound for timeout between sequential retry attempts.  When retrying queries,
160    /// the timeout is increased from the requested timeout parameter, this caps the value.
161    #[cfg(cares1_22)]
162    pub fn set_max_timeout(&mut self, max_timeout: std::time::Duration) -> &mut Self {
163        self.inner.set_max_timeout(max_timeout);
164        self
165    }
166
167    /// Enable the built-in query cache.  Will cache queries based on the returned TTL in the DNS
168    /// message.  Only fully successful and NXDOMAIN query results will be cached.
169    ///
170    /// The provided value is the maximum number of seconds a query result may be cached; this will
171    /// override a larger TTL in the response message. This must be a non-zero value otherwise the
172    /// cache will be disabled.
173    #[cfg(cares1_23)]
174    pub fn set_query_cache_max_ttl(&mut self, qcache_max_ttl: u32) -> &mut Self {
175        self.inner.set_query_cache_max_ttl(qcache_max_ttl);
176        self
177    }
178
179    /// Set server failover options.
180    ///
181    /// When a DNS server fails to respond to a query, c-ares will deprioritize the server.  On
182    /// subsequent queries, servers with fewer consecutive failures will be selected in preference.
183    /// However, in order to detect when such a server has recovered, c-ares will occasionally
184    /// retry failed servers.  [`c_ares::ServerFailoverOptions`] contains options to control this
185    /// behaviour.
186    ///
187    /// If this option is not specified then c-ares will use a retry chance of 10% and a minimum
188    /// delay of 5 seconds.
189    #[cfg(cares1_29)]
190    pub fn set_server_failover_options(
191        &mut self,
192        server_failover_options: &ServerFailoverOptions,
193    ) -> &mut Self {
194        self.inner
195            .set_server_failover_options(server_failover_options);
196        self
197    }
198}
199
200/// An asynchronous DNS resolver, which returns results via callbacks.
201///
202/// Note that dropping the resolver will cause all outstanding requests to fail with result
203/// `c_ares::Error::EDESTRUCTION`.
204pub struct Resolver {
205    ares_channel: Arc<Mutex<c_ares::Channel>>,
206
207    // Present when we run our own event loop; `None` when c-ares manages its built-in event
208    // thread (the thread stops automatically when the Channel is destroyed).
209    _event_loop_stopper: Option<EventLoopStopper>,
210}
211
212impl fmt::Debug for Resolver {
213    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214        f.debug_struct("Resolver").finish_non_exhaustive()
215    }
216}
217
218impl Resolver {
219    /// Create a new `Resolver`, using default `Options`.
220    pub fn new() -> Result<Self, Error> {
221        let options = Options::default();
222        Self::with_options(options)
223    }
224
225    /// Create a new `Resolver`, with the given `Options`.
226    pub fn with_options(options: Options) -> Result<Self, Error> {
227        let mut inner = options.inner;
228
229        // Use the c-ares built-in event thread if available, else our custom event loop.
230        #[cfg(cares1_26)]
231        let event_loop = if c_ares::thread_safety() {
232            inner.set_event_thread(c_ares::EventSys::Default);
233            None
234        } else {
235            Some(EventLoop::new(&mut inner)?)
236        };
237        #[cfg(not(cares1_26))]
238        let event_loop = Some(EventLoop::new(&mut inner)?);
239
240        // Create the channel.
241        let ares_channel = Arc::new(Mutex::new(c_ares::Channel::with_options(inner)?));
242
243        // Start the custom event loop if we're using one.
244        let stopper = event_loop.map(|el| el.run(Arc::clone(&ares_channel)));
245
246        Ok(Self {
247            ares_channel,
248            _event_loop_stopper: stopper,
249        })
250    }
251
252    /// Reinitialize a channel from system configuration.
253    #[cfg(cares1_22)]
254    pub fn reinit(&self) -> c_ares::Result<&Self> {
255        self.ares_channel.lock().unwrap().reinit()?;
256        Ok(self)
257    }
258
259    /// Set the list of servers to contact, instead of the servers specified in resolv.conf or the
260    /// local named.
261    ///
262    /// String format is `host[:port]`.  IPv6 addresses with ports require square brackets eg
263    /// `[2001:4860:4860::8888]:53`.
264    pub fn set_servers<I, S>(&self, servers: I) -> c_ares::Result<&Self>
265    where
266        I: IntoIterator<Item = S>,
267        S: AsRef<str>,
268    {
269        self.ares_channel.lock().unwrap().set_servers(servers)?;
270        Ok(self)
271    }
272
273    /// Retrieves the list of configured servers.
274    ///
275    /// Each entry is in `host[:port]` format, matching what [`set_servers`](Self::set_servers)
276    /// accepts.
277    #[cfg(cares1_24)]
278    pub fn servers(&self) -> Vec<String> {
279        self.ares_channel.lock().unwrap().servers()
280    }
281
282    /// Set the local IPv4 address from which to make queries.
283    pub fn set_local_ipv4(&self, ipv4: Ipv4Addr) -> &Self {
284        self.ares_channel.lock().unwrap().set_local_ipv4(ipv4);
285        self
286    }
287
288    /// Set the local IPv6 address from which to make queries.
289    pub fn set_local_ipv6(&self, ipv6: Ipv6Addr) -> &Self {
290        self.ares_channel.lock().unwrap().set_local_ipv6(ipv6);
291        self
292    }
293
294    /// Set the local device from which to make queries.
295    pub fn set_local_device(&self, device: &str) -> c_ares::Result<&Self> {
296        self.ares_channel.lock().unwrap().set_local_device(device)?;
297        Ok(self)
298    }
299
300    /// Initializes an address sortlist configuration, so that addresses returned by
301    /// `get_host_by_name()` are sorted according to the sortlist.
302    ///
303    /// Each element of the sortlist holds an IP-address/netmask pair. The netmask is optional but
304    /// follows the address after a slash if present. For example: "130.155.160.0/255.255.240.0",
305    /// or "130.155.0.0".
306    pub fn set_sortlist<I, S>(&self, sortlist: I) -> c_ares::Result<&Self>
307    where
308        I: IntoIterator<Item = S>,
309        S: AsRef<str>,
310    {
311        self.ares_channel.lock().unwrap().set_sortlist(sortlist)?;
312        Ok(self)
313    }
314
315    /// Set a callback function to be invoked whenever a query on the channel completes.
316    ///
317    /// `callback(server, success, flags)` will be called when a query completes.
318    ///
319    /// - `server` indicates the DNS server that was used for the query.
320    /// - `success` indicates whether the query succeeded or not.
321    /// - `flags` is a bitmask of flags describing various aspects of the query.
322    #[cfg(cares1_29)]
323    pub fn set_server_state_callback<F>(&self, callback: F) -> &Self
324    where
325        F: Fn(&str, bool, ServerStateFlags) + Send + Sync + 'static,
326    {
327        self.ares_channel
328            .lock()
329            .unwrap()
330            .set_server_state_callback(callback);
331        self
332    }
333
334    /// Look up the A records associated with `name`.
335    ///
336    /// On completion, `handler` is called with the result.
337    pub fn query_a<F>(&self, name: &str, handler: F)
338    where
339        F: FnOnce(c_ares::Result<c_ares::AResults>) + Send + 'static,
340    {
341        self.ares_channel.lock().unwrap().query_a(name, handler);
342    }
343
344    /// Search for the A records associated with `name`.
345    ///
346    /// On completion, `handler` is called with the result.
347    pub fn search_a<F>(&self, name: &str, handler: F)
348    where
349        F: FnOnce(c_ares::Result<c_ares::AResults>) + Send + 'static,
350    {
351        self.ares_channel.lock().unwrap().search_a(name, handler);
352    }
353
354    /// Look up the AAAA records associated with `name`.
355    ///
356    /// On completion, `handler` is called with the result.
357    pub fn query_aaaa<F>(&self, name: &str, handler: F)
358    where
359        F: FnOnce(c_ares::Result<c_ares::AAAAResults>) + Send + 'static,
360    {
361        self.ares_channel.lock().unwrap().query_aaaa(name, handler);
362    }
363
364    /// Search for the AAAA records associated with `name`.
365    ///
366    /// On completion, `handler` is called with the result.
367    pub fn search_aaaa<F>(&self, name: &str, handler: F)
368    where
369        F: FnOnce(c_ares::Result<c_ares::AAAAResults>) + Send + 'static,
370    {
371        self.ares_channel.lock().unwrap().search_aaaa(name, handler);
372    }
373
374    /// Look up the CAA records associated with `name`.
375    ///
376    /// On completion, `handler` is called with the result.
377    pub fn query_caa<F>(&self, name: &str, handler: F)
378    where
379        F: FnOnce(c_ares::Result<c_ares::CAAResults>) + Send + 'static,
380    {
381        self.ares_channel.lock().unwrap().query_caa(name, handler);
382    }
383
384    /// Search for the CAA records associated with `name`.
385    ///
386    /// On completion, `handler` is called with the result.
387    pub fn search_caa<F>(&self, name: &str, handler: F)
388    where
389        F: FnOnce(c_ares::Result<c_ares::CAAResults>) + Send + 'static,
390    {
391        self.ares_channel.lock().unwrap().search_caa(name, handler);
392    }
393
394    /// Look up the CNAME records associated with `name`.
395    ///
396    /// On completion, `handler` is called with the result.
397    pub fn query_cname<F>(&self, name: &str, handler: F)
398    where
399        F: FnOnce(c_ares::Result<c_ares::CNameResults>) + Send + 'static,
400    {
401        self.ares_channel.lock().unwrap().query_cname(name, handler);
402    }
403
404    /// Search for the CNAME records associated with `name`.
405    ///
406    /// On completion, `handler` is called with the result.
407    pub fn search_cname<F>(&self, name: &str, handler: F)
408    where
409        F: FnOnce(c_ares::Result<c_ares::CNameResults>) + Send + 'static,
410    {
411        self.ares_channel
412            .lock()
413            .unwrap()
414            .search_cname(name, handler);
415    }
416
417    /// Look up the MX records associated with `name`.
418    ///
419    /// On completion, `handler` is called with the result.
420    pub fn query_mx<F>(&self, name: &str, handler: F)
421    where
422        F: FnOnce(c_ares::Result<c_ares::MXResults>) + Send + 'static,
423    {
424        self.ares_channel.lock().unwrap().query_mx(name, handler);
425    }
426
427    /// Search for the MX records associated with `name`.
428    ///
429    /// On completion, `handler` is called with the result.
430    pub fn search_mx<F>(&self, name: &str, handler: F)
431    where
432        F: FnOnce(c_ares::Result<c_ares::MXResults>) + Send + 'static,
433    {
434        self.ares_channel.lock().unwrap().search_mx(name, handler);
435    }
436
437    /// Look up the NAPTR records associated with `name`.
438    ///
439    /// On completion, `handler` is called with the result.
440    pub fn query_naptr<F>(&self, name: &str, handler: F)
441    where
442        F: FnOnce(c_ares::Result<c_ares::NAPTRResults>) + Send + 'static,
443    {
444        self.ares_channel.lock().unwrap().query_naptr(name, handler);
445    }
446
447    /// Search for the NAPTR records associated with `name`.
448    ///
449    /// On completion, `handler` is called with the result.
450    pub fn search_naptr<F>(&self, name: &str, handler: F)
451    where
452        F: FnOnce(c_ares::Result<c_ares::NAPTRResults>) + Send + 'static,
453    {
454        self.ares_channel
455            .lock()
456            .unwrap()
457            .search_naptr(name, handler);
458    }
459
460    /// Look up the NS records associated with `name`.
461    ///
462    /// On completion, `handler` is called with the result.
463    pub fn query_ns<F>(&self, name: &str, handler: F)
464    where
465        F: FnOnce(c_ares::Result<c_ares::NSResults>) + Send + 'static,
466    {
467        self.ares_channel.lock().unwrap().query_ns(name, handler);
468    }
469
470    /// Search for the NS records associated with `name`.
471    ///
472    /// On completion, `handler` is called with the result.
473    pub fn search_ns<F>(&self, name: &str, handler: F)
474    where
475        F: FnOnce(c_ares::Result<c_ares::NSResults>) + Send + 'static,
476    {
477        self.ares_channel.lock().unwrap().search_ns(name, handler);
478    }
479
480    /// Look up the PTR records associated with `name`.
481    ///
482    /// On completion, `handler` is called with the result.
483    pub fn query_ptr<F>(&self, name: &str, handler: F)
484    where
485        F: FnOnce(c_ares::Result<c_ares::PTRResults>) + Send + 'static,
486    {
487        self.ares_channel.lock().unwrap().query_ptr(name, handler);
488    }
489
490    /// Search for the PTR records associated with `name`.
491    ///
492    /// On completion, `handler` is called with the result.
493    pub fn search_ptr<F>(&self, name: &str, handler: F)
494    where
495        F: FnOnce(c_ares::Result<c_ares::PTRResults>) + Send + 'static,
496    {
497        self.ares_channel.lock().unwrap().search_ptr(name, handler);
498    }
499
500    /// Look up the SOA record associated with `name`.
501    ///
502    /// On completion, `handler` is called with the result.
503    pub fn query_soa<F>(&self, name: &str, handler: F)
504    where
505        F: FnOnce(c_ares::Result<c_ares::SOAResult>) + Send + 'static,
506    {
507        self.ares_channel.lock().unwrap().query_soa(name, handler);
508    }
509
510    /// Search for the SOA record associated with `name`.
511    ///
512    /// On completion, `handler` is called with the result.
513    pub fn search_soa<F>(&self, name: &str, handler: F)
514    where
515        F: FnOnce(c_ares::Result<c_ares::SOAResult>) + Send + 'static,
516    {
517        self.ares_channel.lock().unwrap().search_soa(name, handler);
518    }
519
520    /// Look up the SRV records associated with `name`.
521    ///
522    /// On completion, `handler` is called with the result.
523    pub fn query_srv<F>(&self, name: &str, handler: F)
524    where
525        F: FnOnce(c_ares::Result<c_ares::SRVResults>) + Send + 'static,
526    {
527        self.ares_channel.lock().unwrap().query_srv(name, handler);
528    }
529
530    /// Search for the SRV records associated with `name`.
531    ///
532    /// On completion, `handler` is called with the result.
533    pub fn search_srv<F>(&self, name: &str, handler: F)
534    where
535        F: FnOnce(c_ares::Result<c_ares::SRVResults>) + Send + 'static,
536    {
537        self.ares_channel.lock().unwrap().search_srv(name, handler);
538    }
539
540    /// Look up the TXT records associated with `name`.
541    ///
542    /// On completion, `handler` is called with the result.
543    pub fn query_txt<F>(&self, name: &str, handler: F)
544    where
545        F: FnOnce(c_ares::Result<c_ares::TXTResults>) + Send + 'static,
546    {
547        self.ares_channel.lock().unwrap().query_txt(name, handler);
548    }
549
550    /// Search for the TXT records associated with `name`.
551    ///
552    /// On completion, `handler` is called with the result.
553    pub fn search_txt<F>(&self, name: &str, handler: F)
554    where
555        F: FnOnce(c_ares::Result<c_ares::TXTResults>) + Send + 'static,
556    {
557        self.ares_channel.lock().unwrap().search_txt(name, handler);
558    }
559
560    /// Look up the URI records associated with `name`.
561    ///
562    /// On completion, `handler` is called with the result.
563    pub fn query_uri<F>(&self, name: &str, handler: F)
564    where
565        F: FnOnce(c_ares::Result<c_ares::URIResults>) + Send + 'static,
566    {
567        self.ares_channel.lock().unwrap().query_uri(name, handler);
568    }
569
570    /// Search for the URI records associated with `name`.
571    ///
572    /// On completion, `handler` is called with the result.
573    pub fn search_uri<F>(&self, name: &str, handler: F)
574    where
575        F: FnOnce(c_ares::Result<c_ares::URIResults>) + Send + 'static,
576    {
577        self.ares_channel.lock().unwrap().search_uri(name, handler);
578    }
579
580    /// Perform a host query by address.
581    ///
582    /// On completion, `handler` is called with the result.
583    pub fn get_host_by_address<F>(&self, address: &IpAddr, handler: F)
584    where
585        F: FnOnce(c_ares::Result<&c_ares::HostResults>) + Send + 'static,
586    {
587        self.ares_channel
588            .lock()
589            .unwrap()
590            .get_host_by_address(address, handler);
591    }
592
593    /// Perform a host query by name.
594    ///
595    /// On completion, `handler` is called with the result.
596    pub fn get_host_by_name<F>(&self, name: &str, family: c_ares::AddressFamily, handler: F)
597    where
598        F: FnOnce(c_ares::Result<&c_ares::HostResults>) + Send + 'static,
599    {
600        self.ares_channel
601            .lock()
602            .unwrap()
603            .get_host_by_name(name, family, handler);
604    }
605
606    /// Address-to-nodename translation in protocol-independent manner.
607    ///
608    /// On completion, `handler` is called with the result.
609    pub fn get_name_info<F>(&self, address: &SocketAddr, flags: c_ares::NIFlags, handler: F)
610    where
611        F: FnOnce(c_ares::Result<c_ares::NameInfoResult>) + Send + 'static,
612    {
613        self.ares_channel
614            .lock()
615            .unwrap()
616            .get_name_info(address, flags, handler);
617    }
618
619    /// Initiate a host query by name and service.
620    ///
621    /// On completion, `handler` is called with the result.
622    pub fn get_addrinfo<F>(
623        &self,
624        name: &str,
625        service: Option<&str>,
626        hints: &c_ares::AddrInfoHints,
627        handler: F,
628    ) where
629        F: FnOnce(c_ares::Result<c_ares::AddrInfoResults>) + Send + 'static,
630    {
631        self.ares_channel
632            .lock()
633            .unwrap()
634            .get_addrinfo(name, service, hints, handler);
635    }
636
637    /// Initiate a single-question DNS query for `name`.  The class and type of the query are per
638    /// the provided parameters, taking values as defined in `arpa/nameser.h`.
639    ///
640    /// On completion, `handler` is called with the result.
641    ///
642    /// This method is provided so that users can query DNS types for which `c-ares` does not
643    /// provide a parser; or in case a third-party parser is preferred.  Usually, if a suitable
644    /// `query_xxx()` is available, that should be used.
645    pub fn query<F>(&self, name: &str, dns_class: u16, query_type: u16, handler: F)
646    where
647        F: FnOnce(c_ares::Result<&[u8]>) + Send + 'static,
648    {
649        self.ares_channel
650            .lock()
651            .unwrap()
652            .query(name, dns_class, query_type, handler);
653    }
654
655    /// Initiate a series of single-question DNS queries for `name`.  The class and type of the
656    /// query are per the provided parameters, taking values as defined in `arpa/nameser.h`.
657    ///
658    /// On completion, `handler` is called with the result.
659    ///
660    /// This method is provided so that users can search DNS types for which `c-ares` does not
661    /// provide a parser; or in case a third-party parser is preferred.  Usually, if a suitable
662    /// `search_xxx()` is available, that should be used.
663    pub fn search<F>(&self, name: &str, dns_class: u16, query_type: u16, handler: F)
664    where
665        F: FnOnce(c_ares::Result<&[u8]>) + Send + 'static,
666    {
667        self.ares_channel
668            .lock()
669            .unwrap()
670            .search(name, dns_class, query_type, handler);
671    }
672
673    /// Send a DNS query using a pre-built [`c_ares::DnsRecord`].
674    ///
675    /// On completion, `handler` is called with the result.
676    #[cfg(cares1_28)]
677    pub fn send_dnsrec<F>(&self, dnsrec: &c_ares::DnsRecord, handler: F) -> c_ares::Result<u16>
678    where
679        F: FnOnce(c_ares::Result<&c_ares::DnsRecord>) + Send + 'static,
680    {
681        self.ares_channel
682            .lock()
683            .unwrap()
684            .send_dnsrec(dnsrec, handler)
685    }
686
687    /// Initiate a DNS query for `name` with the given class and type, receiving a parsed
688    /// [`c_ares::DnsRecord`] in the callback.
689    #[cfg(cares1_28)]
690    pub fn query_dnsrec<F>(
691        &self,
692        name: &str,
693        dns_class: c_ares::DnsCls,
694        query_type: c_ares::DnsRecordType,
695        handler: F,
696    ) -> c_ares::Result<u16>
697    where
698        F: FnOnce(c_ares::Result<&c_ares::DnsRecord>) + Send + 'static,
699    {
700        self.ares_channel
701            .lock()
702            .unwrap()
703            .query_dnsrec(name, dns_class, query_type, handler)
704    }
705
706    /// Initiate a series of DNS queries using a pre-built [`c_ares::DnsRecord`], receiving a
707    /// parsed [`c_ares::DnsRecord`] in the callback.
708    #[cfg(cares1_28)]
709    pub fn search_dnsrec<F>(&self, dnsrec: &c_ares::DnsRecord, handler: F) -> c_ares::Result<()>
710    where
711        F: FnOnce(c_ares::Result<&c_ares::DnsRecord>) + Send + 'static,
712    {
713        self.ares_channel
714            .lock()
715            .unwrap()
716            .search_dnsrec(dnsrec, handler)
717    }
718
719    /// Block until notified that there are no longer any queries in queue, or the specified
720    /// timeout has expired.
721    ///
722    /// Pass `None` to wait indefinitely.
723    #[cfg(cares1_27)]
724    pub fn queue_wait_empty(&self, timeout: Option<std::time::Duration>) -> c_ares::Result<()> {
725        // Call ares_queue_wait_empty without holding the channel Mutex.
726        //
727        // This function blocks until all queries complete. If we held the
728        // Mutex, the event loop thread would be unable to call process_fd,
729        // causing a deadlock. This is safe because ares_queue_wait_empty is
730        // only functional when c-ares has thread safety (otherwise it returns
731        // ENOTIMP), and thread-safe c-ares has internal locking.
732        let raw = self.ares_channel.lock().unwrap().as_raw();
733        let timeout_ms: core::ffi::c_int = match timeout {
734            None => -1,
735            Some(d) => d.as_millis().try_into().unwrap_or(core::ffi::c_int::MAX),
736        };
737        let rc = unsafe { c_ares_sys::ares_queue_wait_empty(raw, timeout_ms) };
738        c_ares::Error::try_from(rc).map_or(Ok(()), Err)
739    }
740
741    /// Retrieve the total number of active queries pending answers from servers.
742    #[cfg(cares1_27)]
743    pub fn queue_active_queries(&self) -> usize {
744        self.ares_channel.lock().unwrap().queue_active_queries()
745    }
746
747    /// Cancel all requests made on this `Resolver`.
748    pub fn cancel(&self) {
749        self.ares_channel.lock().unwrap().cancel();
750    }
751}
752
753#[cfg(test)]
754mod tests {
755    use super::*;
756
757    fn assert_send<T: Send>() {}
758    fn assert_sync<T: Sync>() {}
759
760    #[test]
761    fn options_is_send() {
762        assert_send::<Options>();
763    }
764
765    #[test]
766    fn options_is_sync() {
767        assert_sync::<Options>();
768    }
769
770    #[test]
771    fn resolver_is_send() {
772        assert_send::<Resolver>();
773    }
774
775    #[test]
776    fn resolver_is_sync() {
777        assert_sync::<Resolver>();
778    }
779
780    #[test]
781    fn options_new() {
782        let options = Options::new();
783        // Options::new() should create default options
784        assert!(std::mem::size_of_val(&options) > 0);
785    }
786
787    #[test]
788    fn options_default() {
789        let options = Options::default();
790        assert!(std::mem::size_of_val(&options) > 0);
791    }
792
793    #[test]
794    fn options_set_flags() {
795        let mut options = Options::new();
796        options.set_flags(c_ares::Flags::STAYOPEN);
797        // Builder pattern should return self
798    }
799
800    #[test]
801    fn options_set_timeout() {
802        let mut options = Options::new();
803        let result = options.set_timeout(std::time::Duration::from_secs(1));
804        assert!(std::ptr::eq(result, &raw const options));
805    }
806
807    #[test]
808    fn options_set_tries() {
809        let mut options = Options::new();
810        let result = options.set_tries(3);
811        assert!(std::ptr::eq(result, &raw const options));
812    }
813
814    #[test]
815    fn options_set_ndots() {
816        let mut options = Options::new();
817        let result = options.set_ndots(2);
818        assert!(std::ptr::eq(result, &raw const options));
819    }
820
821    #[test]
822    fn options_set_udp_port() {
823        let mut options = Options::new();
824        let result = options.set_udp_port(5353);
825        assert!(std::ptr::eq(result, &raw const options));
826    }
827
828    #[test]
829    fn options_set_tcp_port() {
830        let mut options = Options::new();
831        let result = options.set_tcp_port(5353);
832        assert!(std::ptr::eq(result, &raw const options));
833    }
834
835    #[test]
836    fn options_set_domains() {
837        let mut options = Options::new();
838        let result = options.set_domains(["example.com", "test.com"]).unwrap();
839        assert!(std::ptr::eq(result, &raw const options));
840    }
841
842    #[test]
843    fn options_set_lookups() {
844        let mut options = Options::new();
845        let result = options.set_lookups("bf").unwrap();
846        assert!(std::ptr::eq(result, &raw const options));
847    }
848
849    #[test]
850    fn options_set_sock_send_buffer_size() {
851        let mut options = Options::new();
852        let result = options.set_sock_send_buffer_size(65536);
853        assert!(std::ptr::eq(result, &raw const options));
854    }
855
856    #[test]
857    fn options_set_sock_receive_buffer_size() {
858        let mut options = Options::new();
859        let result = options.set_sock_receive_buffer_size(65536);
860        assert!(std::ptr::eq(result, &raw const options));
861    }
862
863    #[test]
864    fn options_set_rotate() {
865        let mut options = Options::new();
866        let result = options.set_rotate();
867        assert!(std::ptr::eq(result, &raw const options));
868    }
869
870    #[test]
871    fn options_set_no_rotate() {
872        let mut options = Options::new();
873        let result = options.set_no_rotate();
874        assert!(std::ptr::eq(result, &raw const options));
875    }
876
877    #[test]
878    fn options_set_ednspsz() {
879        let mut options = Options::new();
880        let result = options.set_ednspsz(4096);
881        assert!(std::ptr::eq(result, &raw const options));
882    }
883
884    #[test]
885    fn options_set_resolvconf_path() {
886        let mut options = Options::new();
887        let result = options.set_resolvconf_path("/etc/resolv.conf").unwrap();
888        assert!(std::ptr::eq(result, &raw const options));
889    }
890
891    #[test]
892    #[cfg(cares1_19)]
893    fn options_set_hosts_path() {
894        let mut options = Options::new();
895        let result = options.set_hosts_path("/etc/hosts").unwrap();
896        assert!(std::ptr::eq(result, &raw const options));
897    }
898
899    #[test]
900    #[cfg(cares1_20)]
901    fn options_set_udp_max_queries() {
902        let mut options = Options::new();
903        let result = options.set_udp_max_queries(Some(100));
904        assert!(std::ptr::eq(result, &raw const options));
905    }
906
907    #[test]
908    #[cfg(cares1_22)]
909    fn options_set_max_timeout() {
910        let mut options = Options::new();
911        let result = options.set_max_timeout(std::time::Duration::from_secs(30));
912        assert!(std::ptr::eq(result, &raw const options));
913    }
914
915    #[test]
916    #[cfg(cares1_23)]
917    fn options_set_query_cache_max_ttl() {
918        let mut options = Options::new();
919        let result = options.set_query_cache_max_ttl(3600);
920        assert!(std::ptr::eq(result, &raw const options));
921    }
922
923    #[test]
924    fn resolver_new() {
925        let resolver = Resolver::new();
926        assert!(resolver.is_ok());
927    }
928
929    #[test]
930    fn resolver_with_options() {
931        let options = Options::new();
932        let resolver = Resolver::with_options(options);
933        assert!(resolver.is_ok());
934    }
935
936    #[test]
937    fn resolver_with_custom_options() {
938        let mut options = Options::new();
939        options
940            .set_timeout(std::time::Duration::from_secs(2))
941            .set_tries(2);
942        let resolver = Resolver::with_options(options);
943        assert!(resolver.is_ok());
944    }
945
946    #[test]
947    fn resolver_set_local_ipv4() {
948        let resolver = Resolver::new().unwrap();
949        let result = resolver.set_local_ipv4(Ipv4Addr::LOCALHOST);
950        assert!(std::ptr::eq(result, &raw const resolver));
951    }
952
953    #[test]
954    fn resolver_set_local_ipv6() {
955        let resolver = Resolver::new().unwrap();
956        let ipv6 = Ipv6Addr::LOCALHOST;
957        let result = resolver.set_local_ipv6(ipv6);
958        assert!(std::ptr::eq(result, &raw const resolver));
959    }
960
961    #[test]
962    fn resolver_set_local_device() {
963        let resolver = Resolver::new().unwrap();
964        let result = resolver.set_local_device("lo").unwrap();
965        assert!(std::ptr::eq(result, &raw const resolver));
966    }
967
968    #[test]
969    fn resolver_set_servers_valid() {
970        let resolver = Resolver::new().unwrap();
971        let result = resolver.set_servers(["8.8.8.8", "8.8.4.4"]);
972        assert!(result.is_ok());
973    }
974
975    #[test]
976    fn resolver_set_servers_with_port() {
977        let resolver = Resolver::new().unwrap();
978        let result = resolver.set_servers(["8.8.8.8:53"]);
979        assert!(result.is_ok());
980    }
981
982    #[test]
983    fn resolver_set_servers_ipv6() {
984        let resolver = Resolver::new().unwrap();
985        let result = resolver.set_servers(["[2001:4860:4860::8888]:53"]);
986        assert!(result.is_ok());
987    }
988
989    #[test]
990    fn resolver_set_sortlist_valid() {
991        let resolver = Resolver::new().unwrap();
992        let result = resolver.set_sortlist(["130.155.160.0/255.255.240.0"]);
993        assert!(result.is_ok());
994    }
995
996    #[test]
997    fn resolver_cancel() {
998        let resolver = Resolver::new().unwrap();
999        resolver.cancel(); // Should not panic
1000    }
1001
1002    #[test]
1003    #[cfg(cares1_22)]
1004    fn resolver_reinit() {
1005        let resolver = Resolver::new().unwrap();
1006        let result = resolver.reinit();
1007        assert!(result.is_ok());
1008    }
1009
1010    #[test]
1011    #[cfg(cares1_24)]
1012    fn resolver_servers() {
1013        let resolver = Resolver::new().unwrap();
1014        let _ = resolver.set_servers(["8.8.8.8"]);
1015        let servers = resolver.servers();
1016        assert!(!servers.is_empty());
1017    }
1018
1019    #[test]
1020    #[cfg(cares1_29)]
1021    fn resolver_set_server_state_callback() {
1022        let resolver = Resolver::new().unwrap();
1023        let result = resolver.set_server_state_callback(|_server, _success, _flags| {});
1024        assert!(std::ptr::eq(result, &raw const resolver));
1025    }
1026
1027    #[test]
1028    #[cfg(cares1_29)]
1029    fn options_set_server_failover_options() {
1030        let mut options = Options::new();
1031        let failover_opts = c_ares::ServerFailoverOptions::new();
1032        let result = options.set_server_failover_options(&failover_opts);
1033        assert!(std::ptr::eq(result, &raw const options));
1034    }
1035
1036    #[test]
1037    #[cfg(cares1_27)]
1038    fn resolver_queue_active_queries() {
1039        let resolver = Resolver::new().unwrap();
1040        assert_eq!(resolver.queue_active_queries(), 0);
1041    }
1042
1043    #[test]
1044    #[cfg(cares1_27)]
1045    fn resolver_queue_wait_empty() {
1046        let resolver = Resolver::new().unwrap();
1047        let result = resolver.queue_wait_empty(Some(std::time::Duration::ZERO));
1048        assert!(result.is_ok() || result == Err(c_ares::Error::ENOTIMP));
1049    }
1050
1051    #[test]
1052    #[cfg(cares1_27)]
1053    fn resolver_queue_wait_empty_none_timeout() {
1054        let resolver = Resolver::new().unwrap();
1055        // None means "wait indefinitely", but with no pending queries it returns immediately.
1056        let result = resolver.queue_wait_empty(None);
1057        assert!(result.is_ok() || result == Err(c_ares::Error::ENOTIMP));
1058    }
1059
1060    // Regression test: queue_wait_empty must not hold the channel Mutex while
1061    // blocking, otherwise the event loop thread cannot call process_fd and
1062    // queries can never complete.
1063    //
1064    // The query has a 1s timeout against a non-routable address. If the event
1065    // loop can make progress, the query times out at ~1s and queue_wait_empty
1066    // returns promptly. If deadlocked, the query timeout never fires.
1067    #[test]
1068    #[cfg(cares1_27)]
1069    fn resolver_queue_wait_empty_with_pending_query() {
1070        let mut options = Options::new();
1071        options
1072            .set_timeout(std::time::Duration::from_secs(1))
1073            .set_tries(1);
1074        let resolver = std::sync::Arc::new(Resolver::with_options(options).unwrap());
1075
1076        // Point at a non-routable address so the query stays pending until it times out.
1077        resolver.set_servers(["192.0.2.1"]).unwrap();
1078        resolver.query_a("example.com", |_| {});
1079
1080        let start = std::time::Instant::now();
1081        let _ = resolver.queue_wait_empty(Some(std::time::Duration::from_secs(5)));
1082        let elapsed = start.elapsed();
1083
1084        // Should complete at ~1s when the query times out, not at 5s.
1085        assert!(
1086            elapsed < std::time::Duration::from_secs(3),
1087            "queue_wait_empty took {elapsed:.2?}, expected ~1s: event loop likely deadlocked"
1088        );
1089    }
1090
1091    #[test]
1092    fn debug_resolver_options() {
1093        let options = Options::new();
1094        let debug = format!("{options:?}");
1095        assert!(debug.contains("Options"));
1096    }
1097
1098    #[test]
1099    fn debug_resolver() {
1100        let resolver = Resolver::new().unwrap();
1101        let debug = format!("{resolver:?}");
1102        assert!(debug.contains("Resolver"));
1103    }
1104}