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 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 fn default() -> Self {
84 AddrInfoHints {
85 flags: 0,
86 address: 0,
87 socktype: 0,
88 protocol: 0,
89 }
90 }
91}
92
93#[derive(Clone, Debug, PartialEq, Eq)]
95pub struct AddrInfo {
96 pub flags: i32,
98 pub address: i32,
102 pub socktype: i32,
106 pub protocol: i32,
110 pub sockaddr: SocketAddr,
113 pub canonname: Option<String>,
115}
116
117impl AddrInfo {
118 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
162pub 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
197pub fn getaddrinfo(
208 host: Option<&str>,
209 service: Option<&str>,
210 hints: Option<AddrInfoHints>,
211) -> Result<AddrInfoIter, LookupError> {
212 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 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 #[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}