From d1820c1516a31a149fc51a9e5126bf899e4c4e08 Mon Sep 17 00:00:00 2001 From: Chelsea Holland Komlo Date: Wed, 27 Sep 2017 19:48:07 +0000 Subject: [PATCH 01/14] rust implementation of protover --- Makefile.am | 2 + configure.ac | 6 + src/common/include.am | 2 + src/common/rust_types.c | 55 ++ src/common/rust_types.h | 22 + src/or/include.am | 1 + src/or/protover.c | 4 + src/or/protover.h | 6 +- src/or/protover_rust.c | 111 ++++ src/rust/Cargo.lock | 31 + src/rust/Cargo.toml | 2 +- src/rust/c_string/Cargo.toml | 13 + src/rust/c_string/ffi.rs | 19 + src/rust/c_string/include.am | 12 + src/rust/external/Cargo.toml | 13 + src/rust/external/external.rs | 29 + src/rust/external/lib.rs | 14 + src/rust/include.am | 2 + src/rust/protover/Cargo.toml | 22 + src/rust/protover/ffi.rs | 205 +++++++ src/rust/protover/include.am | 14 + src/rust/protover/lib.rs | 34 ++ src/rust/protover/protover.rs | 847 ++++++++++++++++++++++++++++ src/rust/protover/tests/protover.rs | 288 ++++++++++ src/rust/smartlist/Cargo.toml | 13 + src/rust/smartlist/lib.rs | 5 + src/rust/smartlist/smartlist.rs | 100 ++++ src/rust/tor_util/Cargo.toml | 2 +- src/test/test_protover.c | 43 +- 29 files changed, 1913 insertions(+), 4 deletions(-) create mode 100644 src/common/rust_types.c create mode 100644 src/common/rust_types.h create mode 100644 src/or/protover_rust.c create mode 100644 src/rust/c_string/Cargo.toml create mode 100644 src/rust/c_string/ffi.rs create mode 100644 src/rust/c_string/include.am create mode 100644 src/rust/external/Cargo.toml create mode 100644 src/rust/external/external.rs create mode 100644 src/rust/external/lib.rs create mode 100644 src/rust/protover/Cargo.toml create mode 100644 src/rust/protover/ffi.rs create mode 100644 src/rust/protover/include.am create mode 100644 src/rust/protover/lib.rs create mode 100644 src/rust/protover/protover.rs create mode 100644 src/rust/protover/tests/protover.rs create mode 100644 src/rust/smartlist/Cargo.toml create mode 100644 src/rust/smartlist/lib.rs create mode 100644 src/rust/smartlist/smartlist.rs diff --git a/Makefile.am b/Makefile.am index ad2ceb66a9..9067e9a8a4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -27,6 +27,8 @@ endif if USE_RUST rust_ldadd=$(top_builddir)/src/rust/target/release/@TOR_RUST_UTIL_STATIC_NAME@ +rust_ldadd+=$(top_builddir)/src/rust/target/release/@TOR_RUST_PROTOVER_STATIC_NAME@ +rust_ldadd+=$(top_builddir)/src/rust/target/release/@TOR_RUST_C_STRING_STATIC_NAME@ else rust_ldadd= endif diff --git a/configure.ac b/configure.ac index d2682944fb..b2b353f6ba 100644 --- a/configure.ac +++ b/configure.ac @@ -440,11 +440,17 @@ if test "x$enable_rust" = "xyes"; then dnl the MSVC naming convention. if test "$bwin32" = "true"; then TOR_RUST_UTIL_STATIC_NAME=tor_util.lib + TOR_RUST_PROTOVER_STATIC_NAME=libprotover.lib + TOR_RUST_C_STRING_STATIC_NAME=libc_string.lib else TOR_RUST_UTIL_STATIC_NAME=libtor_util.a + TOR_RUST_PROTOVER_STATIC_NAME=libprotover.a + TOR_RUST_C_STRING_STATIC_NAME=libc_string.a fi AC_SUBST(TOR_RUST_UTIL_STATIC_NAME) + AC_SUBST(TOR_RUST_PROTOVER_STATIC_NAME) + AC_SUBST(TOR_RUST_C_STRING_STATIC_NAME) AC_SUBST(CARGO_ONLINE) AC_SUBST(RUST_DL) diff --git a/src/common/include.am b/src/common/include.am index cd5eea3404..7ce84e17c5 100644 --- a/src/common/include.am +++ b/src/common/include.am @@ -94,6 +94,7 @@ LIBOR_A_SRC = \ src/common/util_bug.c \ src/common/util_format.c \ src/common/util_process.c \ + src/common/rust_types.c \ src/common/sandbox.c \ src/common/storagedir.c \ src/common/workqueue.c \ @@ -179,6 +180,7 @@ COMMONHEADERS = \ src/common/procmon.h \ src/common/pubsub.h \ src/common/sandbox.h \ + src/common/rust_types.h \ src/common/storagedir.h \ src/common/testsupport.h \ src/common/timers.h \ diff --git a/src/common/rust_types.c b/src/common/rust_types.c new file mode 100644 index 0000000000..d116b515b8 --- /dev/null +++ b/src/common/rust_types.c @@ -0,0 +1,55 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file rust_types.c + * \brief This file is used for handling types returned from Rust to C. + **/ + +#include "or.h" +#include "rust_types.h" + +#ifdef HAVE_RUST + +void free_rust_str(char *ret); + +/* Because Rust strings can only be freed from Rust, we first copy the string's + * contents to a c pointer, and then free the Rust string. + * This function can be extended to return a success/error value if needed. + */ +void +move_rust_str_to_c_and_free(rust_str_ref_t src, char **dest) +{ + if (!src) { + log_warn(LD_BUG, "Received a null pointer from protover rust."); + return; + } + + if (!dest) { + log_warn(LD_BUG, "Received a null pointer from caller to protover rust. " + "This results in a memory leak due to not freeing the rust " + "string that was meant to be copied.."); + return; + } + + *dest = tor_strdup(src); + free_rust_str(src); + return; +} + +#else + +/* When Rust is not enabled, this function should never be used. Log a warning + * in the case that it is ever called when Rust is not enabled. + */ +void +move_rust_str_to_c_and_free(rust_str_ref_t src, char **dest) +{ + (void) src; + (void) dest; + log_warn(LD_BUG, "Received a call to free a Rust string when we are " + " not running with Rust enabled."); + return; +} +#endif /* defined(HAVE_RUST) */ + diff --git a/src/common/rust_types.h b/src/common/rust_types.h new file mode 100644 index 0000000000..b6d807e656 --- /dev/null +++ b/src/common/rust_types.h @@ -0,0 +1,22 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file rust_types.h + * \brief Headers for rust_types.c + **/ + +#include "or.h" + +#ifndef TOR_RUST_TYPES_H +#define TOR_RUST_TYPES_H + +/* This type is used to clearly mark strings that have been allocated in Rust, + * and therefore strictly need to use the free_rust_str method to free. + */ +typedef char *rust_str_ref_t; + +void move_rust_str_to_c_and_free(rust_str_ref_t src, char **dest); + +#endif + diff --git a/src/or/include.am b/src/or/include.am index 7216aba9af..bf3715e95e 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -78,6 +78,7 @@ LIBTOR_A_SOURCES = \ src/or/parsecommon.c \ src/or/periodic.c \ src/or/protover.c \ + src/or/protover_rust.c \ src/or/proto_cell.c \ src/or/proto_control0.c \ src/or/proto_ext_or.c \ diff --git a/src/or/protover.c b/src/or/protover.c index 1a3e69be10..0e74deb112 100644 --- a/src/or/protover.c +++ b/src/or/protover.c @@ -27,6 +27,8 @@ #include "protover.h" #include "routerparse.h" +#ifndef HAVE_RUST + static const smartlist_t *get_supported_protocol_list(void); static int protocol_list_contains(const smartlist_t *protos, protocol_type_t pr, uint32_t ver); @@ -735,3 +737,5 @@ protover_free_all(void) } } +#endif + diff --git a/src/or/protover.h b/src/or/protover.h index 657977279e..7f1938e0c9 100644 --- a/src/or/protover.h +++ b/src/or/protover.h @@ -70,11 +70,15 @@ typedef struct proto_entry_t { smartlist_t *ranges; } proto_entry_t; +#if !defined(HAVE_RUST) && defined(TOR_UNIT_TESTS) STATIC smartlist_t *parse_protocol_list(const char *s); -STATIC void proto_entry_free(proto_entry_t *entry); STATIC char *encode_protocol_list(const smartlist_t *sl); STATIC const char *protocol_type_to_str(protocol_type_t pr); STATIC int str_to_protocol_type(const char *s, protocol_type_t *pr_out); +STATIC void proto_entry_free(proto_entry_t *entry); + +#endif + #endif /* defined(PROTOVER_PRIVATE) */ #endif /* !defined(TOR_PROTOVER_H) */ diff --git a/src/or/protover_rust.c b/src/or/protover_rust.c new file mode 100644 index 0000000000..ebe815357b --- /dev/null +++ b/src/or/protover_rust.c @@ -0,0 +1,111 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/* + * \file protover_rust.c + * \brief Provide a C wrapper for functions exposed in /src/rust/protover, + * and safe translation/handling between the Rust/C boundary. + */ + +#include "or.h" +#include "protover.h" +#include "rust_types.h" + +#ifdef HAVE_RUST + +int rust_protover_all_supported(const char *s, char **missing); +rust_str_ref_t rust_protover_compute_for_old_tor(const char *version); +rust_str_ref_t rust_protover_compute_vote(const smartlist_t *proto_votes, + int threshold); +rust_str_ref_t rust_protover_get_supported_protocols(void); +int rust_protocol_list_supports_protocol(const char *list, protocol_type_t tp, + uint32_t version); +int rust_protover_is_supported_here(protocol_type_t pr, uint32_t ver); + +/* Define for compatibility, used in main.c */ +void protover_free_all(void) {}; + +/* + * Wrap rust_protover_is_supported_here, located in /src/rust/protover + */ +int +protover_is_supported_here(protocol_type_t pr, uint32_t ver) +{ + return rust_protover_is_supported_here(pr, ver); +} + +/* + * Wrap rust_protover_list_supports_protocol, located in /src/rust/protover + */ +int +protocol_list_supports_protocol(const char *list, protocol_type_t tp, + uint32_t version) +{ + return rust_protocol_list_supports_protocol(list, tp, version); +} + +/* + * Wrap rust_protover_get_supported_protocols, located in /src/rust/protover + */ +const char * +protover_get_supported_protocols(void) +{ + rust_str_ref_t rust_protocols = rust_protover_get_supported_protocols(); + + char *protocols = NULL; + if (rust_protocols != NULL) { + move_rust_str_to_c_and_free(rust_protocols, &protocols); + } + return protocols; +} + +/* + * Wrap rust_protover_compute_vote, located in /src/rust/protover + */ +char * +protover_compute_vote(const smartlist_t *proto_strings, + int threshold) +{ + rust_str_ref_t rust_protocols = rust_protover_compute_vote(proto_strings, + threshold); + + char *protocols = NULL; + if (rust_protocols != NULL) { + move_rust_str_to_c_and_free(rust_protocols, &protocols); + } + return protocols; +} + +/* + * Wrap rust_protover_all_supported, located in /src/rust/protover + */ +int +protover_all_supported(const char *s, char **missing_out) +{ + rust_str_ref_t missing_out_copy = NULL; + int is_supported = rust_protover_all_supported(s, &missing_out_copy); + + if (!is_supported) { + move_rust_str_to_c_and_free(missing_out_copy, missing_out); + } + + return is_supported; +} + +/* + * Wrap rust_compute_for_old_tor, located in /src/rust/protover + */ +const char * +protover_compute_for_old_tor(const char *version) +{ + rust_str_ref_t rust_protocols = rust_protover_compute_for_old_tor(version); + + char *protocols = NULL; + if (rust_protocols != NULL) { + move_rust_str_to_c_and_free(rust_protocols, &protocols); + } + return protocols; +} + +#endif + diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock index 4ac9606ce8..aa91ea355c 100644 --- a/src/rust/Cargo.lock +++ b/src/rust/Cargo.lock @@ -5,10 +5,41 @@ dependencies = [ "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "c_string" +version = "0.0.1" +dependencies = [ + "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "external" +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" +[[package]] +name = "protover" +version = "0.0.1" +dependencies = [ + "external 0.0.1", + "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", + "smartlist 0.0.1", + "tor_util 0.0.1", +] + +[[package]] +name = "smartlist" +version = "0.0.1" +dependencies = [ + "libc 0.2.22 (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 index fc4377e8b4..6943627e38 100644 --- a/src/rust/Cargo.toml +++ b/src/rust/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["tor_util"] +members = ["tor_util", "protover", "smartlist", "external", "c_string"] [profile.release] debug = true diff --git a/src/rust/c_string/Cargo.toml b/src/rust/c_string/Cargo.toml new file mode 100644 index 0000000000..dc7504856b --- /dev/null +++ b/src/rust/c_string/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["The Tor Project"] +version = "0.0.1" +name = "c_string" + +[dependencies] +libc = "0.2.22" + +[lib] +name = "c_string" +path = "ffi.rs" +crate_type = ["rlib", "staticlib"] + diff --git a/src/rust/c_string/ffi.rs b/src/rust/c_string/ffi.rs new file mode 100644 index 0000000000..edce250829 --- /dev/null +++ b/src/rust/c_string/ffi.rs @@ -0,0 +1,19 @@ +//! FFI functions, only to be called from C. +//! +//! This module provides the ability for C to free strings that have been +//! allocated in Rust. + +extern crate libc; + +use libc::c_char; +use std::ffi::CString; + +/// This allows strings allocated in Rust to be freed in Rust. Every string +/// sent across the Rust/C FFI boundary should utilize this function for +/// freeing strings allocated in Rust. +#[no_mangle] +pub extern "C" fn free_rust_str(ptr: *mut c_char) { + if !ptr.is_null() { + unsafe { CString::from_raw(ptr) }; + } +} diff --git a/src/rust/c_string/include.am b/src/rust/c_string/include.am new file mode 100644 index 0000000000..8e9229ae6a --- /dev/null +++ b/src/rust/c_string/include.am @@ -0,0 +1,12 @@ +EXTRA_DIST +=\ + src/rust/c_string/Cargo.toml \ + src/rust/c_string/ffi.rs + +src/rust/target/release/@TOR_RUST_C_STRING_STATIC_NAME@: FORCE + ( cd "$(abs_top_srcdir)/src/rust/c_string" ; \ + CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \ + CARGO_HOME="$(abs_top_builddir)/src/rust" \ + $(CARGO) build --release --quiet $(CARGO_ONLINE) ) + +FORCE: + diff --git a/src/rust/external/Cargo.toml b/src/rust/external/Cargo.toml new file mode 100644 index 0000000000..bccd7033a7 --- /dev/null +++ b/src/rust/external/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["The Tor Project"] +version = "0.0.1" +name = "external" + +[dependencies] +libc = "0.2.22" + +[lib] +name = "external" +path = "lib.rs" +crate_type = ["rlib", "staticlib"] + diff --git a/src/rust/external/external.rs b/src/rust/external/external.rs new file mode 100644 index 0000000000..0e8d1eb0d8 --- /dev/null +++ b/src/rust/external/external.rs @@ -0,0 +1,29 @@ +use libc::{c_char, c_int}; +use std::ffi::CString; + +extern "C" { + fn tor_version_as_new_as( + platform: *const c_char, + cutoff: *const c_char, + ) -> c_int; +} + +/// Wrap calls to tor_version_as_new_as, defined in src/or/routerparse.c +pub fn c_tor_version_as_new_as(platform: &str, cutoff: &str) -> bool { + // CHK: These functions should log a warning if an error occurs. This + // can be added when integration with tor's logger is added to rust + let c_platform = match CString::new(platform) { + Ok(n) => n, + Err(_) => return false, + }; + let c_cutoff = match CString::new(cutoff) { + Ok(n) => n, + Err(_) => return false, + }; + + let result: c_int; + unsafe { + result = tor_version_as_new_as(c_platform.as_ptr(), c_cutoff.as_ptr()); + result == 1 + } +} diff --git a/src/rust/external/lib.rs b/src/rust/external/lib.rs new file mode 100644 index 0000000000..0af0d6452d --- /dev/null +++ b/src/rust/external/lib.rs @@ -0,0 +1,14 @@ +//! Copyright (c) 2016-2017, The Tor Project, Inc. */ +//! See LICENSE for licensing information */ + +//! Interface for external calls to tor C ABI +//! +//! The purpose of this module is to provide a clean interface for when Rust +//! modules need to interact with functionality in tor C code rather than each +//! module implementing this functionality repeatedly. + +extern crate libc; + +mod external; + +pub use external::*; diff --git a/src/rust/include.am b/src/rust/include.am index 20afc6c4db..cba92c28aa 100644 --- a/src/rust/include.am +++ b/src/rust/include.am @@ -1,4 +1,6 @@ include src/rust/tor_util/include.am +include src/rust/protover/include.am +include src/rust/c_string/include.am EXTRA_DIST +=\ src/rust/Cargo.toml \ diff --git a/src/rust/protover/Cargo.toml b/src/rust/protover/Cargo.toml new file mode 100644 index 0000000000..a8f794f838 --- /dev/null +++ b/src/rust/protover/Cargo.toml @@ -0,0 +1,22 @@ +[package] +authors = ["The Tor Project"] +version = "0.0.1" +name = "protover" + +[dependencies] +libc = "0.2.22" + +[dependencies.smartlist] +path = "../smartlist" + +[dependencies.external] +path = "../external" + +[dependencies.tor_util] +path = "../tor_util" + +[lib] +name = "protover" +path = "lib.rs" +crate_type = ["rlib", "staticlib"] + diff --git a/src/rust/protover/ffi.rs b/src/rust/protover/ffi.rs new file mode 100644 index 0000000000..7365d7cd8f --- /dev/null +++ b/src/rust/protover/ffi.rs @@ -0,0 +1,205 @@ +//! FFI functions, only to be called from C. +//! +//! Equivalent C versions of this api are in `src/or/protover.c` + +use libc::{c_char, c_int, uint32_t}; +use std::ffi::CStr; +use std::ffi::CString; + +use protover::*; +use smartlist::*; + +/// Translate C enums to Rust Proto enums, using the integer value of the C +/// enum to map to its associated Rust enum +/// This is dependant on the associated C enum preserving ordering. +/// Modify the C documentation to give warnings- you must also re-order the rust +fn translate_to_rust(c_proto: uint32_t) -> Result { + match c_proto { + 0 => Ok(Proto::Link), + 1 => Ok(Proto::LinkAuth), + 2 => Ok(Proto::Relay), + 3 => Ok(Proto::DirCache), + 4 => Ok(Proto::HSDir), + 5 => Ok(Proto::HSIntro), + 6 => Ok(Proto::HSRend), + 7 => Ok(Proto::Desc), + 8 => Ok(Proto::Microdesc), + 9 => Ok(Proto::Cons), + _ => Err("Invalid protocol type"), + } +} + +/// Provide an interface for C to translate arguments and return types for +/// protover::all_supported +#[no_mangle] +pub extern "C" fn rust_protover_all_supported( + c_relay_version: *const c_char, + missing_out: *mut *mut c_char, +) -> c_int { + + if c_relay_version.is_null() { + return 1; + } + + // Require an unsafe block to read the version from a C string. The pointer + // is checked above to ensure it is not null. + let c_str: &CStr; + unsafe { + c_str = CStr::from_ptr(c_relay_version); + } + + let relay_version = match c_str.to_str() { + Ok(n) => n, + Err(_) => return 1, + }; + + let (is_supported, unsupported) = all_supported(relay_version); + + if unsupported.len() > 0 { + let c_unsupported = match CString::new(unsupported) { + Ok(n) => n, + Err(_) => return 1, + }; + + let ptr = c_unsupported.into_raw(); + unsafe { *missing_out = ptr }; + } + + return if is_supported { 1 } else { 0 }; +} + +/// Provide an interface for C to translate arguments and return types for +/// protover::list_supports_protocol +#[no_mangle] +pub extern "C" fn rust_protocol_list_supports_protocol( + c_protocol_list: *const c_char, + c_protocol: uint32_t, + version: uint32_t, +) -> c_int { + if c_protocol_list.is_null() { + return 1; + } + + // Require an unsafe block to read the version from a C string. The pointer + // is checked above to ensure it is not null. + let c_str: &CStr; + unsafe { + c_str = CStr::from_ptr(c_protocol_list); + } + + let protocol_list = match c_str.to_str() { + Ok(n) => n, + Err(_) => return 1, + }; + + let protocol = match translate_to_rust(c_protocol) { + Ok(n) => n, + Err(_) => return 0, + }; + + let is_supported = + protover_string_supports_protocol(protocol_list, protocol, version); + + return if is_supported { 1 } else { 0 }; +} + +/// Provide an interface for C to translate arguments and return types for +/// protover::get_supported_protocols +#[no_mangle] +pub extern "C" fn rust_protover_get_supported_protocols() -> *mut c_char { + // Not handling errors when unwrapping as the content is controlled + // and is an empty string + let empty = CString::new("").unwrap(); + + let supported = get_supported_protocols(); + let c_supported = match CString::new(supported) { + Ok(n) => n, + Err(_) => return empty.into_raw(), + }; + + c_supported.into_raw() +} + +/// Provide an interface for C to translate arguments and return types for +/// protover::compute_vote +#[no_mangle] +pub extern "C" fn rust_protover_compute_vote( + list: *const Stringlist, + threshold: c_int, +) -> *mut c_char { + // Not handling errors when unwrapping as the content is controlled + // and is an empty string + let empty = CString::new("").unwrap(); + + if list.is_null() { + return empty.into_raw(); + } + + // Dereference of raw pointer requires an unsafe block. The pointer is + // checked above to ensure it is not null. + let data: Vec; + unsafe { + data = (*list).get_list(); + } + + let vote = compute_vote(data, threshold); + let c_vote = match CString::new(vote) { + Ok(n) => n, + Err(_) => return empty.into_raw(), + }; + + c_vote.into_raw() +} + +/// Provide an interface for C to translate arguments and return types for +/// protover::is_supported_here +#[no_mangle] +pub extern "C" fn rust_protover_is_supported_here( + c_protocol: uint32_t, + version: uint32_t, +) -> c_int { + let protocol = match translate_to_rust(c_protocol) { + Ok(n) => n, + Err(_) => return 0, + }; + + let is_supported = is_supported_here(protocol, version); + + return if is_supported { 1 } else { 0 }; +} + +/// Provide an interface for C to translate arguments and return types for +/// protover::compute_for_old_tor +#[no_mangle] +pub extern "C" fn rust_protover_compute_for_old_tor( + version: *const c_char, +) -> *mut c_char { + // Not handling errors when unwrapping as the content is controlled + // and is an empty string + let empty = CString::new("").unwrap(); + + if version.is_null() { + return empty.into_raw(); + } + + // Require an unsafe block to read the version from a C string. The pointer + // is checked above to ensure it is not null. + let c_str: &CStr; + unsafe { + c_str = CStr::from_ptr(version); + } + + let version = match c_str.to_str() { + Ok(n) => n, + Err(_) => return empty.into_raw(), + }; + + let supported = compute_for_old_tor(&version); + + let c_supported = match CString::new(supported) { + Ok(n) => n, + Err(_) => return empty.into_raw(), + }; + + c_supported.into_raw() +} diff --git a/src/rust/protover/include.am b/src/rust/protover/include.am new file mode 100644 index 0000000000..326dbbb7f2 --- /dev/null +++ b/src/rust/protover/include.am @@ -0,0 +1,14 @@ +EXTRA_DIST +=\ + src/rust/protover/Cargo.toml \ + src/rust/protover/lib.rs \ + src/rust/protover/ffi.rs \ + src/rust/protover/external.rs + +src/rust/target/release/@TOR_RUST_PROTOVER_STATIC_NAME@: FORCE + ( cd "$(abs_top_srcdir)/src/rust/protover" ; \ + CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \ + CARGO_HOME="$(abs_top_builddir)/src/rust" \ + $(CARGO) build --release --quiet $(CARGO_ONLINE) ) + +FORCE: + diff --git a/src/rust/protover/lib.rs b/src/rust/protover/lib.rs new file mode 100644 index 0000000000..89378c7b7e --- /dev/null +++ b/src/rust/protover/lib.rs @@ -0,0 +1,34 @@ +#![feature(inclusive_range_syntax)] + +//! Copyright (c) 2016-2017, The Tor Project, Inc. */ +//! See LICENSE for licensing information */ + +//! Versioning information for different pieces of the Tor protocol. +//! +//! The below description is taken from src/rust/protover.c, which is currently +//! enabled by default. We are in the process of experimenting with Rust in +//! tor, and this protover module is implemented to help achieve this goal. +//! +//! Starting in version 0.2.9.3-alpha, Tor places separate version numbers on +//! each of the different components of its protocol. Relays use these numbers +//! to advertise what versions of the protocols they can support, and clients +//! use them to find what they can ask a given relay to do. Authorities vote +//! on the supported protocol versions for each relay, and also vote on the +//! which protocols you should have to support in order to be on the Tor +//! network. All Tor instances use these required/recommended protocol versions +//! to tell what level of support for recent protocols each relay has, and +//! to decide whether they should be running given their current protocols. +//! +//! The main advantage of these protocol versions numbers over using Tor +//! version numbers is that they allow different implementations of the Tor +//! protocols to develop independently, without having to claim compatibility +//! with specific versions of Tor. + +extern crate libc; +extern crate smartlist; +extern crate external; + +mod protover; +pub mod ffi; + +pub use protover::*; diff --git a/src/rust/protover/protover.rs b/src/rust/protover/protover.rs new file mode 100644 index 0000000000..0893362cec --- /dev/null +++ b/src/rust/protover/protover.rs @@ -0,0 +1,847 @@ +use external::c_tor_version_as_new_as; + +use std::str::FromStr; +use std::str::SplitN; +use std::fmt; +use std::collections::HashMap; +use std::collections::HashSet; +use std::string::String; + +/// The first version of Tor that included "proto" entries in its descriptors. +/// Authorities should use this to decide whether to guess proto lines. +const FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS: &'static str = "0.2.9.3-alpha"; + +/// The maximum number of subprotocol version numbers we will attempt to expand +/// before concluding that someone is trying to DoS us +const MAX_PROTOCOLS_TO_EXPAND: u32 = 500; + +/// Currently supported protocols and their versions +const SUPPORTED_PROTOCOLS: &'static [&'static str] = &[ + "Cons=1-2", + "Desc=1-2", + "DirCache=1-2", + "HSDir=1-2", + "HSIntro=3-4", + "HSRend=1-2", + "Link=1-4", + "LinkAuth=1,3", + "Microdesc=1-2", + "Relay=1-2", +]; + +/// Known subprotocols in Tor. Indicates which subprotocol a relay supports. +#[derive(Hash, Eq, PartialEq, Debug)] +pub enum Proto { + Cons, + Desc, + DirCache, + HSDir, + HSIntro, + HSRend, + Link, + LinkAuth, + Microdesc, + Relay, +} + +impl fmt::Display for Proto { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +/// Translates a string representation of a protocol into a Proto type. +/// Error if the string is an unrecognized protocol name. +impl FromStr for Proto { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s { + "Cons" => Ok(Proto::Cons), + "Desc" => Ok(Proto::Desc), + "DirCache" => Ok(Proto::DirCache), + "HSDir" => Ok(Proto::HSDir), + "HSIntro" => Ok(Proto::HSIntro), + "HSRend" => Ok(Proto::HSRend), + "Link" => Ok(Proto::Link), + "LinkAuth" => Ok(Proto::LinkAuth), + "Microdesc" => Ok(Proto::Microdesc), + "Relay" => Ok(Proto::Relay), + _ => Err("Not a valid protocol type"), + } + } +} + +/// Get the string representation of current supported protocols +/// +/// # Returns +/// +/// A `String` whose value is the existing protocols supported by tor. +/// Returned data is in the format as follows: +/// +/// "HSDir=1-1 LinkAuth=1" +/// +pub fn get_supported_protocols() -> String { + SUPPORTED_PROTOCOLS.join(" ") +} + +/// Translates a vector representation of a protocol list into a HashMap +fn parse_protocols( + protocols: &[&str], +) -> Result>, &'static str> { + let mut parsed = HashMap::new(); + + for subproto in protocols { + let (name, version) = get_proto_and_vers(subproto)?; + parsed.insert(name, version); + } + Ok(parsed) +} + +/// Translates a string representation of a protocol list to a HashMap +fn parse_protocols_from_string<'a>( + protocol_string: &'a str, +) -> Result>, &'static str> { + let protocols: &[&'a str] = + &protocol_string.split(" ").collect::>()[..]; + + parse_protocols(protocols) +} + +/// Translates supported tor versions from a string into a HashMap, which is +/// useful when looking up a specific subprotocol. +/// +/// # Returns +/// +/// A `Result` whose `Ok` value is a `HashMap>` holding all +/// subprotocols and versions currently supported by tor. +/// +/// The returned `Result`'s `Err` value is an `&'static str` with a description +/// of the error. +/// +fn tor_supported() -> Result>, &'static str> { + parse_protocols(&SUPPORTED_PROTOCOLS) +} + +/// Get the unique version numbers supported by a subprotocol. +/// +/// # Inputs +/// +/// * `version_string`, a string comprised of "[0-9,-]" +/// +/// # Returns +/// +/// A `Result` whose `Ok` value is a `HashSet` holding all of the unique +/// version numbers. If there were ranges in the `version_string`, then these +/// are expanded, i.e. `"1-3"` would expand to `HashSet::new([1, 2, 3])`. +/// The returned HashSet is *unordered*. +/// +/// The returned `Result`'s `Err` value is an `&'static str` with a description +/// of the error. +/// +/// # Errors +/// +/// This function will error if: +/// +/// * the `version_string` is empty or contains an equals (`"="`) sign, +/// * the expansion of a version range produces an error (see `expand_version_range`), +/// * any single version number is not parseable as an `u32` in radix 10, or +/// * there are greater than 2^16 version numbers to expand. +/// +fn get_versions(version_string: &str) -> Result, &'static str> { + if version_string.is_empty() { + return Err("version string is empty"); + } + + let mut versions = HashSet::::new(); + + for piece in version_string.split(",") { + if piece.contains("-") { + for p in expand_version_range(piece)? { + versions.insert(p); + } + } else { + versions.insert(u32::from_str(piece).or( + Err("invalid protocol entry"), + )?); + } + + if versions.len() > MAX_PROTOCOLS_TO_EXPAND as usize { + return Err("Too many versions to expand"); + } + } + Ok(versions) +} + + +/// Parse the subprotocol type and its version numbers. +/// +/// # Inputs +/// +/// * A `protocol_entry` string, comprised of a keyword, an "=" sign, and one +/// or more version numbers. +/// +/// # Returns +/// +/// A `Result` whose `Ok` value is a tuple of `(Proto, HashSet)`, where the +/// first element is the subprotocol type (see `protover::Proto`) and the last +/// element is a(n unordered) set of unique version numbers which are supported. +/// Otherwise, the `Err` value of this `Result` is a description of the error +/// +fn get_proto_and_vers<'a>( + protocol_entry: &'a str, +) -> Result<(Proto, HashSet), &'static str> { + let mut parts: SplitN<'a, &str> = protocol_entry.splitn(2, "="); + + let proto: &str = match parts.next() { + Some(n) => n, + None => return Err("invalid protover entry"), + }; + + let vers: &str = match parts.next() { + Some(n) => n, + None => return Err("invalid protover entry"), + }; + + let versions = get_versions(vers)?; + let proto_name = proto.parse()?; + + Ok((proto_name, versions)) +} + +/// Parses a single subprotocol entry string into subprotocol and version +/// parts, and then checks whether any of those versions are unsupported. +/// Helper for protover::all_supported +/// +/// # Inputs +/// +/// Accepted data is in the string format as follows: +/// +/// "HSDir=1-1" +/// +/// # Returns +/// +/// Returns `true` if the protocol entry is well-formatted and only contains +/// versions that are also supported in tor. Otherwise, returns false +/// +fn contains_only_supported_protocols(proto_entry: &str) -> bool { + let (name, mut vers) = match get_proto_and_vers(proto_entry) { + Ok(n) => n, + Err(_) => return false, + }; + + let currently_supported: HashMap> = + match tor_supported() { + Ok(n) => n, + Err(_) => return false, + }; + + let supported_versions = match currently_supported.get(&name) { + Some(n) => n, + None => return false, + }; + + vers.retain(|x| !supported_versions.contains(x)); + vers.is_empty() +} + +/// Determine if we support every protocol a client supports, and if not, +/// determine which protocols we do not have support for. +/// +/// # Inputs +/// +/// Accepted data is in the string format as follows: +/// +/// "HSDir=1-1 LinkAuth=1-2" +/// +/// # Returns +/// +/// Return `true` if every protocol version is one that we support. +/// Otherwise, return `false`. +/// Optionally, return parameters which the client supports but which we do not +/// +/// # Examples +/// ``` +/// use protover::all_supported; +/// +/// let (is_supported, unsupported) = all_supported("Link=1"); +/// assert_eq!(true, is_supported); +/// +/// let (is_supported, unsupported) = all_supported("Link=5-6"); +/// assert_eq!(false, is_supported); +/// assert_eq!("Link=5-6", unsupported); +/// +pub fn all_supported(protocols: &str) -> (bool, String) { + let unsupported: Vec<&str> = protocols + .split_whitespace() + .filter(|v| !contains_only_supported_protocols(v)) + .collect::>(); + + (unsupported.is_empty(), unsupported.join(" ")) +} + +/// Return true iff the provided protocol list includes support for the +/// indicated protocol and version. +/// Otherwise, return false +/// +/// # Inputs +/// +/// * `list`, a string representation of a list of protocol entries. +/// * `proto`, a `Proto` to test support for +/// * `vers`, a `u32` version which we will go on to determine whether the +/// specified protocol supports. +/// +/// # Examples +/// ``` +/// use protover::*; +/// +/// let is_supported = protover_string_supports_protocol("Link=3-4 Cons=1", Proto::Cons,1); +/// assert_eq!(true, is_supported); +/// +/// let is_not_supported = protover_string_supports_protocol("Link=3-4 Cons=1", Proto::Cons,5); +/// assert_eq!(false, is_not_supported) +/// ``` +pub fn protover_string_supports_protocol( + list: &str, + proto: Proto, + vers: u32, +) -> bool { + let supported: HashMap>; + + match parse_protocols_from_string(list) { + Ok(result) => supported = result, + Err(_) => return false, + } + + let supported_versions = match supported.get(&proto) { + Some(n) => n, + None => return false, + }; + + supported_versions.contains(&vers) +} + +/// Fully expand a version range. For example, 1-3 expands to 1,2,3 +/// Helper for get_versions +/// +/// # Inputs +/// +/// `range`, a string comprised of "[0-9,-]" +/// +/// # Returns +/// +/// A `Result` whose `Ok` value a vector of unsigned integers representing the +/// expanded range of supported versions by a single protocol. +/// Otherwise, the `Err` value of this `Result` is a description of the error +/// +/// # Errors +/// +/// This function will error if: +/// +/// * the specified range is empty +/// * the version range does not contain both a valid lower and upper bound. +/// +fn expand_version_range(range: &str) -> Result, &'static str> { + if range.is_empty() { + return Err("version string empty"); + } + + let mut parts = range.split("-"); + + let lower_string: &str = parts.next().ok_or( + "cannot parse protocol range lower bound", + )?; + + let lower: u32 = u32::from_str_radix(lower_string, 10).or(Err( + "cannot parse protocol range lower bound", + ))?; + + let higher_string: &str = parts.next().ok_or( + "cannot parse protocol range upper bound", + )?; + + let higher: u32 = u32::from_str_radix(higher_string, 10).or(Err( + "cannot parse protocol range upper bound", + ))?; + + Ok((lower...higher).collect()) +} + +/// Checks to see if there is a continuous range of integers, starting at the +/// first in the list. Returns the last integer in the range if a range exists. +/// Helper for compute_vote +/// +/// # Inputs +/// +/// `list`, an ordered vector of `u32` integers of "[0-9,-]" representing the +/// supported versions for a single protocol. +/// +/// # Returns +/// +/// A `bool` indicating whether the list contains a range, starting at the +/// first in the list, and an `u32` of the last integer in the range. +/// +/// For example, if given vec![1, 2, 3, 5], find_range will return true, +/// as there is a continuous range, and 3, which is the last number in the +/// continuous range. +/// +fn find_range(list: &Vec) -> (bool, u32) { + if list.len() == 0 { + return (false, 0); + } + + let mut iterable = list.iter().peekable(); + let mut range_end: u32 = match iterable.next() { + Some(n) => *n, + None => return (false, 0), + }; + + let mut has_range = false; + + while iterable.peek().is_some() { + let n = *iterable.next().unwrap(); + if n != range_end + 1 { + break; + } + + has_range = true; + range_end = n; + } + + (has_range, range_end) +} + +/// Contracts a HashSet representation of supported versions into a string. +/// Helper for compute_vote +/// +/// # Inputs +/// +/// `supported_set`, a set of integers of "[0-9,-]" representing the +/// supported versions for a single protocol. +/// +/// # Returns +/// +/// A `String` representation of this set in ascending order. +/// +fn contract_protocol_list<'a>(supported_set: &'a HashSet) -> String { + let mut supported_clone = supported_set.clone(); + let mut supported: Vec = supported_clone.drain().collect(); + supported.sort(); + + let mut final_output: Vec = Vec::new(); + + while supported.len() != 0 { + let (has_range, end) = find_range(&supported); + let current = supported.remove(0); + + if has_range { + final_output.push(format!( + "{}-{}", + current.to_string(), + &end.to_string(), + )); + supported.retain(|&x| x > end); + } else { + final_output.push(current.to_string()); + } + } + + final_output.join(",") +} + +/// Parses a protocol list without validating the protocol names +/// +/// # Inputs +/// +/// * `protocol_string`, a string comprised of keys and values, both which are +/// strings. The keys are the protocol names while values are a string +/// representation of the supported versions. +/// +/// The input is _not_ expected to be a subset of the Proto types +/// +/// # Returns +/// +/// A `Result` whose `Ok` value is a `HashSet` holding all of the unique +/// version numbers. +/// +/// The returned `Result`'s `Err` value is an `&'static str` with a description +/// of the error. +/// +/// # Errors +/// +/// This function will error if: +/// +/// * The protocol string does not follow the "protocol_name=version_list" +/// expected format +/// * If the version string is malformed. See `get_versions`. +/// +fn parse_protocols_from_string_with_no_validation<'a>( + protocol_string: &'a str, +) -> Result>, &'static str> { + let protocols: &[&'a str] = + &protocol_string.split(" ").collect::>()[..]; + + let mut parsed: HashMap> = HashMap::new(); + + for subproto in protocols { + let mut parts: SplitN<'a, &str> = subproto.splitn(2, "="); + + let name: &str = match parts.next() { + Some(n) => n, + None => return Err("invalid protover entry"), + }; + + let vers: &str = match parts.next() { + Some(n) => n, + None => return Err("invalid protover entry"), + }; + + let versions = get_versions(vers)?; + + parsed.insert(String::from(name), versions); + } + Ok(parsed) +} + +/// Protocol voting implementation. +/// +/// Given a list of strings describing protocol versions, return a new +/// string encoding all of the protocols that are listed by at +/// least threshold of the inputs. +/// +/// The string is sorted according to the following conventions: +/// - Protocols names are alphabetized +/// - Protocols are in order low to high +/// - Individual and ranges are listed together. For example, +/// "3, 5-10,13" +/// - All entries are unique +/// +/// # Examples +/// ``` +/// use protover::compute_vote; +/// +/// let protos = vec![String::from("Link=3-4"), String::from("Link=3")]; +/// let vote = compute_vote(protos, 2); +/// assert_eq!("Link=3", vote) +/// ``` +pub fn compute_vote( + list_of_proto_strings: Vec, + threshold: i32, +) -> String { + let empty = String::from(""); + + if list_of_proto_strings.is_empty() { + return empty; + } + + // all_count is a structure to represent the count of the number of + // supported versions for a specific protocol. For example, in JSON format: + // { + // "FirstSupportedProtocol": { + // "1": "3", + // "2": "1" + // } + // } + // means that FirstSupportedProtocol has three votes which support version + // 1, and one vote that supports version 2 + let mut all_count: HashMap> = HashMap::new(); + + // parse and collect all of the protos and their versions and collect them + for vote in list_of_proto_strings { + let this_vote: HashMap> = + match parse_protocols_from_string_with_no_validation(&vote) { + Ok(result) => result, + Err(_) => continue, + }; + + for (protocol, versions) in this_vote { + let supported_vers: &mut HashMap = + all_count.entry(protocol).or_insert(HashMap::new()); + + for version in versions { + let counter: &mut usize = + supported_vers.entry(version).or_insert(0); + *counter += 1; + } + } + } + + let mut final_output: HashMap = + HashMap::with_capacity(SUPPORTED_PROTOCOLS.len()); + + // Go through and remove verstions that are less than the threshold + for (protocol, versions) in all_count { + let mut meets_threshold = HashSet::new(); + for (version, count) in versions { + if count >= threshold as usize { + meets_threshold.insert(version); + } + } + + // For each protocol, compress its version list into the expected + // protocol version string format + let contracted = contract_protocol_list(&meets_threshold); + if !contracted.is_empty() { + final_output.insert(protocol, contracted); + } + } + + write_vote_to_string(&final_output) +} + +/// Return a String comprised of protocol entries in alphabetical order +/// +/// # Inputs +/// +/// * `vote`, a `HashMap` comprised of keys and values, both which are strings. +/// The keys are the protocol names while values are a string representation of +/// the supported versions. +/// +/// # Returns +/// +/// A `String` whose value is series of pairs, comprising of the protocol name +/// and versions that it supports. The string takes the following format: +/// +/// "first_protocol_name=1,2-5, second_protocol_name=4,5" +/// +/// Sorts the keys in alphabetical order and creates the expected subprotocol +/// entry format. +/// +fn write_vote_to_string(vote: &HashMap) -> String { + let mut keys: Vec<&String> = vote.keys().collect(); + keys.sort(); + + let mut output = Vec::new(); + for key in keys { + // TODO error in indexing here? + output.push(format!("{}={}", key, vote[key])); + } + output.join(" ") +} + +/// Returns a boolean indicating whether the given protocol and version is +/// supported in any of the existing Tor protocols +/// +/// # Examples +/// ``` +/// use protover::*; +/// +/// let is_supported = is_supported_here(Proto::Link, 5); +/// assert_eq!(false, is_supported); +/// +/// let is_supported = is_supported_here(Proto::Link, 1); +/// assert_eq!(true, is_supported); +/// ``` +pub fn is_supported_here(proto: Proto, vers: u32) -> bool { + let currently_supported: HashMap>; + + match tor_supported() { + Ok(result) => currently_supported = result, + Err(_) => return false, + } + + let supported_versions = match currently_supported.get(&proto) { + Some(n) => n, + None => return false, + }; + + supported_versions.contains(&vers) +} + +/// Older versions of Tor cannot infer their own subprotocols +/// Used to determine which subprotocols are supported by older Tor versions. +/// +/// # Inputs +/// +/// * `version`, a string comprised of "[0-9,-]" +/// +/// # Returns +/// +/// A `String` whose value is series of pairs, comprising of the protocol name +/// and versions that it supports. The string takes the following format: +/// +/// "HSDir=1-1 LinkAuth=1" +/// +/// This function returns the protocols that are supported by the version input, +/// only for tor versions older than FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS. +/// +pub fn compute_for_old_tor(version: &str) -> String { + if c_tor_version_as_new_as( + version, + FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS, + ) + { + return String::new(); + } + + if c_tor_version_as_new_as(version, "0.2.9.1-alpha") { + let ret = "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1-2 \ + Link=1-4 LinkAuth=1 Microdesc=1-2 Relay=1-2"; + return String::from(ret); + } + + if c_tor_version_as_new_as(version, "0.2.7.5") { + let ret = "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 \ + Link=1-4 LinkAuth=1 Microdesc=1-2 Relay=1-2"; + return String::from(ret); + } + + if c_tor_version_as_new_as(version, "0.2.4.19") { + let ret = "Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 \ + Link=1-4 LinkAuth=1 Microdesc=1 Relay=1-2"; + return String::from(ret); + } + String::new() +} + +#[cfg(test)] +mod test { + #[test] + fn test_get_versions() { + use std::collections::HashSet; + + use super::get_versions; + + assert_eq!(Err("version string is empty"), get_versions("")); + assert_eq!(Err("invalid protocol entry"), get_versions("a,b")); + assert_eq!(Err("invalid protocol entry"), get_versions("1,!")); + + { + let mut versions: HashSet = HashSet::new(); + versions.insert(1); + assert_eq!(Ok(versions), get_versions("1")); + } + { + let mut versions: HashSet = HashSet::new(); + versions.insert(1); + versions.insert(2); + assert_eq!(Ok(versions), get_versions("1,2")); + } + { + let mut versions: HashSet = HashSet::new(); + versions.insert(1); + versions.insert(2); + versions.insert(3); + assert_eq!(Ok(versions), get_versions("1-3")); + } + { + let mut versions: HashSet = HashSet::new(); + versions.insert(1); + versions.insert(2); + versions.insert(5); + assert_eq!(Ok(versions), get_versions("1-2,5")); + } + { + let mut versions: HashSet = HashSet::new(); + versions.insert(1); + versions.insert(3); + versions.insert(4); + versions.insert(5); + assert_eq!(Ok(versions), get_versions("1,3-5")); + } + } + + #[test] + fn test_contains_only_supported_protocols() { + use super::contains_only_supported_protocols; + + assert_eq!(false, contains_only_supported_protocols("")); + assert_eq!(false, contains_only_supported_protocols("Cons=")); + assert_eq!(true, contains_only_supported_protocols("Cons=1")); + assert_eq!(false, contains_only_supported_protocols("Cons=0")); + assert_eq!(false, contains_only_supported_protocols("Cons=0-1")); + assert_eq!(false, contains_only_supported_protocols("Cons=5")); + assert_eq!(false, contains_only_supported_protocols("Cons=1-5")); + assert_eq!(false, contains_only_supported_protocols("Cons=1,5")); + assert_eq!(false, contains_only_supported_protocols("Cons=5,6")); + assert_eq!(false, contains_only_supported_protocols("Cons=1,5,6")); + assert_eq!(true, contains_only_supported_protocols("Cons=1,2")); + assert_eq!(true, contains_only_supported_protocols("Cons=1-2")); + } + + #[test] + fn test_find_range() { + use super::find_range; + + assert_eq!((false, 0), find_range(&vec![])); + assert_eq!((false, 1), find_range(&vec![1])); + assert_eq!((true, 2), find_range(&vec![1, 2])); + assert_eq!((true, 3), find_range(&vec![1, 2, 3])); + assert_eq!((true, 3), find_range(&vec![1, 2, 3, 5])); + } + + #[test] + fn test_expand_version_range() { + use super::expand_version_range; + + assert_eq!(Err("version string empty"), expand_version_range("")); + assert_eq!(Ok(vec![1, 2]), expand_version_range("1-2")); + assert_eq!(Ok(vec![1, 2, 3, 4]), expand_version_range("1-4")); + assert_eq!( + Err("cannot parse protocol range lower bound"), + expand_version_range("a") + ); + assert_eq!( + Err("cannot parse protocol range upper bound"), + expand_version_range("1-a") + ); + } + + #[test] + fn test_contract_protocol_list() { + use std::collections::HashSet; + use super::contract_protocol_list; + + { + let mut versions = HashSet::::new(); + assert_eq!(String::from(""), contract_protocol_list(&versions)); + + versions.insert(1); + assert_eq!(String::from("1"), contract_protocol_list(&versions)); + + versions.insert(2); + assert_eq!(String::from("1-2"), contract_protocol_list(&versions)); + } + + { + let mut versions = HashSet::::new(); + versions.insert(1); + versions.insert(3); + assert_eq!(String::from("1,3"), contract_protocol_list(&versions)); + } + + { + let mut versions = HashSet::::new(); + versions.insert(1); + versions.insert(2); + versions.insert(3); + versions.insert(4); + assert_eq!(String::from("1-4"), contract_protocol_list(&versions)); + } + + { + let mut versions = HashSet::::new(); + versions.insert(1); + versions.insert(3); + versions.insert(5); + versions.insert(6); + versions.insert(7); + assert_eq!( + String::from("1,3,5-7"), + contract_protocol_list(&versions) + ); + } + + { + let mut versions = HashSet::::new(); + versions.insert(1); + versions.insert(2); + versions.insert(3); + versions.insert(500); + assert_eq!( + String::from("1-3,500"), + contract_protocol_list(&versions) + ); + } + } +} diff --git a/src/rust/protover/tests/protover.rs b/src/rust/protover/tests/protover.rs new file mode 100644 index 0000000000..7d8484ecc2 --- /dev/null +++ b/src/rust/protover/tests/protover.rs @@ -0,0 +1,288 @@ +extern crate protover; + +#[test] +fn parse_protocol_list_with_single_protocol_and_single_version_returns_set_of_one(){ + let protocol = "Cons=1"; + let (is_supported, unsupported) = protover::all_supported(protocol); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn parse_protocol_list_with_single_protocol_and_multiple_versions_returns_set_of_one(){ + let protocol = "Cons=1-2"; + let (is_supported, unsupported) = protover::all_supported(protocol); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn parse_protocol_list_with_different_single_protocol_and_single_version_returns_set_of_one(){ + let protocol = "HSDir=1"; + let (is_supported, unsupported) = protover::all_supported(protocol); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn parse_protocol_list_with_single_protocol_and_supported_version_returns_set_of_one(){ + let protocol = "Desc=2"; + let (is_supported, unsupported) = protover::all_supported(protocol); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn parse_protocol_list_with_two_protocols_and_single_version_returns_set_of_one(){ + let protocols = "Cons=1 HSDir=1"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + + +#[test] +fn parse_protocol_list_with_single_protocol_and_two_nonsequential_versions_returns_set_of_two(){ + let protocol = "Desc=1,2"; + let (is_supported, unsupported) = protover::all_supported(protocol); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + + +#[test] +fn parse_protocol_list_with_single_protocol_and_two_sequential_versions_returns_set_of_two(){ + let protocol = "Desc=1-2"; + let (is_supported, unsupported) = protover::all_supported(protocol); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn parse_protocol_list_with_single_protocol_and_protocol_range_returns_set() { + let protocol = "Link=1-4"; + let (is_supported, unsupported) = protover::all_supported(protocol); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn parse_protocol_list_with_single_protocol_and_protocol_set() { + let protocols = "Link=3-4 Desc=2"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn protover_all_supported_with_two_values() { + let protocols = "Microdesc=1-2 Relay=2"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!("", &unsupported); + assert_eq!(true, is_supported); +} + +#[test] +fn protover_all_supported_with_one_value() { + let protocols = "Microdesc=1-2"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!("", &unsupported); + assert_eq!(true, is_supported); +} + +#[test] +fn protover_all_supported_with_empty() { + let protocols = ""; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn protover_all_supported_with_three_values() { + let protocols = "LinkAuth=1 Microdesc=1-2 Relay=2"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!("", &unsupported); + assert_eq!(true, is_supported); +} + +#[test] +fn protover_all_supported_with_unsupported_protocol() { + let protocols = "Wombat=9"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(false, is_supported); + assert_eq!("Wombat=9", &unsupported); +} + +#[test] +fn protover_all_supported_with_unsupported_versions() { + let protocols = "Link=3-999"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(false, is_supported); + assert_eq!("Link=3-999", &unsupported); +} + +#[test] +fn protover_all_supported_with_unsupported_low_version() { + let protocols = "Cons=0-1"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(false, is_supported); + assert_eq!("Cons=0-1", &unsupported); +} + +#[test] +fn protover_all_supported_with_unsupported_high_version() { + let protocols = "Cons=1-3"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(false, is_supported); + assert_eq!("Cons=1-3", &unsupported); +} + +#[test] +fn protover_all_supported_with_mix_of_supported_and_unsupproted() { + let protocols = "Link=3-4 Wombat=9"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(false, is_supported); + assert_eq!("Wombat=9", &unsupported); +} + +#[test] +fn protover_string_supports_protocol_returns_true_for_single_supported() { + let protocols = "Link=3-4 Cons=1"; + let is_supported = protover::protover_string_supports_protocol( + protocols, + protover::Proto::Cons, + 1, + ); + assert_eq!(true, is_supported); +} + +#[test] +fn protover_string_supports_protocol_returns_false_for_single_unsupported() { + let protocols = "Link=3-4 Cons=1"; + let is_supported = protover::protover_string_supports_protocol( + protocols, + protover::Proto::Cons, + 2, + ); + assert_eq!(false, is_supported); +} + +#[test] +fn protover_string_supports_protocol_returns_false_when_protocol_name_is_not_in_map(){ + let protocols = "Link=3-4"; + let is_supported = protover::protover_string_supports_protocol( + protocols, + protover::Proto::Cons, + 2, + ); + assert_eq!(false, is_supported); +} + +#[test] +fn protover_all_supported_with_unexpected_characters() { + let protocols = "Cons=*-%"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(false, is_supported); + assert_eq!("Cons=*-%", &unsupported); +} + +#[test] +fn protover_compute_vote_returns_empty_for_empty_string() { + let protocols = vec![String::from("")]; + let listed = protover::compute_vote(protocols, 1); + assert_eq!("", listed); +} + +#[test] +fn protover_compute_vote_returns_single_protocol_for_matching() { + let protocols = vec![String::from("Cons=1")]; + let listed = protover::compute_vote(protocols, 1); + assert_eq!("Cons=1", listed); +} + +#[test] +fn protover_compute_vote_returns_two_protocols_for_two_matching() { + let protocols = vec![String::from("Link=1 Cons=1")]; + let listed = protover::compute_vote(protocols, 1); + assert_eq!("Cons=1 Link=1", listed); +} + +#[test] +fn protover_compute_vote_returns_one_protocol_when_one_out_of_two_matches() { + let protocols = vec![String::from("Cons=1 Link=2"), String::from("Cons=1")]; + let listed = protover::compute_vote(protocols, 2); + assert_eq!("Cons=1", listed); +} + +#[test] +fn protover_compute_vote_returns_protocols_that_it_doesnt_currently_support() { + let protocols = vec![String::from("Foo=1 Cons=2"), String::from("Bar=1")]; + let listed = protover::compute_vote(protocols, 1); + assert_eq!("Bar=1 Cons=2 Foo=1", listed); +} + +#[test] +fn protover_compute_vote_returns_matching_for_mix() { + let protocols = vec![String::from("Link=1-10,500 Cons=1,3-7,8")]; + let listed = protover::compute_vote(protocols, 1); + assert_eq!("Cons=1,3-8 Link=1-10,500", listed); +} + +#[test] +fn protover_compute_vote_returns_matching_for_longer_mix() { + let protocols = vec![ + String::from("Desc=1-10,500 Cons=1,3-7,8"), + String::from("Link=123-456,78 Cons=2-6,8 Desc=9"), + ]; + + let listed = protover::compute_vote(protocols, 1); + assert_eq!("Cons=1-8 Desc=1-10,500 Link=78,123-456", listed); +} + +#[test] +fn protover_compute_vote_returns_matching_for_longer_mix_with_threshold_two() { + let protocols = vec![ + String::from("Desc=1-10,500 Cons=1,3-7,8"), + String::from("Link=123-456,78 Cons=2-6,8 Desc=9"), + ]; + + let listed = protover::compute_vote(protocols, 2); + assert_eq!("Cons=3-6,8 Desc=9", listed); +} + +#[test] +fn protover_compute_vote_handles_duplicated_versions() { + let protocols = vec![String::from("Cons=1"), String::from("Cons=1")]; + assert_eq!("Cons=1", protover::compute_vote(protocols, 2)); + + let protocols = vec![String::from("Cons=1-2"), String::from("Cons=1-2")]; + assert_eq!("Cons=1-2", protover::compute_vote(protocols, 2)); +} + +#[test] +fn protover_compute_vote_handles_invalid_proto_entries() { + let protocols = vec![ + String::from("Cons=1"), + String::from("Cons=1"), + String::from("Link=a"), + ]; + assert_eq!("Cons=1", protover::compute_vote(protocols, 2)); + + let protocols = vec![ + String::from("Cons=1"), + String::from("Cons=1"), + String::from("Link=1-%"), + ]; + assert_eq!("Cons=1", protover::compute_vote(protocols, 2)); +} + +#[test] +fn protover_is_supported_here_returns_true_for_supported_protocol() { + assert_eq!(true, protover::is_supported_here(protover::Proto::Cons, 1)); +} + +#[test] +fn protover_is_supported_here_returns_false_for_unsupported_protocol() { + assert_eq!(false, protover::is_supported_here(protover::Proto::Cons, 5)); +} diff --git a/src/rust/smartlist/Cargo.toml b/src/rust/smartlist/Cargo.toml new file mode 100644 index 0000000000..51f486c4d7 --- /dev/null +++ b/src/rust/smartlist/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["The Tor Project"] +version = "0.0.1" +name = "smartlist" + +[dependencies] +libc = "0.2.22" + +[lib] +name = "smartlist" +path = "lib.rs" +crate_type = ["rlib", "staticlib"] + diff --git a/src/rust/smartlist/lib.rs b/src/rust/smartlist/lib.rs new file mode 100644 index 0000000000..71d89a3b87 --- /dev/null +++ b/src/rust/smartlist/lib.rs @@ -0,0 +1,5 @@ +extern crate libc; + +mod smartlist; + +pub use smartlist::*; diff --git a/src/rust/smartlist/smartlist.rs b/src/rust/smartlist/smartlist.rs new file mode 100644 index 0000000000..5eff3fe43e --- /dev/null +++ b/src/rust/smartlist/smartlist.rs @@ -0,0 +1,100 @@ +use std::slice; +use libc::c_char; +use std::ffi::CStr; + +/// Smartlists are a type used in C code in tor to define a collection of a +/// generic type, which has a capacity and a number used. Each Smartlist +/// defines how to extract the list of values from the underlying C structure +/// Implementations are required to have a C representation +pub trait Smartlist { + fn get_list(&self) -> Vec; +} +#[repr(C)] +pub struct Stringlist { + pub list: *const *const c_char, + pub num_used: u8, + pub capacity: u8, +} + +impl Smartlist for Stringlist { + fn get_list(&self) -> Vec { + let empty: Vec = Vec::new(); + let mut v: Vec = Vec::new(); + + if self.list.is_null() { + return empty; + } + + // unsafe, as we need to extract the smartlist list into a vector of + // pointers, and then transform each element into a Rust string. + unsafe { + let elems = + slice::from_raw_parts(self.list, self.num_used as usize); + + for i in elems.iter() { + let c_str = CStr::from_ptr(*i); + let r_str = match c_str.to_str() { + Ok(n) => n, + Err(_) => return empty, + }; + v.push(String::from(r_str)); + } + } + + v + } +} + +#[cfg(test)] +mod test { + #[test] + fn test_get_list_of_strings() { + extern crate libc; + + use std::ffi::CString; + use libc::c_char; + + use super::Smartlist; + use super::Stringlist; + + { + // test to verify that null pointers are gracefully handled + use std::ptr; + + let sl = Stringlist { + list: ptr::null(), + num_used: 0, + capacity: 0, + }; + + let data = sl.get_list(); + assert_eq!(0, data.len()); + } + + { + let args = vec![String::from("a"), String::from("b")]; + + // for each string, transform it into a CString + let c_strings: Vec<_> = args.iter() + .map(|arg| CString::new(arg.as_str()).unwrap()) + .collect(); + + // then, collect a pointer for each CString + let p_args: Vec<_> = + c_strings.iter().map(|arg| arg.as_ptr()).collect(); + + // then, collect a pointer for the list itself + let p: *const *const c_char = p_args.as_ptr(); + + let sl = Stringlist { + list: p, + num_used: 2, + capacity: 2, + }; + + let data = sl.get_list(); + assert_eq!("a", &data[0]); + assert_eq!("b", &data[1]); + } + } +} diff --git a/src/rust/tor_util/Cargo.toml b/src/rust/tor_util/Cargo.toml index f175fbdfb0..906833ce24 100644 --- a/src/rust/tor_util/Cargo.toml +++ b/src/rust/tor_util/Cargo.toml @@ -9,5 +9,5 @@ path = "lib.rs" crate_type = ["rlib", "staticlib"] [dependencies] -libc = "*" +libc = "0.2.22" diff --git a/src/test/test_protover.c b/src/test/test_protover.c index 6ce54890d6..9ae907183b 100644 --- a/src/test/test_protover.c +++ b/src/test/test_protover.c @@ -12,6 +12,13 @@ static void test_protover_parse(void *arg) { (void) arg; +#ifdef HAVE_RUST + /** This test is disabled on rust builds, because it only exists to test + * internal C functions. */ + tt_skip(); + done: + ; +#else char *re_encoded = NULL; const char *orig = "Foo=1,3 Bar=3 Baz= Quux=9-12,14,15-16,900"; @@ -78,12 +85,18 @@ test_protover_parse(void *arg) SMARTLIST_FOREACH(elts, proto_entry_t *, ent, proto_entry_free(ent)); smartlist_free(elts); tor_free(re_encoded); +#endif } static void test_protover_parse_fail(void *arg) { (void)arg; +#ifdef HAVE_RUST + /** This test is disabled on rust builds, because it only exists to test + * internal C functions. */ + tt_skip(); +#else smartlist_t *elts; /* random junk */ @@ -109,7 +122,7 @@ test_protover_parse_fail(void *arg) /* Broken range */ elts = parse_protocol_list("Link=1,9-8,3"); tt_ptr_op(elts, OP_EQ, NULL); - +#endif done: ; } @@ -182,6 +195,32 @@ test_protover_all_supported(void *arg) tor_free(msg); } +static void +test_protover_list_supports_protocol_returns_true(void *arg) +{ + (void)arg; + + const char *protocols = "Link=1"; + int is_supported = protocol_list_supports_protocol(protocols, PRT_LINK, 1); + tt_int_op(is_supported, OP_EQ, 1); + + done: + ; +} + +static void +test_protover_list_supports_protocol_for_unsupported_returns_false(void *arg) +{ + (void)arg; + + const char *protocols = "Link=1"; + int is_supported = protocol_list_supports_protocol(protocols, PRT_LINK, 10); + tt_int_op(is_supported, OP_EQ, 0); + + done: + ; +} + #define PV_TEST(name, flags) \ { #name, test_protover_ ##name, (flags), NULL, NULL } @@ -190,6 +229,8 @@ struct testcase_t protover_tests[] = { PV_TEST(parse_fail, 0), PV_TEST(vote, 0), PV_TEST(all_supported, 0), + PV_TEST(list_supports_protocol_for_unsupported_returns_false, 0), + PV_TEST(list_supports_protocol_returns_true, 0), END_OF_TESTCASES }; From 0c04b54d4d78f7f3117948358b3d05e644fa1aeb Mon Sep 17 00:00:00 2001 From: Chelsea Holland Komlo Date: Thu, 28 Sep 2017 03:08:37 +0000 Subject: [PATCH 02/14] minimize scope for unsafe update documentation missing check for null --- src/common/rust_types.c | 2 +- src/or/protover_rust.c | 2 +- src/rust/external/external.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/rust_types.c b/src/common/rust_types.c index d116b515b8..1b6dd3990e 100644 --- a/src/common/rust_types.c +++ b/src/common/rust_types.c @@ -26,7 +26,7 @@ move_rust_str_to_c_and_free(rust_str_ref_t src, char **dest) } if (!dest) { - log_warn(LD_BUG, "Received a null pointer from caller to protover rust. " + log_warn(LD_BUG, "Received a null pointer from caller to rust. " "This results in a memory leak due to not freeing the rust " "string that was meant to be copied.."); return; diff --git a/src/or/protover_rust.c b/src/or/protover_rust.c index ebe815357b..261555d1ec 100644 --- a/src/or/protover_rust.c +++ b/src/or/protover_rust.c @@ -85,7 +85,7 @@ protover_all_supported(const char *s, char **missing_out) rust_str_ref_t missing_out_copy = NULL; int is_supported = rust_protover_all_supported(s, &missing_out_copy); - if (!is_supported) { + if (!is_supported && missing_out_copy != NULL) { move_rust_str_to_c_and_free(missing_out_copy, missing_out); } diff --git a/src/rust/external/external.rs b/src/rust/external/external.rs index 0e8d1eb0d8..f3267949c7 100644 --- a/src/rust/external/external.rs +++ b/src/rust/external/external.rs @@ -24,6 +24,6 @@ pub fn c_tor_version_as_new_as(platform: &str, cutoff: &str) -> bool { let result: c_int; unsafe { result = tor_version_as_new_as(c_platform.as_ptr(), c_cutoff.as_ptr()); - result == 1 } + result == 1 } From be583a34a3815c2c10e86094ab0610e4b7f9c869 Mon Sep 17 00:00:00 2001 From: Chelsea Holland Komlo Date: Wed, 11 Oct 2017 15:21:20 -0400 Subject: [PATCH 03/14] use tor allocator for string allocation in rust --- Makefile.am | 1 - configure.ac | 2 - src/common/include.am | 2 - src/common/rust_types.c | 55 --------------------- src/common/rust_types.h | 22 --------- src/or/include.am | 2 +- src/or/protover_rust.c | 92 ----------------------------------- src/rust/Cargo.lock | 15 +++--- src/rust/Cargo.toml | 2 +- src/rust/c_string/Cargo.toml | 13 ----- src/rust/c_string/ffi.rs | 19 -------- src/rust/c_string/include.am | 12 ----- src/rust/external/external.rs | 9 ++-- src/rust/include.am | 1 - src/rust/protover/Cargo.toml | 3 ++ src/rust/protover/ffi.rs | 62 ++++++++--------------- src/rust/protover/lib.rs | 1 + src/rust/protover/protover.rs | 2 +- 18 files changed, 40 insertions(+), 275 deletions(-) delete mode 100644 src/common/rust_types.c delete mode 100644 src/common/rust_types.h delete mode 100644 src/rust/c_string/Cargo.toml delete mode 100644 src/rust/c_string/ffi.rs delete mode 100644 src/rust/c_string/include.am diff --git a/Makefile.am b/Makefile.am index 9067e9a8a4..27ee33e1e9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -28,7 +28,6 @@ endif if USE_RUST rust_ldadd=$(top_builddir)/src/rust/target/release/@TOR_RUST_UTIL_STATIC_NAME@ rust_ldadd+=$(top_builddir)/src/rust/target/release/@TOR_RUST_PROTOVER_STATIC_NAME@ -rust_ldadd+=$(top_builddir)/src/rust/target/release/@TOR_RUST_C_STRING_STATIC_NAME@ else rust_ldadd= endif diff --git a/configure.ac b/configure.ac index b2b353f6ba..e20a6b9904 100644 --- a/configure.ac +++ b/configure.ac @@ -441,11 +441,9 @@ if test "x$enable_rust" = "xyes"; then if test "$bwin32" = "true"; then TOR_RUST_UTIL_STATIC_NAME=tor_util.lib TOR_RUST_PROTOVER_STATIC_NAME=libprotover.lib - TOR_RUST_C_STRING_STATIC_NAME=libc_string.lib else TOR_RUST_UTIL_STATIC_NAME=libtor_util.a TOR_RUST_PROTOVER_STATIC_NAME=libprotover.a - TOR_RUST_C_STRING_STATIC_NAME=libc_string.a fi AC_SUBST(TOR_RUST_UTIL_STATIC_NAME) diff --git a/src/common/include.am b/src/common/include.am index 7ce84e17c5..cd5eea3404 100644 --- a/src/common/include.am +++ b/src/common/include.am @@ -94,7 +94,6 @@ LIBOR_A_SRC = \ src/common/util_bug.c \ src/common/util_format.c \ src/common/util_process.c \ - src/common/rust_types.c \ src/common/sandbox.c \ src/common/storagedir.c \ src/common/workqueue.c \ @@ -180,7 +179,6 @@ COMMONHEADERS = \ src/common/procmon.h \ src/common/pubsub.h \ src/common/sandbox.h \ - src/common/rust_types.h \ src/common/storagedir.h \ src/common/testsupport.h \ src/common/timers.h \ diff --git a/src/common/rust_types.c b/src/common/rust_types.c deleted file mode 100644 index 1b6dd3990e..0000000000 --- a/src/common/rust_types.c +++ /dev/null @@ -1,55 +0,0 @@ -/* Copyright (c) 2017, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file rust_types.c - * \brief This file is used for handling types returned from Rust to C. - **/ - -#include "or.h" -#include "rust_types.h" - -#ifdef HAVE_RUST - -void free_rust_str(char *ret); - -/* Because Rust strings can only be freed from Rust, we first copy the string's - * contents to a c pointer, and then free the Rust string. - * This function can be extended to return a success/error value if needed. - */ -void -move_rust_str_to_c_and_free(rust_str_ref_t src, char **dest) -{ - if (!src) { - log_warn(LD_BUG, "Received a null pointer from protover rust."); - return; - } - - if (!dest) { - log_warn(LD_BUG, "Received a null pointer from caller to rust. " - "This results in a memory leak due to not freeing the rust " - "string that was meant to be copied.."); - return; - } - - *dest = tor_strdup(src); - free_rust_str(src); - return; -} - -#else - -/* When Rust is not enabled, this function should never be used. Log a warning - * in the case that it is ever called when Rust is not enabled. - */ -void -move_rust_str_to_c_and_free(rust_str_ref_t src, char **dest) -{ - (void) src; - (void) dest; - log_warn(LD_BUG, "Received a call to free a Rust string when we are " - " not running with Rust enabled."); - return; -} -#endif /* defined(HAVE_RUST) */ - diff --git a/src/common/rust_types.h b/src/common/rust_types.h deleted file mode 100644 index b6d807e656..0000000000 --- a/src/common/rust_types.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Copyright (c) 2017, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file rust_types.h - * \brief Headers for rust_types.c - **/ - -#include "or.h" - -#ifndef TOR_RUST_TYPES_H -#define TOR_RUST_TYPES_H - -/* This type is used to clearly mark strings that have been allocated in Rust, - * and therefore strictly need to use the free_rust_str method to free. - */ -typedef char *rust_str_ref_t; - -void move_rust_str_to_c_and_free(rust_str_ref_t src, char **dest); - -#endif - diff --git a/src/or/include.am b/src/or/include.am index bf3715e95e..3ff71d5ad7 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -78,7 +78,7 @@ LIBTOR_A_SOURCES = \ src/or/parsecommon.c \ src/or/periodic.c \ src/or/protover.c \ - src/or/protover_rust.c \ + src/or/protover_rust.c \ src/or/proto_cell.c \ src/or/proto_control0.c \ src/or/proto_ext_or.c \ diff --git a/src/or/protover_rust.c b/src/or/protover_rust.c index 261555d1ec..27f19c5fea 100644 --- a/src/or/protover_rust.c +++ b/src/or/protover_rust.c @@ -9,103 +9,11 @@ #include "or.h" #include "protover.h" -#include "rust_types.h" #ifdef HAVE_RUST -int rust_protover_all_supported(const char *s, char **missing); -rust_str_ref_t rust_protover_compute_for_old_tor(const char *version); -rust_str_ref_t rust_protover_compute_vote(const smartlist_t *proto_votes, - int threshold); -rust_str_ref_t rust_protover_get_supported_protocols(void); -int rust_protocol_list_supports_protocol(const char *list, protocol_type_t tp, - uint32_t version); -int rust_protover_is_supported_here(protocol_type_t pr, uint32_t ver); - /* Define for compatibility, used in main.c */ void protover_free_all(void) {}; -/* - * Wrap rust_protover_is_supported_here, located in /src/rust/protover - */ -int -protover_is_supported_here(protocol_type_t pr, uint32_t ver) -{ - return rust_protover_is_supported_here(pr, ver); -} - -/* - * Wrap rust_protover_list_supports_protocol, located in /src/rust/protover - */ -int -protocol_list_supports_protocol(const char *list, protocol_type_t tp, - uint32_t version) -{ - return rust_protocol_list_supports_protocol(list, tp, version); -} - -/* - * Wrap rust_protover_get_supported_protocols, located in /src/rust/protover - */ -const char * -protover_get_supported_protocols(void) -{ - rust_str_ref_t rust_protocols = rust_protover_get_supported_protocols(); - - char *protocols = NULL; - if (rust_protocols != NULL) { - move_rust_str_to_c_and_free(rust_protocols, &protocols); - } - return protocols; -} - -/* - * Wrap rust_protover_compute_vote, located in /src/rust/protover - */ -char * -protover_compute_vote(const smartlist_t *proto_strings, - int threshold) -{ - rust_str_ref_t rust_protocols = rust_protover_compute_vote(proto_strings, - threshold); - - char *protocols = NULL; - if (rust_protocols != NULL) { - move_rust_str_to_c_and_free(rust_protocols, &protocols); - } - return protocols; -} - -/* - * Wrap rust_protover_all_supported, located in /src/rust/protover - */ -int -protover_all_supported(const char *s, char **missing_out) -{ - rust_str_ref_t missing_out_copy = NULL; - int is_supported = rust_protover_all_supported(s, &missing_out_copy); - - if (!is_supported && missing_out_copy != NULL) { - move_rust_str_to_c_and_free(missing_out_copy, missing_out); - } - - return is_supported; -} - -/* - * Wrap rust_compute_for_old_tor, located in /src/rust/protover - */ -const char * -protover_compute_for_old_tor(const char *version) -{ - rust_str_ref_t rust_protocols = rust_protover_compute_for_old_tor(version); - - char *protocols = NULL; - if (rust_protocols != NULL) { - move_rust_str_to_c_and_free(rust_protocols, &protocols); - } - return protocols; -} - #endif diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock index aa91ea355c..56cb9d76ba 100644 --- a/src/rust/Cargo.lock +++ b/src/rust/Cargo.lock @@ -5,13 +5,6 @@ dependencies = [ "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "c_string" -version = "0.0.1" -dependencies = [ - "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "external" version = "0.0.1" @@ -31,6 +24,7 @@ dependencies = [ "external 0.0.1", "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", "smartlist 0.0.1", + "tor_allocate 0.0.1", "tor_util 0.0.1", ] @@ -41,5 +35,12 @@ dependencies = [ "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tor_allocate" +version = "0.0.1" +dependencies = [ + "libc 0.2.22 (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 index 6943627e38..5d4292bbd3 100644 --- a/src/rust/Cargo.toml +++ b/src/rust/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["tor_util", "protover", "smartlist", "external", "c_string"] +members = ["tor_util", "protover", "smartlist", "external", "tor_allocate"] [profile.release] debug = true diff --git a/src/rust/c_string/Cargo.toml b/src/rust/c_string/Cargo.toml deleted file mode 100644 index dc7504856b..0000000000 --- a/src/rust/c_string/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -authors = ["The Tor Project"] -version = "0.0.1" -name = "c_string" - -[dependencies] -libc = "0.2.22" - -[lib] -name = "c_string" -path = "ffi.rs" -crate_type = ["rlib", "staticlib"] - diff --git a/src/rust/c_string/ffi.rs b/src/rust/c_string/ffi.rs deleted file mode 100644 index edce250829..0000000000 --- a/src/rust/c_string/ffi.rs +++ /dev/null @@ -1,19 +0,0 @@ -//! FFI functions, only to be called from C. -//! -//! This module provides the ability for C to free strings that have been -//! allocated in Rust. - -extern crate libc; - -use libc::c_char; -use std::ffi::CString; - -/// This allows strings allocated in Rust to be freed in Rust. Every string -/// sent across the Rust/C FFI boundary should utilize this function for -/// freeing strings allocated in Rust. -#[no_mangle] -pub extern "C" fn free_rust_str(ptr: *mut c_char) { - if !ptr.is_null() { - unsafe { CString::from_raw(ptr) }; - } -} diff --git a/src/rust/c_string/include.am b/src/rust/c_string/include.am deleted file mode 100644 index 8e9229ae6a..0000000000 --- a/src/rust/c_string/include.am +++ /dev/null @@ -1,12 +0,0 @@ -EXTRA_DIST +=\ - src/rust/c_string/Cargo.toml \ - src/rust/c_string/ffi.rs - -src/rust/target/release/@TOR_RUST_C_STRING_STATIC_NAME@: FORCE - ( cd "$(abs_top_srcdir)/src/rust/c_string" ; \ - CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \ - CARGO_HOME="$(abs_top_builddir)/src/rust" \ - $(CARGO) build --release --quiet $(CARGO_ONLINE) ) - -FORCE: - diff --git a/src/rust/external/external.rs b/src/rust/external/external.rs index f3267949c7..09d80cb2ba 100644 --- a/src/rust/external/external.rs +++ b/src/rust/external/external.rs @@ -16,14 +16,15 @@ pub fn c_tor_version_as_new_as(platform: &str, cutoff: &str) -> bool { Ok(n) => n, Err(_) => return false, }; + let c_cutoff = match CString::new(cutoff) { Ok(n) => n, Err(_) => return false, }; - let result: c_int; - unsafe { - result = tor_version_as_new_as(c_platform.as_ptr(), c_cutoff.as_ptr()); - } + let result: c_int = unsafe { + tor_version_as_new_as(c_platform.as_ptr(), c_cutoff.as_ptr()) + }; + result == 1 } diff --git a/src/rust/include.am b/src/rust/include.am index cba92c28aa..716d46f4cc 100644 --- a/src/rust/include.am +++ b/src/rust/include.am @@ -1,6 +1,5 @@ include src/rust/tor_util/include.am include src/rust/protover/include.am -include src/rust/c_string/include.am EXTRA_DIST +=\ src/rust/Cargo.toml \ diff --git a/src/rust/protover/Cargo.toml b/src/rust/protover/Cargo.toml index a8f794f838..04d2f2ed7d 100644 --- a/src/rust/protover/Cargo.toml +++ b/src/rust/protover/Cargo.toml @@ -15,6 +15,9 @@ path = "../external" [dependencies.tor_util] path = "../tor_util" +[dependencies.tor_allocate] +path = "../tor_allocate" + [lib] name = "protover" path = "lib.rs" diff --git a/src/rust/protover/ffi.rs b/src/rust/protover/ffi.rs index 7365d7cd8f..539b6c0bac 100644 --- a/src/rust/protover/ffi.rs +++ b/src/rust/protover/ffi.rs @@ -8,6 +8,7 @@ use std::ffi::CString; use protover::*; use smartlist::*; +use tor_allocate::allocate_string; /// Translate C enums to Rust Proto enums, using the integer value of the C /// enum to map to its associated Rust enum @@ -32,7 +33,7 @@ fn translate_to_rust(c_proto: uint32_t) -> Result { /// Provide an interface for C to translate arguments and return types for /// protover::all_supported #[no_mangle] -pub extern "C" fn rust_protover_all_supported( +pub extern "C" fn protover_all_supported( c_relay_version: *const c_char, missing_out: *mut *mut c_char, ) -> c_int { @@ -43,10 +44,7 @@ pub extern "C" fn rust_protover_all_supported( // Require an unsafe block to read the version from a C string. The pointer // is checked above to ensure it is not null. - let c_str: &CStr; - unsafe { - c_str = CStr::from_ptr(c_relay_version); - } + let c_str: &CStr = unsafe { CStr::from_ptr(c_relay_version) }; let relay_version = match c_str.to_str() { Ok(n) => n, @@ -71,7 +69,7 @@ pub extern "C" fn rust_protover_all_supported( /// Provide an interface for C to translate arguments and return types for /// protover::list_supports_protocol #[no_mangle] -pub extern "C" fn rust_protocol_list_supports_protocol( +pub extern "C" fn protocol_list_supports_protocol( c_protocol_list: *const c_char, c_protocol: uint32_t, version: uint32_t, @@ -82,10 +80,7 @@ pub extern "C" fn rust_protocol_list_supports_protocol( // Require an unsafe block to read the version from a C string. The pointer // is checked above to ensure it is not null. - let c_str: &CStr; - unsafe { - c_str = CStr::from_ptr(c_protocol_list); - } + let c_str: &CStr = unsafe { CStr::from_ptr(c_protocol_list) }; let protocol_list = match c_str.to_str() { Ok(n) => n, @@ -106,7 +101,7 @@ pub extern "C" fn rust_protocol_list_supports_protocol( /// Provide an interface for C to translate arguments and return types for /// protover::get_supported_protocols #[no_mangle] -pub extern "C" fn rust_protover_get_supported_protocols() -> *mut c_char { +pub extern "C" fn protover_get_supported_protocols() -> *mut c_char { // Not handling errors when unwrapping as the content is controlled // and is an empty string let empty = CString::new("").unwrap(); @@ -123,38 +118,29 @@ pub extern "C" fn rust_protover_get_supported_protocols() -> *mut c_char { /// Provide an interface for C to translate arguments and return types for /// protover::compute_vote #[no_mangle] -pub extern "C" fn rust_protover_compute_vote( +pub extern "C" fn protover_compute_vote( list: *const Stringlist, threshold: c_int, ) -> *mut c_char { - // Not handling errors when unwrapping as the content is controlled - // and is an empty string - let empty = CString::new("").unwrap(); if list.is_null() { - return empty.into_raw(); + let mut empty = String::new(); + return allocate_string(&mut empty); } // Dereference of raw pointer requires an unsafe block. The pointer is // checked above to ensure it is not null. - let data: Vec; - unsafe { - data = (*list).get_list(); - } + let data: Vec = unsafe { (*list).get_list() }; - let vote = compute_vote(data, threshold); - let c_vote = match CString::new(vote) { - Ok(n) => n, - Err(_) => return empty.into_raw(), - }; + let mut vote = compute_vote(data, threshold); - c_vote.into_raw() + allocate_string(&mut vote) } /// Provide an interface for C to translate arguments and return types for /// protover::is_supported_here #[no_mangle] -pub extern "C" fn rust_protover_is_supported_here( +pub extern "C" fn protover_is_supported_here( c_protocol: uint32_t, version: uint32_t, ) -> c_int { @@ -171,35 +157,27 @@ pub extern "C" fn rust_protover_is_supported_here( /// Provide an interface for C to translate arguments and return types for /// protover::compute_for_old_tor #[no_mangle] -pub extern "C" fn rust_protover_compute_for_old_tor( +pub extern "C" fn protover_compute_for_old_tor( version: *const c_char, ) -> *mut c_char { // Not handling errors when unwrapping as the content is controlled // and is an empty string - let empty = CString::new("").unwrap(); + let mut empty = String::new(); if version.is_null() { - return empty.into_raw(); + return allocate_string(&mut empty); } // Require an unsafe block to read the version from a C string. The pointer // is checked above to ensure it is not null. - let c_str: &CStr; - unsafe { - c_str = CStr::from_ptr(version); - } + let c_str: &CStr = unsafe { CStr::from_ptr(version) }; let version = match c_str.to_str() { Ok(n) => n, - Err(_) => return empty.into_raw(), + Err(_) => return allocate_string(&mut empty), }; - let supported = compute_for_old_tor(&version); + let mut supported = compute_for_old_tor(&version); - let c_supported = match CString::new(supported) { - Ok(n) => n, - Err(_) => return empty.into_raw(), - }; - - c_supported.into_raw() + allocate_string(&mut supported) } diff --git a/src/rust/protover/lib.rs b/src/rust/protover/lib.rs index 89378c7b7e..620191f88d 100644 --- a/src/rust/protover/lib.rs +++ b/src/rust/protover/lib.rs @@ -27,6 +27,7 @@ extern crate libc; extern crate smartlist; extern crate external; +extern crate tor_allocate; mod protover; pub mod ffi; diff --git a/src/rust/protover/protover.rs b/src/rust/protover/protover.rs index 0893362cec..f85d16b73c 100644 --- a/src/rust/protover/protover.rs +++ b/src/rust/protover/protover.rs @@ -364,7 +364,7 @@ fn expand_version_range(range: &str) -> Result, &'static str> { "cannot parse protocol range upper bound", ))?; - Ok((lower...higher).collect()) + Ok((lower..=higher).collect()) } /// Checks to see if there is a continuous range of integers, starting at the From 0c4d535972bcf14fec0b65c82359db28b0cc0091 Mon Sep 17 00:00:00 2001 From: Chelsea Holland Komlo Date: Thu, 12 Oct 2017 17:29:51 -0400 Subject: [PATCH 04/14] refactor build infrastructure for single rust binary --- Makefile.am | 3 +-- configure.ac | 10 +++------- src/rust/Cargo.lock | 8 ++++++++ src/rust/Cargo.toml | 2 +- src/rust/include.am | 3 +-- src/rust/tor_rust/Cargo.toml | 16 ++++++++++++++++ src/rust/tor_rust/include.am | 12 ++++++++++++ src/rust/tor_rust/lib.rs | 5 +++++ src/rust/tor_util/include.am | 13 ------------- 9 files changed, 47 insertions(+), 25 deletions(-) create mode 100644 src/rust/tor_rust/Cargo.toml create mode 100644 src/rust/tor_rust/include.am create mode 100644 src/rust/tor_rust/lib.rs delete mode 100644 src/rust/tor_util/include.am diff --git a/Makefile.am b/Makefile.am index 27ee33e1e9..137fb1edcb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,8 +26,7 @@ TESTING_TOR_BINARY=$(top_builddir)/src/or/tor$(EXEEXT) endif if USE_RUST -rust_ldadd=$(top_builddir)/src/rust/target/release/@TOR_RUST_UTIL_STATIC_NAME@ -rust_ldadd+=$(top_builddir)/src/rust/target/release/@TOR_RUST_PROTOVER_STATIC_NAME@ +rust_ldadd=$(top_builddir)/src/rust/target/release/@TOR_RUST_STATIC_NAME@ else rust_ldadd= endif diff --git a/configure.ac b/configure.ac index e20a6b9904..c0d454ae45 100644 --- a/configure.ac +++ b/configure.ac @@ -439,16 +439,12 @@ if test "x$enable_rust" = "xyes"; then dnl For now both MSVC and MinGW rust libraries will output static libs with dnl the MSVC naming convention. if test "$bwin32" = "true"; then - TOR_RUST_UTIL_STATIC_NAME=tor_util.lib - TOR_RUST_PROTOVER_STATIC_NAME=libprotover.lib + TOR_RUST_STATIC_NAME=tor_rust.lib else - TOR_RUST_UTIL_STATIC_NAME=libtor_util.a - TOR_RUST_PROTOVER_STATIC_NAME=libprotover.a + TOR_RUST_STATIC_NAME=libtor_rust.a fi - AC_SUBST(TOR_RUST_UTIL_STATIC_NAME) - AC_SUBST(TOR_RUST_PROTOVER_STATIC_NAME) - AC_SUBST(TOR_RUST_C_STRING_STATIC_NAME) + AC_SUBST(TOR_RUST_STATIC_NAME) AC_SUBST(CARGO_ONLINE) AC_SUBST(RUST_DL) diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock index 56cb9d76ba..a5686979fa 100644 --- a/src/rust/Cargo.lock +++ b/src/rust/Cargo.lock @@ -42,5 +42,13 @@ dependencies = [ "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tor_rust" +version = "0.1.0" +dependencies = [ + "protover 0.0.1", + "tor_util 0.0.1", +] + [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 index 5d4292bbd3..953c9b96b7 100644 --- a/src/rust/Cargo.toml +++ b/src/rust/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["tor_util", "protover", "smartlist", "external", "tor_allocate"] +members = ["tor_util", "protover", "smartlist", "external", "tor_allocate", "tor_rust"] [profile.release] debug = true diff --git a/src/rust/include.am b/src/rust/include.am index 716d46f4cc..025d921b31 100644 --- a/src/rust/include.am +++ b/src/rust/include.am @@ -1,5 +1,4 @@ -include src/rust/tor_util/include.am -include src/rust/protover/include.am +include src/rust/tor_rust/include.am EXTRA_DIST +=\ src/rust/Cargo.toml \ diff --git a/src/rust/tor_rust/Cargo.toml b/src/rust/tor_rust/Cargo.toml new file mode 100644 index 0000000000..86fad3ee76 --- /dev/null +++ b/src/rust/tor_rust/Cargo.toml @@ -0,0 +1,16 @@ +[package] +authors = ["The Tor Project"] +name = "tor_rust" +version = "0.1.0" + +[lib] +name = "tor_rust" +path = "lib.rs" +crate_type = ["rlib", "staticlib"] + +[dependencies.tor_util] +path = "../tor_util" + +[dependencies.protover] +path = "../protover" + diff --git a/src/rust/tor_rust/include.am b/src/rust/tor_rust/include.am new file mode 100644 index 0000000000..7d30592b07 --- /dev/null +++ b/src/rust/tor_rust/include.am @@ -0,0 +1,12 @@ +EXTRA_DIST +=\ + src/rust/tor_rust/Cargo.toml \ + src/rust/tor_rust/lib.rs \ + src/rust/tor_rust/tor_rust.rs + +src/rust/target/release/@TOR_RUST_STATIC_NAME@: FORCE + ( cd "$(abs_top_srcdir)/src/rust/tor_rust" ; \ + CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \ + CARGO_HOME="$(abs_top_builddir)/src/rust" \ + $(CARGO) build --release --quiet $(CARGO_ONLINE) ) + +FORCE: diff --git a/src/rust/tor_rust/lib.rs b/src/rust/tor_rust/lib.rs new file mode 100644 index 0000000000..c1585c0480 --- /dev/null +++ b/src/rust/tor_rust/lib.rs @@ -0,0 +1,5 @@ +extern crate tor_util; +extern crate protover; + +pub use tor_util::*; +pub use protover::*; diff --git a/src/rust/tor_util/include.am b/src/rust/tor_util/include.am deleted file mode 100644 index ec3898577b..0000000000 --- a/src/rust/tor_util/include.am +++ /dev/null @@ -1,13 +0,0 @@ -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/@TOR_RUST_UTIL_STATIC_NAME@: FORCE - ( cd "$(abs_top_srcdir)/src/rust/tor_util" ; \ - CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \ - CARGO_HOME="$(abs_top_builddir)/src/rust" \ - $(CARGO) build --release --quiet $(CARGO_ONLINE) ) - -FORCE: From d14a83f74fcbe5fae97d944ac8f7f8645e160d3a Mon Sep 17 00:00:00 2001 From: Chelsea Holland Komlo Date: Sat, 14 Oct 2017 22:27:36 -0400 Subject: [PATCH 05/14] remove unneeded dependencies, remove types where unnecessary --- src/rust/protover/protover.rs | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/rust/protover/protover.rs b/src/rust/protover/protover.rs index f85d16b73c..37589a83f9 100644 --- a/src/rust/protover/protover.rs +++ b/src/rust/protover/protover.rs @@ -1,10 +1,8 @@ use external::c_tor_version_as_new_as; use std::str::FromStr; -use std::str::SplitN; use std::fmt; -use std::collections::HashMap; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::string::String; /// The first version of Tor that included "proto" entries in its descriptors. @@ -191,14 +189,14 @@ fn get_versions(version_string: &str) -> Result, &'static str> { fn get_proto_and_vers<'a>( protocol_entry: &'a str, ) -> Result<(Proto, HashSet), &'static str> { - let mut parts: SplitN<'a, &str> = protocol_entry.splitn(2, "="); + let mut parts = protocol_entry.splitn(2, "="); - let proto: &str = match parts.next() { + let proto = match parts.next() { Some(n) => n, None => return Err("invalid protover entry"), }; - let vers: &str = match parts.next() { + let vers = match parts.next() { Some(n) => n, None => return Err("invalid protover entry"), }; @@ -272,7 +270,7 @@ fn contains_only_supported_protocols(proto_entry: &str) -> bool { /// assert_eq!("Link=5-6", unsupported); /// pub fn all_supported(protocols: &str) -> (bool, String) { - let unsupported: Vec<&str> = protocols + let unsupported = protocols .split_whitespace() .filter(|v| !contains_only_supported_protocols(v)) .collect::>(); @@ -348,19 +346,19 @@ fn expand_version_range(range: &str) -> Result, &'static str> { let mut parts = range.split("-"); - let lower_string: &str = parts.next().ok_or( + let lower_string = parts.next().ok_or( "cannot parse protocol range lower bound", )?; - let lower: u32 = u32::from_str_radix(lower_string, 10).or(Err( + let lower = u32::from_str_radix(lower_string, 10).or(Err( "cannot parse protocol range lower bound", ))?; - let higher_string: &str = parts.next().ok_or( + let higher_string = parts.next().ok_or( "cannot parse protocol range upper bound", )?; - let higher: u32 = u32::from_str_radix(higher_string, 10).or(Err( + let higher = u32::from_str_radix(higher_string, 10).or(Err( "cannot parse protocol range upper bound", ))?; @@ -391,7 +389,7 @@ fn find_range(list: &Vec) -> (bool, u32) { } let mut iterable = list.iter().peekable(); - let mut range_end: u32 = match iterable.next() { + let mut range_end = match iterable.next() { Some(n) => *n, None => return (false, 0), }; @@ -478,20 +476,20 @@ fn contract_protocol_list<'a>(supported_set: &'a HashSet) -> String { fn parse_protocols_from_string_with_no_validation<'a>( protocol_string: &'a str, ) -> Result>, &'static str> { - let protocols: &[&'a str] = - &protocol_string.split(" ").collect::>()[..]; + let protocols = &protocol_string.split(" ") + .collect::>()[..]; let mut parsed: HashMap> = HashMap::new(); for subproto in protocols { - let mut parts: SplitN<'a, &str> = subproto.splitn(2, "="); + let mut parts = subproto.splitn(2, "="); - let name: &str = match parts.next() { + let name = match parts.next() { Some(n) => n, None => return Err("invalid protover entry"), }; - let vers: &str = match parts.next() { + let vers = match parts.next() { Some(n) => n, None => return Err("invalid protover entry"), }; From 76bbdfbfa9eca46b53d3ec5a44deafce51d2875a Mon Sep 17 00:00:00 2001 From: Chelsea Holland Komlo Date: Sat, 14 Oct 2017 23:05:31 -0400 Subject: [PATCH 06/14] add tor allocator for rust --- src/rust/protover/ffi.rs | 20 +++--- src/rust/tor_allocate/Cargo.toml | 13 ++++ src/rust/tor_allocate/include.am | 13 ++++ src/rust/tor_allocate/lib.rs | 12 ++++ src/rust/tor_allocate/tor_allocate.rs | 90 +++++++++++++++++++++++++++ 5 files changed, 138 insertions(+), 10 deletions(-) create mode 100644 src/rust/tor_allocate/Cargo.toml create mode 100644 src/rust/tor_allocate/include.am create mode 100644 src/rust/tor_allocate/lib.rs create mode 100644 src/rust/tor_allocate/tor_allocate.rs diff --git a/src/rust/protover/ffi.rs b/src/rust/protover/ffi.rs index 539b6c0bac..23a289bd56 100644 --- a/src/rust/protover/ffi.rs +++ b/src/rust/protover/ffi.rs @@ -8,7 +8,7 @@ use std::ffi::CString; use protover::*; use smartlist::*; -use tor_allocate::allocate_string; +use tor_allocate::allocate_and_copy_string; /// Translate C enums to Rust Proto enums, using the integer value of the C /// enum to map to its associated Rust enum @@ -124,17 +124,17 @@ pub extern "C" fn protover_compute_vote( ) -> *mut c_char { if list.is_null() { - let mut empty = String::new(); - return allocate_string(&mut empty); + let empty = String::new(); + return allocate_and_copy_string(&empty); } // Dereference of raw pointer requires an unsafe block. The pointer is // checked above to ensure it is not null. let data: Vec = unsafe { (*list).get_list() }; - let mut vote = compute_vote(data, threshold); + let vote = compute_vote(data, threshold); - allocate_string(&mut vote) + allocate_and_copy_string(&vote) } /// Provide an interface for C to translate arguments and return types for @@ -162,10 +162,10 @@ pub extern "C" fn protover_compute_for_old_tor( ) -> *mut c_char { // Not handling errors when unwrapping as the content is controlled // and is an empty string - let mut empty = String::new(); + let empty = String::new(); if version.is_null() { - return allocate_string(&mut empty); + return allocate_and_copy_string(&empty); } // Require an unsafe block to read the version from a C string. The pointer @@ -174,10 +174,10 @@ pub extern "C" fn protover_compute_for_old_tor( let version = match c_str.to_str() { Ok(n) => n, - Err(_) => return allocate_string(&mut empty), + Err(_) => return allocate_and_copy_string(&empty), }; - let mut supported = compute_for_old_tor(&version); + let supported = compute_for_old_tor(&version); - allocate_string(&mut supported) + allocate_and_copy_string(&supported) } diff --git a/src/rust/tor_allocate/Cargo.toml b/src/rust/tor_allocate/Cargo.toml new file mode 100644 index 0000000000..ceb08b78ab --- /dev/null +++ b/src/rust/tor_allocate/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["The Tor Project"] +version = "0.0.1" +name = "tor_allocate" + +[dependencies] +libc = "0.2.22" + +[lib] +name = "tor_allocate" +path = "lib.rs" +crate_type = ["rlib", "staticlib"] + diff --git a/src/rust/tor_allocate/include.am b/src/rust/tor_allocate/include.am new file mode 100644 index 0000000000..9e770dbc05 --- /dev/null +++ b/src/rust/tor_allocate/include.am @@ -0,0 +1,13 @@ +EXTRA_DIST +=\ + src/rust/tor_allocate/Cargo.toml \ + src/rust/tor_allocate/lib.rs \ + src/rust/tor_allocate/tor_allocate.rs + +src/rust/target/release/@TOR_RUST_C_STRING_STATIC_NAME@: FORCE + ( cd "$(abs_top_srcdir)/src/rust/tor_allocate" ; \ + CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \ + CARGO_HOME="$(abs_top_builddir)/src/rust" \ + $(CARGO) build --release --quiet $(CARGO_ONLINE) ) + +FORCE: + diff --git a/src/rust/tor_allocate/lib.rs b/src/rust/tor_allocate/lib.rs new file mode 100644 index 0000000000..81afd095f9 --- /dev/null +++ b/src/rust/tor_allocate/lib.rs @@ -0,0 +1,12 @@ +//! Allocation helper functions that allow data to be allocated in Rust +//! using tor's specified allocator. In doing so, this can be later freed +//! from C. +//! +//! This is currently a temporary solution, we will later use tor's allocator +//! by default for any allocation that occurs in Rust. However, as this will +//! stabalize in 2018, we can use this as a temporary measure. + +extern crate libc; + +mod tor_allocate; +pub use tor_allocate::*; diff --git a/src/rust/tor_allocate/tor_allocate.rs b/src/rust/tor_allocate/tor_allocate.rs new file mode 100644 index 0000000000..de1d13942e --- /dev/null +++ b/src/rust/tor_allocate/tor_allocate.rs @@ -0,0 +1,90 @@ +use libc::{c_char, c_void}; +use std::{ptr, slice}; + +#[cfg(not(test))] +extern "C" { + fn tor_malloc_ ( size: usize) -> *mut c_void; +} + +// Defined only for tests, used for testing purposes, so that we don't need +// to link to tor C files. Uses the system allocator +#[cfg(test)] +extern "C" fn tor_malloc_ ( size: usize) -> *mut c_void { + use libc::malloc; + unsafe { malloc(size) } +} + +/// Allocate memory using tor_malloc_ and copy an existing string into the +/// allocated buffer, returning a pointer that can later be called in C. +/// +/// # Inputs +/// +/// * `src`, a reference to a String that will be copied. +/// +/// # Returns +/// +/// A `String` that should be freed by tor_free in C +/// +pub fn allocate_and_copy_string(src: &String) -> *mut c_char { + let bytes = s.as_bytes(); + + let size = s.len(); + let size_with_null_byte = size + 1; + + let dest = unsafe { tor_malloc_(size_with_null_byte) as *mut u8 }; + + if dest.is_null() { + return dest as *mut c_char; + } + + unsafe { ptr::copy_nonoverlapping(bytes.as_ptr(), dest, size) }; + + // set the last byte as null, using the ability to index into a slice + // rather than doing pointer arithmatic + let slice = unsafe { slice::from_raw_parts_mut(dest, size_with_null_byte) }; + slice[size] = 0; // add a null terminator + + dest as *mut c_char +} + +#[cfg(test)] +mod test { + + #[test] + fn test_allocate_and_copy_string_with_empty() { + use std::ffi::CStr; + use libc::{free, c_void}; + + use tor_allocate::allocate_and_copy_string; + + let empty = String::new(); + let allocated_empty = allocate_and_copy_string(&empty); + + let allocated_empty_rust = unsafe { + CStr::from_ptr(allocated_empty).to_str().unwrap() + }; + + assert_eq!("", allocated_empty_rust); + + unsafe { free(allocated_empty as *mut c_void) }; + } + + #[test] + fn test_allocate_and_copy_string_with_not_empty_string() { + use std::ffi::CStr; + use libc::{free, c_void}; + + use tor_allocate::allocate_and_copy_string; + + let empty = String::from("foo bar biz"); + let allocated_empty = allocate_and_copy_string(&empty); + + let allocated_empty_rust = unsafe { + CStr::from_ptr(allocated_empty).to_str().unwrap() + }; + + assert_eq!("foo bar biz", allocated_empty_rust); + + unsafe { free(allocated_empty as *mut c_void) }; + } +} From 91bca5c31b9eefe4d07645f690f69914c89a5594 Mon Sep 17 00:00:00 2001 From: Chelsea Holland Komlo Date: Sun, 22 Oct 2017 00:07:16 -0400 Subject: [PATCH 07/14] move to allocating c strings from rust --- src/common/compat_rust.c | 39 ---------- src/common/compat_rust.h | 28 ------- src/common/include.am | 6 -- src/or/main.c | 18 +++-- src/rust/Cargo.lock | 1 + src/rust/tor_allocate/tor_allocate.rs | 4 +- src/rust/tor_util/Cargo.toml | 3 + src/rust/tor_util/ffi.rs | 57 +++----------- src/rust/tor_util/lib.rs | 11 +-- src/rust/tor_util/rust_string.rs | 101 ------------------------- src/rust/tor_util/tests/rust_string.rs | 37 --------- src/test/include.am | 1 - src/test/test.c | 1 - src/test/test.h | 1 - src/test/test_rust.c | 31 -------- src/test/test_rust.sh | 5 +- 16 files changed, 33 insertions(+), 311 deletions(-) delete mode 100644 src/common/compat_rust.c delete mode 100644 src/common/compat_rust.h delete mode 100644 src/rust/tor_util/rust_string.rs delete mode 100644 src/rust/tor_util/tests/rust_string.rs delete mode 100644 src/test/test_rust.c diff --git a/src/common/compat_rust.c b/src/common/compat_rust.c deleted file mode 100644 index 366fd4037b..0000000000 --- a/src/common/compat_rust.c +++ /dev/null @@ -1,39 +0,0 @@ -/* 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 str, 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 deleted file mode 100644 index 72fde39296..0000000000 --- a/src/common/compat_rust.h +++ /dev/null @@ -1,28 +0,0 @@ -/* 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 /* !defined(TOR_RUST_COMPAT_H) */ - diff --git a/src/common/include.am b/src/common/include.am index cd5eea3404..2856c40fdc 100644 --- a/src/common/include.am +++ b/src/common/include.am @@ -101,11 +101,6 @@ 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 @@ -156,7 +151,6 @@ 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 65b0b8f4df..e9f636aa55 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -60,7 +60,6 @@ #include "circuitlist.h" #include "circuituse.h" #include "command.h" -#include "compat_rust.h" #include "compress.h" #include "config.h" #include "confparse.h" @@ -128,6 +127,10 @@ void evdns_shutdown(int); +// helper function defined in Rust to output a log message indicating if tor is +// running with Rust enabled. See src/rust/tor_util +char *rust_welcome_string(void); + /********* PROTOTYPES **********/ static void dumpmemusage(int severity); @@ -3111,14 +3114,13 @@ 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); +#ifdef HAVE_RUST + char *rust_str = rust_welcome_string(); + if (rust_str != NULL && strlen(rust_str) > 0) { + log_notice(LD_GENERAL, "%s", rust_str); } + tor_free(rust_str); +#endif if (network_init()<0) { log_err(LD_BUG,"Error initializing network; exiting."); diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock index a5686979fa..224d2135bf 100644 --- a/src/rust/Cargo.lock +++ b/src/rust/Cargo.lock @@ -3,6 +3,7 @@ name = "tor_util" version = "0.0.1" dependencies = [ "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", + "tor_allocate 0.0.1", ] [[package]] diff --git a/src/rust/tor_allocate/tor_allocate.rs b/src/rust/tor_allocate/tor_allocate.rs index de1d13942e..e2fc3ea36c 100644 --- a/src/rust/tor_allocate/tor_allocate.rs +++ b/src/rust/tor_allocate/tor_allocate.rs @@ -26,9 +26,9 @@ extern "C" fn tor_malloc_ ( size: usize) -> *mut c_void { /// A `String` that should be freed by tor_free in C /// pub fn allocate_and_copy_string(src: &String) -> *mut c_char { - let bytes = s.as_bytes(); + let bytes = src.as_bytes(); - let size = s.len(); + let size = bytes.len(); let size_with_null_byte = size + 1; let dest = unsafe { tor_malloc_(size_with_null_byte) as *mut u8 }; diff --git a/src/rust/tor_util/Cargo.toml b/src/rust/tor_util/Cargo.toml index 906833ce24..d7379a5988 100644 --- a/src/rust/tor_util/Cargo.toml +++ b/src/rust/tor_util/Cargo.toml @@ -8,6 +8,9 @@ name = "tor_util" path = "lib.rs" crate_type = ["rlib", "staticlib"] +[dependencies.tor_allocate] +path = "../tor_allocate" + [dependencies] libc = "0.2.22" diff --git a/src/rust/tor_util/ffi.rs b/src/rust/tor_util/ffi.rs index af4bfc41af..9a56309365 100644 --- a/src/rust/tor_util/ffi.rs +++ b/src/rust/tor_util/ffi.rs @@ -1,56 +1,21 @@ -//! FFI functions, only to be called from C. +//! FFI functions to announce Rust support during tor startup, 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 -} +use libc::c_char; +use tor_allocate::allocate_and_copy_string; /// 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); +/// char *rust_str = rust_welcome_string(); +/// printf("%s", rust_str); +/// tor_free(rust_str); /// ``` #[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) +pub extern "C" fn rust_welcome_string() -> *mut c_char { + let rust_welcome = String::from("Tor is running with Rust integration. Please report \ + any bugs you encouter."); + allocate_and_copy_string(&rust_welcome) } diff --git a/src/rust/tor_util/lib.rs b/src/rust/tor_util/lib.rs index 79d583d1ae..9c863e39bd 100644 --- a/src/rust/tor_util/lib.rs +++ b/src/rust/tor_util/lib.rs @@ -1,13 +1,8 @@ -//! C <-> Rust compatibility helpers and types. +//! Small module to announce Rust support during startup for demonstration +//! purposes. //! -//! 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; +extern crate tor_allocate; -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 deleted file mode 100644 index 46ec3fd7a8..0000000000 --- a/src/rust/tor_util/rust_string.rs +++ /dev/null @@ -1,101 +0,0 @@ -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 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 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::()) - } -} diff --git a/src/rust/tor_util/tests/rust_string.rs b/src/rust/tor_util/tests/rust_string.rs deleted file mode 100644 index 1ff605a43c..0000000000 --- a/src/rust/tor_util/tests/rust_string.rs +++ /dev/null @@ -1,37 +0,0 @@ -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 95efe4e795..f66986316a 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -148,7 +148,6 @@ 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 27c776b304..95cab99b5a 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1225,7 +1225,6 @@ 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 ba7b767c37..454573c2f9 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -238,7 +238,6 @@ 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 deleted file mode 100644 index 6ad57d6fcb..0000000000 --- a/src/test/test_rust.c +++ /dev/null @@ -1,31 +0,0 @@ -/* 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 -}; - diff --git a/src/test/test_rust.sh b/src/test/test_rust.sh index d559f94ce0..50740fd18e 100755 --- a/src/test/test_rust.sh +++ b/src/test/test_rust.sh @@ -1,13 +1,14 @@ #!/bin/sh -# Test all the Rust crates we're using +# Test all Rust crates -crates=tor_util +crates="protover tor_util smartlist tor_allocate" exitcode=0 for crate in $crates; do cd "${abs_top_srcdir:-.}/src/rust/${crate}" CARGO_TARGET_DIR="${abs_top_builddir}/src/rust/target" CARGO_HOME="${abs_top_builddir}/src/rust" "${CARGO:-cargo}" test ${CARGO_ONLINE-"--frozen"} || exitcode=1 + cd - done exit $exitcode From cd2a036959e90e77bc5002c05dae120f54f8ecb8 Mon Sep 17 00:00:00 2001 From: Chelsea Holland Komlo Date: Sun, 22 Oct 2017 00:24:15 -0400 Subject: [PATCH 08/14] refactor smartlist for readability limit scoping of unsafe, and other cleanup --- src/rust/smartlist/smartlist.rs | 49 +++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/src/rust/smartlist/smartlist.rs b/src/rust/smartlist/smartlist.rs index 5eff3fe43e..59296ab3b1 100644 --- a/src/rust/smartlist/smartlist.rs +++ b/src/rust/smartlist/smartlist.rs @@ -1,50 +1,62 @@ use std::slice; -use libc::c_char; +use libc::{c_char, c_int}; use std::ffi::CStr; /// Smartlists are a type used in C code in tor to define a collection of a /// generic type, which has a capacity and a number used. Each Smartlist /// defines how to extract the list of values from the underlying C structure -/// Implementations are required to have a C representation +/// +/// Implementations are required to have a C representation, as this module +/// serves purely to translate smartlists as defined in tor to vectors in Rust. pub trait Smartlist { fn get_list(&self) -> Vec; } + #[repr(C)] pub struct Stringlist { pub list: *const *const c_char, - pub num_used: u8, - pub capacity: u8, + pub num_used: c_int, + pub capacity: c_int, } impl Smartlist for Stringlist { fn get_list(&self) -> Vec { let empty: Vec = Vec::new(); - let mut v: Vec = Vec::new(); + let mut rust_list: Vec = Vec::new(); - if self.list.is_null() { + if self.list.is_null() || self.num_used == 0 { return empty; } // unsafe, as we need to extract the smartlist list into a vector of // pointers, and then transform each element into a Rust string. - unsafe { - let elems = - slice::from_raw_parts(self.list, self.num_used as usize); + let elems: &[*const i8] = unsafe { + slice::from_raw_parts(self.list, self.num_used as usize) + }; - for i in elems.iter() { - let c_str = CStr::from_ptr(*i); - let r_str = match c_str.to_str() { - Ok(n) => n, - Err(_) => return empty, - }; - v.push(String::from(r_str)); + for elem in elems.iter() { + if elem.is_null() { + continue; } + + // unsafe, as we need to create a cstring from the referenced + // element + let c_string = unsafe { CStr::from_ptr(*elem) }; + + let r_string = match c_string.to_str() { + Ok(n) => n, + Err(_) => return empty, + }; + + rust_list.push(String::from(r_string)); } - v + rust_list } } +// TODO: CHK: this module maybe should be tested from a test in C with a +// smartlist as defined in tor. #[cfg(test)] mod test { #[test] @@ -83,9 +95,10 @@ mod test { let p_args: Vec<_> = c_strings.iter().map(|arg| arg.as_ptr()).collect(); - // then, collect a pointer for the list itself let p: *const *const c_char = p_args.as_ptr(); + // This is the representation that we expect when receiving a + // smartlist at the Rust/C FFI layer. let sl = Stringlist { list: p, num_used: 2, From 7d126b9db9628740aeb40a007d247a8affee466f Mon Sep 17 00:00:00 2001 From: Chelsea Holland Komlo Date: Mon, 23 Oct 2017 23:19:44 -0400 Subject: [PATCH 09/14] remove unused include.am for rust modules --- src/rust/protover/include.am | 14 -------------- src/rust/tor_allocate/include.am | 13 ------------- 2 files changed, 27 deletions(-) delete mode 100644 src/rust/protover/include.am delete mode 100644 src/rust/tor_allocate/include.am diff --git a/src/rust/protover/include.am b/src/rust/protover/include.am deleted file mode 100644 index 326dbbb7f2..0000000000 --- a/src/rust/protover/include.am +++ /dev/null @@ -1,14 +0,0 @@ -EXTRA_DIST +=\ - src/rust/protover/Cargo.toml \ - src/rust/protover/lib.rs \ - src/rust/protover/ffi.rs \ - src/rust/protover/external.rs - -src/rust/target/release/@TOR_RUST_PROTOVER_STATIC_NAME@: FORCE - ( cd "$(abs_top_srcdir)/src/rust/protover" ; \ - CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \ - CARGO_HOME="$(abs_top_builddir)/src/rust" \ - $(CARGO) build --release --quiet $(CARGO_ONLINE) ) - -FORCE: - diff --git a/src/rust/tor_allocate/include.am b/src/rust/tor_allocate/include.am deleted file mode 100644 index 9e770dbc05..0000000000 --- a/src/rust/tor_allocate/include.am +++ /dev/null @@ -1,13 +0,0 @@ -EXTRA_DIST +=\ - src/rust/tor_allocate/Cargo.toml \ - src/rust/tor_allocate/lib.rs \ - src/rust/tor_allocate/tor_allocate.rs - -src/rust/target/release/@TOR_RUST_C_STRING_STATIC_NAME@: FORCE - ( cd "$(abs_top_srcdir)/src/rust/tor_allocate" ; \ - CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \ - CARGO_HOME="$(abs_top_builddir)/src/rust" \ - $(CARGO) build --release --quiet $(CARGO_ONLINE) ) - -FORCE: - From 7999d0bf6b362972f7e2edab9586435bd4daf563 Mon Sep 17 00:00:00 2001 From: Chelsea Holland Komlo Date: Wed, 25 Oct 2017 22:22:10 -0400 Subject: [PATCH 10/14] update tor_allocate and add tests --- src/rust/tor_allocate/tor_allocate.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/rust/tor_allocate/tor_allocate.rs b/src/rust/tor_allocate/tor_allocate.rs index e2fc3ea36c..7b348b6d02 100644 --- a/src/rust/tor_allocate/tor_allocate.rs +++ b/src/rust/tor_allocate/tor_allocate.rs @@ -1,5 +1,5 @@ use libc::{c_char, c_void}; -use std::{ptr, slice}; +use std::{ptr, slice, mem}; #[cfg(not(test))] extern "C" { @@ -19,29 +19,35 @@ extern "C" fn tor_malloc_ ( size: usize) -> *mut c_void { /// /// # Inputs /// -/// * `src`, a reference to a String that will be copied. +/// * `src`, a reference to a String. /// /// # Returns /// -/// A `String` that should be freed by tor_free in C +/// A `*mut c_char` that should be freed by tor_free in C /// pub fn allocate_and_copy_string(src: &String) -> *mut c_char { - let bytes = src.as_bytes(); + let bytes: &[u8] = src.as_bytes(); - let size = bytes.len(); - let size_with_null_byte = size + 1; + let size = mem::size_of_val::<[u8]>(bytes); + let size_one_byte = mem::size_of::(); + + // handle integer overflow when adding one to the calculated length + let size_with_null_byte = match size.checked_add(size_one_byte) { + Some(n) => n, + None => return ptr::null_mut(), + }; let dest = unsafe { tor_malloc_(size_with_null_byte) as *mut u8 }; if dest.is_null() { - return dest as *mut c_char; + return ptr::null_mut(); } unsafe { ptr::copy_nonoverlapping(bytes.as_ptr(), dest, size) }; // set the last byte as null, using the ability to index into a slice // rather than doing pointer arithmatic - let slice = unsafe { slice::from_raw_parts_mut(dest, size_with_null_byte) }; + let slice = unsafe { slice::from_raw_parts_mut(dest, size_with_null_byte)}; slice[size] = 0; // add a null terminator dest as *mut c_char From 90daad999e78c8ec8239e63ea03df6b3b2e364b6 Mon Sep 17 00:00:00 2001 From: Chelsea Holland Komlo Date: Wed, 25 Oct 2017 23:02:38 -0400 Subject: [PATCH 11/14] remove experimental rust features allow unsafe on function that calls C --- src/rust/protover/lib.rs | 2 -- src/rust/protover/protover.rs | 3 ++- src/rust/tor_allocate/tor_allocate.rs | 4 ++++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/rust/protover/lib.rs b/src/rust/protover/lib.rs index 620191f88d..5a5dea4408 100644 --- a/src/rust/protover/lib.rs +++ b/src/rust/protover/lib.rs @@ -1,5 +1,3 @@ -#![feature(inclusive_range_syntax)] - //! Copyright (c) 2016-2017, The Tor Project, Inc. */ //! See LICENSE for licensing information */ diff --git a/src/rust/protover/protover.rs b/src/rust/protover/protover.rs index 37589a83f9..8a546e09a3 100644 --- a/src/rust/protover/protover.rs +++ b/src/rust/protover/protover.rs @@ -362,7 +362,8 @@ fn expand_version_range(range: &str) -> Result, &'static str> { "cannot parse protocol range upper bound", ))?; - Ok((lower..=higher).collect()) + // We can use inclusive range syntax when it becomes stable. + Ok((lower..higher+1).collect()) } /// Checks to see if there is a continuous range of integers, starting at the diff --git a/src/rust/tor_allocate/tor_allocate.rs b/src/rust/tor_allocate/tor_allocate.rs index 7b348b6d02..03ed2499c7 100644 --- a/src/rust/tor_allocate/tor_allocate.rs +++ b/src/rust/tor_allocate/tor_allocate.rs @@ -25,6 +25,10 @@ extern "C" fn tor_malloc_ ( size: usize) -> *mut c_void { /// /// A `*mut c_char` that should be freed by tor_free in C /// +/// Allow unused unsafe as at compile-time, we get warnings that unsafe is not +/// needed even though this calls tor_malloc in C. +/// +#[allow(unused_unsafe)] pub fn allocate_and_copy_string(src: &String) -> *mut c_char { let bytes: &[u8] = src.as_bytes(); From 6be75bd61d72636a1c23b6fd9866d33a35433a73 Mon Sep 17 00:00:00 2001 From: Chelsea Holland Komlo Date: Thu, 26 Oct 2017 09:50:50 -0400 Subject: [PATCH 12/14] cargo fmt; fix line length warnings --- src/rust/protover/ffi.rs | 1 - src/rust/protover/protover.rs | 14 ++++++++------ src/rust/protover/tests/protover.rs | 16 ++++++++-------- src/rust/smartlist/smartlist.rs | 5 ++--- src/rust/tor_allocate/tor_allocate.rs | 18 ++++++++---------- src/rust/tor_util/ffi.rs | 6 ++++-- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/rust/protover/ffi.rs b/src/rust/protover/ffi.rs index 23a289bd56..f897c98083 100644 --- a/src/rust/protover/ffi.rs +++ b/src/rust/protover/ffi.rs @@ -13,7 +13,6 @@ use tor_allocate::allocate_and_copy_string; /// Translate C enums to Rust Proto enums, using the integer value of the C /// enum to map to its associated Rust enum /// This is dependant on the associated C enum preserving ordering. -/// Modify the C documentation to give warnings- you must also re-order the rust fn translate_to_rust(c_proto: uint32_t) -> Result { match c_proto { 0 => Ok(Proto::Link), diff --git a/src/rust/protover/protover.rs b/src/rust/protover/protover.rs index 8a546e09a3..d75da61aa8 100644 --- a/src/rust/protover/protover.rs +++ b/src/rust/protover/protover.rs @@ -142,7 +142,8 @@ fn tor_supported() -> Result>, &'static str> { /// This function will error if: /// /// * the `version_string` is empty or contains an equals (`"="`) sign, -/// * the expansion of a version range produces an error (see `expand_version_range`), +/// * the expansion of a version range produces an error (see +/// `expand_version_range`), /// * any single version number is not parseable as an `u32` in radix 10, or /// * there are greater than 2^16 version numbers to expand. /// @@ -293,10 +294,12 @@ pub fn all_supported(protocols: &str) -> (bool, String) { /// ``` /// use protover::*; /// -/// let is_supported = protover_string_supports_protocol("Link=3-4 Cons=1", Proto::Cons,1); +/// let is_supported = protover_string_supports_protocol("Link=3-4 Cons=1", +/// Proto::Cons,1); /// assert_eq!(true, is_supported); /// -/// let is_not_supported = protover_string_supports_protocol("Link=3-4 Cons=1", Proto::Cons,5); +/// let is_not_supported = protover_string_supports_protocol("Link=3-4 Cons=1", +/// Proto::Cons,5); /// assert_eq!(false, is_not_supported) /// ``` pub fn protover_string_supports_protocol( @@ -363,7 +366,7 @@ fn expand_version_range(range: &str) -> Result, &'static str> { ))?; // We can use inclusive range syntax when it becomes stable. - Ok((lower..higher+1).collect()) + Ok((lower..higher + 1).collect()) } /// Checks to see if there is a continuous range of integers, starting at the @@ -477,8 +480,7 @@ fn contract_protocol_list<'a>(supported_set: &'a HashSet) -> String { fn parse_protocols_from_string_with_no_validation<'a>( protocol_string: &'a str, ) -> Result>, &'static str> { - let protocols = &protocol_string.split(" ") - .collect::>()[..]; + let protocols = &protocol_string.split(" ").collect::>()[..]; let mut parsed: HashMap> = HashMap::new(); diff --git a/src/rust/protover/tests/protover.rs b/src/rust/protover/tests/protover.rs index 7d8484ecc2..af7633a484 100644 --- a/src/rust/protover/tests/protover.rs +++ b/src/rust/protover/tests/protover.rs @@ -1,7 +1,7 @@ extern crate protover; #[test] -fn parse_protocol_list_with_single_protocol_and_single_version_returns_set_of_one(){ +fn parse_protocol_list_with_single_proto_and_single_version() { let protocol = "Cons=1"; let (is_supported, unsupported) = protover::all_supported(protocol); assert_eq!(true, is_supported); @@ -9,7 +9,7 @@ fn parse_protocol_list_with_single_protocol_and_single_version_returns_set_of_on } #[test] -fn parse_protocol_list_with_single_protocol_and_multiple_versions_returns_set_of_one(){ +fn parse_protocol_list_with_single_protocol_and_multiple_versions() { let protocol = "Cons=1-2"; let (is_supported, unsupported) = protover::all_supported(protocol); assert_eq!(true, is_supported); @@ -17,7 +17,7 @@ fn parse_protocol_list_with_single_protocol_and_multiple_versions_returns_set_of } #[test] -fn parse_protocol_list_with_different_single_protocol_and_single_version_returns_set_of_one(){ +fn parse_protocol_list_with_different_single_protocol_and_single_version() { let protocol = "HSDir=1"; let (is_supported, unsupported) = protover::all_supported(protocol); assert_eq!(true, is_supported); @@ -25,7 +25,7 @@ fn parse_protocol_list_with_different_single_protocol_and_single_version_returns } #[test] -fn parse_protocol_list_with_single_protocol_and_supported_version_returns_set_of_one(){ +fn parse_protocol_list_with_single_protocol_and_supported_version() { let protocol = "Desc=2"; let (is_supported, unsupported) = protover::all_supported(protocol); assert_eq!(true, is_supported); @@ -33,7 +33,7 @@ fn parse_protocol_list_with_single_protocol_and_supported_version_returns_set_of } #[test] -fn parse_protocol_list_with_two_protocols_and_single_version_returns_set_of_one(){ +fn parse_protocol_list_with_two_protocols_and_single_version() { let protocols = "Cons=1 HSDir=1"; let (is_supported, unsupported) = protover::all_supported(protocols); assert_eq!(true, is_supported); @@ -42,7 +42,7 @@ fn parse_protocol_list_with_two_protocols_and_single_version_returns_set_of_one( #[test] -fn parse_protocol_list_with_single_protocol_and_two_nonsequential_versions_returns_set_of_two(){ +fn parse_protocol_list_with_single_protocol_and_two_nonsequential_versions() { let protocol = "Desc=1,2"; let (is_supported, unsupported) = protover::all_supported(protocol); assert_eq!(true, is_supported); @@ -51,7 +51,7 @@ fn parse_protocol_list_with_single_protocol_and_two_nonsequential_versions_retur #[test] -fn parse_protocol_list_with_single_protocol_and_two_sequential_versions_returns_set_of_two(){ +fn parse_protocol_list_with_single_protocol_and_two_sequential_versions() { let protocol = "Desc=1-2"; let (is_supported, unsupported) = protover::all_supported(protocol); assert_eq!(true, is_supported); @@ -169,7 +169,7 @@ fn protover_string_supports_protocol_returns_false_for_single_unsupported() { } #[test] -fn protover_string_supports_protocol_returns_false_when_protocol_name_is_not_in_map(){ +fn protover_string_supports_protocol_returns_false_for_unsupported() { let protocols = "Link=3-4"; let is_supported = protover::protover_string_supports_protocol( protocols, diff --git a/src/rust/smartlist/smartlist.rs b/src/rust/smartlist/smartlist.rs index 59296ab3b1..9f5e14f1ad 100644 --- a/src/rust/smartlist/smartlist.rs +++ b/src/rust/smartlist/smartlist.rs @@ -30,9 +30,8 @@ impl Smartlist for Stringlist { // unsafe, as we need to extract the smartlist list into a vector of // pointers, and then transform each element into a Rust string. - let elems: &[*const i8] = unsafe { - slice::from_raw_parts(self.list, self.num_used as usize) - }; + let elems: &[*const i8] = + unsafe { slice::from_raw_parts(self.list, self.num_used as usize) }; for elem in elems.iter() { if elem.is_null() { diff --git a/src/rust/tor_allocate/tor_allocate.rs b/src/rust/tor_allocate/tor_allocate.rs index 03ed2499c7..663600ec5c 100644 --- a/src/rust/tor_allocate/tor_allocate.rs +++ b/src/rust/tor_allocate/tor_allocate.rs @@ -3,13 +3,13 @@ use std::{ptr, slice, mem}; #[cfg(not(test))] extern "C" { - fn tor_malloc_ ( size: usize) -> *mut c_void; + fn tor_malloc_(size: usize) -> *mut c_void; } // Defined only for tests, used for testing purposes, so that we don't need // to link to tor C files. Uses the system allocator #[cfg(test)] -extern "C" fn tor_malloc_ ( size: usize) -> *mut c_void { +extern "C" fn tor_malloc_(size: usize) -> *mut c_void { use libc::malloc; unsafe { malloc(size) } } @@ -32,7 +32,7 @@ extern "C" fn tor_malloc_ ( size: usize) -> *mut c_void { pub fn allocate_and_copy_string(src: &String) -> *mut c_char { let bytes: &[u8] = src.as_bytes(); - let size = mem::size_of_val::<[u8]>(bytes); + let size = mem::size_of_val::<[u8]>(bytes); let size_one_byte = mem::size_of::(); // handle integer overflow when adding one to the calculated length @@ -51,7 +51,7 @@ pub fn allocate_and_copy_string(src: &String) -> *mut c_char { // set the last byte as null, using the ability to index into a slice // rather than doing pointer arithmatic - let slice = unsafe { slice::from_raw_parts_mut(dest, size_with_null_byte)}; + let slice = unsafe { slice::from_raw_parts_mut(dest, size_with_null_byte) }; slice[size] = 0; // add a null terminator dest as *mut c_char @@ -70,9 +70,8 @@ mod test { let empty = String::new(); let allocated_empty = allocate_and_copy_string(&empty); - let allocated_empty_rust = unsafe { - CStr::from_ptr(allocated_empty).to_str().unwrap() - }; + let allocated_empty_rust = + unsafe { CStr::from_ptr(allocated_empty).to_str().unwrap() }; assert_eq!("", allocated_empty_rust); @@ -89,9 +88,8 @@ mod test { let empty = String::from("foo bar biz"); let allocated_empty = allocate_and_copy_string(&empty); - let allocated_empty_rust = unsafe { - CStr::from_ptr(allocated_empty).to_str().unwrap() - }; + let allocated_empty_rust = + unsafe { CStr::from_ptr(allocated_empty).to_str().unwrap() }; assert_eq!("foo bar biz", allocated_empty_rust); diff --git a/src/rust/tor_util/ffi.rs b/src/rust/tor_util/ffi.rs index 9a56309365..214727a190 100644 --- a/src/rust/tor_util/ffi.rs +++ b/src/rust/tor_util/ffi.rs @@ -15,7 +15,9 @@ use tor_allocate::allocate_and_copy_string; /// ``` #[no_mangle] pub extern "C" fn rust_welcome_string() -> *mut c_char { - let rust_welcome = String::from("Tor is running with Rust integration. Please report \ - any bugs you encouter."); + let rust_welcome = String::from( + "Tor is running with Rust integration. Please report \ + any bugs you encouter.", + ); allocate_and_copy_string(&rust_welcome) } From 2ca8fcb892d6e0452160ed9a19fe7cb4c3741b40 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 27 Oct 2017 09:54:54 -0400 Subject: [PATCH 13/14] Add missing copyright/license statements on all .rs files (Yes, I have Chelsea's permission.) --- src/rust/external/external.rs | 3 +++ src/rust/protover/ffi.rs | 3 +++ src/rust/protover/protover.rs | 3 +++ src/rust/protover/tests/protover.rs | 3 +++ src/rust/smartlist/lib.rs | 3 +++ src/rust/smartlist/smartlist.rs | 3 +++ src/rust/tor_allocate/lib.rs | 3 +++ src/rust/tor_allocate/tor_allocate.rs | 3 +++ src/rust/tor_util/ffi.rs | 3 +++ src/rust/tor_util/lib.rs | 3 +++ 10 files changed, 30 insertions(+) diff --git a/src/rust/external/external.rs b/src/rust/external/external.rs index 09d80cb2ba..b9e17f021d 100644 --- a/src/rust/external/external.rs +++ b/src/rust/external/external.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + use libc::{c_char, c_int}; use std::ffi::CString; diff --git a/src/rust/protover/ffi.rs b/src/rust/protover/ffi.rs index f897c98083..cf2e9fd783 100644 --- a/src/rust/protover/ffi.rs +++ b/src/rust/protover/ffi.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + //! FFI functions, only to be called from C. //! //! Equivalent C versions of this api are in `src/or/protover.c` diff --git a/src/rust/protover/protover.rs b/src/rust/protover/protover.rs index d75da61aa8..11e9d0079e 100644 --- a/src/rust/protover/protover.rs +++ b/src/rust/protover/protover.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + use external::c_tor_version_as_new_as; use std::str::FromStr; diff --git a/src/rust/protover/tests/protover.rs b/src/rust/protover/tests/protover.rs index af7633a484..f4e394b3e2 100644 --- a/src/rust/protover/tests/protover.rs +++ b/src/rust/protover/tests/protover.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + extern crate protover; #[test] diff --git a/src/rust/smartlist/lib.rs b/src/rust/smartlist/lib.rs index 71d89a3b87..14a8148315 100644 --- a/src/rust/smartlist/lib.rs +++ b/src/rust/smartlist/lib.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + extern crate libc; mod smartlist; diff --git a/src/rust/smartlist/smartlist.rs b/src/rust/smartlist/smartlist.rs index 9f5e14f1ad..ec5d7a57f5 100644 --- a/src/rust/smartlist/smartlist.rs +++ b/src/rust/smartlist/smartlist.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + use std::slice; use libc::{c_char, c_int}; use std::ffi::CStr; diff --git a/src/rust/tor_allocate/lib.rs b/src/rust/tor_allocate/lib.rs index 81afd095f9..937a5dcf63 100644 --- a/src/rust/tor_allocate/lib.rs +++ b/src/rust/tor_allocate/lib.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + //! Allocation helper functions that allow data to be allocated in Rust //! using tor's specified allocator. In doing so, this can be later freed //! from C. diff --git a/src/rust/tor_allocate/tor_allocate.rs b/src/rust/tor_allocate/tor_allocate.rs index 663600ec5c..8a6fabe9cb 100644 --- a/src/rust/tor_allocate/tor_allocate.rs +++ b/src/rust/tor_allocate/tor_allocate.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + use libc::{c_char, c_void}; use std::{ptr, slice, mem}; diff --git a/src/rust/tor_util/ffi.rs b/src/rust/tor_util/ffi.rs index 214727a190..76c6e6d394 100644 --- a/src/rust/tor_util/ffi.rs +++ b/src/rust/tor_util/ffi.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + //! FFI functions to announce Rust support during tor startup, only to be //! called from C. //! diff --git a/src/rust/tor_util/lib.rs b/src/rust/tor_util/lib.rs index 9c863e39bd..42fa9d5ad0 100644 --- a/src/rust/tor_util/lib.rs +++ b/src/rust/tor_util/lib.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + //! Small module to announce Rust support during startup for demonstration //! purposes. //! From 91a1b9058bccf17893bea3e0c8dd98b5141bdfa9 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 27 Oct 2017 10:01:47 -0400 Subject: [PATCH 14/14] Add a changes file --- changes/ticket22840 | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 changes/ticket22840 diff --git a/changes/ticket22840 b/changes/ticket22840 new file mode 100644 index 0000000000..6d234fb0d4 --- /dev/null +++ b/changes/ticket22840 @@ -0,0 +1,8 @@ + o Major features (Rust experimentation): + - Tor now ships with an optional implementation of one of its smaller + modules (protover.c) in the Rust programming language. To try it + out, install a Rust build environment, and configure Tor with + "--enable-rust --enable-cargo-online-mode". This should not + cause any user-visible changes, but should help us gain more experience + with Rust, and plan future Rust integration work. + Implementation by Chelsea Komlo. Closes ticket 22840.