]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | // Copyright 2018 Developers of the Rand project. |
2 | // | |
3 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
4 | // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
5 | // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your | |
6 | // option. This file may not be copied, modified, or distributed | |
7 | // except according to those terms. | |
8 | use core::{fmt, num::NonZeroU32}; | |
9 | ||
10 | /// A small and `no_std` compatible error type | |
11 | /// | |
12 | /// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and | |
13 | /// if so, which error code the OS gave the application. If such an error is | |
14 | /// encountered, please consult with your system documentation. | |
15 | /// | |
16 | /// Internally this type is a NonZeroU32, with certain values reserved for | |
17 | /// certain purposes, see [`Error::INTERNAL_START`] and [`Error::CUSTOM_START`]. | |
18 | /// | |
19 | /// *If this crate's `"std"` Cargo feature is enabled*, then: | |
20 | /// - [`getrandom::Error`][Error] implements | |
21 | /// [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html) | |
22 | /// - [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) implements | |
23 | /// [`From<getrandom::Error>`](https://doc.rust-lang.org/std/convert/trait.From.html). | |
24 | #[derive(Copy, Clone, Eq, PartialEq)] | |
25 | pub struct Error(NonZeroU32); | |
26 | ||
27 | const fn internal_error(n: u16) -> Error { | |
28 | // SAFETY: code > 0 as INTERNAL_START > 0 and adding n won't overflow a u32. | |
29 | let code = Error::INTERNAL_START + (n as u32); | |
30 | Error(unsafe { NonZeroU32::new_unchecked(code) }) | |
31 | } | |
32 | ||
33 | impl Error { | |
34 | /// This target/platform is not supported by `getrandom`. | |
35 | pub const UNSUPPORTED: Error = internal_error(0); | |
36 | /// The platform-specific `errno` returned a non-positive value. | |
37 | pub const ERRNO_NOT_POSITIVE: Error = internal_error(1); | |
38 | /// Call to iOS [`SecRandomCopyBytes`](https://developer.apple.com/documentation/security/1399291-secrandomcopybytes) failed. | |
39 | pub const IOS_SEC_RANDOM: Error = internal_error(3); | |
40 | /// Call to Windows [`RtlGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom) failed. | |
41 | pub const WINDOWS_RTL_GEN_RANDOM: Error = internal_error(4); | |
42 | /// RDRAND instruction failed due to a hardware issue. | |
43 | pub const FAILED_RDRAND: Error = internal_error(5); | |
44 | /// RDRAND instruction unsupported on this target. | |
45 | pub const NO_RDRAND: Error = internal_error(6); | |
46 | /// The browser does not have support for `self.crypto`. | |
47 | pub const WEB_CRYPTO: Error = internal_error(7); | |
48 | /// The browser does not have support for `crypto.getRandomValues`. | |
49 | pub const WEB_GET_RANDOM_VALUES: Error = internal_error(8); | |
50 | /// On VxWorks, call to `randSecure` failed (random number generator is not yet initialized). | |
51 | pub const VXWORKS_RAND_SECURE: Error = internal_error(11); | |
52 | /// NodeJS does not have support for the `crypto` module. | |
53 | pub const NODE_CRYPTO: Error = internal_error(12); | |
54 | /// NodeJS does not have support for `crypto.randomFillSync`. | |
55 | pub const NODE_RANDOM_FILL_SYNC: Error = internal_error(13); | |
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 | Some(self.0.get() as i32) | |
77 | } else { | |
78 | None | |
79 | } | |
80 | } | |
81 | ||
82 | /// Extract the bare error code. | |
83 | /// | |
84 | /// This code can either come from the underlying OS, or be a custom error. | |
85 | /// Use [`Error::raw_os_error()`] to disambiguate. | |
86 | #[inline] | |
87 | pub const fn code(self) -> NonZeroU32 { | |
88 | self.0 | |
89 | } | |
90 | } | |
91 | ||
92 | cfg_if! { | |
93 | if #[cfg(unix)] { | |
94 | fn os_err(errno: i32, buf: &mut [u8]) -> Option<&str> { | |
95 | let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char; | |
96 | if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 { | |
97 | return None; | |
98 | } | |
99 | ||
100 | // Take up to trailing null byte | |
101 | let n = buf.len(); | |
102 | let idx = buf.iter().position(|&b| b == 0).unwrap_or(n); | |
103 | core::str::from_utf8(&buf[..idx]).ok() | |
104 | } | |
105 | } else if #[cfg(target_os = "wasi")] { | |
106 | fn os_err(errno: i32, _buf: &mut [u8]) -> Option<wasi::Error> { | |
107 | wasi::Error::from_raw_error(errno as _) | |
108 | } | |
109 | } else { | |
110 | fn os_err(_errno: i32, _buf: &mut [u8]) -> Option<&str> { | |
111 | None | |
112 | } | |
113 | } | |
114 | } | |
115 | ||
116 | impl fmt::Debug for Error { | |
117 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
118 | let mut dbg = f.debug_struct("Error"); | |
119 | if let Some(errno) = self.raw_os_error() { | |
120 | dbg.field("os_error", &errno); | |
121 | let mut buf = [0u8; 128]; | |
122 | if let Some(err) = os_err(errno, &mut buf) { | |
123 | dbg.field("description", &err); | |
124 | } | |
125 | } else if let Some(desc) = internal_desc(*self) { | |
126 | dbg.field("internal_code", &self.0.get()); | |
127 | dbg.field("description", &desc); | |
128 | } else { | |
129 | dbg.field("unknown_code", &self.0.get()); | |
130 | } | |
131 | dbg.finish() | |
132 | } | |
133 | } | |
134 | ||
135 | impl fmt::Display for Error { | |
136 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
137 | if let Some(errno) = self.raw_os_error() { | |
138 | let mut buf = [0u8; 128]; | |
139 | match os_err(errno, &mut buf) { | |
140 | Some(err) => err.fmt(f), | |
141 | None => write!(f, "OS Error: {}", errno), | |
142 | } | |
143 | } else if let Some(desc) = internal_desc(*self) { | |
144 | f.write_str(desc) | |
145 | } else { | |
146 | write!(f, "Unknown Error: {}", self.0.get()) | |
147 | } | |
148 | } | |
149 | } | |
150 | ||
151 | impl From<NonZeroU32> for Error { | |
152 | fn from(code: NonZeroU32) -> Self { | |
153 | Self(code) | |
154 | } | |
155 | } | |
156 | ||
157 | fn internal_desc(error: Error) -> Option<&'static str> { | |
158 | match error { | |
159 | Error::UNSUPPORTED => Some("getrandom: this target is not supported"), | |
160 | Error::ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"), | |
161 | Error::IOS_SEC_RANDOM => Some("SecRandomCopyBytes: iOS Security framework failure"), | |
162 | Error::WINDOWS_RTL_GEN_RANDOM => Some("RtlGenRandom: Windows system function failure"), | |
163 | Error::FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"), | |
164 | Error::NO_RDRAND => Some("RDRAND: instruction not supported"), | |
165 | Error::WEB_CRYPTO => Some("Web API self.crypto is unavailable"), | |
166 | Error::WEB_GET_RANDOM_VALUES => Some("Web API crypto.getRandomValues is unavailable"), | |
167 | Error::VXWORKS_RAND_SECURE => Some("randSecure: VxWorks RNG module is not initialized"), | |
168 | Error::NODE_CRYPTO => Some("Node.js crypto module is unavailable"), | |
169 | Error::NODE_RANDOM_FILL_SYNC => Some("Node.js API crypto.randomFillSync is unavailable"), | |
170 | _ => None, | |
171 | } | |
172 | } | |
173 | ||
174 | #[cfg(test)] | |
175 | mod tests { | |
176 | use super::Error; | |
177 | use core::mem::size_of; | |
178 | ||
179 | #[test] | |
180 | fn test_size() { | |
181 | assert_eq!(size_of::<Error>(), 4); | |
182 | assert_eq!(size_of::<Result<(), Error>>(), 4); | |
183 | } | |
184 | } |