diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/common/compat_rust.c | 39 | ||||
-rw-r--r-- | src/common/compat_rust.h | 28 | ||||
-rw-r--r-- | src/common/include.am | 6 | ||||
-rw-r--r-- | src/or/main.c | 10 | ||||
-rw-r--r-- | src/rust/Cargo.lock | 14 | ||||
-rw-r--r-- | src/rust/Cargo.toml | 14 | ||||
-rw-r--r-- | src/rust/include.am | 5 | ||||
-rw-r--r-- | src/rust/tor_util/Cargo.toml | 13 | ||||
-rw-r--r-- | src/rust/tor_util/ffi.rs | 56 | ||||
-rw-r--r-- | src/rust/tor_util/include.am | 12 | ||||
-rw-r--r-- | src/rust/tor_util/lib.rs | 13 | ||||
-rw-r--r-- | src/rust/tor_util/rust_string.rs | 101 | ||||
-rw-r--r-- | src/rust/tor_util/tests/rust_string.rs | 37 | ||||
-rw-r--r-- | src/test/include.am | 1 | ||||
-rw-r--r-- | src/test/test.c | 1 | ||||
-rw-r--r-- | src/test/test.h | 1 | ||||
-rw-r--r-- | src/test/test_rust.c | 31 |
17 files changed, 382 insertions, 0 deletions
diff --git a/src/common/compat_rust.c b/src/common/compat_rust.c new file mode 100644 index 0000000000..366fd4037b --- /dev/null +++ b/src/common/compat_rust.c @@ -0,0 +1,39 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file rust_compat.c + * \brief Rust FFI compatibility functions and helpers. This file is only built + * if Rust is not used. + **/ + +#include "compat_rust.h" +#include "util.h" + +/** + * Free storage pointed to by <b>str</b>, and itself. + */ +void +rust_str_free(rust_str_t str) +{ + char *s = (char *)str; + tor_free(s); +} + +/** + * Return zero-terminated contained string. + */ +const char * +rust_str_get(const rust_str_t str) +{ + return (const char *)str; +} + +/* If we were using Rust, we'd say so on startup. */ +rust_str_t +rust_welcome_string(void) +{ + char *s = tor_malloc_zero(1); + return (rust_str_t)s; +} + diff --git a/src/common/compat_rust.h b/src/common/compat_rust.h new file mode 100644 index 0000000000..752a29b56c --- /dev/null +++ b/src/common/compat_rust.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file rust_compat.h + * \brief Headers for rust_compat.c + **/ + +#ifndef TOR_RUST_COMPAT_H +#define TOR_RUST_COMPAT_H + +#include "torint.h" + +/** + * Strings allocated in Rust must be freed from Rust code again. Let's make + * it less likely to accidentally mess up and call tor_free() on it, because + * currently it'll just work but might break at any time. + */ +typedef uintptr_t rust_str_t; + +void rust_str_free(rust_str_t); + +const char *rust_str_get(const rust_str_t); + +rust_str_t rust_welcome_string(void); + +#endif + diff --git a/src/common/include.am b/src/common/include.am index e285ef5f86..b37b363b82 100644 --- a/src/common/include.am +++ b/src/common/include.am @@ -100,6 +100,11 @@ LIBOR_A_SRC = \ $(threads_impl_source) \ $(readpassphrase_source) +if USE_RUST +else +LIBOR_A_SRC += src/common/compat_rust.c +endif + src/common/src_common_libor_testing_a-log.$(OBJEXT) \ src/common/log.$(OBJEXT): micro-revision.i @@ -146,6 +151,7 @@ COMMONHEADERS = \ src/common/compat.h \ src/common/compat_libevent.h \ src/common/compat_openssl.h \ + src/common/compat_rust.h \ src/common/compat_threads.h \ src/common/compat_time.h \ src/common/compress.h \ diff --git a/src/or/main.c b/src/or/main.c index 0da43dc232..dcd7ef215e 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -58,6 +58,7 @@ #include "circuitlist.h" #include "circuituse.h" #include "command.h" +#include "compat_rust.h" #include "compress.h" #include "config.h" #include "confparse.h" @@ -3039,6 +3040,15 @@ tor_init(int argc, char *argv[]) "Expect more bugs than usual."); } + { + rust_str_t rust_str = rust_welcome_string(); + const char *s = rust_str_get(rust_str); + if (strlen(s) > 0) { + log_notice(LD_GENERAL, "%s", s); + } + rust_str_free(rust_str); + } + if (network_init()<0) { log_err(LD_BUG,"Error initializing network; exiting."); return -1; diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock new file mode 100644 index 0000000000..4ac9606ce8 --- /dev/null +++ b/src/rust/Cargo.lock @@ -0,0 +1,14 @@ +[root] +name = "tor_util" +version = "0.0.1" +dependencies = [ + "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libc" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)" = "babb8281da88cba992fa1f4ddec7d63ed96280a1a53ec9b919fd37b53d71e502" diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml new file mode 100644 index 0000000000..527c536323 --- /dev/null +++ b/src/rust/Cargo.toml @@ -0,0 +1,14 @@ +[workspace] +members = ["tor_util"] + +[profile.release] +debug = true +panic = "abort" + +[source.crates-io] +registry = 'https://github.com/rust-lang/crates.io-index' +replace-with = 'vendored-sources' + +[source.vendored-sources] +directory = 'vendor' + diff --git a/src/rust/include.am b/src/rust/include.am new file mode 100644 index 0000000000..e198049071 --- /dev/null +++ b/src/rust/include.am @@ -0,0 +1,5 @@ +include src/rust/tor_util/include.am + +EXTRA_DIST +=\ + src/rust/Cargo.toml \ + src/rust/Cargo.lock diff --git a/src/rust/tor_util/Cargo.toml b/src/rust/tor_util/Cargo.toml new file mode 100644 index 0000000000..f175fbdfb0 --- /dev/null +++ b/src/rust/tor_util/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["The Tor Project"] +name = "tor_util" +version = "0.0.1" + +[lib] +name = "tor_util" +path = "lib.rs" +crate_type = ["rlib", "staticlib"] + +[dependencies] +libc = "*" + diff --git a/src/rust/tor_util/ffi.rs b/src/rust/tor_util/ffi.rs new file mode 100644 index 0000000000..af4bfc41af --- /dev/null +++ b/src/rust/tor_util/ffi.rs @@ -0,0 +1,56 @@ +//! FFI functions, only to be called from C. +//! +//! Equivalent C versions of these live in `src/common/compat_rust.c` + +use std::mem::forget; +use std::ffi::CString; + +use libc; +use rust_string::RustString; + +/// Free the passed `RustString` (`rust_str_t` in C), to be used in place of +/// `tor_free`(). +/// +/// # Examples +/// ```c +/// rust_str_t r_s = rust_welcome_string(); +/// rust_str_free(r_s); +/// ``` +#[no_mangle] +#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))] +pub unsafe extern "C" fn rust_str_free(_str: RustString) { + // Empty body: Just drop _str and we're done (Drop takes care of it). +} + +/// Lends an immutable, NUL-terminated C String. +/// +/// # Examples +/// ```c +/// rust_str_t r_s = rust_welcome_string(); +/// const char *s = rust_str_get(r_s); +/// printf("%s", s); +/// rust_str_free(r_s); +/// ``` +#[no_mangle] +pub unsafe extern "C" fn rust_str_get(str: RustString) -> *const libc::c_char { + let res = str.as_ptr(); + forget(str); + res +} + +/// Returns a short string to announce Rust support during startup. +/// +/// # Examples +/// ```c +/// rust_str_t r_s = rust_welcome_string(); +/// const char *s = rust_str_get(r_s); +/// printf("%s", s); +/// rust_str_free(r_s); +/// ``` +#[no_mangle] +pub extern "C" fn rust_welcome_string() -> RustString { + let s = CString::new("Tor is running with Rust integration. Please report \ + any bugs you encouter.") + .unwrap(); + RustString::from(s) +} diff --git a/src/rust/tor_util/include.am b/src/rust/tor_util/include.am new file mode 100644 index 0000000000..3b877649a3 --- /dev/null +++ b/src/rust/tor_util/include.am @@ -0,0 +1,12 @@ +EXTRA_DIST +=\ + src/rust/tor_util/Cargo.toml \ + src/rust/tor_util/lib.rs \ + src/rust/tor_util/ffi.rs \ + src/rust/tor_util/rust_string.rs + +src/rust/target/release/libtor_util.a: FORCE + ( cd "$(abs_top_srcdir)/src/rust/tor_util" ; \ + CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \ + $(CARGO) build --release --quiet --frozen ) + +FORCE: diff --git a/src/rust/tor_util/lib.rs b/src/rust/tor_util/lib.rs new file mode 100644 index 0000000000..79d583d1ae --- /dev/null +++ b/src/rust/tor_util/lib.rs @@ -0,0 +1,13 @@ +//! C <-> Rust compatibility helpers and types. +//! +//! Generically useful, small scale helpers should go here. This goes for both +//! the C side (in the form of the ffi module) as well as the Rust side +//! (individual modules per functionality). The corresponding C stuff lives in +//! `src/common/compat_rust.{c,h}`. + +extern crate libc; + +mod rust_string; +pub mod ffi; + +pub use rust_string::*; diff --git a/src/rust/tor_util/rust_string.rs b/src/rust/tor_util/rust_string.rs new file mode 100644 index 0000000000..46ec3fd7a8 --- /dev/null +++ b/src/rust/tor_util/rust_string.rs @@ -0,0 +1,101 @@ +use std::ffi::CString; +use std::mem::forget; +use libc; + +/// Compatibility wrapper for strings allocated in Rust and passed to C. +/// +/// Rust doesn't ensure the safety of freeing memory across an FFI boundary, so +/// we need to take special care to ensure we're not accidentally calling +/// `tor_free`() on any string allocated in Rust. To more easily differentiate +/// between strings that possibly (if Rust support is enabled) were allocated +/// in Rust, C has the `rust_str_t` helper type. The equivalent on the Rust +/// side is `RustString`. +/// +/// Note: This type must not be used for strings allocated in C. +#[repr(C)] +#[derive(Debug)] +pub struct RustString(*mut libc::c_char); + +impl RustString { + /// Returns a pointer to the underlying NUL-terminated byte array. + /// + /// Note that this function is not typically useful for Rust callers, + /// except in a direct FFI context. + /// + /// # Examples + /// ``` + /// # use tor_util::RustString; + /// use std::ffi::CString; + /// + /// let r = RustString::from(CString::new("asdf").unwrap()); + /// let c_str = r.as_ptr(); + /// assert_eq!(b'a', unsafe { *c_str as u8}); + /// ``` + pub fn as_ptr(&self) -> *const libc::c_char { + self.0 as *const libc::c_char + } +} + +impl From<CString> for RustString { + /// Constructs a new `RustString` + /// + /// # Examples + /// ``` + /// # use tor_util::RustString; + /// use std::ffi::CString; + /// + /// let r = RustString::from(CString::new("asdf").unwrap()); + /// ``` + fn from(str: CString) -> RustString { + RustString(str.into_raw()) + } +} + +impl Into<CString> for RustString { + /// Reconstructs a `CString` from this `RustString`. + /// + /// Useful to take ownership back from a `RustString` that was given to C + /// code. + /// + /// # Examples + /// ``` + /// # use tor_util::RustString; + /// use std::ffi::CString; + /// + /// let cs = CString::new("asdf").unwrap(); + /// let r = RustString::from(cs.clone()); + /// let cs2 = r.into(); + /// assert_eq!(cs, cs2); + /// ``` + fn into(self) -> CString { + // Calling from_raw is always OK here: We only construct self using + // valid CStrings and don't expose anything that could mutate it + let ret = unsafe { CString::from_raw(self.0) }; + forget(self); + ret + } +} + +impl Drop for RustString { + fn drop(&mut self) { + // Don't use into() here, because we would need to move out of + // self. Same safety consideration. Immediately drop the created + // CString, which takes care of freeing the wrapped string. + unsafe { CString::from_raw(self.0) }; + } +} + +#[cfg(test)] +mod test { + use std::mem; + use super::*; + + use libc; + + /// Ensures we're not adding overhead by using RustString. + #[test] + fn size_of() { + assert_eq!(mem::size_of::<*mut libc::c_char>(), + mem::size_of::<RustString>()) + } +} diff --git a/src/rust/tor_util/tests/rust_string.rs b/src/rust/tor_util/tests/rust_string.rs new file mode 100644 index 0000000000..1ff605a43c --- /dev/null +++ b/src/rust/tor_util/tests/rust_string.rs @@ -0,0 +1,37 @@ +extern crate tor_util; +extern crate libc; + +use std::ffi::CString; +use tor_util::RustString; + +#[test] +fn rust_string_conversions_preserve_c_string() { + let s = CString::new("asdf foo").unwrap(); + let r = RustString::from(s.clone()); + let r2 = RustString::from(s.clone()); + let c = r2.as_ptr(); + assert_eq!(unsafe { libc::strlen(c) }, 8); + let c_str = r.into(); + assert_eq!(s, c_str); +} + +#[test] +fn empty_string() { + let s = CString::new("").unwrap(); + let r = RustString::from(s.clone()); + let c = r.as_ptr(); + assert_eq!(unsafe { libc::strlen(c) }, 0); + let c_str = r.into(); + assert_eq!(s, c_str); +} + +#[test] +fn c_string_with_unicode() { + // The euro sign is three bytes + let s = CString::new("asd€asd").unwrap(); + let r = RustString::from(s.clone()); + let c = r.as_ptr(); + assert_eq!(unsafe { libc::strlen(c) }, 9); + let c_str = r.into(); + assert_eq!(s, c_str); +} diff --git a/src/test/include.am b/src/test/include.am index 8a465877d3..438eaddba8 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -130,6 +130,7 @@ src_test_test_SOURCES = \ src/test/test_routerkeys.c \ src/test/test_routerlist.c \ src/test/test_routerset.c \ + src/test/test_rust.c \ src/test/test_scheduler.c \ src/test/test_shared_random.c \ src/test/test_socks.c \ diff --git a/src/test/test.c b/src/test/test.c index 4d2cf1536b..18805cb3b7 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1233,6 +1233,7 @@ struct testgroup_t testgroups[] = { { "routerkeys/", routerkeys_tests }, { "routerlist/", routerlist_tests }, { "routerset/" , routerset_tests }, + { "rust/", rust_tests }, { "scheduler/", scheduler_tests }, { "socks/", socks_tests }, { "shared-random/", sr_tests }, diff --git a/src/test/test.h b/src/test/test.h index 3d7d05e771..3dc1c332c7 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -231,6 +231,7 @@ extern struct testcase_t router_tests[]; extern struct testcase_t routerkeys_tests[]; extern struct testcase_t routerlist_tests[]; extern struct testcase_t routerset_tests[]; +extern struct testcase_t rust_tests[]; extern struct testcase_t scheduler_tests[]; extern struct testcase_t storagedir_tests[]; extern struct testcase_t socks_tests[]; diff --git a/src/test/test_rust.c b/src/test/test_rust.c new file mode 100644 index 0000000000..6ad57d6fcb --- /dev/null +++ b/src/test/test_rust.c @@ -0,0 +1,31 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" +#include "compat_rust.h" +#include "test.h" +#include "util.h" + +static void +test_welcome_string(void *arg) +{ + (void)arg; + rust_str_t s = rust_welcome_string(); + const char *c_str = rust_str_get(s); + tt_assert(c_str); + size_t len = strlen(c_str); +#ifdef HAVE_RUST + tt_assert(len > 0); +#else + tt_assert(len == 0); +#endif + + done: + rust_str_free(s); +} + +struct testcase_t rust_tests[] = { + { "welcome_string", test_welcome_string, 0, NULL, NULL }, + END_OF_TESTCASES +}; + |