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#[derive(Copy, Clone, Eq, PartialEq, Hash)]
24pub enum Error {
25 Timeout,
27 NetUnreachable,
29 HostUnreachable,
31 TtlExpired,
33 ReassemblyExpired,
35 NeedsFragmented,
37 ProtocolUnreachable,
39 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 pub(crate) fn from_lasterror() -> Self {
58 Self::from_winerror(unsafe { GetLastError() })
59 }
60 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, &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 {}