| 1 | use core::{fmt, num::NonZeroU32}; | 
| 2 |  | 
|---|
| 3 | /// A small and `no_std` compatible error type | 
|---|
| 4 | /// | 
|---|
| 5 | /// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and | 
|---|
| 6 | /// if so, which error code the OS gave the application. If such an error is | 
|---|
| 7 | /// encountered, please consult with your system documentation. | 
|---|
| 8 | /// | 
|---|
| 9 | /// Internally this type is a NonZeroU32, with certain values reserved for | 
|---|
| 10 | /// certain purposes, see [`Error::INTERNAL_START`] and [`Error::CUSTOM_START`]. | 
|---|
| 11 | /// | 
|---|
| 12 | /// *If this crate's `"std"` Cargo feature is enabled*, then: | 
|---|
| 13 | /// - [`getrandom::Error`][Error] implements | 
|---|
| 14 | ///   [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html) | 
|---|
| 15 | /// - [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) implements | 
|---|
| 16 | ///   [`From<getrandom::Error>`](https://doc.rust-lang.org/std/convert/trait.From.html). | 
|---|
| 17 | #[ derive(Copy, Clone, Eq, PartialEq)] | 
|---|
| 18 | pub struct Error(NonZeroU32); | 
|---|
| 19 |  | 
|---|
| 20 | const fn internal_error(n: u16) -> Error { | 
|---|
| 21 | // SAFETY: code > 0 as INTERNAL_START > 0 and adding n won't overflow a u32. | 
|---|
| 22 | let code: u32 = Error::INTERNAL_START + (n as u32); | 
|---|
| 23 | Error(unsafe { NonZeroU32::new_unchecked(code) }) | 
|---|
| 24 | } | 
|---|
| 25 |  | 
|---|
| 26 | impl Error { | 
|---|
| 27 | /// This target/platform is not supported by `getrandom`. | 
|---|
| 28 | pub const UNSUPPORTED: Error = internal_error(0); | 
|---|
| 29 | /// The platform-specific `errno` returned a non-positive value. | 
|---|
| 30 | pub const ERRNO_NOT_POSITIVE: Error = internal_error(1); | 
|---|
| 31 | /// Encountered an unexpected situation which should not happen in practice. | 
|---|
| 32 | pub const UNEXPECTED: Error = internal_error(2); | 
|---|
| 33 | /// Call to [`CCRandomGenerateBytes`](https://opensource.apple.com/source/CommonCrypto/CommonCrypto-60074/include/CommonRandom.h.auto.html) failed | 
|---|
| 34 | /// on iOS, tvOS, or waatchOS. | 
|---|
| 35 | // TODO: Update this constant name in the next breaking release. | 
|---|
| 36 | pub const IOS_SEC_RANDOM: Error = internal_error(3); | 
|---|
| 37 | /// Call to Windows [`RtlGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom) failed. | 
|---|
| 38 | pub const WINDOWS_RTL_GEN_RANDOM: Error = internal_error(4); | 
|---|
| 39 | /// RDRAND instruction failed due to a hardware issue. | 
|---|
| 40 | pub const FAILED_RDRAND: Error = internal_error(5); | 
|---|
| 41 | /// RDRAND instruction unsupported on this target. | 
|---|
| 42 | pub const NO_RDRAND: Error = internal_error(6); | 
|---|
| 43 | /// The environment does not support the Web Crypto API. | 
|---|
| 44 | pub const WEB_CRYPTO: Error = internal_error(7); | 
|---|
| 45 | /// Calling Web Crypto API `crypto.getRandomValues` failed. | 
|---|
| 46 | pub const WEB_GET_RANDOM_VALUES: Error = internal_error(8); | 
|---|
| 47 | /// On VxWorks, call to `randSecure` failed (random number generator is not yet initialized). | 
|---|
| 48 | pub const VXWORKS_RAND_SECURE: Error = internal_error(11); | 
|---|
| 49 | /// Node.js does not have the `crypto` CommonJS module. | 
|---|
| 50 | pub const NODE_CRYPTO: Error = internal_error(12); | 
|---|
| 51 | /// Calling Node.js function `crypto.randomFillSync` failed. | 
|---|
| 52 | pub const NODE_RANDOM_FILL_SYNC: Error = internal_error(13); | 
|---|
| 53 | /// Called from an ES module on Node.js. This is unsupported, see: | 
|---|
| 54 | /// <https://docs.rs/getrandom#nodejs-es-module-support>. | 
|---|
| 55 | pub const NODE_ES_MODULE: Error = internal_error(14); | 
|---|
| 56 |  | 
|---|
| 57 | /// Codes below this point represent OS Errors (i.e. positive i32 values). | 
|---|
| 58 | /// Codes at or above this point, but below [`Error::CUSTOM_START`] are | 
|---|
| 59 | /// reserved for use by the `rand` and `getrandom` crates. | 
|---|
| 60 | pub const INTERNAL_START: u32 = 1 << 31; | 
|---|
| 61 |  | 
|---|
| 62 | /// Codes at or above this point can be used by users to define their own | 
|---|
| 63 | /// custom errors. | 
|---|
| 64 | pub const CUSTOM_START: u32 = (1 << 31) + (1 << 30); | 
|---|
| 65 |  | 
|---|
| 66 | /// Extract the raw OS error code (if this error came from the OS) | 
|---|
| 67 | /// | 
|---|
| 68 | /// This method is identical to [`std::io::Error::raw_os_error()`][1], except | 
|---|
| 69 | /// that it works in `no_std` contexts. If this method returns `None`, the | 
|---|
| 70 | /// error value can still be formatted via the `Display` implementation. | 
|---|
| 71 | /// | 
|---|
| 72 | /// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error | 
|---|
| 73 | #[ inline] | 
|---|
| 74 | pub fn raw_os_error(self) -> Option<i32> { | 
|---|
| 75 | if self.0.get() < Self::INTERNAL_START { | 
|---|
| 76 | match () { | 
|---|
| 77 | #[ cfg(target_os = "solid_asp3")] | 
|---|
| 78 | // On SOLID, negate the error code again to obtain the original | 
|---|
| 79 | // error code. | 
|---|
| 80 | () => Some(-(self.0.get() as i32)), | 
|---|
| 81 | #[ cfg(not(target_os = "solid_asp3"))] | 
|---|
| 82 | () => Some(self.0.get() as i32), | 
|---|
| 83 | } | 
|---|
| 84 | } else { | 
|---|
| 85 | None | 
|---|
| 86 | } | 
|---|
| 87 | } | 
|---|
| 88 |  | 
|---|
| 89 | /// Extract the bare error code. | 
|---|
| 90 | /// | 
|---|
| 91 | /// This code can either come from the underlying OS, or be a custom error. | 
|---|
| 92 | /// Use [`Error::raw_os_error()`] to disambiguate. | 
|---|
| 93 | #[ inline] | 
|---|
| 94 | pub const fn code(self) -> NonZeroU32 { | 
|---|
| 95 | self.0 | 
|---|
| 96 | } | 
|---|
| 97 | } | 
|---|
| 98 |  | 
|---|
| 99 | cfg_if! { | 
|---|
| 100 | if #[cfg(unix)] { | 
|---|
| 101 | fn os_err(errno: i32, buf: &mut [u8]) -> Option<&str> { | 
|---|
| 102 | let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char; | 
|---|
| 103 | if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 { | 
|---|
| 104 | return None; | 
|---|
| 105 | } | 
|---|
| 106 |  | 
|---|
| 107 | // Take up to trailing null byte | 
|---|
| 108 | let n = buf.len(); | 
|---|
| 109 | let idx = buf.iter().position(|&b| b == 0).unwrap_or(n); | 
|---|
| 110 | core::str::from_utf8(&buf[..idx]).ok() | 
|---|
| 111 | } | 
|---|
| 112 | } else { | 
|---|
| 113 | fn os_err(_errno: i32, _buf: &mut [u8]) -> Option<&str> { | 
|---|
| 114 | None | 
|---|
| 115 | } | 
|---|
| 116 | } | 
|---|
| 117 | } | 
|---|
| 118 |  | 
|---|
| 119 | impl fmt::Debug for Error { | 
|---|
| 120 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 
|---|
| 121 | let mut dbg: DebugStruct<'_, '_> = f.debug_struct(name: "Error"); | 
|---|
| 122 | if let Some(errno: i32) = self.raw_os_error() { | 
|---|
| 123 | dbg.field(name: "os_error", &errno); | 
|---|
| 124 | let mut buf: [u8; 128] = [0u8; 128]; | 
|---|
| 125 | if let Some(err: &str) = os_err(errno, &mut buf) { | 
|---|
| 126 | dbg.field(name: "description", &err); | 
|---|
| 127 | } | 
|---|
| 128 | } else if let Some(desc: &'static str) = internal_desc(*self) { | 
|---|
| 129 | dbg.field(name: "internal_code", &self.0.get()); | 
|---|
| 130 | dbg.field(name: "description", &desc); | 
|---|
| 131 | } else { | 
|---|
| 132 | dbg.field(name: "unknown_code", &self.0.get()); | 
|---|
| 133 | } | 
|---|
| 134 | dbg.finish() | 
|---|
| 135 | } | 
|---|
| 136 | } | 
|---|
| 137 |  | 
|---|
| 138 | impl fmt::Display for Error { | 
|---|
| 139 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 
|---|
| 140 | if let Some(errno: i32) = self.raw_os_error() { | 
|---|
| 141 | let mut buf: [u8; 128] = [0u8; 128]; | 
|---|
| 142 | match os_err(errno, &mut buf) { | 
|---|
| 143 | Some(err: &str) => err.fmt(f), | 
|---|
| 144 | None => write!(f, "OS Error: {} ", errno), | 
|---|
| 145 | } | 
|---|
| 146 | } else if let Some(desc: &'static str) = internal_desc(*self) { | 
|---|
| 147 | f.write_str(data:desc) | 
|---|
| 148 | } else { | 
|---|
| 149 | write!(f, "Unknown Error: {} ", self.0.get()) | 
|---|
| 150 | } | 
|---|
| 151 | } | 
|---|
| 152 | } | 
|---|
| 153 |  | 
|---|
| 154 | impl From<NonZeroU32> for Error { | 
|---|
| 155 | fn from(code: NonZeroU32) -> Self { | 
|---|
| 156 | Self(code) | 
|---|
| 157 | } | 
|---|
| 158 | } | 
|---|
| 159 |  | 
|---|
| 160 | fn internal_desc(error: Error) -> Option<&'static str> { | 
|---|
| 161 | match error { | 
|---|
| 162 | Error::UNSUPPORTED => Some( "getrandom: this target is not supported"), | 
|---|
| 163 | Error::ERRNO_NOT_POSITIVE => Some( "errno: did not return a positive value"), | 
|---|
| 164 | Error::UNEXPECTED => Some( "unexpected situation"), | 
|---|
| 165 | Error::IOS_SEC_RANDOM => Some( "SecRandomCopyBytes: iOS Security framework failure"), | 
|---|
| 166 | Error::WINDOWS_RTL_GEN_RANDOM => Some( "RtlGenRandom: Windows system function failure"), | 
|---|
| 167 | Error::FAILED_RDRAND => Some( "RDRAND: failed multiple times: CPU issue likely"), | 
|---|
| 168 | Error::NO_RDRAND => Some( "RDRAND: instruction not supported"), | 
|---|
| 169 | Error::WEB_CRYPTO => Some( "Web Crypto API is unavailable"), | 
|---|
| 170 | Error::WEB_GET_RANDOM_VALUES => Some( "Calling Web API crypto.getRandomValues failed"), | 
|---|
| 171 | Error::VXWORKS_RAND_SECURE => Some( "randSecure: VxWorks RNG module is not initialized"), | 
|---|
| 172 | Error::NODE_CRYPTO => Some( "Node.js crypto CommonJS module is unavailable"), | 
|---|
| 173 | Error::NODE_RANDOM_FILL_SYNC => Some( "Calling Node.js API crypto.randomFillSync failed"), | 
|---|
| 174 | Error::NODE_ES_MODULE => Some( "Node.js ES modules are not directly supported, see https://docs.rs/getrandom#nodejs-es-module-support"), | 
|---|
| 175 | _ => None, | 
|---|
| 176 | } | 
|---|
| 177 | } | 
|---|
| 178 |  | 
|---|
| 179 | #[ cfg(test)] | 
|---|
| 180 | mod tests { | 
|---|
| 181 | use super::Error; | 
|---|
| 182 | use core::mem::size_of; | 
|---|
| 183 |  | 
|---|
| 184 | #[ test] | 
|---|
| 185 | fn test_size() { | 
|---|
| 186 | assert_eq!(size_of::<Error>(), 4); | 
|---|
| 187 | assert_eq!(size_of::<Result<(), Error>>(), 4); | 
|---|
| 188 | } | 
|---|
| 189 | } | 
|---|
| 190 |  | 
|---|