From 8fff331bb095dc6f5e2fe2ecfc9ab08ea9e2fe97 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 8 Feb 2018 20:06:19 +0000 Subject: rust: Add macro for passing static borrowed strings from Rust to C. * ADD a new macro, tor_util::string::cstr!() which takes Rust strings, concatenates them together, appends a NUL byte, and converts it into a std::ffi::CStr for handing to C. --- src/rust/tor_util/strings.rs | 136 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) (limited to 'src/rust/tor_util/strings.rs') diff --git a/src/rust/tor_util/strings.rs b/src/rust/tor_util/strings.rs index 9321ce4f85..659e792a34 100644 --- a/src/rust/tor_util/strings.rs +++ b/src/rust/tor_util/strings.rs @@ -80,3 +80,139 @@ pub fn empty_static_cstr() -> &'static CStr { empty } + +/// Create a `CStr` from a literal byte slice, appending a NUL byte to it first. +/// +/// # Warning +/// +/// The literal byte slice which is taken as an argument *MUST NOT* have any NUL +/// bytes (`b"\0"`) in it, anywhere, or else an empty string will be returned +/// (`CStr::from_bytes_with_nul_unchecked(b"\0")`) so as to avoid `panic!()`ing. +/// +/// # Examples +/// +/// ``` +/// #[macro_use] +/// extern crate tor_util; +/// +/// use std::ffi::CStr; +/// +/// # fn do_test() -> Result<&'static CStr, &'static str> { +/// let message: &'static str = "This is a test of the tsunami warning system."; +/// let tuesday: &'static CStr; +/// let original: &str; +/// +/// tuesday = cstr!("This is a test of the tsunami warning system."); +/// original = tuesday.to_str().or(Err("Couldn't unwrap CStr!"))?; +/// +/// assert!(original == message); +/// # +/// # Ok(tuesday) +/// # } +/// # fn main() { +/// # do_test(); // so that we can use the ? operator in the test +/// # } +/// ``` +/// It is also possible to pass several string literals to this macro. They +/// will be concatenated together in the order of the arguments, unmodified, +/// before finally being suffixed with a NUL byte: +/// +/// ``` +/// #[macro_use] +/// extern crate tor_util; +/// # +/// # use std::ffi::CStr; +/// # +/// # fn do_test() -> Result<&'static CStr, &'static str> { +/// +/// let quux: &'static CStr = cstr!("foo", "bar", "baz"); +/// let orig: &'static str = quux.to_str().or(Err("Couldn't unwrap CStr!"))?; +/// +/// assert!(orig == "foobarbaz"); +/// # Ok(quux) +/// # } +/// # fn main() { +/// # do_test(); // so that we can use the ? operator in the test +/// # } +/// ``` +/// This is useful for passing static strings to C from Rust FFI code. To do so +/// so, use the `.as_ptr()` method on the resulting `&'static CStr` to convert +/// it to the Rust equivalent of a C `const char*`: +/// +/// ``` +/// #[macro_use] +/// extern crate tor_util; +/// +/// use std::ffi::CStr; +/// use std::os::raw::c_char; +/// +/// pub extern "C" fn give_static_borrowed_string_to_c() -> *const c_char { +/// let hello: &'static CStr = cstr!("Hello, language my parents wrote."); +/// +/// hello.as_ptr() +/// } +/// # fn main() { +/// # let greetings = give_static_borrowed_string_to_c(); +/// # } +/// ``` +/// Note that the C code this static borrowed string is passed to *MUST NOT* +/// attempt to free the memory for the string. +/// +/// # Note +/// +/// An unfortunate limitation of the rustc compiler (as of 1.25.0-nightly), is +/// that the above code compiles, however if we were to change the assignment of +/// `tuesday` as follows, it will fail to compile, because Rust macros are +/// expanded at parse time, and at parse time there is no symbols table +/// available. +/// +/// ```ignore +/// tuesday = cstr!(message); +/// ``` +/// with the error message `error: expected a literal`. +/// +/// # Returns +/// +/// If the string literals passed as arguments contain no NUL bytes anywhere, +/// then an `&'static CStr` containing the (concatenated) bytes of the string +/// literal(s) passed as arguments, with a NUL byte appended, is returned. +/// Otherwise, an `&'static CStr` containing a single NUL byte is returned (an +/// "empty" string in C). +#[macro_export] +macro_rules! cstr { + ($($bytes:expr),*) => ( + ::std::ffi::CStr::from_bytes_with_nul( + concat!($($bytes),*, "\0").as_bytes() + ).unwrap_or( + unsafe{ + ::std::ffi::CStr::from_bytes_with_nul_unchecked(b"\0") + } + ) + ) +} + +#[cfg(test)] +mod test { + use std::ffi::CStr; + + #[test] + fn cstr_macro() { + let _: &'static CStr = cstr!("boo"); + } + + #[test] + fn cstr_macro_multi_input() { + let quux: &'static CStr = cstr!("foo", "bar", "baz"); + + assert!(quux.to_str().unwrap() == "foobarbaz"); + } + + #[test] + fn cstr_macro_bad_input() { + let waving: &'static CStr = cstr!("waving not drowning o/"); + let drowning: &'static CStr = cstr!("\0 drowning not waving"); + + assert!(waving.to_str().unwrap() == "waving not drowning o/"); + assert!(drowning.to_str().unwrap() == "") + } +} -- cgit v1.2.3-54-g00ecf ee/src/test?id=01bda6c23f58947ad1e20ea6367a5c260f53dfab'>test/test_rng.c
blob: 6b830eda1508b6c5eca976e2016f6fb384773606 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/* Copyright (c) 2016-2021, The Tor Project, Inc. */
/* See LICENSE for licensing information */

/*
 * Example usage:
 *
 * ./src/test/test-rng --emit | dieharder -g 200 -a
 *
 * Remember, dieharder can tell you that your RNG is completely broken, but if
 * your RNG is not _completely_ broken, dieharder cannot tell you whether your
 * RNG is actually secure.
 */

#include "orconfig.h"

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include "lib/crypt_ops/crypto_rand.h"

int
main(int argc, char **argv)
{
  uint8_t buf[0x123];

  if (argc != 2 || strcmp(argv[1], "--emit")) {
    fprintf(stderr, "If you want me to fill stdout with a bunch of random "
            "bytes, you need to say --emit.\n");
    return 1;
  }

  if (crypto_seed_rng() < 0) {
    fprintf(stderr, "Can't seed RNG.\n");
    return 1;
  }

#if 0
  while (1) {
    crypto_rand(buf, sizeof(buf));
    if (write(1 /*stdout*/, buf, sizeof(buf)) != sizeof(buf)) {
      fprintf(stderr, "write() failed: %s\n", strerror(errno));
      return 1;
    }
  }
#endif /* 0 */

  crypto_fast_rng_t *rng = crypto_fast_rng_new();
  while (1) {
    crypto_fast_rng_getbytes(rng, buf, sizeof(buf));
    if (write(1 /*stdout*/, buf, sizeof(buf)) != sizeof(buf)) {
      fprintf(stderr, "write() failed: %s\n", strerror(errno));
      return 1;
    }
  }
}