use socket2::SockAddr;
use std::ffi::{CStr, CString};
use std::io;
use std::iter::FusedIterator;
use std::mem;
use std::net::SocketAddr;
use std::os::raw::c_char;
use std::ptr;
#[cfg(unix)]
use libc::{
addrinfo as c_addrinfo, c_char as libc_c_char, freeaddrinfo as c_freeaddrinfo,
getaddrinfo as c_getaddrinfo,
};
#[cfg(windows)]
#[allow(non_camel_case_types)]
type libc_c_char = u8;
#[cfg(windows)]
use windows_sys::Win32::Networking::WinSock::{
freeaddrinfo as c_freeaddrinfo, getaddrinfo as c_getaddrinfo, ADDRINFOA as c_addrinfo,
};
use crate::err::LookupError;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct AddrInfoHints {
pub flags: i32,
pub address: i32,
pub socktype: i32,
pub protocol: i32,
}
impl AddrInfoHints {
#[allow(dead_code)]
fn new(
flags: Option<i32>,
address: Option<crate::AddrFamily>,
socktype: Option<crate::SockType>,
protocol: Option<crate::Protocol>,
) -> AddrInfoHints {
AddrInfoHints {
flags: flags.unwrap_or(0),
address: address.map_or(0, |a| a.into()),
socktype: socktype.map_or(0, |a| a.into()),
protocol: protocol.map_or(0, |a| a.into()),
}
}
unsafe fn as_addrinfo(&self) -> c_addrinfo {
unsafe {
let mut addrinfo: c_addrinfo = mem::zeroed();
addrinfo.ai_flags = self.flags;
addrinfo.ai_family = self.address;
addrinfo.ai_socktype = self.socktype;
addrinfo.ai_protocol = self.protocol;
addrinfo
}
}
}
impl Default for AddrInfoHints {
fn default() -> Self {
AddrInfoHints {
flags: 0,
address: 0,
socktype: 0,
protocol: 0,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AddrInfo {
pub flags: i32,
pub address: i32,
pub socktype: i32,
pub protocol: i32,
pub sockaddr: SocketAddr,
pub canonname: Option<String>,
}
impl AddrInfo {
unsafe fn from_ptr(a: *mut c_addrinfo) -> io::Result<Self> {
unsafe {
if a.is_null() {
return Err(io::Error::other("Supplied pointer is null."))?;
}
let addrinfo = *a;
let ((), sockaddr) = SockAddr::try_init(|storage, len| {
*len = addrinfo.ai_addrlen as _;
#[cfg_attr(windows, allow(clippy::unnecessary_cast))]
std::ptr::copy_nonoverlapping(
addrinfo.ai_addr as *const u8,
storage as *mut u8,
addrinfo.ai_addrlen as usize,
);
Ok(())
})?;
let sock = sockaddr.as_socket().ok_or_else(|| {
io::Error::other(format!(
"Found unknown address family: {}",
sockaddr.family()
))
})?;
Ok(AddrInfo {
flags: 0,
address: addrinfo.ai_family,
socktype: addrinfo.ai_socktype,
protocol: addrinfo.ai_protocol,
sockaddr: sock,
canonname: addrinfo.ai_canonname.as_ref().map(|s| {
CStr::from_ptr(s as *const libc_c_char as *const c_char)
.to_str()
.unwrap()
.to_owned()
}),
})
}
}
}
pub struct AddrInfoIter {
orig: *mut c_addrinfo,
cur: *mut c_addrinfo,
}
impl Iterator for AddrInfoIter {
type Item = io::Result<AddrInfo>;
fn next(&mut self) -> Option<Self::Item> {
unsafe {
if self.cur.is_null() {
return None;
}
let ret = AddrInfo::from_ptr(self.cur);
#[allow(clippy::unnecessary_cast)]
{
self.cur = (*self.cur).ai_next as *mut c_addrinfo;
}
Some(ret)
}
}
}
impl FusedIterator for AddrInfoIter {}
unsafe impl Sync for AddrInfoIter {}
unsafe impl Send for AddrInfoIter {}
impl Drop for AddrInfoIter {
fn drop(&mut self) {
unsafe { c_freeaddrinfo(self.orig) }
}
}
pub fn getaddrinfo(
host: Option<&str>,
service: Option<&str>,
hints: Option<AddrInfoHints>,
) -> Result<AddrInfoIter, LookupError> {
if host.is_none() && service.is_none() {
Err(io::Error::other("Either host or service must be supplied"))?;
}
let host = match host {
Some(host_str) => Some(CString::new(host_str)?),
None => None,
};
let c_host = host.as_ref().map_or(ptr::null(), |s| s.as_ptr()) as *const libc_c_char;
let service = match service {
Some(service_str) => Some(CString::new(service_str)?),
None => None,
};
let c_service = service.as_ref().map_or(ptr::null(), |s| s.as_ptr()) as *const libc_c_char;
let c_hints = unsafe {
match hints {
Some(hints) => hints.as_addrinfo(),
None => mem::zeroed(),
}
};
let mut res = ptr::null_mut();
#[cfg(windows)]
crate::win::init_winsock();
unsafe {
LookupError::match_gai_error(c_getaddrinfo(c_host, c_service, &c_hints, &mut res))?;
}
Ok(AddrInfoIter {
orig: res,
cur: res,
})
}
#[test]
fn test_addrinfohints() {
use crate::{AddrFamily, SockType};
assert_eq!(
AddrInfoHints {
flags: 1,
address: AddrFamily::Inet.into(),
socktype: SockType::Stream.into(),
..AddrInfoHints::default()
},
AddrInfoHints::new(
Some(1),
Some(AddrFamily::Inet),
Some(SockType::Stream),
None
)
);
assert_eq!(
AddrInfoHints {
address: AddrFamily::Inet.into(),
socktype: SockType::Stream.into(),
..AddrInfoHints::default()
},
AddrInfoHints::new(None, Some(AddrFamily::Inet), Some(SockType::Stream), None)
);
}