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
use std::ffi;
use std::io;
#[cfg(unix)]
use {std::os::raw::c_char, std::str};
/// Struct that stores a lookup error from `getaddrinfo`
/// or `getnameinfo`. Can automatically be coerced to an io::Error using `?`.
#[derive(Debug)]
pub struct LookupError {
kind: LookupErrorKind,
err_num: i32,
inner: io::Error,
}
impl LookupError {
/// Match a `gai` error, returning Ok() if it's
/// `0`. Otherwise return Err(LookupError) with
/// the specific error details.
pub fn match_gai_error(err: i32) -> Result<(), Self> {
match err {
0 => Ok(()),
_ => Err(LookupError::new(err)),
}
}
/// Create a new LookupError from a `gai` error,
/// returned by `getaddrinfo` and `getnameinfo`.
pub fn new(err: i32) -> Self {
LookupError {
kind: LookupErrorKind::new(err),
err_num: err,
inner: gai_err_to_io_err(err),
}
}
/// Get the error kind explicitly. If this is an
/// io::Error, use From/Into to convert it.
pub fn kind(&self) -> LookupErrorKind {
self.kind
}
/// Get the actual error number. This can be used
/// to find non-standard return codes from some
/// implementations (be careful of portability here).
pub fn error_num(&self) -> i32 {
self.err_num
}
}
/// Different kinds of lookup errors that `getaddrinfo` and
/// `getnameinfo` can return. These can be a little inconsitant
/// between platforms, so it's recommended not to rely on them.
#[derive(Copy, Clone, Debug)]
pub enum LookupErrorKind {
/// Temporary failure in name resolution.
///
/// May also be returend when DNS server returns a SERVFAIL.
Again,
/// Invalid value for `ai_flags' field.
Badflags,
/// NAME or SERVICE is unknown.
///
/// May also be returned when domain doesn't exist (NXDOMAIN) or domain
/// exists but contains no address records (NODATA).
NoName,
/// The specified network host exists, but has no data defined.
///
/// This is no longer a POSIX standard, however it's still returned by
/// some platforms. Be warned that FreeBSD does not include the corresponding
/// `EAI_NODATA` symbol.
NoData,
/// Non-recoverable failure in name resolution.
Fail,
/// `ai_family' not supported.
Family,
/// `ai_socktype' not supported.
Socktype,
/// SERVICE not supported for `ai_socktype'.
Service,
/// Memory allocation failure.
Memory,
/// System error returned in `errno'.
System,
/// An unknown result code was returned.
///
/// For some platforms, you may wish to match on an unknown value directly.
/// Note that `gai_strerr` is used to get error messages, so the generated IO
/// error should contain the correct error message for the platform.
Unknown,
/// A generic C error or IO error occured.
///
/// You should convert this `LookupError` into an IO error directly. Note
/// that the error code is set to 0 in the case this is returned.
IO,
}
impl LookupErrorKind {
#[cfg(all(not(windows), not(unix)))]
/// Create a `LookupErrorKind` from a `gai` error.
pub fn new(err: i32) -> Self {
LookupErrorKind::IO
}
#[cfg(unix)]
/// Create a `LookupErrorKind` from a `gai` error.
pub fn new(err: i32) -> Self {
use libc as c;
match err {
c::EAI_AGAIN => LookupErrorKind::Again,
c::EAI_BADFLAGS => LookupErrorKind::Badflags,
c::EAI_FAIL => LookupErrorKind::Fail,
c::EAI_FAMILY => LookupErrorKind::Family,
c::EAI_MEMORY => LookupErrorKind::Memory,
c::EAI_NONAME => LookupErrorKind::NoName,
// FreeBSD has no EAI_NODATA, so don't match it on that platform.
#[cfg(not(any(target_os = "freebsd", target_os = "emscripten")))]
c::EAI_NODATA => LookupErrorKind::NoData,
c::EAI_SERVICE => LookupErrorKind::Service,
c::EAI_SOCKTYPE => LookupErrorKind::Socktype,
c::EAI_SYSTEM => LookupErrorKind::System,
_ => LookupErrorKind::IO,
}
}
#[cfg(windows)]
/// Create a `LookupErrorKind` from a `gai` error.
pub fn new(err: i32) -> Self {
use windows_sys::Win32::Networking::WinSock;
match err {
WinSock::WSATRY_AGAIN => LookupErrorKind::Again,
WinSock::WSAEINVAL => LookupErrorKind::Badflags,
WinSock::WSANO_RECOVERY => LookupErrorKind::Fail,
WinSock::WSAEAFNOSUPPORT => LookupErrorKind::Family,
WinSock::WSA_NOT_ENOUGH_MEMORY => LookupErrorKind::Memory,
WinSock::WSAHOST_NOT_FOUND => LookupErrorKind::NoName,
WinSock::WSANO_DATA => LookupErrorKind::NoData,
WinSock::WSATYPE_NOT_FOUND => LookupErrorKind::Service,
WinSock::WSAESOCKTNOSUPPORT => LookupErrorKind::Socktype,
_ => LookupErrorKind::IO,
}
}
}
impl From<LookupError> for io::Error {
fn from(err: LookupError) -> io::Error {
err.inner
}
}
impl From<io::Error> for LookupError {
fn from(err: io::Error) -> LookupError {
LookupError {
kind: LookupErrorKind::IO,
err_num: 0,
inner: err,
}
}
}
impl From<ffi::NulError> for LookupError {
fn from(err: ffi::NulError) -> LookupError {
let err: io::Error = err.into();
err.into()
}
}
#[cfg(all(not(windows), not(unix)))]
/// Given a gai error, return an `std::io::Error` with
/// the appropriate error message. Note `0` is not an
/// error, but will still map to an error
pub(crate) fn gai_err_to_io_err(err: i32) -> io::Error {
match (err) {
0 => io::Error::new(io::ErrorKind::Other, "address information lookup success"),
_ => io::Error::new(io::ErrorKind::Other, "failed to lookup address information"),
}
}
#[cfg(unix)]
/// Given a gai error, return an `std::io::Error` with
/// the appropriate error message. Note `0` is not an
/// error, but will still map to an error
pub(crate) fn gai_err_to_io_err(err: i32) -> io::Error {
use libc::{gai_strerror, EAI_SYSTEM};
match err {
0 => return io::Error::new(io::ErrorKind::Other, "address information lookup success"),
EAI_SYSTEM => return io::Error::last_os_error(),
_ => {}
}
let detail = unsafe {
str::from_utf8(ffi::CStr::from_ptr(gai_strerror(err) as *const c_char).to_bytes())
.unwrap()
.to_owned()
};
io::Error::new(
io::ErrorKind::Other,
&format!("failed to lookup address information: {}", detail)[..],
)
}
#[cfg(windows)]
/// Given a gai error, return an `std::io::Error` with
/// the appropriate error message. Note `0` is not an
/// error, but will still map to an error
pub(crate) fn gai_err_to_io_err(err: i32) -> io::Error {
use windows_sys::Win32::Networking::WinSock::WSAGetLastError;
match err {
0 => io::Error::new(io::ErrorKind::Other, "address information lookup success"),
_ => io::Error::from_raw_os_error(unsafe { WSAGetLastError() }),
}
}