1use core::ffi::{c_int, c_void};
2use std::fmt;
3use std::net::{IpAddr, SocketAddr, SocketAddrV4, SocketAddrV6};
4
5use bitflags::bitflags;
6use itertools::Itertools;
7
8use crate::error::{Error, Result};
9use crate::panic;
10use crate::types::AddressFamily;
11use crate::utils::{hostname_as_str, ipv4_from_in_addr, ipv6_from_in6_addr, sockaddr_in6_scope_id};
12
13bitflags!(
14 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
16 pub struct AddrInfoFlags: i32 {
17 const CANONNAME = c_ares_sys::ARES_AI_CANONNAME;
19
20 const NUMERICHOST = c_ares_sys::ARES_AI_NUMERICHOST;
22
23 const PASSIVE = c_ares_sys::ARES_AI_PASSIVE;
25
26 const NUMERICSERV = c_ares_sys::ARES_AI_NUMERICSERV;
28
29 const V4MAPPED = c_ares_sys::ARES_AI_V4MAPPED;
32
33 const ALL = c_ares_sys::ARES_AI_ALL;
35
36 const ADDRCONFIG = c_ares_sys::ARES_AI_ADDRCONFIG;
38
39 const NOSORT = c_ares_sys::ARES_AI_NOSORT;
42
43 const ENVHOSTS = c_ares_sys::ARES_AI_ENVHOSTS;
45 }
46);
47
48#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
50pub struct AddrInfoHints {
51 pub flags: AddrInfoFlags,
53 pub family: Option<AddressFamily>,
55 pub socktype: i32,
58 pub protocol: i32,
61}
62
63impl Default for AddrInfoFlags {
64 fn default() -> Self {
65 Self::empty()
66 }
67}
68
69impl From<&AddrInfoHints> for c_ares_sys::ares_addrinfo_hints {
70 fn from(hints: &AddrInfoHints) -> Self {
71 c_ares_sys::ares_addrinfo_hints {
72 ai_flags: hints.flags.bits(),
73 ai_family: hints
74 .family
75 .map_or(c_types::AF_UNSPEC as c_int, |f| f as c_int),
76 ai_socktype: hints.socktype,
77 ai_protocol: hints.protocol,
78 }
79 }
80}
81
82pub struct AddrInfoResults {
86 addrinfo: *mut c_ares_sys::ares_addrinfo,
87}
88
89impl AddrInfoResults {
90 pub(crate) fn new(addrinfo: *mut c_ares_sys::ares_addrinfo) -> Self {
91 AddrInfoResults { addrinfo }
92 }
93
94 pub fn name(&self) -> Option<&str> {
96 unsafe { (*self.addrinfo).name.as_ref() }.map(|name| unsafe { hostname_as_str(name) })
97 }
98
99 pub fn nodes(&self) -> AddrInfoNodeIter<'_> {
101 AddrInfoNodeIter {
102 next: unsafe { (*self.addrinfo).nodes.as_ref() },
103 }
104 }
105
106 pub fn cnames(&self) -> AddrInfoCNameIter<'_> {
108 AddrInfoCNameIter {
109 next: unsafe { (*self.addrinfo).cnames.as_ref() },
110 }
111 }
112}
113
114impl fmt::Display for AddrInfoResults {
115 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
116 if let Some(name) = self.name() {
117 write!(fmt, "Name: {name}, ")?;
118 }
119 let nodes = self.nodes().format(", ");
120 write!(fmt, "Nodes: [{nodes}]")?;
121 let cnames: Vec<_> = self.cnames().collect();
122 if !cnames.is_empty() {
123 let cnames = cnames.iter().format(", ");
124 write!(fmt, ", CNames: [{cnames}]")?;
125 }
126 Ok(())
127 }
128}
129
130impl fmt::Debug for AddrInfoResults {
131 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132 f.debug_struct("AddrInfoResults")
133 .field("name", &self.name())
134 .finish_non_exhaustive()
135 }
136}
137
138impl Drop for AddrInfoResults {
139 fn drop(&mut self) {
140 unsafe {
141 c_ares_sys::ares_freeaddrinfo(self.addrinfo);
142 }
143 }
144}
145
146unsafe impl Send for AddrInfoResults {}
147unsafe impl Sync for AddrInfoResults {}
148
149#[derive(Clone, Copy)]
151pub struct AddrInfoNode<'a> {
152 node: &'a c_ares_sys::ares_addrinfo_node,
153}
154
155impl AddrInfoNode<'_> {
156 pub fn ttl(&self) -> i32 {
158 self.node.ai_ttl
159 }
160
161 pub fn family(&self) -> AddressFamily {
163 match self.node.ai_family as c_types::ADDRESS_FAMILY {
164 c_types::AF_INET => AddressFamily::INET,
165 c_types::AF_INET6 => AddressFamily::INET6,
166 _ => AddressFamily::UNSPEC,
167 }
168 }
169
170 pub fn socktype(&self) -> i32 {
173 self.node.ai_socktype
174 }
175
176 pub fn protocol(&self) -> i32 {
179 self.node.ai_protocol
180 }
181
182 pub fn socket_addr(&self) -> Option<SocketAddr> {
184 let addr = self.node.ai_addr;
185 if addr.is_null() {
186 return None;
187 }
188 match self.node.ai_family as c_types::ADDRESS_FAMILY {
189 c_types::AF_INET => {
190 #[allow(clippy::cast_ptr_alignment)]
193 let sa = unsafe { &*(addr.cast::<c_types::sockaddr_in>()) };
194 let ip = ipv4_from_in_addr(sa.sin_addr);
195 let port = u16::from_be(sa.sin_port);
196 Some(SocketAddr::V4(SocketAddrV4::new(ip, port)))
197 }
198 c_types::AF_INET6 => {
199 #[allow(clippy::cast_ptr_alignment)]
202 let sa = unsafe { &*(addr.cast::<c_types::sockaddr_in6>()) };
203 let ip = ipv6_from_in6_addr(sa.sin6_addr);
204 let port = u16::from_be(sa.sin6_port);
205 let scope_id = sockaddr_in6_scope_id(sa);
206 Some(SocketAddr::V6(SocketAddrV6::new(
207 ip,
208 port,
209 sa.sin6_flowinfo,
210 scope_id,
211 )))
212 }
213 _ => None,
214 }
215 }
216
217 pub fn ip_addr(&self) -> Option<IpAddr> {
219 self.socket_addr().map(|sa| sa.ip())
220 }
221}
222
223impl fmt::Debug for AddrInfoNode<'_> {
224 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225 f.debug_struct("AddrInfoNode")
226 .field("ttl", &self.ttl())
227 .field("family", &self.family())
228 .field("socktype", &self.socktype())
229 .field("protocol", &self.protocol())
230 .field("socket_addr", &self.socket_addr())
231 .finish()
232 }
233}
234
235impl fmt::Display for AddrInfoNode<'_> {
236 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
237 if let Some(addr) = self.ip_addr() {
238 write!(fmt, "{addr}")?;
239 } else {
240 write!(fmt, "<unknown>")?;
241 }
242 write!(fmt, " (ttl: {})", self.ttl())
243 }
244}
245
246unsafe impl Send for AddrInfoNode<'_> {}
247unsafe impl Sync for AddrInfoNode<'_> {}
248
249#[derive(Clone)]
251pub struct AddrInfoNodeIter<'a> {
252 next: Option<&'a c_ares_sys::ares_addrinfo_node>,
253}
254
255impl fmt::Debug for AddrInfoNodeIter<'_> {
256 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257 f.debug_struct("AddrInfoNodeIter").finish_non_exhaustive()
258 }
259}
260
261impl<'a> Iterator for AddrInfoNodeIter<'a> {
262 type Item = AddrInfoNode<'a>;
263
264 fn next(&mut self) -> Option<Self::Item> {
265 let opt_node = self.next;
266 self.next = opt_node.and_then(|node| unsafe { node.ai_next.as_ref() });
267 opt_node.map(|node| AddrInfoNode { node })
268 }
269}
270
271impl std::iter::FusedIterator for AddrInfoNodeIter<'_> {}
272
273unsafe impl Send for AddrInfoNodeIter<'_> {}
274unsafe impl Sync for AddrInfoNodeIter<'_> {}
275
276#[derive(Clone, Copy, Debug)]
278pub struct AddrInfoCName<'a> {
279 cname: &'a c_ares_sys::ares_addrinfo_cname,
280}
281
282impl AddrInfoCName<'_> {
283 pub fn ttl(&self) -> i32 {
285 self.cname.ttl
286 }
287
288 pub fn alias(&self) -> &str {
290 unsafe { hostname_as_str(self.cname.alias) }
291 }
292
293 pub fn name(&self) -> &str {
295 unsafe { hostname_as_str(self.cname.name) }
296 }
297}
298
299impl fmt::Display for AddrInfoCName<'_> {
300 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
301 write!(
302 fmt,
303 "{} -> {} (ttl: {})",
304 self.alias(),
305 self.name(),
306 self.ttl()
307 )
308 }
309}
310
311unsafe impl Send for AddrInfoCName<'_> {}
312unsafe impl Sync for AddrInfoCName<'_> {}
313
314#[derive(Clone, Debug)]
316pub struct AddrInfoCNameIter<'a> {
317 next: Option<&'a c_ares_sys::ares_addrinfo_cname>,
318}
319
320impl<'a> Iterator for AddrInfoCNameIter<'a> {
321 type Item = AddrInfoCName<'a>;
322
323 fn next(&mut self) -> Option<Self::Item> {
324 let opt_cname = self.next;
325 self.next = opt_cname.and_then(|cname| unsafe { cname.next.as_ref() });
326 opt_cname.map(|cname| AddrInfoCName { cname })
327 }
328}
329
330impl std::iter::FusedIterator for AddrInfoCNameIter<'_> {}
331
332unsafe impl Send for AddrInfoCNameIter<'_> {}
333unsafe impl Sync for AddrInfoCNameIter<'_> {}
334
335pub(crate) unsafe extern "C" fn get_addrinfo_callback<F>(
336 arg: *mut c_void,
337 status: c_int,
338 _timeouts: c_int,
339 addrinfo: *mut c_ares_sys::ares_addrinfo,
340) where
341 F: FnOnce(Result<AddrInfoResults>) + Send + 'static,
342{
343 let result = if status == c_ares_sys::ares_status_t::ARES_SUCCESS as i32 {
344 Ok(AddrInfoResults::new(addrinfo))
345 } else {
346 Err(Error::from(status))
347 };
348 let handler = unsafe { Box::from_raw(arg.cast::<F>()) };
349 panic::abort_on_panic(|| handler(result));
350}
351
352#[cfg(test)]
353mod tests {
354 use super::*;
355
356 #[test]
357 fn is_send() {
358 fn assert_send<T: Send>() {}
359 assert_send::<AddrInfoResults>();
360 assert_send::<AddrInfoNode<'_>>();
361 assert_send::<AddrInfoNodeIter<'_>>();
362 assert_send::<AddrInfoCName<'_>>();
363 assert_send::<AddrInfoCNameIter<'_>>();
364 }
365
366 #[test]
367 fn is_sync() {
368 fn assert_sync<T: Sync>() {}
369 assert_sync::<AddrInfoResults>();
370 assert_sync::<AddrInfoNode<'_>>();
371 assert_sync::<AddrInfoNodeIter<'_>>();
372 assert_sync::<AddrInfoCName<'_>>();
373 assert_sync::<AddrInfoCNameIter<'_>>();
374 }
375
376 #[test]
377 fn hints_default() {
378 let hints = AddrInfoHints::default();
379 assert_eq!(hints.flags, AddrInfoFlags::empty());
380 assert_eq!(hints.family, None);
381 assert_eq!(hints.socktype, 0);
382 assert_eq!(hints.protocol, 0);
383 }
384
385 #[test]
386 fn hints_into_raw() {
387 let hints = AddrInfoHints {
388 flags: AddrInfoFlags::CANONNAME | AddrInfoFlags::NUMERICSERV,
389 family: Some(AddressFamily::INET),
390 socktype: 1,
391 protocol: 6,
392 };
393 let raw: c_ares_sys::ares_addrinfo_hints = (&hints).into();
394 assert_eq!(
395 raw.ai_flags,
396 (AddrInfoFlags::CANONNAME | AddrInfoFlags::NUMERICSERV).bits()
397 );
398 assert_eq!(raw.ai_family, AddressFamily::INET as c_int);
399 assert_eq!(raw.ai_socktype, 1);
400 assert_eq!(raw.ai_protocol, 6);
401 }
402
403 #[test]
404 fn hints_unspec_family() {
405 let hints = AddrInfoHints::default();
406 let raw: c_ares_sys::ares_addrinfo_hints = (&hints).into();
407 assert_eq!(raw.ai_family, c_types::AF_UNSPEC as c_int);
408 }
409
410 #[test]
411 fn flags_empty() {
412 let flags = AddrInfoFlags::empty();
413 assert!(flags.is_empty());
414 }
415
416 #[test]
417 fn flags_combine() {
418 let flags = AddrInfoFlags::CANONNAME | AddrInfoFlags::NOSORT;
419 assert!(flags.contains(AddrInfoFlags::CANONNAME));
420 assert!(flags.contains(AddrInfoFlags::NOSORT));
421 assert!(!flags.contains(AddrInfoFlags::PASSIVE));
422 }
423
424 #[test]
425 fn flags_debug() {
426 let flags = AddrInfoFlags::CANONNAME | AddrInfoFlags::NUMERICHOST;
427 let debug = format!("{flags:?}");
428 assert!(!debug.is_empty());
429 }
430}