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#[derive(Copy, Clone, Debug, PartialEq, Eq)]
28pub struct AddrInfoHints {
29 pub flags: i32,
34 pub address: i32,
38 pub socktype: i32,
42 pub protocol: i32,
46}
47
48impl AddrInfoHints {
49 #[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 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 fn default() -> Self {
86 AddrInfoHints {
87 flags: 0,
88 address: 0,
89 socktype: 0,
90 protocol: 0,
91 }
92 }
93}
94
95#[derive(Clone, Debug, PartialEq, Eq)]
97pub struct AddrInfo {
98 pub flags: i32,
100 pub address: i32,
104 pub socktype: i32,
108 pub protocol: i32,
112 pub sockaddr: SocketAddr,
115 pub canonname: Option<String>,
117}
118
119impl AddrInfo {
120 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
164pub 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
202pub fn getaddrinfo(
213 host: Option<&str>,
214 service: Option<&str>,
215 hints: Option<AddrInfoHints>,
216) -> Result<AddrInfoIter, LookupError> {
217 if host.is_none() && service.is_none() {
219 Err(io::Error::other("Either host or service must be supplied"))?;
220 }
221
222 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 #[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}