aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/address.c3
-rw-r--r--src/common/backtrace.c205
-rw-r--r--src/common/backtrace.h12
-rw-r--r--src/common/compat.c177
-rw-r--r--src/common/compat.h29
-rw-r--r--src/common/compat_libevent.c8
-rw-r--r--src/common/compat_libevent.h1
-rw-r--r--src/common/container.c19
-rw-r--r--src/common/container.h1
-rw-r--r--src/common/crypto.c167
-rw-r--r--src/common/crypto.h17
-rw-r--r--src/common/crypto_curve25519.c2
-rw-r--r--src/common/crypto_curve25519.h5
-rw-r--r--src/common/crypto_format.c1
-rw-r--r--src/common/include.am45
-rw-r--r--src/common/log.c130
-rw-r--r--src/common/sandbox.c1493
-rw-r--r--src/common/sandbox.h247
-rw-r--r--src/common/testsupport.h80
-rw-r--r--src/common/torgzip.c16
-rw-r--r--src/common/torgzip.h6
-rw-r--r--src/common/torlog.h13
-rw-r--r--src/common/tortls.c158
-rw-r--r--src/common/util.c249
-rw-r--r--src/common/util.h72
-rwxr-xr-xsrc/config/deanonymind.py27
-rw-r--r--src/config/geoip-manual108
-rw-r--r--src/config/torrc.sample.in11
-rw-r--r--src/ext/eventdns.c9
-rw-r--r--src/or/addressmap.c2
-rw-r--r--src/or/addressmap.h6
-rw-r--r--src/or/buffers.c61
-rw-r--r--src/or/buffers.h12
-rw-r--r--src/or/channel.c81
-rw-r--r--src/or/channel.h7
-rw-r--r--src/or/channeltls.c41
-rw-r--r--src/or/circpathbias.c1538
-rw-r--r--src/or/circpathbias.h29
-rw-r--r--src/or/circuitbuild.c1567
-rw-r--r--src/or/circuitbuild.h10
-rw-r--r--src/or/circuitlist.c268
-rw-r--r--src/or/circuitlist.h16
-rw-r--r--src/or/circuitmux.c171
-rw-r--r--src/or/circuitmux.h19
-rw-r--r--src/or/circuitstats.c120
-rw-r--r--src/or/circuitstats.h63
-rw-r--r--src/or/circuituse.c85
-rw-r--r--src/or/command.c28
-rw-r--r--src/or/command.h2
-rw-r--r--src/or/config.c963
-rw-r--r--src/or/config.h50
-rw-r--r--src/or/confparse.c89
-rw-r--r--src/or/confparse.h9
-rw-r--r--src/or/connection.c504
-rw-r--r--src/or/connection.h27
-rw-r--r--src/or/connection_edge.c5
-rw-r--r--src/or/connection_edge.h6
-rw-r--r--src/or/connection_or.c90
-rw-r--r--src/or/connection_or.h3
-rw-r--r--src/or/control.c465
-rw-r--r--src/or/control.h105
-rw-r--r--src/or/directory.c114
-rw-r--r--src/or/directory.h5
-rw-r--r--src/or/dirserv.c48
-rw-r--r--src/or/dirserv.h24
-rw-r--r--src/or/dirvote.c27
-rw-r--r--src/or/dirvote.h14
-rw-r--r--src/or/dns.c17
-rw-r--r--src/or/entrynodes.c121
-rw-r--r--src/or/entrynodes.h14
-rw-r--r--src/or/ext_orport.c648
-rw-r--r--src/or/ext_orport.h42
-rw-r--r--src/or/geoip.c241
-rw-r--r--src/or/geoip.h12
-rw-r--r--src/or/hibernate.c9
-rw-r--r--src/or/hibernate.h3
-rw-r--r--src/or/include.am34
-rw-r--r--src/or/main.c266
-rw-r--r--src/or/main.h15
-rw-r--r--src/or/microdesc.c9
-rw-r--r--src/or/networkstatus.c17
-rw-r--r--src/or/nodelist.c31
-rw-r--r--src/or/ntmain.c2
-rw-r--r--src/or/onion.c5
-rw-r--r--src/or/onion_fast.c2
-rw-r--r--src/or/or.h275
-rw-r--r--src/or/relay.c299
-rw-r--r--src/or/relay.h13
-rw-r--r--src/or/rendclient.c3
-rw-r--r--src/or/rendservice.c7
-rw-r--r--src/or/rephist.c50
-rw-r--r--src/or/replaycache.c27
-rw-r--r--src/or/replaycache.h10
-rw-r--r--src/or/router.c5
-rw-r--r--src/or/router.h4
-rw-r--r--src/or/routerlist.c70
-rw-r--r--src/or/routerlist.h9
-rw-r--r--src/or/routerparse.c23
-rw-r--r--src/or/statefile.c45
-rw-r--r--src/or/statefile.h6
-rw-r--r--src/or/status.c33
-rw-r--r--src/or/transports.c201
-rw-r--r--src/or/transports.h26
-rw-r--r--src/test/Makefile.nmake6
-rw-r--r--src/test/bench.c4
-rwxr-xr-xsrc/test/bt_test.py42
-rw-r--r--src/test/include.am56
-rwxr-xr-x[-rw-r--r--]src/test/ntor_ref.py16
-rwxr-xr-xsrc/test/test-network.sh47
-rw-r--r--src/test/test.c940
-rw-r--r--src/test/test_addr.c102
-rw-r--r--src/test/test_bt_cl.c109
-rw-r--r--src/test/test_buffers.c342
-rw-r--r--src/test/test_cell_queue.c148
-rw-r--r--src/test/test_circuitlist.c168
-rw-r--r--src/test/test_circuitmux.c84
-rwxr-xr-xsrc/test/test_cmdline_args.py273
-rw-r--r--src/test/test_config.c384
-rw-r--r--src/test/test_containers.c59
-rw-r--r--src/test/test_controller_events.c298
-rw-r--r--src/test/test_crypto.c92
-rw-r--r--src/test/test_data.c185
-rw-r--r--src/test/test_dir.c78
-rw-r--r--src/test/test_extorport.c604
-rw-r--r--src/test/test_logging.c135
-rw-r--r--src/test/test_options.c170
-rw-r--r--src/test/test_pt.c343
-rw-r--r--src/test/test_replay.c192
-rw-r--r--src/test/test_socks.c393
-rw-r--r--src/test/test_util.c425
-rw-r--r--src/tools/tor-checkkey.c2
-rw-r--r--src/tools/tor-fw-helper/tor-fw-helper.c2
-rw-r--r--src/tools/tor-gencert.c4
-rw-r--r--src/win32/orconfig.h2
134 files changed, 14223 insertions, 4061 deletions
diff --git a/src/common/address.c b/src/common/address.c
index 14a7b6bc9..b9f2d9315 100644
--- a/src/common/address.c
+++ b/src/common/address.c
@@ -14,6 +14,7 @@
#include "address.h"
#include "torlog.h"
#include "container.h"
+#include "sandbox.h"
#ifdef _WIN32
#include <process.h>
@@ -234,7 +235,7 @@ tor_addr_lookup(const char *name, uint16_t family, tor_addr_t *addr)
memset(&hints, 0, sizeof(hints));
hints.ai_family = family;
hints.ai_socktype = SOCK_STREAM;
- err = getaddrinfo(name, NULL, &hints, &res);
+ err = sandbox_getaddrinfo(name, NULL, &hints, &res);
if (!err) {
best = NULL;
for (res_p = res; res_p; res_p = res_p->ai_next) {
diff --git a/src/common/backtrace.c b/src/common/backtrace.c
new file mode 100644
index 000000000..5049298a1
--- /dev/null
+++ b/src/common/backtrace.c
@@ -0,0 +1,205 @@
+/* Copyright (c) 2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define __USE_GNU
+#define _GNU_SOURCE 1
+
+#include "orconfig.h"
+#include "backtrace.h"
+#include "compat.h"
+#include "util.h"
+#include "torlog.h"
+
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#ifdef HAVE_CYGWIN_SIGNAL_H
+#include <cygwin/signal.h>
+#elif defined(HAVE_SYS_UCONTEXT_H)
+#include <sys/ucontext.h>
+#elif defined(HAVE_UCONTEXT_H)
+#include <ucontext.h>
+#endif
+
+#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && \
+ defined(HAVE_BACKTRACE_SYMBOLS_FD) && defined(HAVE_SIGACTION)
+#define USE_BACKTRACE
+#endif
+
+#if !defined(USE_BACKTRACE)
+#define NO_BACKTRACE_IMPL
+#endif
+
+/** Version of Tor to report in backtrace messages. */
+static char *bt_version = NULL;
+
+#ifdef USE_BACKTRACE
+/** Largest stack depth to try to dump. */
+#define MAX_DEPTH 256
+/** Static allocation of stack to dump. This is static so we avoid stack
+ * pressure. */
+static void *cb_buf[MAX_DEPTH];
+
+/** Change a stacktrace in <b>stack</b> of depth <b>depth</b> so that it will
+ * log the correct function from which a signal was received with context
+ * <b>ctx</b>. (When we get a signal, the current function will not have
+ * called any other function, and will therefore have not pushed its address
+ * onto the stack. Fortunately, we usually have the program counter in the
+ * ucontext_t structure.
+ */
+static void
+clean_backtrace(void **stack, int depth, const ucontext_t *ctx)
+{
+#ifdef PC_FROM_UCONTEXT
+#if defined(__linux__)
+ const int n = 1;
+#elif defined(__darwin__) || defined(__APPLE__) || defined(__OpenBSD__) \
+ || defined(__FreeBSD__)
+ const int n = 2;
+#else
+ const int n = 1;
+#endif
+ if (depth <= n)
+ return;
+
+ stack[n] = (void*) ctx->PC_FROM_UCONTEXT;
+#else
+ (void) depth;
+ (void) ctx;
+#endif
+}
+
+/** Log a message <b>msg</b> at <b>severity</b> in <b>domain</b>, and follow
+ * that with a backtrace log. */
+void
+log_backtrace(int severity, int domain, const char *msg)
+{
+ int depth = backtrace(cb_buf, MAX_DEPTH);
+ char **symbols = backtrace_symbols(cb_buf, depth);
+ int i;
+ tor_log(severity, domain, "%s. Stack trace:", msg);
+ if (!symbols) {
+ tor_log(severity, domain, " Unable to generate backtrace.");
+ return;
+ }
+ for (i=0; i < depth; ++i) {
+ tor_log(severity, domain, " %s", symbols[i]);
+ }
+ free(symbols);
+}
+
+static void crash_handler(int sig, siginfo_t *si, void *ctx_)
+ __attribute__((noreturn));
+
+/** Signal handler: write a crash message with a stack trace, and die. */
+static void
+crash_handler(int sig, siginfo_t *si, void *ctx_)
+{
+ char buf[40];
+ int depth;
+ ucontext_t *ctx = (ucontext_t *) ctx_;
+ int n_fds, i;
+ const int *fds = NULL;
+
+ (void) si;
+
+ depth = backtrace(cb_buf, MAX_DEPTH);
+ /* Clean up the top stack frame so we get the real function
+ * name for the most recently failing function. */
+ clean_backtrace(cb_buf, depth, ctx);
+
+ format_dec_number_sigsafe((unsigned)sig, buf, sizeof(buf));
+
+ tor_log_err_sigsafe(bt_version, " died: Caught signal ", buf, "\n",
+ NULL);
+
+ n_fds = tor_log_get_sigsafe_err_fds(&fds);
+ for (i=0; i < n_fds; ++i)
+ backtrace_symbols_fd(cb_buf, depth, fds[i]);
+
+ abort();
+}
+
+/** Install signal handlers as needed so that when we crash, we produce a
+ * useful stack trace. Return 0 on success, -1 on failure. */
+static int
+install_bt_handler(void)
+{
+ int trap_signals[] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGSYS,
+ SIGIO, -1 };
+ int i, rv=0;
+
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = crash_handler;
+ sa.sa_flags = SA_SIGINFO;
+ sigfillset(&sa.sa_mask);
+
+ for (i = 0; trap_signals[i] >= 0; ++i) {
+ if (sigaction(trap_signals[i], &sa, NULL) == -1) {
+ log_warn(LD_BUG, "Sigaction failed: %s", strerror(errno));
+ rv = -1;
+ }
+ }
+ return rv;
+}
+
+/** Uninstall crash handlers. */
+static void
+remove_bt_handler(void)
+{
+ /* We don't need to actually free anything at exit here. */
+}
+#endif
+
+#ifdef NO_BACKTRACE_IMPL
+void
+log_backtrace(int severity, int domain, const char *msg)
+{
+ tor_log(severity, domain, "%s. (Stack trace not available)", msg);
+}
+
+static int
+install_bt_handler(void)
+{
+ return 0;
+}
+
+static void
+remove_bt_handler(void)
+{
+}
+#endif
+
+/** Set up code to handle generating error messages on crashes. */
+int
+configure_backtrace_handler(const char *tor_version)
+{
+ tor_free(bt_version);
+ if (!tor_version)
+ tor_version = "";
+ tor_asprintf(&bt_version, "Tor %s", tor_version);
+
+ return install_bt_handler();
+}
+
+/** Perform end-of-process cleanup for code that generates error messages on
+ * crashes. */
+void
+clean_up_backtrace_handler(void)
+{
+ remove_bt_handler();
+
+ tor_free(bt_version);
+}
+
diff --git a/src/common/backtrace.h b/src/common/backtrace.h
new file mode 100644
index 000000000..765436fee
--- /dev/null
+++ b/src/common/backtrace.h
@@ -0,0 +1,12 @@
+/* Copyright (c) 2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_BACKTRACE_H
+#define TOR_BACKTRACE_H
+
+void log_backtrace(int severity, int domain, const char *msg);
+int configure_backtrace_handler(const char *tor_version);
+void clean_up_backtrace_handler(void);
+
+#endif
+
diff --git a/src/common/compat.c b/src/common/compat.c
index d88c5f92d..8e2619f84 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -23,6 +23,7 @@
* we can also take out the configure check. */
#define _GNU_SOURCE
+#define COMPAT_PRIVATE
#include "compat.h"
#ifdef _WIN32
@@ -109,6 +110,7 @@
#include "util.h"
#include "container.h"
#include "address.h"
+#include "sandbox.h"
/* Inline the strl functions if the platform doesn't have them. */
#ifndef HAVE_STRLCPY
@@ -125,6 +127,7 @@ tor_open_cloexec(const char *path, int flags, unsigned mode)
{
int fd;
#ifdef O_CLOEXEC
+ path = sandbox_intern_string(path);
fd = open(path, flags|O_CLOEXEC, mode);
if (fd >= 0)
return fd;
@@ -948,24 +951,40 @@ socket_accounting_unlock(void)
}
/** As close(), but guaranteed to work for sockets across platforms (including
- * Windows, where close()ing a socket doesn't work. Returns 0 on success, -1
- * on failure. */
+ * Windows, where close()ing a socket doesn't work. Returns 0 on success and
+ * the socket error code on failure. */
int
-tor_close_socket(tor_socket_t s)
+tor_close_socket_simple(tor_socket_t s)
{
int r = 0;
/* On Windows, you have to call close() on fds returned by open(),
- * and closesocket() on fds returned by socket(). On Unix, everything
- * gets close()'d. We abstract this difference by always using
- * tor_close_socket to close sockets, and always using close() on
- * files.
- */
-#if defined(_WIN32)
- r = closesocket(s);
-#else
- r = close(s);
-#endif
+ * and closesocket() on fds returned by socket(). On Unix, everything
+ * gets close()'d. We abstract this difference by always using
+ * tor_close_socket to close sockets, and always using close() on
+ * files.
+ */
+ #if defined(_WIN32)
+ r = closesocket(s);
+ #else
+ r = close(s);
+ #endif
+
+ if (r != 0) {
+ int err = tor_socket_errno(-1);
+ log_info(LD_NET, "Close returned an error: %s", tor_socket_strerror(err));
+ return err;
+ }
+
+ return r;
+}
+
+/** As tor_close_socket_simple(), but keeps track of the number
+ * of open sockets. Returns 0 on success, -1 on failure. */
+int
+tor_close_socket(tor_socket_t s)
+{
+ int r = tor_close_socket_simple(s);
socket_accounting_lock();
#ifdef DEBUG_SOCKET_COUNTING
@@ -980,13 +999,11 @@ tor_close_socket(tor_socket_t s)
if (r == 0) {
--n_sockets_open;
} else {
- int err = tor_socket_errno(-1);
- log_info(LD_NET, "Close returned an error: %s", tor_socket_strerror(err));
#ifdef _WIN32
- if (err != WSAENOTSOCK)
+ if (r != WSAENOTSOCK)
--n_sockets_open;
#else
- if (err != EBADF)
+ if (r != EBADF)
--n_sockets_open;
#endif
r = -1;
@@ -1032,33 +1049,61 @@ mark_socket_open(tor_socket_t s)
tor_socket_t
tor_open_socket(int domain, int type, int protocol)
{
+ return tor_open_socket_with_extensions(domain, type, protocol, 1, 0);
+}
+
+/** As socket(), but creates a nonblocking socket and
+ * counts the number of open sockets. */
+tor_socket_t
+tor_open_socket_nonblocking(int domain, int type, int protocol)
+{
+ return tor_open_socket_with_extensions(domain, type, protocol, 1, 1);
+}
+
+/** As socket(), but counts the number of open sockets and handles
+ * socket creation with either of SOCK_CLOEXEC and SOCK_NONBLOCK specified.
+ * <b>cloexec</b> and <b>nonblock</b> should be either 0 or 1 to indicate
+ * if the corresponding extension should be used.*/
+tor_socket_t
+tor_open_socket_with_extensions(int domain, int type, int protocol,
+ int cloexec, int nonblock)
+{
tor_socket_t s;
-#ifdef SOCK_CLOEXEC
- s = socket(domain, type|SOCK_CLOEXEC, protocol);
+#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
+ int ext_flags = (cloexec ? SOCK_CLOEXEC : 0) |
+ (nonblock ? SOCK_NONBLOCK : 0);
+ s = socket(domain, type|ext_flags, protocol);
if (SOCKET_OK(s))
goto socket_ok;
/* If we got an error, see if it is EINVAL. EINVAL might indicate that,
- * even though we were built on a system with SOCK_CLOEXEC support, we
- * are running on one without. */
+ * even though we were built on a system with SOCK_CLOEXEC and SOCK_NONBLOCK
+ * support, we are running on one without. */
if (errno != EINVAL)
return s;
-#endif /* SOCK_CLOEXEC */
+#endif /* SOCK_CLOEXEC && SOCK_NONBLOCK */
s = socket(domain, type, protocol);
if (! SOCKET_OK(s))
return s;
#if defined(FD_CLOEXEC)
- if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) {
- log_warn(LD_FS,"Couldn't set FD_CLOEXEC: %s", strerror(errno));
-#if defined(_WIN32)
- closesocket(s);
+ if (cloexec) {
+ if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) {
+ log_warn(LD_FS,"Couldn't set FD_CLOEXEC: %s", strerror(errno));
+ tor_close_socket_simple(s);
+ return TOR_INVALID_SOCKET;
+ }
+ }
#else
- close(s);
+ (void)cloexec;
#endif
- return -1;
+
+ if (nonblock) {
+ if (set_socket_nonblocking(s) == -1) {
+ tor_close_socket_simple(s);
+ return TOR_INVALID_SOCKET;
+ }
}
-#endif
goto socket_ok; /* So that socket_ok will not be unused. */
@@ -1070,19 +1115,41 @@ tor_open_socket(int domain, int type, int protocol)
return s;
}
-/** As socket(), but counts the number of open sockets. */
+/** As accept(), but counts the number of open sockets. */
tor_socket_t
tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len)
{
+ return tor_accept_socket_with_extensions(sockfd, addr, len, 1, 0);
+}
+
+/** As accept(), but returns a nonblocking socket and
+ * counts the number of open sockets. */
+tor_socket_t
+tor_accept_socket_nonblocking(tor_socket_t sockfd, struct sockaddr *addr,
+ socklen_t *len)
+{
+ return tor_accept_socket_with_extensions(sockfd, addr, len, 1, 1);
+}
+
+/** As accept(), but counts the number of open sockets and handles
+ * socket creation with either of SOCK_CLOEXEC and SOCK_NONBLOCK specified.
+ * <b>cloexec</b> and <b>nonblock</b> should be either 0 or 1 to indicate
+ * if the corresponding extension should be used.*/
+tor_socket_t
+tor_accept_socket_with_extensions(tor_socket_t sockfd, struct sockaddr *addr,
+ socklen_t *len, int cloexec, int nonblock)
+{
tor_socket_t s;
-#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
- s = accept4(sockfd, addr, len, SOCK_CLOEXEC);
+#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
+ int ext_flags = (cloexec ? SOCK_CLOEXEC : 0) |
+ (nonblock ? SOCK_NONBLOCK : 0);
+ s = accept4(sockfd, addr, len, ext_flags);
if (SOCKET_OK(s))
goto socket_ok;
/* If we got an error, see if it is ENOSYS. ENOSYS indicates that,
* even though we were built on a system with accept4 support, we
* are running on one without. Also, check for EINVAL, which indicates that
- * we are missing SOCK_CLOEXEC support. */
+ * we are missing SOCK_CLOEXEC/SOCK_NONBLOCK support. */
if (errno != EINVAL && errno != ENOSYS)
return s;
#endif
@@ -1092,13 +1159,24 @@ tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len)
return s;
#if defined(FD_CLOEXEC)
- if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) {
- log_warn(LD_NET, "Couldn't set FD_CLOEXEC: %s", strerror(errno));
- close(s);
- return TOR_INVALID_SOCKET;
+ if (cloexec) {
+ if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) {
+ log_warn(LD_NET, "Couldn't set FD_CLOEXEC: %s", strerror(errno));
+ tor_close_socket_simple(s);
+ return TOR_INVALID_SOCKET;
+ }
}
+#else
+ (void)cloexec;
#endif
+ if (nonblock) {
+ if (set_socket_nonblocking(s) == -1) {
+ tor_close_socket_simple(s);
+ return TOR_INVALID_SOCKET;
+ }
+ }
+
goto socket_ok; /* So that socket_ok will not be unused. */
socket_ok:
@@ -1220,6 +1298,18 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
return 0;
#else
+ return tor_ersatz_socketpair(family, type, protocol, fd);
+#endif
+}
+
+#ifdef NEED_ERSATZ_SOCKETPAIR
+/**
+ * Helper used to implement socketpair on systems that lack it, by
+ * making a direct connection to localhost.
+ */
+STATIC int
+tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
+{
/* This socketpair does not work when localhost is down. So
* it's really not the same thing at all. But it's close enough
* for now, and really, when localhost is down sometimes, we
@@ -1230,7 +1320,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
tor_socket_t acceptor = TOR_INVALID_SOCKET;
struct sockaddr_in listen_addr;
struct sockaddr_in connect_addr;
- int size;
+ socklen_t size;
int saved_errno = -1;
if (protocol
@@ -1313,8 +1403,8 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
if (SOCKET_OK(acceptor))
tor_close_socket(acceptor);
return -saved_errno;
-#endif
}
+#endif
/** Number of extra file descriptors to keep in reserve beyond those that we
* tell Tor it's allowed to use. */
@@ -1746,6 +1836,15 @@ get_user_homedir(const char *username)
* actually examine the filesystem; does a purely syntactic modification.
*
* The parent of the root director is considered to be iteself.
+ *
+ * Path separators are the forward slash (/) everywhere and additionally
+ * the backslash (\) on Win32.
+ *
+ * Cuts off any number of trailing path separators but otherwise ignores
+ * them for purposes of finding the parent directory.
+ *
+ * Returns 0 if a parent directory was successfully found, -1 otherwise (fname
+ * did not have any path separators or only had them at the end).
* */
int
get_parent_directory(char *fname)
diff --git a/src/common/compat.h b/src/common/compat.h
index 8ab719052..8e700a9a1 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -8,6 +8,7 @@
#include "orconfig.h"
#include "torint.h"
+#include "testsupport.h"
#ifdef _WIN32
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
@@ -84,13 +85,19 @@
/* ===== Compiler compatibility */
-/* GCC can check printf types on arbitrary functions. */
+/* GCC can check printf and scanf types on arbitrary functions. */
#ifdef __GNUC__
#define CHECK_PRINTF(formatIdx, firstArg) \
__attribute__ ((format(printf, formatIdx, firstArg)))
#else
#define CHECK_PRINTF(formatIdx, firstArg)
#endif
+#ifdef __GNUC__
+#define CHECK_SCANF(formatIdx, firstArg) \
+ __attribute__ ((format(scanf, formatIdx, firstArg)))
+#else
+#define CHECK_SCANF(formatIdx, firstArg)
+#endif
/* inline is __inline on windows. */
#ifdef _WIN32
@@ -444,10 +451,22 @@ typedef int socklen_t;
#define TOR_INVALID_SOCKET (-1)
#endif
+int tor_close_socket_simple(tor_socket_t s);
int tor_close_socket(tor_socket_t s);
+tor_socket_t tor_open_socket_with_extensions(
+ int domain, int type, int protocol,
+ int cloexec, int nonblock);
tor_socket_t tor_open_socket(int domain, int type, int protocol);
+tor_socket_t tor_open_socket_nonblocking(int domain, int type, int protocol);
tor_socket_t tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr,
socklen_t *len);
+tor_socket_t tor_accept_socket_nonblocking(tor_socket_t sockfd,
+ struct sockaddr *addr,
+ socklen_t *len);
+tor_socket_t tor_accept_socket_with_extensions(tor_socket_t sockfd,
+ struct sockaddr *addr,
+ socklen_t *len,
+ int cloexec, int nonblock);
int get_n_open_sockets(void);
#define tor_socket_send(s, buf, len, flags) send(s, buf, len, flags)
@@ -720,5 +739,13 @@ char *format_win32_error(DWORD err);
#endif
+#ifdef COMPAT_PRIVATE
+#if !defined(HAVE_SOCKETPAIR) || defined(_WIN32) || defined(TOR_UNIT_TESTS)
+#define NEED_ERSATZ_SOCKETPAIR
+STATIC int tor_ersatz_socketpair(int family, int type, int protocol,
+ tor_socket_t fd[2]);
+#endif
+#endif
+
#endif
diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c
index 200a7c65f..4d0fff833 100644
--- a/src/common/compat_libevent.c
+++ b/src/common/compat_libevent.c
@@ -415,6 +415,14 @@ tor_check_libevent_version(const char *m, int server,
#define HEADER_VERSION _EVENT_VERSION
#endif
+/** Return a string representation of the version of Libevent that was used
+* at compilation time. */
+const char *
+tor_libevent_get_header_version_str(void)
+{
+ return HEADER_VERSION;
+}
+
/** See whether the headers we were built against differ from the library we
* linked against so much that we're likely to crash. If so, warn the
* user. */
diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h
index 2472e2c49..fda873359 100644
--- a/src/common/compat_libevent.h
+++ b/src/common/compat_libevent.h
@@ -78,6 +78,7 @@ void tor_check_libevent_version(const char *m, int server,
const char **badness_out);
void tor_check_libevent_header_compatibility(void);
const char *tor_libevent_get_version_str(void);
+const char *tor_libevent_get_header_version_str(void);
#ifdef USE_BUFFEREVENTS
const struct timeval *tor_libevent_get_one_tick_timeout(void);
diff --git a/src/common/container.c b/src/common/container.c
index eec497a3e..476dc8291 100644
--- a/src/common/container.c
+++ b/src/common/container.c
@@ -243,6 +243,25 @@ smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2)
return 1;
}
+/** Return true iff the two lists contain the same int pointer values in
+ * the same order, or if they are both NULL. */
+int
+smartlist_ints_eq(const smartlist_t *sl1, const smartlist_t *sl2)
+{
+ if (sl1 == NULL)
+ return sl2 == NULL;
+ if (sl2 == NULL)
+ return 0;
+ if (smartlist_len(sl1) != smartlist_len(sl2))
+ return 0;
+ SMARTLIST_FOREACH(sl1, int *, cp1, {
+ int *cp2 = smartlist_get(sl2, cp1_sl_idx);
+ if (*cp1 != *cp2)
+ return 0;
+ });
+ return 1;
+}
+
/** Return true iff <b>sl</b> has some element E such that
* tor_memeq(E,<b>element</b>,DIGEST_LEN)
*/
diff --git a/src/common/container.h b/src/common/container.h
index 1a68b8f67..1bcc54066 100644
--- a/src/common/container.h
+++ b/src/common/container.h
@@ -42,6 +42,7 @@ int smartlist_contains_string_case(const smartlist_t *sl, const char *element);
int smartlist_contains_int_as_string(const smartlist_t *sl, int num);
int smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2);
int smartlist_contains_digest(const smartlist_t *sl, const char *element);
+int smartlist_ints_eq(const smartlist_t *sl1, const smartlist_t *sl2);
int smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2);
void smartlist_intersect(smartlist_t *sl1, const smartlist_t *sl2);
void smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2);
diff --git a/src/common/crypto.c b/src/common/crypto.c
index 940a756f6..347f27e13 100644
--- a/src/common/crypto.c
+++ b/src/common/crypto.c
@@ -56,6 +56,7 @@
#include "../common/util.h"
#include "container.h"
#include "compat.h"
+#include "sandbox.h"
#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,8)
#error "We require OpenSSL >= 0.9.8"
@@ -114,7 +115,6 @@ crypto_get_rsa_padding_overhead(int padding)
switch (padding)
{
case RSA_PKCS1_OAEP_PADDING: return PKCS1_OAEP_PADDING_OVERHEAD;
- case RSA_PKCS1_PADDING: return PKCS1_PADDING_OVERHEAD;
default: tor_assert(0); return -1;
}
}
@@ -126,7 +126,6 @@ crypto_get_rsa_padding(int padding)
{
switch (padding)
{
- case PK_PKCS1_PADDING: return RSA_PKCS1_PADDING;
case PK_PKCS1_OAEP_PADDING: return RSA_PKCS1_OAEP_PADDING;
default: tor_assert(0); return -1;
}
@@ -197,6 +196,27 @@ try_load_engine(const char *path, const char *engine)
}
#endif
+/* Returns a trimmed and human-readable version of an openssl version string
+* <b>raw_version</b>. They are usually in the form of 'OpenSSL 1.0.0b 10
+* May 2012' and this will parse them into a form similar to '1.0.0b' */
+static char *
+parse_openssl_version_str(const char *raw_version)
+{
+ const char *end_of_version = NULL;
+ /* The output should be something like "OpenSSL 1.0.0b 10 May 2012. Let's
+ trim that down. */
+ if (!strcmpstart(raw_version, "OpenSSL ")) {
+ raw_version += strlen("OpenSSL ");
+ end_of_version = strchr(raw_version, ' ');
+ }
+
+ if (end_of_version)
+ return tor_strndup(raw_version,
+ end_of_version-raw_version);
+ else
+ return tor_strdup(raw_version);
+}
+
static char *crypto_openssl_version_str = NULL;
/* Return a human-readable version of the run-time openssl version number. */
const char *
@@ -204,23 +224,24 @@ crypto_openssl_get_version_str(void)
{
if (crypto_openssl_version_str == NULL) {
const char *raw_version = SSLeay_version(SSLEAY_VERSION);
- const char *end_of_version = NULL;
- /* The output should be something like "OpenSSL 1.0.0b 10 May 2012. Let's
- trim that down. */
- if (!strcmpstart(raw_version, "OpenSSL ")) {
- raw_version += strlen("OpenSSL ");
- end_of_version = strchr(raw_version, ' ');
- }
-
- if (end_of_version)
- crypto_openssl_version_str = tor_strndup(raw_version,
- end_of_version-raw_version);
- else
- crypto_openssl_version_str = tor_strdup(raw_version);
+ crypto_openssl_version_str = parse_openssl_version_str(raw_version);
}
return crypto_openssl_version_str;
}
+static char *crypto_openssl_header_version_str = NULL;
+/* Return a human-readable version of the compile-time openssl version
+* number. */
+const char *
+crypto_openssl_get_header_version_str(void)
+{
+ if (crypto_openssl_header_version_str == NULL) {
+ crypto_openssl_header_version_str =
+ parse_openssl_version_str(OPENSSL_VERSION_TEXT);
+ }
+ return crypto_openssl_header_version_str;
+}
+
/** Initialize the crypto library. Return 0 on success, -1 on failure.
*/
int
@@ -286,12 +307,29 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
" setting default ciphers.");
ENGINE_set_default(e, ENGINE_METHOD_ALL);
}
+ /* Log, if available, the intersection of the set of algorithms
+ used by Tor and the set of algorithms available in the engine */
log_engine("RSA", ENGINE_get_default_RSA());
log_engine("DH", ENGINE_get_default_DH());
+ log_engine("ECDH", ENGINE_get_default_ECDH());
+ log_engine("ECDSA", ENGINE_get_default_ECDSA());
+ log_engine("RAND", ENGINE_get_default_RAND());
log_engine("RAND (which we will not use)", ENGINE_get_default_RAND());
log_engine("SHA1", ENGINE_get_digest_engine(NID_sha1));
- log_engine("3DES", ENGINE_get_cipher_engine(NID_des_ede3_ecb));
- log_engine("AES", ENGINE_get_cipher_engine(NID_aes_128_ecb));
+ log_engine("3DES-CBC", ENGINE_get_cipher_engine(NID_des_ede3_cbc));
+ log_engine("AES-128-ECB", ENGINE_get_cipher_engine(NID_aes_128_ecb));
+ log_engine("AES-128-CBC", ENGINE_get_cipher_engine(NID_aes_128_cbc));
+#ifdef NID_aes_128_ctr
+ log_engine("AES-128-CTR", ENGINE_get_cipher_engine(NID_aes_128_ctr));
+#endif
+#ifdef NID_aes_128_gcm
+ log_engine("AES-128-GCM", ENGINE_get_cipher_engine(NID_aes_128_gcm));
+#endif
+ log_engine("AES-256-CBC", ENGINE_get_cipher_engine(NID_aes_256_cbc));
+#ifdef NID_aes_256_gcm
+ log_engine("AES-256-GCM", ENGINE_get_cipher_engine(NID_aes_256_gcm));
+#endif
+
#endif
} else {
log_info(LD_CRYPTO, "NOT using OpenSSL engine support.");
@@ -1161,22 +1199,21 @@ int
crypto_pk_asn1_encode(crypto_pk_t *pk, char *dest, size_t dest_len)
{
int len;
- unsigned char *buf, *cp;
- len = i2d_RSAPublicKey(pk->key, NULL);
- if (len < 0 || (size_t)len > dest_len || dest_len > SIZE_T_CEILING)
+ unsigned char *buf = NULL;
+
+ len = i2d_RSAPublicKey(pk->key, &buf);
+ if (len < 0 || buf == NULL)
return -1;
- cp = buf = tor_malloc(len+1);
- len = i2d_RSAPublicKey(pk->key, &cp);
- if (len < 0) {
- crypto_log_errors(LOG_WARN,"encoding public key");
- tor_free(buf);
+
+ if ((size_t)len > dest_len || dest_len > SIZE_T_CEILING) {
+ OPENSSL_free(buf);
return -1;
}
/* We don't encode directly into 'dest', because that would be illegal
* type-punning. (C99 is smarter than me, C99 is smarter than me...)
*/
memcpy(dest,buf,len);
- tor_free(buf);
+ OPENSSL_free(buf);
return len;
}
@@ -1207,24 +1244,17 @@ crypto_pk_asn1_decode(const char *str, size_t len)
int
crypto_pk_get_digest(crypto_pk_t *pk, char *digest_out)
{
- unsigned char *buf, *bufp;
+ unsigned char *buf = NULL;
int len;
- len = i2d_RSAPublicKey(pk->key, NULL);
- if (len < 0)
- return -1;
- buf = bufp = tor_malloc(len+1);
- len = i2d_RSAPublicKey(pk->key, &bufp);
- if (len < 0) {
- crypto_log_errors(LOG_WARN,"encoding public key");
- tor_free(buf);
+ len = i2d_RSAPublicKey(pk->key, &buf);
+ if (len < 0 || buf == NULL)
return -1;
- }
if (crypto_digest(digest_out, (char*)buf, len) < 0) {
- tor_free(buf);
+ OPENSSL_free(buf);
return -1;
}
- tor_free(buf);
+ OPENSSL_free(buf);
return 0;
}
@@ -1233,31 +1263,24 @@ crypto_pk_get_digest(crypto_pk_t *pk, char *digest_out)
int
crypto_pk_get_all_digests(crypto_pk_t *pk, digests_t *digests_out)
{
- unsigned char *buf, *bufp;
+ unsigned char *buf = NULL;
int len;
- len = i2d_RSAPublicKey(pk->key, NULL);
- if (len < 0)
- return -1;
- buf = bufp = tor_malloc(len+1);
- len = i2d_RSAPublicKey(pk->key, &bufp);
- if (len < 0) {
- crypto_log_errors(LOG_WARN,"encoding public key");
- tor_free(buf);
+ len = i2d_RSAPublicKey(pk->key, &buf);
+ if (len < 0 || buf == NULL)
return -1;
- }
if (crypto_digest_all(digests_out, (char*)buf, len) < 0) {
- tor_free(buf);
+ OPENSSL_free(buf);
return -1;
}
- tor_free(buf);
+ OPENSSL_free(buf);
return 0;
}
/** Copy <b>in</b> to the <b>outlen</b>-byte buffer <b>out</b>, adding spaces
* every four spaces. */
-/* static */ void
-add_spaces_to_fp(char *out, size_t outlen, const char *in)
+void
+crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in)
{
int n = 0;
char *end = out+outlen;
@@ -1294,7 +1317,7 @@ crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out, int add_space)
}
base16_encode(hexdigest,sizeof(hexdigest),digest,DIGEST_LEN);
if (add_space) {
- add_spaces_to_fp(fp_out, FINGERPRINT_LEN+1, hexdigest);
+ crypto_add_spaces_to_fp(fp_out, FINGERPRINT_LEN+1, hexdigest);
} else {
strncpy(fp_out, hexdigest, HEX_DIGEST_LEN+1);
}
@@ -1644,21 +1667,6 @@ crypto_digest_smartlist(char *digest_out, size_t len_out,
crypto_digest_free(d);
}
-/** Compute the HMAC-SHA-1 of the <b>msg_len</b> bytes in <b>msg</b>, using
- * the <b>key</b> of length <b>key_len</b>. Store the DIGEST_LEN-byte result
- * in <b>hmac_out</b>.
- */
-void
-crypto_hmac_sha1(char *hmac_out,
- const char *key, size_t key_len,
- const char *msg, size_t msg_len)
-{
- tor_assert(key_len < INT_MAX);
- tor_assert(msg_len < INT_MAX);
- HMAC(EVP_sha1(), key, (int)key_len, (unsigned char*)msg, (int)msg_len,
- (unsigned char*)hmac_out, NULL);
-}
-
/** Compute the HMAC-SHA-256 of the <b>msg_len</b> bytes in <b>msg</b>, using
* the <b>key</b> of length <b>key_len</b>. Store the DIGEST256_LEN-byte
* result in <b>hmac_out</b>.
@@ -1727,7 +1735,7 @@ crypto_store_dynamic_dh_modulus(const char *fname)
{
int len, new_len;
DH *dh = NULL;
- unsigned char *dh_string_repr = NULL, *cp = NULL;
+ unsigned char *dh_string_repr = NULL;
char *base64_encoded_dh = NULL;
char *file_string = NULL;
int retval = -1;
@@ -1751,15 +1759,8 @@ crypto_store_dynamic_dh_modulus(const char *fname)
if (!BN_set_word(dh->g, DH_GENERATOR))
goto done;
- len = i2d_DHparams(dh, NULL);
- if (len < 0) {
- log_warn(LD_CRYPTO, "Error occured while DER encoding DH modulus (1).");
- goto done;
- }
-
- cp = dh_string_repr = tor_malloc_zero(len+1);
- len = i2d_DHparams(dh, &cp);
- if ((len < 0) || ((cp - dh_string_repr) != len)) {
+ len = i2d_DHparams(dh, &dh_string_repr);
+ if ((len < 0) || (dh_string_repr == NULL)) {
log_warn(LD_CRYPTO, "Error occured while DER encoding DH modulus (2).");
goto done;
}
@@ -1786,7 +1787,8 @@ crypto_store_dynamic_dh_modulus(const char *fname)
done:
if (dh)
DH_free(dh);
- tor_free(dh_string_repr);
+ if (dh_string_repr)
+ OPENSSL_free(dh_string_repr);
tor_free(base64_encoded_dh);
tor_free(file_string);
@@ -2394,7 +2396,7 @@ crypto_strongest_rand(uint8_t *out, size_t out_len)
return 0;
#else
for (i = 0; filenames[i]; ++i) {
- fd = open(filenames[i], O_RDONLY, 0);
+ fd = open(sandbox_intern_string(filenames[i]), O_RDONLY, 0);
if (fd<0) continue;
log_info(LD_CRYPTO, "Reading entropy from \"%s\"", filenames[i]);
n = read_all(fd, (char*)out, out_len, 0);
@@ -2449,8 +2451,8 @@ crypto_seed_rng(int startup)
/** Write <b>n</b> bytes of strong random data to <b>to</b>. Return 0 on
* success, -1 on failure.
*/
-int
-crypto_rand(char *to, size_t n)
+MOCK_IMPL(int,
+crypto_rand, (char *to, size_t n))
{
int r;
tor_assert(n < INT_MAX);
@@ -3144,6 +3146,7 @@ crypto_global_cleanup(void)
}
#endif
tor_free(crypto_openssl_version_str);
+ tor_free(crypto_openssl_header_version_str);
return 0;
}
diff --git a/src/common/crypto.h b/src/common/crypto.h
index 2fbca4c26..6ce3697c9 100644
--- a/src/common/crypto.h
+++ b/src/common/crypto.h
@@ -15,6 +15,7 @@
#include <stdio.h>
#include "torint.h"
+#include "testsupport.h"
/*
Macro to create an arbitrary OpenSSL version number as used by
@@ -69,13 +70,9 @@
* signs removed. */
#define BASE64_DIGEST256_LEN 43
-/** Constant used to indicate PKCS1 padding for public-key encryption */
-#define PK_PKCS1_PADDING 60001
/** Constant used to indicate OAEP padding for public-key encryption */
#define PK_PKCS1_OAEP_PADDING 60002
-/** Number of bytes added for PKCS1 padding. */
-#define PKCS1_PADDING_OVERHEAD 11
/** Number of bytes added for PKCS1-OAEP padding. */
#define PKCS1_OAEP_PADDING_OVERHEAD 42
@@ -112,6 +109,7 @@ typedef struct crypto_dh_t crypto_dh_t;
/* global state */
const char * crypto_openssl_get_version_str(void);
+const char * crypto_openssl_get_header_version_str(void);
int crypto_global_init(int hardwareAccel,
const char *accelName,
const char *accelPath);
@@ -221,9 +219,6 @@ void crypto_digest_get_digest(crypto_digest_t *digest,
crypto_digest_t *crypto_digest_dup(const crypto_digest_t *digest);
void crypto_digest_assign(crypto_digest_t *into,
const crypto_digest_t *from);
-void crypto_hmac_sha1(char *hmac_out,
- const char *key, size_t key_len,
- const char *msg, size_t msg_len);
void crypto_hmac_sha256(char *hmac_out,
const char *key, size_t key_len,
const char *msg, size_t msg_len);
@@ -254,7 +249,7 @@ int crypto_expand_key_material_rfc5869_sha256(
/* random numbers */
int crypto_seed_rng(int startup);
-int crypto_rand(char *to, size_t n);
+MOCK_DECL(int,crypto_rand,(char *to, size_t n));
int crypto_strongest_rand(uint8_t *out, size_t out_len);
int crypto_rand_int(unsigned int max);
uint64_t crypto_rand_uint64(uint64_t max);
@@ -290,7 +285,6 @@ void secret_to_key(char *key_out, size_t key_out_len, const char *secret,
/** OpenSSL-based utility functions. */
void memwipe(void *mem, uint8_t byte, size_t sz);
-#ifdef CRYPTO_PRIVATE
/* Prototypes for private functions only used by tortls.c, crypto.c, and the
* unit tests. */
struct rsa_st;
@@ -301,9 +295,8 @@ crypto_pk_t *crypto_new_pk_from_rsa_(struct rsa_st *rsa);
struct evp_pkey_st *crypto_pk_get_evp_pkey_(crypto_pk_t *env,
int private);
struct dh_st *crypto_dh_get_dh_(crypto_dh_t *dh);
-/* Prototypes for private functions only used by crypto.c and test.c*/
-void add_spaces_to_fp(char *out, size_t outlen, const char *in);
-#endif
+
+void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in);
#endif
diff --git a/src/common/crypto_curve25519.c b/src/common/crypto_curve25519.c
index 88c723f37..9e83440e1 100644
--- a/src/common/crypto_curve25519.c
+++ b/src/common/crypto_curve25519.c
@@ -29,7 +29,7 @@ int curve25519_donna(uint8_t *mypublic,
#endif
#endif
-int
+STATIC int
curve25519_impl(uint8_t *output, const uint8_t *secret,
const uint8_t *basepoint)
{
diff --git a/src/common/crypto_curve25519.h b/src/common/crypto_curve25519.h
index 652f1883c..f9d533ba2 100644
--- a/src/common/crypto_curve25519.h
+++ b/src/common/crypto_curve25519.h
@@ -4,6 +4,7 @@
#ifndef TOR_CRYPTO_CURVE25519_H
#define TOR_CRYPTO_CURVE25519_H
+#include "testsupport.h"
#include "torint.h"
/** Length of a curve25519 public key when encoded. */
@@ -52,8 +53,8 @@ int curve25519_keypair_read_from_file(curve25519_keypair_t *keypair_out,
const char *fname);
#ifdef CRYPTO_CURVE25519_PRIVATE
-int curve25519_impl(uint8_t *output, const uint8_t *secret,
- const uint8_t *basepoint);
+STATIC int curve25519_impl(uint8_t *output, const uint8_t *secret,
+ const uint8_t *basepoint);
#endif
#endif
diff --git a/src/common/crypto_format.c b/src/common/crypto_format.c
index 93932f839..be669c8d2 100644
--- a/src/common/crypto_format.c
+++ b/src/common/crypto_format.c
@@ -3,7 +3,6 @@
/* Formatting and parsing code for crypto-related data structures. */
-#define CRYPTO_CURVE25519_PRIVATE
#include "orconfig.h"
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
diff --git a/src/common/include.am b/src/common/include.am
index b796ebfae..814786b77 100644
--- a/src/common/include.am
+++ b/src/common/include.am
@@ -1,5 +1,15 @@
-noinst_LIBRARIES+= src/common/libor.a src/common/libor-crypto.a src/common/libor-event.a
+noinst_LIBRARIES += \
+ src/common/libor.a \
+ src/common/libor-crypto.a \
+ src/common/libor-event.a
+
+if UNITTESTS_ENABLED
+noinst_LIBRARIES += \
+ src/common/libor-testing.a \
+ src/common/libor-crypto-testing.a \
+ src/common/libor-event-testing.a
+endif
EXTRA_DIST+= \
src/common/common_sha1.i \
@@ -14,9 +24,13 @@ else
libor_extra_source=
endif
+src_common_libcurve25519_donna_a_CFLAGS=
+
if BUILD_CURVE25519_DONNA
src_common_libcurve25519_donna_a_SOURCES=\
src/ext/curve25519_donna/curve25519-donna.c
+src_common_libcurve25519_donna_a_CFLAGS+=\
+ @F_OMIT_FRAME_POINTER@
noinst_LIBRARIES+=src/common/libcurve25519_donna.a
LIBDONNA=src/common/libcurve25519_donna.a
else
@@ -30,14 +44,13 @@ LIBDONNA=
endif
endif
-src_common_libcurve25519_donna_a_CFLAGS =
-
if CURVE25519_ENABLED
libcrypto_extra_source=src/common/crypto_curve25519.c
endif
-src_common_libor_a_SOURCES = \
+LIBOR_A_SOURCES = \
src/common/address.c \
+ src/common/backtrace.c \
src/common/compat.c \
src/common/container.c \
src/common/di_ops.c \
@@ -47,9 +60,10 @@ src_common_libor_a_SOURCES = \
src/common/procmon.c \
src/common/util.c \
src/common/util_codedigest.c \
+ src/common/sandbox.c \
$(libor_extra_source)
-src_common_libor_crypto_a_SOURCES = \
+LIBOR_CRYPTO_A_SOURCES = \
src/common/aes.c \
src/common/crypto.c \
src/common/crypto_format.c \
@@ -57,10 +71,27 @@ src_common_libor_crypto_a_SOURCES = \
src/common/tortls.c \
$(libcrypto_extra_source)
-src_common_libor_event_a_SOURCES = src/common/compat_libevent.c
+LIBOR_EVENT_A_SOURCES = src/common/compat_libevent.c
+
+src_common_libor_a_SOURCES = $(LIBOR_A_SOURCES)
+src_common_libor_crypto_a_SOURCES = $(LIBOR_CRYPTO_A_SOURCES)
+src_common_libor_event_a_SOURCES = $(LIBOR_EVENT_A_SOURCES)
+
+src_common_libor_testing_a_SOURCES = $(LIBOR_A_SOURCES)
+src_common_libor_crypto_testing_a_SOURCES = $(LIBOR_CRYPTO_A_SOURCES)
+src_common_libor_event_testing_a_SOURCES = $(LIBOR_EVENT_A_SOURCES)
+
+src_common_libor_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS)
+src_common_libor_crypto_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS)
+src_common_libor_event_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS)
+src_common_libor_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+src_common_libor_crypto_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+src_common_libor_event_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+
COMMONHEADERS = \
src/common/address.h \
+ src/common/backtrace.h \
src/common/aes.h \
src/common/ciphers.inc \
src/common/compat.h \
@@ -72,6 +103,8 @@ COMMONHEADERS = \
src/common/memarea.h \
src/common/mempool.h \
src/common/procmon.h \
+ src/common/sandbox.h \
+ src/common/testsupport.h \
src/common/torgzip.h \
src/common/torint.h \
src/common/torlog.h \
diff --git a/src/common/log.c b/src/common/log.c
index e196a1128..9c67de320 100644
--- a/src/common/log.c
+++ b/src/common/log.c
@@ -36,6 +36,10 @@
#include "torlog.h"
#include "container.h"
+/** Given a severity, yields an index into log_severity_list_t.masks to use
+ * for that severity. */
+#define SEVERITY_MASK_IDX(sev) ((sev) - LOG_ERR)
+
/** @{ */
/** The string we stick at the end of a log message when it is too long,
* and its length. */
@@ -83,12 +87,12 @@ should_log_function_name(log_domain_mask_t domain, int severity)
case LOG_DEBUG:
case LOG_INFO:
/* All debugging messages occur in interesting places. */
- return 1;
+ return (domain & LD_NOFUNCNAME) == 0;
case LOG_NOTICE:
case LOG_WARN:
case LOG_ERR:
/* We care about places where bugs occur. */
- return (domain == LD_BUG);
+ return (domain & (LD_BUG|LD_NOFUNCNAME)) == LD_BUG;
default:
/* Call assert, not tor_assert, since tor_assert calls log on failure. */
assert(0); return 0;
@@ -439,6 +443,128 @@ tor_log(int severity, log_domain_mask_t domain, const char *format, ...)
va_end(ap);
}
+/** Maximum number of fds that will get notifications if we crash */
+#define MAX_SIGSAFE_FDS 8
+/** Array of fds to log crash-style warnings to. */
+static int sigsafe_log_fds[MAX_SIGSAFE_FDS] = { STDERR_FILENO };
+/** The number of elements used in sigsafe_log_fds */
+static int n_sigsafe_log_fds = 1;
+
+/** Write <b>s</b> to each element of sigsafe_log_fds. Return 0 on success, -1
+ * on failure. */
+static int
+tor_log_err_sigsafe_write(const char *s)
+{
+ int i;
+ ssize_t r;
+ size_t len = strlen(s);
+ int err = 0;
+ for (i=0; i < n_sigsafe_log_fds; ++i) {
+ r = write(sigsafe_log_fds[i], s, len);
+ err += (r != (ssize_t)len);
+ }
+ return err ? -1 : 0;
+}
+
+/** Given a list of string arguments ending with a NULL, writes them
+ * to our logs and to stderr (if possible). This function is safe to call
+ * from within a signal handler. */
+void
+tor_log_err_sigsafe(const char *m, ...)
+{
+ va_list ap;
+ const char *x;
+ char timebuf[33];
+ time_t now = time(NULL);
+
+ if (!m)
+ return;
+ if (log_time_granularity >= 2000) {
+ int g = log_time_granularity / 1000;
+ now -= now % g;
+ }
+ timebuf[0] = now < 0 ? '-' : ' ';
+ if (now < 0) now = -now;
+ timebuf[1] = '\0';
+ format_dec_number_sigsafe(now, timebuf+1, sizeof(timebuf)-1);
+ tor_log_err_sigsafe_write("\n=========================================="
+ "================== T=");
+ tor_log_err_sigsafe_write(timebuf);
+ tor_log_err_sigsafe_write("\n");
+ tor_log_err_sigsafe_write(m);
+ va_start(ap, m);
+ while ((x = va_arg(ap, const char*))) {
+ tor_log_err_sigsafe_write(x);
+ }
+ va_end(ap);
+}
+
+/** Set *<b>out</b> to a pointer to an array of the fds to log errors to from
+ * inside a signal handler. Return the number of elements in the array. */
+int
+tor_log_get_sigsafe_err_fds(const int **out)
+{
+ *out = sigsafe_log_fds;
+ return n_sigsafe_log_fds;
+}
+
+/** Helper function; return true iff the <b>n</b>-element array <b>array</b>
+ * contains <b>item</b>. */
+static int
+int_array_contains(const int *array, int n, int item)
+{
+ int j;
+ for (j = 0; j < n; ++j) {
+ if (array[j] == item)
+ return 1;
+ }
+ return 0;
+}
+
+/** Function to call whenever the list of logs changes to get ready to log
+ * from signal handlers. */
+void
+tor_log_update_sigsafe_err_fds(void)
+{
+ const logfile_t *lf;
+ int found_real_stderr = 0;
+
+ LOCK_LOGS();
+ /* Reserve the first one for stderr. This is safe because when we daemonize,
+ * we dup2 /dev/null to stderr, */
+ sigsafe_log_fds[0] = STDERR_FILENO;
+ n_sigsafe_log_fds = 1;
+
+ for (lf = logfiles; lf; lf = lf->next) {
+ /* Don't try callback to the control port, or syslogs: We can't
+ * do them from a signal handler. Don't try stdout: we always do stderr.
+ */
+ if (lf->is_temporary || lf->is_syslog ||
+ lf->callback || lf->seems_dead || lf->fd < 0)
+ continue;
+ if (lf->severities->masks[SEVERITY_MASK_IDX(LOG_ERR)] &
+ (LD_BUG|LD_GENERAL)) {
+ if (lf->fd == STDERR_FILENO)
+ found_real_stderr = 1;
+ /* Avoid duplicates */
+ if (int_array_contains(sigsafe_log_fds, n_sigsafe_log_fds, lf->fd))
+ continue;
+ sigsafe_log_fds[n_sigsafe_log_fds++] = lf->fd;
+ if (n_sigsafe_log_fds == MAX_SIGSAFE_FDS)
+ break;
+ }
+ }
+
+ if (!found_real_stderr &&
+ int_array_contains(sigsafe_log_fds, n_sigsafe_log_fds, STDOUT_FILENO)) {
+ /* Don't use a virtual stderr when we're also logging to stdout. */
+ assert(n_sigsafe_log_fds >= 2); /* Don't use assert inside log functions*/
+ sigsafe_log_fds[0] = sigsafe_log_fds[--n_sigsafe_log_fds];
+ }
+
+ UNLOCK_LOGS();
+}
+
/** Output a message to the log, prefixed with a function name <b>fn</b>. */
#ifdef __GNUC__
/** GCC-based implementation of the log_fn backend, used when we have
diff --git a/src/common/sandbox.c b/src/common/sandbox.c
new file mode 100644
index 000000000..7ef577dbe
--- /dev/null
+++ b/src/common/sandbox.c
@@ -0,0 +1,1493 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file sandbox.c
+ * \brief Code to enable sandboxing.
+ **/
+
+#include "orconfig.h"
+
+#ifndef _LARGEFILE64_SOURCE
+/**
+ * Temporarily required for O_LARGEFILE flag. Needs to be removed
+ * with the libevent fix.
+ */
+#define _LARGEFILE64_SOURCE
+#endif
+
+/** Malloc mprotect limit in bytes. */
+#define MALLOC_MP_LIM 1048576
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "sandbox.h"
+#include "torlog.h"
+#include "torint.h"
+#include "util.h"
+#include "tor_queue.h"
+
+#define DEBUGGING_CLOSE
+
+#if defined(USE_LIBSECCOMP)
+
+#define _GNU_SOURCE
+
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/epoll.h>
+#include <sys/prctl.h>
+#include <linux/futex.h>
+#include <bits/signum.h>
+#include <event2/event.h>
+
+#include <stdarg.h>
+#include <seccomp.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <poll.h>
+
+/**Determines if at least one sandbox is active.*/
+static int sandbox_active = 0;
+/** Holds the parameter list configuration for the sandbox.*/
+static sandbox_cfg_t *filter_dynamic = NULL;
+/** Holds a list of pre-recorded results from getaddrinfo().*/
+static sb_addr_info_t *sb_addr_info = NULL;
+
+#undef SCMP_CMP
+#define SCMP_CMP(a,b,c) ((struct scmp_arg_cmp){(a),(b),(c),0})
+
+/** Variable used for storing all syscall numbers that will be allowed with the
+ * stage 1 general Tor sandbox.
+ */
+static int filter_nopar_gen[] = {
+ SCMP_SYS(access),
+ SCMP_SYS(brk),
+ SCMP_SYS(clock_gettime),
+ SCMP_SYS(close),
+ SCMP_SYS(clone),
+ SCMP_SYS(epoll_create),
+ SCMP_SYS(epoll_wait),
+ SCMP_SYS(fcntl),
+ SCMP_SYS(fstat),
+#ifdef __NR_fstat64
+ SCMP_SYS(fstat64),
+#endif
+ SCMP_SYS(getdents64),
+ SCMP_SYS(getegid),
+#ifdef __NR_getegid32
+ SCMP_SYS(getegid32),
+#endif
+ SCMP_SYS(geteuid),
+#ifdef __NR_geteuid32
+ SCMP_SYS(geteuid32),
+#endif
+ SCMP_SYS(getgid),
+#ifdef __NR_getgid32
+ SCMP_SYS(getgid32),
+#endif
+ SCMP_SYS(getrlimit),
+ SCMP_SYS(gettimeofday),
+ SCMP_SYS(getuid),
+#ifdef __NR_getuid32
+ SCMP_SYS(getuid32),
+#endif
+ SCMP_SYS(lseek),
+#ifdef __NR__llseek
+ SCMP_SYS(_llseek),
+#endif
+ SCMP_SYS(mkdir),
+ SCMP_SYS(mlockall),
+ SCMP_SYS(mmap),
+ SCMP_SYS(munmap),
+ SCMP_SYS(read),
+ SCMP_SYS(rename),
+ SCMP_SYS(rt_sigreturn),
+ SCMP_SYS(set_robust_list),
+#ifdef __NR_sigreturn
+ SCMP_SYS(sigreturn),
+#endif
+ SCMP_SYS(stat),
+ SCMP_SYS(uname),
+ SCMP_SYS(write),
+ SCMP_SYS(exit_group),
+ SCMP_SYS(exit),
+
+ SCMP_SYS(madvise),
+#ifdef __NR_stat64
+ // getaddrinfo uses this..
+ SCMP_SYS(stat64),
+#endif
+
+ /*
+ * These socket syscalls are not required on x86_64 and not supported with
+ * some libseccomp versions (eg: 1.0.1)
+ */
+#if defined(__i386)
+ SCMP_SYS(recv),
+ SCMP_SYS(send),
+#endif
+
+ // socket syscalls
+ SCMP_SYS(bind),
+ SCMP_SYS(connect),
+ SCMP_SYS(getsockname),
+ SCMP_SYS(recvmsg),
+ SCMP_SYS(recvfrom),
+ SCMP_SYS(sendto),
+ SCMP_SYS(unlink)
+};
+
+/**
+ * Function responsible for setting up the rt_sigaction syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_rt_sigaction(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ unsigned i;
+ int rc;
+ int param[] = { SIGINT, SIGTERM, SIGPIPE, SIGUSR1, SIGUSR2, SIGHUP, SIGCHLD,
+#ifdef SIGXFSZ
+ SIGXFSZ
+#endif
+ };
+ (void) filter;
+
+ for (i = 0; i < ARRAY_LENGTH(param); i++) {
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigaction), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, param[i]));
+ if (rc)
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * Function responsible for setting up the execve syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_execve(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc;
+ sandbox_cfg_t *elem = NULL;
+
+ // for each dynamic parameter filters
+ for (elem = filter; elem != NULL; elem = elem->next) {
+ smp_param_t *param = (smp_param_t*) elem->param;
+
+ if (param != NULL && param->prot == 1 && param->syscall
+ == SCMP_SYS(execve)) {
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(execve), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, param->value));
+ if (rc != 0) {
+ log_err(LD_BUG,"(Sandbox) failed to add execve syscall, received "
+ "libseccomp error %d", rc);
+ return rc;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the time syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_time(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ (void) filter;
+ return seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(time), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, 0));
+}
+
+/**
+ * Function responsible for setting up the accept4 syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_accept4(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void)filter;
+
+#ifdef __i386__
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketcall), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, 18));
+ if (rc) {
+ return rc;
+ }
+#endif
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept4), 1,
+ SCMP_CMP(3, SCMP_CMP_EQ, SOCK_CLOEXEC));
+ if (rc) {
+ return rc;
+ }
+
+ return 0;
+}
+
+#ifdef __NR_mmap2
+/**
+ * Function responsible for setting up the mmap2 syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_mmap2(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), 2,
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ),
+ SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE));
+ if (rc) {
+ return rc;
+ }
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), 2,
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_NONE),
+ SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE));
+ if (rc) {
+ return rc;
+ }
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), 2,
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE),
+ SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_ANONYMOUS));
+ if (rc) {
+ return rc;
+ }
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), 2,
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE),
+ SCMP_CMP(3, SCMP_CMP_EQ,MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK));
+ if (rc) {
+ return rc;
+ }
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), 2,
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE),
+ SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE));
+ if (rc) {
+ return rc;
+ }
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), 2,
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE),
+ SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS));
+ if (rc) {
+ return rc;
+ }
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), 2,
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_EXEC),
+ SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_DENYWRITE));
+ if (rc) {
+ return rc;
+ }
+
+ return 0;
+}
+#endif
+
+/**
+ * Function responsible for setting up the open syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_open(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc;
+ sandbox_cfg_t *elem = NULL;
+
+ // for each dynamic parameter filters
+ for (elem = filter; elem != NULL; elem = elem->next) {
+ smp_param_t *param = elem->param;
+
+ if (param != NULL && param->prot == 1 && param->syscall
+ == SCMP_SYS(open)) {
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, param->value));
+ if (rc != 0) {
+ log_err(LD_BUG,"(Sandbox) failed to add open syscall, received "
+ "libseccomp error %d", rc);
+ return rc;
+ }
+ }
+ }
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(-1), SCMP_SYS(open), 1,
+ SCMP_CMP(1, SCMP_CMP_EQ, O_RDONLY|O_CLOEXEC));
+ if (rc != 0) {
+ log_err(LD_BUG,"(Sandbox) failed to add open syscall, received libseccomp "
+ "error %d", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the openat syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_openat(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc;
+ sandbox_cfg_t *elem = NULL;
+
+ // for each dynamic parameter filters
+ for (elem = filter; elem != NULL; elem = elem->next) {
+ smp_param_t *param = elem->param;
+
+ if (param != NULL && param->prot == 1 && param->syscall
+ == SCMP_SYS(openat)) {
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, AT_FDCWD),
+ SCMP_CMP(1, SCMP_CMP_EQ, param->value),
+ SCMP_CMP(2, SCMP_CMP_EQ, O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY|
+ O_CLOEXEC));
+ if (rc != 0) {
+ log_err(LD_BUG,"(Sandbox) failed to add openat syscall, received "
+ "libseccomp error %d", rc);
+ return rc;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the socket syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_socket(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+#ifdef __i386__
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 0);
+ if (rc)
+ return rc;
+#endif
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 3,
+ SCMP_CMP(0, SCMP_CMP_EQ, PF_FILE),
+ SCMP_CMP(1, SCMP_CMP_EQ, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK),
+ SCMP_CMP(2, SCMP_CMP_EQ, IPPROTO_IP));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 3,
+ SCMP_CMP(0, SCMP_CMP_EQ, PF_INET),
+ SCMP_CMP(1, SCMP_CMP_EQ, SOCK_STREAM|SOCK_CLOEXEC),
+ SCMP_CMP(2, SCMP_CMP_EQ, IPPROTO_TCP));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 3,
+ SCMP_CMP(0, SCMP_CMP_EQ, PF_INET),
+ SCMP_CMP(1, SCMP_CMP_EQ, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK),
+ SCMP_CMP(2, SCMP_CMP_EQ, IPPROTO_IP));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 3,
+ SCMP_CMP(0, SCMP_CMP_EQ, PF_NETLINK),
+ SCMP_CMP(1, SCMP_CMP_EQ, SOCK_RAW),
+ SCMP_CMP(2, SCMP_CMP_EQ, 0));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the socketpair syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_socketpair(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+#ifdef __i386__
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketpair), 0);
+ if (rc)
+ return rc;
+#endif
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketpair), 2,
+ SCMP_CMP(0, SCMP_CMP_EQ, PF_FILE),
+ SCMP_CMP(1, SCMP_CMP_EQ, SOCK_STREAM|SOCK_CLOEXEC));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the setsockopt syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_setsockopt(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+#ifdef __i386__
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), 0);
+ if (rc)
+ return rc;
+#endif
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), 2,
+ SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET),
+ SCMP_CMP(2, SCMP_CMP_EQ, SO_REUSEADDR));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the getsockopt syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_getsockopt(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+#ifdef __i386__
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt), 0);
+ if (rc)
+ return rc;
+#endif
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt), 2,
+ SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET),
+ SCMP_CMP(2, SCMP_CMP_EQ, SO_ERROR));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+#ifdef __NR_fcntl64
+/**
+ * Function responsible for setting up the fcntl64 syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_fcntl64(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), 1,
+ SCMP_CMP(1, SCMP_CMP_EQ, F_GETFL));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), 2,
+ SCMP_CMP(1, SCMP_CMP_EQ, F_SETFL),
+ SCMP_CMP(2, SCMP_CMP_EQ, O_RDWR|O_NONBLOCK));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), 1,
+ SCMP_CMP(1, SCMP_CMP_EQ, F_GETFD));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), 2,
+ SCMP_CMP(1, SCMP_CMP_EQ, F_SETFD),
+ SCMP_CMP(2, SCMP_CMP_EQ, FD_CLOEXEC));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+#endif
+
+/**
+ * Function responsible for setting up the epoll_ctl syscall for
+ * the seccomp filter sandbox.
+ *
+ * Note: basically allows everything but will keep for now..
+ */
+static int
+sb_epoll_ctl(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), 1,
+ SCMP_CMP(1, SCMP_CMP_EQ, EPOLL_CTL_ADD));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), 1,
+ SCMP_CMP(1, SCMP_CMP_EQ, EPOLL_CTL_MOD));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), 1,
+ SCMP_CMP(1, SCMP_CMP_EQ, EPOLL_CTL_DEL));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the fcntl64 syscall for
+ * the seccomp filter sandbox.
+ *
+ * NOTE: if multiple filters need to be added, the PR_SECCOMP parameter needs
+ * to be whitelisted in this function.
+ */
+static int
+sb_prctl(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(prctl), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, PR_SET_DUMPABLE));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the fcntl64 syscall for
+ * the seccomp filter sandbox.
+ *
+ * NOTE: does not NEED to be here.. currently only occurs before filter; will
+ * keep just in case for the future.
+ */
+static int
+sb_mprotect(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 1,
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 1,
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_NONE));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the rt_sigprocmask syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_rt_sigprocmask(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, SIG_UNBLOCK));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, SIG_SETMASK));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the flock syscall for
+ * the seccomp filter sandbox.
+ *
+ * NOTE: does not need to be here, occurs before filter is applied.
+ */
+static int
+sb_flock(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(flock), 1,
+ SCMP_CMP(1, SCMP_CMP_EQ, LOCK_EX|LOCK_NB));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(flock), 1,
+ SCMP_CMP(1, SCMP_CMP_EQ, LOCK_UN));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the futex syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_futex(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+ // can remove
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 1,
+ SCMP_CMP(1, SCMP_CMP_EQ,
+ FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 1,
+ SCMP_CMP(1, SCMP_CMP_EQ, FUTEX_WAKE_PRIVATE));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 1,
+ SCMP_CMP(1, SCMP_CMP_EQ, FUTEX_WAIT_PRIVATE));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the mremap syscall for
+ * the seccomp filter sandbox.
+ *
+ * NOTE: so far only occurs before filter is applied.
+ */
+static int
+sb_mremap(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mremap), 1,
+ SCMP_CMP(3, SCMP_CMP_EQ, MREMAP_MAYMOVE));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the poll syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_poll(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(poll), 2,
+ SCMP_CMP(1, SCMP_CMP_EQ, 1),
+ SCMP_CMP(2, SCMP_CMP_EQ, 10));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+#ifdef __NR_stat64
+/**
+ * Function responsible for setting up the stat64 syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_stat64(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ sandbox_cfg_t *elem = NULL;
+
+ // for each dynamic parameter filters
+ for (elem = filter; elem != NULL; elem = elem->next) {
+ smp_param_t *param = elem->param;
+
+ if (param != NULL && param->prot == 1 && (param->syscall == SCMP_SYS(open)
+ || param->syscall == SCMP_SYS(stat64))) {
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(stat64), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, param->value));
+ if (rc != 0) {
+ log_err(LD_BUG,"(Sandbox) failed to add open syscall, received "
+ "libseccomp error %d", rc);
+ return rc;
+ }
+ }
+ }
+
+ return 0;
+}
+#endif
+
+/**
+ * Array of function pointers responsible for filtering different syscalls at
+ * a parameter level.
+ */
+static sandbox_filter_func_t filter_func[] = {
+ sb_rt_sigaction,
+ sb_rt_sigprocmask,
+ sb_execve,
+ sb_time,
+ sb_accept4,
+#ifdef __NR_mmap2
+ sb_mmap2,
+#endif
+ sb_open,
+ sb_openat,
+#ifdef __NR_fcntl64
+ sb_fcntl64,
+#endif
+ sb_epoll_ctl,
+ sb_prctl,
+ sb_mprotect,
+ sb_flock,
+ sb_futex,
+ sb_mremap,
+ sb_poll,
+#ifdef __NR_stat64
+ sb_stat64,
+#endif
+
+ sb_socket,
+ sb_setsockopt,
+ sb_getsockopt,
+ sb_socketpair
+};
+
+const char*
+sandbox_intern_string(const char *str)
+{
+ sandbox_cfg_t *elem;
+
+ if (str == NULL)
+ return NULL;
+
+ for (elem = filter_dynamic; elem != NULL; elem = elem->next) {
+ smp_param_t *param = elem->param;
+
+ if (param->prot && !strcmp(str, (char*)(param->value))) {
+ return (char*)(param->value);
+ }
+ }
+
+ log_info(LD_GENERAL, "(Sandbox) Parameter %s not found", str);
+ return str;
+}
+
+/**
+ * Protects all the strings in the sandbox's parameter list configuration. It
+ * works by calculating the total amount of memory required by the parameter
+ * list, allocating the memory using mmap, and protecting it from writes with
+ * mprotect().
+ */
+static int
+prot_strings(scmp_filter_ctx ctx, sandbox_cfg_t* cfg)
+{
+ int ret = 0;
+ size_t pr_mem_size = 0, pr_mem_left = 0;
+ char *pr_mem_next = NULL, *pr_mem_base;
+ sandbox_cfg_t *el = NULL;
+
+ // get total number of bytes required to mmap
+ for (el = cfg; el != NULL; el = el->next) {
+ pr_mem_size += strlen((char*) ((smp_param_t*)el->param)->value) + 1;
+ }
+
+ // allocate protected memory with MALLOC_MP_LIM canary
+ pr_mem_base = (char*) mmap(NULL, MALLOC_MP_LIM + pr_mem_size,
+ PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (pr_mem_base == MAP_FAILED) {
+ log_err(LD_BUG,"(Sandbox) failed allocate protected memory! mmap: %s",
+ strerror(errno));
+ ret = -1;
+ goto out;
+ }
+
+ pr_mem_next = pr_mem_base + MALLOC_MP_LIM;
+ pr_mem_left = pr_mem_size;
+
+ // change el value pointer to protected
+ for (el = cfg; el != NULL; el = el->next) {
+ char *param_val = (char*)((smp_param_t *)el->param)->value;
+ size_t param_size = strlen(param_val) + 1;
+
+ if (pr_mem_left >= param_size) {
+ // copy to protected
+ memcpy(pr_mem_next, param_val, param_size);
+
+ // re-point el parameter to protected
+ {
+ void *old_val = (void *) ((smp_param_t*)el->param)->value;
+ tor_free(old_val);
+ }
+ ((smp_param_t*)el->param)->value = (intptr_t) pr_mem_next;
+ ((smp_param_t*)el->param)->prot = 1;
+
+ // move next available protected memory
+ pr_mem_next += param_size;
+ pr_mem_left -= param_size;
+ } else {
+ log_err(LD_BUG,"(Sandbox) insufficient protected memory!");
+ ret = -2;
+ goto out;
+ }
+ }
+
+ // protecting from writes
+ if (mprotect(pr_mem_base, MALLOC_MP_LIM + pr_mem_size, PROT_READ)) {
+ log_err(LD_BUG,"(Sandbox) failed to protect memory! mprotect: %s",
+ strerror(errno));
+ ret = -3;
+ goto out;
+ }
+
+ /*
+ * Setting sandbox restrictions so the string memory cannot be tampered with
+ */
+ // no mremap of the protected base address
+ ret = seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(mremap), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, (intptr_t) pr_mem_base));
+ if (ret) {
+ log_err(LD_BUG,"(Sandbox) mremap protected memory filter fail!");
+ return ret;
+ }
+
+ // no munmap of the protected base address
+ ret = seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(munmap), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, (intptr_t) pr_mem_base));
+ if (ret) {
+ log_err(LD_BUG,"(Sandbox) munmap protected memory filter fail!");
+ return ret;
+ }
+
+ /*
+ * Allow mprotect with PROT_READ|PROT_WRITE because openssl uses it, but
+ * never over the memory region used by the protected strings.
+ *
+ * PROT_READ|PROT_WRITE was originally fully allowed in sb_mprotect(), but
+ * had to be removed due to limitation of libseccomp regarding intervals.
+ *
+ * There is a restriction on how much you can mprotect with R|W up to the
+ * size of the canary.
+ */
+ ret = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 2,
+ SCMP_CMP(0, SCMP_CMP_LT, (intptr_t) pr_mem_base),
+ SCMP_CMP(1, SCMP_CMP_LE, MALLOC_MP_LIM),
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE));
+ if (ret) {
+ log_err(LD_BUG,"(Sandbox) mprotect protected memory filter fail (LT)!");
+ return ret;
+ }
+
+ ret = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 2,
+ SCMP_CMP(0, SCMP_CMP_GT, (intptr_t) pr_mem_base + pr_mem_size +
+ MALLOC_MP_LIM),
+ SCMP_CMP(1, SCMP_CMP_LE, MALLOC_MP_LIM),
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE));
+ if (ret) {
+ log_err(LD_BUG,"(Sandbox) mprotect protected memory filter fail (GT)!");
+ return ret;
+ }
+
+ out:
+ return ret;
+}
+
+/**
+ * Auxiliary function used in order to allocate a sandbox_cfg_t element and set
+ * it's values according the the parameter list. All elements are initialised
+ * with the 'prot' field set to false, as the pointer is not protected at this
+ * point.
+ */
+static sandbox_cfg_t*
+new_element(int syscall, int index, intptr_t value)
+{
+ smp_param_t *param = NULL;
+
+ sandbox_cfg_t *elem = tor_malloc(sizeof(sandbox_cfg_t));
+ elem->param = tor_malloc(sizeof(smp_param_t));
+
+ param = elem->param;
+ param->syscall = syscall;
+ param->pindex = index;
+ param->value = value;
+ param->prot = 0;
+
+ return elem;
+}
+
+#ifdef __NR_stat64
+#define SCMP_stat SCMP_SYS(stat64)
+#else
+#define SCMP_stat SCMP_SYS(stat)
+#endif
+
+int
+sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file, int fr)
+{
+ sandbox_cfg_t *elem = NULL;
+
+ elem = new_element(SCMP_stat, 0, (intptr_t)(void*) tor_strdup(file));
+ if (!elem) {
+ log_err(LD_BUG,"(Sandbox) failed to register parameter!");
+ return -1;
+ }
+
+ elem->next = *cfg;
+ *cfg = elem;
+
+ if (fr) tor_free(file);
+ return 0;
+}
+
+int
+sandbox_cfg_allow_stat_filename_array(sandbox_cfg_t **cfg, ...)
+{
+ int rc = 0;
+ char *fn = NULL;
+
+ va_list ap;
+ va_start(ap, cfg);
+
+ while ((fn = va_arg(ap, char*)) != NULL) {
+ int fr = va_arg(ap, int);
+
+ rc = sandbox_cfg_allow_stat_filename(cfg, fn, fr);
+ if (rc) {
+ log_err(LD_BUG,"(Sandbox) sandbox_cfg_allow_stat_filename_array fail");
+ goto end;
+ }
+ }
+
+ end:
+ va_end(ap);
+ return 0;
+}
+
+int
+sandbox_cfg_allow_open_filename(sandbox_cfg_t **cfg, char *file, int fr)
+{
+ sandbox_cfg_t *elem = NULL;
+
+ elem = new_element(SCMP_SYS(open), 0, (intptr_t)(void *)tor_strdup(file));
+ if (!elem) {
+ log_err(LD_BUG,"(Sandbox) failed to register parameter!");
+ return -1;
+ }
+
+ elem->next = *cfg;
+ *cfg = elem;
+
+ if (fr) tor_free(file);
+
+ return 0;
+}
+
+int
+sandbox_cfg_allow_open_filename_array(sandbox_cfg_t **cfg, ...)
+{
+ int rc = 0;
+ char *fn = NULL;
+
+ va_list ap;
+ va_start(ap, cfg);
+
+ while ((fn = va_arg(ap, char*)) != NULL) {
+ int fr = va_arg(ap, int);
+
+ rc = sandbox_cfg_allow_open_filename(cfg, fn, fr);
+ if (rc) {
+ log_err(LD_BUG,"(Sandbox) sandbox_cfg_allow_open_filename_array fail");
+ goto end;
+ }
+ }
+
+ end:
+ va_end(ap);
+ return 0;
+}
+
+int
+sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file, int fr)
+{
+ sandbox_cfg_t *elem = NULL;
+
+ elem = new_element(SCMP_SYS(openat), 1, (intptr_t)(void *)tor_strdup(file));
+ if (!elem) {
+ log_err(LD_BUG,"(Sandbox) failed to register parameter!");
+ return -1;
+ }
+
+ elem->next = *cfg;
+ *cfg = elem;
+
+ if (fr) tor_free(file);
+
+ return 0;
+}
+
+int
+sandbox_cfg_allow_openat_filename_array(sandbox_cfg_t **cfg, ...)
+{
+ int rc = 0;
+ char *fn = NULL;
+
+ va_list ap;
+ va_start(ap, cfg);
+
+ while ((fn = va_arg(ap, char*)) != NULL) {
+ int fr = va_arg(ap, int);
+
+ rc = sandbox_cfg_allow_openat_filename(cfg, fn, fr);
+ if (rc) {
+ log_err(LD_BUG,"(Sandbox) sandbox_cfg_allow_openat_filename_array fail");
+ goto end;
+ }
+ }
+
+ end:
+ va_end(ap);
+ return 0;
+}
+
+int
+sandbox_cfg_allow_execve(sandbox_cfg_t **cfg, const char *com)
+{
+ sandbox_cfg_t *elem = NULL;
+
+ elem = new_element(SCMP_SYS(execve), 1, (intptr_t)(void *)tor_strdup(com));
+ if (!elem) {
+ log_err(LD_BUG,"(Sandbox) failed to register parameter!");
+ return -1;
+ }
+
+ elem->next = *cfg;
+ *cfg = elem;
+
+ return 0;
+}
+
+int
+sandbox_cfg_allow_execve_array(sandbox_cfg_t **cfg, ...)
+{
+ int rc = 0;
+ char *fn = NULL;
+
+ va_list ap;
+ va_start(ap, cfg);
+
+ while ((fn = va_arg(ap, char*)) != NULL) {
+
+ rc = sandbox_cfg_allow_execve(cfg, fn);
+ if (rc) {
+ log_err(LD_BUG,"(Sandbox) sandbox_cfg_allow_execve_array failed");
+ goto end;
+ }
+ }
+
+ end:
+ va_end(ap);
+ return 0;
+}
+
+int
+sandbox_getaddrinfo(const char *name, const char *servname,
+ const struct addrinfo *hints,
+ struct addrinfo **res)
+{
+ sb_addr_info_t *el;
+
+ if (servname != NULL)
+ return -1;
+
+ *res = NULL;
+
+ for (el = sb_addr_info; el; el = el->next) {
+ if (!strcmp(el->name, name)) {
+ *res = tor_malloc(sizeof(struct addrinfo));
+
+ memcpy(*res, el->info, sizeof(struct addrinfo));
+ /* XXXX What if there are multiple items in the list? */
+ return 0;
+ }
+ }
+
+ if (!sandbox_active) {
+ if (getaddrinfo(name, NULL, hints, res)) {
+ log_err(LD_BUG,"(Sandbox) getaddrinfo failed!");
+ return -1;
+ }
+
+ return 0;
+ }
+
+ // getting here means something went wrong
+ log_err(LD_BUG,"(Sandbox) failed to get address %s!", name);
+ if (*res) {
+ tor_free(*res);
+ res = NULL;
+ }
+ return -1;
+}
+
+int
+sandbox_add_addrinfo(const char* name)
+{
+ int ret;
+ struct addrinfo hints;
+ sb_addr_info_t *el = NULL;
+
+ el = tor_malloc(sizeof(sb_addr_info_t));
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+
+ ret = getaddrinfo(name, NULL, &hints, &(el->info));
+ if (ret) {
+ log_err(LD_BUG,"(Sandbox) failed to getaddrinfo");
+ ret = -2;
+ tor_free(el);
+ goto out;
+ }
+
+ el->name = tor_strdup(name);
+ el->next = sb_addr_info;
+ sb_addr_info = el;
+
+ out:
+ return ret;
+}
+
+/**
+ * Function responsible for going through the parameter syscall filters and
+ * call each function pointer in the list.
+ */
+static int
+add_param_filter(scmp_filter_ctx ctx, sandbox_cfg_t* cfg)
+{
+ unsigned i;
+ int rc = 0;
+
+ // function pointer
+ for (i = 0; i < ARRAY_LENGTH(filter_func); i++) {
+ if ((filter_func[i])(ctx, cfg)) {
+ log_err(LD_BUG,"(Sandbox) failed to add syscall %d, received libseccomp "
+ "error %d", i, rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Function responsible of loading the libseccomp syscall filters which do not
+ * have parameter filtering.
+ */
+static int
+add_noparam_filter(scmp_filter_ctx ctx)
+{
+ unsigned i;
+ int rc = 0;
+
+ // add general filters
+ for (i = 0; i < ARRAY_LENGTH(filter_nopar_gen); i++) {
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, filter_nopar_gen[i], 0);
+ if (rc != 0) {
+ log_err(LD_BUG,"(Sandbox) failed to add syscall index %d (NR=%d), "
+ "received libseccomp error %d", i, filter_nopar_gen[i], rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up and enabling a global syscall filter.
+ * The function is a prototype developed for stage 1 of sandboxing Tor.
+ * Returns 0 on success.
+ */
+static int
+install_syscall_filter(sandbox_cfg_t* cfg)
+{
+ int rc = 0;
+ scmp_filter_ctx ctx;
+
+ ctx = seccomp_init(SCMP_ACT_TRAP);
+ if (ctx == NULL) {
+ log_err(LD_BUG,"(Sandbox) failed to initialise libseccomp context");
+ rc = -1;
+ goto end;
+ }
+
+ // protectign sandbox parameter strings
+ if ((rc = prot_strings(ctx, cfg))) {
+ goto end;
+ }
+
+ // add parameter filters
+ if ((rc = add_param_filter(ctx, cfg))) {
+ log_err(LD_BUG, "(Sandbox) failed to add param filters!");
+ goto end;
+ }
+
+ // adding filters with no parameters
+ if ((rc = add_noparam_filter(ctx))) {
+ log_err(LD_BUG, "(Sandbox) failed to add param filters!");
+ goto end;
+ }
+
+ // loading the seccomp2 filter
+ if ((rc = seccomp_load(ctx))) {
+ log_err(LD_BUG, "(Sandbox) failed to load!");
+ goto end;
+ }
+
+ // marking the sandbox as active
+ sandbox_active = 1;
+
+ end:
+ seccomp_release(ctx);
+ return (rc < 0 ? -rc : rc);
+}
+
+/**
+ * Function called when a SIGSYS is caught by the application. It notifies the
+ * user that an error has occurred and either terminates or allows the
+ * application to continue execution, based on the DEBUGGING_CLOSE symbol.
+ */
+static void
+sigsys_debugging(int nr, siginfo_t *info, void *void_context)
+{
+ ucontext_t *ctx = (ucontext_t *) (void_context);
+ char number[32];
+ int syscall;
+ (void) nr;
+
+ if (info->si_code != SYS_SECCOMP)
+ return;
+
+ if (!ctx)
+ return;
+
+ syscall = ctx->uc_mcontext.gregs[REG_SYSCALL];
+
+ format_dec_number_sigsafe(syscall, number, sizeof(number));
+ tor_log_err_sigsafe("(Sandbox) Caught a bad syscall attempt (syscall ",
+ number,
+ ")\n",
+ NULL);
+
+#if defined(DEBUGGING_CLOSE)
+ _exit(1);
+#endif // DEBUGGING_CLOSE
+}
+
+/**
+ * Function that adds a handler for SIGSYS, which is the signal thrown
+ * when the application is issuing a syscall which is not allowed. The
+ * main purpose of this function is to help with debugging by identifying
+ * filtered syscalls.
+ */
+static int
+install_sigsys_debugging(void)
+{
+ struct sigaction act;
+ sigset_t mask;
+
+ memset(&act, 0, sizeof(act));
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGSYS);
+
+ act.sa_sigaction = &sigsys_debugging;
+ act.sa_flags = SA_SIGINFO;
+ if (sigaction(SIGSYS, &act, NULL) < 0) {
+ log_err(LD_BUG,"(Sandbox) Failed to register SIGSYS signal handler");
+ return -1;
+ }
+
+ if (sigprocmask(SIG_UNBLOCK, &mask, NULL)) {
+ log_err(LD_BUG,"(Sandbox) Failed call to sigprocmask()");
+ return -2;
+ }
+
+ return 0;
+}
+
+/**
+ * Function responsible of registering the sandbox_cfg_t list of parameter
+ * syscall filters to the existing parameter list. This is used for incipient
+ * multiple-sandbox support.
+ */
+static int
+register_cfg(sandbox_cfg_t* cfg)
+{
+ sandbox_cfg_t *elem = NULL;
+
+ if (filter_dynamic == NULL) {
+ filter_dynamic = cfg;
+ return 0;
+ }
+
+ for (elem = filter_dynamic; elem->next != NULL; elem = elem->next);
+
+ elem->next = cfg;
+
+ return 0;
+}
+
+#endif // USE_LIBSECCOMP
+
+#ifdef USE_LIBSECCOMP
+/**
+ * Initialises the syscall sandbox filter for any linux architecture, taking
+ * into account various available features for different linux flavours.
+ */
+static int
+initialise_libseccomp_sandbox(sandbox_cfg_t* cfg)
+{
+ if (install_sigsys_debugging())
+ return -1;
+
+ if (install_syscall_filter(cfg))
+ return -2;
+
+ if (register_cfg(cfg))
+ return -3;
+
+ return 0;
+}
+
+#endif // USE_LIBSECCOMP
+
+sandbox_cfg_t*
+sandbox_cfg_new(void)
+{
+ return NULL;
+}
+
+int
+sandbox_init(sandbox_cfg_t *cfg)
+{
+#if defined(USE_LIBSECCOMP)
+ return initialise_libseccomp_sandbox(cfg);
+
+#elif defined(_WIN32)
+ (void)cfg;
+ log_warn(LD_BUG,"Windows sandboxing is not implemented. The feature is "
+ "currently disabled.");
+ return 0;
+
+#elif defined(TARGET_OS_MAC)
+ (void)cfg;
+ log_warn(LD_BUG,"Mac OSX sandboxing is not implemented. The feature is "
+ "currently disabled");
+ return 0;
+#else
+ (void)cfg;
+ log_warn(LD_BUG,"Sandboxing is not implemented for your platform. The "
+ "feature is currently disabled");
+ return 0;
+#endif
+}
+
+#ifndef USE_LIBSECCOMP
+int
+sandbox_cfg_allow_open_filename(sandbox_cfg_t **cfg, char *file,
+ int fr)
+{
+ (void)cfg; (void)file; (void)fr;
+ return 0;
+}
+
+int
+sandbox_cfg_allow_open_filename_array(sandbox_cfg_t **cfg, ...)
+{
+ (void)cfg;
+ return 0;
+}
+
+int
+sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file,
+ int fr)
+{
+ (void)cfg; (void)file; (void)fr;
+ return 0;
+}
+
+int
+sandbox_cfg_allow_openat_filename_array(sandbox_cfg_t **cfg, ...)
+{
+ (void)cfg;
+ return 0;
+}
+
+int
+sandbox_cfg_allow_execve(sandbox_cfg_t **cfg, const char *com)
+{
+ (void)cfg; (void)com;
+ return 0;
+}
+
+int
+sandbox_cfg_allow_execve_array(sandbox_cfg_t **cfg, ...)
+{
+ (void)cfg;
+ return 0;
+}
+
+int
+sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file,
+ int fr)
+{
+ (void)cfg; (void)file; (void)fr;
+ return 0;
+}
+
+int
+sandbox_cfg_allow_stat_filename_array(sandbox_cfg_t **cfg, ...)
+{
+ (void)cfg;
+ return 0;
+}
+#endif
+
diff --git a/src/common/sandbox.h b/src/common/sandbox.h
new file mode 100644
index 000000000..d64d427d3
--- /dev/null
+++ b/src/common/sandbox.h
@@ -0,0 +1,247 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file sandbox.h
+ * \brief Header file for sandbox.c.
+ **/
+
+#ifndef SANDBOX_H_
+#define SANDBOX_H_
+
+#include "orconfig.h"
+#include "torint.h"
+
+#ifndef SYS_SECCOMP
+
+/**
+ * Used by SIGSYS signal handler to check if the signal was issued due to a
+ * seccomp2 filter violation.
+ */
+#define SYS_SECCOMP 1
+
+#endif
+
+#if defined(HAVE_SECCOMP_H) && defined(__linux__)
+#define USE_LIBSECCOMP
+#endif
+
+struct sandbox_cfg_elem;
+
+/** Typedef to structure used to manage a sandbox configuration. */
+typedef struct sandbox_cfg_elem sandbox_cfg_t;
+
+/**
+ * Linux definitions
+ */
+#ifdef USE_LIBSECCOMP
+
+#ifndef __USE_GNU
+#define __USE_GNU
+#endif
+#include <sys/ucontext.h>
+#include <seccomp.h>
+#include <netdb.h>
+
+#define PARAM_PTR 0
+#define PARAM_NUM 1
+
+/**
+ * Enum used to manage the type of the implementation for general purpose.
+ */
+typedef enum {
+ /** Libseccomp implementation based on seccomp2*/
+ LIBSECCOMP2 = 0
+} SB_IMPL;
+
+/**
+ * Configuration parameter structure associated with the LIBSECCOMP2
+ * implementation.
+ */
+typedef struct smp_param {
+ /** syscall associated with parameter. */
+ int syscall;
+
+ /** parameter index. */
+ int pindex;
+ /** parameter value. */
+ intptr_t value;
+
+ /** parameter flag (0 = not protected, 1 = protected). */
+ int prot;
+} smp_param_t;
+
+/**
+ * Structure used to manage a sandbox configuration.
+ *
+ * It is implemented as a linked list of parameters. Currently only controls
+ * parameters for open, openat, execve, stat64.
+ */
+struct sandbox_cfg_elem {
+ /** Sandbox implementation which dictates the parameter type. */
+ SB_IMPL implem;
+
+ /** Configuration parameter. */
+ void *param;
+
+ /** Next element of the configuration*/
+ struct sandbox_cfg_elem *next;
+};
+
+/**
+ * Structure used for keeping a linked list of getaddrinfo pre-recorded
+ * results.
+ */
+struct sb_addr_info_el {
+ /** Name of the address info result. */
+ char *name;
+ /** Pre-recorded getaddrinfo result. */
+ struct addrinfo *info;
+ /** Next element in the list. */
+ struct sb_addr_info_el *next;
+};
+/** Typedef to structure used to manage an addrinfo list. */
+typedef struct sb_addr_info_el sb_addr_info_t;
+
+/** Function pointer defining the prototype of a filter function.*/
+typedef int (*sandbox_filter_func_t)(scmp_filter_ctx ctx,
+ sandbox_cfg_t *filter);
+
+/** Type that will be used in step 3 in order to manage multiple sandboxes.*/
+typedef struct {
+ /** function pointers associated with the filter */
+ sandbox_filter_func_t *filter_func;
+
+ /** filter function pointer parameters */
+ sandbox_cfg_t *filter_dynamic;
+} sandbox_t;
+
+/**
+ * Linux 32 bit definitions
+ */
+#if defined(__i386__)
+
+#define REG_SYSCALL REG_EAX
+
+/**
+ * Linux 64 bit definitions
+ */
+#elif defined(__x86_64__)
+
+#define REG_SYSCALL REG_RAX
+
+#endif
+
+#endif // USE_LIBSECCOMP
+
+#ifdef USE_LIBSECCOMP
+/** Pre-calls getaddrinfo in order to pre-record result. */
+int sandbox_add_addrinfo(const char *addr);
+
+struct addrinfo;
+/** Replacement for getaddrinfo(), using pre-recorded results. */
+int sandbox_getaddrinfo(const char *name, const char *servname,
+ const struct addrinfo *hints,
+ struct addrinfo **res);
+#else
+#define sandbox_getaddrinfo(name, servname, hints, res) \
+ getaddrinfo((name),(servname), (hints),(res))
+#define sandbox_add_addrinfo(name) \
+ ((void)(name))
+#endif
+
+#ifdef USE_LIBSECCOMP
+/** Returns a registered protected string used with the sandbox, given that
+ * it matches the parameter.
+ */
+const char* sandbox_intern_string(const char *param);
+#else
+#define sandbox_intern_string(s) (s)
+#endif
+
+/** Creates an empty sandbox configuration file.*/
+sandbox_cfg_t * sandbox_cfg_new(void);
+
+/**
+ * Function used to add a open allowed filename to a supplied configuration.
+ * The (char*) specifies the path to the allowed file, fr = 1 tells the
+ * function that the char* needs to be free-ed, 0 means the pointer does not
+ * need to be free-ed.
+ */
+int sandbox_cfg_allow_open_filename(sandbox_cfg_t **cfg, char *file,
+ int fr);
+
+/** Function used to add a series of open allowed filenames to a supplied
+ * configuration.
+ * @param cfg sandbox configuration.
+ * @param ... all future parameters are specified as pairs of <(char*), 1 / 0>
+ * the char* specifies the path to the allowed file, 1 tells the function
+ * that the char* needs to be free-ed, 0 means the pointer does not need to
+ * be free-ed; the final parameter needs to be <NULL, 0>.
+ */
+int sandbox_cfg_allow_open_filename_array(sandbox_cfg_t **cfg, ...);
+
+/**
+ * Function used to add a openat allowed filename to a supplied configuration.
+ * The (char*) specifies the path to the allowed file, fr = 1 tells the
+ * function that the char* needs to be free-ed, 0 means the pointer does not
+ * need to be free-ed.
+ */
+int sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file,
+ int fr);
+
+/** Function used to add a series of openat allowed filenames to a supplied
+ * configuration.
+ * @param cfg sandbox configuration.
+ * @param ... all future parameters are specified as pairs of <(char*), 1 / 0>
+ * the char* specifies the path to the allowed file, 1 tells the function
+ * that the char* needs to be free-ed, 0 means the pointer does not need to
+ * be free-ed; the final parameter needs to be <NULL, 0>.
+ */
+int sandbox_cfg_allow_openat_filename_array(sandbox_cfg_t **cfg, ...);
+
+/**
+ * Function used to add a execve allowed filename to a supplied configuration.
+ * The (char*) specifies the path to the allowed file, fr = 1 tells the
+ * function that the char* needs to be free-ed, 0 means the pointer does not
+ * need to be free-ed.
+ */
+int sandbox_cfg_allow_execve(sandbox_cfg_t **cfg, const char *com);
+
+/** Function used to add a series of execve allowed filenames to a supplied
+ * configuration.
+ * @param cfg sandbox configuration.
+ * @param ... all future parameters are specified as pairs of <(char*), 1 / 0>
+ * the char* specifies the path to the allowed file, 1 tells the function
+ * that the char* needs to be free-ed, 0 means the pointer does not need to
+ * be free-ed; the final parameter needs to be <NULL, 0>.
+ */
+int sandbox_cfg_allow_execve_array(sandbox_cfg_t **cfg, ...);
+
+/**
+ * Function used to add a stat/stat64 allowed filename to a configuration.
+ * The (char*) specifies the path to the allowed file, fr = 1 tells the
+ * function that the char* needs to be free-ed, 0 means the pointer does not
+ * need to be free-ed.
+ */
+int sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file,
+ int fr);
+
+/** Function used to add a series of stat64 allowed filenames to a supplied
+ * configuration.
+ * @param cfg sandbox configuration.
+ * @param ... all future parameters are specified as pairs of <(char*), 1 / 0>
+ * the char* specifies the path to the allowed file, 1 tells the function
+ * that the char* needs to be free-ed, 0 means the pointer does not need to
+ * be free-ed; the final parameter needs to be <NULL, 0>.
+ */
+int sandbox_cfg_allow_stat_filename_array(sandbox_cfg_t **cfg, ...);
+
+/** Function used to initialise a sandbox configuration.*/
+int sandbox_init(sandbox_cfg_t* cfg);
+
+#endif /* SANDBOX_H_ */
+
diff --git a/src/common/testsupport.h b/src/common/testsupport.h
new file mode 100644
index 000000000..4a4f50b69
--- /dev/null
+++ b/src/common/testsupport.h
@@ -0,0 +1,80 @@
+/* Copyright (c) 2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_TESTSUPPORT_H
+#define TOR_TESTSUPPORT_H
+
+#ifdef TOR_UNIT_TESTS
+#define STATIC
+#else
+#define STATIC static
+#endif
+
+/** Quick and dirty macros to implement test mocking.
+ *
+ * To use them, suppose that you have a function you'd like to mock
+ * with the signature "void writebuf(size_t n, char *buf)". You can then
+ * declare the function as:
+ *
+ * MOCK_DECL(void, writebuf, (size_t n, char *buf));
+ *
+ * and implement it as:
+ *
+ * MOCK_IMPL(void
+ * writebuf,(size_t n, char *buf)
+ * {
+ * ...
+ * }
+ *
+ * For the non-testing build, this will expand simply into:
+ *
+ * void writebuf(size_t n, char *buf);
+ * void
+ * writebuf(size_t n, char *buf)
+ * {
+ * ...
+ * }
+ *
+ * But for the testing case, it will expand into:
+ *
+ * void writebuf__real(size_t n, char *buf);
+ * extern void (*writebuf)(size_t n, char *buf);
+ *
+ * void (*writebuf)(size_t n, char *buf) = writebuf__real;
+ * void
+ * writebuf__real(size_t n, char *buf)
+ * {
+ * ...
+ * }
+ *
+ * This is not a great mocking system! It is deliberately "the simplest
+ * thing that could work", and pays for its simplicity in its lack of
+ * features, and in its uglification of the Tor code. Replacing it with
+ * something clever would be a fine thing.
+ *
+ * @{ */
+#ifdef TOR_UNIT_TESTS
+#define MOCK_DECL(rv, funcname, arglist) \
+ rv funcname ##__real arglist; \
+ extern rv(*funcname) arglist
+#define MOCK_IMPL(rv, funcname, arglist) \
+ rv(*funcname) arglist = funcname ##__real; \
+ rv funcname ##__real arglist
+#define MOCK(func, replacement) \
+ do { \
+ (func) = (replacement); \
+ } while (0)
+#define UNMOCK(func) \
+ do { \
+ func = func ##__real; \
+ } while (0)
+#else
+#define MOCK_DECL(rv, funcname, arglist) \
+ rv funcname arglist
+#define MOCK_IMPL(rv, funcname, arglist) \
+ rv funcname arglist
+#endif
+/** @} */
+
+#endif
+
diff --git a/src/common/torgzip.c b/src/common/torgzip.c
index 4328c63c8..15451ee30 100644
--- a/src/common/torgzip.c
+++ b/src/common/torgzip.c
@@ -68,6 +68,22 @@ is_gzip_supported(void)
return gzip_is_supported;
}
+/** Return a string representation of the version of the currently running
+ * version of zlib. */
+const char *
+tor_zlib_get_version_str(void)
+{
+ return zlibVersion();
+}
+
+/** Return a string representation of the version of the version of zlib
+* used at compilation. */
+const char *
+tor_zlib_get_header_version_str(void)
+{
+ return ZLIB_VERSION;
+}
+
/** Return the 'bits' value to tell zlib to use <b>method</b>.*/
static INLINE int
method_bits(compress_method_t method)
diff --git a/src/common/torgzip.h b/src/common/torgzip.h
index be1016445..5db03fe6e 100644
--- a/src/common/torgzip.h
+++ b/src/common/torgzip.h
@@ -32,6 +32,12 @@ tor_gzip_uncompress(char **out, size_t *out_len,
int is_gzip_supported(void);
+const char *
+tor_zlib_get_version_str(void);
+
+const char *
+tor_zlib_get_header_version_str(void);
+
compress_method_t detect_compression_method(const char *in, size_t in_len);
/** Return values from tor_zlib_process; see that function's documentation for
diff --git a/src/common/torlog.h b/src/common/torlog.h
index 8675d7b6e..d210c8b24 100644
--- a/src/common/torlog.h
+++ b/src/common/torlog.h
@@ -102,6 +102,9 @@
/** This log message is not safe to send to a callback-based logger
* immediately. Used as a flag, not a log domain. */
#define LD_NOCB (1u<<31)
+/** This log message should not include a function name, even if it otherwise
+ * would. Used as a flag, not a log domain. */
+#define LD_NOFUNCNAME (1u<<30)
/** Mask of zero or more log domains, OR'd together. */
typedef uint32_t log_domain_mask_t;
@@ -114,12 +117,6 @@ typedef struct log_severity_list_t {
log_domain_mask_t masks[LOG_DEBUG-LOG_ERR+1];
} log_severity_list_t;
-#ifdef LOG_PRIVATE
-/** Given a severity, yields an index into log_severity_list_t.masks to use
- * for that severity. */
-#define SEVERITY_MASK_IDX(sev) ((sev) - LOG_ERR)
-#endif
-
/** Callback type used for add_callback_log. */
typedef void (*log_callback)(int severity, uint32_t domain, const char *msg);
@@ -154,6 +151,10 @@ void set_log_time_granularity(int granularity_msec);
void tor_log(int severity, log_domain_mask_t domain, const char *format, ...)
CHECK_PRINTF(3,4);
+void tor_log_err_sigsafe(const char *m, ...);
+int tor_log_get_sigsafe_err_fds(const int **out);
+void tor_log_update_sigsafe_err_fds(void);
+
#if defined(__GNUC__) || defined(RUNNING_DOXYGEN)
extern int log_global_min_severity_;
diff --git a/src/common/tortls.c b/src/common/tortls.c
index 60444f1b8..3d6efeba0 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -48,9 +48,6 @@
#include "compat_libevent.h"
#endif
-#define CRYPTO_PRIVATE /* to import prototypes from crypto.h */
-#define TORTLS_PRIVATE
-
#include "crypto.h"
#include "tortls.h"
#include "util.h"
@@ -806,24 +803,24 @@ tor_cert_new(X509 *x509_cert)
tor_cert_t *cert;
EVP_PKEY *pkey;
RSA *rsa;
- int length, length2;
- unsigned char *cp;
+ int length;
+ unsigned char *buf = NULL;
if (!x509_cert)
return NULL;
- length = i2d_X509(x509_cert, NULL);
+ length = i2d_X509(x509_cert, &buf);
cert = tor_malloc_zero(sizeof(tor_cert_t));
- if (length <= 0) {
+ if (length <= 0 || buf == NULL) {
tor_free(cert);
log_err(LD_CRYPTO, "Couldn't get length of encoded x509 certificate");
X509_free(x509_cert);
return NULL;
}
cert->encoded_len = (size_t) length;
- cp = cert->encoded = tor_malloc(length);
- length2 = i2d_X509(x509_cert, &cp);
- tor_assert(length2 == length);
+ cert->encoded = tor_malloc(length);
+ memcpy(cert->encoded, buf, length);
+ OPENSSL_free(buf);
cert->cert = x509_cert;
@@ -979,31 +976,6 @@ tor_tls_cert_get_key(tor_cert_t *cert)
return result;
}
-/** Return true iff <b>a</b> and <b>b</b> represent the same public key. */
-static int
-pkey_eq(EVP_PKEY *a, EVP_PKEY *b)
-{
- /* We'd like to do this, but openssl 0.9.7 doesn't have it:
- return EVP_PKEY_cmp(a,b) == 1;
- */
- unsigned char *a_enc=NULL, *b_enc=NULL, *a_ptr, *b_ptr;
- int a_len1, b_len1, a_len2, b_len2, result;
- a_len1 = i2d_PublicKey(a, NULL);
- b_len1 = i2d_PublicKey(b, NULL);
- if (a_len1 != b_len1)
- return 0;
- a_ptr = a_enc = tor_malloc(a_len1);
- b_ptr = b_enc = tor_malloc(b_len1);
- a_len2 = i2d_PublicKey(a, &a_ptr);
- b_len2 = i2d_PublicKey(b, &b_ptr);
- tor_assert(a_len2 == a_len1);
- tor_assert(b_len2 == b_len1);
- result = tor_memeq(a_enc, b_enc, a_len1);
- tor_free(a_enc);
- tor_free(b_enc);
- return result;
-}
-
/** Return true iff the other side of <b>tls</b> has authenticated to us, and
* the key certified in <b>cert</b> is the same as the key they used to do it.
*/
@@ -1019,7 +991,7 @@ tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert)
link_key = X509_get_pubkey(peercert);
cert_key = X509_get_pubkey(cert->cert);
- result = link_key && cert_key && pkey_eq(cert_key, link_key);
+ result = link_key && cert_key && EVP_PKEY_cmp(cert_key, link_key) == 1;
X509_free(peercert);
if (link_key)
@@ -1418,6 +1390,21 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
return NULL;
}
+/** Invoked when a TLS state changes: log the change at severity 'debug' */
+static void
+tor_tls_debug_state_callback(const SSL *ssl, int type, int val)
+{
+ log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].",
+ ssl, SSL_state_string_long(ssl), type, val);
+}
+
+/* Return the name of the negotiated ciphersuite in use on <b>tls</b> */
+const char *
+tor_tls_get_ciphersuite_name(tor_tls_t *tls)
+{
+ return SSL_get_cipher(tls->ssl);
+}
+
#ifdef V2_HANDSHAKE_SERVER
/* Here's the old V2 cipher list we sent from 0.2.1.1-alpha up to
@@ -1486,13 +1473,6 @@ prune_v2_cipher_list(void)
v2_cipher_list_pruned = 1;
}
-/* Return the name of the negotiated ciphersuite in use on <b>tls</b> */
-const char *
-tor_tls_get_ciphersuite_name(tor_tls_t *tls)
-{
- return SSL_get_cipher(tls->ssl);
-}
-
/** Examine the client cipher list in <b>ssl</b>, and determine what kind of
* client it is. Return one of CIPHERS_ERR, CIPHERS_V1, CIPHERS_V2,
* CIPHERS_UNRESTRICTED.
@@ -1591,56 +1571,6 @@ tor_tls_client_is_using_v2_ciphers(const SSL *ssl)
return tor_tls_classify_client_ciphers(ssl, session->ciphers) >= CIPHERS_V2;
}
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0)
-/** Callback to get invoked on a server after we've read the list of ciphers
- * the client supports, but before we pick our own ciphersuite.
- *
- * We can't abuse an info_cb for this, since by the time one of the
- * client_hello info_cbs is called, we've already picked which ciphersuite to
- * use.
- *
- * Technically, this function is an abuse of this callback, since the point of
- * a session_secret_cb is to try to set up and/or verify a shared-secret for
- * authentication on the fly. But as long as we return 0, we won't actually be
- * setting up a shared secret, and all will be fine.
- */
-static int
-tor_tls_session_secret_cb(SSL *ssl, void *secret, int *secret_len,
- STACK_OF(SSL_CIPHER) *peer_ciphers,
- SSL_CIPHER **cipher, void *arg)
-{
- (void) secret;
- (void) secret_len;
- (void) peer_ciphers;
- (void) cipher;
- (void) arg;
-
- if (tor_tls_classify_client_ciphers(ssl, peer_ciphers) ==
- CIPHERS_UNRESTRICTED) {
- SSL_set_cipher_list(ssl, UNRESTRICTED_SERVER_CIPHER_LIST);
- }
-
- SSL_set_session_secret_cb(ssl, NULL, NULL);
-
- return 0;
-}
-static void
-tor_tls_setup_session_secret_cb(tor_tls_t *tls)
-{
- SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL);
-}
-#else
-#define tor_tls_setup_session_secret_cb(tls) STMT_NIL
-#endif
-
-/** Invoked when a TLS state changes: log the change at severity 'debug' */
-static void
-tor_tls_debug_state_callback(const SSL *ssl, int type, int val)
-{
- log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].",
- ssl, SSL_state_string_long(ssl), type, val);
-}
-
/** Invoked when we're accepting a connection on <b>ssl</b>, and the connection
* changes state. We use this:
* <ul><li>To alter the state of the handshake partway through, so we
@@ -1700,6 +1630,48 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
}
#endif
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0)
+/** Callback to get invoked on a server after we've read the list of ciphers
+ * the client supports, but before we pick our own ciphersuite.
+ *
+ * We can't abuse an info_cb for this, since by the time one of the
+ * client_hello info_cbs is called, we've already picked which ciphersuite to
+ * use.
+ *
+ * Technically, this function is an abuse of this callback, since the point of
+ * a session_secret_cb is to try to set up and/or verify a shared-secret for
+ * authentication on the fly. But as long as we return 0, we won't actually be
+ * setting up a shared secret, and all will be fine.
+ */
+static int
+tor_tls_session_secret_cb(SSL *ssl, void *secret, int *secret_len,
+ STACK_OF(SSL_CIPHER) *peer_ciphers,
+ SSL_CIPHER **cipher, void *arg)
+{
+ (void) secret;
+ (void) secret_len;
+ (void) peer_ciphers;
+ (void) cipher;
+ (void) arg;
+
+ if (tor_tls_classify_client_ciphers(ssl, peer_ciphers) ==
+ CIPHERS_UNRESTRICTED) {
+ SSL_set_cipher_list(ssl, UNRESTRICTED_SERVER_CIPHER_LIST);
+ }
+
+ SSL_set_session_secret_cb(ssl, NULL, NULL);
+
+ return 0;
+}
+static void
+tor_tls_setup_session_secret_cb(tor_tls_t *tls)
+{
+ SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL);
+}
+#else
+#define tor_tls_setup_session_secret_cb(tls) STMT_NIL
+#endif
+
/** Explain which ciphers we're missing. */
static void
log_unsupported_ciphers(smartlist_t *unsupported)
diff --git a/src/common/util.c b/src/common/util.c
index 5eb0f9a69..054de3dbe 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -24,6 +24,8 @@
#include "torint.h"
#include "container.h"
#include "address.h"
+#include "sandbox.h"
+#include "backtrace.h"
#ifdef _WIN32
#include <io.h>
@@ -94,6 +96,23 @@
#endif
/* =====
+ * Assertion helper.
+ * ===== */
+/** Helper for tor_assert: report the assertion failure. */
+void
+tor_assertion_failed_(const char *fname, unsigned int line,
+ const char *func, const char *expr)
+{
+ char buf[256];
+ log_err(LD_BUG, "%s:%u: %s: Assertion %s failed; aborting.",
+ fname, line, func, expr);
+ tor_snprintf(buf, sizeof(buf),
+ "Assertion %s failed in %s at %s:%u",
+ expr, func, fname, line);
+ log_backtrace(LOG_ERR, LD_BUG, buf);
+}
+
+/* =====
* Memory management
* ===== */
#ifdef USE_DMALLOC
@@ -879,6 +898,39 @@ tor_digest_is_zero(const char *digest)
return tor_memeq(digest, ZERO_DIGEST, DIGEST_LEN);
}
+/** Return true if <b>string</b> is a valid '<key>=[<value>]' string.
+ * <value> is optional, to indicate the empty string. Log at logging
+ * <b>severity</b> if something ugly happens. */
+int
+string_is_key_value(int severity, const char *string)
+{
+ /* position of equal sign in string */
+ const char *equal_sign_pos = NULL;
+
+ tor_assert(string);
+
+ if (strlen(string) < 2) { /* "x=" is shortest args string */
+ tor_log(severity, LD_GENERAL, "'%s' is too short to be a k=v value.",
+ escaped(string));
+ return 0;
+ }
+
+ equal_sign_pos = strchr(string, '=');
+ if (!equal_sign_pos) {
+ tor_log(severity, LD_GENERAL, "'%s' is not a k=v value.", escaped(string));
+ return 0;
+ }
+
+ /* validate that the '=' is not in the beginning of the string. */
+ if (equal_sign_pos == string) {
+ tor_log(severity, LD_GENERAL, "'%s' is not a valid k=v value.",
+ escaped(string));
+ return 0;
+ }
+
+ return 1;
+}
+
/** Return true iff the DIGEST256_LEN bytes in digest are all zero. */
int
tor_digest256_is_zero(const char *digest)
@@ -1190,6 +1242,43 @@ escaped(const char *s)
return escaped_val_;
}
+/** Return a newly allocated string equal to <b>string</b>, except that every
+ * character in <b>chars_to_escape</b> is preceded by a backslash. */
+char *
+tor_escape_str_for_pt_args(const char *string, const char *chars_to_escape)
+{
+ char *new_string = NULL;
+ char *new_cp = NULL;
+ size_t length, new_length;
+
+ tor_assert(string);
+
+ length = strlen(string);
+
+ if (!length) /* If we were given the empty string, return the same. */
+ return tor_strdup("");
+ /* (new_length > SIZE_MAX) => ((length * 2) + 1 > SIZE_MAX) =>
+ (length*2 > SIZE_MAX - 1) => (length > (SIZE_MAX - 1)/2) */
+ if (length > (SIZE_MAX - 1)/2) /* check for overflow */
+ return NULL;
+
+ /* this should be enough even if all characters must be escaped */
+ new_length = (length * 2) + 1;
+
+ new_string = new_cp = tor_malloc(new_length);
+
+ while (*string) {
+ if (strchr(chars_to_escape, *string))
+ *new_cp++ = '\\';
+
+ *new_cp++ = *string++;
+ }
+
+ *new_cp = '\0'; /* NUL-terminate the new string */
+
+ return new_string;
+}
+
/* =====
* Time
* ===== */
@@ -1741,7 +1830,7 @@ file_status(const char *fname)
int r;
f = tor_strdup(fname);
clean_name_for_stat(f);
- r = stat(f, &st);
+ r = stat(sandbox_intern_string(f), &st);
tor_free(f);
if (r) {
if (errno == ENOENT) {
@@ -1791,7 +1880,7 @@ check_private_dir(const char *dirname, cpd_check_t check,
tor_assert(dirname);
f = tor_strdup(dirname);
clean_name_for_stat(f);
- r = stat(f, &st);
+ r = stat(sandbox_intern_string(f), &st);
tor_free(f);
if (r) {
if (errno != ENOENT) {
@@ -1977,8 +2066,10 @@ start_writing_to_file(const char *fname, int open_flags, int mode,
open_flags &= ~O_EXCL;
new_file->rename_on_close = 1;
}
+#if O_BINARY != 0
if (open_flags & O_BINARY)
new_file->binary = 1;
+#endif
new_file->fd = tor_open_cloexec(open_name, open_flags, mode);
if (new_file->fd < 0) {
@@ -2132,12 +2223,20 @@ write_chunks_to_file_impl(const char *fname, const smartlist_t *chunks,
return -1;
}
-/** Given a smartlist of sized_chunk_t, write them atomically to a file
- * <b>fname</b>, overwriting or creating the file as necessary. */
+/** Given a smartlist of sized_chunk_t, write them to a file
+ * <b>fname</b>, overwriting or creating the file as necessary.
+ * If <b>no_tempfile</b> is 0 then the file will be written
+ * atomically. */
int
-write_chunks_to_file(const char *fname, const smartlist_t *chunks, int bin)
+write_chunks_to_file(const char *fname, const smartlist_t *chunks, int bin,
+ int no_tempfile)
{
int flags = OPEN_FLAGS_REPLACE|(bin?O_BINARY:O_TEXT);
+
+ if (no_tempfile) {
+ /* O_APPEND stops write_chunks_to_file from using tempfiles */
+ flags |= O_APPEND;
+ }
return write_chunks_to_file_impl(fname, chunks, flags);
}
@@ -2158,9 +2257,9 @@ write_bytes_to_file_impl(const char *fname, const char *str, size_t len,
/** As write_str_to_file, but does not assume a NUL-terminated
* string. Instead, we write <b>len</b> bytes, starting at <b>str</b>. */
-int
-write_bytes_to_file(const char *fname, const char *str, size_t len,
- int bin)
+MOCK_IMPL(int,
+write_bytes_to_file,(const char *fname, const char *str, size_t len,
+ int bin))
{
return write_bytes_to_file_impl(fname, str, len,
OPEN_FLAGS_REPLACE|(bin?O_BINARY:O_TEXT));
@@ -3022,9 +3121,10 @@ tor_listdir(const char *dirname)
FindClose(handle);
tor_free(pattern);
#else
+ const char *prot_dname = sandbox_intern_string(dirname);
DIR *d;
struct dirent *de;
- if (!(d = opendir(dirname)))
+ if (!(d = opendir(prot_dname)))
return NULL;
result = smartlist_new();
@@ -3320,14 +3420,59 @@ tor_join_win_cmdline(const char *argv[])
return joined_argv;
}
+/* As format_{hex,dex}_number_sigsafe, but takes a <b>radix</b> argument
+ * in range 2..16 inclusive. */
+static int
+format_number_sigsafe(unsigned long x, char *buf, int buf_len,
+ unsigned int radix)
+{
+ unsigned long tmp;
+ int len;
+ char *cp;
+
+ /* NOT tor_assert. This needs to be safe to run from within a signal handler,
+ * and from within the 'tor_assert() has failed' code. */
+ if (radix < 2 || radix > 16)
+ return 0;
+
+ /* Count how many digits we need. */
+ tmp = x;
+ len = 1;
+ while (tmp >= radix) {
+ tmp /= radix;
+ ++len;
+ }
+
+ /* Not long enough */
+ if (!buf || len >= buf_len)
+ return 0;
+
+ cp = buf + len;
+ *cp = '\0';
+ do {
+ unsigned digit = x % radix;
+ tor_assert(cp > buf);
+ --cp;
+ *cp = "0123456789ABCDEF"[digit];
+ x /= radix;
+ } while (x);
+
+ /* NOT tor_assert; see above. */
+ if (cp != buf) {
+ abort();
+ }
+
+ return len;
+}
+
/**
- * Helper function to output hex numbers, called by
- * format_helper_exit_status(). This writes the hexadecimal digits of x into
- * buf, up to max_len digits, and returns the actual number of digits written.
- * If there is insufficient space, it will write nothing and return 0.
+ * Helper function to output hex numbers from within a signal handler.
+ *
+ * Writes the nul-terminated hexadecimal digits of <b>x</b> into a buffer
+ * <b>buf</b> of size <b>buf_len</b>, and return the actual number of digits
+ * written, not counting the terminal NUL.
*
- * This function DOES NOT add a terminating NUL character to its output: be
- * careful!
+ * If there is insufficient space, write nothing and return 0.
*
* This accepts an unsigned int because format_helper_exit_status() needs to
* call it with a signed int and an unsigned char, and since the C standard
@@ -3342,46 +3487,19 @@ tor_join_win_cmdline(const char *argv[])
* arbitrary C functions.
*/
int
-format_hex_number_for_helper_exit_status(unsigned int x, char *buf,
- int max_len)
+format_hex_number_sigsafe(unsigned long x, char *buf, int buf_len)
{
- int len;
- unsigned int tmp;
- char *cur;
-
- /* Sanity check */
- if (!buf || max_len <= 0)
- return 0;
-
- /* How many chars do we need for x? */
- if (x > 0) {
- len = 0;
- tmp = x;
- while (tmp > 0) {
- tmp >>= 4;
- ++len;
- }
- } else {
- len = 1;
- }
-
- /* Bail if we would go past the end of the buffer */
- if (len > max_len)
- return 0;
-
- /* Point to last one */
- cur = buf + len - 1;
-
- /* Convert x to hex */
- do {
- *cur-- = "0123456789ABCDEF"[x & 0xf];
- x >>= 4;
- } while (x != 0 && cur >= buf);
+ return format_number_sigsafe(x, buf, buf_len, 16);
+}
- /* Return len */
- return len;
+/** As format_hex_number_sigsafe, but format the number in base 10. */
+int
+format_dec_number_sigsafe(unsigned long x, char *buf, int buf_len)
+{
+ return format_number_sigsafe(x, buf, buf_len, 10);
}
+#ifndef _WIN32
/** Format <b>child_state</b> and <b>saved_errno</b> as a hex string placed in
* <b>hex_errno</b>. Called between fork and _exit, so must be signal-handler
* safe.
@@ -3397,7 +3515,7 @@ format_hex_number_for_helper_exit_status(unsigned int x, char *buf,
* On success return the number of characters added to hex_errno, not counting
* the terminating NUL; return -1 on error.
*/
-int
+STATIC int
format_helper_exit_status(unsigned char child_state, int saved_errno,
char *hex_errno)
{
@@ -3428,8 +3546,8 @@ format_helper_exit_status(unsigned char child_state, int saved_errno,
cur = hex_errno;
/* Emit child_state */
- written = format_hex_number_for_helper_exit_status(child_state,
- cur, left);
+ written = format_hex_number_sigsafe(child_state, cur, left);
+
if (written <= 0)
goto err;
@@ -3458,8 +3576,7 @@ format_helper_exit_status(unsigned char child_state, int saved_errno,
}
/* Emit unsigned_errno */
- written = format_hex_number_for_helper_exit_status(unsigned_errno,
- cur, left);
+ written = format_hex_number_sigsafe(unsigned_errno, cur, left);
if (written <= 0)
goto err;
@@ -3490,6 +3607,7 @@ format_helper_exit_status(unsigned char child_state, int saved_errno,
done:
return res;
}
+#endif
/* Maximum number of file descriptors, if we cannot get it via sysconf() */
#define DEFAULT_MAX_FD 256
@@ -3685,7 +3803,7 @@ tor_spawn_background(const char *const filename, const char **argv,
TRUE, // handles are inherited
/*(TODO: set CREATE_NEW CONSOLE/PROCESS_GROUP to make GetExitCodeProcess()
* work?) */
- 0, // creation flags
+ CREATE_NO_WINDOW, // creation flags
(env==NULL) ? NULL : env->windows_environment_block,
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
@@ -3906,9 +4024,9 @@ tor_spawn_background(const char *const filename, const char **argv,
* <b>process_handle</b>.
* If <b>also_terminate_process</b> is true, also terminate the
* process of the process handle. */
-void
-tor_process_handle_destroy(process_handle_t *process_handle,
- int also_terminate_process)
+MOCK_IMPL(void,
+tor_process_handle_destroy,(process_handle_t *process_handle,
+ int also_terminate_process))
{
if (!process_handle)
return;
@@ -4417,9 +4535,9 @@ stream_status_to_string(enum stream_status stream_status)
/** Return a smartlist containing lines outputted from
* <b>handle</b>. Return NULL on error, and set
* <b>stream_status_out</b> appropriately. */
-smartlist_t *
-tor_get_lines_from_handle(HANDLE *handle,
- enum stream_status *stream_status_out)
+MOCK_IMPL(smartlist_t *,
+tor_get_lines_from_handle, (HANDLE *handle,
+ enum stream_status *stream_status_out))
{
int pos;
char stdout_buf[600] = {0};
@@ -4507,8 +4625,9 @@ log_from_handle(HANDLE *pipe, int severity)
/** Return a smartlist containing lines outputted from
* <b>handle</b>. Return NULL on error, and set
* <b>stream_status_out</b> appropriately. */
-smartlist_t *
-tor_get_lines_from_handle(FILE *handle, enum stream_status *stream_status_out)
+MOCK_IMPL(smartlist_t *,
+tor_get_lines_from_handle, (FILE *handle,
+ enum stream_status *stream_status_out))
{
enum stream_status stream_status;
char stdout_buf[400];
diff --git a/src/common/util.h b/src/common/util.h
index 73daa6e2a..18dc20639 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -15,6 +15,7 @@
#include "torint.h"
#include "compat.h"
#include "di_ops.h"
+#include "testsupport.h"
#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN32
@@ -47,13 +48,13 @@
/** Like assert(3), but send assertion failures to the log as well as to
* stderr. */
#define tor_assert(expr) STMT_BEGIN \
- if (PREDICT_UNLIKELY(!(expr))) { \
- log_err(LD_BUG, "%s:%d: %s: Assertion %s failed; aborting.", \
- SHORT_FILE__, __LINE__, __func__, #expr); \
- fprintf(stderr,"%s:%d %s: Assertion %s failed; aborting.\n", \
- SHORT_FILE__, __LINE__, __func__, #expr); \
- abort(); \
- } STMT_END
+ if (PREDICT_UNLIKELY(!(expr))) { \
+ tor_assertion_failed_(SHORT_FILE__, __LINE__, __func__, #expr); \
+ abort(); \
+ } STMT_END
+
+void tor_assertion_failed_(const char *fname, unsigned int line,
+ const char *func, const char *expr);
/* If we're building with dmalloc, we want all of our memory allocation
* functions to take an extra file/line pair of arguments. If not, not.
@@ -222,23 +223,22 @@ const char *find_whitespace_eos(const char *s, const char *eos);
const char *find_str_at_start_of_line(const char *haystack,
const char *needle);
int string_is_C_identifier(const char *string);
+int string_is_key_value(int severity, const char *string);
int tor_mem_is_zero(const char *mem, size_t len);
int tor_digest_is_zero(const char *digest);
int tor_digest256_is_zero(const char *digest);
char *esc_for_log(const char *string) ATTR_MALLOC;
const char *escaped(const char *string);
+
+char *tor_escape_str_for_pt_args(const char *string,
+ const char *chars_to_escape);
+
struct smartlist_t;
-int tor_vsscanf(const char *buf, const char *pattern, va_list ap)
-#ifdef __GNUC__
- __attribute__((format(scanf, 2, 0)))
-#endif
- ;
+int tor_vsscanf(const char *buf, const char *pattern, va_list ap) \
+ CHECK_SCANF(2, 0);
int tor_sscanf(const char *buf, const char *pattern, ...)
-#ifdef __GNUC__
- __attribute__((format(scanf, 2, 3)))
-#endif
- ;
+ CHECK_SCANF(2, 3);
void smartlist_add_asprintf(struct smartlist_t *sl, const char *pattern, ...)
CHECK_PRINTF(2, 3);
@@ -356,8 +356,9 @@ FILE *fdopen_file(open_file_t *file_data);
int finish_writing_to_file(open_file_t *file_data);
int abort_writing_to_file(open_file_t *file_data);
int write_str_to_file(const char *fname, const char *str, int bin);
-int write_bytes_to_file(const char *fname, const char *str, size_t len,
- int bin);
+MOCK_DECL(int,
+write_bytes_to_file,(const char *fname, const char *str, size_t len,
+ int bin));
/** An ad-hoc type to hold a string of characters and a count; used by
* write_chunks_to_file. */
typedef struct sized_chunk_t {
@@ -365,7 +366,7 @@ typedef struct sized_chunk_t {
size_t len;
} sized_chunk_t;
int write_chunks_to_file(const char *fname, const struct smartlist_t *chunks,
- int bin);
+ int bin, int no_tempfile);
int append_bytes_to_file(const char *fname, const char *str, size_t len,
int bin);
int write_bytes_to_new_file(const char *fname, const char *str, size_t len,
@@ -493,18 +494,21 @@ FILE *tor_process_get_stdout_pipe(process_handle_t *process_handle);
#endif
#ifdef _WIN32
-struct smartlist_t *
-tor_get_lines_from_handle(HANDLE *handle,
- enum stream_status *stream_status);
+MOCK_DECL(struct smartlist_t *,
+tor_get_lines_from_handle,(HANDLE *handle,
+ enum stream_status *stream_status));
#else
-struct smartlist_t *
-tor_get_lines_from_handle(FILE *handle,
- enum stream_status *stream_status);
+MOCK_DECL(struct smartlist_t *,
+tor_get_lines_from_handle,(FILE *handle,
+ enum stream_status *stream_status));
#endif
-int tor_terminate_process(process_handle_t *process_handle);
-void tor_process_handle_destroy(process_handle_t *process_handle,
- int also_terminate_process);
+int
+tor_terminate_process(process_handle_t *process_handle);
+
+MOCK_DECL(void,
+tor_process_handle_destroy,(process_handle_t *process_handle,
+ int also_terminate_process));
/* ===== Insecure rng */
typedef struct tor_weak_rng_t {
@@ -520,12 +524,14 @@ int32_t tor_weak_random_range(tor_weak_rng_t *rng, int32_t top);
* <b>n</b> */
#define tor_weak_random_one_in_n(rng, n) (0==tor_weak_random_range((rng),(n)))
+int format_hex_number_sigsafe(unsigned long x, char *buf, int max_len);
+int format_dec_number_sigsafe(unsigned long x, char *buf, int max_len);
+
#ifdef UTIL_PRIVATE
/* Prototypes for private functions only used by util.c (and unit tests) */
-int format_hex_number_for_helper_exit_status(unsigned int x, char *buf,
- int max_len);
-int format_helper_exit_status(unsigned char child_state,
+#ifndef _WIN32
+STATIC int format_helper_exit_status(unsigned char child_state,
int saved_errno, char *hex_errno);
/* Space for hex values of child state, a slash, saved_errno (with
@@ -534,7 +540,11 @@ int format_helper_exit_status(unsigned char child_state,
1 + sizeof(int) * 2 + 1)
#endif
+#endif
+
const char *libor_get_digests(void);
+#define ARRAY_LENGTH(x) (sizeof(x)) / sizeof(x[0])
+
#endif
diff --git a/src/config/deanonymind.py b/src/config/deanonymind.py
index c86dadca9..31d0658ee 100755
--- a/src/config/deanonymind.py
+++ b/src/config/deanonymind.py
@@ -156,23 +156,34 @@ def apply_manual_changes(assignments, manual_assignments):
entry['end_num'] == manual_entry['end_num']:
if len(manual_entry['country_code']) != 2:
print '-%s' % (line, ) # only remove, don't replace
- else:
+ del manual_dict[start_num]
+ elif entry['country_code'] != \
+ manual_entry['country_code']:
new_line = format_line_with_other_country(entry,
manual_entry)
print '-%s\n+%s' % (line, new_line, )
result.append(new_line)
- del manual_dict[start_num]
+ del manual_dict[start_num]
+ else:
+ print ('Warning: not applying ineffective manual '
+ 'change:\n %s\n %s' % (line, manual_line, ))
+ result.append(line)
else:
- print ('Warning: only partial match between '
- 'original/automatically replaced assignment and '
- 'manual assignment:\n %s\n %s\nNot applying '
- 'manual change.' % (line, manual_line, ))
+ print ('Warning: not applying manual change that is only '
+ 'a partial match:\n %s\n %s' %
+ (line, manual_line, ))
result.append(line)
+ elif 'country_code' in entry and \
+ entry['country_code'] == 'A1':
+ print ('Warning: no manual replacement for A1 entry:\n %s'
+ % (line, ))
+ result.append(line)
else:
result.append(line)
if len(manual_dict) > 0:
- print ('Warning: could not apply all manual assignments: %s' %
- ('\n '.join(manual_dict.values())), )
+ print 'Warning: could not apply all manual assignments:'
+ for line in manual_dict.values():
+ print ' %s' % (line, )
return result
def write_file(path, assignments, long_format=True):
diff --git a/src/config/geoip-manual b/src/config/geoip-manual
index 99c897ff4..d51a12a1c 100644
--- a/src/config/geoip-manual
+++ b/src/config/geoip-manual
@@ -3,22 +3,11 @@
# directory to process this file when producing a new geoip file. See
# README.geoip in the same directory for details.
-# Remove MaxMind entry 0.116.0.0-0.119.255.255 which MaxMind says is AT,
-# but which is part of reserved range 0.0.0.0/8. -KL 2012-06-13
-# Disabled, because MaxMind apparently removed this range from their
-# database. -KL 2013-02-08
-#"0.116.0.0","0.119.255.255","7602176","7864319","",""
-
-# NL, because previous MaxMind entry 31.171.128.0-31.171.133.255 is NL,
-# and RIR delegation files say 31.171.128.0-31.171.135.255 is NL.
-# -KL 2012-11-27
-"31.171.134.0","31.171.135.255","531334656","531335167","NL","Netherlands"
-
-# EU, because next MaxMind entry 37.139.64.1-37.139.64.9 is EU, because
-# RIR delegation files say 37.139.64.0-37.139.71.255 is EU, and because it
-# just makes more sense for the next entry to start at .0 and not .1.
-# -KL 2012-11-27
-"37.139.64.0","37.139.64.0","629882880","629882880","EU","Europe"
+# GB, because RIR delegation files say exactly this range
+# 46.16.32.0-46.16.39.255 is GB, even though neither previous nor next
+# MaxMind range is GB. Both previous and next MaxMind ranges match RIR
+# delegation files, too. -KL 2013-03-07
+"46.16.32.0","46.16.39.255","772808704","772810751","GB","United Kingdom"
# CH, because previous MaxMind entry 46.19.141.0-46.19.142.255 is CH, and
# RIR delegation files say 46.19.136.0-46.19.143.255 is CH.
@@ -30,23 +19,6 @@
# -KL 2012-11-27
"46.166.128.0","46.166.128.255","782663680","782663935","GB","United Kingdom"
-# US, though could as well be CA. Previous MaxMind entry
-# 64.237.32.52-64.237.34.127 is US, next MaxMind entry
-# 64.237.34.144-64.237.34.151 is CA, and RIR delegation files say the
-# entire block 64.237.32.0-64.237.63.255 is US. -KL 2012-11-27
-"64.237.34.128","64.237.34.143","1089282688","1089282703","US","United States"
-
-# US, though could as well be UY. Previous MaxMind entry
-# 67.15.170.0-67.15.182.255 is US, next MaxMind entry
-# 67.15.183.128-67.15.183.159 is UY, and RIR delegation files say the
-# entire block 67.15.0.0-67.15.255.255 is US. -KL 2012-11-27
-"67.15.183.0","67.15.183.127","1125103360","1125103487","US","United States"
-
-# US, because next MaxMind entry 67.43.145.0-67.43.155.255 is US, and RIR
-# delegation files say 67.43.144.0-67.43.159.255 is US.
-# -KL 2012-11-27
-"67.43.144.0","67.43.144.255","1126928384","1126928639","US","United States"
-
# US, because previous MaxMind entry 70.159.21.51-70.232.244.255 is US,
# because next MaxMind entry 70.232.245.58-70.232.245.59 is A2 ("Satellite
# Provider") which is a country information about as useless as A1, and
@@ -66,45 +38,37 @@
# MaxMind entry and say GB for 91.228.0.0-91.228.3.255. -KL 2012-11-27
"91.228.0.0","91.228.3.255","1541668864","1541669887","GB","United Kingdom"
-# GB, because next MaxMind entry 91.232.125.0-91.232.125.255 is GB, and
-# RIR delegation files say 91.232.124.0-91.232.125.255 is GB.
-# -KL 2012-11-27
-"91.232.124.0","91.232.124.255","1541962752","1541963007","GB","United Kingdom"
-
-# GB, despite neither previous (RU) nor next (PL) MaxMind entry being GB,
-# but because RIR delegation files agree with both previous and next
-# MaxMind entry and say GB for 91.238.214.0-91.238.215.255.
-# -KL 2012-11-27
-"91.238.214.0","91.238.215.255","1542379008","1542379519","GB","United Kingdom"
-
-# US, because next MaxMind entry 173.0.16.0-173.0.65.255 is US, and RIR
-# delegation files say 173.0.0.0-173.0.15.255 is US. -KL 2012-11-27
-"173.0.0.0","173.0.15.255","2902458368","2902462463","US","United States"
-
-# US, because next MaxMind entry 176.67.84.0-176.67.84.79 is US, and RIR
-# delegation files say 176.67.80.0-176.67.87.255 is US. -KL 2012-11-27
-"176.67.80.0","176.67.83.255","2957201408","2957202431","US","United States"
-
-# US, because previous MaxMind entry 176.67.84.192-176.67.85.255 is US,
-# and RIR delegation files say 176.67.80.0-176.67.87.255 is US.
-# -KL 2012-11-27
-"176.67.86.0","176.67.87.255","2957202944","2957203455","US","United States"
-
-# EU, despite neither previous (RU) nor next (UA) MaxMind entry being EU,
-# but because RIR delegation files agree with both previous and next
-# MaxMind entry and say EU for 193.200.150.0-193.200.150.255.
-# -KL 2012-11-27
-"193.200.150.0","193.200.150.255","3251148288","3251148543","EU","Europe"
-
-# US, because previous MaxMind entry 199.96.68.0-199.96.87.127 is US, and
-# RIR delegation files say 199.96.80.0-199.96.87.255 is US.
-# -KL 2012-11-27
-"199.96.87.128","199.96.87.255","3344979840","3344979967","US","United States"
-
-# US, because previous MaxMind entry 209.58.176.144-209.59.31.255 is US,
-# and RIR delegation files say 209.59.32.0-209.59.63.255 is US.
-# -KL 2012-11-27
-"209.59.32.0","209.59.63.255","3510312960","3510321151","US","United States"
+# NL, because next MaxMind entry 176.56.173.0-176.56.173.63 is NL, and RIR
+# delegation files say 176.56.160.0-176.56.191.255 is NL. -KL 2013-05-13
+"176.56.172.0","176.56.172.255","2956504064","2956504319","NL","Netherlands"
+
+# NL, despite neither previous (RU) nor next (GB) MaxMind entry being NL,
+# but because RIR delegation files say entire range
+# 176.56.160.0-176.56.191.255 is NL. -KL 2013-05-13
+"176.56.174.0","176.56.174.255","2956504576","2956504831","NL","Netherlands"
+
+# GB, because RIR delegation files say exactly this range
+# 185.25.84.0-185.25.87.255 is GB, even though neither previous nor next
+# MaxMind range is GB. Both previous and next MaxMind ranges match RIR
+# delegation files, too. -KL 2013-05-13
+"185.25.84.0","185.25.87.255","3105444864","3105445887","GB","United Kingdom"
+
+# US, because next MaxMind entry 199.101.193.0-199.101.195.255 is US, and,
+# together with next entries, matches RIR delegation file entry
+# 199.101.192.0-199.101.199.255 which is US. -KL 2013-05-13
+"199.101.192.0","199.101.192.255","3345334272","3345334527","US","United States"
+
+# US, because ARIN says 199.255.208.0-199.255.215.255 is US.
+# Changed entry start from 199.255.213.0 to 199.255.208.0 on 2013-08-12.
+# Split up into 199.255.208.0-199.255.209.127 and
+# 199.255.210.0-199.255.215.255 on 2013-10-11. -KL 2013-10-11
+"199.255.208.0","199.255.209.127","3355430912","3355431295","US","United States"
+"199.255.210.0","199.255.215.255","3355431424","3355432959","US","United States"
+
+# EU, despite neither previous (RU) nor next (SE) MaxMind entry being EU,
+# but because RIR delegation files agree with previous MaxMind entry and
+# say EU for 217.15.160.0-217.15.175.255. -KL 2013-05-13
+"217.15.160.0","217.15.164.255","3641679872","3641681151","EU","Europe"
# FR, because previous MaxMind entry 217.15.166.0-217.15.166.255 is FR,
# and RIR delegation files contain a block 217.15.160.0-217.15.175.255
diff --git a/src/config/torrc.sample.in b/src/config/torrc.sample.in
index c667efc5c..d842fbcaf 100644
--- a/src/config/torrc.sample.in
+++ b/src/config/torrc.sample.in
@@ -1,5 +1,5 @@
## Configuration file for a typical Tor user
-## Last updated 12 September 2012 for Tor 0.2.4.3-alpha.
+## Last updated 9 October 2013 for Tor 0.2.5.2-alpha.
## (may or may not work for much older or much newer versions of Tor.)
##
## Lines that begin with "## " try to explain what's going on. Lines
@@ -120,9 +120,12 @@
## is per month)
#AccountingStart month 3 15:00
-## Contact info to be published in the directory, so we can contact you
-## if your relay is misconfigured or something else goes wrong. Google
-## indexes this, so spammers might also collect it.
+## Administrative contact information for this relay or bridge. This line
+## can be used to contact you if your relay or bridge is misconfigured or
+## something else goes wrong. Note that we archive and publish all
+## descriptors containing these lines and that Google indexes them, so
+## spammers might also collect them. You may want to obscure the fact that
+## it's an email address and/or generate a new address for this purpose.
#ContactInfo Random Person <nobody AT example dot com>
## You might also include your PGP or GPG fingerprint if you have one:
#ContactInfo 0xFFFFFFFF Random Person <nobody AT example dot com>
diff --git a/src/ext/eventdns.c b/src/ext/eventdns.c
index 66280cccd..8b934c443 100644
--- a/src/ext/eventdns.c
+++ b/src/ext/eventdns.c
@@ -2298,6 +2298,10 @@ _evdns_nameserver_add_impl(const struct sockaddr *address,
evtimer_set(&ns->timeout_event, nameserver_prod_callback, ns);
+#if 1
+ ns->socket = tor_open_socket_nonblocking(address->sa_family, SOCK_DGRAM, 0);
+ if (!SOCKET_OK(ns->socket)) { err = 1; goto out1; }
+#else
ns->socket = tor_open_socket(address->sa_family, SOCK_DGRAM, 0);
if (ns->socket < 0) { err = 1; goto out1; }
#ifdef _WIN32
@@ -2314,6 +2318,7 @@ _evdns_nameserver_add_impl(const struct sockaddr *address,
}
#endif
+#endif /* 1 */
if (global_bind_addr_is_set &&
!sockaddr_is_loopback((struct sockaddr*)&global_bind_address)) {
if (bind(ns->socket, (struct sockaddr *)&global_bind_address,
@@ -3473,8 +3478,12 @@ main(int c, char **v) {
if (servertest) {
int sock;
struct sockaddr_in my_addr;
+#if 1
+ sock = tor_open_socket_nonblocking(PF_INET, SOCK_DGRAM, 0)
+#else
sock = tor_open_socket(PF_INET, SOCK_DGRAM, 0);
fcntl(sock, F_SETFL, O_NONBLOCK);
+#endif
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(10053);
my_addr.sin_addr.s_addr = INADDR_ANY;
diff --git a/src/or/addressmap.c b/src/or/addressmap.c
index 79e4b7c5e..9bc79bd84 100644
--- a/src/or/addressmap.c
+++ b/src/or/addressmap.c
@@ -798,7 +798,7 @@ address_is_in_virtual_range(const char *address)
/** Return a random address conforming to the virtual address configuration
* in <b>conf</b>.
*/
-/* private */ void
+STATIC void
get_random_virtual_addr(const virtual_addr_conf_t *conf, tor_addr_t *addr_out)
{
uint8_t tmp[4];
diff --git a/src/or/addressmap.h b/src/or/addressmap.h
index 40210ee99..417832b31 100644
--- a/src/or/addressmap.h
+++ b/src/or/addressmap.h
@@ -7,6 +7,8 @@
#ifndef TOR_ADDRESSMAP_H
#define TOR_ADDRESSMAP_H
+#include "testsupport.h"
+
void addressmap_init(void);
void addressmap_clear_excluded_trackexithosts(const or_options_t *options);
void addressmap_clear_invalid_automaps(const or_options_t *options);
@@ -52,8 +54,8 @@ typedef struct virtual_addr_conf_t {
maskbits_t bits;
} virtual_addr_conf_t;
-void get_random_virtual_addr(const virtual_addr_conf_t *conf,
- tor_addr_t *addr_out);
+STATIC void get_random_virtual_addr(const virtual_addr_conf_t *conf,
+ tor_addr_t *addr_out);
#endif
#endif
diff --git a/src/or/buffers.c b/src/or/buffers.c
index c4c847ec8..50016d3a8 100644
--- a/src/or/buffers.c
+++ b/src/or/buffers.c
@@ -19,6 +19,7 @@
#include "connection_or.h"
#include "control.h"
#include "reasons.h"
+#include "ext_orport.h"
#include "../common/util.h"
#include "../common/torlog.h"
#ifdef HAVE_UNISTD_H
@@ -1294,7 +1295,7 @@ buf_matches_at_pos(const buf_pos_t *pos, const char *s, size_t n)
/** Return the first position in <b>buf</b> at which the <b>n</b>-character
* string <b>s</b> occurs, or -1 if it does not occur. */
-/*private*/ int
+STATIC int
buf_find_string_offset(const buf_t *buf, const char *s, size_t n)
{
buf_pos_t pos;
@@ -1702,6 +1703,64 @@ fetch_from_evbuffer_socks(struct evbuffer *buf, socks_request_t *req,
}
#endif
+/** The size of the header of an Extended ORPort message: 2 bytes for
+ * COMMAND, 2 bytes for BODYLEN */
+#define EXT_OR_CMD_HEADER_SIZE 4
+
+/** Read <b>buf</b>, which should contain an Extended ORPort message
+ * from a transport proxy. If well-formed, create and populate
+ * <b>out</b> with the Extended ORport message. Return 0 if the
+ * buffer was incomplete, 1 if it was well-formed and -1 if we
+ * encountered an error while parsing it. */
+int
+fetch_ext_or_command_from_buf(buf_t *buf, ext_or_cmd_t **out)
+{
+ char hdr[EXT_OR_CMD_HEADER_SIZE];
+ uint16_t len;
+
+ check();
+ if (buf->datalen < EXT_OR_CMD_HEADER_SIZE)
+ return 0;
+ peek_from_buf(hdr, sizeof(hdr), buf);
+ len = ntohs(get_uint16(hdr+2));
+ if (buf->datalen < (unsigned)len + EXT_OR_CMD_HEADER_SIZE)
+ return 0;
+ *out = ext_or_cmd_new(len);
+ (*out)->cmd = ntohs(get_uint16(hdr));
+ (*out)->len = len;
+ buf_remove_from_front(buf, EXT_OR_CMD_HEADER_SIZE);
+ fetch_from_buf((*out)->body, len, buf);
+ return 1;
+}
+
+#ifdef USE_BUFFEREVENTS
+/** Read <b>buf</b>, which should contain an Extended ORPort message
+ * from a transport proxy. If well-formed, create and populate
+ * <b>out</b> with the Extended ORport message. Return 0 if the
+ * buffer was incomplete, 1 if it was well-formed and -1 if we
+ * encountered an error while parsing it. */
+int
+fetch_ext_or_command_from_evbuffer(struct evbuffer *buf, ext_or_cmd_t **out)
+{
+ char hdr[EXT_OR_CMD_HEADER_SIZE];
+ uint16_t len;
+ size_t buf_len = evbuffer_get_length(buf);
+
+ if (buf_len < EXT_OR_CMD_HEADER_SIZE)
+ return 0;
+ evbuffer_copyout(buf, hdr, EXT_OR_CMD_HEADER_SIZE);
+ len = ntohs(get_uint16(hdr+2));
+ if (buf_len < (unsigned)len + EXT_OR_CMD_HEADER_SIZE)
+ return 0;
+ *out = ext_or_cmd_new(len);
+ (*out)->cmd = ntohs(get_uint16(hdr));
+ (*out)->len = len;
+ evbuffer_drain(buf, EXT_OR_CMD_HEADER_SIZE);
+ evbuffer_remove(buf, (*out)->body, len);
+ return 1;
+}
+#endif
+
/** Implementation helper to implement fetch_from_*_socks. Instead of looking
* at a buffer's contents, we look at the <b>datalen</b> bytes of data in
* <b>data</b>. Instead of removing data from the buffer, we set
diff --git a/src/or/buffers.h b/src/or/buffers.h
index c947f0ba9..48b118520 100644
--- a/src/or/buffers.h
+++ b/src/or/buffers.h
@@ -12,6 +12,8 @@
#ifndef TOR_BUFFERS_H
#define TOR_BUFFERS_H
+#include "testsupport.h"
+
buf_t *buf_new(void);
buf_t *buf_new_with_capacity(size_t size);
void buf_free(buf_t *buf);
@@ -51,6 +53,8 @@ int fetch_from_buf_line(buf_t *buf, char *data_out, size_t *data_len);
int peek_buf_has_control0_command(buf_t *buf);
+int fetch_ext_or_command_from_buf(buf_t *buf, ext_or_cmd_t **out);
+
#ifdef USE_BUFFEREVENTS
int fetch_var_cell_from_evbuffer(struct evbuffer *buf, var_cell_t **out,
int linkproto);
@@ -66,6 +70,8 @@ int peek_evbuffer_has_control0_command(struct evbuffer *buf);
int write_to_evbuffer_zlib(struct evbuffer *buf, tor_zlib_state_t *state,
const char *data, size_t data_len,
int done);
+int fetch_ext_or_command_from_evbuffer(struct evbuffer *buf,
+ ext_or_cmd_t **out);
#endif
#ifdef USE_BUFFEREVENTS
@@ -75,6 +81,8 @@ int write_to_evbuffer_zlib(struct evbuffer *buf, tor_zlib_state_t *state,
#define generic_buffer_get(b,buf,buflen) evbuffer_remove((b),(buf),(buflen))
#define generic_buffer_clear(b) evbuffer_drain((b), evbuffer_get_length((b)))
#define generic_buffer_free(b) evbuffer_free((b))
+#define generic_buffer_fetch_ext_or_cmd(b, out) \
+ fetch_ext_or_command_from_evbuffer((b), (out))
#else
#define generic_buffer_new() buf_new()
#define generic_buffer_len(b) buf_datalen((b))
@@ -82,6 +90,8 @@ int write_to_evbuffer_zlib(struct evbuffer *buf, tor_zlib_state_t *state,
#define generic_buffer_get(b,buf,buflen) fetch_from_buf((buf),(buflen),(b))
#define generic_buffer_clear(b) buf_clear((b))
#define generic_buffer_free(b) buf_free((b))
+#define generic_buffer_fetch_ext_or_cmd(b, out) \
+ fetch_ext_or_command_from_buf((b), (out))
#endif
int generic_buffer_set_to_copy(generic_buffer_t **output,
const generic_buffer_t *input);
@@ -89,7 +99,7 @@ int generic_buffer_set_to_copy(generic_buffer_t **output,
void assert_buf_ok(buf_t *buf);
#ifdef BUFFERS_PRIVATE
-int buf_find_string_offset(const buf_t *buf, const char *s, size_t n);
+STATIC int buf_find_string_offset(const buf_t *buf, const char *s, size_t n);
#endif
#endif
diff --git a/src/or/channel.c b/src/or/channel.c
index 1270eace7..a345bab20 100644
--- a/src/or/channel.c
+++ b/src/or/channel.c
@@ -122,6 +122,8 @@ static cell_queue_entry_t *
cell_queue_entry_new_fixed(cell_t *cell);
static cell_queue_entry_t *
cell_queue_entry_new_var(var_cell_t *var_cell);
+static int is_destroy_cell(channel_t *chan,
+ const cell_queue_entry_t *q, circid_t *circid_out);
/* Functions to maintain the digest map */
static void channel_add_to_digest_map(channel_t *chan);
@@ -804,6 +806,7 @@ channel_free(channel_t *chan)
/* Get rid of cmux */
if (chan->cmux) {
circuitmux_detach_all_circuits(chan->cmux);
+ circuitmux_mark_destroyed_circids_usable(chan->cmux, chan);
circuitmux_free(chan->cmux);
chan->cmux = NULL;
}
@@ -1688,6 +1691,13 @@ channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q)
chan->timestamp_last_added_nonpadding = approx_time();
}
+ {
+ circid_t circ_id;
+ if (is_destroy_cell(chan, q, &circ_id)) {
+ channel_note_destroy_not_pending(chan, circ_id);
+ }
+ }
+
/* Can we send it right out? If so, try */
if (TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue) &&
chan->state == CHANNEL_STATE_OPEN) {
@@ -2355,7 +2365,7 @@ channel_do_open_actions(channel_t *chan)
started_here = channel_is_outgoing(chan);
if (started_here) {
- circuit_build_times_network_is_live(&circ_times);
+ circuit_build_times_network_is_live(get_circuit_build_times_mutable());
rep_hist_note_connect_succeeded(chan->identity_digest, now);
if (entry_guard_register_connect_status(
chan->identity_digest, 1, 0, now) < 0) {
@@ -2373,8 +2383,14 @@ channel_do_open_actions(channel_t *chan)
/* only report it to the geoip module if it's not a known router */
if (!router_get_by_id_digest(chan->identity_digest)) {
if (channel_get_addr_if_possible(chan, &remote_addr)) {
- geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &remote_addr,
+ char *transport_name = NULL;
+ if (chan->get_transport_name(chan, &transport_name) < 0)
+ transport_name = NULL;
+
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT,
+ &remote_addr, transport_name,
now);
+ tor_free(transport_name);
}
/* Otherwise the underlying transport can't tell us this, so skip it */
}
@@ -2611,6 +2627,54 @@ channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell)
}
}
+/** If <b>packed_cell</b> on <b>chan</b> is a destroy cell, then set
+ * *<b>circid_out</b> to its circuit ID, and return true. Otherwise, return
+ * false. */
+/* XXXX Move this function. */
+int
+packed_cell_is_destroy(channel_t *chan,
+ const packed_cell_t *packed_cell,
+ circid_t *circid_out)
+{
+ if (chan->wide_circ_ids) {
+ if (packed_cell->body[4] == CELL_DESTROY) {
+ *circid_out = ntohl(get_uint32(packed_cell->body));
+ return 1;
+ }
+ } else {
+ if (packed_cell->body[2] == CELL_DESTROY) {
+ *circid_out = ntohs(get_uint16(packed_cell->body));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/** DOCDOC */
+static int
+is_destroy_cell(channel_t *chan,
+ const cell_queue_entry_t *q, circid_t *circid_out)
+{
+ *circid_out = 0;
+ switch (q->type) {
+ case CELL_QUEUE_FIXED:
+ if (q->u.fixed.cell->command == CELL_DESTROY) {
+ *circid_out = q->u.fixed.cell->circ_id;
+ return 1;
+ }
+ break;
+ case CELL_QUEUE_VAR:
+ if (q->u.var.var_cell->command == CELL_DESTROY) {
+ *circid_out = q->u.var.var_cell->circ_id;
+ return 1;
+ }
+ break;
+ case CELL_QUEUE_PACKED:
+ return packed_cell_is_destroy(chan, q->u.packed.packed_cell, circid_out);
+ }
+ return 0;
+}
+
/**
* Send destroy cell on a channel
*
@@ -2622,25 +2686,20 @@ channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell)
int
channel_send_destroy(circid_t circ_id, channel_t *chan, int reason)
{
- cell_t cell;
-
tor_assert(chan);
/* Check to make sure we can send on this channel first */
if (!(chan->state == CHANNEL_STATE_CLOSING ||
chan->state == CHANNEL_STATE_CLOSED ||
- chan->state == CHANNEL_STATE_ERROR)) {
- memset(&cell, 0, sizeof(cell_t));
- cell.circ_id = circ_id;
- cell.command = CELL_DESTROY;
- cell.payload[0] = (uint8_t) reason;
+ chan->state == CHANNEL_STATE_ERROR) &&
+ chan->cmux) {
+ channel_note_destroy_pending(chan, circ_id);
+ circuitmux_append_destroy_cell(chan, chan->cmux, circ_id, reason);
log_debug(LD_OR,
"Sending destroy (circID %u) on channel %p "
"(global ID " U64_FORMAT ")",
(unsigned)circ_id, chan,
U64_PRINTF_ARG(chan->global_identifier));
-
- channel_write_cell(chan, &cell);
} else {
log_warn(LD_BUG,
"Someone called channel_send_destroy() for circID %u "
diff --git a/src/or/channel.h b/src/or/channel.h
index 2dca81705..7e3f5ad07 100644
--- a/src/or/channel.h
+++ b/src/or/channel.h
@@ -10,7 +10,6 @@
#define TOR_CHANNEL_H
#include "or.h"
-#include "tor_queue.h"
#include "circuitmux.h"
/* Channel handler function pointer typedefs */
@@ -87,6 +86,8 @@ struct channel_s {
* available.
*/
int (*get_remote_addr)(channel_t *, tor_addr_t *);
+ int (*get_transport_name)(channel_t *chan, char **transport_out);
+
#define GRD_FLAG_ORIGINAL 1
#define GRD_FLAG_ADDR_ONLY 2
/*
@@ -480,5 +481,9 @@ uint64_t channel_count_xmitted(channel_t *chan);
uint64_t channel_listener_count_accepted(channel_listener_t *chan_l);
+int packed_cell_is_destroy(channel_t *chan,
+ const packed_cell_t *packed_cell,
+ circid_t *circid_out);
+
#endif
diff --git a/src/or/channeltls.c b/src/or/channeltls.c
index f751c0da9..4943054f5 100644
--- a/src/or/channeltls.c
+++ b/src/or/channeltls.c
@@ -55,6 +55,8 @@ static void channel_tls_close_method(channel_t *chan);
static const char * channel_tls_describe_transport_method(channel_t *chan);
static int
channel_tls_get_remote_addr_method(channel_t *chan, tor_addr_t *addr_out);
+static int
+channel_tls_get_transport_name_method(channel_t *chan, char **transport_out);
static const char *
channel_tls_get_remote_descr_method(channel_t *chan, int flags);
static int channel_tls_has_queued_writes_method(channel_t *chan);
@@ -114,6 +116,7 @@ channel_tls_common_init(channel_tls_t *tlschan)
chan->describe_transport = channel_tls_describe_transport_method;
chan->get_remote_addr = channel_tls_get_remote_addr_method;
chan->get_remote_descr = channel_tls_get_remote_descr_method;
+ chan->get_transport_name = channel_tls_get_transport_name_method;
chan->has_queued_writes = channel_tls_has_queued_writes_method;
chan->is_canonical = channel_tls_is_canonical_method;
chan->matches_extend_info = channel_tls_matches_extend_info_method;
@@ -406,6 +409,30 @@ channel_tls_get_remote_addr_method(channel_t *chan, tor_addr_t *addr_out)
}
/**
+ * Get the name of the pluggable transport used by a channel_tls_t.
+ *
+ * This implements the get_transport_name for channel_tls_t. If the
+ * channel uses a pluggable transport, copy its name to
+ * <b>transport_out</b> and return 0. If the channel did not use a
+ * pluggable transport, return -1. */
+
+static int
+channel_tls_get_transport_name_method(channel_t *chan, char **transport_out)
+{
+ channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan);
+
+ tor_assert(tlschan);
+ tor_assert(transport_out);
+ tor_assert(tlschan->conn);
+
+ if (!tlschan->conn->ext_or_transport)
+ return -1;
+
+ *transport_out = tor_strdup(tlschan->conn->ext_or_transport);
+ return 0;
+}
+
+/**
* Get endpoint description of a channel_tls_t
*
* This implements the get_remote_descr method for channel_tls_t; it returns
@@ -1408,12 +1435,14 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
my_addr_ptr = (uint8_t*) cell->payload + 6;
end = cell->payload + CELL_PAYLOAD_SIZE;
cp = cell->payload + 6 + my_addr_len;
- if (cp >= end) {
- log_fn(LOG_PROTOCOL_WARN, LD_OR,
- "Addresses too long in netinfo cell; closing connection.");
- connection_or_close_for_error(chan->conn, 0);
- return;
- } else if (my_addr_type == RESOLVED_TYPE_IPV4 && my_addr_len == 4) {
+
+ /* We used to check:
+ * if (my_addr_len >= CELL_PAYLOAD_SIZE - 6) {
+ *
+ * This is actually never going to happen, since my_addr_len is at most 255,
+ * and CELL_PAYLOAD_LEN - 6 is 503. So we know that cp is < end. */
+
+ if (my_addr_type == RESOLVED_TYPE_IPV4 && my_addr_len == 4) {
tor_addr_from_ipv4n(&my_apparent_addr, get_uint32(my_addr_ptr));
} else if (my_addr_type == RESOLVED_TYPE_IPV6 && my_addr_len == 16) {
tor_addr_from_ipv6_bytes(&my_apparent_addr, (const char *) my_addr_ptr);
diff --git a/src/or/circpathbias.c b/src/or/circpathbias.c
new file mode 100644
index 000000000..51a75cf50
--- /dev/null
+++ b/src/or/circpathbias.c
@@ -0,0 +1,1538 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+#include "channel.h"
+#include "circpathbias.h"
+#include "circuitbuild.h"
+#include "circuitlist.h"
+#include "circuituse.h"
+#include "circuitstats.h"
+#include "connection_edge.h"
+#include "config.h"
+#include "entrynodes.h"
+#include "networkstatus.h"
+#include "relay.h"
+
+static void pathbias_count_successful_close(origin_circuit_t *circ);
+static void pathbias_count_collapse(origin_circuit_t *circ);
+static void pathbias_count_use_failed(origin_circuit_t *circ);
+static void pathbias_measure_use_rate(entry_guard_t *guard);
+static void pathbias_measure_close_rate(entry_guard_t *guard);
+static void pathbias_scale_use_rates(entry_guard_t *guard);
+static void pathbias_scale_close_rates(entry_guard_t *guard);
+static int entry_guard_inc_circ_attempt_count(entry_guard_t *guard);
+
+/** Increment the number of times we successfully extended a circuit to
+ * <b>guard</b>, first checking if the failure rate is high enough that
+ * we should eliminate the guard. Return -1 if the guard looks no good;
+ * return 0 if the guard looks fine.
+ */
+static int
+entry_guard_inc_circ_attempt_count(entry_guard_t *guard)
+{
+ entry_guards_changed();
+
+ pathbias_measure_close_rate(guard);
+
+ if (guard->path_bias_disabled)
+ return -1;
+
+ pathbias_scale_close_rates(guard);
+ guard->circ_attempts++;
+
+ log_info(LD_CIRC, "Got success count %f/%f for guard %s ($%s)",
+ guard->circ_successes, guard->circ_attempts, guard->nickname,
+ hex_str(guard->identity, DIGEST_LEN));
+ return 0;
+}
+
+/** The minimum number of circuit attempts before we start
+ * thinking about warning about path bias and dropping guards */
+static int
+pathbias_get_min_circs(const or_options_t *options)
+{
+#define DFLT_PATH_BIAS_MIN_CIRC 150
+ if (options->PathBiasCircThreshold >= 5)
+ return options->PathBiasCircThreshold;
+ else
+ return networkstatus_get_param(NULL, "pb_mincircs",
+ DFLT_PATH_BIAS_MIN_CIRC,
+ 5, INT32_MAX);
+}
+
+/** The circuit success rate below which we issue a notice */
+static double
+pathbias_get_notice_rate(const or_options_t *options)
+{
+#define DFLT_PATH_BIAS_NOTICE_PCT 70
+ if (options->PathBiasNoticeRate >= 0.0)
+ return options->PathBiasNoticeRate;
+ else
+ return networkstatus_get_param(NULL, "pb_noticepct",
+ DFLT_PATH_BIAS_NOTICE_PCT, 0, 100)/100.0;
+}
+
+/* XXXX024 I'd like to have this be static again, but entrynodes.c needs it. */
+/** The circuit success rate below which we issue a warn */
+static double
+pathbias_get_warn_rate(const or_options_t *options)
+{
+#define DFLT_PATH_BIAS_WARN_PCT 50
+ if (options->PathBiasWarnRate >= 0.0)
+ return options->PathBiasWarnRate;
+ else
+ return networkstatus_get_param(NULL, "pb_warnpct",
+ DFLT_PATH_BIAS_WARN_PCT, 0, 100)/100.0;
+}
+
+/* XXXX024 I'd like to have this be static again, but entrynodes.c needs it. */
+/**
+ * The extreme rate is the rate at which we would drop the guard,
+ * if pb_dropguard is also set. Otherwise we just warn.
+ */
+double
+pathbias_get_extreme_rate(const or_options_t *options)
+{
+#define DFLT_PATH_BIAS_EXTREME_PCT 30
+ if (options->PathBiasExtremeRate >= 0.0)
+ return options->PathBiasExtremeRate;
+ else
+ return networkstatus_get_param(NULL, "pb_extremepct",
+ DFLT_PATH_BIAS_EXTREME_PCT, 0, 100)/100.0;
+}
+
+/* XXXX024 I'd like to have this be static again, but entrynodes.c needs it. */
+/**
+ * If 1, we actually disable use of guards that fall below
+ * the extreme_pct.
+ */
+int
+pathbias_get_dropguards(const or_options_t *options)
+{
+#define DFLT_PATH_BIAS_DROP_GUARDS 0
+ if (options->PathBiasDropGuards >= 0)
+ return options->PathBiasDropGuards;
+ else
+ return networkstatus_get_param(NULL, "pb_dropguards",
+ DFLT_PATH_BIAS_DROP_GUARDS, 0, 1);
+}
+
+/**
+ * This is the number of circuits at which we scale our
+ * counts by mult_factor/scale_factor. Note, this count is
+ * not exact, as we only perform the scaling in the event
+ * of no integer truncation.
+ */
+static int
+pathbias_get_scale_threshold(const or_options_t *options)
+{
+#define DFLT_PATH_BIAS_SCALE_THRESHOLD 300
+ if (options->PathBiasScaleThreshold >= 10)
+ return options->PathBiasScaleThreshold;
+ else
+ return networkstatus_get_param(NULL, "pb_scalecircs",
+ DFLT_PATH_BIAS_SCALE_THRESHOLD, 10,
+ INT32_MAX);
+}
+
+/**
+ * Compute the path bias scaling ratio from the consensus
+ * parameters pb_multfactor/pb_scalefactor.
+ *
+ * Returns a value in (0, 1.0] which we multiply our pathbias
+ * counts with to scale them down.
+ */
+static double
+pathbias_get_scale_ratio(const or_options_t *options)
+{
+ /*
+ * The scale factor is the denominator for our scaling
+ * of circuit counts for our path bias window.
+ *
+ * Note that our use of doubles for the path bias state
+ * file means that powers of 2 work best here.
+ */
+ int denominator = networkstatus_get_param(NULL, "pb_scalefactor",
+ 2, 2, INT32_MAX);
+ (void) options;
+ /**
+ * The mult factor is the numerator for our scaling
+ * of circuit counts for our path bias window. It
+ * allows us to scale by fractions.
+ */
+ return networkstatus_get_param(NULL, "pb_multfactor",
+ 1, 1, denominator)/((double)denominator);
+}
+
+/** The minimum number of circuit usage attempts before we start
+ * thinking about warning about path use bias and dropping guards */
+static int
+pathbias_get_min_use(const or_options_t *options)
+{
+#define DFLT_PATH_BIAS_MIN_USE 20
+ if (options->PathBiasUseThreshold >= 3)
+ return options->PathBiasUseThreshold;
+ else
+ return networkstatus_get_param(NULL, "pb_minuse",
+ DFLT_PATH_BIAS_MIN_USE,
+ 3, INT32_MAX);
+}
+
+/** The circuit use success rate below which we issue a notice */
+static double
+pathbias_get_notice_use_rate(const or_options_t *options)
+{
+#define DFLT_PATH_BIAS_NOTICE_USE_PCT 80
+ if (options->PathBiasNoticeUseRate >= 0.0)
+ return options->PathBiasNoticeUseRate;
+ else
+ return networkstatus_get_param(NULL, "pb_noticeusepct",
+ DFLT_PATH_BIAS_NOTICE_USE_PCT,
+ 0, 100)/100.0;
+}
+
+/**
+ * The extreme use rate is the rate at which we would drop the guard,
+ * if pb_dropguard is also set. Otherwise we just warn.
+ */
+double
+pathbias_get_extreme_use_rate(const or_options_t *options)
+{
+#define DFLT_PATH_BIAS_EXTREME_USE_PCT 60
+ if (options->PathBiasExtremeUseRate >= 0.0)
+ return options->PathBiasExtremeUseRate;
+ else
+ return networkstatus_get_param(NULL, "pb_extremeusepct",
+ DFLT_PATH_BIAS_EXTREME_USE_PCT,
+ 0, 100)/100.0;
+}
+
+/**
+ * This is the number of circuits at which we scale our
+ * use counts by mult_factor/scale_factor. Note, this count is
+ * not exact, as we only perform the scaling in the event
+ * of no integer truncation.
+ */
+static int
+pathbias_get_scale_use_threshold(const or_options_t *options)
+{
+#define DFLT_PATH_BIAS_SCALE_USE_THRESHOLD 100
+ if (options->PathBiasScaleUseThreshold >= 10)
+ return options->PathBiasScaleUseThreshold;
+ else
+ return networkstatus_get_param(NULL, "pb_scaleuse",
+ DFLT_PATH_BIAS_SCALE_USE_THRESHOLD,
+ 10, INT32_MAX);
+}
+
+/**
+ * Convert a Guard's path state to string.
+ */
+const char *
+pathbias_state_to_string(path_state_t state)
+{
+ switch (state) {
+ case PATH_STATE_NEW_CIRC:
+ return "new";
+ case PATH_STATE_BUILD_ATTEMPTED:
+ return "build attempted";
+ case PATH_STATE_BUILD_SUCCEEDED:
+ return "build succeeded";
+ case PATH_STATE_USE_ATTEMPTED:
+ return "use attempted";
+ case PATH_STATE_USE_SUCCEEDED:
+ return "use succeeded";
+ case PATH_STATE_USE_FAILED:
+ return "use failed";
+ case PATH_STATE_ALREADY_COUNTED:
+ return "already counted";
+ }
+
+ return "unknown";
+}
+
+/**
+ * This function decides if a circuit has progressed far enough to count
+ * as a circuit "attempt". As long as end-to-end tagging is possible,
+ * we assume the adversary will use it over hop-to-hop failure. Therefore,
+ * we only need to account bias for the last hop. This should make us
+ * much more resilient to ambient circuit failure, and also make that
+ * failure easier to measure (we only need to measure Exit failure rates).
+ */
+static int
+pathbias_is_new_circ_attempt(origin_circuit_t *circ)
+{
+#define N2N_TAGGING_IS_POSSIBLE
+#ifdef N2N_TAGGING_IS_POSSIBLE
+ /* cpath is a circular list. We want circs with more than one hop,
+ * and the second hop must be waiting for keys still (it's just
+ * about to get them). */
+ return circ->cpath &&
+ circ->cpath->next != circ->cpath &&
+ circ->cpath->next->state == CPATH_STATE_AWAITING_KEYS;
+#else
+ /* If tagging attacks are no longer possible, we probably want to
+ * count bias from the first hop. However, one could argue that
+ * timing-based tagging is still more useful than per-hop failure.
+ * In which case, we'd never want to use this.
+ */
+ return circ->cpath &&
+ circ->cpath->state == CPATH_STATE_AWAITING_KEYS;
+#endif
+}
+
+/**
+ * Decide if the path bias code should count a circuit.
+ *
+ * @returns 1 if we should count it, 0 otherwise.
+ */
+static int
+pathbias_should_count(origin_circuit_t *circ)
+{
+#define PATHBIAS_COUNT_INTERVAL (600)
+ static ratelim_t count_limit =
+ RATELIM_INIT(PATHBIAS_COUNT_INTERVAL);
+ char *rate_msg = NULL;
+
+ /* We can't do path bias accounting without entry guards.
+ * Testing and controller circuits also have no guards.
+ *
+ * We also don't count server-side rends, because their
+ * endpoint could be chosen maliciously.
+ * Similarly, we can't count client-side intro attempts,
+ * because clients can be manipulated into connecting to
+ * malicious intro points. */
+ if (get_options()->UseEntryGuards == 0 ||
+ circ->base_.purpose == CIRCUIT_PURPOSE_TESTING ||
+ circ->base_.purpose == CIRCUIT_PURPOSE_CONTROLLER ||
+ circ->base_.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND ||
+ circ->base_.purpose == CIRCUIT_PURPOSE_S_REND_JOINED ||
+ (circ->base_.purpose >= CIRCUIT_PURPOSE_C_INTRODUCING &&
+ circ->base_.purpose <= CIRCUIT_PURPOSE_C_INTRODUCE_ACKED)) {
+
+ /* Check to see if the shouldcount result has changed due to a
+ * unexpected purpose change that would affect our results.
+ *
+ * The reason we check the path state too here is because for the
+ * cannibalized versions of these purposes, we count them as successful
+ * before their purpose change.
+ */
+ if (circ->pathbias_shouldcount == PATHBIAS_SHOULDCOUNT_COUNTED
+ && circ->path_state != PATH_STATE_ALREADY_COUNTED) {
+ log_info(LD_BUG,
+ "Circuit %d is now being ignored despite being counted "
+ "in the past. Purpose is %s, path state is %s",
+ circ->global_identifier,
+ circuit_purpose_to_string(circ->base_.purpose),
+ pathbias_state_to_string(circ->path_state));
+ }
+ circ->pathbias_shouldcount = PATHBIAS_SHOULDCOUNT_IGNORED;
+ return 0;
+ }
+
+ /* Completely ignore one hop circuits */
+ if (circ->build_state->onehop_tunnel ||
+ circ->build_state->desired_path_len == 1) {
+ /* Check for inconsistency */
+ if (circ->build_state->desired_path_len != 1 ||
+ !circ->build_state->onehop_tunnel) {
+ if ((rate_msg = rate_limit_log(&count_limit, approx_time()))) {
+ log_info(LD_BUG,
+ "One-hop circuit has length %d. Path state is %s. "
+ "Circuit is a %s currently %s.%s",
+ circ->build_state->desired_path_len,
+ pathbias_state_to_string(circ->path_state),
+ circuit_purpose_to_string(circ->base_.purpose),
+ circuit_state_to_string(circ->base_.state),
+ rate_msg);
+ tor_free(rate_msg);
+ }
+ tor_fragile_assert();
+ }
+
+ /* Check to see if the shouldcount result has changed due to a
+ * unexpected change that would affect our results */
+ if (circ->pathbias_shouldcount == PATHBIAS_SHOULDCOUNT_COUNTED) {
+ log_info(LD_BUG,
+ "One-hop circuit %d is now being ignored despite being counted "
+ "in the past. Purpose is %s, path state is %s",
+ circ->global_identifier,
+ circuit_purpose_to_string(circ->base_.purpose),
+ pathbias_state_to_string(circ->path_state));
+ }
+ circ->pathbias_shouldcount = PATHBIAS_SHOULDCOUNT_IGNORED;
+ return 0;
+ }
+
+ /* Check to see if the shouldcount result has changed due to a
+ * unexpected purpose change that would affect our results */
+ if (circ->pathbias_shouldcount == PATHBIAS_SHOULDCOUNT_IGNORED) {
+ log_info(LD_BUG,
+ "Circuit %d is now being counted despite being ignored "
+ "in the past. Purpose is %s, path state is %s",
+ circ->global_identifier,
+ circuit_purpose_to_string(circ->base_.purpose),
+ pathbias_state_to_string(circ->path_state));
+ }
+ circ->pathbias_shouldcount = PATHBIAS_SHOULDCOUNT_COUNTED;
+
+ return 1;
+}
+
+/**
+ * Check our circuit state to see if this is a successful circuit attempt.
+ * If so, record it in the current guard's path bias circ_attempt count.
+ *
+ * Also check for several potential error cases for bug #6475.
+ */
+int
+pathbias_count_build_attempt(origin_circuit_t *circ)
+{
+#define CIRC_ATTEMPT_NOTICE_INTERVAL (600)
+ static ratelim_t circ_attempt_notice_limit =
+ RATELIM_INIT(CIRC_ATTEMPT_NOTICE_INTERVAL);
+ char *rate_msg = NULL;
+
+ if (!pathbias_should_count(circ)) {
+ return 0;
+ }
+
+ if (pathbias_is_new_circ_attempt(circ)) {
+ /* Help track down the real cause of bug #6475: */
+ if (circ->has_opened && circ->path_state != PATH_STATE_BUILD_ATTEMPTED) {
+ if ((rate_msg = rate_limit_log(&circ_attempt_notice_limit,
+ approx_time()))) {
+ log_info(LD_BUG,
+ "Opened circuit is in strange path state %s. "
+ "Circuit is a %s currently %s.%s",
+ pathbias_state_to_string(circ->path_state),
+ circuit_purpose_to_string(circ->base_.purpose),
+ circuit_state_to_string(circ->base_.state),
+ rate_msg);
+ tor_free(rate_msg);
+ }
+ }
+
+ /* Don't re-count cannibalized circs.. */
+ if (!circ->has_opened) {
+ entry_guard_t *guard = NULL;
+
+ if (circ->cpath && circ->cpath->extend_info) {
+ guard = entry_guard_get_by_id_digest(
+ circ->cpath->extend_info->identity_digest);
+ } else if (circ->base_.n_chan) {
+ guard =
+ entry_guard_get_by_id_digest(circ->base_.n_chan->identity_digest);
+ }
+
+ if (guard) {
+ if (circ->path_state == PATH_STATE_NEW_CIRC) {
+ circ->path_state = PATH_STATE_BUILD_ATTEMPTED;
+
+ if (entry_guard_inc_circ_attempt_count(guard) < 0) {
+ /* Bogus guard; we already warned. */
+ return -END_CIRC_REASON_TORPROTOCOL;
+ }
+ } else {
+ if ((rate_msg = rate_limit_log(&circ_attempt_notice_limit,
+ approx_time()))) {
+ log_info(LD_BUG,
+ "Unopened circuit has strange path state %s. "
+ "Circuit is a %s currently %s.%s",
+ pathbias_state_to_string(circ->path_state),
+ circuit_purpose_to_string(circ->base_.purpose),
+ circuit_state_to_string(circ->base_.state),
+ rate_msg);
+ tor_free(rate_msg);
+ }
+ }
+ } else {
+ if ((rate_msg = rate_limit_log(&circ_attempt_notice_limit,
+ approx_time()))) {
+ log_info(LD_CIRC,
+ "Unopened circuit has no known guard. "
+ "Circuit is a %s currently %s.%s",
+ circuit_purpose_to_string(circ->base_.purpose),
+ circuit_state_to_string(circ->base_.state),
+ rate_msg);
+ tor_free(rate_msg);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Check our circuit state to see if this is a successful circuit
+ * completion. If so, record it in the current guard's path bias
+ * success count.
+ *
+ * Also check for several potential error cases for bug #6475.
+ */
+void
+pathbias_count_build_success(origin_circuit_t *circ)
+{
+#define SUCCESS_NOTICE_INTERVAL (600)
+ static ratelim_t success_notice_limit =
+ RATELIM_INIT(SUCCESS_NOTICE_INTERVAL);
+ char *rate_msg = NULL;
+ entry_guard_t *guard = NULL;
+
+ if (!pathbias_should_count(circ)) {
+ return;
+ }
+
+ /* Don't count cannibalized/reused circs for path bias
+ * "build" success, since they get counted under "use" success. */
+ if (!circ->has_opened) {
+ if (circ->cpath && circ->cpath->extend_info) {
+ guard = entry_guard_get_by_id_digest(
+ circ->cpath->extend_info->identity_digest);
+ }
+
+ if (guard) {
+ if (circ->path_state == PATH_STATE_BUILD_ATTEMPTED) {
+ circ->path_state = PATH_STATE_BUILD_SUCCEEDED;
+ guard->circ_successes++;
+ entry_guards_changed();
+
+ log_info(LD_CIRC, "Got success count %f/%f for guard %s ($%s)",
+ guard->circ_successes, guard->circ_attempts,
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN));
+ } else {
+ if ((rate_msg = rate_limit_log(&success_notice_limit,
+ approx_time()))) {
+ log_info(LD_BUG,
+ "Succeeded circuit is in strange path state %s. "
+ "Circuit is a %s currently %s.%s",
+ pathbias_state_to_string(circ->path_state),
+ circuit_purpose_to_string(circ->base_.purpose),
+ circuit_state_to_string(circ->base_.state),
+ rate_msg);
+ tor_free(rate_msg);
+ }
+ }
+
+ if (guard->circ_attempts < guard->circ_successes) {
+ log_notice(LD_BUG, "Unexpectedly high successes counts (%f/%f) "
+ "for guard %s ($%s)",
+ guard->circ_successes, guard->circ_attempts,
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN));
+ }
+ /* In rare cases, CIRCUIT_PURPOSE_TESTING can get converted to
+ * CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT and have no guards here.
+ * No need to log that case. */
+ } else if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
+ if ((rate_msg = rate_limit_log(&success_notice_limit,
+ approx_time()))) {
+ log_info(LD_CIRC,
+ "Completed circuit has no known guard. "
+ "Circuit is a %s currently %s.%s",
+ circuit_purpose_to_string(circ->base_.purpose),
+ circuit_state_to_string(circ->base_.state),
+ rate_msg);
+ tor_free(rate_msg);
+ }
+ }
+ } else {
+ if (circ->path_state < PATH_STATE_BUILD_SUCCEEDED) {
+ if ((rate_msg = rate_limit_log(&success_notice_limit,
+ approx_time()))) {
+ log_info(LD_BUG,
+ "Opened circuit is in strange path state %s. "
+ "Circuit is a %s currently %s.%s",
+ pathbias_state_to_string(circ->path_state),
+ circuit_purpose_to_string(circ->base_.purpose),
+ circuit_state_to_string(circ->base_.state),
+ rate_msg);
+ tor_free(rate_msg);
+ }
+ }
+ }
+}
+
+/**
+ * Record an attempt to use a circuit. Changes the circuit's
+ * path state and update its guard's usage counter.
+ *
+ * Used for path bias usage accounting.
+ */
+void
+pathbias_count_use_attempt(origin_circuit_t *circ)
+{
+ entry_guard_t *guard;
+
+ if (!pathbias_should_count(circ)) {
+ return;
+ }
+
+ if (circ->path_state < PATH_STATE_BUILD_SUCCEEDED) {
+ log_notice(LD_BUG,
+ "Used circuit is in strange path state %s. "
+ "Circuit is a %s currently %s.",
+ pathbias_state_to_string(circ->path_state),
+ circuit_purpose_to_string(circ->base_.purpose),
+ circuit_state_to_string(circ->base_.state));
+ } else if (circ->path_state < PATH_STATE_USE_ATTEMPTED) {
+ guard = entry_guard_get_by_id_digest(
+ circ->cpath->extend_info->identity_digest);
+ if (guard) {
+ pathbias_measure_use_rate(guard);
+ pathbias_scale_use_rates(guard);
+ guard->use_attempts++;
+ entry_guards_changed();
+
+ log_debug(LD_CIRC,
+ "Marked circuit %d (%f/%f) as used for guard %s ($%s).",
+ circ->global_identifier,
+ guard->use_successes, guard->use_attempts,
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN));
+ }
+
+ circ->path_state = PATH_STATE_USE_ATTEMPTED;
+ } else {
+ /* Harmless but educational log message */
+ log_info(LD_CIRC,
+ "Used circuit %d is already in path state %s. "
+ "Circuit is a %s currently %s.",
+ circ->global_identifier,
+ pathbias_state_to_string(circ->path_state),
+ circuit_purpose_to_string(circ->base_.purpose),
+ circuit_state_to_string(circ->base_.state));
+ }
+
+ return;
+}
+
+/**
+ * Check the circuit's path state is appropriate and mark it as
+ * successfully used. Used for path bias usage accounting.
+ *
+ * We don't actually increment the guard's counters until
+ * pathbias_check_close(), because the circuit can still transition
+ * back to PATH_STATE_USE_ATTEMPTED if a stream fails later (this
+ * is done so we can probe the circuit for liveness at close).
+ */
+void
+pathbias_mark_use_success(origin_circuit_t *circ)
+{
+ if (!pathbias_should_count(circ)) {
+ return;
+ }
+
+ if (circ->path_state < PATH_STATE_USE_ATTEMPTED) {
+ log_notice(LD_BUG,
+ "Used circuit %d is in strange path state %s. "
+ "Circuit is a %s currently %s.",
+ circ->global_identifier,
+ pathbias_state_to_string(circ->path_state),
+ circuit_purpose_to_string(circ->base_.purpose),
+ circuit_state_to_string(circ->base_.state));
+
+ pathbias_count_use_attempt(circ);
+ }
+
+ /* We don't do any accounting at the guard until actual circuit close */
+ circ->path_state = PATH_STATE_USE_SUCCEEDED;
+
+ return;
+}
+
+/**
+ * If a stream ever detatches from a circuit in a retriable way,
+ * we need to mark this circuit as still needing either another
+ * successful stream, or in need of a probe.
+ *
+ * An adversary could let the first stream request succeed (ie the
+ * resolve), but then tag and timeout the remainder (via cell
+ * dropping), forcing them on new circuits.
+ *
+ * Rolling back the state will cause us to probe such circuits, which
+ * should lead to probe failures in the event of such tagging due to
+ * either unrecognized cells coming in while we wait for the probe,
+ * or the cipher state getting out of sync in the case of dropped cells.
+ */
+void
+pathbias_mark_use_rollback(origin_circuit_t *circ)
+{
+ if (circ->path_state == PATH_STATE_USE_SUCCEEDED) {
+ log_info(LD_CIRC,
+ "Rolling back pathbias use state to 'attempted' for detached "
+ "circuit %d", circ->global_identifier);
+ circ->path_state = PATH_STATE_USE_ATTEMPTED;
+ }
+}
+
+/**
+ * Actually count a circuit success towards a guard's usage counters
+ * if the path state is appropriate.
+ */
+static void
+pathbias_count_use_success(origin_circuit_t *circ)
+{
+ entry_guard_t *guard;
+
+ if (!pathbias_should_count(circ)) {
+ return;
+ }
+
+ if (circ->path_state != PATH_STATE_USE_SUCCEEDED) {
+ log_notice(LD_BUG,
+ "Successfully used circuit %d is in strange path state %s. "
+ "Circuit is a %s currently %s.",
+ circ->global_identifier,
+ pathbias_state_to_string(circ->path_state),
+ circuit_purpose_to_string(circ->base_.purpose),
+ circuit_state_to_string(circ->base_.state));
+ } else {
+ guard = entry_guard_get_by_id_digest(
+ circ->cpath->extend_info->identity_digest);
+ if (guard) {
+ guard->use_successes++;
+ entry_guards_changed();
+
+ if (guard->use_attempts < guard->use_successes) {
+ log_notice(LD_BUG, "Unexpectedly high use successes counts (%f/%f) "
+ "for guard %s=%s",
+ guard->use_successes, guard->use_attempts,
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN));
+ }
+
+ log_debug(LD_CIRC,
+ "Marked circuit %d (%f/%f) as used successfully for guard "
+ "%s ($%s).",
+ circ->global_identifier, guard->use_successes,
+ guard->use_attempts, guard->nickname,
+ hex_str(guard->identity, DIGEST_LEN));
+ }
+ }
+
+ return;
+}
+
+/**
+ * Send a probe down a circuit that the client attempted to use,
+ * but for which the stream timed out/failed. The probe is a
+ * RELAY_BEGIN cell with a 0.a.b.c destination address, which
+ * the exit will reject and reply back, echoing that address.
+ *
+ * The reason for such probes is because it is possible to bias
+ * a user's paths simply by causing timeouts, and these timeouts
+ * are not possible to differentiate from unresponsive servers.
+ *
+ * The probe is sent at the end of the circuit lifetime for two
+ * reasons: to prevent cryptographic taggers from being able to
+ * drop cells to cause timeouts, and to prevent easy recognition
+ * of probes before any real client traffic happens.
+ *
+ * Returns -1 if we couldn't probe, 0 otherwise.
+ */
+static int
+pathbias_send_usable_probe(circuit_t *circ)
+{
+ /* Based on connection_ap_handshake_send_begin() */
+ char payload[CELL_PAYLOAD_SIZE];
+ int payload_len;
+ origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+ crypt_path_t *cpath_layer = NULL;
+ char *probe_nonce = NULL;
+
+ tor_assert(ocirc);
+
+ cpath_layer = ocirc->cpath->prev;
+
+ if (cpath_layer->state != CPATH_STATE_OPEN) {
+ /* This can happen for cannibalized circuits. Their
+ * last hop isn't yet open */
+ log_info(LD_CIRC,
+ "Got pathbias probe request for unopened circuit %d. "
+ "Opened %d, len %d", ocirc->global_identifier,
+ ocirc->has_opened, ocirc->build_state->desired_path_len);
+ return -1;
+ }
+
+ /* We already went down this road. */
+ if (circ->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING &&
+ ocirc->pathbias_probe_id) {
+ log_info(LD_CIRC,
+ "Got pathbias probe request for circuit %d with "
+ "outstanding probe", ocirc->global_identifier);
+ return -1;
+ }
+
+ /* Can't probe if the channel isn't open */
+ if (circ->n_chan == NULL ||
+ (circ->n_chan->state != CHANNEL_STATE_OPEN
+ && circ->n_chan->state != CHANNEL_STATE_MAINT)) {
+ log_info(LD_CIRC,
+ "Skipping pathbias probe for circuit %d: Channel is not open.",
+ ocirc->global_identifier);
+ return -1;
+ }
+
+ circuit_change_purpose(circ, CIRCUIT_PURPOSE_PATH_BIAS_TESTING);
+
+ /* Update timestamp for when circuit_expire_building() should kill us */
+ tor_gettimeofday(&circ->timestamp_began);
+
+ /* Generate a random address for the nonce */
+ crypto_rand((char*)&ocirc->pathbias_probe_nonce,
+ sizeof(ocirc->pathbias_probe_nonce));
+ ocirc->pathbias_probe_nonce &= 0x00ffffff;
+ probe_nonce = tor_dup_ip(ocirc->pathbias_probe_nonce);
+
+ tor_snprintf(payload,RELAY_PAYLOAD_SIZE, "%s:25", probe_nonce);
+ payload_len = (int)strlen(payload)+1;
+
+ // XXX: need this? Can we assume ipv4 will always be supported?
+ // If not, how do we tell?
+ //if (payload_len <= RELAY_PAYLOAD_SIZE - 4 && edge_conn->begincell_flags) {
+ // set_uint32(payload + payload_len, htonl(edge_conn->begincell_flags));
+ // payload_len += 4;
+ //}
+
+ /* Generate+Store stream id, make sure it's non-zero */
+ ocirc->pathbias_probe_id = get_unique_stream_id_by_circ(ocirc);
+
+ if (ocirc->pathbias_probe_id==0) {
+ log_warn(LD_CIRC,
+ "Ran out of stream IDs on circuit %u during "
+ "pathbias probe attempt.", ocirc->global_identifier);
+ tor_free(probe_nonce);
+ return -1;
+ }
+
+ log_info(LD_CIRC,
+ "Sending pathbias testing cell to %s:25 on stream %d for circ %d.",
+ probe_nonce, ocirc->pathbias_probe_id, ocirc->global_identifier);
+ tor_free(probe_nonce);
+
+ /* Send a test relay cell */
+ if (relay_send_command_from_edge(ocirc->pathbias_probe_id, circ,
+ RELAY_COMMAND_BEGIN, payload,
+ payload_len, cpath_layer) < 0) {
+ log_notice(LD_CIRC,
+ "Failed to send pathbias probe cell on circuit %d.",
+ ocirc->global_identifier);
+ return -1;
+ }
+
+ /* Mark it freshly dirty so it doesn't get expired in the meantime */
+ circ->timestamp_dirty = time(NULL);
+
+ return 0;
+}
+
+/**
+ * Check the response to a pathbias probe, to ensure the
+ * cell is recognized and the nonce and other probe
+ * characteristics are as expected.
+ *
+ * If the response is valid, return 0. Otherwise return < 0.
+ */
+int
+pathbias_check_probe_response(circuit_t *circ, const cell_t *cell)
+{
+ /* Based on connection_edge_process_relay_cell() */
+ relay_header_t rh;
+ int reason;
+ uint32_t ipv4_host;
+ origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+
+ tor_assert(cell);
+ tor_assert(ocirc);
+ tor_assert(circ->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING);
+
+ relay_header_unpack(&rh, cell->payload);
+
+ reason = rh.length > 0 ?
+ get_uint8(cell->payload+RELAY_HEADER_SIZE) : END_STREAM_REASON_MISC;
+
+ if (rh.command == RELAY_COMMAND_END &&
+ reason == END_STREAM_REASON_EXITPOLICY &&
+ ocirc->pathbias_probe_id == rh.stream_id) {
+
+ /* Check length+extract host: It is in network order after the reason code.
+ * See connection_edge_end(). */
+ if (rh.length < 9) { /* reason+ipv4+dns_ttl */
+ log_notice(LD_PROTOCOL,
+ "Short path bias probe response length field (%d).", rh.length);
+ return - END_CIRC_REASON_TORPROTOCOL;
+ }
+
+ ipv4_host = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+1));
+
+ /* Check nonce */
+ if (ipv4_host == ocirc->pathbias_probe_nonce) {
+ pathbias_mark_use_success(ocirc);
+ circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED);
+ log_info(LD_CIRC,
+ "Got valid path bias probe back for circ %d, stream %d.",
+ ocirc->global_identifier, ocirc->pathbias_probe_id);
+ return 0;
+ } else {
+ log_notice(LD_CIRC,
+ "Got strange probe value 0x%x vs 0x%x back for circ %d, "
+ "stream %d.", ipv4_host, ocirc->pathbias_probe_nonce,
+ ocirc->global_identifier, ocirc->pathbias_probe_id);
+ return -1;
+ }
+ }
+ log_info(LD_CIRC,
+ "Got another cell back back on pathbias probe circuit %d: "
+ "Command: %d, Reason: %d, Stream-id: %d",
+ ocirc->global_identifier, rh.command, reason, rh.stream_id);
+ return -1;
+}
+
+/**
+ * Check if a circuit was used and/or closed successfully.
+ *
+ * If we attempted to use the circuit to carry a stream but failed
+ * for whatever reason, or if the circuit mysteriously died before
+ * we could attach any streams, record these two cases.
+ *
+ * If we *have* successfully used the circuit, or it appears to
+ * have been closed by us locally, count it as a success.
+ *
+ * Returns 0 if we're done making decisions with the circ,
+ * or -1 if we want to probe it first.
+ */
+int
+pathbias_check_close(origin_circuit_t *ocirc, int reason)
+{
+ circuit_t *circ = &ocirc->base_;
+
+ if (!pathbias_should_count(ocirc)) {
+ return 0;
+ }
+
+ switch (ocirc->path_state) {
+ /* If the circuit was closed after building, but before use, we need
+ * to ensure we were the ones who tried to close it (and not a remote
+ * actor). */
+ case PATH_STATE_BUILD_SUCCEEDED:
+ if (reason & END_CIRC_REASON_FLAG_REMOTE) {
+ /* Remote circ close reasons on an unused circuit all could be bias */
+ log_info(LD_CIRC,
+ "Circuit %d remote-closed without successful use for reason %d. "
+ "Circuit purpose %d currently %d,%s. Len %d.",
+ ocirc->global_identifier,
+ reason, circ->purpose, ocirc->has_opened,
+ circuit_state_to_string(circ->state),
+ ocirc->build_state->desired_path_len);
+ pathbias_count_collapse(ocirc);
+ } else if ((reason & ~END_CIRC_REASON_FLAG_REMOTE)
+ == END_CIRC_REASON_CHANNEL_CLOSED &&
+ circ->n_chan &&
+ circ->n_chan->reason_for_closing
+ != CHANNEL_CLOSE_REQUESTED) {
+ /* If we didn't close the channel ourselves, it could be bias */
+ /* XXX: Only count bias if the network is live?
+ * What about clock jumps/suspends? */
+ log_info(LD_CIRC,
+ "Circuit %d's channel closed without successful use for reason "
+ "%d, channel reason %d. Circuit purpose %d currently %d,%s. Len "
+ "%d.", ocirc->global_identifier,
+ reason, circ->n_chan->reason_for_closing,
+ circ->purpose, ocirc->has_opened,
+ circuit_state_to_string(circ->state),
+ ocirc->build_state->desired_path_len);
+ pathbias_count_collapse(ocirc);
+ } else {
+ pathbias_count_successful_close(ocirc);
+ }
+ break;
+
+ /* If we tried to use a circuit but failed, we should probe it to ensure
+ * it has not been tampered with. */
+ case PATH_STATE_USE_ATTEMPTED:
+ /* XXX: Only probe and/or count failure if the network is live?
+ * What about clock jumps/suspends? */
+ if (pathbias_send_usable_probe(circ) == 0)
+ return -1;
+ else
+ pathbias_count_use_failed(ocirc);
+
+ /* Any circuit where there were attempted streams but no successful
+ * streams could be bias */
+ log_info(LD_CIRC,
+ "Circuit %d closed without successful use for reason %d. "
+ "Circuit purpose %d currently %d,%s. Len %d.",
+ ocirc->global_identifier,
+ reason, circ->purpose, ocirc->has_opened,
+ circuit_state_to_string(circ->state),
+ ocirc->build_state->desired_path_len);
+ break;
+
+ case PATH_STATE_USE_SUCCEEDED:
+ pathbias_count_successful_close(ocirc);
+ pathbias_count_use_success(ocirc);
+ break;
+
+ case PATH_STATE_USE_FAILED:
+ pathbias_count_use_failed(ocirc);
+ break;
+
+ case PATH_STATE_NEW_CIRC:
+ case PATH_STATE_BUILD_ATTEMPTED:
+ case PATH_STATE_ALREADY_COUNTED:
+ default:
+ // Other states are uninteresting. No stats to count.
+ break;
+ }
+
+ ocirc->path_state = PATH_STATE_ALREADY_COUNTED;
+
+ return 0;
+}
+
+/**
+ * Count a successfully closed circuit.
+ */
+static void
+pathbias_count_successful_close(origin_circuit_t *circ)
+{
+ entry_guard_t *guard = NULL;
+ if (!pathbias_should_count(circ)) {
+ return;
+ }
+
+ if (circ->cpath && circ->cpath->extend_info) {
+ guard = entry_guard_get_by_id_digest(
+ circ->cpath->extend_info->identity_digest);
+ }
+
+ if (guard) {
+ /* In the long run: circuit_success ~= successful_circuit_close +
+ * circ_failure + stream_failure */
+ guard->successful_circuits_closed++;
+ entry_guards_changed();
+ } else if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
+ /* In rare cases, CIRCUIT_PURPOSE_TESTING can get converted to
+ * CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT and have no guards here.
+ * No need to log that case. */
+ log_info(LD_CIRC,
+ "Successfully closed circuit has no known guard. "
+ "Circuit is a %s currently %s",
+ circuit_purpose_to_string(circ->base_.purpose),
+ circuit_state_to_string(circ->base_.state));
+ }
+}
+
+/**
+ * Count a circuit that fails after it is built, but before it can
+ * carry any traffic.
+ *
+ * This is needed because there are ways to destroy a
+ * circuit after it has successfully completed. Right now, this is
+ * used for purely informational/debugging purposes.
+ */
+static void
+pathbias_count_collapse(origin_circuit_t *circ)
+{
+ entry_guard_t *guard = NULL;
+
+ if (!pathbias_should_count(circ)) {
+ return;
+ }
+
+ if (circ->cpath && circ->cpath->extend_info) {
+ guard = entry_guard_get_by_id_digest(
+ circ->cpath->extend_info->identity_digest);
+ }
+
+ if (guard) {
+ guard->collapsed_circuits++;
+ entry_guards_changed();
+ } else if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
+ /* In rare cases, CIRCUIT_PURPOSE_TESTING can get converted to
+ * CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT and have no guards here.
+ * No need to log that case. */
+ log_info(LD_CIRC,
+ "Destroyed circuit has no known guard. "
+ "Circuit is a %s currently %s",
+ circuit_purpose_to_string(circ->base_.purpose),
+ circuit_state_to_string(circ->base_.state));
+ }
+}
+
+/**
+ * Count a known failed circuit (because we could not probe it).
+ *
+ * This counter is informational.
+ */
+static void
+pathbias_count_use_failed(origin_circuit_t *circ)
+{
+ entry_guard_t *guard = NULL;
+ if (!pathbias_should_count(circ)) {
+ return;
+ }
+
+ if (circ->cpath && circ->cpath->extend_info) {
+ guard = entry_guard_get_by_id_digest(
+ circ->cpath->extend_info->identity_digest);
+ }
+
+ if (guard) {
+ guard->unusable_circuits++;
+ entry_guards_changed();
+ } else if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
+ /* In rare cases, CIRCUIT_PURPOSE_TESTING can get converted to
+ * CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT and have no guards here.
+ * No need to log that case. */
+ /* XXX note cut-and-paste code in this function compared to nearby
+ * functions. Would be nice to refactor. -RD */
+ log_info(LD_CIRC,
+ "Stream-failing circuit has no known guard. "
+ "Circuit is a %s currently %s",
+ circuit_purpose_to_string(circ->base_.purpose),
+ circuit_state_to_string(circ->base_.state));
+ }
+}
+
+/**
+ * Count timeouts for path bias log messages.
+ *
+ * These counts are purely informational.
+ */
+void
+pathbias_count_timeout(origin_circuit_t *circ)
+{
+ entry_guard_t *guard = NULL;
+
+ if (!pathbias_should_count(circ)) {
+ return;
+ }
+
+ /* For hidden service circs, they can actually be used
+ * successfully and then time out later (because
+ * the other side declines to use them). */
+ if (circ->path_state == PATH_STATE_USE_SUCCEEDED) {
+ return;
+ }
+
+ if (circ->cpath && circ->cpath->extend_info) {
+ guard = entry_guard_get_by_id_digest(
+ circ->cpath->extend_info->identity_digest);
+ }
+
+ if (guard) {
+ guard->timeouts++;
+ entry_guards_changed();
+ }
+}
+
+/**
+ * Helper function to count all of the currently opened circuits
+ * for a guard that are in a given path state range. The state
+ * range is inclusive on both ends.
+ */
+static int
+pathbias_count_circs_in_states(entry_guard_t *guard,
+ path_state_t from,
+ path_state_t to)
+{
+ circuit_t *circ;
+ int open_circuits = 0;
+
+ /* Count currently open circuits. Give them the benefit of the doubt. */
+ TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) {
+ origin_circuit_t *ocirc = NULL;
+ if (!CIRCUIT_IS_ORIGIN(circ) || /* didn't originate here */
+ circ->marked_for_close) /* already counted */
+ continue;
+
+ ocirc = TO_ORIGIN_CIRCUIT(circ);
+
+ if (!ocirc->cpath || !ocirc->cpath->extend_info)
+ continue;
+
+ if (ocirc->path_state >= from &&
+ ocirc->path_state <= to &&
+ pathbias_should_count(ocirc) &&
+ fast_memeq(guard->identity,
+ ocirc->cpath->extend_info->identity_digest,
+ DIGEST_LEN)) {
+ log_debug(LD_CIRC, "Found opened circuit %d in path_state %s",
+ ocirc->global_identifier,
+ pathbias_state_to_string(ocirc->path_state));
+ open_circuits++;
+ }
+ }
+
+ return open_circuits;
+}
+
+/**
+ * Return the number of circuits counted as successfully closed for
+ * this guard.
+ *
+ * Also add in the currently open circuits to give them the benefit
+ * of the doubt.
+ */
+double
+pathbias_get_close_success_count(entry_guard_t *guard)
+{
+ return guard->successful_circuits_closed +
+ pathbias_count_circs_in_states(guard,
+ PATH_STATE_BUILD_SUCCEEDED,
+ PATH_STATE_USE_SUCCEEDED);
+}
+
+/**
+ * Return the number of circuits counted as successfully used
+ * this guard.
+ *
+ * Also add in the currently open circuits that we are attempting
+ * to use to give them the benefit of the doubt.
+ */
+double
+pathbias_get_use_success_count(entry_guard_t *guard)
+{
+ return guard->use_successes +
+ pathbias_count_circs_in_states(guard,
+ PATH_STATE_USE_ATTEMPTED,
+ PATH_STATE_USE_SUCCEEDED);
+}
+
+/**
+ * Check the path bias use rate against our consensus parameter limits.
+ *
+ * Emits a log message if the use success rates are too low.
+ *
+ * If pathbias_get_dropguards() is set, we also disable the use of
+ * very failure prone guards.
+ */
+static void
+pathbias_measure_use_rate(entry_guard_t *guard)
+{
+ const or_options_t *options = get_options();
+
+ if (guard->use_attempts > pathbias_get_min_use(options)) {
+ /* Note: We rely on the < comparison here to allow us to set a 0
+ * rate and disable the feature entirely. If refactoring, don't
+ * change to <= */
+ if (pathbias_get_use_success_count(guard)/guard->use_attempts
+ < pathbias_get_extreme_use_rate(options)) {
+ /* Dropping is currently disabled by default. */
+ if (pathbias_get_dropguards(options)) {
+ if (!guard->path_bias_disabled) {
+ log_warn(LD_CIRC,
+ "Your Guard %s ($%s) is failing to carry an extremely large "
+ "amount of stream on its circuits. "
+ "To avoid potential route manipulation attacks, Tor has "
+ "disabled use of this guard. "
+ "Use counts are %ld/%ld. Success counts are %ld/%ld. "
+ "%ld circuits completed, %ld were unusable, %ld collapsed, "
+ "and %ld timed out. "
+ "For reference, your timeout cutoff is %ld seconds.",
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN),
+ tor_lround(pathbias_get_use_success_count(guard)),
+ tor_lround(guard->use_attempts),
+ tor_lround(pathbias_get_close_success_count(guard)),
+ tor_lround(guard->circ_attempts),
+ tor_lround(guard->circ_successes),
+ tor_lround(guard->unusable_circuits),
+ tor_lround(guard->collapsed_circuits),
+ tor_lround(guard->timeouts),
+ tor_lround(get_circuit_build_close_time_ms()/1000));
+ guard->path_bias_disabled = 1;
+ guard->bad_since = approx_time();
+ entry_guards_changed();
+ return;
+ }
+ } else if (!guard->path_bias_use_extreme) {
+ guard->path_bias_use_extreme = 1;
+ log_warn(LD_CIRC,
+ "Your Guard %s ($%s) is failing to carry an extremely large "
+ "amount of streams on its circuits. "
+ "This could indicate a route manipulation attack, network "
+ "overload, bad local network connectivity, or a bug. "
+ "Use counts are %ld/%ld. Success counts are %ld/%ld. "
+ "%ld circuits completed, %ld were unusable, %ld collapsed, "
+ "and %ld timed out. "
+ "For reference, your timeout cutoff is %ld seconds.",
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN),
+ tor_lround(pathbias_get_use_success_count(guard)),
+ tor_lround(guard->use_attempts),
+ tor_lround(pathbias_get_close_success_count(guard)),
+ tor_lround(guard->circ_attempts),
+ tor_lround(guard->circ_successes),
+ tor_lround(guard->unusable_circuits),
+ tor_lround(guard->collapsed_circuits),
+ tor_lround(guard->timeouts),
+ tor_lround(get_circuit_build_close_time_ms()/1000));
+ }
+ } else if (pathbias_get_use_success_count(guard)/guard->use_attempts
+ < pathbias_get_notice_use_rate(options)) {
+ if (!guard->path_bias_use_noticed) {
+ guard->path_bias_use_noticed = 1;
+ log_notice(LD_CIRC,
+ "Your Guard %s ($%s) is failing to carry more streams on its "
+ "circuits than usual. "
+ "Most likely this means the Tor network is overloaded "
+ "or your network connection is poor. "
+ "Use counts are %ld/%ld. Success counts are %ld/%ld. "
+ "%ld circuits completed, %ld were unusable, %ld collapsed, "
+ "and %ld timed out. "
+ "For reference, your timeout cutoff is %ld seconds.",
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN),
+ tor_lround(pathbias_get_use_success_count(guard)),
+ tor_lround(guard->use_attempts),
+ tor_lround(pathbias_get_close_success_count(guard)),
+ tor_lround(guard->circ_attempts),
+ tor_lround(guard->circ_successes),
+ tor_lround(guard->unusable_circuits),
+ tor_lround(guard->collapsed_circuits),
+ tor_lround(guard->timeouts),
+ tor_lround(get_circuit_build_close_time_ms()/1000));
+ }
+ }
+ }
+}
+
+/**
+ * Check the path bias circuit close status rates against our consensus
+ * parameter limits.
+ *
+ * Emits a log message if the use success rates are too low.
+ *
+ * If pathbias_get_dropguards() is set, we also disable the use of
+ * very failure prone guards.
+ *
+ * XXX: This function shares similar log messages and checks to
+ * pathbias_measure_use_rate(). It may be possible to combine them
+ * eventually, especially if we can ever remove the need for 3
+ * levels of closure warns (if the overall circuit failure rate
+ * goes down with ntor). One way to do so would be to multiply
+ * the build rate with the use rate to get an idea of the total
+ * fraction of the total network paths the user is able to use.
+ * See ticket #8159.
+ */
+static void
+pathbias_measure_close_rate(entry_guard_t *guard)
+{
+ const or_options_t *options = get_options();
+
+ if (guard->circ_attempts > pathbias_get_min_circs(options)) {
+ /* Note: We rely on the < comparison here to allow us to set a 0
+ * rate and disable the feature entirely. If refactoring, don't
+ * change to <= */
+ if (pathbias_get_close_success_count(guard)/guard->circ_attempts
+ < pathbias_get_extreme_rate(options)) {
+ /* Dropping is currently disabled by default. */
+ if (pathbias_get_dropguards(options)) {
+ if (!guard->path_bias_disabled) {
+ log_warn(LD_CIRC,
+ "Your Guard %s ($%s) is failing an extremely large "
+ "amount of circuits. "
+ "To avoid potential route manipulation attacks, Tor has "
+ "disabled use of this guard. "
+ "Success counts are %ld/%ld. Use counts are %ld/%ld. "
+ "%ld circuits completed, %ld were unusable, %ld collapsed, "
+ "and %ld timed out. "
+ "For reference, your timeout cutoff is %ld seconds.",
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN),
+ tor_lround(pathbias_get_close_success_count(guard)),
+ tor_lround(guard->circ_attempts),
+ tor_lround(pathbias_get_use_success_count(guard)),
+ tor_lround(guard->use_attempts),
+ tor_lround(guard->circ_successes),
+ tor_lround(guard->unusable_circuits),
+ tor_lround(guard->collapsed_circuits),
+ tor_lround(guard->timeouts),
+ tor_lround(get_circuit_build_close_time_ms()/1000));
+ guard->path_bias_disabled = 1;
+ guard->bad_since = approx_time();
+ entry_guards_changed();
+ return;
+ }
+ } else if (!guard->path_bias_extreme) {
+ guard->path_bias_extreme = 1;
+ log_warn(LD_CIRC,
+ "Your Guard %s ($%s) is failing an extremely large "
+ "amount of circuits. "
+ "This could indicate a route manipulation attack, "
+ "extreme network overload, or a bug. "
+ "Success counts are %ld/%ld. Use counts are %ld/%ld. "
+ "%ld circuits completed, %ld were unusable, %ld collapsed, "
+ "and %ld timed out. "
+ "For reference, your timeout cutoff is %ld seconds.",
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN),
+ tor_lround(pathbias_get_close_success_count(guard)),
+ tor_lround(guard->circ_attempts),
+ tor_lround(pathbias_get_use_success_count(guard)),
+ tor_lround(guard->use_attempts),
+ tor_lround(guard->circ_successes),
+ tor_lround(guard->unusable_circuits),
+ tor_lround(guard->collapsed_circuits),
+ tor_lround(guard->timeouts),
+ tor_lround(get_circuit_build_close_time_ms()/1000));
+ }
+ } else if (pathbias_get_close_success_count(guard)/guard->circ_attempts
+ < pathbias_get_warn_rate(options)) {
+ if (!guard->path_bias_warned) {
+ guard->path_bias_warned = 1;
+ log_warn(LD_CIRC,
+ "Your Guard %s ($%s) is failing a very large "
+ "amount of circuits. "
+ "Most likely this means the Tor network is "
+ "overloaded, but it could also mean an attack against "
+ "you or potentially the guard itself. "
+ "Success counts are %ld/%ld. Use counts are %ld/%ld. "
+ "%ld circuits completed, %ld were unusable, %ld collapsed, "
+ "and %ld timed out. "
+ "For reference, your timeout cutoff is %ld seconds.",
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN),
+ tor_lround(pathbias_get_close_success_count(guard)),
+ tor_lround(guard->circ_attempts),
+ tor_lround(pathbias_get_use_success_count(guard)),
+ tor_lround(guard->use_attempts),
+ tor_lround(guard->circ_successes),
+ tor_lround(guard->unusable_circuits),
+ tor_lround(guard->collapsed_circuits),
+ tor_lround(guard->timeouts),
+ tor_lround(get_circuit_build_close_time_ms()/1000));
+ }
+ } else if (pathbias_get_close_success_count(guard)/guard->circ_attempts
+ < pathbias_get_notice_rate(options)) {
+ if (!guard->path_bias_noticed) {
+ guard->path_bias_noticed = 1;
+ log_notice(LD_CIRC,
+ "Your Guard %s ($%s) is failing more circuits than "
+ "usual. "
+ "Most likely this means the Tor network is overloaded. "
+ "Success counts are %ld/%ld. Use counts are %ld/%ld. "
+ "%ld circuits completed, %ld were unusable, %ld collapsed, "
+ "and %ld timed out. "
+ "For reference, your timeout cutoff is %ld seconds.",
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN),
+ tor_lround(pathbias_get_close_success_count(guard)),
+ tor_lround(guard->circ_attempts),
+ tor_lround(pathbias_get_use_success_count(guard)),
+ tor_lround(guard->use_attempts),
+ tor_lround(guard->circ_successes),
+ tor_lround(guard->unusable_circuits),
+ tor_lround(guard->collapsed_circuits),
+ tor_lround(guard->timeouts),
+ tor_lround(get_circuit_build_close_time_ms()/1000));
+ }
+ }
+ }
+}
+
+/**
+ * This function scales the path bias use rates if we have
+ * more data than the scaling threshold. This allows us to
+ * be more sensitive to recent measurements.
+ *
+ * XXX: The attempt count transfer stuff here might be done
+ * better by keeping separate pending counters that get
+ * transfered at circuit close. See ticket #8160.
+ */
+static void
+pathbias_scale_close_rates(entry_guard_t *guard)
+{
+ const or_options_t *options = get_options();
+
+ /* If we get a ton of circuits, just scale everything down */
+ if (guard->circ_attempts > pathbias_get_scale_threshold(options)) {
+ double scale_ratio = pathbias_get_scale_ratio(options);
+ int opened_attempts = pathbias_count_circs_in_states(guard,
+ PATH_STATE_BUILD_ATTEMPTED, PATH_STATE_BUILD_ATTEMPTED);
+ int opened_built = pathbias_count_circs_in_states(guard,
+ PATH_STATE_BUILD_SUCCEEDED,
+ PATH_STATE_USE_FAILED);
+ /* Verify that the counts are sane before and after scaling */
+ int counts_are_sane = (guard->circ_attempts >= guard->circ_successes);
+
+ guard->circ_attempts -= (opened_attempts+opened_built);
+ guard->circ_successes -= opened_built;
+
+ guard->circ_attempts *= scale_ratio;
+ guard->circ_successes *= scale_ratio;
+ guard->timeouts *= scale_ratio;
+ guard->successful_circuits_closed *= scale_ratio;
+ guard->collapsed_circuits *= scale_ratio;
+ guard->unusable_circuits *= scale_ratio;
+
+ guard->circ_attempts += (opened_attempts+opened_built);
+ guard->circ_successes += opened_built;
+
+ entry_guards_changed();
+
+ log_info(LD_CIRC,
+ "Scaled pathbias counts to (%f,%f)/%f (%d/%d open) for guard "
+ "%s ($%s)",
+ guard->circ_successes, guard->successful_circuits_closed,
+ guard->circ_attempts, opened_built, opened_attempts,
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN));
+
+ /* Have the counts just become invalid by this scaling attempt? */
+ if (counts_are_sane && guard->circ_attempts < guard->circ_successes) {
+ log_notice(LD_BUG,
+ "Scaling has mangled pathbias counts to %f/%f (%d/%d open) "
+ "for guard %s ($%s)",
+ guard->circ_successes, guard->circ_attempts, opened_built,
+ opened_attempts, guard->nickname,
+ hex_str(guard->identity, DIGEST_LEN));
+ }
+ }
+}
+
+/**
+ * This function scales the path bias circuit close rates if we have
+ * more data than the scaling threshold. This allows us to be more
+ * sensitive to recent measurements.
+ *
+ * XXX: The attempt count transfer stuff here might be done
+ * better by keeping separate pending counters that get
+ * transfered at circuit close. See ticket #8160.
+ */
+void
+pathbias_scale_use_rates(entry_guard_t *guard)
+{
+ const or_options_t *options = get_options();
+
+ /* If we get a ton of circuits, just scale everything down */
+ if (guard->use_attempts > pathbias_get_scale_use_threshold(options)) {
+ double scale_ratio = pathbias_get_scale_ratio(options);
+ int opened_attempts = pathbias_count_circs_in_states(guard,
+ PATH_STATE_USE_ATTEMPTED, PATH_STATE_USE_SUCCEEDED);
+ /* Verify that the counts are sane before and after scaling */
+ int counts_are_sane = (guard->use_attempts >= guard->use_successes);
+
+ guard->use_attempts -= opened_attempts;
+
+ guard->use_attempts *= scale_ratio;
+ guard->use_successes *= scale_ratio;
+
+ guard->use_attempts += opened_attempts;
+
+ log_info(LD_CIRC,
+ "Scaled pathbias use counts to %f/%f (%d open) for guard %s ($%s)",
+ guard->use_successes, guard->use_attempts, opened_attempts,
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN));
+
+ /* Have the counts just become invalid by this scaling attempt? */
+ if (counts_are_sane && guard->use_attempts < guard->use_successes) {
+ log_notice(LD_BUG,
+ "Scaling has mangled pathbias usage counts to %f/%f "
+ "(%d open) for guard %s ($%s)",
+ guard->circ_successes, guard->circ_attempts,
+ opened_attempts, guard->nickname,
+ hex_str(guard->identity, DIGEST_LEN));
+ }
+
+ entry_guards_changed();
+ }
+}
+
diff --git a/src/or/circpathbias.h b/src/or/circpathbias.h
new file mode 100644
index 000000000..c95d801a4
--- /dev/null
+++ b/src/or/circpathbias.h
@@ -0,0 +1,29 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file circuitbuild.h
+ * \brief Header file for circuitbuild.c.
+ **/
+
+#ifndef TOR_CIRCPATHBIAS_H
+#define TOR_CIRCPATHBIAS_H
+
+double pathbias_get_extreme_rate(const or_options_t *options);
+double pathbias_get_extreme_use_rate(const or_options_t *options);
+int pathbias_get_dropguards(const or_options_t *options);
+void pathbias_count_timeout(origin_circuit_t *circ);
+void pathbias_count_build_success(origin_circuit_t *circ);
+int pathbias_count_build_attempt(origin_circuit_t *circ);
+int pathbias_check_close(origin_circuit_t *circ, int reason);
+int pathbias_check_probe_response(circuit_t *circ, const cell_t *cell);
+void pathbias_count_use_attempt(origin_circuit_t *circ);
+void pathbias_mark_use_success(origin_circuit_t *circ);
+void pathbias_mark_use_rollback(origin_circuit_t *circ);
+const char *pathbias_state_to_string(path_state_t state);
+
+#endif
+
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index 43d2ffe4d..8e6bb59ef 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -11,6 +11,7 @@
#include "or.h"
#include "channel.h"
+#include "circpathbias.h"
#include "circuitbuild.h"
#include "circuitlist.h"
#include "circuitstats.h"
@@ -40,19 +41,11 @@
#include "routerparse.h"
#include "routerset.h"
#include "crypto.h"
-#include "connection_edge.h"
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
-/********* START VARIABLES **********/
-
-/** A global list of all circuits at this hop. */
-extern circuit_t *global_circuitlist;
-
-/********* END VARIABLES ************/
-
static channel_t * channel_connect_for_circuit(const tor_addr_t *addr,
uint16_t port,
const char *id_digest);
@@ -64,14 +57,6 @@ static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath);
static int onion_extend_cpath(origin_circuit_t *circ);
static int count_acceptable_nodes(smartlist_t *routers);
static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
-static int entry_guard_inc_circ_attempt_count(entry_guard_t *guard);
-static void pathbias_count_build_success(origin_circuit_t *circ);
-static void pathbias_count_successful_close(origin_circuit_t *circ);
-static void pathbias_count_collapse(origin_circuit_t *circ);
-static void pathbias_count_use_failed(origin_circuit_t *circ);
-static void pathbias_measure_use_rate(entry_guard_t *guard);
-static void pathbias_measure_close_rate(entry_guard_t *guard);
-static void pathbias_scale_use_rates(entry_guard_t *guard);
/** This function tries to get a channel to the specified endpoint,
* and then calls command_setup_channel() to give it the right
@@ -600,16 +585,18 @@ should_use_create_fast_for_circuit(origin_circuit_t *circ)
if (!circ->cpath->extend_info->onion_key)
return 1; /* our hand is forced: only a create_fast will work. */
- if (!options->FastFirstHopPK)
- return 0; /* we prefer to avoid create_fast */
if (public_server_mode(options)) {
/* We're a server, and we know an onion key. We can choose.
* Prefer to blend our circuit into the other circuits we are
* creating on behalf of others. */
return 0;
}
+ if (options->FastFirstHopPK == -1) {
+ /* option is "auto", so look at the consensus. */
+ return networkstatus_get_param(NULL, "usecreatefast", 1, 0, 1);
+ }
- return 1;
+ return options->FastFirstHopPK;
}
/** Return true if <b>circ</b> is the type of circuit we want to count
@@ -779,20 +766,24 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
* it off at, we probably had a suspend event along this codepath,
* and we should discard the value.
*/
- if (timediff < 0 || timediff > 2*circ_times.close_ms+1000) {
+ if (timediff < 0 ||
+ timediff > 2*get_circuit_build_close_time_ms()+1000) {
log_notice(LD_CIRC, "Strange value for circuit build time: %ldmsec. "
"Assuming clock jump. Purpose %d (%s)", timediff,
circ->base_.purpose,
circuit_purpose_to_string(circ->base_.purpose));
} else if (!circuit_build_times_disabled()) {
/* Only count circuit times if the network is live */
- if (circuit_build_times_network_check_live(&circ_times)) {
- circuit_build_times_add_time(&circ_times, (build_time_t)timediff);
- circuit_build_times_set_timeout(&circ_times);
+ if (circuit_build_times_network_check_live(
+ get_circuit_build_times())) {
+ circuit_build_times_add_time(get_circuit_build_times_mutable(),
+ (build_time_t)timediff);
+ circuit_build_times_set_timeout(get_circuit_build_times_mutable());
}
if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
- circuit_build_times_network_circ_success(&circ_times);
+ circuit_build_times_network_circ_success(
+ get_circuit_build_times_mutable());
}
}
}
@@ -1087,1516 +1078,6 @@ circuit_init_cpath_crypto(crypt_path_t *cpath, const char *key_data,
return 0;
}
-/** The minimum number of circuit attempts before we start
- * thinking about warning about path bias and dropping guards */
-static int
-pathbias_get_min_circs(const or_options_t *options)
-{
-#define DFLT_PATH_BIAS_MIN_CIRC 150
- if (options->PathBiasCircThreshold >= 5)
- return options->PathBiasCircThreshold;
- else
- return networkstatus_get_param(NULL, "pb_mincircs",
- DFLT_PATH_BIAS_MIN_CIRC,
- 5, INT32_MAX);
-}
-
-/** The circuit success rate below which we issue a notice */
-static double
-pathbias_get_notice_rate(const or_options_t *options)
-{
-#define DFLT_PATH_BIAS_NOTICE_PCT 70
- if (options->PathBiasNoticeRate >= 0.0)
- return options->PathBiasNoticeRate;
- else
- return networkstatus_get_param(NULL, "pb_noticepct",
- DFLT_PATH_BIAS_NOTICE_PCT, 0, 100)/100.0;
-}
-
-/* XXXX024 I'd like to have this be static again, but entrynodes.c needs it. */
-/** The circuit success rate below which we issue a warn */
-static double
-pathbias_get_warn_rate(const or_options_t *options)
-{
-#define DFLT_PATH_BIAS_WARN_PCT 50
- if (options->PathBiasWarnRate >= 0.0)
- return options->PathBiasWarnRate;
- else
- return networkstatus_get_param(NULL, "pb_warnpct",
- DFLT_PATH_BIAS_WARN_PCT, 0, 100)/100.0;
-}
-
-/* XXXX024 I'd like to have this be static again, but entrynodes.c needs it. */
-/**
- * The extreme rate is the rate at which we would drop the guard,
- * if pb_dropguard is also set. Otherwise we just warn.
- */
-double
-pathbias_get_extreme_rate(const or_options_t *options)
-{
-#define DFLT_PATH_BIAS_EXTREME_PCT 30
- if (options->PathBiasExtremeRate >= 0.0)
- return options->PathBiasExtremeRate;
- else
- return networkstatus_get_param(NULL, "pb_extremepct",
- DFLT_PATH_BIAS_EXTREME_PCT, 0, 100)/100.0;
-}
-
-/* XXXX024 I'd like to have this be static again, but entrynodes.c needs it. */
-/**
- * If 1, we actually disable use of guards that fall below
- * the extreme_pct.
- */
-int
-pathbias_get_dropguards(const or_options_t *options)
-{
-#define DFLT_PATH_BIAS_DROP_GUARDS 0
- if (options->PathBiasDropGuards >= 0)
- return options->PathBiasDropGuards;
- else
- return networkstatus_get_param(NULL, "pb_dropguards",
- DFLT_PATH_BIAS_DROP_GUARDS, 0, 1);
-}
-
-/**
- * This is the number of circuits at which we scale our
- * counts by mult_factor/scale_factor. Note, this count is
- * not exact, as we only perform the scaling in the event
- * of no integer truncation.
- */
-static int
-pathbias_get_scale_threshold(const or_options_t *options)
-{
-#define DFLT_PATH_BIAS_SCALE_THRESHOLD 300
- if (options->PathBiasScaleThreshold >= 10)
- return options->PathBiasScaleThreshold;
- else
- return networkstatus_get_param(NULL, "pb_scalecircs",
- DFLT_PATH_BIAS_SCALE_THRESHOLD, 10,
- INT32_MAX);
-}
-
-/**
- * Compute the path bias scaling ratio from the consensus
- * parameters pb_multfactor/pb_scalefactor.
- *
- * Returns a value in (0, 1.0] which we multiply our pathbias
- * counts with to scale them down.
- */
-static double
-pathbias_get_scale_ratio(const or_options_t *options)
-{
- /*
- * The scale factor is the denominator for our scaling
- * of circuit counts for our path bias window.
- *
- * Note that our use of doubles for the path bias state
- * file means that powers of 2 work best here.
- */
- int denominator = networkstatus_get_param(NULL, "pb_scalefactor",
- 2, 2, INT32_MAX);
- (void) options;
- /**
- * The mult factor is the numerator for our scaling
- * of circuit counts for our path bias window. It
- * allows us to scale by fractions.
- */
- return networkstatus_get_param(NULL, "pb_multfactor",
- 1, 1, denominator)/((double)denominator);
-}
-
-/** The minimum number of circuit usage attempts before we start
- * thinking about warning about path use bias and dropping guards */
-static int
-pathbias_get_min_use(const or_options_t *options)
-{
-#define DFLT_PATH_BIAS_MIN_USE 20
- if (options->PathBiasUseThreshold >= 3)
- return options->PathBiasUseThreshold;
- else
- return networkstatus_get_param(NULL, "pb_minuse",
- DFLT_PATH_BIAS_MIN_USE,
- 3, INT32_MAX);
-}
-
-/** The circuit use success rate below which we issue a notice */
-static double
-pathbias_get_notice_use_rate(const or_options_t *options)
-{
-#define DFLT_PATH_BIAS_NOTICE_USE_PCT 80
- if (options->PathBiasNoticeUseRate >= 0.0)
- return options->PathBiasNoticeUseRate;
- else
- return networkstatus_get_param(NULL, "pb_noticeusepct",
- DFLT_PATH_BIAS_NOTICE_USE_PCT,
- 0, 100)/100.0;
-}
-
-/**
- * The extreme use rate is the rate at which we would drop the guard,
- * if pb_dropguard is also set. Otherwise we just warn.
- */
-double
-pathbias_get_extreme_use_rate(const or_options_t *options)
-{
-#define DFLT_PATH_BIAS_EXTREME_USE_PCT 60
- if (options->PathBiasExtremeUseRate >= 0.0)
- return options->PathBiasExtremeUseRate;
- else
- return networkstatus_get_param(NULL, "pb_extremeusepct",
- DFLT_PATH_BIAS_EXTREME_USE_PCT,
- 0, 100)/100.0;
-}
-
-/**
- * This is the number of circuits at which we scale our
- * use counts by mult_factor/scale_factor. Note, this count is
- * not exact, as we only perform the scaling in the event
- * of no integer truncation.
- */
-static int
-pathbias_get_scale_use_threshold(const or_options_t *options)
-{
-#define DFLT_PATH_BIAS_SCALE_USE_THRESHOLD 100
- if (options->PathBiasScaleUseThreshold >= 10)
- return options->PathBiasScaleUseThreshold;
- else
- return networkstatus_get_param(NULL, "pb_scaleuse",
- DFLT_PATH_BIAS_SCALE_USE_THRESHOLD,
- 10, INT32_MAX);
-}
-
-/**
- * Convert a Guard's path state to string.
- */
-const char *
-pathbias_state_to_string(path_state_t state)
-{
- switch (state) {
- case PATH_STATE_NEW_CIRC:
- return "new";
- case PATH_STATE_BUILD_ATTEMPTED:
- return "build attempted";
- case PATH_STATE_BUILD_SUCCEEDED:
- return "build succeeded";
- case PATH_STATE_USE_ATTEMPTED:
- return "use attempted";
- case PATH_STATE_USE_SUCCEEDED:
- return "use succeeded";
- case PATH_STATE_USE_FAILED:
- return "use failed";
- case PATH_STATE_ALREADY_COUNTED:
- return "already counted";
- }
-
- return "unknown";
-}
-
-/**
- * This function decides if a circuit has progressed far enough to count
- * as a circuit "attempt". As long as end-to-end tagging is possible,
- * we assume the adversary will use it over hop-to-hop failure. Therefore,
- * we only need to account bias for the last hop. This should make us
- * much more resilient to ambient circuit failure, and also make that
- * failure easier to measure (we only need to measure Exit failure rates).
- */
-static int
-pathbias_is_new_circ_attempt(origin_circuit_t *circ)
-{
-#define N2N_TAGGING_IS_POSSIBLE
-#ifdef N2N_TAGGING_IS_POSSIBLE
- /* cpath is a circular list. We want circs with more than one hop,
- * and the second hop must be waiting for keys still (it's just
- * about to get them). */
- return circ->cpath &&
- circ->cpath->next != circ->cpath &&
- circ->cpath->next->state == CPATH_STATE_AWAITING_KEYS;
-#else
- /* If tagging attacks are no longer possible, we probably want to
- * count bias from the first hop. However, one could argue that
- * timing-based tagging is still more useful than per-hop failure.
- * In which case, we'd never want to use this.
- */
- return circ->cpath &&
- circ->cpath->state == CPATH_STATE_AWAITING_KEYS;
-#endif
-}
-
-/**
- * Decide if the path bias code should count a circuit.
- *
- * @returns 1 if we should count it, 0 otherwise.
- */
-static int
-pathbias_should_count(origin_circuit_t *circ)
-{
-#define PATHBIAS_COUNT_INTERVAL (600)
- static ratelim_t count_limit =
- RATELIM_INIT(PATHBIAS_COUNT_INTERVAL);
- char *rate_msg = NULL;
-
- /* We can't do path bias accounting without entry guards.
- * Testing and controller circuits also have no guards.
- *
- * We also don't count server-side rends, because their
- * endpoint could be chosen maliciously.
- * Similarly, we can't count client-side intro attempts,
- * because clients can be manipulated into connecting to
- * malicious intro points. */
- if (get_options()->UseEntryGuards == 0 ||
- circ->base_.purpose == CIRCUIT_PURPOSE_TESTING ||
- circ->base_.purpose == CIRCUIT_PURPOSE_CONTROLLER ||
- circ->base_.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND ||
- circ->base_.purpose == CIRCUIT_PURPOSE_S_REND_JOINED ||
- (circ->base_.purpose >= CIRCUIT_PURPOSE_C_INTRODUCING &&
- circ->base_.purpose <= CIRCUIT_PURPOSE_C_INTRODUCE_ACKED)) {
-
- /* Check to see if the shouldcount result has changed due to a
- * unexpected purpose change that would affect our results.
- *
- * The reason we check the path state too here is because for the
- * cannibalized versions of these purposes, we count them as successful
- * before their purpose change.
- */
- if (circ->pathbias_shouldcount == PATHBIAS_SHOULDCOUNT_COUNTED
- && circ->path_state != PATH_STATE_ALREADY_COUNTED) {
- log_info(LD_BUG,
- "Circuit %d is now being ignored despite being counted "
- "in the past. Purpose is %s, path state is %s",
- circ->global_identifier,
- circuit_purpose_to_string(circ->base_.purpose),
- pathbias_state_to_string(circ->path_state));
- }
- circ->pathbias_shouldcount = PATHBIAS_SHOULDCOUNT_IGNORED;
- return 0;
- }
-
- /* Completely ignore one hop circuits */
- if (circ->build_state->onehop_tunnel ||
- circ->build_state->desired_path_len == 1) {
- /* Check for inconsistency */
- if (circ->build_state->desired_path_len != 1 ||
- !circ->build_state->onehop_tunnel) {
- if ((rate_msg = rate_limit_log(&count_limit, approx_time()))) {
- log_info(LD_BUG,
- "One-hop circuit has length %d. Path state is %s. "
- "Circuit is a %s currently %s.%s",
- circ->build_state->desired_path_len,
- pathbias_state_to_string(circ->path_state),
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state),
- rate_msg);
- tor_free(rate_msg);
- }
- tor_fragile_assert();
- }
-
- /* Check to see if the shouldcount result has changed due to a
- * unexpected change that would affect our results */
- if (circ->pathbias_shouldcount == PATHBIAS_SHOULDCOUNT_COUNTED) {
- log_info(LD_BUG,
- "One-hop circuit %d is now being ignored despite being counted "
- "in the past. Purpose is %s, path state is %s",
- circ->global_identifier,
- circuit_purpose_to_string(circ->base_.purpose),
- pathbias_state_to_string(circ->path_state));
- }
- circ->pathbias_shouldcount = PATHBIAS_SHOULDCOUNT_IGNORED;
- return 0;
- }
-
- /* Check to see if the shouldcount result has changed due to a
- * unexpected purpose change that would affect our results */
- if (circ->pathbias_shouldcount == PATHBIAS_SHOULDCOUNT_IGNORED) {
- log_info(LD_BUG,
- "Circuit %d is now being counted despite being ignored "
- "in the past. Purpose is %s, path state is %s",
- circ->global_identifier,
- circuit_purpose_to_string(circ->base_.purpose),
- pathbias_state_to_string(circ->path_state));
- }
- circ->pathbias_shouldcount = PATHBIAS_SHOULDCOUNT_COUNTED;
-
- return 1;
-}
-
-/**
- * Check our circuit state to see if this is a successful circuit attempt.
- * If so, record it in the current guard's path bias circ_attempt count.
- *
- * Also check for several potential error cases for bug #6475.
- */
-static int
-pathbias_count_build_attempt(origin_circuit_t *circ)
-{
-#define CIRC_ATTEMPT_NOTICE_INTERVAL (600)
- static ratelim_t circ_attempt_notice_limit =
- RATELIM_INIT(CIRC_ATTEMPT_NOTICE_INTERVAL);
- char *rate_msg = NULL;
-
- if (!pathbias_should_count(circ)) {
- return 0;
- }
-
- if (pathbias_is_new_circ_attempt(circ)) {
- /* Help track down the real cause of bug #6475: */
- if (circ->has_opened && circ->path_state != PATH_STATE_BUILD_ATTEMPTED) {
- if ((rate_msg = rate_limit_log(&circ_attempt_notice_limit,
- approx_time()))) {
- log_info(LD_BUG,
- "Opened circuit is in strange path state %s. "
- "Circuit is a %s currently %s.%s",
- pathbias_state_to_string(circ->path_state),
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state),
- rate_msg);
- tor_free(rate_msg);
- }
- }
-
- /* Don't re-count cannibalized circs.. */
- if (!circ->has_opened) {
- entry_guard_t *guard = NULL;
-
- if (circ->cpath && circ->cpath->extend_info) {
- guard = entry_guard_get_by_id_digest(
- circ->cpath->extend_info->identity_digest);
- } else if (circ->base_.n_chan) {
- guard =
- entry_guard_get_by_id_digest(circ->base_.n_chan->identity_digest);
- }
-
- if (guard) {
- if (circ->path_state == PATH_STATE_NEW_CIRC) {
- circ->path_state = PATH_STATE_BUILD_ATTEMPTED;
-
- if (entry_guard_inc_circ_attempt_count(guard) < 0) {
- /* Bogus guard; we already warned. */
- return -END_CIRC_REASON_TORPROTOCOL;
- }
- } else {
- if ((rate_msg = rate_limit_log(&circ_attempt_notice_limit,
- approx_time()))) {
- log_info(LD_BUG,
- "Unopened circuit has strange path state %s. "
- "Circuit is a %s currently %s.%s",
- pathbias_state_to_string(circ->path_state),
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state),
- rate_msg);
- tor_free(rate_msg);
- }
- }
- } else {
- if ((rate_msg = rate_limit_log(&circ_attempt_notice_limit,
- approx_time()))) {
- log_info(LD_CIRC,
- "Unopened circuit has no known guard. "
- "Circuit is a %s currently %s.%s",
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state),
- rate_msg);
- tor_free(rate_msg);
- }
- }
- }
- }
-
- return 0;
-}
-
-/**
- * Check our circuit state to see if this is a successful circuit
- * completion. If so, record it in the current guard's path bias
- * success count.
- *
- * Also check for several potential error cases for bug #6475.
- */
-static void
-pathbias_count_build_success(origin_circuit_t *circ)
-{
-#define SUCCESS_NOTICE_INTERVAL (600)
- static ratelim_t success_notice_limit =
- RATELIM_INIT(SUCCESS_NOTICE_INTERVAL);
- char *rate_msg = NULL;
- entry_guard_t *guard = NULL;
-
- if (!pathbias_should_count(circ)) {
- return;
- }
-
- /* Don't count cannibalized/reused circs for path bias
- * "build" success, since they get counted under "use" success. */
- if (!circ->has_opened) {
- if (circ->cpath && circ->cpath->extend_info) {
- guard = entry_guard_get_by_id_digest(
- circ->cpath->extend_info->identity_digest);
- }
-
- if (guard) {
- if (circ->path_state == PATH_STATE_BUILD_ATTEMPTED) {
- circ->path_state = PATH_STATE_BUILD_SUCCEEDED;
- guard->circ_successes++;
- entry_guards_changed();
-
- log_info(LD_CIRC, "Got success count %f/%f for guard %s ($%s)",
- guard->circ_successes, guard->circ_attempts,
- guard->nickname, hex_str(guard->identity, DIGEST_LEN));
- } else {
- if ((rate_msg = rate_limit_log(&success_notice_limit,
- approx_time()))) {
- log_info(LD_BUG,
- "Succeeded circuit is in strange path state %s. "
- "Circuit is a %s currently %s.%s",
- pathbias_state_to_string(circ->path_state),
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state),
- rate_msg);
- tor_free(rate_msg);
- }
- }
-
- if (guard->circ_attempts < guard->circ_successes) {
- log_notice(LD_BUG, "Unexpectedly high successes counts (%f/%f) "
- "for guard %s ($%s)",
- guard->circ_successes, guard->circ_attempts,
- guard->nickname, hex_str(guard->identity, DIGEST_LEN));
- }
- /* In rare cases, CIRCUIT_PURPOSE_TESTING can get converted to
- * CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT and have no guards here.
- * No need to log that case. */
- } else if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
- if ((rate_msg = rate_limit_log(&success_notice_limit,
- approx_time()))) {
- log_info(LD_CIRC,
- "Completed circuit has no known guard. "
- "Circuit is a %s currently %s.%s",
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state),
- rate_msg);
- tor_free(rate_msg);
- }
- }
- } else {
- if (circ->path_state < PATH_STATE_BUILD_SUCCEEDED) {
- if ((rate_msg = rate_limit_log(&success_notice_limit,
- approx_time()))) {
- log_info(LD_BUG,
- "Opened circuit is in strange path state %s. "
- "Circuit is a %s currently %s.%s",
- pathbias_state_to_string(circ->path_state),
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state),
- rate_msg);
- tor_free(rate_msg);
- }
- }
- }
-}
-
-/**
- * Record an attempt to use a circuit. Changes the circuit's
- * path state and update its guard's usage counter.
- *
- * Used for path bias usage accounting.
- */
-void
-pathbias_count_use_attempt(origin_circuit_t *circ)
-{
- entry_guard_t *guard;
-
- if (!pathbias_should_count(circ)) {
- return;
- }
-
- if (circ->path_state < PATH_STATE_BUILD_SUCCEEDED) {
- log_notice(LD_BUG,
- "Used circuit is in strange path state %s. "
- "Circuit is a %s currently %s.",
- pathbias_state_to_string(circ->path_state),
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state));
- } else if (circ->path_state < PATH_STATE_USE_ATTEMPTED) {
- guard = entry_guard_get_by_id_digest(
- circ->cpath->extend_info->identity_digest);
- if (guard) {
- pathbias_measure_use_rate(guard);
- pathbias_scale_use_rates(guard);
- guard->use_attempts++;
- entry_guards_changed();
-
- log_debug(LD_CIRC,
- "Marked circuit %d (%f/%f) as used for guard %s ($%s).",
- circ->global_identifier,
- guard->use_successes, guard->use_attempts,
- guard->nickname, hex_str(guard->identity, DIGEST_LEN));
- }
-
- circ->path_state = PATH_STATE_USE_ATTEMPTED;
- } else {
- /* Harmless but educational log message */
- log_info(LD_CIRC,
- "Used circuit %d is already in path state %s. "
- "Circuit is a %s currently %s.",
- circ->global_identifier,
- pathbias_state_to_string(circ->path_state),
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state));
- }
-
- return;
-}
-
-/**
- * Check the circuit's path state is appropriate and mark it as
- * successfully used. Used for path bias usage accounting.
- *
- * We don't actually increment the guard's counters until
- * pathbias_check_close(), because the circuit can still transition
- * back to PATH_STATE_USE_ATTEMPTED if a stream fails later (this
- * is done so we can probe the circuit for liveness at close).
- */
-void
-pathbias_mark_use_success(origin_circuit_t *circ)
-{
- if (!pathbias_should_count(circ)) {
- return;
- }
-
- if (circ->path_state < PATH_STATE_USE_ATTEMPTED) {
- log_notice(LD_BUG,
- "Used circuit %d is in strange path state %s. "
- "Circuit is a %s currently %s.",
- circ->global_identifier,
- pathbias_state_to_string(circ->path_state),
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state));
-
- pathbias_count_use_attempt(circ);
- }
-
- /* We don't do any accounting at the guard until actual circuit close */
- circ->path_state = PATH_STATE_USE_SUCCEEDED;
-
- return;
-}
-
-/**
- * If a stream ever detatches from a circuit in a retriable way,
- * we need to mark this circuit as still needing either another
- * successful stream, or in need of a probe.
- *
- * An adversary could let the first stream request succeed (ie the
- * resolve), but then tag and timeout the remainder (via cell
- * dropping), forcing them on new circuits.
- *
- * Rolling back the state will cause us to probe such circuits, which
- * should lead to probe failures in the event of such tagging due to
- * either unrecognized cells coming in while we wait for the probe,
- * or the cipher state getting out of sync in the case of dropped cells.
- */
-void
-pathbias_mark_use_rollback(origin_circuit_t *circ)
-{
- if (circ->path_state == PATH_STATE_USE_SUCCEEDED) {
- log_info(LD_CIRC,
- "Rolling back pathbias use state to 'attempted' for detached "
- "circuit %d", circ->global_identifier);
- circ->path_state = PATH_STATE_USE_ATTEMPTED;
- }
-}
-
-/**
- * Actually count a circuit success towards a guard's usage counters
- * if the path state is appropriate.
- */
-static void
-pathbias_count_use_success(origin_circuit_t *circ)
-{
- entry_guard_t *guard;
-
- if (!pathbias_should_count(circ)) {
- return;
- }
-
- if (circ->path_state != PATH_STATE_USE_SUCCEEDED) {
- log_notice(LD_BUG,
- "Successfully used circuit %d is in strange path state %s. "
- "Circuit is a %s currently %s.",
- circ->global_identifier,
- pathbias_state_to_string(circ->path_state),
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state));
- } else {
- guard = entry_guard_get_by_id_digest(
- circ->cpath->extend_info->identity_digest);
- if (guard) {
- guard->use_successes++;
- entry_guards_changed();
-
- if (guard->use_attempts < guard->use_successes) {
- log_notice(LD_BUG, "Unexpectedly high use successes counts (%f/%f) "
- "for guard %s=%s",
- guard->use_successes, guard->use_attempts,
- guard->nickname, hex_str(guard->identity, DIGEST_LEN));
- }
-
- log_debug(LD_CIRC,
- "Marked circuit %d (%f/%f) as used successfully for guard "
- "%s ($%s).",
- circ->global_identifier, guard->use_successes,
- guard->use_attempts, guard->nickname,
- hex_str(guard->identity, DIGEST_LEN));
- }
- }
-
- return;
-}
-
-/**
- * Send a probe down a circuit that the client attempted to use,
- * but for which the stream timed out/failed. The probe is a
- * RELAY_BEGIN cell with a 0.a.b.c destination address, which
- * the exit will reject and reply back, echoing that address.
- *
- * The reason for such probes is because it is possible to bias
- * a user's paths simply by causing timeouts, and these timeouts
- * are not possible to differentiate from unresponsive servers.
- *
- * The probe is sent at the end of the circuit lifetime for two
- * reasons: to prevent cryptographic taggers from being able to
- * drop cells to cause timeouts, and to prevent easy recognition
- * of probes before any real client traffic happens.
- *
- * Returns -1 if we couldn't probe, 0 otherwise.
- */
-static int
-pathbias_send_usable_probe(circuit_t *circ)
-{
- /* Based on connection_ap_handshake_send_begin() */
- char payload[CELL_PAYLOAD_SIZE];
- int payload_len;
- origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
- crypt_path_t *cpath_layer = NULL;
- char *probe_nonce = NULL;
-
- tor_assert(ocirc);
-
- cpath_layer = ocirc->cpath->prev;
-
- if (cpath_layer->state != CPATH_STATE_OPEN) {
- /* This can happen for cannibalized circuits. Their
- * last hop isn't yet open */
- log_info(LD_CIRC,
- "Got pathbias probe request for unopened circuit %d. "
- "Opened %d, len %d", ocirc->global_identifier,
- ocirc->has_opened, ocirc->build_state->desired_path_len);
- return -1;
- }
-
- /* We already went down this road. */
- if (circ->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING &&
- ocirc->pathbias_probe_id) {
- log_info(LD_CIRC,
- "Got pathbias probe request for circuit %d with "
- "outstanding probe", ocirc->global_identifier);
- return -1;
- }
-
- /* Can't probe if the channel isn't open */
- if (circ->n_chan == NULL ||
- (circ->n_chan->state != CHANNEL_STATE_OPEN
- && circ->n_chan->state != CHANNEL_STATE_MAINT)) {
- log_info(LD_CIRC,
- "Skipping pathbias probe for circuit %d: Channel is not open.",
- ocirc->global_identifier);
- return -1;
- }
-
- circuit_change_purpose(circ, CIRCUIT_PURPOSE_PATH_BIAS_TESTING);
-
- /* Update timestamp for when circuit_expire_building() should kill us */
- tor_gettimeofday(&circ->timestamp_began);
-
- /* Generate a random address for the nonce */
- crypto_rand((char*)&ocirc->pathbias_probe_nonce,
- sizeof(ocirc->pathbias_probe_nonce));
- ocirc->pathbias_probe_nonce &= 0x00ffffff;
- probe_nonce = tor_dup_ip(ocirc->pathbias_probe_nonce);
-
- tor_snprintf(payload,RELAY_PAYLOAD_SIZE, "%s:25", probe_nonce);
- payload_len = (int)strlen(payload)+1;
-
- // XXX: need this? Can we assume ipv4 will always be supported?
- // If not, how do we tell?
- //if (payload_len <= RELAY_PAYLOAD_SIZE - 4 && edge_conn->begincell_flags) {
- // set_uint32(payload + payload_len, htonl(edge_conn->begincell_flags));
- // payload_len += 4;
- //}
-
- /* Generate+Store stream id, make sure it's non-zero */
- ocirc->pathbias_probe_id = get_unique_stream_id_by_circ(ocirc);
-
- if (ocirc->pathbias_probe_id==0) {
- log_warn(LD_CIRC,
- "Ran out of stream IDs on circuit %u during "
- "pathbias probe attempt.", ocirc->global_identifier);
- tor_free(probe_nonce);
- return -1;
- }
-
- log_info(LD_CIRC,
- "Sending pathbias testing cell to %s:25 on stream %d for circ %d.",
- probe_nonce, ocirc->pathbias_probe_id, ocirc->global_identifier);
- tor_free(probe_nonce);
-
- /* Send a test relay cell */
- if (relay_send_command_from_edge(ocirc->pathbias_probe_id, circ,
- RELAY_COMMAND_BEGIN, payload,
- payload_len, cpath_layer) < 0) {
- log_notice(LD_CIRC,
- "Failed to send pathbias probe cell on circuit %d.",
- ocirc->global_identifier);
- return -1;
- }
-
- /* Mark it freshly dirty so it doesn't get expired in the meantime */
- circ->timestamp_dirty = time(NULL);
-
- return 0;
-}
-
-/**
- * Check the response to a pathbias probe, to ensure the
- * cell is recognized and the nonce and other probe
- * characteristics are as expected.
- *
- * If the response is valid, return 0. Otherwise return < 0.
- */
-int
-pathbias_check_probe_response(circuit_t *circ, const cell_t *cell)
-{
- /* Based on connection_edge_process_relay_cell() */
- relay_header_t rh;
- int reason;
- uint32_t ipv4_host;
- origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
-
- tor_assert(cell);
- tor_assert(ocirc);
- tor_assert(circ->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING);
-
- relay_header_unpack(&rh, cell->payload);
-
- reason = rh.length > 0 ?
- get_uint8(cell->payload+RELAY_HEADER_SIZE) : END_STREAM_REASON_MISC;
-
- if (rh.command == RELAY_COMMAND_END &&
- reason == END_STREAM_REASON_EXITPOLICY &&
- ocirc->pathbias_probe_id == rh.stream_id) {
-
- /* Check length+extract host: It is in network order after the reason code.
- * See connection_edge_end(). */
- if (rh.length < 9) { /* reason+ipv4+dns_ttl */
- log_notice(LD_PROTOCOL,
- "Short path bias probe response length field (%d).", rh.length);
- return - END_CIRC_REASON_TORPROTOCOL;
- }
-
- ipv4_host = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+1));
-
- /* Check nonce */
- if (ipv4_host == ocirc->pathbias_probe_nonce) {
- pathbias_mark_use_success(ocirc);
- circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED);
- log_info(LD_CIRC,
- "Got valid path bias probe back for circ %d, stream %d.",
- ocirc->global_identifier, ocirc->pathbias_probe_id);
- return 0;
- } else {
- log_notice(LD_CIRC,
- "Got strange probe value 0x%x vs 0x%x back for circ %d, "
- "stream %d.", ipv4_host, ocirc->pathbias_probe_nonce,
- ocirc->global_identifier, ocirc->pathbias_probe_id);
- return -1;
- }
- }
- log_info(LD_CIRC,
- "Got another cell back back on pathbias probe circuit %d: "
- "Command: %d, Reason: %d, Stream-id: %d",
- ocirc->global_identifier, rh.command, reason, rh.stream_id);
- return -1;
-}
-
-/**
- * Check if a circuit was used and/or closed successfully.
- *
- * If we attempted to use the circuit to carry a stream but failed
- * for whatever reason, or if the circuit mysteriously died before
- * we could attach any streams, record these two cases.
- *
- * If we *have* successfully used the circuit, or it appears to
- * have been closed by us locally, count it as a success.
- *
- * Returns 0 if we're done making decisions with the circ,
- * or -1 if we want to probe it first.
- */
-int
-pathbias_check_close(origin_circuit_t *ocirc, int reason)
-{
- circuit_t *circ = &ocirc->base_;
-
- if (!pathbias_should_count(ocirc)) {
- return 0;
- }
-
- switch (ocirc->path_state) {
- /* If the circuit was closed after building, but before use, we need
- * to ensure we were the ones who tried to close it (and not a remote
- * actor). */
- case PATH_STATE_BUILD_SUCCEEDED:
- if (reason & END_CIRC_REASON_FLAG_REMOTE) {
- /* Remote circ close reasons on an unused circuit all could be bias */
- log_info(LD_CIRC,
- "Circuit %d remote-closed without successful use for reason %d. "
- "Circuit purpose %d currently %d,%s. Len %d.",
- ocirc->global_identifier,
- reason, circ->purpose, ocirc->has_opened,
- circuit_state_to_string(circ->state),
- ocirc->build_state->desired_path_len);
- pathbias_count_collapse(ocirc);
- } else if ((reason & ~END_CIRC_REASON_FLAG_REMOTE)
- == END_CIRC_REASON_CHANNEL_CLOSED &&
- circ->n_chan &&
- circ->n_chan->reason_for_closing
- != CHANNEL_CLOSE_REQUESTED) {
- /* If we didn't close the channel ourselves, it could be bias */
- /* XXX: Only count bias if the network is live?
- * What about clock jumps/suspends? */
- log_info(LD_CIRC,
- "Circuit %d's channel closed without successful use for reason "
- "%d, channel reason %d. Circuit purpose %d currently %d,%s. Len "
- "%d.", ocirc->global_identifier,
- reason, circ->n_chan->reason_for_closing,
- circ->purpose, ocirc->has_opened,
- circuit_state_to_string(circ->state),
- ocirc->build_state->desired_path_len);
- pathbias_count_collapse(ocirc);
- } else {
- pathbias_count_successful_close(ocirc);
- }
- break;
-
- /* If we tried to use a circuit but failed, we should probe it to ensure
- * it has not been tampered with. */
- case PATH_STATE_USE_ATTEMPTED:
- /* XXX: Only probe and/or count failure if the network is live?
- * What about clock jumps/suspends? */
- if (pathbias_send_usable_probe(circ) == 0)
- return -1;
- else
- pathbias_count_use_failed(ocirc);
-
- /* Any circuit where there were attempted streams but no successful
- * streams could be bias */
- log_info(LD_CIRC,
- "Circuit %d closed without successful use for reason %d. "
- "Circuit purpose %d currently %d,%s. Len %d.",
- ocirc->global_identifier,
- reason, circ->purpose, ocirc->has_opened,
- circuit_state_to_string(circ->state),
- ocirc->build_state->desired_path_len);
- break;
-
- case PATH_STATE_USE_SUCCEEDED:
- pathbias_count_successful_close(ocirc);
- pathbias_count_use_success(ocirc);
- break;
-
- case PATH_STATE_USE_FAILED:
- pathbias_count_use_failed(ocirc);
- break;
-
- case PATH_STATE_NEW_CIRC:
- case PATH_STATE_BUILD_ATTEMPTED:
- case PATH_STATE_ALREADY_COUNTED:
- default:
- // Other states are uninteresting. No stats to count.
- break;
- }
-
- ocirc->path_state = PATH_STATE_ALREADY_COUNTED;
-
- return 0;
-}
-
-/**
- * Count a successfully closed circuit.
- */
-static void
-pathbias_count_successful_close(origin_circuit_t *circ)
-{
- entry_guard_t *guard = NULL;
- if (!pathbias_should_count(circ)) {
- return;
- }
-
- if (circ->cpath && circ->cpath->extend_info) {
- guard = entry_guard_get_by_id_digest(
- circ->cpath->extend_info->identity_digest);
- }
-
- if (guard) {
- /* In the long run: circuit_success ~= successful_circuit_close +
- * circ_failure + stream_failure */
- guard->successful_circuits_closed++;
- entry_guards_changed();
- } else if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
- /* In rare cases, CIRCUIT_PURPOSE_TESTING can get converted to
- * CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT and have no guards here.
- * No need to log that case. */
- log_info(LD_CIRC,
- "Successfully closed circuit has no known guard. "
- "Circuit is a %s currently %s",
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state));
- }
-}
-
-/**
- * Count a circuit that fails after it is built, but before it can
- * carry any traffic.
- *
- * This is needed because there are ways to destroy a
- * circuit after it has successfully completed. Right now, this is
- * used for purely informational/debugging purposes.
- */
-static void
-pathbias_count_collapse(origin_circuit_t *circ)
-{
- entry_guard_t *guard = NULL;
-
- if (!pathbias_should_count(circ)) {
- return;
- }
-
- if (circ->cpath && circ->cpath->extend_info) {
- guard = entry_guard_get_by_id_digest(
- circ->cpath->extend_info->identity_digest);
- }
-
- if (guard) {
- guard->collapsed_circuits++;
- entry_guards_changed();
- } else if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
- /* In rare cases, CIRCUIT_PURPOSE_TESTING can get converted to
- * CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT and have no guards here.
- * No need to log that case. */
- log_info(LD_CIRC,
- "Destroyed circuit has no known guard. "
- "Circuit is a %s currently %s",
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state));
- }
-}
-
-/**
- * Count a known failed circuit (because we could not probe it).
- *
- * This counter is informational.
- */
-static void
-pathbias_count_use_failed(origin_circuit_t *circ)
-{
- entry_guard_t *guard = NULL;
- if (!pathbias_should_count(circ)) {
- return;
- }
-
- if (circ->cpath && circ->cpath->extend_info) {
- guard = entry_guard_get_by_id_digest(
- circ->cpath->extend_info->identity_digest);
- }
-
- if (guard) {
- guard->unusable_circuits++;
- entry_guards_changed();
- } else if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
- /* In rare cases, CIRCUIT_PURPOSE_TESTING can get converted to
- * CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT and have no guards here.
- * No need to log that case. */
- /* XXX note cut-and-paste code in this function compared to nearby
- * functions. Would be nice to refactor. -RD */
- log_info(LD_CIRC,
- "Stream-failing circuit has no known guard. "
- "Circuit is a %s currently %s",
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state));
- }
-}
-
-/**
- * Count timeouts for path bias log messages.
- *
- * These counts are purely informational.
- */
-void
-pathbias_count_timeout(origin_circuit_t *circ)
-{
- entry_guard_t *guard = NULL;
-
- if (!pathbias_should_count(circ)) {
- return;
- }
-
- /* For hidden service circs, they can actually be used
- * successfully and then time out later (because
- * the other side declines to use them). */
- if (circ->path_state == PATH_STATE_USE_SUCCEEDED) {
- return;
- }
-
- if (circ->cpath && circ->cpath->extend_info) {
- guard = entry_guard_get_by_id_digest(
- circ->cpath->extend_info->identity_digest);
- }
-
- if (guard) {
- guard->timeouts++;
- entry_guards_changed();
- }
-}
-
-/**
- * Helper function to count all of the currently opened circuits
- * for a guard that are in a given path state range. The state
- * range is inclusive on both ends.
- */
-static int
-pathbias_count_circs_in_states(entry_guard_t *guard,
- path_state_t from,
- path_state_t to)
-{
- circuit_t *circ;
- int open_circuits = 0;
-
- /* Count currently open circuits. Give them the benefit of the doubt. */
- for (circ = global_circuitlist; circ; circ = circ->next) {
- origin_circuit_t *ocirc = NULL;
- if (!CIRCUIT_IS_ORIGIN(circ) || /* didn't originate here */
- circ->marked_for_close) /* already counted */
- continue;
-
- ocirc = TO_ORIGIN_CIRCUIT(circ);
-
- if (!ocirc->cpath || !ocirc->cpath->extend_info)
- continue;
-
- if (ocirc->path_state >= from &&
- ocirc->path_state <= to &&
- pathbias_should_count(ocirc) &&
- fast_memeq(guard->identity,
- ocirc->cpath->extend_info->identity_digest,
- DIGEST_LEN)) {
- log_debug(LD_CIRC, "Found opened circuit %d in path_state %s",
- ocirc->global_identifier,
- pathbias_state_to_string(ocirc->path_state));
- open_circuits++;
- }
- }
-
- return open_circuits;
-}
-
-/**
- * Return the number of circuits counted as successfully closed for
- * this guard.
- *
- * Also add in the currently open circuits to give them the benefit
- * of the doubt.
- */
-double
-pathbias_get_close_success_count(entry_guard_t *guard)
-{
- return guard->successful_circuits_closed +
- pathbias_count_circs_in_states(guard,
- PATH_STATE_BUILD_SUCCEEDED,
- PATH_STATE_USE_SUCCEEDED);
-}
-
-/**
- * Return the number of circuits counted as successfully used
- * this guard.
- *
- * Also add in the currently open circuits that we are attempting
- * to use to give them the benefit of the doubt.
- */
-double
-pathbias_get_use_success_count(entry_guard_t *guard)
-{
- return guard->use_successes +
- pathbias_count_circs_in_states(guard,
- PATH_STATE_USE_ATTEMPTED,
- PATH_STATE_USE_SUCCEEDED);
-}
-
-/**
- * Check the path bias use rate against our consensus parameter limits.
- *
- * Emits a log message if the use success rates are too low.
- *
- * If pathbias_get_dropguards() is set, we also disable the use of
- * very failure prone guards.
- */
-static void
-pathbias_measure_use_rate(entry_guard_t *guard)
-{
- const or_options_t *options = get_options();
-
- if (guard->use_attempts > pathbias_get_min_use(options)) {
- /* Note: We rely on the < comparison here to allow us to set a 0
- * rate and disable the feature entirely. If refactoring, don't
- * change to <= */
- if (pathbias_get_use_success_count(guard)/guard->use_attempts
- < pathbias_get_extreme_use_rate(options)) {
- /* Dropping is currently disabled by default. */
- if (pathbias_get_dropguards(options)) {
- if (!guard->path_bias_disabled) {
- log_warn(LD_CIRC,
- "Your Guard %s ($%s) is failing to carry an extremely large "
- "amount of stream on its circuits. "
- "To avoid potential route manipulation attacks, Tor has "
- "disabled use of this guard. "
- "Use counts are %ld/%ld. Success counts are %ld/%ld. "
- "%ld circuits completed, %ld were unusable, %ld collapsed, "
- "and %ld timed out. "
- "For reference, your timeout cutoff is %ld seconds.",
- guard->nickname, hex_str(guard->identity, DIGEST_LEN),
- tor_lround(pathbias_get_use_success_count(guard)),
- tor_lround(guard->use_attempts),
- tor_lround(pathbias_get_close_success_count(guard)),
- tor_lround(guard->circ_attempts),
- tor_lround(guard->circ_successes),
- tor_lround(guard->unusable_circuits),
- tor_lround(guard->collapsed_circuits),
- tor_lround(guard->timeouts),
- tor_lround(circ_times.close_ms/1000));
- guard->path_bias_disabled = 1;
- guard->bad_since = approx_time();
- entry_guards_changed();
- return;
- }
- } else if (!guard->path_bias_use_extreme) {
- guard->path_bias_use_extreme = 1;
- log_warn(LD_CIRC,
- "Your Guard %s ($%s) is failing to carry an extremely large "
- "amount of streams on its circuits. "
- "This could indicate a route manipulation attack, network "
- "overload, bad local network connectivity, or a bug. "
- "Use counts are %ld/%ld. Success counts are %ld/%ld. "
- "%ld circuits completed, %ld were unusable, %ld collapsed, "
- "and %ld timed out. "
- "For reference, your timeout cutoff is %ld seconds.",
- guard->nickname, hex_str(guard->identity, DIGEST_LEN),
- tor_lround(pathbias_get_use_success_count(guard)),
- tor_lround(guard->use_attempts),
- tor_lround(pathbias_get_close_success_count(guard)),
- tor_lround(guard->circ_attempts),
- tor_lround(guard->circ_successes),
- tor_lround(guard->unusable_circuits),
- tor_lround(guard->collapsed_circuits),
- tor_lround(guard->timeouts),
- tor_lround(circ_times.close_ms/1000));
- }
- } else if (pathbias_get_use_success_count(guard)/guard->use_attempts
- < pathbias_get_notice_use_rate(options)) {
- if (!guard->path_bias_use_noticed) {
- guard->path_bias_use_noticed = 1;
- log_notice(LD_CIRC,
- "Your Guard %s ($%s) is failing to carry more streams on its "
- "circuits than usual. "
- "Most likely this means the Tor network is overloaded "
- "or your network connection is poor. "
- "Use counts are %ld/%ld. Success counts are %ld/%ld. "
- "%ld circuits completed, %ld were unusable, %ld collapsed, "
- "and %ld timed out. "
- "For reference, your timeout cutoff is %ld seconds.",
- guard->nickname, hex_str(guard->identity, DIGEST_LEN),
- tor_lround(pathbias_get_use_success_count(guard)),
- tor_lround(guard->use_attempts),
- tor_lround(pathbias_get_close_success_count(guard)),
- tor_lround(guard->circ_attempts),
- tor_lround(guard->circ_successes),
- tor_lround(guard->unusable_circuits),
- tor_lround(guard->collapsed_circuits),
- tor_lround(guard->timeouts),
- tor_lround(circ_times.close_ms/1000));
- }
- }
- }
-}
-
-/**
- * Check the path bias circuit close status rates against our consensus
- * parameter limits.
- *
- * Emits a log message if the use success rates are too low.
- *
- * If pathbias_get_dropguards() is set, we also disable the use of
- * very failure prone guards.
- *
- * XXX: This function shares similar log messages and checks to
- * pathbias_measure_use_rate(). It may be possible to combine them
- * eventually, especially if we can ever remove the need for 3
- * levels of closure warns (if the overall circuit failure rate
- * goes down with ntor). One way to do so would be to multiply
- * the build rate with the use rate to get an idea of the total
- * fraction of the total network paths the user is able to use.
- * See ticket #8159.
- */
-static void
-pathbias_measure_close_rate(entry_guard_t *guard)
-{
- const or_options_t *options = get_options();
-
- if (guard->circ_attempts > pathbias_get_min_circs(options)) {
- /* Note: We rely on the < comparison here to allow us to set a 0
- * rate and disable the feature entirely. If refactoring, don't
- * change to <= */
- if (pathbias_get_close_success_count(guard)/guard->circ_attempts
- < pathbias_get_extreme_rate(options)) {
- /* Dropping is currently disabled by default. */
- if (pathbias_get_dropguards(options)) {
- if (!guard->path_bias_disabled) {
- log_warn(LD_CIRC,
- "Your Guard %s ($%s) is failing an extremely large "
- "amount of circuits. "
- "To avoid potential route manipulation attacks, Tor has "
- "disabled use of this guard. "
- "Success counts are %ld/%ld. Use counts are %ld/%ld. "
- "%ld circuits completed, %ld were unusable, %ld collapsed, "
- "and %ld timed out. "
- "For reference, your timeout cutoff is %ld seconds.",
- guard->nickname, hex_str(guard->identity, DIGEST_LEN),
- tor_lround(pathbias_get_close_success_count(guard)),
- tor_lround(guard->circ_attempts),
- tor_lround(pathbias_get_use_success_count(guard)),
- tor_lround(guard->use_attempts),
- tor_lround(guard->circ_successes),
- tor_lround(guard->unusable_circuits),
- tor_lround(guard->collapsed_circuits),
- tor_lround(guard->timeouts),
- tor_lround(circ_times.close_ms/1000));
- guard->path_bias_disabled = 1;
- guard->bad_since = approx_time();
- entry_guards_changed();
- return;
- }
- } else if (!guard->path_bias_extreme) {
- guard->path_bias_extreme = 1;
- log_warn(LD_CIRC,
- "Your Guard %s ($%s) is failing an extremely large "
- "amount of circuits. "
- "This could indicate a route manipulation attack, "
- "extreme network overload, or a bug. "
- "Success counts are %ld/%ld. Use counts are %ld/%ld. "
- "%ld circuits completed, %ld were unusable, %ld collapsed, "
- "and %ld timed out. "
- "For reference, your timeout cutoff is %ld seconds.",
- guard->nickname, hex_str(guard->identity, DIGEST_LEN),
- tor_lround(pathbias_get_close_success_count(guard)),
- tor_lround(guard->circ_attempts),
- tor_lround(pathbias_get_use_success_count(guard)),
- tor_lround(guard->use_attempts),
- tor_lround(guard->circ_successes),
- tor_lround(guard->unusable_circuits),
- tor_lround(guard->collapsed_circuits),
- tor_lround(guard->timeouts),
- tor_lround(circ_times.close_ms/1000));
- }
- } else if (pathbias_get_close_success_count(guard)/guard->circ_attempts
- < pathbias_get_warn_rate(options)) {
- if (!guard->path_bias_warned) {
- guard->path_bias_warned = 1;
- log_warn(LD_CIRC,
- "Your Guard %s ($%s) is failing a very large "
- "amount of circuits. "
- "Most likely this means the Tor network is "
- "overloaded, but it could also mean an attack against "
- "you or potentially the guard itself. "
- "Success counts are %ld/%ld. Use counts are %ld/%ld. "
- "%ld circuits completed, %ld were unusable, %ld collapsed, "
- "and %ld timed out. "
- "For reference, your timeout cutoff is %ld seconds.",
- guard->nickname, hex_str(guard->identity, DIGEST_LEN),
- tor_lround(pathbias_get_close_success_count(guard)),
- tor_lround(guard->circ_attempts),
- tor_lround(pathbias_get_use_success_count(guard)),
- tor_lround(guard->use_attempts),
- tor_lround(guard->circ_successes),
- tor_lround(guard->unusable_circuits),
- tor_lround(guard->collapsed_circuits),
- tor_lround(guard->timeouts),
- tor_lround(circ_times.close_ms/1000));
- }
- } else if (pathbias_get_close_success_count(guard)/guard->circ_attempts
- < pathbias_get_notice_rate(options)) {
- if (!guard->path_bias_noticed) {
- guard->path_bias_noticed = 1;
- log_notice(LD_CIRC,
- "Your Guard %s ($%s) is failing more circuits than "
- "usual. "
- "Most likely this means the Tor network is overloaded. "
- "Success counts are %ld/%ld. Use counts are %ld/%ld. "
- "%ld circuits completed, %ld were unusable, %ld collapsed, "
- "and %ld timed out. "
- "For reference, your timeout cutoff is %ld seconds.",
- guard->nickname, hex_str(guard->identity, DIGEST_LEN),
- tor_lround(pathbias_get_close_success_count(guard)),
- tor_lround(guard->circ_attempts),
- tor_lround(pathbias_get_use_success_count(guard)),
- tor_lround(guard->use_attempts),
- tor_lround(guard->circ_successes),
- tor_lround(guard->unusable_circuits),
- tor_lround(guard->collapsed_circuits),
- tor_lround(guard->timeouts),
- tor_lround(circ_times.close_ms/1000));
- }
- }
- }
-}
-
-/**
- * This function scales the path bias use rates if we have
- * more data than the scaling threshold. This allows us to
- * be more sensitive to recent measurements.
- *
- * XXX: The attempt count transfer stuff here might be done
- * better by keeping separate pending counters that get
- * transfered at circuit close. See ticket #8160.
- */
-static void
-pathbias_scale_close_rates(entry_guard_t *guard)
-{
- const or_options_t *options = get_options();
-
- /* If we get a ton of circuits, just scale everything down */
- if (guard->circ_attempts > pathbias_get_scale_threshold(options)) {
- double scale_ratio = pathbias_get_scale_ratio(options);
- int opened_attempts = pathbias_count_circs_in_states(guard,
- PATH_STATE_BUILD_ATTEMPTED, PATH_STATE_BUILD_ATTEMPTED);
- int opened_built = pathbias_count_circs_in_states(guard,
- PATH_STATE_BUILD_SUCCEEDED,
- PATH_STATE_USE_FAILED);
- /* Verify that the counts are sane before and after scaling */
- int counts_are_sane = (guard->circ_attempts >= guard->circ_successes);
-
- guard->circ_attempts -= (opened_attempts+opened_built);
- guard->circ_successes -= opened_built;
-
- guard->circ_attempts *= scale_ratio;
- guard->circ_successes *= scale_ratio;
- guard->timeouts *= scale_ratio;
- guard->successful_circuits_closed *= scale_ratio;
- guard->collapsed_circuits *= scale_ratio;
- guard->unusable_circuits *= scale_ratio;
-
- guard->circ_attempts += (opened_attempts+opened_built);
- guard->circ_successes += opened_built;
-
- entry_guards_changed();
-
- log_info(LD_CIRC,
- "Scaled pathbias counts to (%f,%f)/%f (%d/%d open) for guard "
- "%s ($%s)",
- guard->circ_successes, guard->successful_circuits_closed,
- guard->circ_attempts, opened_built, opened_attempts,
- guard->nickname, hex_str(guard->identity, DIGEST_LEN));
-
- /* Have the counts just become invalid by this scaling attempt? */
- if (counts_are_sane && guard->circ_attempts < guard->circ_successes) {
- log_notice(LD_BUG,
- "Scaling has mangled pathbias counts to %f/%f (%d/%d open) "
- "for guard %s ($%s)",
- guard->circ_successes, guard->circ_attempts, opened_built,
- opened_attempts, guard->nickname,
- hex_str(guard->identity, DIGEST_LEN));
- }
- }
-}
-
-/**
- * This function scales the path bias circuit close rates if we have
- * more data than the scaling threshold. This allows us to be more
- * sensitive to recent measurements.
- *
- * XXX: The attempt count transfer stuff here might be done
- * better by keeping separate pending counters that get
- * transfered at circuit close. See ticket #8160.
- */
-void
-pathbias_scale_use_rates(entry_guard_t *guard)
-{
- const or_options_t *options = get_options();
-
- /* If we get a ton of circuits, just scale everything down */
- if (guard->use_attempts > pathbias_get_scale_use_threshold(options)) {
- double scale_ratio = pathbias_get_scale_ratio(options);
- int opened_attempts = pathbias_count_circs_in_states(guard,
- PATH_STATE_USE_ATTEMPTED, PATH_STATE_USE_SUCCEEDED);
- /* Verify that the counts are sane before and after scaling */
- int counts_are_sane = (guard->use_attempts >= guard->use_successes);
-
- guard->use_attempts -= opened_attempts;
-
- guard->use_attempts *= scale_ratio;
- guard->use_successes *= scale_ratio;
-
- guard->use_attempts += opened_attempts;
-
- log_info(LD_CIRC,
- "Scaled pathbias use counts to %f/%f (%d open) for guard %s ($%s)",
- guard->use_successes, guard->use_attempts, opened_attempts,
- guard->nickname, hex_str(guard->identity, DIGEST_LEN));
-
- /* Have the counts just become invalid by this scaling attempt? */
- if (counts_are_sane && guard->use_attempts < guard->use_successes) {
- log_notice(LD_BUG,
- "Scaling has mangled pathbias usage counts to %f/%f "
- "(%d open) for guard %s ($%s)",
- guard->circ_successes, guard->circ_attempts,
- opened_attempts, guard->nickname,
- hex_str(guard->identity, DIGEST_LEN));
- }
-
- entry_guards_changed();
- }
-}
-
-/** Increment the number of times we successfully extended a circuit to
- * <b>guard</b>, first checking if the failure rate is high enough that
- * we should eliminate the guard. Return -1 if the guard looks no good;
- * return 0 if the guard looks fine.
- */
-static int
-entry_guard_inc_circ_attempt_count(entry_guard_t *guard)
-{
- entry_guards_changed();
-
- pathbias_measure_close_rate(guard);
-
- if (guard->path_bias_disabled)
- return -1;
-
- pathbias_scale_close_rates(guard);
- guard->circ_attempts++;
-
- log_info(LD_CIRC, "Got success count %f/%f for guard %s ($%s)",
- guard->circ_successes, guard->circ_attempts, guard->nickname,
- hex_str(guard->identity, DIGEST_LEN));
- return 0;
-}
-
/** A "created" cell <b>reply</b> came back to us on circuit <b>circ</b>.
* (The body of <b>reply</b> varies depending on what sort of handshake
* this is.)
@@ -2765,11 +1246,7 @@ onionskin_answer(or_circuit_t *circ,
* number of endpoints that would give something away about our destination.
*
* If the routerlist <b>nodes</b> doesn't have enough routers
- * to handle the desired path length, return as large a path length as
- * is feasible, except if it's less than 2, in which case return -1.
- * XXX ^^ I think this behavior is a hold-over from back when we had only a
- * few relays in the network, and certainly back before guards existed.
- * We should very likely get rid of it. -RD
+ * to handle the desired path length, return -1.
*/
static int
new_route_len(uint8_t purpose, extend_info_t *exit, smartlist_t *nodes)
@@ -2790,19 +1267,13 @@ new_route_len(uint8_t purpose, extend_info_t *exit, smartlist_t *nodes)
log_debug(LD_CIRC,"Chosen route length %d (%d/%d routers suitable).",
routelen, num_acceptable_routers, smartlist_len(nodes));
- if (num_acceptable_routers < 2) {
+ if (num_acceptable_routers < routelen) {
log_info(LD_CIRC,
- "Not enough acceptable routers (%d). Discarding this circuit.",
- num_acceptable_routers);
+ "Not enough acceptable routers (%d/%d). Discarding this circuit.",
+ num_acceptable_routers, routelen);
return -1;
}
- if (num_acceptable_routers < routelen) {
- log_info(LD_CIRC,"Not enough routers: cutting routelen from %d to %d.",
- routelen, num_acceptable_routers);
- routelen = num_acceptable_routers;
- }
-
return routelen;
}
diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h
index a3091707e..ebcb22c45 100644
--- a/src/or/circuitbuild.h
+++ b/src/or/circuitbuild.h
@@ -57,16 +57,6 @@ const char *build_state_get_exit_nickname(cpath_build_state_t *state);
const node_t *choose_good_entry_server(uint8_t purpose,
cpath_build_state_t *state);
-double pathbias_get_extreme_rate(const or_options_t *options);
-double pathbias_get_extreme_use_rate(const or_options_t *options);
-int pathbias_get_dropguards(const or_options_t *options);
-void pathbias_count_timeout(origin_circuit_t *circ);
-int pathbias_check_close(origin_circuit_t *circ, int reason);
-int pathbias_check_probe_response(circuit_t *circ, const cell_t *cell);
-void pathbias_count_use_attempt(origin_circuit_t *circ);
-void pathbias_mark_use_success(origin_circuit_t *circ);
-void pathbias_mark_use_rollback(origin_circuit_t *circ);
-const char *pathbias_state_to_string(path_state_t state);
#endif
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index b0e24a5fe..c31bc49d0 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -8,9 +8,10 @@
* \file circuitlist.c
* \brief Manage the global circuit list.
**/
-
+#define CIRCUITLIST_PRIVATE
#include "or.h"
#include "channel.h"
+#include "circpathbias.h"
#include "circuitbuild.h"
#include "circuitlist.h"
#include "circuituse.h"
@@ -36,12 +37,12 @@
/********* START VARIABLES **********/
/** A global list of all circuits at this hop. */
-circuit_t *global_circuitlist=NULL;
+struct global_circuitlist_s global_circuitlist =
+ TOR_LIST_HEAD_INITIALIZER(global_circuitlist);
/** A list of all the circuits in CIRCUIT_STATE_CHAN_WAIT. */
static smartlist_t *circuits_pending_chans = NULL;
-static void circuit_free(circuit_t *circ);
static void circuit_free_cpath(crypt_path_t *cpath);
static void circuit_free_cpath_node(crypt_path_t *victim);
static void cpath_ref_decref(crypt_path_reference_t *cpath_ref);
@@ -207,18 +208,123 @@ circuit_set_circid_chan_helper(circuit_t *circ, int direction,
}
}
+/** Mark that circuit id <b>id</b> shouldn't be used on channel <b>chan</b>,
+ * even if there is no circuit on the channel. We use this to keep the
+ * circuit id from getting re-used while we have queued but not yet sent
+ * a destroy cell. */
+void
+channel_mark_circid_unusable(channel_t *chan, circid_t id)
+{
+ chan_circid_circuit_map_t search;
+ chan_circid_circuit_map_t *ent;
+
+ /* See if there's an entry there. That wouldn't be good. */
+ memset(&search, 0, sizeof(search));
+ search.chan = chan;
+ search.circ_id = id;
+ ent = HT_FIND(chan_circid_map, &chan_circid_map, &search);
+
+ if (ent && ent->circuit) {
+ /* we have a problem. */
+ log_warn(LD_BUG, "Tried to mark %u unusable on %p, but there was already "
+ "a circuit there.", (unsigned)id, chan);
+ } else if (ent) {
+ /* It's already marked. */
+ } else {
+ ent = tor_malloc_zero(sizeof(chan_circid_circuit_map_t));
+ ent->chan = chan;
+ ent->circ_id = id;
+ /* leave circuit at NULL */
+ HT_INSERT(chan_circid_map, &chan_circid_map, ent);
+ }
+}
+
+/** Mark that a circuit id <b>id</b> can be used again on <b>chan</b>.
+ * We use this to re-enable the circuit ID after we've sent a destroy cell.
+ */
+void
+channel_mark_circid_usable(channel_t *chan, circid_t id)
+{
+ chan_circid_circuit_map_t search;
+ chan_circid_circuit_map_t *ent;
+
+ /* See if there's an entry there. That wouldn't be good. */
+ memset(&search, 0, sizeof(search));
+ search.chan = chan;
+ search.circ_id = id;
+ ent = HT_REMOVE(chan_circid_map, &chan_circid_map, &search);
+ if (ent && ent->circuit) {
+ log_warn(LD_BUG, "Tried to mark %u usable on %p, but there was already "
+ "a circuit there.", (unsigned)id, chan);
+ return;
+ }
+ if (_last_circid_chan_ent == ent)
+ _last_circid_chan_ent = NULL;
+ tor_free(ent);
+}
+
+/** Called to indicate that a DESTROY is pending on <b>chan</b> with
+ * circuit ID <b>id</b>, but hasn't been sent yet. */
+void
+channel_note_destroy_pending(channel_t *chan, circid_t id)
+{
+ circuit_t *circ = circuit_get_by_circid_channel_even_if_marked(id,chan);
+ if (circ) {
+ if (circ->n_chan == chan && circ->n_circ_id == id) {
+ circ->n_delete_pending = 1;
+ } else {
+ or_circuit_t *orcirc = TO_OR_CIRCUIT(circ);
+ if (orcirc->p_chan == chan && orcirc->p_circ_id == id) {
+ circ->p_delete_pending = 1;
+ }
+ }
+ return;
+ }
+ channel_mark_circid_unusable(chan, id);
+}
+
+/** Called to indicate that a DESTROY is no longer pending on <b>chan</b> with
+ * circuit ID <b>id</b> -- typically, because it has been sent. */
+void
+channel_note_destroy_not_pending(channel_t *chan, circid_t id)
+{
+ circuit_t *circ = circuit_get_by_circid_channel_even_if_marked(id,chan);
+ if (circ) {
+ if (circ->n_chan == chan && circ->n_circ_id == id) {
+ circ->n_delete_pending = 0;
+ } else {
+ or_circuit_t *orcirc = TO_OR_CIRCUIT(circ);
+ if (orcirc->p_chan == chan && orcirc->p_circ_id == id) {
+ circ->p_delete_pending = 0;
+ }
+ }
+ /* XXXX this shouldn't happen; log a bug here. */
+ return;
+ }
+ channel_mark_circid_usable(chan, id);
+}
+
/** Set the p_conn field of a circuit <b>circ</b>, along
* with the corresponding circuit ID, and add the circuit as appropriate
* to the (chan,id)-\>circuit map. */
void
-circuit_set_p_circid_chan(or_circuit_t *circ, circid_t id,
+circuit_set_p_circid_chan(or_circuit_t *or_circ, circid_t id,
channel_t *chan)
{
- circuit_set_circid_chan_helper(TO_CIRCUIT(circ), CELL_DIRECTION_IN,
- id, chan);
+ circuit_t *circ = TO_CIRCUIT(or_circ);
+ channel_t *old_chan = or_circ->p_chan;
+ circid_t old_id = or_circ->p_circ_id;
+
+ circuit_set_circid_chan_helper(circ, CELL_DIRECTION_IN, id, chan);
if (chan)
- tor_assert(bool_eq(circ->p_chan_cells.n, circ->next_active_on_p_chan));
+ tor_assert(bool_eq(or_circ->p_chan_cells.n,
+ or_circ->next_active_on_p_chan));
+
+ if (circ->p_delete_pending && old_chan) {
+ channel_mark_circid_unusable(old_chan, old_id);
+ circ->p_delete_pending = 0;
+ }
}
/** Set the n_conn field of a circuit <b>circ</b>, along
@@ -228,10 +334,18 @@ void
circuit_set_n_circid_chan(circuit_t *circ, circid_t id,
channel_t *chan)
{
+ channel_t *old_chan = circ->n_chan;
+ circid_t old_id = circ->n_circ_id;
+
circuit_set_circid_chan_helper(circ, CELL_DIRECTION_OUT, id, chan);
if (chan)
tor_assert(bool_eq(circ->n_chan_cells.n, circ->next_active_on_n_chan));
+
+ if (circ->n_delete_pending && old_chan) {
+ channel_mark_circid_unusable(old_chan, old_id);
+ circ->n_delete_pending = 0;
+ }
}
/** Change the state of <b>circ</b> to <b>state</b>, adding it to or removing
@@ -257,21 +371,6 @@ circuit_set_state(circuit_t *circ, uint8_t state)
circ->state = state;
}
-/** Add <b>circ</b> to the global list of circuits. This is called only from
- * within circuit_new.
- */
-static void
-circuit_add(circuit_t *circ)
-{
- if (!global_circuitlist) { /* first one */
- global_circuitlist = circ;
- circ->next = NULL;
- } else {
- circ->next = global_circuitlist;
- global_circuitlist = circ;
- }
-}
-
/** Append to <b>out</b> all circuits in state CHAN_WAIT waiting for
* the given connection. */
void
@@ -329,33 +428,17 @@ circuit_count_pending_on_channel(channel_t *chan)
void
circuit_close_all_marked(void)
{
- circuit_t *tmp,*m;
-
- while (global_circuitlist && global_circuitlist->marked_for_close) {
- tmp = global_circuitlist->next;
- circuit_free(global_circuitlist);
- global_circuitlist = tmp;
- }
-
- tmp = global_circuitlist;
- while (tmp && tmp->next) {
- if (tmp->next->marked_for_close) {
- m = tmp->next->next;
- circuit_free(tmp->next);
- tmp->next = m;
- /* Need to check new tmp->next; don't advance tmp. */
- } else {
- /* Advance tmp. */
- tmp = tmp->next;
- }
- }
+ circuit_t *circ, *tmp;
+ TOR_LIST_FOREACH_SAFE(circ, &global_circuitlist, head, tmp)
+ if (circ->marked_for_close)
+ circuit_free(circ);
}
/** Return the head of the global linked list of circuits. */
-circuit_t *
-circuit_get_global_list_(void)
+struct global_circuitlist_s *
+circuit_get_global_list(void)
{
- return global_circuitlist;
+ return &global_circuitlist;
}
/** Function to make circ-\>state human-readable */
@@ -570,8 +653,9 @@ init_circuit_base(circuit_t *circ)
circ->package_window = circuit_initial_package_window();
circ->deliver_window = CIRCWINDOW_START;
+ cell_queue_init(&circ->n_chan_cells);
- circuit_add(circ);
+ TOR_LIST_INSERT_HEAD(&global_circuitlist, circ, head);
}
/** Allocate space for a new circuit, initializing with <b>p_circ_id</b>
@@ -595,7 +679,7 @@ origin_circuit_new(void)
init_circuit_base(TO_CIRCUIT(circ));
- circ_times.last_circ_at = approx_time();
+ circuit_build_times_update_last_circ(get_circuit_build_times_mutable());
return circ;
}
@@ -615,6 +699,7 @@ or_circuit_new(circid_t p_circ_id, channel_t *p_chan)
circuit_set_p_circid_chan(circ, p_circ_id, p_chan);
circ->remaining_relay_early_cells = MAX_RELAY_EARLY_CELLS_PER_CIRCUIT;
+ cell_queue_init(&circ->p_chan_cells);
init_circuit_base(TO_CIRCUIT(circ));
@@ -623,7 +708,7 @@ or_circuit_new(circid_t p_circ_id, channel_t *p_chan)
/** Deallocate space associated with circ.
*/
-static void
+STATIC void
circuit_free(circuit_t *circ)
{
void *mem;
@@ -689,6 +774,8 @@ circuit_free(circuit_t *circ)
extend_info_free(circ->n_hop);
tor_free(circ->n_chan_create_cell);
+ TOR_LIST_REMOVE(circ, head);
+
/* Remove from map. */
circuit_set_n_circid_chan(circ, 0, NULL);
@@ -724,11 +811,11 @@ circuit_free_cpath(crypt_path_t *cpath)
void
circuit_free_all(void)
{
- circuit_t *next;
- while (global_circuitlist) {
- next = global_circuitlist->next;
- if (! CIRCUIT_IS_ORIGIN(global_circuitlist)) {
- or_circuit_t *or_circ = TO_OR_CIRCUIT(global_circuitlist);
+ circuit_t *tmp, *tmp2;
+
+ TOR_LIST_FOREACH_SAFE(tmp, &global_circuitlist, head, tmp2) {
+ if (! CIRCUIT_IS_ORIGIN(tmp)) {
+ or_circuit_t *or_circ = TO_OR_CIRCUIT(tmp);
while (or_circ->resolving_streams) {
edge_connection_t *next_conn;
next_conn = or_circ->resolving_streams->next_stream;
@@ -736,8 +823,7 @@ circuit_free_all(void)
or_circ->resolving_streams = next_conn;
}
}
- circuit_free(global_circuitlist);
- global_circuitlist = next;
+ circuit_free(tmp);
}
smartlist_free(circuits_pending_chans);
@@ -807,7 +893,7 @@ circuit_dump_by_conn(connection_t *conn, int severity)
circuit_t *circ;
edge_connection_t *tmpconn;
- for (circ = global_circuitlist; circ; circ = circ->next) {
+ TOR_LIST_FOREACH(circ, &global_circuitlist, head) {
circid_t n_circ_id = circ->n_circ_id, p_circ_id = 0;
if (circ->marked_for_close) {
@@ -871,7 +957,7 @@ circuit_dump_by_chan(channel_t *chan, int severity)
tor_assert(chan);
- for (circ = global_circuitlist; circ; circ = circ->next) {
+ TOR_LIST_FOREACH(circ, &global_circuitlist, head) {
circid_t n_circ_id = circ->n_circ_id, p_circ_id = 0;
if (circ->marked_for_close) {
@@ -912,7 +998,7 @@ origin_circuit_t *
circuit_get_by_global_id(uint32_t id)
{
circuit_t *circ;
- for (circ=global_circuitlist;circ;circ = circ->next) {
+ TOR_LIST_FOREACH(circ, &global_circuitlist, head) {
if (CIRCUIT_IS_ORIGIN(circ) &&
TO_ORIGIN_CIRCUIT(circ)->global_identifier == id) {
if (circ->marked_for_close)
@@ -928,9 +1014,13 @@ circuit_get_by_global_id(uint32_t id)
* - circ-\>n_circ_id or circ-\>p_circ_id is equal to <b>circ_id</b>, and
* - circ is attached to <b>chan</b>, either as p_chan or n_chan.
* Return NULL if no such circuit exists.
+ *
+ * If <b>found_entry_out</b> is provided, set it to true if we have a
+ * placeholder entry for circid/chan, and leave it unset otherwise.
*/
static INLINE circuit_t *
-circuit_get_by_circid_channel_impl(circid_t circ_id, channel_t *chan)
+circuit_get_by_circid_channel_impl(circid_t circ_id, channel_t *chan,
+ int *found_entry_out)
{
chan_circid_circuit_map_t search;
chan_circid_circuit_map_t *found;
@@ -951,21 +1041,27 @@ circuit_get_by_circid_channel_impl(circid_t circ_id, channel_t *chan)
" circ_id %u, channel ID " U64_FORMAT " (%p)",
found->circuit, (unsigned)circ_id,
U64_PRINTF_ARG(chan->global_identifier), chan);
+ if (found_entry_out)
+ *found_entry_out = 1;
return found->circuit;
}
log_debug(LD_CIRC,
- "circuit_get_by_circid_channel_impl() found nothing for"
+ "circuit_get_by_circid_channel_impl() found %s for"
" circ_id %u, channel ID " U64_FORMAT " (%p)",
+ found ? "placeholder" : "nothing",
(unsigned)circ_id,
U64_PRINTF_ARG(chan->global_identifier), chan);
+ if (found_entry_out)
+ *found_entry_out = found ? 1 : 0;
+
return NULL;
/* The rest of this checks for bugs. Disabled by default. */
/* We comment it out because coverity complains otherwise.
{
circuit_t *circ;
- for (circ=global_circuitlist;circ;circ = circ->next) {
+ TOR_LIST_FOREACH(circ, &global_circuitlist, head) {
if (! CIRCUIT_IS_ORIGIN(circ)) {
or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
if (or_circ->p_chan == chan && or_circ->p_circ_id == circ_id) {
@@ -993,7 +1089,7 @@ circuit_get_by_circid_channel_impl(circid_t circ_id, channel_t *chan)
circuit_t *
circuit_get_by_circid_channel(circid_t circ_id, channel_t *chan)
{
- circuit_t *circ = circuit_get_by_circid_channel_impl(circ_id, chan);
+ circuit_t *circ = circuit_get_by_circid_channel_impl(circ_id, chan, NULL);
if (!circ || circ->marked_for_close)
return NULL;
else
@@ -1009,7 +1105,7 @@ circuit_t *
circuit_get_by_circid_channel_even_if_marked(circid_t circ_id,
channel_t *chan)
{
- return circuit_get_by_circid_channel_impl(circ_id, chan);
+ return circuit_get_by_circid_channel_impl(circ_id, chan, NULL);
}
/** Return true iff the circuit ID <b>circ_id</b> is currently used by a
@@ -1017,7 +1113,9 @@ circuit_get_by_circid_channel_even_if_marked(circid_t circ_id,
int
circuit_id_in_use_on_channel(circid_t circ_id, channel_t *chan)
{
- return circuit_get_by_circid_channel_impl(circ_id, chan) != NULL;
+ int found = 0;
+ return circuit_get_by_circid_channel_impl(circ_id, chan, &found) != NULL
+ || found;
}
/** Return the circuit that a given edge connection is using. */
@@ -1045,7 +1143,7 @@ circuit_unlink_all_from_channel(channel_t *chan, int reason)
channel_unlink_all_circuits(chan);
- for (circ = global_circuitlist; circ; circ = circ->next) {
+ TOR_LIST_FOREACH(circ, &global_circuitlist, head) {
int mark = 0;
if (circ->n_chan == chan) {
circuit_set_n_circid_chan(circ, 0, NULL);
@@ -1081,8 +1179,7 @@ origin_circuit_t *
circuit_get_ready_rend_circ_by_rend_data(const rend_data_t *rend_data)
{
circuit_t *circ;
-
- for (circ = global_circuitlist; circ; circ = circ->next) {
+ TOR_LIST_FOREACH(circ, &global_circuitlist, head) {
if (!circ->marked_for_close &&
circ->purpose == CIRCUIT_PURPOSE_C_REND_READY) {
origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
@@ -1110,11 +1207,11 @@ circuit_get_next_by_pk_and_purpose(origin_circuit_t *start,
circuit_t *circ;
tor_assert(CIRCUIT_PURPOSE_IS_ORIGIN(purpose));
if (start == NULL)
- circ = global_circuitlist;
+ circ = TOR_LIST_FIRST(&global_circuitlist);
else
- circ = TO_CIRCUIT(start)->next;
+ circ = TOR_LIST_NEXT(TO_CIRCUIT(start), head);
- for ( ; circ; circ = circ->next) {
+ for ( ; circ; circ = TOR_LIST_NEXT(circ, head)) {
if (circ->marked_for_close)
continue;
if (circ->purpose != purpose)
@@ -1137,7 +1234,7 @@ circuit_get_by_rend_token_and_purpose(uint8_t purpose, const char *token,
size_t len)
{
circuit_t *circ;
- for (circ = global_circuitlist; circ; circ = circ->next) {
+ TOR_LIST_FOREACH(circ, &global_circuitlist, head) {
if (! circ->marked_for_close &&
circ->purpose == purpose &&
tor_memeq(TO_OR_CIRCUIT(circ)->rend_token, token, len))
@@ -1199,7 +1296,7 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info,
"capacity %d, internal %d",
purpose, need_uptime, need_capacity, internal);
- for (circ_=global_circuitlist; circ_; circ_ = circ_->next) {
+ TOR_LIST_FOREACH(circ_, &global_circuitlist, head) {
if (CIRCUIT_IS_ORIGIN(circ_) &&
circ_->state == CIRCUIT_STATE_OPEN &&
!circ_->marked_for_close &&
@@ -1289,8 +1386,7 @@ void
circuit_mark_all_unused_circs(void)
{
circuit_t *circ;
-
- for (circ=global_circuitlist; circ; circ = circ->next) {
+ TOR_LIST_FOREACH(circ, &global_circuitlist, head) {
if (CIRCUIT_IS_ORIGIN(circ) &&
!circ->marked_for_close &&
!circ->timestamp_dirty)
@@ -1309,8 +1405,7 @@ void
circuit_mark_all_dirty_circs_as_unusable(void)
{
circuit_t *circ;
-
- for (circ=global_circuitlist; circ; circ = circ->next) {
+ TOR_LIST_FOREACH(circ, &global_circuitlist, head) {
if (CIRCUIT_IS_ORIGIN(circ) &&
!circ->marked_for_close &&
circ->timestamp_dirty) {
@@ -1514,7 +1609,7 @@ marked_circuit_free_cells(circuit_t *circ)
}
/** Return the number of cells used by the circuit <b>c</b>'s cell queues. */
-static size_t
+STATIC size_t
n_cells_in_circ_queues(const circuit_t *c)
{
size_t n = c->n_chan_cells.n;
@@ -1537,13 +1632,15 @@ static uint32_t
circuit_max_queued_cell_age(const circuit_t *c, uint32_t now)
{
uint32_t age = 0;
- if (c->n_chan_cells.head)
- age = now - c->n_chan_cells.head->inserted_time;
+ packed_cell_t *cell;
+
+ if (NULL != (cell = TOR_SIMPLEQ_FIRST(&c->n_chan_cells.head)))
+ age = now - cell->inserted_time;
if (! CIRCUIT_IS_ORIGIN(c)) {
const or_circuit_t *orcirc = TO_OR_CIRCUIT((circuit_t*)c);
- if (orcirc->p_chan_cells.head) {
- uint32_t age2 = now - orcirc->p_chan_cells.head->inserted_time;
+ if (NULL != (cell = TOR_SIMPLEQ_FIRST(&orcirc->p_chan_cells.head))) {
+ uint32_t age2 = now - cell->inserted_time;
if (age2 > age)
return age2;
}
@@ -1604,7 +1701,7 @@ circuits_handle_oom(size_t current_allocation)
/* This algorithm itself assumes that you've got enough memory slack
* to actually run it. */
- for (circ = global_circuitlist; circ; circ = circ->next)
+ TOR_LIST_FOREACH(circ, &global_circuitlist, head)
smartlist_add(circlist, circ);
/* Set circcomp_now_tmp so that the sort can work. */
@@ -1725,15 +1822,16 @@ assert_circuit_ok(const circuit_t *c)
/* We use the _impl variant here to make sure we don't fail on marked
* circuits, which would not be returned by the regular function. */
circuit_t *c2 = circuit_get_by_circid_channel_impl(c->n_circ_id,
- c->n_chan);
+ c->n_chan, NULL);
tor_assert(c == c2);
}
}
if (or_circ && or_circ->p_chan) {
if (or_circ->p_circ_id) {
/* ibid */
- circuit_t *c2 = circuit_get_by_circid_channel_impl(or_circ->p_circ_id,
- or_circ->p_chan);
+ circuit_t *c2 =
+ circuit_get_by_circid_channel_impl(or_circ->p_circ_id,
+ or_circ->p_chan, NULL);
tor_assert(c == c2);
}
}
diff --git a/src/or/circuitlist.h b/src/or/circuitlist.h
index 874f68cd2..bf3d1b467 100644
--- a/src/or/circuitlist.h
+++ b/src/or/circuitlist.h
@@ -12,7 +12,11 @@
#ifndef TOR_CIRCUITLIST_H
#define TOR_CIRCUITLIST_H
-circuit_t * circuit_get_global_list_(void);
+#include "testsupport.h"
+
+TOR_LIST_HEAD(global_circuitlist_s, circuit_t);
+
+struct global_circuitlist_s* circuit_get_global_list(void);
const char *circuit_state_to_string(int state);
const char *circuit_purpose_to_controller_string(uint8_t purpose);
const char *circuit_purpose_to_controller_hs_state_string(uint8_t purpose);
@@ -23,6 +27,8 @@ void circuit_set_p_circid_chan(or_circuit_t *circ, circid_t id,
channel_t *chan);
void circuit_set_n_circid_chan(circuit_t *circ, circid_t id,
channel_t *chan);
+void channel_mark_circid_unusable(channel_t *chan, circid_t id);
+void channel_mark_circid_usable(channel_t *chan, circid_t id);
void circuit_set_state(circuit_t *circ, uint8_t state);
void circuit_close_all_marked(void);
int32_t circuit_initial_package_window(void);
@@ -63,5 +69,13 @@ void assert_circuit_ok(const circuit_t *c);
void circuit_free_all(void);
void circuits_handle_oom(size_t current_allocation);
+void channel_note_destroy_pending(channel_t *chan, circid_t id);
+void channel_note_destroy_not_pending(channel_t *chan, circid_t id);
+
+#ifdef CIRCUITLIST_PRIVATE
+STATIC void circuit_free(circuit_t *circ);
+STATIC size_t n_cells_in_circ_queues(const circuit_t *c);
+#endif
+
#endif
diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c
index 545cfd065..f2af94393 100644
--- a/src/or/circuitmux.c
+++ b/src/or/circuitmux.c
@@ -10,6 +10,7 @@
#include "channel.h"
#include "circuitlist.h"
#include "circuitmux.h"
+#include "relay.h"
/*
* Private typedefs for circuitmux.c
@@ -115,6 +116,22 @@ struct circuitmux_s {
*/
struct circuit_t *active_circuits_head, *active_circuits_tail;
+ /** List of queued destroy cells */
+ cell_queue_t destroy_cell_queue;
+ /** Boolean: True iff the last cell to circuitmux_get_first_active_circuit
+ * returned the destroy queue. Used to force alternation between
+ * destroy/non-destroy cells.
+ *
+ * XXXX There is no reason to think that alternating is a particularly good
+ * approach -- it's just designed to prevent destroys from starving other
+ * cells completely.
+ */
+ unsigned int last_cell_was_destroy : 1;
+ /** Destroy counter: increment this when a destroy gets queued, decrement
+ * when we unqueue it, so we can test to make sure they don't starve.
+ */
+ int64_t destroy_ctr;
+
/*
* Circuitmux policy; if this is non-NULL, it can override the built-
* in round-robin active circuits behavior. This is how EWMA works in
@@ -193,6 +210,11 @@ static void circuitmux_assert_okay_pass_one(circuitmux_t *cmux);
static void circuitmux_assert_okay_pass_two(circuitmux_t *cmux);
static void circuitmux_assert_okay_pass_three(circuitmux_t *cmux);
+/* Static global variables */
+
+/** Count the destroy balance to debug destroy queue logic */
+static int64_t global_destroy_ctr = 0;
+
/* Function definitions */
/**
@@ -361,6 +383,7 @@ circuitmux_alloc(void)
rv = tor_malloc_zero(sizeof(*rv));
rv->chanid_circid_map = tor_malloc_zero(sizeof(*( rv->chanid_circid_map)));
HT_INIT(chanid_circid_muxinfo_map, rv->chanid_circid_map);
+ cell_queue_init(&rv->destroy_cell_queue);
return rv;
}
@@ -476,6 +499,31 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux)
cmux->n_cells = 0;
}
+/** Reclaim all circuit IDs currently marked as unusable on <b>chan</b> because
+ * of pending destroy cells in <b>cmux</b>.
+ *
+ * This function must be called AFTER circuits are unlinked from the (channel,
+ * circuid-id) map with circuit_unlink_all_from_channel(), but before calling
+ * circuitmux_free().
+ */
+void
+circuitmux_mark_destroyed_circids_usable(circuitmux_t *cmux, channel_t *chan)
+{
+ packed_cell_t *cell;
+ int n_bad = 0;
+ TOR_SIMPLEQ_FOREACH(cell, &cmux->destroy_cell_queue.head, next) {
+ circid_t circid = 0;
+ if (packed_cell_is_destroy(chan, cell, &circid)) {
+ channel_mark_circid_usable(chan, circid);
+ } else {
+ ++n_bad;
+ }
+ }
+ if (n_bad)
+ log_warn(LD_BUG, "%d cell(s) on destroy queue did not look like a "
+ "DESTROY cell.", n_bad);
+}
+
/**
* Free a circuitmux_t; the circuits must be detached first with
* circuitmux_detach_all_circuits().
@@ -508,6 +556,30 @@ circuitmux_free(circuitmux_t *cmux)
tor_free(cmux->chanid_circid_map);
}
+ /*
+ * We're throwing away some destroys; log the counter and
+ * adjust the global counter by the queue size.
+ */
+ if (cmux->destroy_cell_queue.n > 0) {
+ cmux->destroy_ctr -= cmux->destroy_cell_queue.n;
+ global_destroy_ctr -= cmux->destroy_cell_queue.n;
+ log_debug(LD_CIRC,
+ "Freeing cmux at %p with %u queued destroys; the last cmux "
+ "destroy balance was "I64_FORMAT", global is "I64_FORMAT,
+ cmux, cmux->destroy_cell_queue.n,
+ I64_PRINTF_ARG(cmux->destroy_ctr),
+ I64_PRINTF_ARG(global_destroy_ctr));
+ } else {
+ log_debug(LD_CIRC,
+ "Freeing cmux at %p with no queued destroys, the cmux destroy "
+ "balance was "I64_FORMAT", global is "I64_FORMAT,
+ cmux,
+ I64_PRINTF_ARG(cmux->destroy_ctr),
+ I64_PRINTF_ARG(global_destroy_ctr));
+ }
+
+ cell_queue_clear(&cmux->destroy_cell_queue);
+
tor_free(cmux);
}
@@ -816,7 +888,7 @@ circuitmux_num_cells(circuitmux_t *cmux)
{
tor_assert(cmux);
- return cmux->n_cells;
+ return cmux->n_cells + cmux->destroy_cell_queue.n;
}
/**
@@ -851,9 +923,9 @@ circuitmux_num_circuits(circuitmux_t *cmux)
* Attach a circuit to a circuitmux, for the specified direction.
*/
-void
-circuitmux_attach_circuit(circuitmux_t *cmux, circuit_t *circ,
- cell_direction_t direction)
+MOCK_IMPL(void,
+circuitmux_attach_circuit,(circuitmux_t *cmux, circuit_t *circ,
+ cell_direction_t direction))
{
channel_t *chan = NULL;
uint64_t channel_id;
@@ -1000,8 +1072,8 @@ circuitmux_attach_circuit(circuitmux_t *cmux, circuit_t *circ,
* no-op if not attached.
*/
-void
-circuitmux_detach_circuit(circuitmux_t *cmux, circuit_t *circ)
+MOCK_IMPL(void,
+circuitmux_detach_circuit,(circuitmux_t *cmux, circuit_t *circ))
{
chanid_circid_muxinfo_t search, *hashent = NULL;
/*
@@ -1368,16 +1440,36 @@ circuitmux_set_num_cells(circuitmux_t *cmux, circuit_t *circ,
/**
* Pick a circuit to send from, using the active circuits list or a
* circuitmux policy if one is available. This is called from channel.c.
+ *
+ * If we would rather send a destroy cell, return NULL and set
+ * *<b>destroy_queue_out</b> to the destroy queue.
+ *
+ * If we have nothing to send, set *<b>destroy_queue_out</b> to NULL and
+ * return NULL.
*/
circuit_t *
-circuitmux_get_first_active_circuit(circuitmux_t *cmux)
+circuitmux_get_first_active_circuit(circuitmux_t *cmux,
+ cell_queue_t **destroy_queue_out)
{
circuit_t *circ = NULL;
tor_assert(cmux);
+ tor_assert(destroy_queue_out);
+
+ *destroy_queue_out = NULL;
+
+ if (cmux->destroy_cell_queue.n &&
+ (!cmux->last_cell_was_destroy || cmux->n_active_circuits == 0)) {
+ /* We have destroy cells to send, and either we just sent a relay cell,
+ * or we have no relay cells to send. */
+
+ /* XXXX We should let the cmux policy have some say in this eventually. */
+ /* XXXX Alternating is not a terribly brilliant approach here. */
+ *destroy_queue_out = &cmux->destroy_cell_queue;
- if (cmux->n_active_circuits > 0) {
+ cmux->last_cell_was_destroy = 1;
+ } else if (cmux->n_active_circuits > 0) {
/* We also must have a cell available for this to be the case */
tor_assert(cmux->n_cells > 0);
/* Do we have a policy-provided circuit selector? */
@@ -1389,7 +1481,11 @@ circuitmux_get_first_active_circuit(circuitmux_t *cmux)
tor_assert(cmux->active_circuits_head);
circ = cmux->active_circuits_head;
}
- } else tor_assert(cmux->n_cells == 0);
+ cmux->last_cell_was_destroy = 0;
+ } else {
+ tor_assert(cmux->n_cells == 0);
+ tor_assert(cmux->destroy_cell_queue.n == 0);
+ }
return circ;
}
@@ -1463,6 +1559,26 @@ circuitmux_notify_xmit_cells(circuitmux_t *cmux, circuit_t *circ,
circuitmux_assert_okay_paranoid(cmux);
}
+/**
+ * Notify the circuitmux that a destroy was sent, so we can update
+ * the counter.
+ */
+
+void
+circuitmux_notify_xmit_destroy(circuitmux_t *cmux)
+{
+ tor_assert(cmux);
+
+ --(cmux->destroy_ctr);
+ --(global_destroy_ctr);
+ log_debug(LD_CIRC,
+ "Cmux at %p sent a destroy, cmux counter is now "I64_FORMAT", "
+ "global counter is now "I64_FORMAT,
+ cmux,
+ I64_PRINTF_ARG(cmux->destroy_ctr),
+ I64_PRINTF_ARG(global_destroy_ctr));
+}
+
/*
* Circuitmux consistency checking assertions
*/
@@ -1743,3 +1859,40 @@ circuitmux_assert_okay_pass_three(circuitmux_t *cmux)
}
}
+/*DOCDOC */
+void
+circuitmux_append_destroy_cell(channel_t *chan,
+ circuitmux_t *cmux,
+ circid_t circ_id,
+ uint8_t reason)
+{
+ cell_t cell;
+ memset(&cell, 0, sizeof(cell_t));
+ cell.circ_id = circ_id;
+ cell.command = CELL_DESTROY;
+ cell.payload[0] = (uint8_t) reason;
+
+ cell_queue_append_packed_copy(NULL, &cmux->destroy_cell_queue, 0, &cell,
+ chan->wide_circ_ids, 0);
+
+ /* Destroy entering the queue, update counters */
+ ++(cmux->destroy_ctr);
+ ++global_destroy_ctr;
+ log_debug(LD_CIRC,
+ "Cmux at %p queued a destroy for circ %u, cmux counter is now "
+ I64_FORMAT", global counter is now "I64_FORMAT,
+ cmux, circ_id,
+ I64_PRINTF_ARG(cmux->destroy_ctr),
+ I64_PRINTF_ARG(global_destroy_ctr));
+
+ /* XXXX Duplicate code from append_cell_to_circuit_queue */
+ if (!channel_has_queued_writes(chan)) {
+ /* There is no data at all waiting to be sent on the outbuf. Add a
+ * cell, so that we can notice when it gets flushed, flushed_some can
+ * get called, and we can start putting more data onto the buffer then.
+ */
+ log_debug(LD_GENERAL, "Primed a buffer.");
+ channel_flush_from_first_active_circuit(chan, 1);
+ }
+}
+
diff --git a/src/or/circuitmux.h b/src/or/circuitmux.h
index 25644ffab..ee2f5d153 100644
--- a/src/or/circuitmux.h
+++ b/src/or/circuitmux.h
@@ -10,6 +10,7 @@
#define TOR_CIRCUITMUX_H
#include "or.h"
+#include "testsupport.h"
typedef struct circuitmux_policy_s circuitmux_policy_t;
typedef struct circuitmux_policy_data_s circuitmux_policy_data_t;
@@ -120,17 +121,27 @@ unsigned int circuitmux_num_circuits(circuitmux_t *cmux);
unsigned int circuitmux_num_active_circuits(circuitmux_t *cmux);
/* Channel interface */
-circuit_t * circuitmux_get_first_active_circuit(circuitmux_t *cmux);
+circuit_t * circuitmux_get_first_active_circuit(circuitmux_t *cmux,
+ cell_queue_t **destroy_queue_out);
void circuitmux_notify_xmit_cells(circuitmux_t *cmux, circuit_t *circ,
unsigned int n_cells);
+void circuitmux_notify_xmit_destroy(circuitmux_t *cmux);
/* Circuit interface */
-void circuitmux_attach_circuit(circuitmux_t *cmux, circuit_t *circ,
- cell_direction_t direction);
-void circuitmux_detach_circuit(circuitmux_t *cmux, circuit_t *circ);
+MOCK_DECL(void, circuitmux_attach_circuit, (circuitmux_t *cmux,
+ circuit_t *circ,
+ cell_direction_t direction));
+MOCK_DECL(void, circuitmux_detach_circuit,
+ (circuitmux_t *cmux, circuit_t *circ));
void circuitmux_clear_num_cells(circuitmux_t *cmux, circuit_t *circ);
void circuitmux_set_num_cells(circuitmux_t *cmux, circuit_t *circ,
unsigned int n_cells);
+void circuitmux_append_destroy_cell(channel_t *chan,
+ circuitmux_t *cmux, circid_t circ_id,
+ uint8_t reason);
+void circuitmux_mark_destroyed_circids_usable(circuitmux_t *cmux,
+ channel_t *chan);
+
#endif /* TOR_CIRCUITMUX_H */
diff --git a/src/or/circuitstats.c b/src/or/circuitstats.c
index 1d7812bf2..eaefc9edd 100644
--- a/src/or/circuitstats.c
+++ b/src/or/circuitstats.c
@@ -18,6 +18,10 @@
#undef log
#include <math.h>
+static void cbt_control_event_buildtimeout_set(
+ const circuit_build_times_t *cbt,
+ buildtimeout_set_event_t type);
+
#define CBT_BIN_TO_MS(bin) ((bin)*CBT_BIN_WIDTH + (CBT_BIN_WIDTH/2))
/** Global list of circuit build times */
@@ -26,12 +30,46 @@
// vary in their own latency. The downside of this is that guards
// can change frequently, so we'd be building a lot more circuits
// most likely.
-/* XXXX024 Make this static; add accessor functions. */
-circuit_build_times_t circ_times;
+static circuit_build_times_t circ_times;
+#ifdef TOR_UNIT_TESTS
/** If set, we're running the unit tests: we should avoid clobbering
* our state file or accessing get_options() or get_or_state() */
static int unit_tests = 0;
+#else
+#define unit_tests 0
+#endif
+
+/** Return a pointer to the data structure describing our current circuit
+ * build time history and computations. */
+const circuit_build_times_t *
+get_circuit_build_times(void)
+{
+ return &circ_times;
+}
+
+/** As get_circuit_build_times, but return a mutable pointer. */
+circuit_build_times_t *
+get_circuit_build_times_mutable(void)
+{
+ return &circ_times;
+}
+
+/** Return the time to wait before actually closing an under-construction, in
+ * milliseconds. */
+double
+get_circuit_build_close_time_ms(void)
+{
+ return circ_times.close_ms;
+}
+
+/** Return the time to wait before giving up on an under-construction circuit,
+ * in milliseconds. */
+double
+get_circuit_build_timeout_ms(void)
+{
+ return circ_times.timeout_ms;
+}
/**
* This function decides if CBT learning should be disabled. It returns
@@ -154,7 +192,7 @@ circuit_build_times_min_circs_to_observe(void)
/** Return true iff <b>cbt</b> has recorded enough build times that we
* want to start acting on the timeout it implies. */
int
-circuit_build_times_enough_to_compute(circuit_build_times_t *cbt)
+circuit_build_times_enough_to_compute(const circuit_build_times_t *cbt)
{
return cbt->total_build_times >= circuit_build_times_min_circs_to_observe();
}
@@ -438,7 +476,7 @@ circuit_build_times_get_initial_timeout(void)
* Leave estimated parameters, timeout and network liveness intact
* for future use.
*/
-void
+STATIC void
circuit_build_times_reset(circuit_build_times_t *cbt)
{
memset(cbt->circuit_build_times, 0, sizeof(cbt->circuit_build_times));
@@ -471,7 +509,7 @@ circuit_build_times_init(circuit_build_times_t *cbt)
cbt->liveness.timeouts_after_firsthop = NULL;
}
cbt->close_ms = cbt->timeout_ms = circuit_build_times_get_initial_timeout();
- control_event_buildtimeout_set(cbt, BUILDTIMEOUT_SET_EVENT_RESET);
+ cbt_control_event_buildtimeout_set(cbt, BUILDTIMEOUT_SET_EVENT_RESET);
}
/**
@@ -557,7 +595,7 @@ circuit_build_times_add_time(circuit_build_times_t *cbt, build_time_t time)
* Return maximum circuit build time
*/
static build_time_t
-circuit_build_times_max(circuit_build_times_t *cbt)
+circuit_build_times_max(const circuit_build_times_t *cbt)
{
int i = 0;
build_time_t max_build_time = 0;
@@ -598,7 +636,7 @@ circuit_build_times_min(circuit_build_times_t *cbt)
* The return value must be freed by the caller.
*/
static uint32_t *
-circuit_build_times_create_histogram(circuit_build_times_t *cbt,
+circuit_build_times_create_histogram(const circuit_build_times_t *cbt,
build_time_t *nbins)
{
uint32_t *histogram;
@@ -688,7 +726,7 @@ circuit_build_times_get_xm(circuit_build_times_t *cbt)
* the or_state_t state structure.
*/
void
-circuit_build_times_update_state(circuit_build_times_t *cbt,
+circuit_build_times_update_state(const circuit_build_times_t *cbt,
or_state_t *state)
{
uint32_t *histogram;
@@ -949,7 +987,7 @@ circuit_build_times_parse_state(circuit_build_times_t *cbt,
* an acceptable approximation because we are only concerned with the
* accuracy of the CDF of the tail.
*/
-int
+STATIC int
circuit_build_times_update_alpha(circuit_build_times_t *cbt)
{
build_time_t *x=cbt->circuit_build_times;
@@ -1033,7 +1071,7 @@ circuit_build_times_update_alpha(circuit_build_times_t *cbt)
*
* Return value is in milliseconds.
*/
-double
+STATIC double
circuit_build_times_calculate_timeout(circuit_build_times_t *cbt,
double quantile)
{
@@ -1050,6 +1088,7 @@ circuit_build_times_calculate_timeout(circuit_build_times_t *cbt,
return ret;
}
+#ifdef TOR_UNIT_TESTS
/** Pareto CDF */
double
circuit_build_times_cdf(circuit_build_times_t *cbt, double x)
@@ -1060,7 +1099,9 @@ circuit_build_times_cdf(circuit_build_times_t *cbt, double x)
tor_assert(0 <= ret && ret <= 1.0);
return ret;
}
+#endif
+#ifdef TOR_UNIT_TESTS
/**
* Generate a synthetic time using our distribution parameters.
*
@@ -1093,7 +1134,9 @@ circuit_build_times_generate_sample(circuit_build_times_t *cbt,
tor_assert(ret > 0);
return ret;
}
+#endif
+#ifdef TOR_UNIT_TESTS
/**
* Estimate an initial alpha parameter by solving the quantile
* function with a quantile point and a specific timeout value.
@@ -1114,12 +1157,13 @@ circuit_build_times_initial_alpha(circuit_build_times_t *cbt,
(tor_mathlog(cbt->Xm)-tor_mathlog(timeout_ms));
tor_assert(cbt->alpha > 0);
}
+#endif
/**
* Returns true if we need circuits to be built
*/
int
-circuit_build_times_needs_circuits(circuit_build_times_t *cbt)
+circuit_build_times_needs_circuits(const circuit_build_times_t *cbt)
{
/* Return true if < MIN_CIRCUITS_TO_OBSERVE */
return !circuit_build_times_enough_to_compute(cbt);
@@ -1130,7 +1174,7 @@ circuit_build_times_needs_circuits(circuit_build_times_t *cbt)
* right now.
*/
int
-circuit_build_times_needs_circuits_now(circuit_build_times_t *cbt)
+circuit_build_times_needs_circuits_now(const circuit_build_times_t *cbt)
{
return circuit_build_times_needs_circuits(cbt) &&
approx_time()-cbt->last_circ_at > circuit_build_times_test_frequency();
@@ -1263,7 +1307,7 @@ circuit_build_times_network_close(circuit_build_times_t *cbt,
* in the case of recent liveness changes.
*/
int
-circuit_build_times_network_check_live(circuit_build_times_t *cbt)
+circuit_build_times_network_check_live(const circuit_build_times_t *cbt)
{
if (cbt->liveness.nonlive_timeouts > 0) {
return 0;
@@ -1282,7 +1326,7 @@ circuit_build_times_network_check_live(circuit_build_times_t *cbt)
* to restart the process of building test circuits and estimating a
* new timeout.
*/
-int
+STATIC int
circuit_build_times_network_check_changed(circuit_build_times_t *cbt)
{
int total_build_times = cbt->total_build_times;
@@ -1329,7 +1373,7 @@ circuit_build_times_network_check_changed(circuit_build_times_t *cbt)
= circuit_build_times_get_initial_timeout();
}
- control_event_buildtimeout_set(cbt, BUILDTIMEOUT_SET_EVENT_RESET);
+ cbt_control_event_buildtimeout_set(cbt, BUILDTIMEOUT_SET_EVENT_RESET);
log_notice(LD_CIRC,
"Your network connection speed appears to have changed. Resetting "
@@ -1511,7 +1555,7 @@ circuit_build_times_set_timeout(circuit_build_times_t *cbt)
}
}
- control_event_buildtimeout_set(cbt, BUILDTIMEOUT_SET_EVENT_COMPUTED);
+ cbt_control_event_buildtimeout_set(cbt, BUILDTIMEOUT_SET_EVENT_COMPUTED);
timeout_rate = circuit_build_times_timeout_rate(cbt);
@@ -1546,6 +1590,8 @@ circuit_build_times_set_timeout(circuit_build_times_t *cbt)
cbt->total_build_times);
}
}
+
+#ifdef TOR_UNIT_TESTS
/** Make a note that we're running unit tests (rather than running Tor
* itself), so we avoid clobbering our state file. */
void
@@ -1553,4 +1599,46 @@ circuitbuild_running_unit_tests(void)
{
unit_tests = 1;
}
+#endif
+
+void
+circuit_build_times_update_last_circ(circuit_build_times_t *cbt)
+{
+ cbt->last_circ_at = approx_time();
+}
+
+static void
+cbt_control_event_buildtimeout_set(const circuit_build_times_t *cbt,
+ buildtimeout_set_event_t type)
+{
+ char *args = NULL;
+ double qnt;
+
+ switch (type) {
+ case BUILDTIMEOUT_SET_EVENT_RESET:
+ case BUILDTIMEOUT_SET_EVENT_SUSPENDED:
+ case BUILDTIMEOUT_SET_EVENT_DISCARD:
+ qnt = 1.0;
+ break;
+ case BUILDTIMEOUT_SET_EVENT_COMPUTED:
+ case BUILDTIMEOUT_SET_EVENT_RESUME:
+ default:
+ qnt = circuit_build_times_quantile_cutoff();
+ break;
+ }
+
+ tor_asprintf(&args, "TOTAL_TIMES=%lu "
+ "TIMEOUT_MS=%lu XM=%lu ALPHA=%f CUTOFF_QUANTILE=%f "
+ "TIMEOUT_RATE=%f CLOSE_MS=%lu CLOSE_RATE=%f",
+ (unsigned long)cbt->total_build_times,
+ (unsigned long)cbt->timeout_ms,
+ (unsigned long)cbt->Xm, cbt->alpha, qnt,
+ circuit_build_times_timeout_rate(cbt),
+ (unsigned long)cbt->close_ms,
+ circuit_build_times_close_rate(cbt));
+
+ control_event_buildtimeout_set(type, args);
+
+ tor_free(args);
+}
diff --git a/src/or/circuitstats.h b/src/or/circuitstats.h
index 87dce99f4..3343310b8 100644
--- a/src/or/circuitstats.h
+++ b/src/or/circuitstats.h
@@ -12,11 +12,14 @@
#ifndef TOR_CIRCUITSTATS_H
#define TOR_CIRCUITSTATS_H
-extern circuit_build_times_t circ_times;
+const circuit_build_times_t *get_circuit_build_times(void);
+circuit_build_times_t *get_circuit_build_times_mutable(void);
+double get_circuit_build_close_time_ms(void);
+double get_circuit_build_timeout_ms(void);
int circuit_build_times_disabled(void);
-int circuit_build_times_enough_to_compute(circuit_build_times_t *cbt);
-void circuit_build_times_update_state(circuit_build_times_t *cbt,
+int circuit_build_times_enough_to_compute(const circuit_build_times_t *cbt);
+void circuit_build_times_update_state(const circuit_build_times_t *cbt,
or_state_t *state);
int circuit_build_times_parse_state(circuit_build_times_t *cbt,
or_state_t *state);
@@ -27,9 +30,9 @@ int circuit_build_times_count_close(circuit_build_times_t *cbt,
void circuit_build_times_set_timeout(circuit_build_times_t *cbt);
int circuit_build_times_add_time(circuit_build_times_t *cbt,
build_time_t time);
-int circuit_build_times_needs_circuits(circuit_build_times_t *cbt);
+int circuit_build_times_needs_circuits(const circuit_build_times_t *cbt);
-int circuit_build_times_needs_circuits_now(circuit_build_times_t *cbt);
+int circuit_build_times_needs_circuits_now(const circuit_build_times_t *cbt);
void circuit_build_times_init(circuit_build_times_t *cbt);
void circuit_build_times_free_timeouts(circuit_build_times_t *cbt);
void circuit_build_times_new_consensus_params(circuit_build_times_t *cbt,
@@ -37,29 +40,59 @@ void circuit_build_times_new_consensus_params(circuit_build_times_t *cbt,
double circuit_build_times_timeout_rate(const circuit_build_times_t *cbt);
double circuit_build_times_close_rate(const circuit_build_times_t *cbt);
+void circuit_build_times_update_last_circ(circuit_build_times_t *cbt);
+
#ifdef CIRCUITSTATS_PRIVATE
-double circuit_build_times_calculate_timeout(circuit_build_times_t *cbt,
+STATIC double circuit_build_times_calculate_timeout(circuit_build_times_t *cbt,
double quantile);
+STATIC int circuit_build_times_update_alpha(circuit_build_times_t *cbt);
+STATIC void circuit_build_times_reset(circuit_build_times_t *cbt);
+
+/* Network liveness functions */
+STATIC int circuit_build_times_network_check_changed(
+ circuit_build_times_t *cbt);
+#endif
+
+#ifdef TOR_UNIT_TESTS
build_time_t circuit_build_times_generate_sample(circuit_build_times_t *cbt,
double q_lo, double q_hi);
+double circuit_build_times_cdf(circuit_build_times_t *cbt, double x);
void circuit_build_times_initial_alpha(circuit_build_times_t *cbt,
double quantile, double time_ms);
-int circuit_build_times_update_alpha(circuit_build_times_t *cbt);
-double circuit_build_times_cdf(circuit_build_times_t *cbt, double x);
void circuitbuild_running_unit_tests(void);
-void circuit_build_times_reset(circuit_build_times_t *cbt);
-
-/* Network liveness functions */
-int circuit_build_times_network_check_changed(circuit_build_times_t *cbt);
#endif
/* Network liveness functions */
void circuit_build_times_network_is_live(circuit_build_times_t *cbt);
-int circuit_build_times_network_check_live(circuit_build_times_t *cbt);
+int circuit_build_times_network_check_live(const circuit_build_times_t *cbt);
void circuit_build_times_network_circ_success(circuit_build_times_t *cbt);
-/* DOCDOC circuit_build_times_get_bw_scale */
-int circuit_build_times_get_bw_scale(networkstatus_t *ns);
+#ifdef CIRCUITSTATS_PRIVATE
+/** Structure for circuit build times history */
+struct circuit_build_times_s {
+ /** The circular array of recorded build times in milliseconds */
+ build_time_t circuit_build_times[CBT_NCIRCUITS_TO_OBSERVE];
+ /** Current index in the circuit_build_times circular array */
+ int build_times_idx;
+ /** Total number of build times accumulated. Max CBT_NCIRCUITS_TO_OBSERVE */
+ int total_build_times;
+ /** Information about the state of our local network connection */
+ network_liveness_t liveness;
+ /** Last time we built a circuit. Used to decide to build new test circs */
+ time_t last_circ_at;
+ /** "Minimum" value of our pareto distribution (actually mode) */
+ build_time_t Xm;
+ /** alpha exponent for pareto dist. */
+ double alpha;
+ /** Have we computed a timeout? */
+ int have_computed_timeout;
+ /** The exact value for that timeout in milliseconds. Stored as a double
+ * to maintain precision from calculations to and from quantile value. */
+ double timeout_ms;
+ /** How long we wait before actually closing the circuit. */
+ double close_ms;
+};
+#endif
#endif
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index 598469198..8b82de0f9 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -12,6 +12,7 @@
#include "or.h"
#include "addressmap.h"
#include "channel.h"
+#include "circpathbias.h"
#include "circuitbuild.h"
#include "circuitlist.h"
#include "circuitstats.h"
@@ -31,12 +32,6 @@
#include "router.h"
#include "routerlist.h"
-/********* START VARIABLES **********/
-
-extern circuit_t *global_circuitlist; /* from circuitlist.c */
-
-/********* END VARIABLES ************/
-
static void circuit_expire_old_circuits_clientside(void);
static void circuit_increment_failure_count(void);
@@ -286,7 +281,7 @@ circuit_get_best(const entry_connection_t *conn,
tor_gettimeofday(&now);
- for (circ=global_circuitlist;circ;circ = circ->next) {
+ TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) {
origin_circuit_t *origin_circ;
if (!CIRCUIT_IS_ORIGIN(circ))
continue;
@@ -327,7 +322,7 @@ count_pending_general_client_circuits(void)
int count = 0;
- for (circ = global_circuitlist; circ; circ = circ->next) {
+ TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) {
if (circ->marked_for_close ||
circ->state == CIRCUIT_STATE_OPEN ||
circ->purpose != CIRCUIT_PURPOSE_C_GENERAL ||
@@ -375,7 +370,7 @@ circuit_conforms_to_options(const origin_circuit_t *circ,
void
circuit_expire_building(void)
{
- circuit_t *victim, *next_circ = global_circuitlist;
+ circuit_t *victim, *next_circ;
/* circ_times.timeout_ms and circ_times.close_ms are from
* circuit_build_times_get_initial_timeout() if we haven't computed
* custom timeouts yet */
@@ -393,10 +388,9 @@ circuit_expire_building(void)
* we want to be more lenient with timeouts, in case the
* user has relocated and/or changed network connections.
* See bug #3443. */
- while (next_circ) {
+ TOR_LIST_FOREACH(next_circ, circuit_get_global_list(), head) {
if (!CIRCUIT_IS_ORIGIN(next_circ) || /* didn't originate here */
next_circ->marked_for_close) { /* don't mess with marked circs */
- next_circ = next_circ->next;
continue;
}
@@ -408,9 +402,7 @@ circuit_expire_building(void)
any_opened_circs = 1;
break;
}
- next_circ = next_circ->next;
}
- next_circ = global_circuitlist;
#define SET_CUTOFF(target, msec) do { \
long ms = tor_lround(msec); \
@@ -451,12 +443,12 @@ circuit_expire_building(void)
* RTTs = 4a + 3b + 2c
* RTTs = 9h
*/
- SET_CUTOFF(general_cutoff, circ_times.timeout_ms);
- SET_CUTOFF(begindir_cutoff, circ_times.timeout_ms);
+ SET_CUTOFF(general_cutoff, get_circuit_build_timeout_ms());
+ SET_CUTOFF(begindir_cutoff, get_circuit_build_timeout_ms());
/* > 3hop circs seem to have a 1.0 second delay on their cannibalized
* 4th hop. */
- SET_CUTOFF(fourhop_cutoff, circ_times.timeout_ms * (10/6.0) + 1000);
+ SET_CUTOFF(fourhop_cutoff, get_circuit_build_timeout_ms() * (10/6.0) + 1000);
/* CIRCUIT_PURPOSE_C_ESTABLISH_REND behaves more like a RELAY cell.
* Use the stream cutoff (more or less). */
@@ -465,26 +457,25 @@ circuit_expire_building(void)
/* Be lenient with cannibalized circs. They already survived the official
* CBT, and they're usually not performance-critical. */
SET_CUTOFF(cannibalized_cutoff,
- MAX(circ_times.close_ms*(4/6.0),
+ MAX(get_circuit_build_close_time_ms()*(4/6.0),
options->CircuitStreamTimeout * 1000) + 1000);
/* Intro circs have an extra round trip (and are also 4 hops long) */
- SET_CUTOFF(c_intro_cutoff, circ_times.timeout_ms * (14/6.0) + 1000);
+ SET_CUTOFF(c_intro_cutoff, get_circuit_build_timeout_ms() * (14/6.0) + 1000);
/* Server intro circs have an extra round trip */
- SET_CUTOFF(s_intro_cutoff, circ_times.timeout_ms * (9/6.0) + 1000);
+ SET_CUTOFF(s_intro_cutoff, get_circuit_build_timeout_ms() * (9/6.0) + 1000);
- SET_CUTOFF(close_cutoff, circ_times.close_ms);
- SET_CUTOFF(extremely_old_cutoff, circ_times.close_ms*2 + 1000);
+ SET_CUTOFF(close_cutoff, get_circuit_build_close_time_ms());
+ SET_CUTOFF(extremely_old_cutoff, get_circuit_build_close_time_ms()*2 + 1000);
SET_CUTOFF(hs_extremely_old_cutoff,
- MAX(circ_times.close_ms*2 + 1000,
+ MAX(get_circuit_build_close_time_ms()*2 + 1000,
options->SocksTimeout * 1000));
- while (next_circ) {
+ TOR_LIST_FOREACH(next_circ, circuit_get_global_list(), head) {
struct timeval cutoff;
victim = next_circ;
- next_circ = next_circ->next;
if (!CIRCUIT_IS_ORIGIN(victim) || /* didn't originate here */
victim->marked_for_close) /* don't mess with marked circs */
continue;
@@ -555,12 +546,14 @@ circuit_expire_building(void)
* was a timeout, and the timeout value needs to reset if we
* see enough of them. Note this means we also need to avoid
* double-counting below, too. */
- circuit_build_times_count_timeout(&circ_times, first_hop_succeeded);
+ circuit_build_times_count_timeout(get_circuit_build_times_mutable(),
+ first_hop_succeeded);
TO_ORIGIN_CIRCUIT(victim)->relaxed_timeout = 1;
}
continue;
} else {
static ratelim_t relax_timeout_limit = RATELIM_INIT(3600);
+ const double build_close_ms = get_circuit_build_close_time_ms();
log_fn_ratelim(&relax_timeout_limit, LOG_NOTICE, LD_CIRC,
"No circuits are opened. Relaxed timeout for circuit %d "
"(a %s %d-hop circuit in state %s with channel state %s) to "
@@ -571,7 +564,8 @@ circuit_expire_building(void)
TO_ORIGIN_CIRCUIT(victim)->build_state->desired_path_len,
circuit_state_to_string(victim->state),
channel_state_to_string(victim->n_chan->state),
- (long)circ_times.close_ms, num_live_entry_guards(0));
+ (long)build_close_ms,
+ num_live_entry_guards(0));
}
}
@@ -651,7 +645,7 @@ circuit_expire_building(void)
}
if (circuit_timeout_want_to_count_circ(TO_ORIGIN_CIRCUIT(victim)) &&
- circuit_build_times_enough_to_compute(&circ_times)) {
+ circuit_build_times_enough_to_compute(get_circuit_build_times())) {
/* Circuits are allowed to last longer for measurement.
* Switch their purpose and wait. */
if (victim->purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
@@ -665,8 +659,9 @@ circuit_expire_building(void)
* have a timeout. We also want to avoid double-counting
* already "relaxed" circuits, which are counted above. */
if (!TO_ORIGIN_CIRCUIT(victim)->relaxed_timeout) {
- circuit_build_times_count_timeout(&circ_times,
- first_hop_succeeded);
+ circuit_build_times_count_timeout(
+ get_circuit_build_times_mutable(),
+ first_hop_succeeded);
}
continue;
}
@@ -683,10 +678,11 @@ circuit_expire_building(void)
(long)(now.tv_sec - victim->timestamp_began.tv_sec),
victim->purpose,
circuit_purpose_to_string(victim->purpose));
- } else if (circuit_build_times_count_close(&circ_times,
+ } else if (circuit_build_times_count_close(
+ get_circuit_build_times_mutable(),
first_hop_succeeded,
victim->timestamp_created.tv_sec)) {
- circuit_build_times_set_timeout(&circ_times);
+ circuit_build_times_set_timeout(get_circuit_build_times_mutable());
}
}
}
@@ -750,7 +746,7 @@ circuit_expire_building(void)
if (victim->n_chan)
log_info(LD_CIRC,
- "Abandoning circ %u %s:%d (state %d,%d:%s, purpose %d, "
+ "Abandoning circ %u %s:%u (state %d,%d:%s, purpose %d, "
"len %d)", TO_ORIGIN_CIRCUIT(victim)->global_identifier,
channel_get_canonical_remote_descr(victim->n_chan),
(unsigned)victim->n_circ_id,
@@ -760,7 +756,7 @@ circuit_expire_building(void)
TO_ORIGIN_CIRCUIT(victim)->build_state->desired_path_len);
else
log_info(LD_CIRC,
- "Abandoning circ %u %d (state %d,%d:%s, purpose %d, len %d)",
+ "Abandoning circ %u %u (state %d,%d:%s, purpose %d, len %d)",
TO_ORIGIN_CIRCUIT(victim)->global_identifier,
(unsigned)victim->n_circ_id,
TO_ORIGIN_CIRCUIT(victim)->has_opened,
@@ -818,7 +814,7 @@ circuit_stream_is_being_handled(entry_connection_t *conn,
get_options()->LongLivedPorts,
conn ? conn->socks_request->port : port);
- for (circ=global_circuitlist;circ;circ = circ->next) {
+ TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) {
if (CIRCUIT_IS_ORIGIN(circ) &&
!circ->marked_for_close &&
circ->purpose == CIRCUIT_PURPOSE_C_GENERAL &&
@@ -869,7 +865,7 @@ circuit_predict_and_launch_new(void)
int flags = 0;
/* First, count how many of each type of circuit we have already. */
- for (circ=global_circuitlist;circ;circ = circ->next) {
+ TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) {
cpath_build_state_t *build_state;
origin_circuit_t *origin_circ;
if (!CIRCUIT_IS_ORIGIN(circ))
@@ -949,7 +945,7 @@ circuit_predict_and_launch_new(void)
* we can still build circuits preemptively as needed. */
if (num < MAX_UNUSED_OPEN_CIRCUITS-2 &&
! circuit_build_times_disabled() &&
- circuit_build_times_needs_circuits_now(&circ_times)) {
+ circuit_build_times_needs_circuits_now(get_circuit_build_times())) {
flags = CIRCLAUNCH_NEED_CAPACITY;
log_info(LD_CIRC,
"Have %d clean circs need another buildtime test circ.", num);
@@ -1085,7 +1081,7 @@ circuit_expire_old_circuits_clientside(void)
cutoff = now;
if (! circuit_build_times_disabled() &&
- circuit_build_times_needs_circuits(&circ_times)) {
+ circuit_build_times_needs_circuits(get_circuit_build_times())) {
/* Circuits should be shorter lived if we need more of them
* for learning a good build timeout */
cutoff.tv_sec -= IDLE_TIMEOUT_WHILE_LEARNING;
@@ -1093,7 +1089,7 @@ circuit_expire_old_circuits_clientside(void)
cutoff.tv_sec -= get_options()->CircuitIdleTimeout;
}
- for (circ = global_circuitlist; circ; circ = circ->next) {
+ TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) {
if (circ->marked_for_close || !CIRCUIT_IS_ORIGIN(circ))
continue;
/* If the circuit has been dirty for too long, and there are no streams
@@ -1176,7 +1172,7 @@ circuit_expire_old_circuits_serverside(time_t now)
or_circuit_t *or_circ;
time_t cutoff = now - IDLE_ONE_HOP_CIRC_TIMEOUT;
- for (circ = global_circuitlist; circ; circ = circ->next) {
+ TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) {
if (circ->marked_for_close || CIRCUIT_IS_ORIGIN(circ))
continue;
or_circ = TO_OR_CIRCUIT(circ);
@@ -1223,7 +1219,7 @@ circuit_enough_testing_circs(void)
if (have_performed_bandwidth_test)
return 1;
- for (circ = global_circuitlist; circ; circ = circ->next) {
+ TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) {
if (!circ->marked_for_close && CIRCUIT_IS_ORIGIN(circ) &&
circ->purpose == CIRCUIT_PURPOSE_TESTING &&
circ->state == CIRCUIT_STATE_OPEN)
@@ -1382,10 +1378,11 @@ circuit_build_failed(origin_circuit_t *circ)
failed_at_last_hop = 1;
}
if (circ->cpath &&
- circ->cpath->state != CPATH_STATE_OPEN) {
- /* We failed at the first hop. If there's an OR connection
- * to blame, blame it. Also, avoid this relay for a while, and
- * fail any one-hop directory fetches destined for it. */
+ circ->cpath->state != CPATH_STATE_OPEN &&
+ ! circ->base_.received_destroy) {
+ /* We failed at the first hop for some reason other than a DESTROY cell.
+ * If there's an OR connection to blame, blame it. Also, avoid this relay
+ * for a while, and fail any one-hop directory fetches destined for it. */
const char *n_chan_id = circ->cpath->extend_info->identity_digest;
int already_marked = 0;
if (circ->base_.n_chan) {
diff --git a/src/or/command.c b/src/or/command.c
index 699b02fb4..9b3ff16f2 100644
--- a/src/or/command.c
+++ b/src/or/command.c
@@ -53,6 +53,33 @@ static void command_process_created_cell(cell_t *cell, channel_t *chan);
static void command_process_relay_cell(cell_t *cell, channel_t *chan);
static void command_process_destroy_cell(cell_t *cell, channel_t *chan);
+/** Convert the cell <b>command</b> into a lower-case, human-readable
+ * string. */
+const char *
+cell_command_to_string(uint8_t command)
+{
+ switch (command) {
+ case CELL_PADDING: return "padding";
+ case CELL_CREATE: return "create";
+ case CELL_CREATED: return "created";
+ case CELL_RELAY: return "relay";
+ case CELL_DESTROY: return "destroy";
+ case CELL_CREATE_FAST: return "create_fast";
+ case CELL_CREATED_FAST: return "created_fast";
+ case CELL_VERSIONS: return "versions";
+ case CELL_NETINFO: return "netinfo";
+ case CELL_RELAY_EARLY: return "relay_early";
+ case CELL_CREATE2: return "create2";
+ case CELL_CREATED2: return "created2";
+ case CELL_VPADDING: return "vpadding";
+ case CELL_CERTS: return "certs";
+ case CELL_AUTH_CHALLENGE: return "auth_challenge";
+ case CELL_AUTHENTICATE: return "authenticate";
+ case CELL_AUTHORIZE: return "authorize";
+ default: return "unrecognized";
+ }
+}
+
#ifdef KEEP_TIMING_STATS
/** This is a wrapper function around the actual function that processes the
* <b>cell</b> that just arrived on <b>conn</b>. Increment <b>*time</b>
@@ -499,6 +526,7 @@ command_process_destroy_cell(cell_t *cell, channel_t *chan)
log_debug(LD_OR,"Received for circID %u.",(unsigned)cell->circ_id);
reason = (uint8_t)cell->payload[0];
+ circ->received_destroy = 1;
if (!CIRCUIT_IS_ORIGIN(circ) &&
cell->circ_id == TO_OR_CIRCUIT(circ)->p_circ_id) {
diff --git a/src/or/command.h b/src/or/command.h
index 913f46a5c..adea6adea 100644
--- a/src/or/command.h
+++ b/src/or/command.h
@@ -19,6 +19,8 @@ void command_process_var_cell(channel_t *chan, var_cell_t *cell);
void command_setup_channel(channel_t *chan);
void command_setup_listener(channel_listener_t *chan_l);
+const char *cell_command_to_string(uint8_t command);
+
extern uint64_t stats_n_padding_cells_processed;
extern uint64_t stats_n_create_cells_processed;
extern uint64_t stats_n_created_cells_processed;
diff --git a/src/or/config.c b/src/or/config.c
index 3984755dd..d348f1036 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -10,7 +10,6 @@
**/
#define CONFIG_PRIVATE
-
#include "or.h"
#include "addressmap.h"
#include "channel.h"
@@ -40,11 +39,14 @@
#include "rendservice.h"
#include "rephist.h"
#include "router.h"
+#include "sandbox.h"
#include "util.h"
#include "routerlist.h"
#include "routerset.h"
#include "statefile.h"
#include "transports.h"
+#include "ext_orport.h"
+#include "torgzip.h"
#ifdef _WIN32
#include <shlobj.h>
#endif
@@ -218,6 +220,9 @@ static config_var_t option_vars_[] = {
VPORT(DNSPort, LINELIST, NULL),
V(DNSListenAddress, LINELIST, NULL),
V(DownloadExtraInfo, BOOL, "0"),
+ V(TestingEnableConnBwEvent, BOOL, "0"),
+ V(TestingEnableCellStatsEvent, BOOL, "0"),
+ V(TestingEnableTbEmptyEvent, BOOL, "0"),
V(EnforceDistinctSubnets, BOOL, "1"),
V(EntryNodes, ROUTERSET, NULL),
V(EntryStatistics, BOOL, "0"),
@@ -230,13 +235,14 @@ static config_var_t option_vars_[] = {
V(ExitPolicyRejectPrivate, BOOL, "1"),
V(ExitPortStatistics, BOOL, "0"),
V(ExtendAllowPrivateAddresses, BOOL, "0"),
+ VPORT(ExtORPort, LINELIST, NULL),
V(ExtraInfoStatistics, BOOL, "1"),
V(FallbackDir, LINELIST, NULL),
OBSOLETE("FallbackNetworkstatusFile"),
V(FascistFirewall, BOOL, "0"),
V(FirewallPorts, CSV, ""),
- V(FastFirstHopPK, BOOL, "1"),
+ V(FastFirstHopPK, AUTOBOOL, "auto"),
V(FetchDirInfoEarly, BOOL, "0"),
V(FetchDirInfoExtraEarly, BOOL, "0"),
V(FetchServerDescriptors, BOOL, "1"),
@@ -281,6 +287,7 @@ static config_var_t option_vars_[] = {
V(IPv6Exit, BOOL, "0"),
VAR("ServerTransportPlugin", LINELIST, ServerTransportPlugin, NULL),
V(ServerTransportListenAddr, LINELIST, NULL),
+ V(ServerTransportOptions, LINELIST, NULL),
V(Socks4Proxy, STRING, NULL),
V(Socks5Proxy, STRING, NULL),
V(Socks5ProxyUsername, STRING, NULL),
@@ -370,6 +377,7 @@ static config_var_t option_vars_[] = {
V(RunAsDaemon, BOOL, "0"),
// V(RunTesting, BOOL, "0"),
OBSOLETE("RunTesting"), // currently unused
+ V(Sandbox, BOOL, "0"),
V(SafeLogging, STRING, "1"),
V(SafeSocks, BOOL, "0"),
V(ServerDNSAllowBrokenConfig, BOOL, "1"),
@@ -406,7 +414,7 @@ static config_var_t option_vars_[] = {
V(UseEntryGuards, BOOL, "1"),
V(UseEntryGuardsAsDirGuards, BOOL, "1"),
V(UseMicrodescriptors, AUTOBOOL, "auto"),
- V(UseNTorHandshake, AUTOBOOL, "auto"),
+ V(UseNTorHandshake, AUTOBOOL, "1"),
V(User, STRING, NULL),
V(UserspaceIOCPBuffers, BOOL, "0"),
VAR("V1AuthoritativeDirectory",BOOL, V1AuthoritativeDir, "0"),
@@ -415,6 +423,7 @@ static config_var_t option_vars_[] = {
V(TestingV3AuthInitialVotingInterval, INTERVAL, "30 minutes"),
V(TestingV3AuthInitialVoteDelay, INTERVAL, "5 minutes"),
V(TestingV3AuthInitialDistDelay, INTERVAL, "5 minutes"),
+ V(TestingV3AuthVotingStartOffset, INTERVAL, "0"),
V(V3AuthVotingInterval, INTERVAL, "1 hour"),
V(V3AuthVoteDelay, INTERVAL, "5 minutes"),
V(V3AuthDistDelay, INTERVAL, "5 minutes"),
@@ -435,6 +444,24 @@ static config_var_t option_vars_[] = {
VAR("__OwningControllerProcess",STRING,OwningControllerProcess, NULL),
V(MinUptimeHidServDirectoryV2, INTERVAL, "25 hours"),
V(VoteOnHidServDirectoriesV2, BOOL, "1"),
+ V(TestingServerDownloadSchedule, CSV_INTERVAL, "0, 0, 0, 60, 60, 120, "
+ "300, 900, 2147483647"),
+ V(TestingClientDownloadSchedule, CSV_INTERVAL, "0, 0, 60, 300, 600, "
+ "2147483647"),
+ V(TestingServerConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 60, "
+ "300, 600, 1800, 1800, 1800, 1800, "
+ "1800, 3600, 7200"),
+ V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 60, "
+ "300, 600, 1800, 3600, 3600, 3600, "
+ "10800, 21600, 43200"),
+ V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "3600, 900, 900, 3600"),
+ V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "10 minutes"),
+ V(TestingDirConnectionMaxStall, INTERVAL, "5 minutes"),
+ V(TestingConsensusMaxDownloadTries, UINT, "8"),
+ V(TestingDescriptorMaxDownloadTries, UINT, "8"),
+ V(TestingMicrodescMaxDownloadTries, UINT, "8"),
+ V(TestingCertMaxDownloadTries, UINT, "8"),
+ V(TestingDirAuthVoteGuard, ROUTERSET, NULL),
VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "0"),
{ NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
@@ -460,9 +487,28 @@ static const config_var_t testing_tor_network_defaults[] = {
V(TestingV3AuthInitialVotingInterval, INTERVAL, "5 minutes"),
V(TestingV3AuthInitialVoteDelay, INTERVAL, "20 seconds"),
V(TestingV3AuthInitialDistDelay, INTERVAL, "20 seconds"),
+ V(TestingV3AuthVotingStartOffset, INTERVAL, "0"),
V(TestingAuthDirTimeToLearnReachability, INTERVAL, "0 minutes"),
V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "0 minutes"),
V(MinUptimeHidServDirectoryV2, INTERVAL, "0 minutes"),
+ V(TestingServerDownloadSchedule, CSV_INTERVAL, "0, 0, 0, 5, 10, 15, "
+ "20, 30, 60"),
+ V(TestingClientDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, 15, 20, "
+ "30, 60"),
+ V(TestingServerConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, "
+ "15, 20, 30, 60"),
+ V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, "
+ "15, 20, 30, 60"),
+ V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "60, 30, 30, 60"),
+ V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "5 seconds"),
+ V(TestingDirConnectionMaxStall, INTERVAL, "30 seconds"),
+ V(TestingConsensusMaxDownloadTries, UINT, "80"),
+ V(TestingDescriptorMaxDownloadTries, UINT, "80"),
+ V(TestingMicrodescMaxDownloadTries, UINT, "80"),
+ V(TestingCertMaxDownloadTries, UINT, "80"),
+ V(TestingEnableConnBwEvent, BOOL, "1"),
+ V(TestingEnableCellStatsEvent, BOOL, "1"),
+ V(TestingEnableTbEmptyEvent, BOOL, "1"),
VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "1"),
{ NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
@@ -475,9 +521,6 @@ static const config_var_t testing_tor_network_defaults[] = {
#ifdef _WIN32
static char *get_windows_conf_root(void);
#endif
-static int options_validate(or_options_t *old_options,
- or_options_t *options,
- int from_setconf, char **msg);
static int options_act_reversible(const or_options_t *old_options, char **msg);
static int options_act(const or_options_t *old_options);
static int options_transition_allowed(const or_options_t *old,
@@ -487,9 +530,8 @@ static int options_transition_affects_workers(
const or_options_t *old_options, const or_options_t *new_options);
static int options_transition_affects_descriptor(
const or_options_t *old_options, const or_options_t *new_options);
-static int check_nickname_list(const char *lst, const char *name, char **msg);
+static int check_nickname_list(char **lst, const char *name, char **msg);
-static int parse_bridge_line(const char *line, int validate_only);
static int parse_client_transport_line(const char *line, int validate_only);
static int parse_server_transport_line(const char *line, int validate_only);
@@ -517,18 +559,21 @@ static int parse_outbound_addresses(or_options_t *options, int validate_only,
char **msg);
static void config_maybe_load_geoip_files_(const or_options_t *options,
const or_options_t *old_options);
+static int options_validate_cb(void *old_options, void *options,
+ void *default_options,
+ int from_setconf, char **msg);
/** Magic value for or_options_t. */
#define OR_OPTIONS_MAGIC 9090909
/** Configuration format for or_options_t. */
-static config_format_t options_format = {
+STATIC config_format_t options_format = {
sizeof(or_options_t),
OR_OPTIONS_MAGIC,
STRUCT_OFFSET(or_options_t, magic_),
option_abbrevs_,
option_vars_,
- (validate_fn_t)options_validate,
+ options_validate_cb,
NULL
};
@@ -545,8 +590,12 @@ static or_options_t *global_default_options = NULL;
static char *torrc_fname = NULL;
/** Name of the most recently read torrc-defaults file.*/
static char *torrc_defaults_fname;
-/** Configuration Options set by command line. */
+/** Configuration options set by command line. */
static config_line_t *global_cmdline_options = NULL;
+/** Non-configuration options set by the command line */
+static config_line_t *global_cmdline_only_options = NULL;
+/** Boolean: Have we parsed the command line? */
+static int have_parsed_cmdline = 0;
/** Contents of most recently read DirPortFrontPage file. */
static char *global_dirfrontpagecontents = NULL;
/** List of port_cfg_t for all configured ports. */
@@ -678,7 +727,7 @@ get_short_version(void)
/** Release additional memory allocated in options
*/
-static void
+STATIC void
or_options_free(or_options_t *options)
{
if (!options)
@@ -691,6 +740,7 @@ or_options_free(or_options_t *options)
smartlist_free(options->NodeFamilySets);
}
tor_free(options->BridgePassword_AuthDigest_);
+ tor_free(options->command_arg);
config_free(&options_format, options);
}
@@ -707,6 +757,9 @@ config_free_all(void)
config_free_lines(global_cmdline_options);
global_cmdline_options = NULL;
+ config_free_lines(global_cmdline_only_options);
+ global_cmdline_only_options = NULL;
+
if (configured_ports) {
SMARTLIST_FOREACH(configured_ports,
port_cfg_t *, p, port_cfg_free(p));
@@ -972,6 +1025,7 @@ options_act_reversible(const or_options_t *old_options, char **msg)
int set_conn_limit = 0;
int r = -1;
int logs_marked = 0;
+ int old_min_log_level = get_min_log_level();
/* Daemonize _first_, since we only want to open most of this stuff in
* the subprocess. Libevent bases can't be reliably inherited across
@@ -1081,9 +1135,6 @@ options_act_reversible(const or_options_t *old_options, char **msg)
/* No need to roll back, since you can't change the value. */
}
- /* Write control ports to disk as appropriate */
- control_ports_write_to_file();
-
if (directory_caches_v2_dir_info(options)) {
char *fn = NULL;
tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-status",
@@ -1119,7 +1170,16 @@ options_act_reversible(const or_options_t *old_options, char **msg)
add_callback_log(severity, control_event_logmsg);
control_adjust_event_log_severity();
tor_free(severity);
+ tor_log_update_sigsafe_err_fds();
+ }
+ if (get_min_log_level() >= LOG_INFO &&
+ get_min_log_level() != old_min_log_level) {
+ log_warn(LD_GENERAL, "Your log may contain sensitive information: you're "
+ "logging more than \"notice\". Please log safely. Don't log "
+ "unless it serves an important reason, and overwrite the log "
+ "afterwards.");
}
+
SMARTLIST_FOREACH(replaced_listeners, connection_t *, conn,
{
log_notice(LD_NET, "Closing old %s on %s:%d",
@@ -1272,6 +1332,9 @@ options_act(const or_options_t *old_options)
}
}
+ /* Write control ports to disk as appropriate */
+ control_ports_write_to_file();
+
if (running_tor && !have_lockfile()) {
if (try_locking(options, 1) < 0)
return -1;
@@ -1302,14 +1365,23 @@ options_act(const or_options_t *old_options)
}
#endif
+ if (options->SafeLogging_ != SAFELOG_SCRUB_ALL &&
+ (!old_options || old_options->SafeLogging_ != options->SafeLogging_)) {
+ log_warn(LD_GENERAL, "Your log may contain sensitive information - you "
+ "disabled SafeLogging. Please log safely. Don't log unless it "
+ "serves an important reason. Overwrite the log afterwards.");
+ }
+
if (options->Bridges) {
mark_bridge_list();
for (cl = options->Bridges; cl; cl = cl->next) {
- if (parse_bridge_line(cl->value, 0)<0) {
+ bridge_line_t *bridge_line = parse_bridge_line(cl->value);
+ if (!bridge_line) {
log_warn(LD_BUG,
"Previously validated Bridge line could not be added!");
return -1;
}
+ bridge_add_from_config(bridge_line);
}
sweep_bridge_list();
}
@@ -1423,8 +1495,14 @@ options_act(const or_options_t *old_options)
return -1;
}
- if (init_cookie_authentication(options->CookieAuthentication) < 0) {
- log_warn(LD_CONFIG,"Error creating cookie authentication file.");
+ if (init_control_cookie_authentication(options->CookieAuthentication) < 0) {
+ log_warn(LD_CONFIG,"Error creating control cookie authentication file.");
+ return -1;
+ }
+
+ /* If we have an ExtORPort, initialize its auth cookie. */
+ if (init_ext_or_cookie_authentication(!!options->ExtORPort_lines) < 0) {
+ log_warn(LD_CONFIG,"Error creating Extended ORPort cookie file.");
return -1;
}
@@ -1732,40 +1810,66 @@ options_act(const or_options_t *old_options)
return 0;
}
-/** Helper: Read a list of configuration options from the command line.
- * If successful, put them in *<b>result</b> and return 0, and return
- * -1 and leave *<b>result</b> alone. */
-static int
-config_get_commandlines(int argc, char **argv, config_line_t **result)
+static const struct {
+ const char *name;
+ int takes_argument;
+} CMDLINE_ONLY_OPTIONS[] = {
+ { "-f", 1 },
+ { "--allow-missing-torrc", 0 },
+ { "--defaults-torrc", 1 },
+ { "--hash-password", 1 },
+ { "--dump-config", 1 },
+ { "--list-fingerprint", 0 },
+ { "--verify-config", 0 },
+ { "--ignore-missing-torrc", 0 },
+ { "--quiet", 0 },
+ { "--hush", 0 },
+ { "--version", 0 },
+ { "--library-versions", 0 },
+ { "-h", 0 },
+ { "--help", 0 },
+ { "--list-torrc-options", 0 },
+ { "--digests", 0 },
+ { "--nt-service", 0 },
+ { "-nt-service", 0 },
+ { NULL, 0 },
+};
+
+/** Helper: Read a list of configuration options from the command line. If
+ * successful, or if ignore_errors is set, put them in *<b>result</b>, put the
+ * commandline-only options in *<b>cmdline_result</b>, and return 0;
+ * otherwise, return -1 and leave *<b>result</b> and <b>cmdline_result</b>
+ * alone. */
+int
+config_parse_commandline(int argc, char **argv, int ignore_errors,
+ config_line_t **result,
+ config_line_t **cmdline_result)
{
+ config_line_t *param = NULL;
+
config_line_t *front = NULL;
config_line_t **new = &front;
- char *s;
+
+ config_line_t *front_cmdline = NULL;
+ config_line_t **new_cmdline = &front_cmdline;
+
+ char *s, *arg;
int i = 1;
while (i < argc) {
unsigned command = CONFIG_LINE_NORMAL;
int want_arg = 1;
+ int is_cmdline = 0;
+ int j;
- if (!strcmp(argv[i],"-f") ||
- !strcmp(argv[i],"--defaults-torrc") ||
- !strcmp(argv[i],"--hash-password")) {
- i += 2; /* command-line option with argument. ignore them. */
- continue;
- } else if (!strcmp(argv[i],"--list-fingerprint") ||
- !strcmp(argv[i],"--verify-config") ||
- !strcmp(argv[i],"--ignore-missing-torrc") ||
- !strcmp(argv[i],"--quiet") ||
- !strcmp(argv[i],"--hush")) {
- i += 1; /* command-line option. ignore it. */
- continue;
- } else if (!strcmp(argv[i],"--nt-service") ||
- !strcmp(argv[i],"-nt-service")) {
- i += 1;
- continue;
+ for (j = 0; CMDLINE_ONLY_OPTIONS[j].name != NULL; ++j) {
+ if (!strcmp(argv[i], CMDLINE_ONLY_OPTIONS[j].name)) {
+ is_cmdline = 1;
+ want_arg = CMDLINE_ONLY_OPTIONS[j].takes_argument;
+ break;
+ }
}
- *new = tor_malloc_zero(sizeof(config_line_t));
s = argv[i];
/* Each keyword may be prefixed with one or two dashes. */
@@ -1785,22 +1889,38 @@ config_get_commandlines(int argc, char **argv, config_line_t **result)
}
if (want_arg && i == argc-1) {
- log_warn(LD_CONFIG,"Command-line option '%s' with no value. Failing.",
- argv[i]);
- config_free_lines(front);
- return -1;
+ if (ignore_errors) {
+ arg = strdup("");
+ } else {
+ log_warn(LD_CONFIG,"Command-line option '%s' with no value. Failing.",
+ argv[i]);
+ config_free_lines(front);
+ config_free_lines(front_cmdline);
+ return -1;
+ }
+ } else {
+ arg = want_arg ? tor_strdup(argv[i+1]) : strdup("");
}
- (*new)->key = tor_strdup(config_expand_abbrev(&options_format, s, 1, 1));
- (*new)->value = want_arg ? tor_strdup(argv[i+1]) : tor_strdup("");
- (*new)->command = command;
- (*new)->next = NULL;
+ param = tor_malloc_zero(sizeof(config_line_t));
+ param->key = is_cmdline ? tor_strdup(argv[i]) : tor_strdup(s);
+ param->value = arg;
+ param->command = command;
+ param->next = NULL;
log_debug(LD_CONFIG, "command line: parsed keyword '%s', value '%s'",
- (*new)->key, (*new)->value);
+ param->key, param->value);
+
+ if (is_cmdline) {
+ *new_cmdline = param;
+ new_cmdline = &((*new_cmdline)->next);
+ } else {
+ *new = param;
+ new = &((*new)->next);
+ }
- new = &((*new)->next);
i += want_arg ? 2 : 1;
}
+ *cmdline_result = front_cmdline;
*result = front;
return 0;
}
@@ -1852,7 +1972,8 @@ options_trial_assign(config_line_t *list, int use_defaults,
return r;
}
- if (options_validate(get_options_mutable(), trial_options, 1, msg) < 0) {
+ if (options_validate(get_options_mutable(), trial_options,
+ global_default_options, 1, msg) < 0) {
config_free(&options_format, trial_options);
return SETOPT_ERR_PARSE; /*XXX make this a separate return value. */
}
@@ -2166,10 +2287,29 @@ options_init(or_options_t *options)
* include options that are the same as Tor's defaults.
*/
char *
-options_dump(const or_options_t *options, int minimal)
+options_dump(const or_options_t *options, int how_to_dump)
{
- return config_dump(&options_format, global_default_options,
- options, minimal, 0);
+ const or_options_t *use_defaults;
+ int minimal;
+ switch (how_to_dump) {
+ case OPTIONS_DUMP_MINIMAL:
+ use_defaults = global_default_options;
+ minimal = 1;
+ break;
+ case OPTIONS_DUMP_DEFAULTS:
+ use_defaults = NULL;
+ minimal = 1;
+ break;
+ case OPTIONS_DUMP_ALL:
+ use_defaults = NULL;
+ minimal = 0;
+ break;
+ default:
+ log_warn(LD_BUG, "Bogus value for how_to_dump==%d", how_to_dump);
+ return NULL;
+ }
+
+ return config_dump(&options_format, use_defaults, options, minimal, 0);
}
/** Return 0 if every element of sl is a string holding a decimal
@@ -2286,10 +2426,19 @@ compute_publishserverdescriptor(or_options_t *options)
* */
#define RECOMMENDED_MIN_CIRCUIT_BUILD_TIMEOUT (10)
-/** Return 0 if every setting in <b>options</b> is reasonable, and a
- * permissible transition from <b>old_options</b>. Else return -1.
- * Should have no side effects, except for normalizing the contents of
- * <b>options</b>.
+static int
+options_validate_cb(void *old_options, void *options, void *default_options,
+ int from_setconf, char **msg)
+{
+ return options_validate(old_options, options, default_options,
+ from_setconf, msg);
+}
+
+/** Return 0 if every setting in <b>options</b> is reasonable, is a
+ * permissible transition from <b>old_options</b>, and none of the
+ * testing-only settings differ from <b>default_options</b> unless in
+ * testing mode. Else return -1. Should have no side effects, except for
+ * normalizing the contents of <b>options</b>.
*
* On error, tor_strdup an error explanation into *<b>msg</b>.
*
@@ -2298,9 +2447,9 @@ compute_publishserverdescriptor(or_options_t *options)
* Log line should stay empty. If it's 0, then give us a default log
* if there are no logs defined.
*/
-static int
+STATIC int
options_validate(or_options_t *old_options, or_options_t *options,
- int from_setconf, char **msg)
+ or_options_t *default_options, int from_setconf, char **msg)
{
int i;
config_line_t *cl;
@@ -2973,14 +3122,14 @@ options_validate(or_options_t *old_options, or_options_t *options,
size_t len;
len = strlen(options->Socks5ProxyUsername);
- if (len < 1 || len > 255)
+ if (len < 1 || len > MAX_SOCKS5_AUTH_FIELD_SIZE)
REJECT("Socks5ProxyUsername must be between 1 and 255 characters.");
if (!options->Socks5ProxyPassword)
REJECT("Socks5ProxyPassword must be included with Socks5ProxyUsername.");
len = strlen(options->Socks5ProxyPassword);
- if (len < 1 || len > 255)
+ if (len < 1 || len > MAX_SOCKS5_AUTH_FIELD_SIZE)
REJECT("Socks5ProxyPassword must be between 1 and 255 characters.");
} else if (options->Socks5ProxyPassword)
REJECT("Socks5ProxyPassword must be included with Socks5ProxyUsername.");
@@ -3040,7 +3189,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
"You should also make sure you aren't listing this bridge's "
"fingerprint in any other MyFamily.");
}
- if (check_nickname_list(options->MyFamily, "MyFamily", msg))
+ if (check_nickname_list(&options->MyFamily, "MyFamily", msg))
return -1;
for (cl = options->NodeFamilies; cl; cl = cl->next) {
routerset_t *rs = routerset_new();
@@ -3064,8 +3213,10 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("If you set UseBridges, you must set TunnelDirConns.");
for (cl = options->Bridges; cl; cl = cl->next) {
- if (parse_bridge_line(cl->value, 1)<0)
- REJECT("Bridge line did not parse. See logs for details.");
+ bridge_line_t *bridge_line = parse_bridge_line(cl->value);
+ if (!bridge_line)
+ REJECT("Bridge line did not parse. See logs for details.");
+ bridge_line_free(bridge_line);
}
for (cl = options->ClientTransportPlugin; cl; cl = cl->next) {
@@ -3101,6 +3252,30 @@ options_validate(or_options_t *old_options, or_options_t *options,
"ServerTransportListenAddr line will be ignored.");
}
+ for (cl = options->ServerTransportOptions; cl; cl = cl->next) {
+ /** If get_options_from_transport_options_line() fails with
+ 'transport' being NULL, it means that something went wrong
+ while parsing the ServerTransportOptions line. */
+ smartlist_t *options_sl =
+ get_options_from_transport_options_line(cl->value, NULL);
+ if (!options_sl)
+ REJECT("ServerTransportOptions did not parse. See logs for details.");
+
+ SMARTLIST_FOREACH(options_sl, char *, cp, tor_free(cp));
+ smartlist_free(options_sl);
+ }
+
+ /* If we are a bridge with a pluggable transport proxy but no
+ Extended ORPort, inform the user that she is missing out. */
+ if (server_mode(options) && options->ServerTransportPlugin &&
+ !options->ExtORPort_lines) {
+ log_notice(LD_CONFIG, "We are a bridge with a pluggable transport "
+ "proxy but the Extended ORPort is disabled. The "
+ "Extended ORPort helps Tor communicate with the pluggable "
+ "transport proxy. Please enable it using the ExtORPort "
+ "torrc option.");
+ }
+
if (options->ConstrainedSockets) {
/* If the user wants to constrain socket buffer use, make sure the desired
* limit is between MIN|MAX_TCPSOCK_BUFFER in k increments. */
@@ -3193,35 +3368,46 @@ options_validate(or_options_t *old_options, or_options_t *options,
"ignore you.");
}
- /*XXXX checking for defaults manually like this is a bit fragile.*/
-
- /* Keep changes to hard-coded values synchronous to man page and default
- * values table. */
- if (options->TestingV3AuthInitialVotingInterval != 30*60 &&
- !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) {
- REJECT("TestingV3AuthInitialVotingInterval may only be changed in testing "
- "Tor networks!");
- } else if (options->TestingV3AuthInitialVotingInterval < MIN_VOTE_INTERVAL) {
+#define CHECK_DEFAULT(arg) \
+ STMT_BEGIN \
+ if (!options->TestingTorNetwork && \
+ !options->UsingTestNetworkDefaults_ && \
+ !config_is_same(&options_format,options, \
+ default_options,#arg)) { \
+ REJECT(#arg " may only be changed in testing Tor " \
+ "networks!"); \
+ } STMT_END
+ CHECK_DEFAULT(TestingV3AuthInitialVotingInterval);
+ CHECK_DEFAULT(TestingV3AuthInitialVoteDelay);
+ CHECK_DEFAULT(TestingV3AuthInitialDistDelay);
+ CHECK_DEFAULT(TestingV3AuthVotingStartOffset);
+ CHECK_DEFAULT(TestingAuthDirTimeToLearnReachability);
+ CHECK_DEFAULT(TestingEstimatedDescriptorPropagationTime);
+ CHECK_DEFAULT(TestingServerDownloadSchedule);
+ CHECK_DEFAULT(TestingClientDownloadSchedule);
+ CHECK_DEFAULT(TestingServerConsensusDownloadSchedule);
+ CHECK_DEFAULT(TestingClientConsensusDownloadSchedule);
+ CHECK_DEFAULT(TestingBridgeDownloadSchedule);
+ CHECK_DEFAULT(TestingClientMaxIntervalWithoutRequest);
+ CHECK_DEFAULT(TestingDirConnectionMaxStall);
+ CHECK_DEFAULT(TestingConsensusMaxDownloadTries);
+ CHECK_DEFAULT(TestingDescriptorMaxDownloadTries);
+ CHECK_DEFAULT(TestingMicrodescMaxDownloadTries);
+ CHECK_DEFAULT(TestingCertMaxDownloadTries);
+#undef CHECK_DEFAULT
+
+ if (options->TestingV3AuthInitialVotingInterval < MIN_VOTE_INTERVAL) {
REJECT("TestingV3AuthInitialVotingInterval is insanely low.");
} else if (((30*60) % options->TestingV3AuthInitialVotingInterval) != 0) {
REJECT("TestingV3AuthInitialVotingInterval does not divide evenly into "
"30 minutes.");
}
- if (options->TestingV3AuthInitialVoteDelay != 5*60 &&
- !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) {
-
- REJECT("TestingV3AuthInitialVoteDelay may only be changed in testing "
- "Tor networks!");
- } else if (options->TestingV3AuthInitialVoteDelay < MIN_VOTE_SECONDS) {
+ if (options->TestingV3AuthInitialVoteDelay < MIN_VOTE_SECONDS) {
REJECT("TestingV3AuthInitialVoteDelay is way too low.");
}
- if (options->TestingV3AuthInitialDistDelay != 5*60 &&
- !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) {
- REJECT("TestingV3AuthInitialDistDelay may only be changed in testing "
- "Tor networks!");
- } else if (options->TestingV3AuthInitialDistDelay < MIN_DIST_SECONDS) {
+ if (options->TestingV3AuthInitialDistDelay < MIN_DIST_SECONDS) {
REJECT("TestingV3AuthInitialDistDelay is way too low.");
}
@@ -3232,26 +3418,79 @@ options_validate(or_options_t *old_options, or_options_t *options,
"must be less than half TestingV3AuthInitialVotingInterval");
}
- if (options->TestingAuthDirTimeToLearnReachability != 30*60 &&
- !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) {
- REJECT("TestingAuthDirTimeToLearnReachability may only be changed in "
- "testing Tor networks!");
- } else if (options->TestingAuthDirTimeToLearnReachability < 0) {
+ if (options->TestingV3AuthVotingStartOffset >
+ MIN(options->TestingV3AuthInitialVotingInterval,
+ options->V3AuthVotingInterval)) {
+ REJECT("TestingV3AuthVotingStartOffset is higher than the voting "
+ "interval.");
+ }
+
+ if (options->TestingAuthDirTimeToLearnReachability < 0) {
REJECT("TestingAuthDirTimeToLearnReachability must be non-negative.");
} else if (options->TestingAuthDirTimeToLearnReachability > 2*60*60) {
COMPLAIN("TestingAuthDirTimeToLearnReachability is insanely high.");
}
- if (options->TestingEstimatedDescriptorPropagationTime != 10*60 &&
- !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) {
- REJECT("TestingEstimatedDescriptorPropagationTime may only be changed in "
- "testing Tor networks!");
- } else if (options->TestingEstimatedDescriptorPropagationTime < 0) {
+ if (options->TestingEstimatedDescriptorPropagationTime < 0) {
REJECT("TestingEstimatedDescriptorPropagationTime must be non-negative.");
} else if (options->TestingEstimatedDescriptorPropagationTime > 60*60) {
COMPLAIN("TestingEstimatedDescriptorPropagationTime is insanely high.");
}
+ if (options->TestingClientMaxIntervalWithoutRequest < 1) {
+ REJECT("TestingClientMaxIntervalWithoutRequest is way too low.");
+ } else if (options->TestingClientMaxIntervalWithoutRequest > 3600) {
+ COMPLAIN("TestingClientMaxIntervalWithoutRequest is insanely high.");
+ }
+
+ if (options->TestingDirConnectionMaxStall < 5) {
+ REJECT("TestingDirConnectionMaxStall is way too low.");
+ } else if (options->TestingDirConnectionMaxStall > 3600) {
+ COMPLAIN("TestingDirConnectionMaxStall is insanely high.");
+ }
+
+ if (options->TestingConsensusMaxDownloadTries < 2) {
+ REJECT("TestingConsensusMaxDownloadTries must be greater than 1.");
+ } else if (options->TestingConsensusMaxDownloadTries > 800) {
+ COMPLAIN("TestingConsensusMaxDownloadTries is insanely high.");
+ }
+
+ if (options->TestingDescriptorMaxDownloadTries < 2) {
+ REJECT("TestingDescriptorMaxDownloadTries must be greater than 1.");
+ } else if (options->TestingDescriptorMaxDownloadTries > 800) {
+ COMPLAIN("TestingDescriptorMaxDownloadTries is insanely high.");
+ }
+
+ if (options->TestingMicrodescMaxDownloadTries < 2) {
+ REJECT("TestingMicrodescMaxDownloadTries must be greater than 1.");
+ } else if (options->TestingMicrodescMaxDownloadTries > 800) {
+ COMPLAIN("TestingMicrodescMaxDownloadTries is insanely high.");
+ }
+
+ if (options->TestingCertMaxDownloadTries < 2) {
+ REJECT("TestingCertMaxDownloadTries must be greater than 1.");
+ } else if (options->TestingCertMaxDownloadTries > 800) {
+ COMPLAIN("TestingCertMaxDownloadTries is insanely high.");
+ }
+
+ if (options->TestingEnableConnBwEvent &&
+ !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) {
+ REJECT("TestingEnableConnBwEvent may only be changed in testing "
+ "Tor networks!");
+ }
+
+ if (options->TestingEnableCellStatsEvent &&
+ !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) {
+ REJECT("TestingEnableCellStatsEvent may only be changed in testing "
+ "Tor networks!");
+ }
+
+ if (options->TestingEnableTbEmptyEvent &&
+ !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) {
+ REJECT("TestingEnableTbEmptyEvent may only be changed in testing "
+ "Tor networks!");
+ }
+
if (options->TestingTorNetwork) {
log_warn(LD_CONFIG, "TestingTorNetwork is set. This will make your node "
"almost unusable in the public Tor network, and is "
@@ -3510,31 +3749,63 @@ get_default_conf_file(int defaults_file)
}
/** Verify whether lst is a string containing valid-looking comma-separated
- * nicknames, or NULL. Return 0 on success. Warn and return -1 on failure.
+ * nicknames, or NULL. Will normalise <b>lst</b> to prefix '$' to any nickname
+ * or fingerprint that needs it. Return 0 on success.
+ * Warn and return -1 on failure.
*/
static int
-check_nickname_list(const char *lst, const char *name, char **msg)
+check_nickname_list(char **lst, const char *name, char **msg)
{
int r = 0;
smartlist_t *sl;
+ int changes = 0;
- if (!lst)
+ if (!*lst)
return 0;
sl = smartlist_new();
- smartlist_split_string(sl, lst, ",",
+ smartlist_split_string(sl, *lst, ",",
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK|SPLIT_STRIP_SPACE, 0);
- SMARTLIST_FOREACH(sl, const char *, s,
+ SMARTLIST_FOREACH_BEGIN(sl, char *, s)
{
if (!is_legal_nickname_or_hexdigest(s)) {
+ // check if first char is dollar
+ if (s[0] != '$') {
+ // Try again but with a dollar symbol prepended
+ char *prepended;
+ tor_asprintf(&prepended, "$%s", s);
+
+ if (is_legal_nickname_or_hexdigest(prepended)) {
+ // The nickname is valid when it's prepended, swap the current
+ // version with a prepended one
+ tor_free(s);
+ SMARTLIST_REPLACE_CURRENT(sl, s, prepended);
+ changes = 1;
+ continue;
+ }
+
+ // Still not valid, free and fallback to error message
+ tor_free(prepended);
+ }
+
tor_asprintf(msg, "Invalid nickname '%s' in %s line", s, name);
r = -1;
break;
}
- });
+ }
+ SMARTLIST_FOREACH_END(s);
+
+ // Replace the caller's nickname list with a fixed one
+ if (changes && r == 0) {
+ char *newNicknames = smartlist_join_strings(sl, ", ", 0, NULL);
+ tor_free(*lst);
+ *lst = newNicknames;
+ }
+
SMARTLIST_FOREACH(sl, char *, s, tor_free(s));
smartlist_free(sl);
+
return r;
}
@@ -3550,26 +3821,26 @@ check_nickname_list(const char *lst, const char *name, char **msg)
* filename if it doesn't exist.
*/
static char *
-find_torrc_filename(int argc, char **argv,
+find_torrc_filename(config_line_t *cmd_arg,
int defaults_file,
int *using_default_fname, int *ignore_missing_torrc)
{
char *fname=NULL;
- int i;
+ config_line_t *p_index;
const char *fname_opt = defaults_file ? "--defaults-torrc" : "-f";
const char *ignore_opt = defaults_file ? NULL : "--ignore-missing-torrc";
if (defaults_file)
*ignore_missing_torrc = 1;
- for (i = 1; i < argc; ++i) {
- if (i < argc-1 && !strcmp(argv[i],fname_opt)) {
+ for (p_index = cmd_arg; p_index; p_index = p_index->next) {
+ if (!strcmp(p_index->key, fname_opt)) {
if (fname) {
log_warn(LD_CONFIG, "Duplicate %s options on command line.",
fname_opt);
tor_free(fname);
}
- fname = expand_filename(argv[i+1]);
+ fname = expand_filename(p_index->value);
{
char *absfname;
@@ -3579,8 +3850,7 @@ find_torrc_filename(int argc, char **argv,
}
*using_default_fname = 0;
- ++i;
- } else if (ignore_opt && !strcmp(argv[i],ignore_opt)) {
+ } else if (ignore_opt && !strcmp(p_index->key,ignore_opt)) {
*ignore_missing_torrc = 1;
}
}
@@ -3617,7 +3887,7 @@ find_torrc_filename(int argc, char **argv,
* Return the contents of the file on success, and NULL on failure.
*/
static char *
-load_torrc_from_disk(int argc, char **argv, int defaults_file)
+load_torrc_from_disk(config_line_t *cmd_arg, int defaults_file)
{
char *fname=NULL;
char *cf = NULL;
@@ -3625,7 +3895,7 @@ load_torrc_from_disk(int argc, char **argv, int defaults_file)
int ignore_missing_torrc = 0;
char **fname_var = defaults_file ? &torrc_defaults_fname : &torrc_fname;
- fname = find_torrc_filename(argc, argv, defaults_file,
+ fname = find_torrc_filename(cmd_arg, defaults_file,
&using_default_torrc, &ignore_missing_torrc);
tor_assert(fname);
log_debug(LD_CONFIG, "Opening config file \"%s\"", fname);
@@ -3667,59 +3937,75 @@ int
options_init_from_torrc(int argc, char **argv)
{
char *cf=NULL, *cf_defaults=NULL;
- int i, command;
+ int command;
int retval = -1;
- static char **backup_argv;
- static int backup_argc;
char *command_arg = NULL;
char *errmsg=NULL;
+ config_line_t *p_index = NULL;
+ config_line_t *cmdline_only_options = NULL;
- if (argv) { /* first time we're called. save command line args */
- backup_argv = argv;
- backup_argc = argc;
- } else { /* we're reloading. need to clean up old options first. */
- argv = backup_argv;
- argc = backup_argc;
+ /* Go through command-line variables */
+ if (! have_parsed_cmdline) {
+ /* Or we could redo the list every time we pass this place.
+ * It does not really matter */
+ if (config_parse_commandline(argc, argv, 0, &global_cmdline_options,
+ &global_cmdline_only_options) < 0) {
+ goto err;
+ }
+ have_parsed_cmdline = 1;
}
- if (argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1],"--help"))) {
+ cmdline_only_options = global_cmdline_only_options;
+
+ if (config_line_find(cmdline_only_options, "-h") ||
+ config_line_find(cmdline_only_options, "--help")) {
print_usage();
exit(0);
}
- if (argc > 1 && !strcmp(argv[1], "--list-torrc-options")) {
+ if (config_line_find(cmdline_only_options, "--list-torrc-options")) {
/* For documenting validating whether we've documented everything. */
list_torrc_options();
exit(0);
}
- if (argc > 1 && (!strcmp(argv[1],"--version"))) {
+ if (config_line_find(cmdline_only_options, "--version")) {
printf("Tor version %s.\n",get_version());
exit(0);
}
- if (argc > 1 && (!strcmp(argv[1],"--digests"))) {
+
+ if (config_line_find(cmdline_only_options, "--digests")) {
printf("Tor version %s.\n",get_version());
printf("%s", libor_get_digests());
printf("%s", tor_get_digests());
exit(0);
}
- /* Go through command-line variables */
- if (!global_cmdline_options) {
- /* Or we could redo the list every time we pass this place.
- * It does not really matter */
- if (config_get_commandlines(argc, argv, &global_cmdline_options) < 0) {
- goto err;
- }
+ if (config_line_find(cmdline_only_options, "--library-versions")) {
+ printf("Tor version %s. \n", get_version());
+ printf("Library versions\tCompiled\t\tRuntime\n");
+ printf("Libevent\t\t%-15s\t\t%s\n",
+ tor_libevent_get_header_version_str(),
+ tor_libevent_get_version_str());
+ printf("OpenSSL \t\t%-15s\t\t%s\n",
+ crypto_openssl_get_header_version_str(),
+ crypto_openssl_get_version_str());
+ printf("Zlib \t\t%-15s\t\t%s\n",
+ tor_zlib_get_header_version_str(),
+ tor_zlib_get_version_str());
+ //TODO: Hex versions?
+ exit(0);
}
command = CMD_RUN_TOR;
- for (i = 1; i < argc; ++i) {
- if (!strcmp(argv[i],"--list-fingerprint")) {
+ for (p_index = cmdline_only_options; p_index; p_index = p_index->next) {
+ if (!strcmp(p_index->key,"--list-fingerprint")) {
command = CMD_LIST_FINGERPRINT;
- } else if (!strcmp(argv[i],"--hash-password")) {
+ } else if (!strcmp(p_index->key, "--hash-password")) {
command = CMD_HASH_PASSWORD;
- command_arg = tor_strdup( (i < argc-1) ? argv[i+1] : "");
- ++i;
- } else if (!strcmp(argv[i],"--verify-config")) {
+ command_arg = p_index->value;
+ } else if (!strcmp(p_index->key, "--dump-config")) {
+ command = CMD_DUMP_CONFIG;
+ command_arg = p_index->value;
+ } else if (!strcmp(p_index->key, "--verify-config")) {
command = CMD_VERIFY_CONFIG;
}
}
@@ -3728,10 +4014,15 @@ options_init_from_torrc(int argc, char **argv)
cf_defaults = tor_strdup("");
cf = tor_strdup("");
} else {
- cf_defaults = load_torrc_from_disk(argc, argv, 1);
- cf = load_torrc_from_disk(argc, argv, 0);
- if (!cf)
- goto err;
+ cf_defaults = load_torrc_from_disk(cmdline_only_options, 1);
+ cf = load_torrc_from_disk(cmdline_only_options, 0);
+ if (!cf) {
+ if (config_line_find(cmdline_only_options, "--allow-missing-torrc")) {
+ cf = tor_strdup("");
+ } else {
+ goto err;
+ }
+ }
}
retval = options_init_from_string(cf_defaults, cf, command, command_arg,
@@ -3775,7 +4066,7 @@ options_init_from_string(const char *cf_defaults, const char *cf,
newoptions->magic_ = OR_OPTIONS_MAGIC;
options_init(newoptions);
newoptions->command = command;
- newoptions->command_arg = command_arg;
+ newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL;
for (i = 0; i < 2; ++i) {
const char *body = i==0 ? cf_defaults : cf;
@@ -3839,7 +4130,7 @@ options_init_from_string(const char *cf_defaults, const char *cf,
newoptions->magic_ = OR_OPTIONS_MAGIC;
options_init(newoptions);
newoptions->command = command;
- newoptions->command_arg = command_arg;
+ newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL;
/* Assign all options a second time. */
for (i = 0; i < 2; ++i) {
@@ -3871,7 +4162,8 @@ options_init_from_string(const char *cf_defaults, const char *cf,
}
/* Validate newoptions */
- if (options_validate(oldoptions, newoptions, 0, msg) < 0) {
+ if (options_validate(oldoptions, newoptions, newdefaultoptions,
+ 0, msg) < 0) {
err = SETOPT_ERR_PARSE; /*XXX make this a separate return value.*/
goto err;
}
@@ -4128,21 +4420,72 @@ options_init_logs(or_options_t *options, int validate_only)
return ok?0:-1;
}
+/** Given a smartlist of SOCKS arguments to be passed to a transport
+ * proxy in <b>args</b>, validate them and return -1 if they are
+ * corrupted. Return 0 if they seem OK. */
+static int
+validate_transport_socks_arguments(const smartlist_t *args)
+{
+ char *socks_string = NULL;
+ size_t socks_string_len;
+
+ tor_assert(args);
+ tor_assert(smartlist_len(args) > 0);
+
+ SMARTLIST_FOREACH_BEGIN(args, const char *, s) {
+ if (!string_is_key_value(LOG_WARN, s)) { /* items should be k=v items */
+ log_warn(LD_CONFIG, "'%s' is not a k=v item.", s);
+ return -1;
+ }
+ } SMARTLIST_FOREACH_END(s);
+
+ socks_string = pt_stringify_socks_args(args);
+ if (!socks_string)
+ return -1;
+
+ socks_string_len = strlen(socks_string);
+ tor_free(socks_string);
+
+ if (socks_string_len > MAX_SOCKS5_AUTH_SIZE_TOTAL) {
+ log_warn(LD_CONFIG, "SOCKS arguments can't be more than %u bytes (%lu).",
+ MAX_SOCKS5_AUTH_SIZE_TOTAL,
+ (unsigned long) socks_string_len);
+ return -1;
+ }
+
+ return 0;
+}
+
+/** Deallocate a bridge_line_t structure. */
+/* private */ void
+bridge_line_free(bridge_line_t *bridge_line)
+{
+ if (!bridge_line)
+ return;
+
+ if (bridge_line->socks_args) {
+ SMARTLIST_FOREACH(bridge_line->socks_args, char*, s, tor_free(s));
+ smartlist_free(bridge_line->socks_args);
+ }
+ tor_free(bridge_line->transport_name);
+ tor_free(bridge_line);
+}
+
/** Read the contents of a Bridge line from <b>line</b>. Return 0
* if the line is well-formed, and -1 if it isn't. If
* <b>validate_only</b> is 0, and the line is well-formed, then add
- * the bridge described in the line to our internal bridge list. */
-static int
-parse_bridge_line(const char *line, int validate_only)
+ * the bridge described in the line to our internal bridge list.
+ *
+ * Bridge line format:
+ * Bridge [transport] IP:PORT [id-fingerprint] [k=v] [k=v] ...
+ */
+/* private */ bridge_line_t *
+parse_bridge_line(const char *line)
{
smartlist_t *items = NULL;
- int r;
char *addrport=NULL, *fingerprint=NULL;
- char *transport_name=NULL;
- char *field1=NULL;
- tor_addr_t addr;
- uint16_t port = 0;
- char digest[DIGEST_LEN];
+ char *field=NULL;
+ bridge_line_t *bridge_line = tor_malloc_zero(sizeof(bridge_line_t));
items = smartlist_new();
smartlist_split_string(items, line, NULL,
@@ -4152,75 +4495,110 @@ parse_bridge_line(const char *line, int validate_only)
goto err;
}
- /* field1 is either a transport name or addrport */
- field1 = smartlist_get(items, 0);
+ /* first field is either a transport name or addrport */
+ field = smartlist_get(items, 0);
smartlist_del_keeporder(items, 0);
- if (!(strstr(field1, ".") || strstr(field1, ":"))) {
- /* new-style bridge line */
- transport_name = field1;
+ if (string_is_C_identifier(field)) {
+ /* It's a transport name. */
+ bridge_line->transport_name = field;
if (smartlist_len(items) < 1) {
log_warn(LD_CONFIG, "Too few items to Bridge line.");
goto err;
}
- addrport = smartlist_get(items, 0);
+ addrport = smartlist_get(items, 0); /* Next field is addrport then. */
smartlist_del_keeporder(items, 0);
} else {
- addrport = field1;
+ addrport = field;
}
- if (tor_addr_port_lookup(addrport, &addr, &port)<0) {
+ /* Parse addrport. */
+ if (tor_addr_port_lookup(addrport,
+ &bridge_line->addr, &bridge_line->port)<0) {
log_warn(LD_CONFIG, "Error parsing Bridge address '%s'", addrport);
goto err;
}
- if (!port) {
+ if (!bridge_line->port) {
log_info(LD_CONFIG,
"Bridge address '%s' has no port; using default port 443.",
addrport);
- port = 443;
+ bridge_line->port = 443;
}
+ /* If transports are enabled, next field could be a fingerprint or a
+ socks argument. If transports are disabled, next field must be
+ a fingerprint. */
if (smartlist_len(items)) {
- fingerprint = smartlist_join_strings(items, "", 0, NULL);
+ if (bridge_line->transport_name) { /* transports enabled: */
+ field = smartlist_get(items, 0);
+ smartlist_del_keeporder(items, 0);
+
+ /* If it's a key=value pair, then it's a SOCKS argument for the
+ transport proxy... */
+ if (string_is_key_value(LOG_DEBUG, field)) {
+ bridge_line->socks_args = smartlist_new();
+ smartlist_add(bridge_line->socks_args, field);
+ } else { /* ...otherwise, it's the bridge fingerprint. */
+ fingerprint = field;
+ }
+
+ } else { /* transports disabled: */
+ fingerprint = smartlist_join_strings(items, "", 0, NULL);
+ }
+ }
+
+ /* Handle fingerprint, if it was provided. */
+ if (fingerprint) {
if (strlen(fingerprint) != HEX_DIGEST_LEN) {
log_warn(LD_CONFIG, "Key digest for Bridge is wrong length.");
goto err;
}
- if (base16_decode(digest, DIGEST_LEN, fingerprint, HEX_DIGEST_LEN)<0) {
+ if (base16_decode(bridge_line->digest, DIGEST_LEN,
+ fingerprint, HEX_DIGEST_LEN)<0) {
log_warn(LD_CONFIG, "Unable to decode Bridge key digest.");
goto err;
}
}
- if (!validate_only) {
- log_debug(LD_DIR, "Bridge at %s (transport: %s) (%s)",
- fmt_addrport(&addr, port),
- transport_name ? transport_name : "no transport",
- fingerprint ? fingerprint : "no key listed");
- bridge_add_from_config(&addr, port,
- fingerprint ? digest : NULL, transport_name);
+ /* If we are using transports, any remaining items in the smartlist
+ should be k=v values. */
+ if (bridge_line->transport_name && smartlist_len(items)) {
+ if (!bridge_line->socks_args)
+ bridge_line->socks_args = smartlist_new();
+
+ /* append remaining items of 'items' to 'socks_args' */
+ smartlist_add_all(bridge_line->socks_args, items);
+ smartlist_clear(items);
+
+ tor_assert(smartlist_len(bridge_line->socks_args) > 0);
+ }
+
+ if (bridge_line->socks_args) {
+ if (validate_transport_socks_arguments(bridge_line->socks_args) < 0)
+ goto err;
}
- r = 0;
goto done;
err:
- r = -1;
+ bridge_line_free(bridge_line);
+ bridge_line = NULL;
done:
SMARTLIST_FOREACH(items, char*, s, tor_free(s));
smartlist_free(items);
tor_free(addrport);
- tor_free(transport_name);
tor_free(fingerprint);
- return r;
+
+ return bridge_line;
}
/** Read the contents of a ClientTransportPlugin line from
* <b>line</b>. Return 0 if the line is well-formed, and -1 if it
* isn't.
*
- * If <b>validate_only</b> is 0, and the line is well-formed:
+ * If <b>validate_only</b> is 0, the line is well-formed, and the
+ * transport is needed by some bridge:
* - If it's an external proxy line, add the transport described in the line to
* our internal transport list.
* - If it's a managed proxy line, launch the managed proxy. */
@@ -4242,7 +4620,8 @@ parse_client_transport_line(const char *line, int validate_only)
int is_managed=0;
char **proxy_argv=NULL;
char **tmp=NULL;
- int proxy_argc,i;
+ int proxy_argc, i;
+ int is_useless_proxy=1;
int line_length;
@@ -4264,11 +4643,16 @@ parse_client_transport_line(const char *line, int validate_only)
smartlist_split_string(transport_list, transports, ",",
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
SMARTLIST_FOREACH_BEGIN(transport_list, const char *, transport_name) {
+ /* validate transport names */
if (!string_is_C_identifier(transport_name)) {
log_warn(LD_CONFIG, "Transport name is not a C identifier (%s).",
transport_name);
goto err;
}
+
+ /* see if we actually need the transports provided by this proxy */
+ if (!validate_only && transport_is_needed(transport_name))
+ is_useless_proxy = 0;
} SMARTLIST_FOREACH_END(transport_name);
/* field2 is either a SOCKS version or "exec" */
@@ -4287,9 +4671,15 @@ parse_client_transport_line(const char *line, int validate_only)
}
if (is_managed) { /* managed */
- if (!validate_only) { /* if we are not just validating, use the
- rest of the line as the argv of the proxy
- to be launched */
+ if (!validate_only && is_useless_proxy) {
+ log_warn(LD_GENERAL, "Pluggable transport proxy (%s) does not provide "
+ "any needed transports and will not be launched.", line);
+ }
+
+ /* If we are not just validating, use the rest of the line as the
+ argv of the proxy to be launched. Also, make sure that we are
+ only launching proxies that contribute useful transports. */
+ if (!validate_only && !is_useless_proxy) {
proxy_argc = line_length-2;
tor_assert(proxy_argc > 0);
proxy_argv = tor_malloc_zero(sizeof(char*)*(proxy_argc+1));
@@ -4403,6 +4793,63 @@ get_bindaddr_from_transport_listen_line(const char *line,const char *transport)
return addrport;
}
+/** Given a ServerTransportOptions <b>line</b>, return a smartlist
+ * with the options. Return NULL if the line was not well-formed.
+ *
+ * If <b>transport</b> is set, return NULL if the line is not
+ * referring to <b>transport</b>.
+ *
+ * The returned smartlist and its strings are allocated on the heap
+ * and it's the responsibility of the caller to free it. */
+smartlist_t *
+get_options_from_transport_options_line(const char *line,const char *transport)
+{
+ smartlist_t *items = smartlist_new();
+ smartlist_t *options = smartlist_new();
+ const char *parsed_transport = NULL;
+
+ smartlist_split_string(items, line, NULL,
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+
+ if (smartlist_len(items) < 2) {
+ log_warn(LD_CONFIG,"Too few arguments on ServerTransportOptions line.");
+ goto err;
+ }
+
+ parsed_transport = smartlist_get(items, 0);
+ /* If 'transport' is given, check if it matches the one on the line */
+ if (transport && strcmp(transport, parsed_transport))
+ goto err;
+
+ SMARTLIST_FOREACH_BEGIN(items, const char *, option) {
+ if (option_sl_idx == 0) /* skip the transport field (first field)*/
+ continue;
+
+ /* validate that it's a k=v value */
+ if (!string_is_key_value(LOG_WARN, option)) {
+ log_warn(LD_CONFIG, "%s is not a k=v value.", escaped(option));
+ goto err;
+ }
+
+ /* add it to the options smartlist */
+ smartlist_add(options, tor_strdup(option));
+ log_debug(LD_CONFIG, "Added %s to the list of options", escaped(option));
+ } SMARTLIST_FOREACH_END(option);
+
+ goto done;
+
+ err:
+ SMARTLIST_FOREACH(options, char*, s, tor_free(s));
+ smartlist_free(options);
+ options = NULL;
+
+ done:
+ SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+ smartlist_free(items);
+
+ return options;
+}
+
/** Given the name of a pluggable transport in <b>transport</b>, check
* the configuration file to see if the user has explicitly asked for
* it to listen on a specific port. Return a <address:port> string if
@@ -4423,6 +4870,26 @@ get_transport_bindaddr_from_config(const char *transport)
return NULL;
}
+/** Given the name of a pluggable transport in <b>transport</b>, check
+ * the configuration file to see if the user has asked us to pass any
+ * parameters to the pluggable transport. Return a smartlist
+ * containing the parameters, otherwise NULL. */
+smartlist_t *
+get_options_for_server_transport(const char *transport)
+{
+ config_line_t *cl;
+ const or_options_t *options = get_options();
+
+ for (cl = options->ServerTransportOptions; cl; cl = cl->next) {
+ smartlist_t *options_sl =
+ get_options_from_transport_options_line(cl->value, transport);
+ if (options_sl)
+ return options_sl;
+ }
+
+ return NULL;
+}
+
/** Read the contents of a ServerTransportPlugin line from
* <b>line</b>. Return 0 if the line is well-formed, and -1 if it
* isn't.
@@ -4827,6 +5294,27 @@ warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname,
} SMARTLIST_FOREACH_END(port);
}
+/** Warn for every Extended ORPort port in <b>ports</b> that is on a
+ * publicly routable address. */
+static void
+warn_nonlocal_ext_orports(const smartlist_t *ports, const char *portname)
+{
+ SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) {
+ if (port->type != CONN_TYPE_EXT_OR_LISTENER)
+ continue;
+ if (port->is_unix_addr)
+ continue;
+ /* XXX maybe warn even if address is RFC1918? */
+ if (!tor_addr_is_internal(&port->addr, 1)) {
+ log_warn(LD_CONFIG, "You specified a public address '%s' for %sPort. "
+ "This is not advised; this address is supposed to only be "
+ "exposed on localhost so that your pluggable transport "
+ "proxies can connect to it.",
+ fmt_addrport(&port->addr, port->port), portname);
+ }
+ } SMARTLIST_FOREACH_END(port);
+}
+
/** Given a list of port_cfg_t in <b>ports</b>, warn any controller port there
* is listening on any non-loopback address. If <b>forbid</b> is true,
* then emit a stronger warning and remove the port from the list.
@@ -4927,6 +5415,7 @@ parse_port_config(smartlist_t *out,
smartlist_t *elts;
int retval = -1;
const unsigned is_control = (listener_type == CONN_TYPE_CONTROL_LISTENER);
+ const unsigned is_ext_orport = (listener_type == CONN_TYPE_EXT_OR_LISTENER);
const unsigned allow_no_options = flags & CL_PORT_NO_OPTIONS;
const unsigned use_server_options = flags & CL_PORT_SERVER_OPTIONS;
const unsigned warn_nonlocal = flags & CL_PORT_WARN_NONLOCAL;
@@ -5004,6 +5493,8 @@ parse_port_config(smartlist_t *out,
if (warn_nonlocal && out) {
if (is_control)
warn_nonlocal_controller_ports(out, forbid_nonlocal);
+ else if (is_ext_orport)
+ warn_nonlocal_ext_orports(out, portname);
else
warn_nonlocal_client_ports(out, portname, listener_type);
}
@@ -5277,6 +5768,8 @@ parse_port_config(smartlist_t *out,
if (warn_nonlocal && out) {
if (is_control)
warn_nonlocal_controller_ports(out, forbid_nonlocal);
+ else if (is_ext_orport)
+ warn_nonlocal_ext_orports(out, portname);
else
warn_nonlocal_client_ports(out, portname, listener_type);
}
@@ -5423,6 +5916,14 @@ parse_ports(or_options_t *options, int validate_only,
goto err;
}
if (parse_port_config(ports,
+ options->ExtORPort_lines, NULL,
+ "ExtOR", CONN_TYPE_EXT_OR_LISTENER,
+ "127.0.0.1", 0,
+ CL_PORT_SERVER_OPTIONS|CL_PORT_WARN_NONLOCAL) < 0) {
+ *msg = tor_strdup("Invalid ExtORPort configuration");
+ goto err;
+ }
+ if (parse_port_config(ports,
options->DirPort_lines, options->DirListenAddress,
"Dir", CONN_TYPE_DIR_LISTENER,
"0.0.0.0", 0,
@@ -5457,6 +5958,8 @@ parse_ports(or_options_t *options, int validate_only,
!! count_real_listeners(ports, CONN_TYPE_DIR_LISTENER);
options->DNSPort_set =
!! count_real_listeners(ports, CONN_TYPE_AP_DNS_LISTENER);
+ options->ExtORPort_set =
+ !! count_real_listeners(ports, CONN_TYPE_EXT_OR_LISTENER);
if (!validate_only) {
if (configured_ports) {
@@ -5744,7 +6247,7 @@ write_configuration_file(const char *fname, const or_options_t *options)
return -1;
}
- if (!(new_conf = options_dump(options, 1))) {
+ if (!(new_conf = options_dump(options, OPTIONS_DUMP_MINIMAL))) {
log_warn(LD_BUG, "Couldn't get configuration string");
goto err;
}
@@ -5904,6 +6407,43 @@ options_get_datadir_fname2_suffix(const or_options_t *options,
return fname;
}
+/** Check wether the data directory has a private subdirectory
+ * <b>subdir</b>. If not, try to create it. Return 0 on success,
+ * -1 otherwise. */
+int
+check_or_create_data_subdir(const char *subdir)
+{
+ char *statsdir = get_datadir_fname(subdir);
+ int return_val = 0;
+
+ if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) {
+ log_warn(LD_HIST, "Unable to create %s/ directory!", subdir);
+ return_val = -1;
+ }
+ tor_free(statsdir);
+ return return_val;
+}
+
+/** Create a file named <b>fname</b> with contents <b>str</b> in the
+ * subdirectory <b>subdir</b> of the data directory. <b>descr</b>
+ * should be a short description of the file's content and will be
+ * used for the warning message, if it's present and the write process
+ * fails. Return 0 on success, -1 otherwise.*/
+int
+write_to_data_subdir(const char* subdir, const char* fname,
+ const char* str, const char* descr)
+{
+ char *filename = get_datadir_fname2(subdir, fname);
+ int return_val = 0;
+
+ if (write_str_to_file(filename, str, 0) < 0) {
+ log_warn(LD_HIST, "Unable to write %s to disk!", descr ? descr : fname);
+ return_val = -1;
+ }
+ tor_free(filename);
+ return return_val;
+}
+
/** Given a file name check to see whether the file exists but has not been
* modified for a very long time. If so, remove it. */
void
@@ -5912,7 +6452,8 @@ remove_file_if_very_old(const char *fname, time_t now)
#define VERY_OLD_FILE_AGE (28*24*60*60)
struct stat st;
- if (stat(fname, &st)==0 && st.st_mtime < now-VERY_OLD_FILE_AGE) {
+ if (stat(sandbox_intern_string(fname), &st)==0 &&
+ st.st_mtime < now-VERY_OLD_FILE_AGE) {
char buf[ISO_TIME_LEN+1];
format_local_iso_time(buf, st.st_mtime);
log_notice(LD_GENERAL, "Obsolete file %s hasn't been modified since %s. "
@@ -5993,6 +6534,7 @@ getinfo_helper_config(control_connection_t *conn,
case CONFIG_TYPE_ISOTIME: type = "Time"; break;
case CONFIG_TYPE_ROUTERSET: type = "RouterList"; break;
case CONFIG_TYPE_CSV: type = "CommaList"; break;
+ case CONFIG_TYPE_CSV_INTERVAL: type = "TimeIntervalCommaList"; break;
case CONFIG_TYPE_LINELIST: type = "LineList"; break;
case CONFIG_TYPE_LINELIST_S: type = "Dependant"; break;
case CONFIG_TYPE_LINELIST_V: type = "Virtual"; break;
@@ -6124,3 +6666,58 @@ config_maybe_load_geoip_files_(const or_options_t *options,
config_load_geoip_file_(AF_INET6, options->GeoIPv6File, "geoip6");
}
+/** Initialize cookie authentication (used so far by the ControlPort
+ * and Extended ORPort).
+ *
+ * Allocate memory and create a cookie (of length <b>cookie_len</b>)
+ * in <b>cookie_out</b>.
+ * Then write it down to <b>fname</b> and prepend it with <b>header</b>.
+ *
+ * If the whole procedure was successful, set
+ * <b>cookie_is_set_out</b> to True. */
+int
+init_cookie_authentication(const char *fname, const char *header,
+ int cookie_len,
+ uint8_t **cookie_out, int *cookie_is_set_out)
+{
+ char cookie_file_str_len = strlen(header) + cookie_len;
+ char *cookie_file_str = tor_malloc(cookie_file_str_len);
+ int retval = -1;
+
+ /* We don't want to generate a new cookie every time we call
+ * options_act(). One should be enough. */
+ if (*cookie_is_set_out) {
+ retval = 0; /* we are all set */
+ goto done;
+ }
+
+ /* If we've already set the cookie, free it before re-setting
+ it. This can happen if we previously generated a cookie, but
+ couldn't write it to a disk. */
+ if (*cookie_out)
+ tor_free(*cookie_out);
+
+ /* Generate the cookie */
+ *cookie_out = tor_malloc(cookie_len);
+ if (crypto_rand((char *)*cookie_out, cookie_len) < 0)
+ goto done;
+
+ /* Create the string that should be written on the file. */
+ memcpy(cookie_file_str, header, strlen(header));
+ memcpy(cookie_file_str+strlen(header), *cookie_out, cookie_len);
+ if (write_bytes_to_file(fname, cookie_file_str, cookie_file_str_len, 1)) {
+ log_warn(LD_FS,"Error writing auth cookie to %s.", escaped(fname));
+ goto done;
+ }
+
+ /* Success! */
+ log_info(LD_GENERAL, "Generated auth cookie file in '%s'.", escaped(fname));
+ *cookie_is_set_out = 1;
+ retval = 0;
+
+ done:
+ memwipe(cookie_file_str, 0, cookie_file_str_len);
+ tor_free(cookie_file_str);
+ return retval;
+}
+
diff --git a/src/or/config.h b/src/or/config.h
index ef4acac51..8ee2a4572 100644
--- a/src/or/config.h
+++ b/src/or/config.h
@@ -32,7 +32,11 @@ int resolve_my_address(int warn_severity, const or_options_t *options,
const char **method_out, char **hostname_out);
int is_local_addr(const tor_addr_t *addr);
void options_init(or_options_t *options);
-char *options_dump(const or_options_t *options, int minimal);
+
+#define OPTIONS_DUMP_MINIMAL 1
+#define OPTIONS_DUMP_DEFAULTS 2
+#define OPTIONS_DUMP_ALL 3
+char *options_dump(const or_options_t *options, int how_to_dump);
int options_init_from_torrc(int argc, char **argv);
setopt_err_t options_init_from_string(const char *cf_defaults, const char *cf,
int command, const char *command_arg, char **msg);
@@ -59,6 +63,10 @@ char *options_get_datadir_fname2_suffix(const or_options_t *options,
#define get_datadir_fname_suffix(sub1, suffix) \
get_datadir_fname2_suffix((sub1), NULL, (suffix))
+int check_or_create_data_subdir(const char *subdir);
+int write_to_data_subdir(const char* subdir, const char* fname,
+ const char* str, const char* descr);
+
int get_num_cpus(const or_options_t *options);
const smartlist_t *get_configured_ports(void);
@@ -86,10 +94,15 @@ uint32_t get_effective_bwburst(const or_options_t *options);
char *get_transport_bindaddr_from_config(const char *transport);
-#ifdef CONFIG_PRIVATE
-/* Used only by config.c and test.c */
+int init_cookie_authentication(const char *fname, const char *header,
+ int cookie_len,
+ uint8_t **cookie_out, int *cookie_is_set_out);
+
or_options_t *options_new(void);
-#endif
+
+int config_parse_commandline(int argc, char **argv, int ignore_errors,
+ config_line_t **result,
+ config_line_t **cmdline_result);
void config_register_addressmaps(const or_options_t *options);
/* XXXX024 move to connection_edge.h */
@@ -98,5 +111,34 @@ int addressmap_register_auto(const char *from, const char *to,
addressmap_entry_source_t addrmap_source,
const char **msg);
+/** Represents the information stored in a torrc Bridge line. */
+typedef struct bridge_line_t {
+ tor_addr_t addr; /* The IP address of the bridge. */
+ uint16_t port; /* The TCP port of the bridge. */
+ char *transport_name; /* The name of the pluggable transport that
+ should be used to connect to the bridge. */
+ char digest[DIGEST_LEN]; /* The bridge's identity key digest. */
+ smartlist_t *socks_args; /* SOCKS arguments for the pluggable
+ transport proxy. */
+} bridge_line_t;
+
+void bridge_line_free(bridge_line_t *bridge_line);
+bridge_line_t *parse_bridge_line(const char *line);
+smartlist_t *get_options_from_transport_options_line(const char *line,
+ const char *transport);
+smartlist_t *get_options_for_server_transport(const char *transport);
+
+#ifdef CONFIG_PRIVATE
+#ifdef TOR_UNIT_TESTS
+extern struct config_format_t options_format;
+#endif
+
+STATIC void or_options_free(or_options_t *options);
+STATIC int options_validate(or_options_t *old_options,
+ or_options_t *options,
+ or_options_t *default_options,
+ int from_setconf, char **msg);
+#endif
+
#endif
diff --git a/src/or/confparse.c b/src/or/confparse.c
index 8863d9240..c5400a651 100644
--- a/src/or/confparse.c
+++ b/src/or/confparse.c
@@ -79,6 +79,21 @@ config_line_append(config_line_t **lst,
(*lst) = newline;
}
+/** Return the line in <b>lines</b> whose key is exactly <b>key</b>, or NULL
+ * if no such key exists. For handling commandline-only options only; other
+ * options should be looked up in the appropriate data structure. */
+const config_line_t *
+config_line_find(const config_line_t *lines,
+ const char *key)
+{
+ const config_line_t *cl;
+ for (cl = lines; cl; cl = cl->next) {
+ if (!strcmp(cl->key, key))
+ return cl;
+ }
+ return NULL;
+}
+
/** Helper: parse the config string and strdup into key/value
* strings. Set *result to the list, or NULL if parsing the string
* failed. Return 0 on success, -1 on failure. Warn and ignore any
@@ -223,6 +238,8 @@ config_assign_value(const config_format_t *fmt, void *options,
int i, ok;
const config_var_t *var;
void *lvalue;
+ int *csv_int;
+ smartlist_t *csv_str;
CONFIG_CHECK(fmt, options);
@@ -357,6 +374,36 @@ config_assign_value(const config_format_t *fmt, void *options,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
break;
+ case CONFIG_TYPE_CSV_INTERVAL:
+ if (*(smartlist_t**)lvalue) {
+ SMARTLIST_FOREACH(*(smartlist_t**)lvalue, int *, cp, tor_free(cp));
+ smartlist_clear(*(smartlist_t**)lvalue);
+ } else {
+ *(smartlist_t**)lvalue = smartlist_new();
+ }
+ csv_str = smartlist_new();
+ smartlist_split_string(csv_str, c->value, ",",
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ SMARTLIST_FOREACH_BEGIN(csv_str, char *, str)
+ {
+ i = config_parse_interval(str, &ok);
+ if (!ok) {
+ tor_asprintf(msg,
+ "Interval in '%s %s' is malformed or out of bounds.",
+ c->key, c->value);
+ SMARTLIST_FOREACH(csv_str, char *, cp, tor_free(cp));
+ smartlist_free(csv_str);
+ return -1;
+ }
+ csv_int = tor_malloc_zero(sizeof(int));
+ *csv_int = i;
+ smartlist_add(*(smartlist_t**)lvalue, csv_int);
+ }
+ SMARTLIST_FOREACH_END(str);
+ SMARTLIST_FOREACH(csv_str, char *, cp, tor_free(cp));
+ smartlist_free(csv_str);
+ break;
+
case CONFIG_TYPE_LINELIST:
case CONFIG_TYPE_LINELIST_S:
{
@@ -555,6 +602,7 @@ config_get_assigned_option(const config_format_t *fmt, const void *options,
const config_var_t *var;
const void *value;
config_line_t *result;
+ smartlist_t *csv_str;
tor_assert(options && key);
CONFIG_CHECK(fmt, options);
@@ -637,6 +685,20 @@ config_get_assigned_option(const config_format_t *fmt, const void *options,
else
result->value = tor_strdup("");
break;
+ case CONFIG_TYPE_CSV_INTERVAL:
+ if (*(smartlist_t**)value) {
+ csv_str = smartlist_new();
+ SMARTLIST_FOREACH_BEGIN(*(smartlist_t**)value, int *, i)
+ {
+ smartlist_add_asprintf(csv_str, "%d", *i);
+ }
+ SMARTLIST_FOREACH_END(i);
+ result->value = smartlist_join_strings(csv_str, ",", 0, NULL);
+ SMARTLIST_FOREACH(csv_str, char *, cp, tor_free(cp));
+ smartlist_free(csv_str);
+ } else
+ result->value = tor_strdup("");
+ break;
case CONFIG_TYPE_OBSOLETE:
log_fn(LOG_INFO, LD_CONFIG,
"You asked me for the value of an obsolete config option '%s'.",
@@ -826,6 +888,13 @@ config_clear(const config_format_t *fmt, void *options,
*(smartlist_t **)lvalue = NULL;
}
break;
+ case CONFIG_TYPE_CSV_INTERVAL:
+ if (*(smartlist_t**)lvalue) {
+ SMARTLIST_FOREACH(*(smartlist_t **)lvalue, int *, cp, tor_free(cp));
+ smartlist_free(*(smartlist_t **)lvalue);
+ *(smartlist_t **)lvalue = NULL;
+ }
+ break;
case CONFIG_TYPE_LINELIST:
case CONFIG_TYPE_LINELIST_S:
config_free_lines(*(config_line_t **)lvalue);
@@ -1005,8 +1074,8 @@ config_dump(const config_format_t *fmt, const void *default_options,
/* XXX use a 1 here so we don't add a new log line while dumping */
if (default_options == NULL) {
- if (fmt->validate_fn(NULL, defaults_tmp, 1, &msg) < 0) {
- log_err(LD_BUG, "Failed to validate default config.");
+ if (fmt->validate_fn(NULL, defaults_tmp, defaults_tmp, 1, &msg) < 0) {
+ log_err(LD_BUG, "Failed to validate default config: %s", msg);
tor_free(msg);
tor_assert(0);
}
@@ -1072,20 +1141,36 @@ static struct unit_table_t memory_units[] = {
{ "kbytes", 1<<10 },
{ "kilobyte", 1<<10 },
{ "kilobytes", 1<<10 },
+ { "kilobits", 1<<7 },
+ { "kilobit", 1<<7 },
+ { "kbits", 1<<7 },
+ { "kbit", 1<<7 },
{ "m", 1<<20 },
{ "mb", 1<<20 },
{ "mbyte", 1<<20 },
{ "mbytes", 1<<20 },
{ "megabyte", 1<<20 },
{ "megabytes", 1<<20 },
+ { "megabits", 1<<17 },
+ { "megabit", 1<<17 },
+ { "mbits", 1<<17 },
+ { "mbit", 1<<17 },
{ "gb", 1<<30 },
{ "gbyte", 1<<30 },
{ "gbytes", 1<<30 },
{ "gigabyte", 1<<30 },
{ "gigabytes", 1<<30 },
+ { "gigabits", 1<<27 },
+ { "gigabit", 1<<27 },
+ { "gbits", 1<<27 },
+ { "gbit", 1<<27 },
{ "tb", U64_LITERAL(1)<<40 },
{ "terabyte", U64_LITERAL(1)<<40 },
{ "terabytes", U64_LITERAL(1)<<40 },
+ { "terabits", U64_LITERAL(1)<<37 },
+ { "terabit", U64_LITERAL(1)<<37 },
+ { "tbits", U64_LITERAL(1)<<37 },
+ { "tbit", U64_LITERAL(1)<<37 },
{ NULL, 0 },
};
diff --git a/src/or/confparse.h b/src/or/confparse.h
index 1b987f3bf..2cd6c49a2 100644
--- a/src/or/confparse.h
+++ b/src/or/confparse.h
@@ -26,6 +26,9 @@ typedef enum config_type_t {
CONFIG_TYPE_ISOTIME, /**< An ISO-formatted time relative to UTC. */
CONFIG_TYPE_CSV, /**< A list of strings, separated by commas and
* optional whitespace. */
+ CONFIG_TYPE_CSV_INTERVAL, /**< A list of strings, separated by commas and
+ * optional whitespace, representing intervals in
+ * seconds, with optional units */
CONFIG_TYPE_LINELIST, /**< Uninterpreted config lines */
CONFIG_TYPE_LINELIST_S, /**< Uninterpreted, context-sensitive config lines,
* mixed with other keywords. */
@@ -68,12 +71,12 @@ typedef struct config_var_description_t {
/** Type of a callback to validate whether a given configuration is
* well-formed and consistent. See options_trial_assign() for documentation
* of arguments. */
-typedef int (*validate_fn_t)(void*,void*,int,char**);
+typedef int (*validate_fn_t)(void*,void*,void*,int,char**);
/** Information on the keys, value types, key-to-struct-member mappings,
* variable descriptions, validation functions, and abbreviations for a
* configuration or storage format. */
-typedef struct {
+typedef struct config_format_t {
size_t size; /**< Size of the struct that everything gets parsed into. */
uint32_t magic; /**< Required 'magic value' to make sure we have a struct
* of the right type. */
@@ -100,6 +103,8 @@ void *config_new(const config_format_t *fmt);
void config_line_append(config_line_t **lst,
const char *key, const char *val);
config_line_t *config_lines_dup(const config_line_t *inp);
+const config_line_t *config_line_find(const config_line_t *lines,
+ const char *key);
void config_free(const config_format_t *fmt, void *options);
int config_lines_eq(config_line_t *a, config_line_t *b);
int config_count_key(const config_line_t *a, const char *key);
diff --git a/src/or/connection.c b/src/or/connection.c
index 78cc31e89..1f6e11fac 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -10,6 +10,7 @@
* on connections.
**/
+#define CONNECTION_PRIVATE
#include "or.h"
#include "buffers.h"
/*
@@ -17,6 +18,7 @@
* part of a subclass (channel_tls_t).
*/
#define TOR_CHANNEL_INTERNAL_
+#define CONNECTION_PRIVATE
#include "channel.h"
#include "channeltls.h"
#include "circuitbuild.h"
@@ -33,6 +35,7 @@
#include "dns.h"
#include "dnsserv.h"
#include "entrynodes.h"
+#include "ext_orport.h"
#include "geoip.h"
#include "main.h"
#include "policies.h"
@@ -44,6 +47,7 @@
#include "router.h"
#include "transports.h"
#include "routerparse.h"
+#include "transports.h"
#ifdef USE_BUFFEREVENTS
#include <event2/event.h>
@@ -97,6 +101,7 @@ static smartlist_t *outgoing_addrs = NULL;
#define CASE_ANY_LISTENER_TYPE \
case CONN_TYPE_OR_LISTENER: \
+ case CONN_TYPE_EXT_OR_LISTENER: \
case CONN_TYPE_AP_LISTENER: \
case CONN_TYPE_DIR_LISTENER: \
case CONN_TYPE_CONTROL_LISTENER: \
@@ -128,6 +133,8 @@ conn_type_to_string(int type)
case CONN_TYPE_CPUWORKER: return "CPU worker";
case CONN_TYPE_CONTROL_LISTENER: return "Control listener";
case CONN_TYPE_CONTROL: return "Control";
+ case CONN_TYPE_EXT_OR: return "Extended OR";
+ case CONN_TYPE_EXT_OR_LISTENER: return "Extended OR listener";
default:
log_warn(LD_BUG, "unknown connection type %d", type);
tor_snprintf(buf, sizeof(buf), "unknown [%d]", type);
@@ -164,6 +171,18 @@ conn_state_to_string(int type, int state)
case OR_CONN_STATE_OPEN: return "open";
}
break;
+ case CONN_TYPE_EXT_OR:
+ switch (state) {
+ case EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE:
+ return "waiting for authentication type";
+ case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE:
+ return "waiting for client nonce";
+ case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH:
+ return "waiting for client hash";
+ case EXT_OR_CONN_STATE_OPEN: return "open";
+ case EXT_OR_CONN_STATE_FLUSHING: return "flushing final OKAY";
+ }
+ break;
case CONN_TYPE_EXIT:
switch (state) {
case EXIT_CONN_STATE_RESOLVING: return "waiting for dest info";
@@ -228,6 +247,7 @@ connection_type_uses_bufferevent(connection_t *conn)
case CONN_TYPE_DIR:
case CONN_TYPE_CONTROL:
case CONN_TYPE_OR:
+ case CONN_TYPE_EXT_OR:
case CONN_TYPE_CPUWORKER:
return 1;
default:
@@ -258,14 +278,18 @@ dir_connection_new(int socket_family)
* Set active_circuit_pqueue_last_recalibrated to current cell_ewma tick.
*/
or_connection_t *
-or_connection_new(int socket_family)
+or_connection_new(int type, int socket_family)
{
or_connection_t *or_conn = tor_malloc_zero(sizeof(or_connection_t));
time_t now = time(NULL);
- connection_init(now, TO_CONN(or_conn), CONN_TYPE_OR, socket_family);
+ tor_assert(type == CONN_TYPE_OR || type == CONN_TYPE_EXT_OR);
+ connection_init(now, TO_CONN(or_conn), type, socket_family);
or_conn->timestamp_last_added_nonpadding = time(NULL);
+ if (type == CONN_TYPE_EXT_OR)
+ connection_or_set_ext_or_identifier(or_conn);
+
return or_conn;
}
@@ -311,7 +335,6 @@ control_connection_new(int socket_family)
tor_malloc_zero(sizeof(control_connection_t));
connection_init(time(NULL),
TO_CONN(control_conn), CONN_TYPE_CONTROL, socket_family);
- log_notice(LD_CONTROL, "New control connection opened.");
return control_conn;
}
@@ -334,7 +357,8 @@ connection_new(int type, int socket_family)
{
switch (type) {
case CONN_TYPE_OR:
- return TO_CONN(or_connection_new(socket_family));
+ case CONN_TYPE_EXT_OR:
+ return TO_CONN(or_connection_new(type, socket_family));
case CONN_TYPE_EXIT:
return TO_CONN(edge_connection_new(type, socket_family));
@@ -376,6 +400,7 @@ connection_init(time_t now, connection_t *conn, int type, int socket_family)
switch (type) {
case CONN_TYPE_OR:
+ case CONN_TYPE_EXT_OR:
conn->magic = OR_CONNECTION_MAGIC;
break;
case CONN_TYPE_EXIT:
@@ -434,7 +459,7 @@ connection_link_connections(connection_t *conn_a, connection_t *conn_b)
* necessary, close its socket if necessary, and mark the directory as dirty
* if <b>conn</b> is an OR or OP connection.
*/
-static void
+STATIC void
connection_free_(connection_t *conn)
{
void *mem;
@@ -444,6 +469,7 @@ connection_free_(connection_t *conn)
switch (conn->type) {
case CONN_TYPE_OR:
+ case CONN_TYPE_EXT_OR:
tor_assert(conn->magic == OR_CONNECTION_MAGIC);
mem = TO_OR_CONN(conn);
memlen = sizeof(or_connection_t);
@@ -574,6 +600,13 @@ connection_free_(connection_t *conn)
log_warn(LD_BUG, "called on OR conn with non-zeroed identity_digest");
connection_or_remove_from_identity_map(TO_OR_CONN(conn));
}
+ if (conn->type == CONN_TYPE_OR || conn->type == CONN_TYPE_EXT_OR) {
+ connection_or_remove_from_ext_or_id_map(TO_OR_CONN(conn));
+ tor_free(TO_OR_CONN(conn)->ext_or_conn_id);
+ tor_free(TO_OR_CONN(conn)->ext_or_auth_correct_client_hash);
+ tor_free(TO_OR_CONN(conn)->ext_or_transport);
+ }
+
#ifdef USE_BUFFEREVENTS
if (conn->type == CONN_TYPE_OR && TO_OR_CONN(conn)->bucket_cfg) {
ev_token_bucket_cfg_free(TO_OR_CONN(conn)->bucket_cfg);
@@ -637,6 +670,7 @@ connection_about_to_close_connection(connection_t *conn)
connection_dir_about_to_close(TO_DIR_CONN(conn));
break;
case CONN_TYPE_OR:
+ case CONN_TYPE_EXT_OR:
connection_or_about_to_close(TO_OR_CONN(conn));
break;
case CONN_TYPE_AP:
@@ -876,8 +910,11 @@ check_location_for_unix_socket(const or_options_t *options, const char *path)
int r = -1;
char *p = tor_strdup(path);
cpd_check_t flags = CPD_CHECK_MODE_ONLY;
- if (get_parent_directory(p)<0)
+ if (get_parent_directory(p)<0 || p[0] != '/') {
+ log_warn(LD_GENERAL, "Bad unix socket address '%s'. Tor does not support "
+ "relative paths for unix sockets.", path);
goto done;
+ }
if (options->ControlSocketsGroupWritable)
flags |= CPD_GROUP_OK;
@@ -926,6 +963,27 @@ make_socket_reuseable(tor_socket_t sock)
#endif
}
+/** Max backlog to pass to listen. We start at */
+static int listen_limit = INT_MAX;
+
+/* Listen on <b>fd</b> with appropriate backlog. Return as for listen. */
+static int
+tor_listen(tor_socket_t fd)
+{
+ int r;
+
+ if ((r = listen(fd, listen_limit)) < 0) {
+ if (listen_limit == SOMAXCONN)
+ return r;
+ if ((r = listen(fd, SOMAXCONN)) == 0) {
+ listen_limit = SOMAXCONN;
+ log_warn(LD_NET, "Setting listen backlog to INT_MAX connections "
+ "didn't work, but SOMAXCONN did. Lowering backlog limit.");
+ }
+ }
+ return r;
+}
+
/** Bind a new non-blocking socket listening to the socket described
* by <b>listensockaddr</b>.
*
@@ -939,8 +997,8 @@ connection_listener_new(const struct sockaddr *listensockaddr,
const port_cfg_t *port_cfg)
{
listener_connection_t *lis_conn;
- connection_t *conn;
- tor_socket_t s; /* the socket we're going to make */
+ connection_t *conn = NULL;
+ tor_socket_t s = TOR_INVALID_SOCKET; /* the socket we're going to make */
or_options_t const *options = get_options();
#if defined(HAVE_PWD_H) && defined(HAVE_SYS_UN_H)
struct passwd *pw = NULL;
@@ -966,7 +1024,7 @@ connection_listener_new(const struct sockaddr *listensockaddr,
log_notice(LD_NET, "Opening %s on %s",
conn_type_to_string(type), fmt_addrport(&addr, usePort));
- s = tor_open_socket(tor_addr_family(&addr),
+ s = tor_open_socket_nonblocking(tor_addr_family(&addr),
is_tcp ? SOCK_STREAM : SOCK_DGRAM,
is_tcp ? IPPROTO_TCP: IPPROTO_UDP);
if (!SOCKET_OK(s)) {
@@ -988,7 +1046,7 @@ connection_listener_new(const struct sockaddr *listensockaddr,
/* We need to set IPV6_V6ONLY so that this socket can't get used for
* IPv4 connections. */
if (setsockopt(s,IPPROTO_IPV6, IPV6_V6ONLY,
- (void*)&one, sizeof(one))<0) {
+ (void*)&one, sizeof(one)) < 0) {
int e = tor_socket_errno(s);
log_warn(LD_NET, "Error setting IPV6_V6ONLY flag: %s",
tor_socket_strerror(e));
@@ -1004,15 +1062,13 @@ connection_listener_new(const struct sockaddr *listensockaddr,
helpfulhint = ". Is Tor already running?";
log_warn(LD_NET, "Could not bind to %s:%u: %s%s", address, usePort,
tor_socket_strerror(e), helpfulhint);
- tor_close_socket(s);
goto err;
}
if (is_tcp) {
- if (listen(s,SOMAXCONN) < 0) {
+ if (tor_listen(s) < 0) {
log_warn(LD_NET, "Could not listen on %s:%u: %s", address, usePort,
tor_socket_strerror(tor_socket_errno(s)));
- tor_close_socket(s);
goto err;
}
}
@@ -1052,7 +1108,7 @@ connection_listener_new(const struct sockaddr *listensockaddr,
strerror(errno));
goto err;
}
- s = tor_open_socket(AF_UNIX, SOCK_STREAM, 0);
+ s = tor_open_socket_nonblocking(AF_UNIX, SOCK_STREAM, 0);
if (! SOCKET_OK(s)) {
log_warn(LD_NET,"Socket creation failed: %s.", strerror(errno));
goto err;
@@ -1061,7 +1117,6 @@ connection_listener_new(const struct sockaddr *listensockaddr,
if (bind(s, listensockaddr, (socklen_t)sizeof(struct sockaddr_un)) == -1) {
log_warn(LD_NET,"Bind to %s failed: %s.", address,
tor_socket_strerror(tor_socket_errno(s)));
- tor_close_socket(s);
goto err;
}
#ifdef HAVE_PWD_H
@@ -1070,12 +1125,10 @@ connection_listener_new(const struct sockaddr *listensockaddr,
if (pw == NULL) {
log_warn(LD_NET,"Unable to chown() %s socket: user %s not found.",
address, options->User);
- tor_close_socket(s);
goto err;
} else if (chown(address, pw->pw_uid, pw->pw_gid) < 0) {
log_warn(LD_NET,"Unable to chown() %s socket: %s.",
address, strerror(errno));
- tor_close_socket(s);
goto err;
}
}
@@ -1085,35 +1138,29 @@ connection_listener_new(const struct sockaddr *listensockaddr,
* platforms. */
if (chmod(address, 0660) < 0) {
log_warn(LD_FS,"Unable to make %s group-writable.", address);
- tor_close_socket(s);
goto err;
}
}
- if (listen(s,SOMAXCONN) < 0) {
+ if (listen(s, SOMAXCONN) < 0) {
log_warn(LD_NET, "Could not listen on %s: %s", address,
tor_socket_strerror(tor_socket_errno(s)));
- tor_close_socket(s);
goto err;
}
#else
(void)options;
#endif /* HAVE_SYS_UN_H */
} else {
- log_err(LD_BUG,"Got unexpected address family %d.",
- listensockaddr->sa_family);
- tor_assert(0);
- }
-
- if (set_socket_nonblocking(s) == -1) {
- tor_close_socket(s);
- goto err;
+ log_err(LD_BUG, "Got unexpected address family %d.",
+ listensockaddr->sa_family);
+ tor_assert(0);
}
lis_conn = listener_connection_new(type, listensockaddr->sa_family);
conn = TO_CONN(lis_conn);
conn->socket_family = listensockaddr->sa_family;
conn->s = s;
+ s = TOR_INVALID_SOCKET; /* Prevent double-close */
conn->address = tor_strdup(address);
conn->port = gotPort;
tor_addr_copy(&conn->addr, &addr);
@@ -1149,7 +1196,6 @@ connection_listener_new(const struct sockaddr *listensockaddr,
if (connection_add(conn) < 0) { /* no space, forget it */
log_warn(LD_NET,"connection_add for listener failed. Giving up.");
- connection_free(conn);
goto err;
}
@@ -1168,6 +1214,11 @@ connection_listener_new(const struct sockaddr *listensockaddr,
return conn;
err:
+ if (SOCKET_OK(s))
+ tor_close_socket(s);
+ if (conn)
+ connection_free(conn);
+
return NULL;
}
@@ -1252,7 +1303,7 @@ connection_handle_listener_read(connection_t *conn, int new_type)
tor_assert((size_t)remotelen >= sizeof(struct sockaddr_in));
memset(&addrbuf, 0, sizeof(addrbuf));
- news = tor_accept_socket(conn->s,remote,&remotelen);
+ news = tor_accept_socket_nonblocking(conn->s,remote,&remotelen);
if (!SOCKET_OK(news)) { /* accept() error */
int e = tor_socket_errno(conn->s);
if (ERRNO_IS_ACCEPT_EAGAIN(e)) {
@@ -1272,10 +1323,6 @@ connection_handle_listener_read(connection_t *conn, int new_type)
(int)news,(int)conn->s);
make_socket_reuseable(news);
- if (set_socket_nonblocking(news) == -1) {
- tor_close_socket(news);
- return 0;
- }
if (options->ConstrainedSockets)
set_constrained_socket_buffers(news, (int)options->ConstrainedSockSize);
@@ -1330,11 +1377,17 @@ connection_handle_listener_read(connection_t *conn, int new_type)
TO_ENTRY_CONN(newconn)->socks_request->socks_prefer_no_auth =
TO_LISTENER_CONN(conn)->socks_prefer_no_auth;
}
+ if (new_type == CONN_TYPE_CONTROL) {
+ log_notice(LD_CONTROL, "New control connection opened from %s.",
+ fmt_and_decorate_addr(&addr));
+ }
} else if (conn->socket_family == AF_UNIX) {
/* For now only control ports can be Unix domain sockets
* and listeners at the same time */
tor_assert(conn->type == CONN_TYPE_CONTROL_LISTENER);
+ tor_assert(new_type == CONN_TYPE_CONTROL);
+ log_notice(LD_CONTROL, "New control connection opened.");
newconn = connection_new(new_type, conn->socket_family);
newconn->s = news;
@@ -1374,6 +1427,9 @@ connection_init_accepted_conn(connection_t *conn,
connection_start_reading(conn);
switch (conn->type) {
+ case CONN_TYPE_EXT_OR:
+ /* Initiate Extended ORPort authentication. */
+ return connection_ext_or_start_auth(TO_OR_CONN(conn));
case CONN_TYPE_OR:
control_event_or_conn_status(TO_OR_CONN(conn), OR_CONN_EVENT_NEW, 0);
rv = connection_tls_start_handshake(TO_OR_CONN(conn), 1);
@@ -1467,7 +1523,7 @@ connection_connect(connection_t *conn, const char *address,
return -1;
}
- s = tor_open_socket(protocol_family,SOCK_STREAM,IPPROTO_TCP);
+ s = tor_open_socket_nonblocking(protocol_family,SOCK_STREAM,IPPROTO_TCP);
if (! SOCKET_OK(s)) {
*socket_error = tor_socket_errno(-1);
log_warn(LD_NET,"Error creating network socket: %s",
@@ -1509,12 +1565,6 @@ connection_connect(connection_t *conn, const char *address,
}
}
- if (set_socket_nonblocking(s) == -1) {
- *socket_error = tor_socket_errno(s);
- tor_close_socket(s);
- return -1;
- }
-
if (options->ConstrainedSockets)
set_constrained_socket_buffers(s, (int)options->ConstrainedSockSize);
@@ -1580,6 +1630,32 @@ connection_proxy_state_to_string(int state)
return states[state];
}
+/** Returns the global proxy type used by tor. Use this function for
+ * logging or high-level purposes, don't use it to fill the
+ * <b>proxy_type</b> field of or_connection_t; use the actual proxy
+ * protocol instead.*/
+static int
+get_proxy_type(void)
+{
+ const or_options_t *options = get_options();
+
+ if (options->HTTPSProxy)
+ return PROXY_CONNECT;
+ else if (options->Socks4Proxy)
+ return PROXY_SOCKS4;
+ else if (options->Socks5Proxy)
+ return PROXY_SOCKS5;
+ else if (options->ClientTransportPlugin)
+ return PROXY_PLUGGABLE;
+ else
+ return PROXY_NONE;
+}
+
+/* One byte for the version, one for the command, two for the
+ port, and four for the addr... and, one more for the
+ username NUL: */
+#define SOCKS4_STANDARD_BUFFER_SIZE (1 + 1 + 2 + 4 + 1)
+
/** Write a proxy request of <b>type</b> (socks4, socks5, https) to conn
* for conn->addr:conn->port, authenticating with the auth details given
* in the configuration (if available). SOCKS 5 and HTTP CONNECT proxies
@@ -1634,17 +1710,45 @@ connection_proxy_connect(connection_t *conn, int type)
}
case PROXY_SOCKS4: {
- unsigned char buf[9];
+ unsigned char *buf;
uint16_t portn;
uint32_t ip4addr;
+ size_t buf_size = 0;
+ char *socks_args_string = NULL;
- /* Send a SOCKS4 connect request with empty user id */
+ /* Send a SOCKS4 connect request */
if (tor_addr_family(&conn->addr) != AF_INET) {
log_warn(LD_NET, "SOCKS4 client is incompatible with IPv6");
return -1;
}
+ { /* If we are here because we are trying to connect to a
+ pluggable transport proxy, check if we have any SOCKS
+ arguments to transmit. If we do, compress all arguments to
+ a single string in 'socks_args_string': */
+
+ if (get_proxy_type() == PROXY_PLUGGABLE) {
+ socks_args_string =
+ pt_get_socks_args_for_proxy_addrport(&conn->addr, conn->port);
+ if (socks_args_string)
+ log_debug(LD_NET, "Sending out '%s' as our SOCKS argument string.",
+ socks_args_string);
+ }
+ }
+
+ { /* Figure out the buffer size we need for the SOCKS message: */
+
+ buf_size = SOCKS4_STANDARD_BUFFER_SIZE;
+
+ /* If we have a SOCKS argument string, consider its size when
+ calculating the buffer size: */
+ if (socks_args_string)
+ buf_size += strlen(socks_args_string);
+ }
+
+ buf = tor_malloc_zero(buf_size);
+
ip4addr = tor_addr_to_ipv4n(&conn->addr);
portn = htons(conn->port);
@@ -1652,9 +1756,23 @@ connection_proxy_connect(connection_t *conn, int type)
buf[1] = SOCKS_COMMAND_CONNECT; /* command */
memcpy(buf + 2, &portn, 2); /* port */
memcpy(buf + 4, &ip4addr, 4); /* addr */
- buf[8] = 0; /* userid (empty) */
- connection_write_to_buf((char *)buf, sizeof(buf), conn);
+ /* Next packet field is the userid. If we have pluggable
+ transport SOCKS arguments, we have to embed them
+ there. Otherwise, we use an empty userid. */
+ if (socks_args_string) { /* place the SOCKS args string: */
+ tor_assert(strlen(socks_args_string) > 0);
+ tor_assert(buf_size >=
+ SOCKS4_STANDARD_BUFFER_SIZE + strlen(socks_args_string));
+ strlcpy((char *)buf + 8, socks_args_string, buf_size - 8);
+ tor_free(socks_args_string);
+ } else {
+ buf[8] = 0; /* no userid */
+ }
+
+ connection_write_to_buf((char *)buf, buf_size, conn);
+ tor_free(buf);
+
conn->proxy_state = PROXY_SOCKS4_WANT_CONNECT_OK;
break;
}
@@ -1666,8 +1784,13 @@ connection_proxy_connect(connection_t *conn, int type)
buf[0] = 5; /* version */
+ /* We have to use SOCKS5 authentication, if we have a
+ Socks5ProxyUsername or if we want to pass arguments to our
+ pluggable transport proxy: */
+ if ((options->Socks5ProxyUsername) ||
+ (get_proxy_type() == PROXY_PLUGGABLE &&
+ (get_socks_args_by_bridge_addrport(&conn->addr, conn->port)))) {
/* number of auth methods */
- if (options->Socks5ProxyUsername) {
buf[1] = 2;
buf[2] = 0x00; /* no authentication */
buf[3] = 0x02; /* rfc1929 Username/Passwd auth */
@@ -1861,15 +1984,49 @@ connection_read_proxy_handshake(connection_t *conn)
unsigned char buf[1024];
size_t reqsize, usize, psize;
const char *user, *pass;
+ char *socks_args_string = NULL;
+
+ if (get_proxy_type() == PROXY_PLUGGABLE) {
+ socks_args_string =
+ pt_get_socks_args_for_proxy_addrport(&conn->addr, conn->port);
+ if (!socks_args_string) {
+ log_warn(LD_NET, "Could not create SOCKS args string.");
+ ret = -1;
+ break;
+ }
+
+ log_debug(LD_NET, "SOCKS5 arguments: %s", socks_args_string);
+ tor_assert(strlen(socks_args_string) > 0);
+ tor_assert(strlen(socks_args_string) <= MAX_SOCKS5_AUTH_SIZE_TOTAL);
+
+ if (strlen(socks_args_string) > MAX_SOCKS5_AUTH_FIELD_SIZE) {
+ user = socks_args_string;
+ usize = MAX_SOCKS5_AUTH_FIELD_SIZE;
+ pass = socks_args_string + MAX_SOCKS5_AUTH_FIELD_SIZE;
+ psize = strlen(socks_args_string) - MAX_SOCKS5_AUTH_FIELD_SIZE;
+ } else {
+ user = socks_args_string;
+ usize = strlen(socks_args_string);
+ pass = "\0";
+ psize = 1;
+ }
+ } else if (get_options()->Socks5ProxyUsername) {
+ user = get_options()->Socks5ProxyUsername;
+ pass = get_options()->Socks5ProxyPassword;
+ tor_assert(user && pass);
+ usize = strlen(user);
+ psize = strlen(pass);
+ } else {
+ log_err(LD_BUG, "We entered %s for no reason!", __func__);
+ tor_fragile_assert();
+ ret = -1;
+ break;
+ }
- user = get_options()->Socks5ProxyUsername;
- pass = get_options()->Socks5ProxyPassword;
- tor_assert(user && pass);
-
- /* XXX len of user and pass must be <= 255 !!! */
- usize = strlen(user);
- psize = strlen(pass);
- tor_assert(usize <= 255 && psize <= 255);
+ /* Username and password lengths should have been checked
+ above and during torrc parsing. */
+ tor_assert(usize <= MAX_SOCKS5_AUTH_FIELD_SIZE &&
+ psize <= MAX_SOCKS5_AUTH_FIELD_SIZE);
reqsize = 3 + usize + psize;
buf[0] = 1; /* negotiation version */
@@ -1878,6 +2035,9 @@ connection_read_proxy_handshake(connection_t *conn)
buf[2 + usize] = psize;
memcpy(buf + 3 + usize, pass, psize);
+ if (socks_args_string)
+ tor_free(socks_args_string);
+
connection_write_to_buf((char *)buf, reqsize, conn);
conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_RFC1929_OK;
@@ -2430,6 +2590,35 @@ record_num_bytes_transferred(connection_t *conn,
#endif
#ifndef USE_BUFFEREVENTS
+/** Last time at which the global or relay buckets were emptied in msec
+ * since midnight. */
+static uint32_t global_relayed_read_emptied = 0,
+ global_relayed_write_emptied = 0,
+ global_read_emptied = 0,
+ global_write_emptied = 0;
+
+/** Helper: convert given <b>tvnow</b> time value to milliseconds since
+ * midnight. */
+static uint32_t
+msec_since_midnight(const struct timeval *tvnow)
+{
+ return (uint32_t)(((tvnow->tv_sec % 86400L) * 1000L) +
+ ((uint32_t)tvnow->tv_usec / (uint32_t)1000L));
+}
+
+/** Check if a bucket which had <b>tokens_before</b> tokens and which got
+ * <b>tokens_removed</b> tokens removed at timestamp <b>tvnow</b> has run
+ * out of tokens, and if so, note the milliseconds since midnight in
+ * <b>timestamp_var</b> for the next TB_EMPTY event. */
+void
+connection_buckets_note_empty_ts(uint32_t *timestamp_var,
+ int tokens_before, size_t tokens_removed,
+ const struct timeval *tvnow)
+{
+ if (tokens_before > 0 && (uint32_t)tokens_before <= tokens_removed)
+ *timestamp_var = msec_since_midnight(tvnow);
+}
+
/** We just read <b>num_read</b> and wrote <b>num_written</b> bytes
* onto <b>conn</b>. Decrement buckets appropriately. */
static void
@@ -2452,6 +2641,30 @@ connection_buckets_decrement(connection_t *conn, time_t now,
if (!connection_is_rate_limited(conn))
return; /* local IPs are free */
+ /* If one or more of our token buckets ran dry just now, note the
+ * timestamp for TB_EMPTY events. */
+ if (get_options()->TestingEnableTbEmptyEvent) {
+ struct timeval tvnow;
+ tor_gettimeofday_cached(&tvnow);
+ if (connection_counts_as_relayed_traffic(conn, now)) {
+ connection_buckets_note_empty_ts(&global_relayed_read_emptied,
+ global_relayed_read_bucket, num_read, &tvnow);
+ connection_buckets_note_empty_ts(&global_relayed_write_emptied,
+ global_relayed_write_bucket, num_written, &tvnow);
+ }
+ connection_buckets_note_empty_ts(&global_read_emptied,
+ global_read_bucket, num_read, &tvnow);
+ connection_buckets_note_empty_ts(&global_write_emptied,
+ global_write_bucket, num_written, &tvnow);
+ if (connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN) {
+ or_connection_t *or_conn = TO_OR_CONN(conn);
+ connection_buckets_note_empty_ts(&or_conn->read_emptied_time,
+ or_conn->read_bucket, num_read, &tvnow);
+ connection_buckets_note_empty_ts(&or_conn->write_emptied_time,
+ or_conn->write_bucket, num_written, &tvnow);
+ }
+ }
+
if (connection_counts_as_relayed_traffic(conn, now)) {
global_relayed_read_bucket -= (int)num_read;
global_relayed_write_bucket -= (int)num_written;
@@ -2471,6 +2684,9 @@ connection_consider_empty_read_buckets(connection_t *conn)
{
const char *reason;
+ if (!connection_is_rate_limited(conn))
+ return; /* Always okay. */
+
if (global_read_bucket <= 0) {
reason = "global read bucket exhausted. Pausing.";
} else if (connection_counts_as_relayed_traffic(conn, approx_time()) &&
@@ -2483,9 +2699,6 @@ connection_consider_empty_read_buckets(connection_t *conn)
} else
return; /* all good, no need to stop it */
- if (conn->type == CONN_TYPE_CPUWORKER)
- return; /* Always okay. */
-
LOG_FN_CONN(conn, (LOG_DEBUG, LD_NET, "%s", reason));
conn->read_blocked_on_bw = 1;
connection_stop_reading(conn);
@@ -2498,6 +2711,9 @@ connection_consider_empty_write_buckets(connection_t *conn)
{
const char *reason;
+ if (!connection_is_rate_limited(conn))
+ return; /* Always okay. */
+
if (global_write_bucket <= 0) {
reason = "global write bucket exhausted. Pausing.";
} else if (connection_counts_as_relayed_traffic(conn, approx_time()) &&
@@ -2510,9 +2726,6 @@ connection_consider_empty_write_buckets(connection_t *conn)
} else
return; /* all good, no need to stop it */
- if (conn->type == CONN_TYPE_CPUWORKER)
- return; /* Always okay. */
-
LOG_FN_CONN(conn, (LOG_DEBUG, LD_NET, "%s", reason));
conn->write_blocked_on_bw = 1;
connection_stop_writing(conn);
@@ -2564,6 +2777,28 @@ connection_bucket_refill_helper(int *bucket, int rate, int burst,
}
}
+/** Helper: return the time in milliseconds since <b>last_empty_time</b>
+ * when a bucket ran empty that previously had <b>tokens_before</b> tokens
+ * now has <b>tokens_after</b> tokens after refilling at timestamp
+ * <b>tvnow</b>, capped at <b>milliseconds_elapsed</b> milliseconds since
+ * last refilling that bucket. Return 0 if the bucket has not been empty
+ * since the last refill or has not been refilled. */
+uint32_t
+bucket_millis_empty(int tokens_before, uint32_t last_empty_time,
+ int tokens_after, int milliseconds_elapsed,
+ const struct timeval *tvnow)
+{
+ uint32_t result = 0, refilled;
+ if (tokens_before <= 0 && tokens_after > tokens_before) {
+ refilled = msec_since_midnight(tvnow);
+ result = (uint32_t)((refilled + 86400L * 1000L - last_empty_time) %
+ (86400L * 1000L));
+ if (result > (uint32_t)milliseconds_elapsed)
+ result = (uint32_t)milliseconds_elapsed;
+ }
+ return result;
+}
+
/** Time has passed; increment buckets appropriately. */
void
connection_bucket_refill(int milliseconds_elapsed, time_t now)
@@ -2572,6 +2807,12 @@ connection_bucket_refill(int milliseconds_elapsed, time_t now)
smartlist_t *conns = get_connection_array();
int bandwidthrate, bandwidthburst, relayrate, relayburst;
+ int prev_global_read = global_read_bucket;
+ int prev_global_write = global_write_bucket;
+ int prev_relay_read = global_relayed_read_bucket;
+ int prev_relay_write = global_relayed_write_bucket;
+ struct timeval tvnow; /*< Only used if TB_EMPTY events are enabled. */
+
bandwidthrate = (int)options->BandwidthRate;
bandwidthburst = (int)options->BandwidthBurst;
@@ -2606,12 +2847,42 @@ connection_bucket_refill(int milliseconds_elapsed, time_t now)
milliseconds_elapsed,
"global_relayed_write_bucket");
+ /* If buckets were empty before and have now been refilled, tell any
+ * interested controllers. */
+ if (get_options()->TestingEnableTbEmptyEvent) {
+ uint32_t global_read_empty_time, global_write_empty_time,
+ relay_read_empty_time, relay_write_empty_time;
+ tor_gettimeofday_cached(&tvnow);
+ global_read_empty_time = bucket_millis_empty(prev_global_read,
+ global_read_emptied, global_read_bucket,
+ milliseconds_elapsed, &tvnow);
+ global_write_empty_time = bucket_millis_empty(prev_global_write,
+ global_write_emptied, global_write_bucket,
+ milliseconds_elapsed, &tvnow);
+ control_event_tb_empty("GLOBAL", global_read_empty_time,
+ global_write_empty_time, milliseconds_elapsed);
+ relay_read_empty_time = bucket_millis_empty(prev_relay_read,
+ global_relayed_read_emptied,
+ global_relayed_read_bucket,
+ milliseconds_elapsed, &tvnow);
+ relay_write_empty_time = bucket_millis_empty(prev_relay_write,
+ global_relayed_write_emptied,
+ global_relayed_write_bucket,
+ milliseconds_elapsed, &tvnow);
+ control_event_tb_empty("RELAY", relay_read_empty_time,
+ relay_write_empty_time, milliseconds_elapsed);
+ }
+
/* refill the per-connection buckets */
SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
if (connection_speaks_cells(conn)) {
or_connection_t *or_conn = TO_OR_CONN(conn);
int orbandwidthrate = or_conn->bandwidthrate;
int orbandwidthburst = or_conn->bandwidthburst;
+
+ int prev_conn_read = or_conn->read_bucket;
+ int prev_conn_write = or_conn->write_bucket;
+
if (connection_bucket_should_increase(or_conn->read_bucket, or_conn)) {
connection_bucket_refill_helper(&or_conn->read_bucket,
orbandwidthrate,
@@ -2626,6 +2897,27 @@ connection_bucket_refill(int milliseconds_elapsed, time_t now)
milliseconds_elapsed,
"or_conn->write_bucket");
}
+
+ /* If buckets were empty before and have now been refilled, tell any
+ * interested controllers. */
+ if (get_options()->TestingEnableTbEmptyEvent) {
+ char *bucket;
+ uint32_t conn_read_empty_time, conn_write_empty_time;
+ tor_asprintf(&bucket, "ORCONN ID="U64_FORMAT,
+ U64_PRINTF_ARG(or_conn->base_.global_identifier));
+ conn_read_empty_time = bucket_millis_empty(prev_conn_read,
+ or_conn->read_emptied_time,
+ or_conn->read_bucket,
+ milliseconds_elapsed, &tvnow);
+ conn_write_empty_time = bucket_millis_empty(prev_conn_write,
+ or_conn->write_emptied_time,
+ or_conn->write_bucket,
+ milliseconds_elapsed, &tvnow);
+ control_event_tb_empty(bucket, conn_read_empty_time,
+ conn_write_empty_time,
+ milliseconds_elapsed);
+ tor_free(bucket);
+ }
}
if (conn->read_blocked_on_bw == 1 /* marked to turn reading back on now */
@@ -2782,6 +3074,8 @@ connection_handle_read_impl(connection_t *conn)
switch (conn->type) {
case CONN_TYPE_OR_LISTENER:
return connection_handle_listener_read(conn, CONN_TYPE_OR);
+ case CONN_TYPE_EXT_OR_LISTENER:
+ return connection_handle_listener_read(conn, CONN_TYPE_EXT_OR);
case CONN_TYPE_AP_LISTENER:
case CONN_TYPE_AP_TRANS_LISTENER:
case CONN_TYPE_AP_NATD_LISTENER:
@@ -3034,14 +3328,37 @@ connection_read_to_buf(connection_t *conn, ssize_t *max_to_read,
/* change *max_to_read */
*max_to_read = at_most - n_read;
- /* Update edge_conn->n_read */
+ /* Update edge_conn->n_read and ocirc->n_read_circ_bw */
if (conn->type == CONN_TYPE_AP) {
edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
+ circuit_t *circ = circuit_get_by_edge_conn(edge_conn);
+ origin_circuit_t *ocirc;
+
/* Check for overflow: */
if (PREDICT_LIKELY(UINT32_MAX - edge_conn->n_read > n_read))
edge_conn->n_read += (int)n_read;
else
edge_conn->n_read = UINT32_MAX;
+
+ if (circ && CIRCUIT_IS_ORIGIN(circ)) {
+ ocirc = TO_ORIGIN_CIRCUIT(circ);
+ if (PREDICT_LIKELY(UINT32_MAX - ocirc->n_read_circ_bw > n_read))
+ ocirc->n_read_circ_bw += (int)n_read;
+ else
+ ocirc->n_read_circ_bw = UINT32_MAX;
+ }
+ }
+
+ /* If CONN_BW events are enabled, update conn->n_read_conn_bw for
+ * OR/DIR/EXIT connections, checking for overflow. */
+ if (get_options()->TestingEnableConnBwEvent &&
+ (conn->type == CONN_TYPE_OR ||
+ conn->type == CONN_TYPE_DIR ||
+ conn->type == CONN_TYPE_EXIT)) {
+ if (PREDICT_LIKELY(UINT32_MAX - conn->n_read_conn_bw > n_read))
+ conn->n_read_conn_bw += (int)n_read;
+ else
+ conn->n_read_conn_bw = UINT32_MAX;
}
}
@@ -3294,8 +3611,8 @@ connection_outbuf_too_full(connection_t *conn)
/** Try to flush more bytes onto <b>conn</b>-\>s.
*
- * This function gets called either from conn_write() in main.c
- * when poll() has declared that conn wants to write, or below
+ * This function gets called either from conn_write_callback() in main.c
+ * when libevent tells us that conn wants to write, or below
* from connection_write_to_buf() when an entire TLS record is ready.
*
* Update <b>conn</b>-\>timestamp_lastwritten to now, and call flush_buf
@@ -3481,12 +3798,34 @@ connection_handle_write_impl(connection_t *conn, int force)
if (n_written && conn->type == CONN_TYPE_AP) {
edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
+ circuit_t *circ = circuit_get_by_edge_conn(edge_conn);
+ origin_circuit_t *ocirc;
/* Check for overflow: */
if (PREDICT_LIKELY(UINT32_MAX - edge_conn->n_written > n_written))
edge_conn->n_written += (int)n_written;
else
edge_conn->n_written = UINT32_MAX;
+
+ if (circ && CIRCUIT_IS_ORIGIN(circ)) {
+ ocirc = TO_ORIGIN_CIRCUIT(circ);
+ if (PREDICT_LIKELY(UINT32_MAX - ocirc->n_written_circ_bw > n_written))
+ ocirc->n_written_circ_bw += (int)n_written;
+ else
+ ocirc->n_written_circ_bw = UINT32_MAX;
+ }
+ }
+
+ /* If CONN_BW events are enabled, update conn->n_written_conn_bw for
+ * OR/DIR/EXIT connections, checking for overflow. */
+ if (n_written && get_options()->TestingEnableConnBwEvent &&
+ (conn->type == CONN_TYPE_OR ||
+ conn->type == CONN_TYPE_DIR ||
+ conn->type == CONN_TYPE_EXIT)) {
+ if (PREDICT_LIKELY(UINT32_MAX - conn->n_written_conn_bw > n_written))
+ conn->n_written_conn_bw += (int)n_written;
+ else
+ conn->n_written_conn_bw = UINT32_MAX;
}
connection_buckets_decrement(conn, approx_time(), n_read, n_written);
@@ -3572,9 +3911,9 @@ connection_flush(connection_t *conn)
* it all, so we don't end up with many megabytes of controller info queued at
* once.
*/
-void
-connection_write_to_buf_impl_(const char *string, size_t len,
- connection_t *conn, int zlib)
+MOCK_IMPL(void,
+connection_write_to_buf_impl_,(const char *string, size_t len,
+ connection_t *conn, int zlib))
{
/* XXXX This function really needs to return -1 on failure. */
int r;
@@ -3814,6 +4153,7 @@ int
connection_is_listener(connection_t *conn)
{
if (conn->type == CONN_TYPE_OR_LISTENER ||
+ conn->type == CONN_TYPE_EXT_OR_LISTENER ||
conn->type == CONN_TYPE_AP_LISTENER ||
conn->type == CONN_TYPE_AP_TRANS_LISTENER ||
conn->type == CONN_TYPE_AP_DNS_LISTENER ||
@@ -3836,6 +4176,7 @@ connection_state_is_open(connection_t *conn)
return 0;
if ((conn->type == CONN_TYPE_OR && conn->state == OR_CONN_STATE_OPEN) ||
+ (conn->type == CONN_TYPE_EXT_OR) ||
(conn->type == CONN_TYPE_AP && conn->state == AP_CONN_STATE_OPEN) ||
(conn->type == CONN_TYPE_EXIT && conn->state == EXIT_CONN_STATE_OPEN) ||
(conn->type == CONN_TYPE_CONTROL &&
@@ -4005,6 +4346,8 @@ connection_process_inbuf(connection_t *conn, int package_partial)
switch (conn->type) {
case CONN_TYPE_OR:
return connection_or_process_inbuf(TO_OR_CONN(conn));
+ case CONN_TYPE_EXT_OR:
+ return connection_ext_or_process_inbuf(TO_OR_CONN(conn));
case CONN_TYPE_EXIT:
case CONN_TYPE_AP:
return connection_edge_process_inbuf(TO_EDGE_CONN(conn),
@@ -4065,6 +4408,8 @@ connection_finished_flushing(connection_t *conn)
switch (conn->type) {
case CONN_TYPE_OR:
return connection_or_finished_flushing(TO_OR_CONN(conn));
+ case CONN_TYPE_EXT_OR:
+ return connection_ext_or_finished_flushing(TO_OR_CONN(conn));
case CONN_TYPE_AP:
case CONN_TYPE_EXIT:
return connection_edge_finished_flushing(TO_EDGE_CONN(conn));
@@ -4120,6 +4465,7 @@ connection_reached_eof(connection_t *conn)
{
switch (conn->type) {
case CONN_TYPE_OR:
+ case CONN_TYPE_EXT_OR:
return connection_or_reached_eof(TO_OR_CONN(conn));
case CONN_TYPE_AP:
case CONN_TYPE_EXIT:
@@ -4206,6 +4552,7 @@ assert_connection_ok(connection_t *conn, time_t now)
switch (conn->type) {
case CONN_TYPE_OR:
+ case CONN_TYPE_EXT_OR:
tor_assert(conn->magic == OR_CONNECTION_MAGIC);
break;
case CONN_TYPE_AP:
@@ -4311,6 +4658,10 @@ assert_connection_ok(connection_t *conn, time_t now)
tor_assert(conn->state >= OR_CONN_STATE_MIN_);
tor_assert(conn->state <= OR_CONN_STATE_MAX_);
break;
+ case CONN_TYPE_EXT_OR:
+ tor_assert(conn->state >= EXT_OR_CONN_STATE_MIN_);
+ tor_assert(conn->state <= EXT_OR_CONN_STATE_MAX_);
+ break;
case CONN_TYPE_EXIT:
tor_assert(conn->state >= EXIT_CONN_STATE_MIN_);
tor_assert(conn->state <= EXIT_CONN_STATE_MAX_);
@@ -4372,7 +4723,7 @@ get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type,
options->Bridges) {
const transport_t *transport = NULL;
int r;
- r = find_transport_by_bridge_addrport(&conn->addr, conn->port, &transport);
+ r = get_transport_by_bridge_addrport(&conn->addr, conn->port, &transport);
if (r<0)
return -1;
if (transport) { /* transport found */
@@ -4387,24 +4738,6 @@ get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type,
return 0;
}
-/** Returns the global proxy type used by tor. */
-static int
-get_proxy_type(void)
-{
- const or_options_t *options = get_options();
-
- if (options->HTTPSProxy)
- return PROXY_CONNECT;
- else if (options->Socks4Proxy)
- return PROXY_SOCKS4;
- else if (options->Socks5Proxy)
- return PROXY_SOCKS5;
- else if (options->ClientTransportPlugin)
- return PROXY_PLUGGABLE;
- else
- return PROXY_NONE;
-}
-
/** Log a failed connection to a proxy server.
* <b>conn</b> is the connection we use the proxy server for. */
void
@@ -4461,6 +4794,7 @@ connection_free_all(void)
/* Unlink everything from the identity map. */
connection_or_clear_identity_map();
+ connection_or_clear_ext_or_id_map();
/* Clear out our list of broken connections */
clear_broken_connection_map(0);
diff --git a/src/or/connection.h b/src/or/connection.h
index c78fe6e65..4073d9fc9 100644
--- a/src/or/connection.h
+++ b/src/or/connection.h
@@ -19,7 +19,7 @@ const char *conn_type_to_string(int type);
const char *conn_state_to_string(int type, int state);
dir_connection_t *dir_connection_new(int socket_family);
-or_connection_t *or_connection_new(int socket_family);
+or_connection_t *or_connection_new(int type, int socket_family);
edge_connection_t *edge_connection_new(int type, int socket_family);
entry_connection_t *entry_connection_new(int type, int socket_family);
control_connection_t *control_connection_new(int socket_family);
@@ -89,6 +89,14 @@ int connection_connect(connection_t *conn, const char *address,
const tor_addr_t *addr,
uint16_t port, int *socket_error);
+/** Maximum size of information that we can fit into SOCKS5 username
+ or password fields. */
+#define MAX_SOCKS5_AUTH_FIELD_SIZE 255
+
+/** Total maximum size of information that we can fit into SOCKS5
+ username and password fields. */
+#define MAX_SOCKS5_AUTH_SIZE_TOTAL 2*MAX_SOCKS5_AUTH_FIELD_SIZE
+
int connection_proxy_connect(connection_t *conn, int type);
int connection_read_proxy_handshake(connection_t *conn);
void log_failed_proxy_connection(connection_t *conn);
@@ -122,8 +130,8 @@ int connection_outbuf_too_full(connection_t *conn);
int connection_handle_write(connection_t *conn, int force);
int connection_flush(connection_t *conn);
-void connection_write_to_buf_impl_(const char *string, size_t len,
- connection_t *conn, int zlib);
+MOCK_DECL(void, connection_write_to_buf_impl_,
+ (const char *string, size_t len, connection_t *conn, int zlib));
/* DOCDOC connection_write_to_buf */
static void connection_write_to_buf(const char *string, size_t len,
connection_t *conn);
@@ -206,5 +214,18 @@ void connection_enable_rate_limiting(connection_t *conn);
#define connection_type_uses_bufferevent(c) (0)
#endif
+#ifdef CONNECTION_PRIVATE
+STATIC void connection_free_(connection_t *conn);
+
+/* Used only by connection.c and test*.c */
+uint32_t bucket_millis_empty(int tokens_before, uint32_t last_empty_time,
+ int tokens_after, int milliseconds_elapsed,
+ const struct timeval *tvnow);
+void connection_buckets_note_empty_ts(uint32_t *timestamp_var,
+ int tokens_before,
+ size_t tokens_removed,
+ const struct timeval *tvnow);
+#endif
+
#endif
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 3a72110ea..d5969aef9 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -14,6 +14,7 @@
#include "addressmap.h"
#include "buffers.h"
#include "channel.h"
+#include "circpathbias.h"
#include "circuitlist.h"
#include "circuituse.h"
#include "config.h"
@@ -412,7 +413,7 @@ connection_edge_finished_flushing(edge_connection_t *conn)
* that the name resolution that led us to <b>addr</b> will be valid for
* <b>ttl</b> seconds. Return -1 on error, or the number of bytes used on
* success. */
-/* private */int
+STATIC int
connected_cell_format_payload(uint8_t *payload_out,
const tor_addr_t *addr,
uint32_t ttl)
@@ -2295,7 +2296,7 @@ connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply,
* Return -1 in the case where want to send a RELAY_END cell, and < -1 when
* we don't.
**/
-/* static */ int
+STATIC int
begin_cell_parse(const cell_t *cell, begin_cell_t *bcell,
uint8_t *end_reason_out)
{
diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h
index ea284cbcf..e3a95ad9e 100644
--- a/src/or/connection_edge.h
+++ b/src/or/connection_edge.h
@@ -12,6 +12,8 @@
#ifndef TOR_CONNECTION_EDGE_H
#define TOR_CONNECTION_EDGE_H
+#include "testsupport.h"
+
#define connection_mark_unattached_ap(conn, endreason) \
connection_mark_unattached_ap_((conn), (endreason), __LINE__, SHORT_FILE__)
@@ -130,9 +132,9 @@ typedef struct begin_cell_t {
unsigned is_begindir : 1;
} begin_cell_t;
-int begin_cell_parse(const cell_t *cell, begin_cell_t *bcell,
+STATIC int begin_cell_parse(const cell_t *cell, begin_cell_t *bcell,
uint8_t *end_reason_out);
-int connected_cell_format_payload(uint8_t *payload_out,
+STATIC int connected_cell_format_payload(uint8_t *payload_out,
const tor_addr_t *addr,
uint32_t ttl);
#endif
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index 3d16e1453..089de93f7 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -37,7 +37,7 @@
#include "rephist.h"
#include "router.h"
#include "routerlist.h"
-
+#include "ext_orport.h"
#ifdef USE_BUFFEREVENTS
#include <event2/bufferevent_ssl.h>
#endif
@@ -75,6 +75,10 @@ static void connection_or_handle_event_cb(struct bufferevent *bufev,
* they form a linked list, with next_with_same_id as the next pointer. */
static digestmap_t *orconn_identity_map = NULL;
+/** Global map between Extended ORPort identifiers and OR
+ * connections. */
+static digestmap_t *orconn_ext_or_id_map = NULL;
+
/** If conn is listed in orconn_identity_map, remove it, and clear
* conn->identity_digest. Otherwise do nothing. */
void
@@ -174,6 +178,71 @@ connection_or_set_identity_digest(or_connection_t *conn, const char *digest)
#endif
}
+/** Remove the Extended ORPort identifier of <b>conn</b> from the
+ * global identifier list. Also, clear the identifier from the
+ * connection itself. */
+void
+connection_or_remove_from_ext_or_id_map(or_connection_t *conn)
+{
+ or_connection_t *tmp;
+ if (!orconn_ext_or_id_map)
+ return;
+ if (!conn->ext_or_conn_id)
+ return;
+
+ tmp = digestmap_remove(orconn_ext_or_id_map, conn->ext_or_conn_id);
+ if (!tor_digest_is_zero(conn->ext_or_conn_id))
+ tor_assert(tmp == conn);
+
+ memset(conn->ext_or_conn_id, 0, EXT_OR_CONN_ID_LEN);
+}
+
+/** Return the connection whose ext_or_id is <b>id</b>. Return NULL if no such
+ * connection is found. */
+or_connection_t *
+connection_or_get_by_ext_or_id(const char *id)
+{
+ if (!orconn_ext_or_id_map)
+ return NULL;
+ return digestmap_get(orconn_ext_or_id_map, id);
+}
+
+/** Deallocate the global Extended ORPort identifier list */
+void
+connection_or_clear_ext_or_id_map(void)
+{
+ digestmap_free(orconn_ext_or_id_map, NULL);
+ orconn_ext_or_id_map = NULL;
+}
+
+/** Creates an Extended ORPort identifier for <b>conn<b/> and deposits
+ * it into the global list of identifiers. */
+void
+connection_or_set_ext_or_identifier(or_connection_t *conn)
+{
+ char random_id[EXT_OR_CONN_ID_LEN];
+ or_connection_t *tmp;
+
+ if (!orconn_ext_or_id_map)
+ orconn_ext_or_id_map = digestmap_new();
+
+ /* Remove any previous identifiers: */
+ if (conn->ext_or_conn_id && !tor_digest_is_zero(conn->ext_or_conn_id))
+ connection_or_remove_from_ext_or_id_map(conn);
+
+ do {
+ crypto_rand(random_id, sizeof(random_id));
+ } while (digestmap_get(orconn_ext_or_id_map, random_id));
+
+ if (!conn->ext_or_conn_id)
+ conn->ext_or_conn_id = tor_malloc_zero(EXT_OR_CONN_ID_LEN);
+
+ memcpy(conn->ext_or_conn_id, random_id, EXT_OR_CONN_ID_LEN);
+
+ tmp = digestmap_set(orconn_ext_or_id_map, random_id, conn);
+ tor_assert(!tmp);
+}
+
/**************************************************************/
/** Map from a string describing what a non-open OR connection was doing when
@@ -228,7 +297,7 @@ connection_or_get_state_description(or_connection_t *orconn,
const char *conn_state;
char tls_state[256];
- tor_assert(conn->type == CONN_TYPE_OR);
+ tor_assert(conn->type == CONN_TYPE_OR || conn->type == CONN_TYPE_EXT_OR);
conn_state = conn_state_to_string(conn->type, conn->state);
tor_tls_get_state_description(orconn->tls, tls_state, sizeof(tls_state));
@@ -1077,7 +1146,7 @@ connection_or_connect(const tor_addr_t *_addr, uint16_t port,
return NULL;
}
- conn = or_connection_new(tor_addr_family(&addr));
+ conn = or_connection_new(CONN_TYPE_OR, tor_addr_family(&addr));
/*
* Set up conn so it's got all the data we need to remember for channels
@@ -1222,8 +1291,8 @@ connection_or_close_for_error(or_connection_t *orconn, int flush)
*
* Return -1 if <b>conn</b> is broken, else return 0.
*/
-int
-connection_tls_start_handshake(or_connection_t *conn, int receiving)
+MOCK_IMPL(int,
+connection_tls_start_handshake,(or_connection_t *conn, int receiving))
{
channel_listener_t *chan_listener;
channel_t *chan;
@@ -1480,7 +1549,8 @@ connection_or_handle_event_cb(struct bufferevent *bufev, short event,
int
connection_or_nonopen_was_started_here(or_connection_t *conn)
{
- tor_assert(conn->base_.type == CONN_TYPE_OR);
+ tor_assert(conn->base_.type == CONN_TYPE_OR ||
+ conn->base_.type == CONN_TYPE_EXT_OR);
if (!conn->tls)
return 1; /* it's still in proxy states or something */
if (conn->handshake_state)
@@ -1689,7 +1759,7 @@ connection_tls_finish_handshake(or_connection_t *conn)
digest_rcvd) < 0)
return -1;
- circuit_build_times_network_is_live(&circ_times);
+ circuit_build_times_network_is_live(get_circuit_build_times_mutable());
if (tor_tls_used_v1_handshake(conn->tls)) {
conn->link_proto = 1;
@@ -1723,7 +1793,7 @@ connection_or_launch_v3_or_handshake(or_connection_t *conn)
tor_assert(connection_or_nonopen_was_started_here(conn));
tor_assert(tor_tls_received_v3_certificate(conn->tls));
- circuit_build_times_network_is_live(&circ_times);
+ circuit_build_times_network_is_live(get_circuit_build_times_mutable());
connection_or_change_state(conn, OR_CONN_STATE_OR_HANDSHAKING_V3);
if (connection_init_or_handshake_state(conn, 1) < 0)
@@ -1956,7 +2026,7 @@ connection_or_process_cells_from_inbuf(or_connection_t *conn)
if (conn->chan)
channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan));
- circuit_build_times_network_is_live(&circ_times);
+ circuit_build_times_network_is_live(get_circuit_build_times_mutable());
channel_tls_handle_var_cell(var_cell, conn);
var_cell_free(var_cell);
} else {
@@ -1972,7 +2042,7 @@ connection_or_process_cells_from_inbuf(or_connection_t *conn)
if (conn->chan)
channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan));
- circuit_build_times_network_is_live(&circ_times);
+ circuit_build_times_network_is_live(get_circuit_build_times_mutable());
connection_fetch_from_buf(buf, cell_network_size, TO_CONN(conn));
/* retrieve cell info from buf (create the host-order struct from the
diff --git a/src/or/connection_or.h b/src/or/connection_or.h
index 85e68f1a3..8d9302893 100644
--- a/src/or/connection_or.h
+++ b/src/or/connection_or.h
@@ -45,7 +45,8 @@ void connection_or_close_for_error(or_connection_t *orconn, int flush);
void connection_or_report_broken_states(int severity, int domain);
-int connection_tls_start_handshake(or_connection_t *conn, int receiving);
+MOCK_DECL(int,connection_tls_start_handshake,(or_connection_t *conn,
+ int receiving));
int connection_tls_continue_handshake(or_connection_t *conn);
int connection_init_or_handshake_state(or_connection_t *conn,
diff --git a/src/or/control.c b/src/or/control.c
index a88de12d6..49212de65 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -19,6 +19,7 @@
#include "circuitlist.h"
#include "circuitstats.h"
#include "circuituse.h"
+#include "command.h"
#include "config.h"
#include "confparse.h"
#include "connection.h"
@@ -52,46 +53,13 @@
* finished authentication and is accepting commands. */
#define STATE_IS_OPEN(s) ((s) == CONTROL_CONN_STATE_OPEN)
-/* Recognized asynchronous event types. It's okay to expand this list
- * because it is used both as a list of v0 event types, and as indices
- * into the bitfield to determine which controllers want which events.
- */
-#define EVENT_MIN_ 0x0001
-#define EVENT_CIRCUIT_STATUS 0x0001
-#define EVENT_STREAM_STATUS 0x0002
-#define EVENT_OR_CONN_STATUS 0x0003
-#define EVENT_BANDWIDTH_USED 0x0004
-#define EVENT_CIRCUIT_STATUS_MINOR 0x0005
-#define EVENT_NEW_DESC 0x0006
-#define EVENT_DEBUG_MSG 0x0007
-#define EVENT_INFO_MSG 0x0008
-#define EVENT_NOTICE_MSG 0x0009
-#define EVENT_WARN_MSG 0x000A
-#define EVENT_ERR_MSG 0x000B
-#define EVENT_ADDRMAP 0x000C
-// #define EVENT_AUTHDIR_NEWDESCS 0x000D
-#define EVENT_DESCCHANGED 0x000E
-// #define EVENT_NS 0x000F
-#define EVENT_STATUS_CLIENT 0x0010
-#define EVENT_STATUS_SERVER 0x0011
-#define EVENT_STATUS_GENERAL 0x0012
-#define EVENT_GUARD 0x0013
-#define EVENT_STREAM_BANDWIDTH_USED 0x0014
-#define EVENT_CLIENTS_SEEN 0x0015
-#define EVENT_NEWCONSENSUS 0x0016
-#define EVENT_BUILDTIMEOUT_SET 0x0017
-#define EVENT_SIGNAL 0x0018
-#define EVENT_CONF_CHANGED 0x0019
-#define EVENT_MAX_ 0x0019
-/* If EVENT_MAX_ ever hits 0x0020, we need to make the mask wider. */
-
/** Bitfield: The bit 1&lt;&lt;e is set if <b>any</b> open control
* connection is interested in events of type <b>e</b>. We use this
* so that we can decide to skip generating event messages that nobody
* has interest in without having to walk over the global connection
* list to find out.
**/
-typedef uint32_t event_mask_t;
+typedef uint64_t event_mask_t;
/** An event mask of all the events that any controller is interested in
* receiving. */
@@ -103,7 +71,7 @@ static int disable_log_messages = 0;
/** Macro: true if any control connection is interested in events of type
* <b>e</b>. */
#define EVENT_IS_INTERESTING(e) \
- (global_event_mask & (1<<(e)))
+ (!! (global_event_mask & (((uint64_t)1)<<(e))))
/** If we're using cookie-type authentication, how long should our cookies be?
*/
@@ -115,7 +83,7 @@ static int authentication_cookie_is_set = 0;
/** If authentication_cookie_is_set, a secret cookie that we've stored to disk
* and which we're using to authenticate controllers. (If the controller can
* read it off disk, it has permission to connect.) */
-static char authentication_cookie[AUTHENTICATION_COOKIE_LEN];
+static uint8_t *authentication_cookie = NULL;
#define SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT \
"Tor safe cookie authentication server-to-controller hash"
@@ -130,15 +98,6 @@ static char authentication_cookie[AUTHENTICATION_COOKIE_LEN];
* of this so we can respond to getinfo status/bootstrap-phase queries. */
static char last_sent_bootstrap_message[BOOTSTRAP_MSG_LEN];
-/** Flag for event_format_t. Indicates that we should use the one standard
- format.
- */
-#define ALL_FORMATS 1
-
-/** Bit field of flags to select how to format a controller event. Recognized
- * flag is ALL_FORMATS. */
-typedef int event_format_t;
-
static void connection_printf_to_buf(control_connection_t *conn,
const char *format, ...)
CHECK_PRINTF(2,3);
@@ -232,6 +191,20 @@ log_severity_to_event(int severity)
}
}
+/** Helper: clear bandwidth counters of all origin circuits. */
+static void
+clear_circ_bw_fields(void)
+{
+ circuit_t *circ;
+ origin_circuit_t *ocirc;
+ TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) {
+ if (!CIRCUIT_IS_ORIGIN(circ))
+ continue;
+ ocirc = TO_ORIGIN_CIRCUIT(circ);
+ ocirc->n_written_circ_bw = ocirc->n_read_circ_bw = 0;
+ }
+}
+
/** Set <b>global_event_mask*</b> to the bitwise OR of each live control
* connection's event_mask field. */
void
@@ -257,8 +230,8 @@ control_update_global_event_mask(void)
* we want to hear...*/
control_adjust_event_log_severity();
- /* ...then, if we've started logging stream bw, clear the appropriate
- * fields. */
+ /* ...then, if we've started logging stream or circ bw, clear the
+ * appropriate fields. */
if (! (old_mask & EVENT_STREAM_BANDWIDTH_USED) &&
(new_mask & EVENT_STREAM_BANDWIDTH_USED)) {
SMARTLIST_FOREACH(conns, connection_t *, conn,
@@ -269,6 +242,10 @@ control_update_global_event_mask(void)
}
});
}
+ if (! (old_mask & EVENT_CIRC_BANDWIDTH_USED) &&
+ (new_mask & EVENT_CIRC_BANDWIDTH_USED)) {
+ clear_circ_bw_fields();
+ }
}
/** Adjust the log severities that result in control_event_logmsg being called
@@ -334,7 +311,7 @@ connection_write_str_to_buf(const char *s, control_connection_t *conn)
* the end. Replace all LF characters sequences with CRLF. Return the number
* of bytes in *<b>out</b>.
*/
-/* static */ size_t
+STATIC size_t
write_escaped_data(const char *data, size_t len, char **out)
{
size_t sz_out = len+8;
@@ -382,7 +359,7 @@ write_escaped_data(const char *data, size_t len, char **out)
* that appears at the start of a line, and replacing all CRLF sequences
* with LF. Return the number of
* bytes in *<b>out</b>. */
-/* static */ size_t
+STATIC size_t
read_escaped_data(const char *data, size_t len, char **out)
{
char *outp;
@@ -592,9 +569,9 @@ send_control_done(control_connection_t *conn)
*
* The EXTENDED_FORMAT and NONEXTENDED_FORMAT flags behave similarly with
* respect to the EXTENDED_EVENTS feature. */
-static void
-send_control_event_string(uint16_t event, event_format_t which,
- const char *msg)
+MOCK_IMPL(STATIC void,
+send_control_event_string,(uint16_t event, event_format_t which,
+ const char *msg))
{
smartlist_t *conns = get_connection_array();
(void)which;
@@ -958,6 +935,11 @@ static const struct control_event_t control_event_table[] = {
{ EVENT_BUILDTIMEOUT_SET, "BUILDTIMEOUT_SET" },
{ EVENT_SIGNAL, "SIGNAL" },
{ EVENT_CONF_CHANGED, "CONF_CHANGED"},
+ { EVENT_CONN_BW, "CONN_BW" },
+ { EVENT_CELL_STATS, "CELL_STATS" },
+ { EVENT_TB_EMPTY, "TB_EMPTY" },
+ { EVENT_CIRC_BANDWIDTH_USED, "CIRC_BW" },
+ { EVENT_TRANSPORT_LAUNCHED, "TRANSPORT_LAUNCHED" },
{ 0, NULL },
};
@@ -1447,7 +1429,7 @@ getinfo_helper_misc(control_connection_t *conn, const char *question,
} else if (!strcmp(question, "config-defaults-file")) {
*answer = tor_strdup(get_torrc_fname(1));
} else if (!strcmp(question, "config-text")) {
- *answer = options_dump(get_options(), 1);
+ *answer = options_dump(get_options(), OPTIONS_DUMP_MINIMAL);
} else if (!strcmp(question, "info/names")) {
*answer = list_getinfo_options();
} else if (!strcmp(question, "dormant")) {
@@ -1572,7 +1554,8 @@ munge_extrainfo_into_routerinfo(const char *ri_body,
if (!(cp = tor_memstr(ei_body, ei_len, kwd)))
continue;
++cp;
- eol = memchr(cp, '\n', ei_len - (cp-ei_body));
+ if (!(eol = memchr(cp, '\n', ei_len - (cp-ei_body))))
+ continue;
memcpy(outp, cp, eol-cp+1);
outp += eol-cp+1;
}
@@ -1927,7 +1910,7 @@ getinfo_helper_events(control_connection_t *control_conn,
if (!strcmp(question, "circuit-status")) {
circuit_t *circ_;
smartlist_t *status = smartlist_new();
- for (circ_ = circuit_get_global_list_(); circ_; circ_ = circ_->next) {
+ TOR_LIST_FOREACH(circ_, circuit_get_global_list(), head) {
origin_circuit_t *circ;
char *circdesc;
const char *state;
@@ -3181,6 +3164,30 @@ handle_control_usefeature(control_connection_t *conn,
return 0;
}
+/** Implementation for the DROPGUARDS command. */
+static int
+handle_control_dropguards(control_connection_t *conn,
+ uint32_t len,
+ const char *body)
+{
+ smartlist_t *args;
+ (void) len; /* body is nul-terminated; it's safe to ignore the length */
+ args = smartlist_new();
+ smartlist_split_string(args, body, " ",
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+
+ if (smartlist_len(args)) {
+ connection_printf_to_buf(conn, "512 Too many arguments to DROPGUARDS\r\n");
+ } else {
+ remove_all_entry_guards();
+ send_control_done(conn);
+ }
+
+ SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
+ smartlist_free(args);
+ return 0;
+}
+
/** Called when <b>conn</b> has no more bytes left on its outbuf. */
int
connection_control_finished_flushing(control_connection_t *conn)
@@ -3480,6 +3487,9 @@ connection_control_process_inbuf(control_connection_t *conn)
} else if (!strcasecmp(conn->incoming_cmd, "AUTHCHALLENGE")) {
if (handle_control_authchallenge(conn, cmd_data_len, args))
return -1;
+ } else if (!strcasecmp(conn->incoming_cmd, "DROPGUARDS")) {
+ if (handle_control_dropguards(conn, cmd_data_len, args))
+ return -1;
} else {
connection_printf_to_buf(conn, "510 Unrecognized command \"%s\"\r\n",
conn->incoming_cmd);
@@ -3847,17 +3857,17 @@ control_event_or_conn_status(or_connection_t *conn, or_conn_status_event_t tp,
}
ncircs += connection_or_get_num_circuits(conn);
if (ncircs && (tp == OR_CONN_EVENT_FAILED || tp == OR_CONN_EVENT_CLOSED)) {
- tor_snprintf(ncircs_buf, sizeof(ncircs_buf), "%sNCIRCS=%d",
- reason ? " " : "", ncircs);
+ tor_snprintf(ncircs_buf, sizeof(ncircs_buf), " NCIRCS=%d", ncircs);
}
orconn_target_get_name(name, sizeof(name), conn);
send_control_event(EVENT_OR_CONN_STATUS, ALL_FORMATS,
- "650 ORCONN %s %s %s%s%s\r\n",
+ "650 ORCONN %s %s%s%s%s ID="U64_FORMAT"\r\n",
name, status,
- reason ? "REASON=" : "",
+ reason ? " REASON=" : "",
orconn_end_reason_to_control_string(reason),
- ncircs_buf);
+ ncircs_buf,
+ U64_PRINTF_ARG(conn->base_.global_identifier));
return 0;
}
@@ -3868,6 +3878,8 @@ control_event_or_conn_status(or_connection_t *conn, or_conn_status_event_t tp,
int
control_event_stream_bandwidth(edge_connection_t *edge_conn)
{
+ circuit_t *circ;
+ origin_circuit_t *ocirc;
if (EVENT_IS_INTERESTING(EVENT_STREAM_BANDWIDTH_USED)) {
if (!edge_conn->n_read && !edge_conn->n_written)
return 0;
@@ -3878,6 +3890,12 @@ control_event_stream_bandwidth(edge_connection_t *edge_conn)
(unsigned long)edge_conn->n_read,
(unsigned long)edge_conn->n_written);
+ circ = circuit_get_by_edge_conn(edge_conn);
+ if (circ && CIRCUIT_IS_ORIGIN(circ)) {
+ ocirc = TO_ORIGIN_CIRCUIT(circ);
+ ocirc->n_read_circ_bw += edge_conn->n_read;
+ ocirc->n_written_circ_bw += edge_conn->n_written;
+ }
edge_conn->n_written = edge_conn->n_read = 0;
}
@@ -3915,6 +3933,235 @@ control_event_stream_bandwidth_used(void)
return 0;
}
+/** A second or more has elapsed: tell any interested control connections
+ * how much bandwidth origin circuits have used. */
+int
+control_event_circ_bandwidth_used(void)
+{
+ circuit_t *circ;
+ origin_circuit_t *ocirc;
+ if (!EVENT_IS_INTERESTING(EVENT_CIRC_BANDWIDTH_USED))
+ return 0;
+
+ TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) {
+ if (!CIRCUIT_IS_ORIGIN(circ))
+ continue;
+ ocirc = TO_ORIGIN_CIRCUIT(circ);
+ if (!ocirc->n_read_circ_bw && !ocirc->n_written_circ_bw)
+ continue;
+ send_control_event(EVENT_CIRC_BANDWIDTH_USED, ALL_FORMATS,
+ "650 CIRC_BW ID=%d READ=%lu WRITTEN=%lu\r\n",
+ ocirc->global_identifier,
+ (unsigned long)ocirc->n_read_circ_bw,
+ (unsigned long)ocirc->n_written_circ_bw);
+ ocirc->n_written_circ_bw = ocirc->n_read_circ_bw = 0;
+ }
+
+ return 0;
+}
+
+/** Print out CONN_BW event for a single OR/DIR/EXIT <b>conn</b> and reset
+ * bandwidth counters. */
+int
+control_event_conn_bandwidth(connection_t *conn)
+{
+ const char *conn_type_str;
+ if (!get_options()->TestingEnableConnBwEvent ||
+ !EVENT_IS_INTERESTING(EVENT_CONN_BW))
+ return 0;
+ if (!conn->n_read_conn_bw && !conn->n_written_conn_bw)
+ return 0;
+ switch (conn->type) {
+ case CONN_TYPE_OR:
+ conn_type_str = "OR";
+ break;
+ case CONN_TYPE_DIR:
+ conn_type_str = "DIR";
+ break;
+ case CONN_TYPE_EXIT:
+ conn_type_str = "EXIT";
+ break;
+ default:
+ return 0;
+ }
+ send_control_event(EVENT_CONN_BW, ALL_FORMATS,
+ "650 CONN_BW ID="U64_FORMAT" TYPE=%s "
+ "READ=%lu WRITTEN=%lu\r\n",
+ U64_PRINTF_ARG(conn->global_identifier),
+ conn_type_str,
+ (unsigned long)conn->n_read_conn_bw,
+ (unsigned long)conn->n_written_conn_bw);
+ conn->n_written_conn_bw = conn->n_read_conn_bw = 0;
+ return 0;
+}
+
+/** A second or more has elapsed: tell any interested control
+ * connections how much bandwidth connections have used. */
+int
+control_event_conn_bandwidth_used(void)
+{
+ if (get_options()->TestingEnableConnBwEvent &&
+ EVENT_IS_INTERESTING(EVENT_CONN_BW)) {
+ SMARTLIST_FOREACH(get_connection_array(), connection_t *, conn,
+ control_event_conn_bandwidth(conn));
+ }
+ return 0;
+}
+
+/** Helper: iterate over cell statistics of <b>circ</b> and sum up added
+ * cells, removed cells, and waiting times by cell command and direction.
+ * Store results in <b>cell_stats</b>. Free cell statistics of the
+ * circuit afterwards. */
+void
+sum_up_cell_stats_by_command(circuit_t *circ, cell_stats_t *cell_stats)
+{
+ memset(cell_stats, 0, sizeof(cell_stats_t));
+ SMARTLIST_FOREACH_BEGIN(circ->testing_cell_stats,
+ testing_cell_stats_entry_t *, ent) {
+ tor_assert(ent->command <= CELL_COMMAND_MAX_);
+ if (!ent->removed && !ent->exitward) {
+ cell_stats->added_cells_appward[ent->command] += 1;
+ } else if (!ent->removed && ent->exitward) {
+ cell_stats->added_cells_exitward[ent->command] += 1;
+ } else if (!ent->exitward) {
+ cell_stats->removed_cells_appward[ent->command] += 1;
+ cell_stats->total_time_appward[ent->command] += ent->waiting_time * 10;
+ } else {
+ cell_stats->removed_cells_exitward[ent->command] += 1;
+ cell_stats->total_time_exitward[ent->command] += ent->waiting_time * 10;
+ }
+ tor_free(ent);
+ } SMARTLIST_FOREACH_END(ent);
+ smartlist_free(circ->testing_cell_stats);
+ circ->testing_cell_stats = NULL;
+}
+
+/** Helper: append a cell statistics string to <code>event_parts</code>,
+ * prefixed with <code>key</code>=. Statistics consist of comma-separated
+ * key:value pairs with lower-case command strings as keys and cell
+ * numbers or total waiting times as values. A key:value pair is included
+ * if the entry in <code>include_if_non_zero</code> is not zero, but with
+ * the (possibly zero) entry from <code>number_to_include</code>. Both
+ * arrays are expected to have a length of CELL_COMMAND_MAX_ + 1. If no
+ * entry in <code>include_if_non_zero</code> is positive, no string will
+ * be added to <code>event_parts</code>. */
+void
+append_cell_stats_by_command(smartlist_t *event_parts, const char *key,
+ const uint64_t *include_if_non_zero,
+ const uint64_t *number_to_include)
+{
+ smartlist_t *key_value_strings = smartlist_new();
+ int i;
+ for (i = 0; i <= CELL_COMMAND_MAX_; i++) {
+ if (include_if_non_zero[i] > 0) {
+ smartlist_add_asprintf(key_value_strings, "%s:"U64_FORMAT,
+ cell_command_to_string(i),
+ U64_PRINTF_ARG(number_to_include[i]));
+ }
+ }
+ if (smartlist_len(key_value_strings) > 0) {
+ char *joined = smartlist_join_strings(key_value_strings, ",", 0, NULL);
+ smartlist_add_asprintf(event_parts, "%s=%s", key, joined);
+ SMARTLIST_FOREACH(key_value_strings, char *, cp, tor_free(cp));
+ tor_free(joined);
+ }
+ smartlist_free(key_value_strings);
+}
+
+/** Helper: format <b>cell_stats</b> for <b>circ</b> for inclusion in a
+ * CELL_STATS event and write result string to <b>event_string</b>. */
+void
+format_cell_stats(char **event_string, circuit_t *circ,
+ cell_stats_t *cell_stats)
+{
+ smartlist_t *event_parts = smartlist_new();
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+ smartlist_add_asprintf(event_parts, "ID=%lu",
+ (unsigned long)ocirc->global_identifier);
+ } else if (TO_OR_CIRCUIT(circ)->p_chan) {
+ or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
+ smartlist_add_asprintf(event_parts, "InboundQueue=%lu",
+ (unsigned long)or_circ->p_circ_id);
+ smartlist_add_asprintf(event_parts, "InboundConn="U64_FORMAT,
+ U64_PRINTF_ARG(or_circ->p_chan->global_identifier));
+ append_cell_stats_by_command(event_parts, "InboundAdded",
+ cell_stats->added_cells_appward,
+ cell_stats->added_cells_appward);
+ append_cell_stats_by_command(event_parts, "InboundRemoved",
+ cell_stats->removed_cells_appward,
+ cell_stats->removed_cells_appward);
+ append_cell_stats_by_command(event_parts, "InboundTime",
+ cell_stats->removed_cells_appward,
+ cell_stats->total_time_appward);
+ }
+ if (circ->n_chan) {
+ smartlist_add_asprintf(event_parts, "OutboundQueue=%lu",
+ (unsigned long)circ->n_circ_id);
+ smartlist_add_asprintf(event_parts, "OutboundConn="U64_FORMAT,
+ U64_PRINTF_ARG(circ->n_chan->global_identifier));
+ append_cell_stats_by_command(event_parts, "OutboundAdded",
+ cell_stats->added_cells_exitward,
+ cell_stats->added_cells_exitward);
+ append_cell_stats_by_command(event_parts, "OutboundRemoved",
+ cell_stats->removed_cells_exitward,
+ cell_stats->removed_cells_exitward);
+ append_cell_stats_by_command(event_parts, "OutboundTime",
+ cell_stats->removed_cells_exitward,
+ cell_stats->total_time_exitward);
+ }
+ *event_string = smartlist_join_strings(event_parts, " ", 0, NULL);
+ SMARTLIST_FOREACH(event_parts, char *, cp, tor_free(cp));
+ smartlist_free(event_parts);
+}
+
+/** A second or more has elapsed: tell any interested control connection
+ * how many cells have been processed for a given circuit. */
+int
+control_event_circuit_cell_stats(void)
+{
+ circuit_t *circ;
+ cell_stats_t *cell_stats;
+ char *event_string;
+ if (!get_options()->TestingEnableCellStatsEvent ||
+ !EVENT_IS_INTERESTING(EVENT_CELL_STATS))
+ return 0;
+ cell_stats = tor_malloc(sizeof(cell_stats_t));;
+ TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) {
+ if (!circ->testing_cell_stats)
+ continue;
+ sum_up_cell_stats_by_command(circ, cell_stats);
+ format_cell_stats(&event_string, circ, cell_stats);
+ send_control_event(EVENT_CELL_STATS, ALL_FORMATS,
+ "650 CELL_STATS %s\r\n", event_string);
+ tor_free(event_string);
+ }
+ tor_free(cell_stats);
+ return 0;
+}
+
+/** Tokens in <b>bucket</b> have been refilled: the read bucket was empty
+ * for <b>read_empty_time</b> millis, the write bucket was empty for
+ * <b>write_empty_time</b> millis, and buckets were last refilled
+ * <b>milliseconds_elapsed</b> millis ago. Only emit TB_EMPTY event if
+ * either read or write bucket have been empty before. */
+int
+control_event_tb_empty(const char *bucket, uint32_t read_empty_time,
+ uint32_t write_empty_time,
+ int milliseconds_elapsed)
+{
+ if (get_options()->TestingEnableTbEmptyEvent &&
+ EVENT_IS_INTERESTING(EVENT_TB_EMPTY) &&
+ (read_empty_time > 0 || write_empty_time > 0)) {
+ send_control_event(EVENT_TB_EMPTY, ALL_FORMATS,
+ "650 TB_EMPTY %s READ=%d WRITTEN=%d "
+ "LAST=%d\r\n",
+ bucket, read_empty_time, write_empty_time,
+ milliseconds_elapsed);
+ }
+ return 0;
+}
+
/** A second or more has elapsed: tell any interested control
* connections how much bandwidth we used. */
int
@@ -4162,32 +4409,26 @@ control_event_newconsensus(const networkstatus_t *consensus)
/** Called when we compute a new circuitbuildtimeout */
int
-control_event_buildtimeout_set(const circuit_build_times_t *cbt,
- buildtimeout_set_event_t type)
+control_event_buildtimeout_set(buildtimeout_set_event_t type,
+ const char *args)
{
const char *type_string = NULL;
- double qnt;
if (!control_event_is_interesting(EVENT_BUILDTIMEOUT_SET))
return 0;
- qnt = circuit_build_times_quantile_cutoff();
-
switch (type) {
case BUILDTIMEOUT_SET_EVENT_COMPUTED:
type_string = "COMPUTED";
break;
case BUILDTIMEOUT_SET_EVENT_RESET:
type_string = "RESET";
- qnt = 1.0;
break;
case BUILDTIMEOUT_SET_EVENT_SUSPENDED:
type_string = "SUSPENDED";
- qnt = 1.0;
break;
case BUILDTIMEOUT_SET_EVENT_DISCARD:
type_string = "DISCARD";
- qnt = 1.0;
break;
case BUILDTIMEOUT_SET_EVENT_RESUME:
type_string = "RESUME";
@@ -4198,15 +4439,8 @@ control_event_buildtimeout_set(const circuit_build_times_t *cbt,
}
send_control_event(EVENT_BUILDTIMEOUT_SET, ALL_FORMATS,
- "650 BUILDTIMEOUT_SET %s TOTAL_TIMES=%lu "
- "TIMEOUT_MS=%lu XM=%lu ALPHA=%f CUTOFF_QUANTILE=%f "
- "TIMEOUT_RATE=%f CLOSE_MS=%lu CLOSE_RATE=%f\r\n",
- type_string, (unsigned long)cbt->total_build_times,
- (unsigned long)cbt->timeout_ms,
- (unsigned long)cbt->Xm, cbt->alpha, qnt,
- circuit_build_times_timeout_rate(cbt),
- (unsigned long)cbt->close_ms,
- circuit_build_times_close_rate(cbt));
+ "650 BUILDTIMEOUT_SET %s %s\r\n",
+ type_string, args);
return 0;
}
@@ -4445,44 +4679,27 @@ get_cookie_file(void)
}
}
-/** Choose a random authentication cookie and write it to disk.
- * Anybody who can read the cookie from disk will be considered
- * authorized to use the control connection. Return -1 if we can't
- * write the file, or 0 on success. */
+/* Initialize the cookie-based authentication system of the
+ * ControlPort. If <b>enabled</b> is 0, then disable the cookie
+ * authentication system. */
int
-init_cookie_authentication(int enabled)
+init_control_cookie_authentication(int enabled)
{
- char *fname;
+ char *fname = NULL;
+ int retval;
+
if (!enabled) {
authentication_cookie_is_set = 0;
return 0;
}
- /* We don't want to generate a new cookie every time we call
- * options_act(). One should be enough. */
- if (authentication_cookie_is_set)
- return 0; /* all set */
-
fname = get_cookie_file();
- crypto_rand(authentication_cookie, AUTHENTICATION_COOKIE_LEN);
- authentication_cookie_is_set = 1;
- if (write_bytes_to_file(fname, authentication_cookie,
- AUTHENTICATION_COOKIE_LEN, 1)) {
- log_warn(LD_FS,"Error writing authentication cookie to %s.",
- escaped(fname));
- tor_free(fname);
- return -1;
- }
-#ifndef _WIN32
- if (get_options()->CookieAuthFileGroupReadable) {
- if (chmod(fname, 0640)) {
- log_warn(LD_FS,"Unable to make %s group-readable.", escaped(fname));
- }
- }
-#endif
-
+ retval = init_cookie_authentication(fname, "", /* no header */
+ AUTHENTICATION_COOKIE_LEN,
+ &authentication_cookie,
+ &authentication_cookie_is_set);
tor_free(fname);
- return 0;
+ return retval;
}
/** A copy of the process specifier of Tor's owning controller, or
@@ -4698,8 +4915,8 @@ control_event_bootstrap(bootstrap_status_t status, int progress)
* that indicates a problem. <b>warn</b> gives a hint as to why, and
* <b>reason</b> provides an "or_conn_end_reason" tag.
*/
-void
-control_event_bootstrap_problem(const char *warn, int reason)
+MOCK_IMPL(void,
+control_event_bootstrap_problem, (const char *warn, int reason))
{
int status = bootstrap_percent;
const char *tag, *summary;
@@ -4766,3 +4983,35 @@ control_event_clients_seen(const char *controller_str)
"650 CLIENTS_SEEN %s\r\n", controller_str);
}
+/** A new pluggable transport called <b>transport_name</b> was
+ * launched on <b>addr</b>:<b>port</b>. <b>mode</b> is either
+ * "server" or "client" depending on the mode of the pluggable
+ * transport.
+ * "650" SP "TRANSPORT_LAUNCHED" SP Mode SP Name SP Address SP Port
+ */
+void
+control_event_transport_launched(const char *mode, const char *transport_name,
+ tor_addr_t *addr, uint16_t port)
+{
+ send_control_event(EVENT_TRANSPORT_LAUNCHED, ALL_FORMATS,
+ "650 TRANSPORT_LAUNCHED %s %s %s %u\r\n",
+ mode, transport_name, fmt_addr(addr), port);
+}
+
+/** Free any leftover allocated memory of the control.c subsystem. */
+void
+control_free_all(void)
+{
+ if (authentication_cookie) /* Free the auth cookie */
+ tor_free(authentication_cookie);
+}
+
+#ifdef TOR_UNIT_TESTS
+/* For testing: change the value of global_event_mask */
+void
+control_testing_set_global_event_mask(uint64_t mask)
+{
+ global_event_mask = mask;
+}
+#endif
+
diff --git a/src/or/control.h b/src/or/control.h
index 61062da2c..c8db643b7 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -50,6 +50,13 @@ int control_event_or_conn_status(or_connection_t *conn,
int control_event_bandwidth_used(uint32_t n_read, uint32_t n_written);
int control_event_stream_bandwidth(edge_connection_t *edge_conn);
int control_event_stream_bandwidth_used(void);
+int control_event_circ_bandwidth_used(void);
+int control_event_conn_bandwidth(connection_t *conn);
+int control_event_conn_bandwidth_used(void);
+int control_event_circuit_cell_stats(void);
+int control_event_tb_empty(const char *bucket, uint32_t read_empty_time,
+ uint32_t write_empty_time,
+ int milliseconds_elapsed);
void control_event_logmsg(int severity, uint32_t domain, const char *msg);
int control_event_descriptors_changed(smartlist_t *routers);
int control_event_address_mapped(const char *from, const char *to,
@@ -73,11 +80,11 @@ int control_event_server_status(int severity, const char *format, ...)
int control_event_guard(const char *nickname, const char *digest,
const char *status);
int control_event_conf_changed(const smartlist_t *elements);
-int control_event_buildtimeout_set(const circuit_build_times_t *cbt,
- buildtimeout_set_event_t type);
+int control_event_buildtimeout_set(buildtimeout_set_event_t type,
+ const char *args);
int control_event_signal(uintptr_t signal);
-int init_cookie_authentication(int enabled);
+int init_control_cookie_authentication(int enabled);
smartlist_t *decode_hashed_passwords(config_line_t *passwords);
void disable_control_logging(void);
void enable_control_logging(void);
@@ -85,14 +92,100 @@ void enable_control_logging(void);
void monitor_owning_controller_process(const char *process_spec);
void control_event_bootstrap(bootstrap_status_t status, int progress);
-void control_event_bootstrap_problem(const char *warn, int reason);
+MOCK_DECL(void, control_event_bootstrap_problem,(const char *warn,
+ int reason));
void control_event_clients_seen(const char *controller_str);
+void control_event_transport_launched(const char *mode,
+ const char *transport_name,
+ tor_addr_t *addr, uint16_t port);
+
+void control_free_all(void);
#ifdef CONTROL_PRIVATE
+/* Recognized asynchronous event types. It's okay to expand this list
+ * because it is used both as a list of v0 event types, and as indices
+ * into the bitfield to determine which controllers want which events.
+ */
+#define EVENT_MIN_ 0x0001
+#define EVENT_CIRCUIT_STATUS 0x0001
+#define EVENT_STREAM_STATUS 0x0002
+#define EVENT_OR_CONN_STATUS 0x0003
+#define EVENT_BANDWIDTH_USED 0x0004
+#define EVENT_CIRCUIT_STATUS_MINOR 0x0005
+#define EVENT_NEW_DESC 0x0006
+#define EVENT_DEBUG_MSG 0x0007
+#define EVENT_INFO_MSG 0x0008
+#define EVENT_NOTICE_MSG 0x0009
+#define EVENT_WARN_MSG 0x000A
+#define EVENT_ERR_MSG 0x000B
+#define EVENT_ADDRMAP 0x000C
+/* Exposed above */
+// #define EVENT_AUTHDIR_NEWDESCS 0x000D
+#define EVENT_DESCCHANGED 0x000E
+/* Exposed above */
+// #define EVENT_NS 0x000F
+#define EVENT_STATUS_CLIENT 0x0010
+#define EVENT_STATUS_SERVER 0x0011
+#define EVENT_STATUS_GENERAL 0x0012
+#define EVENT_GUARD 0x0013
+#define EVENT_STREAM_BANDWIDTH_USED 0x0014
+#define EVENT_CLIENTS_SEEN 0x0015
+#define EVENT_NEWCONSENSUS 0x0016
+#define EVENT_BUILDTIMEOUT_SET 0x0017
+#define EVENT_SIGNAL 0x0018
+#define EVENT_CONF_CHANGED 0x0019
+#define EVENT_CONN_BW 0x001A
+#define EVENT_CELL_STATS 0x001B
+#define EVENT_TB_EMPTY 0x001C
+#define EVENT_CIRC_BANDWIDTH_USED 0x001D
+#define EVENT_TRANSPORT_LAUNCHED 0x0020
+#define EVENT_MAX_ 0x0020
+/* If EVENT_MAX_ ever hits 0x0040, we need to make the mask into a
+ * different structure. */
+
/* Used only by control.c and test.c */
-size_t write_escaped_data(const char *data, size_t len, char **out);
-size_t read_escaped_data(const char *data, size_t len, char **out);
+STATIC size_t write_escaped_data(const char *data, size_t len, char **out);
+STATIC size_t read_escaped_data(const char *data, size_t len, char **out);
+/** Flag for event_format_t. Indicates that we should use the one standard
+ format. (Other formats previous existed, and are now deprecated)
+ */
+#define ALL_FORMATS 1
+/** Bit field of flags to select how to format a controller event. Recognized
+ * flag is ALL_FORMATS. */
+typedef int event_format_t;
+
+#ifdef TOR_UNIT_TESTS
+MOCK_DECL(STATIC void,
+send_control_event_string,(uint16_t event, event_format_t which,
+ const char *msg));
+
+void control_testing_set_global_event_mask(uint64_t mask);
+#endif
+
+/** Helper structure: temporarily stores cell statistics for a circuit. */
+typedef struct cell_stats_t {
+ /** Number of cells added in app-ward direction by command. */
+ uint64_t added_cells_appward[CELL_COMMAND_MAX_ + 1];
+ /** Number of cells added in exit-ward direction by command. */
+ uint64_t added_cells_exitward[CELL_COMMAND_MAX_ + 1];
+ /** Number of cells removed in app-ward direction by command. */
+ uint64_t removed_cells_appward[CELL_COMMAND_MAX_ + 1];
+ /** Number of cells removed in exit-ward direction by command. */
+ uint64_t removed_cells_exitward[CELL_COMMAND_MAX_ + 1];
+ /** Total waiting time of cells in app-ward direction by command. */
+ uint64_t total_time_appward[CELL_COMMAND_MAX_ + 1];
+ /** Total waiting time of cells in exit-ward direction by command. */
+ uint64_t total_time_exitward[CELL_COMMAND_MAX_ + 1];
+} cell_stats_t;
+void sum_up_cell_stats_by_command(circuit_t *circ,
+ cell_stats_t *cell_stats);
+void append_cell_stats_by_command(smartlist_t *event_parts,
+ const char *key,
+ const uint64_t *include_if_non_zero,
+ const uint64_t *number_to_include);
+void format_cell_stats(char **event_string, circuit_t *circ,
+ cell_stats_t *cell_stats);
#endif
#endif
diff --git a/src/or/directory.c b/src/or/directory.c
index 3752367c4..12c5b189f 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -747,7 +747,7 @@ connection_dir_request_failed(dir_connection_t *conn)
conn->base_.address);
} else if (conn->base_.purpose == DIR_PURPOSE_FETCH_MICRODESC) {
log_info(LD_DIR, "Giving up on downloading microdescriptors from "
- " directory server at '%s'; will retry", conn->base_.address);
+ "directory server at '%s'; will retry", conn->base_.address);
connection_dir_download_routerdesc_failed(conn);
}
}
@@ -1387,7 +1387,7 @@ directory_send_command(dir_connection_t *conn,
* so it does. Return 0.
* Otherwise, return -1.
*/
-static int
+STATIC int
parse_http_url(const char *headers, char **url)
{
char *s, *start, *tmp;
@@ -1416,6 +1416,19 @@ parse_http_url(const char *headers, char **url)
}
}
+ /* Check if the header is well formed (next sequence
+ * should be HTTP/1.X\r\n). Assumes we're supporting 1.0? */
+ {
+ unsigned minor_ver;
+ char ch;
+ char *e = (char *)eat_whitespace_no_nl(s);
+ if (2 != tor_sscanf(e, "HTTP/1.%u%c", &minor_ver, &ch)) {
+ return -1;
+ }
+ if (ch != '\r')
+ return -1;
+ }
+
if (s-start < 5 || strcmpstart(start,"/tor/")) { /* need to rewrite it */
*url = tor_malloc(s - start + 5);
strlcpy(*url,"/tor", s-start+5);
@@ -1626,8 +1639,9 @@ load_downloaded_routers(const char *body, smartlist_t *which,
added = router_load_routers_from_string(body, NULL, SAVED_NOWHERE, which,
descriptor_digests, buf);
- control_event_bootstrap(BOOTSTRAP_STATUS_LOADING_DESCRIPTORS,
- count_loading_descriptors_progress());
+ if (general)
+ control_event_bootstrap(BOOTSTRAP_STATUS_LOADING_DESCRIPTORS,
+ count_loading_descriptors_progress());
return added;
}
@@ -2968,7 +2982,9 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
tor_addr_t addr;
if (tor_inet_aton((TO_CONN(conn))->address, &in)) {
tor_addr_from_ipv4h(&addr, ntohl(in.s_addr));
- geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, time(NULL));
+ geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS,
+ &addr, NULL,
+ time(NULL));
geoip_note_ns_response(GEOIP_SUCCESS);
/* Note that a request for a network status has started, so that we
* can measure the download time later on. */
@@ -3725,57 +3741,27 @@ dir_networkstatus_download_failed(smartlist_t *failed, int status_code)
} SMARTLIST_FOREACH_END(fp);
}
-/** Schedule for when servers should download things in general. */
-static const int server_dl_schedule[] = {
- 0, 0, 0, 60, 60, 60*2, 60*5, 60*15, INT_MAX
-};
-/** Schedule for when clients should download things in general. */
-static const int client_dl_schedule[] = {
- 0, 0, 60, 60*5, 60*10, INT_MAX
-};
-/** Schedule for when servers should download consensuses. */
-static const int server_consensus_dl_schedule[] = {
- 0, 0, 60, 60*5, 60*10, 60*30, 60*30, 60*30, 60*30, 60*30, 60*60, 60*60*2
-};
-/** Schedule for when clients should download consensuses. */
-static const int client_consensus_dl_schedule[] = {
- 0, 0, 60, 60*5, 60*10, 60*30, 60*60, 60*60, 60*60, 60*60*3, 60*60*6, 60*60*12
-};
-/** Schedule for when clients should download bridge descriptors. */
-static const int bridge_dl_schedule[] = {
- 60*60, 15*60, 15*60, 60*60
-};
-
-/** Decide which download schedule we want to use, and then return a
- * pointer to it along with a pointer to its length. Helper function for
- * download_status_increment_failure() and download_status_reset(). */
-static void
-find_dl_schedule_and_len(download_status_t *dls, int server,
- const int **schedule, size_t *schedule_len)
+/** Decide which download schedule we want to use based on descriptor type
+ * in <b>dls</b> and whether we are acting as directory <b>server</b>, and
+ * then return a list of int pointers defining download delays in seconds.
+ * Helper function for download_status_increment_failure() and
+ * download_status_reset(). */
+static const smartlist_t *
+find_dl_schedule_and_len(download_status_t *dls, int server)
{
switch (dls->schedule) {
case DL_SCHED_GENERIC:
- if (server) {
- *schedule = server_dl_schedule;
- *schedule_len = sizeof(server_dl_schedule)/sizeof(int);
- } else {
- *schedule = client_dl_schedule;
- *schedule_len = sizeof(client_dl_schedule)/sizeof(int);
- }
- break;
+ if (server)
+ return get_options()->TestingServerDownloadSchedule;
+ else
+ return get_options()->TestingClientDownloadSchedule;
case DL_SCHED_CONSENSUS:
- if (server) {
- *schedule = server_consensus_dl_schedule;
- *schedule_len = sizeof(server_consensus_dl_schedule)/sizeof(int);
- } else {
- *schedule = client_consensus_dl_schedule;
- *schedule_len = sizeof(client_consensus_dl_schedule)/sizeof(int);
- }
- break;
+ if (server)
+ return get_options()->TestingServerConsensusDownloadSchedule;
+ else
+ return get_options()->TestingClientConsensusDownloadSchedule;
case DL_SCHED_BRIDGE:
- *schedule = bridge_dl_schedule;
- *schedule_len = sizeof(bridge_dl_schedule)/sizeof(int);
- break;
+ return get_options()->TestingBridgeDownloadSchedule;
default:
tor_assert(0);
}
@@ -3789,8 +3775,7 @@ time_t
download_status_increment_failure(download_status_t *dls, int status_code,
const char *item, int server, time_t now)
{
- const int *schedule;
- size_t schedule_len;
+ const smartlist_t *schedule;
int increment;
tor_assert(dls);
if (status_code != 503 || server) {
@@ -3798,14 +3783,14 @@ download_status_increment_failure(download_status_t *dls, int status_code,
++dls->n_download_failures;
}
- find_dl_schedule_and_len(dls, server, &schedule, &schedule_len);
+ schedule = find_dl_schedule_and_len(dls, server);
- if (dls->n_download_failures < schedule_len)
- increment = schedule[dls->n_download_failures];
+ if (dls->n_download_failures < smartlist_len(schedule))
+ increment = *(int *)smartlist_get(schedule, dls->n_download_failures);
else if (dls->n_download_failures == IMPOSSIBLE_TO_DOWNLOAD)
increment = INT_MAX;
else
- increment = schedule[schedule_len-1];
+ increment = *(int *)smartlist_get(schedule, smartlist_len(schedule) - 1);
if (increment < INT_MAX)
dls->next_attempt_at = now+increment;
@@ -3838,14 +3823,11 @@ download_status_increment_failure(download_status_t *dls, int status_code,
void
download_status_reset(download_status_t *dls)
{
- const int *schedule;
- size_t schedule_len;
-
- find_dl_schedule_and_len(dls, get_options()->DirPort_set,
- &schedule, &schedule_len);
+ const smartlist_t *schedule = find_dl_schedule_and_len(
+ dls, get_options()->DirPort_set);
dls->n_download_failures = 0;
- dls->next_attempt_at = time(NULL) + schedule[0];
+ dls->next_attempt_at = time(NULL) + *(int *)smartlist_get(schedule, 0);
}
/** Return the number of failures on <b>dls</b> since the last success (if
@@ -3890,7 +3872,8 @@ dir_routerdesc_download_failed(smartlist_t *failed, int status_code,
} else {
dls = router_get_dl_status_by_descriptor_digest(digest);
}
- if (!dls || dls->n_download_failures >= MAX_ROUTERDESC_DOWNLOAD_FAILURES)
+ if (!dls || dls->n_download_failures >=
+ get_options()->TestingDescriptorMaxDownloadTries)
continue;
download_status_increment_failure(dls, status_code, cp, server, now);
} SMARTLIST_FOREACH_END(cp);
@@ -3921,7 +3904,8 @@ dir_microdesc_download_failed(smartlist_t *failed,
if (!rs)
continue;
dls = &rs->dl_status;
- if (dls->n_download_failures >= MAX_MICRODESC_DOWNLOAD_FAILURES)
+ if (dls->n_download_failures >=
+ get_options()->TestingMicrodescMaxDownloadTries)
continue;
{
char buf[BASE64_DIGEST256_LEN+1];
diff --git a/src/or/directory.h b/src/or/directory.h
index 41f18a172..0453160f7 100644
--- a/src/or/directory.h
+++ b/src/or/directory.h
@@ -118,5 +118,10 @@ download_status_mark_impossible(download_status_t *dl)
int download_status_get_n_failures(const download_status_t *dls);
+#ifdef TOR_UNIT_TESTS
+/* Used only by directory.c and test_dir.c */
+STATIC int parse_http_url(const char *headers, char **url);
+#endif
+
#endif
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index 3e46153a5..8d2da5b50 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -26,6 +26,7 @@
#include "router.h"
#include "routerlist.h"
#include "routerparse.h"
+#include "routerset.h"
/**
* \file dirserv.c
@@ -1907,7 +1908,7 @@ router_counts_toward_thresholds(const node_t *node, time_t now,
* the Weighted Fractional Uptime history, and use them to set thresholds for
* the Stable, Fast, and Guard flags. Update the fields stable_uptime,
* stable_mtbf, enough_mtbf_info, guard_wfu, guard_tk, fast_bandwidth,
- * guard_bandwidh_including_exits, guard_bandwidth_excluding_exits,
+ * guard_bandwidth_including_exits, and guard_bandwidth_excluding_exits.
*
* Also, set the is_exit flag of each router appropriately. */
static void
@@ -1956,6 +1957,10 @@ dirserv_compute_performance_thresholds(routerlist_t *rl,
/* Now, fill in the arrays. */
SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) {
+ if (options->BridgeAuthoritativeDir &&
+ node->ri &&
+ node->ri->purpose != ROUTER_PURPOSE_BRIDGE)
+ continue;
if (router_counts_toward_thresholds(node, now, omit_as_sybil,
require_mbw)) {
routerinfo_t *ri = node->ri;
@@ -2070,6 +2075,21 @@ dirserv_compute_performance_thresholds(routerlist_t *rl,
tor_free(wfus);
}
+/* Use dirserv_compute_performance_thresholds() to compute the thresholds
+ * for the status flags, specifically for bridges.
+ *
+ * This is only called by a Bridge Authority from
+ * networkstatus_getinfo_by_purpose().
+ */
+void
+dirserv_compute_bridge_flag_thresholds(routerlist_t *rl)
+{
+
+ digestmap_t *omit_as_sybil = digestmap_new();
+ dirserv_compute_performance_thresholds(rl, omit_as_sybil);
+ digestmap_free(omit_as_sybil, NULL);
+}
+
/** Measured bandwidth cache entry */
typedef struct mbw_cache_entry_s {
long mbw_kb;
@@ -2082,7 +2102,7 @@ static digestmap_t *mbw_cache = NULL;
/** Store a measured bandwidth cache entry when reading the measured
* bandwidths file. */
-void
+STATIC void
dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line,
time_t as_of)
{
@@ -2112,7 +2132,7 @@ dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line,
}
/** Clear and free the measured bandwidth cache */
-void
+STATIC void
dirserv_clear_measured_bw_cache(void)
{
if (mbw_cache) {
@@ -2123,7 +2143,7 @@ dirserv_clear_measured_bw_cache(void)
}
/** Scan the measured bandwidth cache and remove expired entries */
-void
+STATIC void
dirserv_expire_measured_bw_cache(time_t now)
{
@@ -2145,7 +2165,7 @@ dirserv_expire_measured_bw_cache(time_t now)
}
/** Get the current size of the measured bandwidth cache */
-int
+STATIC int
dirserv_get_measured_bw_cache_size(void)
{
if (mbw_cache) return digestmap_size(mbw_cache);
@@ -2155,7 +2175,7 @@ dirserv_get_measured_bw_cache_size(void)
/** Query the cache by identity digest, return value indicates whether
* we found it. The bw_out and as_of_out pointers receive the cached
* bandwidth value and the time it was cached if not NULL. */
-int
+STATIC int
dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_kb_out,
time_t *as_of_out)
{
@@ -2176,7 +2196,7 @@ dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_kb_out,
}
/** Predicate wrapper for dirserv_query_measured_bw_cache() */
-int
+STATIC int
dirserv_has_measured_bw(const char *node_id)
{
return dirserv_query_measured_bw_cache_kb(node_id, NULL, NULL);
@@ -2705,6 +2725,11 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs,
} else {
rs->is_possible_guard = 0;
}
+ if (options->TestingTorNetwork &&
+ routerset_contains_routerstatus(options->TestingDirAuthVoteGuard,
+ rs, 0)) {
+ rs->is_possible_guard = 1;
+ }
rs->is_bad_directory = listbaddirs && node->is_bad_directory;
rs->is_bad_exit = listbadexits && node->is_bad_exit;
@@ -2754,7 +2779,7 @@ clear_status_flags_on_sybil(routerstatus_t *rs)
* into a measured_bw_line_t output structure. Returns -1 on failure
* or 0 on success.
*/
-int
+STATIC int
measured_bw_line_parse(measured_bw_line_t *out, const char *orig_line)
{
char *line = tor_strdup(orig_line);
@@ -2835,7 +2860,7 @@ measured_bw_line_parse(measured_bw_line_t *out, const char *orig_line)
* of bandwidth statuses. Returns true if a line is found,
* false otherwise.
*/
-int
+STATIC int
measured_bw_line_apply(measured_bw_line_t *parsed_line,
smartlist_t *routerstatuses)
{
@@ -3093,7 +3118,8 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
else
last_consensus_interval = options->TestingV3AuthInitialVotingInterval;
v3_out->valid_after =
- dirvote_get_start_of_next_interval(now, (int)last_consensus_interval);
+ dirvote_get_start_of_next_interval(now, (int)last_consensus_interval,
+ options->TestingV3AuthVotingStartOffset);
format_iso_time(tbuf, v3_out->valid_after);
log_notice(LD_DIR,"Choosing valid-after time in vote as %s: "
"consensus_set=%d, last_interval=%d",
@@ -3167,7 +3193,7 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
/** For v2 authoritative directories only: Replace the contents of
* <b>the_v2_networkstatus</b> with a newly generated network status
* object. */
-cached_dir_t *
+STATIC cached_dir_t *
generate_v2_networkstatus_opinion(void)
{
cached_dir_t *r = NULL;
diff --git a/src/or/dirserv.h b/src/or/dirserv.h
index f9d36d760..c2d6131c7 100644
--- a/src/or/dirserv.h
+++ b/src/or/dirserv.h
@@ -12,6 +12,8 @@
#ifndef TOR_DIRSERV_H
#define TOR_DIRSERV_H
+#include "testsupport.h"
+
/** What fraction (1 over this number) of the relay ID space do we
* (as a directory authority) launch connections to at each reachability
* test? */
@@ -49,6 +51,7 @@ int list_server_status_v1(smartlist_t *routers, char **router_status_out,
int dirserv_dump_directory_to_string(char **dir_out,
crypto_pk_t *private_key);
char *dirserv_get_flag_thresholds_line(void);
+void dirserv_compute_bridge_flag_thresholds(routerlist_t *rl);
int directory_fetches_from_authorities(const or_options_t *options);
int directory_fetches_dir_info_early(const or_options_t *options);
@@ -119,20 +122,21 @@ cached_dir_t *new_cached_dir(char *s, time_t published);
/* Put the MAX_MEASUREMENT_AGE #define here so unit tests can see it */
#define MAX_MEASUREMENT_AGE (3*24*60*60) /* 3 days */
-int measured_bw_line_parse(measured_bw_line_t *out, const char *line);
+STATIC int measured_bw_line_parse(measured_bw_line_t *out, const char *line);
-int measured_bw_line_apply(measured_bw_line_t *parsed_line,
+STATIC int measured_bw_line_apply(measured_bw_line_t *parsed_line,
smartlist_t *routerstatuses);
-void dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line,
+STATIC void dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line,
time_t as_of);
-void dirserv_clear_measured_bw_cache(void);
-void dirserv_expire_measured_bw_cache(time_t now);
-int dirserv_get_measured_bw_cache_size(void);
-int dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_out,
- time_t *as_of_out);
-int dirserv_has_measured_bw(const char *node_id);
-cached_dir_t *generate_v2_networkstatus_opinion(void);
+STATIC void dirserv_clear_measured_bw_cache(void);
+STATIC void dirserv_expire_measured_bw_cache(time_t now);
+STATIC int dirserv_get_measured_bw_cache_size(void);
+STATIC int dirserv_query_measured_bw_cache_kb(const char *node_id,
+ long *bw_out,
+ time_t *as_of_out);
+STATIC int dirserv_has_measured_bw(const char *node_id);
+STATIC cached_dir_t *generate_v2_networkstatus_opinion(void);
#endif
int dirserv_read_measured_bandwidths(const char *from_file,
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index c6d124490..4d3ee9cdb 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -60,7 +60,7 @@ static char *make_consensus_method_list(int low, int high, const char *sep);
/** Return a new string containing the string representation of the vote in
* <b>v3_ns</b>, signed with our v3 signing key <b>private_signing_key</b>.
* For v3 authorities. */
-char *
+STATIC char *
format_networkstatus_vote(crypto_pk_t *private_signing_key,
networkstatus_t *v3_ns)
{
@@ -587,7 +587,7 @@ compute_consensus_versions_list(smartlist_t *lst, int n_versioning)
/** Helper: given a list of valid networkstatus_t, return a new string
* containing the contents of the consensus network parameter set.
*/
-/* private */ char *
+STATIC char *
dirvote_compute_params(smartlist_t *votes, int method, int total_authorities)
{
int i;
@@ -2533,12 +2533,13 @@ dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out)
timing_out->dist_delay = options->V3AuthDistDelay;
}
-/** Return the start of the next interval of size <b>interval</b> (in seconds)
- * after <b>now</b>. Midnight always starts a fresh interval, and if the last
- * interval of a day would be truncated to less than half its size, it is
- * rolled into the previous interval. */
+/** Return the start of the next interval of size <b>interval</b> (in
+ * seconds) after <b>now</b>, plus <b>offset</b>. Midnight always
+ * starts a fresh interval, and if the last interval of a day would be
+ * truncated to less than half its size, it is rolled into the
+ * previous interval. */
time_t
-dirvote_get_start_of_next_interval(time_t now, int interval)
+dirvote_get_start_of_next_interval(time_t now, int interval, int offset)
{
struct tm tm;
time_t midnight_today=0;
@@ -2566,6 +2567,10 @@ dirvote_get_start_of_next_interval(time_t now, int interval)
if (next + interval/2 > midnight_tomorrow)
next = midnight_tomorrow;
+ next += offset;
+ if (next - interval > now)
+ next -= interval;
+
return next;
}
@@ -2629,8 +2634,10 @@ dirvote_recalculate_timing(const or_options_t *options, time_t now)
vote_delay = dist_delay = interval / 4;
start = voting_schedule.interval_starts =
- dirvote_get_start_of_next_interval(now,interval);
- end = dirvote_get_start_of_next_interval(start+1, interval);
+ dirvote_get_start_of_next_interval(now,interval,
+ options->TestingV3AuthVotingStartOffset);
+ end = dirvote_get_start_of_next_interval(start+1, interval,
+ options->TestingV3AuthVotingStartOffset);
tor_assert(end > start);
@@ -3136,7 +3143,7 @@ dirvote_compute_consensuses(void)
});
votefile = get_datadir_fname("v3-status-votes");
- write_chunks_to_file(votefile, votestrings, 0);
+ write_chunks_to_file(votefile, votestrings, 0, 0);
tor_free(votefile);
SMARTLIST_FOREACH(votestrings, sized_chunk_t *, c, tor_free(c));
smartlist_free(votestrings);
diff --git a/src/or/dirvote.h b/src/or/dirvote.h
index b23645212..3a4951a95 100644
--- a/src/or/dirvote.h
+++ b/src/or/dirvote.h
@@ -12,10 +12,12 @@
#ifndef TOR_DIRVOTE_H
#define TOR_DIRVOTE_H
+#include "testsupport.h"
+
/** Lowest allowable value for VoteSeconds. */
-#define MIN_VOTE_SECONDS 20
+#define MIN_VOTE_SECONDS 2
/** Lowest allowable value for DistSeconds. */
-#define MIN_DIST_SECONDS 20
+#define MIN_DIST_SECONDS 2
/** Smallest allowable voting interval. */
#define MIN_VOTE_INTERVAL 300
@@ -86,7 +88,9 @@ authority_cert_t *authority_cert_dup(authority_cert_t *cert);
/* vote scheduling */
void dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out);
-time_t dirvote_get_start_of_next_interval(time_t now, int interval);
+time_t dirvote_get_start_of_next_interval(time_t now,
+ int interval,
+ int offset);
void dirvote_recalculate_timing(const or_options_t *options, time_t now);
void dirvote_act(const or_options_t *options, time_t now);
@@ -134,9 +138,9 @@ document_signature_t *voter_get_sig_by_algorithm(
digest_algorithm_t alg);
#ifdef DIRVOTE_PRIVATE
-char *format_networkstatus_vote(crypto_pk_t *private_key,
+STATIC char *format_networkstatus_vote(crypto_pk_t *private_key,
networkstatus_t *v3_ns);
-char *dirvote_compute_params(smartlist_t *votes, int method,
+STATIC char *dirvote_compute_params(smartlist_t *votes, int method,
int total_authorities);
#endif
diff --git a/src/or/dns.c b/src/or/dns.c
index f2b7eecc3..a1fe0de1d 100644
--- a/src/or/dns.c
+++ b/src/or/dns.c
@@ -24,6 +24,7 @@
#include "relay.h"
#include "router.h"
#include "ht.h"
+#include "../common/sandbox.h"
#ifdef HAVE_EVENT2_DNS_H
#include <event2/event.h>
#include <event2/dns.h>
@@ -1443,13 +1444,14 @@ configure_nameservers(int force)
const or_options_t *options;
const char *conf_fname;
struct stat st;
- int r;
+ int r, flags;
options = get_options();
conf_fname = options->ServerDNSResolvConfFile;
#ifndef _WIN32
if (!conf_fname)
conf_fname = "/etc/resolv.conf";
#endif
+ flags = DNS_OPTIONS_ALL;
if (!the_evdns_base) {
if (!(the_evdns_base = evdns_base_new(tor_libevent_get_base(), 0))) {
@@ -1477,7 +1479,7 @@ configure_nameservers(int force)
evdns_set_log_fn(evdns_log_cb);
if (conf_fname) {
- if (stat(conf_fname, &st)) {
+ if (stat(sandbox_intern_string(conf_fname), &st)) {
log_warn(LD_EXIT, "Unable to stat resolver configuration in '%s': %s",
conf_fname, strerror(errno));
goto err;
@@ -1491,9 +1493,16 @@ configure_nameservers(int force)
evdns_base_search_clear(the_evdns_base);
evdns_base_clear_nameservers_and_suspend(the_evdns_base);
}
+#if defined(DNS_OPTION_HOSTSFILE) && defined(USE_LIBSECCOMP)
+ if (flags & DNS_OPTION_HOSTSFILE) {
+ flags ^= DNS_OPTION_HOSTSFILE;
+ evdns_base_load_hosts(the_evdns_base,
+ sandbox_intern_string("/etc/hosts"));
+ }
+#endif
log_info(LD_EXIT, "Parsing resolver configuration in '%s'", conf_fname);
- if ((r = evdns_base_resolv_conf_parse(the_evdns_base,
- DNS_OPTIONS_ALL, conf_fname))) {
+ if ((r = evdns_base_resolv_conf_parse(the_evdns_base, flags,
+ sandbox_intern_string(conf_fname)))) {
log_warn(LD_EXIT, "Unable to parse '%s', or no nameservers in '%s' (%d)",
conf_fname, conf_fname, r);
goto err;
diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c
index 2aa063cda..d463303fc 100644
--- a/src/or/entrynodes.c
+++ b/src/or/entrynodes.c
@@ -13,6 +13,7 @@
**/
#include "or.h"
+#include "circpathbias.h"
#include "circuitbuild.h"
#include "circuitstats.h"
#include "config.h"
@@ -54,6 +55,10 @@ typedef struct {
/** When should we next try to fetch a descriptor for this bridge? */
download_status_t fetch_status;
+
+ /** A smartlist of k=v values to be passed to the SOCKS proxy, if
+ transports are used for this bridge. */
+ smartlist_t *socks_args;
} bridge_info_t;
/** A list of our chosen entry guards. */
@@ -359,7 +364,7 @@ add_an_entry_guard(const node_t *chosen, int reset_status, int prepend,
entry->can_retry = 1;
}
entry->is_dir_cache = node->rs &&
- node->rs->version_supports_microdesc_cache;
+ node->rs->version_supports_microdesc_cache;
if (get_options()->UseBridges && node_is_a_configured_bridge(node))
entry->is_dir_cache = 1;
return NULL;
@@ -392,8 +397,8 @@ add_an_entry_guard(const node_t *chosen, int reset_status, int prepend,
node_describe(node));
strlcpy(entry->nickname, node_get_nickname(node), sizeof(entry->nickname));
memcpy(entry->identity, node->identity, DIGEST_LEN);
- entry->is_dir_cache = node_is_dir(node) &&
- node->rs && node->rs->version_supports_microdesc_cache;
+ entry->is_dir_cache = node_is_dir(node) && node->rs &&
+ node->rs->version_supports_microdesc_cache;
if (get_options()->UseBridges && node_is_a_configured_bridge(node))
entry->is_dir_cache = 1;
@@ -594,6 +599,25 @@ remove_dead_entry_guards(time_t now)
return changed ? 1 : 0;
}
+/** Remove all currently listed entry guards. So new ones will be chosen. */
+void
+remove_all_entry_guards(void)
+{
+ char dbuf[HEX_DIGEST_LEN+1];
+
+ while (smartlist_len(entry_guards)) {
+ entry_guard_t *entry = smartlist_get(entry_guards, 0);
+ base16_encode(dbuf, sizeof(dbuf), entry->identity, DIGEST_LEN);
+ log_info(LD_CIRC, "Entry guard '%s' (%s) has been dropped.",
+ entry->nickname, dbuf);
+ control_event_guard(entry->nickname, entry->identity, "DROPPED");
+ entry_guard_free(entry);
+ smartlist_del(entry_guards, 0);
+ }
+ log_entry_guards(LOG_INFO);
+ entry_guards_changed();
+}
+
/** A new directory or router-status has arrived; update the down/listed
* status of the entry guards.
*
@@ -1595,6 +1619,11 @@ bridge_free(bridge_info_t *bridge)
return;
tor_free(bridge->transport_name);
+ if (bridge->socks_args) {
+ SMARTLIST_FOREACH(bridge->socks_args, char*, s, tor_free(s));
+ smartlist_free(bridge->socks_args);
+ }
+
tor_free(bridge);
}
@@ -1628,7 +1657,8 @@ get_configured_bridge_by_orports_digest(const char *digest,
/** If we have a bridge configured whose digest matches <b>digest</b>, or a
* bridge with no known digest whose address matches <b>addr</b>:<b>/port</b>,
- * return that bridge. Else return NULL. */
+ * return that bridge. Else return NULL. If <b>digest</b> is NULL, check for
+ * address/port matches only. */
static bridge_info_t *
get_configured_bridge_by_addr_port_digest(const tor_addr_t *addr,
uint16_t port,
@@ -1638,7 +1668,7 @@ get_configured_bridge_by_addr_port_digest(const tor_addr_t *addr,
return NULL;
SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge)
{
- if (tor_digest_is_zero(bridge->identity) &&
+ if ((tor_digest_is_zero(bridge->identity) || digest == NULL) &&
!tor_addr_compare(&bridge->addr, addr, CMP_EXACT) &&
bridge->port == port)
return bridge;
@@ -1773,30 +1803,68 @@ bridge_resolve_conflicts(const tor_addr_t *addr, uint16_t port,
} SMARTLIST_FOREACH_END(bridge);
}
-/** Remember a new bridge at <b>addr</b>:<b>port</b>. If <b>digest</b>
- * is set, it tells us the identity key too. If we already had the
- * bridge in our list, unmark it, and don't actually add anything new.
- * If <b>transport_name</b> is non-NULL - the bridge is associated with a
- * pluggable transport - we assign the transport to the bridge. */
+/** Return True if we have a bridge that uses a transport with name
+ * <b>transport_name</b>. */
+int
+transport_is_needed(const char *transport_name)
+{
+ if (!bridge_list)
+ return 0;
+
+ SMARTLIST_FOREACH_BEGIN(bridge_list, const bridge_info_t *, bridge) {
+ if (bridge->transport_name &&
+ !strcmp(bridge->transport_name, transport_name))
+ return 1;
+ } SMARTLIST_FOREACH_END(bridge);
+
+ return 0;
+}
+
+/** Register the bridge information in <b>bridge_line</b> to the
+ * bridge subsystem. Steals reference of <b>bridge_line</b>. */
void
-bridge_add_from_config(const tor_addr_t *addr, uint16_t port,
- const char *digest, const char *transport_name)
+bridge_add_from_config(bridge_line_t *bridge_line)
{
bridge_info_t *b;
- bridge_resolve_conflicts(addr, port, digest, transport_name);
+ { /* Log the bridge we are about to register: */
+ log_debug(LD_GENERAL, "Registering bridge at %s (transport: %s) (%s)",
+ fmt_addrport(&bridge_line->addr, bridge_line->port),
+ bridge_line->transport_name ?
+ bridge_line->transport_name : "no transport",
+ tor_digest_is_zero(bridge_line->digest) ?
+ "no key listed" : hex_str(bridge_line->digest, DIGEST_LEN));
+
+ if (bridge_line->socks_args) { /* print socks arguments */
+ int i = 0;
+
+ tor_assert(smartlist_len(bridge_line->socks_args) > 0);
+
+ log_debug(LD_GENERAL, "Bridge uses %d SOCKS arguments:",
+ smartlist_len(bridge_line->socks_args));
+ SMARTLIST_FOREACH(bridge_line->socks_args, const char *, arg,
+ log_debug(LD_CONFIG, "%d: %s", ++i, arg));
+ }
+ }
+
+ bridge_resolve_conflicts(&bridge_line->addr,
+ bridge_line->port,
+ bridge_line->digest,
+ bridge_line->transport_name);
b = tor_malloc_zero(sizeof(bridge_info_t));
- tor_addr_copy(&b->addr, addr);
- b->port = port;
- if (digest)
- memcpy(b->identity, digest, DIGEST_LEN);
- if (transport_name)
- b->transport_name = tor_strdup(transport_name);
+ tor_addr_copy(&b->addr, &bridge_line->addr);
+ b->port = bridge_line->port;
+ memcpy(b->identity, bridge_line->digest, DIGEST_LEN);
+ if (bridge_line->transport_name)
+ b->transport_name = bridge_line->transport_name;
b->fetch_status.schedule = DL_SCHED_BRIDGE;
+ b->socks_args = bridge_line->socks_args;
if (!bridge_list)
bridge_list = smartlist_new();
+ tor_free(bridge_line); /* Deallocate bridge_line now. */
+
smartlist_add(bridge_list, b);
}
@@ -1857,7 +1925,7 @@ find_transport_name_by_bridge_addrport(const tor_addr_t *addr, uint16_t port)
* transport, but the transport could not be found.
*/
int
-find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port,
+get_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port,
const transport_t **transport)
{
*transport = NULL;
@@ -1884,6 +1952,17 @@ find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port,
return 0;
}
+/** Return a smartlist containing all the SOCKS arguments that we
+ * should pass to the SOCKS proxy. */
+const smartlist_t *
+get_socks_args_by_bridge_addrport(const tor_addr_t *addr, uint16_t port)
+{
+ bridge_info_t *bridge = get_configured_bridge_by_addr_port_digest(addr,
+ port,
+ NULL);
+ return bridge ? bridge->socks_args : NULL;
+}
+
/** We need to ask <b>bridge</b> for its server descriptor. */
static void
launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge)
@@ -2250,6 +2329,6 @@ entry_guards_free_all(void)
clear_bridge_list();
smartlist_free(bridge_list);
bridge_list = NULL;
- circuit_build_times_free_timeouts(&circ_times);
+ circuit_build_times_free_timeouts(get_circuit_build_times_mutable());
}
diff --git a/src/or/entrynodes.h b/src/or/entrynodes.h
index 52b8dc00e..772c6662d 100644
--- a/src/or/entrynodes.h
+++ b/src/or/entrynodes.h
@@ -77,6 +77,8 @@ int num_live_entry_guards(int for_directory);
#endif
+void remove_all_entry_guards(void);
+
void entry_guards_compute_status(const or_options_t *options, time_t now);
int entry_guard_register_connect_status(const char *digest, int succeeded,
int mark_relay_status, time_t now);
@@ -97,9 +99,8 @@ int routerinfo_is_a_configured_bridge(const routerinfo_t *ri);
int node_is_a_configured_bridge(const node_t *node);
void learned_router_identity(const tor_addr_t *addr, uint16_t port,
const char *digest);
-void bridge_add_from_config(const tor_addr_t *addr, uint16_t port,
- const char *digest,
- const char *transport_name);
+struct bridge_line_t;
+void bridge_add_from_config(struct bridge_line_t *bridge_line);
void retry_bridge_descriptor_fetch_directly(const char *digest);
void fetch_bridge_descriptors(const or_options_t *options, time_t now);
void learned_bridge_descriptor(routerinfo_t *ri, int from_cache);
@@ -109,15 +110,20 @@ int entries_known_but_down(const or_options_t *options);
void entries_retry_all(const or_options_t *options);
int any_bridge_supports_microdescriptors(void);
+const smartlist_t *get_socks_args_by_bridge_addrport(const tor_addr_t *addr,
+ uint16_t port);
+
+int any_bridges_dont_support_microdescriptors(void);
void entry_guards_free_all(void);
const char *find_transport_name_by_bridge_addrport(const tor_addr_t *addr,
uint16_t port);
struct transport_t;
-int find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port,
+int get_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port,
const struct transport_t **transport);
+int transport_is_needed(const char *transport_name);
int validate_pluggable_transports_config(void);
double pathbias_get_close_success_count(entry_guard_t *guard);
diff --git a/src/or/ext_orport.c b/src/or/ext_orport.c
new file mode 100644
index 000000000..d5a0fa1ee
--- /dev/null
+++ b/src/or/ext_orport.c
@@ -0,0 +1,648 @@
+/* Copyright (c) 2012, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file ext_orport.c
+ * \brief Code implementing the Extended ORPort.
+*/
+
+#define EXT_ORPORT_PRIVATE
+#include "or.h"
+#include "connection.h"
+#include "connection_or.h"
+#include "ext_orport.h"
+#include "control.h"
+#include "config.h"
+#include "util.h"
+#include "main.h"
+
+/** Allocate and return a structure capable of holding an Extended
+ * ORPort message of body length <b>len</b>. */
+ext_or_cmd_t *
+ext_or_cmd_new(uint16_t len)
+{
+ size_t size = STRUCT_OFFSET(ext_or_cmd_t, body) + len;
+ ext_or_cmd_t *cmd = tor_malloc(size);
+ cmd->len = len;
+ return cmd;
+}
+
+/** Deallocate the Extended ORPort message in <b>cmd</b>. */
+void
+ext_or_cmd_free(ext_or_cmd_t *cmd)
+{
+ tor_free(cmd);
+}
+
+/** Get an Extended ORPort message from <b>conn</b>, and place it in
+ * <b>out</b>. Return -1 on fail, 0 if we need more data, and 1 if we
+ * successfully extracted an Extended ORPort command from the
+ * buffer. */
+static int
+connection_fetch_ext_or_cmd_from_buf(connection_t *conn, ext_or_cmd_t **out)
+{
+ IF_HAS_BUFFEREVENT(conn, {
+ struct evbuffer *input = bufferevent_get_input(conn->bufev);
+ return fetch_ext_or_command_from_evbuffer(input, out);
+ }) ELSE_IF_NO_BUFFEREVENT {
+ return fetch_ext_or_command_from_buf(conn->inbuf, out);
+ }
+}
+
+/** Write an Extended ORPort message to <b>conn</b>. Use
+ * <b>command</b> as the command type, <b>bodylen</b> as the body
+ * length, and <b>body</b>, if it's present, as the body of the
+ * message. */
+STATIC int
+connection_write_ext_or_command(connection_t *conn,
+ uint16_t command,
+ const char *body,
+ size_t bodylen)
+{
+ char header[4];
+ if (bodylen > UINT16_MAX)
+ return -1;
+ set_uint16(header, htons(command));
+ set_uint16(header+2, htons(bodylen));
+ connection_write_to_buf(header, 4, conn);
+ if (bodylen) {
+ tor_assert(body);
+ connection_write_to_buf(body, bodylen, conn);
+ }
+ return 0;
+}
+
+/** Transition from an Extended ORPort which accepts Extended ORPort
+ * messages, to an Extended ORport which accepts OR traffic. */
+static void
+connection_ext_or_transition(or_connection_t *conn)
+{
+ tor_assert(conn->base_.type == CONN_TYPE_EXT_OR);
+
+ conn->base_.type = CONN_TYPE_OR;
+ TO_CONN(conn)->state = 0; // set the state to a neutral value
+ control_event_or_conn_status(conn, OR_CONN_EVENT_NEW, 0);
+ connection_tls_start_handshake(conn, 1);
+}
+
+/** Length of authentication cookie. */
+#define EXT_OR_PORT_AUTH_COOKIE_LEN 32
+/** Length of the header of the cookie file. */
+#define EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN 32
+/** Static cookie file header. */
+#define EXT_OR_PORT_AUTH_COOKIE_HEADER "! Extended ORPort Auth Cookie !\x0a"
+/** Length of safe-cookie protocol hashes. */
+#define EXT_OR_PORT_AUTH_HASH_LEN DIGEST256_LEN
+/** Length of safe-cookie protocol nonces. */
+#define EXT_OR_PORT_AUTH_NONCE_LEN 32
+/** Safe-cookie protocol constants. */
+#define EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST \
+ "ExtORPort authentication server-to-client hash"
+#define EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST \
+ "ExtORPort authentication client-to-server hash"
+
+/* Code to indicate cookie authentication */
+#define EXT_OR_AUTHTYPE_SAFECOOKIE 0x01
+
+/** If true, we've set ext_or_auth_cookie to a secret code and stored
+ * it to disk. */
+STATIC int ext_or_auth_cookie_is_set = 0;
+/** If ext_or_auth_cookie_is_set, a secret cookie that we've stored to disk
+ * and which we're using to authenticate controllers. (If the controller can
+ * read it off disk, it has permission to connect.) */
+STATIC uint8_t *ext_or_auth_cookie = NULL;
+
+/** Helper: Return a newly allocated string containing a path to the
+ * file where we store our authentication cookie. */
+char *
+get_ext_or_auth_cookie_file_name(void)
+{
+ const or_options_t *options = get_options();
+ if (options->ExtORPortCookieAuthFile &&
+ strlen(options->ExtORPortCookieAuthFile)) {
+ return tor_strdup(options->ExtORPortCookieAuthFile);
+ } else {
+ return get_datadir_fname("extended_orport_auth_cookie");
+ }
+}
+
+/* Initialize the cookie-based authentication system of the
+ * Extended ORPort. If <b>is_enabled</b> is 0, then disable the cookie
+ * authentication system. */
+int
+init_ext_or_cookie_authentication(int is_enabled)
+{
+ char *fname = NULL;
+ int retval;
+
+ if (!is_enabled) {
+ ext_or_auth_cookie_is_set = 0;
+ return 0;
+ }
+
+ fname = get_ext_or_auth_cookie_file_name();
+ retval = init_cookie_authentication(fname, EXT_OR_PORT_AUTH_COOKIE_HEADER,
+ EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN,
+ &ext_or_auth_cookie,
+ &ext_or_auth_cookie_is_set);
+ tor_free(fname);
+ return retval;
+}
+
+/** Read data from <b>conn</b> and see if the client sent us the
+ * authentication type that she prefers to use in this session.
+ *
+ * Return -1 if we received corrupted data or if we don't support the
+ * authentication type. Return 0 if we need more data in
+ * <b>conn</b>. Return 1 if the authentication type negotiation was
+ * successful. */
+static int
+connection_ext_or_auth_neg_auth_type(connection_t *conn)
+{
+ char authtype[1] = {0};
+
+ if (connection_get_inbuf_len(conn) < 1)
+ return 0;
+
+ if (connection_fetch_from_buf(authtype, 1, conn) < 0)
+ return -1;
+
+ log_debug(LD_GENERAL, "Client wants us to use %d auth type", authtype[0]);
+ if (authtype[0] != EXT_OR_AUTHTYPE_SAFECOOKIE) {
+ /* '1' is the only auth type supported atm */
+ return -1;
+ }
+
+ conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE;
+ return 1;
+}
+
+/** DOCDOC */
+STATIC int
+handle_client_auth_nonce(const char *client_nonce, size_t client_nonce_len,
+ char **client_hash_out,
+ char **reply_out, size_t *reply_len_out)
+{
+ char server_hash[EXT_OR_PORT_AUTH_HASH_LEN] = {0};
+ char server_nonce[EXT_OR_PORT_AUTH_NONCE_LEN] = {0};
+ char *reply;
+ size_t reply_len;
+
+ if (client_nonce_len != EXT_OR_PORT_AUTH_NONCE_LEN)
+ return -1;
+
+ /* Get our nonce */
+ if (crypto_rand(server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN) < 0)
+ return -1;
+
+ { /* set up macs */
+ size_t hmac_s_msg_len = strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST) +
+ 2*EXT_OR_PORT_AUTH_NONCE_LEN;
+ size_t hmac_c_msg_len = strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST) +
+ 2*EXT_OR_PORT_AUTH_NONCE_LEN;
+
+ char *hmac_s_msg = tor_malloc_zero(hmac_s_msg_len);
+ char *hmac_c_msg = tor_malloc_zero(hmac_c_msg_len);
+ char *correct_client_hash = tor_malloc_zero(EXT_OR_PORT_AUTH_HASH_LEN);
+
+ memcpy(hmac_s_msg,
+ EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST,
+ strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST));
+ memcpy(hmac_s_msg + strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST),
+ client_nonce, EXT_OR_PORT_AUTH_NONCE_LEN);
+ memcpy(hmac_s_msg + strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST) +
+ EXT_OR_PORT_AUTH_NONCE_LEN,
+ server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN);
+
+ memcpy(hmac_c_msg,
+ EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST,
+ strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST));
+ memcpy(hmac_c_msg + strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST),
+ client_nonce, EXT_OR_PORT_AUTH_NONCE_LEN);
+ memcpy(hmac_c_msg + strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST) +
+ EXT_OR_PORT_AUTH_NONCE_LEN,
+ server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN);
+
+ crypto_hmac_sha256(server_hash,
+ (char*)ext_or_auth_cookie,
+ EXT_OR_PORT_AUTH_COOKIE_LEN,
+ hmac_s_msg,
+ hmac_s_msg_len);
+
+ crypto_hmac_sha256(correct_client_hash,
+ (char*)ext_or_auth_cookie,
+ EXT_OR_PORT_AUTH_COOKIE_LEN,
+ hmac_c_msg,
+ hmac_c_msg_len);
+
+ /* Store the client hash we generated. We will need to compare it
+ with the hash sent by the client. */
+ *client_hash_out = correct_client_hash;
+
+ memwipe(hmac_s_msg, 0, hmac_s_msg_len);
+ memwipe(hmac_c_msg, 0, hmac_c_msg_len);
+
+ tor_free(hmac_s_msg);
+ tor_free(hmac_c_msg);
+ }
+
+ { /* debug logging */ /* XXX disable this codepath if not logging on debug?*/
+ char server_hash_encoded[(2*EXT_OR_PORT_AUTH_HASH_LEN) + 1];
+ char server_nonce_encoded[(2*EXT_OR_PORT_AUTH_NONCE_LEN) + 1];
+ char client_nonce_encoded[(2*EXT_OR_PORT_AUTH_NONCE_LEN) + 1];
+
+ base16_encode(server_hash_encoded, sizeof(server_hash_encoded),
+ server_hash, sizeof(server_hash));
+ base16_encode(server_nonce_encoded, sizeof(server_nonce_encoded),
+ server_nonce, sizeof(server_nonce));
+ base16_encode(client_nonce_encoded, sizeof(client_nonce_encoded),
+ client_nonce, sizeof(client_nonce));
+
+ log_debug(LD_GENERAL,
+ "server_hash: '%s'\nserver_nonce: '%s'\nclient_nonce: '%s'",
+ server_hash_encoded, server_nonce_encoded, client_nonce_encoded);
+
+ memwipe(server_hash_encoded, 0, sizeof(server_hash_encoded));
+ memwipe(server_nonce_encoded, 0, sizeof(server_nonce_encoded));
+ memwipe(client_nonce_encoded, 0, sizeof(client_nonce_encoded));
+ }
+
+ { /* write reply: (server_hash, server_nonce) */
+
+ reply_len = EXT_OR_PORT_AUTH_COOKIE_LEN+EXT_OR_PORT_AUTH_NONCE_LEN;
+ reply = tor_malloc_zero(reply_len);
+ memcpy(reply, server_hash, EXT_OR_PORT_AUTH_HASH_LEN);
+ memcpy(reply + EXT_OR_PORT_AUTH_HASH_LEN, server_nonce,
+ EXT_OR_PORT_AUTH_NONCE_LEN);
+ }
+
+ *reply_out = reply;
+ *reply_len_out = reply_len;
+
+ return 0;
+}
+
+/** Read the client's nonce out of <b>conn</b>, setup the safe-cookie
+ * crypto, and then send our own hash and nonce to the client
+ *
+ * Return -1 if there was an error; return 0 if we need more data in
+ * <b>conn</b>, and return 1 if we successfully retrieved the
+ * client's nonce and sent our own. */
+static int
+connection_ext_or_auth_handle_client_nonce(connection_t *conn)
+{
+ char client_nonce[EXT_OR_PORT_AUTH_NONCE_LEN];
+ char *reply=NULL;
+ size_t reply_len=0;
+
+ if (!ext_or_auth_cookie_is_set) { /* this should not happen */
+ log_warn(LD_BUG, "Extended ORPort authentication cookie was not set. "
+ "That's weird since we should have done that on startup. "
+ "This might be a Tor bug, please file a bug report. ");
+ return -1;
+ }
+
+ if (connection_get_inbuf_len(conn) < EXT_OR_PORT_AUTH_NONCE_LEN)
+ return 0;
+
+ if (connection_fetch_from_buf(client_nonce,
+ EXT_OR_PORT_AUTH_NONCE_LEN, conn) < 0)
+ return -1;
+
+ /* We extract the ClientNonce from the received data, and use it to
+ calculate ServerHash and ServerNonce according to proposal 217.
+
+ We also calculate our own ClientHash value and save it in the
+ connection state. We validate it later against the ClientHash
+ sent by the client. */
+ if (handle_client_auth_nonce(client_nonce, sizeof(client_nonce),
+ &TO_OR_CONN(conn)->ext_or_auth_correct_client_hash,
+ &reply, &reply_len) < 0)
+ return -1;
+
+ connection_write_to_buf(reply, reply_len, conn);
+
+ memwipe(reply, 0, reply_len);
+ tor_free(reply);
+
+ log_debug(LD_GENERAL, "Got client nonce, and sent our own nonce and hash.");
+
+ conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH;
+ return 1;
+}
+
+#define connection_ext_or_auth_send_result_success(c) \
+ connection_ext_or_auth_send_result(c, 1)
+#define connection_ext_or_auth_send_result_fail(c) \
+ connection_ext_or_auth_send_result(c, 0)
+
+/** Send authentication results to <b>conn</b>. Successful results if
+ * <b>success</b> is set; failure results otherwise. */
+static void
+connection_ext_or_auth_send_result(connection_t *conn, int success)
+{
+ if (success)
+ connection_write_to_buf("\x01", 1, conn);
+ else
+ connection_write_to_buf("\x00", 1, conn);
+}
+
+/** Receive the client's hash from <b>conn</b>, validate that it's
+ * correct, and then send the authentication results to the client.
+ *
+ * Return -1 if there was an error during validation; return 0 if we
+ * need more data in <b>conn</b>, and return 1 if we successfully
+ * validated the client's hash and sent a happy authentication
+ * result. */
+static int
+connection_ext_or_auth_handle_client_hash(connection_t *conn)
+{
+ char provided_client_hash[EXT_OR_PORT_AUTH_HASH_LEN] = {0};
+
+ if (connection_get_inbuf_len(conn) < EXT_OR_PORT_AUTH_HASH_LEN)
+ return 0;
+
+ if (connection_fetch_from_buf(provided_client_hash,
+ EXT_OR_PORT_AUTH_HASH_LEN, conn) < 0)
+ return -1;
+
+ if (tor_memneq(TO_OR_CONN(conn)->ext_or_auth_correct_client_hash,
+ provided_client_hash, EXT_OR_PORT_AUTH_HASH_LEN)) {
+ log_warn(LD_GENERAL, "Incorrect client hash. Authentication failed.");
+ connection_ext_or_auth_send_result_fail(conn);
+ return -1;
+ }
+
+ log_debug(LD_GENERAL, "Got client's hash and it was legit.");
+
+ /* send positive auth result */
+ connection_ext_or_auth_send_result_success(conn);
+ conn->state = EXT_OR_CONN_STATE_OPEN;
+ return 1;
+}
+
+/** Handle data from <b>or_conn</b> received on Extended ORPort.
+ * Return -1 on error. 0 on unsufficient data. 1 on correct. */
+static int
+connection_ext_or_auth_process_inbuf(or_connection_t *or_conn)
+{
+ connection_t *conn = TO_CONN(or_conn);
+
+ /* State transitions of the Extended ORPort authentication protocol:
+
+ EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE (start state) ->
+ EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE ->
+ EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH ->
+ EXT_OR_CONN_STATE_OPEN
+
+ During EXT_OR_CONN_STATE_OPEN, data is handled by
+ connection_ext_or_process_inbuf().
+ */
+
+ switch (conn->state) { /* Functionify */
+ case EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE:
+ return connection_ext_or_auth_neg_auth_type(conn);
+
+ case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE:
+ return connection_ext_or_auth_handle_client_nonce(conn);
+
+ case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH:
+ return connection_ext_or_auth_handle_client_hash(conn);
+
+ default:
+ log_warn(LD_BUG, "Encountered unexpected connection state %d while trying "
+ "to process Extended ORPort authentication data.", conn->state);
+ return -1;
+ }
+}
+
+/** Extended ORPort commands (Transport-to-Bridge) */
+#define EXT_OR_CMD_TB_DONE 0x0000
+#define EXT_OR_CMD_TB_USERADDR 0x0001
+#define EXT_OR_CMD_TB_TRANSPORT 0x0002
+
+/** Extended ORPort commands (Bridge-to-Transport) */
+#define EXT_OR_CMD_BT_OKAY 0x1000
+#define EXT_OR_CMD_BT_DENY 0x1001
+#define EXT_OR_CMD_BT_CONTROL 0x1002
+
+/** Process a USERADDR command from the Extended
+ * ORPort. <b>payload</b> is a payload of size <b>len</b>.
+ *
+ * If the USERADDR command was well formed, change the address of
+ * <b>conn</b> to the address on the USERADDR command.
+ *
+ * Return 0 on success and -1 on error. */
+static int
+connection_ext_or_handle_cmd_useraddr(connection_t *conn,
+ const char *payload, uint16_t len)
+{
+ /* Copy address string. */
+ tor_addr_t addr;
+ uint16_t port;
+ char *addr_str;
+ char *address_part=NULL;
+ int res;
+ if (memchr(payload, '\0', len)) {
+ log_fn(LOG_PROTOCOL_WARN, LD_NET, "Unexpected NUL in ExtORPort UserAddr");
+ return -1;
+ }
+
+ addr_str = tor_memdup_nulterm(payload, len);
+
+ res = tor_addr_port_split(LOG_INFO, addr_str, &address_part, &port);
+ tor_free(addr_str);
+ if (res<0)
+ return -1;
+
+ res = tor_addr_parse(&addr, address_part);
+ tor_free(address_part);
+ if (res<0)
+ return -1;
+
+ { /* do some logging */
+ char *old_address = tor_dup_addr(&conn->addr);
+ char *new_address = tor_dup_addr(&addr);
+
+ log_debug(LD_NET, "Received USERADDR."
+ "We rewrite our address from '%s:%u' to '%s:%u'.",
+ safe_str(old_address), conn->port, safe_str(new_address), port);
+
+ tor_free(old_address);
+ tor_free(new_address);
+ }
+
+ /* record the address */
+ tor_addr_copy(&conn->addr, &addr);
+ conn->port = port;
+ if (conn->address) {
+ tor_free(conn->address);
+ }
+ conn->address = tor_dup_addr(&addr);
+
+ return 0;
+}
+
+/** Process a TRANSPORT command from the Extended
+ * ORPort. <b>payload</b> is a payload of size <b>len</b>.
+ *
+ * If the TRANSPORT command was well formed, register the name of the
+ * transport on <b>conn</b>.
+ *
+ * Return 0 on success and -1 on error. */
+static int
+connection_ext_or_handle_cmd_transport(or_connection_t *conn,
+ const char *payload, uint16_t len)
+{
+ char *transport_str;
+ if (memchr(payload, '\0', len)) {
+ log_fn(LOG_PROTOCOL_WARN, LD_NET, "Unexpected NUL in ExtORPort Transport");
+ return -1;
+ }
+
+ transport_str = tor_memdup_nulterm(payload, len);
+
+ /* Transport names MUST be C-identifiers. */
+ if (!string_is_C_identifier(transport_str)) {
+ tor_free(transport_str);
+ return -1;
+ }
+
+ /* If ext_or_transport is already occupied (because the PT sent two
+ * TRANSPORT commands), deallocate the old name and keep the new
+ * one */
+ if (conn->ext_or_transport)
+ tor_free(conn->ext_or_transport);
+
+ conn->ext_or_transport = transport_str;
+ return 0;
+}
+
+#define EXT_OR_CONN_STATE_IS_AUTHENTICATING(st) \
+ ((st) <= EXT_OR_CONN_STATE_AUTH_MAX)
+
+/** Process Extended ORPort messages from <b>or_conn</b>. */
+int
+connection_ext_or_process_inbuf(or_connection_t *or_conn)
+{
+ connection_t *conn = TO_CONN(or_conn);
+ ext_or_cmd_t *command;
+ int r;
+
+ /* DOCDOC Document the state machine and transitions in this function */
+
+ /* If we are still in the authentication stage, process traffic as
+ authentication data: */
+ while (EXT_OR_CONN_STATE_IS_AUTHENTICATING(conn->state)) {
+ log_debug(LD_GENERAL, "Got Extended ORPort authentication data (%u).",
+ (unsigned int) connection_get_inbuf_len(conn));
+ r = connection_ext_or_auth_process_inbuf(or_conn);
+ if (r < 0) {
+ connection_mark_for_close(conn);
+ return -1;
+ } else if (r == 0) {
+ return 0;
+ }
+ /* if r > 0, loop and process more data (if any). */
+ }
+
+ while (1) {
+ log_debug(LD_GENERAL, "Got Extended ORPort data.");
+ command = NULL;
+ r = connection_fetch_ext_or_cmd_from_buf(conn, &command);
+ if (r < 0)
+ goto err;
+ else if (r == 0)
+ return 0; /* need to wait for more data */
+
+ /* Got a command! */
+ tor_assert(command);
+
+ if (command->cmd == EXT_OR_CMD_TB_DONE) {
+ if (connection_get_inbuf_len(conn)) {
+ /* The inbuf isn't empty; the client is misbehaving. */
+ goto err;
+ }
+
+ log_debug(LD_NET, "Received DONE.");
+
+ /* If the transport proxy did not use the TRANSPORT command to
+ * specify the transport name, mark this as unknown transport. */
+ if (!or_conn->ext_or_transport) {
+ /* We write this string this way to avoid ??>, which is a C
+ * trigraph. */
+ or_conn->ext_or_transport = tor_strdup("<?" "?>");
+ }
+
+ connection_write_ext_or_command(conn, EXT_OR_CMD_BT_OKAY, NULL, 0);
+
+ /* can't transition immediately; need to flush first. */
+ conn->state = EXT_OR_CONN_STATE_FLUSHING;
+ connection_stop_reading(conn);
+ } else if (command->cmd == EXT_OR_CMD_TB_USERADDR) {
+ if (connection_ext_or_handle_cmd_useraddr(conn,
+ command->body, command->len) < 0)
+ goto err;
+ } else if (command->cmd == EXT_OR_CMD_TB_TRANSPORT) {
+ if (connection_ext_or_handle_cmd_transport(or_conn,
+ command->body, command->len) < 0)
+ goto err;
+ } else {
+ log_notice(LD_NET,"Got Extended ORPort command we don't regognize (%u).",
+ command->cmd);
+ }
+
+ ext_or_cmd_free(command);
+ }
+
+ return 0;
+
+ err:
+ ext_or_cmd_free(command);
+ connection_mark_for_close(conn);
+ return -1;
+}
+
+/** <b>conn</b> finished flushing Extended ORPort messages to the
+ * network, and is now ready to accept OR traffic. This function
+ * does the transition. */
+int
+connection_ext_or_finished_flushing(or_connection_t *conn)
+{
+ if (conn->base_.state == EXT_OR_CONN_STATE_FLUSHING) {
+ connection_start_reading(TO_CONN(conn));
+ connection_ext_or_transition(conn);
+ }
+ return 0;
+}
+
+/** Initiate Extended ORPort authentication, by sending the list of
+ * supported authentication types to the client. */
+int
+connection_ext_or_start_auth(or_connection_t *or_conn)
+{
+ connection_t *conn = TO_CONN(or_conn);
+ const uint8_t authtypes[] = {
+ /* We only support authtype '1' for now. */
+ EXT_OR_AUTHTYPE_SAFECOOKIE,
+ /* Marks the end of the list. */
+ 0
+ };
+
+ log_debug(LD_GENERAL,
+ "ExtORPort authentication: Sending supported authentication types");
+
+ connection_write_to_buf((const char *)authtypes, sizeof(authtypes), conn);
+ conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE;
+
+ return 0;
+}
+
+/** Free any leftover allocated memory of the ext_orport.c subsystem. */
+void
+ext_orport_free_all(void)
+{
+ if (ext_or_auth_cookie) /* Free the auth cookie */
+ tor_free(ext_or_auth_cookie);
+}
+
diff --git a/src/or/ext_orport.h b/src/or/ext_orport.h
new file mode 100644
index 000000000..ce45e5f41
--- /dev/null
+++ b/src/or/ext_orport.h
@@ -0,0 +1,42 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef EXT_ORPORT_H
+#define EXT_ORPORT_H
+
+int connection_ext_or_start_auth(or_connection_t *or_conn);
+
+ext_or_cmd_t *ext_or_cmd_new(uint16_t len);
+void ext_or_cmd_free(ext_or_cmd_t *cmd);
+void connection_or_set_ext_or_identifier(or_connection_t *conn);
+void connection_or_remove_from_ext_or_id_map(or_connection_t *conn);
+void connection_or_clear_ext_or_id_map(void);
+or_connection_t *connection_or_get_by_ext_or_id(const char *id);
+
+int connection_ext_or_finished_flushing(or_connection_t *conn);
+int connection_ext_or_process_inbuf(or_connection_t *or_conn);
+
+int init_ext_or_cookie_authentication(int is_enabled);
+char *get_ext_or_auth_cookie_file_name(void);
+void ext_orport_free_all(void);
+
+#ifdef EXT_ORPORT_PRIVATE
+STATIC int connection_write_ext_or_command(connection_t *conn,
+ uint16_t command,
+ const char *body,
+ size_t bodylen);
+STATIC int handle_client_auth_nonce(const char *client_nonce,
+ size_t client_nonce_len,
+ char **client_hash_out,
+ char **reply_out, size_t *reply_len_out);
+#ifdef TOR_UNIT_TESTS
+extern uint8_t *ext_or_auth_cookie;
+extern int ext_or_auth_cookie_is_set;
+#endif
+#endif
+
+#endif
+
diff --git a/src/or/geoip.c b/src/or/geoip.c
index e2e98e8ec..dc4730c81 100644
--- a/src/or/geoip.c
+++ b/src/or/geoip.c
@@ -120,7 +120,7 @@ geoip_add_entry(const tor_addr_t *low, const tor_addr_t *high,
/** Add an entry to the GeoIP table indicated by <b>family</b>,
* parsing it from <b>line</b>. The format is as for geoip_load_file(). */
-/*private*/ int
+STATIC int
geoip_parse_entry(const char *line, sa_family_t family)
{
tor_addr_t low_addr, high_addr;
@@ -363,7 +363,7 @@ geoip_load_file(sa_family_t family, const char *filename)
* be less than geoip_get_n_countries(). To decode it, call
* geoip_get_country_name().
*/
-int
+STATIC int
geoip_get_country_by_ipv4(uint32_t ipaddr)
{
geoip_ipv4_entry_t *ent;
@@ -379,7 +379,7 @@ geoip_get_country_by_ipv4(uint32_t ipaddr)
* 0 for the 'unknown country'. The return value will always be less than
* geoip_get_n_countries(). To decode it, call geoip_get_country_name().
*/
-int
+STATIC int
geoip_get_country_by_ipv6(const struct in6_addr *addr)
{
geoip_ipv6_entry_t *ent;
@@ -461,6 +461,10 @@ geoip_db_digest(sa_family_t family)
typedef struct clientmap_entry_t {
HT_ENTRY(clientmap_entry_t) node;
tor_addr_t addr;
+ /* Name of pluggable transport used by this client. NULL if no
+ pluggable transport was used. */
+ char *transport_name;
+
/** Time when we last saw this IP address, in MINUTES since the epoch.
*
* (This will run out of space around 4011 CE. If Tor is still in use around
@@ -482,12 +486,18 @@ static HT_HEAD(clientmap, clientmap_entry_t) client_history =
static INLINE unsigned
clientmap_entry_hash(const clientmap_entry_t *a)
{
- return ht_improve_hash(tor_addr_hash(&a->addr));
+ unsigned h = tor_addr_hash(&a->addr);
+ if (a->transport_name)
+ h += ht_string_hash(a->transport_name);
+ return ht_improve_hash(h);
}
/** Hashtable helper: compare two clientmap_entry_t values for equality. */
static INLINE int
clientmap_entries_eq(const clientmap_entry_t *a, const clientmap_entry_t *b)
{
+ if (strcmp_opt(a->transport_name, b->transport_name))
+ return 0;
+
return !tor_addr_compare(&a->addr, &b->addr, CMP_EXACT) &&
a->action == b->action;
}
@@ -497,6 +507,17 @@ HT_PROTOTYPE(clientmap, clientmap_entry_t, node, clientmap_entry_hash,
HT_GENERATE(clientmap, clientmap_entry_t, node, clientmap_entry_hash,
clientmap_entries_eq, 0.6, malloc, realloc, free);
+/** Free all storage held by <b>ent</b>. */
+static void
+clientmap_entry_free(clientmap_entry_t *ent)
+{
+ if (!ent)
+ return;
+
+ tor_free(ent->transport_name);
+ tor_free(ent);
+}
+
/** Clear history of connecting clients used by entry and bridge stats. */
static void
client_history_clear(void)
@@ -507,7 +528,7 @@ client_history_clear(void)
if ((*ent)->action == GEOIP_CLIENT_CONNECT) {
this = *ent;
next = HT_NEXT_RMV(clientmap, &client_history, ent);
- tor_free(this);
+ clientmap_entry_free(this);
} else {
next = HT_NEXT(clientmap, &client_history, ent);
}
@@ -519,10 +540,14 @@ client_history_clear(void)
* configured accordingly. */
void
geoip_note_client_seen(geoip_client_action_t action,
- const tor_addr_t *addr, time_t now)
+ const tor_addr_t *addr,
+ const char *transport_name,
+ time_t now)
{
const or_options_t *options = get_options();
clientmap_entry_t lookup, *ent;
+ memset(&lookup, 0, sizeof(clientmap_entry_t));
+
if (action == GEOIP_CLIENT_CONNECT) {
/* Only remember statistics as entry guard or as bridge. */
if (!options->EntryStatistics &&
@@ -534,12 +559,20 @@ geoip_note_client_seen(geoip_client_action_t action,
return;
}
+ log_debug(LD_GENERAL, "Seen client from '%s' with transport '%s'.",
+ safe_str_client(fmt_addr((addr))),
+ transport_name ? transport_name : "<no transport>");
+
tor_addr_copy(&lookup.addr, addr);
lookup.action = (int)action;
+ lookup.transport_name = (char*) transport_name;
ent = HT_FIND(clientmap, &client_history, &lookup);
+
if (! ent) {
ent = tor_malloc_zero(sizeof(clientmap_entry_t));
tor_addr_copy(&ent->addr, addr);
+ if (transport_name)
+ ent->transport_name = tor_strdup(transport_name);
ent->action = (int)action;
HT_INSERT(clientmap, &client_history, ent);
}
@@ -566,7 +599,7 @@ remove_old_client_helper_(struct clientmap_entry_t *ent, void *_cutoff)
{
time_t cutoff = *(time_t*)_cutoff / 60;
if (ent->last_seen_in_minutes < cutoff) {
- tor_free(ent);
+ clientmap_entry_free(ent);
return 1;
} else {
return 0;
@@ -769,6 +802,106 @@ geoip_change_dirreq_state(uint64_t dirreq_id, dirreq_type_t type,
}
}
+/** Return the bridge-ip-transports string that should be inserted in
+ * our extra-info descriptor. Return NULL if the bridge-ip-transports
+ * line should be empty. */
+char *
+geoip_get_transport_history(void)
+{
+ unsigned granularity = IP_GRANULARITY;
+ /** String hash table <name of transport> -> <number of users>. */
+ strmap_t *transport_counts = strmap_new();
+
+ /** Smartlist that contains copies of the names of the transports
+ that have been used. */
+ smartlist_t *transports_used = smartlist_new();
+
+ /* Special string to signify that no transport was used for this
+ connection. Pluggable transport names can't have symbols in their
+ names, so this string will never collide with a real transport. */
+ static const char* no_transport_str = "<OR>";
+
+ clientmap_entry_t **ent;
+ const char *transport_name = NULL;
+ smartlist_t *string_chunks = smartlist_new();
+ char *the_string = NULL;
+
+ /* If we haven't seen any clients yet, return NULL. */
+ if (HT_EMPTY(&client_history))
+ goto done;
+
+ /** We do the following steps to form the transport history string:
+ * a) Foreach client that uses a pluggable transport, we increase the
+ * times that transport was used by one. If the client did not use
+ * a transport, we increase the number of times someone connected
+ * without obfuscation.
+ * b) Foreach transport we observed, we write its transport history
+ * string and push it to string_chunks. So, for example, if we've
+ * seen 665 obfs2 clients, we write "obfs2=665".
+ * c) We concatenate string_chunks to form the final string.
+ */
+
+ log_debug(LD_GENERAL,"Starting iteration for transport history. %d clients.",
+ HT_SIZE(&client_history));
+
+ /* Loop through all clients. */
+ HT_FOREACH(ent, clientmap, &client_history) {
+ uintptr_t val;
+ void *ptr;
+ transport_name = (*ent)->transport_name;
+ if (!transport_name)
+ transport_name = no_transport_str;
+
+ /* Increase the count for this transport name. */
+ ptr = strmap_get(transport_counts, transport_name);
+ val = (uintptr_t)ptr;
+ val++;
+ ptr = (void*)val;
+ strmap_set(transport_counts, transport_name, ptr);
+
+ /* If it's the first time we see this transport, note it. */
+ if (val == 1)
+ smartlist_add(transports_used, tor_strdup(transport_name));
+
+ log_debug(LD_GENERAL, "Client from '%s' with transport '%s'. "
+ "I've now seen %d clients.",
+ safe_str_client(fmt_addr(&(*ent)->addr)),
+ transport_name ? transport_name : "<no transport>",
+ (int)val);
+ }
+
+ /* Sort the transport names (helps with unit testing). */
+ smartlist_sort_strings(transports_used);
+
+ /* Loop through all seen transports. */
+ SMARTLIST_FOREACH_BEGIN(transports_used, const char *, transport_name) {
+ void *transport_count_ptr = strmap_get(transport_counts, transport_name);
+ uintptr_t transport_count = (uintptr_t) transport_count_ptr;
+
+ log_debug(LD_GENERAL, "We got "U64_FORMAT" clients with transport '%s'.",
+ U64_PRINTF_ARG((uint64_t)transport_count), transport_name);
+
+ smartlist_add_asprintf(string_chunks, "%s="U64_FORMAT,
+ transport_name,
+ U64_PRINTF_ARG(round_uint64_to_next_multiple_of(
+ (uint64_t)transport_count,
+ granularity)));
+ } SMARTLIST_FOREACH_END(transport_name);
+
+ the_string = smartlist_join_strings(string_chunks, ",", 0, NULL);
+
+ log_debug(LD_GENERAL, "Final bridge-ip-transports string: '%s'", the_string);
+
+ done:
+ strmap_free(transport_counts, NULL);
+ SMARTLIST_FOREACH(transports_used, char *, s, tor_free(s));
+ smartlist_free(transports_used);
+ SMARTLIST_FOREACH(string_chunks, char *, s, tor_free(s));
+ smartlist_free(string_chunks);
+
+ return the_string;
+}
+
/** Return a newly allocated comma-separated string containing statistics
* on network status downloads. The string contains the number of completed
* requests, timeouts, and still running requests as well as the download
@@ -1037,7 +1170,7 @@ geoip_reset_dirreq_stats(time_t now)
if ((*ent)->action == GEOIP_CLIENT_NETWORKSTATUS) {
this = *ent;
next = HT_NEXT_RMV(clientmap, &client_history, ent);
- tor_free(this);
+ clientmap_entry_free(this);
} else {
next = HT_NEXT(clientmap, &client_history, ent);
}
@@ -1132,7 +1265,7 @@ geoip_format_dirreq_stats(time_t now)
time_t
geoip_dirreq_stats_write(time_t now)
{
- char *statsdir = NULL, *filename = NULL, *str = NULL;
+ char *str = NULL;
if (!start_of_dirreq_stats_interval)
return 0; /* Not initialized. */
@@ -1146,21 +1279,13 @@ geoip_dirreq_stats_write(time_t now)
str = geoip_format_dirreq_stats(now);
/* Write dirreq-stats string to disk. */
- statsdir = get_datadir_fname("stats");
- if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) {
- log_warn(LD_HIST, "Unable to create stats/ directory!");
- goto done;
+ if (!check_or_create_data_subdir("stats")) {
+ write_to_data_subdir("stats", "dirreq-stats", str, "dirreq statistics");
+ /* Reset measurement interval start. */
+ geoip_reset_dirreq_stats(now);
}
- filename = get_datadir_fname2("stats", "dirreq-stats");
- if (write_str_to_file(filename, str, 0) < 0)
- log_warn(LD_HIST, "Unable to write dirreq statistics to disk!");
-
- /* Reset measurement interval start. */
- geoip_reset_dirreq_stats(now);
done:
- tor_free(statsdir);
- tor_free(filename);
tor_free(str);
return start_of_dirreq_stats_interval + WRITE_STATS_INTERVAL;
}
@@ -1197,6 +1322,8 @@ validate_bridge_stats(const char *stats_str, time_t now)
const char *BRIDGE_STATS_END = "bridge-stats-end ";
const char *BRIDGE_IPS = "bridge-ips ";
const char *BRIDGE_IPS_EMPTY_LINE = "bridge-ips\n";
+ const char *BRIDGE_TRANSPORTS = "bridge-ip-transports ";
+ const char *BRIDGE_TRANSPORTS_EMPTY_LINE = "bridge-ip-transports\n";
const char *tmp;
time_t stats_end_time;
int seconds;
@@ -1231,6 +1358,15 @@ validate_bridge_stats(const char *stats_str, time_t now)
return 0;
}
+ /* Parse: "bridge-ip-transports PT=N,PT=N,..." */
+ tmp = find_str_at_start_of_line(stats_str, BRIDGE_TRANSPORTS);
+ if (!tmp) {
+ /* Look if there is an empty "bridge-ip-transports" line */
+ tmp = find_str_at_start_of_line(stats_str, BRIDGE_TRANSPORTS_EMPTY_LINE);
+ if (!tmp)
+ return 0;
+ }
+
return 1;
}
@@ -1244,7 +1380,8 @@ static char *bridge_stats_extrainfo = NULL;
char *
geoip_format_bridge_stats(time_t now)
{
- char *out = NULL, *country_data = NULL, *ipver_data = NULL;
+ char *out = NULL;
+ char *country_data = NULL, *ipver_data = NULL, *transport_data = NULL;
long duration = now - start_of_bridge_stats_interval;
char written[ISO_TIME_LEN+1];
@@ -1255,16 +1392,20 @@ geoip_format_bridge_stats(time_t now)
format_iso_time(written, now);
geoip_get_client_history(GEOIP_CLIENT_CONNECT, &country_data, &ipver_data);
+ transport_data = geoip_get_transport_history();
tor_asprintf(&out,
"bridge-stats-end %s (%ld s)\n"
"bridge-ips %s\n"
- "bridge-ip-versions %s\n",
+ "bridge-ip-versions %s\n"
+ "bridge-ip-transports %s\n",
written, duration,
country_data ? country_data : "",
- ipver_data ? ipver_data : "");
+ ipver_data ? ipver_data : "",
+ transport_data ? transport_data : "");
tor_free(country_data);
tor_free(ipver_data);
+ tor_free(transport_data);
return out;
}
@@ -1297,7 +1438,7 @@ format_bridge_stats_controller(time_t now)
time_t
geoip_bridge_stats_write(time_t now)
{
- char *filename = NULL, *val = NULL, *statsdir = NULL;
+ char *val = NULL;
/* Check if 24 hours have passed since starting measurements. */
if (now < start_of_bridge_stats_interval + WRITE_STATS_INTERVAL)
@@ -1317,24 +1458,20 @@ geoip_bridge_stats_write(time_t now)
start_of_bridge_stats_interval = now;
/* Write it to disk. */
- statsdir = get_datadir_fname("stats");
- if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0)
- goto done;
- filename = get_datadir_fname2("stats", "bridge-stats");
-
- write_str_to_file(filename, bridge_stats_extrainfo, 0);
-
- /* Tell the controller, "hey, there are clients!" */
- {
- char *controller_str = format_bridge_stats_controller(now);
- if (controller_str)
- control_event_clients_seen(controller_str);
- tor_free(controller_str);
+ if (!check_or_create_data_subdir("stats")) {
+ write_to_data_subdir("stats", "bridge-stats",
+ bridge_stats_extrainfo, "bridge statistics");
+
+ /* Tell the controller, "hey, there are clients!" */
+ {
+ char *controller_str = format_bridge_stats_controller(now);
+ if (controller_str)
+ control_event_clients_seen(controller_str);
+ tor_free(controller_str);
+ }
}
- done:
- tor_free(filename);
- tor_free(statsdir);
+ done:
return start_of_bridge_stats_interval + WRITE_STATS_INTERVAL;
}
@@ -1436,7 +1573,7 @@ geoip_format_entry_stats(time_t now)
time_t
geoip_entry_stats_write(time_t now)
{
- char *statsdir = NULL, *filename = NULL, *str = NULL;
+ char *str = NULL;
if (!start_of_entry_stats_interval)
return 0; /* Not initialized. */
@@ -1450,21 +1587,14 @@ geoip_entry_stats_write(time_t now)
str = geoip_format_entry_stats(now);
/* Write entry-stats string to disk. */
- statsdir = get_datadir_fname("stats");
- if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) {
- log_warn(LD_HIST, "Unable to create stats/ directory!");
- goto done;
- }
- filename = get_datadir_fname2("stats", "entry-stats");
- if (write_str_to_file(filename, str, 0) < 0)
- log_warn(LD_HIST, "Unable to write entry statistics to disk!");
+ if (!check_or_create_data_subdir("stats")) {
+ write_to_data_subdir("stats", "entry-stats", str, "entry statistics");
- /* Reset measurement interval start. */
- geoip_reset_entry_stats(now);
+ /* Reset measurement interval start. */
+ geoip_reset_entry_stats(now);
+ }
done:
- tor_free(statsdir);
- tor_free(filename);
tor_free(str);
return start_of_entry_stats_interval + WRITE_STATS_INTERVAL;
}
@@ -1534,7 +1664,7 @@ geoip_free_all(void)
for (ent = HT_START(clientmap, &client_history); ent != NULL; ent = next) {
this = *ent;
next = HT_NEXT_RMV(clientmap, &client_history, ent);
- tor_free(this);
+ clientmap_entry_free(this);
}
HT_CLEAR(clientmap, &client_history);
}
@@ -1549,5 +1679,6 @@ geoip_free_all(void)
}
clear_geoip_db();
+ tor_free(bridge_stats_extrainfo);
}
diff --git a/src/or/geoip.h b/src/or/geoip.h
index ebefee5f4..b9b53c300 100644
--- a/src/or/geoip.h
+++ b/src/or/geoip.h
@@ -12,10 +12,12 @@
#ifndef TOR_GEOIP_H
#define TOR_GEOIP_H
+#include "testsupport.h"
+
#ifdef GEOIP_PRIVATE
-int geoip_parse_entry(const char *line, sa_family_t family);
-int geoip_get_country_by_ipv4(uint32_t ipaddr);
-int geoip_get_country_by_ipv6(const struct in6_addr *addr);
+STATIC int geoip_parse_entry(const char *line, sa_family_t family);
+STATIC int geoip_get_country_by_ipv4(uint32_t ipaddr);
+STATIC int geoip_get_country_by_ipv6(const struct in6_addr *addr);
#endif
int should_record_bridge_info(const or_options_t *options);
int geoip_load_file(sa_family_t family, const char *filename);
@@ -27,10 +29,12 @@ const char *geoip_db_digest(sa_family_t family);
country_t geoip_get_country(const char *countrycode);
void geoip_note_client_seen(geoip_client_action_t action,
- const tor_addr_t *addr, time_t now);
+ const tor_addr_t *addr, const char *transport_name,
+ time_t now);
void geoip_remove_old_clients(time_t cutoff);
void geoip_note_ns_response(geoip_ns_response_t response);
+char *geoip_get_transport_history(void);
int geoip_get_client_history(geoip_client_action_t action,
char **country_str, char **ipver_str);
char *geoip_get_request_history(void);
diff --git a/src/or/hibernate.c b/src/or/hibernate.c
index a41257133..607dec8cd 100644
--- a/src/or/hibernate.c
+++ b/src/or/hibernate.c
@@ -255,6 +255,13 @@ accounting_get_interval_length(void)
return (int)(interval_end_time - interval_start_time);
}
+/** Return the time at which the current accounting interval will end. */
+time_t
+accounting_get_end_time(void)
+{
+ return interval_end_time;
+}
+
/** Called from main.c to tell us that <b>seconds</b> seconds have
* passed, <b>n_read</b> bytes have been read, and <b>n_written</b>
* bytes have been written. */
@@ -1010,6 +1017,7 @@ getinfo_helper_accounting(control_connection_t *conn,
return 0;
}
+#ifdef TOR_UNIT_TESTS
/**
* Manually change the hibernation state. Private; used only by the unit
* tests.
@@ -1019,4 +1027,5 @@ hibernate_set_state_for_testing_(hibernate_state_t newstate)
{
hibernate_state = newstate;
}
+#endif
diff --git a/src/or/hibernate.h b/src/or/hibernate.h
index d2d6989e1..4f7331ce8 100644
--- a/src/or/hibernate.h
+++ b/src/or/hibernate.h
@@ -15,6 +15,7 @@
int accounting_parse_options(const or_options_t *options, int validate_only);
int accounting_is_enabled(const or_options_t *options);
int accounting_get_interval_length(void);
+time_t accounting_get_end_time(void);
void configure_accounting(time_t now);
void accounting_run_housekeeping(time_t now);
void accounting_add_bytes(size_t n_read, size_t n_written, int seconds);
@@ -45,8 +46,10 @@ typedef enum {
HIBERNATE_STATE_INITIAL=5
} hibernate_state_t;
+#ifdef TOR_UNIT_TESTS
void hibernate_set_state_for_testing_(hibernate_state_t newstate);
#endif
+#endif
#endif
diff --git a/src/or/include.am b/src/or/include.am
index 65dbeff53..47bdd0990 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -1,5 +1,13 @@
bin_PROGRAMS+= src/or/tor
-noinst_LIBRARIES+= src/or/libtor.a
+noinst_LIBRARIES += \
+ src/or/libtor.a
+if UNITTESTS_ENABLED
+noinst_LIBRARIES += \
+ src/or/libtor-testing.a
+endif
+if COVERAGE_ENABLED
+noinst_PROGRAMS+= src/or/tor-cov
+endif
if BUILD_NT_SERVICES
tor_platform_source=src/or/ntmain.c
@@ -21,11 +29,12 @@ else
onion_ntor_source=
endif
-src_or_libtor_a_SOURCES = \
+LIBTOR_A_SOURCES = \
src/or/addressmap.c \
src/or/buffers.c \
src/or/channel.c \
src/or/channeltls.c \
+ src/or/circpathbias.c \
src/or/circuitbuild.c \
src/or/circuitlist.c \
src/or/circuitmux.c \
@@ -48,6 +57,7 @@ src_or_libtor_a_SOURCES = \
src/or/fp_pair.c \
src/or/geoip.c \
src/or/entrynodes.c \
+ src/or/ext_orport.c \
src/or/hibernate.c \
src/or/main.c \
src/or/microdesc.c \
@@ -77,6 +87,9 @@ src_or_libtor_a_SOURCES = \
$(onion_ntor_source) \
src/or/config_codedigest.c
+src_or_libtor_a_SOURCES = $(LIBTOR_A_SOURCES)
+src_or_libtor_testing_a_SOURCES = $(LIBTOR_A_SOURCES)
+
#libtor_a_LIBADD = ../common/libor.a ../common/libor-crypto.a \
# ../common/libor-event.a
@@ -90,6 +103,9 @@ AM_CPPFLAGS += -DSHARE_DATADIR="\"$(datadir)\"" \
-DLOCALSTATEDIR="\"$(localstatedir)\"" \
-DBINDIR="\"$(bindir)\""
+src_or_libtor_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS)
+src_or_libtor_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+
# -L flags need to go in LDFLAGS. -l flags need to go in LDADD.
# This seems to matter nowhere but on windows, but I assure you that it
# matters a lot there, and is quite hard to debug if you forget to do it.
@@ -102,11 +118,24 @@ src_or_tor_LDADD = src/or/libtor.a src/common/libor.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
@TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
+if COVERAGE_ENABLED
+src_or_tor_cov_SOURCES = src/or/tor_main.c
+src_or_tor_cov_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS)
+src_or_tor_cov_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+src_or_tor_cov_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@
+src_or_tor_cov_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \
+ src/common/libor-crypto-testing.a $(LIBDONNA) \
+ src/common/libor-event-testing.a \
+ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
+ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
+endif
+
ORHEADERS = \
src/or/addressmap.h \
src/or/buffers.h \
src/or/channel.h \
src/or/channeltls.h \
+ src/or/circpathbias.h \
src/or/circuitbuild.h \
src/or/circuitlist.h \
src/or/circuitmux.h \
@@ -127,6 +156,7 @@ ORHEADERS = \
src/or/dns.h \
src/or/dnsserv.h \
src/or/eventdns_tor.h \
+ src/or/ext_orport.h \
src/or/fp_pair.h \
src/or/geoip.h \
src/or/entrynodes.h \
diff --git a/src/or/main.c b/src/or/main.c
index deed798e8..e14001f10 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -13,6 +13,7 @@
#define MAIN_PRIVATE
#include "or.h"
#include "addressmap.h"
+#include "backtrace.h"
#include "buffers.h"
#include "channel.h"
#include "channeltls.h"
@@ -21,6 +22,7 @@
#include "circuituse.h"
#include "command.h"
#include "config.h"
+#include "confparse.h"
#include "connection.h"
#include "connection_edge.h"
#include "connection_or.h"
@@ -52,11 +54,13 @@
#include "routerparse.h"
#include "statefile.h"
#include "status.h"
+#include "ext_orport.h"
#ifdef USE_DMALLOC
#include <dmalloc.h>
#include <openssl/crypto.h>
#endif
#include "memarea.h"
+#include "../common/sandbox.h"
#ifdef HAVE_EVENT2_EVENT_H
#include <event2/event.h>
@@ -155,8 +159,6 @@ int can_complete_circuit=0;
/** How often do we 'forgive' undownloadable router descriptors and attempt
* to download them again? */
#define DESCRIPTOR_FAILURE_RESET_INTERVAL (60*60)
-/** How long do we let a directory connection stall before expiring it? */
-#define DIR_CONN_MAX_STALL (5*60)
/** Decides our behavior when no logs are configured/before any
* logs have been configured. For 0, we log notice to stdout as normal.
@@ -351,6 +353,8 @@ connection_remove(connection_t *conn)
(int)conn->s, conn_type_to_string(conn->type),
smartlist_len(connection_array));
+ control_event_conn_bandwidth(conn);
+
tor_assert(conn->conn_array_index >= 0);
current_index = conn->conn_array_index;
connection_unregister_events(conn); /* This is redundant, but cheap. */
@@ -414,6 +418,19 @@ connection_unlink(connection_t *conn)
connection_free(conn);
}
+/** Initialize the global connection list, closeable connection list,
+ * and active connection list. */
+STATIC void
+init_connection_lists(void)
+{
+ if (!connection_array)
+ connection_array = smartlist_new();
+ if (!closeable_connection_lst)
+ closeable_connection_lst = smartlist_new();
+ if (!active_linked_connection_lst)
+ active_linked_connection_lst = smartlist_new();
+}
+
/** Schedule <b>conn</b> to be closed. **/
void
add_connection_to_closeable_list(connection_t *conn)
@@ -507,8 +524,8 @@ connection_is_reading(connection_t *conn)
}
/** Tell the main loop to stop notifying <b>conn</b> of any read events. */
-void
-connection_stop_reading(connection_t *conn)
+MOCK_IMPL(void,
+connection_stop_reading,(connection_t *conn))
{
tor_assert(conn);
@@ -532,8 +549,8 @@ connection_stop_reading(connection_t *conn)
}
/** Tell the main loop to start notifying <b>conn</b> of any read events. */
-void
-connection_start_reading(connection_t *conn)
+MOCK_IMPL(void,
+connection_start_reading,(connection_t *conn))
{
tor_assert(conn);
@@ -572,8 +589,8 @@ connection_is_writing(connection_t *conn)
}
/** Tell the main loop to stop notifying <b>conn</b> of any write events. */
-void
-connection_stop_writing(connection_t *conn)
+MOCK_IMPL(void,
+connection_stop_writing,(connection_t *conn))
{
tor_assert(conn);
@@ -598,8 +615,8 @@ connection_stop_writing(connection_t *conn)
}
/** Tell the main loop to start notifying <b>conn</b> of any write events. */
-void
-connection_start_writing(connection_t *conn)
+MOCK_IMPL(void,
+connection_start_writing,(connection_t *conn))
{
tor_assert(conn);
@@ -687,7 +704,7 @@ connection_stop_reading_from_linked_conn(connection_t *conn)
}
/** Close all connections that have been scheduled to get closed. */
-static void
+STATIC void
close_closeable_connections(void)
{
int i;
@@ -1028,9 +1045,11 @@ run_connection_housekeeping(int i, time_t now)
* if a server or received if a client) for 5 min */
if (conn->type == CONN_TYPE_DIR &&
((DIR_CONN_IS_SERVER(conn) &&
- conn->timestamp_lastwritten + DIR_CONN_MAX_STALL < now) ||
+ conn->timestamp_lastwritten
+ + options->TestingDirConnectionMaxStall < now) ||
(!DIR_CONN_IS_SERVER(conn) &&
- conn->timestamp_lastread + DIR_CONN_MAX_STALL < now))) {
+ conn->timestamp_lastread
+ + options->TestingDirConnectionMaxStall < now))) {
log_info(LD_DIR,"Expiring wedged directory conn (fd %d, purpose %d)",
(int)conn->s, conn->purpose);
/* This check is temporary; it's to let us know whether we should consider
@@ -1153,6 +1172,7 @@ run_scheduled_events(time_t now)
static time_t time_to_check_v3_certificate = 0;
static time_t time_to_check_listeners = 0;
static time_t time_to_check_descriptor = 0;
+ static time_t time_to_download_networkstatus = 0;
static time_t time_to_shrink_memory = 0;
static time_t time_to_try_getting_descriptors = 0;
static time_t time_to_reset_descriptor_failures = 0;
@@ -1447,10 +1467,18 @@ run_scheduled_events(time_t now)
networkstatus_v2_list_clean(now);
/* Remove dead routers. */
routerlist_remove_old_routers();
+ }
- /* Also, once per minute, check whether we want to download any
- * networkstatus documents.
- */
+ /* 2c. Every minute (or every second if TestingTorNetwork), check
+ * whether we want to download any networkstatus documents. */
+
+/* How often do we check whether we should download network status
+ * documents? */
+#define networkstatus_dl_check_interval(o) ((o)->TestingTorNetwork ? 1 : 60)
+
+ if (time_to_download_networkstatus < now && !options->DisableNetwork) {
+ time_to_download_networkstatus =
+ now + networkstatus_dl_check_interval(options);
update_networkstatus_downloads(now);
}
@@ -1643,6 +1671,9 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg)
control_event_bandwidth_used((uint32_t)bytes_read,(uint32_t)bytes_written);
control_event_stream_bandwidth_used();
+ control_event_conn_bandwidth_used();
+ control_event_circ_bandwidth_used();
+ control_event_circuit_cell_stats();
if (server_mode(options) &&
!net_is_disabled() &&
@@ -1870,7 +1901,7 @@ do_hup(void)
}
/** Tor main loop. */
-/* static */ int
+int
do_main_loop(void)
{
int loop_result;
@@ -2297,21 +2328,17 @@ handle_signals(int is_parent)
/** Main entry point for the Tor command-line client.
*/
-/* static */ int
+int
tor_init(int argc, char *argv[])
{
- char buf[256];
- int i, quiet = 0;
+ char progname[256];
+ int quiet = 0;
+
time_of_process_start = time(NULL);
- if (!connection_array)
- connection_array = smartlist_new();
- if (!closeable_connection_lst)
- closeable_connection_lst = smartlist_new();
- if (!active_linked_connection_lst)
- active_linked_connection_lst = smartlist_new();
+ init_connection_lists();
/* Have the log set up with our application name. */
- tor_snprintf(buf, sizeof(buf), "Tor %s", get_version());
- log_set_application_name(buf);
+ tor_snprintf(progname, sizeof(progname), "Tor %s", get_version());
+ log_set_application_name(progname);
/* Initialize the history structures. */
rep_hist_init();
/* Initialize the service cache. */
@@ -2319,17 +2346,31 @@ tor_init(int argc, char *argv[])
addressmap_init(); /* Init the client dns cache. Do it always, since it's
* cheap. */
+ {
/* We search for the "quiet" option first, since it decides whether we
* will log anything at all to the command line. */
- for (i=1;i<argc;++i) {
- if (!strcmp(argv[i], "--hush"))
- quiet = 1;
- if (!strcmp(argv[i], "--quiet"))
- quiet = 2;
- /* --version implies --quiet */
- if (!strcmp(argv[i], "--version"))
- quiet = 2;
+ config_line_t *opts = NULL, *cmdline_opts = NULL;
+ const config_line_t *cl;
+ (void) config_parse_commandline(argc, argv, 1, &opts, &cmdline_opts);
+ for (cl = cmdline_opts; cl; cl = cl->next) {
+ if (!strcmp(cl->key, "--hush"))
+ quiet = 1;
+ if (!strcmp(cl->key, "--quiet") ||
+ !strcmp(cl->key, "--dump-config"))
+ quiet = 2;
+ /* --version, --digests, and --help imply --hush */
+ if (!strcmp(cl->key, "--version") || !strcmp(cl->key, "--digests") ||
+ !strcmp(cl->key, "--list-torrc-options") ||
+ !strcmp(cl->key, "--library-versions") ||
+ !strcmp(cl->key, "-h") || !strcmp(cl->key, "--help")) {
+ if (quiet < 1)
+ quiet = 1;
+ }
+ }
+ config_free_lines(opts);
+ config_free_lines(cmdline_opts);
}
+
/* give it somewhere to log to initially */
switch (quiet) {
case 2:
@@ -2351,11 +2392,12 @@ tor_init(int argc, char *argv[])
#else
"";
#endif
- log_notice(LD_GENERAL, "Tor v%s %srunning on %s with Libevent %s "
- "and OpenSSL %s.", version, bev_str,
+ log_notice(LD_GENERAL, "Tor v%s %srunning on %s with Libevent %s, "
+ "OpenSSL %s and Zlib %s.", version, bev_str,
get_uname(),
tor_libevent_get_version_str(),
- crypto_openssl_get_version_str());
+ crypto_openssl_get_version_str(),
+ tor_zlib_get_version_str());
log_notice(LD_GENERAL, "Tor can't help you if you use it wrong! "
"Learn how to be safe at "
@@ -2497,6 +2539,8 @@ tor_free_all(int postfork)
memarea_clear_freelist();
nodelist_free_all();
microdesc_free_all();
+ ext_orport_free_all();
+ control_free_all();
if (!postfork) {
config_free_all();
or_state_free_all();
@@ -2563,7 +2607,7 @@ tor_cleanup(void)
}
/** Read/create keys as needed, and echo our fingerprint to stdout. */
-/* static */ int
+static int
do_list_fingerprint(void)
{
char buf[FINGERPRINT_LEN+1];
@@ -2571,7 +2615,7 @@ do_list_fingerprint(void)
const char *nickname = get_options()->Nickname;
if (!server_mode(get_options())) {
log_err(LD_GENERAL,
- "Clients don't have long-term identity keys. Exiting.\n");
+ "Clients don't have long-term identity keys. Exiting.");
return -1;
}
tor_assert(nickname);
@@ -2593,7 +2637,7 @@ do_list_fingerprint(void)
/** Entry point for password hashing: take the desired password from
* the command line, and print its salted hash to stdout. **/
-/* static */ void
+static void
do_hash_password(void)
{
@@ -2609,6 +2653,34 @@ do_hash_password(void)
printf("16:%s\n",output);
}
+/** Entry point for configuration dumping: write the configuration to
+ * stdout. */
+static int
+do_dump_config(void)
+{
+ const or_options_t *options = get_options();
+ const char *arg = options->command_arg;
+ int how;
+ char *opts;
+ if (!strcmp(arg, "short")) {
+ how = OPTIONS_DUMP_MINIMAL;
+ } else if (!strcmp(arg, "non-builtin")) {
+ how = OPTIONS_DUMP_DEFAULTS;
+ } else if (!strcmp(arg, "full")) {
+ how = OPTIONS_DUMP_ALL;
+ } else {
+ printf("%s is not a recognized argument to --dump-config. "
+ "Please select 'short', 'non-builtin', or 'full'", arg);
+ return -1;
+ }
+
+ opts = options_dump(options, how);
+ printf("%s", opts);
+ tor_free(opts);
+
+ return 0;
+}
+
#if defined (WINCE)
int
find_flashcard_path(PWCHAR path, size_t size)
@@ -2634,6 +2706,95 @@ find_flashcard_path(PWCHAR path, size_t size)
}
#endif
+static void
+init_addrinfo(void)
+{
+ char hname[256];
+
+ // host name to sandbox
+ gethostname(hname, sizeof(hname));
+ sandbox_add_addrinfo(hname);
+}
+
+static sandbox_cfg_t*
+sandbox_init_filter(void)
+{
+ sandbox_cfg_t *cfg = sandbox_cfg_new();
+
+ sandbox_cfg_allow_openat_filename(&cfg,
+ get_datadir_fname("cached-status"), 1);
+
+ sandbox_cfg_allow_open_filename_array(&cfg,
+ get_datadir_fname("cached-certs"), 1,
+ get_datadir_fname("cached-certs.tmp"), 1,
+ get_datadir_fname("cached-consensus"), 1,
+ get_datadir_fname("unverified-consensus"), 1,
+ get_datadir_fname("unverified-consensus.tmp"), 1,
+ get_datadir_fname("cached-microdesc-consensus"), 1,
+ get_datadir_fname("cached-microdesc-consensus.tmp"), 1,
+ get_datadir_fname("cached-microdescs"), 1,
+ get_datadir_fname("cached-microdescs.tmp"), 1,
+ get_datadir_fname("cached-microdescs.new"), 1,
+ get_datadir_fname("cached-microdescs.new.tmp"), 1,
+ get_datadir_fname("unverified-microdesc-consensus"), 1,
+ get_datadir_fname("cached-descriptors"), 1,
+ get_datadir_fname("cached-descriptors.new"), 1,
+ get_datadir_fname("cached-descriptors.tmp"), 1,
+ get_datadir_fname("cached-descriptors.new.tmp"), 1,
+ get_datadir_fname("cached-descriptors.tmp.tmp"), 1,
+ get_datadir_fname("cached-extrainfo"), 1,
+ get_datadir_fname("state.tmp"), 1,
+ get_datadir_fname("unparseable-desc.tmp"), 1,
+ get_datadir_fname("unparseable-desc"), 1,
+ "/dev/srandom", 0,
+ "/dev/urandom", 0,
+ "/dev/random", 0,
+ NULL, 0
+ );
+
+ sandbox_cfg_allow_stat_filename_array(&cfg,
+ get_datadir_fname(NULL), 1,
+ get_datadir_fname("lock"), 1,
+ get_datadir_fname("state"), 1,
+ get_datadir_fname("router-stability"), 1,
+ get_datadir_fname("cached-extrainfo.new"), 1,
+ NULL, 0
+ );
+
+ // orport
+ if (server_mode(get_options())) {
+ sandbox_cfg_allow_open_filename_array(&cfg,
+ get_datadir_fname2("keys", "secret_id_key"), 1,
+ get_datadir_fname2("keys", "secret_onion_key"), 1,
+ get_datadir_fname2("keys", "secret_onion_key_ntor"), 1,
+ get_datadir_fname2("keys", "secret_onion_key_ntor.tmp"), 1,
+ get_datadir_fname2("keys", "secret_id_key.old"), 1,
+ get_datadir_fname2("keys", "secret_onion_key.old"), 1,
+ get_datadir_fname2("keys", "secret_onion_key_ntor.old"), 1,
+ get_datadir_fname2("keys", "secret_onion_key.tmp"), 1,
+ get_datadir_fname2("keys", "secret_id_key.tmp"), 1,
+ get_datadir_fname("fingerprint"), 1,
+ get_datadir_fname("fingerprint.tmp"), 1,
+ get_datadir_fname("cached-consensus"), 1,
+ get_datadir_fname("cached-consensus.tmp"), 1,
+ "/etc/resolv.conf", 0,
+ NULL, 0
+ );
+
+ sandbox_cfg_allow_stat_filename_array(&cfg,
+ get_datadir_fname("keys"), 1,
+ get_datadir_fname("stats/dirreq-stats"), 1,
+ NULL, 0
+ );
+ }
+
+ sandbox_cfg_allow_execve(&cfg, "/usr/local/bin/tor");
+
+ init_addrinfo();
+
+ 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. */
@@ -2680,6 +2841,8 @@ tor_main(int argc, char *argv[])
}
#endif
+ configure_backtrace_handler(get_version());
+
update_approx_time(time(NULL));
tor_threads_init();
init_logging();
@@ -2700,6 +2863,22 @@ tor_main(int argc, char *argv[])
#endif
if (tor_init(argc, argv)<0)
return -1;
+
+ if (get_options()->Sandbox) {
+ sandbox_cfg_t* cfg = sandbox_init_filter();
+
+ if (sandbox_init(cfg)) {
+ log_err(LD_BUG,"Failed to create syscall sandbox filter");
+ return -1;
+ }
+
+ // registering libevent rng
+#ifdef HAVE_EVUTIL_SECURE_RNG_SET_URANDOM_DEVICE_FILE
+ evutil_secure_rng_set_urandom_device_file(
+ (char*) sandbox_intern_string("/dev/urandom"));
+#endif
+ }
+
switch (get_options()->command) {
case CMD_RUN_TOR:
#ifdef NT_SERVICE
@@ -2718,6 +2897,9 @@ tor_main(int argc, char *argv[])
printf("Configuration was valid\n");
result = 0;
break;
+ case CMD_DUMP_CONFIG:
+ result = do_dump_config();
+ break;
case CMD_RUN_UNITTESTS: /* only set by test.c */
default:
log_warn(LD_BUG,"Illegal command number %d: internal error.",
diff --git a/src/or/main.h b/src/or/main.h
index 338449b6a..df302ffa7 100644
--- a/src/or/main.h
+++ b/src/or/main.h
@@ -36,12 +36,12 @@ typedef enum watchable_events {
} watchable_events_t;
void connection_watch_events(connection_t *conn, watchable_events_t events);
int connection_is_reading(connection_t *conn);
-void connection_stop_reading(connection_t *conn);
-void connection_start_reading(connection_t *conn);
+MOCK_DECL(void,connection_stop_reading,(connection_t *conn));
+MOCK_DECL(void,connection_start_reading,(connection_t *conn));
int connection_is_writing(connection_t *conn);
-void connection_stop_writing(connection_t *conn);
-void connection_start_writing(connection_t *conn);
+MOCK_DECL(void,connection_stop_writing,(connection_t *conn));
+MOCK_DECL(void,connection_start_writing,(connection_t *conn));
void connection_stop_reading_from_linked_conn(connection_t *conn);
@@ -66,11 +66,12 @@ void tor_free_all(int postfork);
int tor_main(int argc, char *argv[]);
-#ifdef MAIN_PRIVATE
int do_main_loop(void);
-int do_list_fingerprint(void);
-void do_hash_password(void);
int tor_init(int argc, char **argv);
+
+#ifdef MAIN_PRIVATE
+STATIC void init_connection_lists(void);
+STATIC void close_closeable_connections(void);
#endif
#endif
diff --git a/src/or/microdesc.c b/src/or/microdesc.c
index 90ac0ac64..11249910c 100644
--- a/src/or/microdesc.c
+++ b/src/or/microdesc.c
@@ -139,7 +139,7 @@ get_microdesc_cache(void)
* ending at <b>eos</b>, and store them in <b>cache</b>. If <b>no_save</b>,
* mark them as non-writable to disk. If <b>where</b> is SAVED_IN_CACHE,
* leave their bodies as pointers to the mmap'd cache. If where is
- * <b>SAVED_NOWHERE</b>, do not allow annotations. If listed_at is positive,
+ * <b>SAVED_NOWHERE</b>, do not allow annotations. If listed_at is not -1,
* set the last_listed field of every microdesc to listed_at. If
* requested_digests is non-null, then it contains a list of digests we mean
* to allow, so we should reject any non-requested microdesc with a different
@@ -158,7 +158,7 @@ microdescs_add_to_cache(microdesc_cache_t *cache,
descriptors = microdescs_parse_from_string(s, eos,
allow_annotations,
where);
- if (listed_at > 0) {
+ if (listed_at != (time_t)-1) {
SMARTLIST_FOREACH(descriptors, microdesc_t *, md,
md->last_listed = listed_at);
}
@@ -489,7 +489,8 @@ microdesc_cache_rebuild(microdesc_cache_t *cache, int force)
"By my count, I'm at "I64_FORMAT
", but I should be at "I64_FORMAT,
I64_PRINTF_ARG(off), I64_PRINTF_ARG(off_real));
- off = off_real;
+ if (off_real >= 0)
+ off = off_real;
}
if (md->saved_location != SAVED_IN_CACHE) {
tor_free(md->body);
@@ -696,7 +697,7 @@ microdesc_list_missing_digest256(networkstatus_t *ns, microdesc_cache_t *cache,
continue;
if (downloadable_only &&
!download_status_is_ready(&rs->dl_status, now,
- MAX_MICRODESC_DOWNLOAD_FAILURES))
+ get_options()->TestingMicrodescMaxDownloadTries))
continue;
if (skip && digestmap_get(skip, rs->descriptor_digest))
continue;
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index 1b5c6dbb3..a935a4672 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -1203,8 +1203,6 @@ we_want_to_fetch_flavor(const or_options_t *options, int flavor)
return flavor == usable_consensus_flavor();
}
-/** How many times will we try to fetch a consensus before we give up? */
-#define CONSENSUS_NETWORKSTATUS_MAX_DL_TRIES 8
/** How long will we hang onto a possibly live consensus for which we're
* fetching certs before we check whether there is a better one? */
#define DELAY_WHILE_FETCHING_CERTS (20*60)
@@ -1238,7 +1236,7 @@ update_consensus_networkstatus_downloads(time_t now)
resource = networkstatus_get_flavor_name(i);
if (!download_status_is_ready(&consensus_dl_status[i], now,
- CONSENSUS_NETWORKSTATUS_MAX_DL_TRIES))
+ options->TestingConsensusMaxDownloadTries))
continue; /* We failed downloading a consensus too recently. */
if (connection_dir_get_by_purpose_and_resource(
DIR_PURPOSE_FETCH_CONSENSUS, resource))
@@ -1824,7 +1822,8 @@ networkstatus_set_current_consensus(const char *consensus,
* current consensus really alter our view of any OR's rate limits? */
connection_or_update_token_buckets(get_connection_array(), options);
- circuit_build_times_new_consensus_params(&circ_times, current_consensus);
+ circuit_build_times_new_consensus_params(get_circuit_build_times_mutable(),
+ current_consensus);
}
if (directory_caches_dir_info(options)) {
@@ -2172,9 +2171,17 @@ networkstatus_dump_bridge_status_to_file(time_t now)
char *status = networkstatus_getinfo_by_purpose("bridge", now);
const or_options_t *options = get_options();
char *fname = NULL;
+ char *thresholds = NULL, *thresholds_and_status = NULL;
+ routerlist_t *rl = router_get_routerlist();
+ dirserv_compute_bridge_flag_thresholds(rl);
+ thresholds = dirserv_get_flag_thresholds_line();
+ tor_asprintf(&thresholds_and_status, "flag-thresholds %s\n%s",
+ thresholds, status);
tor_asprintf(&fname, "%s"PATH_SEPARATOR"networkstatus-bridges",
options->DataDirectory);
- write_str_to_file(fname,status,0);
+ write_str_to_file(fname,thresholds_and_status,0);
+ tor_free(thresholds);
+ tor_free(thresholds_and_status);
tor_free(fname);
tor_free(status);
}
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
index 178f084b6..86219b77c 100644
--- a/src/or/nodelist.c
+++ b/src/or/nodelist.c
@@ -25,6 +25,8 @@
static void nodelist_drop_node(node_t *node, int remove_from_ht);
static void node_free(node_t *node);
static void update_router_have_minimum_dir_info(void);
+static double get_frac_paths_needed_for_circs(const or_options_t *options,
+ const networkstatus_t *ns);
/** A nodelist_t holds a node_t object for every router we're "willing to use
* for something". Specifically, it should hold a node_t for every node that
@@ -1317,7 +1319,7 @@ count_usable_descriptors(int *num_present, int *num_usable,
md ? "microdesc" : "desc", exit_only ? " exits" : "s");
}
-/** Return an extimate of which fraction of usable paths through the Tor
+/** Return an estimate of which fraction of usable paths through the Tor
* network we have available for use. */
static double
compute_frac_paths_available(const networkstatus_t *consensus,
@@ -1372,13 +1374,14 @@ compute_frac_paths_available(const networkstatus_t *consensus,
if (f_myexit < f_exit)
f_exit = f_myexit;
- tor_asprintf(status_out,
- "%d%% of guards bw, "
- "%d%% of midpoint bw, and "
- "%d%% of exit bw",
- (int)(f_guard*100),
- (int)(f_mid*100),
- (int)(f_exit*100));
+ if (status_out)
+ tor_asprintf(status_out,
+ "%d%% of guards bw, "
+ "%d%% of midpoint bw, and "
+ "%d%% of exit bw",
+ (int)(f_guard*100),
+ (int)(f_mid*100),
+ (int)(f_exit*100));
return f_guard * f_mid * f_exit;
}
@@ -1391,19 +1394,19 @@ count_loading_descriptors_progress(void)
{
int num_present = 0, num_usable=0;
time_t now = time(NULL);
+ const or_options_t *options = get_options();
const networkstatus_t *consensus =
networkstatus_get_reasonably_live_consensus(now,usable_consensus_flavor());
- double fraction;
+ double paths, fraction;
if (!consensus)
return 0; /* can't count descriptors if we have no list of them */
- count_usable_descriptors(&num_present, &num_usable, NULL,
- consensus, get_options(), now, NULL, 0);
+ paths = compute_frac_paths_available(consensus, options, now,
+ &num_present, &num_usable,
+ NULL);
- if (num_usable == 0)
- return 0; /* don't div by 0 */
- fraction = num_present / (num_usable/4.);
+ fraction = paths / get_frac_paths_needed_for_circs(options,consensus);
if (fraction > 1.0)
return 0; /* it's not the number of descriptors holding us back */
return BOOTSTRAP_STATUS_LOADING_DESCRIPTORS + (int)
diff --git a/src/or/ntmain.c b/src/or/ntmain.c
index 8b67b8682..e84831404 100644
--- a/src/or/ntmain.c
+++ b/src/or/ntmain.c
@@ -3,7 +3,6 @@
* Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
-#define MAIN_PRIVATE
#include "or.h"
#include "config.h"
#include "main.h"
@@ -315,6 +314,7 @@ nt_service_main(void)
case CMD_LIST_FINGERPRINT:
case CMD_HASH_PASSWORD:
case CMD_VERIFY_CONFIG:
+ case CMD_DUMP_CONFIG:
log_err(LD_CONFIG, "Unsupported command (--list-fingerprint, "
"--hash-password, or --verify-config) in NT service.");
break;
diff --git a/src/or/onion.c b/src/or/onion.c
index 1a0bcf106..3e1d63d4e 100644
--- a/src/or/onion.c
+++ b/src/or/onion.c
@@ -22,7 +22,6 @@
#include "relay.h"
#include "rephist.h"
#include "router.h"
-#include "tor_queue.h"
/** Type for a linked list of circuits that are waiting for a free CPU worker
* to process a waiting onion handshake. */
@@ -59,7 +58,7 @@ static void onion_queue_entry_remove(onion_queue_t *victim);
* MAX_ONIONSKIN_CHALLENGE/REPLY_LEN." Also, make sure that we can pass
* over-large values via EXTEND2/EXTENDED2, for future-compatibility.*/
-/** Return true iff we have room to queue another oninoskin of type
+/** Return true iff we have room to queue another onionskin of type
* <b>type</b>. */
static int
have_room_for_onionskin(uint16_t type)
@@ -870,7 +869,7 @@ extend_cell_parse(extend_cell_t *cell_out, const uint8_t command,
cell_out->cell_type = RELAY_COMMAND_EXTEND2;
++payload;
/* Parse the specifiers. We'll only take the first IPv4 and first IPv6
- * addres, and the node ID, and ignore everything else */
+ * address, and the node ID, and ignore everything else */
for (i = 0; i < n_specs; ++i) {
if (eop - payload < 2)
return -1;
diff --git a/src/or/onion_fast.c b/src/or/onion_fast.c
index aa034a8bd..8e778dbc6 100644
--- a/src/or/onion_fast.c
+++ b/src/or/onion_fast.c
@@ -22,7 +22,7 @@ fast_handshake_state_free(fast_handshake_state_t *victim)
tor_free(victim);
}
-/** Create the state needed to perform a CREATE_FAST hasnshake. Return 0
+/** Create the state needed to perform a CREATE_FAST handshake. Return 0
* on success, -1 on failure. */
int
fast_onionskin_create(fast_handshake_state_t **handshake_state_out,
diff --git a/src/or/or.h b/src/or/or.h
index 5318b0fe5..7df6c37f7 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -99,6 +99,7 @@
#include "ht.h"
#include "replaycache.h"
#include "crypto_curve25519.h"
+#include "tor_queue.h"
/* These signals are defined to help handle_control_signal work.
*/
@@ -227,8 +228,14 @@ typedef enum {
#define CONN_TYPE_AP_NATD_LISTENER 14
/** Type for sockets listening for DNS requests. */
#define CONN_TYPE_AP_DNS_LISTENER 15
-#define CONN_TYPE_MAX_ 15
-/* !!!! If CONN_TYPE_MAX_ is ever over 15, we must grow the type field in
+
+/** Type for connections from the Extended ORPort. */
+#define CONN_TYPE_EXT_OR 16
+/** Type for sockets listening for Extended ORPort connections. */
+#define CONN_TYPE_EXT_OR_LISTENER 17
+
+#define CONN_TYPE_MAX_ 17
+/* !!!! If _CONN_TYPE_MAX is ever over 31, we must grow the type field in
* connection_t. */
/* Proxy client types */
@@ -238,7 +245,9 @@ typedef enum {
#define PROXY_SOCKS5 3
/* !!!! If there is ever a PROXY_* type over 2, we must grow the proxy_type
* field in or_connection_t */
-/* pluggable transports proxy type */
+
+/* Pluggable transport proxy type. Don't use this in or_connection_t,
+ * instead use the actual underlying proxy type (see above). */
#define PROXY_PLUGGABLE 4
/* Proxy client handshake states */
@@ -306,6 +315,25 @@ typedef enum {
#define OR_CONN_STATE_OPEN 8
#define OR_CONN_STATE_MAX_ 8
+/** States of the Extended ORPort protocol. Be careful before changing
+ * the numbers: they matter. */
+#define EXT_OR_CONN_STATE_MIN_ 1
+/** Extended ORPort authentication is waiting for the authentication
+ * type selected by the client. */
+#define EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE 1
+/** Extended ORPort authentication is waiting for the client nonce. */
+#define EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE 2
+/** Extended ORPort authentication is waiting for the client hash. */
+#define EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH 3
+#define EXT_OR_CONN_STATE_AUTH_MAX 3
+/** Authentication finished and the Extended ORPort is now accepting
+ * traffic. */
+#define EXT_OR_CONN_STATE_OPEN 4
+/** Extended ORPort is flushing its last messages and preparing to
+ * start accepting OR connections. */
+#define EXT_OR_CONN_STATE_FLUSHING 5
+#define EXT_OR_CONN_STATE_MAX_ 5
+
#define EXIT_CONN_STATE_MIN_ 1
/** State for an exit connection: waiting for response from DNS farm. */
#define EXIT_CONN_STATE_RESOLVING 1
@@ -823,9 +851,15 @@ typedef enum {
/** Maximum number of queued cells on a circuit for which we are the
* midpoint before we give up and kill it. This must be >= circwindow
* to avoid killing innocent circuits, and >= circwindow*2 to give
- * leaky-pipe a chance for being useful someday.
+ * leaky-pipe a chance of working someday. The ORCIRC_MAX_MIDDLE_KILL_THRESH
+ * ratio controls the margin of error between emitting a warning and
+ * killing the circuit.
+ */
+#define ORCIRC_MAX_MIDDLE_CELLS (CIRCWINDOW_START_MAX*2)
+/** Ratio of hard (circuit kill) to soft (warning) thresholds for the
+ * ORCIRC_MAX_MIDDLE_CELLS tests.
*/
-#define ORCIRC_MAX_MIDDLE_CELLS (21*(CIRCWINDOW_START_MAX)/10)
+#define ORCIRC_MAX_MIDDLE_KILL_THRESH (1.1f)
/* Cell commands. These values are defined in tor-spec.txt. */
#define CELL_PADDING 0
@@ -846,6 +880,7 @@ typedef enum {
#define CELL_AUTH_CHALLENGE 130
#define CELL_AUTHENTICATE 131
#define CELL_AUTHORIZE 132
+#define CELL_COMMAND_MAX_ 132
/** How long to test reachability before complaining to the user. */
#define TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT (20*60)
@@ -1073,9 +1108,17 @@ typedef struct var_cell_t {
uint8_t payload[FLEXIBLE_ARRAY_MEMBER];
} var_cell_t;
+/** A parsed Extended ORPort message. */
+typedef struct ext_or_cmd_t {
+ uint16_t cmd; /** Command type */
+ uint16_t len; /** Body length */
+ char body[FLEXIBLE_ARRAY_MEMBER]; /** Message body */
+} ext_or_cmd_t;
+
/** A cell as packed for writing to the network. */
typedef struct packed_cell_t {
- struct packed_cell_t *next; /**< Next cell queued on this circuit. */
+ /** Next cell queued on this circuit. */
+ TOR_SIMPLEQ_ENTRY(packed_cell_t) next;
char body[CELL_MAX_NETWORK_SIZE]; /**< Cell as packed for network. */
uint32_t inserted_time; /**< Time (in milliseconds since epoch, with high
* bits truncated) when this cell was inserted. */
@@ -1099,13 +1142,30 @@ typedef struct insertion_time_queue_t {
struct insertion_time_elem_t *last; /**< Last element in queue. */
} insertion_time_queue_t;
+/** Number of cells with the same command consecutively added to a circuit
+ * queue; used for cell statistics only if CELL_STATS events are enabled. */
+typedef struct insertion_command_elem_t {
+ struct insertion_command_elem_t *next; /**< Next element in queue. */
+ /** Which command did these consecutively added cells have? */
+ uint8_t command;
+ unsigned counter; /**< How many cells were inserted? */
+} insertion_command_elem_t;
+
+/** Queue of insertion commands. */
+typedef struct insertion_command_queue_t {
+ struct insertion_command_elem_t *first; /**< First element in queue. */
+ struct insertion_command_elem_t *last; /**< Last element in queue. */
+} insertion_command_queue_t;
+
/** A queue of cells on a circuit, waiting to be added to the
* or_connection_t's outbuf. */
typedef struct cell_queue_t {
- packed_cell_t *head; /**< The first cell, or NULL if the queue is empty. */
- packed_cell_t *tail; /**< The last cell, or NULL if the queue is empty. */
+ /** Linked list of packed_cell_t*/
+ TOR_SIMPLEQ_HEAD(cell_simpleq, packed_cell_t) head;
int n; /**< The number of cells in the queue. */
insertion_time_queue_t *insertion_times; /**< Insertion times of cells. */
+ /** Commands of inserted cells. */
+ insertion_command_queue_t *insertion_commands;
} cell_queue_t;
/** Beginning of a RELAY cell payload. */
@@ -1158,7 +1218,7 @@ typedef struct connection_t {
* *_CONNECTION_MAGIC. */
uint8_t state; /**< Current state of this connection. */
- unsigned int type:4; /**< What kind of connection is this? */
+ unsigned int type:5; /**< What kind of connection is this? */
unsigned int purpose:5; /**< Only used for DIR and EXIT types currently. */
/* The next fields are all one-bit booleans. Some are only applicable to
@@ -1242,6 +1302,14 @@ typedef struct connection_t {
/** Unique identifier for this connection on this Tor instance. */
uint64_t global_identifier;
+
+ /** Bytes read since last call to control_event_conn_bandwidth_used().
+ * Only used if we're configured to emit CONN_BW events. */
+ uint32_t n_read_conn_bw;
+
+ /** Bytes written since last call to control_event_conn_bandwidth_used().
+ * Only used if we're configured to emit CONN_BW events. */
+ uint32_t n_written_conn_bw;
} connection_t;
/** Subtype of connection_t; used for a listener socket. */
@@ -1403,6 +1471,9 @@ typedef struct or_handshake_state_t {
/**@}*/
} or_handshake_state_t;
+/** Length of Extended ORPort connection identifier. */
+#define EXT_OR_CONN_ID_LEN DIGEST_LEN /* 20 */
+
/** Subtype of connection_t for an "OR connection" -- that is, one that speaks
* cells over TLS. */
typedef struct or_connection_t {
@@ -1411,6 +1482,20 @@ typedef struct or_connection_t {
/** Hash of the public RSA key for the other side's identity key, or zeroes
* if the other side hasn't shown us a valid identity key. */
char identity_digest[DIGEST_LEN];
+
+ /** Extended ORPort connection identifier. */
+ char *ext_or_conn_id;
+ /** This is the ClientHash value we expect to receive from the
+ * client during the Extended ORPort authentication protocol. We
+ * compute it upon receiving the ClientNoce from the client, and we
+ * compare it with the acual ClientHash value sent by the
+ * client. */
+ char *ext_or_auth_correct_client_hash;
+ /** String carrying the name of the pluggable transport
+ * (e.g. "obfs2") that is obfuscating this connection. If no
+ * pluggable transports are used, it's NULL. */
+ char *ext_or_transport;
+
char *nickname; /**< Nickname of OR on other side (if any). */
tor_tls_t *tls; /**< TLS connection state. */
@@ -1468,6 +1553,12 @@ typedef struct or_connection_t {
struct or_connection_t *next_with_same_id; /**< Next connection with same
* identity digest as this one. */
+ /** Last emptied read token bucket in msec since midnight; only used if
+ * TB_EMPTY events are enabled. */
+ uint32_t read_emptied_time;
+ /** Last emptied write token bucket in msec since midnight; only used if
+ * TB_EMPTY events are enabled. */
+ uint32_t write_emptied_time;
} or_connection_t;
/** Subtype of connection_t for an "edge connection" -- that is, an entry (ap)
@@ -2294,14 +2385,6 @@ typedef struct node_t {
} node_t;
-/** How many times will we try to download a router's descriptor before giving
- * up? */
-#define MAX_ROUTERDESC_DOWNLOAD_FAILURES 8
-
-/** How many times will we try to download a microdescriptor before giving
- * up? */
-#define MAX_MICRODESC_DOWNLOAD_FAILURES 8
-
/** Contents of a v2 (non-consensus, non-vote) network status object. */
typedef struct networkstatus_v2_t {
/** When did we receive the network-status document? */
@@ -2511,10 +2594,6 @@ typedef struct desc_store_t {
* filename for a temporary file when rebuilding the store, and .new to this
* filename for the journal. */
const char *fname_base;
- /** Alternative (obsolete) value for fname_base: if the file named by
- * fname_base isn't present, we read from here instead, but we never write
- * here. */
- const char *fname_alt_base;
/** Human-readable description of what this store contains. */
const char *description;
@@ -2591,9 +2670,6 @@ typedef struct authority_cert_t {
uint32_t addr;
/** This authority's directory port. */
uint16_t dir_port;
- /** True iff this certificate was cross-certified by signing the identity
- * key with the signing key. */
- uint8_t is_cross_certified;
} authority_cert_t;
/** Bitfield enum type listing types of information that directory authorities
@@ -2743,6 +2819,19 @@ typedef struct {
struct create_cell_t;
+/** Entry in the cell stats list of a circuit; used only if CELL_STATS
+ * events are enabled. */
+typedef struct testing_cell_stats_entry_t {
+ uint8_t command; /**< cell command number. */
+ /** Waiting time in centiseconds if this event is for a removed cell,
+ * or 0 if this event is for adding a cell to the queue. 22 bits can
+ * store more than 11 hours, enough to assume that a circuit with this
+ * delay would long have been closed. */
+ unsigned int waiting_time:22;
+ unsigned int removed:1; /**< 0 for added to, 1 for removed from queue. */
+ unsigned int exitward:1; /**< 0 for app-ward, 1 for exit-ward. */
+} testing_cell_stats_entry_t;
+
/**
* A circuit is a path over the onion routing
* network. Applications can connect to one end of the circuit, and can
@@ -2804,6 +2893,16 @@ typedef struct circuit_t {
* allowing n_streams to add any more cells. (OR circuit only.) */
unsigned int streams_blocked_on_p_chan : 1;
+ /** True iff we have queued a delete backwards on this circuit, but not put
+ * it on the output buffer. */
+ unsigned int p_delete_pending : 1;
+ /** True iff we have queued a delete forwards on this circuit, but not put
+ * it on the output buffer. */
+ unsigned int n_delete_pending : 1;
+
+ /** True iff this circuit has received a DESTROY cell in either direction */
+ unsigned int received_destroy : 1;
+
uint8_t state; /**< Current status of this circuit. */
uint8_t purpose; /**< Why are we creating this circuit? */
@@ -2858,7 +2957,8 @@ typedef struct circuit_t {
/** Unique ID for measuring tunneled network status requests. */
uint64_t dirreq_id;
- struct circuit_t *next; /**< Next circuit in linked list of all circuits. */
+ /** Next circuit in linked list of all circuits (global_circuitlist). */
+ TOR_LIST_ENTRY(circuit_t) head;
/** Next circuit in the doubly-linked ring of circuits waiting to add
* cells to n_conn. NULL if we have no cells pending, or if we're not
@@ -2868,6 +2968,11 @@ typedef struct circuit_t {
* cells to n_conn. NULL if we have no cells pending, or if we're not
* linked to an OR connection. */
struct circuit_t *prev_active_on_n_chan;
+
+ /** Various statistics about cells being added to or removed from this
+ * circuit's queues; used only if CELL_STATS events are enabled and
+ * cleared after being sent to control port. */
+ smartlist_t *testing_cell_stats;
} circuit_t;
/** Largest number of relay_early cells that we can send on a given
@@ -2938,6 +3043,17 @@ typedef struct origin_circuit_t {
/** Linked list of AP streams (or EXIT streams if hidden service)
* associated with this circuit. */
edge_connection_t *p_streams;
+
+ /** Bytes read from any attached stream since last call to
+ * control_event_circ_bandwidth_used(). Only used if we're configured
+ * to emit CIRC_BW events. */
+ uint32_t n_read_circ_bw;
+
+ /** Bytes written to any attached stream since last call to
+ * control_event_circ_bandwidth_used(). Only used if we're configured
+ * to emit CIRC_BW events. */
+ uint32_t n_written_circ_bw;
+
/** Build state for this circuit. It includes the intended path
* length, the chosen exit router, rendezvous information, etc.
*/
@@ -3184,6 +3300,12 @@ typedef struct or_circuit_t {
* exit-ward queues of this circuit; reset every time when writing
* buffer stats to disk. */
uint64_t total_cell_waiting_time;
+
+ /** Maximum cell queue size for a middle relay; this is stored per circuit
+ * so append_cell_to_circuit_queue() can adjust it if it changes. If set
+ * to zero, it is initialized to the default value.
+ */
+ uint32_t max_middle_cells;
} or_circuit_t;
/** Convert a circuit subtype to a circuit_t. */
@@ -3342,9 +3464,9 @@ typedef struct {
/** What should the tor process actually do? */
enum {
CMD_RUN_TOR=0, CMD_LIST_FINGERPRINT, CMD_HASH_PASSWORD,
- CMD_VERIFY_CONFIG, CMD_RUN_UNITTESTS
+ CMD_VERIFY_CONFIG, CMD_RUN_UNITTESTS, CMD_DUMP_CONFIG
} command;
- const char *command_arg; /**< Argument for command-line option. */
+ char *command_arg; /**< Argument for command-line option. */
config_line_t *Logs; /**< New-style list of configuration lines
* for logs */
@@ -3425,6 +3547,8 @@ typedef struct {
char *User; /**< Name of user to run Tor as. */
char *Group; /**< Name of group to run Tor as. */
config_line_t *ORPort_lines; /**< Ports to listen on for OR connections. */
+ /** Ports to listen on for extended OR connections. */
+ config_line_t *ExtORPort_lines;
/** Ports to listen on for SOCKS connections. */
config_line_t *SocksPort_lines;
/** Ports to listen on for transparent pf/netfilter connections. */
@@ -3460,6 +3584,7 @@ typedef struct {
unsigned int ControlPort_set : 1;
unsigned int DirPort_set : 1;
unsigned int DNSPort_set : 1;
+ unsigned int ExtORPort_set : 1;
/**@}*/
int AssumeReachable; /**< Whether to publish our descriptor regardless. */
@@ -3499,6 +3624,9 @@ typedef struct {
/** List of TCP/IP addresses that transports should listen at. */
config_line_t *ServerTransportListenAddr;
+ /** List of options that must be passed to pluggable transports. */
+ config_line_t *ServerTransportOptions;
+
int BridgeRelay; /**< Boolean: are we acting as a bridge relay? We make
* this explicit so we can change how we behave in the
* future. */
@@ -3736,7 +3864,10 @@ typedef struct {
int CookieAuthentication; /**< Boolean: do we enable cookie-based auth for
* the control system? */
- char *CookieAuthFile; /**< Location of a cookie authentication file. */
+ char *CookieAuthFile; /**< Filesystem location of a ControlPort
+ * authentication cookie. */
+ char *ExtORPortCookieAuthFile; /**< Filesystem location of Extended
+ * ORPort authentication cookie. */
int CookieAuthFileGroupReadable; /**< Boolean: Is the CookieAuthFile g+r? */
int LeaveStreamsUnattached; /**< Boolean: Does Tor attach new streams to
* circuits itself (0), or does it expect a controller
@@ -3758,6 +3889,7 @@ typedef struct {
SAFELOG_SCRUB_ALL, SAFELOG_SCRUB_RELAY, SAFELOG_SCRUB_NONE
} SafeLogging_;
+ int Sandbox; /**< Boolean: should sandboxing be enabled? */
int SafeSocks; /**< Boolean: should we outright refuse application
* connections that use socks4 or socks5-with-local-dns? */
#define LOG_PROTOCOL_WARN (get_options()->ProtocolWarnings ? \
@@ -3929,6 +4061,10 @@ typedef struct {
* signatures. Only altered on testing networks.*/
int TestingV3AuthInitialDistDelay;
+ /** Offset in seconds added to the starting time for consensus
+ voting. Only altered on testing networks. */
+ int TestingV3AuthVotingStartOffset;
+
/** If an authority has been around for less than this amount of time, it
* does not believe its reachability information is accurate. Only
* altered on testing networks. */
@@ -3939,6 +4075,51 @@ typedef struct {
* networks. */
int TestingEstimatedDescriptorPropagationTime;
+ /** Schedule for when servers should download things in general. Only
+ * altered on testing networks. */
+ smartlist_t *TestingServerDownloadSchedule;
+
+ /** Schedule for when clients should download things in general. Only
+ * altered on testing networks. */
+ smartlist_t *TestingClientDownloadSchedule;
+
+ /** Schedule for when servers should download consensuses. Only altered
+ * on testing networks. */
+ smartlist_t *TestingServerConsensusDownloadSchedule;
+
+ /** Schedule for when clients should download consensuses. Only altered
+ * on testing networks. */
+ smartlist_t *TestingClientConsensusDownloadSchedule;
+
+ /** Schedule for when clients should download bridge descriptors. Only
+ * altered on testing networks. */
+ smartlist_t *TestingBridgeDownloadSchedule;
+
+ /** When directory clients have only a few descriptors to request, they
+ * batch them until they have more, or until this amount of time has
+ * passed. Only altered on testing networks. */
+ int TestingClientMaxIntervalWithoutRequest;
+
+ /** How long do we let a directory connection stall before expiring
+ * it? Only altered on testing networks. */
+ int TestingDirConnectionMaxStall;
+
+ /** How many times will we try to fetch a consensus before we give
+ * up? Only altered on testing networks. */
+ int TestingConsensusMaxDownloadTries;
+
+ /** How many times will we try to download a router's descriptor before
+ * giving up? Only altered on testing networks. */
+ int TestingDescriptorMaxDownloadTries;
+
+ /** How many times will we try to download a microdescriptor before
+ * giving up? Only altered on testing networks. */
+ int TestingMicrodescMaxDownloadTries;
+
+ /** How many times will we try to fetch a certificate before giving
+ * up? Only altered on testing networks. */
+ int TestingCertMaxDownloadTries;
+
/** If true, we take part in a testing network. Change the defaults of a
* couple of other configuration options and allow to change the values
* of certain configuration options. */
@@ -3950,6 +4131,19 @@ typedef struct {
/** Minimum value for the Fast flag threshold on testing networks. */
uint64_t TestingMinFastFlagThreshold;
+ /** Relays in a testing network which should be voted Guard
+ * regardless of uptime and bandwidth. */
+ routerset_t *TestingDirAuthVoteGuard;
+
+ /** Enable CONN_BW events. Only altered on testing networks. */
+ int TestingEnableConnBwEvent;
+
+ /** Enable CELL_STATS events. Only altered on testing networks. */
+ int TestingEnableCellStatsEvent;
+
+ /** Enable TB_EMPTY events. Only altered on testing networks. */
+ int TestingEnableTbEmptyEvent;
+
/** If true, and we have GeoIP data, and we're a bridge, keep a per-country
* count of how many client addresses have contacted us so that we can help
* the bridge authority guess which countries have blocked access to us. */
@@ -4362,30 +4556,7 @@ typedef struct {
int after_firsthop_idx;
} network_liveness_t;
-/** Structure for circuit build times history */
-typedef struct {
- /** The circular array of recorded build times in milliseconds */
- build_time_t circuit_build_times[CBT_NCIRCUITS_TO_OBSERVE];
- /** Current index in the circuit_build_times circular array */
- int build_times_idx;
- /** Total number of build times accumulated. Max CBT_NCIRCUITS_TO_OBSERVE */
- int total_build_times;
- /** Information about the state of our local network connection */
- network_liveness_t liveness;
- /** Last time we built a circuit. Used to decide to build new test circs */
- time_t last_circ_at;
- /** "Minimum" value of our pareto distribution (actually mode) */
- build_time_t Xm;
- /** alpha exponent for pareto dist. */
- double alpha;
- /** Have we computed a timeout? */
- int have_computed_timeout;
- /** The exact value for that timeout in milliseconds. Stored as a double
- * to maintain precision from calculations to and from quantile value. */
- double timeout_ms;
- /** How long we wait before actually closing the circuit. */
- double close_ms;
-} circuit_build_times_t;
+typedef struct circuit_build_times_s circuit_build_times_t;
/********************************* config.c ***************************/
diff --git a/src/or/relay.c b/src/or/relay.c
index 63119cbf0..b5e4ff7cc 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -15,6 +15,7 @@
#include "addressmap.h"
#include "buffers.h"
#include "channel.h"
+#include "circpathbias.h"
#include "circuitbuild.h"
#include "circuitlist.h"
#include "circuituse.h"
@@ -58,6 +59,9 @@ static void adjust_exit_policy_from_exitpolicy_failure(origin_circuit_t *circ,
entry_connection_t *conn,
node_t *node,
const tor_addr_t *addr);
+#if 0
+static int get_max_middle_cells(void);
+#endif
/** Stop reading on edge connections when we have this many cells
* waiting on the appropriate queue. */
@@ -966,7 +970,7 @@ remap_event_helper(entry_connection_t *conn, const tor_addr_t *new_addr)
* <b>addr_out</b> to the address we're connected to, and <b>ttl_out</b> to
* the ttl of that address, in seconds, and return 0. On failure, return
* -1. */
-int
+STATIC int
connected_cell_parse(const relay_header_t *rh, const cell_t *cell,
tor_addr_t *addr_out, int *ttl_out)
{
@@ -1104,8 +1108,9 @@ connection_edge_process_relay_cell_not_open(
break;
case DIR_PURPOSE_FETCH_SERVERDESC:
case DIR_PURPOSE_FETCH_MICRODESC:
- control_event_bootstrap(BOOTSTRAP_STATUS_LOADING_DESCRIPTORS,
- count_loading_descriptors_progress());
+ if (TO_DIR_CONN(dirconn)->router_purpose == ROUTER_PURPOSE_GENERAL)
+ control_event_bootstrap(BOOTSTRAP_STATUS_LOADING_DESCRIPTORS,
+ count_loading_descriptors_progress());
break;
}
}
@@ -1495,7 +1500,8 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
if (layer_hint) {
if (layer_hint->package_window + CIRCWINDOW_INCREMENT >
CIRCWINDOW_START_MAX) {
- log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ static struct ratelim_t exit_warn_ratelim = RATELIM_INIT(600);
+ log_fn_ratelim(&exit_warn_ratelim, LOG_WARN, LD_PROTOCOL,
"Unexpected sendme cell from exit relay. "
"Closing circ.");
return -END_CIRC_REASON_TORPROTOCOL;
@@ -1507,7 +1513,8 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
} else {
if (circ->package_window + CIRCWINDOW_INCREMENT >
CIRCWINDOW_START_MAX) {
- log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ static struct ratelim_t client_warn_ratelim = RATELIM_INIT(600);
+ log_fn_ratelim(&client_warn_ratelim, LOG_WARN, LD_PROTOCOL,
"Unexpected sendme cell from client. "
"Closing circ (window %d).",
circ->package_window);
@@ -2044,6 +2051,10 @@ static mp_pool_t *cell_pool = NULL;
* statistics. */
static mp_pool_t *it_pool = NULL;
+/** Memory pool to allocate insertion_command_elem_t objects used for cell
+ * statistics if CELL_STATS events are enabled. */
+static mp_pool_t *ic_pool = NULL;
+
/** Allocate structures to hold cells. */
void
init_cell_pool(void)
@@ -2052,8 +2063,8 @@ init_cell_pool(void)
cell_pool = mp_pool_new(sizeof(packed_cell_t), 128*1024);
}
-/** Free all storage used to hold cells (and insertion times if we measure
- * cell statistics). */
+/** Free all storage used to hold cells (and insertion times/commands if we
+ * measure cell statistics and/or if CELL_STATS events are enabled). */
void
free_cell_pool(void)
{
@@ -2066,6 +2077,10 @@ free_cell_pool(void)
mp_pool_destroy(it_pool);
it_pool = NULL;
}
+ if (ic_pool) {
+ mp_pool_destroy(ic_pool);
+ ic_pool = NULL;
+ }
}
/** Free excess storage in cell pool. */
@@ -2085,7 +2100,7 @@ packed_cell_free_unchecked(packed_cell_t *cell)
}
/** Allocate and return a new packed_cell_t. */
-static INLINE packed_cell_t *
+STATIC packed_cell_t *
packed_cell_new(void)
{
++total_cells_allocated;
@@ -2096,6 +2111,8 @@ packed_cell_new(void)
void
packed_cell_free(packed_cell_t *cell)
{
+ if (!cell)
+ return;
packed_cell_free_unchecked(cell);
}
@@ -2107,7 +2124,7 @@ dump_cell_pool_usage(int severity)
circuit_t *c;
int n_circs = 0;
int n_cells = 0;
- for (c = circuit_get_global_list_(); c; c = c->next) {
+ TOR_LIST_FOREACH(c, circuit_get_global_list(), head) {
n_cells += c->n_chan_cells.n;
if (!CIRCUIT_IS_ORIGIN(c))
n_cells += TO_OR_CIRCUIT(c)->p_chan_cells.n;
@@ -2125,7 +2142,6 @@ packed_cell_copy(const cell_t *cell, int wide_circ_ids)
{
packed_cell_t *c = packed_cell_new();
cell_pack(c, cell, wide_circ_ids);
- c->next = NULL;
return c;
}
@@ -2133,21 +2149,69 @@ packed_cell_copy(const cell_t *cell, int wide_circ_ids)
void
cell_queue_append(cell_queue_t *queue, packed_cell_t *cell)
{
- if (queue->tail) {
- tor_assert(!queue->tail->next);
- queue->tail->next = cell;
+ TOR_SIMPLEQ_INSERT_TAIL(&queue->head, cell, next);
+ ++queue->n;
+}
+
+/** Append command of type <b>command</b> in direction to <b>queue</b> for
+ * CELL_STATS event. */
+static void
+cell_command_queue_append(cell_queue_t *queue, uint8_t command)
+{
+ insertion_command_queue_t *ic_queue = queue->insertion_commands;
+ if (!ic_pool)
+ ic_pool = mp_pool_new(sizeof(insertion_command_elem_t), 1024);
+ if (!ic_queue) {
+ ic_queue = tor_malloc_zero(sizeof(insertion_command_queue_t));
+ queue->insertion_commands = ic_queue;
+ }
+ if (ic_queue->last && ic_queue->last->command == command) {
+ ic_queue->last->counter++;
} else {
- queue->head = cell;
+ insertion_command_elem_t *elem = mp_pool_get(ic_pool);
+ elem->next = NULL;
+ elem->command = command;
+ elem->counter = 1;
+ if (ic_queue->last) {
+ ic_queue->last->next = elem;
+ ic_queue->last = elem;
+ } else {
+ ic_queue->first = ic_queue->last = elem;
+ }
}
- queue->tail = cell;
- cell->next = NULL;
- ++queue->n;
}
-/** Append a newly allocated copy of <b>cell</b> to the end of <b>queue</b> */
+/** Retrieve oldest command from <b>queue</b> and write it to
+ * <b>command</b> for CELL_STATS event. Return 0 for success, -1
+ * otherwise. */
+static int
+cell_command_queue_pop(uint8_t *command, cell_queue_t *queue)
+{
+ int res = -1;
+ insertion_command_queue_t *ic_queue = queue->insertion_commands;
+ if (ic_queue && ic_queue->first) {
+ insertion_command_elem_t *ic_elem = ic_queue->first;
+ ic_elem->counter--;
+ if (ic_elem->counter < 1) {
+ ic_queue->first = ic_elem->next;
+ if (ic_elem == ic_queue->last)
+ ic_queue->last = NULL;
+ mp_pool_release(ic_elem);
+ }
+ *command = ic_elem->command;
+ res = 0;
+ }
+ return res;
+}
+
+/** Append a newly allocated copy of <b>cell</b> to the end of the
+ * <b>exitward</b> (or app-ward) <b>queue</b> of <b>circ</b>. If
+ * <b>use_stats</b> is true, record statistics about the cell.
+ */
void
-cell_queue_append_packed_copy(cell_queue_t *queue, const cell_t *cell,
- int wide_circ_ids)
+cell_queue_append_packed_copy(circuit_t *circ, cell_queue_t *queue,
+ int exitward, const cell_t *cell,
+ int wide_circ_ids, int use_stats)
{
struct timeval now;
packed_cell_t *copy = packed_cell_copy(cell, wide_circ_ids);
@@ -2156,7 +2220,8 @@ cell_queue_append_packed_copy(cell_queue_t *queue, const cell_t *cell,
/* Remember the time when this cell was put in the queue. */
/*XXXX This may be obsoleted by inserted_time */
- if (get_options()->CellStatistics) {
+ if ((get_options()->CellStatistics ||
+ get_options()->TestingEnableCellStatsEvent) && use_stats) {
uint32_t added;
insertion_time_queue_t *it_queue = queue->insertion_times;
if (!it_pool)
@@ -2184,21 +2249,39 @@ cell_queue_append_packed_copy(cell_queue_t *queue, const cell_t *cell,
}
}
}
+ /* Remember that we added a cell to the queue, and remember the cell
+ * command. */
+ if (get_options()->TestingEnableCellStatsEvent && circ) {
+ testing_cell_stats_entry_t *ent =
+ tor_malloc_zero(sizeof(testing_cell_stats_entry_t));
+ ent->command = cell->command;
+ ent->exitward = exitward;
+ if (!circ->testing_cell_stats)
+ circ->testing_cell_stats = smartlist_new();
+ smartlist_add(circ->testing_cell_stats, ent);
+ cell_command_queue_append(queue, cell->command);
+ }
cell_queue_append(queue, copy);
}
+/** Initialize <b>queue</b> as an empty cell queue. */
+void
+cell_queue_init(cell_queue_t *queue)
+{
+ memset(queue, 0, sizeof(cell_queue_t));
+ TOR_SIMPLEQ_INIT(&queue->head);
+}
+
/** Remove and free every cell in <b>queue</b>. */
void
cell_queue_clear(cell_queue_t *queue)
{
- packed_cell_t *cell, *next;
- cell = queue->head;
- while (cell) {
- next = cell->next;
+ packed_cell_t *cell;
+ while ((cell = TOR_SIMPLEQ_FIRST(&queue->head))) {
+ TOR_SIMPLEQ_REMOVE_HEAD(&queue->head, next);
packed_cell_free_unchecked(cell);
- cell = next;
}
- queue->head = queue->tail = NULL;
+ TOR_SIMPLEQ_INIT(&queue->head);
queue->n = 0;
if (queue->insertion_times) {
while (queue->insertion_times->first) {
@@ -2212,17 +2295,13 @@ cell_queue_clear(cell_queue_t *queue)
/** Extract and return the cell at the head of <b>queue</b>; return NULL if
* <b>queue</b> is empty. */
-static INLINE packed_cell_t *
+STATIC packed_cell_t *
cell_queue_pop(cell_queue_t *queue)
{
- packed_cell_t *cell = queue->head;
+ packed_cell_t *cell = TOR_SIMPLEQ_FIRST(&queue->head);
if (!cell)
return NULL;
- queue->head = cell->next;
- if (cell == queue->tail) {
- tor_assert(!queue->head);
- queue->tail = NULL;
- }
+ TOR_SIMPLEQ_REMOVE_HEAD(&queue->head, next);
--queue->n;
return cell;
}
@@ -2373,7 +2452,7 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max)
{
circuitmux_t *cmux = NULL;
int n_flushed = 0;
- cell_queue_t *queue;
+ cell_queue_t *queue, *destroy_queue=NULL;
circuit_t *circ;
or_circuit_t *or_circ;
int streams_blocked;
@@ -2386,7 +2465,18 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max)
/* Main loop: pick a circuit, send a cell, update the cmux */
while (n_flushed < max) {
- circ = circuitmux_get_first_active_circuit(cmux);
+ circ = circuitmux_get_first_active_circuit(cmux, &destroy_queue);
+ if (destroy_queue) {
+ /* this code is duplicated from some of the logic below. Ugly! XXXX */
+ tor_assert(destroy_queue->n > 0);
+ cell = cell_queue_pop(destroy_queue);
+ channel_write_packed_cell(chan, cell);
+ /* Update the cmux destroy counter */
+ circuitmux_notify_xmit_destroy(cmux);
+ cell = NULL;
+ ++n_flushed;
+ continue;
+ }
/* If it returns NULL, no cells left to send */
if (!circ) break;
assert_cmux_ok_paranoid(chan);
@@ -2412,7 +2502,8 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max)
cell = cell_queue_pop(queue);
/* Calculate the exact time that this cell has spent in the queue. */
- if (get_options()->CellStatistics && !CIRCUIT_IS_ORIGIN(circ)) {
+ if (get_options()->CellStatistics ||
+ get_options()->TestingEnableCellStatsEvent) {
struct timeval tvnow;
uint32_t flushed;
uint32_t cell_waiting_time;
@@ -2426,7 +2517,6 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max)
"recently enabled.");
} else {
insertion_time_elem_t *elem = it_queue->first;
- or_circ = TO_OR_CIRCUIT(circ);
cell_waiting_time =
(uint32_t)((flushed * 10L + SECONDS_IN_A_DAY * 1000L -
elem->insertion_time * 10L) %
@@ -2439,8 +2529,30 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max)
it_queue->last = NULL;
mp_pool_release(elem);
}
- or_circ->total_cell_waiting_time += cell_waiting_time;
- or_circ->processed_cells++;
+ if (get_options()->CellStatistics && !CIRCUIT_IS_ORIGIN(circ)) {
+ or_circ = TO_OR_CIRCUIT(circ);
+ or_circ->total_cell_waiting_time += cell_waiting_time;
+ or_circ->processed_cells++;
+ }
+ if (get_options()->TestingEnableCellStatsEvent) {
+ uint8_t command;
+ if (cell_command_queue_pop(&command, queue) < 0) {
+ log_info(LD_GENERAL, "Cannot determine command of cell. "
+ "Looks like the CELL_STATS event was "
+ "recently enabled.");
+ } else {
+ testing_cell_stats_entry_t *ent =
+ tor_malloc_zero(sizeof(testing_cell_stats_entry_t));
+ ent->command = command;
+ ent->waiting_time = (unsigned int)cell_waiting_time / 10;
+ ent->removed = 1;
+ if (circ->n_chan == chan)
+ ent->exitward = 1;
+ if (!circ->testing_cell_stats)
+ circ->testing_cell_stats = smartlist_new();
+ smartlist_add(circ->testing_cell_stats, ent);
+ }
+ }
}
}
@@ -2487,6 +2599,20 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max)
return n_flushed;
}
+#if 0
+/** Indicate the current preferred cap for middle circuits; zero disables
+ * the cap. Right now it's just a constant, ORCIRC_MAX_MIDDLE_CELLS, but
+ * the logic in append_cell_to_circuit_queue() is written to be correct
+ * if we want to base it on a consensus param or something that might change
+ * in the future.
+ */
+static int
+get_max_middle_cells(void)
+{
+ return ORCIRC_MAX_MIDDLE_CELLS;
+}
+#endif
+
/** Add <b>cell</b> to the queue of <b>circ</b> writing to <b>chan</b>
* transmitting in <b>direction</b>. */
void
@@ -2497,11 +2623,16 @@ append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan,
or_circuit_t *orcirc = NULL;
cell_queue_t *queue;
int streams_blocked;
+#if 0
+ uint32_t tgt_max_middle_cells, p_len, n_len, tmp, hard_max_middle_cells;
+#endif
+ int exitward;
if (circ->marked_for_close)
return;
- if (direction == CELL_DIRECTION_OUT) {
+ exitward = (direction == CELL_DIRECTION_OUT);
+ if (exitward) {
queue = &circ->n_chan_cells;
streams_blocked = circ->streams_blocked_on_n_chan;
} else {
@@ -2518,28 +2649,82 @@ append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan,
if ((circ->n_chan != NULL) && CIRCUIT_IS_ORCIRC(circ)) {
orcirc = TO_OR_CIRCUIT(circ);
if (orcirc->p_chan) {
- if (queue->n + 1 >= ORCIRC_MAX_MIDDLE_CELLS) {
- /* Queueing this cell would put queue over the cap */
- log_warn(LD_CIRC,
- "Got a cell exceeding the cap of %u in the %s direction "
- "on middle circ ID %u on chan ID " U64_FORMAT
- "; killing the circuit.",
- ORCIRC_MAX_MIDDLE_CELLS,
- (direction == CELL_DIRECTION_OUT) ? "n" : "p",
- (direction == CELL_DIRECTION_OUT) ?
- circ->n_circ_id : orcirc->p_circ_id,
- U64_PRINTF_ARG(
+ /* We are a middle circuit if we have both n_chan and p_chan */
+ /* We'll need to know the current preferred maximum */
+ tgt_max_middle_cells = get_max_middle_cells();
+ if (tgt_max_middle_cells > 0) {
+ /* Do we need to initialize middle_max_cells? */
+ if (orcirc->max_middle_cells == 0) {
+ orcirc->max_middle_cells = tgt_max_middle_cells;
+ } else {
+ if (tgt_max_middle_cells > orcirc->max_middle_cells) {
+ /* If we want to increase the cap, we can do so right away */
+ orcirc->max_middle_cells = tgt_max_middle_cells;
+ } else if (tgt_max_middle_cells < orcirc->max_middle_cells) {
+ /*
+ * If we're shrinking the cap, we can't shrink past either queue;
+ * compare tgt_max_middle_cells rather than tgt_max_middle_cells *
+ * ORCIRC_MAX_MIDDLE_KILL_THRESH so the queues don't shrink enough
+ * to generate spurious warnings, either.
+ */
+ n_len = circ->n_chan_cells.n;
+ p_len = orcirc->p_chan_cells.n;
+ tmp = tgt_max_middle_cells;
+ if (tmp < n_len) tmp = n_len;
+ if (tmp < p_len) tmp = p_len;
+ orcirc->max_middle_cells = tmp;
+ }
+ /* else no change */
+ }
+ } else {
+ /* tgt_max_middle_cells == 0 indicates we should disable the cap */
+ orcirc->max_middle_cells = 0;
+ }
+
+ /* Now we know orcirc->max_middle_cells is set correctly */
+ if (orcirc->max_middle_cells > 0) {
+ hard_max_middle_cells =
+ (uint32_t)(((double)orcirc->max_middle_cells) *
+ ORCIRC_MAX_MIDDLE_KILL_THRESH);
+
+ if ((unsigned)queue->n + 1 >= hard_max_middle_cells) {
+ /* Queueing this cell would put queue over the kill theshold */
+ log_warn(LD_CIRC,
+ "Got a cell exceeding the hard cap of %u in the "
+ "%s direction on middle circ ID %u on chan ID "
+ U64_FORMAT "; killing the circuit.",
+ hard_max_middle_cells,
+ (direction == CELL_DIRECTION_OUT) ? "n" : "p",
+ (direction == CELL_DIRECTION_OUT) ?
+ circ->n_circ_id : orcirc->p_circ_id,
+ U64_PRINTF_ARG(
+ (direction == CELL_DIRECTION_OUT) ?
+ circ->n_chan->global_identifier :
+ orcirc->p_chan->global_identifier));
+ circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT);
+ return;
+ } else if ((unsigned)queue->n + 1 == orcirc->max_middle_cells) {
+ /* Only use ==, not >= for this test so we don't spam the log */
+ log_warn(LD_CIRC,
+ "While trying to queue a cell, reached the soft cap of %u "
+ "in the %s direction on middle circ ID %u "
+ "on chan ID " U64_FORMAT ".",
+ orcirc->max_middle_cells,
+ (direction == CELL_DIRECTION_OUT) ? "n" : "p",
(direction == CELL_DIRECTION_OUT) ?
- circ->n_chan->global_identifier :
- orcirc->p_chan->global_identifier));
- circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT);
- return;
+ circ->n_circ_id : orcirc->p_circ_id,
+ U64_PRINTF_ARG(
+ (direction == CELL_DIRECTION_OUT) ?
+ circ->n_chan->global_identifier :
+ orcirc->p_chan->global_identifier));
+ }
}
}
}
#endif
- cell_queue_append_packed_copy(queue, cell, chan->wide_circ_ids);
+ cell_queue_append_packed_copy(circ, queue, exitward, cell,
+ chan->wide_circ_ids, 1);
if (PREDICT_UNLIKELY(cell_queues_check_size())) {
/* We ran the OOM handler */
diff --git a/src/or/relay.h b/src/or/relay.h
index 1fef10a7d..20eecfb40 100644
--- a/src/or/relay.h
+++ b/src/or/relay.h
@@ -51,10 +51,12 @@ size_t packed_cell_mem_cost(void);
/* For channeltls.c */
void packed_cell_free(packed_cell_t *cell);
+void cell_queue_init(cell_queue_t *queue);
void cell_queue_clear(cell_queue_t *queue);
void cell_queue_append(cell_queue_t *queue, packed_cell_t *cell);
-void cell_queue_append_packed_copy(cell_queue_t *queue, const cell_t *cell,
- int wide_circ_ids);
+void cell_queue_append_packed_copy(circuit_t *circ, cell_queue_t *queue,
+ int exitward, const cell_t *cell,
+ int wide_circ_ids, int use_stats);
void append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan,
cell_t *cell, cell_direction_t direction,
@@ -75,11 +77,14 @@ void circuit_clear_cell_queue(circuit_t *circ, channel_t *chan);
void stream_choice_seed_weak_rng(void);
-#ifdef RELAY_PRIVATE
int relay_crypt(circuit_t *circ, cell_t *cell, cell_direction_t cell_direction,
crypt_path_t **layer_hint, char *recognized);
-int connected_cell_parse(const relay_header_t *rh, const cell_t *cell,
+
+#ifdef RELAY_PRIVATE
+STATIC int connected_cell_parse(const relay_header_t *rh, const cell_t *cell,
tor_addr_t *addr_out, int *ttl_out);
+STATIC packed_cell_t *packed_cell_new(void);
+STATIC packed_cell_t *cell_queue_pop(cell_queue_t *queue);
#endif
#endif
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index bb4bd9bfd..f00303f18 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -8,6 +8,7 @@
**/
#include "or.h"
+#include "circpathbias.h"
#include "circuitbuild.h"
#include "circuitlist.h"
#include "circuituse.h"
@@ -376,7 +377,7 @@ rend_client_close_other_intros(const char *onion_address)
{
circuit_t *c;
/* abort parallel intro circs, if any */
- for (c = circuit_get_global_list_(); c; c = c->next) {
+ TOR_LIST_FOREACH(c, circuit_get_global_list(), head) {
if ((c->purpose == CIRCUIT_PURPOSE_C_INTRODUCING ||
c->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) &&
!c->marked_for_close && CIRCUIT_IS_ORIGIN(c)) {
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 8a4a11e47..cb2f96299 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -10,6 +10,7 @@
#define RENDSERVICE_PRIVATE
#include "or.h"
+#include "circpathbias.h"
#include "circuitbuild.h"
#include "circuitlist.h"
#include "circuituse.h"
@@ -543,7 +544,7 @@ rend_config_services(const or_options_t *options, int validate_only)
/* XXXX it would be nicer if we had a nicer abstraction to use here,
* so we could just iterate over the list of services to close, but
* once again, this isn't critical-path code. */
- for (circ = circuit_get_global_list_(); circ; circ = circ->next) {
+ TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) {
if (!circ->marked_for_close &&
circ->state == CIRCUIT_STATE_OPEN &&
(circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
@@ -1208,7 +1209,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
/* check for replay of PK-encrypted portion. */
replay = replaycache_add_test_and_elapsed(
intro_point->accepted_intro_rsa_parts,
- parsed_req->ciphertext, (int)parsed_req->ciphertext_len,
+ parsed_req->ciphertext, parsed_req->ciphertext_len,
&elapsed);
if (replay) {
@@ -2376,7 +2377,7 @@ count_established_intro_points(const char *query)
{
int num_ipos = 0;
circuit_t *circ;
- for (circ = circuit_get_global_list_(); circ; circ = circ->next) {
+ TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) {
if (!circ->marked_for_close &&
circ->state == CIRCUIT_STATE_OPEN &&
(circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
diff --git a/src/or/rephist.c b/src/or/rephist.c
index 131e531b1..13404badf 100644
--- a/src/or/rephist.c
+++ b/src/or/rephist.c
@@ -2313,7 +2313,7 @@ rep_hist_format_exit_stats(time_t now)
time_t
rep_hist_exit_stats_write(time_t now)
{
- char *statsdir = NULL, *filename = NULL, *str = NULL;
+ char *str = NULL;
if (!start_of_exit_stats_interval)
return 0; /* Not initialized. */
@@ -2329,19 +2329,12 @@ rep_hist_exit_stats_write(time_t now)
rep_hist_reset_exit_stats(now);
/* Try to write to disk. */
- statsdir = get_datadir_fname("stats");
- if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) {
- log_warn(LD_HIST, "Unable to create stats/ directory!");
- goto done;
+ if (!check_or_create_data_subdir("stats")) {
+ write_to_data_subdir("stats", "exit-stats", str, "exit port statistics");
}
- filename = get_datadir_fname2("stats", "exit-stats");
- if (write_str_to_file(filename, str, 0) < 0)
- log_warn(LD_HIST, "Unable to write exit port statistics to disk!");
done:
tor_free(str);
- tor_free(statsdir);
- tor_free(filename);
return start_of_exit_stats_interval + WRITE_STATS_INTERVAL;
}
@@ -2598,7 +2591,7 @@ time_t
rep_hist_buffer_stats_write(time_t now)
{
circuit_t *circ;
- char *statsdir = NULL, *filename = NULL, *str = NULL;
+ char *str = NULL;
if (!start_of_buffer_stats_interval)
return 0; /* Not initialized. */
@@ -2606,7 +2599,7 @@ rep_hist_buffer_stats_write(time_t now)
goto done; /* Not ready to write */
/* Add open circuits to the history. */
- for (circ = circuit_get_global_list_(); circ; circ = circ->next) {
+ TOR_LIST_FOREACH(circ, circuit_get_global_list(), head) {
rep_hist_buffer_stats_add_circ(circ, now);
}
@@ -2617,19 +2610,12 @@ rep_hist_buffer_stats_write(time_t now)
rep_hist_reset_buffer_stats(now);
/* Try to write to disk. */
- statsdir = get_datadir_fname("stats");
- if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) {
- log_warn(LD_HIST, "Unable to create stats/ directory!");
- goto done;
+ if (!check_or_create_data_subdir("stats")) {
+ write_to_data_subdir("stats", "buffer-stats", str, "buffer statistics");
}
- filename = get_datadir_fname2("stats", "buffer-stats");
- if (write_str_to_file(filename, str, 0) < 0)
- log_warn(LD_HIST, "Unable to write buffer stats to disk!");
done:
tor_free(str);
- tor_free(filename);
- tor_free(statsdir);
return start_of_buffer_stats_interval + WRITE_STATS_INTERVAL;
}
@@ -2741,7 +2727,7 @@ rep_hist_format_desc_stats(time_t now)
time_t
rep_hist_desc_stats_write(time_t now)
{
- char *statsdir = NULL, *filename = NULL, *str = NULL;
+ char *filename = NULL, *str = NULL;
if (!start_of_served_descs_stats_interval)
return 0; /* We're not collecting stats. */
@@ -2751,10 +2737,8 @@ rep_hist_desc_stats_write(time_t now)
str = rep_hist_format_desc_stats(now);
tor_assert(str != NULL);
- statsdir = get_datadir_fname("stats");
- if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) {
- log_warn(LD_HIST, "Unable to create stats/ directory!");
- goto done;
+ if (check_or_create_data_subdir("stats") < 0) {
+ goto done;
}
filename = get_datadir_fname2("stats", "served-desc-stats");
if (append_bytes_to_file(filename, str, strlen(str), 0) < 0)
@@ -2763,7 +2747,6 @@ rep_hist_desc_stats_write(time_t now)
rep_hist_reset_desc_stats(now);
done:
- tor_free(statsdir);
tor_free(filename);
tor_free(str);
return start_of_served_descs_stats_interval + WRITE_STATS_INTERVAL;
@@ -2981,7 +2964,7 @@ rep_hist_format_conn_stats(time_t now)
time_t
rep_hist_conn_stats_write(time_t now)
{
- char *statsdir = NULL, *filename = NULL, *str = NULL;
+ char *str = NULL;
if (!start_of_conn_stats_interval)
return 0; /* Not initialized. */
@@ -2995,19 +2978,12 @@ rep_hist_conn_stats_write(time_t now)
rep_hist_reset_conn_stats(now);
/* Try to write to disk. */
- statsdir = get_datadir_fname("stats");
- if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) {
- log_warn(LD_HIST, "Unable to create stats/ directory!");
- goto done;
+ if (!check_or_create_data_subdir("stats")) {
+ write_to_data_subdir("stats", "conn-stats", str, "connection statistics");
}
- filename = get_datadir_fname2("stats", "conn-stats");
- if (write_str_to_file(filename, str, 0) < 0)
- log_warn(LD_HIST, "Unable to write conn stats to disk!");
done:
tor_free(str);
- tor_free(filename);
- tor_free(statsdir);
return start_of_conn_stats_interval + WRITE_STATS_INTERVAL;
}
diff --git a/src/or/replaycache.c b/src/or/replaycache.c
index 59b98489b..90f87c12d 100644
--- a/src/or/replaycache.c
+++ b/src/or/replaycache.c
@@ -63,9 +63,9 @@ replaycache_new(time_t horizon, time_t interval)
/** See documentation for replaycache_add_and_test()
*/
-int
+STATIC int
replaycache_add_and_test_internal(
- time_t present, replaycache_t *r, const void *data, int len,
+ time_t present, replaycache_t *r, const void *data, size_t len,
time_t *elapsed)
{
int rv = 0;
@@ -73,7 +73,7 @@ replaycache_add_and_test_internal(
time_t *access_time;
/* sanity check */
- if (present <= 0 || !r || !data || len <= 0) {
+ if (present <= 0 || !r || !data || len == 0) {
log_info(LD_BUG, "replaycache_add_and_test_internal() called with stupid"
" parameters; please fix this.");
goto done;
@@ -127,14 +127,13 @@ replaycache_add_and_test_internal(
/** See documentation for replaycache_scrub_if_needed()
*/
-void
+STATIC void
replaycache_scrub_if_needed_internal(time_t present, replaycache_t *r)
{
digestmap_iter_t *itr = NULL;
const char *digest;
void *valp;
time_t *access_time;
- char scrub_this;
/* sanity check */
if (!r || !(r->digests_seen)) {
@@ -152,20 +151,10 @@ replaycache_scrub_if_needed_internal(time_t present, replaycache_t *r)
/* okay, scrub time */
itr = digestmap_iter_init(r->digests_seen);
while (!digestmap_iter_done(itr)) {
- scrub_this = 0;
digestmap_iter_get(itr, &digest, &valp);
access_time = (time_t *)valp;
- if (access_time) {
- /* aged out yet? */
- if (*access_time < present - r->horizon) scrub_this = 1;
- } else {
- /* Buh? Get rid of it, anyway */
- log_info(LD_BUG, "replaycache_scrub_if_needed_internal() saw a NULL"
- " entry in the digestmap.");
- scrub_this = 1;
- }
-
- if (scrub_this) {
+ /* aged out yet? */
+ if (*access_time < present - r->horizon) {
/* Advance the iterator and remove this one */
itr = digestmap_iter_next_rmv(r->digests_seen, itr);
/* Free the value removed */
@@ -187,7 +176,7 @@ replaycache_scrub_if_needed_internal(time_t present, replaycache_t *r)
*/
int
-replaycache_add_and_test(replaycache_t *r, const void *data, int len)
+replaycache_add_and_test(replaycache_t *r, const void *data, size_t len)
{
return replaycache_add_and_test_internal(time(NULL), r, data, len, NULL);
}
@@ -198,7 +187,7 @@ replaycache_add_and_test(replaycache_t *r, const void *data, int len)
int
replaycache_add_test_and_elapsed(
- replaycache_t *r, const void *data, int len, time_t *elapsed)
+ replaycache_t *r, const void *data, size_t len, time_t *elapsed)
{
return replaycache_add_and_test_internal(time(NULL), r, data, len, elapsed);
}
diff --git a/src/or/replaycache.h b/src/or/replaycache.h
index de20cab62..cd713fe89 100644
--- a/src/or/replaycache.h
+++ b/src/or/replaycache.h
@@ -45,10 +45,10 @@ replaycache_t * replaycache_new(time_t horizon, time_t interval);
* testing. For everything else, use the wrappers below instead.
*/
-int replaycache_add_and_test_internal(
- time_t present, replaycache_t *r, const void *data, int len,
+STATIC int replaycache_add_and_test_internal(
+ time_t present, replaycache_t *r, const void *data, size_t len,
time_t *elapsed);
-void replaycache_scrub_if_needed_internal(
+STATIC void replaycache_scrub_if_needed_internal(
time_t present, replaycache_t *r);
#endif /* REPLAYCACHE_PRIVATE */
@@ -57,9 +57,9 @@ void replaycache_scrub_if_needed_internal(
* replaycache_t methods
*/
-int replaycache_add_and_test(replaycache_t *r, const void *data, int len);
+int replaycache_add_and_test(replaycache_t *r, const void *data, size_t len);
int replaycache_add_test_and_elapsed(
- replaycache_t *r, const void *data, int len, time_t *elapsed);
+ replaycache_t *r, const void *data, size_t len, time_t *elapsed);
void replaycache_scrub_if_needed(replaycache_t *r);
#endif
diff --git a/src/or/router.c b/src/or/router.c
index eabd9c3f5..959e5e34c 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -232,7 +232,8 @@ get_server_identity_key(void)
return server_identitykey;
}
-/** Return true iff the server identity key has been set. */
+/** Return true iff we are a server and the server identity key
+ * has been set. */
int
server_identity_key_is_set(void)
{
@@ -2249,7 +2250,7 @@ router_guess_address_from_dir_headers(uint32_t *guess)
* string describing the version of Tor and the operating system we're
* currently running on.
*/
-void
+STATIC void
get_platform_str(char *platform, size_t len)
{
tor_snprintf(platform, len, "Tor %s on %s",
diff --git a/src/or/router.h b/src/or/router.h
index 60095d087..1079ec78c 100644
--- a/src/or/router.h
+++ b/src/or/router.h
@@ -12,6 +12,8 @@
#ifndef TOR_ROUTER_H
#define TOR_ROUTER_H
+#include "testsupport.h"
+
crypto_pk_t *get_onion_key(void);
time_t get_onion_key_set_at(void);
void set_server_identity_key(crypto_pk_t *k);
@@ -146,7 +148,7 @@ smartlist_t *router_get_all_orports(const routerinfo_t *ri);
#ifdef ROUTER_PRIVATE
/* Used only by router.c and test.c */
-void get_platform_str(char *platform, size_t len);
+STATIC void get_platform_str(char *platform, size_t len);
#endif
#endif
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index c28de24b6..c0c4d9a4c 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -37,7 +37,7 @@
#include "routerlist.h"
#include "routerparse.h"
#include "routerset.h"
-
+#include "../common/sandbox.h"
// #define DEBUG_ROUTERLIST
/****************************************************************************/
@@ -449,7 +449,7 @@ trusted_dirs_flush_certs_to_disk(void)
} DIGESTMAP_FOREACH_END;
filename = get_datadir_fname("cached-certs");
- if (write_chunks_to_file(filename, chunks, 0)) {
+ if (write_chunks_to_file(filename, chunks, 0, 0)) {
log_warn(LD_FS, "Error writing certificates to disk.");
}
tor_free(filename);
@@ -650,9 +650,6 @@ authority_cert_dl_looks_uncertain(const char *id_digest)
return n_failures >= N_AUTH_CERT_DL_FAILURES_TO_BUG_USER;
}
-/** How many times will we try to fetch a certificate before giving up? */
-#define MAX_CERT_DL_FAILURES 8
-
/** Try to download any v3 authority certificates that we may be missing. If
* <b>status</b> is provided, try to get all the ones that were used to sign
* <b>status</b>. Additionally, try to have a non-expired certificate for
@@ -724,7 +721,7 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
} SMARTLIST_FOREACH_END(cert);
if (!found &&
download_status_is_ready(&(cl->dl_status_by_id), now,
- MAX_CERT_DL_FAILURES) &&
+ get_options()->TestingCertMaxDownloadTries) &&
!digestmap_get(pending_id, ds->v3_identity_digest)) {
log_info(LD_DIR,
"No current certificate known for authority %s "
@@ -786,7 +783,7 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
}
if (download_status_is_ready_by_sk_in_cl(
cl, sig->signing_key_digest,
- now, MAX_CERT_DL_FAILURES) &&
+ now, get_options()->TestingCertMaxDownloadTries) &&
!fp_pair_map_get_by_digests(pending_cert,
voter->identity_digest,
sig->signing_key_digest)) {
@@ -1072,7 +1069,7 @@ router_rebuild_store(int flags, desc_store_t *store)
smartlist_add(chunk_list, c);
} SMARTLIST_FOREACH_END(sd);
- if (write_chunks_to_file(fname_tmp, chunk_list, 1)<0) {
+ if (write_chunks_to_file(fname_tmp, chunk_list, 1, 1)<0) {
log_warn(LD_FS, "Error writing router store to disk.");
goto done;
}
@@ -1147,32 +1144,18 @@ router_rebuild_store(int flags, desc_store_t *store)
static int
router_reload_router_list_impl(desc_store_t *store)
{
- char *fname = NULL, *altname = NULL, *contents = NULL;
+ char *fname = NULL, *contents = NULL;
struct stat st;
- int read_from_old_location = 0;
int extrainfo = (store->type == EXTRAINFO_STORE);
- time_t now = time(NULL);
store->journal_len = store->store_len = 0;
fname = get_datadir_fname(store->fname_base);
- if (store->fname_alt_base)
- altname = get_datadir_fname(store->fname_alt_base);
if (store->mmap) /* get rid of it first */
tor_munmap_file(store->mmap);
store->mmap = NULL;
store->mmap = tor_mmap_file(fname);
- if (!store->mmap && altname && file_status(altname) == FN_FILE) {
- read_from_old_location = 1;
- log_notice(LD_DIR, "Couldn't read %s; trying to load routers from old "
- "location %s.", fname, altname);
- if ((store->mmap = tor_mmap_file(altname)))
- read_from_old_location = 1;
- }
- if (altname && !read_from_old_location) {
- remove_file_if_very_old(altname, now);
- }
if (store->mmap) {
store->store_len = store->mmap->size;
if (extrainfo)
@@ -1189,14 +1172,6 @@ router_reload_router_list_impl(desc_store_t *store)
fname = get_datadir_fname_suffix(store->fname_base, ".new");
if (file_status(fname) == FN_FILE)
contents = read_file_to_str(fname, RFTS_BIN|RFTS_IGNORE_MISSING, &st);
- if (read_from_old_location) {
- tor_free(altname);
- altname = get_datadir_fname_suffix(store->fname_alt_base, ".new");
- if (!contents)
- contents = read_file_to_str(altname, RFTS_BIN|RFTS_IGNORE_MISSING, &st);
- else
- remove_file_if_very_old(altname, now);
- }
if (contents) {
if (extrainfo)
router_load_extrainfo_from_string(contents, NULL,SAVED_IN_JOURNAL,
@@ -1209,9 +1184,8 @@ router_reload_router_list_impl(desc_store_t *store)
}
tor_free(fname);
- tor_free(altname);
- if (store->journal_len || read_from_old_location) {
+ if (store->journal_len) {
/* Always clear the journal on startup.*/
router_rebuild_store(RRS_FORCE, store);
} else if (!extrainfo) {
@@ -1848,7 +1822,7 @@ router_get_advertised_bandwidth_capped(const routerinfo_t *router)
* doubles, convert them to uint64_t, and try to scale them linearly so as to
* much of the range of uint64_t. If <b>total_out</b> is provided, set it to
* the sum of all elements in the array _before_ scaling. */
-/* private */ void
+STATIC void
scale_array_elements_to_u64(u64_dbl_t *entries, int n_entries,
uint64_t *total_out)
{
@@ -1891,7 +1865,7 @@ gt_i64_timei(uint64_t a, uint64_t b)
* value, and return the index of that element. If all elements are 0, choose
* an index at random. Return -1 on error.
*/
-/* private */ int
+STATIC int
choose_array_element_by_weight(const u64_dbl_t *entries, int n_entries)
{
int i, i_chosen=-1, n_chosen=0;
@@ -2591,19 +2565,6 @@ router_is_named(const routerinfo_t *router)
tor_memeq(digest, router->cache_info.identity_digest, DIGEST_LEN));
}
-/** Return true iff the digest of <b>router</b>'s identity key,
- * encoded in hexadecimal, matches <b>hexdigest</b> (which is
- * optionally prefixed with a single dollar sign). Return false if
- * <b>hexdigest</b> is malformed, or it doesn't match. */
-static INLINE int
-router_hex_digest_matches(const routerinfo_t *router, const char *hexdigest)
-{
- return hex_digest_nickname_matches(hexdigest,
- router->cache_info.identity_digest,
- router->nickname,
- router_is_named(router));
-}
-
/** Return true iff <b>digest</b> is the digest of the identity key of a
* trusted directory matching at least one bit of <b>type</b>. If <b>type</b>
* is zero, any authority is okay. */
@@ -2798,7 +2759,6 @@ router_get_routerlist(void)
routerlist->extra_info_map = eimap_new();
routerlist->desc_store.fname_base = "cached-descriptors";
- routerlist->desc_store.fname_alt_base = "cached-routers";
routerlist->extrainfo_store.fname_base = "cached-extrainfo";
routerlist->desc_store.type = ROUTER_STORE;
@@ -4502,12 +4462,8 @@ initiate_descriptor_downloads(const routerstatus_t *source,
* try to split our requests into at least this many requests. */
#define MIN_REQUESTS 3
/** If we want fewer than this many descriptors, wait until we
- * want more, or until MAX_CLIENT_INTERVAL_WITHOUT_REQUEST has
- * passed. */
+ * want more, or until TestingClientMaxIntervalWithoutRequest has passed. */
#define MAX_DL_TO_DELAY 16
-/** When directory clients have only a few servers to request, they batch
- * them until they have more, or until this amount of time has passed. */
-#define MAX_CLIENT_INTERVAL_WITHOUT_REQUEST (10*60)
/** Given a <b>purpose</b> (FETCH_MICRODESC or FETCH_SERVERDESC) and a list of
* router descriptor digests or microdescriptor digest256s in
@@ -4539,7 +4495,7 @@ launch_descriptor_downloads(int purpose,
should_delay = 0;
} else {
should_delay = (last_descriptor_download_attempted +
- MAX_CLIENT_INTERVAL_WITHOUT_REQUEST) > now;
+ options->TestingClientMaxIntervalWithoutRequest) > now;
if (!should_delay && n_downloadable) {
if (last_descriptor_download_attempted) {
log_info(LD_DIR,
@@ -4812,7 +4768,7 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote,
continue; /* We have an in-progress download. */
}
if (!download_status_is_ready(&rs->dl_status, now,
- MAX_ROUTERDESC_DOWNLOAD_FAILURES)) {
+ options->TestingDescriptorMaxDownloadTries)) {
++n_delayed; /* Not ready for retry. */
continue;
}
@@ -4972,7 +4928,7 @@ update_extrainfo_downloads(time_t now)
continue;
}
if (!download_status_is_ready(&sd->ei_dl_status, now,
- MAX_ROUTERDESC_DOWNLOAD_FAILURES)) {
+ options->TestingDescriptorMaxDownloadTries)) {
++n_delay;
continue;
}
diff --git a/src/or/routerlist.h b/src/or/routerlist.h
index ce0f0f2e3..0162297ca 100644
--- a/src/or/routerlist.h
+++ b/src/or/routerlist.h
@@ -11,6 +11,8 @@
#ifndef TOR_ROUTERLIST_H
#define TOR_ROUTERLIST_H
+#include "testsupport.h"
+
int get_n_authorities(dirinfo_type_t type);
int trusted_dirs_reload_certs(void);
@@ -206,9 +208,10 @@ typedef union u64_dbl_t {
double dbl;
} u64_dbl_t;
-int choose_array_element_by_weight(const u64_dbl_t *entries, int n_entries);
-void scale_array_elements_to_u64(u64_dbl_t *entries, int n_entries,
- uint64_t *total_out);
+STATIC int choose_array_element_by_weight(const u64_dbl_t *entries,
+ int n_entries);
+STATIC void scale_array_elements_to_u64(u64_dbl_t *entries, int n_entries,
+ uint64_t *total_out);
#endif
#endif
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 3aa4bdf8a..571167fad 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -386,7 +386,7 @@ static token_rule_t dir_footer_token_table[] = {
T1("dir-key-published",K_DIR_KEY_PUBLISHED, CONCAT_ARGS, NO_OBJ), \
T1("dir-key-expires", K_DIR_KEY_EXPIRES, CONCAT_ARGS, NO_OBJ), \
T1("dir-signing-key", K_DIR_SIGNING_KEY, NO_ARGS, NEED_KEY ),\
- T01("dir-key-crosscert", K_DIR_KEY_CROSSCERT, NO_ARGS, NEED_OBJ ),\
+ T1("dir-key-crosscert", K_DIR_KEY_CROSSCERT, NO_ARGS, NEED_OBJ ),\
T1("dir-key-certification", K_DIR_KEY_CERTIFICATION, \
NO_ARGS, NEED_OBJ), \
T01("dir-address", K_DIR_ADDRESS, GE(1), NO_OBJ),
@@ -1728,7 +1728,6 @@ authority_cert_parse_from_string(const char *s, const char **end_of_string)
log_debug(LD_DIR, "We already checked the signature on this "
"certificate; no need to do so again.");
found = 1;
- cert->is_cross_certified = old_cert->is_cross_certified;
}
}
if (!found) {
@@ -1737,18 +1736,14 @@ authority_cert_parse_from_string(const char *s, const char **end_of_string)
goto err;
}
- if ((tok = find_opt_by_keyword(tokens, K_DIR_KEY_CROSSCERT))) {
- /* XXXX Once all authorities generate cross-certified certificates,
- * make this field mandatory. */
- if (check_signature_token(cert->cache_info.identity_digest,
- DIGEST_LEN,
- tok,
- cert->signing_key,
- CST_NO_CHECK_OBJTYPE,
- "key cross-certification")) {
- goto err;
- }
- cert->is_cross_certified = 1;
+ tok = find_by_keyword(tokens, K_DIR_KEY_CROSSCERT);
+ if (check_signature_token(cert->cache_info.identity_digest,
+ DIGEST_LEN,
+ tok,
+ cert->signing_key,
+ CST_NO_CHECK_OBJTYPE,
+ "key cross-certification")) {
+ goto err;
}
}
diff --git a/src/or/statefile.c b/src/or/statefile.c
index bcb7b0741..8ab04763d 100644
--- a/src/or/statefile.c
+++ b/src/or/statefile.c
@@ -4,6 +4,7 @@
* Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+#define STATEFILE_PRIVATE
#include "or.h"
#include "circuitstats.h"
#include "config.h"
@@ -90,8 +91,11 @@ static config_var_t state_vars_[] = {
#undef VAR
#undef V
-static int or_state_validate(or_state_t *old_options, or_state_t *options,
- int from_setconf, char **msg);
+static int or_state_validate(or_state_t *state, char **msg);
+
+static int or_state_validate_cb(void *old_options, void *options,
+ void *default_options,
+ int from_setconf, char **msg);
/** Magic value for or_state_t. */
#define OR_STATE_MAGIC 0x57A73f57
@@ -109,7 +113,7 @@ static const config_format_t state_format = {
STRUCT_OFFSET(or_state_t, magic_),
state_abbrevs_,
state_vars_,
- (validate_fn_t)or_state_validate,
+ or_state_validate_cb,
&state_extra_var,
};
@@ -117,8 +121,8 @@ static const config_format_t state_format = {
static or_state_t *global_state = NULL;
/** Return the persistent state struct for this Tor. */
-or_state_t *
-get_or_state(void)
+MOCK_IMPL(or_state_t *,
+get_or_state, (void))
{
tor_assert(global_state);
return global_state;
@@ -194,21 +198,27 @@ validate_transports_in_state(or_state_t *state)
return 0;
}
-/** Return 0 if every setting in <b>state</b> is reasonable, and a
- * permissible transition from <b>old_state</b>. Else warn and return -1.
- * Should have no side effects, except for normalizing the contents of
- * <b>state</b>.
- */
-/* XXX from_setconf is here because of bug 238 */
static int
-or_state_validate(or_state_t *old_state, or_state_t *state,
- int from_setconf, char **msg)
+or_state_validate_cb(void *old_state, void *state, void *default_state,
+ int from_setconf, char **msg)
{
/* We don't use these; only options do. Still, we need to match that
* signature. */
(void) from_setconf;
+ (void) default_state;
(void) old_state;
+ return or_state_validate(state, msg);
+}
+
+/** Return 0 if every setting in <b>state</b> is reasonable, and a
+ * permissible transition from <b>old_state</b>. Else warn and return -1.
+ * Should have no side effects, except for normalizing the contents of
+ * <b>state</b>.
+ */
+static int
+or_state_validate(or_state_t *state, char **msg)
+{
if (entry_guards_parse_state(state, 0, msg)<0)
return -1;
@@ -237,7 +247,8 @@ or_state_set(or_state_t *new_state)
tor_free(err);
ret = -1;
}
- if (circuit_build_times_parse_state(&circ_times, global_state) < 0) {
+ if (circuit_build_times_parse_state(
+ get_circuit_build_times_mutable(),global_state) < 0) {
ret = -1;
}
return ret;
@@ -322,7 +333,7 @@ or_state_load(void)
}
}
- if (!badstate && or_state_validate(NULL, new_state, 1, &errmsg) < 0)
+ if (!badstate && or_state_validate(new_state, &errmsg) < 0)
badstate = 1;
if (errmsg) {
@@ -404,7 +415,7 @@ or_state_save(time_t now)
* to avoid redundant writes. */
entry_guards_update_state(global_state);
rep_hist_update_state(global_state);
- circuit_build_times_update_state(&circ_times, global_state);
+ circuit_build_times_update_state(get_circuit_build_times(), global_state);
if (accounting_is_enabled(get_options()))
accounting_run_housekeeping(now);
@@ -449,7 +460,7 @@ or_state_save(time_t now)
/** Return the config line for transport <b>transport</b> in the current state.
* Return NULL if there is no config line for <b>transport</b>. */
-static config_line_t *
+STATIC config_line_t *
get_transport_in_state_by_name(const char *transport)
{
or_state_t *or_state = get_or_state();
diff --git a/src/or/statefile.h b/src/or/statefile.h
index dcdee6c60..c1413ff95 100644
--- a/src/or/statefile.h
+++ b/src/or/statefile.h
@@ -7,7 +7,7 @@
#ifndef TOR_STATEFILE_H
#define TOR_STATEFILE_H
-or_state_t *get_or_state(void);
+MOCK_DECL(or_state_t *,get_or_state,(void));
int did_last_state_file_write_fail(void);
int or_state_save(time_t now);
@@ -18,5 +18,9 @@ int or_state_load(void);
int or_state_loaded(void);
void or_state_free_all(void);
+#ifdef STATEFILE_PRIVATE
+STATIC config_line_t *get_transport_in_state_by_name(const char *transport);
+#endif
+
#endif
diff --git a/src/or/status.c b/src/or/status.c
index d239e6ee7..6e2206e5e 100644
--- a/src/or/status.c
+++ b/src/or/status.c
@@ -15,6 +15,9 @@
#include "circuitlist.h"
#include "main.h"
#include "hibernate.h"
+#include "statefile.h"
+
+static void log_accounting(const time_t now, const or_options_t *options);
/** Return the total number of circuits. */
static int
@@ -23,7 +26,7 @@ count_circuits(void)
circuit_t *circ;
int nr=0;
- for (circ = circuit_get_global_list_(); circ; circ = circ->next)
+ TOR_LIST_FOREACH(circ, circuit_get_global_list(), head)
nr++;
return nr;
@@ -111,6 +114,10 @@ log_heartbeat(time_t now)
uptime, count_circuits(),bw_sent,bw_rcvd,
hibernating?" We are currently hibernating.":"");
+ if (server_mode(options) && accounting_is_enabled(options) && !hibernating) {
+ log_accounting(now, options);
+ }
+
if (stats_n_data_cells_packaged && !hibernating)
log_notice(LD_HEARTBEAT, "Average packaged cell fullness: %2.3f%%",
100*(U64_TO_DBL(stats_n_data_bytes_packaged) /
@@ -128,3 +135,27 @@ log_heartbeat(time_t now)
return 0;
}
+static void
+log_accounting(const time_t now, const or_options_t *options)
+{
+ or_state_t *state = get_or_state();
+ char *acc_rcvd = bytes_to_usage(state->AccountingBytesReadInInterval);
+ char *acc_sent = bytes_to_usage(state->AccountingBytesWrittenInInterval);
+ char *acc_max = bytes_to_usage(options->AccountingMax);
+ time_t interval_end = accounting_get_end_time();
+ char end_buf[ISO_TIME_LEN + 1];
+ char *remaining = NULL;
+ format_local_iso_time(end_buf, interval_end);
+ remaining = secs_to_uptime(interval_end - now);
+
+ log_notice(LD_HEARTBEAT, "Heartbeat: Accounting enabled. "
+ "Sent: %s / %s, Received: %s / %s. The "
+ "current accounting interval ends on %s, in %s.",
+ acc_sent, acc_max, acc_rcvd, acc_max, end_buf, remaining);
+
+ tor_free(acc_rcvd);
+ tor_free(acc_sent);
+ tor_free(acc_max);
+ tor_free(remaining);
+}
+
diff --git a/src/or/transports.c b/src/or/transports.c
index 3749d6bb2..8b4a11882 100644
--- a/src/or/transports.c
+++ b/src/or/transports.c
@@ -95,18 +95,17 @@
#include "util.h"
#include "router.h"
#include "statefile.h"
+#include "entrynodes.h"
+#include "connection_or.h"
+#include "ext_orport.h"
+#include "control.h"
static process_environment_t *
create_managed_proxy_environment(const managed_proxy_t *mp);
static INLINE int proxy_configuration_finished(const managed_proxy_t *mp);
-static void managed_proxy_destroy(managed_proxy_t *mp,
- int also_terminate_process);
-
static void handle_finished_proxy(managed_proxy_t *mp);
-static int configure_proxy(managed_proxy_t *mp);
-
static void parse_method_error(const char *line, int is_server_method);
#define parse_server_method_error(l) parse_method_error(l, 1)
#define parse_client_method_error(l) parse_method_error(l, 0)
@@ -136,7 +135,8 @@ static smartlist_t *transport_list = NULL;
SOCKS version <b>socks_ver</b>. */
static transport_t *
transport_new(const tor_addr_t *addr, uint16_t port,
- const char *name, int socks_ver)
+ const char *name, int socks_ver,
+ const char *extra_info_args)
{
transport_t *t = tor_malloc_zero(sizeof(transport_t));
@@ -144,6 +144,8 @@ transport_new(const tor_addr_t *addr, uint16_t port,
t->port = port;
t->name = tor_strdup(name);
t->socks_version = socks_ver;
+ if (extra_info_args)
+ t->extra_info_args = tor_strdup(extra_info_args);
return t;
}
@@ -156,6 +158,7 @@ transport_free(transport_t *transport)
return;
tor_free(transport->name);
+ tor_free(transport->extra_info_args);
tor_free(transport);
}
@@ -323,7 +326,7 @@ int
transport_add_from_config(const tor_addr_t *addr, uint16_t port,
const char *name, int socks_ver)
{
- transport_t *t = transport_new(addr, port, name, socks_ver);
+ transport_t *t = transport_new(addr, port, name, socks_ver, NULL);
int r = transport_add(t);
@@ -549,7 +552,7 @@ pt_configure_remaining_proxies(void)
assert_unconfigured_count_ok();
SMARTLIST_FOREACH_BEGIN(tmp, managed_proxy_t *, mp) {
- tor_assert(mp->conf_state != PT_PROTO_BROKEN ||
+ tor_assert(mp->conf_state != PT_PROTO_BROKEN &&
mp->conf_state != PT_PROTO_FAILED_LAUNCH);
if (mp->got_hup) {
@@ -589,7 +592,7 @@ pt_configure_remaining_proxies(void)
* Return 1 if the transport configuration finished, and return 0
* otherwise (if we still have more configuring to do for this
* proxy). */
-static int
+STATIC int
configure_proxy(managed_proxy_t *mp)
{
int configuration_finished = 0;
@@ -657,6 +660,7 @@ register_server_proxy(const managed_proxy_t *mp)
save_transport_to_state(t->name, &t->addr, t->port);
log_notice(LD_GENERAL, "Registered server transport '%s' at '%s'",
t->name, fmt_addrport(&t->addr, t->port));
+ control_event_transport_launched("server", t->name, &t->addr, t->port);
} SMARTLIST_FOREACH_END(t);
}
@@ -679,9 +683,11 @@ register_client_proxy(const managed_proxy_t *mp)
break;
case 0:
log_info(LD_GENERAL, "Successfully registered transport %s", t->name);
+ control_event_transport_launched("client", t->name, &t->addr, t->port);
break;
case 1:
log_info(LD_GENERAL, "Successfully registered transport %s", t->name);
+ control_event_transport_launched("client", t->name, &t->addr, t->port);
transport_free(transport_tmp);
break;
}
@@ -699,7 +705,7 @@ register_proxy(const managed_proxy_t *mp)
}
/** Free memory allocated by managed proxy <b>mp</b>. */
-static void
+STATIC void
managed_proxy_destroy(managed_proxy_t *mp,
int also_terminate_process)
{
@@ -713,7 +719,8 @@ managed_proxy_destroy(managed_proxy_t *mp,
smartlist_free(mp->transports_to_launch);
/* remove it from the list of managed proxies */
- smartlist_remove(managed_proxy_list, mp);
+ if (managed_proxy_list)
+ smartlist_remove(managed_proxy_list, mp);
/* free the argv */
free_execve_args(mp->argv);
@@ -750,7 +757,6 @@ handle_finished_proxy(managed_proxy_t *mp)
}
unconfigured_proxies_n--;
- tor_assert(unconfigured_proxies_n >= 0);
}
/** Return true if the configuration of the managed proxy <b>mp</b> is
@@ -781,7 +787,7 @@ handle_methods_done(const managed_proxy_t *mp)
/** Handle a configuration protocol <b>line</b> received from a
* managed proxy <b>mp</b>. */
-void
+STATIC void
handle_proxy_line(const char *line, managed_proxy_t *mp)
{
log_info(LD_GENERAL, "Got a line from managed proxy '%s': (%s)",
@@ -882,7 +888,7 @@ handle_proxy_line(const char *line, managed_proxy_t *mp)
}
/** Parses an ENV-ERROR <b>line</b> and warns the user accordingly. */
-void
+STATIC void
parse_env_error(const char *line)
{
/* (Length of the protocol string) plus (a space) and (the first char of
@@ -898,7 +904,7 @@ parse_env_error(const char *line)
/** Handles a VERSION <b>line</b>. Updates the configuration protocol
* version in <b>mp</b>. */
-int
+STATIC int
parse_version(const char *line, managed_proxy_t *mp)
{
if (strlen(line) < (strlen(PROTO_NEG_SUCCESS) + 2)) {
@@ -939,14 +945,14 @@ parse_method_error(const char *line, int is_server)
/** Parses an SMETHOD <b>line</b> and if well-formed it registers the
* new transport in <b>mp</b>. */
-int
+STATIC int
parse_smethod_line(const char *line, managed_proxy_t *mp)
{
int r;
smartlist_t *items = NULL;
char *method_name=NULL;
-
+ char *args_string=NULL;
char *addrport=NULL;
tor_addr_t tor_addr;
char *address=NULL;
@@ -963,6 +969,9 @@ parse_smethod_line(const char *line, managed_proxy_t *mp)
goto err;
}
+ /* Example of legit SMETHOD line:
+ SMETHOD obfs2 0.0.0.0:25612 ARGS:secret=supersekrit,key=superkey */
+
tor_assert(!strcmp(smartlist_get(items,0),PROTO_SMETHOD));
method_name = smartlist_get(items,1);
@@ -990,7 +999,19 @@ parse_smethod_line(const char *line, managed_proxy_t *mp)
goto err;
}
- transport = transport_new(&tor_addr, port, method_name, PROXY_NONE);
+ if (smartlist_len(items) > 3) {
+ /* Seems like there are also some [options] in the SMETHOD line.
+ Let's see if we can parse them. */
+ char *options_string = smartlist_get(items, 3);
+ log_debug(LD_CONFIG, "Got options_string: %s", options_string);
+ if (!strcmpstart(options_string, "ARGS:")) {
+ args_string = options_string+strlen("ARGS:");
+ log_debug(LD_CONFIG, "Got ARGS: %s", args_string);
+ }
+ }
+
+ transport = transport_new(&tor_addr, port, method_name,
+ PROXY_NONE, args_string);
if (!transport)
goto err;
@@ -1016,7 +1037,7 @@ parse_smethod_line(const char *line, managed_proxy_t *mp)
/** Parses a CMETHOD <b>line</b>, and if well-formed it registers
* the new transport in <b>mp</b>. */
-int
+STATIC int
parse_cmethod_line(const char *line, managed_proxy_t *mp)
{
int r;
@@ -1082,7 +1103,7 @@ parse_cmethod_line(const char *line, managed_proxy_t *mp)
goto err;
}
- transport = transport_new(&tor_addr, port, method_name, socks_ver);
+ transport = transport_new(&tor_addr, port, method_name, socks_ver, NULL);
if (!transport)
goto err;
@@ -1105,6 +1126,50 @@ parse_cmethod_line(const char *line, managed_proxy_t *mp)
return r;
}
+/** Return a newly allocated string that tor should place in
+ * TOR_PT_SERVER_TRANSPORT_OPTIONS while configuring the server
+ * manged proxy in <b>mp</b>. Return NULL if no such options are found. */
+STATIC char *
+get_transport_options_for_server_proxy(const managed_proxy_t *mp)
+{
+ char *options_string = NULL;
+ smartlist_t *string_sl = smartlist_new();
+
+ tor_assert(mp->is_server);
+
+ /** Loop over the transports of the proxy. If we have options for
+ any of them, format them appropriately and place them in our
+ smartlist. Finally, join our smartlist to get the final
+ string. */
+ SMARTLIST_FOREACH_BEGIN(mp->transports_to_launch, const char *, transport) {
+ smartlist_t *options_tmp_sl = NULL;
+ options_tmp_sl = get_options_for_server_transport(transport);
+ if (!options_tmp_sl)
+ continue;
+
+ /** Loop over the options of this transport, escape them, and
+ place them in the smartlist. */
+ SMARTLIST_FOREACH_BEGIN(options_tmp_sl, const char *, options) {
+ char *escaped_opts = tor_escape_str_for_pt_args(options, ":;\\");
+ smartlist_add_asprintf(string_sl, "%s:%s",
+ transport, escaped_opts);
+ tor_free(escaped_opts);
+ } SMARTLIST_FOREACH_END(options);
+
+ SMARTLIST_FOREACH(options_tmp_sl, char *, c, tor_free(c));
+ smartlist_free(options_tmp_sl);
+ } SMARTLIST_FOREACH_END(transport);
+
+ if (smartlist_len(string_sl)) {
+ options_string = smartlist_join_strings(string_sl, ";", 0, NULL);
+ }
+
+ SMARTLIST_FOREACH(string_sl, char *, t, tor_free(t));
+ smartlist_free(string_sl);
+
+ return options_string;
+}
+
/** Return the string that tor should place in TOR_PT_SERVER_BINDADDR
* while configuring the server managed proxy in <b>mp</b>. The
* string is stored in the heap, and it's the the responsibility of
@@ -1139,6 +1204,8 @@ get_bindaddr_for_server_proxy(const managed_proxy_t *mp)
static process_environment_t *
create_managed_proxy_environment(const managed_proxy_t *mp)
{
+ const or_options_t *options = get_options();
+
/* Environment variables to be added to or set in mp's environment. */
smartlist_t *envs = smartlist_new();
/* XXXX The next time someone touches this code, shorten the name of
@@ -1186,13 +1253,39 @@ create_managed_proxy_environment(const managed_proxy_t *mp)
tor_free(bindaddr_tmp);
}
+ {
+ char *server_transport_options =
+ get_transport_options_for_server_proxy(mp);
+ if (server_transport_options) {
+ smartlist_add_asprintf(envs, "TOR_PT_SERVER_TRANSPORT_OPTIONS=%s",
+ server_transport_options);
+ tor_free(server_transport_options);
+ }
+ }
+
/* XXX024 Remove the '=' here once versions of obfsproxy which
* assert that this env var exists are sufficiently dead.
*
* (If we remove this line entirely, some joker will stick this
* variable in Tor's environment and crash PTs that try to parse
* it even when not run in server mode.) */
- smartlist_add(envs, tor_strdup("TOR_PT_EXTENDED_SERVER_PORT="));
+
+ if (options->ExtORPort_lines) {
+ char *ext_or_addrport_tmp =
+ get_first_listener_addrport_string(CONN_TYPE_EXT_OR_LISTENER);
+ char *cookie_file_loc = get_ext_or_auth_cookie_file_name();
+
+ smartlist_add_asprintf(envs, "TOR_PT_EXTENDED_SERVER_PORT=%s",
+ ext_or_addrport_tmp);
+ smartlist_add_asprintf(envs, "TOR_PT_AUTH_COOKIE_FILE=%s",
+ cookie_file_loc);
+
+ tor_free(ext_or_addrport_tmp);
+ tor_free(cookie_file_loc);
+
+ } else {
+ smartlist_add_asprintf(envs, "TOR_PT_EXTENDED_SERVER_PORT=");
+ }
}
SMARTLIST_FOREACH_BEGIN(envs, const char *, env_var) {
@@ -1216,7 +1309,7 @@ create_managed_proxy_environment(const managed_proxy_t *mp)
* <b>proxy_argv</b>.
*
* Requires that proxy_argv have at least one element. */
-static managed_proxy_t *
+STATIC managed_proxy_t *
managed_proxy_create(const smartlist_t *transport_list,
char **proxy_argv, int is_server)
{
@@ -1390,6 +1483,8 @@ pt_get_extra_info_descriptor_string(void)
tor_assert(mp->transports);
SMARTLIST_FOREACH_BEGIN(mp->transports, const transport_t *, t) {
+ char *transport_args = NULL;
+
/* If the transport proxy returned "0.0.0.0" as its address, and
* we know our external IP address, use it. Otherwise, use the
* returned address. */
@@ -1405,9 +1500,16 @@ pt_get_extra_info_descriptor_string(void)
addrport = fmt_addrport(&t->addr, t->port);
}
+ /* If this transport has any arguments with it, prepend a space
+ to them so that we can add them to the transport line. */
+ if (t->extra_info_args)
+ tor_asprintf(&transport_args, " %s", t->extra_info_args);
+
smartlist_add_asprintf(string_chunks,
- "transport %s %s",
- t->name, addrport);
+ "transport %s %s%s",
+ t->name, addrport,
+ transport_args ? transport_args : "");
+ tor_free(transport_args);
} SMARTLIST_FOREACH_END(t);
} SMARTLIST_FOREACH_END(mp);
@@ -1426,6 +1528,57 @@ pt_get_extra_info_descriptor_string(void)
return the_string;
}
+/** Stringify the SOCKS arguments in <b>socks_args</b> according to
+ * 180_pluggable_transport.txt. The string is allocated on the heap
+ * and it's the responsibility of the caller to free it after use. */
+char *
+pt_stringify_socks_args(const smartlist_t *socks_args)
+{
+ /* tmp place to store escaped socks arguments, so that we can
+ concatenate them up afterwards */
+ smartlist_t *sl_tmp = NULL;
+ char *escaped_string = NULL;
+ char *new_string = NULL;
+
+ tor_assert(socks_args);
+ tor_assert(smartlist_len(socks_args) > 0);
+
+ sl_tmp = smartlist_new();
+
+ SMARTLIST_FOREACH_BEGIN(socks_args, const char *, s) {
+ /* Escape ';' and '\'. */
+ escaped_string = tor_escape_str_for_pt_args(s, ";\\");
+ if (!escaped_string)
+ goto done;
+
+ smartlist_add(sl_tmp, escaped_string);
+ } SMARTLIST_FOREACH_END(s);
+
+ new_string = smartlist_join_strings(sl_tmp, ";", 0, NULL);
+
+ done:
+ SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s));
+ smartlist_free(sl_tmp);
+
+ return new_string;
+}
+
+/** Return a string of the SOCKS arguments that we should pass to the
+ * pluggable transports proxy in <b>addr</b>:<b>port</b> according to
+ * 180_pluggable_transport.txt. The string is allocated on the heap
+ * and it's the responsibility of the caller to free it after use. */
+char *
+pt_get_socks_args_for_proxy_addrport(const tor_addr_t *addr, uint16_t port)
+{
+ const smartlist_t *socks_args = NULL;
+
+ socks_args = get_socks_args_by_bridge_addrport(addr, port);
+ if (!socks_args)
+ return NULL;
+
+ return pt_stringify_socks_args(socks_args);
+}
+
/** The tor config was read.
* Destroy all managed proxies that were marked by a previous call to
* prepare_proxy_list_for_config_read() and are not used by the new
diff --git a/src/or/transports.h b/src/or/transports.h
index 6ee82f455..7b524f207 100644
--- a/src/or/transports.h
+++ b/src/or/transports.h
@@ -25,6 +25,9 @@ typedef struct transport_t {
/** Boolean: We are re-parsing our transport list, and we are going to remove
* this one if we don't find it in the list of configured transports. */
unsigned marked_for_removal : 1;
+ /** Arguments for this transport that must be written to the
+ extra-info descriptor. */
+ char *extra_info_args;
} transport_t;
void mark_transport_list(void);
@@ -55,6 +58,10 @@ void pt_prepare_proxy_list_for_config_read(void);
void sweep_proxy_list(void);
smartlist_t *get_transport_proxy_ports(void);
+char *pt_stringify_socks_args(const smartlist_t *socks_args);
+
+char *pt_get_socks_args_for_proxy_addrport(const tor_addr_t *addr,
+ uint16_t port);
#ifdef PT_PRIVATE
/** State of the managed proxy configuration protocol. */
@@ -100,12 +107,21 @@ typedef struct {
smartlist_t *transports;
} managed_proxy_t;
-int parse_cmethod_line(const char *line, managed_proxy_t *mp);
-int parse_smethod_line(const char *line, managed_proxy_t *mp);
+STATIC int parse_cmethod_line(const char *line, managed_proxy_t *mp);
+STATIC int parse_smethod_line(const char *line, managed_proxy_t *mp);
+
+STATIC int parse_version(const char *line, managed_proxy_t *mp);
+STATIC void parse_env_error(const char *line);
+STATIC void handle_proxy_line(const char *line, managed_proxy_t *mp);
+STATIC char *get_transport_options_for_server_proxy(const managed_proxy_t *mp);
+
+STATIC void managed_proxy_destroy(managed_proxy_t *mp,
+ int also_terminate_process);
+
+STATIC managed_proxy_t *managed_proxy_create(const smartlist_t *transport_list,
+ char **proxy_argv, int is_server);
-int parse_version(const char *line, managed_proxy_t *mp);
-void parse_env_error(const char *line);
-void handle_proxy_line(const char *line, managed_proxy_t *mp);
+STATIC int configure_proxy(managed_proxy_t *mp);
#endif
diff --git a/src/test/Makefile.nmake b/src/test/Makefile.nmake
index 562c8df8b..6479f9d39 100644
--- a/src/test/Makefile.nmake
+++ b/src/test/Makefile.nmake
@@ -12,9 +12,9 @@ LIBS = ..\..\..\build-alpha\lib\libevent.lib \
crypt32.lib gdi32.lib user32.lib
TEST_OBJECTS = test.obj test_addr.obj test_containers.obj \
- test_crypto.obj test_data.obj test_dir.obj test_microdesc.obj \
- test_pt.obj test_util.obj test_config.obj test_cell_formats.obj \
- test_replay.obj test_introduce.obj tinytest.obj
+ test_controller_events.ogj test_crypto.obj test_data.obj test_dir.obj \
+ test_microdesc.obj test_pt.obj test_util.obj test_config.obj \
+ test_cell_formats.obj test_replay.obj test_introduce.obj tinytest.obj
tinytest.obj: ..\ext\tinytest.c
$(CC) $(CFLAGS) /D snprintf=_snprintf /c ..\ext\tinytest.c
diff --git a/src/test/bench.c b/src/test/bench.c
index 5a8d21d17..e650569a7 100644
--- a/src/test/bench.c
+++ b/src/test/bench.c
@@ -14,9 +14,6 @@ const char tor_git_revision[] = "";
#include "orconfig.h"
-#define RELAY_PRIVATE
-#define CONFIG_PRIVATE
-
#include "or.h"
#include "onion_tap.h"
#include "relay.h"
@@ -204,6 +201,7 @@ bench_onion_ntor(void)
for (i = 0; i < iters; ++i) {
onion_skin_ntor_create(nodeid, &keypair1.pubkey, &state, os);
ntor_handshake_state_free(state);
+ state = NULL;
}
end = perftime();
printf("Client-side, part 1: %f usec.\n", NANOCOUNT(start, end, iters)/1e3);
diff --git a/src/test/bt_test.py b/src/test/bt_test.py
new file mode 100755
index 000000000..2de9924a5
--- /dev/null
+++ b/src/test/bt_test.py
@@ -0,0 +1,42 @@
+# Copyright 2013, The Tor Project, Inc
+# See LICENSE for licensing information
+
+"""
+bt_test.py
+
+This file tests the output from test-bt-cl to make sure it's as expected.
+
+Example usage:
+
+$ ./src/test/test-bt-cl crash | ./src/test/bt_test.py
+OK
+$ ./src/test/test-bt-cl assert | ./src/test/bt_test.py
+OK
+
+"""
+
+import sys
+
+
+def matches(lines, funcs):
+ if len(lines) < len(funcs):
+ return False
+ try:
+ for l, f in zip(lines, funcs):
+ l.index(f)
+ except ValueError:
+ return False
+ else:
+ return True
+
+FUNCNAMES = "crash oh_what a_tangled_web we_weave main".split()
+
+LINES = sys.stdin.readlines()
+
+for I in range(len(LINES)):
+ if matches(LINES[I:], FUNCNAMES):
+ print "OK"
+ break
+else:
+ print "BAD"
+
diff --git a/src/test/include.am b/src/test/include.am
index 112d1a79d..c16dd14fe 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -1,11 +1,15 @@
-TESTS+= src/test/test
+TESTS += src/test/test
-noinst_PROGRAMS+= src/test/test src/test/test-child src/test/bench
+noinst_PROGRAMS+= src/test/bench
+if UNITTESTS_ENABLED
+noinst_PROGRAMS+= src/test/test src/test/test-child
+endif
src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
-DLOCALSTATEDIR="\"$(localstatedir)\"" \
-DBINDIR="\"$(bindir)\"" \
- -I"$(top_srcdir)/src/or" -I"$(top_srcdir)/src/ext"
+ -I"$(top_srcdir)/src/or" -I"$(top_srcdir)/src/ext" \
+ -DTOR_UNIT_TESTS
# -L flags need to go in LDFLAGS. -l flags need to go in LDADD.
# This seems to matter nowhere but on Windows, but I assure you that it
@@ -14,31 +18,40 @@ src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
src_test_test_SOURCES = \
src/test/test.c \
src/test/test_addr.c \
+ src/test/test_buffers.c \
src/test/test_cell_formats.c \
+ src/test/test_circuitlist.c \
+ src/test/test_circuitmux.c \
src/test/test_containers.c \
+ src/test/test_controller_events.c \
src/test/test_crypto.c \
+ src/test/test_cell_queue.c \
src/test/test_data.c \
src/test/test_dir.c \
+ src/test/test_extorport.c \
src/test/test_introduce.c \
+ src/test/test_logging.c \
src/test/test_microdesc.c \
+ src/test/test_options.c \
src/test/test_pt.c \
src/test/test_replay.c \
+ src/test/test_socks.c \
src/test/test_util.c \
src/test/test_config.c \
src/ext/tinytest.c
+src_test_test_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+
src_test_test_CPPFLAGS= $(src_test_AM_CPPFLAGS)
src_test_bench_SOURCES = \
src/test/bench.c
-src_test_bench_CPPFLAGS= $(src_test_AM_CPPFLAGS)
-
src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
@TOR_LDFLAGS_libevent@
-src_test_test_LDADD = src/or/libtor.a src/common/libor.a \
- src/common/libor-crypto.a $(LIBDONNA) \
- src/common/libor-event.a \
+src_test_test_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \
+ src/common/libor-crypto-testing.a $(LIBDONNA) \
+ src/common/libor-event-testing.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
@@ -63,6 +76,33 @@ src_test_test_ntor_cl_LDADD = src/or/libtor.a src/common/libor.a \
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
src_test_test_ntor_cl_AM_CPPFLAGS = \
-I"$(top_srcdir)/src/or"
+NTOR_TEST_DEPS=src/test/test-ntor-cl
+else
+NTOR_TEST_DEPS=
+endif
+if COVERAGE_ENABLED
+CMDLINE_TEST_TOR = ./src/or/tor-cov
+else
+CMDLINE_TEST_TOR = ./src/or/tor
endif
+noinst_PROGRAMS += src/test/test-bt-cl
+src_test_test_bt_cl_SOURCES = src/test/test_bt_cl.c
+src_test_test_bt_cl_LDADD = src/common/libor-testing.a \
+ @TOR_LIB_MATH@ \
+ @TOR_LIB_WS32@ @TOR_LIB_GDI@
+src_test_test_bt_cl_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+src_test_test_bt_cl_CPPFLAGS= $(src_test_AM_CPPFLAGS)
+
+
+check-local: $(NTOR_TEST_DEPS) $(CMDLINE_TEST_TOR)
+if USEPYTHON
+ $(PYTHON) $(top_srcdir)/src/test/test_cmdline_args.py $(CMDLINE_TEST_TOR) "${top_srcdir}"
+if CURVE25519_ENABLED
+ $(PYTHON) $(top_srcdir)/src/test/ntor_ref.py test-tor
+ $(PYTHON) $(top_srcdir)/src/test/ntor_ref.py self-test
+endif
+ ./src/test/test-bt-cl assert | $(PYTHON) $(top_srcdir)/src/test/bt_test.py
+ ./src/test/test-bt-cl crash | $(PYTHON) $(top_srcdir)/src/test/bt_test.py
+endif
diff --git a/src/test/ntor_ref.py b/src/test/ntor_ref.py
index ade468da7..2a5f98460 100644..100755
--- a/src/test/ntor_ref.py
+++ b/src/test/ntor_ref.py
@@ -1,3 +1,4 @@
+#!/usr/bin/python
# Copyright 2012-2013, The Tor Project, Inc
# See LICENSE for licensing information
@@ -27,7 +28,13 @@ commands:
"""
import binascii
-import curve25519
+try:
+ import curve25519
+except ImportError:
+ curve25519 = None
+ print "SKIPPING: No Python curve25519 module installed"
+ import sys
+ sys.exit(0)
import hashlib
import hmac
import subprocess
@@ -286,6 +293,7 @@ def demo(node_id="iToldYouAboutStairs.", server_key=PrivateKey()):
assert len(skeys) == 72
assert len(ckeys) == 72
assert skeys == ckeys
+ print "OK"
# ======================================================================
def timing():
@@ -368,13 +376,15 @@ def test_tor():
assert c_keys == s_keys
assert len(c_keys) == 90
- print "We just interoperated."
+ print "OK"
# ======================================================================
if __name__ == '__main__':
import sys
- if sys.argv[1] == 'gen_kdf_vectors':
+ if len(sys.argv) < 2:
+ print __doc__
+ elif sys.argv[1] == 'gen_kdf_vectors':
kdf_vectors()
elif sys.argv[1] == 'timing':
timing()
diff --git a/src/test/test-network.sh b/src/test/test-network.sh
new file mode 100755
index 000000000..7b5986416
--- /dev/null
+++ b/src/test/test-network.sh
@@ -0,0 +1,47 @@
+#! /bin/sh
+
+until [ -z $1 ]
+do
+ case $1 in
+ --chutney-path)
+ export CHUTNEY_PATH="$2"
+ shift
+ ;;
+ --tor-path)
+ export TOR_DIR="$2"
+ shift
+ ;;
+ --flavo?r|--network-flavo?r)
+ export NETWORK_FLAVOUR="$2"
+ shift
+ ;;
+ *)
+ echo "Sorry, I don't know what to do with '$1'."
+ exit 2
+ ;;
+ esac
+ shift
+done
+
+TOR_DIR="${TOR_DIR:-$PWD}"
+NETWORK_FLAVOUR=${NETWORK_FLAVOUR:-basic}
+CHUTNEY_NETWORK=networks/$NETWORK_FLAVOUR
+myname=$(basename $0)
+
+[ -d "$CHUTNEY_PATH" ] && [ -x "$CHUTNEY_PATH/chutney" ] || {
+ echo "$myname: missing 'chutney' in CHUTNEY_PATH ($CHUTNEY_PATH)"
+ exit 1
+}
+cd "$CHUTNEY_PATH"
+# For picking up the right tor binaries.
+PATH="$TOR_DIR/src/or:$TOR_DIR/src/tools:$PATH"
+./tools/bootstrap-network.sh $NETWORK_FLAVOUR || exit 2
+
+# Sleep some, waiting for the network to bootstrap.
+# TODO: Add chutney command 'bootstrap-status' and use that instead.
+BOOTSTRAP_TIME=18
+echo -n "$myname: sleeping for $BOOTSTRAP_TIME seconds"
+n=$BOOTSTRAP_TIME; while [ $n -gt 0 ]; do
+ sleep 1; n=$(expr $n - 1); echo -n .
+done; echo ""
+./chutney verify $CHUTNEY_NETWORK
diff --git a/src/test/test.c b/src/test/test.c
index 4ec879234..6ea41b140 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -28,11 +28,10 @@ const char tor_git_revision[] = "";
/* These macros pull in declarations for some functions and structures that
* are typically file-private. */
-#define BUFFERS_PRIVATE
-#define CONFIG_PRIVATE
#define GEOIP_PRIVATE
#define ROUTER_PRIVATE
#define CIRCUITSTATS_PRIVATE
+#define CIRCUITLIST_PRIVATE
/*
* Linux doesn't provide lround in math.h by default, but mac os does...
@@ -55,6 +54,7 @@ double fabs(double x);
#include "mempool.h"
#include "memarea.h"
#include "onion.h"
+#include "onion_ntor.h"
#include "onion_tap.h"
#include "policies.h"
#include "rephist.h"
@@ -218,667 +218,138 @@ free_pregenerated_keys(void)
}
}
-typedef struct socks_test_data_t {
- socks_request_t *req;
- buf_t *buf;
-} socks_test_data_t;
-
-static void *
-socks_test_setup(const struct testcase_t *testcase)
-{
- socks_test_data_t *data = tor_malloc(sizeof(socks_test_data_t));
- (void)testcase;
- data->buf = buf_new_with_capacity(256);
- data->req = socks_request_new();
- config_register_addressmaps(get_options());
- return data;
-}
-static int
-socks_test_cleanup(const struct testcase_t *testcase, void *ptr)
-{
- socks_test_data_t *data = ptr;
- (void)testcase;
- buf_free(data->buf);
- socks_request_free(data->req);
- tor_free(data);
- return 1;
-}
-
-const struct testcase_setup_t socks_setup = {
- socks_test_setup, socks_test_cleanup
-};
-
-#define SOCKS_TEST_INIT() \
- socks_test_data_t *testdata = ptr; \
- buf_t *buf = testdata->buf; \
- socks_request_t *socks = testdata->req;
-#define ADD_DATA(buf, s) \
- write_to_buf(s, sizeof(s)-1, buf)
-
-static void
-socks_request_clear(socks_request_t *socks)
-{
- tor_free(socks->username);
- tor_free(socks->password);
- memset(socks, 0, sizeof(socks_request_t));
-}
-
-/** Perform unsupported SOCKS 4 commands */
-static void
-test_socks_4_unsupported_commands(void *ptr)
-{
- SOCKS_TEST_INIT();
-
- /* SOCKS 4 Send BIND [02] to IP address 2.2.2.2:4369 */
- ADD_DATA(buf, "\x04\x02\x11\x11\x02\x02\x02\x02\x00");
- test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
- get_options()->SafeSocks) == -1);
- test_eq(4, socks->socks_version);
- test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
-
- done:
- ;
-}
-
-/** Perform supported SOCKS 4 commands */
-static void
-test_socks_4_supported_commands(void *ptr)
-{
- SOCKS_TEST_INIT();
-
- test_eq(0, buf_datalen(buf));
-
- /* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4370 */
- ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x03\x00");
- test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
- get_options()->SafeSocks) == 1);
- test_eq(4, socks->socks_version);
- test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
- test_eq(SOCKS_COMMAND_CONNECT, socks->command);
- test_streq("2.2.2.3", socks->address);
- test_eq(4370, socks->port);
- test_assert(socks->got_auth == 0);
- test_assert(! socks->username);
-
- test_eq(0, buf_datalen(buf));
- socks_request_clear(socks);
-
- /* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4369 with userid*/
- ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x04me\x00");
- test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
- get_options()->SafeSocks) == 1);
- test_eq(4, socks->socks_version);
- test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
- test_eq(SOCKS_COMMAND_CONNECT, socks->command);
- test_streq("2.2.2.4", socks->address);
- test_eq(4370, socks->port);
- test_assert(socks->got_auth == 1);
- test_assert(socks->username);
- test_eq(2, socks->usernamelen);
- test_memeq("me", socks->username, 2);
-
- test_eq(0, buf_datalen(buf));
- socks_request_clear(socks);
-
- /* SOCKS 4a Send RESOLVE [F0] request for torproject.org */
- ADD_DATA(buf, "\x04\xF0\x01\x01\x00\x00\x00\x02me\x00torproject.org\x00");
- test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
- get_options()->SafeSocks) == 1);
- test_eq(4, socks->socks_version);
- test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
- test_streq("torproject.org", socks->address);
-
- test_eq(0, buf_datalen(buf));
-
- done:
- ;
-}
-
-/** Perform unsupported SOCKS 5 commands */
-static void
-test_socks_5_unsupported_commands(void *ptr)
-{
- SOCKS_TEST_INIT();
-
- /* SOCKS 5 Send unsupported BIND [02] command */
- ADD_DATA(buf, "\x05\x02\x00\x01");
-
- test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
- get_options()->SafeSocks), 0);
- test_eq(0, buf_datalen(buf));
- test_eq(5, socks->socks_version);
- test_eq(2, socks->replylen);
- test_eq(5, socks->reply[0]);
- test_eq(0, socks->reply[1]);
- ADD_DATA(buf, "\x05\x02\x00\x01\x02\x02\x02\x01\x01\x01");
- test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
- get_options()->SafeSocks), -1);
- /* XXX: shouldn't tor reply 'command not supported' [07]? */
-
- buf_clear(buf);
- socks_request_clear(socks);
-
- /* SOCKS 5 Send unsupported UDP_ASSOCIATE [03] command */
- ADD_DATA(buf, "\x05\x03\x00\x01\x02");
- test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
- get_options()->SafeSocks), 0);
- test_eq(5, socks->socks_version);
- test_eq(2, socks->replylen);
- test_eq(5, socks->reply[0]);
- test_eq(2, socks->reply[1]);
- ADD_DATA(buf, "\x05\x03\x00\x01\x02\x02\x02\x01\x01\x01");
- test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
- get_options()->SafeSocks), -1);
- /* XXX: shouldn't tor reply 'command not supported' [07]? */
-
- done:
- ;
-}
-
-/** Perform supported SOCKS 5 commands */
-static void
-test_socks_5_supported_commands(void *ptr)
-{
- SOCKS_TEST_INIT();
-
- /* SOCKS 5 Send CONNECT [01] to IP address 2.2.2.2:4369 */
- ADD_DATA(buf, "\x05\x01\x00");
- test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
- get_options()->SafeSocks), 0);
- test_eq(5, socks->socks_version);
- test_eq(2, socks->replylen);
- test_eq(5, socks->reply[0]);
- test_eq(0, socks->reply[1]);
-
- ADD_DATA(buf, "\x05\x01\x00\x01\x02\x02\x02\x02\x11\x11");
- test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
- get_options()->SafeSocks), 1);
- test_streq("2.2.2.2", socks->address);
- test_eq(4369, socks->port);
-
- test_eq(0, buf_datalen(buf));
- socks_request_clear(socks);
-
- /* SOCKS 5 Send CONNECT [01] to FQDN torproject.org:4369 */
- ADD_DATA(buf, "\x05\x01\x00");
- ADD_DATA(buf, "\x05\x01\x00\x03\x0Etorproject.org\x11\x11");
- test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
- get_options()->SafeSocks), 1);
-
- test_eq(5, socks->socks_version);
- test_eq(2, socks->replylen);
- test_eq(5, socks->reply[0]);
- test_eq(0, socks->reply[1]);
- test_streq("torproject.org", socks->address);
- test_eq(4369, socks->port);
-
- test_eq(0, buf_datalen(buf));
- socks_request_clear(socks);
-
- /* SOCKS 5 Send RESOLVE [F0] request for torproject.org:4369 */
- ADD_DATA(buf, "\x05\x01\x00");
- ADD_DATA(buf, "\x05\xF0\x00\x03\x0Etorproject.org\x01\x02");
- test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
- get_options()->SafeSocks) == 1);
- test_eq(5, socks->socks_version);
- test_eq(2, socks->replylen);
- test_eq(5, socks->reply[0]);
- test_eq(0, socks->reply[1]);
- test_streq("torproject.org", socks->address);
-
- test_eq(0, buf_datalen(buf));
- socks_request_clear(socks);
-
- /* SOCKS 5 Send RESOLVE_PTR [F1] for IP address 2.2.2.5 */
- ADD_DATA(buf, "\x05\x01\x00");
- ADD_DATA(buf, "\x05\xF1\x00\x01\x02\x02\x02\x05\x01\x03");
- test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
- get_options()->SafeSocks) == 1);
- test_eq(5, socks->socks_version);
- test_eq(2, socks->replylen);
- test_eq(5, socks->reply[0]);
- test_eq(0, socks->reply[1]);
- test_streq("2.2.2.5", socks->address);
-
- test_eq(0, buf_datalen(buf));
-
- done:
- ;
-}
-
-/** Perform SOCKS 5 authentication */
-static void
-test_socks_5_no_authenticate(void *ptr)
-{
- SOCKS_TEST_INIT();
-
- /*SOCKS 5 No Authentication */
- ADD_DATA(buf,"\x05\x01\x00");
- test_assert(!fetch_from_buf_socks(buf, socks,
- get_options()->TestSocks,
- get_options()->SafeSocks));
- test_eq(2, socks->replylen);
- test_eq(5, socks->reply[0]);
- test_eq(SOCKS_NO_AUTH, socks->reply[1]);
-
- test_eq(0, buf_datalen(buf));
-
- /*SOCKS 5 Send username/password anyway - pretend to be broken */
- ADD_DATA(buf,"\x01\x02\x01\x01\x02\x01\x01");
- test_assert(!fetch_from_buf_socks(buf, socks,
- get_options()->TestSocks,
- get_options()->SafeSocks));
- test_eq(5, socks->socks_version);
- test_eq(2, socks->replylen);
- test_eq(1, socks->reply[0]);
- test_eq(0, socks->reply[1]);
-
- test_eq(2, socks->usernamelen);
- test_eq(2, socks->passwordlen);
-
- test_memeq("\x01\x01", socks->username, 2);
- test_memeq("\x01\x01", socks->password, 2);
-
- done:
- ;
-}
-
-/** Perform SOCKS 5 authentication */
-static void
-test_socks_5_authenticate(void *ptr)
-{
- SOCKS_TEST_INIT();
-
- /* SOCKS 5 Negotiate username/password authentication */
- ADD_DATA(buf, "\x05\x01\x02");
-
- test_assert(!fetch_from_buf_socks(buf, socks,
- get_options()->TestSocks,
- get_options()->SafeSocks));
- test_eq(2, socks->replylen);
- test_eq(5, socks->reply[0]);
- test_eq(SOCKS_USER_PASS, socks->reply[1]);
- test_eq(5, socks->socks_version);
-
- test_eq(0, buf_datalen(buf));
-
- /* SOCKS 5 Send username/password */
- ADD_DATA(buf, "\x01\x02me\x08mypasswd");
- test_assert(!fetch_from_buf_socks(buf, socks,
- get_options()->TestSocks,
- get_options()->SafeSocks));
- test_eq(5, socks->socks_version);
- test_eq(2, socks->replylen);
- test_eq(1, socks->reply[0]);
- test_eq(0, socks->reply[1]);
-
- test_eq(2, socks->usernamelen);
- test_eq(8, socks->passwordlen);
-
- test_memeq("me", socks->username, 2);
- test_memeq("mypasswd", socks->password, 8);
-
- done:
- ;
-}
-
-/** Perform SOCKS 5 authentication and send data all in one go */
-static void
-test_socks_5_authenticate_with_data(void *ptr)
-{
- SOCKS_TEST_INIT();
-
- /* SOCKS 5 Negotiate username/password authentication */
- ADD_DATA(buf, "\x05\x01\x02");
-
- test_assert(!fetch_from_buf_socks(buf, socks,
- get_options()->TestSocks,
- get_options()->SafeSocks));
- test_eq(2, socks->replylen);
- test_eq(5, socks->reply[0]);
- test_eq(SOCKS_USER_PASS, socks->reply[1]);
- test_eq(5, socks->socks_version);
-
- test_eq(0, buf_datalen(buf));
-
- /* SOCKS 5 Send username/password */
- /* SOCKS 5 Send CONNECT [01] to IP address 2.2.2.2:4369 */
- ADD_DATA(buf, "\x01\x02me\x03you\x05\x01\x00\x01\x02\x02\x02\x02\x11\x11");
- test_assert(fetch_from_buf_socks(buf, socks,
- get_options()->TestSocks,
- get_options()->SafeSocks) == 1);
- test_eq(5, socks->socks_version);
- test_eq(2, socks->replylen);
- test_eq(1, socks->reply[0]);
- test_eq(0, socks->reply[1]);
-
- test_streq("2.2.2.2", socks->address);
- test_eq(4369, socks->port);
-
- test_eq(2, socks->usernamelen);
- test_eq(3, socks->passwordlen);
- test_memeq("me", socks->username, 2);
- test_memeq("you", socks->password, 3);
-
- done:
- ;
-}
-
-/** Perform SOCKS 5 authentication before method negotiated */
-static void
-test_socks_5_auth_before_negotiation(void *ptr)
-{
- SOCKS_TEST_INIT();
-
- /* SOCKS 5 Send username/password */
- ADD_DATA(buf, "\x01\x02me\x02me");
- test_assert(fetch_from_buf_socks(buf, socks,
- get_options()->TestSocks,
- get_options()->SafeSocks) == -1);
- test_eq(0, socks->socks_version);
- test_eq(0, socks->replylen);
- test_eq(0, socks->reply[0]);
- test_eq(0, socks->reply[1]);
-
- done:
- ;
-}
-
+/** Run unit tests for the onion handshake code. */
static void
-test_buffer_copy(void *arg)
+test_onion_handshake(void)
{
- generic_buffer_t *buf=NULL, *buf2=NULL;
- const char *s;
- size_t len;
- char b[256];
+ /* client-side */
+ crypto_dh_t *c_dh = NULL;
+ char c_buf[TAP_ONIONSKIN_CHALLENGE_LEN];
+ char c_keys[40];
+ /* server-side */
+ char s_buf[TAP_ONIONSKIN_REPLY_LEN];
+ char s_keys[40];
int i;
- (void)arg;
-
- buf = generic_buffer_new();
- tt_assert(buf);
-
- /* Copy an empty buffer. */
- tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
- tt_assert(buf2);
- tt_int_op(0, ==, generic_buffer_len(buf2));
-
- /* Now try with a short buffer. */
- s = "And now comes an act of enormous enormance!";
- len = strlen(s);
- generic_buffer_add(buf, s, len);
- tt_int_op(len, ==, generic_buffer_len(buf));
- /* Add junk to buf2 so we can test replacing.*/
- generic_buffer_add(buf2, "BLARG", 5);
- tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
- tt_int_op(len, ==, generic_buffer_len(buf2));
- generic_buffer_get(buf2, b, len);
- test_mem_op(b, ==, s, len);
- /* Now free buf2 and retry so we can test allocating */
- generic_buffer_free(buf2);
- buf2 = NULL;
- tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
- tt_int_op(len, ==, generic_buffer_len(buf2));
- generic_buffer_get(buf2, b, len);
- test_mem_op(b, ==, s, len);
- /* Clear buf for next test */
- generic_buffer_get(buf, b, len);
- tt_int_op(generic_buffer_len(buf),==,0);
-
- /* Okay, now let's try a bigger buffer. */
- s = "Quis autem vel eum iure reprehenderit qui in ea voluptate velit "
- "esse quam nihil molestiae consequatur, vel illum qui dolorem eum "
- "fugiat quo voluptas nulla pariatur?";
- len = strlen(s);
- for (i = 0; i < 256; ++i) {
- b[0]=i;
- generic_buffer_add(buf, b, 1);
- generic_buffer_add(buf, s, len);
- }
- tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
- tt_int_op(generic_buffer_len(buf2), ==, generic_buffer_len(buf));
- for (i = 0; i < 256; ++i) {
- generic_buffer_get(buf2, b, len+1);
- tt_int_op((unsigned char)b[0],==,i);
- test_mem_op(b+1, ==, s, len);
- }
-
- done:
- if (buf)
- generic_buffer_free(buf);
- if (buf2)
- generic_buffer_free(buf2);
-}
-
-/** Run unit tests for buffers.c */
-static void
-test_buffers(void)
-{
- char str[256];
- char str2[256];
-
- buf_t *buf = NULL, *buf2 = NULL;
- const char *cp;
+ /* shared */
+ crypto_pk_t *pk = NULL, *pk2 = NULL;
- int j;
- size_t r;
+ pk = pk_generate(0);
+ pk2 = pk_generate(1);
- /****
- * buf_new
- ****/
- if (!(buf = buf_new()))
- test_fail();
+ /* client handshake 1. */
+ memset(c_buf, 0, TAP_ONIONSKIN_CHALLENGE_LEN);
+ test_assert(! onion_skin_TAP_create(pk, &c_dh, c_buf));
- //test_eq(buf_capacity(buf), 4096);
- test_eq(buf_datalen(buf), 0);
+ for (i = 1; i <= 3; ++i) {
+ crypto_pk_t *k1, *k2;
+ if (i==1) {
+ /* server handshake: only one key known. */
+ k1 = pk; k2 = NULL;
+ } else if (i==2) {
+ /* server handshake: try the right key first. */
+ k1 = pk; k2 = pk2;
+ } else {
+ /* server handshake: try the right key second. */
+ k1 = pk2; k2 = pk;
+ }
- /****
- * General pointer frobbing
- */
- for (j=0;j<256;++j) {
- str[j] = (char)j;
- }
- write_to_buf(str, 256, buf);
- write_to_buf(str, 256, buf);
- test_eq(buf_datalen(buf), 512);
- fetch_from_buf(str2, 200, buf);
- test_memeq(str, str2, 200);
- test_eq(buf_datalen(buf), 312);
- memset(str2, 0, sizeof(str2));
-
- fetch_from_buf(str2, 256, buf);
- test_memeq(str+200, str2, 56);
- test_memeq(str, str2+56, 200);
- test_eq(buf_datalen(buf), 56);
- memset(str2, 0, sizeof(str2));
- /* Okay, now we should be 512 bytes into the 4096-byte buffer. If we add
- * another 3584 bytes, we hit the end. */
- for (j=0;j<15;++j) {
- write_to_buf(str, 256, buf);
- }
- assert_buf_ok(buf);
- test_eq(buf_datalen(buf), 3896);
- fetch_from_buf(str2, 56, buf);
- test_eq(buf_datalen(buf), 3840);
- test_memeq(str+200, str2, 56);
- for (j=0;j<15;++j) {
- memset(str2, 0, sizeof(str2));
- fetch_from_buf(str2, 256, buf);
- test_memeq(str, str2, 256);
- }
- test_eq(buf_datalen(buf), 0);
- buf_free(buf);
- buf = NULL;
-
- /* Okay, now make sure growing can work. */
- buf = buf_new_with_capacity(16);
- //test_eq(buf_capacity(buf), 16);
- write_to_buf(str+1, 255, buf);
- //test_eq(buf_capacity(buf), 256);
- fetch_from_buf(str2, 254, buf);
- test_memeq(str+1, str2, 254);
- //test_eq(buf_capacity(buf), 256);
- assert_buf_ok(buf);
- write_to_buf(str, 32, buf);
- //test_eq(buf_capacity(buf), 256);
- assert_buf_ok(buf);
- write_to_buf(str, 256, buf);
- assert_buf_ok(buf);
- //test_eq(buf_capacity(buf), 512);
- test_eq(buf_datalen(buf), 33+256);
- fetch_from_buf(str2, 33, buf);
- test_eq(*str2, str[255]);
-
- test_memeq(str2+1, str, 32);
- //test_eq(buf_capacity(buf), 512);
- test_eq(buf_datalen(buf), 256);
- fetch_from_buf(str2, 256, buf);
- test_memeq(str, str2, 256);
-
- /* now try shrinking: case 1. */
- buf_free(buf);
- buf = buf_new_with_capacity(33668);
- for (j=0;j<67;++j) {
- write_to_buf(str,255, buf);
- }
- //test_eq(buf_capacity(buf), 33668);
- test_eq(buf_datalen(buf), 17085);
- for (j=0; j < 40; ++j) {
- fetch_from_buf(str2, 255,buf);
- test_memeq(str2, str, 255);
- }
+ memset(s_buf, 0, TAP_ONIONSKIN_REPLY_LEN);
+ memset(s_keys, 0, 40);
+ test_assert(! onion_skin_TAP_server_handshake(c_buf, k1, k2,
+ s_buf, s_keys, 40));
- /* now try shrinking: case 2. */
- buf_free(buf);
- buf = buf_new_with_capacity(33668);
- for (j=0;j<67;++j) {
- write_to_buf(str,255, buf);
- }
- for (j=0; j < 20; ++j) {
- fetch_from_buf(str2, 255,buf);
- test_memeq(str2, str, 255);
- }
- for (j=0;j<80;++j) {
- write_to_buf(str,255, buf);
- }
- //test_eq(buf_capacity(buf),33668);
- for (j=0; j < 120; ++j) {
- fetch_from_buf(str2, 255,buf);
- test_memeq(str2, str, 255);
- }
+ /* client handshake 2 */
+ memset(c_keys, 0, 40);
+ test_assert(! onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40));
- /* Move from buf to buf. */
- buf_free(buf);
- buf = buf_new_with_capacity(4096);
- buf2 = buf_new_with_capacity(4096);
- for (j=0;j<100;++j)
- write_to_buf(str, 255, buf);
- test_eq(buf_datalen(buf), 25500);
- for (j=0;j<100;++j) {
- r = 10;
- move_buf_to_buf(buf2, buf, &r);
- test_eq(r, 0);
- }
- test_eq(buf_datalen(buf), 24500);
- test_eq(buf_datalen(buf2), 1000);
- for (j=0;j<3;++j) {
- fetch_from_buf(str2, 255, buf2);
- test_memeq(str2, str, 255);
- }
- r = 8192; /*big move*/
- move_buf_to_buf(buf2, buf, &r);
- test_eq(r, 0);
- r = 30000; /* incomplete move */
- move_buf_to_buf(buf2, buf, &r);
- test_eq(r, 13692);
- for (j=0;j<97;++j) {
- fetch_from_buf(str2, 255, buf2);
- test_memeq(str2, str, 255);
+ test_memeq(c_keys, s_keys, 40);
+ memset(s_buf, 0, 40);
+ test_memneq(c_keys, s_buf, 40);
}
- buf_free(buf);
- buf_free(buf2);
- buf = buf2 = NULL;
-
- buf = buf_new_with_capacity(5);
- cp = "Testing. This is a moderately long Testing string.";
- for (j = 0; cp[j]; j++)
- write_to_buf(cp+j, 1, buf);
- test_eq(0, buf_find_string_offset(buf, "Testing", 7));
- test_eq(1, buf_find_string_offset(buf, "esting", 6));
- test_eq(1, buf_find_string_offset(buf, "est", 3));
- test_eq(39, buf_find_string_offset(buf, "ing str", 7));
- test_eq(35, buf_find_string_offset(buf, "Testing str", 11));
- test_eq(32, buf_find_string_offset(buf, "ng ", 3));
- test_eq(43, buf_find_string_offset(buf, "string.", 7));
- test_eq(-1, buf_find_string_offset(buf, "shrdlu", 6));
- test_eq(-1, buf_find_string_offset(buf, "Testing thing", 13));
- test_eq(-1, buf_find_string_offset(buf, "ngx", 3));
- buf_free(buf);
- buf = NULL;
-
- /* Try adding a string too long for any freelist. */
- {
- char *cp = tor_malloc_zero(65536);
- buf = buf_new();
- write_to_buf(cp, 65536, buf);
- tor_free(cp);
-
- tt_int_op(buf_datalen(buf), ==, 65536);
- buf_free(buf);
- buf = NULL;
- }
-
done:
- if (buf)
- buf_free(buf);
- if (buf2)
- buf_free(buf2);
+ crypto_dh_free(c_dh);
+ crypto_pk_free(pk);
+ crypto_pk_free(pk2);
}
-/** Run unit tests for the onion handshake code. */
static void
-test_onion_handshake(void)
+test_bad_onion_handshake(void *arg)
{
+ char junk_buf[TAP_ONIONSKIN_CHALLENGE_LEN];
+ char junk_buf2[TAP_ONIONSKIN_CHALLENGE_LEN];
/* client-side */
crypto_dh_t *c_dh = NULL;
char c_buf[TAP_ONIONSKIN_CHALLENGE_LEN];
char c_keys[40];
-
/* server-side */
char s_buf[TAP_ONIONSKIN_REPLY_LEN];
char s_keys[40];
-
/* shared */
- crypto_pk_t *pk = NULL;
+ crypto_pk_t *pk = NULL, *pk2 = NULL;
+
+ (void)arg;
pk = pk_generate(0);
+ pk2 = pk_generate(1);
- /* client handshake 1. */
+ /* Server: Case 1: the encrypted data is degenerate. */
+ memset(junk_buf, 0, sizeof(junk_buf));
+ crypto_pk_public_hybrid_encrypt(pk, junk_buf2, TAP_ONIONSKIN_CHALLENGE_LEN,
+ junk_buf, DH_KEY_LEN, PK_PKCS1_OAEP_PADDING, 1);
+ tt_int_op(-1, ==,
+ onion_skin_TAP_server_handshake(junk_buf2, pk, NULL,
+ s_buf, s_keys, 40));
+
+ /* Server: Case 2: the encrypted data is not long enough. */
+ memset(junk_buf, 0, sizeof(junk_buf));
+ memset(junk_buf2, 0, sizeof(junk_buf2));
+ crypto_pk_public_encrypt(pk, junk_buf2, sizeof(junk_buf2),
+ junk_buf, 48, PK_PKCS1_OAEP_PADDING);
+ tt_int_op(-1, ==,
+ onion_skin_TAP_server_handshake(junk_buf2, pk, NULL,
+ s_buf, s_keys, 40));
+
+ /* client handshake 1: do it straight. */
memset(c_buf, 0, TAP_ONIONSKIN_CHALLENGE_LEN);
test_assert(! onion_skin_TAP_create(pk, &c_dh, c_buf));
- /* server handshake */
- memset(s_buf, 0, TAP_ONIONSKIN_REPLY_LEN);
- memset(s_keys, 0, 40);
- test_assert(! onion_skin_TAP_server_handshake(c_buf, pk, NULL,
+ /* Server: Case 3: we just don't have the right key. */
+ tt_int_op(-1, ==,
+ onion_skin_TAP_server_handshake(c_buf, pk2, NULL,
s_buf, s_keys, 40));
- /* client handshake 2 */
- memset(c_keys, 0, 40);
- test_assert(! onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40));
+ /* Server: Case 4: The RSA-encrypted portion is corrupt. */
+ c_buf[64] ^= 33;
+ tt_int_op(-1, ==,
+ onion_skin_TAP_server_handshake(c_buf, pk, NULL,
+ s_buf, s_keys, 40));
+ c_buf[64] ^= 33;
- if (memcmp(c_keys, s_keys, 40)) {
- puts("Aiiiie");
- exit(1);
- }
- test_memeq(c_keys, s_keys, 40);
- memset(s_buf, 0, 40);
- test_memneq(c_keys, s_buf, 40);
+ /* (Let the server procede) */
+ tt_int_op(0, ==,
+ onion_skin_TAP_server_handshake(c_buf, pk, NULL,
+ s_buf, s_keys, 40));
+
+ /* Client: Case 1: The server sent back junk. */
+ s_buf[64] ^= 33;
+ tt_int_op(-1, ==,
+ onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40));
+ s_buf[64] ^= 33;
+
+ /* Let the client finish; make sure it can. */
+ tt_int_op(0, ==,
+ onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40));
+ test_memeq(s_keys, c_keys, 40);
+
+ /* Client: Case 2: The server sent back a degenerate DH. */
+ memset(s_buf, 0, sizeof(s_buf));
+ tt_int_op(-1, ==,
+ onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40));
done:
- if (c_dh)
- crypto_dh_free(c_dh);
- if (pk)
- crypto_pk_free(pk);
+ crypto_dh_free(c_dh);
+ crypto_pk_free(pk);
+ crypto_pk_free(pk2);
}
#ifdef CURVE25519_ENABLED
@@ -956,10 +427,12 @@ test_onion_queues(void)
test_eq(0, onion_num_pending(ONION_HANDSHAKE_TYPE_TAP));
test_eq(0, onion_pending_add(circ1, create1));
+ create1 = NULL;
test_eq(1, onion_num_pending(ONION_HANDSHAKE_TYPE_TAP));
test_eq(0, onion_num_pending(ONION_HANDSHAKE_TYPE_NTOR));
test_eq(0, onion_pending_add(circ2, create2));
+ create2 = NULL;
test_eq(1, onion_num_pending(ONION_HANDSHAKE_TYPE_NTOR));
test_eq_ptr(circ2, onion_next_task(&onionskin));
@@ -971,11 +444,10 @@ test_onion_queues(void)
test_eq(0, onion_num_pending(ONION_HANDSHAKE_TYPE_NTOR));
done:
- ;
-// circuit_free(circ1);
-// circuit_free(circ2);
- /* and free create1 and create2 */
- /* XXX leaks everything here */
+ circuit_free(TO_CIRCUIT(circ1));
+ circuit_free(TO_CIRCUIT(circ2));
+ tor_free(create1);
+ tor_free(create2);
}
static void
@@ -1579,6 +1051,34 @@ test_rend_fns(void)
tor_free(intro_points_encrypted);
}
+ /* Record odd numbered fake-IPs using ipv6, even numbered fake-IPs
+ * using ipv4. Since our fake geoip database is the same between
+ * ipv4 and ipv6, we should get the same result no matter which
+ * address family we pick for each IP. */
+#define SET_TEST_ADDRESS(i) do { \
+ if ((i) & 1) { \
+ SET_TEST_IPV6(i); \
+ tor_addr_from_in6(&addr, &in6); \
+ } else { \
+ tor_addr_from_ipv4h(&addr, (uint32_t) i); \
+ } \
+ } while (0)
+
+ /* Make sure that country ID actually works. */
+#define SET_TEST_IPV6(i) \
+ do { \
+ set_uint32(in6.s6_addr + 12, htonl((uint32_t) (i))); \
+ } while (0)
+#define CHECK_COUNTRY(country, val) do { \
+ /* test ipv4 country lookup */ \
+ test_streq(country, \
+ geoip_get_country_name(geoip_get_country_by_ipv4(val))); \
+ /* test ipv6 country lookup */ \
+ SET_TEST_IPV6(val); \
+ test_streq(country, \
+ geoip_get_country_name(geoip_get_country_by_ipv6(&in6))); \
+ } while (0)
+
/** Run unit tests for GeoIP code. */
static void
test_geoip(void)
@@ -1589,7 +1089,8 @@ test_geoip(void)
const char *bridge_stats_1 =
"bridge-stats-end 2010-08-12 13:27:30 (86400 s)\n"
"bridge-ips zz=24,xy=8\n"
- "bridge-ip-versions v4=16,v6=16\n",
+ "bridge-ip-versions v4=16,v6=16\n"
+ "bridge-ip-transports <OR>=24\n",
*dirreq_stats_1 =
"dirreq-stats-end 2010-08-12 13:27:30 (86400 s)\n"
"dirreq-v3-ips ab=8\n"
@@ -1653,21 +1154,6 @@ test_geoip(void)
test_eq(4, geoip_get_n_countries());
memset(&in6, 0, sizeof(in6));
- /* Make sure that country ID actually works. */
-#define SET_TEST_IPV6(i) \
- do { \
- set_uint32(in6.s6_addr + 12, htonl((uint32_t) (i))); \
- } while (0)
-#define CHECK_COUNTRY(country, val) do { \
- /* test ipv4 country lookup */ \
- test_streq(country, \
- geoip_get_country_name(geoip_get_country_by_ipv4(val))); \
- /* test ipv6 country lookup */ \
- SET_TEST_IPV6(val); \
- test_streq(country, \
- geoip_get_country_name(geoip_get_country_by_ipv6(&in6))); \
- } while (0)
-
CHECK_COUNTRY("??", 3);
CHECK_COUNTRY("ab", 32);
CHECK_COUNTRY("??", 5);
@@ -1680,40 +1166,25 @@ test_geoip(void)
SET_TEST_IPV6(3);
test_eq(0, geoip_get_country_by_ipv6(&in6));
-#undef CHECK_COUNTRY
-
- /* Record odd numbered fake-IPs using ipv6, even numbered fake-IPs
- * using ipv4. Since our fake geoip database is the same between
- * ipv4 and ipv6, we should get the same result no matter which
- * address family we pick for each IP. */
-#define SET_TEST_ADDRESS(i) do { \
- if ((i) & 1) { \
- SET_TEST_IPV6(i); \
- tor_addr_from_in6(&addr, &in6); \
- } else { \
- tor_addr_from_ipv4h(&addr, (uint32_t) i); \
- } \
- } while (0)
-
get_options_mutable()->BridgeRelay = 1;
get_options_mutable()->BridgeRecordUsageByCountry = 1;
/* Put 9 observations in AB... */
for (i=32; i < 40; ++i) {
SET_TEST_ADDRESS(i);
- geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now-7200);
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now-7200);
}
SET_TEST_ADDRESS(225);
- geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now-7200);
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now-7200);
/* and 3 observations in XY, several times. */
for (j=0; j < 10; ++j)
for (i=52; i < 55; ++i) {
SET_TEST_ADDRESS(i);
- geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now-3600);
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now-3600);
}
/* and 17 observations in ZZ... */
for (i=110; i < 127; ++i) {
SET_TEST_ADDRESS(i);
- geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now);
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now);
}
geoip_get_client_history(GEOIP_CLIENT_CONNECT, &s, &v);
test_assert(s);
@@ -1762,7 +1233,7 @@ test_geoip(void)
/* Start testing dirreq statistics by making sure that we don't collect
* dirreq stats without initializing them. */
SET_TEST_ADDRESS(100);
- geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, now);
+ geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, NULL, now);
s = geoip_format_dirreq_stats(now + 86400);
test_assert(!s);
@@ -1770,7 +1241,7 @@ test_geoip(void)
* dirreq-stats history string. */
geoip_dirreq_stats_init(now);
SET_TEST_ADDRESS(100);
- geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, now);
+ geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, NULL, now);
s = geoip_format_dirreq_stats(now + 86400);
test_streq(dirreq_stats_1, s);
tor_free(s);
@@ -1779,7 +1250,7 @@ test_geoip(void)
* don't generate a history string. */
geoip_dirreq_stats_term();
SET_TEST_ADDRESS(101);
- geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, now);
+ geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, NULL, now);
s = geoip_format_dirreq_stats(now + 86400);
test_assert(!s);
@@ -1787,7 +1258,7 @@ test_geoip(void)
* that we get an all empty history string. */
geoip_dirreq_stats_init(now);
SET_TEST_ADDRESS(100);
- geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, now);
+ geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, NULL, now);
geoip_reset_dirreq_stats(now);
s = geoip_format_dirreq_stats(now + 86400);
test_streq(dirreq_stats_2, s);
@@ -1814,7 +1285,7 @@ test_geoip(void)
/* Start testing entry statistics by making sure that we don't collect
* anything without initializing entry stats. */
SET_TEST_ADDRESS(100);
- geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now);
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now);
s = geoip_format_entry_stats(now + 86400);
test_assert(!s);
@@ -1822,7 +1293,7 @@ test_geoip(void)
* entry-stats history string. */
geoip_entry_stats_init(now);
SET_TEST_ADDRESS(100);
- geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now);
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now);
s = geoip_format_entry_stats(now + 86400);
test_streq(entry_stats_1, s);
tor_free(s);
@@ -1831,7 +1302,7 @@ test_geoip(void)
* don't generate a history string. */
geoip_entry_stats_term();
SET_TEST_ADDRESS(101);
- geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now);
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now);
s = geoip_format_entry_stats(now + 86400);
test_assert(!s);
@@ -1839,15 +1310,12 @@ test_geoip(void)
* that we get an all empty history string. */
geoip_entry_stats_init(now);
SET_TEST_ADDRESS(100);
- geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now);
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now);
geoip_reset_entry_stats(now);
s = geoip_format_entry_stats(now + 86400);
test_streq(entry_stats_2, s);
tor_free(s);
-#undef SET_TEST_ADDRESS
-#undef SET_TEST_IPV6
-
/* Stop collecting entry statistics. */
geoip_entry_stats_term();
get_options_mutable()->EntryStatistics = 0;
@@ -1857,6 +1325,79 @@ test_geoip(void)
tor_free(v);
}
+static void
+test_geoip_with_pt(void)
+{
+ time_t now = 1281533250; /* 2010-08-11 13:27:30 UTC */
+ char *s = NULL;
+ int i;
+ tor_addr_t addr;
+ struct in6_addr in6;
+
+ get_options_mutable()->BridgeRelay = 1;
+ get_options_mutable()->BridgeRecordUsageByCountry = 1;
+
+ /* No clients seen yet. */
+ s = geoip_get_transport_history();
+ tor_assert(!s);
+
+ /* 4 connections without a pluggable transport */
+ for (i=0; i < 4; ++i) {
+ SET_TEST_ADDRESS(i);
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now-7200);
+ }
+
+ /* 9 connections with "alpha" */
+ for (i=4; i < 13; ++i) {
+ SET_TEST_ADDRESS(i);
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "alpha", now-7200);
+ }
+
+ /* one connection with "beta" */
+ SET_TEST_ADDRESS(13);
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "beta", now-7200);
+
+ /* 14 connections with "charlie" */
+ for (i=14; i < 28; ++i) {
+ SET_TEST_ADDRESS(i);
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "charlie", now-7200);
+ }
+
+ /* 131 connections with "ddr" */
+ for (i=28; i < 159; ++i) {
+ SET_TEST_ADDRESS(i);
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "ddr", now-7200);
+ }
+
+ /* 8 connections with "entropy" */
+ for (i=159; i < 167; ++i) {
+ SET_TEST_ADDRESS(i);
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "entropy", now-7200);
+ }
+
+ /* 2 connections from the same IP with two different transports. */
+ SET_TEST_ADDRESS(++i);
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "fire", now-7200);
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "google", now-7200);
+
+ /* Test the transport history string. */
+ s = geoip_get_transport_history();
+ tor_assert(s);
+ test_streq(s, "<OR>=8,alpha=16,beta=8,charlie=16,ddr=136,"
+ "entropy=8,fire=8,google=8");
+
+ /* Stop collecting entry statistics. */
+ geoip_entry_stats_term();
+ get_options_mutable()->EntryStatistics = 0;
+
+ done:
+ tor_free(s);
+}
+
+#undef SET_TEST_ADDRESS
+#undef SET_TEST_IPV6
+#undef CHECK_COUNTRY
+
/** Run unit tests for stats code. */
static void
test_stats(void)
@@ -2047,9 +1588,8 @@ const struct testcase_setup_t legacy_setup = {
{ #name, legacy_test_helper, TT_FORK, &legacy_setup, test_ ## name }
static struct testcase_t test_array[] = {
- ENT(buffers),
- { "buffer_copy", test_buffer_copy, 0, NULL, NULL },
ENT(onion_handshake),
+ { "bad_onion_handshake", test_bad_onion_handshake, 0, NULL, NULL },
ENT(onion_queues),
#ifdef CURVE25519_ENABLED
{ "ntor_handshake", test_ntor_handshake, 0, NULL, NULL },
@@ -2058,29 +1598,14 @@ static struct testcase_t test_array[] = {
ENT(policies),
ENT(rend_fns),
ENT(geoip),
+ FORK(geoip_with_pt),
FORK(stats),
END_OF_TESTCASES
};
-#define SOCKSENT(name) \
- { #name, test_socks_##name, TT_FORK, &socks_setup, NULL }
-
-static struct testcase_t socks_tests[] = {
- SOCKSENT(4_unsupported_commands),
- SOCKSENT(4_supported_commands),
-
- SOCKSENT(5_unsupported_commands),
- SOCKSENT(5_supported_commands),
- SOCKSENT(5_no_authenticate),
- SOCKSENT(5_auth_before_negotiation),
- SOCKSENT(5_authenticate),
- SOCKSENT(5_authenticate_with_data),
-
- END_OF_TESTCASES
-};
-
extern struct testcase_t addr_tests[];
+extern struct testcase_t buffer_tests[];
extern struct testcase_t crypto_tests[];
extern struct testcase_t container_tests[];
extern struct testcase_t util_tests[];
@@ -2091,21 +1616,38 @@ extern struct testcase_t config_tests[];
extern struct testcase_t introduce_tests[];
extern struct testcase_t replaycache_tests[];
extern struct testcase_t cell_format_tests[];
+extern struct testcase_t circuitlist_tests[];
+extern struct testcase_t circuitmux_tests[];
+extern struct testcase_t cell_queue_tests[];
+extern struct testcase_t options_tests[];
+extern struct testcase_t socks_tests[];
+extern struct testcase_t extorport_tests[];
+extern struct testcase_t controller_event_tests[];
+extern struct testcase_t logging_tests[];
+extern struct testcase_t backtrace_tests[];
static struct testgroup_t testgroups[] = {
{ "", test_array },
+ { "buffer/", buffer_tests },
{ "socks/", socks_tests },
{ "addr/", addr_tests },
{ "crypto/", crypto_tests },
{ "container/", container_tests },
{ "util/", util_tests },
+ { "util/logging/", logging_tests },
{ "cellfmt/", cell_format_tests },
+ { "cellqueue/", cell_queue_tests },
{ "dir/", dir_tests },
{ "dir/md/", microdesc_tests },
{ "pt/", pt_tests },
{ "config/", config_tests },
{ "replaycache/", replaycache_tests },
{ "introduce/", introduce_tests },
+ { "circuitlist/", circuitlist_tests },
+ { "circuitmux/", circuitmux_tests },
+ { "options/", options_tests },
+ { "extorport/", extorport_tests },
+ { "control/", controller_event_tests },
END_OF_GROUPS
};
diff --git a/src/test/test_addr.c b/src/test/test_addr.c
index fec85a469..4bc602df8 100644
--- a/src/test/test_addr.c
+++ b/src/test/test_addr.c
@@ -44,6 +44,10 @@ test_addr_basic(void)
test_eq(u32, 0x7f000001u);
test_eq(u16, 0);
tor_free(cp);
+
+ test_assert(addr_port_lookup(LOG_WARN, "localhost:3", &cp, &u32, NULL));
+ tor_free(cp);
+
test_eq(0, addr_mask_get_bits(0x0u));
test_eq(32, addr_mask_get_bits(0xFFFFFFFFu));
test_eq(16, addr_mask_get_bits(0xFFFF0000u));
@@ -217,11 +221,12 @@ test_addr_ip6_helpers(void)
/* ==== Converting to and from sockaddr_t. */
sin = (struct sockaddr_in *)&sa_storage;
sin->sin_family = AF_INET;
- sin->sin_port = 9090;
+ sin->sin_port = htons(9090);
sin->sin_addr.s_addr = htonl(0x7f7f0102); /*127.127.1.2*/
- tor_addr_from_sockaddr(&t1, (struct sockaddr *)sin, NULL);
+ tor_addr_from_sockaddr(&t1, (struct sockaddr *)sin, &port1);
test_eq(tor_addr_family(&t1), AF_INET);
test_eq(tor_addr_to_ipv4h(&t1), 0x7f7f0102);
+ tt_int_op(port1, ==, 9090);
memset(&sa_storage, 0, sizeof(sa_storage));
test_eq(sizeof(struct sockaddr_in),
@@ -235,8 +240,9 @@ test_addr_ip6_helpers(void)
sin6->sin6_family = AF_INET6;
sin6->sin6_port = htons(7070);
sin6->sin6_addr.s6_addr[0] = 128;
- tor_addr_from_sockaddr(&t1, (struct sockaddr *)sin6, NULL);
+ tor_addr_from_sockaddr(&t1, (struct sockaddr *)sin6, &port1);
test_eq(tor_addr_family(&t1), AF_INET6);
+ tt_int_op(port1, ==, 7070);
p1 = tor_addr_to_str(buf, &t1, sizeof(buf), 0);
test_streq(p1, "8000::");
@@ -464,6 +470,9 @@ test_addr_ip6_helpers(void)
test_eq(0, i);
i = tor_addr_parse_PTR_name(&t1, "Foobar.baz", AF_UNSPEC, 1);
test_eq(0, i);
+ i = tor_addr_parse_PTR_name(&t1, "9999999999999999999999999999.in-addr.arpa",
+ AF_UNSPEC, 1);
+ test_eq(-1, i);
i = tor_addr_parse_PTR_name(&t1, "1.0.168.192.in-addr.arpa",
AF_UNSPEC, 1);
test_eq(1, i);
@@ -844,6 +853,90 @@ test_virtaddrmap(void *data)
}
static void
+test_addr_localname(void *arg)
+{
+ (void)arg;
+ tt_assert(tor_addr_hostname_is_local("localhost"));
+ tt_assert(tor_addr_hostname_is_local("LOCALHOST"));
+ tt_assert(tor_addr_hostname_is_local("LocalHost"));
+ tt_assert(tor_addr_hostname_is_local("local"));
+ tt_assert(tor_addr_hostname_is_local("LOCAL"));
+ tt_assert(tor_addr_hostname_is_local("here.now.local"));
+ tt_assert(tor_addr_hostname_is_local("here.now.LOCAL"));
+
+ tt_assert(!tor_addr_hostname_is_local(" localhost"));
+ tt_assert(!tor_addr_hostname_is_local("www.torproject.org"));
+ done:
+ ;
+}
+
+static void
+test_addr_dup_ip(void *arg)
+{
+ char *v = NULL;
+ (void)arg;
+#define CHECK(ip, s) do { \
+ v = tor_dup_ip(ip); \
+ tt_str_op(v,==,(s)); \
+ tor_free(v); \
+ } while (0)
+
+ CHECK(0xffffffff, "255.255.255.255");
+ CHECK(0x00000000, "0.0.0.0");
+ CHECK(0x7f000001, "127.0.0.1");
+ CHECK(0x01020304, "1.2.3.4");
+
+#undef CHECK
+ done:
+ tor_free(v);
+}
+
+static void
+test_addr_sockaddr_to_str(void *arg)
+{
+ char *v = NULL;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ struct sockaddr_storage ss;
+#ifdef HAVE_SYS_UN_H
+ struct sockaddr_un sun;
+#endif
+#define CHECK(sa, s) do { \
+ v = tor_sockaddr_to_str((const struct sockaddr*) &(sa)); \
+ tt_str_op(v,==,(s)); \
+ tor_free(v); \
+ } while (0)
+ (void)arg;
+
+ memset(&ss,0,sizeof(ss));
+ ss.ss_family = AF_UNSPEC;
+ CHECK(ss, "unspec");
+
+ memset(&sin,0,sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(0x7f808001);
+ sin.sin_port = htons(1234);
+ CHECK(sin, "127.128.128.1:1234");
+
+#ifdef HAVE_SYS_UN_H
+ memset(&sun,0,sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strlcpy(sun.sun_path, "/here/is/a/path", sizeof(sun.sun_path));
+ CHECK(sun, "unix:/here/is/a/path");
+#endif
+
+ memset(&sin6,0,sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ memcpy(sin6.sin6_addr.s6_addr, "\x20\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x1a\x2b\x3c\x4d\x5e\x00\x01", 16);
+ sin6.sin6_port = htons(1234);
+ CHECK(sin6, "[2000::1a:2b3c:4d5e:1]:1234");
+
+ done:
+ tor_free(v);
+}
+
+static void
test_addr_is_loopback(void *data)
{
static const struct loopback_item {
@@ -886,6 +979,9 @@ struct testcase_t addr_tests[] = {
ADDR_LEGACY(ip6_helpers),
ADDR_LEGACY(parse),
{ "virtaddr", test_virtaddrmap, 0, NULL, NULL },
+ { "localname", test_addr_localname, 0, NULL, NULL },
+ { "dup_ip", test_addr_dup_ip, 0, NULL, NULL },
+ { "sockaddr_to_str", test_addr_sockaddr_to_str, 0, NULL, NULL },
{ "is_loopback", test_addr_is_loopback, 0, NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_bt_cl.c b/src/test/test_bt_cl.c
new file mode 100644
index 000000000..45ae82fb8
--- /dev/null
+++ b/src/test/test_bt_cl.c
@@ -0,0 +1,109 @@
+/* Copyright (c) 2012-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "or.h"
+#include "util.h"
+#include "backtrace.h"
+#include "torlog.h"
+
+/* -1: no crash.
+ * 0: crash with a segmentation fault.
+ * 1x: crash with an assertion failure. */
+static int crashtype = 0;
+
+#ifdef __GNUC__
+#define NOINLINE __attribute__((noinline))
+#define NORETURN __attribute__((noreturn))
+#endif
+
+int crash(int x) NOINLINE;
+int oh_what(int x) NOINLINE;
+int a_tangled_web(int x) NOINLINE;
+int we_weave(int x) NOINLINE;
+static void abort_handler(int s) NORETURN;
+
+int
+crash(int x)
+{
+ if (crashtype == 0) {
+ *(volatile int *)0 = 0;
+ } else if (crashtype == 1) {
+ tor_assert(1 == 0);
+ } else if (crashtype == -1) {
+ ;
+ }
+
+ crashtype *= x;
+ return crashtype;
+}
+
+int
+oh_what(int x)
+{
+ /* We call crash() twice here, so that the compiler won't try to do a
+ * tail-call optimization. Only the first call will actually happen, but
+ * telling the compiler to maybe do the second call will prevent it from
+ * replacing the first call with a jump. */
+ return crash(x) + crash(x*2);
+}
+
+int
+a_tangled_web(int x)
+{
+ return oh_what(x) * 99 + oh_what(x);
+}
+
+int
+we_weave(int x)
+{
+ return a_tangled_web(x) + a_tangled_web(x+1);
+}
+
+static void
+abort_handler(int s)
+{
+ (void)s;
+ exit(0);
+}
+
+int
+main(int argc, char **argv)
+{
+ log_severity_list_t severity;
+
+ if (argc < 2) {
+ puts("I take an argument. It should be \"assert\" or \"crash\" or "
+ "\"none\"");
+ return 1;
+ }
+ if (!strcmp(argv[1], "assert")) {
+ crashtype = 1;
+ } else if (!strcmp(argv[1], "crash")) {
+ crashtype = 0;
+ } else if (!strcmp(argv[1], "none")) {
+ crashtype = -1;
+ } else {
+ puts("Argument should be \"assert\" or \"crash\" or \"none\"");
+ return 1;
+ }
+
+ init_logging();
+ set_log_severity_config(LOG_WARN, LOG_ERR, &severity);
+ add_stream_log(&severity, "stdout", STDOUT_FILENO);
+ tor_log_update_sigsafe_err_fds();
+
+ configure_backtrace_handler(NULL);
+
+ signal(SIGABRT, abort_handler);
+
+ printf("%d\n", we_weave(2));
+
+ clean_up_backtrace_handler();
+
+ return 0;
+}
+
diff --git a/src/test/test_buffers.c b/src/test/test_buffers.c
new file mode 100644
index 000000000..a009faa0b
--- /dev/null
+++ b/src/test/test_buffers.c
@@ -0,0 +1,342 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define BUFFERS_PRIVATE
+#include "or.h"
+#include "buffers.h"
+#include "ext_orport.h"
+#include "test.h"
+
+/** Run unit tests for buffers.c */
+static void
+test_buffers_basic(void *arg)
+{
+ char str[256];
+ char str2[256];
+
+ buf_t *buf = NULL, *buf2 = NULL;
+ const char *cp;
+
+ int j;
+ size_t r;
+ (void) arg;
+
+ /****
+ * buf_new
+ ****/
+ if (!(buf = buf_new()))
+ test_fail();
+
+ //test_eq(buf_capacity(buf), 4096);
+ test_eq(buf_datalen(buf), 0);
+
+ /****
+ * General pointer frobbing
+ */
+ for (j=0;j<256;++j) {
+ str[j] = (char)j;
+ }
+ write_to_buf(str, 256, buf);
+ write_to_buf(str, 256, buf);
+ test_eq(buf_datalen(buf), 512);
+ fetch_from_buf(str2, 200, buf);
+ test_memeq(str, str2, 200);
+ test_eq(buf_datalen(buf), 312);
+ memset(str2, 0, sizeof(str2));
+
+ fetch_from_buf(str2, 256, buf);
+ test_memeq(str+200, str2, 56);
+ test_memeq(str, str2+56, 200);
+ test_eq(buf_datalen(buf), 56);
+ memset(str2, 0, sizeof(str2));
+ /* Okay, now we should be 512 bytes into the 4096-byte buffer. If we add
+ * another 3584 bytes, we hit the end. */
+ for (j=0;j<15;++j) {
+ write_to_buf(str, 256, buf);
+ }
+ assert_buf_ok(buf);
+ test_eq(buf_datalen(buf), 3896);
+ fetch_from_buf(str2, 56, buf);
+ test_eq(buf_datalen(buf), 3840);
+ test_memeq(str+200, str2, 56);
+ for (j=0;j<15;++j) {
+ memset(str2, 0, sizeof(str2));
+ fetch_from_buf(str2, 256, buf);
+ test_memeq(str, str2, 256);
+ }
+ test_eq(buf_datalen(buf), 0);
+ buf_free(buf);
+ buf = NULL;
+
+ /* Okay, now make sure growing can work. */
+ buf = buf_new_with_capacity(16);
+ //test_eq(buf_capacity(buf), 16);
+ write_to_buf(str+1, 255, buf);
+ //test_eq(buf_capacity(buf), 256);
+ fetch_from_buf(str2, 254, buf);
+ test_memeq(str+1, str2, 254);
+ //test_eq(buf_capacity(buf), 256);
+ assert_buf_ok(buf);
+ write_to_buf(str, 32, buf);
+ //test_eq(buf_capacity(buf), 256);
+ assert_buf_ok(buf);
+ write_to_buf(str, 256, buf);
+ assert_buf_ok(buf);
+ //test_eq(buf_capacity(buf), 512);
+ test_eq(buf_datalen(buf), 33+256);
+ fetch_from_buf(str2, 33, buf);
+ test_eq(*str2, str[255]);
+
+ test_memeq(str2+1, str, 32);
+ //test_eq(buf_capacity(buf), 512);
+ test_eq(buf_datalen(buf), 256);
+ fetch_from_buf(str2, 256, buf);
+ test_memeq(str, str2, 256);
+
+ /* now try shrinking: case 1. */
+ buf_free(buf);
+ buf = buf_new_with_capacity(33668);
+ for (j=0;j<67;++j) {
+ write_to_buf(str,255, buf);
+ }
+ //test_eq(buf_capacity(buf), 33668);
+ test_eq(buf_datalen(buf), 17085);
+ for (j=0; j < 40; ++j) {
+ fetch_from_buf(str2, 255,buf);
+ test_memeq(str2, str, 255);
+ }
+
+ /* now try shrinking: case 2. */
+ buf_free(buf);
+ buf = buf_new_with_capacity(33668);
+ for (j=0;j<67;++j) {
+ write_to_buf(str,255, buf);
+ }
+ for (j=0; j < 20; ++j) {
+ fetch_from_buf(str2, 255,buf);
+ test_memeq(str2, str, 255);
+ }
+ for (j=0;j<80;++j) {
+ write_to_buf(str,255, buf);
+ }
+ //test_eq(buf_capacity(buf),33668);
+ for (j=0; j < 120; ++j) {
+ fetch_from_buf(str2, 255,buf);
+ test_memeq(str2, str, 255);
+ }
+
+ /* Move from buf to buf. */
+ buf_free(buf);
+ buf = buf_new_with_capacity(4096);
+ buf2 = buf_new_with_capacity(4096);
+ for (j=0;j<100;++j)
+ write_to_buf(str, 255, buf);
+ test_eq(buf_datalen(buf), 25500);
+ for (j=0;j<100;++j) {
+ r = 10;
+ move_buf_to_buf(buf2, buf, &r);
+ test_eq(r, 0);
+ }
+ test_eq(buf_datalen(buf), 24500);
+ test_eq(buf_datalen(buf2), 1000);
+ for (j=0;j<3;++j) {
+ fetch_from_buf(str2, 255, buf2);
+ test_memeq(str2, str, 255);
+ }
+ r = 8192; /*big move*/
+ move_buf_to_buf(buf2, buf, &r);
+ test_eq(r, 0);
+ r = 30000; /* incomplete move */
+ move_buf_to_buf(buf2, buf, &r);
+ test_eq(r, 13692);
+ for (j=0;j<97;++j) {
+ fetch_from_buf(str2, 255, buf2);
+ test_memeq(str2, str, 255);
+ }
+ buf_free(buf);
+ buf_free(buf2);
+ buf = buf2 = NULL;
+
+ buf = buf_new_with_capacity(5);
+ cp = "Testing. This is a moderately long Testing string.";
+ for (j = 0; cp[j]; j++)
+ write_to_buf(cp+j, 1, buf);
+ test_eq(0, buf_find_string_offset(buf, "Testing", 7));
+ test_eq(1, buf_find_string_offset(buf, "esting", 6));
+ test_eq(1, buf_find_string_offset(buf, "est", 3));
+ test_eq(39, buf_find_string_offset(buf, "ing str", 7));
+ test_eq(35, buf_find_string_offset(buf, "Testing str", 11));
+ test_eq(32, buf_find_string_offset(buf, "ng ", 3));
+ test_eq(43, buf_find_string_offset(buf, "string.", 7));
+ test_eq(-1, buf_find_string_offset(buf, "shrdlu", 6));
+ test_eq(-1, buf_find_string_offset(buf, "Testing thing", 13));
+ test_eq(-1, buf_find_string_offset(buf, "ngx", 3));
+ buf_free(buf);
+ buf = NULL;
+
+ /* Try adding a string too long for any freelist. */
+ {
+ char *cp = tor_malloc_zero(65536);
+ buf = buf_new();
+ write_to_buf(cp, 65536, buf);
+ tor_free(cp);
+
+ tt_int_op(buf_datalen(buf), ==, 65536);
+ buf_free(buf);
+ buf = NULL;
+ }
+
+ done:
+ if (buf)
+ buf_free(buf);
+ if (buf2)
+ buf_free(buf2);
+}
+static void
+test_buffer_copy(void *arg)
+{
+ generic_buffer_t *buf=NULL, *buf2=NULL;
+ const char *s;
+ size_t len;
+ char b[256];
+ int i;
+ (void)arg;
+
+ buf = generic_buffer_new();
+ tt_assert(buf);
+
+ /* Copy an empty buffer. */
+ tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
+ tt_assert(buf2);
+ tt_int_op(0, ==, generic_buffer_len(buf2));
+
+ /* Now try with a short buffer. */
+ s = "And now comes an act of enormous enormance!";
+ len = strlen(s);
+ generic_buffer_add(buf, s, len);
+ tt_int_op(len, ==, generic_buffer_len(buf));
+ /* Add junk to buf2 so we can test replacing.*/
+ generic_buffer_add(buf2, "BLARG", 5);
+ tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
+ tt_int_op(len, ==, generic_buffer_len(buf2));
+ generic_buffer_get(buf2, b, len);
+ test_mem_op(b, ==, s, len);
+ /* Now free buf2 and retry so we can test allocating */
+ generic_buffer_free(buf2);
+ buf2 = NULL;
+ tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
+ tt_int_op(len, ==, generic_buffer_len(buf2));
+ generic_buffer_get(buf2, b, len);
+ test_mem_op(b, ==, s, len);
+ /* Clear buf for next test */
+ generic_buffer_get(buf, b, len);
+ tt_int_op(generic_buffer_len(buf),==,0);
+
+ /* Okay, now let's try a bigger buffer. */
+ s = "Quis autem vel eum iure reprehenderit qui in ea voluptate velit "
+ "esse quam nihil molestiae consequatur, vel illum qui dolorem eum "
+ "fugiat quo voluptas nulla pariatur?";
+ len = strlen(s);
+ for (i = 0; i < 256; ++i) {
+ b[0]=i;
+ generic_buffer_add(buf, b, 1);
+ generic_buffer_add(buf, s, len);
+ }
+ tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
+ tt_int_op(generic_buffer_len(buf2), ==, generic_buffer_len(buf));
+ for (i = 0; i < 256; ++i) {
+ generic_buffer_get(buf2, b, len+1);
+ tt_int_op((unsigned char)b[0],==,i);
+ test_mem_op(b+1, ==, s, len);
+ }
+
+ done:
+ if (buf)
+ generic_buffer_free(buf);
+ if (buf2)
+ generic_buffer_free(buf2);
+}
+
+static void
+test_buffer_ext_or_cmd(void *arg)
+{
+ ext_or_cmd_t *cmd = NULL;
+ generic_buffer_t *buf = generic_buffer_new();
+ char *tmp = NULL;
+ (void) arg;
+
+ /* Empty -- should give "not there. */
+ tt_int_op(0, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+ tt_ptr_op(NULL, ==, cmd);
+
+ /* Three bytes: shouldn't work. */
+ generic_buffer_add(buf, "\x00\x20\x00", 3);
+ tt_int_op(0, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+ tt_ptr_op(NULL, ==, cmd);
+ tt_int_op(3, ==, generic_buffer_len(buf));
+
+ /* 0020 0000: That's a nil command. It should work. */
+ generic_buffer_add(buf, "\x00", 1);
+ tt_int_op(1, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+ tt_ptr_op(NULL, !=, cmd);
+ tt_int_op(0x20, ==, cmd->cmd);
+ tt_int_op(0, ==, cmd->len);
+ tt_int_op(0, ==, generic_buffer_len(buf));
+ ext_or_cmd_free(cmd);
+ cmd = NULL;
+
+ /* Now try a length-6 command with one byte missing. */
+ generic_buffer_add(buf, "\x10\x21\x00\x06""abcde", 9);
+ tt_int_op(0, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+ tt_ptr_op(NULL, ==, cmd);
+ generic_buffer_add(buf, "f", 1);
+ tt_int_op(1, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+ tt_ptr_op(NULL, !=, cmd);
+ tt_int_op(0x1021, ==, cmd->cmd);
+ tt_int_op(6, ==, cmd->len);
+ test_mem_op("abcdef", ==, cmd->body, 6);
+ tt_int_op(0, ==, generic_buffer_len(buf));
+ ext_or_cmd_free(cmd);
+ cmd = NULL;
+
+ /* Now try a length-10 command with 4 extra bytes. */
+ generic_buffer_add(buf, "\xff\xff\x00\x0a"
+ "loremipsum\x10\x00\xff\xff", 18);
+ tt_int_op(1, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+ tt_ptr_op(NULL, !=, cmd);
+ tt_int_op(0xffff, ==, cmd->cmd);
+ tt_int_op(10, ==, cmd->len);
+ test_mem_op("loremipsum", ==, cmd->body, 10);
+ tt_int_op(4, ==, generic_buffer_len(buf));
+ ext_or_cmd_free(cmd);
+ cmd = NULL;
+
+ /* Finally, let's try a maximum-length command. We already have the header
+ * waiting. */
+ tt_int_op(0, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+ tmp = tor_malloc_zero(65535);
+ generic_buffer_add(buf, tmp, 65535);
+ tt_int_op(1, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+ tt_ptr_op(NULL, !=, cmd);
+ tt_int_op(0x1000, ==, cmd->cmd);
+ tt_int_op(0xffff, ==, cmd->len);
+ test_mem_op(tmp, ==, cmd->body, 65535);
+ tt_int_op(0, ==, generic_buffer_len(buf));
+ ext_or_cmd_free(cmd);
+ cmd = NULL;
+
+ done:
+ ext_or_cmd_free(cmd);
+ generic_buffer_free(buf);
+ tor_free(tmp);
+}
+
+struct testcase_t buffer_tests[] = {
+ { "basic", test_buffers_basic, 0, NULL, NULL },
+ { "copy", test_buffer_copy, 0, NULL, NULL },
+ { "ext_or_cmd", test_buffer_ext_or_cmd, 0, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_cell_queue.c b/src/test/test_cell_queue.c
new file mode 100644
index 000000000..1eac07310
--- /dev/null
+++ b/src/test/test_cell_queue.c
@@ -0,0 +1,148 @@
+/* Copyright (c) 2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define CIRCUITLIST_PRIVATE
+#define RELAY_PRIVATE
+#include "or.h"
+#include "circuitlist.h"
+#include "relay.h"
+#include "test.h"
+
+static void
+test_cq_manip(void *arg)
+{
+ packed_cell_t *pc1=NULL, *pc2=NULL, *pc3=NULL, *pc4=NULL, *pc_tmp=NULL;
+ cell_queue_t cq;
+ cell_t cell;
+ (void) arg;
+
+ init_cell_pool();
+ cell_queue_init(&cq);
+ tt_int_op(cq.n, ==, 0);
+
+ pc1 = packed_cell_new();
+ pc2 = packed_cell_new();
+ pc3 = packed_cell_new();
+ pc4 = packed_cell_new();
+ tt_assert(pc1 && pc2 && pc3 && pc4);
+
+ tt_ptr_op(NULL, ==, cell_queue_pop(&cq));
+
+ /* Add and remove a singleton. */
+ cell_queue_append(&cq, pc1);
+ tt_int_op(cq.n, ==, 1);
+ tt_ptr_op(pc1, ==, cell_queue_pop(&cq));
+ tt_int_op(cq.n, ==, 0);
+
+ /* Add and remove four items */
+ cell_queue_append(&cq, pc4);
+ cell_queue_append(&cq, pc3);
+ cell_queue_append(&cq, pc2);
+ cell_queue_append(&cq, pc1);
+ tt_int_op(cq.n, ==, 4);
+ tt_ptr_op(pc4, ==, cell_queue_pop(&cq));
+ tt_ptr_op(pc3, ==, cell_queue_pop(&cq));
+ tt_ptr_op(pc2, ==, cell_queue_pop(&cq));
+ tt_ptr_op(pc1, ==, cell_queue_pop(&cq));
+ tt_int_op(cq.n, ==, 0);
+ tt_ptr_op(NULL, ==, cell_queue_pop(&cq));
+
+ /* Try a packed copy (wide, then narrow, which is a bit of a cheat, since a
+ * real cell queue has only one type.) */
+ memset(&cell, 0, sizeof(cell));
+ cell.circ_id = 0x12345678;
+ cell.command = 10;
+ strlcpy((char*)cell.payload, "Lorax ipsum gruvvulus thneed amet, snergelly "
+ "once-ler lerkim, sed do barbaloot tempor gluppitus ut labore et "
+ "truffula magna aliqua.",
+ sizeof(cell.payload));
+ cell_queue_append_packed_copy(NULL /*circ*/, &cq, 0 /*exitward*/, &cell,
+ 1 /*wide*/, 0 /*stats*/);
+ cell.circ_id = 0x2013;
+ cell_queue_append_packed_copy(NULL /*circ*/, &cq, 0 /*exitward*/, &cell,
+ 0 /*wide*/, 0 /*stats*/);
+ tt_int_op(cq.n, ==, 2);
+
+ pc_tmp = cell_queue_pop(&cq);
+ tt_int_op(cq.n, ==, 1);
+ tt_ptr_op(pc_tmp, !=, NULL);
+ test_mem_op(pc_tmp->body, ==, "\x12\x34\x56\x78\x0a", 5);
+ test_mem_op(pc_tmp->body+5, ==, cell.payload, sizeof(cell.payload));
+ packed_cell_free(pc_tmp);
+
+ pc_tmp = cell_queue_pop(&cq);
+ tt_int_op(cq.n, ==, 0);
+ tt_ptr_op(pc_tmp, !=, NULL);
+ test_mem_op(pc_tmp->body, ==, "\x20\x13\x0a", 3);
+ test_mem_op(pc_tmp->body+3, ==, cell.payload, sizeof(cell.payload));
+ packed_cell_free(pc_tmp);
+ pc_tmp = NULL;
+
+ tt_ptr_op(NULL, ==, cell_queue_pop(&cq));
+
+ /* Now make sure cell_queue_clear works. */
+ cell_queue_append(&cq, pc2);
+ cell_queue_append(&cq, pc1);
+ tt_int_op(cq.n, ==, 2);
+ cell_queue_clear(&cq);
+ pc2 = pc1 = NULL; /* prevent double-free */
+ tt_int_op(cq.n, ==, 0);
+
+ done:
+ packed_cell_free(pc1);
+ packed_cell_free(pc2);
+ packed_cell_free(pc3);
+ packed_cell_free(pc4);
+ packed_cell_free(pc_tmp);
+
+ cell_queue_clear(&cq);
+ free_cell_pool();
+}
+
+static void
+test_circuit_n_cells(void *arg)
+{
+ packed_cell_t *pc1=NULL, *pc2=NULL, *pc3=NULL, *pc4=NULL, *pc5=NULL;
+ origin_circuit_t *origin_c=NULL;
+ or_circuit_t *or_c=NULL;
+
+ (void)arg;
+
+ init_cell_pool();
+
+ pc1 = packed_cell_new();
+ pc2 = packed_cell_new();
+ pc3 = packed_cell_new();
+ pc4 = packed_cell_new();
+ pc5 = packed_cell_new();
+ tt_assert(pc1 && pc2 && pc3 && pc4 && pc5);
+
+ or_c = or_circuit_new(0, NULL);
+ origin_c = origin_circuit_new();
+ origin_c->base_.purpose = CIRCUIT_PURPOSE_C_GENERAL;
+
+ tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(or_c)), ==, 0);
+ cell_queue_append(&or_c->p_chan_cells, pc1);
+ tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(or_c)), ==, 1);
+ cell_queue_append(&or_c->base_.n_chan_cells, pc2);
+ cell_queue_append(&or_c->base_.n_chan_cells, pc3);
+ tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(or_c)), ==, 3);
+
+ tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(origin_c)), ==, 0);
+ cell_queue_append(&origin_c->base_.n_chan_cells, pc4);
+ cell_queue_append(&origin_c->base_.n_chan_cells, pc5);
+ tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(origin_c)), ==, 2);
+
+ done:
+ circuit_free(TO_CIRCUIT(or_c));
+ circuit_free(TO_CIRCUIT(origin_c));
+
+ free_cell_pool();
+}
+
+struct testcase_t cell_queue_tests[] = {
+ { "basic", test_cq_manip, TT_FORK, NULL, NULL, },
+ { "circ_n_cells", test_circuit_n_cells, TT_FORK, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_circuitlist.c b/src/test/test_circuitlist.c
new file mode 100644
index 000000000..720b40765
--- /dev/null
+++ b/src/test/test_circuitlist.c
@@ -0,0 +1,168 @@
+/* Copyright (c) 2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define TOR_CHANNEL_INTERNAL_
+#define CIRCUITLIST_PRIVATE
+#include "or.h"
+#include "channel.h"
+#include "circuitlist.h"
+#include "test.h"
+
+static channel_t *
+new_fake_channel(void)
+{
+ channel_t *chan = tor_malloc_zero(sizeof(channel_t));
+ channel_init(chan);
+ return chan;
+}
+
+static struct {
+ int ncalls;
+ void *cmux;
+ void *circ;
+ cell_direction_t dir;
+} cam;
+
+static void
+circuitmux_attach_mock(circuitmux_t *cmux, circuit_t *circ,
+ cell_direction_t dir)
+{
+ ++cam.ncalls;
+ cam.cmux = cmux;
+ cam.circ = circ;
+ cam.dir = dir;
+}
+
+static struct {
+ int ncalls;
+ void *cmux;
+ void *circ;
+} cdm;
+
+static void
+circuitmux_detach_mock(circuitmux_t *cmux, circuit_t *circ)
+{
+ ++cdm.ncalls;
+ cdm.cmux = cmux;
+ cdm.circ = circ;
+}
+
+#define GOT_CMUX_ATTACH(mux_, circ_, dir_) do { \
+ tt_int_op(cam.ncalls, ==, 1); \
+ tt_ptr_op(cam.cmux, ==, (mux_)); \
+ tt_ptr_op(cam.circ, ==, (circ_)); \
+ tt_ptr_op(cam.dir, ==, (dir_)); \
+ memset(&cam, 0, sizeof(cam)); \
+ } while (0)
+
+#define GOT_CMUX_DETACH(mux_, circ_) do { \
+ tt_int_op(cdm.ncalls, ==, 1); \
+ tt_ptr_op(cdm.cmux, ==, (mux_)); \
+ tt_ptr_op(cdm.circ, ==, (circ_)); \
+ memset(&cdm, 0, sizeof(cdm)); \
+ } while (0)
+
+static void
+test_clist_maps(void *arg)
+{
+ channel_t *ch1 = new_fake_channel();
+ channel_t *ch2 = new_fake_channel();
+ channel_t *ch3 = new_fake_channel();
+ or_circuit_t *or_c1=NULL, *or_c2=NULL;
+
+ (void) arg;
+
+ MOCK(circuitmux_attach_circuit, circuitmux_attach_mock);
+ MOCK(circuitmux_detach_circuit, circuitmux_detach_mock);
+ memset(&cam, 0, sizeof(cam));
+ memset(&cdm, 0, sizeof(cdm));
+
+ ch1->cmux = (void*)0x1001;
+ ch2->cmux = (void*)0x1002;
+ ch3->cmux = (void*)0x1003;
+
+ or_c1 = or_circuit_new(100, ch2);
+ tt_assert(or_c1);
+ GOT_CMUX_ATTACH(ch2->cmux, or_c1, CELL_DIRECTION_IN);
+ tt_int_op(or_c1->p_circ_id, ==, 100);
+ tt_ptr_op(or_c1->p_chan, ==, ch2);
+
+ or_c2 = or_circuit_new(100, ch1);
+ tt_assert(or_c2);
+ GOT_CMUX_ATTACH(ch1->cmux, or_c2, CELL_DIRECTION_IN);
+ tt_int_op(or_c2->p_circ_id, ==, 100);
+ tt_ptr_op(or_c2->p_chan, ==, ch1);
+
+ circuit_set_n_circid_chan(TO_CIRCUIT(or_c1), 200, ch1);
+ GOT_CMUX_ATTACH(ch1->cmux, or_c1, CELL_DIRECTION_OUT);
+
+ circuit_set_n_circid_chan(TO_CIRCUIT(or_c2), 200, ch2);
+ GOT_CMUX_ATTACH(ch2->cmux, or_c2, CELL_DIRECTION_OUT);
+
+ tt_ptr_op(circuit_get_by_circid_channel(200, ch1), ==, TO_CIRCUIT(or_c1));
+ tt_ptr_op(circuit_get_by_circid_channel(200, ch2), ==, TO_CIRCUIT(or_c2));
+ tt_ptr_op(circuit_get_by_circid_channel(100, ch2), ==, TO_CIRCUIT(or_c1));
+ /* Try the same thing again, to test the "fast" path. */
+ tt_ptr_op(circuit_get_by_circid_channel(100, ch2), ==, TO_CIRCUIT(or_c1));
+ tt_assert(circuit_id_in_use_on_channel(100, ch2));
+ tt_assert(! circuit_id_in_use_on_channel(101, ch2));
+
+ /* Try changing the circuitid and channel of that circuit. */
+ circuit_set_p_circid_chan(or_c1, 500, ch3);
+ GOT_CMUX_DETACH(ch2->cmux, TO_CIRCUIT(or_c1));
+ GOT_CMUX_ATTACH(ch3->cmux, TO_CIRCUIT(or_c1), CELL_DIRECTION_IN);
+ tt_ptr_op(circuit_get_by_circid_channel(100, ch2), ==, NULL);
+ tt_assert(! circuit_id_in_use_on_channel(100, ch2));
+ tt_ptr_op(circuit_get_by_circid_channel(500, ch3), ==, TO_CIRCUIT(or_c1));
+
+ /* Now let's see about destroy handling. */
+ tt_assert(! circuit_id_in_use_on_channel(205, ch2));
+ tt_assert(circuit_id_in_use_on_channel(200, ch2));
+ channel_note_destroy_pending(ch2, 200);
+ channel_note_destroy_pending(ch2, 205);
+ channel_note_destroy_pending(ch1, 100);
+ tt_assert(circuit_id_in_use_on_channel(205, ch2))
+ tt_assert(circuit_id_in_use_on_channel(200, ch2));
+ tt_assert(circuit_id_in_use_on_channel(100, ch1));
+
+ tt_assert(TO_CIRCUIT(or_c2)->n_delete_pending != 0);
+ tt_ptr_op(circuit_get_by_circid_channel(200, ch2), ==, TO_CIRCUIT(or_c2));
+ tt_ptr_op(circuit_get_by_circid_channel(100, ch1), ==, TO_CIRCUIT(or_c2));
+
+ /* Okay, now free ch2 and make sure that the circuit ID is STILL not
+ * usable, because we haven't declared the destroy to be nonpending */
+ tt_int_op(cdm.ncalls, ==, 0);
+ circuit_free(TO_CIRCUIT(or_c2));
+ or_c2 = NULL; /* prevent free */
+ tt_int_op(cdm.ncalls, ==, 2);
+ memset(&cdm, 0, sizeof(cdm));
+ tt_assert(circuit_id_in_use_on_channel(200, ch2));
+ tt_assert(circuit_id_in_use_on_channel(100, ch1));
+ tt_ptr_op(circuit_get_by_circid_channel(200, ch2), ==, NULL);
+ tt_ptr_op(circuit_get_by_circid_channel(100, ch1), ==, NULL);
+
+ /* Now say that the destroy is nonpending */
+ channel_note_destroy_not_pending(ch2, 200);
+ tt_ptr_op(circuit_get_by_circid_channel(200, ch2), ==, NULL);
+ channel_note_destroy_not_pending(ch1, 100);
+ tt_ptr_op(circuit_get_by_circid_channel(100, ch1), ==, NULL);
+ tt_assert(! circuit_id_in_use_on_channel(200, ch2));
+ tt_assert(! circuit_id_in_use_on_channel(100, ch1));
+
+ done:
+ tor_free(ch1);
+ tor_free(ch2);
+ tor_free(ch3);
+ if (or_c1)
+ circuit_free(TO_CIRCUIT(or_c1));
+ if (or_c2)
+ circuit_free(TO_CIRCUIT(or_c2));
+ UNMOCK(circuitmux_attach_circuit);
+ UNMOCK(circuitmux_detach_circuit);
+}
+
+struct testcase_t circuitlist_tests[] = {
+ { "maps", test_clist_maps, TT_FORK, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_circuitmux.c b/src/test/test_circuitmux.c
new file mode 100644
index 000000000..0f592001c
--- /dev/null
+++ b/src/test/test_circuitmux.c
@@ -0,0 +1,84 @@
+/* Copyright (c) 2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define TOR_CHANNEL_INTERNAL_
+#define CIRCUITMUX_PRIVATE
+#define RELAY_PRIVATE
+#include "or.h"
+#include "channel.h"
+#include "circuitmux.h"
+#include "relay.h"
+#include "test.h"
+
+/* XXXX duplicated function from test_circuitlist.c */
+static channel_t *
+new_fake_channel(void)
+{
+ channel_t *chan = tor_malloc_zero(sizeof(channel_t));
+ channel_init(chan);
+ return chan;
+}
+
+static int
+has_queued_writes(channel_t *c)
+{
+ (void) c;
+ return 1;
+}
+
+/** Test destroy cell queue with no interference from other queues. */
+static void
+test_cmux_destroy_cell_queue(void *arg)
+{
+ circuitmux_t *cmux = NULL;
+ channel_t *ch = NULL;
+ circuit_t *circ = NULL;
+ cell_queue_t *cq = NULL;
+ packed_cell_t *pc = NULL;
+
+ init_cell_pool();
+ (void) arg;
+
+ cmux = circuitmux_alloc();
+ tt_assert(cmux);
+ ch = new_fake_channel();
+ ch->has_queued_writes = has_queued_writes;
+ ch->wide_circ_ids = 1;
+
+ circ = circuitmux_get_first_active_circuit(cmux, &cq);
+ tt_assert(!circ);
+ tt_assert(!cq);
+
+ circuitmux_append_destroy_cell(ch, cmux, 100, 10);
+ circuitmux_append_destroy_cell(ch, cmux, 190, 6);
+ circuitmux_append_destroy_cell(ch, cmux, 30, 1);
+
+ tt_int_op(circuitmux_num_cells(cmux), ==, 3);
+
+ circ = circuitmux_get_first_active_circuit(cmux, &cq);
+ tt_assert(!circ);
+ tt_assert(cq);
+
+ tt_int_op(cq->n, ==, 3);
+
+ pc = cell_queue_pop(cq);
+ tt_assert(pc);
+ test_mem_op(pc->body, ==, "\x00\x00\x00\x64\x04\x0a\x00\x00\x00", 9);
+ packed_cell_free(pc);
+ pc = NULL;
+
+ tt_int_op(circuitmux_num_cells(cmux), ==, 2);
+
+ done:
+ circuitmux_free(cmux);
+ channel_free(ch);
+ packed_cell_free(pc);
+
+ free_cell_pool();
+}
+
+struct testcase_t circuitmux_tests[] = {
+ { "destroy_cell_queue", test_cmux_destroy_cell_queue, TT_FORK, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_cmdline_args.py b/src/test/test_cmdline_args.py
new file mode 100755
index 000000000..6d9cf44db
--- /dev/null
+++ b/src/test/test_cmdline_args.py
@@ -0,0 +1,273 @@
+#!/usr/bin/python
+
+import binascii
+import hashlib
+import os
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+import unittest
+
+TOR = "./src/or/tor"
+TOP_SRCDIR = "."
+
+if len(sys.argv) > 1:
+ TOR = sys.argv[1]
+ del sys.argv[1]
+
+if len(sys.argv) > 1:
+ TOP_SRCDIR = sys.argv[1]
+ del sys.argv[1]
+
+class UnexpectedSuccess(Exception):
+ pass
+
+class UnexpectedFailure(Exception):
+ pass
+
+def contents(fn):
+ f = open(fn)
+ try:
+ return f.read()
+ finally:
+ f.close()
+
+def run_tor(args, failure=False):
+ p = subprocess.Popen([TOR] + args, stdout=subprocess.PIPE)
+ output, _ = p.communicate()
+ result = p.poll()
+ if result and not failure:
+ raise UnexpectedFailure()
+ elif not result and failure:
+ raise UnexpectedSuccess()
+ return output
+
+def spaceify_fp(fp):
+ for i in xrange(0, len(fp), 4):
+ yield fp[i:i+4]
+
+def lines(s):
+ out = s.split("\n")
+ if out and out[-1] == '':
+ del out[-1]
+ return out
+
+def strip_log_junk(line):
+ m = re.match(r'([^\[]+\[[a-z]*\] *)(.*)', line)
+ if not m:
+ return ""+line
+ return m.group(2).strip()
+
+def randstring(entropy_bytes):
+ s = os.urandom(entropy_bytes)
+ return binascii.b2a_hex(s)
+
+def findLineContaining(lines, s):
+ for ln in lines:
+ if s in ln:
+ return True
+ return False
+
+class CmdlineTests(unittest.TestCase):
+
+ def test_version(self):
+ out = run_tor(["--version"])
+ self.failUnless(out.startswith("Tor version "))
+ self.assertEquals(len(lines(out)), 1)
+
+ def test_quiet(self):
+ out = run_tor(["--quiet", "--quumblebluffin", "1"], failure=True)
+ self.assertEquals(out, "")
+
+ def test_help(self):
+ out = run_tor(["--help"], failure=False)
+ out2 = run_tor(["-h"], failure=False)
+ self.assert_(out.startswith("Copyright (c) 2001"))
+ self.assert_(out.endswith(
+ "tor -f <torrc> [args]\n"
+ "See man page for options, or https://www.torproject.org/ for documentation.\n"))
+ self.assert_(out == out2)
+
+ def test_hush(self):
+ torrc = tempfile.NamedTemporaryFile(delete=False)
+ torrc.close()
+ try:
+ out = run_tor(["--hush", "-f", torrc.name,
+ "--quumblebluffin", "1"], failure=True)
+ finally:
+ os.unlink(torrc.name)
+ self.assertEquals(len(lines(out)), 2)
+ ln = [ strip_log_junk(l) for l in lines(out) ]
+ self.assertEquals(ln[0], "Failed to parse/validate config: Unknown option 'quumblebluffin'. Failing.")
+ self.assertEquals(ln[1], "Reading config failed--see warnings above.")
+
+ def test_missing_argument(self):
+ out = run_tor(["--hush", "--hash-password"], failure=True)
+ self.assertEquals(len(lines(out)), 2)
+ ln = [ strip_log_junk(l) for l in lines(out) ]
+ self.assertEquals(ln[0], "Command-line option '--hash-password' with no value. Failing.")
+
+ def test_hash_password(self):
+ out = run_tor(["--hash-password", "woodwose"])
+ result = lines(out)[-1]
+ self.assertEquals(result[:3], "16:")
+ self.assertEquals(len(result), 61)
+ r = binascii.a2b_hex(result[3:])
+ self.assertEquals(len(r), 29)
+
+ salt, how, hashed = r[:8], r[8], r[9:]
+ self.assertEquals(len(hashed), 20)
+
+ count = (16 + (ord(how) & 15)) << ((ord(how) >> 4) + 6)
+ stuff = salt + "woodwose"
+ repetitions = count // len(stuff) + 1
+ inp = stuff * repetitions
+ inp = inp[:count]
+
+ self.assertEquals(hashlib.sha1(inp).digest(), hashed)
+
+ def test_digests(self):
+ main_c = os.path.join(TOP_SRCDIR, "src", "or", "main.c")
+
+ if os.stat(TOR).st_mtime < os.stat(main_c).st_mtime:
+ self.skipTest(TOR+" not up to date")
+ out = run_tor(["--digests"])
+ main_line = [ l for l in lines(out) if l.endswith("/main.c") ]
+ digest, name = main_line[0].split()
+ actual = hashlib.sha1(open(main_c).read()).hexdigest()
+ self.assertEquals(digest, actual)
+
+ def test_dump_options(self):
+ default_torrc = tempfile.NamedTemporaryFile(delete=False)
+ torrc = tempfile.NamedTemporaryFile(delete=False)
+ torrc.write("SocksPort 9999")
+ torrc.close()
+ default_torrc.write("SafeLogging 0")
+ default_torrc.close()
+ out_sh = out_nb = out_fl = None
+ opts = [ "-f", torrc.name,
+ "--defaults-torrc", default_torrc.name ]
+ try:
+ out_sh = run_tor(["--dump-config", "short"]+opts)
+ out_nb = run_tor(["--dump-config", "non-builtin"]+opts)
+ out_fl = run_tor(["--dump-config", "full"]+opts)
+ out_nr = run_tor(["--dump-config", "bliznert"]+opts,
+ failure=True)
+
+ out_verif = run_tor(["--verify-config"]+opts)
+ finally:
+ os.unlink(torrc.name)
+ os.unlink(default_torrc.name)
+
+ self.assertEquals(len(lines(out_sh)), 2)
+ self.assert_(lines(out_sh)[0].startswith("DataDirectory "))
+ self.assertEquals(lines(out_sh)[1:],
+ [ "SocksPort 9999" ])
+
+ self.assertEquals(len(lines(out_nb)), 2)
+ self.assertEquals(lines(out_nb),
+ [ "SafeLogging 0",
+ "SocksPort 9999" ])
+
+ out_fl = lines(out_fl)
+ self.assert_(len(out_fl) > 100)
+ self.assert_("SocksPort 9999" in out_fl)
+ self.assert_("SafeLogging 0" in out_fl)
+ self.assert_("ClientOnly 0" in out_fl)
+
+ self.assert_(out_verif.endswith("Configuration was valid\n"))
+
+ def test_list_fingerprint(self):
+ tmpdir = tempfile.mkdtemp(prefix='ttca_')
+ torrc = tempfile.NamedTemporaryFile(delete=False)
+ torrc.write("ORPort 9999\n")
+ torrc.write("DataDirectory %s\n"%tmpdir)
+ torrc.write("Nickname tippi")
+ torrc.close()
+ opts = ["-f", torrc.name]
+ try:
+ out = run_tor(["--list-fingerprint"]+opts)
+ fp = contents(os.path.join(tmpdir, "fingerprint"))
+ finally:
+ os.unlink(torrc.name)
+ shutil.rmtree(tmpdir)
+
+ out = lines(out)
+ lastlog = strip_log_junk(out[-2])
+ lastline = out[-1]
+ fp = fp.strip()
+ nn_fp = fp.split()[0]
+ space_fp = " ".join(spaceify_fp(fp.split()[1]))
+ self.assertEquals(lastlog,
+ "Your Tor server's identity key fingerprint is '%s'"%fp)
+ self.assertEquals(lastline, "tippi %s"%space_fp)
+ self.assertEquals(nn_fp, "tippi")
+
+ def test_list_options(self):
+ out = lines(run_tor(["--list-torrc-options"]))
+ self.assert_(len(out)>100)
+ self.assert_(out[0] <= 'AccountingMax')
+ self.assert_("UseBridges" in out)
+ self.assert_("SocksPort" in out)
+
+ def test_cmdline_args(self):
+ default_torrc = tempfile.NamedTemporaryFile(delete=False)
+ torrc = tempfile.NamedTemporaryFile(delete=False)
+ torrc.write("SocksPort 9999\n")
+ torrc.write("SocksPort 9998\n")
+ torrc.write("ORPort 9000\n")
+ torrc.write("ORPort 9001\n")
+ torrc.write("Nickname eleventeen\n")
+ torrc.write("ControlPort 9500\n")
+ torrc.close()
+ default_torrc.write("")
+ default_torrc.close()
+ out_sh = out_nb = out_fl = None
+ opts = [ "-f", torrc.name,
+ "--defaults-torrc", default_torrc.name,
+ "--dump-config", "short" ]
+ try:
+ out_1 = run_tor(opts)
+ out_2 = run_tor(opts+["+ORPort", "9003",
+ "SocksPort", "9090",
+ "/ControlPort",
+ "/TransPort",
+ "+ExtORPort", "9005"])
+ finally:
+ os.unlink(torrc.name)
+ os.unlink(default_torrc.name)
+
+ out_1 = [ l for l in lines(out_1) if not l.startswith("DataDir") ]
+ out_2 = [ l for l in lines(out_2) if not l.startswith("DataDir") ]
+
+ self.assertEquals(out_1,
+ ["ControlPort 9500",
+ "Nickname eleventeen",
+ "ORPort 9000",
+ "ORPort 9001",
+ "SocksPort 9999",
+ "SocksPort 9998"])
+ self.assertEquals(out_2,
+ ["ExtORPort 9005",
+ "Nickname eleventeen",
+ "ORPort 9000",
+ "ORPort 9001",
+ "ORPort 9003",
+ "SocksPort 9090"])
+
+ def test_missing_torrc(self):
+ fname = "nonexistent_file_"+randstring(8)
+ out = run_tor(["-f", fname, "--verify-config"], failure=True)
+ ln = [ strip_log_junk(l) for l in lines(out) ]
+ self.assert_("Unable to open configuration file" in ln[-2])
+ self.assert_("Reading config failed" in ln[-1])
+
+ out = run_tor(["-f", fname, "--verify-config", "--ignore-missing-torrc"])
+ ln = [ strip_log_junk(l) for l in lines(out) ]
+ self.assert_(findLineContaining(ln, ", using reasonable defaults"))
+ self.assert_("Configuration was valid" in ln[-1])
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/test/test_config.c b/src/test/test_config.c
index e20fe7329..6a285db6e 100644
--- a/src/test/test_config.c
+++ b/src/test/test_config.c
@@ -4,12 +4,16 @@
/* See LICENSE for licensing information */
#include "orconfig.h"
+
+#define CONFIG_PRIVATE
#include "or.h"
#include "addressmap.h"
#include "config.h"
#include "confparse.h"
#include "connection_edge.h"
#include "test.h"
+#include "util.h"
+#include "address.h"
static void
test_config_addressmap(void *arg)
@@ -169,11 +173,391 @@ test_config_addressmap(void *arg)
;
}
+static int
+is_private_dir(const char* path)
+{
+ struct stat st;
+ int r = stat(path, &st);
+ if (r) {
+ return 0;
+ }
+#if !defined (_WIN32) || defined (WINCE)
+ if ((st.st_mode & (S_IFDIR | 0777)) != (S_IFDIR | 0700)) {
+ return 0;
+ }
+#endif
+ return 1;
+}
+
+static void
+test_config_check_or_create_data_subdir(void *arg)
+{
+ or_options_t *options = get_options_mutable();
+ char *datadir = options->DataDirectory = tor_strdup(get_fname("datadir-0"));
+ const char *subdir = "test_stats";
+ char *subpath = get_datadir_fname(subdir);
+ struct stat st;
+ int r;
+#if !defined (_WIN32) || defined (WINCE)
+ unsigned group_permission;
+#endif
+ (void)arg;
+
+#if defined (_WIN32) && !defined (WINCE)
+ tt_int_op(mkdir(options->DataDirectory), ==, 0);
+#else
+ tt_int_op(mkdir(options->DataDirectory, 0700), ==, 0);
+#endif
+
+ r = stat(subpath, &st);
+
+ // The subdirectory shouldn't exist yet,
+ // but should be created by the call to check_or_create_data_subdir.
+ test_assert(r && (errno == ENOENT));
+ test_assert(!check_or_create_data_subdir(subdir));
+ test_assert(is_private_dir(subpath));
+
+ // The check should return 0, if the directory already exists
+ // and is private to the user.
+ test_assert(!check_or_create_data_subdir(subdir));
+
+#if !defined (_WIN32) || defined (WINCE)
+ group_permission = st.st_mode | 0070;
+ r = chmod(subpath, group_permission);
+
+ if (r) {
+ test_fail_msg("Changing permissions for the subdirectory failed.");
+ }
+
+ // If the directory exists, but its mode is too permissive
+ // a call to check_or_create_data_subdir should reset the mode.
+ test_assert(!is_private_dir(subpath));
+ test_assert(!check_or_create_data_subdir(subdir));
+ test_assert(is_private_dir(subpath));
+#endif
+
+ done:
+ rmdir(subpath);
+ tor_free(datadir);
+ tor_free(subpath);
+}
+
+static void
+test_config_write_to_data_subdir(void *arg)
+{
+ or_options_t* options = get_options_mutable();
+ char *datadir = options->DataDirectory = tor_strdup(get_fname("datadir-1"));
+ const char* subdir = "test_stats";
+ const char* fname = "test_file";
+ const char* str =
+ "Lorem ipsum dolor sit amet, consetetur sadipscing\n"
+ "elitr, sed diam nonumy eirmod\n"
+ "tempor invidunt ut labore et dolore magna aliquyam\n"
+ "erat, sed diam voluptua.\n"
+ "At vero eos et accusam et justo duo dolores et ea\n"
+ "rebum. Stet clita kasd gubergren,\n"
+ "no sea takimata sanctus est Lorem ipsum dolor sit amet.\n"
+ "Lorem ipsum dolor sit amet,\n"
+ "consetetur sadipscing elitr, sed diam nonumy eirmod\n"
+ "tempor invidunt ut labore et dolore\n"
+ "magna aliquyam erat, sed diam voluptua. At vero eos et\n"
+ "accusam et justo duo dolores et\n"
+ "ea rebum. Stet clita kasd gubergren, no sea takimata\n"
+ "sanctus est Lorem ipsum dolor sit amet.";
+ char* filepath = get_datadir_fname2(subdir, fname);
+ (void)arg;
+
+#if defined (_WIN32) && !defined (WINCE)
+ tt_int_op(mkdir(options->DataDirectory), ==, 0);
+#else
+ tt_int_op(mkdir(options->DataDirectory, 0700), ==, 0);
+#endif
+
+ // Write attempt shoudl fail, if subdirectory doesn't exist.
+ test_assert(write_to_data_subdir(subdir, fname, str, NULL));
+ test_assert(! check_or_create_data_subdir(subdir));
+
+ // Content of file after write attempt should be
+ // equal to the original string.
+ test_assert(!write_to_data_subdir(subdir, fname, str, NULL));
+ test_streq(read_file_to_str(filepath, 0, NULL), str);
+
+ // A second write operation should overwrite the old content.
+ test_assert(!write_to_data_subdir(subdir, fname, str, NULL));
+ test_streq(read_file_to_str(filepath, 0, NULL), str);
+
+ done:
+ (void) unlink(filepath);
+ rmdir(options->DataDirectory);
+ tor_free(datadir);
+ tor_free(filepath);
+}
+
+/* Test helper function: Make sure that a bridge line gets parsed
+ * properly. Also make sure that the resulting bridge_line_t structure
+ * has its fields set correctly. */
+static void
+good_bridge_line_test(const char *string, const char *test_addrport,
+ const char *test_digest, const char *test_transport,
+ const smartlist_t *test_socks_args)
+{
+ char *tmp = NULL;
+ bridge_line_t *bridge_line = parse_bridge_line(string);
+ test_assert(bridge_line);
+
+ /* test addrport */
+ tmp = tor_strdup(fmt_addrport(&bridge_line->addr, bridge_line->port));
+ test_streq(test_addrport, tmp);
+ tor_free(tmp);
+
+ /* If we were asked to validate a digest, but we did not get a
+ digest after parsing, we failed. */
+ if (test_digest && tor_digest_is_zero(bridge_line->digest))
+ test_assert(0);
+
+ /* If we were not asked to validate a digest, and we got a digest
+ after parsing, we failed again. */
+ if (!test_digest && !tor_digest_is_zero(bridge_line->digest))
+ test_assert(0);
+
+ /* If we were asked to validate a digest, and we got a digest after
+ parsing, make sure it's correct. */
+ if (test_digest) {
+ tmp = tor_strdup(hex_str(bridge_line->digest, DIGEST_LEN));
+ tor_strlower(tmp);
+ test_streq(test_digest, tmp);
+ tor_free(tmp);
+ }
+
+ /* If we were asked to validate a transport name, make sure tha it
+ matches with the transport name that was parsed. */
+ if (test_transport && !bridge_line->transport_name)
+ test_assert(0);
+ if (!test_transport && bridge_line->transport_name)
+ test_assert(0);
+ if (test_transport)
+ test_streq(test_transport, bridge_line->transport_name);
+
+ /* Validate the SOCKS argument smartlist. */
+ if (test_socks_args && !bridge_line->socks_args)
+ test_assert(0);
+ if (!test_socks_args && bridge_line->socks_args)
+ test_assert(0);
+ if (test_socks_args)
+ test_assert(smartlist_strings_eq(test_socks_args,
+ bridge_line->socks_args));
+
+ done:
+ tor_free(tmp);
+ bridge_line_free(bridge_line);
+}
+
+/* Test helper function: Make sure that a bridge line is
+ * unparseable. */
+static void
+bad_bridge_line_test(const char *string)
+{
+ bridge_line_t *bridge_line = parse_bridge_line(string);
+ test_assert(!bridge_line);
+
+ done:
+ bridge_line_free(bridge_line);
+}
+
+static void
+test_config_parse_bridge_line(void *arg)
+{
+ (void) arg;
+ good_bridge_line_test("192.0.2.1:4123",
+ "192.0.2.1:4123", NULL, NULL, NULL);
+
+ good_bridge_line_test("192.0.2.1",
+ "192.0.2.1:443", NULL, NULL, NULL);
+
+ good_bridge_line_test("transport [::1]",
+ "[::1]:443", NULL, "transport", NULL);
+
+ good_bridge_line_test("transport 192.0.2.1:12 "
+ "4352e58420e68f5e40bf7c74faddccd9d1349413",
+ "192.0.2.1:12",
+ "4352e58420e68f5e40bf7c74faddccd9d1349413",
+ "transport", NULL);
+
+ {
+ smartlist_t *sl_tmp = smartlist_new();
+ smartlist_add_asprintf(sl_tmp, "twoandtwo=five");
+
+ good_bridge_line_test("transport 192.0.2.1:12 "
+ "4352e58420e68f5e40bf7c74faddccd9d1349413 twoandtwo=five",
+ "192.0.2.1:12", "4352e58420e68f5e40bf7c74faddccd9d1349413",
+ "transport", sl_tmp);
+
+ SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s));
+ smartlist_free(sl_tmp);
+ }
+
+ {
+ smartlist_t *sl_tmp = smartlist_new();
+ smartlist_add_asprintf(sl_tmp, "twoandtwo=five");
+ smartlist_add_asprintf(sl_tmp, "z=z");
+
+ good_bridge_line_test("transport 192.0.2.1:12 twoandtwo=five z=z",
+ "192.0.2.1:12", NULL, "transport", sl_tmp);
+
+ SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s));
+ smartlist_free(sl_tmp);
+ }
+
+ good_bridge_line_test("192.0.2.1:1231 "
+ "4352e58420e68f5e40bf7c74faddccd9d1349413",
+ "192.0.2.1:1231",
+ "4352e58420e68f5e40bf7c74faddccd9d1349413",
+ NULL, NULL);
+
+ /* Empty line */
+ bad_bridge_line_test("");
+ /* bad transport name */
+ bad_bridge_line_test("tr$n_sp0r7 190.20.2.2");
+ /* weird ip address */
+ bad_bridge_line_test("a.b.c.d");
+ /* invalid fpr */
+ bad_bridge_line_test("2.2.2.2:1231 4352e58420e68f5e40bf7c74faddccd9d1349");
+ /* no k=v in the end */
+ bad_bridge_line_test("obfs2 2.2.2.2:1231 "
+ "4352e58420e68f5e40bf7c74faddccd9d1349413 what");
+ /* no addrport */
+ bad_bridge_line_test("asdw");
+ /* huge k=v value that can't fit in SOCKS fields */
+ bad_bridge_line_test(
+ "obfs2 2.2.2.2:1231 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aa=b");
+}
+
+static void
+test_config_parse_transport_options_line(void *arg)
+{
+ smartlist_t *options_sl = NULL, *sl_tmp = NULL;
+
+ (void) arg;
+
+ { /* too small line */
+ options_sl = get_options_from_transport_options_line("valley", NULL);
+ test_assert(!options_sl);
+ }
+
+ { /* no k=v values */
+ options_sl = get_options_from_transport_options_line("hit it!", NULL);
+ test_assert(!options_sl);
+ }
+
+ { /* correct line, but wrong transport specified */
+ options_sl =
+ get_options_from_transport_options_line("trebuchet k=v", "rook");
+ test_assert(!options_sl);
+ }
+
+ { /* correct -- no transport specified */
+ sl_tmp = smartlist_new();
+ smartlist_add_asprintf(sl_tmp, "ladi=dadi");
+ smartlist_add_asprintf(sl_tmp, "weliketo=party");
+
+ options_sl =
+ get_options_from_transport_options_line("rook ladi=dadi weliketo=party",
+ NULL);
+ test_assert(options_sl);
+ test_assert(smartlist_strings_eq(options_sl, sl_tmp));
+
+ SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s));
+ smartlist_free(sl_tmp);
+ sl_tmp = NULL;
+ SMARTLIST_FOREACH(options_sl, char *, s, tor_free(s));
+ smartlist_free(options_sl);
+ options_sl = NULL;
+ }
+
+ { /* correct -- correct transport specified */
+ sl_tmp = smartlist_new();
+ smartlist_add_asprintf(sl_tmp, "ladi=dadi");
+ smartlist_add_asprintf(sl_tmp, "weliketo=party");
+
+ options_sl =
+ get_options_from_transport_options_line("rook ladi=dadi weliketo=party",
+ "rook");
+ test_assert(options_sl);
+ test_assert(smartlist_strings_eq(options_sl, sl_tmp));
+ SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s));
+ smartlist_free(sl_tmp);
+ sl_tmp = NULL;
+ SMARTLIST_FOREACH(options_sl, char *, s, tor_free(s));
+ smartlist_free(options_sl);
+ options_sl = NULL;
+ }
+
+ done:
+ if (options_sl) {
+ SMARTLIST_FOREACH(options_sl, char *, s, tor_free(s));
+ smartlist_free(options_sl);
+ }
+ if (sl_tmp) {
+ SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s));
+ smartlist_free(sl_tmp);
+ }
+}
+
+// Tests if an options with MyFamily fingerprints missing '$' normalises
+// them correctly and also ensure it also works with multiple fingerprints
+static void
+test_config_fix_my_family(void *arg)
+{
+ char *err = NULL;
+ const char *family = "$1111111111111111111111111111111111111111, "
+ "1111111111111111111111111111111111111112, "
+ "$1111111111111111111111111111111111111113";
+
+ or_options_t* options = options_new();
+ or_options_t* defaults = options_new();
+ (void) arg;
+
+ options_init(options);
+ options_init(defaults);
+ options->MyFamily = tor_strdup(family);
+
+ options_validate(NULL, options, defaults, 0, &err) ;
+
+ if (err != NULL) {
+ TT_FAIL(("options_validate failed: %s", err));
+ }
+
+ test_streq(options->MyFamily, "$1111111111111111111111111111111111111111, "
+ "$1111111111111111111111111111111111111112, "
+ "$1111111111111111111111111111111111111113");
+
+ done:
+ if (err != NULL) {
+ tor_free(err);
+ }
+
+ or_options_free(options);
+ or_options_free(defaults);
+}
+
#define CONFIG_TEST(name, flags) \
{ #name, test_config_ ## name, flags, NULL, NULL }
struct testcase_t config_tests[] = {
CONFIG_TEST(addressmap, 0),
+ CONFIG_TEST(parse_bridge_line, 0),
+ CONFIG_TEST(parse_transport_options_line, 0),
+ CONFIG_TEST(check_or_create_data_subdir, TT_FORK),
+ CONFIG_TEST(write_to_data_subdir, TT_FORK),
+ CONFIG_TEST(fix_my_family, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_containers.c b/src/test/test_containers.c
index 005e102e2..067c4c190 100644
--- a/src/test/test_containers.c
+++ b/src/test/test_containers.c
@@ -469,6 +469,51 @@ test_container_smartlist_join(void)
tor_free(joined);
}
+static void
+test_container_smartlist_ints_eq(void *arg)
+{
+ smartlist_t *sl1 = NULL, *sl2 = NULL;
+ int x;
+ (void)arg;
+
+ tt_assert(smartlist_ints_eq(NULL, NULL));
+
+ sl1 = smartlist_new();
+ tt_assert(!smartlist_ints_eq(sl1, NULL));
+ tt_assert(!smartlist_ints_eq(NULL, sl1));
+
+ sl2 = smartlist_new();
+ tt_assert(smartlist_ints_eq(sl1, sl2));
+
+ x = 5;
+ smartlist_add(sl1, tor_memdup(&x, sizeof(int)));
+ smartlist_add(sl2, tor_memdup(&x, sizeof(int)));
+ x = 90;
+ smartlist_add(sl1, tor_memdup(&x, sizeof(int)));
+ smartlist_add(sl2, tor_memdup(&x, sizeof(int)));
+ tt_assert(smartlist_ints_eq(sl1, sl2));
+
+ x = -50;
+ smartlist_add(sl1, tor_memdup(&x, sizeof(int)));
+ tt_assert(! smartlist_ints_eq(sl1, sl2));
+ tt_assert(! smartlist_ints_eq(sl2, sl1));
+ smartlist_add(sl2, tor_memdup(&x, sizeof(int)));
+ tt_assert(smartlist_ints_eq(sl1, sl2));
+
+ *(int*)smartlist_get(sl1, 1) = 101010;
+ tt_assert(! smartlist_ints_eq(sl2, sl1));
+ *(int*)smartlist_get(sl2, 1) = 101010;
+ tt_assert(smartlist_ints_eq(sl1, sl2));
+
+ done:
+ if (sl1)
+ SMARTLIST_FOREACH(sl1, int *, ip, tor_free(ip));
+ if (sl2)
+ SMARTLIST_FOREACH(sl2, int *, ip, tor_free(ip));
+ smartlist_free(sl1);
+ smartlist_free(sl2);
+}
+
/** Run unit tests for bitarray code */
static void
test_container_bitarray(void)
@@ -784,7 +829,7 @@ test_container_order_functions(void)
}
static void
-test_di_map(void *arg)
+test_container_di_map(void *arg)
{
di_digest256_map_t *map = NULL;
const uint8_t key1[] = "In view of the fact that it was ";
@@ -856,12 +901,12 @@ test_container_fp_pair_map(void)
memset(fp6.second, 0x62, DIGEST_LEN);
v = fp_pair_map_set(map, &fp1, (void*)99);
- test_eq(v, NULL);
+ tt_ptr_op(v, ==, NULL);
test_assert(!fp_pair_map_isempty(map));
v = fp_pair_map_set(map, &fp2, (void*)101);
- test_eq(v, NULL);
+ tt_ptr_op(v, ==, NULL);
v = fp_pair_map_set(map, &fp1, (void*)100);
- test_eq(v, (void*)99);
+ tt_ptr_op(v, ==, (void*)99);
test_eq_ptr(fp_pair_map_get(map, &fp1), (void*)100);
test_eq_ptr(fp_pair_map_get(map, &fp2), (void*)101);
test_eq_ptr(fp_pair_map_get(map, &fp3), NULL);
@@ -912,18 +957,22 @@ test_container_fp_pair_map(void)
#define CONTAINER_LEGACY(name) \
{ #name, legacy_test_helper, 0, &legacy_setup, test_container_ ## name }
+#define CONTAINER(name, flags) \
+ { #name, test_container_ ## name, (flags), NULL, NULL }
+
struct testcase_t container_tests[] = {
CONTAINER_LEGACY(smartlist_basic),
CONTAINER_LEGACY(smartlist_strings),
CONTAINER_LEGACY(smartlist_overlap),
CONTAINER_LEGACY(smartlist_digests),
CONTAINER_LEGACY(smartlist_join),
+ CONTAINER(smartlist_ints_eq, 0),
CONTAINER_LEGACY(bitarray),
CONTAINER_LEGACY(digestset),
CONTAINER_LEGACY(strmap),
CONTAINER_LEGACY(pqueue),
CONTAINER_LEGACY(order_functions),
- { "di_map", test_di_map, 0, NULL, NULL },
+ CONTAINER(di_map, 0),
CONTAINER_LEGACY(fp_pair_map),
END_OF_TESTCASES
};
diff --git a/src/test/test_controller_events.c b/src/test/test_controller_events.c
new file mode 100644
index 000000000..3a9aeca2f
--- /dev/null
+++ b/src/test/test_controller_events.c
@@ -0,0 +1,298 @@
+/* Copyright (c) 2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define CONNECTION_PRIVATE
+#define TOR_CHANNEL_INTERNAL_
+#define CONTROL_PRIVATE
+#include "or.h"
+#include "channel.h"
+#include "channeltls.h"
+#include "connection.h"
+#include "control.h"
+#include "test.h"
+
+static void
+help_test_bucket_note_empty(uint32_t expected_msec_since_midnight,
+ int tokens_before, size_t tokens_removed,
+ uint32_t msec_since_epoch)
+{
+ uint32_t timestamp_var = 0;
+ struct timeval tvnow;
+ tvnow.tv_sec = msec_since_epoch / 1000;
+ tvnow.tv_usec = (msec_since_epoch % 1000) * 1000;
+ connection_buckets_note_empty_ts(&timestamp_var, tokens_before,
+ tokens_removed, &tvnow);
+ tt_int_op(expected_msec_since_midnight, ==, timestamp_var);
+
+ done:
+ ;
+}
+
+static void
+test_cntev_bucket_note_empty(void *arg)
+{
+ (void)arg;
+
+ /* Two cases with nothing to note, because bucket was empty before;
+ * 86442200 == 1970-01-02 00:00:42.200000 */
+ help_test_bucket_note_empty(0, 0, 0, 86442200);
+ help_test_bucket_note_empty(0, -100, 100, 86442200);
+
+ /* Nothing to note, because bucket has not been emptied. */
+ help_test_bucket_note_empty(0, 101, 100, 86442200);
+
+ /* Bucket was emptied, note 42200 msec since midnight. */
+ help_test_bucket_note_empty(42200, 101, 101, 86442200);
+ help_test_bucket_note_empty(42200, 101, 102, 86442200);
+}
+
+static void
+test_cntev_bucket_millis_empty(void *arg)
+{
+ struct timeval tvnow;
+ (void)arg;
+
+ /* 1970-01-02 00:00:42.200000 */
+ tvnow.tv_sec = 86400 + 42;
+ tvnow.tv_usec = 200000;
+
+ /* Bucket has not been refilled. */
+ tt_int_op(0, ==, bucket_millis_empty(0, 42120, 0, 100, &tvnow));
+ tt_int_op(0, ==, bucket_millis_empty(-10, 42120, -10, 100, &tvnow));
+
+ /* Bucket was not empty. */
+ tt_int_op(0, ==, bucket_millis_empty(10, 42120, 20, 100, &tvnow));
+
+ /* Bucket has been emptied 80 msec ago and has just been refilled. */
+ tt_int_op(80, ==, bucket_millis_empty(-20, 42120, -10, 100, &tvnow));
+ tt_int_op(80, ==, bucket_millis_empty(-10, 42120, 0, 100, &tvnow));
+ tt_int_op(80, ==, bucket_millis_empty(0, 42120, 10, 100, &tvnow));
+
+ /* Bucket has been emptied 180 msec ago, last refill was 100 msec ago
+ * which was insufficient to make it positive, so cap msec at 100. */
+ tt_int_op(100, ==, bucket_millis_empty(0, 42020, 1, 100, &tvnow));
+
+ /* 1970-01-02 00:00:00:050000 */
+ tvnow.tv_sec = 86400;
+ tvnow.tv_usec = 50000;
+
+ /* Last emptied 30 msec before midnight, tvnow is 50 msec after
+ * midnight, that's 80 msec in total. */
+ tt_int_op(80, ==, bucket_millis_empty(0, 86400000 - 30, 1, 100, &tvnow));
+
+ done:
+ ;
+}
+
+static void
+add_testing_cell_stats_entry(circuit_t *circ, uint8_t command,
+ unsigned int waiting_time,
+ unsigned int removed, unsigned int exitward)
+{
+ testing_cell_stats_entry_t *ent = tor_malloc_zero(
+ sizeof(testing_cell_stats_entry_t));
+ ent->command = command;
+ ent->waiting_time = waiting_time;
+ ent->removed = removed;
+ ent->exitward = exitward;
+ if (!circ->testing_cell_stats)
+ circ->testing_cell_stats = smartlist_new();
+ smartlist_add(circ->testing_cell_stats, ent);
+}
+
+static void
+test_cntev_sum_up_cell_stats(void *arg)
+{
+ or_circuit_t *or_circ;
+ circuit_t *circ;
+ cell_stats_t *cell_stats = NULL;
+ (void)arg;
+
+ /* This circuit is fake. */
+ or_circ = tor_malloc_zero(sizeof(or_circuit_t));
+ or_circ->base_.magic = OR_CIRCUIT_MAGIC;
+ or_circ->base_.purpose = CIRCUIT_PURPOSE_OR;
+ circ = TO_CIRCUIT(or_circ);
+
+ /* A single RELAY cell was added to the appward queue. */
+ cell_stats = tor_malloc_zero(sizeof(cell_stats_t));
+ add_testing_cell_stats_entry(circ, CELL_RELAY, 0, 0, 0);
+ sum_up_cell_stats_by_command(circ, cell_stats);
+ tt_int_op(1, ==, cell_stats->added_cells_appward[CELL_RELAY]);
+
+ /* A single RELAY cell was added to the exitward queue. */
+ add_testing_cell_stats_entry(circ, CELL_RELAY, 0, 0, 1);
+ sum_up_cell_stats_by_command(circ, cell_stats);
+ tt_int_op(1, ==, cell_stats->added_cells_exitward[CELL_RELAY]);
+
+ /* A single RELAY cell was removed from the appward queue where it spent
+ * 20 msec. */
+ add_testing_cell_stats_entry(circ, CELL_RELAY, 2, 1, 0);
+ sum_up_cell_stats_by_command(circ, cell_stats);
+ tt_int_op(20, ==, cell_stats->total_time_appward[CELL_RELAY]);
+ tt_int_op(1, ==, cell_stats->removed_cells_appward[CELL_RELAY]);
+
+ /* A single RELAY cell was removed from the exitward queue where it
+ * spent 30 msec. */
+ add_testing_cell_stats_entry(circ, CELL_RELAY, 3, 1, 1);
+ sum_up_cell_stats_by_command(circ, cell_stats);
+ tt_int_op(30, ==, cell_stats->total_time_exitward[CELL_RELAY]);
+ tt_int_op(1, ==, cell_stats->removed_cells_exitward[CELL_RELAY]);
+
+ done:
+ tor_free(cell_stats);
+ tor_free(or_circ);
+}
+
+static void
+test_cntev_append_cell_stats(void *arg)
+{
+ smartlist_t *event_parts;
+ const char *key = "Z";
+ uint64_t include_if_non_zero[CELL_COMMAND_MAX_ + 1],
+ number_to_include[CELL_COMMAND_MAX_ + 1];
+ (void)arg;
+
+ event_parts = smartlist_new();
+ memset(include_if_non_zero, 0,
+ (CELL_COMMAND_MAX_ + 1) * sizeof(uint64_t));
+ memset(number_to_include, 0,
+ (CELL_COMMAND_MAX_ + 1) * sizeof(uint64_t));
+
+ /* All array entries empty. */
+ append_cell_stats_by_command(event_parts, key,
+ include_if_non_zero,
+ number_to_include);
+ tt_int_op(0, ==, smartlist_len(event_parts));
+
+ /* There's a RELAY cell to include, but the corresponding field in
+ * include_if_non_zero is still zero. */
+ number_to_include[CELL_RELAY] = 1;
+ append_cell_stats_by_command(event_parts, key,
+ include_if_non_zero,
+ number_to_include);
+ tt_int_op(0, ==, smartlist_len(event_parts));
+
+ /* Now include single RELAY cell. */
+ include_if_non_zero[CELL_RELAY] = 2;
+ append_cell_stats_by_command(event_parts, key,
+ include_if_non_zero,
+ number_to_include);
+ tt_str_op("Z=relay:1", ==, smartlist_pop_last(event_parts));
+
+ /* Add four CREATE cells. */
+ include_if_non_zero[CELL_CREATE] = 3;
+ number_to_include[CELL_CREATE] = 4;
+ append_cell_stats_by_command(event_parts, key,
+ include_if_non_zero,
+ number_to_include);
+ tt_str_op("Z=create:4,relay:1", ==, smartlist_pop_last(event_parts));
+
+ done:
+ ;
+}
+
+static void
+test_cntev_format_cell_stats(void *arg)
+{
+ char *event_string = NULL;
+ origin_circuit_t *ocirc;
+ or_circuit_t *or_circ;
+ cell_stats_t *cell_stats = NULL;
+ channel_tls_t *n_chan, *p_chan;
+ (void)arg;
+
+ n_chan = tor_malloc_zero(sizeof(channel_tls_t));
+ n_chan->base_.global_identifier = 1;
+
+ ocirc = tor_malloc_zero(sizeof(origin_circuit_t));
+ ocirc->base_.magic = ORIGIN_CIRCUIT_MAGIC;
+ ocirc->base_.purpose = CIRCUIT_PURPOSE_C_GENERAL;
+ ocirc->global_identifier = 2;
+ ocirc->base_.n_circ_id = 3;
+ ocirc->base_.n_chan = &(n_chan->base_);
+
+ /* Origin circuit was completely idle. */
+ cell_stats = tor_malloc_zero(sizeof(cell_stats_t));
+ format_cell_stats(&event_string, TO_CIRCUIT(ocirc), cell_stats);
+ tt_str_op("ID=2 OutboundQueue=3 OutboundConn=1", ==, event_string);
+ tor_free(event_string);
+
+ /* Origin circuit had 4 RELAY cells added to its exitward queue. */
+ cell_stats->added_cells_exitward[CELL_RELAY] = 4;
+ format_cell_stats(&event_string, TO_CIRCUIT(ocirc), cell_stats);
+ tt_str_op("ID=2 OutboundQueue=3 OutboundConn=1 OutboundAdded=relay:4",
+ ==, event_string);
+ tor_free(event_string);
+
+ /* Origin circuit also had 5 CREATE2 cells added to its exitward
+ * queue. */
+ cell_stats->added_cells_exitward[CELL_CREATE2] = 5;
+ format_cell_stats(&event_string, TO_CIRCUIT(ocirc), cell_stats);
+ tt_str_op("ID=2 OutboundQueue=3 OutboundConn=1 OutboundAdded=relay:4,"
+ "create2:5", ==, event_string);
+ tor_free(event_string);
+
+ /* Origin circuit also had 7 RELAY cells removed from its exitward queue
+ * which together spent 6 msec in the queue. */
+ cell_stats->total_time_exitward[CELL_RELAY] = 6;
+ cell_stats->removed_cells_exitward[CELL_RELAY] = 7;
+ format_cell_stats(&event_string, TO_CIRCUIT(ocirc), cell_stats);
+ tt_str_op("ID=2 OutboundQueue=3 OutboundConn=1 OutboundAdded=relay:4,"
+ "create2:5 OutboundRemoved=relay:7 OutboundTime=relay:6",
+ ==, event_string);
+ tor_free(event_string);
+
+ p_chan = tor_malloc_zero(sizeof(channel_tls_t));
+ p_chan->base_.global_identifier = 2;
+
+ or_circ = tor_malloc_zero(sizeof(or_circuit_t));
+ or_circ->base_.magic = OR_CIRCUIT_MAGIC;
+ or_circ->base_.purpose = CIRCUIT_PURPOSE_OR;
+ or_circ->p_circ_id = 8;
+ or_circ->p_chan = &(p_chan->base_);
+ or_circ->base_.n_circ_id = 9;
+ or_circ->base_.n_chan = &(n_chan->base_);
+
+ tor_free(cell_stats);
+
+ /* OR circuit was idle. */
+ cell_stats = tor_malloc_zero(sizeof(cell_stats_t));
+ format_cell_stats(&event_string, TO_CIRCUIT(or_circ), cell_stats);
+ tt_str_op("InboundQueue=8 InboundConn=2 OutboundQueue=9 OutboundConn=1",
+ ==, event_string);
+ tor_free(event_string);
+
+ /* OR circuit had 3 RELAY cells added to its appward queue. */
+ cell_stats->added_cells_appward[CELL_RELAY] = 3;
+ format_cell_stats(&event_string, TO_CIRCUIT(or_circ), cell_stats);
+ tt_str_op("InboundQueue=8 InboundConn=2 InboundAdded=relay:3 "
+ "OutboundQueue=9 OutboundConn=1", ==, event_string);
+ tor_free(event_string);
+
+ /* OR circuit had 7 RELAY cells removed from its appward queue which
+ * together spent 6 msec in the queue. */
+ cell_stats->total_time_appward[CELL_RELAY] = 6;
+ cell_stats->removed_cells_appward[CELL_RELAY] = 7;
+ format_cell_stats(&event_string, TO_CIRCUIT(or_circ), cell_stats);
+ tt_str_op("InboundQueue=8 InboundConn=2 InboundAdded=relay:3 "
+ "InboundRemoved=relay:7 InboundTime=relay:6 "
+ "OutboundQueue=9 OutboundConn=1", ==, event_string);
+
+ done:
+ tor_free(cell_stats);
+ tor_free(event_string);
+}
+
+#define TEST(name, flags) \
+ { #name, test_cntev_ ## name, flags, 0, NULL }
+
+struct testcase_t controller_event_tests[] = {
+ TEST(bucket_note_empty, 0),
+ TEST(bucket_millis_empty, 0),
+ TEST(sum_up_cell_stats, 0),
+ TEST(append_cell_stats, 0),
+ TEST(format_cell_stats, 0),
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c
index f92bfd673..22adc6cc3 100644
--- a/src/test/test_crypto.c
+++ b/src/test/test_crypto.c
@@ -4,7 +4,6 @@
/* See LICENSE for licensing information */
#include "orconfig.h"
-#define CRYPTO_PRIVATE
#define CRYPTO_CURVE25519_PRIVATE
#include "or.h"
#include "test.h"
@@ -14,6 +13,10 @@
#include "crypto_curve25519.h"
#endif
+extern const char AUTHORITY_SIGNKEY_3[];
+extern const char AUTHORITY_SIGNKEY_A_DIGEST[];
+extern const char AUTHORITY_SIGNKEY_A_DIGEST256[];
+
/** Run unit tests for Diffie-Hellman functionality. */
static void
test_crypto_dh(void)
@@ -269,34 +272,6 @@ test_crypto_sha(void)
"96177A9CB410FF61F20015AD");
tt_int_op(i, ==, 0);
- /* Test HMAC-SHA-1 with test cases from RFC2202. */
-
- /* Case 1. */
- memset(key, 0x0b, 20);
- crypto_hmac_sha1(digest, key, 20, "Hi There", 8);
- test_streq(hex_str(digest, 20),
- "B617318655057264E28BC0B6FB378C8EF146BE00");
- /* Case 2. */
- crypto_hmac_sha1(digest, "Jefe", 4, "what do ya want for nothing?", 28);
- test_streq(hex_str(digest, 20),
- "EFFCDF6AE5EB2FA2D27416D5F184DF9C259A7C79");
-
- /* Case 4. */
- base16_decode(key, 25,
- "0102030405060708090a0b0c0d0e0f10111213141516171819", 50);
- memset(data, 0xcd, 50);
- crypto_hmac_sha1(digest, key, 25, data, 50);
- test_streq(hex_str(digest, 20),
- "4C9007F4026250C6BC8414F9BF50C86C2D7235DA");
-
- /* Case 5. */
- memset(key, 0xaa, 80);
- crypto_hmac_sha1(digest, key, 80,
- "Test Using Larger Than Block-Size Key - Hash Key First",
- 54);
- test_streq(hex_str(digest, 20),
- "AA4AE5E15272D00E95705637CE8A3B55ED402112");
-
/* Test HMAC-SHA256 with test cases from wikipedia and RFC 4231 */
/* Case empty (wikipedia) */
@@ -422,7 +397,7 @@ test_crypto_pk(void)
char *encoded = NULL;
char data1[1024], data2[1024], data3[1024];
size_t size;
- int i, j, p, len;
+ int i, len;
/* Public-key ciphers */
pk1 = pk_generate(0);
@@ -506,19 +481,16 @@ test_crypto_pk(void)
/* Try with hybrid encryption wrappers. */
crypto_rand(data1, 1024);
- for (i = 0; i < 2; ++i) {
- for (j = 85; j < 140; ++j) {
- memset(data2,0,1024);
- memset(data3,0,1024);
- p = (i==0)?PK_PKCS1_PADDING:PK_PKCS1_OAEP_PADDING;
- len = crypto_pk_public_hybrid_encrypt(pk1,data2,sizeof(data2),
- data1,j,p,0);
- test_assert(len>=0);
- len = crypto_pk_private_hybrid_decrypt(pk1,data3,sizeof(data3),
- data2,len,p,1);
- test_eq(len,j);
- test_memeq(data1,data3,j);
- }
+ for (i = 85; i < 140; ++i) {
+ memset(data2,0,1024);
+ memset(data3,0,1024);
+ len = crypto_pk_public_hybrid_encrypt(pk1,data2,sizeof(data2),
+ data1,i,PK_PKCS1_OAEP_PADDING,0);
+ test_assert(len>=0);
+ len = crypto_pk_private_hybrid_decrypt(pk1,data3,sizeof(data3),
+ data2,len,PK_PKCS1_OAEP_PADDING,1);
+ test_eq(len,i);
+ test_memeq(data1,data3,i);
}
/* Try copy_full */
@@ -536,6 +508,35 @@ test_crypto_pk(void)
tor_free(encoded);
}
+/** Sanity check for crypto pk digests */
+static void
+test_crypto_digests(void)
+{
+ crypto_pk_t *k = NULL;
+ ssize_t r;
+ digests_t pkey_digests;
+ char digest[DIGEST_LEN];
+
+ k = crypto_pk_new();
+ test_assert(k);
+ r = crypto_pk_read_private_key_from_string(k, AUTHORITY_SIGNKEY_3, -1);
+ test_assert(!r);
+
+ r = crypto_pk_get_digest(k, digest);
+ test_assert(r == 0);
+ test_memeq(hex_str(digest, DIGEST_LEN),
+ AUTHORITY_SIGNKEY_A_DIGEST, HEX_DIGEST_LEN);
+
+ r = crypto_pk_get_all_digests(k, &pkey_digests);
+
+ test_memeq(hex_str(pkey_digests.d[DIGEST_SHA1], DIGEST_LEN),
+ AUTHORITY_SIGNKEY_A_DIGEST, HEX_DIGEST_LEN);
+ test_memeq(hex_str(pkey_digests.d[DIGEST_SHA256], DIGEST256_LEN),
+ AUTHORITY_SIGNKEY_A_DIGEST256, HEX_DIGEST256_LEN);
+ done:
+ crypto_pk_free(k);
+}
+
/** Run unit tests for misc crypto formatting functionality (base64, base32,
* fingerprints, etc) */
static void
@@ -630,7 +631,7 @@ test_crypto_formats(void)
data1 = tor_strdup("ABCD1234ABCD56780000ABCD1234ABCD56780000");
test_eq(strlen(data1), 40);
data2 = tor_malloc(FINGERPRINT_LEN+1);
- add_spaces_to_fp(data2, FINGERPRINT_LEN+1, data1);
+ crypto_add_spaces_to_fp(data2, FINGERPRINT_LEN+1, data1);
test_streq(data2, "ABCD 1234 ABCD 5678 0000 ABCD 1234 ABCD 5678 0000");
tor_free(data1);
tor_free(data2);
@@ -730,11 +731,13 @@ test_crypto_aes_iv(void *arg)
/* Decrypt with the wrong key. */
decrypted_size = crypto_cipher_decrypt_with_iv(key2, decrypted2, 4095,
encrypted1, encrypted_size);
+ test_eq(decrypted_size, 4095);
test_memneq(plain, decrypted2, decrypted_size);
/* Alter the initialization vector. */
encrypted1[0] += 42;
decrypted_size = crypto_cipher_decrypt_with_iv(key1, decrypted1, 4095,
encrypted1, encrypted_size);
+ test_eq(decrypted_size, 4095);
test_memneq(plain, decrypted2, 4095);
/* Special length case: 1. */
encrypted_size = crypto_cipher_encrypt_with_iv(key1, encrypted1, 16 + 1,
@@ -1134,6 +1137,7 @@ struct testcase_t crypto_tests[] = {
{ "aes_EVP", test_crypto_aes, TT_FORK, &pass_data, (void*)"evp" },
CRYPTO_LEGACY(sha),
CRYPTO_LEGACY(pk),
+ CRYPTO_LEGACY(digests),
CRYPTO_LEGACY(dh),
CRYPTO_LEGACY(s2k),
{ "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"aes" },
diff --git a/src/test/test_data.c b/src/test/test_data.c
index 5f0f7cba0..0c51c98f1 100644
--- a/src/test/test_data.c
+++ b/src/test/test_data.c
@@ -3,8 +3,18 @@
* Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+/* Our unit test expect that the AUTHORITY_CERT_* public keys will sort
+ * in this order. */
+#define AUTHORITY_CERT_A AUTHORITY_CERT_3
+#define AUTHORITY_CERT_B AUTHORITY_CERT_1
+#define AUTHORITY_CERT_C AUTHORITY_CERT_2
+
+#define AUTHORITY_SIGNKEY_A AUTHORITY_SIGNKEY_3
+#define AUTHORITY_SIGNKEY_B AUTHORITY_SIGNKEY_1
+#define AUTHORITY_SIGNKEY_C AUTHORITY_SIGNKEY_2
+
/** First of 3 example authority certificates for unit testing. */
-const char AUTHORITY_CERT_1[] =
+const char AUTHORITY_CERT_A[] =
"dir-key-certificate-version 3\n"
"fingerprint D867ACF56A9D229B35C25F0090BC9867E906BE69\n"
"dir-key-published 2008-12-12 18:07:24\n"
@@ -46,7 +56,7 @@ const char AUTHORITY_CERT_1[] =
"-----END SIGNATURE-----\n";
/** The private signing key for AUTHORITY_CERT_1 */
-const char AUTHORITY_SIGNKEY_1[] =
+const char AUTHORITY_SIGNKEY_A[] =
"-----BEGIN RSA PRIVATE KEY-----\n"
"MIICWwIBAAKBgQCz0lCJ8rhLujVdzY6M6ZWp4iBAc0FxI79cff/pqp8GQAaWFZrs\n"
"vQPJ8XqMmN7GRbJ2MDVvyGYwIBtt6RJnr7txfi+JsjI42mujkZdzIEWEOIJrhaqX\n"
@@ -63,111 +73,128 @@ const char AUTHORITY_SIGNKEY_1[] =
"Yx4lqK0ca5IkTp3HevwnlWaJgbaOTUspCVshzJBhDA==\n"
"-----END RSA PRIVATE KEY-----\n";
+const char AUTHORITY_SIGNKEY_A_DIGEST[] =
+ "CBF56A83368A5150F1A9AAADAFB4D77F8C4170E2";
+const char AUTHORITY_SIGNKEY_A_DIGEST256[] =
+ "AF7C5468DBE3BA54A052726038D7F15F3C4CA511B1952645B3D96D83A8DFB51C";
+
/** Second of 3 example authority certificates for unit testing. */
-const char AUTHORITY_CERT_2[] =
+const char AUTHORITY_CERT_B[] =
"dir-key-certificate-version 3\n"
-"fingerprint 4D44AE0470B9E88FD4558EFEC82698FB33715400\n"
-"dir-key-published 2007-06-13 16:52:32\n"
-"dir-key-expires 2008-06-13 16:52:32\n"
+"fingerprint AD011E25302925A9D39A80E0E32576442E956467\n"
+"dir-key-published 2013-11-14 14:12:05\n"
+"dir-key-expires 2014-11-14 14:12:05\n"
"dir-identity-key\n"
"-----BEGIN RSA PUBLIC KEY-----\n"
-"MIIBigKCAYEAqukDwQRm1Oy1pPY+7GNRnRNFJzEVPUBfJwC4tBH19tkvdRQPuIGI\n"
-"2jiTy/rmZ6CLcl1G0oulSgxfKEX75QdptOasZu+rKUrRRSxx0QrXhs9a7up0rpXh\n"
-"13fw3mh1Vl/As3rJYF30Hjk01BTOJMxi/HY2y0ALQytFWjiMGY74A9Y6+uDcHkB2\n"
-"KflBjxIl8zpCsXsTTnUhN5kXqaOOnK46XaUShSpXsyOxTMJXuJEtgLz9XCyA8XjW\n"
-"d75QLHucEnlTqxUAdI5YSN2KIlIJiySCVnAorDpJey2mE9VncpHQWMCv/FPFdnSU\n"
-"EMMPUc4bBShcoNFf0mMJeV2sv+dBkgKAL0GLM19PuJIThJhfN/B6+YQTxw4HEpPV\n"
-"plfUqYRN0fYC+5hCTS6rroO/uCfDR7NBtoeDNm9dQrvjfk3b/Mywah1rdWNjnVqs\n"
-"tPJaz3fc/CVBOUUexhmyktgLuwSNEYIQilQ+BydkWN/4RObhV+YSV5BgekEDVaoS\n"
-"RHw4IbYBDHVxAgMBAAE=\n"
+"MIIBigKCAYEAyXYEMlGNRAixXdg65xf2WPkskYj2Wo8ysKMTls1JCXdIOAPvC2k2\n"
+"+AC6i3x9JHzUgCjWr4Jd5PSi7ODGyFC543igYl4wzkxNTU2L+SQ+hMe9qbEuUNhH\n"
+"sRR0xofdoH//3UuKj+HXEiMhhHbRWQGtWFuJqtGBruJqjZqIGOrp5nFjdlP0R98n\n"
+"Rx5wWlPgdJzifkXjKouu4mV+KzLl7f0gAtngA9DkSjt1wzga5IlL/lxDciD0SyJU\n"
+"tKMmls056omrZNbTnBxnY2pOlq9nx/zFrt/KQm1fTAQMjMBCf9KnDIV7NhaaHx7F\n"
+"7Nk8L7Hha353SvR+bsOFpiu05/EMZFTTIhO3MhUxZiCVZ0hKXvW1xe0HoGC5wbB+\n"
+"NyXu8oa4fIKLJ+WJ8Z60BNc0DcxJiQOf1eolGM/qrBul1lFZznds5/7182d+nF2W\n"
+"+bEjSm0fgXIxPfSD/7hB0FvgtmB3TXybHGBfPZgX0sTzFB6LNtP0BHicRoMXKdLF\n"
+"hM3tgIjEAsoZAgMBAAE=\n"
"-----END RSA PUBLIC KEY-----\n"
"dir-signing-key\n"
"-----BEGIN RSA PUBLIC KEY-----\n"
-"MIGJAoGBAOu3dgrQth3iqvi/UzfywaANw0bBUuMOBhnMBeiLEcRLneJHUJkVvrpR\n"
-"/EDQkdMov1e7CX6aqBKygVnbDNYjJ+bcQej8MKpuuW+zIknnz5lfnAVZO5uAmo3Y\n"
-"DpG574oQ2FFMdkWHSBloIRxSj/E4Jn1M2qJjElBXP0E33Ka/Noo7AgMBAAE=\n"
+"MIGJAoGBAJ567PZIGG/mYWEY4szYi/C5XXvf0BkquzKTHKrqVjysZEys9giz56Gv\n"
+"B08kIRxsxYKEWkq60rv0xtTc1WyEMcDpV1WLU0KSTQSVXzLu7BT8jbTsWzGsxdTV\n"
+"TdeyOirwHh8Cyyon5lppuMH5twUHrL5O7pWWbxjjrQjAHCn3gd+NAgMBAAE=\n"
"-----END RSA PUBLIC KEY-----\n"
+"dir-key-crosscert\n"
+"-----BEGIN ID SIGNATURE-----\n"
+"OC+gaukd4K7xJOsgTPbRhacf5mDUGxsu3ho/J1oJdtni4CK9WscVs6/Goj1o5Lot\n"
+"H1nCAMaR96Jnqq5c63Aaj1sEXdeYHlu5cI7YHgtGI5MmtjiUNXUCWMjCwSQYwGKe\n"
+"2YDYGAKAGt97n7XMKhJWGjAmv1TgmK3DvL1jt/aazL8=\n"
+"-----END ID SIGNATURE-----\n"
"dir-key-certification\n"
"-----BEGIN SIGNATURE-----\n"
-"Fv0Li68QUdAiChY3OklZOakHzwXAUfCzDNxkqe+HLC0n6ZECE9ZCvLVo69XmgVhH\n"
-"L5qYr2rxT6QpF+9yuOHbN9gWn8EsDcli06MlhX9TUt/IYVxHa/9tJwNoTfEw2w2D\n"
-"tyHhWm94IfOK7/Sea6jHnjckl80X+kk0ZNtAGs3/6fP4iltKNGXnvBwfgLpEgW7X\n"
-"NpDl0OLeDuA79zem2GogwQZQdoDbePByU0TJVx9jYi2Bzx2Nb2H0hRTPP6+dY0HQ\n"
-"MHb7yyyTQRad5iAUnExKhhyt22p7X3a6lgkAhq4YrNn/zVPkpnT2dzjsOydTHOW8\n"
-"2BQs33QlGNe095i47pJBDYsUgmJaXfqB/RG6dFg7jwIsc3/7dZcvcqfxY7wKcD/T\n"
-"wtogCIKxDvWbZn7f0hqYkT6uQC8Zom8bcnedmyzufOZCyA2SqQ2wvio6lznR4RIB\n"
-"a8qDHR0tPS9/VkqTPcvUWCZeY3UiDeWPjoK1nea1pz6DHDWglKPx86a0amjjayZQ\n"
-"-----END SIGNATURE-----\n";
+"BddmCKsvS6VoFXIf9Aj9OZnfyVCx527517QtsQHN+NaVm20LzUkJ5MWGXYx4wgh3\n"
+"ExsHvVQguiVfnonkQpEHHKg+TbldlkuDhIdlb9f7dL7V3HLCsEdmS1c3A+TEyrPH\n"
+"i44p6QB5IMFAdgUMV/9ueKMh7pMoam6VNakMOd+Axx9BSJTrCRzcepjtM4Z0cPsj\n"
+"nmDgZi0df1+ca1t+HnuWyt3trxlqoUxRcPZKz28kEFDJsgnRNvoHrIvNTuy9qY4x\n"
+"rONnPuLr5kTO7VQVVZxgxt6WX3p6d8tj+WYHubydr2pG0dwu2vGDTy4qXvDIm/I4\n"
+"Gyo6OAoPbYV8fl0584EgiEbAWcX/Pze8mXr9lmXbf73xbSBHqveAs0UfB+4sBI98\n"
+"v4ax4NZkGs8cCIfugtAOLgZE0WCh/TQYnQ3PFcrUtj0RW+tM1z7S8P3UfEVBHVkJ\n"
+"8SqSB+pbsY6PwMuy6TC3WujW7gmjVanbwkbW19El9l9jRzteFerz7grG/WQkshqF\n"
+ "-----END SIGNATURE-----\n";
/** The private signing key for AUTHORITY_CERT_2 */
-const char AUTHORITY_SIGNKEY_2[] =
+const char AUTHORITY_SIGNKEY_B[] =
"-----BEGIN RSA PRIVATE KEY-----\n"
-"MIICXgIBAAKBgQDrt3YK0LYd4qr4v1M38sGgDcNGwVLjDgYZzAXoixHES53iR1CZ\n"
-"Fb66UfxA0JHTKL9Xuwl+mqgSsoFZ2wzWIyfm3EHo/DCqbrlvsyJJ58+ZX5wFWTub\n"
-"gJqN2A6Rue+KENhRTHZFh0gZaCEcUo/xOCZ9TNqiYxJQVz9BN9ymvzaKOwIDAQAB\n"
-"AoGAJ+I9/ex8tCfTSA2PdisEKiHKBeHWNYb870Z/RW6qje1BhLUOZSixwfL3XLwt\n"
-"wG3nml+SZrKid69uhZaz4FPIf0tqCgURf6dDrF5vuzzr7VLVqkZHYSBp0vE6bu0R\n"
-"Sgc5QNxI2talgc4bsp0O0C+Zd4n3Yto0pXl/I6NHVAxlFBECQQD2mahkY+QEHWPV\n"
-"yRY3w3HhRmWBcrkY2zVyvPpqfn/sdHRPYW/yj4Xr/d1CO9VyFmEs4k324lIvu6LT\n"
-"WDdpPlcJAkEA9LOZv5aNeAm8ckvvXH7iv8KiONiSz0n9wlisxMhNYTEkOCo1g7jG\n"
-"AX5ZknRC9s4sWCPOBpMhloUvemdQ5FCEIwJBAMqCFwoSCf7jD8hRcUBr7QodoF/0\n"
-"kVJ7OeI2lMJ9jZnlbFp/3snn2Qeam2e38SnWfQi582KKKwnt4eIDMMXpntkCQQDI\n"
-"v1Lh11wl3y7nQZ6T7lCNatp08k+2mQgCWYcbRQweMRd6sD4I2xwt+372ZETPfyLo\n"
-"CC+sOyYx+v+RVpMJS3irAkEA6l98nMteZKmhOgyKSjdolP+ahpZunb+WnCdAtP97\n"
-"rjZyXmEZS3oe7TRCDD28GAGMmxSDvNfOOpyn14ishEs5AQ==\n"
+"MIICWwIBAAKBgQCeeuz2SBhv5mFhGOLM2IvwuV1739AZKrsykxyq6lY8rGRMrPYI\n"
+"s+ehrwdPJCEcbMWChFpKutK79MbU3NVshDHA6VdVi1NCkk0ElV8y7uwU/I207Fsx\n"
+"rMXU1U3Xsjoq8B4fAssqJ+ZaabjB+bcFB6y+Tu6Vlm8Y460IwBwp94HfjQIDAQAB\n"
+"AoGAfHQ4ZmfTmPyoeGHcqdVcgBxxh3gJqdnezCavGqGQO3F+CqDBTbBKNLSI3uOW\n"
+"hQX+TTK23Xy9RRFCm6MYj3F4x7OOrSHSFyhMmzRnAZi3zGbtQZn30XoqTwCmVevY\n"
+"p5JbVvhP2BJcvdsyQhiIG23FRQ7MMHWtksAxmovTto1h/hkCQQDNCfMqSztgJZDn\n"
+"JSf5ASHBOw8QzfZBeYi3hqfiDtAN1RxT1uQnEiFQFJqwCz5lCbcwVrfQbrrk5M+h\n"
+"ooYrX7tTAkEAxd6Tl0N0WM3zCKz+3/Hoiyty6olnnpzNoPCg7LLBJcetABQi0KUv\n"
+"swYWlKP3eOFZkiBzTqa9nBK7eYLKV3d9nwJAKNM3WI98Nguky3FJgTnpd6kDuevY\n"
+"gXbqcuhb2xXp9Sceqc7axLDGc0R2/GBwvvttPzG1DcpOai7o7J0Iq/A2wwJAYuKI\n"
+"/99GFdtWyc8q0OAkRui/1VY14p6aZQPcaG4s+KSBYLivbXYgEGfKgR4wXsi/6rcs\n"
+"6PGLcKQr7N3gITYmIQJAaQn6djUWygCn1noKyWU+Sa7G5qqU2GWkLq9dMaRLm1/I\n"
+"nqi+2K1mN15rra0QtFVqSH4JXr8h3KAGyU45voGM7A==\n"
"-----END RSA PRIVATE KEY-----\n";
/** Third of 3 example authority certificates for unit testing. */
-const char AUTHORITY_CERT_3[] =
+const char AUTHORITY_CERT_C[] =
"dir-key-certificate-version 3\n"
-"fingerprint ED3719BF554DE9D7D59F5CA5A4F5AD121D020ED9\n"
-"dir-key-published 2007-06-13 16:52:40\n"
-"dir-key-expires 2008-06-13 16:52:40\n"
+"fingerprint 628C2086EC29C9D26E638C5A8B2065BFBD35829B\n"
+"dir-key-published 2013-11-14 14:12:18\n"
+"dir-key-expires 2014-11-14 14:12:18\n"
"dir-identity-key\n"
"-----BEGIN RSA PUBLIC KEY-----\n"
-"MIIBigKCAYEAtB+yw4BNxtZAG4cPaedkhWNmeij7IuNWmXjh58ZYEGurvGyHs1w4\n"
-"QlwNYI2UftSIeIGdWZ5fJ17h9P3xvO6eeJuOt4KPrNOxUbSGrELEx1Lje1fDAJ1X\n"
-"SvN+dvptusxtyFUr8afgTPrFIvYuazQ6q/Rw+NDagjmDx3h/A/enihpBnjwzeH8j\n"
-"Xzu7b+HKnzFnNfveTDdvSy0NSC6tCOnrfXo31XbXRXtlesnMIpbJClUcAv55eyai\n"
-"/PrVPCCUz8mk0sQnn2Xhv1YJmwOlQTGMfg0a0kWLmh+UWcHsGQ4VWxBZJcuzgFHG\n"
-"hu2/Fz6DXSpX5Q6B9HKoGmnH1oBh24l0kUW1jL8BxPY4YDU1Lt5t3qgcDn9dXYcI\n"
-"o8VvyI0ecSc26Q2PYFWX1hpN4VIBZ8uGaW3IpyTdNiRq0g3iMGRFEXcDlWuyMB9E\n"
-"EbSM7m/79V/z7SjDd75EP8Z0qDPESEVB8a8LbuSJtzFVE0KHd7RzkIEN5sorXspZ\n"
-"/THukftSmkIvAgMBAAE=\n"
+"MIIBigKCAYEAuzPA82lRVUAc1uZgfDehhK0rBU5xt+qhJXUSH0DxsuocYCLW//q+\n"
+"7+L7q9SochqZK3R5+SxJaZRlVK4rAeIHsxXFxsnGvuqasGM3he80EV1RpVRkvLaO\n"
+"2dDmHcfEjYBadft2DEq811yvqSRqbFXmK0hLucA6LI6NnEw9VNWlguaV6ACVLyKQ\n"
+"iYVFz2JOJIAi0Zz57WZg7eHypUAGoyXjtYTJPsh6pUe/0NLFJVd3JHcJX+bNqU2a\n"
+"QU37r+CQ9f3T+8fZGJQ/CXNnYUNHa0j+toOFuPEiZBBh8C4PE7FJWjidvhe9uI7T\n"
+"Py41RZhy8e05MAQmUBNRKBHWPKHoy2zWZZxTkcfWFdJJz/dzsNrIjrqf2fYId9To\n"
+"fDpHzYd/UjzZaaVYRVS/Oyf3pN8DKw8LMhEArS0X9pblPVkWWjmYMU6f0VR7pelc\n"
+"gGYuML3gOiKdNbeMWgAv3HNRsVsuW0HZLrhXUGYzTRPJ/GxVCwA/NmYgMTNVWRwF\n"
+"7M78YHpayyEPAgMBAAE=\n"
"-----END RSA PUBLIC KEY-----\n"
"dir-signing-key\n"
"-----BEGIN RSA PUBLIC KEY-----\n"
-"MIGJAoGBANrSZlUq38Boz3iuUOydYTJV57rTbq1bz805FP2QG2Z+2bwpgKIOZag/\n"
-"gN2A1ySJaIYLgZIg9irxrLkqlY/UAjC23y6V9fJXP1S3TXoqLmHleW8PsaDLuwTo\n"
-"hCWaR61Mx9WG7IXcodn2Z7RiCfZpSW4Rztbk5WtjQa5jPXSFOuBJAgMBAAE=\n"
+"MIGJAoGBANESf/hRRWCK3TLQyNb9Y42tYedCORUc8Rl+Q4wrvdz3R0TNr6rztE9N\n"
+"u8v3Wbvjtiqm1xL1I5PaOObFQQj61QZxKiCm1yU4eFH15dNmcvBEy5BjEXVYiDgy\n"
+"zKRyePzjHYQIZF3ZaQTABUplkXVpY0YvAurluhEy+dKEvZMwWFZTAgMBAAE=\n"
"-----END RSA PUBLIC KEY-----\n"
+"dir-key-crosscert\n"
+"-----BEGIN ID SIGNATURE-----\n"
+"NHNBya6Dt7Ww3qSGA0DBEl6pZFBzmYXM+QdqF+ESpdyYCQ54EYimaxl4VcXoGaxy\n"
+"xk8/VOXPC6h7hVnTWDTsC86G6eXug1yzpd/uhQbcDJMH5q8/Yg5WXGOnGhMWNCBh\n"
+"u2UmbtAjdjLrObQaB50FfOpuOV9kdG4SEzaPUBR2ayU=\n"
+"-----END ID SIGNATURE-----\n"
"dir-key-certification\n"
"-----BEGIN SIGNATURE-----\n"
-"UNXZy+4OQ8iat+gw+vg2ynvKj2BYbqZt+EAZAV3rmw6gux44U9TLRECRd6LsA08N\n"
-"4+Vz01TU81xqMgfrUy94ei2YvcfpO8art9/muWHTP9SmOX8S1uqDqLWA+n723C9A\n"
-"HyVXn4aINncO2081gJcIW5+Ul8WTCeZe/n3LVPTCKbTdqxvmrPUdCWlJTQUmb19M\n"
-"T+kcCjaEfgQGLC+Y2MHqYe/nxz+aBKqpjiWUDdjc35va6r/2e3c0jGi1B1xRZxN1\n"
-"xThPZ+CifjDoWBxJdDGlIfZRK1lMnOCJY9w9ibTXQ1UnvE4whFvmB55/t9/XLq4q\n"
-"3pnZz0H7funey3+ilmTxDohoAYT1GX+4a+3xYH07UmAFqlTzqKClj84XEHn+Cer7\n"
-"Nun9kJlJFuBgUpQjwCkzedFZKKLOHgB2h7trJfnqcBpAM8Rup1Bb5u/RcBx9gy1q\n"
-"pMc65FviIrc/Q5TUku6NNbCbnGll1599PvWuUzkG42lJ17V6psKHIsqGtVdHlCUc\n"
+"NocTkLl9iKglVo+yrpY0slsqgPviuScMyEfOJ3i65KeJb4Dr1huIs0Fip40zFD8D\n"
+"cz/SYu09FbANuRwBJIRdVWZLLwVFLBj5F8U65iJRAPBw/O/xgSVBvWoOhBUZqmJA\n"
+"Jp1IUutQHYFfnAOO9za4r8Ox6yPaOWF9Ks5gL0kU/fI8Bdi5E9p3e9fMtoM7hROg\n"
+"oX1AoV/za3LcM0oMsGsdXQ7B8vRqY0eUX523kpRpF1fUDyvBUvvMsXdZDN6anCV6\n"
+"NtSq2UaM/msTX1oQ8gzyD1gMXH0Ek26YMhd+6WZE6KUeb1x5HJgXtKtYzMLB6nQM\n"
+"4Q/OA4NND/Veflofy6xx8uzXe8H+MoUHK9WiORtwqvBl0E9qk6SVCuo4ipR4Ybgk\n"
+"PAFOXA58j80dlNYYEVgV8MXF1Y/g/thuXlf2dWiLAExdHTtE0AzC4quWshegaImC\n"
+"4aziHeA43TRDszAXcJorREAM0AhSxp3aWDde4Jt46ODOJR8t+gHreks29eDttEIn\n"
"-----END SIGNATURE-----\n";
/** The private signing key for AUTHORITY_CERT_3 */
-const char AUTHORITY_SIGNKEY_3[] =
+const char AUTHORITY_SIGNKEY_C[] =
"-----BEGIN RSA PRIVATE KEY-----\n"
-"MIICXgIBAAKBgQDa0mZVKt/AaM94rlDsnWEyVee6026tW8/NORT9kBtmftm8KYCi\n"
-"DmWoP4DdgNckiWiGC4GSIPYq8ay5KpWP1AIwtt8ulfXyVz9Ut016Ki5h5XlvD7Gg\n"
-"y7sE6IQlmketTMfVhuyF3KHZ9me0Ygn2aUluEc7W5OVrY0GuYz10hTrgSQIDAQAB\n"
-"AoGBAIyoeG1AnQmildKeQpiGZackf0uhg2BeRwpFKg//5Q0Sd0Wza+M/2+q1v1Ei\n"
-"86ihxxV7KfPTykk6hmuUSwVkI28Z+5J9NYTr35EzPiUlqpo0iclTkFqrlbqSPULx\n"
-"9fQhvcOGv1c0m5CnYrHsM8eu3tagLg+6OE4abLOYX4Az5pkxAkEA/NwHhVaVJrXH\n"
-"lGDrRAfGtaD5Tzeeg1H9DNZi5lmFiSNR0O11sgDLkiZNP5oM8knyqo8Gq08hwxEb\n"
-"yqMXM3XtJQJBAN2KJbFhOjDIkvJyYvbmcP6P7vV2c9j+oUTKkFMF7vvfWunxMi9j\n"
-"ghbdUKgl7tU0VFpw7ufDDD0pkN6sua3gp1UCQQCvNzTK861U7p/GtMYyFQVf9JTt\n"
-"jMf9jYHBNInBvwTme6AFG5bz6tMlif77dJ9GAXHzODrR2Hq3thJA/3RjR3M1AkBg\n"
-"+6M4ncmtpYC+5lhwob0Bk90WU/6vFflfdhXsYoKWfNb95vsDR9qhS82Nbt25NClh\n"
-"VmMfzoFDHTkwYgj/F4PpAkEA+RaaSRP7BmbvFNqvlm8J/m0RVdAH4+p/Q5Z5u6Yo\n"
-"N7xC/gFi0qFPGKsDvD2CncAYmt+KNsd8S0JGDN4eieKn+Q==\n"
+"MIICXAIBAAKBgQDREn/4UUVgit0y0MjW/WONrWHnQjkVHPEZfkOMK73c90dEza+q\n"
+"87RPTbvL91m747YqptcS9SOT2jjmxUEI+tUGcSogptclOHhR9eXTZnLwRMuQYxF1\n"
+"WIg4Msykcnj84x2ECGRd2WkEwAVKZZF1aWNGLwLq5boRMvnShL2TMFhWUwIDAQAB\n"
+"AoGAU68L+eDN3C65CzX2rdcOmg7kOSSQpJrJBmM7tkdr3546sJeD0PFrIrMCkEmZ\n"
+"aVNj/v545+WnL+8RB4280lNUIF4AMNaMZUL+4FAtwekqWua3QvvqgRMjCdG3/h/d\n"
+"bOAUiiKKEimflTaIVHNVSCvOIntftOu3PhebctuabnZzg0ECQQD9i+FX7M9UXT1A\n"
+"bVm+bRIJuQtG+u9jD3VxrvHsmh0QnOAL3oa/ofTCwoTJLZs8Qy0GeAoJNf28rY1q\n"
+"AgNMEeEXAkEA0xhxNX2fDQ2yvKwPkPMrRycJVWry+KHvSZG2+XYh+V5sVGQ5H7Gu\n"
+"krc6IzRZlIKQhEGktkw8ih0DEHQbAihiJQJBAKi/SnFcePjrPXL91Hb63MB/2dOZ\n"
+"+21wwnexOe6A+8ssvajop8IvJlnhYMMMiX7oLrVZe0R6HLBQyge94zfjxm0CQGye\n"
+"dRIrE34qAEBo4JGbLjesdHcJUwBwgqn+WoI+MPkZhvBdqa8PRF6l/TpEI5vxGt+S\n"
+"z2gmDjia+QqsU4FmuikCQDDOs85uwNSKJFax9XMzd1qd1QwX20F8lvnOsWErXiDw\n"
+"Fy2+rmIRHoSxn4D+rE5ivqkO99E9jAlz+uuQz/6WqwE=\n"
"-----END RSA PRIVATE KEY-----\n";
diff --git a/src/test/test_dir.c b/src/test/test_dir.c
index 56ac3b34c..9b83e768f 100644
--- a/src/test/test_dir.c
+++ b/src/test/test_dir.c
@@ -214,8 +214,10 @@ test_dir_formats(void)
strlcat(buf2, "signing-key\n", sizeof(buf2));
strlcat(buf2, pk1_str, sizeof(buf2));
strlcat(buf2, "hidden-service-dir\n", sizeof(buf2));
+#ifdef CURVE25519_ENABLED
strlcat(buf2, "ntor-onion-key "
"skyinAnvardNostarsNomoonNowindormistsorsnow=\n", sizeof(buf2));
+#endif
strlcat(buf2, "accept *:80\nreject 18.0.0.0/8:24\n", sizeof(buf2));
strlcat(buf2, "router-signature\n", sizeof(buf2));
@@ -235,9 +237,11 @@ test_dir_formats(void)
test_eq(rp2->bandwidthrate, r2->bandwidthrate);
test_eq(rp2->bandwidthburst, r2->bandwidthburst);
test_eq(rp2->bandwidthcapacity, r2->bandwidthcapacity);
+#ifdef CURVE25519_ENABLED
test_memeq(rp2->onion_curve25519_pkey->public_key,
r2->onion_curve25519_pkey->public_key,
CURVE25519_PUBKEY_LEN);
+#endif
test_assert(crypto_pk_cmp_keys(rp2->onion_pkey, pk2) == 0);
test_assert(crypto_pk_cmp_keys(rp2->identity_pkey, pk1) == 0);
@@ -1244,7 +1248,6 @@ test_a_networkstatus(
/* Parse certificates and keys. */
cert1 = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
test_assert(cert1);
- test_assert(cert1->is_cross_certified);
cert2 = authority_cert_parse_from_string(AUTHORITY_CERT_2, NULL);
test_assert(cert2);
cert3 = authority_cert_parse_from_string(AUTHORITY_CERT_3, NULL);
@@ -2317,9 +2320,9 @@ test_dir_v2_dir(void *arg)
/* Make a directory so there's somewhere to store the thing */
#ifdef _WIN32
- mkdir(get_fname("cached-status"));
+ tt_int_op(mkdir(get_fname("cached-status")), ==, 0);
#else
- mkdir(get_fname("cached-status"), 0700);
+ tt_int_op(mkdir(get_fname("cached-status"), 0700), ==, 0);
#endif
v2 = generate_v2_networkstatus_opinion();
@@ -2364,6 +2367,74 @@ test_dir_fmt_control_ns(void *arg)
tor_free(s);
}
+static void
+test_dir_http_handling(void *args)
+{
+ char *url = NULL;
+ (void)args;
+
+ /* Parse http url tests: */
+ /* Good headers */
+ test_eq(parse_http_url("GET /tor/a/b/c.txt HTTP/1.1\r\n"
+ "Host: example.com\r\n"
+ "User-Agent: Mozilla/5.0 (Windows;"
+ " U; Windows NT 6.1; en-US; rv:1.9.1.5)\r\n",
+ &url), 0);
+ test_streq(url, "/tor/a/b/c.txt");
+ tor_free(url);
+
+ test_eq(parse_http_url("GET /tor/a/b/c.txt HTTP/1.0\r\n", &url), 0);
+ test_streq(url, "/tor/a/b/c.txt");
+ tor_free(url);
+
+ test_eq(parse_http_url("GET /tor/a/b/c.txt HTTP/1.600\r\n", &url), 0);
+ test_streq(url, "/tor/a/b/c.txt");
+ tor_free(url);
+
+ /* Should prepend '/tor/' to url if required */
+ test_eq(parse_http_url("GET /a/b/c.txt HTTP/1.1\r\n"
+ "Host: example.com\r\n"
+ "User-Agent: Mozilla/5.0 (Windows;"
+ " U; Windows NT 6.1; en-US; rv:1.9.1.5)\r\n",
+ &url), 0);
+ test_streq(url, "/tor/a/b/c.txt");
+ tor_free(url);
+
+ /* Bad headers -- no HTTP/1.x*/
+ test_eq(parse_http_url("GET /a/b/c.txt\r\n"
+ "Host: example.com\r\n"
+ "User-Agent: Mozilla/5.0 (Windows;"
+ " U; Windows NT 6.1; en-US; rv:1.9.1.5)\r\n",
+ &url), -1);
+ tt_assert(!url);
+
+ /* Bad headers */
+ test_eq(parse_http_url("GET /a/b/c.txt\r\n"
+ "Host: example.com\r\n"
+ "User-Agent: Mozilla/5.0 (Windows;"
+ " U; Windows NT 6.1; en-US; rv:1.9.1.5)\r\n",
+ &url), -1);
+ tt_assert(!url);
+
+ test_eq(parse_http_url("GET /tor/a/b/c.txt", &url), -1);
+ tt_assert(!url);
+
+ test_eq(parse_http_url("GET /tor/a/b/c.txt HTTP/1.1", &url), -1);
+ tt_assert(!url);
+
+ test_eq(parse_http_url("GET /tor/a/b/c.txt HTTP/1.1x\r\n", &url), -1);
+ tt_assert(!url);
+
+ test_eq(parse_http_url("GET /tor/a/b/c.txt HTTP/1.", &url), -1);
+ tt_assert(!url);
+
+ test_eq(parse_http_url("GET /tor/a/b/c.txt HTTP/1.\r", &url), -1);
+ tt_assert(!url);
+
+ done:
+ tor_free(url);
+}
+
#define DIR_LEGACY(name) \
{ #name, legacy_test_helper, TT_FORK, &legacy_setup, test_dir_ ## name }
@@ -2386,6 +2457,7 @@ struct testcase_t dir_tests[] = {
DIR_LEGACY(clip_unmeasured_bw_kb_alt),
DIR(v2_dir, TT_FORK),
DIR(fmt_control_ns, 0),
+ DIR(http_handling, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_extorport.c b/src/test/test_extorport.c
new file mode 100644
index 000000000..7e38ba57d
--- /dev/null
+++ b/src/test/test_extorport.c
@@ -0,0 +1,604 @@
+/* Copyright (c) 2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define CONNECTION_PRIVATE
+#define EXT_ORPORT_PRIVATE
+#define MAIN_PRIVATE
+#include "or.h"
+#include "buffers.h"
+#include "connection.h"
+#include "connection_or.h"
+#include "config.h"
+#include "control.h"
+#include "ext_orport.h"
+#include "main.h"
+#include "test.h"
+
+/* Test connection_or_remove_from_ext_or_id_map and
+ * connection_or_set_ext_or_identifier */
+static void
+test_ext_or_id_map(void *arg)
+{
+ or_connection_t *c1 = NULL, *c2 = NULL, *c3 = NULL;
+ char *idp = NULL, *idp2 = NULL;
+ (void)arg;
+
+ /* pre-initialization */
+ tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id("xxxxxxxxxxxxxxxxxxxx"));
+
+ c1 = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
+ c2 = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
+ c3 = or_connection_new(CONN_TYPE_OR, AF_INET);
+
+ tt_ptr_op(c1->ext_or_conn_id, !=, NULL);
+ tt_ptr_op(c2->ext_or_conn_id, !=, NULL);
+ tt_ptr_op(c3->ext_or_conn_id, ==, NULL);
+
+ tt_ptr_op(c1, ==, connection_or_get_by_ext_or_id(c1->ext_or_conn_id));
+ tt_ptr_op(c2, ==, connection_or_get_by_ext_or_id(c2->ext_or_conn_id));
+ tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id("xxxxxxxxxxxxxxxxxxxx"));
+
+ idp = tor_memdup(c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN);
+
+ /* Give c2 a new ID. */
+ connection_or_set_ext_or_identifier(c2);
+ test_mem_op(idp, !=, c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN);
+ idp2 = tor_memdup(c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN);
+ tt_assert(!tor_digest_is_zero(idp2));
+
+ tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id(idp));
+ tt_ptr_op(c2, ==, connection_or_get_by_ext_or_id(idp2));
+
+ /* Now remove it. */
+ connection_or_remove_from_ext_or_id_map(c2);
+ tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id(idp));
+ tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id(idp2));
+
+ done:
+ if (c1)
+ connection_free_(TO_CONN(c1));
+ if (c2)
+ connection_free_(TO_CONN(c2));
+ if (c3)
+ connection_free_(TO_CONN(c3));
+ tor_free(idp);
+ tor_free(idp2);
+ connection_or_clear_ext_or_id_map();
+}
+
+/* Simple connection_write_to_buf_impl_ replacement that unconditionally
+ * writes to outbuf. */
+static void
+connection_write_to_buf_impl_replacement(const char *string, size_t len,
+ connection_t *conn, int zlib)
+{
+ (void) zlib;
+
+ tor_assert(string);
+ tor_assert(conn);
+ write_to_buf(string, len, conn->outbuf);
+}
+
+static char *
+buf_get_contents(buf_t *buf, size_t *sz_out)
+{
+ char *out;
+ *sz_out = buf_datalen(buf);
+ if (*sz_out >= ULONG_MAX)
+ return NULL; /* C'mon, really? */
+ out = tor_malloc(*sz_out + 1);
+ if (fetch_from_buf(out, (unsigned long)*sz_out, buf) != 0) {
+ tor_free(out);
+ return NULL;
+ }
+ out[*sz_out] = '\0'; /* Hopefully gratuitous. */
+ return out;
+}
+
+static void
+test_ext_or_write_command(void *arg)
+{
+ or_connection_t *c1;
+ char *cp = NULL;
+ char *buf = NULL;
+ size_t sz;
+
+ (void) arg;
+ MOCK(connection_write_to_buf_impl_,
+ connection_write_to_buf_impl_replacement);
+
+ c1 = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
+ tt_assert(c1);
+
+ /* Length too long */
+ tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 100, "X", 100000),
+ <, 0);
+
+ /* Empty command */
+ tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 0x99, NULL, 0),
+ ==, 0);
+ cp = buf_get_contents(TO_CONN(c1)->outbuf, &sz);
+ tt_int_op(sz, ==, 4);
+ test_mem_op(cp, ==, "\x00\x99\x00\x00", 4);
+ tor_free(cp);
+
+ /* Medium command. */
+ tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 0x99,
+ "Wai\0Hello", 9), ==, 0);
+ cp = buf_get_contents(TO_CONN(c1)->outbuf, &sz);
+ tt_int_op(sz, ==, 13);
+ test_mem_op(cp, ==, "\x00\x99\x00\x09Wai\x00Hello", 13);
+ tor_free(cp);
+
+ /* Long command */
+ buf = tor_malloc(65535);
+ memset(buf, 'x', 65535);
+ tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 0xf00d,
+ buf, 65535), ==, 0);
+ cp = buf_get_contents(TO_CONN(c1)->outbuf, &sz);
+ tt_int_op(sz, ==, 65539);
+ test_mem_op(cp, ==, "\xf0\x0d\xff\xff", 4);
+ test_mem_op(cp+4, ==, buf, 65535);
+ tor_free(cp);
+
+ done:
+ if (c1)
+ connection_free_(TO_CONN(c1));
+ tor_free(cp);
+ tor_free(buf);
+ UNMOCK(connection_write_to_buf_impl_);
+}
+
+static int
+write_bytes_to_file_fail(const char *fname, const char *str, size_t len,
+ int bin)
+{
+ (void) fname;
+ (void) str;
+ (void) len;
+ (void) bin;
+
+ return -1;
+}
+
+static void
+test_ext_or_init_auth(void *arg)
+{
+ or_options_t *options = get_options_mutable();
+ const char *fn;
+ char *cp = NULL;
+ struct stat st;
+ char cookie0[32];
+ (void)arg;
+
+ /* Check default filename location */
+ options->DataDirectory = tor_strdup("foo");
+ cp = get_ext_or_auth_cookie_file_name();
+ tt_str_op(cp, ==, "foo"PATH_SEPARATOR"extended_orport_auth_cookie");
+ tor_free(cp);
+
+ /* Shouldn't be initialized already, or our tests will be a bit
+ * meaningless */
+ ext_or_auth_cookie = tor_malloc_zero(32);
+ test_assert(tor_mem_is_zero((char*)ext_or_auth_cookie, 32));
+
+ /* Now make sure we use a temporary file */
+ fn = get_fname("ext_cookie_file");
+ options->ExtORPortCookieAuthFile = tor_strdup(fn);
+ cp = get_ext_or_auth_cookie_file_name();
+ tt_str_op(cp, ==, fn);
+ tor_free(cp);
+
+ /* Test the initialization function with a broken
+ write_bytes_to_file(). See if the problem is handled properly. */
+ MOCK(write_bytes_to_file, write_bytes_to_file_fail);
+ tt_int_op(-1, ==, init_ext_or_cookie_authentication(1));
+ tt_int_op(ext_or_auth_cookie_is_set, ==, 0);
+ UNMOCK(write_bytes_to_file);
+
+ /* Now do the actual initialization. */
+ tt_int_op(0, ==, init_ext_or_cookie_authentication(1));
+ tt_int_op(ext_or_auth_cookie_is_set, ==, 1);
+ cp = read_file_to_str(fn, RFTS_BIN, &st);
+ tt_ptr_op(cp, !=, NULL);
+ tt_int_op(st.st_size, ==, 64);
+ test_memeq(cp, "! Extended ORPort Auth Cookie !\x0a", 32);
+ test_memeq(cp+32, ext_or_auth_cookie, 32);
+ memcpy(cookie0, ext_or_auth_cookie, 32);
+ test_assert(!tor_mem_is_zero((char*)ext_or_auth_cookie, 32));
+
+ /* Operation should be idempotent. */
+ tt_int_op(0, ==, init_ext_or_cookie_authentication(1));
+ test_memeq(cookie0, ext_or_auth_cookie, 32);
+
+ done:
+ tor_free(cp);
+ ext_orport_free_all();
+}
+
+static void
+test_ext_or_cookie_auth(void *arg)
+{
+ char *reply=NULL, *reply2=NULL, *client_hash=NULL, *client_hash2=NULL;
+ size_t reply_len=0;
+ char hmac1[32], hmac2[32];
+
+ const char client_nonce[32] =
+ "Who is the third who walks alway";
+ char server_hash_input[] =
+ "ExtORPort authentication server-to-client hash"
+ "Who is the third who walks alway"
+ "................................";
+ char client_hash_input[] =
+ "ExtORPort authentication client-to-server hash"
+ "Who is the third who walks alway"
+ "................................";
+
+ (void)arg;
+
+ tt_int_op(strlen(client_hash_input), ==, 46+32+32);
+ tt_int_op(strlen(server_hash_input), ==, 46+32+32);
+
+ ext_or_auth_cookie = tor_malloc_zero(32);
+ memcpy(ext_or_auth_cookie, "s beside you? When I count, ther", 32);
+ ext_or_auth_cookie_is_set = 1;
+
+ /* For this authentication, the client sends 32 random bytes (ClientNonce)
+ * The server replies with 32 byte ServerHash and 32 byte ServerNonce,
+ * where ServerHash is:
+ * HMAC-SHA256(CookieString,
+ * "ExtORPort authentication server-to-client hash" | ClientNonce |
+ * ServerNonce)"
+ * The client must reply with 32-byte ClientHash, which we compute as:
+ * ClientHash is computed as:
+ * HMAC-SHA256(CookieString,
+ * "ExtORPort authentication client-to-server hash" | ClientNonce |
+ * ServerNonce)
+ */
+
+ /* Wrong length */
+ tt_int_op(-1, ==,
+ handle_client_auth_nonce(client_nonce, 33, &client_hash, &reply,
+ &reply_len));
+ tt_int_op(-1, ==,
+ handle_client_auth_nonce(client_nonce, 31, &client_hash, &reply,
+ &reply_len));
+
+ /* Now let's try this for real! */
+ tt_int_op(0, ==,
+ handle_client_auth_nonce(client_nonce, 32, &client_hash, &reply,
+ &reply_len));
+ tt_int_op(reply_len, ==, 64);
+ tt_ptr_op(reply, !=, NULL);
+ tt_ptr_op(client_hash, !=, NULL);
+ /* Fill in the server nonce into the hash inputs... */
+ memcpy(server_hash_input+46+32, reply+32, 32);
+ memcpy(client_hash_input+46+32, reply+32, 32);
+ /* Check the HMACs are correct... */
+ crypto_hmac_sha256(hmac1, (char*)ext_or_auth_cookie, 32, server_hash_input,
+ 46+32+32);
+ crypto_hmac_sha256(hmac2, (char*)ext_or_auth_cookie, 32, client_hash_input,
+ 46+32+32);
+ test_memeq(hmac1, reply, 32);
+ test_memeq(hmac2, client_hash, 32);
+
+ /* Now do it again and make sure that the results are *different* */
+ tt_int_op(0, ==,
+ handle_client_auth_nonce(client_nonce, 32, &client_hash2, &reply2,
+ &reply_len));
+ test_memneq(reply2, reply, reply_len);
+ test_memneq(client_hash2, client_hash, 32);
+ /* But that this one checks out too. */
+ memcpy(server_hash_input+46+32, reply2+32, 32);
+ memcpy(client_hash_input+46+32, reply2+32, 32);
+ /* Check the HMACs are correct... */
+ crypto_hmac_sha256(hmac1, (char*)ext_or_auth_cookie, 32, server_hash_input,
+ 46+32+32);
+ crypto_hmac_sha256(hmac2, (char*)ext_or_auth_cookie, 32, client_hash_input,
+ 46+32+32);
+ test_memeq(hmac1, reply2, 32);
+ test_memeq(hmac2, client_hash2, 32);
+
+ done:
+ tor_free(reply);
+ tor_free(client_hash);
+ tor_free(reply2);
+ tor_free(client_hash2);
+}
+
+static int
+crypto_rand_return_tse_str(char *to, size_t n)
+{
+ if (n != 32) {
+ TT_FAIL(("Asked for %d bytes, not 32", (int)n));
+ return -1;
+ }
+ memcpy(to, "te road There is always another ", 32);
+ return 0;
+}
+
+static void
+test_ext_or_cookie_auth_testvec(void *arg)
+{
+ char *reply=NULL, *client_hash=NULL;
+ size_t reply_len;
+ char *mem_op_hex_tmp=NULL;
+
+ const char client_nonce[] = "But when I look ahead up the whi";
+ (void)arg;
+
+ ext_or_auth_cookie = tor_malloc_zero(32);
+ memcpy(ext_or_auth_cookie, "Gliding wrapt in a brown mantle," , 32);
+ ext_or_auth_cookie_is_set = 1;
+
+ MOCK(crypto_rand, crypto_rand_return_tse_str);
+
+ tt_int_op(0, ==,
+ handle_client_auth_nonce(client_nonce, 32, &client_hash, &reply,
+ &reply_len));
+ tt_ptr_op(reply, !=, NULL );
+ tt_ptr_op(reply_len, ==, 64);
+ test_memeq(reply+32, "te road There is always another ", 32);
+ /* HMACSHA256("Gliding wrapt in a brown mantle,"
+ * "ExtORPort authentication server-to-client hash"
+ * "But when I look ahead up the write road There is always another ");
+ */
+ test_memeq_hex(reply,
+ "ec80ed6e546d3b36fdfc22fe1315416b"
+ "029f1ade7610d910878b62eeb7403821");
+ /* HMACSHA256("Gliding wrapt in a brown mantle,"
+ * "ExtORPort authentication client-to-server hash"
+ * "But when I look ahead up the write road There is always another ");
+ * (Both values computed using Python CLI.)
+ */
+ test_memeq_hex(client_hash,
+ "ab391732dd2ed968cd40c087d1b1f25b"
+ "33b3cd77ff79bd80c2074bbf438119a2");
+
+ done:
+ UNMOCK(crypto_rand);
+ tor_free(reply);
+ tor_free(client_hash);
+ tor_free(mem_op_hex_tmp);
+}
+
+static void
+ignore_bootstrap_problem(const char *warn, int reason)
+{
+ (void)warn;
+ (void)reason;
+}
+
+static int is_reading = 1;
+static int handshake_start_called = 0;
+
+static void
+note_read_stopped(connection_t *conn)
+{
+ (void)conn;
+ is_reading=0;
+}
+static void
+note_read_started(connection_t *conn)
+{
+ (void)conn;
+ is_reading=1;
+}
+static int
+handshake_start(or_connection_t *conn, int receiving)
+{
+ if (!conn || !receiving)
+ TT_FAIL(("Bad arguments to handshake_start"));
+ handshake_start_called = 1;
+ return 0;
+}
+
+#define WRITE(s,n) \
+ do { \
+ write_to_buf((s), (n), TO_CONN(conn)->inbuf); \
+ } while (0)
+#define CONTAINS(s,n) \
+ do { \
+ tt_int_op((n), <=, sizeof(b)); \
+ tt_int_op(buf_datalen(TO_CONN(conn)->outbuf), ==, (n)); \
+ if ((n)) { \
+ fetch_from_buf(b, (n), TO_CONN(conn)->outbuf); \
+ test_memeq(b, (s), (n)); \
+ } \
+ } while (0)
+
+/* Helper: Do a successful Extended ORPort authentication handshake. */
+static void
+do_ext_or_handshake(or_connection_t *conn)
+{
+ char b[256];
+
+ tt_int_op(0, ==, connection_ext_or_start_auth(conn));
+ CONTAINS("\x01\x00", 2);
+ WRITE("\x01", 1);
+ WRITE("But when I look ahead up the whi", 32);
+ MOCK(crypto_rand, crypto_rand_return_tse_str);
+ tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
+ UNMOCK(crypto_rand);
+ tt_int_op(TO_CONN(conn)->state, ==, EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH);
+ CONTAINS("\xec\x80\xed\x6e\x54\x6d\x3b\x36\xfd\xfc\x22\xfe\x13\x15\x41\x6b"
+ "\x02\x9f\x1a\xde\x76\x10\xd9\x10\x87\x8b\x62\xee\xb7\x40\x38\x21"
+ "te road There is always another ", 64);
+ /* Send the right response this time. */
+ WRITE("\xab\x39\x17\x32\xdd\x2e\xd9\x68\xcd\x40\xc0\x87\xd1\xb1\xf2\x5b"
+ "\x33\xb3\xcd\x77\xff\x79\xbd\x80\xc2\x07\x4b\xbf\x43\x81\x19\xa2",
+ 32);
+ tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
+ CONTAINS("\x01", 1);
+ tt_assert(! TO_CONN(conn)->marked_for_close);
+ tt_int_op(TO_CONN(conn)->state, ==, EXT_OR_CONN_STATE_OPEN);
+
+ done: ;
+}
+
+static void
+test_ext_or_handshake(void *arg)
+{
+ or_connection_t *conn=NULL;
+ char b[256];
+
+ (void) arg;
+ MOCK(connection_write_to_buf_impl_,
+ connection_write_to_buf_impl_replacement);
+ /* Use same authenticators as for test_ext_or_cookie_auth_testvec */
+ ext_or_auth_cookie = tor_malloc_zero(32);
+ memcpy(ext_or_auth_cookie, "Gliding wrapt in a brown mantle," , 32);
+ ext_or_auth_cookie_is_set = 1;
+
+ init_connection_lists();
+
+ conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
+ tt_int_op(0, ==, connection_ext_or_start_auth(conn));
+ /* The server starts by telling us about the one supported authtype. */
+ CONTAINS("\x01\x00", 2);
+ /* Say the client hasn't responded yet. */
+ tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
+ /* Let's say the client replies badly. */
+ WRITE("\x99", 1);
+ tt_int_op(-1, ==, connection_ext_or_process_inbuf(conn));
+ CONTAINS("", 0);
+ tt_assert(TO_CONN(conn)->marked_for_close);
+ close_closeable_connections();
+ conn = NULL;
+
+ /* Okay, try again. */
+ conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
+ tt_int_op(0, ==, connection_ext_or_start_auth(conn));
+ CONTAINS("\x01\x00", 2);
+ /* Let's say the client replies sensibly this time. "Yes, AUTHTYPE_COOKIE
+ * sounds delicious. Let's have some of that!" */
+ WRITE("\x01", 1);
+ /* Let's say that the client also sends part of a nonce. */
+ WRITE("But when I look ", 16);
+ tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
+ CONTAINS("", 0);
+ tt_int_op(TO_CONN(conn)->state, ==,
+ EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE);
+ /* Pump it again. Nothing should happen. */
+ tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
+ /* send the rest of the nonce. */
+ WRITE("ahead up the whi", 16);
+ MOCK(crypto_rand, crypto_rand_return_tse_str);
+ tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
+ UNMOCK(crypto_rand);
+ /* We should get the right reply from the server. */
+ CONTAINS("\xec\x80\xed\x6e\x54\x6d\x3b\x36\xfd\xfc\x22\xfe\x13\x15\x41\x6b"
+ "\x02\x9f\x1a\xde\x76\x10\xd9\x10\x87\x8b\x62\xee\xb7\x40\x38\x21"
+ "te road There is always another ", 64);
+ /* Send the wrong response. */
+ WRITE("not with a bang but a whimper...", 32);
+ MOCK(control_event_bootstrap_problem, ignore_bootstrap_problem);
+ tt_int_op(-1, ==, connection_ext_or_process_inbuf(conn));
+ CONTAINS("\x00", 1);
+ tt_assert(TO_CONN(conn)->marked_for_close);
+ /* XXXX Hold-open-until-flushed. */
+ close_closeable_connections();
+ conn = NULL;
+ UNMOCK(control_event_bootstrap_problem);
+
+ MOCK(connection_start_reading, note_read_started);
+ MOCK(connection_stop_reading, note_read_stopped);
+ MOCK(connection_tls_start_handshake, handshake_start);
+
+ /* Okay, this time let's succeed. */
+ conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
+ do_ext_or_handshake(conn);
+
+ /* Now let's run through some messages. */
+ /* First let's send some junk and make sure it's ignored. */
+ WRITE("\xff\xf0\x00\x03""ABC", 7);
+ tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
+ CONTAINS("", 0);
+ /* Now let's send a USERADDR command. */
+ WRITE("\x00\x01\x00\x0c""1.2.3.4:5678", 16);
+ tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
+ tt_int_op(TO_CONN(conn)->port, ==, 5678);
+ tt_int_op(tor_addr_to_ipv4h(&TO_CONN(conn)->addr), ==, 0x01020304);
+ /* Now let's send a TRANSPORT command. */
+ WRITE("\x00\x02\x00\x07""rfc1149", 11);
+ tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
+ tt_ptr_op(NULL, !=, conn->ext_or_transport);
+ tt_str_op("rfc1149", ==, conn->ext_or_transport);
+ tt_int_op(is_reading,==,1);
+ tt_int_op(TO_CONN(conn)->state, ==, EXT_OR_CONN_STATE_OPEN);
+ /* DONE */
+ WRITE("\x00\x00\x00\x00", 4);
+ tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
+ tt_int_op(TO_CONN(conn)->state, ==, EXT_OR_CONN_STATE_FLUSHING);
+ tt_int_op(is_reading,==,0);
+ CONTAINS("\x10\x00\x00\x00", 4);
+ tt_int_op(handshake_start_called,==,0);
+ tt_int_op(0, ==, connection_ext_or_finished_flushing(conn));
+ tt_int_op(is_reading,==,1);
+ tt_int_op(handshake_start_called,==,1);
+ tt_int_op(TO_CONN(conn)->type, ==, CONN_TYPE_OR);
+ tt_int_op(TO_CONN(conn)->state, ==, 0);
+ close_closeable_connections();
+ conn = NULL;
+
+ /* Okay, this time let's succeed the handshake but fail the USERADDR
+ command. */
+ conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
+ do_ext_or_handshake(conn);
+ /* USERADDR command with an extra NUL byte */
+ WRITE("\x00\x01\x00\x0d""1.2.3.4:5678\x00", 17);
+ MOCK(control_event_bootstrap_problem, ignore_bootstrap_problem);
+ tt_int_op(-1, ==, connection_ext_or_process_inbuf(conn));
+ CONTAINS("", 0);
+ tt_assert(TO_CONN(conn)->marked_for_close);
+ close_closeable_connections();
+ conn = NULL;
+ UNMOCK(control_event_bootstrap_problem);
+
+ /* Now fail the TRANSPORT command. */
+ conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
+ do_ext_or_handshake(conn);
+ /* TRANSPORT command with an extra NUL byte */
+ WRITE("\x00\x02\x00\x08""rfc1149\x00", 12);
+ MOCK(control_event_bootstrap_problem, ignore_bootstrap_problem);
+ tt_int_op(-1, ==, connection_ext_or_process_inbuf(conn));
+ CONTAINS("", 0);
+ tt_assert(TO_CONN(conn)->marked_for_close);
+ close_closeable_connections();
+ conn = NULL;
+ UNMOCK(control_event_bootstrap_problem);
+
+ /* Now fail the TRANSPORT command. */
+ conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
+ do_ext_or_handshake(conn);
+ /* TRANSPORT command with transport name with symbols (not a
+ C-identifier) */
+ WRITE("\x00\x02\x00\x07""rf*1149", 11);
+ MOCK(control_event_bootstrap_problem, ignore_bootstrap_problem);
+ tt_int_op(-1, ==, connection_ext_or_process_inbuf(conn));
+ CONTAINS("", 0);
+ tt_assert(TO_CONN(conn)->marked_for_close);
+ close_closeable_connections();
+ conn = NULL;
+ UNMOCK(control_event_bootstrap_problem);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ UNMOCK(crypto_rand);
+ if (conn)
+ connection_free_(TO_CONN(conn));
+#undef CONTAINS
+#undef WRITE
+}
+
+struct testcase_t extorport_tests[] = {
+ { "id_map", test_ext_or_id_map, TT_FORK, NULL, NULL },
+ { "write_command", test_ext_or_write_command, TT_FORK, NULL, NULL },
+ { "init_auth", test_ext_or_init_auth, TT_FORK, NULL, NULL },
+ { "cookie_auth", test_ext_or_cookie_auth, TT_FORK, NULL, NULL },
+ { "cookie_auth_testvec", test_ext_or_cookie_auth_testvec, TT_FORK,
+ NULL, NULL },
+ { "handshake", test_ext_or_handshake, TT_FORK, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_logging.c b/src/test/test_logging.c
new file mode 100644
index 000000000..7e558f83b
--- /dev/null
+++ b/src/test/test_logging.c
@@ -0,0 +1,135 @@
+/* Copyright (c) 2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "or.h"
+#include "torlog.h"
+#include "test.h"
+
+static void
+dummy_cb_fn(int severity, uint32_t domain, const char *msg)
+{
+ (void)severity; (void)domain; (void)msg;
+}
+
+static void
+test_get_sigsafe_err_fds(void *arg)
+{
+ const int *fds;
+ int n;
+ log_severity_list_t include_bug, no_bug, no_bug2;
+ (void) arg;
+ init_logging();
+
+ n = tor_log_get_sigsafe_err_fds(&fds);
+ tt_int_op(n, ==, 1);
+ tt_int_op(fds[0], ==, STDERR_FILENO);
+
+ set_log_severity_config(LOG_WARN, LOG_ERR, &include_bug);
+ set_log_severity_config(LOG_WARN, LOG_ERR, &no_bug);
+ no_bug.masks[0] &= ~(LD_BUG|LD_GENERAL);
+ set_log_severity_config(LOG_INFO, LOG_NOTICE, &no_bug2);
+
+ /* Add some logs; make sure the output is as expected. */
+ mark_logs_temp();
+ add_stream_log(&include_bug, "dummy-1", 3);
+ add_stream_log(&no_bug, "dummy-2", 4);
+ add_stream_log(&no_bug2, "dummy-3", 5);
+ add_callback_log(&include_bug, dummy_cb_fn);
+ close_temp_logs();
+ tor_log_update_sigsafe_err_fds();
+
+ n = tor_log_get_sigsafe_err_fds(&fds);
+ tt_int_op(n, ==, 2);
+ tt_int_op(fds[0], ==, STDERR_FILENO);
+ tt_int_op(fds[1], ==, 3);
+
+ /* Allow STDOUT to replace STDERR. */
+ add_stream_log(&include_bug, "dummy-4", STDOUT_FILENO);
+ tor_log_update_sigsafe_err_fds();
+ n = tor_log_get_sigsafe_err_fds(&fds);
+ tt_int_op(n, ==, 2);
+ tt_int_op(fds[0], ==, 3);
+ tt_int_op(fds[1], ==, STDOUT_FILENO);
+
+ /* But don't allow it to replace explicit STDERR. */
+ add_stream_log(&include_bug, "dummy-5", STDERR_FILENO);
+ tor_log_update_sigsafe_err_fds();
+ n = tor_log_get_sigsafe_err_fds(&fds);
+ tt_int_op(n, ==, 3);
+ tt_int_op(fds[0], ==, STDERR_FILENO);
+ tt_int_op(fds[1], ==, STDOUT_FILENO);
+ tt_int_op(fds[2], ==, 3);
+
+ /* Don't overflow the array. */
+ {
+ int i;
+ for (i=5; i<20; ++i) {
+ add_stream_log(&include_bug, "x-dummy", i);
+ }
+ }
+ tor_log_update_sigsafe_err_fds();
+ n = tor_log_get_sigsafe_err_fds(&fds);
+ tt_int_op(n, ==, 8);
+
+ done:
+ ;
+}
+
+static void
+test_sigsafe_err(void *arg)
+{
+ const char *fn=get_fname("sigsafe_err_log");
+ char *content=NULL;
+ log_severity_list_t include_bug;
+ smartlist_t *lines = smartlist_new();
+ (void)arg;
+
+ set_log_severity_config(LOG_WARN, LOG_ERR, &include_bug);
+
+ init_logging();
+ mark_logs_temp();
+ add_file_log(&include_bug, fn);
+ tor_log_update_sigsafe_err_fds();
+ close_temp_logs();
+
+ close(STDERR_FILENO);
+ log_err(LD_BUG, "Say, this isn't too cool.");
+ tor_log_err_sigsafe("Minimal.\n", NULL);
+
+ set_log_time_granularity(100*1000);
+ tor_log_err_sigsafe("Testing any ",
+ "attempt to manually log ",
+ "from a signal.\n",
+ NULL);
+ mark_logs_temp();
+ close_temp_logs();
+ close(STDERR_FILENO);
+ content = read_file_to_str(fn, 0, NULL);
+
+ tt_assert(content != NULL);
+ tor_split_lines(lines, content, (int)strlen(content));
+ tt_int_op(smartlist_len(lines), >=, 5);
+
+ if (strstr(smartlist_get(lines, 0), "opening new log file"))
+ smartlist_del_keeporder(lines, 0);
+ tt_assert(strstr(smartlist_get(lines, 0), "Say, this isn't too cool"));
+ /* Next line is blank. */
+ tt_assert(!strcmpstart(smartlist_get(lines, 1), "=============="));
+ tt_assert(!strcmpstart(smartlist_get(lines, 2), "Minimal."));
+ /* Next line is blank. */
+ tt_assert(!strcmpstart(smartlist_get(lines, 3), "=============="));
+ tt_str_op(smartlist_get(lines, 4), ==,
+ "Testing any attempt to manually log from a signal.");
+
+ done:
+ tor_free(content);
+ smartlist_free(lines);
+}
+
+struct testcase_t logging_tests[] = {
+ { "sigsafe_err_fds", test_get_sigsafe_err_fds, TT_FORK, NULL, NULL },
+ { "sigsafe_err", test_sigsafe_err, TT_FORK, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_options.c b/src/test/test_options.c
new file mode 100644
index 000000000..737f658e2
--- /dev/null
+++ b/src/test/test_options.c
@@ -0,0 +1,170 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define CONFIG_PRIVATE
+#include "or.h"
+#include "confparse.h"
+#include "config.h"
+#include "test.h"
+
+typedef struct {
+ int severity;
+ uint32_t domain;
+ char *msg;
+} logmsg_t;
+
+static smartlist_t *messages = NULL;
+
+static void
+log_cback(int severity, uint32_t domain, const char *msg)
+{
+ logmsg_t *x = tor_malloc(sizeof(*x));
+ x->severity = severity;
+ x->domain = domain;
+ x->msg = tor_strdup(msg);
+ if (!messages)
+ messages = smartlist_new();
+ smartlist_add(messages, x);
+}
+
+static void
+setup_log_callback(void)
+{
+ log_severity_list_t lst;
+ memset(&lst, 0, sizeof(lst));
+ lst.masks[LOG_ERR - LOG_ERR] = ~0;
+ lst.masks[LOG_WARN - LOG_ERR] = ~0;
+ lst.masks[LOG_NOTICE - LOG_ERR] = ~0;
+ add_callback_log(&lst, log_cback);
+}
+
+static char *
+dump_logs(void)
+{
+ smartlist_t *msgs;
+ char *out;
+ if (! messages)
+ return tor_strdup("");
+ msgs = smartlist_new();
+ SMARTLIST_FOREACH_BEGIN(messages, logmsg_t *, x) {
+ smartlist_add_asprintf(msgs, "[%s] %s",
+ log_level_to_string(x->severity), x->msg);
+ } SMARTLIST_FOREACH_END(x);
+ out = smartlist_join_strings(msgs, "", 0, NULL);
+ SMARTLIST_FOREACH(msgs, char *, cp, tor_free(cp));
+ smartlist_free(msgs);
+ return out;
+}
+
+static void
+clear_log_messages(void)
+{
+ if (!messages)
+ return;
+ SMARTLIST_FOREACH(messages, logmsg_t *, m,
+ { tor_free(m->msg); tor_free(m); });
+ smartlist_free(messages);
+ messages = NULL;
+}
+
+static void
+test_options_validate_impl(const char *configuration,
+ const char *expect_errmsg,
+ int expect_log_severity,
+ const char *expect_log)
+{
+ or_options_t *opt = options_new();
+ or_options_t *dflt;
+ config_line_t *cl=NULL;
+ char *msg=NULL;
+ int r;
+ opt->command = CMD_RUN_TOR;
+ options_init(opt);
+
+ dflt = config_dup(&options_format, opt);
+ clear_log_messages();
+
+ r = config_get_lines(configuration, &cl, 1);
+ tt_int_op(r, ==, 0);
+
+ r = config_assign(&options_format, opt, cl, 0, 0, &msg);
+ tt_int_op(r, ==, 0);
+
+ r = options_validate(NULL, opt, dflt, 0, &msg);
+ if (expect_errmsg && !msg) {
+ TT_DIE(("Expected error message <%s> from <%s>, but got none.",
+ expect_errmsg, configuration));
+ } else if (expect_errmsg && !strstr(msg, expect_errmsg)) {
+ TT_DIE(("Expected error message <%s> from <%s>, but got <%s>.",
+ expect_errmsg, configuration, msg));
+ } else if (!expect_errmsg && msg) {
+ TT_DIE(("Expected no error message from <%s> but got <%s>.",
+ configuration, msg));
+ }
+ tt_int_op((r == 0), ==, (msg == NULL));
+
+ if (expect_log) {
+ int found = 0;
+ if (messages) {
+ SMARTLIST_FOREACH_BEGIN(messages, logmsg_t *, m) {
+ if (m->severity == expect_log_severity &&
+ strstr(m->msg, expect_log)) {
+ found = 1;
+ break;
+ }
+ } SMARTLIST_FOREACH_END(m);
+ }
+ if (!found) {
+ tor_free(msg);
+ msg = dump_logs();
+ TT_DIE(("Expected log message [%s] %s from <%s>, but got <%s>.",
+ log_level_to_string(expect_log_severity), expect_log,
+ configuration, msg));
+ }
+ }
+
+ done:
+ config_free_lines(cl);
+ or_options_free(opt);
+ or_options_free(dflt);
+ tor_free(msg);
+ clear_log_messages();
+}
+
+#define WANT_ERR(config, msg) \
+ test_options_validate_impl((config), (msg), 0, NULL)
+#define WANT_LOG(config, severity, msg) \
+ test_options_validate_impl((config), NULL, (severity), (msg))
+#define WANT_ERR_LOG(config, msg, severity, logmsg) \
+ test_options_validate_impl((config), (msg), (severity), (logmsg))
+#define OK(config) \
+ test_options_validate_impl((config), NULL, 0, NULL)
+
+static void
+test_options_validate(void *arg)
+{
+ (void)arg;
+ setup_log_callback();
+
+ WANT_ERR("ExtORPort 500000", "Invalid ExtORPort");
+
+ WANT_ERR_LOG("ServerTransportOptions trebuchet",
+ "ServerTransportOptions did not parse",
+ LOG_WARN, "Too few arguments");
+ OK("ServerTransportOptions trebuchet sling=snappy");
+ OK("ServerTransportOptions trebuchet sling=");
+ WANT_ERR_LOG("ServerTransportOptions trebuchet slingsnappy",
+ "ServerTransportOptions did not parse",
+ LOG_WARN, "\"slingsnappy\" is not a k=v");
+
+ clear_log_messages();
+ return;
+}
+
+struct testcase_t options_tests[] = {
+ { "validate", test_options_validate, TT_FORK, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_pt.c b/src/test/test_pt.c
index 80707f437..327792105 100644
--- a/src/test/test_pt.c
+++ b/src/test/test_pt.c
@@ -5,9 +5,17 @@
#include "orconfig.h"
#define PT_PRIVATE
+#define UTIL_PRIVATE
+#define STATEFILE_PRIVATE
+#define CONTROL_PRIVATE
#include "or.h"
+#include "config.h"
+#include "confparse.h"
+#include "control.h"
#include "transports.h"
#include "circuitbuild.h"
+#include "util.h"
+#include "statefile.h"
#include "test.h"
static void
@@ -22,64 +30,102 @@ static void
test_pt_parsing(void)
{
char line[200];
+ transport_t *transport = NULL;
+ tor_addr_t test_addr;
managed_proxy_t *mp = tor_malloc(sizeof(managed_proxy_t));
mp->conf_state = PT_PROTO_INFANT;
mp->transports = smartlist_new();
/* incomplete cmethod */
- strcpy(line,"CMETHOD trebuchet");
+ strlcpy(line,"CMETHOD trebuchet",sizeof(line));
test_assert(parse_cmethod_line(line, mp) < 0);
reset_mp(mp);
/* wrong proxy type */
- strcpy(line,"CMETHOD trebuchet dog 127.0.0.1:1999");
+ strlcpy(line,"CMETHOD trebuchet dog 127.0.0.1:1999",sizeof(line));
test_assert(parse_cmethod_line(line, mp) < 0);
reset_mp(mp);
/* wrong addrport */
- strcpy(line,"CMETHOD trebuchet socks4 abcd");
+ strlcpy(line,"CMETHOD trebuchet socks4 abcd",sizeof(line));
test_assert(parse_cmethod_line(line, mp) < 0);
reset_mp(mp);
/* correct line */
- strcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999");
+ strlcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999",sizeof(line));
test_assert(parse_cmethod_line(line, mp) == 0);
- test_assert(smartlist_len(mp->transports));
+ test_assert(smartlist_len(mp->transports) == 1);
+ transport = smartlist_get(mp->transports, 0);
+ /* test registered address of transport */
+ tor_addr_parse(&test_addr, "127.0.0.1");
+ test_assert(tor_addr_eq(&test_addr, &transport->addr));
+ /* test registered port of transport */
+ test_assert(transport->port == 1999);
+ /* test registered SOCKS version of transport */
+ test_assert(transport->socks_version == PROXY_SOCKS5);
+ /* test registered name of transport */
+ test_streq(transport->name, "trebuchet");
reset_mp(mp);
/* incomplete smethod */
- strcpy(line,"SMETHOD trebuchet");
+ strlcpy(line,"SMETHOD trebuchet",sizeof(line));
test_assert(parse_smethod_line(line, mp) < 0);
reset_mp(mp);
/* wrong addr type */
- strcpy(line,"SMETHOD trebuchet abcd");
+ strlcpy(line,"SMETHOD trebuchet abcd",sizeof(line));
test_assert(parse_smethod_line(line, mp) < 0);
reset_mp(mp);
/* cowwect */
- strcpy(line,"SMETHOD trebuchy 127.0.0.1:1999");
+ strlcpy(line,"SMETHOD trebuchy 127.0.0.2:2999",sizeof(line));
test_assert(parse_smethod_line(line, mp) == 0);
+ test_assert(smartlist_len(mp->transports) == 1);
+ transport = smartlist_get(mp->transports, 0);
+ /* test registered address of transport */
+ tor_addr_parse(&test_addr, "127.0.0.2");
+ test_assert(tor_addr_eq(&test_addr, &transport->addr));
+ /* test registered port of transport */
+ test_assert(transport->port == 2999);
+ /* test registered name of transport */
+ test_streq(transport->name, "trebuchy");
reset_mp(mp);
+ /* Include some arguments. Good ones. */
+ strlcpy(line,"SMETHOD trebuchet 127.0.0.1:9999 "
+ "ARGS:counterweight=3,sling=snappy",
+ sizeof(line));
+ test_assert(parse_smethod_line(line, mp) == 0);
+ tt_int_op(1, ==, smartlist_len(mp->transports));
+ {
+ const transport_t *transport = smartlist_get(mp->transports, 0);
+ tt_assert(transport);
+ tt_str_op(transport->name, ==, "trebuchet");
+ tt_int_op(transport->port, ==, 9999);
+ tt_str_op(fmt_addr(&transport->addr), ==, "127.0.0.1");
+ tt_str_op(transport->extra_info_args, ==,
+ "counterweight=3,sling=snappy");
+ }
+ reset_mp(mp);
+
/* unsupported version */
- strcpy(line,"VERSION 666");
+ strlcpy(line,"VERSION 666",sizeof(line));
test_assert(parse_version(line, mp) < 0);
/* incomplete VERSION */
- strcpy(line,"VERSION ");
+ strlcpy(line,"VERSION ",sizeof(line));
test_assert(parse_version(line, mp) < 0);
/* correct VERSION */
- strcpy(line,"VERSION 1");
+ strlcpy(line,"VERSION 1",sizeof(line));
test_assert(parse_version(line, mp) == 0);
done:
@@ -87,6 +133,58 @@ test_pt_parsing(void)
}
static void
+test_pt_get_transport_options(void *arg)
+{
+ char **execve_args;
+ smartlist_t *transport_list = smartlist_new();
+ managed_proxy_t *mp;
+ or_options_t *options = get_options_mutable();
+ char *opt_str = NULL;
+ config_line_t *cl = NULL;
+ (void)arg;
+
+ execve_args = tor_malloc(sizeof(char*)*2);
+ execve_args[0] = tor_strdup("cheeseshop");
+ execve_args[1] = NULL;
+
+ mp = managed_proxy_create(transport_list, execve_args, 1);
+ tt_ptr_op(mp, !=, NULL);
+ opt_str = get_transport_options_for_server_proxy(mp);
+ tt_ptr_op(opt_str, ==, NULL);
+
+ smartlist_add(mp->transports_to_launch, tor_strdup("gruyere"));
+ smartlist_add(mp->transports_to_launch, tor_strdup("roquefort"));
+ smartlist_add(mp->transports_to_launch, tor_strdup("stnectaire"));
+
+ tt_assert(options);
+
+ cl = tor_malloc_zero(sizeof(config_line_t));
+ cl->value = tor_strdup("gruyere melty=10 hardness=se;ven");
+ options->ServerTransportOptions = cl;
+
+ cl = tor_malloc_zero(sizeof(config_line_t));
+ cl->value = tor_strdup("stnectaire melty=4 hardness=three");
+ cl->next = options->ServerTransportOptions;
+ options->ServerTransportOptions = cl;
+
+ cl = tor_malloc_zero(sizeof(config_line_t));
+ cl->value = tor_strdup("pepperjack melty=12 hardness=five");
+ cl->next = options->ServerTransportOptions;
+ options->ServerTransportOptions = cl;
+
+ opt_str = get_transport_options_for_server_proxy(mp);
+ tt_str_op(opt_str, ==,
+ "gruyere:melty=10;gruyere:hardness=se\\;ven;"
+ "stnectaire:melty=4;stnectaire:hardness=three");
+
+ done:
+ tor_free(opt_str);
+ config_free_lines(cl);
+ managed_proxy_destroy(mp, 0);
+ smartlist_free(transport_list);
+}
+
+static void
test_pt_protocol(void)
{
char line[200];
@@ -99,32 +197,32 @@ test_pt_protocol(void)
/* various wrong protocol runs: */
- strcpy(line,"VERSION 1");
+ strlcpy(line,"VERSION 1",sizeof(line));
handle_proxy_line(line, mp);
test_assert(mp->conf_state == PT_PROTO_ACCEPTING_METHODS);
- strcpy(line,"VERSION 1");
+ strlcpy(line,"VERSION 1",sizeof(line));
handle_proxy_line(line, mp);
test_assert(mp->conf_state == PT_PROTO_BROKEN);
reset_mp(mp);
- strcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999");
+ strlcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999",sizeof(line));
handle_proxy_line(line, mp);
test_assert(mp->conf_state == PT_PROTO_BROKEN);
reset_mp(mp);
/* correct protocol run: */
- strcpy(line,"VERSION 1");
+ strlcpy(line,"VERSION 1",sizeof(line));
handle_proxy_line(line, mp);
test_assert(mp->conf_state == PT_PROTO_ACCEPTING_METHODS);
- strcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999");
+ strlcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999",sizeof(line));
handle_proxy_line(line, mp);
test_assert(mp->conf_state == PT_PROTO_ACCEPTING_METHODS);
- strcpy(line,"CMETHODS DONE");
+ strlcpy(line,"CMETHODS DONE",sizeof(line));
handle_proxy_line(line, mp);
test_assert(mp->conf_state == PT_PROTO_CONFIGURED);
@@ -132,12 +230,223 @@ test_pt_protocol(void)
tor_free(mp);
}
+static void
+test_pt_get_extrainfo_string(void *arg)
+{
+ managed_proxy_t *mp1 = NULL, *mp2 = NULL;
+ char **argv1, **argv2;
+ smartlist_t *t1 = smartlist_new(), *t2 = smartlist_new();
+ int r;
+ char *s = NULL;
+ (void) arg;
+
+ argv1 = tor_malloc_zero(sizeof(char*)*3);
+ argv1[0] = tor_strdup("ewige");
+ argv1[1] = tor_strdup("Blumenkraft");
+ argv1[2] = NULL;
+ argv2 = tor_malloc_zero(sizeof(char*)*4);
+ argv2[0] = tor_strdup("und");
+ argv2[1] = tor_strdup("ewige");
+ argv2[2] = tor_strdup("Schlangenkraft");
+ argv2[3] = NULL;
+
+ mp1 = managed_proxy_create(t1, argv1, 1);
+ mp2 = managed_proxy_create(t2, argv2, 1);
+
+ r = parse_smethod_line("SMETHOD hagbard 127.0.0.1:5555", mp1);
+ tt_int_op(r, ==, 0);
+ r = parse_smethod_line("SMETHOD celine 127.0.0.1:1723 ARGS:card=no-enemy",
+ mp2);
+ tt_int_op(r, ==, 0);
+
+ /* Force these proxies to look "completed" or they won't generate output. */
+ mp1->conf_state = mp2->conf_state = PT_PROTO_COMPLETED;
+
+ s = pt_get_extra_info_descriptor_string();
+ tt_assert(s);
+ tt_str_op(s, ==,
+ "transport hagbard 127.0.0.1:5555\n"
+ "transport celine 127.0.0.1:1723 card=no-enemy\n");
+
+ done:
+ /* XXXX clean up better */
+ smartlist_free(t1);
+ smartlist_free(t2);
+ tor_free(s);
+}
+
+#ifdef _WIN32
+#define STDIN_HANDLE HANDLE
+#else
+#define STDIN_HANDLE FILE
+#endif
+
+static smartlist_t *
+tor_get_lines_from_handle_replacement(STDIN_HANDLE *handle,
+ enum stream_status *stream_status_out)
+{
+ static int times_called = 0;
+ smartlist_t *retval_sl = smartlist_new();
+
+ (void) handle;
+ (void) stream_status_out;
+
+ /* Generate some dummy CMETHOD lines the first 5 times. The 6th
+ time, send 'CMETHODS DONE' to finish configuring the proxy. */
+ if (times_called++ != 5) {
+ smartlist_add_asprintf(retval_sl, "SMETHOD mock%d 127.0.0.1:555%d",
+ times_called, times_called);
+ } else {
+ smartlist_add(retval_sl, tor_strdup("SMETHODS DONE"));
+ }
+
+ return retval_sl;
+}
+
+/* NOP mock */
+static void
+tor_process_handle_destroy_replacement(process_handle_t *process_handle,
+ int also_terminate_process)
+{
+ (void) process_handle;
+ (void) also_terminate_process;
+}
+
+static or_state_t *dummy_state = NULL;
+
+static or_state_t *
+get_or_state_replacement(void)
+{
+ return dummy_state;
+}
+
+static int controlevent_n = 0;
+static uint16_t controlevent_event = 0;
+static smartlist_t *controlevent_msgs = NULL;
+
+static void
+send_control_event_string_replacement(uint16_t event, event_format_t which,
+ const char *msg)
+{
+ (void) which;
+ ++controlevent_n;
+ controlevent_event = event;
+ if (!controlevent_msgs)
+ controlevent_msgs = smartlist_new();
+ smartlist_add(controlevent_msgs, tor_strdup(msg));
+}
+
+/* Test the configure_proxy() function. */
+static void
+test_pt_configure_proxy(void *arg)
+{
+ int i, retval;
+ managed_proxy_t *mp = NULL;
+ (void) arg;
+
+ dummy_state = tor_malloc_zero(sizeof(or_state_t));
+
+ MOCK(tor_get_lines_from_handle,
+ tor_get_lines_from_handle_replacement);
+ MOCK(tor_process_handle_destroy,
+ tor_process_handle_destroy_replacement);
+ MOCK(get_or_state,
+ get_or_state_replacement);
+ MOCK(send_control_event_string,
+ send_control_event_string_replacement);
+
+ control_testing_set_global_event_mask(EVENT_TRANSPORT_LAUNCHED);
+
+ mp = tor_malloc(sizeof(managed_proxy_t));
+ mp->conf_state = PT_PROTO_ACCEPTING_METHODS;
+ mp->transports = smartlist_new();
+ mp->transports_to_launch = smartlist_new();
+ mp->process_handle = tor_malloc_zero(sizeof(process_handle_t));
+ mp->argv = tor_malloc_zero(sizeof(char*)*2);
+ mp->argv[0] = tor_strdup("<testcase>");
+ mp->is_server = 1;
+
+ /* Test the return value of configure_proxy() by calling it some
+ times while it is uninitialized and then finally finalizing its
+ configuration. */
+ for (i = 0 ; i < 5 ; i++) {
+ retval = configure_proxy(mp);
+ /* retval should be zero because proxy hasn't finished configuring yet */
+ test_assert(retval == 0);
+ /* check the number of registered transports */
+ test_assert(smartlist_len(mp->transports) == i+1);
+ /* check that the mp is still waiting for transports */
+ test_assert(mp->conf_state == PT_PROTO_ACCEPTING_METHODS);
+ }
+
+ /* this last configure_proxy() should finalize the proxy configuration. */
+ retval = configure_proxy(mp);
+ /* retval should be 1 since the proxy finished configuring */
+ test_assert(retval == 1);
+ /* check the mp state */
+ test_assert(mp->conf_state == PT_PROTO_COMPLETED);
+
+ tt_int_op(controlevent_n, ==, 5);
+ tt_int_op(controlevent_event, ==, EVENT_TRANSPORT_LAUNCHED);
+ tt_int_op(smartlist_len(controlevent_msgs), ==, 5);
+ smartlist_sort_strings(controlevent_msgs);
+ tt_str_op(smartlist_get(controlevent_msgs, 0), ==,
+ "650 TRANSPORT_LAUNCHED server mock1 127.0.0.1 5551\r\n");
+ tt_str_op(smartlist_get(controlevent_msgs, 1), ==,
+ "650 TRANSPORT_LAUNCHED server mock2 127.0.0.1 5552\r\n");
+ tt_str_op(smartlist_get(controlevent_msgs, 2), ==,
+ "650 TRANSPORT_LAUNCHED server mock3 127.0.0.1 5553\r\n");
+ tt_str_op(smartlist_get(controlevent_msgs, 3), ==,
+ "650 TRANSPORT_LAUNCHED server mock4 127.0.0.1 5554\r\n");
+ tt_str_op(smartlist_get(controlevent_msgs, 4), ==,
+ "650 TRANSPORT_LAUNCHED server mock5 127.0.0.1 5555\r\n");
+
+ { /* check that the transport info were saved properly in the tor state */
+ config_line_t *transport_in_state = NULL;
+ smartlist_t *transport_info_sl = smartlist_new();
+ char *name_of_transport = NULL;
+ char *bindaddr = NULL;
+
+ /* Get the bindaddr for "mock1" and check it against the bindaddr
+ that the mocked tor_get_lines_from_handle() generated. */
+ transport_in_state = get_transport_in_state_by_name("mock1");
+ test_assert(transport_in_state);
+ smartlist_split_string(transport_info_sl, transport_in_state->value,
+ NULL, 0, 0);
+ name_of_transport = smartlist_get(transport_info_sl, 0);
+ bindaddr = smartlist_get(transport_info_sl, 1);
+ tt_str_op(name_of_transport, ==, "mock1");
+ tt_str_op(bindaddr, ==, "127.0.0.1:5551");
+
+ SMARTLIST_FOREACH(transport_info_sl, char *, cp, tor_free(cp));
+ smartlist_free(transport_info_sl);
+ }
+
+ done:
+ tor_free(dummy_state);
+ UNMOCK(tor_get_lines_from_handle);
+ UNMOCK(tor_process_handle_destroy);
+ UNMOCK(get_or_state);
+ UNMOCK(send_control_event_string);
+ if (controlevent_msgs) {
+ SMARTLIST_FOREACH(controlevent_msgs, char *, cp, tor_free(cp));
+ smartlist_free(controlevent_msgs);
+ controlevent_msgs = NULL;
+ }
+}
+
#define PT_LEGACY(name) \
{ #name, legacy_test_helper, 0, &legacy_setup, test_pt_ ## name }
struct testcase_t pt_tests[] = {
PT_LEGACY(parsing),
PT_LEGACY(protocol),
+ { "get_transport_options", test_pt_get_transport_options, TT_FORK,
+ NULL, NULL },
+ { "get_extrainfo_string", test_pt_get_extrainfo_string, TT_FORK,
+ NULL, NULL },
+ { "configure_proxy",test_pt_configure_proxy, TT_FORK,
+ NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_replay.c b/src/test/test_replay.c
index de841ad59..b48f582f5 100644
--- a/src/test/test_replay.c
+++ b/src/test/test_replay.c
@@ -32,6 +32,40 @@ test_replaycache_alloc(void)
}
static void
+test_replaycache_badalloc(void)
+{
+ replaycache_t *r = NULL;
+
+ /* Negative horizon should fail */
+ r = replaycache_new(-600, 300);
+ test_assert(r == NULL);
+ /* Negative interval should get adjusted to zero */
+ r = replaycache_new(600, -300);
+ test_assert(r != NULL);
+ test_eq(r->scrub_interval, 0);
+ replaycache_free(r);
+ /* Negative horizon and negative interval should still fail */
+ r = replaycache_new(-600, -300);
+ test_assert(r == NULL);
+
+ done:
+ if (r) replaycache_free(r);
+
+ return;
+}
+
+static void
+test_replaycache_free_null(void)
+{
+ replaycache_free(NULL);
+ /* Assert that we're here without horrible death */
+ test_assert(1);
+
+ done:
+ return;
+}
+
+static void
test_replaycache_miss(void)
{
replaycache_t *r = NULL;
@@ -42,7 +76,13 @@ test_replaycache_miss(void)
result =
replaycache_add_and_test_internal(1200, r, test_buffer,
- (int)strlen(test_buffer), NULL);
+ strlen(test_buffer), NULL);
+ test_eq(result, 0);
+
+ /* poke the bad-parameter error case too */
+ result =
+ replaycache_add_and_test_internal(1200, NULL, test_buffer,
+ strlen(test_buffer), NULL);
test_eq(result, 0);
done:
@@ -62,12 +102,12 @@ test_replaycache_hit(void)
result =
replaycache_add_and_test_internal(1200, r, test_buffer,
- (int)strlen(test_buffer), NULL);
+ strlen(test_buffer), NULL);
test_eq(result, 0);
result =
replaycache_add_and_test_internal(1300, r, test_buffer,
- (int)strlen(test_buffer), NULL);
+ strlen(test_buffer), NULL);
test_eq(result, 1);
done:
@@ -87,17 +127,17 @@ test_replaycache_age(void)
result =
replaycache_add_and_test_internal(1200, r, test_buffer,
- (int)strlen(test_buffer), NULL);
+ strlen(test_buffer), NULL);
test_eq(result, 0);
result =
replaycache_add_and_test_internal(1300, r, test_buffer,
- (int)strlen(test_buffer), NULL);
+ strlen(test_buffer), NULL);
test_eq(result, 1);
result =
replaycache_add_and_test_internal(3000, r, test_buffer,
- (int)strlen(test_buffer), NULL);
+ strlen(test_buffer), NULL);
test_eq(result, 0);
done:
@@ -118,12 +158,12 @@ test_replaycache_elapsed(void)
result =
replaycache_add_and_test_internal(1200, r, test_buffer,
- (int)strlen(test_buffer), NULL);
+ strlen(test_buffer), NULL);
test_eq(result, 0);
result =
replaycache_add_and_test_internal(1300, r, test_buffer,
- (int)strlen(test_buffer), &elapsed);
+ strlen(test_buffer), &elapsed);
test_eq(result, 1);
test_eq(elapsed, 100);
@@ -144,18 +184,102 @@ test_replaycache_noexpire(void)
result =
replaycache_add_and_test_internal(1200, r, test_buffer,
- (int)strlen(test_buffer), NULL);
+ strlen(test_buffer), NULL);
test_eq(result, 0);
result =
replaycache_add_and_test_internal(1300, r, test_buffer,
- (int)strlen(test_buffer), NULL);
+ strlen(test_buffer), NULL);
test_eq(result, 1);
result =
replaycache_add_and_test_internal(3000, r, test_buffer,
- (int)strlen(test_buffer), NULL);
+ strlen(test_buffer), NULL);
+ test_eq(result, 1);
+
+ done:
+ if (r) replaycache_free(r);
+
+ return;
+}
+
+static void
+test_replaycache_scrub(void)
+{
+ replaycache_t *r = NULL;
+ int result;
+
+ r = replaycache_new(600, 300);
+ test_assert(r != NULL);
+
+ /* Set up like in test_replaycache_hit() */
+ result =
+ replaycache_add_and_test_internal(100, r, test_buffer,
+ strlen(test_buffer), NULL);
+ test_eq(result, 0);
+
+ result =
+ replaycache_add_and_test_internal(200, r, test_buffer,
+ strlen(test_buffer), NULL);
+ test_eq(result, 1);
+
+ /*
+ * Poke a few replaycache_scrub_if_needed_internal() error cases that
+ * can't happen through replaycache_add_and_test_internal()
+ */
+
+ /* Null cache */
+ replaycache_scrub_if_needed_internal(300, NULL);
+ /* Assert we're still here */
+ test_assert(1);
+
+ /* Make sure we hit the aging-out case too */
+ replaycache_scrub_if_needed_internal(1500, r);
+ /* Assert that we aged it */
+ test_eq(digestmap_size(r->digests_seen), 0);
+
+ done:
+ if (r) replaycache_free(r);
+
+ return;
+}
+
+static void
+test_replaycache_future(void)
+{
+ replaycache_t *r = NULL;
+ int result;
+ time_t elapsed = 0;
+
+ r = replaycache_new(600, 300);
+ test_assert(r != NULL);
+
+ /* Set up like in test_replaycache_hit() */
+ result =
+ replaycache_add_and_test_internal(100, r, test_buffer,
+ strlen(test_buffer), &elapsed);
+ test_eq(result, 0);
+ /* elapsed should still be 0, since it wasn't written */
+ test_eq(elapsed, 0);
+
+ result =
+ replaycache_add_and_test_internal(200, r, test_buffer,
+ strlen(test_buffer), &elapsed);
+ test_eq(result, 1);
+ /* elapsed should be the time since the last hit */
+ test_eq(elapsed, 100);
+
+ /*
+ * Now let's turn the clock back to get coverage on the cache entry from the
+ * future not-supposed-to-happen case.
+ */
+ result =
+ replaycache_add_and_test_internal(150, r, test_buffer,
+ strlen(test_buffer), &elapsed);
+ /* We should still get a hit */
test_eq(result, 1);
+ /* ...but it shouldn't let us see a negative elapsed time */
+ test_eq(elapsed, 0);
done:
if (r) replaycache_free(r);
@@ -163,16 +287,62 @@ test_replaycache_noexpire(void)
return;
}
+static void
+test_replaycache_realtime(void)
+{
+ replaycache_t *r = NULL;
+ /*
+ * Negative so we fail if replaycache_add_test_and_elapsed() doesn't
+ * write to elapsed.
+ */
+ time_t elapsed = -1;
+ int result;
+
+ /* Test the realtime as well as *_internal() entry points */
+ r = replaycache_new(600, 300);
+ test_assert(r != NULL);
+
+ /* This should miss */
+ result =
+ replaycache_add_and_test(r, test_buffer, strlen(test_buffer));
+ test_eq(result, 0);
+
+ /* This should hit */
+ result =
+ replaycache_add_and_test(r, test_buffer, strlen(test_buffer));
+ test_eq(result, 1);
+
+ /* This should hit and return a small elapsed time */
+ result =
+ replaycache_add_test_and_elapsed(r, test_buffer,
+ strlen(test_buffer), &elapsed);
+ test_eq(result, 1);
+ test_assert(elapsed >= 0);
+ test_assert(elapsed <= 5);
+
+ /* Scrub it to exercise that entry point too */
+ replaycache_scrub_if_needed(r);
+
+ done:
+ if (r) replaycache_free(r);
+ return;
+}
+
#define REPLAYCACHE_LEGACY(name) \
{ #name, legacy_test_helper, 0, &legacy_setup, test_replaycache_ ## name }
struct testcase_t replaycache_tests[] = {
REPLAYCACHE_LEGACY(alloc),
+ REPLAYCACHE_LEGACY(badalloc),
+ REPLAYCACHE_LEGACY(free_null),
REPLAYCACHE_LEGACY(miss),
REPLAYCACHE_LEGACY(hit),
REPLAYCACHE_LEGACY(age),
REPLAYCACHE_LEGACY(elapsed),
REPLAYCACHE_LEGACY(noexpire),
+ REPLAYCACHE_LEGACY(scrub),
+ REPLAYCACHE_LEGACY(future),
+ REPLAYCACHE_LEGACY(realtime),
END_OF_TESTCASES
};
diff --git a/src/test/test_socks.c b/src/test/test_socks.c
new file mode 100644
index 000000000..4ce61e068
--- /dev/null
+++ b/src/test/test_socks.c
@@ -0,0 +1,393 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+#include "buffers.h"
+#include "config.h"
+#include "test.h"
+
+typedef struct socks_test_data_t {
+ socks_request_t *req;
+ buf_t *buf;
+} socks_test_data_t;
+
+static void *
+socks_test_setup(const struct testcase_t *testcase)
+{
+ socks_test_data_t *data = tor_malloc(sizeof(socks_test_data_t));
+ (void)testcase;
+ data->buf = buf_new_with_capacity(256);
+ data->req = socks_request_new();
+ config_register_addressmaps(get_options());
+ return data;
+}
+static int
+socks_test_cleanup(const struct testcase_t *testcase, void *ptr)
+{
+ socks_test_data_t *data = ptr;
+ (void)testcase;
+ buf_free(data->buf);
+ socks_request_free(data->req);
+ tor_free(data);
+ return 1;
+}
+
+const struct testcase_setup_t socks_setup = {
+ socks_test_setup, socks_test_cleanup
+};
+
+#define SOCKS_TEST_INIT() \
+ socks_test_data_t *testdata = ptr; \
+ buf_t *buf = testdata->buf; \
+ socks_request_t *socks = testdata->req;
+#define ADD_DATA(buf, s) \
+ write_to_buf(s, sizeof(s)-1, buf)
+
+static void
+socks_request_clear(socks_request_t *socks)
+{
+ tor_free(socks->username);
+ tor_free(socks->password);
+ memset(socks, 0, sizeof(socks_request_t));
+}
+
+/** Perform unsupported SOCKS 4 commands */
+static void
+test_socks_4_unsupported_commands(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /* SOCKS 4 Send BIND [02] to IP address 2.2.2.2:4369 */
+ ADD_DATA(buf, "\x04\x02\x11\x11\x02\x02\x02\x02\x00");
+ test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks) == -1);
+ test_eq(4, socks->socks_version);
+ test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
+
+ done:
+ ;
+}
+
+/** Perform supported SOCKS 4 commands */
+static void
+test_socks_4_supported_commands(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ test_eq(0, buf_datalen(buf));
+
+ /* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4370 */
+ ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x03\x00");
+ test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks) == 1);
+ test_eq(4, socks->socks_version);
+ test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
+ test_eq(SOCKS_COMMAND_CONNECT, socks->command);
+ test_streq("2.2.2.3", socks->address);
+ test_eq(4370, socks->port);
+ test_assert(socks->got_auth == 0);
+ test_assert(! socks->username);
+
+ test_eq(0, buf_datalen(buf));
+ socks_request_clear(socks);
+
+ /* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4369 with userid*/
+ ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x04me\x00");
+ test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks) == 1);
+ test_eq(4, socks->socks_version);
+ test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
+ test_eq(SOCKS_COMMAND_CONNECT, socks->command);
+ test_streq("2.2.2.4", socks->address);
+ test_eq(4370, socks->port);
+ test_assert(socks->got_auth == 1);
+ test_assert(socks->username);
+ test_eq(2, socks->usernamelen);
+ test_memeq("me", socks->username, 2);
+
+ test_eq(0, buf_datalen(buf));
+ socks_request_clear(socks);
+
+ /* SOCKS 4a Send RESOLVE [F0] request for torproject.org */
+ ADD_DATA(buf, "\x04\xF0\x01\x01\x00\x00\x00\x02me\x00torproject.org\x00");
+ test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks) == 1);
+ test_eq(4, socks->socks_version);
+ test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
+ test_streq("torproject.org", socks->address);
+
+ test_eq(0, buf_datalen(buf));
+
+ done:
+ ;
+}
+
+/** Perform unsupported SOCKS 5 commands */
+static void
+test_socks_5_unsupported_commands(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /* SOCKS 5 Send unsupported BIND [02] command */
+ ADD_DATA(buf, "\x05\x02\x00\x01");
+
+ test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks), 0);
+ test_eq(0, buf_datalen(buf));
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+ ADD_DATA(buf, "\x05\x02\x00\x01\x02\x02\x02\x01\x01\x01");
+ test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks), -1);
+ /* XXX: shouldn't tor reply 'command not supported' [07]? */
+
+ buf_clear(buf);
+ socks_request_clear(socks);
+
+ /* SOCKS 5 Send unsupported UDP_ASSOCIATE [03] command */
+ ADD_DATA(buf, "\x05\x03\x00\x01\x02");
+ test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks), 0);
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(2, socks->reply[1]);
+ ADD_DATA(buf, "\x05\x03\x00\x01\x02\x02\x02\x01\x01\x01");
+ test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks), -1);
+ /* XXX: shouldn't tor reply 'command not supported' [07]? */
+
+ done:
+ ;
+}
+
+/** Perform supported SOCKS 5 commands */
+static void
+test_socks_5_supported_commands(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /* SOCKS 5 Send CONNECT [01] to IP address 2.2.2.2:4369 */
+ ADD_DATA(buf, "\x05\x01\x00");
+ test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks), 0);
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+
+ ADD_DATA(buf, "\x05\x01\x00\x01\x02\x02\x02\x02\x11\x11");
+ test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks), 1);
+ test_streq("2.2.2.2", socks->address);
+ test_eq(4369, socks->port);
+
+ test_eq(0, buf_datalen(buf));
+ socks_request_clear(socks);
+
+ /* SOCKS 5 Send CONNECT [01] to FQDN torproject.org:4369 */
+ ADD_DATA(buf, "\x05\x01\x00");
+ ADD_DATA(buf, "\x05\x01\x00\x03\x0Etorproject.org\x11\x11");
+ test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks), 1);
+
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+ test_streq("torproject.org", socks->address);
+ test_eq(4369, socks->port);
+
+ test_eq(0, buf_datalen(buf));
+ socks_request_clear(socks);
+
+ /* SOCKS 5 Send RESOLVE [F0] request for torproject.org:4369 */
+ ADD_DATA(buf, "\x05\x01\x00");
+ ADD_DATA(buf, "\x05\xF0\x00\x03\x0Etorproject.org\x01\x02");
+ test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks) == 1);
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+ test_streq("torproject.org", socks->address);
+
+ test_eq(0, buf_datalen(buf));
+ socks_request_clear(socks);
+
+ /* SOCKS 5 Send RESOLVE_PTR [F1] for IP address 2.2.2.5 */
+ ADD_DATA(buf, "\x05\x01\x00");
+ ADD_DATA(buf, "\x05\xF1\x00\x01\x02\x02\x02\x05\x01\x03");
+ test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks) == 1);
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+ test_streq("2.2.2.5", socks->address);
+
+ test_eq(0, buf_datalen(buf));
+
+ done:
+ ;
+}
+
+/** Perform SOCKS 5 authentication */
+static void
+test_socks_5_no_authenticate(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /*SOCKS 5 No Authentication */
+ ADD_DATA(buf,"\x05\x01\x00");
+ test_assert(!fetch_from_buf_socks(buf, socks,
+ get_options()->TestSocks,
+ get_options()->SafeSocks));
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(SOCKS_NO_AUTH, socks->reply[1]);
+
+ test_eq(0, buf_datalen(buf));
+
+ /*SOCKS 5 Send username/password anyway - pretend to be broken */
+ ADD_DATA(buf,"\x01\x02\x01\x01\x02\x01\x01");
+ test_assert(!fetch_from_buf_socks(buf, socks,
+ get_options()->TestSocks,
+ get_options()->SafeSocks));
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(1, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+
+ test_eq(2, socks->usernamelen);
+ test_eq(2, socks->passwordlen);
+
+ test_memeq("\x01\x01", socks->username, 2);
+ test_memeq("\x01\x01", socks->password, 2);
+
+ done:
+ ;
+}
+
+/** Perform SOCKS 5 authentication */
+static void
+test_socks_5_authenticate(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /* SOCKS 5 Negotiate username/password authentication */
+ ADD_DATA(buf, "\x05\x01\x02");
+
+ test_assert(!fetch_from_buf_socks(buf, socks,
+ get_options()->TestSocks,
+ get_options()->SafeSocks));
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(SOCKS_USER_PASS, socks->reply[1]);
+ test_eq(5, socks->socks_version);
+
+ test_eq(0, buf_datalen(buf));
+
+ /* SOCKS 5 Send username/password */
+ ADD_DATA(buf, "\x01\x02me\x08mypasswd");
+ test_assert(!fetch_from_buf_socks(buf, socks,
+ get_options()->TestSocks,
+ get_options()->SafeSocks));
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(1, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+
+ test_eq(2, socks->usernamelen);
+ test_eq(8, socks->passwordlen);
+
+ test_memeq("me", socks->username, 2);
+ test_memeq("mypasswd", socks->password, 8);
+
+ done:
+ ;
+}
+
+/** Perform SOCKS 5 authentication and send data all in one go */
+static void
+test_socks_5_authenticate_with_data(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /* SOCKS 5 Negotiate username/password authentication */
+ ADD_DATA(buf, "\x05\x01\x02");
+
+ test_assert(!fetch_from_buf_socks(buf, socks,
+ get_options()->TestSocks,
+ get_options()->SafeSocks));
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(SOCKS_USER_PASS, socks->reply[1]);
+ test_eq(5, socks->socks_version);
+
+ test_eq(0, buf_datalen(buf));
+
+ /* SOCKS 5 Send username/password */
+ /* SOCKS 5 Send CONNECT [01] to IP address 2.2.2.2:4369 */
+ ADD_DATA(buf, "\x01\x02me\x03you\x05\x01\x00\x01\x02\x02\x02\x02\x11\x11");
+ test_assert(fetch_from_buf_socks(buf, socks,
+ get_options()->TestSocks,
+ get_options()->SafeSocks) == 1);
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(1, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+
+ test_streq("2.2.2.2", socks->address);
+ test_eq(4369, socks->port);
+
+ test_eq(2, socks->usernamelen);
+ test_eq(3, socks->passwordlen);
+ test_memeq("me", socks->username, 2);
+ test_memeq("you", socks->password, 3);
+
+ done:
+ ;
+}
+
+/** Perform SOCKS 5 authentication before method negotiated */
+static void
+test_socks_5_auth_before_negotiation(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /* SOCKS 5 Send username/password */
+ ADD_DATA(buf, "\x01\x02me\x02me");
+ test_assert(fetch_from_buf_socks(buf, socks,
+ get_options()->TestSocks,
+ get_options()->SafeSocks) == -1);
+ test_eq(0, socks->socks_version);
+ test_eq(0, socks->replylen);
+ test_eq(0, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+
+ done:
+ ;
+}
+
+#define SOCKSENT(name) \
+ { #name, test_socks_##name, TT_FORK, &socks_setup, NULL }
+
+struct testcase_t socks_tests[] = {
+ SOCKSENT(4_unsupported_commands),
+ SOCKSENT(4_supported_commands),
+
+ SOCKSENT(5_unsupported_commands),
+ SOCKSENT(5_supported_commands),
+ SOCKSENT(5_no_authenticate),
+ SOCKSENT(5_auth_before_negotiation),
+ SOCKSENT(5_authenticate),
+ SOCKSENT(5_authenticate_with_data),
+
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_util.c b/src/test/test_util.c
index 65d9d2f87..376e3aab8 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -4,6 +4,7 @@
/* See LICENSE for licensing information */
#include "orconfig.h"
+#define COMPAT_PRIVATE
#define CONTROL_PRIVATE
#define MEMPOOL_PRIVATE
#define UTIL_PRIVATE
@@ -101,6 +102,107 @@ test_util_read_file_eof_zero_bytes(void *arg)
test_util_read_until_eof_impl("tor_test_fifo_empty", 0, 10000);
}
+/* Test the basic expected behaviour for write_chunks_to_file.
+ * NOTE: This will need to be updated if we ever change the tempfile location
+ * or extension */
+static void
+test_util_write_chunks_to_file(void *arg)
+{
+ char *fname = NULL;
+ char *tempname = NULL;
+ char *str = NULL;
+ int r;
+ struct stat st;
+
+ /* These should be two different sizes to ensure the data is different
+ * between the data file and the temp file's 'known string' */
+ int temp_str_len = 1024;
+ int data_str_len = 512;
+ char *data_str = tor_malloc(data_str_len);
+ char *temp_str = tor_malloc(temp_str_len);
+
+ smartlist_t *chunks = smartlist_new();
+ sized_chunk_t c = {data_str, data_str_len/2};
+ sized_chunk_t c2 = {data_str + data_str_len/2, data_str_len/2};
+ (void)arg;
+
+ crypto_rand(temp_str, temp_str_len);
+ crypto_rand(data_str, data_str_len);
+
+ // Ensure it can write multiple chunks
+
+ smartlist_add(chunks, &c);
+ smartlist_add(chunks, &c2);
+
+ /*
+ * Check if it writes using a tempfile
+ */
+ fname = tor_strdup(get_fname("write_chunks_with_tempfile"));
+ tor_asprintf(&tempname, "%s.tmp", fname);
+
+ // write a known string to a file where the tempfile will be
+ r = write_bytes_to_file(tempname, temp_str, temp_str_len, 1);
+ tt_int_op(r, ==, 0);
+
+ // call write_chunks_to_file
+ r = write_chunks_to_file(fname, chunks, 1, 0);
+ tt_int_op(r, ==, 0);
+
+ // assert the file has been written (expected size)
+ str = read_file_to_str(fname, RFTS_BIN, &st);
+ tt_assert(str != NULL);
+ tt_int_op(st.st_size, ==, data_str_len);
+ test_mem_op(data_str, ==, str, data_str_len);
+ tor_free(str);
+
+ // assert that the tempfile is removed (should not leave artifacts)
+ str = read_file_to_str(tempname, RFTS_BIN|RFTS_IGNORE_MISSING, &st);
+ tt_assert(str == NULL);
+
+ // Remove old testfile for second test
+ r = unlink(fname);
+ tt_int_op(r, ==, 0);
+ tor_free(fname);
+ tor_free(tempname);
+
+ /*
+ * Check if it skips using a tempfile with flags
+ */
+ fname = tor_strdup(get_fname("write_chunks_with_no_tempfile"));
+ tor_asprintf(&tempname, "%s.tmp", fname);
+
+ // write a known string to a file where the tempfile will be
+ r = write_bytes_to_file(tempname, temp_str, temp_str_len, 1);
+ tt_int_op(r, ==, 0);
+
+ // call write_chunks_to_file with no_tempfile = true
+ r = write_chunks_to_file(fname, chunks, 1, 1);
+ tt_int_op(r, ==, 0);
+
+ // assert the file has been written (expected size)
+ str = read_file_to_str(fname, RFTS_BIN, &st);
+ tt_assert(str != NULL);
+ tt_int_op(st.st_size, ==, data_str_len);
+ test_mem_op(data_str, ==, str, data_str_len);
+ tor_free(str);
+
+ // assert the tempfile still contains the known string
+ str = read_file_to_str(tempname, RFTS_BIN, &st);
+ tt_assert(str != NULL);
+ tt_int_op(st.st_size, ==, temp_str_len);
+ test_mem_op(temp_str, ==, str, temp_str_len);
+
+ done:
+ unlink(fname);
+ unlink(tempname);
+ smartlist_free(chunks);
+ tor_free(fname);
+ tor_free(tempname);
+ tor_free(str);
+ tor_free(data_str);
+ tor_free(temp_str);
+}
+
static void
test_util_time(void)
{
@@ -796,6 +898,64 @@ test_util_expand_filename(void)
}
#endif
+/** Test tor_escape_str_for_pt_args(). */
+static void
+test_util_escape_string_socks(void)
+{
+ char *escaped_string = NULL;
+
+ /** Simple backslash escape. */
+ escaped_string = tor_escape_str_for_pt_args("This is a backslash: \\",";\\");
+ test_assert(escaped_string);
+ test_streq(escaped_string, "This is a backslash: \\\\");
+ tor_free(escaped_string);
+
+ /** Simple semicolon escape. */
+ escaped_string = tor_escape_str_for_pt_args("First rule:Do not use ;",";\\");
+ test_assert(escaped_string);
+ test_streq(escaped_string, "First rule:Do not use \\;");
+ tor_free(escaped_string);
+
+ /** Empty string. */
+ escaped_string = tor_escape_str_for_pt_args("", ";\\");
+ test_assert(escaped_string);
+ test_streq(escaped_string, "");
+ tor_free(escaped_string);
+
+ /** Escape all characters. */
+ escaped_string = tor_escape_str_for_pt_args(";\\;\\", ";\\");
+ test_assert(escaped_string);
+ test_streq(escaped_string, "\\;\\\\\\;\\\\");
+ tor_free(escaped_string);
+
+ escaped_string = tor_escape_str_for_pt_args(";", ";\\");
+ test_assert(escaped_string);
+ test_streq(escaped_string, "\\;");
+ tor_free(escaped_string);
+
+ done:
+ tor_free(escaped_string);
+}
+
+static void
+test_util_string_is_key_value(void *ptr)
+{
+ (void)ptr;
+ test_assert(string_is_key_value(LOG_WARN, "key=value"));
+ test_assert(string_is_key_value(LOG_WARN, "k=v"));
+ test_assert(string_is_key_value(LOG_WARN, "key="));
+ test_assert(string_is_key_value(LOG_WARN, "x="));
+ test_assert(string_is_key_value(LOG_WARN, "xx="));
+ test_assert(!string_is_key_value(LOG_WARN, "=value"));
+ test_assert(!string_is_key_value(LOG_WARN, "=x"));
+ test_assert(!string_is_key_value(LOG_WARN, "="));
+
+ /* ??? */
+ /* test_assert(!string_is_key_value(LOG_WARN, "===")); */
+ done:
+ ;
+}
+
/** Test basic string functionality. */
static void
test_util_strmisc(void)
@@ -2223,6 +2383,7 @@ test_util_load_win_lib(void *ptr)
}
#endif
+#ifndef _WIN32
static void
clear_hex_errno(char *hex_errno)
{
@@ -2267,6 +2428,7 @@ test_util_exit_status(void *ptr)
done:
;
}
+#endif
#ifndef _WIN32
/** Check that fgets waits until a full line, and not return a partial line, on
@@ -2568,14 +2730,14 @@ test_util_spawn_background_partial_read(void *ptr)
}
/**
- * Test for format_hex_number_for_helper_exit_status()
+ * Test for format_hex_number_sigsafe()
*/
static void
test_util_format_hex_number(void *ptr)
{
int i, len;
- char buf[HEX_ERRNO_SIZE + 1];
+ char buf[33];
const struct {
const char *str;
unsigned int x;
@@ -2584,6 +2746,8 @@ test_util_format_hex_number(void *ptr)
{"1", 1},
{"273A", 0x273a},
{"FFFF", 0xffff},
+ {"7FFFFFFF", 0x7fffffff},
+ {"FFFFFFFF", 0xffffffff},
#if UINT_MAX >= 0xffffffff
{"31BC421D", 0x31bc421d},
{"FFFFFFFF", 0xffffffff},
@@ -2594,19 +2758,73 @@ test_util_format_hex_number(void *ptr)
(void)ptr;
for (i = 0; test_data[i].str != NULL; ++i) {
- len = format_hex_number_for_helper_exit_status(test_data[i].x,
- buf, HEX_ERRNO_SIZE);
+ len = format_hex_number_sigsafe(test_data[i].x, buf, sizeof(buf));
test_neq(len, 0);
- buf[len] = '\0';
+ test_eq(len, strlen(buf));
test_streq(buf, test_data[i].str);
}
+ test_eq(4, format_hex_number_sigsafe(0xffff, buf, 5));
+ test_streq(buf, "FFFF");
+ test_eq(0, format_hex_number_sigsafe(0xffff, buf, 4));
+ test_eq(0, format_hex_number_sigsafe(0, buf, 1));
+
done:
return;
}
/**
- * Test that we can properly format q Windows command line
+ * Test for format_hex_number_sigsafe()
+ */
+
+static void
+test_util_format_dec_number(void *ptr)
+{
+ int i, len;
+ char buf[33];
+ const struct {
+ const char *str;
+ unsigned int x;
+ } test_data[] = {
+ {"0", 0},
+ {"1", 1},
+ {"1234", 1234},
+ {"12345678", 12345678},
+ {"99999999", 99999999},
+ {"100000000", 100000000},
+ {"4294967295", 4294967295u},
+#if UINT_MAX > 0xffffffff
+ {"18446744073709551615", 18446744073709551615u },
+#endif
+ {NULL, 0}
+ };
+
+ (void)ptr;
+
+ for (i = 0; test_data[i].str != NULL; ++i) {
+ len = format_dec_number_sigsafe(test_data[i].x, buf, sizeof(buf));
+ test_neq(len, 0);
+ test_eq(len, strlen(buf));
+ test_streq(buf, test_data[i].str);
+
+ len = format_dec_number_sigsafe(test_data[i].x, buf,
+ (int)(strlen(test_data[i].str) + 1));
+ test_eq(len, strlen(buf));
+ test_streq(buf, test_data[i].str);
+ }
+
+ test_eq(4, format_dec_number_sigsafe(7331, buf, 5));
+ test_streq(buf, "7331");
+ test_eq(0, format_dec_number_sigsafe(7331, buf, 4));
+ test_eq(1, format_dec_number_sigsafe(0, buf, 2));
+ test_eq(0, format_dec_number_sigsafe(0, buf, 1));
+
+ done:
+ return;
+}
+
+/**
+ * Test that we can properly format a Windows command line
*/
static void
test_util_join_win_cmdline(void *ptr)
@@ -2817,7 +3035,7 @@ test_util_eat_whitespace(void *ptr)
(void)ptr;
/* Try one leading ws */
- strcpy(str, "fuubaar");
+ strlcpy(str, "fuubaar", sizeof(str));
for (i = 0; i < sizeof(ws); ++i) {
str[0] = ws[i];
test_eq_ptr(str + 1, eat_whitespace(str));
@@ -2832,14 +3050,14 @@ test_util_eat_whitespace(void *ptr)
test_eq_ptr(str, eat_whitespace_eos_no_nl(str, str + strlen(str)));
/* Empty string */
- strcpy(str, "");
+ strlcpy(str, "", sizeof(str));
test_eq_ptr(str, eat_whitespace(str));
test_eq_ptr(str, eat_whitespace_eos(str, str));
test_eq_ptr(str, eat_whitespace_no_nl(str));
test_eq_ptr(str, eat_whitespace_eos_no_nl(str, str));
/* Only ws */
- strcpy(str, " \t\r\n");
+ strlcpy(str, " \t\r\n", sizeof(str));
test_eq_ptr(str + strlen(str), eat_whitespace(str));
test_eq_ptr(str + strlen(str), eat_whitespace_eos(str, str + strlen(str)));
test_eq_ptr(str + strlen(str) - 1,
@@ -2847,7 +3065,7 @@ test_util_eat_whitespace(void *ptr)
test_eq_ptr(str + strlen(str) - 1,
eat_whitespace_eos_no_nl(str, str + strlen(str)));
- strcpy(str, " \t\r ");
+ strlcpy(str, " \t\r ", sizeof(str));
test_eq_ptr(str + strlen(str), eat_whitespace(str));
test_eq_ptr(str + strlen(str),
eat_whitespace_eos(str, str + strlen(str)));
@@ -2856,7 +3074,7 @@ test_util_eat_whitespace(void *ptr)
eat_whitespace_eos_no_nl(str, str + strlen(str)));
/* Multiple ws */
- strcpy(str, "fuubaar");
+ strlcpy(str, "fuubaar", sizeof(str));
for (i = 0; i < sizeof(ws); ++i)
str[i] = ws[i];
test_eq_ptr(str + sizeof(ws), eat_whitespace(str));
@@ -2866,28 +3084,28 @@ test_util_eat_whitespace(void *ptr)
eat_whitespace_eos_no_nl(str, str + strlen(str)));
/* Eat comment */
- strcpy(str, "# Comment \n No Comment");
+ strlcpy(str, "# Comment \n No Comment", sizeof(str));
test_streq("No Comment", eat_whitespace(str));
test_streq("No Comment", eat_whitespace_eos(str, str + strlen(str)));
test_eq_ptr(str, eat_whitespace_no_nl(str));
test_eq_ptr(str, eat_whitespace_eos_no_nl(str, str + strlen(str)));
/* Eat comment & ws mix */
- strcpy(str, " # \t Comment \n\t\nNo Comment");
+ strlcpy(str, " # \t Comment \n\t\nNo Comment", sizeof(str));
test_streq("No Comment", eat_whitespace(str));
test_streq("No Comment", eat_whitespace_eos(str, str + strlen(str)));
test_eq_ptr(str + 1, eat_whitespace_no_nl(str));
test_eq_ptr(str + 1, eat_whitespace_eos_no_nl(str, str + strlen(str)));
/* Eat entire comment */
- strcpy(str, "#Comment");
+ strlcpy(str, "#Comment", sizeof(str));
test_eq_ptr(str + strlen(str), eat_whitespace(str));
test_eq_ptr(str + strlen(str), eat_whitespace_eos(str, str + strlen(str)));
test_eq_ptr(str, eat_whitespace_no_nl(str));
test_eq_ptr(str, eat_whitespace_eos_no_nl(str, str + strlen(str)));
/* Blank line, then comment */
- strcpy(str, " \t\n # Comment");
+ strlcpy(str, " \t\n # Comment", sizeof(str));
test_eq_ptr(str + strlen(str), eat_whitespace(str));
test_eq_ptr(str + strlen(str), eat_whitespace_eos(str, str + strlen(str)));
test_eq_ptr(str + 2, eat_whitespace_no_nl(str));
@@ -3212,12 +3430,176 @@ test_util_mathlog(void *arg)
;
}
+static void
+test_util_round_to_next_multiple_of(void *arg)
+{
+ (void)arg;
+
+ test_assert(round_uint64_to_next_multiple_of(0,1) == 0);
+ test_assert(round_uint64_to_next_multiple_of(0,7) == 0);
+
+ test_assert(round_uint64_to_next_multiple_of(99,1) == 99);
+ test_assert(round_uint64_to_next_multiple_of(99,7) == 105);
+ test_assert(round_uint64_to_next_multiple_of(99,9) == 99);
+
+ done:
+ ;
+}
+
+static void
+test_util_strclear(void *arg)
+{
+ static const char *vals[] = { "", "a", "abcdef", "abcdefgh", NULL };
+ int i;
+ char *v = NULL;
+ (void)arg;
+
+ for (i = 0; vals[i]; ++i) {
+ size_t n;
+ v = tor_strdup(vals[i]);
+ n = strlen(v);
+ tor_strclear(v);
+ tt_assert(tor_mem_is_zero(v, n+1));
+ tor_free(v);
+ }
+ done:
+ tor_free(v);
+}
+
#define UTIL_LEGACY(name) \
{ #name, legacy_test_helper, 0, &legacy_setup, test_util_ ## name }
#define UTIL_TEST(name, flags) \
{ #name, test_util_ ## name, flags, NULL, NULL }
+#ifdef FD_CLOEXEC
+#define CAN_CHECK_CLOEXEC
+static int
+fd_is_cloexec(tor_socket_t fd)
+{
+ int flags = fcntl(fd, F_GETFD, 0);
+ return (flags & FD_CLOEXEC) == FD_CLOEXEC;
+}
+#endif
+
+#ifndef _WIN32
+#define CAN_CHECK_NONBLOCK
+static int
+fd_is_nonblocking(tor_socket_t fd)
+{
+ int flags = fcntl(fd, F_GETFL, 0);
+ return (flags & O_NONBLOCK) == O_NONBLOCK;
+}
+#endif
+
+static void
+test_util_socket(void *arg)
+{
+ tor_socket_t fd1 = TOR_INVALID_SOCKET;
+ tor_socket_t fd2 = TOR_INVALID_SOCKET;
+ tor_socket_t fd3 = TOR_INVALID_SOCKET;
+ tor_socket_t fd4 = TOR_INVALID_SOCKET;
+ int n = get_n_open_sockets();
+
+ TT_BLATHER(("Starting with %d open sockets.", n));
+
+ (void)arg;
+
+ fd1 = tor_open_socket_with_extensions(AF_INET, SOCK_STREAM, 0, 0, 0);
+ fd2 = tor_open_socket_with_extensions(AF_INET, SOCK_STREAM, 0, 0, 1);
+ tt_assert(SOCKET_OK(fd1));
+ tt_assert(SOCKET_OK(fd2));
+ tt_int_op(get_n_open_sockets(), ==, n + 2);
+ //fd3 = tor_open_socket_with_extensions(AF_INET, SOCK_STREAM, 0, 1, 0);
+ //fd4 = tor_open_socket_with_extensions(AF_INET, SOCK_STREAM, 0, 1, 1);
+ fd3 = tor_open_socket(AF_INET, SOCK_STREAM, 0);
+ fd4 = tor_open_socket_nonblocking(AF_INET, SOCK_STREAM, 0);
+ tt_assert(SOCKET_OK(fd3));
+ tt_assert(SOCKET_OK(fd4));
+ tt_int_op(get_n_open_sockets(), ==, n + 4);
+
+#ifdef CAN_CHECK_CLOEXEC
+ tt_int_op(fd_is_cloexec(fd1), ==, 0);
+ tt_int_op(fd_is_cloexec(fd2), ==, 0);
+ tt_int_op(fd_is_cloexec(fd3), ==, 1);
+ tt_int_op(fd_is_cloexec(fd4), ==, 1);
+#endif
+#ifdef CAN_CHECK_NONBLOCK
+ tt_int_op(fd_is_nonblocking(fd1), ==, 0);
+ tt_int_op(fd_is_nonblocking(fd2), ==, 1);
+ tt_int_op(fd_is_nonblocking(fd3), ==, 0);
+ tt_int_op(fd_is_nonblocking(fd4), ==, 1);
+#endif
+
+ tor_close_socket(fd1);
+ tor_close_socket(fd2);
+ fd1 = fd2 = TOR_INVALID_SOCKET;
+ tt_int_op(get_n_open_sockets(), ==, n + 2);
+ tor_close_socket(fd3);
+ tor_close_socket(fd4);
+ fd3 = fd4 = TOR_INVALID_SOCKET;
+ tt_int_op(get_n_open_sockets(), ==, n);
+
+ done:
+ if (SOCKET_OK(fd1))
+ tor_close_socket(fd1);
+ if (SOCKET_OK(fd2))
+ tor_close_socket(fd2);
+ if (SOCKET_OK(fd3))
+ tor_close_socket(fd3);
+ if (SOCKET_OK(fd4))
+ tor_close_socket(fd4);
+}
+
+static void *
+socketpair_test_setup(const struct testcase_t *testcase)
+{
+ return testcase->setup_data;
+}
+static int
+socketpair_test_cleanup(const struct testcase_t *testcase, void *ptr)
+{
+ (void)testcase;
+ (void)ptr;
+ return 1;
+}
+
+static const struct testcase_setup_t socketpair_setup = {
+ socketpair_test_setup, socketpair_test_cleanup
+};
+
+/* Test for socketpair and ersatz_socketpair(). We test them both, since
+ * the latter is a tolerably good way to exersize tor_accept_socket(). */
+static void
+test_util_socketpair(void *arg)
+{
+ const int ersatz = !strcmp(arg, "1");
+ int (*const tor_socketpair_fn)(int, int, int, tor_socket_t[2]) =
+ ersatz ? tor_ersatz_socketpair : tor_socketpair;
+ int n = get_n_open_sockets();
+ tor_socket_t fds[2] = {TOR_INVALID_SOCKET, TOR_INVALID_SOCKET};
+ const int family = AF_UNIX;
+
+ tt_int_op(0, ==, tor_socketpair_fn(family, SOCK_STREAM, 0, fds));
+ tt_assert(SOCKET_OK(fds[0]));
+ tt_assert(SOCKET_OK(fds[1]));
+ tt_int_op(get_n_open_sockets(), ==, n + 2);
+#ifdef CAN_CHECK_CLOEXEC
+ tt_int_op(fd_is_cloexec(fds[0]), ==, 1);
+ tt_int_op(fd_is_cloexec(fds[1]), ==, 1);
+#endif
+#ifdef CAN_CHECK_NONBLOCK
+ tt_int_op(fd_is_nonblocking(fds[0]), ==, 0);
+ tt_int_op(fd_is_nonblocking(fds[1]), ==, 0);
+#endif
+
+ done:
+ if (SOCKET_OK(fds[0]))
+ tor_close_socket(fds[0]);
+ if (SOCKET_OK(fds[1]))
+ tor_close_socket(fds[1]);
+}
+
struct testcase_t util_tests[] = {
UTIL_LEGACY(time),
UTIL_TEST(parse_http_time, 0),
@@ -3228,6 +3610,8 @@ struct testcase_t util_tests[] = {
#ifndef _WIN32
UTIL_LEGACY(expand_filename),
#endif
+ UTIL_LEGACY(escape_string_socks),
+ UTIL_LEGACY(string_is_key_value),
UTIL_LEGACY(strmisc),
UTIL_LEGACY(pow2),
UTIL_LEGACY(gzip),
@@ -3241,6 +3625,8 @@ struct testcase_t util_tests[] = {
UTIL_LEGACY(path_is_relative),
UTIL_LEGACY(strtok),
UTIL_LEGACY(di_ops),
+ UTIL_TEST(round_to_next_multiple_of, 0),
+ UTIL_TEST(strclear, 0),
UTIL_TEST(find_str_at_start_of_line, 0),
UTIL_TEST(string_is_C_identifier, 0),
UTIL_TEST(asprintf, 0),
@@ -3249,14 +3635,15 @@ struct testcase_t util_tests[] = {
#ifdef _WIN32
UTIL_TEST(load_win_lib, 0),
#endif
- UTIL_TEST(exit_status, 0),
#ifndef _WIN32
+ UTIL_TEST(exit_status, 0),
UTIL_TEST(fgets_eagain, TT_SKIP),
#endif
UTIL_TEST(spawn_background_ok, 0),
UTIL_TEST(spawn_background_fail, 0),
UTIL_TEST(spawn_background_partial_read, 0),
UTIL_TEST(format_hex_number, 0),
+ UTIL_TEST(format_dec_number, 0),
UTIL_TEST(join_win_cmdline, 0),
UTIL_TEST(split_lines, 0),
UTIL_TEST(n_bits_set, 0),
@@ -3268,8 +3655,14 @@ struct testcase_t util_tests[] = {
UTIL_TEST(read_file_eof_tiny_limit, 0),
UTIL_TEST(read_file_eof_two_loops, 0),
UTIL_TEST(read_file_eof_zero_bytes, 0),
+ UTIL_TEST(write_chunks_to_file, 0),
UTIL_TEST(mathlog, 0),
UTIL_TEST(weak_random, 0),
+ UTIL_TEST(socket, TT_FORK),
+ { "socketpair", test_util_socketpair, TT_FORK, &socketpair_setup,
+ (void*)"0" },
+ { "socketpair_ersatz", test_util_socketpair, TT_FORK,
+ &socketpair_setup, (void*)"1" },
END_OF_TESTCASES
};
diff --git a/src/tools/tor-checkkey.c b/src/tools/tor-checkkey.c
index a3860ca4b..d50f12ed2 100644
--- a/src/tools/tor-checkkey.c
+++ b/src/tools/tor-checkkey.c
@@ -1,8 +1,6 @@
/* Copyright (c) 2008-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
-#define CRYPTO_PRIVATE
-
#include "orconfig.h"
#include <stdio.h>
diff --git a/src/tools/tor-fw-helper/tor-fw-helper.c b/src/tools/tor-fw-helper/tor-fw-helper.c
index bb6e70aaa..84cc21e34 100644
--- a/src/tools/tor-fw-helper/tor-fw-helper.c
+++ b/src/tools/tor-fw-helper/tor-fw-helper.c
@@ -496,6 +496,6 @@ main(int argc, char **argv)
smartlist_free(tor_fw_options.ports_to_forward);
}
- exit(r);
+ exit(0);
}
diff --git a/src/tools/tor-gencert.c b/src/tools/tor-gencert.c
index 3809b22d4..2f95cf7c5 100644
--- a/src/tools/tor-gencert.c
+++ b/src/tools/tor-gencert.c
@@ -27,8 +27,6 @@
#include <assert.h>
#endif
-#define CRYPTO_PRIVATE
-
#include "compat.h"
#include "../common/util.h"
#include "../common/torlog.h"
@@ -36,7 +34,7 @@
#include "address.h"
#define IDENTITY_KEY_BITS 3072
-#define SIGNING_KEY_BITS 1024
+#define SIGNING_KEY_BITS 2048
#define DEFAULT_LIFETIME 12
/* These globals are set via command line options. */
diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h
index f5d5cf446..0d6fe692b 100644
--- a/src/win32/orconfig.h
+++ b/src/win32/orconfig.h
@@ -241,7 +241,7 @@
#define USING_TWOS_COMPLEMENT
/* Version number of package */
-#define VERSION "0.2.4.10-alpha-dev"
+#define VERSION "0.2.5.1-alpha-dev"