Skip to main content

c_ares/channel/
mod.rs

1#[allow(unused_imports)]
2use core::ffi::{c_char, c_int, c_void};
3use std::ffi::CString;
4use std::fmt;
5use std::mem;
6use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
7use std::ptr;
8use std::sync::Arc;
9use std::time::Duration;
10
11mod options;
12mod sockets;
13
14pub use options::Options;
15#[cfg(cares1_29)]
16pub use options::ServerFailoverOptions;
17pub use sockets::{Sockets, SocketsIter};
18
19use options::SocketStateCallback;
20
21#[cfg(cares1_29)]
22use crate::ServerStateFlags;
23use crate::a::AResults;
24use crate::aaaa::AAAAResults;
25use crate::addrinfo::{AddrInfoHints, AddrInfoResults, get_addrinfo_callback};
26use crate::caa::CAAResults;
27use crate::cname::CNameResults;
28#[cfg(cares1_28)]
29use crate::dns::callback::dnsrec_callback;
30#[cfg(cares1_28)]
31use crate::dns::{DnsCls, DnsRecord, DnsRecordType};
32use crate::error::{Error, Result};
33#[cfg(cares1_34)]
34use crate::events::{FdEvents, ProcessFlags};
35use crate::host::HostResults;
36use crate::host::get_host_callback;
37use crate::mx::MXResults;
38use crate::nameinfo::{NameInfoResult, get_name_info_callback};
39use crate::naptr::NAPTRResults;
40use crate::ni_flags::NIFlags;
41use crate::ns::NSResults;
42use crate::panic;
43use crate::ptr::PTRResults;
44use crate::query::{query_callback, raw_query_callback};
45use crate::record::QueryRecord;
46use crate::soa::SOAResult;
47use crate::srv::SRVResults;
48#[cfg(cares1_24)]
49use crate::string::AresString;
50use crate::txt::TXTResults;
51use crate::types::{AddressFamily, DnsClass, Socket};
52use crate::uri::URIResults;
53#[allow(unused_imports)]
54use crate::utils::{
55    c_string_as_str_unchecked, ipv4_as_in_addr, ipv6_as_in6_addr, socket_addrv4_as_sockaddr_in,
56    socket_addrv6_as_sockaddr_in6, status_to_result,
57};
58use std::sync::Mutex;
59
60// ares_library_init is not thread-safe, so we put a lock around it.
61static ARES_LIBRARY_LOCK: Mutex<()> = Mutex::new(());
62
63#[cfg(cares1_29)]
64type ServerStateCallback = dyn Fn(&str, bool, ServerStateFlags) + Send + Sync + 'static;
65
66#[cfg(cares1_34)]
67type PendingWriteCallback = dyn Fn() + Send + Sync + 'static;
68
69/// A channel for name service lookups.
70pub struct Channel {
71    // The `ares_` prefix mirrors the FFI type name; the apparent name overlap
72    // with the struct is deliberate.
73    #[allow(clippy::struct_field_names)]
74    ares_channel: c_ares_sys::ares_channel,
75
76    // For ownership only.
77    socket_state_callback: Option<Arc<SocketStateCallback>>,
78
79    // For ownership only.
80    #[cfg(cares1_29)]
81    server_state_callback: Option<Arc<ServerStateCallback>>,
82
83    // For ownership only.
84    #[cfg(cares1_34)]
85    pending_write_callback: Option<Arc<PendingWriteCallback>>,
86}
87
88impl Channel {
89    /// Returns the raw `ares_channel` pointer.
90    ///
91    /// # Safety
92    ///
93    /// The returned pointer is only valid for the lifetime of this `Channel`.
94    /// Callers must ensure that any use of the pointer is compatible with
95    /// concurrent access (e.g. only call thread-safe c-ares functions).
96    pub fn as_raw(&self) -> c_ares_sys::ares_channel {
97        self.ares_channel
98    }
99
100    /// Create a new channel for name service lookups, with default `Options`.
101    ///
102    /// # Examples
103    ///
104    /// ```
105    /// let channel = c_ares::Channel::new().unwrap();
106    /// ```
107    pub fn new() -> Result<Self> {
108        let options = Options::default();
109        Self::with_options(options)
110    }
111
112    /// Create a new channel for name service lookups, with the given `Options`.
113    ///
114    /// # Examples
115    ///
116    /// ```
117    /// let mut options = c_ares::Options::new();
118    /// options.set_flags(c_ares::Flags::STAYOPEN)
119    ///        .set_tries(2);
120    /// let channel = c_ares::Channel::with_options(options).unwrap();
121    /// ```
122    pub fn with_options(mut options: Options) -> Result<Channel> {
123        // Initialize the library.
124        let lib_rc = {
125            let _lock = ARES_LIBRARY_LOCK.lock().unwrap();
126            unsafe { c_ares_sys::ares_library_init(c_ares_sys::ARES_LIB_INIT_ALL) }
127        };
128        if lib_rc != c_ares_sys::ares_status_t::ARES_SUCCESS as i32 {
129            return Err(Error::from(lib_rc));
130        }
131
132        // We deferred setting up domains in the options - do it now.
133        let domains: Vec<_> = options.domains.iter().map(|s| s.as_ptr()).collect();
134        options.ares_options.domains = domains.as_ptr().cast_mut().cast();
135        options.ares_options.ndomains = domains.len() as i32;
136
137        // Likewise for lookups.
138        if let Some(c_lookup) = &options.lookups {
139            options.ares_options.lookups = c_lookup.as_ptr().cast_mut();
140        }
141
142        // And the resolvconf_path.
143        if let Some(c_resolvconf_path) = &options.resolvconf_path {
144            options.ares_options.resolvconf_path = c_resolvconf_path.as_ptr().cast_mut();
145        }
146
147        // And the hosts_path.
148        #[cfg(cares1_19)]
149        if let Some(c_hosts_path) = &options.hosts_path {
150            options.ares_options.hosts_path = c_hosts_path.as_ptr().cast_mut();
151        }
152
153        // Initialize the channel.
154        let mut ares_channel = ptr::null_mut();
155        let channel_rc = unsafe {
156            c_ares_sys::ares_init_options(
157                &raw mut ares_channel,
158                &raw const options.ares_options,
159                options.optmask,
160            )
161        };
162        if channel_rc != c_ares_sys::ares_status_t::ARES_SUCCESS as i32 {
163            let _lock = ARES_LIBRARY_LOCK.lock().unwrap();
164            unsafe { c_ares_sys::ares_library_cleanup() }
165            return Err(Error::from(channel_rc));
166        }
167
168        let channel = Channel {
169            ares_channel,
170            socket_state_callback: options.socket_state_callback,
171            #[cfg(cares1_29)]
172            server_state_callback: None,
173            #[cfg(cares1_34)]
174            pending_write_callback: None,
175        };
176        Ok(channel)
177    }
178
179    /// Reinitialize a channel from system configuration.
180    #[cfg(cares1_22)]
181    pub fn reinit(&mut self) -> Result<&mut Self> {
182        let rc = unsafe { c_ares_sys::ares_reinit(self.ares_channel) };
183        status_to_result(rc)?;
184        Ok(self)
185    }
186
187    /// Duplicate a channel.
188    pub fn try_clone(&self) -> Result<Channel> {
189        // Balance the ares_library_cleanup() that will run when the clone is dropped.
190        let lib_rc = {
191            let _lock = ARES_LIBRARY_LOCK.lock().unwrap();
192            unsafe { c_ares_sys::ares_library_init(c_ares_sys::ARES_LIB_INIT_ALL) }
193        };
194        if lib_rc != c_ares_sys::ares_status_t::ARES_SUCCESS as i32 {
195            return Err(Error::from(lib_rc));
196        }
197
198        let mut ares_channel = ptr::null_mut();
199        let rc = unsafe { c_ares_sys::ares_dup(&raw mut ares_channel, self.ares_channel) };
200        if rc != c_ares_sys::ares_status_t::ARES_SUCCESS as i32 {
201            let _lock = ARES_LIBRARY_LOCK.lock().unwrap();
202            unsafe { c_ares_sys::ares_library_cleanup() }
203            return Err(Error::from(rc));
204        }
205
206        let socket_state_callback = self.socket_state_callback.clone();
207
208        #[cfg(cares1_29)]
209        let server_state_callback = self.server_state_callback.clone();
210
211        #[cfg(cares1_34)]
212        let pending_write_callback = self.pending_write_callback.clone();
213
214        let channel = Channel {
215            ares_channel,
216            socket_state_callback,
217            #[cfg(cares1_29)]
218            server_state_callback,
219            #[cfg(cares1_34)]
220            pending_write_callback,
221        };
222        Ok(channel)
223    }
224
225    /// Handle input, output, and timeout events associated with the specified file descriptors
226    /// (sockets).
227    ///
228    /// Providing a value for `read_fd` indicates that the identified socket is readable; likewise
229    /// providing a value for `write_fd` indicates that the identified socket is writable.  Use
230    /// `None` for "no action".
231    pub fn process_fd(&mut self, read_fd: Option<Socket>, write_fd: Option<Socket>) {
232        let rfd = read_fd.unwrap_or(c_ares_sys::ARES_SOCKET_BAD);
233        let wfd = write_fd.unwrap_or(c_ares_sys::ARES_SOCKET_BAD);
234        unsafe { c_ares_sys::ares_process_fd(self.ares_channel, rfd, wfd) }
235    }
236
237    /// Handle input and output events associated with the specified file descriptors (sockets).
238    /// Also handles timeouts associated with the `Channel`.
239    pub fn process(&mut self, read_fds: &mut c_types::fd_set, write_fds: &mut c_types::fd_set) {
240        unsafe { c_ares_sys::ares_process(self.ares_channel, read_fds, write_fds) }
241    }
242
243    /// Process events on multiple file descriptors based on the event mask associated with each
244    /// file descriptor.  Recommended over calling `process_fd()` multiple times since it would
245    /// trigger additional logic such as timeout processing on each call.
246    #[cfg(cares1_34)]
247    pub fn process_fds(&mut self, events: &[FdEvents], flags: ProcessFlags) -> Result<()> {
248        let rc = unsafe {
249            c_ares_sys::ares_process_fds(
250                self.ares_channel,
251                events.as_ptr().cast(),
252                events.len(),
253                flags.bits(),
254            )
255        };
256        status_to_result(rc)
257    }
258
259    /// Retrieve the set of socket descriptors which the calling application should wait on for
260    /// reading and / or writing.
261    ///
262    /// # Examples
263    ///
264    /// ```
265    /// let channel = c_ares::Channel::new().unwrap();
266    /// for (socket, readable, writable) in &channel.sockets() {
267    ///     println!("socket {socket}: read={readable}, write={writable}");
268    /// }
269    /// ```
270    pub fn sockets(&self) -> Sockets {
271        let mut socks = [0; c_ares_sys::ARES_GETSOCK_MAXNUM];
272        let bitmask = unsafe {
273            c_ares_sys::ares_getsock(
274                self.ares_channel,
275                socks.as_mut_ptr(),
276                c_ares_sys::ARES_GETSOCK_MAXNUM as i32,
277            )
278        };
279        Sockets::new(socks, bitmask as u32)
280    }
281
282    /// Retrieve the set of socket descriptors which the calling application should wait on for
283    /// reading and / or writing.
284    pub fn fds(&self, read_fds: &mut c_types::fd_set, write_fds: &mut c_types::fd_set) -> u32 {
285        unsafe { c_ares_sys::ares_fds(self.ares_channel, read_fds, write_fds) as u32 }
286    }
287
288    /// Set the list of servers to contact, instead of the servers specified in resolv.conf or the
289    /// local named.
290    ///
291    /// String format is `host[:port]`.  IPv6 addresses with ports require square brackets eg
292    /// `[2001:4860:4860::8888]:53`.
293    ///
294    /// # Examples
295    ///
296    /// ```
297    /// let mut channel = c_ares::Channel::new().unwrap();
298    /// channel.set_servers(&["8.8.8.8", "8.8.4.4:53"]).unwrap();
299    /// ```
300    pub fn set_servers<I, S>(&mut self, servers: I) -> Result<&mut Self>
301    where
302        I: IntoIterator<Item = S>,
303        S: AsRef<str>,
304    {
305        let mut servers_csv = String::new();
306        for (i, s) in servers.into_iter().enumerate() {
307            if i > 0 {
308                servers_csv.push(',');
309            }
310            servers_csv.push_str(s.as_ref());
311        }
312        let c_servers = CString::new(servers_csv).map_err(|_| Error::EBADSTR)?;
313        let ares_rc = unsafe {
314            c_ares_sys::ares_set_servers_ports_csv(self.ares_channel, c_servers.as_ptr())
315        };
316        if ares_rc == c_ares_sys::ares_status_t::ARES_SUCCESS as i32 {
317            Ok(self)
318        } else {
319            Err(Error::from(ares_rc))
320        }
321    }
322
323    /// Retrieves the list of configured servers.
324    ///
325    /// Each entry is in `host[:port]` format, matching what [`set_servers`](Self::set_servers)
326    /// accepts.
327    #[cfg(cares1_24)]
328    pub fn servers(&self) -> Vec<String> {
329        let csv = unsafe { c_ares_sys::ares_get_servers_csv(self.ares_channel) };
330        Self::parse_servers_csv(csv)
331    }
332
333    // Parse the result of `ares_get_servers_csv()` into a list of servers,
334    // taking ownership of (and freeing) the c-ares-allocated string.
335    #[cfg(cares1_24)]
336    fn parse_servers_csv(csv: *mut c_char) -> Vec<String> {
337        // `ares_get_servers_csv()` returns NULL on allocation failure.
338        if csv.is_null() {
339            return Vec::new();
340        }
341        let csv = AresString::new(csv);
342        let csv: &str = &csv;
343        if csv.is_empty() {
344            Vec::new()
345        } else {
346            csv.split(',').map(String::from).collect()
347        }
348    }
349
350    /// Set the local IPv4 address from which to make queries.
351    pub fn set_local_ipv4(&mut self, ipv4: Ipv4Addr) -> &mut Self {
352        unsafe { c_ares_sys::ares_set_local_ip4(self.ares_channel, u32::from(ipv4)) }
353        self
354    }
355
356    /// Set the local IPv6 address from which to make queries.
357    pub fn set_local_ipv6(&mut self, ipv6: Ipv6Addr) -> &mut Self {
358        let in6_addr = ipv6_as_in6_addr(&ipv6);
359        unsafe {
360            c_ares_sys::ares_set_local_ip6(self.ares_channel, ptr::from_ref(&in6_addr).cast());
361        }
362        self
363    }
364
365    /// Set the local device from which to make queries.
366    pub fn set_local_device(&mut self, device: &str) -> Result<&mut Self> {
367        let c_dev = CString::new(device).map_err(|_| Error::EBADSTR)?;
368        unsafe { c_ares_sys::ares_set_local_dev(self.ares_channel, c_dev.as_ptr()) }
369        Ok(self)
370    }
371
372    /// Initializes an address sortlist configuration, so that addresses returned by
373    /// `get_host_by_name()` are sorted according to the sortlist.
374    ///
375    /// Each element of the sortlist holds an IP-address/netmask pair. The netmask is optional but
376    /// follows the address after a slash if present. For example: "130.155.160.0/255.255.240.0",
377    /// or "130.155.0.0".
378    pub fn set_sortlist<I, S>(&mut self, sortlist: I) -> Result<&mut Self>
379    where
380        I: IntoIterator<Item = S>,
381        S: AsRef<str>,
382    {
383        let mut sortlist_str = String::new();
384        for (i, s) in sortlist.into_iter().enumerate() {
385            if i > 0 {
386                sortlist_str.push(' ');
387            }
388            sortlist_str.push_str(s.as_ref());
389        }
390        let c_sortlist = CString::new(sortlist_str).map_err(|_| Error::EBADSTR)?;
391        let ares_rc =
392            unsafe { c_ares_sys::ares_set_sortlist(self.ares_channel, c_sortlist.as_ptr()) };
393        if ares_rc == c_ares_sys::ares_status_t::ARES_SUCCESS as i32 {
394            Ok(self)
395        } else {
396            Err(Error::from(ares_rc))
397        }
398    }
399
400    /// Set a callback function to be invoked whenever a query on the channel completes.
401    ///
402    /// `callback(server, success, flags)` will be called when a query completes.
403    ///
404    /// - `server` indicates the DNS server that was used for the query.
405    /// - `success` indicates whether the query succeeded or not.
406    /// - `flags` is a bitmask of flags describing various aspects of the query.
407    #[cfg(cares1_29)]
408    pub fn set_server_state_callback<F>(&mut self, callback: F) -> &mut Self
409    where
410        F: Fn(&str, bool, ServerStateFlags) + Send + Sync + 'static,
411    {
412        let boxed_callback = Arc::new(callback);
413        let data = Arc::as_ptr(&boxed_callback).cast_mut().cast();
414        unsafe {
415            c_ares_sys::ares_set_server_state_callback(
416                self.ares_channel,
417                Some(server_state_callback::<F>),
418                data,
419            );
420        }
421        self.server_state_callback = Some(boxed_callback);
422        self
423    }
424
425    /// Set a callback function to be invoked when there is potential pending data
426    /// which needs to be written.
427    #[cfg(cares1_34)]
428    pub fn set_pending_write_callback<F>(&mut self, callback: F) -> &mut Self
429    where
430        F: Fn() + Send + Sync + 'static,
431    {
432        let boxed_callback = Arc::new(callback);
433        let data = Arc::as_ptr(&boxed_callback).cast_mut().cast();
434        unsafe {
435            c_ares_sys::ares_set_pending_write_cb(
436                self.ares_channel,
437                Some(pending_write_callback::<F>),
438                data,
439            );
440        }
441        self.pending_write_callback = Some(boxed_callback);
442        self
443    }
444
445    /// Initiate a single-question DNS query for the A records associated with `name`.
446    ///
447    /// On completion, `handler` is called with the result.
448    pub fn query_a<F>(&mut self, name: &str, handler: F)
449    where
450        F: FnOnce(Result<AResults>) + Send + 'static,
451    {
452        self.do_query(name, handler);
453    }
454
455    /// Initiate a series of single-question DNS queries for the A records associated with `name`.
456    ///
457    /// On completion, `handler` is called with the result.
458    pub fn search_a<F>(&mut self, name: &str, handler: F)
459    where
460        F: FnOnce(Result<AResults>) + Send + 'static,
461    {
462        self.do_search(name, handler);
463    }
464
465    /// Initiate a single-question DNS query for the AAAA records associated with `name`.
466    ///
467    /// On completion, `handler` is called with the result.
468    pub fn query_aaaa<F>(&mut self, name: &str, handler: F)
469    where
470        F: FnOnce(Result<AAAAResults>) + Send + 'static,
471    {
472        self.do_query(name, handler);
473    }
474
475    /// Initiate a series of single-question DNS queries for the AAAA records associated with
476    /// `name`.
477    ///
478    /// On completion, `handler` is called with the result.
479    pub fn search_aaaa<F>(&mut self, name: &str, handler: F)
480    where
481        F: FnOnce(Result<AAAAResults>) + Send + 'static,
482    {
483        self.do_search(name, handler);
484    }
485
486    /// Initiate a single-question DNS query for the CAA records associated with `name`.
487    ///
488    /// On completion, `handler` is called with the result.
489    pub fn query_caa<F>(&mut self, name: &str, handler: F)
490    where
491        F: FnOnce(Result<CAAResults>) + Send + 'static,
492    {
493        self.do_query(name, handler);
494    }
495
496    /// Initiate a series of single-question DNS queries for the CAA records associated with
497    /// `name`.
498    ///
499    /// On completion, `handler` is called with the result.
500    pub fn search_caa<F>(&mut self, name: &str, handler: F)
501    where
502        F: FnOnce(Result<CAAResults>) + Send + 'static,
503    {
504        self.do_search(name, handler);
505    }
506
507    /// Initiate a single-question DNS query for the CNAME records associated with `name`.
508    ///
509    /// On completion, `handler` is called with the result.
510    pub fn query_cname<F>(&mut self, name: &str, handler: F)
511    where
512        F: FnOnce(Result<CNameResults>) + Send + 'static,
513    {
514        self.do_query(name, handler);
515    }
516
517    /// Initiate a series of single-question DNS queries for the CNAME records associated with
518    /// `name`.
519    ///
520    /// On completion, `handler` is called with the result.
521    pub fn search_cname<F>(&mut self, name: &str, handler: F)
522    where
523        F: FnOnce(Result<CNameResults>) + Send + 'static,
524    {
525        self.do_search(name, handler);
526    }
527
528    /// Initiate a single-question DNS query for the MX records associated with `name`.
529    ///
530    /// On completion, `handler` is called with the result.
531    pub fn query_mx<F>(&mut self, name: &str, handler: F)
532    where
533        F: FnOnce(Result<MXResults>) + Send + 'static,
534    {
535        self.do_query(name, handler);
536    }
537
538    /// Initiate a series of single-question DNS queries for the MX records associated with `name`.
539    ///
540    /// On completion, `handler` is called with the result.
541    pub fn search_mx<F>(&mut self, name: &str, handler: F)
542    where
543        F: FnOnce(Result<MXResults>) + Send + 'static,
544    {
545        self.do_search(name, handler);
546    }
547
548    /// Initiate a single-question DNS query for the NAPTR records associated with `name`.
549    ///
550    /// On completion, `handler` is called with the result.
551    pub fn query_naptr<F>(&mut self, name: &str, handler: F)
552    where
553        F: FnOnce(Result<NAPTRResults>) + Send + 'static,
554    {
555        self.do_query(name, handler);
556    }
557
558    /// Initiate a series of single-question DNS queries for the NAPTR records associated with
559    /// `name`.
560    ///
561    /// On completion, `handler` is called with the result.
562    pub fn search_naptr<F>(&mut self, name: &str, handler: F)
563    where
564        F: FnOnce(Result<NAPTRResults>) + Send + 'static,
565    {
566        self.do_search(name, handler);
567    }
568
569    /// Initiate a single-question DNS query for the NS records associated with `name`.
570    ///
571    /// On completion, `handler` is called with the result.
572    pub fn query_ns<F>(&mut self, name: &str, handler: F)
573    where
574        F: FnOnce(Result<NSResults>) + Send + 'static,
575    {
576        self.do_query(name, handler);
577    }
578
579    /// Initiate a series of single-question DNS queries for the NS records associated with `name`.
580    ///
581    /// On completion, `handler` is called with the result.
582    pub fn search_ns<F>(&mut self, name: &str, handler: F)
583    where
584        F: FnOnce(Result<NSResults>) + Send + 'static,
585    {
586        self.do_search(name, handler);
587    }
588
589    /// Initiate a single-question DNS query for the PTR records associated with `name`.
590    ///
591    /// On completion, `handler` is called with the result.
592    pub fn query_ptr<F>(&mut self, name: &str, handler: F)
593    where
594        F: FnOnce(Result<PTRResults>) + Send + 'static,
595    {
596        self.do_query(name, handler);
597    }
598
599    /// Initiate a series of single-question DNS queries for the PTR records associated with
600    /// `name`.
601    ///
602    /// On completion, `handler` is called with the result.
603    pub fn search_ptr<F>(&mut self, name: &str, handler: F)
604    where
605        F: FnOnce(Result<PTRResults>) + Send + 'static,
606    {
607        self.do_search(name, handler);
608    }
609
610    /// Initiate a single-question DNS query for the SOA records associated with `name`.
611    ///
612    /// On completion, `handler` is called with the result.
613    pub fn query_soa<F>(&mut self, name: &str, handler: F)
614    where
615        F: FnOnce(Result<SOAResult>) + Send + 'static,
616    {
617        self.do_query(name, handler);
618    }
619
620    /// Initiate a series of single-question DNS queries for the SOA records associated with
621    /// `name`.
622    ///
623    /// On completion, `handler` is called with the result.
624    pub fn search_soa<F>(&mut self, name: &str, handler: F)
625    where
626        F: FnOnce(Result<SOAResult>) + Send + 'static,
627    {
628        self.do_search(name, handler);
629    }
630
631    /// Initiate a single-question DNS query for the SRV records associated with `name`.
632    ///
633    /// On completion, `handler` is called with the result.
634    pub fn query_srv<F>(&mut self, name: &str, handler: F)
635    where
636        F: FnOnce(Result<SRVResults>) + Send + 'static,
637    {
638        self.do_query(name, handler);
639    }
640
641    /// Initiate a series of single-question DNS queries for the SRV records associated with
642    /// `name`.
643    ///
644    /// On completion, `handler` is called with the result.
645    pub fn search_srv<F>(&mut self, name: &str, handler: F)
646    where
647        F: FnOnce(Result<SRVResults>) + Send + 'static,
648    {
649        self.do_search(name, handler);
650    }
651
652    /// Initiate a single-question DNS query for the TXT records associated with `name`.
653    ///
654    /// On completion, `handler` is called with the result.
655    pub fn query_txt<F>(&mut self, name: &str, handler: F)
656    where
657        F: FnOnce(Result<TXTResults>) + Send + 'static,
658    {
659        self.do_query(name, handler);
660    }
661
662    /// Initiate a series of single-question DNS queries for the TXT records associated with
663    /// `name`.
664    ///
665    /// On completion, `handler` is called with the result.
666    pub fn search_txt<F>(&mut self, name: &str, handler: F)
667    where
668        F: FnOnce(Result<TXTResults>) + Send + 'static,
669    {
670        self.do_search(name, handler);
671    }
672
673    /// Initiate a single-question DNS query for the URI records associated with `name`.
674    ///
675    /// On completion, `handler` is called with the result.
676    pub fn query_uri<F>(&mut self, name: &str, handler: F)
677    where
678        F: FnOnce(Result<URIResults>) + Send + 'static,
679    {
680        self.do_query(name, handler);
681    }
682
683    /// Initiate a series of single-question DNS queries for the URI records associated with
684    /// `name`.
685    ///
686    /// On completion, `handler` is called with the result.
687    pub fn search_uri<F>(&mut self, name: &str, handler: F)
688    where
689        F: FnOnce(Result<URIResults>) + Send + 'static,
690    {
691        self.do_search(name, handler);
692    }
693
694    fn do_query<R, F>(&mut self, name: &str, handler: F)
695    where
696        R: QueryRecord,
697        F: FnOnce(Result<R>) + Send + 'static,
698    {
699        ares_query!(
700            self.ares_channel,
701            name,
702            DnsClass::IN,
703            R::QUERY_TYPE,
704            query_callback::<R, F>,
705            handler
706        );
707    }
708
709    fn do_search<R, F>(&mut self, name: &str, handler: F)
710    where
711        R: QueryRecord,
712        F: FnOnce(Result<R>) + Send + 'static,
713    {
714        ares_search!(
715            self.ares_channel,
716            name,
717            DnsClass::IN,
718            R::QUERY_TYPE,
719            query_callback::<R, F>,
720            handler
721        );
722    }
723
724    /// Perform a host query by address.
725    ///
726    /// On completion, `handler` is called with the result.
727    // `in_addr` and `in6_addr` mirror the C struct names; the apparent
728    // similarity is deliberate.
729    #[allow(clippy::similar_names)]
730    pub fn get_host_by_address<F>(&mut self, address: &IpAddr, handler: F)
731    where
732        F: FnOnce(Result<&HostResults>) + Send + 'static,
733    {
734        let in_addr: c_types::in_addr;
735        let in6_addr: c_types::in6_addr;
736        let c_addr = match *address {
737            IpAddr::V4(v4) => {
738                in_addr = ipv4_as_in_addr(v4);
739                ptr::from_ref(&in_addr).cast()
740            }
741            IpAddr::V6(ref v6) => {
742                in6_addr = ipv6_as_in6_addr(v6);
743                ptr::from_ref(&in6_addr).cast()
744            }
745        };
746        let (family, length) = match *address {
747            IpAddr::V4(_) => (AddressFamily::INET, mem::size_of::<c_types::in_addr>()),
748            IpAddr::V6(_) => (AddressFamily::INET6, mem::size_of::<c_types::in6_addr>()),
749        };
750        let c_arg = Box::into_raw(Box::new(handler));
751        unsafe {
752            c_ares_sys::ares_gethostbyaddr(
753                self.ares_channel,
754                c_addr,
755                length as i32,
756                family as i32,
757                Some(get_host_callback::<F>),
758                c_arg.cast(),
759            );
760        }
761    }
762
763    /// Perform a host query by name.
764    ///
765    /// On completion, `handler` is called with the result.
766    pub fn get_host_by_name<F>(&mut self, name: &str, family: AddressFamily, handler: F)
767    where
768        F: FnOnce(Result<&HostResults>) + Send + 'static,
769    {
770        let Ok(c_name) = CString::new(name) else {
771            handler(Err(Error::EBADNAME));
772            return;
773        };
774        let c_arg = Box::into_raw(Box::new(handler));
775        unsafe {
776            c_ares_sys::ares_gethostbyname(
777                self.ares_channel,
778                c_name.as_ptr(),
779                family as i32,
780                Some(get_host_callback::<F>),
781                c_arg.cast(),
782            );
783        }
784    }
785
786    /// Address-to-nodename translation in protocol-independent manner.
787    ///
788    /// The valid values for `flags` are documented [here](ni_flags/index.html).
789    ///
790    /// On completion, `handler` is called with the result.
791    pub fn get_name_info<F>(&mut self, address: &SocketAddr, flags: NIFlags, handler: F)
792    where
793        F: FnOnce(Result<NameInfoResult>) + Send + 'static,
794    {
795        let sockaddr_in: c_types::sockaddr_in;
796        let sockaddr_in6: c_types::sockaddr_in6;
797        let c_addr = match *address {
798            SocketAddr::V4(ref v4) => {
799                sockaddr_in = socket_addrv4_as_sockaddr_in(v4);
800                ptr::from_ref(&sockaddr_in).cast()
801            }
802            SocketAddr::V6(ref v6) => {
803                sockaddr_in6 = socket_addrv6_as_sockaddr_in6(v6);
804                ptr::from_ref(&sockaddr_in6).cast()
805            }
806        };
807        let length = match *address {
808            SocketAddr::V4(_) => mem::size_of::<c_types::sockaddr_in>(),
809            SocketAddr::V6(_) => mem::size_of::<c_types::sockaddr_in6>(),
810        };
811        let c_arg = Box::into_raw(Box::new(handler));
812        unsafe {
813            c_ares_sys::ares_getnameinfo(
814                self.ares_channel,
815                c_addr,
816                length as c_ares_sys::ares_socklen_t,
817                flags.bits(),
818                Some(get_name_info_callback::<F>),
819                c_arg.cast(),
820            );
821        }
822    }
823
824    /// Initiate a host query by name and service.
825    ///
826    /// The `hints` parameter controls the desired address family, socket type, protocol, and
827    /// behaviour flags.
828    ///
829    /// On completion, `handler` is called with the result.
830    pub fn get_addrinfo<F>(
831        &mut self,
832        name: &str,
833        service: Option<&str>,
834        hints: &AddrInfoHints,
835        handler: F,
836    ) where
837        F: FnOnce(Result<AddrInfoResults>) + Send + 'static,
838    {
839        let Ok(c_name) = CString::new(name) else {
840            handler(Err(Error::EBADNAME));
841            return;
842        };
843        let Ok(c_service) = service.map(CString::new).transpose() else {
844            handler(Err(Error::EBADNAME));
845            return;
846        };
847        let c_hints: c_ares_sys::ares_addrinfo_hints = hints.into();
848        let c_arg = Box::into_raw(Box::new(handler));
849        unsafe {
850            c_ares_sys::ares_getaddrinfo(
851                self.ares_channel,
852                c_name.as_ptr(),
853                c_service.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
854                &raw const c_hints,
855                Some(get_addrinfo_callback::<F>),
856                c_arg.cast(),
857            );
858        }
859    }
860
861    /// Initiate a single-question DNS query for `name`.  The class and type of the query are per
862    /// the provided parameters, taking values as defined in `arpa/nameser.h`.
863    ///
864    /// On completion, `handler` is called with the result.
865    ///
866    /// This method is provided so that users can query DNS types for which `c-ares` does not
867    /// provide a parser.  This is expected to be a last resort; if a suitable `query_xxx()` is
868    /// available, that should be preferred.
869    pub fn query<F>(&mut self, name: &str, dns_class: u16, query_type: u16, handler: F)
870    where
871        F: FnOnce(Result<&[u8]>) + Send + 'static,
872    {
873        ares_query!(
874            self.ares_channel,
875            name,
876            c_int::from(dns_class),
877            c_int::from(query_type),
878            raw_query_callback::<F>,
879            handler
880        );
881    }
882
883    /// Initiate a series of single-question DNS queries for `name`.  The class and type of the
884    /// query are per the provided parameters, taking values as defined in `arpa/nameser.h`.
885    ///
886    /// On completion, `handler` is called with the result.
887    ///
888    /// This method is provided so that users can search DNS types for which `c-ares` does not
889    /// provide a parser.  This is expected to be a last resort; if a suitable `search_xxx()` is
890    /// available, that should be preferred.
891    pub fn search<F>(&mut self, name: &str, dns_class: u16, query_type: u16, handler: F)
892    where
893        F: FnOnce(Result<&[u8]>) + Send + 'static,
894    {
895        ares_search!(
896            self.ares_channel,
897            name,
898            c_int::from(dns_class),
899            c_int::from(query_type),
900            raw_query_callback::<F>,
901            handler
902        );
903    }
904
905    /// Send a DNS query using a pre-built [`DnsRecord`].
906    ///
907    /// On completion, `handler` is called with a `Result<DnsRecord>` containing
908    /// the parsed response.
909    ///
910    /// Returns the query ID on success.
911    ///
912    /// # Examples
913    ///
914    /// ```no_run
915    /// use c_ares::*;
916    ///
917    /// let mut channel = Channel::new().unwrap();
918    /// let mut query = DnsRecord::new(0, DnsFlags::RD, DnsOpcode::Query, DnsRcode::NoError).unwrap();
919    /// query.query_add("example.com", DnsRecordType::A, DnsCls::IN).unwrap();
920    /// channel.send_dnsrec(&query, move |result| {
921    ///     let record = result.unwrap();
922    ///     for rr in record.rrs(DnsSection::Answer) {
923    ///         if let Some(addr) = rr.get_addr(DnsRrKey::A_ADDR) {
924    ///             println!("address: {addr}");
925    ///         }
926    ///     }
927    /// }).unwrap();
928    /// // ... drive the event loop ...
929    /// ```
930    #[cfg(cares1_28)]
931    pub fn send_dnsrec<F>(&mut self, dnsrec: &DnsRecord, handler: F) -> Result<u16>
932    where
933        F: FnOnce(Result<&DnsRecord>) + Send + 'static,
934    {
935        let mut qid: u16 = 0;
936        let c_arg = Box::into_raw(Box::new(handler));
937        let status = unsafe {
938            c_ares_sys::ares_send_dnsrec(
939                self.ares_channel,
940                dnsrec.as_raw(),
941                Some(dnsrec_callback::<F>),
942                c_arg.cast(),
943                &raw mut qid,
944            )
945        };
946
947        status_to_result(status)?;
948        Ok(qid)
949    }
950
951    /// Initiate a DNS query for `name` with the given class and type, receiving
952    /// a parsed [`DnsRecord`] in the callback.
953    ///
954    /// Returns the query ID on success.
955    ///
956    /// # Examples
957    ///
958    /// ```no_run
959    /// use c_ares::{Channel, DnsCls, DnsRecordType, DnsRrKey, DnsSection};
960    ///
961    /// let mut channel = Channel::new().unwrap();
962    /// channel.query_dnsrec(
963    ///     "example.com",
964    ///     DnsCls::IN,
965    ///     DnsRecordType::A,
966    ///     move |result| {
967    ///         let record = result.unwrap();
968    ///         for rr in record.rrs(DnsSection::Answer) {
969    ///             if let Some(addr) = rr.get_addr(DnsRrKey::A_ADDR) {
970    ///                 println!("address: {addr}");
971    ///             }
972    ///         }
973    ///     },
974    /// ).unwrap();
975    /// // ... drive the event loop with channel.sockets() / channel.process_fd() ...
976    /// ```
977    #[cfg(cares1_28)]
978    pub fn query_dnsrec<F>(
979        &mut self,
980        name: &str,
981        dns_class: DnsCls,
982        query_type: DnsRecordType,
983        handler: F,
984    ) -> Result<u16>
985    where
986        F: FnOnce(Result<&DnsRecord>) + Send + 'static,
987    {
988        let c_name = CString::new(name).map_err(|_| Error::EBADNAME)?;
989        let mut qid: u16 = 0;
990        let c_arg = Box::into_raw(Box::new(handler));
991        let status = unsafe {
992            c_ares_sys::ares_query_dnsrec(
993                self.ares_channel,
994                c_name.as_ptr(),
995                dns_class.into(),
996                query_type.into(),
997                Some(dnsrec_callback::<F>),
998                c_arg.cast(),
999                &raw mut qid,
1000            )
1001        };
1002
1003        status_to_result(status)?;
1004        Ok(qid)
1005    }
1006
1007    /// Initiate a series of DNS queries using a pre-built [`DnsRecord`],
1008    /// receiving a parsed [`DnsRecord`] in the callback.
1009    ///
1010    /// # Examples
1011    ///
1012    /// ```no_run
1013    /// use c_ares::*;
1014    ///
1015    /// let mut channel = Channel::new().unwrap();
1016    /// let mut query = DnsRecord::new(0, DnsFlags::RD, DnsOpcode::Query, DnsRcode::NoError).unwrap();
1017    /// query.query_add("example.com", DnsRecordType::A, DnsCls::IN).unwrap();
1018    /// channel.search_dnsrec(&query, move |result| {
1019    ///     let record = result.unwrap();
1020    ///     for rr in record.rrs(DnsSection::Answer) {
1021    ///         if let Some(addr) = rr.get_addr(DnsRrKey::A_ADDR) {
1022    ///             println!("address: {addr}");
1023    ///         }
1024    ///     }
1025    /// }).unwrap();
1026    /// // ... drive the event loop ...
1027    /// ```
1028    #[cfg(cares1_28)]
1029    pub fn search_dnsrec<F>(&mut self, dnsrec: &DnsRecord, handler: F) -> Result<()>
1030    where
1031        F: FnOnce(Result<&DnsRecord>) + Send + 'static,
1032    {
1033        let c_arg = Box::into_raw(Box::new(handler));
1034        let status = unsafe {
1035            c_ares_sys::ares_search_dnsrec(
1036                self.ares_channel,
1037                dnsrec.as_raw(),
1038                Some(dnsrec_callback::<F>),
1039                c_arg.cast(),
1040            )
1041        };
1042
1043        status_to_result(status)
1044    }
1045
1046    /// Cancel all requests made on this `Channel`.
1047    ///
1048    /// Callbacks will be invoked for each pending query, passing a result
1049    /// `Err(Error::ECANCELLED)`.
1050    pub fn cancel(&mut self) {
1051        unsafe { c_ares_sys::ares_cancel(self.ares_channel) }
1052    }
1053
1054    /// Kick c-ares to process a pending write.
1055    #[cfg(cares1_34)]
1056    pub fn process_pending_write(&mut self) {
1057        unsafe { c_ares_sys::ares_process_pending_write(self.ares_channel) }
1058    }
1059
1060    /// Return the maximum time to wait before processing timeouts.
1061    ///
1062    /// If there are pending queries, returns the time until the next timeout
1063    /// fires, optionally capped at `max_timeout`. If there are no pending
1064    /// queries, returns `max_timeout` (which may be `None`).
1065    pub fn timeout(&self, max_timeout: Option<Duration>) -> Option<Duration> {
1066        let mut maxtv;
1067        let maxtv_ptr = match max_timeout {
1068            Some(d) => {
1069                maxtv = c_ares_sys::timeval {
1070                    tv_sec: d.as_secs() as _,
1071                    // `tv_usec` is `i32` on some targets and `i64` on others;
1072                    // `u32 -> i32` cannot use `From`/`Into`.
1073                    #[allow(clippy::cast_lossless, clippy::cast_possible_wrap)]
1074                    tv_usec: d.subsec_micros() as _,
1075                };
1076                &mut maxtv
1077            }
1078            None => ptr::null_mut(),
1079        };
1080        let mut tv = c_ares_sys::timeval {
1081            tv_sec: 0,
1082            tv_usec: 0,
1083        };
1084        let result = unsafe { c_ares_sys::ares_timeout(self.ares_channel, maxtv_ptr, &raw mut tv) };
1085        unsafe { result.as_ref() }
1086            .map(|tv| Duration::new(tv.tv_sec as u64, (tv.tv_usec as u32) * 1000))
1087    }
1088
1089    /// Block until notified that there are no longer any queries in queue, or
1090    /// the specified timeout has expired.
1091    ///
1092    /// Pass `None` to wait indefinitely.
1093    #[cfg(cares1_27)]
1094    pub fn queue_wait_empty(&self, timeout: Option<Duration>) -> Result<()> {
1095        let timeout_ms = match timeout {
1096            None => -1,
1097            Some(d) => d.as_millis().try_into().unwrap_or(c_int::MAX),
1098        };
1099        let rc = unsafe { c_ares_sys::ares_queue_wait_empty(self.ares_channel, timeout_ms) };
1100        status_to_result(rc)
1101    }
1102
1103    /// Retrieve the total number of active queries pending answers from servers.
1104    ///
1105    /// Some c-ares requests may spawn multiple queries, such as
1106    /// `get_addrinfo()` when using `AddressFamily::UNSPEC`, which will be
1107    /// reflected in this number.
1108    #[cfg(cares1_27)]
1109    pub fn queue_active_queries(&self) -> usize {
1110        unsafe { c_ares_sys::ares_queue_active_queries(self.ares_channel) }
1111    }
1112}
1113
1114impl Drop for Channel {
1115    fn drop(&mut self) {
1116        unsafe { c_ares_sys::ares_destroy(self.ares_channel) }
1117        {
1118            let _lock = ARES_LIBRARY_LOCK.lock().unwrap();
1119            unsafe { c_ares_sys::ares_library_cleanup() }
1120        }
1121    }
1122}
1123
1124unsafe impl Send for Channel {}
1125unsafe impl Sync for Channel {}
1126
1127impl fmt::Debug for Channel {
1128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1129        f.debug_struct("Channel").finish_non_exhaustive()
1130    }
1131}
1132
1133unsafe extern "C" fn socket_state_callback<F>(
1134    data: *mut c_void,
1135    socket_fd: c_ares_sys::ares_socket_t,
1136    readable: c_int,
1137    writable: c_int,
1138) where
1139    F: Fn(Socket, bool, bool) + Send + Sync + 'static,
1140{
1141    let handler = data.cast::<F>();
1142    let handler = unsafe { &*handler };
1143    panic::abort_on_panic(|| handler(socket_fd, readable != 0, writable != 0));
1144}
1145
1146#[cfg(cares1_29)]
1147unsafe extern "C" fn server_state_callback<F>(
1148    server_string: *const c_char,
1149    success: c_ares_sys::ares_bool_t,
1150    flags: c_int,
1151    data: *mut c_void,
1152) where
1153    F: Fn(&str, bool, ServerStateFlags) + Send + Sync + 'static,
1154{
1155    let handler = data.cast::<F>();
1156    let handler = unsafe { &*handler };
1157    let server = unsafe { c_string_as_str_unchecked(server_string) };
1158    panic::abort_on_panic(|| {
1159        handler(
1160            server,
1161            success != c_ares_sys::ares_bool_t::ARES_FALSE,
1162            ServerStateFlags::from_bits_truncate(flags),
1163        );
1164    });
1165}
1166
1167#[cfg(cares1_34)]
1168unsafe extern "C" fn pending_write_callback<F>(data: *mut c_void)
1169where
1170    F: Fn() + Send + Sync + 'static,
1171{
1172    let handler = data.cast::<F>();
1173    let handler = unsafe { &*handler };
1174    panic::abort_on_panic(handler);
1175}
1176
1177#[cfg(test)]
1178mod tests {
1179    use super::*;
1180    use std::net::{Ipv4Addr, Ipv6Addr};
1181
1182    #[test]
1183    fn channel_new_default() {
1184        let channel = Channel::new();
1185        assert!(channel.is_ok());
1186    }
1187
1188    #[test]
1189    fn channel_with_options() {
1190        let mut options = Options::new();
1191        options.set_flags(crate::Flags::STAYOPEN).set_tries(2);
1192        let channel = Channel::with_options(options);
1193        assert!(channel.is_ok());
1194    }
1195
1196    #[test]
1197    fn channel_set_servers_empty() {
1198        let mut channel = Channel::new().unwrap();
1199        let result = channel.set_servers(Vec::<&str>::new());
1200        assert!(result.is_ok());
1201    }
1202
1203    #[test]
1204    fn channel_set_servers_ipv4() {
1205        let mut channel = Channel::new().unwrap();
1206        let result = channel.set_servers(["8.8.8.8", "8.8.4.4"]);
1207        assert!(result.is_ok());
1208    }
1209
1210    #[test]
1211    fn channel_set_servers_ipv6() {
1212        let mut channel = Channel::new().unwrap();
1213        let result = channel.set_servers(["[2001:4860:4860::8888]"]);
1214        assert!(result.is_ok());
1215    }
1216
1217    #[test]
1218    fn channel_set_servers_with_port() {
1219        let mut channel = Channel::new().unwrap();
1220        let result = channel.set_servers(["8.8.8.8:53", "[2001:4860:4860::8888]:53"]);
1221        assert!(result.is_ok());
1222    }
1223
1224    #[test]
1225    fn channel_sockets() {
1226        let channel = Channel::new().unwrap();
1227        let sockets = channel.sockets();
1228        assert_eq!(sockets.iter().count(), 0);
1229    }
1230
1231    #[test]
1232    fn channel_cancel() {
1233        let mut channel = Channel::new().unwrap();
1234        channel.cancel();
1235    }
1236
1237    #[test]
1238    fn channel_process_fd_none() {
1239        let mut channel = Channel::new().unwrap();
1240        channel.process_fd(None, None);
1241    }
1242
1243    #[test]
1244    fn channel_try_clone() {
1245        let channel = Channel::new().unwrap();
1246        let cloned = channel.try_clone();
1247        assert!(cloned.is_ok());
1248    }
1249
1250    #[test]
1251    fn channel_set_local_ipv4() {
1252        let mut channel = Channel::new().unwrap();
1253        channel.set_local_ipv4(Ipv4Addr::UNSPECIFIED);
1254    }
1255
1256    #[test]
1257    fn channel_set_local_ipv6() {
1258        let mut channel = Channel::new().unwrap();
1259        channel.set_local_ipv6(Ipv6Addr::UNSPECIFIED);
1260    }
1261
1262    #[test]
1263    fn channel_set_local_device() {
1264        let mut channel = Channel::new().unwrap();
1265        channel.set_local_device("lo").unwrap();
1266    }
1267
1268    #[test]
1269    fn channel_set_sortlist() {
1270        let mut channel = Channel::new().unwrap();
1271        let result = channel.set_sortlist(["130.155.160.0/255.255.240.0", "130.155.0.0"]);
1272        assert!(result.is_ok());
1273    }
1274
1275    #[cfg(cares1_24)]
1276    #[test]
1277    fn parse_servers_csv_null_is_empty() {
1278        // ares_get_servers_csv() returns NULL on allocation failure; this must
1279        // not dereference the null pointer.
1280        assert!(Channel::parse_servers_csv(std::ptr::null_mut()).is_empty());
1281    }
1282
1283    #[cfg(cares1_24)]
1284    #[test]
1285    fn channel_servers_round_trip() {
1286        let mut channel = Channel::new().unwrap();
1287        channel.set_servers(["8.8.8.8:53"]).unwrap();
1288        let servers = channel.servers();
1289        assert!(servers.iter().any(|s| s.contains("8.8.8.8")));
1290    }
1291
1292    #[test]
1293    fn channel_set_servers_owned_strings() {
1294        let mut channel = Channel::new().unwrap();
1295        let servers: Vec<String> = vec!["8.8.8.8".into(), "8.8.4.4".into()];
1296        channel.set_servers(servers).unwrap();
1297    }
1298
1299    #[test]
1300    fn channel_set_sortlist_owned_strings() {
1301        let mut channel = Channel::new().unwrap();
1302        let sortlist: Vec<String> = vec!["130.155.0.0".into()];
1303        channel.set_sortlist(sortlist).unwrap();
1304    }
1305
1306    #[cfg(cares1_22)]
1307    #[test]
1308    fn channel_reinit() {
1309        let mut channel = Channel::new().unwrap();
1310        let result = channel.reinit();
1311        assert!(result.is_ok());
1312    }
1313
1314    #[cfg(cares1_24)]
1315    #[test]
1316    fn channel_servers() {
1317        let mut channel = Channel::new().unwrap();
1318        channel.set_servers(["8.8.8.8"]).unwrap();
1319        let servers = channel.servers();
1320        assert!(!servers.is_empty());
1321    }
1322
1323    #[cfg(cares1_24)]
1324    #[test]
1325    fn channel_servers_empty() {
1326        let mut channel = Channel::new().unwrap();
1327        channel.set_servers(Vec::<&str>::new()).unwrap();
1328        let servers = channel.servers();
1329        assert!(servers.is_empty());
1330    }
1331
1332    #[cfg(cares1_34)]
1333    #[test]
1334    fn channel_process_fds_empty() {
1335        use crate::ProcessFlags;
1336        let mut channel = Channel::new().unwrap();
1337        let result = channel.process_fds(&[], ProcessFlags::empty());
1338        assert!(result.is_ok());
1339    }
1340
1341    #[cfg(cares1_34)]
1342    #[test]
1343    fn channel_process_pending_write() {
1344        let mut channel = Channel::new().unwrap();
1345        channel.process_pending_write();
1346    }
1347
1348    #[cfg(cares1_28)]
1349    #[test]
1350    fn query_dnsrec_rejects_nul_in_name_with_ebadname() {
1351        let mut channel = Channel::new().unwrap();
1352        let result = channel.query_dnsrec("ex\0ample.com", DnsCls::IN, DnsRecordType::A, |_| {});
1353        assert_eq!(result, Err(Error::EBADNAME));
1354    }
1355
1356    #[test]
1357    fn channel_is_send() {
1358        fn assert_send<T: Send>() {}
1359        assert_send::<Channel>();
1360    }
1361
1362    #[test]
1363    fn channel_is_sync() {
1364        fn assert_sync<T: Sync>() {}
1365        assert_sync::<Channel>();
1366    }
1367
1368    #[test]
1369    fn channel_fds() {
1370        use std::mem::MaybeUninit;
1371        let channel = Channel::new().unwrap();
1372        unsafe {
1373            let mut read_fds: c_types::fd_set = MaybeUninit::zeroed().assume_init();
1374            let mut write_fds: c_types::fd_set = MaybeUninit::zeroed().assume_init();
1375            let nfds = channel.fds(&mut read_fds, &mut write_fds);
1376            // No queries started, so should be 0
1377            assert_eq!(nfds, 0);
1378        }
1379    }
1380
1381    #[test]
1382    fn set_servers_invalid() {
1383        let mut channel = Channel::new().unwrap();
1384        // Invalid server format
1385        let result = channel.set_servers(["not-a-valid-ip"]);
1386        assert!(result.is_err());
1387    }
1388
1389    #[test]
1390    fn set_sortlist_invalid() {
1391        let mut channel = Channel::new().unwrap();
1392        // Invalid sortlist format
1393        let result = channel.set_sortlist(["not-a-valid-address"]);
1394        // Should fail
1395        assert!(result.is_err());
1396    }
1397
1398    #[cfg(cares1_29)]
1399    #[test]
1400    fn channel_set_server_state_callback() {
1401        use crate::ServerStateFlags;
1402        let mut channel = Channel::new().unwrap();
1403        channel.set_server_state_callback(
1404            |_server: &str, _success: bool, _flags: ServerStateFlags| {
1405                // Callback for server state changes
1406            },
1407        );
1408    }
1409
1410    #[cfg(cares1_34)]
1411    #[test]
1412    fn channel_set_pending_write_callback() {
1413        let mut channel = Channel::new().unwrap();
1414        channel.set_pending_write_callback(|| {
1415            // Callback for pending writes
1416        });
1417    }
1418
1419    #[test]
1420    fn channel_process() {
1421        use std::mem::MaybeUninit;
1422        let mut channel = Channel::new().unwrap();
1423        unsafe {
1424            let mut read_fds: c_types::fd_set = MaybeUninit::zeroed().assume_init();
1425            let mut write_fds: c_types::fd_set = MaybeUninit::zeroed().assume_init();
1426            channel.process(&mut read_fds, &mut write_fds);
1427        }
1428    }
1429
1430    #[cfg(cares1_27)]
1431    #[test]
1432    fn channel_queue_active_queries() {
1433        let channel = Channel::new().unwrap();
1434        assert_eq!(channel.queue_active_queries(), 0);
1435    }
1436
1437    #[cfg(cares1_27)]
1438    #[test]
1439    fn channel_queue_wait_empty() {
1440        let channel = Channel::new().unwrap();
1441        // No pending queries, should return immediately.
1442        // Returns ENOTIMP if c-ares was not built with threading support.
1443        let result = channel.queue_wait_empty(Some(Duration::ZERO));
1444        assert!(result.is_ok() || result == Err(Error::ENOTIMP));
1445    }
1446
1447    #[cfg(cares1_27)]
1448    #[test]
1449    fn channel_queue_wait_empty_none_timeout() {
1450        let channel = Channel::new().unwrap();
1451        // None means "wait indefinitely", but with no pending queries it returns immediately.
1452        let result = channel.queue_wait_empty(None);
1453        assert!(result.is_ok() || result == Err(Error::ENOTIMP));
1454    }
1455
1456    #[test]
1457    fn debug_channel() {
1458        let channel = Channel::new().unwrap();
1459        let debug = format!("{channel:?}");
1460        assert!(debug.contains("Channel"));
1461    }
1462
1463    #[test]
1464    fn timeout_no_queries_with_max() {
1465        let channel = Channel::new().unwrap();
1466        // No pending queries: returns the max_timeout we provide.
1467        let max = Duration::from_secs(5);
1468        let result = channel.timeout(Some(max));
1469        assert_eq!(result, Some(max));
1470    }
1471
1472    #[test]
1473    fn timeout_no_queries_without_max() {
1474        let channel = Channel::new().unwrap();
1475        // No pending queries and no max: returns None.
1476        let result = channel.timeout(None);
1477        assert_eq!(result, None);
1478    }
1479}