Skip to main content

c_ares_resolver/
futureresolver.rs

1use std::fmt;
2use std::future::Future;
3use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
4use std::pin::Pin;
5use std::sync::Arc;
6use std::task::{Context, Poll};
7
8use crate::error::Error;
9use crate::host::HostResults;
10use crate::nameinfo::NameInfoResult;
11use crate::resolver::{Options, Resolver};
12
13#[cfg(cares1_29)]
14use c_ares::ServerStateFlags;
15
16/// The type of future returned by methods on the `FutureResolver`.
17#[must_use]
18pub struct CAresFuture<T> {
19    inner: futures_channel::oneshot::Receiver<c_ares::Result<T>>,
20    _resolver: Arc<Resolver>,
21}
22
23impl<T> fmt::Debug for CAresFuture<T> {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        f.debug_struct("CAresFuture").finish_non_exhaustive()
26    }
27}
28
29impl<T> CAresFuture<T> {
30    fn new(
31        promise: futures_channel::oneshot::Receiver<c_ares::Result<T>>,
32        resolver: Arc<Resolver>,
33    ) -> Self {
34        Self {
35            inner: promise,
36            _resolver: resolver,
37        }
38    }
39
40    fn pin_get_inner(
41        self: Pin<&mut Self>,
42    ) -> Pin<&mut futures_channel::oneshot::Receiver<c_ares::Result<T>>> {
43        unsafe { self.map_unchecked_mut(|s| &mut s.inner) }
44    }
45}
46
47impl<T> Future for CAresFuture<T> {
48    type Output = c_ares::Result<T>;
49
50    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
51        self.pin_get_inner()
52            .poll(cx)
53            .map(|result| result.unwrap_or(Err(c_ares::Error::ECANCELLED)))
54    }
55}
56
57/// An asynchronous DNS resolver, which returns results as `futures::Future`s.
58///
59/// Note that dropping the `FutureResolver` does *not* cause outstanding queries to fail - contrast
60/// the `Resolver` - because the returned futures hold a reference to the underlying resolver.
61///
62/// The `c-ares` library returns results via callbacks, and some of those callbacks receive
63/// borrowed data.  To return owned results, this resolver must clone or copy where the
64/// callback-based `Resolver` need not.
65///
66/// Therefore: if you are trying very hard to avoid unnecessary allocations - prefer the
67/// `Resolver`.
68pub struct FutureResolver {
69    inner: Arc<Resolver>,
70}
71
72impl fmt::Debug for FutureResolver {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        f.debug_struct("FutureResolver").finish_non_exhaustive()
75    }
76}
77
78// Most query implementations follow the same pattern: call through to the `Resolver`, arranging
79// that the callback completes a future.
80macro_rules! futurize {
81    ($resolver:expr, $query:ident, $($arg:expr),+ $(,)?) => {{
82        let (sender, receiver) = futures_channel::oneshot::channel();
83        $resolver.$query($($arg,)+ |result| {
84            let _ = sender.send(result);
85        });
86        let resolver = Arc::clone(&$resolver);
87        CAresFuture::new(receiver, resolver)
88    }};
89}
90
91impl FutureResolver {
92    /// Create a new `FutureResolver`, using default `Options`.
93    ///
94    /// # Examples
95    ///
96    /// ```no_run
97    /// let resolver = c_ares_resolver::FutureResolver::new().unwrap();
98    /// let future = resolver.query_a("example.com");
99    /// // await or spawn the future...
100    /// ```
101    pub fn new() -> Result<Self, Error> {
102        let options = Options::default();
103        Self::with_options(options)
104    }
105
106    /// Create a new `FutureResolver`, with the given `Options`.
107    pub fn with_options(options: Options) -> Result<Self, Error> {
108        let inner = Resolver::with_options(options)?;
109        let resolver = Self {
110            inner: Arc::new(inner),
111        };
112        Ok(resolver)
113    }
114
115    /// Reinitialize a channel from system configuration.
116    #[cfg(cares1_22)]
117    pub fn reinit(&self) -> c_ares::Result<&Self> {
118        self.inner.reinit()?;
119        Ok(self)
120    }
121
122    /// Set the list of servers to contact, instead of the servers specified in resolv.conf or the
123    /// local named.
124    ///
125    /// String format is `host[:port]`.  IPv6 addresses with ports require square brackets eg
126    /// `[2001:4860:4860::8888]:53`.
127    pub fn set_servers<I, S>(&self, servers: I) -> c_ares::Result<&Self>
128    where
129        I: IntoIterator<Item = S>,
130        S: AsRef<str>,
131    {
132        self.inner.set_servers(servers)?;
133        Ok(self)
134    }
135
136    /// Retrieves the list of configured servers.
137    ///
138    /// Each entry is in `host[:port]` format, matching what [`set_servers`](Self::set_servers)
139    /// accepts.
140    #[cfg(cares1_24)]
141    pub fn servers(&self) -> Vec<String> {
142        self.inner.servers()
143    }
144
145    /// Set the local IPv4 address from which to make queries.
146    pub fn set_local_ipv4(&self, ipv4: Ipv4Addr) -> &Self {
147        self.inner.set_local_ipv4(ipv4);
148        self
149    }
150
151    /// Set the local IPv6 address from which to make queries.
152    pub fn set_local_ipv6(&self, ipv6: Ipv6Addr) -> &Self {
153        self.inner.set_local_ipv6(ipv6);
154        self
155    }
156
157    /// Set the local device from which to make queries.
158    pub fn set_local_device(&self, device: &str) -> c_ares::Result<&Self> {
159        self.inner.set_local_device(device)?;
160        Ok(self)
161    }
162
163    /// Initializes an address sortlist configuration, so that addresses returned by
164    /// `get_host_by_name()` are sorted according to the sortlist.
165    ///
166    /// Each element of the sortlist holds an IP-address/netmask pair. The netmask is optional but
167    /// follows the address after a slash if present. For example: "130.155.160.0/255.255.240.0",
168    /// or "130.155.0.0".
169    pub fn set_sortlist<I, S>(&self, sortlist: I) -> c_ares::Result<&Self>
170    where
171        I: IntoIterator<Item = S>,
172        S: AsRef<str>,
173    {
174        self.inner.set_sortlist(sortlist)?;
175        Ok(self)
176    }
177
178    /// Set a callback function to be invoked whenever a query on the channel completes.
179    ///
180    /// `callback(server, success, flags)` will be called when a query completes.
181    ///
182    /// - `server` indicates the DNS server that was used for the query.
183    /// - `success` indicates whether the query succeeded or not.
184    /// - `flags` is a bitmask of flags describing various aspects of the query.
185    #[cfg(cares1_29)]
186    pub fn set_server_state_callback<F>(&self, callback: F) -> &Self
187    where
188        F: Fn(&str, bool, ServerStateFlags) + Send + Sync + 'static,
189    {
190        self.inner.set_server_state_callback(callback);
191        self
192    }
193
194    /// Look up the A records associated with `name`.
195    pub fn query_a(&self, name: &str) -> CAresFuture<c_ares::AResults> {
196        futurize!(self.inner, query_a, name)
197    }
198
199    /// Search for the A records associated with `name`.
200    pub fn search_a(&self, name: &str) -> CAresFuture<c_ares::AResults> {
201        futurize!(self.inner, search_a, name)
202    }
203
204    /// Look up the AAAA records associated with `name`.
205    pub fn query_aaaa(&self, name: &str) -> CAresFuture<c_ares::AAAAResults> {
206        futurize!(self.inner, query_aaaa, name)
207    }
208
209    /// Search for the AAAA records associated with `name`.
210    pub fn search_aaaa(&self, name: &str) -> CAresFuture<c_ares::AAAAResults> {
211        futurize!(self.inner, search_aaaa, name)
212    }
213
214    /// Look up the CAA records associated with `name`.
215    pub fn query_caa(&self, name: &str) -> CAresFuture<c_ares::CAAResults> {
216        futurize!(self.inner, query_caa, name)
217    }
218
219    /// Search for the CAA records associated with `name`.
220    pub fn search_caa(&self, name: &str) -> CAresFuture<c_ares::CAAResults> {
221        futurize!(self.inner, search_caa, name)
222    }
223
224    /// Look up the CNAME records associated with `name`.
225    pub fn query_cname(&self, name: &str) -> CAresFuture<c_ares::CNameResults> {
226        futurize!(self.inner, query_cname, name)
227    }
228
229    /// Search for the CNAME records associated with `name`.
230    pub fn search_cname(&self, name: &str) -> CAresFuture<c_ares::CNameResults> {
231        futurize!(self.inner, search_cname, name)
232    }
233
234    /// Look up the MX records associated with `name`.
235    pub fn query_mx(&self, name: &str) -> CAresFuture<c_ares::MXResults> {
236        futurize!(self.inner, query_mx, name)
237    }
238
239    /// Search for the MX records associated with `name`.
240    pub fn search_mx(&self, name: &str) -> CAresFuture<c_ares::MXResults> {
241        futurize!(self.inner, search_mx, name)
242    }
243
244    /// Look up the NAPTR records associated with `name`.
245    pub fn query_naptr(&self, name: &str) -> CAresFuture<c_ares::NAPTRResults> {
246        futurize!(self.inner, query_naptr, name)
247    }
248
249    /// Search for the NAPTR records associated with `name`.
250    pub fn search_naptr(&self, name: &str) -> CAresFuture<c_ares::NAPTRResults> {
251        futurize!(self.inner, search_naptr, name)
252    }
253
254    /// Look up the NS records associated with `name`.
255    pub fn query_ns(&self, name: &str) -> CAresFuture<c_ares::NSResults> {
256        futurize!(self.inner, query_ns, name)
257    }
258
259    /// Search for the NS records associated with `name`.
260    pub fn search_ns(&self, name: &str) -> CAresFuture<c_ares::NSResults> {
261        futurize!(self.inner, search_ns, name)
262    }
263
264    /// Look up the PTR records associated with `name`.
265    pub fn query_ptr(&self, name: &str) -> CAresFuture<c_ares::PTRResults> {
266        futurize!(self.inner, query_ptr, name)
267    }
268
269    /// Search for the PTR records associated with `name`.
270    pub fn search_ptr(&self, name: &str) -> CAresFuture<c_ares::PTRResults> {
271        futurize!(self.inner, search_ptr, name)
272    }
273
274    /// Look up the SOA records associated with `name`.
275    pub fn query_soa(&self, name: &str) -> CAresFuture<c_ares::SOAResult> {
276        futurize!(self.inner, query_soa, name)
277    }
278
279    /// Search for the SOA records associated with `name`.
280    pub fn search_soa(&self, name: &str) -> CAresFuture<c_ares::SOAResult> {
281        futurize!(self.inner, search_soa, name)
282    }
283
284    /// Look up the SRV records associated with `name`.
285    pub fn query_srv(&self, name: &str) -> CAresFuture<c_ares::SRVResults> {
286        futurize!(self.inner, query_srv, name)
287    }
288
289    /// Search for the SRV records associated with `name`.
290    pub fn search_srv(&self, name: &str) -> CAresFuture<c_ares::SRVResults> {
291        futurize!(self.inner, search_srv, name)
292    }
293
294    /// Look up the TXT records associated with `name`.
295    pub fn query_txt(&self, name: &str) -> CAresFuture<c_ares::TXTResults> {
296        futurize!(self.inner, query_txt, name)
297    }
298
299    /// Search for the TXT records associated with `name`.
300    pub fn search_txt(&self, name: &str) -> CAresFuture<c_ares::TXTResults> {
301        futurize!(self.inner, search_txt, name)
302    }
303
304    /// Look up the URI records associated with `name`.
305    pub fn query_uri(&self, name: &str) -> CAresFuture<c_ares::URIResults> {
306        futurize!(self.inner, query_uri, name)
307    }
308
309    /// Search for the URI records associated with `name`.
310    pub fn search_uri(&self, name: &str) -> CAresFuture<c_ares::URIResults> {
311        futurize!(self.inner, search_uri, name)
312    }
313
314    /// Perform a host query by address.
315    pub fn get_host_by_address(&self, address: &IpAddr) -> CAresFuture<HostResults> {
316        let (sender, receiver) = futures_channel::oneshot::channel();
317        self.inner.get_host_by_address(address, |result| {
318            let _ = sender.send(result.map(Into::into));
319        });
320        let resolver = Arc::clone(&self.inner);
321        CAresFuture::new(receiver, resolver)
322    }
323
324    /// Perform a host query by name.
325    pub fn get_host_by_name(
326        &self,
327        name: &str,
328        family: c_ares::AddressFamily,
329    ) -> CAresFuture<HostResults> {
330        let (sender, receiver) = futures_channel::oneshot::channel();
331        self.inner.get_host_by_name(name, family, |result| {
332            let _ = sender.send(result.map(Into::into));
333        });
334        let resolver = Arc::clone(&self.inner);
335        CAresFuture::new(receiver, resolver)
336    }
337
338    /// Address-to-nodename translation in protocol-independent manner.
339    pub fn get_name_info(
340        &self,
341        address: &SocketAddr,
342        flags: c_ares::NIFlags,
343    ) -> CAresFuture<NameInfoResult> {
344        let (sender, receiver) = futures_channel::oneshot::channel();
345        self.inner.get_name_info(address, flags, |result| {
346            let _ = sender.send(result.map(Into::into));
347        });
348        let resolver = Arc::clone(&self.inner);
349        CAresFuture::new(receiver, resolver)
350    }
351
352    /// Initiate a host query by name and service.
353    pub fn get_addrinfo(
354        &self,
355        name: &str,
356        service: Option<&str>,
357        hints: &c_ares::AddrInfoHints,
358    ) -> CAresFuture<c_ares::AddrInfoResults> {
359        futurize!(self.inner, get_addrinfo, name, service, hints)
360    }
361
362    /// Initiate a single-question DNS query for `name`.  The class and type of the query are per
363    /// the provided parameters, taking values as defined in `arpa/nameser.h`.
364    ///
365    /// This method is provided so that users can query DNS types for which `c-ares` does not
366    /// provide a parser; or in case a third-party parser is preferred.  Usually, if a suitable
367    /// `query_xxx()` is available, that should be used.
368    pub fn query(&self, name: &str, dns_class: u16, query_type: u16) -> CAresFuture<Vec<u8>> {
369        let (sender, receiver) = futures_channel::oneshot::channel();
370        self.inner.query(name, dns_class, query_type, |result| {
371            let _ = sender.send(result.map(std::borrow::ToOwned::to_owned));
372        });
373        let resolver = Arc::clone(&self.inner);
374        CAresFuture::new(receiver, resolver)
375    }
376
377    /// Initiate a series of single-question DNS queries for `name`.  The class and type of the
378    /// query are per the provided parameters, taking values as defined in `arpa/nameser.h`.
379    ///
380    /// This method is provided so that users can search DNS types for which `c-ares` does not
381    /// provide a parser; or in case a third-party parser is preferred.  Usually, if a suitable
382    /// `search_xxx()` is available, that should be used.
383    pub fn search(&self, name: &str, dns_class: u16, query_type: u16) -> CAresFuture<Vec<u8>> {
384        let (sender, receiver) = futures_channel::oneshot::channel();
385        self.inner.search(name, dns_class, query_type, |result| {
386            let _ = sender.send(result.map(std::borrow::ToOwned::to_owned));
387        });
388        let resolver = Arc::clone(&self.inner);
389        CAresFuture::new(receiver, resolver)
390    }
391
392    /// Send a DNS query using a pre-built [`c_ares::DnsRecord`].
393    ///
394    /// Returns a tuple of `(query_id, future)`.
395    ///
396    /// # Examples
397    ///
398    /// ```no_run
399    /// use c_ares::*;
400    /// use futures_executor::block_on;
401    ///
402    /// let resolver = c_ares_resolver::FutureResolver::new().unwrap();
403    /// let mut query = DnsRecord::new(0, DnsFlags::RD, DnsOpcode::Query, DnsRcode::NoError).unwrap();
404    /// query.query_add("example.com", DnsRecordType::A, DnsCls::IN).unwrap();
405    /// let response = block_on(resolver.send_dnsrec(&query).unwrap()).unwrap();
406    /// for rr in response.rrs(DnsSection::Answer) {
407    ///     if let Some(addr) = rr.get_addr(DnsRrKey::A_ADDR) {
408    ///         println!("address: {addr}");
409    ///     }
410    /// }
411    /// ```
412    #[cfg(cares1_28)]
413    pub fn send_dnsrec(
414        &self,
415        dnsrec: &c_ares::DnsRecord,
416    ) -> c_ares::Result<CAresFuture<c_ares::DnsRecord>> {
417        let (sender, receiver) = futures_channel::oneshot::channel();
418        self.inner.send_dnsrec(dnsrec, |result| {
419            let _ = sender.send(result.and_then(c_ares::DnsRecord::try_clone));
420        })?;
421        let resolver = Arc::clone(&self.inner);
422        Ok(CAresFuture::new(receiver, resolver))
423    }
424
425    /// Initiate a DNS query for `name` with the given class and type, receiving a parsed
426    /// [`c_ares::DnsRecord`].
427    ///
428    /// # Examples
429    ///
430    /// ```no_run
431    /// use c_ares::{DnsCls, DnsRecordType, DnsRrKey, DnsSection};
432    /// use futures_executor::block_on;
433    ///
434    /// let resolver = c_ares_resolver::FutureResolver::new().unwrap();
435    /// let future = resolver
436    ///     .query_dnsrec("example.com", DnsCls::IN, DnsRecordType::A)
437    ///     .unwrap();
438    /// let record = block_on(future).unwrap();
439    /// for rr in record.rrs(DnsSection::Answer) {
440    ///     if let Some(addr) = rr.get_addr(DnsRrKey::A_ADDR) {
441    ///         println!("address: {addr}");
442    ///     }
443    /// }
444    /// ```
445    #[cfg(cares1_28)]
446    pub fn query_dnsrec(
447        &self,
448        name: &str,
449        dns_class: c_ares::DnsCls,
450        query_type: c_ares::DnsRecordType,
451    ) -> c_ares::Result<CAresFuture<c_ares::DnsRecord>> {
452        let (sender, receiver) = futures_channel::oneshot::channel();
453        self.inner
454            .query_dnsrec(name, dns_class, query_type, |result| {
455                let _ = sender.send(result.and_then(c_ares::DnsRecord::try_clone));
456            })?;
457        let resolver = Arc::clone(&self.inner);
458        Ok(CAresFuture::new(receiver, resolver))
459    }
460
461    /// Initiate a series of DNS queries using a pre-built [`c_ares::DnsRecord`], receiving a
462    /// parsed [`c_ares::DnsRecord`].
463    ///
464    /// # Examples
465    ///
466    /// ```no_run
467    /// use c_ares::*;
468    /// use futures_executor::block_on;
469    ///
470    /// let resolver = c_ares_resolver::FutureResolver::new().unwrap();
471    /// let mut query = DnsRecord::new(0, DnsFlags::RD, DnsOpcode::Query, DnsRcode::NoError).unwrap();
472    /// query.query_add("example.com", DnsRecordType::A, DnsCls::IN).unwrap();
473    /// let response = block_on(resolver.search_dnsrec(&query).unwrap()).unwrap();
474    /// for rr in response.rrs(DnsSection::Answer) {
475    ///     if let Some(addr) = rr.get_addr(DnsRrKey::A_ADDR) {
476    ///         println!("address: {addr}");
477    ///     }
478    /// }
479    /// ```
480    #[cfg(cares1_28)]
481    pub fn search_dnsrec(
482        &self,
483        dnsrec: &c_ares::DnsRecord,
484    ) -> c_ares::Result<CAresFuture<c_ares::DnsRecord>> {
485        let (sender, receiver) = futures_channel::oneshot::channel();
486        self.inner.search_dnsrec(dnsrec, |result| {
487            let _ = sender.send(result.and_then(c_ares::DnsRecord::try_clone));
488        })?;
489        let resolver = Arc::clone(&self.inner);
490        Ok(CAresFuture::new(receiver, resolver))
491    }
492
493    /// Block until notified that there are no longer any queries in queue, or the specified
494    /// timeout has expired.
495    ///
496    /// Pass `None` to wait indefinitely.
497    #[cfg(cares1_27)]
498    pub fn queue_wait_empty(&self, timeout: Option<std::time::Duration>) -> c_ares::Result<()> {
499        self.inner.queue_wait_empty(timeout)
500    }
501
502    /// Retrieve the total number of active queries pending answers from servers.
503    #[cfg(cares1_27)]
504    pub fn queue_active_queries(&self) -> usize {
505        self.inner.queue_active_queries()
506    }
507
508    /// Cancel all requests made on this `FutureResolver`.
509    pub fn cancel(&self) {
510        self.inner.cancel();
511    }
512}
513
514#[cfg(test)]
515mod tests {
516    use super::*;
517    use std::time::Duration;
518
519    fn assert_send<T: Send>() {}
520    fn assert_sync<T: Sync>() {}
521
522    #[test]
523    fn future_resolver_is_send() {
524        assert_send::<FutureResolver>();
525    }
526
527    #[test]
528    fn future_resolver_is_sync() {
529        assert_sync::<FutureResolver>();
530    }
531
532    #[test]
533    fn c_ares_future_is_send() {
534        assert_send::<CAresFuture<c_ares::AResults>>();
535    }
536
537    #[test]
538    fn c_ares_future_is_sync() {
539        assert_sync::<CAresFuture<c_ares::AResults>>();
540    }
541
542    #[test]
543    fn future_resolver_new() {
544        let resolver = FutureResolver::new();
545        assert!(resolver.is_ok());
546    }
547
548    #[test]
549    fn future_resolver_with_options() {
550        let options = Options::new();
551        let resolver = FutureResolver::with_options(options);
552        assert!(resolver.is_ok());
553    }
554
555    #[test]
556    fn future_resolver_with_custom_options() {
557        let mut options = Options::new();
558        options.set_timeout(Duration::from_secs(2)).set_tries(2);
559        let resolver = FutureResolver::with_options(options);
560        assert!(resolver.is_ok());
561    }
562
563    #[test]
564    fn future_resolver_set_local_ipv4() {
565        let resolver = FutureResolver::new().unwrap();
566        let result = resolver.set_local_ipv4(Ipv4Addr::LOCALHOST);
567        assert!(std::ptr::eq(result, &raw const resolver));
568    }
569
570    #[test]
571    fn future_resolver_set_local_ipv6() {
572        let resolver = FutureResolver::new().unwrap();
573        let ipv6 = Ipv6Addr::LOCALHOST;
574        let result = resolver.set_local_ipv6(ipv6);
575        assert!(std::ptr::eq(result, &raw const resolver));
576    }
577
578    #[test]
579    fn future_resolver_set_local_device() {
580        let resolver = FutureResolver::new().unwrap();
581        let result = resolver.set_local_device("lo").unwrap();
582        assert!(std::ptr::eq(result, &raw const resolver));
583    }
584
585    #[test]
586    fn future_resolver_set_servers_valid() {
587        let resolver = FutureResolver::new().unwrap();
588        let result = resolver.set_servers(["8.8.8.8", "8.8.4.4"]);
589        assert!(result.is_ok());
590    }
591
592    #[test]
593    fn future_resolver_set_sortlist_valid() {
594        let resolver = FutureResolver::new().unwrap();
595        let result = resolver.set_sortlist(["130.155.160.0/255.255.240.0"]);
596        assert!(result.is_ok());
597    }
598
599    #[test]
600    fn future_resolver_cancel() {
601        let resolver = FutureResolver::new().unwrap();
602        resolver.cancel(); // Should not panic
603    }
604
605    #[test]
606    #[cfg(cares1_22)]
607    fn future_resolver_reinit() {
608        let resolver = FutureResolver::new().unwrap();
609        let result = resolver.reinit();
610        assert!(result.is_ok());
611    }
612
613    #[test]
614    #[cfg(cares1_24)]
615    fn future_resolver_servers() {
616        let resolver = FutureResolver::new().unwrap();
617        let _ = resolver.set_servers(["8.8.8.8"]);
618        let servers = resolver.servers();
619        assert!(!servers.is_empty());
620    }
621
622    #[test]
623    #[cfg(cares1_27)]
624    fn future_resolver_queue_active_queries() {
625        let resolver = FutureResolver::new().unwrap();
626        assert_eq!(resolver.queue_active_queries(), 0);
627    }
628
629    #[test]
630    #[cfg(cares1_27)]
631    fn future_resolver_queue_wait_empty() {
632        let resolver = FutureResolver::new().unwrap();
633        let result = resolver.queue_wait_empty(Some(std::time::Duration::ZERO));
634        assert!(result.is_ok() || result == Err(c_ares::Error::ENOTIMP));
635    }
636
637    #[test]
638    #[cfg(cares1_29)]
639    fn future_resolver_set_server_state_callback() {
640        let resolver = FutureResolver::new().unwrap();
641        let result = resolver.set_server_state_callback(|_server, _success, _flags| {});
642        assert!(std::ptr::eq(result, &raw const resolver));
643    }
644
645    #[test]
646    fn debug_future_resolver() {
647        let resolver = FutureResolver::new().unwrap();
648        let debug = format!("{resolver:?}");
649        assert!(debug.contains("FutureResolver"));
650    }
651
652    #[test]
653    fn debug_cares_future() {
654        let resolver = FutureResolver::new().unwrap();
655        let future = resolver.query_a("example.com");
656        let debug = format!("{future:?}");
657        assert!(debug.contains("CAresFuture"));
658    }
659}