dns_lookup/
err.rs

1use std::ffi;
2use std::io;
3#[cfg(unix)]
4use {std::os::raw::c_char, std::str};
5
6/// Struct that stores a lookup error from `getaddrinfo`
7/// or `getnameinfo`. Can automatically be coerced to an io::Error using `?`.
8#[derive(Debug)]
9pub struct LookupError {
10    kind: LookupErrorKind,
11    err_num: i32,
12    inner: io::Error,
13}
14
15impl LookupError {
16    /// Match a `gai` error, returning Ok() if it's
17    /// `0`. Otherwise return Err(LookupError) with
18    /// the specific error details.
19    pub fn match_gai_error(err: i32) -> Result<(), Self> {
20        match err {
21            0 => Ok(()),
22            _ => Err(LookupError::new(err)),
23        }
24    }
25
26    /// Create a new LookupError from a `gai` error,
27    /// returned by `getaddrinfo` and `getnameinfo`.
28    pub fn new(err: i32) -> Self {
29        LookupError {
30            kind: LookupErrorKind::new(err),
31            err_num: err,
32            inner: gai_err_to_io_err(err),
33        }
34    }
35    /// Get the error kind explicitly. If this is an
36    /// io::Error, use From/Into to convert it.
37    pub fn kind(&self) -> LookupErrorKind {
38        self.kind
39    }
40
41    /// Get the actual error number. This can be used
42    /// to find non-standard return codes from some
43    /// implementations (be careful of portability here).
44    pub fn error_num(&self) -> i32 {
45        self.err_num
46    }
47}
48
49/// Different kinds of lookup errors that `getaddrinfo` and
50/// `getnameinfo` can return. These can be a little inconsitant
51/// between platforms, so it's recommended not to rely on them.
52#[derive(Copy, Clone, Debug)]
53pub enum LookupErrorKind {
54    /// Temporary failure in name resolution.
55    ///
56    /// May also be returend when DNS server returns a SERVFAIL.
57    Again,
58    /// Invalid value for `ai_flags' field.
59    Badflags,
60    /// NAME or SERVICE is unknown.
61    ///
62    /// May also be returned when domain doesn't exist (NXDOMAIN) or domain
63    /// exists but contains no address records (NODATA).
64    NoName,
65    /// The specified network host exists, but has no data defined.
66    ///
67    /// This is no longer a POSIX standard, however it's still returned by
68    /// some platforms. Be warned that FreeBSD does not include the corresponding
69    /// `EAI_NODATA` symbol.
70    NoData,
71    /// Non-recoverable failure in name resolution.
72    Fail,
73    /// `ai_family' not supported.
74    Family,
75    /// `ai_socktype' not supported.
76    Socktype,
77    /// SERVICE not supported for `ai_socktype'.
78    Service,
79    /// Memory allocation failure.
80    Memory,
81    /// System error returned in `errno'.
82    System,
83    /// An unknown result code was returned.
84    ///
85    /// For some platforms, you may wish to match on an unknown value directly.
86    /// Note that `gai_strerr` is used to get error messages, so the generated IO
87    /// error should contain the correct error message for the platform.
88    Unknown,
89    /// A generic C error or IO error occured.
90    ///
91    /// You should convert this `LookupError` into an IO error directly. Note
92    /// that the error code is set to 0 in the case this is returned.
93    IO,
94}
95
96impl LookupErrorKind {
97    #[cfg(all(not(windows), not(unix)))]
98    /// Create a `LookupErrorKind` from a `gai` error.
99    pub fn new(err: i32) -> Self {
100        LookupErrorKind::IO
101    }
102
103    #[cfg(unix)]
104    /// Create a `LookupErrorKind` from a `gai` error.
105    pub fn new(err: i32) -> Self {
106        use libc as c;
107        match err {
108            c::EAI_AGAIN => LookupErrorKind::Again,
109            c::EAI_BADFLAGS => LookupErrorKind::Badflags,
110            c::EAI_FAIL => LookupErrorKind::Fail,
111            c::EAI_FAMILY => LookupErrorKind::Family,
112            c::EAI_MEMORY => LookupErrorKind::Memory,
113            c::EAI_NONAME => LookupErrorKind::NoName,
114            // FreeBSD has no EAI_NODATA, so don't match it on that platform.
115            #[cfg(not(any(target_os = "freebsd", target_os = "emscripten")))]
116            c::EAI_NODATA => LookupErrorKind::NoData,
117            c::EAI_SERVICE => LookupErrorKind::Service,
118            c::EAI_SOCKTYPE => LookupErrorKind::Socktype,
119            c::EAI_SYSTEM => LookupErrorKind::System,
120            _ => LookupErrorKind::IO,
121        }
122    }
123
124    #[cfg(windows)]
125    /// Create a `LookupErrorKind` from a `gai` error.
126    pub fn new(err: i32) -> Self {
127        use windows_sys::Win32::Networking::WinSock;
128        match err {
129            WinSock::WSATRY_AGAIN => LookupErrorKind::Again,
130            WinSock::WSAEINVAL => LookupErrorKind::Badflags,
131            WinSock::WSANO_RECOVERY => LookupErrorKind::Fail,
132            WinSock::WSAEAFNOSUPPORT => LookupErrorKind::Family,
133            WinSock::WSA_NOT_ENOUGH_MEMORY => LookupErrorKind::Memory,
134            WinSock::WSAHOST_NOT_FOUND => LookupErrorKind::NoName,
135            WinSock::WSANO_DATA => LookupErrorKind::NoData,
136            WinSock::WSATYPE_NOT_FOUND => LookupErrorKind::Service,
137            WinSock::WSAESOCKTNOSUPPORT => LookupErrorKind::Socktype,
138            _ => LookupErrorKind::IO,
139        }
140    }
141}
142
143impl From<LookupError> for io::Error {
144    fn from(err: LookupError) -> io::Error {
145        err.inner
146    }
147}
148
149impl From<io::Error> for LookupError {
150    fn from(err: io::Error) -> LookupError {
151        LookupError {
152            kind: LookupErrorKind::IO,
153            err_num: 0,
154            inner: err,
155        }
156    }
157}
158
159impl From<ffi::NulError> for LookupError {
160    fn from(err: ffi::NulError) -> LookupError {
161        let err: io::Error = err.into();
162        err.into()
163    }
164}
165
166#[cfg(all(not(windows), not(unix)))]
167/// Given a gai error, return an `std::io::Error` with
168/// the appropriate error message. Note `0` is not an
169/// error, but will still map to an error
170pub(crate) fn gai_err_to_io_err(err: i32) -> io::Error {
171    match (err) {
172        0 => io::Error::new(io::ErrorKind::Other, "address information lookup success"),
173        _ => io::Error::new(io::ErrorKind::Other, "failed to lookup address information"),
174    }
175}
176
177#[cfg(unix)]
178/// Given a gai error, return an `std::io::Error` with
179/// the appropriate error message. Note `0` is not an
180/// error, but will still map to an error
181pub(crate) fn gai_err_to_io_err(err: i32) -> io::Error {
182    use libc::{gai_strerror, EAI_SYSTEM};
183
184    match err {
185        0 => return io::Error::new(io::ErrorKind::Other, "address information lookup success"),
186        EAI_SYSTEM => return io::Error::last_os_error(),
187        _ => {}
188    }
189
190    let detail = unsafe {
191        str::from_utf8(ffi::CStr::from_ptr(gai_strerror(err) as *const c_char).to_bytes())
192            .unwrap()
193            .to_owned()
194    };
195    io::Error::new(
196        io::ErrorKind::Other,
197        &format!("failed to lookup address information: {}", detail)[..],
198    )
199}
200
201#[cfg(windows)]
202/// Given a gai error, return an `std::io::Error` with
203/// the appropriate error message. Note `0` is not an
204/// error, but will still map to an error
205pub(crate) fn gai_err_to_io_err(err: i32) -> io::Error {
206    use windows_sys::Win32::Networking::WinSock::WSAGetLastError;
207    match err {
208        0 => io::Error::new(io::ErrorKind::Other, "address information lookup success"),
209        _ => io::Error::from_raw_os_error(unsafe { WSAGetLastError() }),
210    }
211}