dns_lookup/
addrinfo.rs

1use socket2::SockAddr;
2use std::ffi::{CStr, CString};
3use std::io;
4use std::iter::FusedIterator;
5use std::mem;
6use std::net::SocketAddr;
7use std::os::raw::c_char;
8use std::ptr;
9
10#[cfg(unix)]
11use libc::{
12    addrinfo as c_addrinfo, c_char as libc_c_char, freeaddrinfo as c_freeaddrinfo,
13    getaddrinfo as c_getaddrinfo,
14};
15
16#[cfg(windows)]
17#[allow(non_camel_case_types)]
18type libc_c_char = u8;
19#[cfg(windows)]
20use windows_sys::Win32::Networking::WinSock::{
21    freeaddrinfo as c_freeaddrinfo, getaddrinfo as c_getaddrinfo, ADDRINFOA as c_addrinfo,
22};
23
24use crate::err::LookupError;
25
26/// A struct used as the hints argument to getaddrinfo.
27#[derive(Copy, Clone, Debug, PartialEq, Eq)]
28pub struct AddrInfoHints {
29    /// Optional bitmask arguments. Bitwise OR bitflags to change the
30    /// behaviour of getaddrinfo. 0 for none. `ai_flags` in libc.
31    ///
32    /// Values are defined by the libc on your system.
33    pub flags: i32,
34    /// Address family for this socket. 0 for none. `ai_family` in libc.
35    ///
36    /// Values are defined by the libc on your system.
37    pub address: i32,
38    /// Type of this socket. 0 for none. `ai_socktype` in libc.
39    ///
40    /// Values are defined by the libc on your system.
41    pub socktype: i32,
42    /// Protcol for this socket. 0 for none. `ai_protocol` in libc.
43    ///
44    /// Values are defined by the libc on your system.
45    pub protocol: i32,
46}
47
48impl AddrInfoHints {
49    /// Create a new AddrInfoHints using built-in types.
50    ///
51    /// Included Enums only provide common values, for anything else
52    /// create this struct directly using appropriate values from the
53    /// libc crate.
54    #[allow(dead_code)]
55    fn new(
56        flags: Option<i32>,
57        address: Option<crate::AddrFamily>,
58        socktype: Option<crate::SockType>,
59        protocol: Option<crate::Protocol>,
60    ) -> AddrInfoHints {
61        AddrInfoHints {
62            flags: flags.unwrap_or(0),
63            address: address.map_or(0, |a| a.into()),
64            socktype: socktype.map_or(0, |a| a.into()),
65            protocol: protocol.map_or(0, |a| a.into()),
66        }
67    }
68
69    // Create libc addrinfo from AddrInfoHints struct.
70    unsafe fn as_addrinfo(&self) -> c_addrinfo {
71        unsafe {
72            let mut addrinfo: c_addrinfo = mem::zeroed();
73            addrinfo.ai_flags = self.flags;
74            addrinfo.ai_family = self.address;
75            addrinfo.ai_socktype = self.socktype;
76            addrinfo.ai_protocol = self.protocol;
77            addrinfo
78        }
79    }
80}
81
82impl Default for AddrInfoHints {
83    /// Generate a blank AddrInfoHints struct, so new values can easily
84    /// be specified.
85    fn default() -> Self {
86        AddrInfoHints {
87            flags: 0,
88            address: 0,
89            socktype: 0,
90            protocol: 0,
91        }
92    }
93}
94
95/// Struct that stores socket information, as returned by getaddrinfo.
96#[derive(Clone, Debug, PartialEq, Eq)]
97pub struct AddrInfo {
98    /// Optional bitmask arguments, usually set to zero. `ai_flags` in libc.
99    pub flags: i32,
100    /// Address family for this socket (usually matches protocol family). `ai_family` in libc.
101    ///
102    /// Values are defined by the libc on your system.
103    pub address: i32,
104    /// Type of this socket. `ai_socktype` in libc.
105    ///
106    /// Values are defined by the libc on your system.
107    pub socktype: i32,
108    /// Protcol family for this socket. `ai_protocol` in libc.
109    ///
110    /// Values are defined by the libc on your system.
111    pub protocol: i32,
112    /// Socket address for this socket, usually containing an actual
113    /// IP Address and port. Combination of `ai_addrlen` and `ai_addr` in libc.
114    pub sockaddr: SocketAddr,
115    /// If requested, this is the canonical name for this socket/host. `ai_canonname` in libc.
116    pub canonname: Option<String>,
117}
118
119impl AddrInfo {
120    /// Copy the informataion from the given addrinfo pointer, and
121    /// create a new AddrInfo struct with that information.
122    ///
123    /// Used for interfacing with getaddrinfo.
124    unsafe fn from_ptr(a: *mut c_addrinfo) -> io::Result<Self> {
125        unsafe {
126            if a.is_null() {
127                return Err(io::Error::other("Supplied pointer is null."))?;
128            }
129
130            let addrinfo = *a;
131            let ((), sockaddr) = SockAddr::try_init(|storage, len| {
132                *len = addrinfo.ai_addrlen as _;
133                #[cfg_attr(windows, allow(clippy::unnecessary_cast))]
134                std::ptr::copy_nonoverlapping(
135                    addrinfo.ai_addr as *const u8,
136                    storage as *mut u8,
137                    addrinfo.ai_addrlen as usize,
138                );
139                Ok(())
140            })?;
141            let sock = sockaddr.as_socket().ok_or_else(|| {
142                io::Error::other(format!(
143                    "Found unknown address family: {}",
144                    sockaddr.family()
145                ))
146            })?;
147            Ok(AddrInfo {
148                flags: 0,
149                address: addrinfo.ai_family,
150                socktype: addrinfo.ai_socktype,
151                protocol: addrinfo.ai_protocol,
152                sockaddr: sock,
153                canonname: addrinfo.ai_canonname.as_ref().map(|s| {
154                    CStr::from_ptr(s as *const libc_c_char as *const c_char)
155                        .to_str()
156                        .unwrap()
157                        .to_owned()
158                }),
159            })
160        }
161    }
162}
163
164/// An iterator of `AddrInfo` structs, wrapping a linked-list
165/// returned by getaddrinfo.
166///
167/// It's recommended to use `.collect<io::Result<..>>()` on this
168/// to collapse possible errors.
169pub struct AddrInfoIter {
170    orig: *mut c_addrinfo,
171    cur: *mut c_addrinfo,
172}
173
174impl Iterator for AddrInfoIter {
175    type Item = io::Result<AddrInfo>;
176
177    fn next(&mut self) -> Option<Self::Item> {
178        unsafe {
179            if self.cur.is_null() {
180                return None;
181            }
182            let ret = AddrInfo::from_ptr(self.cur);
183            #[allow(clippy::unnecessary_cast)]
184            {
185                self.cur = (*self.cur).ai_next as *mut c_addrinfo;
186            }
187            Some(ret)
188        }
189    }
190}
191
192impl FusedIterator for AddrInfoIter {}
193unsafe impl Sync for AddrInfoIter {}
194unsafe impl Send for AddrInfoIter {}
195
196impl Drop for AddrInfoIter {
197    fn drop(&mut self) {
198        unsafe { c_freeaddrinfo(self.orig) }
199    }
200}
201
202/// Retrieve socket information for a host, service, or both. Acts as a thin
203/// wrapper around the libc getaddrinfo.
204///
205/// The only portable way to support International Domain Names (UTF8 DNS
206/// names) is to manually convert to puny code before calling this function -
207/// which can be done using the `idna` crate. However some libc backends may
208/// support this natively, or by using bitflags in the hints argument.
209///
210/// Resolving names from non-UTF8 locales is currently not supported (as the
211/// interface uses &str). Raise an issue if this is a concern for you.
212pub fn getaddrinfo(
213    host: Option<&str>,
214    service: Option<&str>,
215    hints: Option<AddrInfoHints>,
216) -> Result<AddrInfoIter, LookupError> {
217    // We must have at least host or service.
218    if host.is_none() && service.is_none() {
219        Err(io::Error::other("Either host or service must be supplied"))?;
220    }
221
222    // Allocate CStrings, and keep around to free.
223    let host = match host {
224        Some(host_str) => Some(CString::new(host_str)?),
225        None => None,
226    };
227    let c_host = host.as_ref().map_or(ptr::null(), |s| s.as_ptr()) as *const libc_c_char;
228    let service = match service {
229        Some(service_str) => Some(CString::new(service_str)?),
230        None => None,
231    };
232    let c_service = service.as_ref().map_or(ptr::null(), |s| s.as_ptr()) as *const libc_c_char;
233
234    let c_hints = unsafe {
235        match hints {
236            Some(hints) => hints.as_addrinfo(),
237            None => mem::zeroed(),
238        }
239    };
240
241    let mut res = ptr::null_mut();
242
243    // Prime windows.
244    #[cfg(windows)]
245    crate::win::init_winsock();
246
247    unsafe {
248        LookupError::match_gai_error(c_getaddrinfo(c_host, c_service, &c_hints, &mut res))?;
249    }
250
251    Ok(AddrInfoIter {
252        orig: res,
253        cur: res,
254    })
255}
256
257#[test]
258fn test_addrinfohints() {
259    use crate::{AddrFamily, SockType};
260
261    assert_eq!(
262        AddrInfoHints {
263            flags: 1,
264            address: AddrFamily::Inet.into(),
265            socktype: SockType::Stream.into(),
266            ..AddrInfoHints::default()
267        },
268        AddrInfoHints::new(
269            Some(1),
270            Some(AddrFamily::Inet),
271            Some(SockType::Stream),
272            None
273        )
274    );
275
276    assert_eq!(
277        AddrInfoHints {
278            address: AddrFamily::Inet.into(),
279            socktype: SockType::Stream.into(),
280            ..AddrInfoHints::default()
281        },
282        AddrInfoHints::new(None, Some(AddrFamily::Inet), Some(SockType::Stream), None)
283    );
284}