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