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