diff --git a/build.rs b/build.rs index ba0718473..66bd34c48 100644 --- a/build.rs +++ b/build.rs @@ -53,5 +53,10 @@ fn main() { if version >= 0x1_1e_00 { println!("cargo:rustc-cfg=cares1_30"); } + + println!("cargo::rustc-check-cfg=cfg(cares1_34)"); + if version >= 0x1_22_00 { + println!("cargo:rustc-cfg=cares1_34"); + } } } diff --git a/c-ares-sys/c-ares b/c-ares-sys/c-ares index 27b98d96e..8b2f5e01e 160000 --- a/c-ares-sys/c-ares +++ b/c-ares-sys/c-ares @@ -1 +1 @@ -Subproject commit 27b98d96eff6122fb981e338bddef3d6a57d8d44 +Subproject commit 8b2f5e01ebc39f50438911f610dfd4edc2bc7b26 diff --git a/c-ares-sys/ffi.patch b/c-ares-sys/ffi.patch index 647ea8258..41f80f246 100644 --- a/c-ares-sys/ffi.patch +++ b/c-ares-sys/ffi.patch @@ -1,5 +1,5 @@ ---- src/ffi.rs.orig 2024-08-24 13:21:17.273016107 +0100 -+++ src/ffi.rs 2024-08-24 13:21:59.213021947 +0100 +--- src/ffi.rs.orig 2024-09-07 12:21:29.220612634 +0100 ++++ src/ffi.rs 2024-09-07 12:21:29.230612692 +0100 @@ -1,13 +1,24 @@ /* automatically generated by rust-bindgen 0.70.1 */ +#![allow(non_camel_case_types, non_snake_case)] @@ -51,7 +51,7 @@ pub server_failover_opts: ares_server_failover_options, } #[repr(C)] -@@ -1518,6 +1536,7 @@ +@@ -1567,6 +1585,7 @@ pub struct ares_addrinfo { pub cnames: *mut ares_addrinfo_cname, pub nodes: *mut ares_addrinfo_node, @@ -59,7 +59,7 @@ pub name: *mut ::std::os::raw::c_char, } #[repr(C)] -@@ -1728,3 +1747,17 @@ +@@ -1777,3 +1796,17 @@ #[doc = " Retrieve the total number of active queries pending answers from servers.\n Some c-ares requests may spawn multiple queries, such as ares_getaddrinfo()\n when using AF_UNSPEC, which will be reflected in this number.\n\n \\param[in] channel Initialized ares channel\n \\return Number of active queries to servers"] pub fn ares_queue_active_queries(channel: *const ares_channel_t) -> usize; } diff --git a/c-ares-sys/src/ffi.rs b/c-ares-sys/src/ffi.rs index 33d96db77..ab4743776 100644 --- a/c-ares-sys/src/ffi.rs +++ b/c-ares-sys/src/ffi.rs @@ -1086,6 +1086,8 @@ pub type ares_server_state_callback = ::std::option::Option< data: *mut ::std::os::raw::c_void, ), >; +pub type ares_pending_write_cb = + ::std::option::Option; extern "C" { pub fn ares_library_init(flags: ::std::os::raw::c_int) -> ::std::os::raw::c_int; } @@ -1184,6 +1186,16 @@ extern "C" { user_data: *mut ::std::os::raw::c_void, ); } +extern "C" { + pub fn ares_set_pending_write_cb( + channel: *mut ares_channel_t, + callback: ares_pending_write_cb, + user_data: *mut ::std::os::raw::c_void, + ); +} +extern "C" { + pub fn ares_process_pending_write(channel: *mut ares_channel_t); +} extern "C" { pub fn ares_set_sortlist( channel: *mut ares_channel_t, @@ -1380,6 +1392,43 @@ extern "C" { write_fds: *mut fd_set, ); } +#[repr(u32)] +#[doc = " Events used by ares_fd_events_t"] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum ares_fd_eventflag_t { + #[doc = "< No events"] + ARES_FD_EVENT_NONE = 0, + #[doc = "< Read event (including disconnect/error)"] + ARES_FD_EVENT_READ = 1, + #[doc = "< Write event"] + ARES_FD_EVENT_WRITE = 2, +} +#[doc = " Type holding a file descriptor and mask of events, used by\n ares_process_fds()"] +#[repr(C)] +pub struct ares_fd_events_t { + #[doc = "< File descriptor"] + pub fd: ares_socket_t, + #[doc = "< Mask of ares_fd_eventflag_t"] + pub events: ::std::os::raw::c_uint, +} +#[repr(u32)] +#[doc = " Flags used by ares_process_fds()"] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum ares_process_flag_t { + #[doc = "< No flag value"] + ARES_PROCESS_FLAG_NONE = 0, + #[doc = "< skip any processing unrelated to\n the file descriptor events passed\n in"] + ARES_PROCESS_FLAG_SKIP_NON_FD = 1, +} +extern "C" { + #[doc = " Process events on multiple file descriptors based on the event mask\n associated with each file descriptor. Recommended over calling\n ares_process_fd() multiple times since it would trigger additional logic\n such as timeout processing on each call.\n\n \\param[in] channel Initialized ares channel\n \\param[in] events Array of file descriptors with events. May be NULL if\n no events, but may have timeouts to process.\n \\param[in] nevents Number of elements in the events array. May be 0 if\n no events, but may have timeouts to process.\n \\param[in] flags Flags to alter behavior of the process command.\n \\return ARES_ENOMEM on out of memory, ARES_EFORMERR on misuse,\n otherwise ARES_SUCCESS"] + pub fn ares_process_fds( + channel: *mut ares_channel_t, + events: *const ares_fd_events_t, + nevents: usize, + flags: ::std::os::raw::c_uint, + ) -> ares_status_t; +} extern "C" { pub fn ares_process_fd( channel: *mut ares_channel_t, diff --git a/src/channel.rs b/src/channel.rs index c73fd6f87..8deacef4e 100644 --- a/src/channel.rs +++ b/src/channel.rs @@ -13,6 +13,8 @@ use crate::aaaa::{query_aaaa_callback, AAAAResults}; use crate::caa::{query_caa_callback, CAAResults}; use crate::cname::{query_cname_callback, CNameResults}; use crate::error::{Error, Result}; +#[cfg(cares1_34)] +use crate::events::{FdEvents, ProcessFlags}; use crate::host::{get_host_callback, HostResults}; use crate::mx::{query_mx_callback, MXResults}; use crate::nameinfo::{get_name_info_callback, NameInfoResult}; @@ -47,6 +49,9 @@ type SocketStateCallback = dyn FnMut(Socket, bool, bool) + Send + 'static; #[cfg(cares1_29)] type ServerStateCallback = dyn FnMut(&str, bool, ServerStateFlags) + Send + 'static; +#[cfg(cares1_34)] +type PendingWriteCallback = dyn FnMut() + Send + 'static; + /// Server failover options. /// /// When a DNS server fails to respond to a query, c-ares will deprioritize the server. On @@ -328,6 +333,10 @@ pub struct Channel { // For ownership only. #[cfg(cares1_29)] _server_state_callback: Option>, + + // For ownership only. + #[cfg(cares1_34)] + _pending_write_callback: Option>, } impl Channel { @@ -387,6 +396,8 @@ impl Channel { _socket_state_callback: options.socket_state_callback, #[cfg(cares1_29)] _server_state_callback: None, + #[cfg(cares1_34)] + _pending_write_callback: None, }; Ok(channel) } @@ -416,12 +427,17 @@ impl Channel { #[cfg(cares1_29)] let server_state_callback = self._server_state_callback.as_ref().cloned(); + #[cfg(cares1_34)] + let pending_write_callback = self._pending_write_callback.as_ref().cloned(); + let channel = Channel { ares_channel, phantom: PhantomData, _socket_state_callback: socket_state_callback, #[cfg(cares1_29)] _server_state_callback: server_state_callback, + #[cfg(cares1_34)] + _pending_write_callback: pending_write_callback, }; Ok(channel) } @@ -444,6 +460,27 @@ impl Channel { panic::propagate(); } + /// Process events on multiple file descriptors based on the event mask associated with each + /// file descriptor. Recommended over calling `process_fd()` multiple times since it would + /// trigger additional logic such as timeout processing on each call. + #[cfg(cares1_34)] + pub fn process_fds(&mut self, events: &[FdEvents], flags: ProcessFlags) -> Result<()> { + let rc = unsafe { + c_ares_sys::ares_process_fds( + self.ares_channel, + events.as_ptr().cast(), + events.len(), + flags.bits(), + ) + }; + panic::propagate(); + + if let Ok(err) = Error::try_from(rc) { + return Err(err); + } + return Ok(()) + } + /// Retrieve the set of socket descriptors which the calling application should wait on for /// reading and / or writing. pub fn get_sock(&self) -> GetSock { @@ -554,6 +591,26 @@ impl Channel { self } + /// Set a callback function to be invoked when there is potential pending data + /// which needs to be written. + #[cfg(cares1_34)] + pub fn set_pending_write_callback(&mut self, callback: F) -> &mut Self + where + F: FnMut() + Send + 'static, + { + let boxed_callback = Arc::new(callback); + let data = ptr::from_ref(&*boxed_callback).cast_mut().cast(); + unsafe { + c_ares_sys::ares_set_pending_write_cb( + self.ares_channel, + Some(pending_write_callback::), + data, + ) + } + self._pending_write_callback = Some(boxed_callback); + self + } + /// Initiate a single-question DNS query for the A records associated with `name`. /// /// On completion, `handler` is called with the result. @@ -1122,6 +1179,13 @@ impl Channel { unsafe { c_ares_sys::ares_cancel(self.ares_channel) } panic::propagate(); } + + /// Kick c-ares to process a pending write. + #[cfg(cares1_34)] + pub fn process_pending_write(&mut self) { + unsafe { c_ares_sys::ares_process_pending_write(self.ares_channel) } + panic::propagate(); + } } impl Drop for Channel { @@ -1162,9 +1226,10 @@ unsafe extern "C" fn server_state_callback( F: FnMut(&str, bool, ServerStateFlags) + Send + 'static, { let handler = data.cast::(); + let handler = unsafe { &mut *handler }; let server = c_string_as_str_unchecked(server_string); panic::catch(|| { - (*handler)( + handler( server, success != c_ares_sys::ares_bool_t::ARES_FALSE, ServerStateFlags::from_bits_truncate(flags), @@ -1172,6 +1237,16 @@ unsafe extern "C" fn server_state_callback( }); } +#[cfg(cares1_34)] +unsafe extern "C" fn pending_write_callback(data: *mut c_void) +where + F: FnMut() + Send + 'static, +{ + let handler = data.cast::(); + let handler = unsafe { &mut *handler }; + panic::catch(|| handler()); +} + /// Information about the set of sockets that `c-ares` is interested in, as returned by /// `get_sock()`. #[derive(Clone, Copy, Debug)] diff --git a/src/events.rs b/src/events.rs new file mode 100644 index 000000000..e4c58a577 --- /dev/null +++ b/src/events.rs @@ -0,0 +1,37 @@ +use crate::types::Socket; +use bitflags::bitflags; + +bitflags!( + /// Events used by FdEvents. + #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, PartialOrd, Ord)] + pub struct FdEventFlags: u32 { + /// Read event (including disconnect/error). + const Read = c_ares_sys::ares_fd_eventflag_t::ARES_FD_EVENT_READ as u32; + /// Write event. + const Write = c_ares_sys::ares_fd_eventflag_t::ARES_FD_EVENT_WRITE as u32; + } +); + +bitflags!( + /// Flags used by [`crate::Channel::process_fds()`]. + #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, PartialOrd, Ord)] + pub struct ProcessFlags: u32 { + /// Skip any processing unrelated to the file descriptor events passed in. + const SkipNonFd = c_ares_sys::ares_process_flag_t::ARES_PROCESS_FLAG_SKIP_NON_FD as u32; + } +); + +/// Type holding a file descriptor and mask of events, used by [`crate::Channel::process_fds()`]. +#[repr(transparent)] +pub struct FdEvents(c_ares_sys::ares_fd_events_t); + +impl FdEvents { + /// Returns a new `FdEvents`. + pub fn new(socket: Socket, events: FdEventFlags) -> Self { + let events = c_ares_sys::ares_fd_events_t { + fd: socket, + events: events.bits(), + }; + FdEvents(events) + } +} diff --git a/src/lib.rs b/src/lib.rs index 8b331969a..a9a374b92 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,6 +44,8 @@ mod caa; mod channel; mod cname; mod error; +#[cfg(cares1_34)] +mod events; mod flags; mod host; mod hostent; @@ -77,6 +79,8 @@ pub use crate::channel::ServerFailoverOptions; pub use crate::channel::{Channel, GetSock, GetSockIter, Options}; pub use crate::cname::CNameResults; pub use crate::error::{Error, Result}; +#[cfg(cares1_34)] +pub use crate::events::{FdEventFlags, FdEvents, ProcessFlags}; pub use crate::flags::Flags; pub use crate::host::HostResults; pub use crate::hostent::{HostAddressResultsIter, HostAliasResultsIter};