blob: 6615753768aadf3f1dfbbeb30bd0f42c4d107b05 [file] [log] [blame]
Joel Galenson4be0c6d2020-07-07 13:20:14 -07001// 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.
Thiébaud Weksteen9791b302021-03-03 16:30:20 +01008use core::{fmt, num::NonZeroU32};
Joel Galenson4be0c6d2020-07-07 13:20:14 -07009
Thiébaud Weksteen9791b302021-03-03 16:30:20 +010010/// A small and `no_std` compatible error type
Joel Galenson4be0c6d2020-07-07 13:20:14 -070011///
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`].
Thiébaud Weksteen9791b302021-03-03 16:30:20 +010018///
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).
Joel Galenson4be0c6d2020-07-07 13:20:14 -070024#[derive(Copy, Clone, Eq, PartialEq)]
25pub struct Error(NonZeroU32);
26
Thiébaud Weksteen9791b302021-03-03 16:30:20 +010027const 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
Joel Galenson4be0c6d2020-07-07 13:20:14 -070033impl Error {
Thiébaud Weksteen9791b302021-03-03 16:30:20 +010034 /// 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);
Joel Galenson4be0c6d2020-07-07 13:20:14 -070056
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 ///
Thiébaud Weksteen9791b302021-03-03 16:30:20 +010068 /// This method is identical to [`std::io::Error::raw_os_error()`][1], except
Joel Galenson4be0c6d2020-07-07 13:20:14 -070069 /// that it works in `no_std` contexts. If this method returns `None`, the
70 /// error value can still be formatted via the `Display` implementation.
Thiébaud Weksteen9791b302021-03-03 16:30:20 +010071 ///
72 /// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error
Joel Galenson4be0c6d2020-07-07 13:20:14 -070073 #[inline]
74 pub fn raw_os_error(self) -> Option<i32> {
75 if self.0.get() < Self::INTERNAL_START {
David LeGare010ab1d2022-03-02 16:21:18 +000076 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 }
Joel Galenson4be0c6d2020-07-07 13:20:14 -070084 } 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]
Thiébaud Weksteen9791b302021-03-03 16:30:20 +010094 pub const fn code(self) -> NonZeroU32 {
Joel Galenson4be0c6d2020-07-07 13:20:14 -070095 self.0
96 }
97}
98
99cfg_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 if #[cfg(target_os = "wasi")] {
113 fn os_err(errno: i32, _buf: &mut [u8]) -> Option<wasi::Error> {
114 wasi::Error::from_raw_error(errno as _)
115 }
116 } else {
117 fn os_err(_errno: i32, _buf: &mut [u8]) -> Option<&str> {
118 None
119 }
120 }
121}
122
123impl fmt::Debug for Error {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 let mut dbg = f.debug_struct("Error");
126 if let Some(errno) = self.raw_os_error() {
127 dbg.field("os_error", &errno);
128 let mut buf = [0u8; 128];
129 if let Some(err) = os_err(errno, &mut buf) {
130 dbg.field("description", &err);
131 }
132 } else if let Some(desc) = internal_desc(*self) {
133 dbg.field("internal_code", &self.0.get());
134 dbg.field("description", &desc);
135 } else {
136 dbg.field("unknown_code", &self.0.get());
137 }
138 dbg.finish()
139 }
140}
141
142impl fmt::Display for Error {
143 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144 if let Some(errno) = self.raw_os_error() {
145 let mut buf = [0u8; 128];
146 match os_err(errno, &mut buf) {
147 Some(err) => err.fmt(f),
148 None => write!(f, "OS Error: {}", errno),
149 }
150 } else if let Some(desc) = internal_desc(*self) {
151 f.write_str(desc)
152 } else {
153 write!(f, "Unknown Error: {}", self.0.get())
154 }
155 }
156}
157
158impl From<NonZeroU32> for Error {
159 fn from(code: NonZeroU32) -> Self {
160 Self(code)
161 }
162}
163
Joel Galenson4be0c6d2020-07-07 13:20:14 -0700164fn internal_desc(error: Error) -> Option<&'static str> {
165 match error {
Thiébaud Weksteen9791b302021-03-03 16:30:20 +0100166 Error::UNSUPPORTED => Some("getrandom: this target is not supported"),
167 Error::ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"),
168 Error::IOS_SEC_RANDOM => Some("SecRandomCopyBytes: iOS Security framework failure"),
169 Error::WINDOWS_RTL_GEN_RANDOM => Some("RtlGenRandom: Windows system function failure"),
170 Error::FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"),
171 Error::NO_RDRAND => Some("RDRAND: instruction not supported"),
Joel Galensondf760972021-06-21 12:36:13 -0700172 Error::WEB_CRYPTO => Some("Web Crypto API is unavailable"),
Thiébaud Weksteen9791b302021-03-03 16:30:20 +0100173 Error::WEB_GET_RANDOM_VALUES => Some("Web API crypto.getRandomValues is unavailable"),
174 Error::VXWORKS_RAND_SECURE => Some("randSecure: VxWorks RNG module is not initialized"),
175 Error::NODE_CRYPTO => Some("Node.js crypto module is unavailable"),
176 Error::NODE_RANDOM_FILL_SYNC => Some("Node.js API crypto.randomFillSync is unavailable"),
Joel Galenson4be0c6d2020-07-07 13:20:14 -0700177 _ => None,
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::Error;
184 use core::mem::size_of;
185
186 #[test]
187 fn test_size() {
188 assert_eq!(size_of::<Error>(), 4);
189 assert_eq!(size_of::<Result<(), Error>>(), 4);
190 }
191}