summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/aes.c2
-rw-r--r--src/common/buffers.c69
-rw-r--r--src/common/buffers.h7
-rw-r--r--src/common/compat.c20
-rw-r--r--src/common/compat.h1
-rw-r--r--src/common/compat_libevent.c2
-rw-r--r--src/common/compat_libevent.h1
-rw-r--r--src/common/compat_rust.c39
-rw-r--r--src/common/compat_rust.h28
-rw-r--r--src/common/compat_time.c4
-rw-r--r--src/common/compat_winthreads.c4
-rw-r--r--src/common/confline.c94
-rw-r--r--src/common/confline.h5
-rw-r--r--src/common/crypto_pwbox.h3
-rw-r--r--src/common/include.am6
-rw-r--r--src/common/sandbox.c3
-rw-r--r--src/common/util.c24
-rw-r--r--src/or/addressmap.c3
-rw-r--r--src/or/config.c118
-rw-r--r--src/or/connection.c138
-rw-r--r--src/or/connection.h4
-rw-r--r--src/or/connection_edge.c2
-rw-r--r--src/or/control.c46
-rw-r--r--src/or/control.h4
-rw-r--r--src/or/directory.c48
-rw-r--r--src/or/directory.h3
-rw-r--r--src/or/entrynodes.c6
-rw-r--r--src/or/entrynodes.h4
-rw-r--r--src/or/geoip.c22
-rw-r--r--src/or/git_revision.c17
-rw-r--r--src/or/git_revision.h12
-rw-r--r--src/or/hibernate.c21
-rw-r--r--src/or/include.am12
-rw-r--r--src/or/main.c161
-rw-r--r--src/or/main.h6
-rw-r--r--src/or/networkstatus.c4
-rw-r--r--src/or/ntmain.c4
-rw-r--r--src/or/or.h12
-rw-r--r--src/or/policies.c52
-rw-r--r--src/or/policies.h3
-rw-r--r--src/or/protover.c4
-rw-r--r--src/or/protover.h6
-rw-r--r--src/or/protover_rust.c19
-rw-r--r--src/or/routerlist.c12
-rw-r--r--src/or/routerparse.c4
-rw-r--r--src/or/scheduler.c2
-rw-r--r--src/or/tor_api.c88
-rw-r--r--src/or/tor_api.h102
-rw-r--r--src/or/tor_api_internal.h20
-rw-r--r--src/or/tor_main.c14
-rw-r--r--src/rust/.cargo/config.in2
-rw-r--r--src/rust/Cargo.lock41
-rw-r--r--src/rust/Cargo.toml2
-rw-r--r--src/rust/external/Cargo.toml13
-rw-r--r--src/rust/external/external.rs33
-rw-r--r--src/rust/external/lib.rs14
-rw-r--r--src/rust/include.am24
-rw-r--r--src/rust/protover/Cargo.toml25
-rw-r--r--src/rust/protover/ffi.rs185
-rw-r--r--src/rust/protover/lib.rs33
-rw-r--r--src/rust/protover/protover.rs852
-rw-r--r--src/rust/protover/tests/protover.rs291
-rw-r--r--src/rust/smartlist/Cargo.toml13
-rw-r--r--src/rust/smartlist/lib.rs8
-rw-r--r--src/rust/smartlist/smartlist.rs115
-rw-r--r--src/rust/tor_allocate/Cargo.toml13
-rw-r--r--src/rust/tor_allocate/lib.rs15
-rw-r--r--src/rust/tor_allocate/tor_allocate.rs97
-rw-r--r--src/rust/tor_rust/Cargo.toml16
-rw-r--r--src/rust/tor_rust/include.am24
-rw-r--r--src/rust/tor_rust/lib.rs5
-rw-r--r--src/rust/tor_util/Cargo.toml5
-rw-r--r--src/rust/tor_util/ffi.rs62
-rw-r--r--src/rust/tor_util/include.am13
-rw-r--r--src/rust/tor_util/lib.rs14
-rw-r--r--src/rust/tor_util/rust_string.rs101
-rw-r--r--src/rust/tor_util/tests/rust_string.rs37
-rw-r--r--src/test/bench.c5
-rw-r--r--src/test/fuzz/fuzzing_common.c3
-rw-r--r--src/test/include.am1
-rw-r--r--src/test/test-memwipe.c3
-rw-r--r--src/test/test.c1
-rw-r--r--src/test/test.h1
-rw-r--r--src/test/test_accounting.c3
-rw-r--r--src/test/test_channelpadding.c3
-rw-r--r--src/test/test_config.c117
-rw-r--r--src/test/test_dns.c3
-rwxr-xr-xsrc/test/test_keygen.sh109
-rw-r--r--src/test/test_protover.c43
-rw-r--r--src/test/test_routerlist.c112
-rw-r--r--src/test/test_routerset.c3
-rw-r--r--src/test/test_rust.c31
-rwxr-xr-xsrc/test/test_rust.sh9
-rw-r--r--src/test/test_shared_random.c3
-rw-r--r--src/test/test_status.c3
-rw-r--r--src/test/testing_common.c6
-rw-r--r--src/tools/include.am5
-rw-r--r--src/tools/tor_runner.c101
-rw-r--r--src/win32/orconfig.h2
99 files changed, 3280 insertions, 625 deletions
diff --git a/src/common/aes.c b/src/common/aes.c
index 20b51a6758..df4368fdba 100644
--- a/src/common/aes.c
+++ b/src/common/aes.c
@@ -254,7 +254,7 @@ evaluate_ctr_for_aes(void)
/* LCOV_EXCL_START */
log_err(LD_CRYPTO, "This OpenSSL has a buggy version of counter mode; "
"quitting tor.");
- exit(1);
+ exit(1); // exit ok: openssl is broken.
/* LCOV_EXCL_STOP */
}
return 0;
diff --git a/src/common/buffers.c b/src/common/buffers.c
index c45e13d551..a21cf2ab5b 100644
--- a/src/common/buffers.c
+++ b/src/common/buffers.c
@@ -714,6 +714,53 @@ buf_add(buf_t *buf, const char *string, size_t string_len)
return (int)buf->datalen;
}
+/** Add a nul-terminated <b>string</b> to <b>buf</b>, not including the
+ * terminating NUL. */
+void
+buf_add_string(buf_t *buf, const char *string)
+{
+ buf_add(buf, string, strlen(string));
+}
+
+/** As tor_snprintf, but write the results into a buf_t */
+void
+buf_add_printf(buf_t *buf, const char *format, ...)
+{
+ va_list ap;
+ va_start(ap,format);
+ buf_add_vprintf(buf, format, ap);
+ va_end(ap);
+}
+
+/** As tor_vsnprintf, but write the results into a buf_t. */
+void
+buf_add_vprintf(buf_t *buf, const char *format, va_list args)
+{
+ /* XXXX Faster implementations are easy enough, but let's optimize later */
+ char *tmp;
+ tor_vasprintf(&tmp, format, args);
+ buf_add(buf, tmp, strlen(tmp));
+ tor_free(tmp);
+}
+
+/** Return a heap-allocated string containing the contents of <b>buf</b>, plus
+ * a NUL byte. If <b>sz_out</b> is provided, set *<b>sz_out</b> to the length
+ * of the returned string, not including the terminating NUL. */
+char *
+buf_extract(buf_t *buf, size_t *sz_out)
+{
+ tor_assert(buf);
+
+ size_t sz = buf_datalen(buf);
+ char *result;
+ result = tor_malloc(sz+1);
+ buf_peek(buf, result, sz);
+ result[sz] = 0;
+ if (sz_out)
+ *sz_out = sz;
+ return result;
+}
+
/** Helper: copy the first <b>string_len</b> bytes from <b>buf</b>
* onto <b>string</b>.
*/
@@ -795,6 +842,28 @@ buf_move_to_buf(buf_t *buf_out, buf_t *buf_in, size_t *buf_flushlen)
return (int)cp;
}
+/** Moves all data from <b>buf_in</b> to <b>buf_out</b>, without copying.
+ */
+void
+buf_move_all(buf_t *buf_out, buf_t *buf_in)
+{
+ tor_assert(buf_out);
+ if (!buf_in)
+ return;
+
+ if (buf_out->head == NULL) {
+ buf_out->head = buf_in->head;
+ buf_out->tail = buf_in->tail;
+ } else {
+ buf_out->tail->next = buf_in->head;
+ buf_out->tail = buf_in->tail;
+ }
+
+ buf_out->datalen += buf_in->datalen;
+ buf_in->head = buf_in->tail = NULL;
+ buf_in->datalen = 0;
+}
+
/** Internal structure: represents a position in a buffer. */
typedef struct buf_pos_t {
const chunk_t *chunk; /**< Which chunk are we pointing to? */
diff --git a/src/common/buffers.h b/src/common/buffers.h
index 1eaa5f2d04..dd96ddcb6c 100644
--- a/src/common/buffers.h
+++ b/src/common/buffers.h
@@ -43,9 +43,15 @@ int buf_flush_to_socket(buf_t *buf, tor_socket_t s, size_t sz,
size_t *buf_flushlen);
int buf_add(buf_t *buf, const char *string, size_t string_len);
+void buf_add_string(buf_t *buf, const char *string);
+void buf_add_printf(buf_t *buf, const char *format, ...)
+ CHECK_PRINTF(2, 3);
+void buf_add_vprintf(buf_t *buf, const char *format, va_list args)
+ CHECK_PRINTF(2, 0);
int buf_add_compress(buf_t *buf, struct tor_compress_state_t *state,
const char *data, size_t data_len, int done);
int buf_move_to_buf(buf_t *buf_out, buf_t *buf_in, size_t *buf_flushlen);
+void buf_move_all(buf_t *buf_out, buf_t *buf_in);
void buf_peek(const buf_t *buf, char *string, size_t string_len);
void buf_drain(buf_t *buf, size_t n);
int buf_get_bytes(buf_t *buf, char *string, size_t string_len);
@@ -62,6 +68,7 @@ void buf_assert_ok(buf_t *buf);
int buf_find_string_offset(const buf_t *buf, const char *s, size_t n);
void buf_pullup(buf_t *buf, size_t bytes,
const char **head_out, size_t *len_out);
+char *buf_extract(buf_t *buf, size_t *sz_out);
#ifdef BUFFERS_PRIVATE
#ifdef TOR_UNIT_TESTS
diff --git a/src/common/compat.c b/src/common/compat.c
index 7fe97488e3..38693b21fe 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -1186,7 +1186,7 @@ mark_socket_open(tor_socket_t s)
bitarray_set(open_sockets, s);
}
#else /* !(defined(DEBUG_SOCKET_COUNTING)) */
-#define mark_socket_open(s) STMT_NIL
+#define mark_socket_open(s) ((void) (s))
#endif /* defined(DEBUG_SOCKET_COUNTING) */
/** @} */
@@ -1273,11 +1273,22 @@ tor_open_socket_with_extensions(int domain, int type, int protocol,
goto socket_ok; /* So that socket_ok will not be unused. */
socket_ok:
+ tor_take_socket_ownership(s);
+ return s;
+}
+
+/**
+ * For socket accounting: remember that we are the owner of the socket
+ * <b>s</b>. This will prevent us from overallocating sockets, and prevent us
+ * from asserting later when we close the socket <b>s</b>.
+ */
+void
+tor_take_socket_ownership(tor_socket_t s)
+{
socket_accounting_lock();
++n_sockets_open;
mark_socket_open(s);
socket_accounting_unlock();
- return s;
}
/** As accept(), but counts the number of open sockets. */
@@ -1358,10 +1369,7 @@ tor_accept_socket_with_extensions(tor_socket_t sockfd, struct sockaddr *addr,
goto socket_ok; /* So that socket_ok will not be unused. */
socket_ok:
- socket_accounting_lock();
- ++n_sockets_open;
- mark_socket_open(s);
- socket_accounting_unlock();
+ tor_take_socket_ownership(s);
return s;
}
diff --git a/src/common/compat.h b/src/common/compat.h
index fee9e6587d..0aabee68c8 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -483,6 +483,7 @@ typedef int socklen_t;
int tor_close_socket_simple(tor_socket_t s);
MOCK_DECL(int, tor_close_socket, (tor_socket_t s));
+void tor_take_socket_ownership(tor_socket_t s);
tor_socket_t tor_open_socket_with_extensions(
int domain, int type, int protocol,
int cloexec, int nonblock);
diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c
index 740cc2a11d..1c3a1b9f37 100644
--- a/src/common/compat_libevent.c
+++ b/src/common/compat_libevent.c
@@ -126,7 +126,7 @@ tor_libevent_initialize(tor_libevent_cfg *torcfg)
if (!the_event_base) {
/* LCOV_EXCL_START */
log_err(LD_GENERAL, "Unable to initialize Libevent: cannot continue.");
- exit(1);
+ exit(1); // exit ok: libevent is broken.
/* LCOV_EXCL_STOP */
}
diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h
index 834354c405..3d8672fc63 100644
--- a/src/common/compat_libevent.h
+++ b/src/common/compat_libevent.h
@@ -30,6 +30,7 @@ periodic_timer_t *periodic_timer_new(struct event_base *base,
void periodic_timer_free(periodic_timer_t *);
#define tor_event_base_loopexit event_base_loopexit
+#define tor_event_base_loopbreak event_base_loopbreak
/** Defines a configuration for using libevent with Tor: passed as an argument
* to tor_libevent_initialize() to describe how we want to set up. */
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 <b>str</b>, and itself.
- */
-void
-rust_str_free(rust_str_t str)
-{
- char *s = (char *)str;
- tor_free(s);
-}
-
-/**
- * Return zero-terminated contained string.
- */
-const char *
-rust_str_get(const rust_str_t str)
-{
- return (const char *)str;
-}
-
-/* If we were using Rust, we'd say so on startup. */
-rust_str_t
-rust_welcome_string(void)
-{
- char *s = tor_malloc_zero(1);
- return (rust_str_t)s;
-}
-
diff --git a/src/common/compat_rust.h b/src/common/compat_rust.h
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/compat_time.c b/src/common/compat_time.c
index 1ce6f5ce4e..7fd4281ade 100644
--- a/src/common/compat_time.c
+++ b/src/common/compat_time.c
@@ -90,7 +90,7 @@ tor_gettimeofday(struct timeval *timeval)
if (ft.ft_64 < EPOCH_BIAS) {
/* LCOV_EXCL_START */
log_err(LD_GENERAL,"System time is before 1970; failing.");
- exit(1);
+ exit(1); // exit ok: system clock is broken.
/* LCOV_EXCL_STOP */
}
ft.ft_64 -= EPOCH_BIAS;
@@ -102,7 +102,7 @@ tor_gettimeofday(struct timeval *timeval)
log_err(LD_GENERAL,"gettimeofday failed.");
/* If gettimeofday dies, we have either given a bad timezone (we didn't),
or segfaulted.*/
- exit(1);
+ exit(1); // exit ok: gettimeofday failed.
/* LCOV_EXCL_STOP */
}
#elif defined(HAVE_FTIME)
diff --git a/src/common/compat_winthreads.c b/src/common/compat_winthreads.c
index 50a3c498ca..5f7ec94c23 100644
--- a/src/common/compat_winthreads.c
+++ b/src/common/compat_winthreads.c
@@ -48,10 +48,12 @@ void
spawn_exit(void)
{
_endthread();
+ // LCOV_EXCL_START
//we should never get here. my compiler thinks that _endthread returns, this
//is an attempt to fool it.
tor_assert(0);
- _exit(0);
+ _exit(0); // exit ok: unreachable.
+ // LCOV_EXCL_STOP
}
void
diff --git a/src/common/confline.c b/src/common/confline.c
index 04545bc2c3..781ad2a12b 100644
--- a/src/common/confline.c
+++ b/src/common/confline.c
@@ -12,15 +12,18 @@
static int config_get_lines_aux(const char *string, config_line_t **result,
int extended, int allow_include,
- int *has_include, int recursion_level,
- config_line_t **last);
-static smartlist_t *config_get_file_list(const char *path);
-static int config_get_included_list(const char *path, int recursion_level,
- int extended, config_line_t **list,
- config_line_t **list_last);
+ int *has_include, smartlist_t *opened_lst,
+ int recursion_level, config_line_t **last);
+static smartlist_t *config_get_file_list(const char *path,
+ smartlist_t *opened_files);
+static int config_get_included_config(const char *path, int recursion_level,
+ int extended, config_line_t **config,
+ config_line_t **config_last,
+ smartlist_t *opened_lst);
static int config_process_include(const char *path, int recursion_level,
int extended, config_line_t **list,
- config_line_t **list_last);
+ config_line_t **list_last,
+ smartlist_t *opened_lst);
/** Helper: allocate a new configuration option mapping 'key' to 'val',
* append it to *<b>lst</b>. */
@@ -80,11 +83,13 @@ config_line_find(const config_line_t *lines,
/** Auxiliary function that does all the work of config_get_lines.
* <b>recursion_level</b> is the count of how many nested %includes we have.
+ * <b>opened_lst</b> will have a list of opened files if provided.
* Returns the a pointer to the last element of the <b>result</b> in
* <b>last</b>. */
static int
config_get_lines_aux(const char *string, config_line_t **result, int extended,
- int allow_include, int *has_include, int recursion_level,
+ int allow_include, int *has_include,
+ smartlist_t *opened_lst, int recursion_level,
config_line_t **last)
{
config_line_t *list = NULL, **next, *list_last = NULL;
@@ -134,7 +139,7 @@ config_get_lines_aux(const char *string, config_line_t **result, int extended,
config_line_t *include_list;
if (config_process_include(v, recursion_level, extended, &include_list,
- &list_last) < 0) {
+ &list_last, opened_lst) < 0) {
log_warn(LD_CONFIG, "Error reading included configuration "
"file or directory: \"%s\".", v);
config_free_lines(list);
@@ -176,24 +181,27 @@ config_get_lines_aux(const char *string, config_line_t **result, int extended,
/** Helper: parse the config string and strdup into key/value
* strings. Set *result to the list, or NULL if parsing the string
* failed. Set *has_include to 1 if <b>result</b> has values from
- * %included files. Return 0 on success, -1 on failure. Warn and ignore any
+ * %included files. <b>opened_lst</b> will have a list of opened files if
+ * provided. Return 0 on success, -1 on failure. Warn and ignore any
* misformatted lines.
*
* If <b>extended</b> is set, then treat keys beginning with / and with + as
* indicating "clear" and "append" respectively. */
int
config_get_lines_include(const char *string, config_line_t **result,
- int extended, int *has_include)
+ int extended, int *has_include,
+ smartlist_t *opened_lst)
{
- return config_get_lines_aux(string, result, extended, 1, has_include, 1,
- NULL);
+ return config_get_lines_aux(string, result, extended, 1, has_include,
+ opened_lst, 1, NULL);
}
/** Same as config_get_lines_include but does not allow %include */
int
config_get_lines(const char *string, config_line_t **result, int extended)
{
- return config_get_lines_aux(string, result, extended, 0, NULL, 1, NULL);
+ return config_get_lines_aux(string, result, extended, 0, NULL, NULL, 1,
+ NULL);
}
/** Adds a list of configuration files present on <b>path</b> to
@@ -201,12 +209,18 @@ config_get_lines(const char *string, config_line_t **result, int extended)
* only that file will be added to <b>file_list</b>. If it is a directory,
* all paths for files on that directory root (no recursion) except for files
* whose name starts with a dot will be added to <b>file_list</b>.
- * Return 0 on success, -1 on failure. Ignores empty files.
+ * <b>opened_files</b> will have a list of files opened by this function
+ * if provided. Return 0 on success, -1 on failure. Ignores empty files.
*/
static smartlist_t *
-config_get_file_list(const char *path)
+config_get_file_list(const char *path, smartlist_t *opened_files)
{
smartlist_t *file_list = smartlist_new();
+
+ if (opened_files) {
+ smartlist_add_strdup(opened_files, path);
+ }
+
file_status_t file_type = file_status(path);
if (file_type == FN_FILE) {
smartlist_add_strdup(file_list, path);
@@ -228,6 +242,10 @@ config_get_file_list(const char *path)
tor_asprintf(&fullname, "%s"PATH_SEPARATOR"%s", path, f);
tor_free(f);
+ if (opened_files) {
+ smartlist_add_strdup(opened_files, fullname);
+ }
+
if (file_status(fullname) != FN_FILE) {
tor_free(fullname);
continue;
@@ -245,19 +263,21 @@ config_get_file_list(const char *path)
}
/** Creates a list of config lines present on included <b>path</b>.
- * Set <b>list</b> to the list and <b>list_last</b> to the last element of
- * <b>list</b>. Return 0 on success, -1 on failure. */
+ * Set <b>config</b> to the list and <b>config_last</b> to the last element of
+ * <b>config</b>. <b>opened_lst</b> will have a list of opened files if
+ * provided. Return 0 on success, -1 on failure. */
static int
-config_get_included_list(const char *path, int recursion_level, int extended,
- config_line_t **list, config_line_t **list_last)
+config_get_included_config(const char *path, int recursion_level, int extended,
+ config_line_t **config, config_line_t **config_last,
+ smartlist_t *opened_lst)
{
char *included_conf = read_file_to_str(path, 0, NULL);
if (!included_conf) {
return -1;
}
- if (config_get_lines_aux(included_conf, list, extended, 1, NULL,
- recursion_level+1, list_last) < 0) {
+ if (config_get_lines_aux(included_conf, config, extended, 1, NULL,
+ opened_lst, recursion_level+1, config_last) < 0) {
tor_free(included_conf);
return -1;
}
@@ -268,41 +288,31 @@ config_get_included_list(const char *path, int recursion_level, int extended,
/** Process an %include <b>path</b> in a config file. Set <b>list</b> to the
* list of configuration settings obtained and <b>list_last</b> to the last
- * element of the same list. Return 0 on success, -1 on failure. */
+ * element of the same list. <b>opened_lst</b> will have a list of opened
+ * files if provided. Return 0 on success, -1 on failure. */
static int
config_process_include(const char *path, int recursion_level, int extended,
- config_line_t **list, config_line_t **list_last)
+ config_line_t **list, config_line_t **list_last,
+ smartlist_t *opened_lst)
{
config_line_t *ret_list = NULL;
config_line_t **next = &ret_list;
-#if 0
- // Disabled -- we already unescape_string() on the result. */
- char *unquoted_path = get_unquoted_path(path);
- if (!unquoted_path) {
- return -1;
- }
- smartlist_t *config_files = config_get_file_list(unquoted_path);
- if (!config_files) {
- tor_free(unquoted_path);
- return -1;
- }
- tor_free(unquoted_path);
-#endif /* 0 */
- smartlist_t *config_files = config_get_file_list(path);
+ smartlist_t *config_files = config_get_file_list(path, opened_lst);
if (!config_files) {
return -1;
}
int rv = -1;
SMARTLIST_FOREACH_BEGIN(config_files, const char *, config_file) {
- config_line_t *included_list = NULL;
- if (config_get_included_list(config_file, recursion_level, extended,
- &included_list, list_last) < 0) {
+ config_line_t *included_config = NULL;
+ if (config_get_included_config(config_file, recursion_level, extended,
+ &included_config, list_last,
+ opened_lst) < 0) {
goto done;
}
- *next = included_list;
+ *next = included_config;
if (*list_last)
next = &(*list_last)->next;
diff --git a/src/common/confline.h b/src/common/confline.h
index 8256326f2d..feeb9f249d 100644
--- a/src/common/confline.h
+++ b/src/common/confline.h
@@ -7,6 +7,8 @@
#ifndef TOR_CONFLINE_H
#define TOR_CONFLINE_H
+#include "container.h"
+
/** Ordinary configuration line. */
#define CONFIG_LINE_NORMAL 0
/** Appends to previous configuration for the same option, even if we
@@ -44,7 +46,8 @@ int config_lines_eq(config_line_t *a, config_line_t *b);
int config_count_key(const config_line_t *a, const char *key);
int config_get_lines(const char *string, config_line_t **result, int extended);
int config_get_lines_include(const char *string, config_line_t **result,
- int extended, int *has_include);
+ int extended, int *has_include,
+ smartlist_t *opened_lst);
void config_free_lines(config_line_t *front);
const char *parse_config_line_from_str_verbose(const char *line,
char **key_out, char **value_out,
diff --git a/src/common/crypto_pwbox.h b/src/common/crypto_pwbox.h
index cee8653587..a26b6d2c17 100644
--- a/src/common/crypto_pwbox.h
+++ b/src/common/crypto_pwbox.h
@@ -1,3 +1,6 @@
+/* Copyright (c) 2014-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
#ifndef CRYPTO_PWBOX_H_INCLUDED_
#define CRYPTO_PWBOX_H_INCLUDED_
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/common/sandbox.c b/src/common/sandbox.c
index 7a4e3ece38..8cb78bd28e 100644
--- a/src/common/sandbox.c
+++ b/src/common/sandbox.c
@@ -151,6 +151,7 @@ static int filter_nopar_gen[] = {
SCMP_SYS(fstat64),
#endif
SCMP_SYS(futex),
+ SCMP_SYS(getdents),
SCMP_SYS(getdents64),
SCMP_SYS(getegid),
#ifdef __NR_getegid32
@@ -1756,7 +1757,7 @@ sigsys_debugging(int nr, siginfo_t *info, void *void_context)
#endif
#if defined(DEBUGGING_CLOSE)
- _exit(1);
+ _exit(1); // exit ok: programming error has led to sandbox failure.
#endif // DEBUGGING_CLOSE
}
diff --git a/src/common/util.c b/src/common/util.c
index 5ff7e104d6..7dc5e8144d 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -156,7 +156,7 @@ tor_malloc_(size_t size DMALLOC_PARAMS)
/* If these functions die within a worker process, they won't call
* spawn_exit, but that's ok, since the parent will run out of memory soon
* anyway. */
- exit(1);
+ exit(1); // exit ok: alloc failed.
/* LCOV_EXCL_STOP */
}
return result;
@@ -244,7 +244,7 @@ tor_realloc_(void *ptr, size_t size DMALLOC_PARAMS)
if (PREDICT_UNLIKELY(result == NULL)) {
/* LCOV_EXCL_START */
log_err(LD_MM,"Out of memory on realloc(). Dying.");
- exit(1);
+ exit(1); // exit ok: alloc failed.
/* LCOV_EXCL_STOP */
}
return result;
@@ -282,7 +282,7 @@ tor_strdup_(const char *s DMALLOC_PARAMS)
if (PREDICT_UNLIKELY(duplicate == NULL)) {
/* LCOV_EXCL_START */
log_err(LD_MM,"Out of memory on strdup(). Dying.");
- exit(1);
+ exit(1); // exit ok: alloc failed.
/* LCOV_EXCL_STOP */
}
return duplicate;
@@ -3590,14 +3590,14 @@ start_daemon(void)
if (pipe(daemon_filedes)) {
/* LCOV_EXCL_START */
log_err(LD_GENERAL,"pipe failed; exiting. Error was %s", strerror(errno));
- exit(1);
+ exit(1); // exit ok: during daemonize, pipe failed.
/* LCOV_EXCL_STOP */
}
pid = fork();
if (pid < 0) {
/* LCOV_EXCL_START */
log_err(LD_GENERAL,"fork failed. Exiting.");
- exit(1);
+ exit(1); // exit ok: during daemonize, fork failed
/* LCOV_EXCL_STOP */
}
if (pid) { /* Parent */
@@ -3612,9 +3612,9 @@ start_daemon(void)
}
fflush(stdout);
if (ok == 1)
- exit(0);
+ exit(0); // exit ok: during daemonize, daemonizing.
else
- exit(1); /* child reported error */
+ exit(1); /* child reported error. exit ok: daemonize failed. */
} else { /* Child */
close(daemon_filedes[0]); /* we only write */
@@ -3626,7 +3626,7 @@ start_daemon(void)
* _Advanced Programming in the Unix Environment_.
*/
if (fork() != 0) {
- exit(0);
+ exit(0); // exit ok: during daemonize, fork failed (2)
}
set_main_thread(); /* We are now the main thread. */
@@ -3655,14 +3655,14 @@ finish_daemon(const char *desired_cwd)
/* Don't hold the wrong FS mounted */
if (chdir(desired_cwd) < 0) {
log_err(LD_GENERAL,"chdir to \"%s\" failed. Exiting.",desired_cwd);
- exit(1);
+ exit(1); // exit ok: during daemonize, chdir failed.
}
nullfd = tor_open_cloexec("/dev/null", O_RDWR, 0);
if (nullfd < 0) {
/* LCOV_EXCL_START */
log_err(LD_GENERAL,"/dev/null can't be opened. Exiting.");
- exit(1);
+ exit(1); // exit ok: during daemonize, couldn't open /dev/null
/* LCOV_EXCL_STOP */
}
/* close fds linking to invoking terminal, but
@@ -3674,7 +3674,7 @@ finish_daemon(const char *desired_cwd)
dup2(nullfd,2) < 0) {
/* LCOV_EXCL_START */
log_err(LD_GENERAL,"dup2 failed. Exiting.");
- exit(1);
+ exit(1); // exit ok: during daemonize, dup2 failed.
/* LCOV_EXCL_STOP */
}
if (nullfd > 2)
@@ -4474,7 +4474,7 @@ tor_spawn_background(const char *const filename, const char **argv,
err += (nbytes < 0);
}
- _exit(err?254:255);
+ _exit(err?254:255); // exit ok: in child.
}
/* Never reached, but avoids compiler warning */
diff --git a/src/or/addressmap.c b/src/or/addressmap.c
index 7e92633601..9a2cc26b3b 100644
--- a/src/or/addressmap.c
+++ b/src/or/addressmap.c
@@ -541,7 +541,7 @@ addressmap_have_mapping(const char *address, int update_expiry)
* (virtual address mapping) from the controller.)
*
* <b>new_address</b> should be a newly dup'ed string, which we'll use or
- * free as appropriate. We will leave address alone.
+ * free as appropriate. We will leave <b>address</b> alone.
*
* If <b>wildcard_addr</b> is true, then the mapping will match any address
* equal to <b>address</b>, or any address ending with a period followed by
@@ -554,7 +554,6 @@ addressmap_have_mapping(const char *address, int update_expiry)
* <b>wildcard_new_addr</b>, remove any mappings that exist from
* <b>address</b>.
*
- *
* It is an error to set <b>wildcard_new_addr</b> if <b>wildcard_addr</b> is
* not set. */
void
diff --git a/src/or/config.c b/src/or/config.c
index d6e1abaa10..252d50944b 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -82,6 +82,7 @@
#include "dirvote.h"
#include "dns.h"
#include "entrynodes.h"
+#include "git_revision.h"
#include "geoip.h"
#include "hibernate.h"
#include "main.h"
@@ -483,6 +484,7 @@ static config_var_t option_vars_[] = {
V(RendPostPeriod, INTERVAL, "1 hour"),
V(RephistTrackTime, INTERVAL, "24 hours"),
V(RunAsDaemon, BOOL, "0"),
+ V(ReducedExitPolicy, BOOL, "0"),
OBSOLETE("RunTesting"), // currently unused
V(Sandbox, BOOL, "0"),
V(SafeLogging, STRING, "1"),
@@ -561,6 +563,7 @@ static config_var_t option_vars_[] = {
VAR("__HashedControlSessionPassword", LINELIST, HashedControlSessionPassword,
NULL),
VAR("__OwningControllerProcess",STRING,OwningControllerProcess, NULL),
+ VAR("__OwningControllerFD",INT,OwningControllerFD, "-1"),
V(MinUptimeHidServDirectoryV2, INTERVAL, "96 hours"),
V(TestingServerDownloadSchedule, CSV_INTERVAL, "0, 0, 0, 60, 60, 120, "
"300, 900, 2147483647"),
@@ -838,9 +841,12 @@ set_options(or_options_t *new_val, char **msg)
return -1;
}
if (options_act(old_options) < 0) { /* acting on the options failed. die. */
- log_err(LD_BUG,
- "Acting on config options left us in a broken state. Dying.");
- exit(1);
+ if (! tor_event_loop_shutdown_is_pending()) {
+ log_err(LD_BUG,
+ "Acting on config options left us in a broken state. Dying.");
+ tor_shutdown_event_loop_and_exit(1);
+ }
+ return -1;
}
/* Issues a CONF_CHANGED event to notify controller of the change. If Tor is
* just starting up then the old_options will be undefined. */
@@ -882,8 +888,6 @@ set_options(or_options_t *new_val, char **msg)
return 0;
}
-extern const char tor_git_revision[]; /* from tor_main.c */
-
/** The version of this Tor process, as parsed. */
static char *the_tor_version = NULL;
/** A shorter version of this Tor process's version, for export in our router
@@ -938,6 +942,10 @@ or_options_free(or_options_t *options)
SMARTLIST_FOREACH(options->SchedulerTypes_, int *, i, tor_free(i));
smartlist_free(options->SchedulerTypes_);
}
+ if (options->FilesOpenedByIncludes) {
+ SMARTLIST_FOREACH(options->FilesOpenedByIncludes, char *, f, tor_free(f));
+ smartlist_free(options->FilesOpenedByIncludes);
+ }
tor_free(options->BridgePassword_AuthDigest_);
tor_free(options->command_arg);
tor_free(options->master_key_fname);
@@ -1692,8 +1700,11 @@ options_act(const or_options_t *old_options)
else
protocol_warning_severity_level = LOG_INFO;
- if (consider_adding_dir_servers(options, old_options) < 0)
+ if (consider_adding_dir_servers(options, old_options) < 0) {
+ // XXXX This should get validated earlier, and committed here, to
+ // XXXX lower opportunities for reaching an error case.
return -1;
+ }
if (rend_non_anonymous_mode_enabled(options)) {
log_warn(LD_GENERAL, "This copy of Tor was compiled or configured to run "
@@ -1702,6 +1713,7 @@ options_act(const or_options_t *old_options)
#ifdef ENABLE_TOR2WEB_MODE
/* LCOV_EXCL_START */
+ // XXXX This should move into options_validate()
if (!options->Tor2webMode) {
log_err(LD_CONFIG, "This copy of Tor was compiled to run in "
"'tor2web mode'. It can only be run with the Tor2webMode torrc "
@@ -1710,6 +1722,7 @@ options_act(const or_options_t *old_options)
}
/* LCOV_EXCL_STOP */
#else /* !(defined(ENABLE_TOR2WEB_MODE)) */
+ // XXXX This should move into options_validate()
if (options->Tor2webMode) {
log_err(LD_CONFIG, "This copy of Tor was not compiled to run in "
"'tor2web mode'. It cannot be run with the Tor2webMode torrc "
@@ -1737,9 +1750,11 @@ options_act(const or_options_t *old_options)
for (cl = options->Bridges; cl; cl = cl->next) {
bridge_line_t *bridge_line = parse_bridge_line(cl->value);
if (!bridge_line) {
+ // LCOV_EXCL_START
log_warn(LD_BUG,
"Previously validated Bridge line could not be added!");
return -1;
+ // LCOV_EXCL_STOP
}
bridge_add_from_config(bridge_line);
}
@@ -1747,15 +1762,37 @@ options_act(const or_options_t *old_options)
}
if (running_tor && hs_config_service_all(options, 0)<0) {
+ // LCOV_EXCL_START
log_warn(LD_BUG,
"Previously validated hidden services line could not be added!");
return -1;
+ // LCOV_EXCL_STOP
}
if (running_tor && rend_parse_service_authorization(options, 0) < 0) {
+ // LCOV_EXCL_START
log_warn(LD_BUG, "Previously validated client authorization for "
"hidden services could not be added!");
return -1;
+ // LCOV_EXCL_STOP
+ }
+
+ if (running_tor && !old_options && options->OwningControllerFD != -1) {
+#ifdef _WIN32
+ log_warn(LD_CONFIG, "OwningControllerFD is not supported on Windows. "
+ "If you need it, tell the Tor developers.");
+ return -1;
+#else
+ const unsigned ctrl_flags =
+ CC_LOCAL_FD_IS_OWNER |
+ CC_LOCAL_FD_IS_AUTHENTICATED;
+ tor_socket_t ctrl_sock = (tor_socket_t)options->OwningControllerFD;
+ if (control_connection_add_local_fd(ctrl_sock, ctrl_flags) < 0) {
+ log_warn(LD_CONFIG, "Could not add local controller connection with "
+ "given FD.");
+ return -1;
+ }
+#endif
}
/* Load state */
@@ -1778,10 +1815,12 @@ options_act(const or_options_t *old_options)
if (options->ClientTransportPlugin) {
for (cl = options->ClientTransportPlugin; cl; cl = cl->next) {
if (parse_transport_line(options, cl->value, 0, 0) < 0) {
+ // LCOV_EXCL_START
log_warn(LD_BUG,
"Previously validated ClientTransportPlugin line "
"could not be added!");
return -1;
+ // LCOV_EXCL_STOP
}
}
}
@@ -1789,10 +1828,12 @@ options_act(const or_options_t *old_options)
if (options->ServerTransportPlugin && server_mode(options)) {
for (cl = options->ServerTransportPlugin; cl; cl = cl->next) {
if (parse_transport_line(options, cl->value, 0, 1) < 0) {
+ // LCOV_EXCL_START
log_warn(LD_BUG,
"Previously validated ServerTransportPlugin line "
"could not be added!");
return -1;
+ // LCOV_EXCL_STOP
}
}
}
@@ -1878,8 +1919,10 @@ options_act(const or_options_t *old_options)
/* Set up accounting */
if (accounting_parse_options(options, 0)<0) {
- log_warn(LD_CONFIG,"Error in accounting options");
+ // LCOV_EXCL_START
+ log_warn(LD_BUG,"Error in previously validated accounting options");
return -1;
+ // LCOV_EXCL_STOP
}
if (accounting_is_enabled(options))
configure_accounting(time(NULL));
@@ -1902,6 +1945,7 @@ options_act(const or_options_t *old_options)
char *http_authenticator;
http_authenticator = alloc_http_authenticator(options->BridgePassword);
if (!http_authenticator) {
+ // XXXX This should get validated in options_validate().
log_warn(LD_BUG, "Unable to allocate HTTP authenticator. Not setting "
"BridgePassword.");
return -1;
@@ -1914,9 +1958,12 @@ options_act(const or_options_t *old_options)
}
if (parse_outbound_addresses(options, 0, &msg) < 0) {
- log_warn(LD_BUG, "Failed parsing outbound bind addresses: %s", msg);
+ // LCOV_EXCL_START
+ log_warn(LD_BUG, "Failed parsing previously validated outbound "
+ "bind addresses: %s", msg);
tor_free(msg);
return -1;
+ // LCOV_EXCL_STOP
}
config_maybe_load_geoip_files_(options, old_options);
@@ -4612,6 +4659,12 @@ options_transition_allowed(const or_options_t *old,
return -1;
}
+ if (old->OwningControllerFD != new_val->OwningControllerFD) {
+ *msg = tor_strdup("While Tor is running, changing OwningControllerFD "
+ "is not allowed.");
+ return -1;
+ }
+
if (sandbox_is_active()) {
#define SB_NOCHANGE_STR(opt) \
do { \
@@ -5024,7 +5077,8 @@ load_torrc_from_disk(config_line_t *cmd_arg, int defaults_file)
/** Read a configuration file into <b>options</b>, finding the configuration
* file location based on the command line. After loading the file
* call options_init_from_string() to load the config.
- * Return 0 if success, -1 if failure. */
+ * Return 0 if success, -1 if failure, and 1 if we succeeded but should exit
+ * anyway. */
int
options_init_from_torrc(int argc, char **argv)
{
@@ -5051,22 +5105,22 @@ options_init_from_torrc(int argc, char **argv)
if (config_line_find(cmdline_only_options, "-h") ||
config_line_find(cmdline_only_options, "--help")) {
print_usage();
- exit(0);
+ return 1;
}
if (config_line_find(cmdline_only_options, "--list-torrc-options")) {
/* For validating whether we've documented everything. */
list_torrc_options();
- exit(0);
+ return 1;
}
if (config_line_find(cmdline_only_options, "--list-deprecated-options")) {
/* For validating whether what we have deprecated really exists. */
list_deprecated_options();
- exit(0);
+ return 1;
}
if (config_line_find(cmdline_only_options, "--version")) {
printf("Tor version %s.\n",get_version());
- exit(0);
+ return 1;
}
if (config_line_find(cmdline_only_options, "--library-versions")) {
@@ -5094,7 +5148,7 @@ options_init_from_torrc(int argc, char **argv)
tor_compress_header_version_str(ZSTD_METHOD));
}
//TODO: Hex versions?
- exit(0);
+ return 1;
}
command = CMD_RUN_TOR;
@@ -5155,7 +5209,8 @@ options_init_from_torrc(int argc, char **argv)
get_options_mutable()->keygen_force_passphrase = FORCE_PASSPHRASE_OFF;
} else {
log_err(LD_CONFIG, "--no-passphrase specified without --keygen!");
- exit(1);
+ retval = -1;
+ goto err;
}
}
@@ -5164,7 +5219,8 @@ options_init_from_torrc(int argc, char **argv)
get_options_mutable()->change_key_passphrase = 1;
} else {
log_err(LD_CONFIG, "--newpass specified without --keygen!");
- exit(1);
+ retval = -1;
+ goto err;
}
}
@@ -5174,17 +5230,20 @@ options_init_from_torrc(int argc, char **argv)
if (fd_line) {
if (get_options()->keygen_force_passphrase == FORCE_PASSPHRASE_OFF) {
log_err(LD_CONFIG, "--no-passphrase specified with --passphrase-fd!");
- exit(1);
+ retval = -1;
+ goto err;
} else if (command != CMD_KEYGEN) {
log_err(LD_CONFIG, "--passphrase-fd specified without --keygen!");
- exit(1);
+ retval = -1;
+ goto err;
} else {
const char *v = fd_line->value;
int ok = 1;
long fd = tor_parse_long(v, 10, 0, INT_MAX, &ok, NULL);
if (fd < 0 || ok == 0) {
log_err(LD_CONFIG, "Invalid --passphrase-fd value %s", escaped(v));
- exit(1);
+ retval = -1;
+ goto err;
}
get_options_mutable()->keygen_passphrase_fd = (int)fd;
get_options_mutable()->use_keygen_passphrase_fd = 1;
@@ -5199,7 +5258,8 @@ options_init_from_torrc(int argc, char **argv)
if (key_line) {
if (command != CMD_KEYGEN) {
log_err(LD_CONFIG, "--master-key without --keygen!");
- exit(1);
+ retval = -1;
+ goto err;
} else {
get_options_mutable()->master_key_fname = tor_strdup(key_line->value);
}
@@ -5247,13 +5307,16 @@ options_init_from_string(const char *cf_defaults, const char *cf,
newoptions->command = command;
newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL;
+ smartlist_t *opened_files = smartlist_new();
for (int i = 0; i < 2; ++i) {
const char *body = i==0 ? cf_defaults : cf;
if (!body)
continue;
+
/* get config lines, assign them */
retval = config_get_lines_include(body, &cl, 1,
- body == cf ? &cf_has_include : NULL);
+ body == cf ? &cf_has_include : NULL,
+ opened_files);
if (retval < 0) {
err = SETOPT_ERR_PARSE;
goto err;
@@ -5282,6 +5345,7 @@ options_init_from_string(const char *cf_defaults, const char *cf,
}
newoptions->IncludeUsed = cf_has_include;
+ newoptions->FilesOpenedByIncludes = opened_files;
/* If this is a testing network configuration, change defaults
* for a list of dependent config options, re-initialize newoptions
@@ -5321,13 +5385,16 @@ options_init_from_string(const char *cf_defaults, const char *cf,
newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL;
/* Assign all options a second time. */
+ opened_files = smartlist_new();
for (int i = 0; i < 2; ++i) {
const char *body = i==0 ? cf_defaults : cf;
if (!body)
continue;
+
/* get config lines, assign them */
retval = config_get_lines_include(body, &cl, 1,
- body == cf ? &cf_has_include : NULL);
+ body == cf ? &cf_has_include : NULL,
+ opened_files);
if (retval < 0) {
err = SETOPT_ERR_PARSE;
goto err;
@@ -5352,6 +5419,7 @@ options_init_from_string(const char *cf_defaults, const char *cf,
newoptions->IncludeUsed = cf_has_include;
in_option_validation = 1;
+ newoptions->FilesOpenedByIncludes = opened_files;
/* Validate newoptions */
if (options_validate(oldoptions, newoptions, newdefaultoptions,
@@ -5378,6 +5446,12 @@ options_init_from_string(const char *cf_defaults, const char *cf,
err:
in_option_validation = 0;
+ if (opened_files) {
+ SMARTLIST_FOREACH(opened_files, char *, f, tor_free(f));
+ smartlist_free(opened_files);
+ }
+ // may have been set to opened_files, avoid double free
+ newoptions->FilesOpenedByIncludes = NULL;
or_options_free(newoptions);
or_options_free(newdefaultoptions);
if (*msg) {
diff --git a/src/or/connection.c b/src/or/connection.c
index 632a833652..e6aae71627 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -118,8 +118,6 @@ static connection_t *connection_listener_new(
const port_cfg_t *portcfg);
static void connection_init(time_t now, connection_t *conn, int type,
int socket_family);
-static int connection_init_accepted_conn(connection_t *conn,
- const listener_connection_t *listener);
static int connection_handle_listener_read(connection_t *conn, int new_type);
static int connection_bucket_should_increase(int bucket,
or_connection_t *conn);
@@ -1662,11 +1660,15 @@ connection_handle_listener_read(connection_t *conn, int new_type)
}
/** Initialize states for newly accepted connection <b>conn</b>.
+ *
* If conn is an OR, start the TLS handshake.
+ *
* If conn is a transparent AP, get its original destination
* and place it in circuit_wait.
+ *
+ * The <b>listener</b> parameter is only used for AP connections.
*/
-static int
+int
connection_init_accepted_conn(connection_t *conn,
const listener_connection_t *listener)
{
@@ -4045,6 +4047,68 @@ connection_flush(connection_t *conn)
return connection_handle_write(conn, 1);
}
+/** Helper for connection_write_to_buf_impl and connection_write_buf_to_buf:
+ *
+ * Return true iff it is okay to queue bytes on <b>conn</b>'s outbuf for
+ * writing.
+ */
+static int
+connection_may_write_to_buf(connection_t *conn)
+{
+ /* if it's marked for close, only allow write if we mean to flush it */
+ if (conn->marked_for_close && !conn->hold_open_until_flushed)
+ return 0;
+
+ return 1;
+}
+
+/** Helper for connection_write_to_buf_impl and connection_write_buf_to_buf:
+ *
+ * Called when an attempt to add bytes on <b>conn</b>'s outbuf has failed;
+ * mark the connection and warn as appropriate.
+ */
+static void
+connection_write_to_buf_failed(connection_t *conn)
+{
+ if (CONN_IS_EDGE(conn)) {
+ /* if it failed, it means we have our package/delivery windows set
+ wrong compared to our max outbuf size. close the whole circuit. */
+ log_warn(LD_NET,
+ "write_to_buf failed. Closing circuit (fd %d).", (int)conn->s);
+ circuit_mark_for_close(circuit_get_by_edge_conn(TO_EDGE_CONN(conn)),
+ END_CIRC_REASON_INTERNAL);
+ } else if (conn->type == CONN_TYPE_OR) {
+ or_connection_t *orconn = TO_OR_CONN(conn);
+ log_warn(LD_NET,
+ "write_to_buf failed on an orconn; notifying of error "
+ "(fd %d)", (int)(conn->s));
+ connection_or_close_for_error(orconn, 0);
+ } else {
+ log_warn(LD_NET,
+ "write_to_buf failed. Closing connection (fd %d).",
+ (int)conn->s);
+ connection_mark_for_close(conn);
+ }
+}
+
+/** Helper for connection_write_to_buf_impl and connection_write_buf_to_buf:
+ *
+ * Called when an attempt to add bytes on <b>conn</b>'s outbuf has succeeded:
+ * record the number of bytes added.
+ */
+static void
+connection_write_to_buf_commit(connection_t *conn, size_t len)
+{
+ /* If we receive optimistic data in the EXIT_CONN_STATE_RESOLVING
+ * state, we don't want to try to write it right away, since
+ * conn->write_event won't be set yet. Otherwise, write data from
+ * this conn as the socket is available. */
+ if (conn->write_event) {
+ connection_start_writing(conn);
+ }
+ conn->outbuf_flushlen += len;
+}
+
/** Append <b>len</b> bytes of <b>string</b> onto <b>conn</b>'s
* outbuf, and ask it to start writing.
*
@@ -4059,58 +4123,52 @@ connection_write_to_buf_impl_,(const char *string, size_t len,
{
/* XXXX This function really needs to return -1 on failure. */
int r;
- size_t old_datalen;
if (!len && !(zlib<0))
return;
- /* if it's marked for close, only allow write if we mean to flush it */
- if (conn->marked_for_close && !conn->hold_open_until_flushed)
+
+ if (!connection_may_write_to_buf(conn))
return;
- old_datalen = buf_datalen(conn->outbuf);
+ size_t written;
+
if (zlib) {
+ size_t old_datalen = buf_datalen(conn->outbuf);
dir_connection_t *dir_conn = TO_DIR_CONN(conn);
int done = zlib < 0;
CONN_LOG_PROTECT(conn, r = buf_add_compress(conn->outbuf,
- dir_conn->compress_state,
- string, len, done));
+ dir_conn->compress_state,
+ string, len, done));
+ written = buf_datalen(conn->outbuf) - old_datalen;
} else {
CONN_LOG_PROTECT(conn, r = buf_add(conn->outbuf, string, len));
+ written = len;
}
if (r < 0) {
- if (CONN_IS_EDGE(conn)) {
- /* if it failed, it means we have our package/delivery windows set
- wrong compared to our max outbuf size. close the whole circuit. */
- log_warn(LD_NET,
- "write_to_buf failed. Closing circuit (fd %d).", (int)conn->s);
- circuit_mark_for_close(circuit_get_by_edge_conn(TO_EDGE_CONN(conn)),
- END_CIRC_REASON_INTERNAL);
- } else if (conn->type == CONN_TYPE_OR) {
- or_connection_t *orconn = TO_OR_CONN(conn);
- log_warn(LD_NET,
- "write_to_buf failed on an orconn; notifying of error "
- "(fd %d)", (int)(conn->s));
- connection_or_close_for_error(orconn, 0);
- } else {
- log_warn(LD_NET,
- "write_to_buf failed. Closing connection (fd %d).",
- (int)conn->s);
- connection_mark_for_close(conn);
- }
+ connection_write_to_buf_failed(conn);
return;
}
+ connection_write_to_buf_commit(conn, written);
+}
- /* If we receive optimistic data in the EXIT_CONN_STATE_RESOLVING
- * state, we don't want to try to write it right away, since
- * conn->write_event won't be set yet. Otherwise, write data from
- * this conn as the socket is available. */
- if (conn->write_event) {
- connection_start_writing(conn);
- }
- if (zlib) {
- conn->outbuf_flushlen += buf_datalen(conn->outbuf) - old_datalen;
- } else {
- conn->outbuf_flushlen += len;
- }
+/**
+ * Add all bytes from <b>buf</b> to <b>conn</b>'s outbuf, draining them
+ * from <b>buf</b>. (If the connection is marked and will soon be closed,
+ * nothing is drained.)
+ */
+void
+connection_buf_add_buf(connection_t *conn, buf_t *buf)
+{
+ tor_assert(conn);
+ tor_assert(buf);
+ size_t len = buf_datalen(buf);
+ if (len == 0)
+ return;
+
+ if (!connection_may_write_to_buf(conn))
+ return;
+
+ buf_move_all(conn->outbuf, buf);
+ connection_write_to_buf_commit(conn, len);
}
#define CONN_GET_ALL_TEMPLATE(var, test) \
diff --git a/src/or/connection.h b/src/or/connection.h
index 4a5bd6971b..450229ce8f 100644
--- a/src/or/connection.h
+++ b/src/or/connection.h
@@ -26,7 +26,8 @@ entry_connection_t *entry_connection_new(int type, int socket_family);
control_connection_t *control_connection_new(int socket_family);
listener_connection_t *listener_connection_new(int type, int socket_family);
connection_t *connection_new(int type, int socket_family);
-
+int connection_init_accepted_conn(connection_t *conn,
+ const listener_connection_t *listener);
void connection_link_connections(connection_t *conn_a, connection_t *conn_b);
MOCK_DECL(void,connection_free,(connection_t *conn));
void connection_free_all(void);
@@ -155,6 +156,7 @@ connection_buf_add_compress(const char *string, size_t len,
{
connection_write_to_buf_impl_(string, len, TO_CONN(conn), done ? -1 : 1);
}
+void connection_buf_add_buf(connection_t *conn, buf_t *buf);
/* DOCDOC connection_get_inbuf_len */
static size_t connection_get_inbuf_len(connection_t *conn);
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index f178917f0b..41f0fe6a1d 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -999,7 +999,7 @@ connection_ap_mark_as_pending_circuit_(entry_connection_t *entry_conn,
* So the fix is to tell it right now that it ought to finish its loop at
* its next available opportunity.
*/
- tell_event_loop_to_finish();
+ tell_event_loop_to_run_external_code();
}
/** Mark <b>entry_conn</b> as no longer waiting for a circuit. */
diff --git a/src/or/control.c b/src/or/control.c
index 8173cb1e56..d417d42f73 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -549,6 +549,49 @@ decode_escaped_string(const char *start, size_t in_len_max,
return end+1;
}
+/** Create and add a new controller connection on <b>sock</b>. If
+ * <b>CC_LOCAL_FD_IS_OWNER</b> is set in <b>flags</b>, this Tor process should
+ * exit when the connection closes. If <b>CC_LOCAL_FD_IS_AUTHENTICATED</b>
+ * is set, then the connection does not need to authenticate.
+ */
+int
+control_connection_add_local_fd(tor_socket_t sock, unsigned flags)
+{
+ if (BUG(! SOCKET_OK(sock)))
+ return -1;
+ const int is_owner = !!(flags & CC_LOCAL_FD_IS_OWNER);
+ const int is_authenticated = !!(flags & CC_LOCAL_FD_IS_AUTHENTICATED);
+ control_connection_t *control_conn = control_connection_new(AF_UNSPEC);
+ connection_t *conn = TO_CONN(control_conn);
+ conn->s = sock;
+ tor_addr_make_unspec(&conn->addr);
+ conn->port = 1;
+ conn->address = tor_strdup("<local socket>");
+
+ /* We take ownership of this socket so that later, when we close it,
+ * we don't freak out. */
+ tor_take_socket_ownership(sock);
+
+ if (set_socket_nonblocking(sock) < 0 ||
+ connection_add(conn) < 0) {
+ connection_free(conn);
+ return -1;
+ }
+
+ control_conn->is_owning_control_connection = is_owner;
+
+ if (connection_init_accepted_conn(conn, NULL) < 0) {
+ connection_mark_for_close(conn);
+ return -1;
+ }
+
+ if (is_authenticated) {
+ conn->state = CONTROL_CONN_STATE_OPEN;
+ }
+
+ return 0;
+}
+
/** Acts like sprintf, but writes its formatted string to the end of
* <b>conn</b>-\>outbuf. */
static void
@@ -6571,8 +6614,7 @@ monitor_owning_controller_process(const char *process_spec)
"owning controller: %s. Exiting.",
msg);
owning_controller_process_spec = NULL;
- tor_cleanup();
- exit(1);
+ tor_shutdown_event_loop_and_exit(1);
}
}
diff --git a/src/or/control.h b/src/or/control.h
index e957b593a6..7ec182cb78 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -27,6 +27,10 @@ void control_ports_write_to_file(void);
#define LOG_FN_CONN(conn, args) \
CONN_LOG_PROTECT(conn, log_fn args)
+#define CC_LOCAL_FD_IS_OWNER (1u<<0)
+#define CC_LOCAL_FD_IS_AUTHENTICATED (1u<<1)
+int control_connection_add_local_fd(tor_socket_t sock, unsigned flags);
+
int connection_control_finished_flushing(control_connection_t *conn);
int connection_control_reached_eof(control_connection_t *conn);
void connection_control_closed(control_connection_t *conn);
diff --git a/src/or/directory.c b/src/or/directory.c
index 7a1364bd7d..4dd6da592a 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -2237,8 +2237,6 @@ load_downloaded_routers(const char *body, smartlist_t *which,
return added;
}
-static int handle_response_fetch_consensus(dir_connection_t *,
- const response_handler_args_t *);
static int handle_response_fetch_certificate(dir_connection_t *,
const response_handler_args_t *);
static int handle_response_fetch_status_vote(dir_connection_t *,
@@ -2585,7 +2583,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
* consensus document by checking the consensus, storing it, and marking
* router requests as reachable.
**/
-static int
+STATIC int
handle_response_fetch_consensus(dir_connection_t *conn,
const response_handler_args_t *args)
{
@@ -3517,63 +3515,47 @@ write_http_response_header_impl(dir_connection_t *conn, ssize_t length,
long cache_lifetime)
{
char date[RFC1123_TIME_LEN+1];
- char tmp[1024];
- char *cp;
time_t now = time(NULL);
+ buf_t *buf = buf_new_with_capacity(1024);
tor_assert(conn);
format_rfc1123_time(date, now);
- cp = tmp;
- tor_snprintf(cp, sizeof(tmp),
- "HTTP/1.0 200 OK\r\nDate: %s\r\n",
- date);
- cp += strlen(tmp);
+
+ buf_add_printf(buf, "HTTP/1.0 200 OK\r\nDate: %s\r\n", date);
if (type) {
- tor_snprintf(cp, sizeof(tmp)-(cp-tmp), "Content-Type: %s\r\n", type);
- cp += strlen(cp);
+ buf_add_printf(buf, "Content-Type: %s\r\n", type);
}
if (!is_local_addr(&conn->base_.addr)) {
/* Don't report the source address for a nearby/private connection.
* Otherwise we tend to mis-report in cases where incoming ports are
* being forwarded to a Tor server running behind the firewall. */
- tor_snprintf(cp, sizeof(tmp)-(cp-tmp),
- X_ADDRESS_HEADER "%s\r\n", conn->base_.address);
- cp += strlen(cp);
+ buf_add_printf(buf, X_ADDRESS_HEADER "%s\r\n", conn->base_.address);
}
if (encoding) {
- tor_snprintf(cp, sizeof(tmp)-(cp-tmp),
- "Content-Encoding: %s\r\n", encoding);
- cp += strlen(cp);
+ buf_add_printf(buf, "Content-Encoding: %s\r\n", encoding);
}
if (length >= 0) {
- tor_snprintf(cp, sizeof(tmp)-(cp-tmp),
- "Content-Length: %ld\r\n", (long)length);
- cp += strlen(cp);
+ buf_add_printf(buf, "Content-Length: %ld\r\n", (long)length);
}
if (cache_lifetime > 0) {
char expbuf[RFC1123_TIME_LEN+1];
format_rfc1123_time(expbuf, (time_t)(now + cache_lifetime));
/* We could say 'Cache-control: max-age=%d' here if we start doing
* http/1.1 */
- tor_snprintf(cp, sizeof(tmp)-(cp-tmp),
- "Expires: %s\r\n", expbuf);
- cp += strlen(cp);
+ buf_add_printf(buf, "Expires: %s\r\n", expbuf);
} else if (cache_lifetime == 0) {
/* We could say 'Cache-control: no-cache' here if we start doing
* http/1.1 */
- strlcpy(cp, "Pragma: no-cache\r\n", sizeof(tmp)-(cp-tmp));
- cp += strlen(cp);
+ buf_add_string(buf, "Pragma: no-cache\r\n");
}
if (extra_headers) {
- strlcpy(cp, extra_headers, sizeof(tmp)-(cp-tmp));
- cp += strlen(cp);
+ buf_add_string(buf, extra_headers);
}
- if (sizeof(tmp)-(cp-tmp) > 3)
- memcpy(cp, "\r\n", 3);
- else
- tor_assert(0);
- connection_buf_add(tmp, strlen(tmp), TO_CONN(conn));
+ buf_add_string(buf, "\r\n");
+
+ connection_buf_add_buf(TO_CONN(conn), buf);
+ buf_free(buf);
}
/** As write_http_response_header_impl, but sets encoding and content-typed
diff --git a/src/or/directory.h b/src/or/directory.h
index 79984be32d..764f0092e0 100644
--- a/src/or/directory.h
+++ b/src/or/directory.h
@@ -194,6 +194,9 @@ STATIC void warn_disallowed_anonymous_compression_method(compress_method_t);
STATIC int handle_response_fetch_hsdesc_v3(dir_connection_t *conn,
const response_handler_args_t *args);
+STATIC int handle_response_fetch_consensus(dir_connection_t *conn,
+ const response_handler_args_t *args);
+
#endif /* defined(DIRECTORY_PRIVATE) */
#ifdef TOR_UNIT_TESTS
diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c
index 9fbf426433..76a8f591b9 100644
--- a/src/or/entrynodes.c
+++ b/src/or/entrynodes.c
@@ -2126,9 +2126,9 @@ circuit_guard_state_free(circuit_guard_state_t *state)
/** Allocate and return a new circuit_guard_state_t to track the result
* of using <b>guard</b> for a given operation. */
-static circuit_guard_state_t *
-circuit_guard_state_new(entry_guard_t *guard, unsigned state,
- entry_guard_restriction_t *rst)
+MOCK_IMPL(STATIC circuit_guard_state_t *,
+circuit_guard_state_new,(entry_guard_t *guard, unsigned state,
+ entry_guard_restriction_t *rst))
{
circuit_guard_state_t *result;
diff --git a/src/or/entrynodes.h b/src/or/entrynodes.h
index 9e1e729930..86f0517df2 100644
--- a/src/or/entrynodes.h
+++ b/src/or/entrynodes.h
@@ -488,6 +488,10 @@ STATIC entry_guard_t *get_sampled_guard_with_id(guard_selection_t *gs,
MOCK_DECL(STATIC time_t, randomize_time, (time_t now, time_t max_backdate));
+MOCK_DECL(STATIC circuit_guard_state_t *,
+ circuit_guard_state_new,(entry_guard_t *guard, unsigned state,
+ entry_guard_restriction_t *rst));
+
STATIC entry_guard_t *entry_guard_add_to_sample(guard_selection_t *gs,
const node_t *node);
STATIC entry_guard_t *entry_guards_expand_sample(guard_selection_t *gs);
diff --git a/src/or/geoip.c b/src/or/geoip.c
index 3944b2cf69..c976b8d276 100644
--- a/src/or/geoip.c
+++ b/src/or/geoip.c
@@ -30,6 +30,7 @@
#define GEOIP_PRIVATE
#include "or.h"
#include "ht.h"
+#include "buffers.h"
#include "config.h"
#include "control.h"
#include "dnsserv.h"
@@ -930,9 +931,9 @@ static char *
geoip_get_dirreq_history(dirreq_type_t type)
{
char *result = NULL;
+ buf_t *buf = NULL;
smartlist_t *dirreq_completed = NULL;
uint32_t complete = 0, timeouts = 0, running = 0;
- int bufsize = 1024, written;
dirreq_map_entry_t **ptr, **next;
struct timeval now;
@@ -965,13 +966,9 @@ geoip_get_dirreq_history(dirreq_type_t type)
DIR_REQ_GRANULARITY);
running = round_uint32_to_next_multiple_of(running,
DIR_REQ_GRANULARITY);
- result = tor_malloc_zero(bufsize);
- written = tor_snprintf(result, bufsize, "complete=%u,timeout=%u,"
- "running=%u", complete, timeouts, running);
- if (written < 0) {
- tor_free(result);
- goto done;
- }
+ buf = buf_new_with_capacity(1024);
+ buf_add_printf(buf, "complete=%u,timeout=%u,"
+ "running=%u", complete, timeouts, running);
#define MIN_DIR_REQ_RESPONSES 16
if (complete >= MIN_DIR_REQ_RESPONSES) {
@@ -992,7 +989,7 @@ geoip_get_dirreq_history(dirreq_type_t type)
dltimes[ent_sl_idx] = bytes_per_second;
} SMARTLIST_FOREACH_END(ent);
median_uint32(dltimes, complete); /* sorts as a side effect. */
- written = tor_snprintf(result + written, bufsize - written,
+ buf_add_printf(buf,
",min=%u,d1=%u,d2=%u,q1=%u,d3=%u,d4=%u,md=%u,"
"d6=%u,d7=%u,q3=%u,d8=%u,d9=%u,max=%u",
dltimes[0],
@@ -1008,14 +1005,15 @@ geoip_get_dirreq_history(dirreq_type_t type)
dltimes[8*complete/10-1],
dltimes[9*complete/10-1],
dltimes[complete-1]);
- if (written<0)
- tor_free(result);
tor_free(dltimes);
}
- done:
+
+ result = buf_extract(buf, NULL);
+
SMARTLIST_FOREACH(dirreq_completed, dirreq_map_entry_t *, ent,
tor_free(ent));
smartlist_free(dirreq_completed);
+ buf_free(buf);
return result;
}
diff --git a/src/or/git_revision.c b/src/or/git_revision.c
new file mode 100644
index 0000000000..8f326b8751
--- /dev/null
+++ b/src/or/git_revision.c
@@ -0,0 +1,17 @@
+/* Copyright 2001-2004 Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "git_revision.h"
+
+/** String describing which Tor Git repository version the source was
+ * built from. This string is generated by a bit of shell kludging in
+ * src/or/include.am, and is usually right.
+ */
+const char tor_git_revision[] =
+#ifndef _MSC_VER
+#include "micro-revision.i"
+#endif
+ "";
+
diff --git a/src/or/git_revision.h b/src/or/git_revision.h
new file mode 100644
index 0000000000..1ceaeedf16
--- /dev/null
+++ b/src/or/git_revision.h
@@ -0,0 +1,12 @@
+/* Copyright 2001-2004 Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_GIT_REVISION_H
+#define TOR_GIT_REVISION_H
+
+extern const char tor_git_revision[];
+
+#endif
+
diff --git a/src/or/hibernate.c b/src/or/hibernate.c
index 74ab766468..4dc35f68d0 100644
--- a/src/or/hibernate.c
+++ b/src/or/hibernate.c
@@ -34,6 +34,7 @@ hibernating, phase 2:
#include "config.h"
#include "connection.h"
#include "connection_edge.h"
+#include "connection_or.h"
#include "control.h"
#include "hibernate.h"
#include "main.h"
@@ -818,8 +819,8 @@ hibernate_begin(hibernate_state_t new_state, time_t now)
log_notice(LD_GENERAL,"SIGINT received %s; exiting now.",
hibernate_state == HIBERNATE_STATE_EXITING ?
"a second time" : "while hibernating");
- tor_cleanup();
- exit(0);
+ tor_shutdown_event_loop_and_exit(0);
+ return;
}
if (new_state == HIBERNATE_STATE_LOWBANDWIDTH &&
@@ -906,20 +907,23 @@ hibernate_go_dormant(time_t now)
while ((conn = connection_get_by_type(CONN_TYPE_OR)) ||
(conn = connection_get_by_type(CONN_TYPE_AP)) ||
(conn = connection_get_by_type(CONN_TYPE_EXIT))) {
- if (CONN_IS_EDGE(conn))
+ if (CONN_IS_EDGE(conn)) {
connection_edge_end(TO_EDGE_CONN(conn), END_STREAM_REASON_HIBERNATING);
+ }
log_info(LD_NET,"Closing conn type %d", conn->type);
- if (conn->type == CONN_TYPE_AP) /* send socks failure if needed */
+ if (conn->type == CONN_TYPE_AP) {
+ /* send socks failure if needed */
connection_mark_unattached_ap(TO_ENTRY_CONN(conn),
END_STREAM_REASON_HIBERNATING);
- else if (conn->type == CONN_TYPE_OR) {
+ } else if (conn->type == CONN_TYPE_OR) {
if (TO_OR_CONN(conn)->chan) {
- channel_mark_for_close(TLS_CHAN_TO_BASE(TO_OR_CONN(conn)->chan));
+ connection_or_close_normally(TO_OR_CONN(conn), 0);
} else {
connection_mark_for_close(conn);
}
- } else
+ } else {
connection_mark_for_close(conn);
+ }
}
if (now < interval_wakeup_time)
@@ -980,8 +984,7 @@ consider_hibernation(time_t now)
tor_assert(shutdown_time);
if (shutdown_time <= now) {
log_notice(LD_GENERAL, "Clean shutdown finished. Exiting.");
- tor_cleanup();
- exit(0);
+ tor_shutdown_event_loop_and_exit(0);
}
return; /* if exiting soon, don't worry about bandwidth limits */
}
diff --git a/src/or/include.am b/src/or/include.am
index 7216aba9af..b783f4855a 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -51,6 +51,7 @@ LIBTOR_A_SOURCES = \
src/or/geoip.c \
src/or/entrynodes.c \
src/or/ext_orport.c \
+ src/or/git_revision.c \
src/or/hibernate.c \
src/or/hs_cache.c \
src/or/hs_cell.c \
@@ -78,6 +79,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 \
@@ -104,6 +106,7 @@ LIBTOR_A_SOURCES = \
src/or/statefile.c \
src/or/status.c \
src/or/torcert.c \
+ src/or/tor_api.c \
src/or/onion_ntor.c \
$(tor_platform_source)
@@ -189,6 +192,7 @@ ORHEADERS = \
src/or/fp_pair.h \
src/or/geoip.h \
src/or/entrynodes.h \
+ src/or/git_revision.h \
src/or/hibernate.h \
src/or/hs_cache.h \
src/or/hs_cell.h \
@@ -243,9 +247,13 @@ ORHEADERS = \
src/or/scheduler.h \
src/or/statefile.h \
src/or/status.h \
- src/or/torcert.h
+ src/or/torcert.h \
+ src/or/tor_api_internal.h
-noinst_HEADERS+= $(ORHEADERS) micro-revision.i
+# This may someday want to be an installed file?
+noinst_HEADERS += src/or/tor_api.h
+
+noinst_HEADERS += $(ORHEADERS) micro-revision.i
micro-revision.i: FORCE
$(AM_V_at)rm -f micro-revision.tmp; \
diff --git a/src/or/main.c b/src/or/main.c
index c340e4128b..97dcdcb3c2 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"
@@ -106,6 +105,8 @@
#include "shared_random.h"
#include "statefile.h"
#include "status.h"
+#include "tor_api.h"
+#include "tor_api_internal.h"
#include "util_process.h"
#include "ext_orport.h"
#ifdef USE_DMALLOC
@@ -128,6 +129,12 @@
void evdns_shutdown(int);
+#ifdef HAVE_RUST
+// 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);
+#endif
+
/********* PROTOTYPES **********/
static void dumpmemusage(int severity);
@@ -140,6 +147,8 @@ static void connection_start_reading_from_linked_conn(connection_t *conn);
static int connection_should_read_from_linked_conn(connection_t *conn);
static int run_main_loop_until_done(void);
static void process_signal(int sig);
+static void shutdown_did_not_work_callback(evutil_socket_t fd, short event,
+ void *arg) ATTR_NORETURN;
/********* START VARIABLES **********/
int global_read_bucket; /**< Max number of bytes I can read this second. */
@@ -192,6 +201,14 @@ static smartlist_t *active_linked_connection_lst = NULL;
* <b>loop_once</b>. If so, there's no need to trigger a loopexit in order
* to handle linked connections. */
static int called_loop_once = 0;
+/** Flag: if true, it's time to shut down, so the main loop should exit as
+ * soon as possible.
+ */
+static int main_loop_should_exit = 0;
+/** The return value that the main loop should yield when it exits, if
+ * main_loop_should_exit is true.
+ */
+static int main_loop_exit_value = 0;
/** We set this to 1 when we've opened a circuit, so we can print a log
* entry to inform the user that Tor is working. We set it to 0 when
@@ -637,9 +654,10 @@ connection_should_read_from_linked_conn(connection_t *conn)
/** If we called event_base_loop() and told it to never stop until it
* runs out of events, now we've changed our mind: tell it we want it to
- * finish. */
+ * exit once the current round of callbacks is done, so that we can
+ * run external code, and then return to the main loop. */
void
-tell_event_loop_to_finish(void)
+tell_event_loop_to_run_external_code(void)
{
if (!called_loop_once) {
struct timeval tv = { 0, 0 };
@@ -648,6 +666,54 @@ tell_event_loop_to_finish(void)
}
}
+/** Failsafe measure that should never actually be necessary: If
+ * tor_shutdown_event_loop_and_exit() somehow doesn't successfully exit the
+ * event loop, then this callback will kill Tor with an assertion failure
+ * seconds later
+ */
+static void
+shutdown_did_not_work_callback(evutil_socket_t fd, short event, void *arg)
+{
+ // LCOV_EXCL_START
+ (void) fd;
+ (void) event;
+ (void) arg;
+ tor_assert_unreached();
+ // LCOV_EXCL_STOP
+}
+
+/**
+ * After finishing the current callback (if any), shut down the main loop,
+ * clean up the process, and exit with <b>exitcode</b>.
+ */
+void
+tor_shutdown_event_loop_and_exit(int exitcode)
+{
+ if (main_loop_should_exit)
+ return; /* Ignore multiple calls to this function. */
+
+ main_loop_should_exit = 1;
+ main_loop_exit_value = exitcode;
+
+ /* Die with an assertion failure in ten seconds, if for some reason we don't
+ * exit normally. */
+ /* XXXX We should consider this code if it's never used. */
+ struct timeval ten_seconds = { 10, 0 };
+ event_base_once(tor_libevent_get_base(), -1, EV_TIMEOUT,
+ shutdown_did_not_work_callback, NULL,
+ &ten_seconds);
+
+ /* Unlike loopexit, loopbreak prevents other callbacks from running. */
+ tor_event_base_loopbreak(tor_libevent_get_base());
+}
+
+/** Return true iff tor_shutdown_event_loop_and_exit() has been called. */
+int
+tor_event_loop_shutdown_is_pending(void)
+{
+ return main_loop_should_exit;
+}
+
/** Helper: Tell the main loop to begin reading bytes into <b>conn</b> from
* its linked connection, if it is not doing so already. Called by
* connection_start_reading and connection_start_writing as appropriate. */
@@ -663,7 +729,7 @@ connection_start_reading_from_linked_conn(connection_t *conn)
/* make sure that the event_base_loop() function exits at
* the end of its run through the current connections, so we can
* activate read events for linked connections. */
- tell_event_loop_to_finish();
+ tell_event_loop_to_run_external_code();
} else {
tor_assert(smartlist_contains(active_linked_connection_lst, conn));
}
@@ -1554,8 +1620,7 @@ check_ed_keys_callback(time_t now, const or_options_t *options)
if (new_signing_key < 0 ||
generate_ed_link_cert(options, now, new_signing_key > 0)) {
log_err(LD_OR, "Unable to update Ed25519 keys! Exiting.");
- tor_cleanup();
- exit(1);
+ tor_shutdown_event_loop_and_exit(1);
}
}
return 30;
@@ -2366,10 +2431,18 @@ do_hup(void)
/* first, reload config variables, in case they've changed */
if (options->ReloadTorrcOnSIGHUP) {
/* no need to provide argc/v, they've been cached in init_from_config */
- if (options_init_from_torrc(0, NULL) < 0) {
+ int init_rv = options_init_from_torrc(0, NULL);
+ if (init_rv < 0) {
log_err(LD_CONFIG,"Reading config failed--see warnings above. "
"For usage, try -h.");
return -1;
+ } else if (BUG(init_rv > 0)) {
+ // LCOV_EXCL_START
+ /* This should be impossible: the only "return 1" cases in
+ * options_init_from_torrc are ones caused by command-line arguments;
+ * but they can't change while Tor is running. */
+ return -1;
+ // LCOV_EXCL_STOP
}
options = get_options(); /* they have changed now */
/* Logs are only truncated the first time they are opened, but were
@@ -2595,6 +2668,9 @@ do_main_loop(void)
}
#endif /* defined(HAVE_SYSTEMD) */
+ main_loop_should_exit = 0;
+ main_loop_exit_value = 0;
+
return run_main_loop_until_done();
}
@@ -2610,6 +2686,9 @@ run_main_loop_once(void)
if (nt_service_is_stopping())
return 0;
+ if (main_loop_should_exit)
+ return 0;
+
#ifndef _WIN32
/* Make it easier to tell whether libevent failure is our fault or not. */
errno = 0;
@@ -2656,6 +2735,9 @@ run_main_loop_once(void)
}
}
+ if (main_loop_should_exit)
+ return 0;
+
/* And here is where we put callbacks that happen "every time the event loop
* runs." They must be very fast, or else the whole Tor process will get
* slowed down.
@@ -2684,7 +2766,11 @@ run_main_loop_until_done(void)
do {
loop_result = run_main_loop_once();
} while (loop_result == 1);
- return loop_result;
+
+ if (main_loop_should_exit)
+ return main_loop_exit_value;
+ else
+ return loop_result;
}
/** Libevent callback: invoked when we get a signal.
@@ -2708,14 +2794,13 @@ process_signal(int sig)
{
case SIGTERM:
log_notice(LD_GENERAL,"Catching signal TERM, exiting cleanly.");
- tor_cleanup();
- exit(0);
+ tor_shutdown_event_loop_and_exit(0);
break;
case SIGINT:
if (!server_mode(get_options())) { /* do it now */
log_notice(LD_GENERAL,"Interrupt: exiting cleanly.");
- tor_cleanup();
- exit(0);
+ tor_shutdown_event_loop_and_exit(0);
+ return;
}
#ifdef HAVE_SYSTEMD
sd_notify(0, "STOPPING=1");
@@ -2744,8 +2829,8 @@ process_signal(int sig)
#endif
if (do_hup() < 0) {
log_warn(LD_CONFIG,"Restart failed (config error?). Exiting.");
- tor_cleanup();
- exit(1);
+ tor_shutdown_event_loop_and_exit(1);
+ return;
}
#ifdef HAVE_SYSTEMD
sd_notify(0, "READY=1");
@@ -3019,7 +3104,8 @@ activate_signal(int signal_num)
}
}
-/** Main entry point for the Tor command-line client.
+/** Main entry point for the Tor command-line client. Return 0 on "success",
+ * negative on "failure", and positive on "success and exit".
*/
int
tor_init(int argc, char *argv[])
@@ -3111,14 +3197,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.");
@@ -3126,9 +3211,14 @@ tor_init(int argc, char *argv[])
}
atexit(exit_function);
- if (options_init_from_torrc(argc,argv) < 0) {
+ int init_rv = options_init_from_torrc(argc,argv);
+ if (init_rv < 0) {
log_err(LD_CONFIG,"Reading config failed--see warnings above.");
return -1;
+ } else if (init_rv > 0) {
+ // We succeeded, and should exit anyway -- probably the user just said
+ // "--version" or something like that.
+ return 1;
}
/* The options are now initialised */
@@ -3198,7 +3288,7 @@ try_locking(const or_options_t *options, int err_if_locked)
r = try_locking(options, 0);
if (r<0) {
log_err(LD_GENERAL, "No, it's still there. Exiting.");
- exit(1);
+ return -1;
}
return r;
}
@@ -3521,6 +3611,10 @@ sandbox_init_filter(void)
}
}
+ SMARTLIST_FOREACH(options->FilesOpenedByIncludes, char *, f, {
+ OPEN(f);
+ });
+
#define RENAME_SUFFIX(name, suffix) \
sandbox_cfg_allow_rename(&cfg, \
get_datadir_fname(name suffix), \
@@ -3703,14 +3797,16 @@ sandbox_init_filter(void)
return cfg;
}
-/** Main entry point for the Tor process. Called from main(). */
-/* This function is distinct from main() only so we can link main.c into
- * the unittest binary without conflicting with the unittests' main. */
+/* Main entry point for the Tor process. Called from tor_main(), and by
+ * anybody embedding Tor. */
int
-tor_main(int argc, char *argv[])
+tor_run_main(const tor_main_configuration_t *tor_cfg)
{
int result = 0;
+ int argc = tor_cfg->argc;
+ char **argv = tor_cfg->argv;
+
#ifdef _WIN32
#ifndef HeapEnableTerminationOnCorruption
#define HeapEnableTerminationOnCorruption 1
@@ -3754,8 +3850,13 @@ tor_main(int argc, char *argv[])
if (done) return result;
}
#endif /* defined(NT_SERVICE) */
- if (tor_init(argc, argv)<0)
- return -1;
+ {
+ int init_rv = tor_init(argc, argv);
+ if (init_rv < 0)
+ return -1;
+ else if (init_rv > 0)
+ return 0;
+ }
if (get_options()->Sandbox && get_options()->command == CMD_RUN_TOR) {
sandbox_cfg_t* cfg = sandbox_init_filter();
diff --git a/src/or/main.h b/src/or/main.h
index 132ab12bbb..8eb977575e 100644
--- a/src/or/main.h
+++ b/src/or/main.h
@@ -45,7 +45,9 @@ int connection_is_writing(connection_t *conn);
MOCK_DECL(void,connection_stop_writing,(connection_t *conn));
MOCK_DECL(void,connection_start_writing,(connection_t *conn));
-void tell_event_loop_to_finish(void);
+void tell_event_loop_to_run_external_code(void);
+void tor_shutdown_event_loop_and_exit(int exitcode);
+int tor_event_loop_shutdown_is_pending(void);
void connection_stop_reading_from_linked_conn(connection_t *conn);
@@ -74,8 +76,6 @@ void release_lockfile(void);
void tor_cleanup(void);
void tor_free_all(int postfork);
-int tor_main(int argc, char *argv[]);
-
int do_main_loop(void);
int tor_init(int argc, char **argv);
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index 93bb8643d8..abcd4d8658 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -1671,7 +1671,7 @@ handle_missing_protocol_warning_impl(const networkstatus_t *c,
}
tor_free(protocol_warning);
if (should_exit)
- exit(1);
+ exit(1); // XXXX bad exit: should return from main.
}
/** Called when we have received a networkstatus <b>c</b>. If there are
@@ -1720,7 +1720,7 @@ networkstatus_set_current_consensus(const char *consensus,
{
networkstatus_t *c=NULL;
int r, result = -1;
- time_t now = time(NULL);
+ time_t now = approx_time();
const or_options_t *options = get_options();
char *unverified_fname = NULL, *consensus_fname = NULL;
int flav = networkstatus_parse_flavor_name(flavor);
diff --git a/src/or/ntmain.c b/src/or/ntmain.c
index 508e5844eb..ebbe0018bd 100644
--- a/src/or/ntmain.c
+++ b/src/or/ntmain.c
@@ -195,7 +195,7 @@ nt_service_loadlibrary(void)
return;
err:
printf("Unable to load library support for NT services: exiting.\n");
- exit(1);
+ exit(1); // exit ok: ntmain can't read libraries
}
/** If we're compiled to run as an NT service, and the service wants to
@@ -318,7 +318,7 @@ nt_service_main(void)
printf("Service error %d : %s\n", (int) result, errmsg);
tor_free(errmsg);
if (result == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
- if (tor_init(backup_argc, backup_argv) < 0)
+ if (tor_init(backup_argc, backup_argv))
return;
switch (get_options()->command) {
case CMD_RUN_TOR:
diff --git a/src/or/or.h b/src/or/or.h
index 809714b5e2..a68f1c3c47 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -2234,18 +2234,18 @@ typedef struct {
* uploaded it. */
#define ROUTER_PURPOSE_GENERAL 0
/** Tor should avoid using this router for circuit-building: we got it
- * from a crontroller. If the controller wants to use it, it'll have to
+ * from a controller. If the controller wants to use it, it'll have to
* ask for it by identity. */
#define ROUTER_PURPOSE_CONTROLLER 1
/** Tor should use this router only for bridge positions in circuits: we got
* it via a directory request from the bridge itself, or a bridge
- * authority. x*/
+ * authority. */
#define ROUTER_PURPOSE_BRIDGE 2
/** Tor should not use this router; it was marked in cached-descriptors with
* a purpose we didn't recognize. */
#define ROUTER_PURPOSE_UNKNOWN 255
- /* In what way did we find out about this router? One of ROUTER_PURPOSE_*.
+ /** In what way did we find out about this router? One of ROUTER_PURPOSE_*.
* Routers of different purposes are kept segregated and used for different
* things; see notes on ROUTER_PURPOSE_* macros above.
*/
@@ -3674,6 +3674,7 @@ typedef struct {
* interface addresses?
* Includes OutboundBindAddresses and
* configured ports. */
+ int ReducedExitPolicy; /**<Should we use the Reduced Exit Policy? */
config_line_t *SocksPolicy; /**< Lists of socks policy components */
config_line_t *DirPolicy; /**< Lists of dir policy components */
/** Local address to bind outbound sockets */
@@ -4070,6 +4071,8 @@ typedef struct {
/** Process specifier for a controller that ‘owns’ this Tor
* instance. Tor will terminate if its owning controller does. */
char *OwningControllerProcess;
+ /** FD specifier for a controller that owns this Tor instance. */
+ int OwningControllerFD;
int ShutdownWaitLength; /**< When we get a SIGINT and we're a server, how
* long do we wait before exiting? */
@@ -4627,6 +4630,9 @@ typedef struct {
smartlist_t *Schedulers;
/* An ordered list of scheduler_types mapped from Schedulers. */
smartlist_t *SchedulerTypes_;
+
+ /** List of files that were opened by %include in torrc and torrc-defaults */
+ smartlist_t *FilesOpenedByIncludes;
} or_options_t;
#define LOG_PROTOCOL_WARN (get_protocol_warning_severity_level())
diff --git a/src/or/policies.c b/src/or/policies.c
index 4c24bfbc32..1f80130710 100644
--- a/src/or/policies.c
+++ b/src/or/policies.c
@@ -81,7 +81,8 @@ static int policies_parse_exit_policy_internal(
const smartlist_t *configured_addresses,
int reject_interface_addresses,
int reject_configured_port_addresses,
- int add_default_policy);
+ int add_default_policy,
+ int add_reduced_policy);
/** Replace all "private" entries in *<b>policy</b> with their expanded
* equivalents. */
@@ -1144,7 +1145,7 @@ validate_addr_policies(const or_options_t *options, char **msg)
"to 1 to disable this warning, and for forward compatibility.",
options->ExitPolicy == NULL ?
" with the default exit policy" : "");
- if (options->ExitPolicy == NULL) {
+ if (options->ExitPolicy == NULL && options->ReducedExitPolicy == 0) {
log_warn(LD_CONFIG,
"In a future version of Tor, ExitRelay 0 may become the "
"default when no ExitPolicy is given.");
@@ -1877,6 +1878,24 @@ policies_log_first_redundant_entry(const smartlist_t *policy)
"reject *:563,reject *:1214,reject *:4661-4666," \
"reject *:6346-6429,reject *:6699,reject *:6881-6999,accept *:*"
+#define REDUCED_EXIT_POLICY \
+ "accept *:20-23,accept *:43,accept *:53,accept *:79-81,accept *:88," \
+ "accept *:110,accept *:143,accept *:194,accept *:220,accept *:389," \
+ "accept *:443,accept *:464,accept *:465,accept *:531,accept *:543-544," \
+ "accept *:554,accept *:563,accept *:587,accept *:636,accept *:706," \
+ "accept *:749,accept *:873,accept *:902-904,accept *:981,accept *:989-995," \
+ "accept *:1194,accept *:1220,accept *:1293,accept *:1500,accept *:1533," \
+ "accept *:1677,accept *:1723,accept *:1755,accept *:1863," \
+ "accept *:2082-2083,accept *:2086-2087,accept *:2095-2096," \
+ "accept *:2102-2104,accept *:3128,accept *:3389,accept *:3690," \
+ "accept *:4321,accept *:4643,accept *:5050,accept *:5190," \
+ "accept *:5222-5223,accept *:5228,accept *:5900,accept *:6660-6669," \
+ "accept *:6679,accept *:6697,accept *:8000,accept *:8008,accept *:8074," \
+ "accept *:8080,accept *:8082,accept *:8087-8088,accept *:8232-8233," \
+ "accept *:8332-8333,accept *:8443,accept *:8888,accept *:9418," \
+ "accept *:9999,accept *:10000,accept *:11371,accept *:19294," \
+ "accept *:19638,accept *:50002,accept *:64738,reject *:*"
+
/** Parse the exit policy <b>cfg</b> into the linked list *<b>dest</b>.
*
* If <b>ipv6_exit</b> is false, prepend "reject *6:*" to the policy.
@@ -1912,7 +1931,8 @@ policies_parse_exit_policy_internal(config_line_t *cfg,
const smartlist_t *configured_addresses,
int reject_interface_addresses,
int reject_configured_port_addresses,
- int add_default_policy)
+ int add_default_policy,
+ int add_reduced_policy)
{
if (!ipv6_exit) {
append_exit_policy_string(dest, "reject *6:*");
@@ -1938,7 +1958,9 @@ policies_parse_exit_policy_internal(config_line_t *cfg,
* effect, and are most likely an error. */
policies_log_first_redundant_entry(*dest);
- if (add_default_policy) {
+ if (add_reduced_policy) {
+ append_exit_policy_string(dest, REDUCED_EXIT_POLICY);
+ } else if (add_default_policy) {
append_exit_policy_string(dest, DEFAULT_EXIT_POLICY);
} else {
append_exit_policy_string(dest, "reject *4:*");
@@ -1979,13 +2001,15 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
int add_default = (options & EXIT_POLICY_ADD_DEFAULT) ? 1 : 0;
int reject_local_interfaces = (options &
EXIT_POLICY_REJECT_LOCAL_INTERFACES) ? 1 : 0;
+ int add_reduced = (options & EXIT_POLICY_ADD_REDUCED) ? 1 : 0;
return policies_parse_exit_policy_internal(cfg,dest,ipv6_enabled,
reject_private,
configured_addresses,
reject_local_interfaces,
reject_local_interfaces,
- add_default);
+ add_default,
+ add_reduced);
}
/** Helper function that adds a copy of addr to a smartlist as long as it is
@@ -2095,7 +2119,10 @@ policies_parse_exit_policy_from_options(const or_options_t *or_options,
}
if (!or_options->BridgeRelay) {
- parser_cfg |= EXIT_POLICY_ADD_DEFAULT;
+ if (or_options->ReducedExitPolicy)
+ parser_cfg |= EXIT_POLICY_ADD_REDUCED;
+ else
+ parser_cfg |= EXIT_POLICY_ADD_DEFAULT;
}
if (or_options->ExitPolicyRejectLocalInterfaces) {
@@ -2186,21 +2213,16 @@ exit_policy_is_general_exit_helper(smartlist_t *policy, int port)
}
/** Return true iff <b>ri</b> is "useful as an exit node", meaning
- * it allows exit to at least one /8 address space for at least
- * two of ports 80, 443, and 6667. */
+ * it allows exit to at least one /8 address space for each of ports 80
+ * and 443. */
int
exit_policy_is_general_exit(smartlist_t *policy)
{
- static const int ports[] = { 80, 443, 6667 };
- int n_allowed = 0;
- int i;
if (!policy) /*XXXX disallow NULL policies? */
return 0;
- for (i = 0; i < 3; ++i) {
- n_allowed += exit_policy_is_general_exit_helper(policy, ports[i]);
- }
- return n_allowed >= 2;
+ return (exit_policy_is_general_exit_helper(policy, 80) &&
+ exit_policy_is_general_exit_helper(policy, 443));
}
/** Return false if <b>policy</b> might permit access to some addr:port;
diff --git a/src/or/policies.h b/src/or/policies.h
index 52ff4e2f99..cd97ee7f59 100644
--- a/src/or/policies.h
+++ b/src/or/policies.h
@@ -22,7 +22,8 @@
#define EXIT_POLICY_REJECT_PRIVATE (1 << 1)
#define EXIT_POLICY_ADD_DEFAULT (1 << 2)
#define EXIT_POLICY_REJECT_LOCAL_INTERFACES (1 << 3)
-#define EXIT_POLICY_OPTION_MAX EXIT_POLICY_REJECT_LOCAL_INTERFACES
+#define EXIT_POLICY_ADD_REDUCED (1 << 4)
+#define EXIT_POLICY_OPTION_MAX EXIT_POLICY_ADD_REDUCED
/* All options set: used for unit testing */
#define EXIT_POLICY_OPTION_ALL ((EXIT_POLICY_OPTION_MAX << 1) - 1)
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..0c409b1681
--- /dev/null
+++ b/src/or/protover_rust.c
@@ -0,0 +1,19 @@
+/* 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"
+
+#ifdef HAVE_RUST
+
+/* Define for compatibility, used in main.c */
+void protover_free_all(void) {}
+
+#endif
+
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index f0bd343f45..f04e2ca160 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -3082,7 +3082,7 @@ signed_descriptor_get_body_impl(const signed_descriptor_t *desc,
log_err(LD_DIR, "We couldn't read a descriptor that is supposedly "
"mmaped in our cache. Is another process running in our data "
"directory? Exiting.");
- exit(1);
+ exit(1); // XXXX bad exit: should recover.
}
}
if (!r) /* no mmap, or not in cache. */
@@ -3096,7 +3096,7 @@ signed_descriptor_get_body_impl(const signed_descriptor_t *desc,
log_err(LD_DIR, "descriptor at %p begins with unexpected string %s. "
"Is another process running in our data directory? Exiting.",
desc, escaped(cp));
- exit(1);
+ exit(1); // XXXX bad exit: should recover.
}
}
@@ -5358,8 +5358,10 @@ update_extrainfo_downloads(time_t now)
smartlist_free(wanted);
}
-/** Reset the descriptor download failure count on all routers, so that we
- * can retry any long-failed routers immediately.
+/** Reset the consensus and extra-info download failure count on all routers.
+ * When we get a new consensus,
+ * routers_update_status_from_consensus_networkstatus() will reset the
+ * download statuses on the descriptors in that consensus.
*/
void
router_reset_descriptor_download_failures(void)
@@ -5371,6 +5373,8 @@ router_reset_descriptor_download_failures(void)
last_descriptor_download_attempted = 0;
if (!routerlist)
return;
+ /* We want to download *all* extra-info descriptors, not just those in
+ * the consensus we currently have (or are about to have) */
SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, ri,
{
download_status_reset(&ri->cache_info.ei_dl_status);
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 15cdb0bbde..65874ae068 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -4027,7 +4027,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
/** Return the common_digests_t that holds the digests of the
* <b>flavor_name</b>-flavored networkstatus according to the detached
* signatures document <b>sigs</b>, allocating a new common_digests_t as
- * neeeded. */
+ * needed. */
static common_digests_t *
detached_get_digests(ns_detached_signatures_t *sigs, const char *flavor_name)
{
@@ -4041,7 +4041,7 @@ detached_get_digests(ns_detached_signatures_t *sigs, const char *flavor_name)
/** Return the list of signatures of the <b>flavor_name</b>-flavored
* networkstatus according to the detached signatures document <b>sigs</b>,
- * allocating a new common_digests_t as neeeded. */
+ * allocating a new common_digests_t as needed. */
static smartlist_t *
detached_get_signatures(ns_detached_signatures_t *sigs,
const char *flavor_name)
diff --git a/src/or/scheduler.c b/src/or/scheduler.c
index 1438dc60f6..510c16b217 100644
--- a/src/or/scheduler.c
+++ b/src/or/scheduler.c
@@ -284,7 +284,7 @@ select_scheduler(void)
* wishes of using what it has been configured and don't do a sneaky
* fallback. Because this can be changed at runtime, we have to stop tor
* right now. */
- exit(1);
+ exit(1); // XXXX bad exit
}
/* Set the chosen scheduler. */
diff --git a/src/or/tor_api.c b/src/or/tor_api.c
new file mode 100644
index 0000000000..4260cc88f4
--- /dev/null
+++ b/src/or/tor_api.c
@@ -0,0 +1,88 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file tor_api.c
+ **/
+
+#include "tor_api.h"
+#include "tor_api_internal.h"
+
+// Include this after the above headers, to insure that they don't
+// depend on anything else.
+#include "orconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// We don't want to use tor_malloc and tor_free here, since this needs
+// to run before anything is initialized at all, and ought to run when
+// we're not linked to anything at all.
+
+#define raw_malloc malloc
+#define raw_free free
+
+tor_main_configuration_t *
+tor_main_configuration_new(void)
+{
+ static const char *fake_argv[] = { "tor" };
+ tor_main_configuration_t *cfg = raw_malloc(sizeof(*cfg));
+ if (cfg == NULL)
+ return NULL;
+
+ memset(cfg, 0, sizeof(*cfg));
+
+ cfg->argc = 1;
+ cfg->argv = (char **) fake_argv;
+
+ return cfg;
+}
+
+int
+tor_main_configuration_set_command_line(tor_main_configuration_t *cfg,
+ int argc, char *argv[])
+{
+ if (cfg == NULL)
+ return -1;
+ cfg->argc = argc;
+ cfg->argv = argv;
+ return 0;
+}
+
+void
+tor_main_configuration_free(tor_main_configuration_t *cfg)
+{
+ if (cfg == NULL)
+ return;
+ raw_free(cfg);
+}
+
+/* Main entry point for the Tor process. Called from main().
+ *
+ * This function is distinct from main() only so we can link main.c into
+ * the unittest binary without conflicting with the unittests' main.
+ *
+ * Some embedders have historically called this function; but that usage is
+ * deprecated: they should use tor_run_main() instead.
+ */
+int
+tor_main(int argc, char *argv[])
+{
+ tor_main_configuration_t *cfg = tor_main_configuration_new();
+ if (!cfg) {
+ puts("INTERNAL ERROR: Allocation failure. Cannot proceed");
+ return 1;
+ }
+ if (tor_main_configuration_set_command_line(cfg, argc, argv) < 0) {
+ puts("INTERNAL ERROR: Can't set command line. Cannot proceed.");
+ return 1;
+ }
+ int rv = tor_run_main(cfg);
+ tor_main_configuration_free(cfg);
+ return rv;
+}
+
diff --git a/src/or/tor_api.h b/src/or/tor_api.h
new file mode 100644
index 0000000000..b12ed718c1
--- /dev/null
+++ b/src/or/tor_api.h
@@ -0,0 +1,102 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file tor_api.h
+ * \brief Public C API for the Tor network service.
+ *
+ * This interface is intended for use by programs that need to link Tor as
+ * a library, and launch it in a separate thread. If you have the ability
+ * to run Tor as a separate executable, you should probably do that instead
+ * of embedding it as a library.
+ *
+ * To use this API, first construct a tor_main_configuration_t object using
+ * tor_main_configuration_new(). Then, you use one or more other function
+ * calls (such as tor_main_configuration_set_command_line() to configure how
+ * Tor should be run. Finally, you pass the configuration object to
+ * tor_run_main().
+ *
+ * At this point, tor_run_main() will block its thread to run a Tor daemon;
+ * when the Tor daemon exits, it will return. See notes on bugs and
+ * limitations below.
+ *
+ * There is no other public C API to Tor: calling any C Tor function not
+ * documented in this file is not guaranteed to be stable.
+ **/
+
+#ifndef TOR_API_H
+#define TOR_API_H
+
+typedef struct tor_main_configuration_t tor_main_configuration_t;
+
+/**
+ * Create and return a new tor_main_configuration().
+ */
+tor_main_configuration_t *tor_main_configuration_new(void);
+
+/**
+ * Set the command-line arguments in <b>cfg</b>.
+ *
+ * The <b>argc</b> and <b>argv</b> values here are as for main(). The
+ * contents of the argv pointer must remain unchanged until tor_run_main() has
+ * finished and you call tor_main_configuration_free().
+ *
+ * Return 0 on success, -1 on failure.
+ */
+int tor_main_configuration_set_command_line(tor_main_configuration_t *cfg,
+ int argc, char *argv[]);
+
+/**
+ * Release all storage held in <b>cfg</b>.
+ *
+ * Once you have passed a tor_main_configuration_t to tor_run_main(), you
+ * must not free it until tor_run_main() has finished.
+ */
+void tor_main_configuration_free(tor_main_configuration_t *cfg);
+
+/**
+ * Run the tor process, as if from the command line.
+ *
+ * The command line arguments from tor_main_configuration_set_command_line()
+ * are taken as if they had been passed to main().
+ *
+ * This function will not return until Tor is done running. It returns zero
+ * on success, and nonzero on failure.
+ *
+ * BUG 23848: In many cases, tor_main will call exit() or abort() instead of
+ * returning. This is not the intended long-term behavior; we are trying to
+ * fix it.
+ *
+ * BUG 23847: You can only call tor_main() once in a single process; if it
+ * returns and you call it again, you may crash. This is not intended
+ * long-term behavior; we are trying to fix it.
+ *
+ * LIMITATION: You cannot run more than one instance of Tor in the same
+ * process at the same time. Concurrent calls will cause undefined behavior.
+ * We do not currently have plans to change this.
+ *
+ * LIMITATION: While we will try to fix any problems found here, you
+ * should be aware that Tor was originally written to run as its own
+ * process, and that the functionality of this file was added later. If
+ * you find any bugs or strange behavior, please report them, and we'll
+ * try to straighten them out.
+ */
+int tor_run_main(const tor_main_configuration_t *);
+
+/**
+ * Run the tor process, as if from the command line.
+ *
+ * @deprecated Using this function from outside Tor is deprecated; you should
+ * use tor_run_main() instead.
+ *
+ * BUGS: This function has all the same bugs as tor_run_main().
+ *
+ * LIMITATIONS: This function has all the limitations of tor_run_main().
+ */
+int tor_main(int argc, char **argv);
+
+#endif /* !defined(TOR_API_H) */
+
diff --git a/src/or/tor_api_internal.h b/src/or/tor_api_internal.h
new file mode 100644
index 0000000000..a69ba76420
--- /dev/null
+++ b/src/or/tor_api_internal.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_API_INTERNAL_H
+#define TOR_API_INTERNAL_H
+
+/* The contents of this type are private; don't mess with them from outside
+ * Tor. */
+struct tor_main_configuration_t {
+ /** As in main() */
+ int argc;
+ /** As in main(). This pointer is owned by the caller */
+ char **argv;
+};
+
+#endif
+
diff --git a/src/or/tor_main.c b/src/or/tor_main.c
index a3a8838602..c203d8248f 100644
--- a/src/or/tor_main.c
+++ b/src/or/tor_main.c
@@ -3,18 +3,6 @@
* Copyright (c) 2007-2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
-extern const char tor_git_revision[];
-
-/** String describing which Tor Git repository version the source was
- * built from. This string is generated by a bit of shell kludging in
- * src/or/include.am, and is usually right.
- */
-const char tor_git_revision[] =
-#ifndef _MSC_VER
-#include "micro-revision.i"
-#endif
- "";
-
/**
* \file tor_main.c
* \brief Stub module containing a main() function.
@@ -26,7 +14,7 @@ const char tor_git_revision[] =
int tor_main(int argc, char *argv[]);
/** We keep main() in a separate file so that our unit tests can use
- * functions from main.c)
+ * functions from main.c.
*/
int
main(int argc, char *argv[])
diff --git a/src/rust/.cargo/config.in b/src/rust/.cargo/config.in
index 414b253a57..301e7fdbe7 100644
--- a/src/rust/.cargo/config.in
+++ b/src/rust/.cargo/config.in
@@ -5,4 +5,4 @@
@RUST_DL@ replace-with = 'vendored-sources'
@RUST_DL@ [source.vendored-sources]
-@RUST_DL@ directory = '@RUST_DEPENDENCIES@'
+@RUST_DL@ directory = '@TOR_RUST_DEPENDENCIES@'
diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock
index 4ac9606ce8..224d2135bf 100644
--- a/src/rust/Cargo.lock
+++ b/src/rust/Cargo.lock
@@ -3,6 +3,14 @@ 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]]
+name = "external"
+version = "0.0.1"
+dependencies = [
+ "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -10,5 +18,38 @@ 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_allocate 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)",
+]
+
+[[package]]
+name = "tor_allocate"
+version = "0.0.1"
+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 fc4377e8b4..953c9b96b7 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", "tor_allocate", "tor_rust"]
[profile.release]
debug = true
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..b9e17f021d
--- /dev/null
+++ b/src/rust/external/external.rs
@@ -0,0 +1,33 @@
+// Copyright (c) 2016-2017, The Tor Project, Inc. */
+// See LICENSE for licensing information */
+
+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 {
+ 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..9c4337484a 100644
--- a/src/rust/include.am
+++ b/src/rust/include.am
@@ -1,6 +1,26 @@
-include src/rust/tor_util/include.am
+include src/rust/tor_rust/include.am
EXTRA_DIST +=\
src/rust/Cargo.toml \
src/rust/Cargo.lock \
- src/rust/.cargo/config.in
+ src/rust/.cargo/config.in \
+ src/rust/external/Cargo.toml \
+ src/rust/external/external.rs \
+ src/rust/external/lib.rs \
+ src/rust/protover/Cargo.toml \
+ src/rust/protover/ffi.rs \
+ src/rust/protover/lib.rs \
+ src/rust/protover/protover.rs \
+ src/rust/protover/tests/protover.rs \
+ src/rust/smartlist/Cargo.toml \
+ src/rust/smartlist/lib.rs \
+ src/rust/smartlist/smartlist.rs \
+ src/rust/tor_allocate/Cargo.toml \
+ src/rust/tor_allocate/lib.rs \
+ src/rust/tor_allocate/tor_allocate.rs \
+ src/rust/tor_rust/Cargo.toml \
+ src/rust/tor_rust/include.am \
+ src/rust/tor_rust/lib.rs \
+ src/rust/tor_util/Cargo.toml \
+ src/rust/tor_util/ffi.rs \
+ src/rust/tor_util/lib.rs
diff --git a/src/rust/protover/Cargo.toml b/src/rust/protover/Cargo.toml
new file mode 100644
index 0000000000..04d2f2ed7d
--- /dev/null
+++ b/src/rust/protover/Cargo.toml
@@ -0,0 +1,25 @@
+[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"
+
+[dependencies.tor_allocate]
+path = "../tor_allocate"
+
+[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..cf2e9fd783
--- /dev/null
+++ b/src/rust/protover/ffi.rs
@@ -0,0 +1,185 @@
+// 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`
+
+use libc::{c_char, c_int, uint32_t};
+use std::ffi::CStr;
+use std::ffi::CString;
+
+use protover::*;
+use smartlist::*;
+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.
+fn translate_to_rust(c_proto: uint32_t) -> Result<Proto, &'static str> {
+ 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 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 { 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 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 { 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 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 protover_compute_vote(
+ list: *const Stringlist,
+ threshold: c_int,
+) -> *mut c_char {
+
+ if list.is_null() {
+ 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<String> = unsafe { (*list).get_list() };
+
+ let vote = compute_vote(data, threshold);
+
+ allocate_and_copy_string(&vote)
+}
+
+/// Provide an interface for C to translate arguments and return types for
+/// protover::is_supported_here
+#[no_mangle]
+pub extern "C" fn 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 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 = String::new();
+
+ if version.is_null() {
+ return allocate_and_copy_string(&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 { CStr::from_ptr(version) };
+
+ let version = match c_str.to_str() {
+ Ok(n) => n,
+ Err(_) => return allocate_and_copy_string(&empty),
+ };
+
+ let supported = compute_for_old_tor(&version);
+
+ allocate_and_copy_string(&supported)
+}
diff --git a/src/rust/protover/lib.rs b/src/rust/protover/lib.rs
new file mode 100644
index 0000000000..5a5dea4408
--- /dev/null
+++ b/src/rust/protover/lib.rs
@@ -0,0 +1,33 @@
+//! 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;
+extern crate tor_allocate;
+
+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..f19f7972fd
--- /dev/null
+++ b/src/rust/protover/protover.rs
@@ -0,0 +1,852 @@
+// 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;
+use std::fmt;
+use std::collections::{HashMap, 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<Self, Self::Err> {
+ 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<HashMap<Proto, HashSet<u32>>, &'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<HashMap<Proto, HashSet<u32>>, &'static str> {
+ let protocols: &[&'a str] =
+ &protocol_string.split(" ").collect::<Vec<&'a str>>()[..];
+
+ 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<Proto, <u32>>` 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<HashMap<Proto, HashSet<u32>>, &'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<u32>` 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<u32>::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<HashSet<u32>, &'static str> {
+ if version_string.is_empty() {
+ return Err("version string is empty");
+ }
+
+ let mut versions = HashSet::<u32>::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<u32>)`, 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<u32>), &'static str> {
+ let mut parts = protocol_entry.splitn(2, "=");
+
+ let proto = match parts.next() {
+ Some(n) => n,
+ None => return Err("invalid protover entry"),
+ };
+
+ let vers = 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<Proto, HashSet<u32>> =
+ 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 = protocols
+ .split_whitespace()
+ .filter(|v| !contains_only_supported_protocols(v))
+ .collect::<Vec<&str>>();
+
+ (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<Proto, HashSet<u32>>;
+
+ 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<Vec<u32>, &'static str> {
+ if range.is_empty() {
+ return Err("version string empty");
+ }
+
+ let mut parts = range.split("-");
+
+ let lower_string = parts.next().ok_or(
+ "cannot parse protocol range lower bound",
+ )?;
+
+ let lower = u32::from_str_radix(lower_string, 10).or(Err(
+ "cannot parse protocol range lower bound",
+ ))?;
+
+ let higher_string = parts.next().ok_or(
+ "cannot parse protocol range upper bound",
+ )?;
+
+ let higher = u32::from_str_radix(higher_string, 10).or(Err(
+ "cannot parse protocol range upper bound",
+ ))?;
+
+ // 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
+/// 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<u32>) -> (bool, u32) {
+ if list.len() == 0 {
+ return (false, 0);
+ }
+
+ let mut iterable = list.iter().peekable();
+ let mut range_end = 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<u32>) -> String {
+ let mut supported: Vec<u32> = supported_set.iter()
+ .map(|x| *x)
+ .collect();
+ supported.sort();
+
+ let mut final_output: Vec<String> = 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<u32>` 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<HashMap<String, HashSet<u32>>, &'static str> {
+ let protocols = &protocol_string.split(" ").collect::<Vec<&'a str>>()[..];
+
+ let mut parsed: HashMap<String, HashSet<u32>> = HashMap::new();
+
+ for subproto in protocols {
+ let mut parts = subproto.splitn(2, "=");
+
+ let name = match parts.next() {
+ Some(n) => n,
+ None => return Err("invalid protover entry"),
+ };
+
+ let vers = 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<String>,
+ 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<String, HashMap<u32, usize>> = 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<String, HashSet<u32>> =
+ 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<u32, usize> =
+ 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<String, String> =
+ 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, String>) -> 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<Proto, HashSet<u32>>;
+
+ 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<u32> = HashSet::new();
+ versions.insert(1);
+ assert_eq!(Ok(versions), get_versions("1"));
+ }
+ {
+ let mut versions: HashSet<u32> = HashSet::new();
+ versions.insert(1);
+ versions.insert(2);
+ assert_eq!(Ok(versions), get_versions("1,2"));
+ }
+ {
+ let mut versions: HashSet<u32> = HashSet::new();
+ versions.insert(1);
+ versions.insert(2);
+ versions.insert(3);
+ assert_eq!(Ok(versions), get_versions("1-3"));
+ }
+ {
+ let mut versions: HashSet<u32> = HashSet::new();
+ versions.insert(1);
+ versions.insert(2);
+ versions.insert(5);
+ assert_eq!(Ok(versions), get_versions("1-2,5"));
+ }
+ {
+ let mut versions: HashSet<u32> = 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::<u32>::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::<u32>::new();
+ versions.insert(1);
+ versions.insert(3);
+ assert_eq!(String::from("1,3"), contract_protocol_list(&versions));
+ }
+
+ {
+ let mut versions = HashSet::<u32>::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::<u32>::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::<u32>::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..f4e394b3e2
--- /dev/null
+++ b/src/rust/protover/tests/protover.rs
@@ -0,0 +1,291 @@
+// Copyright (c) 2016-2017, The Tor Project, Inc. */
+// See LICENSE for licensing information */
+
+extern crate protover;
+
+#[test]
+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);
+ assert_eq!("", &unsupported);
+}
+
+#[test]
+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);
+ assert_eq!("", &unsupported);
+}
+
+#[test]
+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);
+ assert_eq!("", &unsupported);
+}
+
+#[test]
+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);
+ assert_eq!("", &unsupported);
+}
+
+#[test]
+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);
+ assert_eq!("", &unsupported);
+}
+
+
+#[test]
+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);
+ assert_eq!("", &unsupported);
+}
+
+
+#[test]
+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);
+ 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_for_unsupported() {
+ 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..14a8148315
--- /dev/null
+++ b/src/rust/smartlist/lib.rs
@@ -0,0 +1,8 @@
+// Copyright (c) 2016-2017, The Tor Project, Inc. */
+// See LICENSE for licensing information */
+
+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..ec5d7a57f5
--- /dev/null
+++ b/src/rust/smartlist/smartlist.rs
@@ -0,0 +1,115 @@
+// 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;
+
+/// 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, as this module
+/// serves purely to translate smartlists as defined in tor to vectors in Rust.
+pub trait Smartlist<T> {
+ fn get_list(&self) -> Vec<T>;
+}
+
+#[repr(C)]
+pub struct Stringlist {
+ pub list: *const *const c_char,
+ pub num_used: c_int,
+ pub capacity: c_int,
+}
+
+impl Smartlist<String> for Stringlist {
+ fn get_list(&self) -> Vec<String> {
+ let empty: Vec<String> = Vec::new();
+ let mut rust_list: Vec<String> = Vec::new();
+
+ 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.
+ 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() {
+ 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));
+ }
+
+ 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]
+ 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();
+
+ 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,
+ capacity: 2,
+ };
+
+ let data = sl.get_list();
+ assert_eq!("a", &data[0]);
+ assert_eq!("b", &data[1]);
+ }
+ }
+}
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/lib.rs b/src/rust/tor_allocate/lib.rs
new file mode 100644
index 0000000000..937a5dcf63
--- /dev/null
+++ b/src/rust/tor_allocate/lib.rs
@@ -0,0 +1,15 @@
+// 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.
+//!
+//! 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..359df1cd7a
--- /dev/null
+++ b/src/rust/tor_allocate/tor_allocate.rs
@@ -0,0 +1,97 @@
+// Copyright (c) 2016-2017, The Tor Project, Inc. */
+// See LICENSE for licensing information */
+
+use libc::{c_char, c_void};
+use std::{ptr, slice, mem};
+
+#[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)]
+unsafe extern "C" fn tor_malloc_(size: usize) -> *mut c_void {
+ use libc::malloc;
+ 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.
+///
+/// # Returns
+///
+/// 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: &[u8] = src.as_bytes();
+
+ let size = mem::size_of_val::<[u8]>(bytes);
+ let size_one_byte = mem::size_of::<u8>();
+
+ // 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 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) };
+ 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) };
+ }
+}
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..90f37a9f1b
--- /dev/null
+++ b/src/rust/tor_rust/include.am
@@ -0,0 +1,24 @@
+EXTRA_DIST +=\
+ src/rust/tor_rust/Cargo.toml \
+ src/rust/tor_rust/lib.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) )
+
+distclean-rust:
+ ( 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) clean --quiet $(CARGO_ONLINE) )
+ rm -rf "$(abs_top_builddir)/src/rust/registry"
+
+if USE_RUST
+build-rust: src/rust/target/release/@TOR_RUST_STATIC_NAME@
+else
+build-rust:
+endif
+
+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/Cargo.toml b/src/rust/tor_util/Cargo.toml
index f175fbdfb0..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 = "*"
+libc = "0.2.22"
diff --git a/src/rust/tor_util/ffi.rs b/src/rust/tor_util/ffi.rs
index af4bfc41af..5c3cdba4be 100644
--- a/src/rust/tor_util/ffi.rs
+++ b/src/rust/tor_util/ffi.rs
@@ -1,56 +1,26 @@
-//! FFI functions, only to be called from C.
-//!
-//! Equivalent C versions of these live in `src/common/compat_rust.c`
-
-use std::mem::forget;
-use std::ffi::CString;
+// Copyright (c) 2016-2017, The Tor Project, Inc. */
+// See LICENSE for licensing information */
-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).
-}
+//! FFI functions to announce Rust support during tor startup, only to be
+//! called from C.
+//!
-/// 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 encounter.",
+ );
+ allocate_and_copy_string(&rust_welcome)
}
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:
diff --git a/src/rust/tor_util/lib.rs b/src/rust/tor_util/lib.rs
index 79d583d1ae..42fa9d5ad0 100644
--- a/src/rust/tor_util/lib.rs
+++ b/src/rust/tor_util/lib.rs
@@ -1,13 +1,11 @@
-//! C <-> Rust compatibility helpers and types.
+// Copyright (c) 2016-2017, The Tor Project, Inc. */
+// See LICENSE for licensing information */
+
+//! 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<CString> for RustString {
- /// Constructs a new `RustString`
- ///
- /// # Examples
- /// ```
- /// # use tor_util::RustString;
- /// use std::ffi::CString;
- ///
- /// let r = RustString::from(CString::new("asdf").unwrap());
- /// ```
- fn from(str: CString) -> RustString {
- RustString(str.into_raw())
- }
-}
-
-impl Into<CString> for RustString {
- /// Reconstructs a `CString` from this `RustString`.
- ///
- /// Useful to take ownership back from a `RustString` that was given to C
- /// code.
- ///
- /// # Examples
- /// ```
- /// # use tor_util::RustString;
- /// use std::ffi::CString;
- ///
- /// let cs = CString::new("asdf").unwrap();
- /// let r = RustString::from(cs.clone());
- /// let cs2 = r.into();
- /// assert_eq!(cs, cs2);
- /// ```
- fn into(self) -> CString {
- // Calling from_raw is always OK here: We only construct self using
- // valid CStrings and don't expose anything that could mutate it
- let ret = unsafe { CString::from_raw(self.0) };
- forget(self);
- ret
- }
-}
-
-impl Drop for RustString {
- fn drop(&mut self) {
- // Don't use into() here, because we would need to move out of
- // self. Same safety consideration. Immediately drop the created
- // CString, which takes care of freeing the wrapped string.
- unsafe { CString::from_raw(self.0) };
- }
-}
-
-#[cfg(test)]
-mod test {
- use std::mem;
- use super::*;
-
- use libc;
-
- /// Ensures we're not adding overhead by using RustString.
- #[test]
- fn size_of() {
- assert_eq!(mem::size_of::<*mut libc::c_char>(),
- mem::size_of::<RustString>())
- }
-}
diff --git a/src/rust/tor_util/tests/rust_string.rs b/src/rust/tor_util/tests/rust_string.rs
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/bench.c b/src/test/bench.c
index b7b123eee2..f30b609900 100644
--- a/src/test/bench.c
+++ b/src/test/bench.c
@@ -3,11 +3,6 @@
* Copyright (c) 2007-2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
-extern const char tor_git_revision[];
-/* Ordinarily defined in tor_main.c; this bit is just here to provide one
- * since we're not linking to tor_main.c */
-const char tor_git_revision[] = "";
-
/**
* \file bench.c
* \brief Benchmarks for lower level Tor modules.
diff --git a/src/test/fuzz/fuzzing_common.c b/src/test/fuzz/fuzzing_common.c
index 7ebddde1a8..d5c71859b5 100644
--- a/src/test/fuzz/fuzzing_common.c
+++ b/src/test/fuzz/fuzzing_common.c
@@ -9,9 +9,6 @@
#include "crypto.h"
#include "crypto_ed25519.h"
-extern const char tor_git_revision[];
-const char tor_git_revision[] = "";
-
static or_options_t *mock_options = NULL;
static const or_options_t *
mock_get_options(void)
diff --git a/src/test/include.am b/src/test/include.am
index 610918e9da..bbf8a370e2 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -152,7 +152,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-memwipe.c b/src/test/test-memwipe.c
index 484f13dd05..89d946d506 100644
--- a/src/test/test-memwipe.c
+++ b/src/test/test-memwipe.c
@@ -1,3 +1,6 @@
+/* Copyright (c) 2015-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
#include "orconfig.h"
#include <string.h>
#include <stdio.h>
diff --git a/src/test/test.c b/src/test/test.c
index da4f7a0553..00857c2386 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -1227,7 +1227,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 634f3e7b02..b1a3366a80 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -239,7 +239,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_accounting.c b/src/test/test_accounting.c
index 7edba988a6..b0d37b2989 100644
--- a/src/test/test_accounting.c
+++ b/src/test/test_accounting.c
@@ -1,3 +1,6 @@
+/* Copyright (c) 2014-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
#include "or.h"
#include "test.h"
#define HIBERNATE_PRIVATE
diff --git a/src/test/test_channelpadding.c b/src/test/test_channelpadding.c
index d5713688a0..4346ee343f 100644
--- a/src/test/test_channelpadding.c
+++ b/src/test/test_channelpadding.c
@@ -1,3 +1,6 @@
+/* Copyright (c) 2016-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
#define TOR_CHANNEL_INTERNAL_
#define MAIN_PRIVATE
#define NETWORKSTATUS_PRIVATE
diff --git a/src/test/test_config.c b/src/test/test_config.c
index 456d8bcc3e..79f72fca1f 100644
--- a/src/test/test_config.c
+++ b/src/test/test_config.c
@@ -4833,7 +4833,7 @@ test_config_include_limit(void *data)
torrc_path);
tt_int_op(write_str_to_file(torrc_path, torrc_contents, 0), OP_EQ, 0);
- tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL),
+ tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL, NULL),
OP_EQ, -1);
done:
@@ -4863,7 +4863,7 @@ test_config_include_does_not_exist(void *data)
tor_snprintf(torrc_contents, sizeof(torrc_contents), "%%include %s",
missing_path);
- tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL),
+ tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL, NULL),
OP_EQ, -1);
done:
@@ -4895,7 +4895,7 @@ test_config_include_error_in_included_file(void *data)
tor_snprintf(torrc_contents, sizeof(torrc_contents), "%%include %s",
invalid_path);
- tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL),
+ tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL, NULL),
OP_EQ, -1);
done:
@@ -4937,8 +4937,8 @@ test_config_include_empty_file_folder(void *data)
folder_path, file_path);
int include_used;
- tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used),
- OP_EQ, 0);
+ tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used,
+ NULL), OP_EQ, 0);
tt_ptr_op(result, OP_EQ, NULL);
tt_int_op(include_used, OP_EQ, 1);
@@ -4975,7 +4975,8 @@ test_config_include_no_permission(void *data)
folder_path);
int include_used;
- tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used),
+ tt_int_op(config_get_lines_include(torrc_contents, &result, 0,
+ &include_used, NULL),
OP_EQ, -1);
tt_ptr_op(result, OP_EQ, NULL);
@@ -5031,8 +5032,8 @@ test_config_include_recursion_before_after(void *data)
}
int include_used;
- tt_int_op(config_get_lines_include(file_contents, &result, 0, &include_used),
- OP_EQ, 0);
+ tt_int_op(config_get_lines_include(file_contents, &result, 0, &include_used,
+ NULL), OP_EQ, 0);
tt_ptr_op(result, OP_NE, NULL);
tt_int_op(include_used, OP_EQ, 1);
@@ -5096,8 +5097,8 @@ test_config_include_recursion_after_only(void *data)
}
int include_used;
- tt_int_op(config_get_lines_include(file_contents, &result, 0, &include_used),
- OP_EQ, 0);
+ tt_int_op(config_get_lines_include(file_contents, &result, 0, &include_used,
+ NULL), OP_EQ, 0);
tt_ptr_op(result, OP_NE, NULL);
tt_int_op(include_used, OP_EQ, 1);
@@ -5185,8 +5186,8 @@ test_config_include_folder_order(void *data)
torrcd);
int include_used;
- tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used),
- OP_EQ, 0);
+ tt_int_op(config_get_lines_include(torrc_contents, &result, 0, &include_used,
+ NULL), OP_EQ, 0);
tt_ptr_op(result, OP_NE, NULL);
tt_int_op(include_used, OP_EQ, 1);
@@ -5239,8 +5240,8 @@ test_config_include_path_syntax(void *data)
esc_dir_with_pathsep);
int include_used;
- tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used),
- OP_EQ, 0);
+ tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used,
+ NULL), OP_EQ, 0);
tt_ptr_op(result, OP_EQ, NULL);
tt_int_op(include_used, OP_EQ, 1);
@@ -5294,14 +5295,14 @@ test_config_include_has_include(void *data)
char torrc_contents[1000] = "Test 1\n";
int include_used;
- tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used),
- OP_EQ, 0);
+ tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used,
+ NULL), OP_EQ, 0);
tt_int_op(include_used, OP_EQ, 0);
config_free_lines(result);
tor_snprintf(torrc_contents, sizeof(torrc_contents), "%%include %s\n", dir);
- tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used),
- OP_EQ, 0);
+ tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used,
+ NULL), OP_EQ, 0);
tt_int_op(include_used, OP_EQ, 1);
done:
@@ -5513,6 +5514,85 @@ test_config_check_bridge_distribution_setting_unrecognised(void *arg)
return;
}
+static void
+test_config_include_opened_file_list(void *data)
+{
+ (void)data;
+
+ config_line_t *result = NULL;
+ smartlist_t *opened_files = smartlist_new();
+ char *dir = tor_strdup(get_fname("test_include_opened_file_list"));
+ tt_ptr_op(dir, OP_NE, NULL);
+
+#ifdef _WIN32
+ tt_int_op(mkdir(dir), OP_EQ, 0);
+#else
+ tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
+#endif
+
+ char torrcd[PATH_MAX+1];
+ tor_snprintf(torrcd, sizeof(torrcd), "%s"PATH_SEPARATOR"%s", dir, "torrc.d");
+
+#ifdef _WIN32
+ tt_int_op(mkdir(torrcd), OP_EQ, 0);
+#else
+ tt_int_op(mkdir(torrcd, 0700), OP_EQ, 0);
+#endif
+
+ char subfolder[PATH_MAX+1];
+ tor_snprintf(subfolder, sizeof(subfolder), "%s"PATH_SEPARATOR"%s", torrcd,
+ "subfolder");
+
+#ifdef _WIN32
+ tt_int_op(mkdir(subfolder), OP_EQ, 0);
+#else
+ tt_int_op(mkdir(subfolder, 0700), OP_EQ, 0);
+#endif
+
+ char path[PATH_MAX+1];
+ tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", subfolder,
+ "01_file_in_subfolder");
+ tt_int_op(write_str_to_file(path, "Test 1\n", 0), OP_EQ, 0);
+
+ char empty[PATH_MAX+1];
+ tor_snprintf(empty, sizeof(empty), "%s"PATH_SEPARATOR"%s", torrcd, "empty");
+ tt_int_op(write_str_to_file(empty, "", 0), OP_EQ, 0);
+
+ char file[PATH_MAX+1];
+ tor_snprintf(file, sizeof(file), "%s"PATH_SEPARATOR"%s", torrcd, "file");
+ tt_int_op(write_str_to_file(file, "Test 2\n", 0), OP_EQ, 0);
+
+ char dot[PATH_MAX+1];
+ tor_snprintf(dot, sizeof(dot), "%s"PATH_SEPARATOR"%s", torrcd, ".dot");
+ tt_int_op(write_str_to_file(dot, "Test 3\n", 0), OP_EQ, 0);
+
+ char torrc_contents[1000];
+ tor_snprintf(torrc_contents, sizeof(torrc_contents),
+ "%%include %s\n",
+ torrcd);
+
+ int include_used;
+ tt_int_op(config_get_lines_include(torrc_contents, &result, 0, &include_used,
+ opened_files), OP_EQ, 0);
+ tt_ptr_op(result, OP_NE, NULL);
+ tt_int_op(include_used, OP_EQ, 1);
+
+ tt_int_op(smartlist_len(opened_files), OP_EQ, 4);
+ tt_int_op(smartlist_contains_string(opened_files, torrcd), OP_EQ, 1);
+ tt_int_op(smartlist_contains_string(opened_files, subfolder), OP_EQ, 1);
+ // files inside subfolders are not opended, only the subfolder is opened
+ tt_int_op(smartlist_contains_string(opened_files, empty), OP_EQ, 1);
+ tt_int_op(smartlist_contains_string(opened_files, file), OP_EQ, 1);
+ // dot files are not opened as we ignore them when we get their name from
+ // their parent folder
+
+ done:
+ SMARTLIST_FOREACH(opened_files, char *, f, tor_free(f));
+ smartlist_free(opened_files);
+ config_free_lines(result);
+ tor_free(dir);
+}
+
#define CONFIG_TEST(name, flags) \
{ #name, test_config_ ## name, flags, NULL, NULL }
@@ -5560,6 +5640,7 @@ struct testcase_t config_tests[] = {
CONFIG_TEST(check_bridge_distribution_setting_valid, 0),
CONFIG_TEST(check_bridge_distribution_setting_invalid, 0),
CONFIG_TEST(check_bridge_distribution_setting_unrecognised, 0),
+ CONFIG_TEST(include_opened_file_list, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_dns.c b/src/test/test_dns.c
index 19dcb02931..66b231b6da 100644
--- a/src/test/test_dns.c
+++ b/src/test/test_dns.c
@@ -1,3 +1,6 @@
+/* Copyright (c) 2015-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
#include "or.h"
#include "test.h"
diff --git a/src/test/test_keygen.sh b/src/test/test_keygen.sh
index 87012cd283..b3d4d8e39a 100755
--- a/src/test/test_keygen.sh
+++ b/src/test/test_keygen.sh
@@ -40,6 +40,12 @@ fi
CASE8=$dflt
CASE9=$dflt
CASE10=$dflt
+ CASE11A=$dflt
+ CASE11B=$dflt
+ CASE11C=$dflt
+ CASE11D=$dflt
+ CASE11E=$dflt
+ CASE11F=$dflt
if [ $# -ge 1 ]; then
eval "CASE${1}"=1
@@ -363,6 +369,109 @@ echo "==== Case 10 ok"
fi
+# Case 11a: -passphrase-fd without --keygen
+
+if [ "$CASE11A" = 1 ]; then
+
+ME="${DATA_DIR}/case11a"
+
+mkdir -p "${ME}/keys"
+
+${TOR} --DataDirectory "${ME}" --passphrase-fd 1 > "${ME}/stdout" && die "Successfully started with passphrase-fd but no keygen?" || true
+
+grep "passphrase-fd specified without --keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments."
+
+echo "==== Case 11A ok"
+
+fi
+
+# Case 11b: --no-passphrase without --keygen
+
+if [ "$CASE11B" = 1 ]; then
+
+ME="${DATA_DIR}/case11b"
+
+mkdir -p "${ME}/keys"
+
+${TOR} --DataDirectory "${ME}" --no-passphrase > "${ME}/stdout" && die "Successfully started with no-passphrase but no keygen?" || true
+
+grep "no-passphrase specified without --keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments."
+
+echo "==== Case 11B ok"
+
+fi
+
+# Case 11c: --newpass without --keygen
+
+if [ "$CASE11C" = 1 ]; then
+
+ME="${DATA_DIR}/case11C"
+
+mkdir -p "${ME}/keys"
+
+${TOR} --DataDirectory "${ME}" --newpass > "${ME}/stdout" && die "Successfully started with newpass but no keygen?" || true
+
+grep "newpass specified without --keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments."
+
+echo "==== Case 11C ok"
+
+fi
+
+######## --master-key does not work yet, but this will test the error case
+######## when it does.
+#
+# Case 11d: --master-key without --keygen
+#
+if [ "$CASE11D" = 1 ]; then
+#
+# ME="${DATA_DIR}/case11d"
+#
+# mkdir -p "${ME}/keys"
+#
+# ${TOR} --DataDirectory "${ME}" --master-key "${ME}/foobar" > "${ME}/stdout" && die "Successfully started with master-key but no keygen?" || true
+#
+# cat "${ME}/stdout"
+#
+# grep "master-key without --keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments."
+
+ echo "==== Case 11D skipped"
+
+fi
+
+
+# Case 11E: Silly passphrase-fd
+
+if [ "$CASE11E" = 1 ]; then
+
+ME="${DATA_DIR}/case11E"
+
+mkdir -p "${ME}/keys"
+
+${TOR} --DataDirectory "${ME}" --keygen --passphrase-fd ewigeblumenkraft > "${ME}/stdout" && die "Successfully started with bogus passphrase-fd?" || true
+
+grep "Invalid --passphrase-fd value" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments."
+
+echo "==== Case 11E ok"
+
+fi
+
+
+# Case 11F: --no-passphrase with --passphrase-fd
+
+if [ "$CASE11F" = 1 ]; then
+
+ME="${DATA_DIR}/case11F"
+
+mkdir -p "${ME}/keys"
+
+${TOR} --DataDirectory "${ME}" --keygen --passphrase-fd 1 --no-passphrase > "${ME}/stdout" && die "Successfully started with bogus passphrase-fd combination?" || true
+
+grep "no-passphrase specified with --passphrase-fd" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments."
+
+echo "==== Case 11F ok"
+
+fi
+
# Check cert-only.
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
};
diff --git a/src/test/test_routerlist.c b/src/test/test_routerlist.c
index 3b0e943ce5..05d0820d83 100644
--- a/src/test/test_routerlist.c
+++ b/src/test/test_routerlist.c
@@ -6,13 +6,17 @@
#include <time.h>
#define DIRVOTE_PRIVATE
+#define ENTRYNODES_PRIVATE
+#define DIRECTORY_PRIVATE
#define NETWORKSTATUS_PRIVATE
+#define CONNECTION_PRIVATE
#define ROUTERLIST_PRIVATE
#define TOR_UNIT_TESTING
#include "or.h"
#include "config.h"
#include "connection.h"
#include "container.h"
+#include "control.h"
#include "directory.h"
#include "dirvote.h"
#include "entrynodes.h"
@@ -22,10 +26,13 @@
#include "policies.h"
#include "router.h"
#include "routerlist.h"
+#include "routerset.h"
#include "routerparse.h"
#include "shared_random.h"
+#include "statefile.h"
#include "test.h"
#include "test_dir_common.h"
+#include "log_test_helpers.h"
void construct_consensus(char **consensus_text_md);
@@ -411,6 +418,109 @@ test_router_pick_directory_server_impl(void *arg)
networkstatus_vote_free(con_md);
}
+static or_state_t *dummy_state = NULL;
+static or_state_t *
+get_or_state_replacement(void)
+{
+ return dummy_state;
+}
+
+static void
+mock_directory_initiate_request(directory_request_t *req)
+{
+ (void)req;
+ return;
+}
+
+static circuit_guard_state_t *
+mock_circuit_guard_state_new(entry_guard_t *guard, unsigned state,
+ entry_guard_restriction_t *rst)
+{
+ (void) guard;
+ (void) state;
+ (void) rst;
+ return NULL;
+}
+
+/** Test that we will use our directory guards to fetch mds even if we don't
+ * have any dirinfo (tests bug #23862). */
+static void
+test_directory_guard_fetch_with_no_dirinfo(void *arg)
+{
+ int retval;
+ char *consensus_text_md = NULL;
+ or_options_t *options = get_options_mutable();
+
+ (void) arg;
+
+ /* Initialize the SRV subsystem */
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+ mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ sr_init(0);
+ UNMOCK(get_my_v3_authority_cert);
+
+ /* Initialize the entry node configuration from the ticket */
+ options->UseEntryGuards = 1;
+ options->StrictNodes = 1;
+ get_options_mutable()->EntryNodes = routerset_new();
+ routerset_parse(get_options_mutable()->EntryNodes,
+ "2121212121212121212121212121212121212121", "foo");
+
+ /* Mock some functions */
+ dummy_state = tor_malloc_zero(sizeof(or_state_t));
+ MOCK(get_or_state, get_or_state_replacement);
+ MOCK(directory_initiate_request, mock_directory_initiate_request);
+ /* we need to mock this one to avoid memleaks */
+ MOCK(circuit_guard_state_new, mock_circuit_guard_state_new);
+
+ /* Call guards_update_all() to simulate loading our state file (see
+ * entry_guards_load_guards_from_state() and ticket #23989). */
+ guards_update_all();
+
+ /* Test logic: Simulate the arrival of a new consensus when we have no
+ * dirinfo at all. Tor will need to fetch the mds from the consensus. Make
+ * sure that Tor will use the specified entry guard instead of relying on the
+ * fallback directories. */
+
+ /* Fixup the dirconn that will deliver the consensus */
+ dir_connection_t *conn = dir_connection_new(AF_INET);
+ tor_addr_from_ipv4h(&conn->base_.addr, 0x7f000001);
+ conn->base_.port = 8800;
+ TO_CONN(conn)->address = tor_strdup("127.0.0.1");
+ conn->base_.purpose = DIR_PURPOSE_FETCH_CONSENSUS;
+ conn->requested_resource = tor_strdup("ns");
+
+ /* Construct a consensus */
+ construct_consensus(&consensus_text_md);
+ tt_assert(consensus_text_md);
+
+ /* Place the consensus in the dirconn */
+ response_handler_args_t args;
+ memset(&args, 0, sizeof(response_handler_args_t));
+ args.status_code = 200;
+ args.body = consensus_text_md;
+ args.body_len = strlen(consensus_text_md);
+
+ /* Update approx time so that the consensus is considered live */
+ update_approx_time(time(NULL)+1010);
+
+ setup_capture_of_logs(LOG_DEBUG);
+
+ /* Now handle the consensus */
+ retval = handle_response_fetch_consensus(conn, &args);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Make sure that our primary guard was chosen */
+ expect_log_msg_containing("Selected primary guard router3");
+
+ done:
+ tor_free(consensus_text_md);
+ tor_free(dummy_state);
+ connection_free_(TO_CONN(conn));
+ entry_guards_free_all();
+ teardown_capture_of_logs();
+}
+
static connection_t *mocked_connection = NULL;
/* Mock connection_get_by_type_addr_port_purpose by returning
@@ -494,6 +604,8 @@ struct testcase_t routerlist_tests[] = {
NODE(launch_descriptor_downloads, 0),
NODE(router_is_already_dir_fetching, TT_FORK),
ROUTER(pick_directory_server_impl, TT_FORK),
+ { "directory_guard_fetch_with_no_dirinfo",
+ test_directory_guard_fetch_with_no_dirinfo, TT_FORK, NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_routerset.c b/src/test/test_routerset.c
index c9c69911da..6da2275c35 100644
--- a/src/test/test_routerset.c
+++ b/src/test/test_routerset.c
@@ -1,3 +1,6 @@
+/* Copyright (c) 2014-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
#define ROUTERSET_PRIVATE
#include "or.h"
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..8d7900e1df 100755
--- a/src/test/test_rust.sh
+++ b/src/test/test_rust.sh
@@ -1,13 +1,16 @@
#!/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
+set -e
+
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
+ 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
diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c
index a9d58e6b8b..80494f64c0 100644
--- a/src/test/test_shared_random.c
+++ b/src/test/test_shared_random.c
@@ -1,3 +1,6 @@
+/* Copyright (c) 2016-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
#define SHARED_RANDOM_PRIVATE
#define SHARED_RANDOM_STATE_PRIVATE
#define CONFIG_PRIVATE
diff --git a/src/test/test_status.c b/src/test/test_status.c
index f86f8e3b9e..50ea203e4d 100644
--- a/src/test/test_status.c
+++ b/src/test/test_status.c
@@ -1,3 +1,6 @@
+/* Copyright (c) 2014-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
#define STATUS_PRIVATE
#define HIBERNATE_PRIVATE
#define LOG_PRIVATE
diff --git a/src/test/testing_common.c b/src/test/testing_common.c
index 7e9c47b48d..e43fa46c84 100644
--- a/src/test/testing_common.c
+++ b/src/test/testing_common.c
@@ -3,12 +3,6 @@
* Copyright (c) 2007-2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
-extern const char tor_git_revision[];
-
-/* Ordinarily defined in tor_main.c; this bit is just here to provide one
- * since we're not linking to tor_main.c */
-const char tor_git_revision[] = "";
-
/**
* \file test_common.c
* \brief Common pieces to implement unit tests.
diff --git a/src/tools/include.am b/src/tools/include.am
index 37936c742f..92cc3f10a2 100644
--- a/src/tools/include.am
+++ b/src/tools/include.am
@@ -45,3 +45,8 @@ src_tools_tor_cov_gencert_LDADD = src/common/libor-testing.a \
endif
EXTRA_DIST += src/tools/tor-fw-helper/README
+
+if BUILD_LIBTORRUNNER
+noinst_LIBRARIES += src/tools/libtorrunner.a
+src_tools_libtorrunner_a_SOURCES = src/tools/tor_runner.c src/or/tor_api.c
+endif
diff --git a/src/tools/tor_runner.c b/src/tools/tor_runner.c
new file mode 100644
index 0000000000..9ed2ee5775
--- /dev/null
+++ b/src/tools/tor_runner.c
@@ -0,0 +1,101 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file tor_runner.c
+ * @brief Experimental module to emulate tor_run_main() API with fork+exec
+ *
+ * The functions here are meant to allow the application developer to
+ * use the tor_run_main() API without having to care whether Tor is
+ * running in-process or out-of-process. For in-process usage, the
+ * developer can link Tor as a library and call tor_run_main(); for
+ * out-of-process usage, the developer can link this library instead.
+ *
+ * This interface is EXPERIMENTAL; please let us know if you would like
+ * to depend on it. We don't know yet whether it will be reliable in
+ * practice.
+ */
+
+/* NOTE: This module is supposed to work without the standard Tor utility
+ * functions. Don't add more dependencies!
+ */
+
+#include "tor_api.h"
+#include "tor_api_internal.h"
+
+#include "orconfig.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef __GNUC__
+#define __attribute__(x)
+#endif
+
+static void child(const tor_main_configuration_t *cfg)
+ __attribute__((noreturn));
+
+int
+tor_run_main(const tor_main_configuration_t *cfg)
+{
+ pid_t pid = fork();
+ if (pid == 0) {
+ child(cfg);
+ exit(0); /* Unreachable */
+ }
+
+ pid_t stopped_pid;
+ int status = 0;
+ do {
+ stopped_pid = waitpid(pid, &status, 0);
+ } while (stopped_pid == -1);
+
+ /* Note: these return values are not documented. No return value is
+ * documented! */
+
+ if (stopped_pid != pid) {
+ return -99999;
+ }
+ if (WIFSTOPPED(status)) {
+ return WEXITSTATUS(status);
+ }
+ if (WIFSIGNALED(status)) {
+ return -WTERMSIG(status);
+ }
+
+ return -999988;
+}
+
+/* circumlocution to avoid getting warned about calling calloc instead of
+ * tor_calloc. */
+#define real_calloc calloc
+
+static void
+child(const tor_main_configuration_t *cfg)
+{
+ /* XXXX Close unused file descriptors. */
+
+ char **args = real_calloc(cfg->argc+1, sizeof(char *));
+ memcpy(args, cfg->argv, cfg->argc * sizeof(char *));
+ args[cfg->argc] = NULL;
+
+ int rv = execv(BINDIR "/tor", args);
+
+ if (rv < 0) {
+ exit(254);
+ } else {
+ abort(); /* Unreachable */
+ }
+}
+
diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h
index 38c6d5118e..5e3c5d87fe 100644
--- a/src/win32/orconfig.h
+++ b/src/win32/orconfig.h
@@ -218,7 +218,7 @@
#define USING_TWOS_COMPLEMENT
/* Version number of package */
-#define VERSION "0.3.2.3-alpha-dev"
+#define VERSION "0.3.3.0-alpha-dev"