]>
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 | ||
9 | //! An implementation which calls out to an externally defined function. | |
10 | use crate::Error; | |
11 | use core::num::NonZeroU32; | |
12 | ||
13 | /// Register a function to be invoked by `getrandom` on unsupported targets. | |
14 | /// | |
15 | /// *This API requires the `"custom"` Cargo feature to be activated*. | |
16 | /// | |
17 | /// ## Writing a custom `getrandom` implementation | |
18 | /// | |
19 | /// The function to register must have the same signature as | |
20 | /// [`getrandom::getrandom`](crate::getrandom). The function can be defined | |
21 | /// wherever you want, either in root crate or a dependant crate. | |
22 | /// | |
23 | /// For example, if we wanted a `failure-getrandom` crate containing an | |
24 | /// implementation that always fails, we would first depend on `getrandom` | |
25 | /// (for the [`Error`] type) in `failure-getrandom/Cargo.toml`: | |
26 | /// ```toml | |
27 | /// [dependencies] | |
28 | /// getrandom = "0.2" | |
29 | /// ``` | |
30 | /// Note that the crate containing this function does **not** need to enable the | |
31 | /// `"custom"` Cargo feature. | |
32 | /// | |
33 | /// Next, in `failure-getrandom/src/lib.rs`, we define our function: | |
34 | /// ```rust | |
35 | /// use core::num::NonZeroU32; | |
36 | /// use getrandom::Error; | |
37 | /// | |
38 | /// // Some application-specific error code | |
39 | /// const MY_CUSTOM_ERROR_CODE: u32 = Error::CUSTOM_START + 42; | |
40 | /// pub fn always_fail(buf: &mut [u8]) -> Result<(), Error> { | |
41 | /// let code = NonZeroU32::new(MY_CUSTOM_ERROR_CODE).unwrap(); | |
42 | /// Err(Error::from(code)) | |
43 | /// } | |
44 | /// ``` | |
45 | /// | |
46 | /// ## Registering a custom `getrandom` implementation | |
47 | /// | |
48 | /// Functions can only be registered in the root binary crate. Attempting to | |
49 | /// register a function in a non-root crate will result in a linker error. | |
50 | /// This is similar to | |
51 | /// [`#[panic_handler]`](https://doc.rust-lang.org/nomicon/panic-handler.html) or | |
52 | /// [`#[global_allocator]`](https://doc.rust-lang.org/edition-guide/rust-2018/platform-and-target-support/global-allocators.html), | |
53 | /// where helper crates define handlers/allocators but only the binary crate | |
54 | /// actually _uses_ the functionality. | |
55 | /// | |
56 | /// To register the function, we first depend on `failure-getrandom` _and_ | |
57 | /// `getrandom` in `Cargo.toml`: | |
58 | /// ```toml | |
59 | /// [dependencies] | |
60 | /// failure-getrandom = "0.1" | |
61 | /// getrandom = { version = "0.2", features = ["custom"] } | |
62 | /// ``` | |
63 | /// | |
64 | /// Then, we register the function in `src/main.rs`: | |
65 | /// ```rust | |
66 | /// # mod failure_getrandom { pub fn always_fail(_: &mut [u8]) -> Result<(), getrandom::Error> { unimplemented!() } } | |
67 | /// use failure_getrandom::always_fail; | |
68 | /// use getrandom::register_custom_getrandom; | |
69 | /// | |
70 | /// register_custom_getrandom!(always_fail); | |
71 | /// ``` | |
72 | /// | |
73 | /// Now any user of `getrandom` (direct or indirect) on this target will use the | |
74 | /// registered function. As noted in the | |
75 | /// [top-level documentation](index.html#use-a-custom-implementation) this | |
76 | /// registration only has an effect on unsupported targets. | |
77 | #[macro_export] | |
78 | macro_rules! register_custom_getrandom { | |
79 | ($path:path) => { | |
80 | // We use an extern "C" function to get the guarantees of a stable ABI. | |
81 | #[no_mangle] | |
82 | extern "C" fn __getrandom_custom(dest: *mut u8, len: usize) -> u32 { | |
83 | let f: fn(&mut [u8]) -> Result<(), ::getrandom::Error> = $path; | |
84 | let slice = unsafe { ::core::slice::from_raw_parts_mut(dest, len) }; | |
85 | match f(slice) { | |
86 | Ok(()) => 0, | |
87 | Err(e) => e.code().get(), | |
88 | } | |
89 | } | |
90 | }; | |
91 | } | |
92 | ||
93 | #[allow(dead_code)] | |
94 | pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { | |
95 | extern "C" { | |
96 | fn __getrandom_custom(dest: *mut u8, len: usize) -> u32; | |
97 | } | |
98 | let ret = unsafe { __getrandom_custom(dest.as_mut_ptr(), dest.len()) }; | |
99 | match NonZeroU32::new(ret) { | |
100 | None => Ok(()), | |
101 | Some(code) => Err(Error::from(code)), | |
102 | } | |
103 | } |