c_ares/
a.rs

1use std::fmt;
2use std::mem;
3use std::net::Ipv4Addr;
4use std::os::raw::{c_int, c_uchar, c_void};
5use std::ptr;
6use std::slice;
7
8use itertools::Itertools;
9
10use crate::error::{Error, Result};
11use crate::panic;
12use crate::types::MAX_ADDRTTLS;
13use crate::utils::ipv4_from_in_addr;
14
15/// The result of a successful A lookup.
16#[derive(Clone, Copy)]
17pub struct AResults {
18    naddrttls: usize,
19    addrttls: [c_ares_sys::ares_addrttl; MAX_ADDRTTLS],
20}
21
22/// The contents of a single A record.
23#[derive(Clone, Copy)]
24pub struct AResult<'a> {
25    addrttl: &'a c_ares_sys::ares_addrttl,
26}
27
28impl AResults {
29    /// Obtain an `AResults` from the response to an A lookup.
30    pub fn parse_from(data: &[u8]) -> Result<AResults> {
31        let mut results: AResults = AResults {
32            naddrttls: MAX_ADDRTTLS,
33            addrttls: unsafe { mem::MaybeUninit::zeroed().assume_init() },
34        };
35        let parse_status = unsafe {
36            c_ares_sys::ares_parse_a_reply(
37                data.as_ptr(),
38                data.len() as c_int,
39                ptr::null_mut(),
40                results.addrttls.as_mut_ptr(),
41                ptr::from_mut(&mut results.naddrttls).cast(),
42            )
43        };
44        if parse_status == c_ares_sys::ares_status_t::ARES_SUCCESS as i32 {
45            Ok(results)
46        } else {
47            Err(Error::from(parse_status))
48        }
49    }
50
51    /// Returns an iterator over the `AResult` values in this `AResults`.
52    pub fn iter(&self) -> AResultsIter {
53        AResultsIter {
54            addrttls: self.addrttls[0..self.naddrttls].iter(),
55        }
56    }
57}
58
59impl fmt::Display for AResults {
60    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
61        let results = self.iter().format("}, {");
62        write!(fmt, "[{{{results}}}]")
63    }
64}
65
66/// Iterator of `AResult`s.
67#[derive(Clone)]
68pub struct AResultsIter<'a> {
69    addrttls: slice::Iter<'a, c_ares_sys::ares_addrttl>,
70}
71
72impl<'a> Iterator for AResultsIter<'a> {
73    type Item = AResult<'a>;
74    fn next(&mut self) -> Option<Self::Item> {
75        self.addrttls.next().map(|addrttl| AResult { addrttl })
76    }
77}
78
79impl<'a> IntoIterator for &'a AResults {
80    type Item = AResult<'a>;
81    type IntoIter = AResultsIter<'a>;
82
83    fn into_iter(self) -> Self::IntoIter {
84        self.iter()
85    }
86}
87
88impl AResult<'_> {
89    /// Returns the IPv4 address in this `AResult`.
90    pub fn ipv4(self) -> Ipv4Addr {
91        ipv4_from_in_addr(self.addrttl.ipaddr)
92    }
93
94    /// Returns the time-to-live in this `AResult`.
95    pub fn ttl(self) -> i32 {
96        #[allow(clippy::unnecessary_cast)]
97        let ttl = self.addrttl.ttl as i32;
98        ttl
99    }
100}
101
102impl fmt::Display for AResult<'_> {
103    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
104        write!(fmt, "IPv4: {}, ", self.ipv4())?;
105        write!(fmt, "TTL: {}", self.ttl())
106    }
107}
108
109pub(crate) unsafe extern "C" fn query_a_callback<F>(
110    arg: *mut c_void,
111    status: c_int,
112    _timeouts: c_int,
113    abuf: *mut c_uchar,
114    alen: c_int,
115) where
116    F: FnOnce(Result<AResults>) + Send + 'static,
117{
118    ares_callback!(arg.cast::<F>(), status, abuf, alen, AResults::parse_from);
119}