Skip to main content

winping/
error.rs

1use winapi::{
2    shared::{
3        ntdef::NULL,
4        winerror::{
5            ERROR_HOST_UNREACHABLE, ERROR_NETWORK_UNREACHABLE, ERROR_PROTOCOL_UNREACHABLE, NO_ERROR,
6        },
7    },
8    um::{
9        errhandlingapi::GetLastError,
10        ipexport::{
11            IP_DEST_HOST_UNREACHABLE, IP_DEST_NET_UNREACHABLE, IP_DEST_PROT_UNREACHABLE,
12            IP_PACKET_TOO_BIG, IP_REQ_TIMED_OUT, IP_STATUS_BASE, IP_TTL_EXPIRED_REASSEM,
13            IP_TTL_EXPIRED_TRANSIT, MAX_IP_STATUS,
14        },
15        iphlpapi::GetIpErrorString,
16        winbase::{FormatMessageW, FORMAT_MESSAGE_FROM_SYSTEM, FORMAT_MESSAGE_IGNORE_INSERTS},
17    },
18};
19
20use std::fmt::{self, Debug, Display, Formatter};
21
22/// An error when sending a ping request.
23#[derive(Copy, Clone, Eq, PartialEq, Hash)]
24pub enum Error {
25    /// The ping request timed out.
26    Timeout,
27    /// The destination network is unreachable.
28    NetUnreachable,
29    /// The destination host is unreachable.
30    HostUnreachable,
31    /// The IP TTL expired during transit.
32    TtlExpired,
33    /// The IP reassembly timer expired.
34    ReassemblyExpired,
35    /// The packet needs fragmented, but the DF bit is set.
36    NeedsFragmented,
37    /// The destination protocol is unreachable.
38    ProtocolUnreachable,
39    /// Some other error ocurred. Format with debug or diplay to get more info.
40    Other(u32),
41}
42
43impl Error {
44    pub(crate) fn from_iperror(err: u32) -> Self {
45        match err {
46            IP_REQ_TIMED_OUT => Error::Timeout,
47            IP_DEST_HOST_UNREACHABLE => Error::HostUnreachable,
48            IP_DEST_NET_UNREACHABLE => Error::NetUnreachable,
49            IP_TTL_EXPIRED_TRANSIT => Error::TtlExpired,
50            IP_TTL_EXPIRED_REASSEM => Error::ReassemblyExpired,
51            IP_DEST_PROT_UNREACHABLE => Error::ProtocolUnreachable,
52            IP_PACKET_TOO_BIG => Error::NeedsFragmented,
53            _ => Error::Other(err),
54        }
55    }
56    /// Creates an Error from the last Windows error
57    pub(crate) fn from_lasterror() -> Self {
58        Self::from_winerror(unsafe { GetLastError() })
59    }
60    /// Takes either a Windows error or IP_STATUS value
61    pub(crate) fn from_winerror(err: u32) -> Self {
62        match err {
63            IP_STATUS_BASE..=MAX_IP_STATUS => Error::from_iperror(err),
64            ERROR_HOST_UNREACHABLE => Error::HostUnreachable,
65            ERROR_NETWORK_UNREACHABLE => Error::NetUnreachable,
66            ERROR_PROTOCOL_UNREACHABLE => Error::ProtocolUnreachable,
67            _ => Error::Other(err),
68        }
69    }
70}
71
72impl Debug for Error {
73    fn fmt(&self, out: &mut Formatter) -> fmt::Result {
74        match self {
75            Error::Timeout => write!(out, "Request timed out"),
76            Error::HostUnreachable => write!(out, "Destination host unreachable"),
77            Error::NetUnreachable => write!(out, "Destination network unreachable"),
78            Error::TtlExpired => write!(out, "TTL expired in transit"),
79            Error::ReassemblyExpired => write!(out, "Reassembly timed out waiting for fragments"),
80            Error::NeedsFragmented => write!(out, "Packet needs fragmented"),
81            Error::ProtocolUnreachable => write!(out, "Destination protocol unreachable"),
82            Error::Other(err @ IP_STATUS_BASE..=MAX_IP_STATUS) => {
83                let mut buf = [0u16; 256];
84                let ret =
85                    unsafe { GetIpErrorString(*err, &mut buf[0], &mut (buf.len() as u32 - 1)) };
86                debug_assert_eq!(NO_ERROR, ret);
87                let len = buf.iter().take_while(|x| **x != 0).count();
88                let s = String::from_utf16_lossy(&buf[..len]);
89                write!(out, "Other IP error ({}): {}", err, s.trim())
90            }
91            Error::Other(err) => {
92                const FLAGS: u32 = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
93                let mut buf = [0u16; 256];
94                let len = unsafe {
95                    FormatMessageW(
96                        FLAGS,
97                        NULL,
98                        *err,
99                        0, // lang id
100                        &mut buf[0],
101                        buf.len() as u32,
102                        NULL as _,
103                    )
104                };
105                let s = String::from_utf16_lossy(&buf[..len as usize]);
106                write!(out, "Other error ({}): {}", err, s.trim())
107            }
108        }
109    }
110}
111
112impl Display for Error {
113    fn fmt(&self, out: &mut Formatter) -> fmt::Result {
114        Debug::fmt(self, out)
115    }
116}
117
118impl std::error::Error for Error {}