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/compat.c88
-rw-r--r--src/common/compat_libevent.c35
-rw-r--r--src/common/compat_libevent.h37
-rw-r--r--src/common/compat_openssl.h37
-rw-r--r--src/common/container.c24
-rw-r--r--src/common/crypto.c198
-rw-r--r--src/common/crypto.h27
-rw-r--r--src/common/include.am1
-rw-r--r--src/common/log.c17
-rw-r--r--src/common/procmon.c3
-rw-r--r--src/common/torlog.h17
-rw-r--r--src/common/tortls.c413
-rw-r--r--src/common/tortls.h116
-rw-r--r--src/common/util.c104
-rw-r--r--src/common/util.h1
-rw-r--r--src/ext/csiphash.c57
-rw-r--r--src/or/circuitlist.c78
-rw-r--r--src/or/circuituse.c8
-rw-r--r--src/or/config.c28
-rw-r--r--src/or/config.h8
-rw-r--r--src/or/connection_edge.c163
-rw-r--r--src/or/connection_edge.h6
-rw-r--r--src/or/connection_or.c70
-rw-r--r--src/or/control.c49
-rw-r--r--src/or/control.h3
-rw-r--r--src/or/directory.c13
-rw-r--r--src/or/directory.h4
-rw-r--r--src/or/dirvote.c8
-rw-r--r--src/or/dirvote.h6
-rw-r--r--src/or/dns.c29
-rw-r--r--src/or/dns.h12
-rw-r--r--src/or/geoip.c11
-rw-r--r--src/or/geoip.h1
-rw-r--r--src/or/include.am2
-rw-r--r--src/or/main.c915
-rw-r--r--src/or/main.h2
-rw-r--r--src/or/or.h13
-rw-r--r--src/or/periodic.c120
-rw-r--r--src/or/periodic.h37
-rw-r--r--src/or/policies.c436
-rw-r--r--src/or/policies.h30
-rw-r--r--src/or/rendcache.c173
-rw-r--r--src/or/rendcache.h37
-rw-r--r--src/or/rendclient.c11
-rw-r--r--src/or/rendcommon.c7
-rw-r--r--src/or/rendservice.c150
-rw-r--r--src/or/rephist.c12
-rw-r--r--src/or/replaycache.c26
-rw-r--r--src/or/replaycache.h2
-rw-r--r--src/or/router.c47
-rw-r--r--src/or/router.h4
-rw-r--r--src/or/routerlist.c8
-rw-r--r--src/or/routerlist.h2
-rw-r--r--src/or/routerset.c6
-rw-r--r--src/or/statefile.c13
-rw-r--r--src/test/include.am16
-rw-r--r--src/test/log_test_helpers.c111
-rw-r--r--src/test/log_test_helpers.h31
-rw-r--r--src/test/rend_test_helpers.c73
-rw-r--r--src/test/rend_test_helpers.h15
-rw-r--r--src/test/test.c32
-rw-r--r--src/test/test_address.c186
-rw-r--r--src/test/test_compat_libevent.c238
-rw-r--r--src/test/test_crypto.c28
-rw-r--r--src/test/test_dir_handle_get.c2549
-rw-r--r--src/test/test_dns.c503
-rw-r--r--src/test/test_policy.c492
-rw-r--r--src/test/test_procmon.c58
-rw-r--r--src/test/test_rendcache.c1333
-rw-r--r--src/test/test_replay.c34
-rw-r--r--src/test/test_routerset.c102
-rw-r--r--src/test/test_threads.c2
-rw-r--r--src/test/test_tortls.c2846
-rw-r--r--src/test/test_util.c207
-rw-r--r--src/test/test_util_format.c262
-rw-r--r--src/test/test_util_process.c84
-rw-r--r--src/test/vote_descriptors.inc94
-rw-r--r--src/trunnel/README21
-rw-r--r--src/trunnel/include.am4
-rw-r--r--src/win32/orconfig.h2
81 files changed, 11679 insertions, 1372 deletions
diff --git a/src/common/address.c b/src/common/address.c
index cfa8fd1dca..aef229b02c 100644
--- a/src/common/address.c
+++ b/src/common/address.c
@@ -1469,7 +1469,8 @@ get_interface_addresses_ioctl(int severity)
int fd;
smartlist_t *result = NULL;
- /* This interface, AFAICT, only supports AF_INET addresses */
+ /* This interface, AFAICT, only supports AF_INET addresses,
+ * except on AIX. For Solaris, we could use SIOCGLIFCONF. */
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
tor_log(severity, LD_NET, "socket failed: %s", strerror(errno));
diff --git a/src/common/compat.c b/src/common/compat.c
index 7d72b4b7fd..bd59e0f5a5 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -1486,6 +1486,12 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
}
#ifdef NEED_ERSATZ_SOCKETPAIR
+
+#define SIZEOF_SOCKADDR(domain) \
+ (domain == AF_INET ? sizeof(struct sockaddr_in) : \
+ (domain == AF_INET6 ? sizeof(struct sockaddr_in6) : \
+ ((size_t)0) /* unsupported, don't match any valid size */))
+
/**
* Helper used to implement socketpair on systems that lack it, by
* making a direct connection to localhost.
@@ -1501,12 +1507,19 @@ tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
tor_socket_t listener = TOR_INVALID_SOCKET;
tor_socket_t connector = TOR_INVALID_SOCKET;
tor_socket_t acceptor = TOR_INVALID_SOCKET;
- struct sockaddr_in listen_addr;
- struct sockaddr_in connect_addr;
+ tor_addr_t listen_tor_addr;
+ struct sockaddr listen_addr;
+ in_port_t listen_port = 0;
+ tor_addr_t connect_tor_addr;
+ in_port_t connect_port = 0;
+ struct sockaddr connect_addr;
socklen_t size;
int saved_errno = -1;
+ int ersatz_domain = AF_INET;
+ memset(&connect_tor_addr, 0, sizeof(connect_tor_addr));
memset(&connect_addr, 0, sizeof(connect_addr));
+ memset(&listen_tor_addr, 0, sizeof(listen_tor_addr));
memset(&listen_addr, 0, sizeof(listen_addr));
if (protocol
@@ -1524,47 +1537,71 @@ tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
return -EINVAL;
}
- listener = tor_open_socket(AF_INET, type, 0);
- if (!SOCKET_OK(listener))
- return -tor_socket_errno(-1);
- memset(&listen_addr, 0, sizeof(listen_addr));
- listen_addr.sin_family = AF_INET;
- listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- listen_addr.sin_port = 0; /* kernel chooses port. */
- if (bind(listener, (struct sockaddr *) &listen_addr, sizeof (listen_addr))
- == -1)
+ listener = tor_open_socket(ersatz_domain, type, 0);
+ if (!SOCKET_OK(listener)) {
+ int first_errno = tor_socket_errno(-1);
+ if (first_errno == SOCK_ERRNO(EPROTONOSUPPORT)
+ && ersatz_domain == AF_INET) {
+ /* Assume we're on an IPv6-only system */
+ ersatz_domain = AF_INET6;
+ listener = tor_open_socket(ersatz_domain, type, 0);
+ if (!SOCKET_OK(listener)) {
+ /* Keep the previous behaviour, which was to return the IPv4 error.
+ * (This may be less informative on IPv6-only systems.)
+ * XX/teor - is there a better way to decide which errno to return?
+ * (I doubt we care much either way, once there is an error.)
+ */
+ return -first_errno;
+ }
+ }
+ }
+ /* If there is no 127.0.0.1 or ::1, this will and must fail. Otherwise, we
+ * risk exposing a socketpair on a routable IP address. (Some BSD jails
+ * use a routable address for localhost. Fortunately, they have the real
+ * AF_UNIX socketpair.) */
+ if (ersatz_domain == AF_INET) {
+ tor_addr_from_ipv4h(&listen_tor_addr, INADDR_LOOPBACK);
+ } else {
+ tor_addr_parse(&listen_tor_addr, "[::1]");
+ }
+ tor_assert(tor_addr_is_loopback(&listen_tor_addr));
+ tor_addr_to_sockaddr(&listen_tor_addr,
+ 0 /* kernel chooses port. */,
+ &listen_addr,
+ sizeof(listen_addr));
+ if (bind(listener, &listen_addr, sizeof (listen_addr)) == -1)
goto tidy_up_and_fail;
if (listen(listener, 1) == -1)
goto tidy_up_and_fail;
- connector = tor_open_socket(AF_INET, type, 0);
+ connector = tor_open_socket(ersatz_domain, type, 0);
if (!SOCKET_OK(connector))
goto tidy_up_and_fail;
/* We want to find out the port number to connect to. */
size = sizeof(connect_addr);
- if (getsockname(listener, (struct sockaddr *) &connect_addr, &size) == -1)
+ if (getsockname(listener, &connect_addr, &size) == -1)
goto tidy_up_and_fail;
- if (size != sizeof (connect_addr))
+ if (size != SIZEOF_SOCKADDR (connect_addr.sa_family))
goto abort_tidy_up_and_fail;
- if (connect(connector, (struct sockaddr *) &connect_addr,
- sizeof(connect_addr)) == -1)
+ if (connect(connector, &connect_addr, sizeof(connect_addr)) == -1)
goto tidy_up_and_fail;
size = sizeof(listen_addr);
- acceptor = tor_accept_socket(listener,
- (struct sockaddr *) &listen_addr, &size);
+ acceptor = tor_accept_socket(listener, &listen_addr, &size);
if (!SOCKET_OK(acceptor))
goto tidy_up_and_fail;
- if (size != sizeof(listen_addr))
+ if (size != SIZEOF_SOCKADDR(listen_addr.sa_family))
goto abort_tidy_up_and_fail;
/* Now check we are talking to ourself by matching port and host on the
two sockets. */
- if (getsockname(connector, (struct sockaddr *) &connect_addr, &size) == -1)
+ if (getsockname(connector, &connect_addr, &size) == -1)
goto tidy_up_and_fail;
- if (size != sizeof (connect_addr)
- || listen_addr.sin_family != connect_addr.sin_family
- || listen_addr.sin_addr.s_addr != connect_addr.sin_addr.s_addr
- || listen_addr.sin_port != connect_addr.sin_port) {
+ /* Set *_tor_addr and *_port to the address and port that was used */
+ tor_addr_from_sockaddr(&listen_tor_addr, &listen_addr, &listen_port);
+ tor_addr_from_sockaddr(&connect_tor_addr, &connect_addr, &connect_port);
+ if (size != SIZEOF_SOCKADDR (connect_addr.sa_family)
+ || tor_addr_compare(&listen_tor_addr, &connect_tor_addr, CMP_SEMANTIC)
+ || listen_port != connect_port) {
goto abort_tidy_up_and_fail;
}
tor_close_socket(listener);
@@ -1590,6 +1627,9 @@ tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
tor_close_socket(acceptor);
return -saved_errno;
}
+
+#undef SIZEOF_SOCKADDR
+
#endif
/* Return the maximum number of allowed sockets. */
diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c
index a366b6c9c6..29e5c5f63c 100644
--- a/src/common/compat_libevent.c
+++ b/src/common/compat_libevent.c
@@ -11,6 +11,7 @@
#include "orconfig.h"
#include "compat.h"
+#define COMPAT_LIBEVENT_PRIVATE
#include "compat_libevent.h"
#include "crypto.h"
@@ -28,39 +29,11 @@
#include <event.h>
#endif
-/** A number representing a version of Libevent.
-
- This is a 4-byte number, with the first three bytes representing the
- major, minor, and patchlevel respectively of the library. The fourth
- byte is unused.
-
- This is equivalent to the format of LIBEVENT_VERSION_NUMBER on Libevent
- 2.0.1 or later. For versions of Libevent before 1.4.0, which followed the
- format of "1.0, 1.0a, 1.0b", we define 1.0 to be equivalent to 1.0.0, 1.0a
- to be equivalent to 1.0.1, and so on.
-*/
-typedef uint32_t le_version_t;
-
-/** @{ */
-/** Macros: returns the number of a libevent version as a le_version_t */
-#define V(major, minor, patch) \
- (((major) << 24) | ((minor) << 16) | ((patch) << 8))
-#define V_OLD(major, minor, patch) \
- V((major), (minor), (patch)-'a'+1)
-/** @} */
-
-/** Represetns a version of libevent so old we can't figure out what version
- * it is. */
-#define LE_OLD V(0,0,0)
-/** Represents a version of libevent so weird we can't figure out what version
- * it is. */
-#define LE_OTHER V(0,0,99)
-
/** A string which, if it appears in a libevent log, should be ignored. */
static const char *suppress_msg = NULL;
/** Callback function passed to event_set_log() so we can intercept
* log messages from libevent. */
-static void
+STATIC void
libevent_logging_callback(int severity, const char *msg)
{
char buf[1024];
@@ -291,7 +264,7 @@ tor_libevent_get_method(void)
/** Return the le_version_t for the version of libevent specified in the
* string <b>v</b>. If the version is very new or uses an unrecognized
* version, format, return LE_OTHER. */
-static le_version_t
+STATIC le_version_t
tor_decode_libevent_version(const char *v)
{
unsigned major, minor, patchlevel;
@@ -322,7 +295,7 @@ tor_decode_libevent_version(const char *v)
* Two different versions with different numbers are sure not to be binary
* compatible. Two different versions with the same numbers have a decent
* chance of binary compatibility.*/
-static int
+STATIC int
le_versions_compatibility(le_version_t v)
{
if (v == LE_OTHER)
diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h
index 39181efb7b..8ee02c0b6d 100644
--- a/src/common/compat_libevent.h
+++ b/src/common/compat_libevent.h
@@ -91,5 +91,42 @@ void tor_gettimeofday_cache_set(const struct timeval *tv);
#endif
void tor_gettimeofday_cached_monotonic(struct timeval *tv);
+#ifdef COMPAT_LIBEVENT_PRIVATE
+/** A number representing a version of Libevent.
+
+ This is a 4-byte number, with the first three bytes representing the
+ major, minor, and patchlevel respectively of the library. The fourth
+ byte is unused.
+
+ This is equivalent to the format of LIBEVENT_VERSION_NUMBER on Libevent
+ 2.0.1 or later. For versions of Libevent before 1.4.0, which followed the
+ format of "1.0, 1.0a, 1.0b", we define 1.0 to be equivalent to 1.0.0, 1.0a
+ to be equivalent to 1.0.1, and so on.
+*/
+typedef uint32_t le_version_t;
+
+/** @{ */
+/** Macros: returns the number of a libevent version as a le_version_t */
+#define V(major, minor, patch) \
+ (((major) << 24) | ((minor) << 16) | ((patch) << 8))
+#define V_OLD(major, minor, patch) \
+ V((major), (minor), (patch)-'a'+1)
+/** @} */
+
+/** Represetns a version of libevent so old we can't figure out what version
+ * it is. */
+#define LE_OLD V(0,0,0)
+/** Represents a version of libevent so weird we can't figure out what version
+ * it is. */
+#define LE_OTHER V(0,0,99)
+
+STATIC void
+libevent_logging_callback(int severity, const char *msg);
+STATIC le_version_t
+tor_decode_libevent_version(const char *v);
+STATIC int
+le_versions_compatibility(le_version_t v);
+#endif
+
#endif
diff --git a/src/common/compat_openssl.h b/src/common/compat_openssl.h
new file mode 100644
index 0000000000..3fcd684c0c
--- /dev/null
+++ b/src/common/compat_openssl.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_COMPAT_OPENSSL_H
+#define TOR_COMPAT_OPENSSL_H
+
+#include <openssl/opensslv.h>
+
+/**
+ * \file compat_openssl.h
+ *
+ * \brief compatability definitions for working with different openssl forks
+ **/
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,0)
+#error "We require OpenSSL >= 1.0.0"
+#endif
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0)
+#define OPENSSL_VERSION SSLEAY_VERSION
+#define OpenSSL_version(v) SSLeay_version(v)
+#define OpenSSL_version_num() SSLeay()
+#define RAND_OpenSSL() RAND_SSLeay()
+#define STATE_IS_SW_SERVER_HELLO(st) \
+ (((st) == SSL3_ST_SW_SRVR_HELLO_A) || \
+ ((st) == SSL3_ST_SW_SRVR_HELLO_B))
+#define OSSL_HANDSHAKE_STATE int
+#else
+#define STATE_IS_SW_SERVER_HELLO(st) \
+ ((st) == TLS_ST_SW_SRVR_HELLO)
+#endif
+
+#endif
+
diff --git a/src/common/container.c b/src/common/container.c
index 636dfb6c57..c6f059170e 100644
--- a/src/common/container.c
+++ b/src/common/container.c
@@ -55,6 +55,7 @@ smartlist_free,(smartlist_t *sl))
void
smartlist_clear(smartlist_t *sl)
{
+ memset(sl->list, 0, sizeof(void *) * sl->num_used);
sl->num_used = 0;
}
@@ -82,9 +83,11 @@ smartlist_ensure_capacity(smartlist_t *sl, int size)
while (size > higher)
higher *= 2;
}
- sl->capacity = higher;
sl->list = tor_reallocarray(sl->list, sizeof(void *),
- ((size_t)sl->capacity));
+ ((size_t)higher));
+ memset(sl->list + sl->capacity, 0,
+ sizeof(void *) * (higher - sl->capacity));
+ sl->capacity = higher;
}
#undef ASSERT_CAPACITY
#undef MAX_CAPACITY
@@ -123,6 +126,7 @@ smartlist_remove(smartlist_t *sl, const void *element)
if (sl->list[i] == element) {
sl->list[i] = sl->list[--sl->num_used]; /* swap with the end */
i--; /* so we process the new i'th element */
+ sl->list[sl->num_used] = NULL;
}
}
@@ -132,9 +136,11 @@ void *
smartlist_pop_last(smartlist_t *sl)
{
tor_assert(sl);
- if (sl->num_used)
- return sl->list[--sl->num_used];
- else
+ if (sl->num_used) {
+ void *tmp = sl->list[--sl->num_used];
+ sl->list[sl->num_used] = NULL;
+ return tmp;
+ } else
return NULL;
}
@@ -165,6 +171,7 @@ smartlist_string_remove(smartlist_t *sl, const char *element)
tor_free(sl->list[i]);
sl->list[i] = sl->list[--sl->num_used]; /* swap with the end */
i--; /* so we process the new i'th element */
+ sl->list[sl->num_used] = NULL;
}
}
}
@@ -321,6 +328,7 @@ smartlist_intersect(smartlist_t *sl1, const smartlist_t *sl2)
if (!smartlist_contains(sl2, sl1->list[i])) {
sl1->list[i] = sl1->list[--sl1->num_used]; /* swap with the end */
i--; /* so we process the new i'th element */
+ sl1->list[sl1->num_used] = NULL;
}
}
@@ -345,6 +353,7 @@ smartlist_del(smartlist_t *sl, int idx)
tor_assert(idx>=0);
tor_assert(idx < sl->num_used);
sl->list[idx] = sl->list[--sl->num_used];
+ sl->list[sl->num_used] = NULL;
}
/** Remove the <b>idx</b>th element of sl; if idx is not the last element,
@@ -360,6 +369,7 @@ smartlist_del_keeporder(smartlist_t *sl, int idx)
--sl->num_used;
if (idx < sl->num_used)
memmove(sl->list+idx, sl->list+idx+1, sizeof(void*)*(sl->num_used-idx));
+ sl->list[sl->num_used] = NULL;
}
/** Insert the value <b>val</b> as the new <b>idx</b>th element of
@@ -937,9 +947,11 @@ smartlist_pqueue_pop(smartlist_t *sl,
*IDXP(top)=-1;
if (--sl->num_used) {
sl->list[0] = sl->list[sl->num_used];
+ sl->list[sl->num_used] = NULL;
UPDATE_IDX(0);
smartlist_heapify(sl, compare, idx_field_offset, 0);
}
+ sl->list[sl->num_used] = NULL;
return top;
}
@@ -959,9 +971,11 @@ smartlist_pqueue_remove(smartlist_t *sl,
--sl->num_used;
*IDXP(item) = -1;
if (idx == sl->num_used) {
+ sl->list[sl->num_used] = NULL;
return;
} else {
sl->list[idx] = sl->list[sl->num_used];
+ sl->list[sl->num_used] = NULL;
UPDATE_IDX(idx);
smartlist_heapify(sl, compare, idx_field_offset, idx);
}
diff --git a/src/common/crypto.c b/src/common/crypto.c
index 9669493a83..baef755d00 100644
--- a/src/common/crypto.c
+++ b/src/common/crypto.c
@@ -21,18 +21,13 @@
#undef OCSP_RESPONSE
#endif
-#include <openssl/opensslv.h>
-
#define CRYPTO_PRIVATE
#include "crypto.h"
+#include "compat_openssl.h"
#include "crypto_curve25519.h"
#include "crypto_ed25519.h"
#include "crypto_format.h"
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,0)
-#error "We require OpenSSL >= 1.0.0"
-#endif
-
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
@@ -227,7 +222,7 @@ const char *
crypto_openssl_get_version_str(void)
{
if (crypto_openssl_version_str == NULL) {
- const char *raw_version = SSLeay_version(SSLEAY_VERSION);
+ const char *raw_version = OpenSSL_version(OPENSSL_VERSION);
crypto_openssl_version_str = parse_openssl_version_str(raw_version);
}
return crypto_openssl_version_str;
@@ -251,11 +246,13 @@ crypto_openssl_get_header_version_str(void)
static int
crypto_force_rand_ssleay(void)
{
- if (RAND_get_rand_method() != RAND_SSLeay()) {
+ RAND_METHOD *default_method;
+ default_method = RAND_OpenSSL();
+ if (RAND_get_rand_method() != default_method) {
log_notice(LD_CRYPTO, "It appears that one of our engines has provided "
"a replacement the OpenSSL RNG. Resetting it to the default "
"implementation.");
- RAND_set_rand_method(RAND_SSLeay());
+ RAND_set_rand_method(default_method);
return 1;
}
return 0;
@@ -290,16 +287,18 @@ crypto_early_init(void)
setup_openssl_threading();
- if (SSLeay() == OPENSSL_VERSION_NUMBER &&
- !strcmp(SSLeay_version(SSLEAY_VERSION), OPENSSL_VERSION_TEXT)) {
+ unsigned long version_num = OpenSSL_version_num();
+ const char *version_str = OpenSSL_version(OPENSSL_VERSION);
+ if (version_num == OPENSSL_VERSION_NUMBER &&
+ !strcmp(version_str, OPENSSL_VERSION_TEXT)) {
log_info(LD_CRYPTO, "OpenSSL version matches version from headers "
- "(%lx: %s).", SSLeay(), SSLeay_version(SSLEAY_VERSION));
+ "(%lx: %s).", version_num, version_str);
} else {
log_warn(LD_CRYPTO, "OpenSSL version from headers does not match the "
"version we're running with. If you get weird crashes, that "
"might be why. (Compiled with %lx: %s; running with %lx: %s).",
(unsigned long)OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_TEXT,
- SSLeay(), SSLeay_version(SSLEAY_VERSION));
+ version_num, version_str);
}
crypto_force_rand_ssleay();
@@ -404,11 +403,7 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
void
crypto_thread_cleanup(void)
{
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)
ERR_remove_thread_state(NULL);
-#else
- ERR_remove_state(0);
-#endif
}
/** used by tortls.c: wrap an RSA* in a crypto_pk_t. */
@@ -432,9 +427,10 @@ crypto_pk_get_rsa_(crypto_pk_t *env)
}
/** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_t. Iff
- * private is set, include the private-key portion of the key. */
-EVP_PKEY *
-crypto_pk_get_evp_pkey_(crypto_pk_t *env, int private)
+ * private is set, include the private-key portion of the key. Return a valid
+ * pointer on success, and NULL on failure. */
+MOCK_IMPL(EVP_PKEY *,
+ crypto_pk_get_evp_pkey_,(crypto_pk_t *env, int private))
{
RSA *key = NULL;
EVP_PKEY *pkey = NULL;
@@ -470,8 +466,8 @@ crypto_dh_get_dh_(crypto_dh_t *dh)
/** Allocate and return storage for a public key. The key itself will not yet
* be set.
*/
-crypto_pk_t *
-crypto_pk_new(void)
+MOCK_IMPL(crypto_pk_t *,
+ crypto_pk_new,(void))
{
RSA *rsa;
@@ -553,8 +549,8 @@ crypto_cipher_free(crypto_cipher_t *env)
/** Generate a <b>bits</b>-bit new public/private keypair in <b>env</b>.
* Return 0 on success, -1 on failure.
*/
-int
-crypto_pk_generate_key_with_bits(crypto_pk_t *env, int bits)
+MOCK_IMPL(int,
+ crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits))
{
tor_assert(env);
@@ -656,7 +652,8 @@ crypto_pk_read_private_key_from_filename(crypto_pk_t *env,
return 0;
}
-/** Helper function to implement crypto_pk_write_*_key_to_string. */
+/** Helper function to implement crypto_pk_write_*_key_to_string. Return 0 on
+ * success, -1 on failure. */
static int
crypto_pk_write_key_to_string_impl(crypto_pk_t *env, char **dest,
size_t *len, int is_public)
@@ -897,7 +894,8 @@ crypto_pk_dup_key(crypto_pk_t *env)
return env;
}
-/** Make a real honest-to-goodness copy of <b>env</b>, and return it. */
+/** Make a real honest-to-goodness copy of <b>env</b>, and return it.
+ * Returns NULL on failure. */
crypto_pk_t *
crypto_pk_copy_full(crypto_pk_t *env)
{
@@ -1189,7 +1187,8 @@ crypto_pk_public_hybrid_encrypt(crypto_pk_t *env,
return -1;
}
-/** Invert crypto_pk_public_hybrid_encrypt. */
+/** Invert crypto_pk_public_hybrid_encrypt. Returns the number of bytes
+ * written on success, -1 on failure. */
int
crypto_pk_private_hybrid_decrypt(crypto_pk_t *env,
char *to,
@@ -1332,7 +1331,7 @@ crypto_pk_get_all_digests(crypto_pk_t *pk, digests_t *digests_out)
}
/** Copy <b>in</b> to the <b>outlen</b>-byte buffer <b>out</b>, adding spaces
- * every four spaces. */
+ * every four characters. */
void
crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in)
{
@@ -1484,7 +1483,7 @@ crypto_cipher_get_key(crypto_cipher_t *env)
/** Encrypt <b>fromlen</b> bytes from <b>from</b> using the cipher
* <b>env</b>; on success, store the result to <b>to</b> and return 0.
- * On failure, return -1.
+ * Does not check for failure.
*/
int
crypto_cipher_encrypt(crypto_cipher_t *env, char *to,
@@ -1503,7 +1502,7 @@ crypto_cipher_encrypt(crypto_cipher_t *env, char *to,
/** Decrypt <b>fromlen</b> bytes from <b>from</b> using the cipher
* <b>env</b>; on success, store the result to <b>to</b> and return 0.
- * On failure, return -1.
+ * Does not check for failure.
*/
int
crypto_cipher_decrypt(crypto_cipher_t *env, char *to,
@@ -1519,7 +1518,7 @@ crypto_cipher_decrypt(crypto_cipher_t *env, char *to,
}
/** Encrypt <b>len</b> bytes on <b>from</b> using the cipher in <b>env</b>;
- * on success, return 0. On failure, return -1.
+ * on success, return 0. Does not check for failure.
*/
int
crypto_cipher_crypt_inplace(crypto_cipher_t *env, char *buf, size_t len)
@@ -1591,7 +1590,7 @@ crypto_cipher_decrypt_with_iv(const char *key,
/** Compute the SHA1 digest of the <b>len</b> bytes on data stored in
* <b>m</b>. Write the DIGEST_LEN byte result into <b>digest</b>.
- * Return 0 on success, -1 on failure.
+ * Return 0 on success, 1 on failure.
*/
int
crypto_digest(char *digest, const char *m, size_t len)
@@ -1603,7 +1602,7 @@ crypto_digest(char *digest, const char *m, size_t len)
/** Compute a 256-bit digest of <b>len</b> bytes in data stored in <b>m</b>,
* using the algorithm <b>algorithm</b>. Write the DIGEST_LEN256-byte result
- * into <b>digest</b>. Return 0 on success, -1 on failure. */
+ * into <b>digest</b>. Return 0 on success, 1 on failure. */
int
crypto_digest256(char *digest, const char *m, size_t len,
digest_algorithm_t algorithm)
@@ -1614,6 +1613,19 @@ crypto_digest256(char *digest, const char *m, size_t len,
return (SHA256((const unsigned char*)m,len,(unsigned char*)digest) == NULL);
}
+/** Compute a 512-bit digest of <b>len</b> bytes in data stored in <b>m</b>,
+ * using the algorithm <b>algorithm</b>. Write the DIGEST_LEN512-byte result
+ * into <b>digest</b>. Return 0 on success, 1 on failure. */
+int
+crypto_digest512(char *digest, const char *m, size_t len,
+ digest_algorithm_t algorithm)
+{
+ tor_assert(m);
+ tor_assert(digest);
+ tor_assert(algorithm == DIGEST_SHA512);
+ return (SHA512((const unsigned char*)m,len,(unsigned char*)digest) == NULL);
+}
+
/** Set the digests_t in <b>ds_out</b> to contain every digest on the
* <b>len</b> bytes in <b>m</b> that we know how to compute. Return 0 on
* success, -1 on failure. */
@@ -1626,8 +1638,18 @@ crypto_digest_all(digests_t *ds_out, const char *m, size_t len)
if (crypto_digest(ds_out->d[DIGEST_SHA1], m, len) < 0)
return -1;
for (i = DIGEST_SHA256; i < N_DIGEST_ALGORITHMS; ++i) {
- if (crypto_digest256(ds_out->d[i], m, len, i) < 0)
- return -1;
+ switch (i) {
+ case DIGEST_SHA256:
+ if (crypto_digest256(ds_out->d[i], m, len, i) < 0)
+ return -1;
+ break;
+ case DIGEST_SHA512:
+ if (crypto_digest512(ds_out->d[i], m, len, i) < 0)
+ return -1;
+ break;
+ default:
+ return -1;
+ }
}
return 0;
}
@@ -1641,6 +1663,8 @@ crypto_digest_algorithm_get_name(digest_algorithm_t alg)
return "sha1";
case DIGEST_SHA256:
return "sha256";
+ case DIGEST_SHA512:
+ return "sha512";
default:
tor_fragile_assert();
return "??unknown_digest??";
@@ -1656,6 +1680,8 @@ crypto_digest_algorithm_parse_name(const char *name)
return DIGEST_SHA1;
else if (!strcmp(name, "sha256"))
return DIGEST_SHA256;
+ else if (!strcmp(name, "sha512"))
+ return DIGEST_SHA512;
else
return -1;
}
@@ -1665,6 +1691,7 @@ struct crypto_digest_t {
union {
SHA_CTX sha1; /**< state for SHA1 */
SHA256_CTX sha2; /**< state for SHA256 */
+ SHA512_CTX sha512; /**< state for SHA512 */
} d; /**< State for the digest we're using. Only one member of the
* union is usable, depending on the value of <b>algorithm</b>. */
digest_algorithm_bitfield_t algorithm : 8; /**< Which algorithm is in use? */
@@ -1695,6 +1722,19 @@ crypto_digest256_new(digest_algorithm_t algorithm)
return r;
}
+/** Allocate and return a new digest object to compute 512-bit digests
+ * using <b>algorithm</b>. */
+crypto_digest_t *
+crypto_digest512_new(digest_algorithm_t algorithm)
+{
+ crypto_digest_t *r;
+ tor_assert(algorithm == DIGEST_SHA512);
+ r = tor_malloc(sizeof(crypto_digest_t));
+ SHA512_Init(&r->d.sha512);
+ r->algorithm = algorithm;
+ return r;
+}
+
/** Deallocate a digest object.
*/
void
@@ -1726,6 +1766,9 @@ crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
case DIGEST_SHA256:
SHA256_Update(&digest->d.sha2, (void*)data, len);
break;
+ case DIGEST_SHA512:
+ SHA512_Update(&digest->d.sha512, (void*)data, len);
+ break;
default:
tor_fragile_assert();
break;
@@ -1734,13 +1777,13 @@ crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
/** Compute the hash of the data that has been passed to the digest
* object; write the first out_len bytes of the result to <b>out</b>.
- * <b>out_len</b> must be \<= DIGEST256_LEN.
+ * <b>out_len</b> must be \<= DIGEST512_LEN.
*/
void
crypto_digest_get_digest(crypto_digest_t *digest,
char *out, size_t out_len)
{
- unsigned char r[DIGEST256_LEN];
+ unsigned char r[DIGEST512_LEN];
crypto_digest_t tmpenv;
tor_assert(digest);
tor_assert(out);
@@ -1755,6 +1798,10 @@ crypto_digest_get_digest(crypto_digest_t *digest,
tor_assert(out_len <= DIGEST256_LEN);
SHA256_Final(r, &tmpenv.d.sha2);
break;
+ case DIGEST_SHA512:
+ tor_assert(out_len <= DIGEST512_LEN);
+ SHA512_Final(r, &tmpenv.d.sha512);
+ break;
default:
log_warn(LD_BUG, "Called with unknown algorithm %d", digest->algorithm);
/* If fragile_assert is not enabled, then we should at least not
@@ -1796,7 +1843,7 @@ crypto_digest_assign(crypto_digest_t *into,
* at <b>digest_out</b> to the hash of the concatenation of those strings,
* plus the optional string <b>append</b>, computed with the algorithm
* <b>alg</b>.
- * <b>out_len</b> must be \<= DIGEST256_LEN. */
+ * <b>out_len</b> must be \<= DIGEST512_LEN. */
void
crypto_digest_smartlist(char *digest_out, size_t len_out,
const smartlist_t *lst,
@@ -1811,7 +1858,7 @@ crypto_digest_smartlist(char *digest_out, size_t len_out,
* optional string <b>prepend</b>, those strings,
* and the optional string <b>append</b>, computed with the algorithm
* <b>alg</b>.
- * <b>out_len</b> must be \<= DIGEST256_LEN. */
+ * <b>len_out</b> must be \<= DIGEST512_LEN. */
void
crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
const char *prepend,
@@ -1819,11 +1866,25 @@ crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
const char *append,
digest_algorithm_t alg)
{
- crypto_digest_t *d;
- if (alg == DIGEST_SHA1)
- d = crypto_digest_new();
- else
- d = crypto_digest256_new(alg);
+ crypto_digest_t *d = NULL;
+ switch (alg) {
+ case DIGEST_SHA1:
+ d = crypto_digest_new();
+ break;
+ case DIGEST_SHA256:
+ d = crypto_digest256_new(alg);
+ break;
+ case DIGEST_SHA512:
+ d = crypto_digest512_new(alg);
+ break;
+ default:
+ log_warn(LD_BUG, "Called with unknown algorithm %d", alg);
+ /* If fragile_assert is not enabled, wipe output and return
+ * without running any calculations */
+ memwipe(digest_out, 0xff, len_out);
+ tor_fragile_assert();
+ goto free;
+ }
if (prepend)
crypto_digest_add_bytes(d, prepend, strlen(prepend));
SMARTLIST_FOREACH(lst, const char *, cp,
@@ -1831,23 +1892,28 @@ crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
if (append)
crypto_digest_add_bytes(d, append, strlen(append));
crypto_digest_get_digest(d, digest_out, len_out);
+
+ free:
crypto_digest_free(d);
}
/** 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>.
+ * result in <b>hmac_out</b>. Asserts on failure.
*/
void
crypto_hmac_sha256(char *hmac_out,
const char *key, size_t key_len,
const char *msg, size_t msg_len)
{
+ unsigned char *rv = NULL;
/* If we've got OpenSSL >=0.9.8 we can use its hmac implementation. */
tor_assert(key_len < INT_MAX);
tor_assert(msg_len < INT_MAX);
- HMAC(EVP_sha256(), key, (int)key_len, (unsigned char*)msg, (int)msg_len,
- (unsigned char*)hmac_out, NULL);
+ tor_assert(hmac_out);
+ rv = HMAC(EVP_sha256(), key, (int)key_len, (unsigned char*)msg, (int)msg_len,
+ (unsigned char*)hmac_out, NULL);
+ tor_assert(rv);
}
/* DH */
@@ -1941,7 +2007,8 @@ init_dh_param(void)
*/
#define DH_PRIVATE_KEY_BITS 320
-/** Allocate and return a new DH object for a key exchange.
+/** Allocate and return a new DH object for a key exchange. Returns NULL on
+ * failure.
*/
crypto_dh_t *
crypto_dh_new(int dh_type)
@@ -2164,7 +2231,7 @@ int
crypto_expand_key_material_TAP(const uint8_t *key_in, size_t key_in_len,
uint8_t *key_out, size_t key_out_len)
{
- int i;
+ int i, r = -1;
uint8_t *cp, *tmp = tor_malloc(key_in_len+1);
uint8_t digest[DIGEST_LEN];
@@ -2176,19 +2243,16 @@ crypto_expand_key_material_TAP(const uint8_t *key_in, size_t key_in_len,
++i, cp += DIGEST_LEN) {
tmp[key_in_len] = i;
if (crypto_digest((char*)digest, (const char *)tmp, key_in_len+1))
- goto err;
+ goto exit;
memcpy(cp, digest, MIN(DIGEST_LEN, key_out_len-(cp-key_out)));
}
- memwipe(tmp, 0, key_in_len+1);
- tor_free(tmp);
- memwipe(digest, 0, sizeof(digest));
- return 0;
- err:
+ r = 0;
+ exit:
memwipe(tmp, 0, key_in_len+1);
tor_free(tmp);
memwipe(digest, 0, sizeof(digest));
- return -1;
+ return r;
}
/** Expand some secret key material according to RFC5869, using SHA256 as the
@@ -2196,7 +2260,7 @@ crypto_expand_key_material_TAP(const uint8_t *key_in, size_t key_in_len,
* secret key material; the <b>salt_in_len</b> bytes at <b>salt_in</b> and the
* <b>info_in_len</b> bytes in <b>info_in_len</b> are the algorithm's "salt"
* and "info" parameters respectively. On success, write <b>key_out_len</b>
- * bytes to <b>key_out</b> and return 0. On failure, return -1.
+ * bytes to <b>key_out</b> and return 0. Assert on failure.
*/
int
crypto_expand_key_material_rfc5869_sha256(
@@ -2280,7 +2344,7 @@ crypto_seed_weak_rng(tor_weak_rng_t *rng)
}
/** Try to get <b>out_len</b> bytes of the strongest entropy we can generate,
- * storing it into <b>out</b>.
+ * storing it into <b>out</b>. Return -1 on success, 0 on failure.
*/
int
crypto_strongest_rand(uint8_t *out, size_t out_len)
@@ -2335,8 +2399,7 @@ crypto_strongest_rand(uint8_t *out, size_t out_len)
}
/** Seed OpenSSL's random number generator with bytes from the operating
- * system. <b>startup</b> should be true iff we have just started Tor and
- * have not yet allocated a bunch of fds. Return 0 on success, -1 on failure.
+ * system. Return 0 on success, -1 on failure.
*/
int
crypto_seed_rng(void)
@@ -2418,8 +2481,8 @@ crypto_rand_int(unsigned int max)
}
}
-/** Return a pseudorandom integer, chosen uniformly from the values <i>i</i>
- * such that <b>min</b> &lt;= <i>i</i> &lt <b>max</b>.
+/** Return a pseudorandom integer, chosen uniformly from the values i such
+ * that min <= i < max.
*
* <b>min</b> MUST be in range [0, <b>max</b>).
* <b>max</b> MUST be in range (min, INT_MAX].
@@ -2496,7 +2559,7 @@ crypto_rand_double(void)
/** Generate and return a new random hostname starting with <b>prefix</b>,
* ending with <b>suffix</b>, and containing no fewer than
* <b>min_rand_len</b> and no more than <b>max_rand_len</b> random base32
- * characters between.
+ * characters. Does not check for failure.
*
* Clip <b>max_rand_len</b> to MAX_DNS_LABEL_SIZE.
**/
@@ -2678,7 +2741,7 @@ tor_set_openssl_thread_id(CRYPTO_THREADID *threadid)
/** @{ */
/** Helper: Construct mutexes, and set callbacks to help OpenSSL handle being
- * multithreaded. */
+ * multithreaded. Returns 0. */
static int
setup_openssl_threading(void)
{
@@ -2696,17 +2759,14 @@ setup_openssl_threading(void)
return 0;
}
-/** Uninitialize the crypto library. Return 0 on success, -1 on failure.
+/** Uninitialize the crypto library. Return 0 on success. Does not detect
+ * failure.
*/
int
crypto_global_cleanup(void)
{
EVP_cleanup();
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)
ERR_remove_thread_state(NULL);
-#else
- ERR_remove_state(0);
-#endif
ERR_free_strings();
if (dh_param_p)
diff --git a/src/common/crypto.h b/src/common/crypto.h
index 3b471c2956..9b922ff818 100644
--- a/src/common/crypto.h
+++ b/src/common/crypto.h
@@ -55,6 +55,8 @@
/** Length of the output of our second (improved) message digests. (For now
* this is just sha256, but it could be any other 256-bit digest.) */
#define DIGEST256_LEN 32
+/** Length of the output of our 64-bit optimized message digests (SHA512). */
+#define DIGEST512_LEN 64
/** Length of our symmetric cipher's keys. */
#define CIPHER_KEY_LEN 16
/** Length of our symmetric cipher's IV. */
@@ -70,6 +72,9 @@
/** Length of a sha256 message digest when encoded in base64 with trailing =
* signs removed. */
#define BASE64_DIGEST256_LEN 43
+/** Length of a sha512 message digest when encoded in base64 with trailing =
+ * signs removed. */
+#define BASE64_DIGEST512_LEN 86
/** Constant used to indicate OAEP padding for public-key encryption */
#define PK_PKCS1_OAEP_PADDING 60002
@@ -84,24 +89,27 @@
#define HEX_DIGEST_LEN 40
/** Length of hex encoding of SHA256 digest, not including final NUL. */
#define HEX_DIGEST256_LEN 64
+/** Length of hex encoding of SHA512 digest, not including final NUL. */
+#define HEX_DIGEST512_LEN 128
typedef enum {
DIGEST_SHA1 = 0,
DIGEST_SHA256 = 1,
+ DIGEST_SHA512 = 2,
} digest_algorithm_t;
-#define N_DIGEST_ALGORITHMS (DIGEST_SHA256+1)
+#define N_DIGEST_ALGORITHMS (DIGEST_SHA512+1)
#define digest_algorithm_bitfield_t ENUM_BF(digest_algorithm_t)
/** A set of all the digests we know how to compute, taken on a single
- * string. Any digests that are shorter than 256 bits are right-padded
+ * string. Any digests that are shorter than 512 bits are right-padded
* with 0 bits.
*
- * Note that this representation wastes 12 bytes for the SHA1 case, so
+ * Note that this representation wastes 44 bytes for the SHA1 case, so
* don't use it for anything where we need to allocate a whole bunch at
* once.
**/
typedef struct {
- char d[N_DIGEST_ALGORITHMS][DIGEST256_LEN];
+ char d[N_DIGEST_ALGORITHMS][DIGEST512_LEN];
} digests_t;
typedef struct crypto_pk_t crypto_pk_t;
@@ -120,7 +128,7 @@ void crypto_thread_cleanup(void);
int crypto_global_cleanup(void);
/* environment setup */
-crypto_pk_t *crypto_pk_new(void);
+MOCK_DECL(crypto_pk_t *,crypto_pk_new,(void));
void crypto_pk_free(crypto_pk_t *env);
void crypto_set_tls_dh_prime(void);
@@ -129,7 +137,7 @@ crypto_cipher_t *crypto_cipher_new_with_iv(const char *key, const char *iv);
void crypto_cipher_free(crypto_cipher_t *env);
/* public key crypto */
-int crypto_pk_generate_key_with_bits(crypto_pk_t *env, int bits);
+MOCK_DECL(int, crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits));
#define crypto_pk_generate_key(env) \
crypto_pk_generate_key_with_bits((env), (PK_BYTES*8))
@@ -208,6 +216,8 @@ int crypto_cipher_decrypt_with_iv(const char *key,
int crypto_digest(char *digest, const char *m, size_t len);
int crypto_digest256(char *digest, const char *m, size_t len,
digest_algorithm_t algorithm);
+int crypto_digest512(char *digest, const char *m, size_t len,
+ digest_algorithm_t algorithm);
int crypto_digest_all(digests_t *ds_out, const char *m, size_t len);
struct smartlist_t;
void crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
@@ -222,6 +232,7 @@ const char *crypto_digest_algorithm_get_name(digest_algorithm_t alg);
int crypto_digest_algorithm_parse_name(const char *name);
crypto_digest_t *crypto_digest_new(void);
crypto_digest_t *crypto_digest256_new(digest_algorithm_t algorithm);
+crypto_digest_t *crypto_digest512_new(digest_algorithm_t algorithm);
void crypto_digest_free(crypto_digest_t *digest);
void crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
size_t len);
@@ -290,8 +301,8 @@ struct evp_pkey_st;
struct dh_st;
struct rsa_st *crypto_pk_get_rsa_(crypto_pk_t *env);
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);
+MOCK_DECL(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);
void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in);
diff --git a/src/common/include.am b/src/common/include.am
index 7de93ba2ac..2fc92e2ceb 100644
--- a/src/common/include.am
+++ b/src/common/include.am
@@ -118,6 +118,7 @@ COMMONHEADERS = \
src/common/ciphers.inc \
src/common/compat.h \
src/common/compat_libevent.h \
+ src/common/compat_openssl.h \
src/common/compat_threads.h \
src/common/container.h \
src/common/crypto.h \
diff --git a/src/common/log.c b/src/common/log.c
index e23691b6ab..7ede6100a2 100644
--- a/src/common/log.c
+++ b/src/common/log.c
@@ -1097,14 +1097,25 @@ add_file_log(const log_severity_list_t *severity, const char *filename,
#ifdef HAVE_SYSLOG_H
/**
* Add a log handler to send messages to they system log facility.
+ *
+ * If this is the first log handler, opens syslog with ident Tor or
+ * Tor-<syslog_identity_tag> if that is not NULL.
*/
int
-add_syslog_log(const log_severity_list_t *severity)
+add_syslog_log(const log_severity_list_t *severity,
+ const char* syslog_identity_tag)
{
logfile_t *lf;
- if (syslog_count++ == 0)
+ if (syslog_count++ == 0) {
/* This is the first syslog. */
- openlog("Tor", LOG_PID | LOG_NDELAY, LOGFACILITY);
+ static char buf[256];
+ if (syslog_identity_tag) {
+ tor_snprintf(buf, sizeof(buf), "Tor-%s", syslog_identity_tag);
+ } else {
+ tor_snprintf(buf, sizeof(buf), "Tor");
+ }
+ openlog(buf, LOG_PID | LOG_NDELAY, LOGFACILITY);
+ }
lf = tor_malloc_zero(sizeof(logfile_t));
lf->fd = -1;
diff --git a/src/common/procmon.c b/src/common/procmon.c
index 2d0f021724..346a0c6943 100644
--- a/src/common/procmon.c
+++ b/src/common/procmon.c
@@ -192,7 +192,8 @@ tor_process_monitor_new(struct event_base *base,
tor_procmon_callback_t cb, void *cb_arg,
const char **msg)
{
- tor_process_monitor_t *procmon = tor_malloc(sizeof(tor_process_monitor_t));
+ tor_process_monitor_t *procmon = tor_malloc_zero(
+ sizeof(tor_process_monitor_t));
struct parsed_process_specifier_t ppspec;
tor_assert(msg != NULL);
diff --git a/src/common/torlog.h b/src/common/torlog.h
index 67edf14c04..3e8667895f 100644
--- a/src/common/torlog.h
+++ b/src/common/torlog.h
@@ -135,7 +135,8 @@ void add_stream_log(const log_severity_list_t *severity, const char *name,
int add_file_log(const log_severity_list_t *severity, const char *filename,
const int truncate);
#ifdef HAVE_SYSLOG_H
-int add_syslog_log(const log_severity_list_t *severity);
+int add_syslog_log(const log_severity_list_t *severity,
+ const char* syslog_identity_tag);
#endif
int add_callback_log(const log_severity_list_t *severity, log_callback cb);
void logs_set_domain_logging(int enabled);
@@ -183,25 +184,25 @@ void log_fn_ratelim_(struct ratelim_t *ratelim, int severity,
/** Log a message at level <b>severity</b>, using a pretty-printed version
* of the current function name. */
#define log_fn(severity, domain, args...) \
- log_fn_(severity, domain, __PRETTY_FUNCTION__, args)
+ log_fn_(severity, domain, __FUNCTION__, args)
/** As log_fn, but use <b>ratelim</b> (an instance of ratelim_t) to control
* the frequency at which messages can appear.
*/
#define log_fn_ratelim(ratelim, severity, domain, args...) \
- log_fn_ratelim_(ratelim, severity, domain, __PRETTY_FUNCTION__, args)
+ log_fn_ratelim_(ratelim, severity, domain, __FUNCTION__, args)
#define log_debug(domain, args...) \
STMT_BEGIN \
if (PREDICT_UNLIKELY(log_global_min_severity_ == LOG_DEBUG)) \
- log_fn_(LOG_DEBUG, domain, __PRETTY_FUNCTION__, args); \
+ log_fn_(LOG_DEBUG, domain, __FUNCTION__, args); \
STMT_END
#define log_info(domain, args...) \
- log_fn_(LOG_INFO, domain, __PRETTY_FUNCTION__, args)
+ log_fn_(LOG_INFO, domain, __FUNCTION__, args)
#define log_notice(domain, args...) \
- log_fn_(LOG_NOTICE, domain, __PRETTY_FUNCTION__, args)
+ log_fn_(LOG_NOTICE, domain, __FUNCTION__, args)
#define log_warn(domain, args...) \
- log_fn_(LOG_WARN, domain, __PRETTY_FUNCTION__, args)
+ log_fn_(LOG_WARN, domain, __FUNCTION__, args)
#define log_err(domain, args...) \
- log_fn_(LOG_ERR, domain, __PRETTY_FUNCTION__, args)
+ log_fn_(LOG_ERR, domain, __FUNCTION__, args)
#else /* ! defined(__GNUC__) */
diff --git a/src/common/tortls.c b/src/common/tortls.c
index e3c6859828..79c6998806 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -16,6 +16,8 @@
#include "orconfig.h"
+#define TORTLS_PRIVATE
+
#include <assert.h>
#ifdef _WIN32 /*wrkard for dtls1.h >= 0.9.8m of "#include <winsock.h>"*/
#include <winsock2.h>
@@ -38,9 +40,6 @@
#include <openssl/opensslv.h>
#include "crypto.h"
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,0)
-#error "We require OpenSSL >= 1.0.0"
-#endif
#ifdef OPENSSL_NO_EC
#error "We require OpenSSL with ECC support"
#endif
@@ -69,6 +68,7 @@
#include "compat_libevent.h"
#endif
+#define TORTLS_PRIVATE
#include "tortls.h"
#include "util.h"
#include "torlog.h"
@@ -80,11 +80,6 @@
#define X509_get_notAfter_const(cert) \
((const ASN1_TIME*) X509_get_notAfter((X509 *)cert))
-/* Enable the "v2" TLS handshake.
- */
-#define V2_HANDSHAKE_SERVER
-#define V2_HANDSHAKE_CLIENT
-
/* Copied from or.h */
#define LEGAL_NICKNAME_CHARACTERS \
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
@@ -113,29 +108,6 @@
#define SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0x0010
#endif
-/** Structure that we use for a single certificate. */
-struct tor_x509_cert_t {
- X509 *cert;
- uint8_t *encoded;
- size_t encoded_len;
- unsigned pkey_digests_set : 1;
- digests_t cert_digests;
- digests_t pkey_digests;
-};
-
-/** Holds a SSL_CTX object and related state used to configure TLS
- * connections.
- */
-typedef struct tor_tls_context_t {
- int refcnt;
- SSL_CTX *ctx;
- tor_x509_cert_t *my_link_cert;
- tor_x509_cert_t *my_id_cert;
- tor_x509_cert_t *my_auth_cert;
- crypto_pk_t *link_key;
- crypto_pk_t *auth_key;
-} tor_tls_context_t;
-
/** Return values for tor_tls_classify_client_ciphers.
*
* @{
@@ -154,60 +126,12 @@ typedef struct tor_tls_context_t {
#define CIPHERS_UNRESTRICTED 3
/** @} */
-#define TOR_TLS_MAGIC 0x71571571
-
-typedef enum {
- TOR_TLS_ST_HANDSHAKE, TOR_TLS_ST_OPEN, TOR_TLS_ST_GOTCLOSE,
- TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED, TOR_TLS_ST_RENEGOTIATE,
- TOR_TLS_ST_BUFFEREVENT
-} tor_tls_state_t;
-#define tor_tls_state_bitfield_t ENUM_BF(tor_tls_state_t)
-
-/** Holds a SSL object and its associated data. Members are only
- * accessed from within tortls.c.
- */
-struct tor_tls_t {
- uint32_t magic;
- tor_tls_context_t *context; /** A link to the context object for this tls. */
- SSL *ssl; /**< An OpenSSL SSL object. */
- int socket; /**< The underlying file descriptor for this TLS connection. */
- char *address; /**< An address to log when describing this connection. */
- tor_tls_state_bitfield_t state : 3; /**< The current SSL state,
- * depending on which operations
- * have completed successfully. */
- unsigned int isServer:1; /**< True iff this is a server-side connection */
- unsigned int wasV2Handshake:1; /**< True iff the original handshake for
- * this connection used the updated version
- * of the connection protocol (client sends
- * different cipher list, server sends only
- * one certificate). */
- /** True iff we should call negotiated_callback when we're done reading. */
- unsigned int got_renegotiate:1;
- /** Return value from tor_tls_classify_client_ciphers, or 0 if we haven't
- * called that function yet. */
- int8_t client_cipher_list_type;
- /** Incremented every time we start the server side of a handshake. */
- uint8_t server_handshake_count;
- size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last
- * time. */
- /** Last values retrieved from BIO_number_read()/write(); see
- * tor_tls_get_n_raw_bytes() for usage.
- */
- unsigned long last_write_count;
- unsigned long last_read_count;
- /** If set, a callback to invoke whenever the client tries to renegotiate
- * the handshake. */
- void (*negotiated_callback)(tor_tls_t *tls, void *arg);
- /** Argument to pass to negotiated_callback. */
- void *callback_arg;
-};
-
/** The ex_data index in which we store a pointer to an SSL object's
* corresponding tor_tls_t object. */
-static int tor_tls_object_ex_data_index = -1;
+STATIC int tor_tls_object_ex_data_index = -1;
/** Helper: Allocate tor_tls_object_ex_data_index. */
-static void
+STATIC void
tor_tls_allocate_tor_tls_object_ex_data_index(void)
{
if (tor_tls_object_ex_data_index == -1) {
@@ -219,7 +143,7 @@ tor_tls_allocate_tor_tls_object_ex_data_index(void)
/** Helper: given a SSL* pointer, return the tor_tls_t object using that
* pointer. */
-static INLINE tor_tls_t *
+STATIC INLINE tor_tls_t *
tor_tls_get_by_ssl(const SSL *ssl)
{
tor_tls_t *result = SSL_get_ex_data(ssl, tor_tls_object_ex_data_index);
@@ -230,21 +154,7 @@ tor_tls_get_by_ssl(const SSL *ssl)
static void tor_tls_context_decref(tor_tls_context_t *ctx);
static void tor_tls_context_incref(tor_tls_context_t *ctx);
-static X509* tor_tls_create_certificate(crypto_pk_t *rsa,
- crypto_pk_t *rsa_sign,
- const char *cname,
- const char *cname_sign,
- unsigned int cert_lifetime);
-
-static int tor_tls_context_init_one(tor_tls_context_t **ppcontext,
- crypto_pk_t *identity,
- unsigned int key_lifetime,
- unsigned int flags,
- int is_client);
-static tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity,
- unsigned int key_lifetime,
- unsigned int flags,
- int is_client);
+
static int check_cert_lifetime_internal(int severity, const X509 *cert,
int past_tolerance, int future_tolerance);
@@ -252,8 +162,8 @@ static int check_cert_lifetime_internal(int severity, const X509 *cert,
* to touch them.
*
* @{ */
-static tor_tls_context_t *server_tls_context = NULL;
-static tor_tls_context_t *client_tls_context = NULL;
+STATIC tor_tls_context_t *server_tls_context = NULL;
+STATIC tor_tls_context_t *client_tls_context = NULL;
/**@}*/
/** True iff tor_tls_init() has been called. */
@@ -347,7 +257,7 @@ tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
/** Log all pending tls errors at level <b>severity</b> in log domain
* <b>domain</b>. Use <b>doing</b> to describe our current activities.
*/
-static void
+STATIC void
tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing)
{
unsigned long err;
@@ -359,7 +269,7 @@ tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing)
/** Convert an errno (or a WSAerrno on windows) into a TOR_TLS_* error
* code. */
-static int
+STATIC int
tor_errno_to_tls_error(int e)
{
switch (e) {
@@ -410,7 +320,7 @@ tor_tls_err_to_string(int err)
* If an error has occurred, log it at level <b>severity</b> and describe the
* current action as <b>doing</b>.
*/
-static int
+STATIC int
tor_tls_get_error(tor_tls_t *tls, int r, int extra,
const char *doing, int severity, int domain)
{
@@ -466,8 +376,9 @@ tor_tls_init(void)
#if (SIZEOF_VOID_P >= 8 && \
OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,1))
- long version = SSLeay();
+ long version = OpenSSL_version_num();
+ /* LCOV_EXCL_START : we can't test these lines on the same machine */
if (version >= OPENSSL_V_SERIES(1,0,1)) {
/* Warn if we could *almost* be running with much faster ECDH.
If we're built for a 64-bit target, using OpenSSL 1.0.1, but we
@@ -494,6 +405,7 @@ tor_tls_init(void)
"support (using the enable-ec_nistp_64_gcc_128 option "
"when configuring it) would make ECDH much faster.");
}
+ /* LCOV_EXCL_STOP */
#endif
tor_tls_allocate_tor_tls_object_ex_data_index();
@@ -524,7 +436,7 @@ tor_tls_free_all(void)
* it: We always accept peer certs and complete the handshake. We
* don't validate them until later.
*/
-static int
+STATIC int
always_accept_verify_cb(int preverify_ok,
X509_STORE_CTX *x509_ctx)
{
@@ -539,16 +451,20 @@ tor_x509_name_new(const char *cname)
{
int nid;
X509_NAME *name;
+ /* LCOV_EXCL_BR_START : these branches will only fail on OOM errors */
if (!(name = X509_NAME_new()))
return NULL;
if ((nid = OBJ_txt2nid("commonName")) == NID_undef) goto error;
if (!(X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC,
(unsigned char*)cname, -1, -1, 0)))
goto error;
+ /* LCOV_EXCL_BR_STOP */
return name;
error:
+ /* LCOV_EXCL_START : these lines will only execute on out of memory errors*/
X509_NAME_free(name);
return NULL;
+ /* LCOV_EXCL_STOP */
}
/** Generate and sign an X509 certificate with the public key <b>rsa</b>,
@@ -559,12 +475,12 @@ tor_x509_name_new(const char *cname)
*
* Return a certificate on success, NULL on failure.
*/
-static X509 *
-tor_tls_create_certificate(crypto_pk_t *rsa,
- crypto_pk_t *rsa_sign,
- const char *cname,
- const char *cname_sign,
- unsigned int cert_lifetime)
+MOCK_IMPL(STATIC X509 *,
+ tor_tls_create_certificate,(crypto_pk_t *rsa,
+ crypto_pk_t *rsa_sign,
+ const char *cname,
+ const char *cname_sign,
+ unsigned int cert_lifetime))
{
/* OpenSSL generates self-signed certificates with random 64-bit serial
* numbers, so let's do that too. */
@@ -730,7 +646,9 @@ tor_x509_cert_free(tor_x509_cert_t *cert)
X509_free(cert->cert);
tor_free(cert->encoded);
memwipe(cert, 0x03, sizeof(*cert));
+ /* LCOV_EXCL_BR_START since cert will never be NULL here */
tor_free(cert);
+ /* LCOV_EXCL_BR_STOP */
}
/**
@@ -738,8 +656,8 @@ tor_x509_cert_free(tor_x509_cert_t *cert)
*
* Steals a reference to x509_cert.
*/
-static tor_x509_cert_t *
-tor_x509_cert_new(X509 *x509_cert)
+MOCK_IMPL(STATIC tor_x509_cert_t *,
+ tor_x509_cert_new,(X509 *x509_cert))
{
tor_x509_cert_t *cert;
EVP_PKEY *pkey;
@@ -753,10 +671,12 @@ tor_x509_cert_new(X509 *x509_cert)
length = i2d_X509(x509_cert, &buf);
cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
if (length <= 0 || buf == NULL) {
+ /* LCOV_EXCL_START for the same reason as the exclusion above */
tor_free(cert);
log_err(LD_CRYPTO, "Couldn't get length of encoded x509 certificate");
X509_free(x509_cert);
return NULL;
+ /* LCOV_EXCL_STOP */
}
cert->encoded_len = (size_t) length;
cert->encoded = tor_malloc(length);
@@ -863,7 +783,9 @@ tor_tls_context_decref(tor_tls_context_t *ctx)
tor_x509_cert_free(ctx->my_auth_cert);
crypto_pk_free(ctx->link_key);
crypto_pk_free(ctx->auth_key);
+ /* LCOV_EXCL_BR_START since ctx will never be NULL here */
tor_free(ctx);
+ /* LCOV_EXCL_BR_STOP */
}
}
@@ -959,11 +881,13 @@ tor_tls_cert_is_valid(int severity,
int check_rsa_1024)
{
check_no_tls_errors();
-
EVP_PKEY *cert_key;
- EVP_PKEY *signing_key = X509_get_pubkey(signing_cert->cert);
int r, key_ok = 0;
+ if (!signing_cert)
+ goto bad;
+
+ EVP_PKEY *signing_key = X509_get_pubkey(signing_cert->cert);
if (!signing_key)
goto bad;
r = X509_verify(cert->cert, signing_key);
@@ -1084,7 +1008,7 @@ tor_tls_context_init(unsigned flags,
* it generates new certificates; all new connections will use
* the new SSL context.
*/
-static int
+STATIC int
tor_tls_context_init_one(tor_tls_context_t **ppcontext,
crypto_pk_t *identity,
unsigned int key_lifetime,
@@ -1118,7 +1042,7 @@ tor_tls_context_init_one(tor_tls_context_t **ppcontext,
* <b>identity</b> should be set to the identity key used to sign the
* certificate.
*/
-static tor_tls_context_t *
+STATIC tor_tls_context_t *
tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
unsigned flags, int is_client)
{
@@ -1199,23 +1123,6 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
* historically been chosen for fingerprinting resistance. */
SSL_CTX_set_options(result->ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
- /* Disable TLS1.1 and TLS1.2 if they exist. We need to do this to
- * workaround a bug present in all OpenSSL 1.0.1 versions (as of 1
- * June 2012), wherein renegotiating while using one of these TLS
- * protocols will cause the client to send a TLS 1.0 ServerHello
- * rather than a ServerHello written with the appropriate protocol
- * version. Once some version of OpenSSL does TLS1.1 and TLS1.2
- * renegotiation properly, we can turn them back on when built with
- * that version. */
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,1,'e')
-#ifdef SSL_OP_NO_TLSv1_2
- SSL_CTX_set_options(result->ctx, SSL_OP_NO_TLSv1_2);
-#endif
-#ifdef SSL_OP_NO_TLSv1_1
- SSL_CTX_set_options(result->ctx, SSL_OP_NO_TLSv1_1);
-#endif
-#endif
-
/* Disable TLS tickets if they're supported. We never want to use them;
* using them can make our perfect forward secrecy a little worse, *and*
* create an opportunity to fingerprint us (since it's unusual to use them
@@ -1342,11 +1249,13 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
}
/** Invoked when a TLS state changes: log the change at severity 'debug' */
-static void
+STATIC void
tor_tls_debug_state_callback(const SSL *ssl, int type, int val)
{
+ /* LCOV_EXCL_START since this depends on whether debug is captured or not */
log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].",
ssl, SSL_state_string_long(ssl), type, val);
+ /* LCOV_EXCL_STOP */
}
/* Return the name of the negotiated ciphersuite in use on <b>tls</b> */
@@ -1356,13 +1265,11 @@ 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
* 0.2.3.17-beta. If a client is using this list, we can't believe the ciphers
* that it claims to support. We'll prune this list to remove the ciphers
* *we* don't recognize. */
-static uint16_t v2_cipher_list[] = {
+STATIC uint16_t v2_cipher_list[] = {
0xc00a, /* TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA */
0xc014, /* TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA */
0x0039, /* TLS1_TXT_DHE_RSA_WITH_AES_256_SHA */
@@ -1398,7 +1305,7 @@ static int v2_cipher_list_pruned = 0;
/** Return 0 if <b>m</b> does not support the cipher with ID <b>cipher</b>;
* return 1 if it does support it, or if we have no way to tell. */
-static int
+STATIC int
find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher)
{
const SSL_CIPHER *c;
@@ -1480,7 +1387,7 @@ prune_v2_cipher_list(const SSL *ssl)
* client it is. Return one of CIPHERS_ERR, CIPHERS_V1, CIPHERS_V2,
* CIPHERS_UNRESTRICTED.
**/
-static int
+STATIC int
tor_tls_classify_client_ciphers(const SSL *ssl,
STACK_OF(SSL_CIPHER) *peer_ciphers)
{
@@ -1562,7 +1469,7 @@ tor_tls_classify_client_ciphers(const SSL *ssl,
/** Return true iff the cipher list suggested by the client for <b>ssl</b> is
* a list that indicates that the client knows how to do the v2 TLS connection
* handshake. */
-static int
+STATIC int
tor_tls_client_is_using_v2_ciphers(const SSL *ssl)
{
STACK_OF(SSL_CIPHER) *ciphers;
@@ -1586,11 +1493,10 @@ tor_tls_client_is_using_v2_ciphers(const SSL *ssl)
* do not send or request extra certificates in v2 handshakes.</li>
* <li>To detect renegotiation</li></ul>
*/
-static void
+STATIC void
tor_tls_server_info_callback(const SSL *ssl, int type, int val)
{
tor_tls_t *tls;
- int ssl_state;
(void) val;
tor_tls_debug_state_callback(ssl, type, val);
@@ -1598,11 +1504,9 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
if (type != SSL_CB_ACCEPT_LOOP)
return;
- ssl_state = SSL_state(ssl);
- if ((ssl_state != SSL3_ST_SW_SRVR_HELLO_A) &&
- (ssl_state != SSL3_ST_SW_SRVR_HELLO_B))
+ OSSL_HANDSHAKE_STATE ssl_state = SSL_get_state(ssl);
+ if (! STATE_IS_SW_SERVER_HELLO(ssl_state))
return;
-
tls = tor_tls_get_by_ssl(ssl);
if (tls) {
/* Check whether we're watching for renegotiates. If so, this is one! */
@@ -1632,11 +1536,12 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
if (tls) {
tls->wasV2Handshake = 1;
} else {
+ /* LCOV_EXCL_START this line is not reachable */
log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!");
+ /* LCOV_EXCL_STOP */
}
}
}
-#endif
/** 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.
@@ -1650,7 +1555,7 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
* 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
+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)
@@ -1746,12 +1651,9 @@ tor_tls_new(int sock, int isServer)
log_warn(LD_NET, "Newly created BIO has read count %lu, write count %lu",
result->last_read_count, result->last_write_count);
}
-#ifdef V2_HANDSHAKE_SERVER
if (isServer) {
SSL_set_info_callback(result->ssl, tor_tls_server_info_callback);
- } else
-#endif
- {
+ } else {
SSL_set_info_callback(result->ssl, tor_tls_debug_state_callback);
}
@@ -1790,13 +1692,11 @@ tor_tls_set_renegotiate_callback(tor_tls_t *tls,
tls->negotiated_callback = cb;
tls->callback_arg = arg;
tls->got_renegotiate = 0;
-#ifdef V2_HANDSHAKE_SERVER
if (cb) {
SSL_set_info_callback(tls->ssl, tor_tls_server_info_callback);
} else {
SSL_set_info_callback(tls->ssl, tor_tls_debug_state_callback);
}
-#endif
}
/** If this version of openssl requires it, turn on renegotiation on
@@ -1883,7 +1783,6 @@ tor_tls_read,(tor_tls_t *tls, char *cp, size_t len))
tor_assert(len<INT_MAX);
r = SSL_read(tls->ssl, cp, (int)len);
if (r > 0) {
-#ifdef V2_HANDSHAKE_SERVER
if (tls->got_renegotiate) {
/* Renegotiation happened! */
log_info(LD_NET, "Got a TLS renegotiation from %s", ADDR(tls));
@@ -1891,7 +1790,6 @@ tor_tls_read,(tor_tls_t *tls, char *cp, size_t len))
tls->negotiated_callback(tls, tls->callback_arg);
tls->got_renegotiate = 0;
}
-#endif
return r;
}
err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading", LOG_DEBUG, LD_NET);
@@ -1908,10 +1806,10 @@ tor_tls_read,(tor_tls_t *tls, char *cp, size_t len))
/** Total number of bytes that we've used TLS to send. Used to track TLS
* overhead. */
-static uint64_t total_bytes_written_over_tls = 0;
+STATIC uint64_t total_bytes_written_over_tls = 0;
/** Total number of bytes that TLS has put on the network for us. Used to
* track TLS overhead. */
-static uint64_t total_bytes_written_by_tls = 0;
+STATIC uint64_t total_bytes_written_by_tls = 0;
/** Underlying function for TLS writing. Write up to <b>n</b>
* characters from <b>cp</b> onto <b>tls</b>. On success, returns the
@@ -1956,12 +1854,14 @@ int
tor_tls_handshake(tor_tls_t *tls)
{
int r;
- int oldstate;
tor_assert(tls);
tor_assert(tls->ssl);
tor_assert(tls->state == TOR_TLS_ST_HANDSHAKE);
+
check_no_tls_errors();
- oldstate = SSL_state(tls->ssl);
+
+ OSSL_HANDSHAKE_STATE oldstate = SSL_get_state(tls->ssl);
+
if (tls->isServer) {
log_debug(LD_HANDSHAKE, "About to call SSL_accept on %p (%s)", tls,
SSL_state_string_long(tls->ssl));
@@ -1971,7 +1871,10 @@ tor_tls_handshake(tor_tls_t *tls)
SSL_state_string_long(tls->ssl));
r = SSL_connect(tls->ssl);
}
- if (oldstate != SSL_state(tls->ssl))
+
+ OSSL_HANDSHAKE_STATE newstate = SSL_get_state(tls->ssl);
+
+ if (oldstate != newstate)
log_debug(LD_HANDSHAKE, "After call, %p was in state %s",
tls, SSL_state_string_long(tls->ssl));
/* We need to call this here and not earlier, since OpenSSL has a penchant
@@ -2007,7 +1910,6 @@ tor_tls_finish_handshake(tor_tls_t *tls)
SSL_set_info_callback(tls->ssl, NULL);
SSL_set_verify(tls->ssl, SSL_VERIFY_PEER, always_accept_verify_cb);
SSL_clear_mode(tls->ssl, SSL_MODE_NO_AUTO_CHAIN);
-#ifdef V2_HANDSHAKE_SERVER
if (tor_tls_client_is_using_v2_ciphers(tls->ssl)) {
/* This check is redundant, but back when we did it in the callback,
* we might have not been able to look up the tor_tls_t if the code
@@ -2022,26 +1924,10 @@ tor_tls_finish_handshake(tor_tls_t *tls)
} else {
tls->wasV2Handshake = 0;
}
-#endif
} else {
-#ifdef V2_HANDSHAKE_CLIENT
- /* If we got no ID cert, we're a v2 handshake. */
- X509 *cert = SSL_get_peer_certificate(tls->ssl);
- STACK_OF(X509) *chain = SSL_get_peer_cert_chain(tls->ssl);
- int n_certs = sk_X509_num(chain);
- if (n_certs > 1 || (n_certs == 1 && cert != sk_X509_value(chain, 0))) {
- log_debug(LD_HANDSHAKE, "Server sent back multiple certificates; it "
- "looks like a v1 handshake on %p", tls);
- tls->wasV2Handshake = 0;
- } else {
- log_debug(LD_HANDSHAKE,
- "Server sent back a single certificate; looks like "
- "a v2 handshake on %p.", tls);
- tls->wasV2Handshake = 1;
- }
- if (cert)
- X509_free(cert);
-#endif
+ /* Client-side */
+ tls->wasV2Handshake = 1;
+ /* XXXX this can move, probably? -NM */
if (SSL_set_cipher_list(tls->ssl, SERVER_CIPHER_LIST) == 0) {
tls_log_errors(NULL, LOG_WARN, LD_HANDSHAKE, "re-setting ciphers");
r = TOR_TLS_ERROR_MISC;
@@ -2051,52 +1937,6 @@ tor_tls_finish_handshake(tor_tls_t *tls)
return r;
}
-#ifdef USE_BUFFEREVENTS
-/** Put <b>tls</b>, which must be a client connection, into renegotiation
- * mode. */
-int
-tor_tls_start_renegotiating(tor_tls_t *tls)
-{
- int r = SSL_renegotiate(tls->ssl);
- if (r <= 0) {
- return tor_tls_get_error(tls, r, 0, "renegotiating", LOG_WARN,
- LD_HANDSHAKE);
- }
- return 0;
-}
-#endif
-
-/** Client only: Renegotiate a TLS session. When finished, returns
- * TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD, or
- * TOR_TLS_WANTWRITE.
- */
-int
-tor_tls_renegotiate(tor_tls_t *tls)
-{
- int r;
- tor_assert(tls);
- /* We could do server-initiated renegotiation too, but that would be tricky.
- * Instead of "SSL_renegotiate, then SSL_do_handshake until done" */
- tor_assert(!tls->isServer);
-
- check_no_tls_errors();
- if (tls->state != TOR_TLS_ST_RENEGOTIATE) {
- int r = SSL_renegotiate(tls->ssl);
- if (r <= 0) {
- return tor_tls_get_error(tls, r, 0, "renegotiating", LOG_WARN,
- LD_HANDSHAKE);
- }
- tls->state = TOR_TLS_ST_RENEGOTIATE;
- }
- r = SSL_do_handshake(tls->ssl);
- if (r == 1) {
- tls->state = TOR_TLS_ST_OPEN;
- return TOR_TLS_DONE;
- } else
- return tor_tls_get_error(tls, r, 0, "renegotiating handshake", LOG_INFO,
- LD_HANDSHAKE);
-}
-
/** Shut down an open tls connection <b>tls</b>. When finished, returns
* TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD,
* or TOR_TLS_WANTWRITE.
@@ -2250,15 +2090,14 @@ log_cert_lifetime(int severity, const X509 *cert, const char *problem)
*
* Note that a reference is added to cert_out, so it needs to be
* freed. id_cert_out doesn't. */
-static void
-try_to_extract_certs_from_tls(int severity, tor_tls_t *tls,
- X509 **cert_out, X509 **id_cert_out)
+MOCK_IMPL(STATIC void,
+try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls,
+ X509 **cert_out, X509 **id_cert_out))
{
X509 *cert = NULL, *id_cert = NULL;
STACK_OF(X509) *chain = NULL;
int num_in_chain, i;
*cert_out = *id_cert_out = NULL;
-
if (!(cert = SSL_get_peer_certificate(tls->ssl)))
return;
*cert_out = cert;
@@ -2475,114 +2314,7 @@ check_no_tls_errors_(const char *fname, int line)
int
tor_tls_used_v1_handshake(tor_tls_t *tls)
{
-#if defined(V2_HANDSHAKE_SERVER) && defined(V2_HANDSHAKE_CLIENT)
return ! tls->wasV2Handshake;
-#else
- if (tls->isServer) {
-# ifdef V2_HANDSHAKE_SERVER
- return ! tls->wasV2Handshake;
-# endif
- } else {
-# ifdef V2_HANDSHAKE_CLIENT
- return ! tls->wasV2Handshake;
-# endif
- }
- return 1;
-#endif
-}
-
-/** Return true iff <b>name</b> is a DN of a kind that could only
- * occur in a v3-handshake-indicating certificate */
-static int
-dn_indicates_v3_cert(X509_NAME *name)
-{
-#ifdef DISABLE_V3_LINKPROTO_CLIENTSIDE
- (void)name;
- return 0;
-#else
- X509_NAME_ENTRY *entry;
- int n_entries;
- ASN1_OBJECT *obj;
- ASN1_STRING *str;
- unsigned char *s;
- int len, r;
-
- n_entries = X509_NAME_entry_count(name);
- if (n_entries != 1)
- return 1; /* More than one entry in the DN. */
- entry = X509_NAME_get_entry(name, 0);
-
- obj = X509_NAME_ENTRY_get_object(entry);
- if (OBJ_obj2nid(obj) != OBJ_txt2nid("commonName"))
- return 1; /* The entry isn't a commonName. */
-
- str = X509_NAME_ENTRY_get_data(entry);
- len = ASN1_STRING_to_UTF8(&s, str);
- if (len < 0)
- return 0;
- if (len < 4) {
- OPENSSL_free(s);
- return 1;
- }
- r = fast_memneq(s + len - 4, ".net", 4);
- OPENSSL_free(s);
- return r;
-#endif
-}
-
-/** Return true iff the peer certificate we're received on <b>tls</b>
- * indicates that this connection should use the v3 (in-protocol)
- * authentication handshake.
- *
- * Only the connection initiator should use this, and only once the initial
- * handshake is done; the responder detects a v1 handshake by cipher types,
- * and a v3/v2 handshake by Versions cell vs renegotiation.
- */
-int
-tor_tls_received_v3_certificate(tor_tls_t *tls)
-{
- check_no_tls_errors();
-
- X509 *cert = SSL_get_peer_certificate(tls->ssl);
- EVP_PKEY *key = NULL;
- X509_NAME *issuer_name, *subject_name;
- int is_v3 = 0;
-
- if (!cert) {
- log_warn(LD_BUG, "Called on a connection with no peer certificate");
- goto done;
- }
-
- subject_name = X509_get_subject_name(cert);
- issuer_name = X509_get_issuer_name(cert);
-
- if (X509_name_cmp(subject_name, issuer_name) == 0) {
- is_v3 = 1; /* purportedly self signed */
- goto done;
- }
-
- if (dn_indicates_v3_cert(subject_name) ||
- dn_indicates_v3_cert(issuer_name)) {
- is_v3 = 1; /* DN is fancy */
- goto done;
- }
-
- key = X509_get_pubkey(cert);
- if (EVP_PKEY_bits(key) != 1024 ||
- EVP_PKEY_type(key->type) != EVP_PKEY_RSA) {
- is_v3 = 1; /* Key is fancy */
- goto done;
- }
-
- done:
- tls_log_errors(tls, LOG_WARN, LD_NET, "checking for a v3 cert");
-
- if (key)
- EVP_PKEY_free(key);
- if (cert)
- X509_free(cert);
-
- return is_v3;
}
/** Return the number of server handshakes that we've noticed doing on
@@ -2628,7 +2360,7 @@ SSL_get_server_random(SSL *s, uint8_t *out, size_t len)
#endif
#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY
-static size_t
+STATIC size_t
SSL_SESSION_get_master_key(SSL_SESSION *s, uint8_t *out, size_t len)
{
tor_assert(s);
@@ -2651,7 +2383,6 @@ tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out))
#define TLSSECRET_MAGIC "Tor V3 handshake TLS cross-certification"
uint8_t buf[128];
size_t len;
-
tor_assert(tls);
SSL *const ssl = tls->ssl;
@@ -2675,12 +2406,14 @@ tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out))
size_t r = SSL_get_client_random(ssl, buf, client_random_len);
tor_assert(r == client_random_len);
}
+
{
size_t r = SSL_get_server_random(ssl,
buf+client_random_len,
server_random_len);
tor_assert(r == server_random_len);
}
+
uint8_t *master_key = tor_malloc_zero(master_key_len);
{
size_t r = SSL_SESSION_get_master_key(session, master_key, master_key_len);
diff --git a/src/common/tortls.h b/src/common/tortls.h
index 124b77160f..6a4ef9aebe 100644
--- a/src/common/tortls.h
+++ b/src/common/tortls.h
@@ -12,6 +12,7 @@
**/
#include "crypto.h"
+#include "compat_openssl.h"
#include "compat.h"
#include "testsupport.h"
@@ -51,6 +52,119 @@ typedef struct tor_x509_cert_t tor_x509_cert_t;
case TOR_TLS_ERROR_IO
#define TOR_TLS_IS_ERROR(rv) ((rv) < TOR_TLS_CLOSE)
+
+#ifdef TORTLS_PRIVATE
+#define TOR_TLS_MAGIC 0x71571571
+
+typedef enum {
+ TOR_TLS_ST_HANDSHAKE, TOR_TLS_ST_OPEN, TOR_TLS_ST_GOTCLOSE,
+ TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED, TOR_TLS_ST_RENEGOTIATE,
+ TOR_TLS_ST_BUFFEREVENT
+} tor_tls_state_t;
+#define tor_tls_state_bitfield_t ENUM_BF(tor_tls_state_t)
+
+/** Holds a SSL_CTX object and related state used to configure TLS
+ * connections.
+ */
+typedef struct tor_tls_context_t {
+ int refcnt;
+ SSL_CTX *ctx;
+ tor_x509_cert_t *my_link_cert;
+ tor_x509_cert_t *my_id_cert;
+ tor_x509_cert_t *my_auth_cert;
+ crypto_pk_t *link_key;
+ crypto_pk_t *auth_key;
+} tor_tls_context_t;
+
+/** Structure that we use for a single certificate. */
+struct tor_x509_cert_t {
+ X509 *cert;
+ uint8_t *encoded;
+ size_t encoded_len;
+ unsigned pkey_digests_set : 1;
+ digests_t cert_digests;
+ digests_t pkey_digests;
+};
+
+/** Holds a SSL object and its associated data. Members are only
+ * accessed from within tortls.c.
+ */
+struct tor_tls_t {
+ uint32_t magic;
+ tor_tls_context_t *context; /** A link to the context object for this tls. */
+ SSL *ssl; /**< An OpenSSL SSL object. */
+ int socket; /**< The underlying file descriptor for this TLS connection. */
+ char *address; /**< An address to log when describing this connection. */
+ tor_tls_state_bitfield_t state : 3; /**< The current SSL state,
+ * depending on which operations
+ * have completed successfully. */
+ unsigned int isServer:1; /**< True iff this is a server-side connection */
+ unsigned int wasV2Handshake:1; /**< True iff the original handshake for
+ * this connection used the updated version
+ * of the connection protocol (client sends
+ * different cipher list, server sends only
+ * one certificate). */
+ /** True iff we should call negotiated_callback when we're done reading. */
+ unsigned int got_renegotiate:1;
+ /** Return value from tor_tls_classify_client_ciphers, or 0 if we haven't
+ * called that function yet. */
+ int8_t client_cipher_list_type;
+ /** Incremented every time we start the server side of a handshake. */
+ uint8_t server_handshake_count;
+ size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last
+ * time. */
+ /** Last values retrieved from BIO_number_read()/write(); see
+ * tor_tls_get_n_raw_bytes() for usage.
+ */
+ unsigned long last_write_count;
+ unsigned long last_read_count;
+ /** If set, a callback to invoke whenever the client tries to renegotiate
+ * the handshake. */
+ void (*negotiated_callback)(tor_tls_t *tls, void *arg);
+ /** Argument to pass to negotiated_callback. */
+ void *callback_arg;
+};
+
+STATIC int tor_errno_to_tls_error(int e);
+STATIC int tor_tls_get_error(tor_tls_t *tls, int r, int extra,
+ const char *doing, int severity, int domain);
+STATIC tor_tls_t *tor_tls_get_by_ssl(const SSL *ssl);
+STATIC void tor_tls_allocate_tor_tls_object_ex_data_index(void);
+STATIC int always_accept_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx);
+STATIC int tor_tls_classify_client_ciphers(const SSL *ssl,
+ STACK_OF(SSL_CIPHER) *peer_ciphers);
+STATIC int tor_tls_client_is_using_v2_ciphers(const SSL *ssl);
+MOCK_DECL(STATIC void, try_to_extract_certs_from_tls,
+ (int severity, tor_tls_t *tls, X509 **cert_out, X509 **id_cert_out));
+#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY
+STATIC size_t SSL_SESSION_get_master_key(SSL_SESSION *s, uint8_t *out,
+ size_t len);
+#endif
+STATIC void tor_tls_debug_state_callback(const SSL *ssl, int type, int val);
+STATIC void tor_tls_server_info_callback(const SSL *ssl, int type, int val);
+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);
+STATIC int find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m,
+ uint16_t cipher);
+MOCK_DECL(STATIC X509*, tor_tls_create_certificate,(crypto_pk_t *rsa,
+ crypto_pk_t *rsa_sign,
+ const char *cname,
+ const char *cname_sign,
+ unsigned int cert_lifetime));
+STATIC tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity,
+ unsigned int key_lifetime, unsigned flags, int is_client);
+MOCK_DECL(STATIC tor_x509_cert_t *, tor_x509_cert_new,(X509 *x509_cert));
+STATIC int tor_tls_context_init_one(tor_tls_context_t **ppcontext,
+ crypto_pk_t *identity,
+ unsigned int key_lifetime,
+ unsigned int flags,
+ int is_client);
+STATIC void tls_log_errors(tor_tls_t *tls, int severity, int domain,
+ const char *doing);
+#endif
+
const char *tor_tls_err_to_string(int err);
void tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz);
@@ -81,7 +195,6 @@ MOCK_DECL(int, tor_tls_read, (tor_tls_t *tls, char *cp, size_t len));
int tor_tls_write(tor_tls_t *tls, const char *cp, size_t n);
int tor_tls_handshake(tor_tls_t *tls);
int tor_tls_finish_handshake(tor_tls_t *tls);
-int tor_tls_renegotiate(tor_tls_t *tls);
void tor_tls_unblock_renegotiation(tor_tls_t *tls);
void tor_tls_block_renegotiation(tor_tls_t *tls);
void tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls);
@@ -99,7 +212,6 @@ int tor_tls_get_buffer_sizes(tor_tls_t *tls,
MOCK_DECL(double, tls_get_write_overhead_ratio, (void));
int tor_tls_used_v1_handshake(tor_tls_t *tls);
-int tor_tls_received_v3_certificate(tor_tls_t *tls);
int tor_tls_get_num_server_handshakes(tor_tls_t *tls);
int tor_tls_server_got_renegotiate(tor_tls_t *tls);
MOCK_DECL(int,tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out));
diff --git a/src/common/util.c b/src/common/util.c
index b33c80fd45..ce3646cd64 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -488,42 +488,58 @@ round_to_power_of_2(uint64_t u64)
}
/** Return the lowest x such that x is at least <b>number</b>, and x modulo
- * <b>divisor</b> == 0. */
+ * <b>divisor</b> == 0. If no such x can be expressed as an unsigned, return
+ * UINT_MAX */
unsigned
round_to_next_multiple_of(unsigned number, unsigned divisor)
{
+ tor_assert(divisor > 0);
+ if (UINT_MAX - divisor + 1 < number)
+ return UINT_MAX;
number += divisor - 1;
number -= number % divisor;
return number;
}
/** Return the lowest x such that x is at least <b>number</b>, and x modulo
- * <b>divisor</b> == 0. */
+ * <b>divisor</b> == 0. If no such x can be expressed as a uint32_t, return
+ * UINT32_MAX */
uint32_t
round_uint32_to_next_multiple_of(uint32_t number, uint32_t divisor)
{
+ tor_assert(divisor > 0);
+ if (UINT32_MAX - divisor + 1 < number)
+ return UINT32_MAX;
+
number += divisor - 1;
number -= number % divisor;
return number;
}
/** Return the lowest x such that x is at least <b>number</b>, and x modulo
- * <b>divisor</b> == 0. */
+ * <b>divisor</b> == 0. If no such x can be expressed as a uint64_t, return
+ * UINT64_MAX */
uint64_t
round_uint64_to_next_multiple_of(uint64_t number, uint64_t divisor)
{
+ tor_assert(divisor > 0);
+ if (UINT64_MAX - divisor + 1 < number)
+ return UINT64_MAX;
number += divisor - 1;
number -= number % divisor;
return number;
}
/** Return the lowest x in [INT64_MIN, INT64_MAX] such that x is at least
- * <b>number</b>, and x modulo <b>divisor</b> == 0. */
+ * <b>number</b>, and x modulo <b>divisor</b> == 0. If no such x can be
+ * expressed as an int64_t, return INT64_MAX */
int64_t
round_int64_to_next_multiple_of(int64_t number, int64_t divisor)
{
tor_assert(divisor > 0);
- if (number >= 0 && INT64_MAX - divisor + 1 >= number)
+ if (INT64_MAX - divisor + 1 < number)
+ return INT64_MAX;
+ if (number >= 0)
number += divisor - 1;
number -= number % divisor;
return number;
@@ -537,33 +553,44 @@ int64_t
sample_laplace_distribution(double mu, double b, double p)
{
double result;
-
tor_assert(p >= 0.0 && p < 1.0);
+
/* This is the "inverse cumulative distribution function" from:
* http://en.wikipedia.org/wiki/Laplace_distribution */
- result = mu - b * (p > 0.5 ? 1.0 : -1.0)
- * tor_mathlog(1.0 - 2.0 * fabs(p - 0.5));
-
- if (result >= INT64_MAX)
- return INT64_MAX;
- else if (result <= INT64_MIN)
+ if (p <= 0.0) {
+ /* Avoid taking log(0.0) == -INFINITY, as some processors or compiler
+ * options can cause the program to trap. */
return INT64_MIN;
- else
- return (int64_t) result;
+ }
+
+ result = mu - b * (p > 0.5 ? 1.0 : -1.0)
+ * tor_mathlog(1.0 - 2.0 * fabs(p - 0.5));
+
+ return clamp_double_to_int64(result);
}
-/** Add random noise between INT64_MIN and INT64_MAX coming from a
- * Laplace distribution with mu = 0 and b = <b>delta_f</b>/<b>epsilon</b>
- * to <b>signal</b> based on the provided <b>random</b> value in
- * [0.0, 1.0[. */
+/** Add random noise between INT64_MIN and INT64_MAX coming from a Laplace
+ * distribution with mu = 0 and b = <b>delta_f</b>/<b>epsilon</b> to
+ * <b>signal</b> based on the provided <b>random</b> value in [0.0, 1.0[.
+ * The epsilon value must be between ]0.0, 1.0]. delta_f must be greater
+ * than 0. */
int64_t
add_laplace_noise(int64_t signal, double random, double delta_f,
double epsilon)
{
- int64_t noise = sample_laplace_distribution(
- 0.0, /* just add noise, no further signal */
- delta_f / epsilon, random);
+ int64_t noise;
+
+ /* epsilon MUST be between ]0.0, 1.0] */
+ tor_assert(epsilon > 0.0 && epsilon <= 1.0);
+ /* delta_f MUST be greater than 0. */
+ tor_assert(delta_f > 0.0);
+ /* Just add noise, no further signal */
+ noise = sample_laplace_distribution(0.0,
+ delta_f / epsilon,
+ random);
+
+ /* Clip (signal + noise) to [INT64_MIN, INT64_MAX] */
if (noise > 0 && INT64_MAX - noise < signal)
return INT64_MAX;
else if (noise < 0 && INT64_MIN - noise > signal)
@@ -5385,3 +5412,38 @@ tor_weak_random_range(tor_weak_rng_t *rng, int32_t top)
return result;
}
+/** Cast a given double value to a int64_t. Return 0 if number is NaN.
+ * Returns either INT64_MIN or INT64_MAX if number is outside of the int64_t
+ * range. */
+int64_t
+clamp_double_to_int64(double number)
+{
+ int exp;
+
+ /* NaN is a special case that can't be used with the logic below. */
+ if (isnan(number)) {
+ return 0;
+ }
+
+ /* Time to validate if result can overflows a int64_t value. Fun with
+ * float! Find that exponent exp such that
+ * number == x * 2^exp
+ * for some x with abs(x) in [0.5, 1.0). Note that this implies that the
+ * magnitude of number is strictly less than 2^exp.
+ *
+ * If number is infinite, the call to frexp is legal but the contents of
+ * exp are unspecified. */
+ frexp(number, &exp);
+
+ /* If the magnitude of number is strictly less than 2^63, the truncated
+ * version of number is guaranteed to be representable. The only
+ * representable integer for which this is not the case is INT64_MIN, but
+ * it is covered by the logic below. */
+ if (isfinite(number) && exp <= 63) {
+ return number;
+ }
+
+ /* Handle infinities and finite numbers with magnitude >= 2^63. */
+ return signbit(number) ? INT64_MIN : INT64_MAX;
+}
+
diff --git a/src/common/util.h b/src/common/util.h
index 8bb4505e86..165bc0dcb3 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -185,6 +185,7 @@ int64_t sample_laplace_distribution(double mu, double b, double p);
int64_t add_laplace_noise(int64_t signal, double random, double delta_f,
double epsilon);
int n_bits_set_u8(uint8_t v);
+int64_t clamp_double_to_int64(double number);
/* Compute the CEIL of <b>a</b> divided by <b>b</b>, for nonnegative <b>a</b>
* and positive <b>b</b>. Works on integer types only. Not defined if a+b can
diff --git a/src/ext/csiphash.c b/src/ext/csiphash.c
index 27c5358ebe..b60f73a7ff 100644
--- a/src/ext/csiphash.c
+++ b/src/ext/csiphash.c
@@ -97,65 +97,48 @@
#endif
uint64_t siphash24(const void *src, unsigned long src_sz, const struct sipkey *key) {
+ const uint8_t *m = src;
uint64_t k0 = key->k0;
uint64_t k1 = key->k1;
- uint64_t b = (uint64_t)src_sz << 56;
-#ifdef UNALIGNED_OK
- const uint64_t *in = (uint64_t*)src;
-#else
- /* On platforms where alignment matters, if 'in' is a pointer to a
- * datatype that must be aligned, the compiler is allowed to
- * generate code that assumes that it is aligned as such.
- */
- const uint8_t *in = (uint8_t *)src;
-#endif
-
- uint64_t t;
- uint8_t *pt, *m;
+ uint64_t last7 = (uint64_t)(src_sz & 0xff) << 56;
+ size_t i, blocks;
uint64_t v0 = k0 ^ 0x736f6d6570736575ULL;
uint64_t v1 = k1 ^ 0x646f72616e646f6dULL;
uint64_t v2 = k0 ^ 0x6c7967656e657261ULL;
uint64_t v3 = k1 ^ 0x7465646279746573ULL;
- while (src_sz >= 8) {
+ for (i = 0, blocks = (src_sz & ~7); i < blocks; i+= 8) {
#ifdef UNALIGNED_OK
- uint64_t mi = _le64toh(*in);
- in += 1;
+ uint64_t mi = _le64toh(*(m + i));
#else
uint64_t mi;
- memcpy(&mi, in, 8);
+ memcpy(&mi, m + i, 8);
mi = _le64toh(mi);
- in += 8;
#endif
- src_sz -= 8;
v3 ^= mi;
DOUBLE_ROUND(v0,v1,v2,v3);
v0 ^= mi;
}
- t = 0; pt = (uint8_t*)&t; m = (uint8_t*)in;
- switch (src_sz) {
- case 7: pt[6] = m[6];
- case 6: pt[5] = m[5];
- case 5: pt[4] = m[4];
-#ifdef UNALIGNED_OK
- case 4: *((uint32_t*)&pt[0]) = *((uint32_t*)&m[0]); break;
-#else
- case 4: pt[3] = m[3];
-#endif
- case 3: pt[2] = m[2];
- case 2: pt[1] = m[1];
- case 1: pt[0] = m[0];
+ switch (src_sz - blocks) {
+ case 7: last7 |= (uint64_t)m[i + 6] << 48;
+ case 6: last7 |= (uint64_t)m[i + 5] << 40;
+ case 5: last7 |= (uint64_t)m[i + 4] << 32;
+ case 4: last7 |= (uint64_t)m[i + 3] << 24;
+ case 3: last7 |= (uint64_t)m[i + 2] << 16;
+ case 2: last7 |= (uint64_t)m[i + 1] << 8;
+ case 1: last7 |= (uint64_t)m[i + 0] ;
+ case 0:
+ default:;
}
- b |= _le64toh(t);
-
- v3 ^= b;
+ v3 ^= last7;
DOUBLE_ROUND(v0,v1,v2,v3);
- v0 ^= b; v2 ^= 0xff;
+ v0 ^= last7;
+ v2 ^= 0xff;
DOUBLE_ROUND(v0,v1,v2,v3);
DOUBLE_ROUND(v0,v1,v2,v3);
- return (v0 ^ v1) ^ (v2 ^ v3);
+ return v0 ^ v1 ^ v2 ^ v3;
}
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index 716024df6a..c4992d47ff 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -44,11 +44,16 @@ static smartlist_t *global_circuitlist = NULL;
/** A list of all the circuits in CIRCUIT_STATE_CHAN_WAIT. */
static smartlist_t *circuits_pending_chans = NULL;
+/** A list of all the circuits that have been marked with
+ * circuit_mark_for_close and which are waiting for circuit_about_to_free. */
+static smartlist_t *circuits_pending_close = NULL;
+
static void circuit_free_cpath_node(crypt_path_t *victim);
static void cpath_ref_decref(crypt_path_reference_t *cpath_ref);
//static void circuit_set_rend_token(or_circuit_t *circ, int is_rend_circ,
// const uint8_t *token);
static void circuit_clear_rend_token(or_circuit_t *circ);
+static void circuit_about_to_free(circuit_t *circ);
/********* END VARIABLES ************/
@@ -451,16 +456,27 @@ circuit_count_pending_on_channel(channel_t *chan)
void
circuit_close_all_marked(void)
{
+ if (circuits_pending_close == NULL)
+ return;
+
smartlist_t *lst = circuit_get_global_list();
- SMARTLIST_FOREACH_BEGIN(lst, circuit_t *, circ) {
- /* Fix up index if SMARTLIST_DEL_CURRENT just moved this one. */
- circ->global_circuitlist_idx = circ_sl_idx;
- if (circ->marked_for_close) {
- circ->global_circuitlist_idx = -1;
- circuit_free(circ);
- SMARTLIST_DEL_CURRENT(lst, circ);
+ SMARTLIST_FOREACH_BEGIN(circuits_pending_close, circuit_t *, circ) {
+ tor_assert(circ->marked_for_close);
+
+ /* Remove it from the circuit list. */
+ int idx = circ->global_circuitlist_idx;
+ smartlist_del(lst, idx);
+ if (idx < smartlist_len(lst)) {
+ circuit_t *replacement = smartlist_get(lst, idx);
+ replacement->global_circuitlist_idx = idx;
}
+ circ->global_circuitlist_idx = -1;
+
+ circuit_about_to_free(circ);
+ circuit_free(circ);
} SMARTLIST_FOREACH_END(circ);
+
+ smartlist_clear(circuits_pending_close);
}
/** Return the head of the global linked list of circuits. */
@@ -1703,6 +1719,39 @@ circuit_mark_for_close_, (circuit_t *circ, int reason, int line,
reason = END_CIRC_REASON_NONE;
}
+ circ->marked_for_close = line;
+ circ->marked_for_close_file = file;
+ circ->marked_for_close_reason = reason;
+ circ->marked_for_close_orig_reason = orig_reason;
+
+ if (!CIRCUIT_IS_ORIGIN(circ)) {
+ or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
+ if (or_circ->rend_splice) {
+ if (!or_circ->rend_splice->base_.marked_for_close) {
+ /* do this after marking this circuit, to avoid infinite recursion. */
+ circuit_mark_for_close(TO_CIRCUIT(or_circ->rend_splice), reason);
+ }
+ or_circ->rend_splice = NULL;
+ }
+ }
+
+ if (circuits_pending_close == NULL)
+ circuits_pending_close = smartlist_new();
+
+ smartlist_add(circuits_pending_close, circ);
+}
+
+/** Called immediately before freeing a marked circuit <b>circ</b>.
+ * Disconnects the circuit from other data structures, launches events
+ * as appropriate, and performs other housekeeping.
+ */
+static void
+circuit_about_to_free(circuit_t *circ)
+{
+
+ int reason = circ->marked_for_close_reason;
+ int orig_reason = circ->marked_for_close_orig_reason;
+
if (circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) {
onion_pending_remove(TO_OR_CIRCUIT(circ));
}
@@ -1726,6 +1775,7 @@ circuit_mark_for_close_, (circuit_t *circ, int reason, int line,
(circ->state == CIRCUIT_STATE_OPEN)?CIRC_EVENT_CLOSED:CIRC_EVENT_FAILED,
orig_reason);
}
+
if (circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
int timed_out = (reason == END_CIRC_REASON_TIMEOUT);
@@ -1810,20 +1860,6 @@ circuit_mark_for_close_, (circuit_t *circ, int reason, int line,
connection_edge_destroy(circ->n_circ_id, conn);
ocirc->p_streams = NULL;
}
-
- circ->marked_for_close = line;
- circ->marked_for_close_file = file;
-
- if (!CIRCUIT_IS_ORIGIN(circ)) {
- or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
- if (or_circ->rend_splice) {
- if (!or_circ->rend_splice->base_.marked_for_close) {
- /* do this after marking this circuit, to avoid infinite recursion. */
- circuit_mark_for_close(TO_CIRCUIT(or_circ->rend_splice), reason);
- }
- or_circ->rend_splice = NULL;
- }
- }
}
/** Given a marked circuit <b>circ</b>, aggressively free its cell queues to
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index 00340fd689..5b24425877 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -1123,7 +1123,7 @@ circuit_build_needed_circs(time_t now)
* don't require an exit circuit, review in #13814.
* This allows HSs to function in a consensus without exits. */
if (router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN)
- connection_ap_attach_pending();
+ connection_ap_rescan_and_attach_pending();
/* make sure any hidden services have enough intro points
* HS intro point streams only require an internal circuit */
@@ -1475,7 +1475,7 @@ circuit_has_opened(origin_circuit_t *circ)
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
rend_client_rendcirc_has_opened(circ);
/* Start building an intro circ if we don't have one yet. */
- connection_ap_attach_pending();
+ connection_ap_attach_pending(1);
/* This isn't a call to circuit_try_attaching_streams because a
* circuit in _C_ESTABLISH_REND state isn't connected to its
* hidden service yet, thus we can't attach streams to it yet,
@@ -1537,14 +1537,14 @@ void
circuit_try_attaching_streams(origin_circuit_t *circ)
{
/* Attach streams to this circuit if we can. */
- connection_ap_attach_pending();
+ connection_ap_attach_pending(1);
/* The call to circuit_try_clearing_isolation_state here will do
* nothing and return 0 if we didn't attach any streams to circ
* above. */
if (circuit_try_clearing_isolation_state(circ)) {
/* Maybe *now* we can attach some streams to this circuit. */
- connection_ap_attach_pending();
+ connection_ap_attach_pending(1);
}
}
diff --git a/src/or/config.c b/src/or/config.c
index 431d3664c5..8d8c186d9c 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -312,6 +312,7 @@ static config_var_t option_vars_[] = {
V(LogMessageDomains, BOOL, "0"),
V(LogTimeGranularity, MSEC_INTERVAL, "1 second"),
V(TruncateLogFile, BOOL, "0"),
+ V(SyslogIdentityTag, STRING, NULL),
V(LongLivedPorts, CSV,
"21,22,706,1863,5050,5190,5222,5223,6523,6667,6697,8300"),
VAR("MapAddress", LINELIST, AddressMap, NULL),
@@ -561,7 +562,6 @@ static char *get_bindaddr_from_transport_listen_line(const char *line,
static int parse_dir_authority_line(const char *line,
dirinfo_type_t required_type,
int validate_only);
-static void port_cfg_free(port_cfg_t *port);
static int parse_ports(or_options_t *options, int validate_only,
char **msg_out, int *n_ports_out,
int *world_writable_control_socket);
@@ -625,8 +625,8 @@ static char *global_dirfrontpagecontents = NULL;
static smartlist_t *configured_ports = NULL;
/** Return the contents of our frontpage string, or NULL if not configured. */
-const char *
-get_dirportfrontpage(void)
+MOCK_IMPL(const char*,
+get_dirportfrontpage, (void))
{
return global_dirfrontpagecontents;
}
@@ -3996,6 +3996,12 @@ options_transition_allowed(const or_options_t *old,
return -1;
}
+ if (!opt_streq(old->SyslogIdentityTag, new_val->SyslogIdentityTag)) {
+ *msg = tor_strdup("While Tor is running, changing "
+ "SyslogIdentityTag is not allowed.");
+ return -1;
+ }
+
if ((old->HardwareAccel != new_val->HardwareAccel)
|| !opt_streq(old->AccelName, new_val->AccelName)
|| !opt_streq(old->AccelDir, new_val->AccelDir)) {
@@ -4937,7 +4943,7 @@ options_init_logs(const or_options_t *old_options, or_options_t *options,
!strcasecmp(smartlist_get(elts,0), "syslog")) {
#ifdef HAVE_SYSLOG_H
if (!validate_only) {
- add_syslog_log(severity);
+ add_syslog_log(severity, options->SyslogIdentityTag);
}
#else
log_warn(LD_CONFIG, "Syslog is not supported on this system. Sorry.");
@@ -5730,7 +5736,7 @@ parse_dir_fallback_line(const char *line,
}
/** Allocate and return a new port_cfg_t with reasonable defaults. */
-static port_cfg_t *
+STATIC port_cfg_t *
port_cfg_new(size_t namelen)
{
tor_assert(namelen <= SIZE_T_CEILING - sizeof(port_cfg_t) - 1);
@@ -5742,7 +5748,7 @@ port_cfg_new(size_t namelen)
}
/** Free all storage held in <b>port</b> */
-static void
+STATIC void
port_cfg_free(port_cfg_t *port)
{
tor_free(port);
@@ -5796,9 +5802,9 @@ warn_nonlocal_ext_orports(const smartlist_t *ports, const char *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_nonlocal</b> is
- * true, then emit a stronger warning and remove the port from the list.
+/** Given a list of port_cfg_t in <b>ports</b>, warn if any controller port
+ * there is listening on any non-loopback address. If <b>forbid_nonlocal</b>
+ * is true, then emit a stronger warning and remove the port from the list.
*/
static void
warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid_nonlocal)
@@ -6666,8 +6672,8 @@ check_server_ports(const smartlist_t *ports,
/** Return a list of port_cfg_t for client ports parsed from the
* options. */
-const smartlist_t *
-get_configured_ports(void)
+MOCK_IMPL(const smartlist_t *,
+get_configured_ports,(void))
{
if (!configured_ports)
configured_ports = smartlist_new();
diff --git a/src/or/config.h b/src/or/config.h
index 0ee1e1a3c4..7e8868804e 100644
--- a/src/or/config.h
+++ b/src/or/config.h
@@ -14,8 +14,8 @@
#include "testsupport.h"
-const char *get_dirportfrontpage(void);
-MOCK_DECL(const or_options_t *,get_options,(void));
+MOCK_DECL(const char*, get_dirportfrontpage, (void));
+MOCK_DECL(const or_options_t *, get_options, (void));
or_options_t *get_options_mutable(void);
int set_options(or_options_t *new_val, char **msg);
void config_free_all(void);
@@ -76,7 +76,7 @@ int write_to_data_subdir(const char* subdir, const char* fname,
int get_num_cpus(const or_options_t *options);
-const smartlist_t *get_configured_ports(void);
+MOCK_DECL(const smartlist_t *,get_configured_ports,(void));
int get_first_advertised_port_by_type_af(int listener_type,
int address_family);
#define get_primary_or_port() \
@@ -140,6 +140,8 @@ smartlist_t *get_options_for_server_transport(const char *transport);
extern struct config_format_t options_format;
#endif
+STATIC port_cfg_t *port_cfg_new(size_t namelen);
+STATIC void port_cfg_free(port_cfg_t *port);
STATIC void or_options_free(or_options_t *options);
STATIC int options_validate(or_options_t *old_options,
or_options_t *options,
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 729ef8a4c7..aad1ea40f6 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -503,6 +503,16 @@ connection_edge_finished_connecting(edge_connection_t *edge_conn)
return connection_edge_process_inbuf(edge_conn, 1);
}
+/** A list of all the entry_connection_t * objects that are not marked
+ * for close, and are in AP_CONN_STATE_CIRCUIT_WAIT.
+ *
+ * (Right now, we check in several places to make sure that this list is
+ * correct. When it's incorrect, we'll fix it, and log a BUG message.)
+ */
+static smartlist_t *pending_entry_connections = NULL;
+
+static int untried_pending_connections = 0;
+
/** Common code to connection_(ap|exit)_about_to_close. */
static void
connection_edge_about_to_close(edge_connection_t *edge_conn)
@@ -514,6 +524,27 @@ connection_edge_about_to_close(edge_connection_t *edge_conn)
conn->marked_for_close_file, conn->marked_for_close);
tor_fragile_assert();
}
+
+ if (TO_CONN(edge_conn)->type != CONN_TYPE_AP ||
+ PREDICT_UNLIKELY(NULL == pending_entry_connections))
+ return;
+
+ entry_connection_t *entry_conn = EDGE_TO_ENTRY_CONN(edge_conn);
+
+ if (TO_CONN(edge_conn)->state == AP_CONN_STATE_CIRCUIT_WAIT) {
+ smartlist_remove(pending_entry_connections, entry_conn);
+ }
+
+#if 1
+ /* Check to make sure that this isn't in pending_entry_connections if it
+ * didn't actually belong there. */
+ if (TO_CONN(edge_conn)->type == CONN_TYPE_AP &&
+ smartlist_contains(pending_entry_connections, entry_conn)) {
+ log_warn(LD_BUG, "What was %p doing in pending_entry_connections???",
+ entry_conn);
+ smartlist_remove(pending_entry_connections, entry_conn);
+ }
+#endif
}
/** Called when we're about to finally unlink and free an AP (client)
@@ -711,26 +742,114 @@ connection_ap_expire_beginning(void)
} SMARTLIST_FOREACH_END(base_conn);
}
-/** Tell any AP streams that are waiting for a new circuit to try again,
- * either attaching to an available circ or launching a new one.
+/**
+ * As connection_ap_attach_pending, but first scans the entire connection
+ * array to see if any elements are missing.
*/
void
-connection_ap_attach_pending(void)
+connection_ap_rescan_and_attach_pending(void)
{
entry_connection_t *entry_conn;
smartlist_t *conns = get_connection_array();
+
+ if (PREDICT_UNLIKELY(NULL == pending_entry_connections))
+ pending_entry_connections = smartlist_new();
+
SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
if (conn->marked_for_close ||
conn->type != CONN_TYPE_AP ||
conn->state != AP_CONN_STATE_CIRCUIT_WAIT)
continue;
+
entry_conn = TO_ENTRY_CONN(conn);
+ if (! smartlist_contains(pending_entry_connections, entry_conn)) {
+ log_warn(LD_BUG, "Found a connection %p that was supposed to be "
+ "in pending_entry_connections, but wasn't. No worries; "
+ "adding it.",
+ pending_entry_connections);
+ untried_pending_connections = 1;
+ smartlist_add(pending_entry_connections, entry_conn);
+ }
+
+ } SMARTLIST_FOREACH_END(conn);
+
+ connection_ap_attach_pending(1);
+}
+
+/** Tell any AP streams that are listed as waiting for a new circuit to try
+ * again, either attaching to an available circ or launching a new one.
+ *
+ * If <b>retry</b> is false, only check the list if it contains at least one
+ * streams that we have not yet tried to attach to a circuit.
+ */
+void
+connection_ap_attach_pending(int retry)
+{
+ if (PREDICT_UNLIKELY(!pending_entry_connections)) {
+ return;
+ }
+
+ if (untried_pending_connections == 0 && !retry)
+ return;
+
+ SMARTLIST_FOREACH_BEGIN(pending_entry_connections,
+ entry_connection_t *, entry_conn) {
+ connection_t *conn = ENTRY_TO_CONN(entry_conn);
+ if (conn->marked_for_close) {
+ SMARTLIST_DEL_CURRENT(pending_entry_connections, entry_conn);
+ continue;
+ }
+ if (conn->state != AP_CONN_STATE_CIRCUIT_WAIT) {
+ log_warn(LD_BUG, "%p is no longer in circuit_wait. Why is it on "
+ "pending_entry_connections?", entry_conn);
+ SMARTLIST_DEL_CURRENT(pending_entry_connections, entry_conn);
+ continue;
+ }
+
if (connection_ap_handshake_attach_circuit(entry_conn) < 0) {
if (!conn->marked_for_close)
connection_mark_unattached_ap(entry_conn,
END_STREAM_REASON_CANT_ATTACH);
}
- } SMARTLIST_FOREACH_END(conn);
+
+ if (conn->marked_for_close ||
+ conn->type != CONN_TYPE_AP ||
+ conn->state != AP_CONN_STATE_CIRCUIT_WAIT) {
+ SMARTLIST_DEL_CURRENT(pending_entry_connections, entry_conn);
+ }
+
+ } SMARTLIST_FOREACH_END(entry_conn);
+
+ untried_pending_connections = 0;
+}
+
+/** Mark <b>entry_conn</b> as needing to get attached to a circuit.
+ *
+ * And <b>entry_conn</b> must be in AP_CONN_STATE_CIRCUIT_WAIT,
+ * should not already be pending a circuit. The circuit will get
+ * launched or the connection will get attached the next time we
+ * call connection_ap_attach_pending().
+ */
+void
+connection_ap_mark_as_pending_circuit(entry_connection_t *entry_conn)
+{
+ connection_t *conn = ENTRY_TO_CONN(entry_conn);
+ tor_assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
+ if (conn->marked_for_close)
+ return;
+
+ if (PREDICT_UNLIKELY(NULL == pending_entry_connections))
+ pending_entry_connections = smartlist_new();
+
+ if (PREDICT_UNLIKELY(smartlist_contains(pending_entry_connections,
+ entry_conn))) {
+ log_warn(LD_BUG, "What?? pending_entry_connections already contains %p!",
+ entry_conn);
+ return;
+ }
+
+ untried_pending_connections = 1;
+ smartlist_add(pending_entry_connections, entry_conn);
}
/** Tell any AP streams that are waiting for a one-hop tunnel to
@@ -851,12 +970,12 @@ connection_ap_detach_retriable(entry_connection_t *conn,
* a tunneled directory connection, then just attach it. */
ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CIRCUIT_WAIT;
circuit_detach_stream(TO_CIRCUIT(circ),ENTRY_TO_EDGE_CONN(conn));
- return connection_ap_handshake_attach_circuit(conn);
+ connection_ap_mark_as_pending_circuit(conn);
} else {
ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CONTROLLER_WAIT;
circuit_detach_stream(TO_CIRCUIT(circ),ENTRY_TO_EDGE_CONN(conn));
- return 0;
}
+ return 0;
}
/** Check if <b>conn</b> is using a dangerous port. Then warn and/or
@@ -1454,10 +1573,12 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
/* If we were given a circuit to attach to, try to attach. Otherwise,
* try to find a good one and attach to that. */
int rv;
- if (circ)
- rv = connection_ap_handshake_attach_chosen_circuit(conn, circ, cpath);
- else
- rv = connection_ap_handshake_attach_circuit(conn);
+ if (circ) {
+ rv = connection_ap_handshake_attach_chosen_circuit(conn, circ, cpath);
+ } else {
+ connection_ap_mark_as_pending_circuit(conn);
+ rv = 0;
+ }
/* If the above function returned 0 then we're waiting for a circuit.
* if it returned 1, we're attached. Both are okay. But if it returned
@@ -1564,11 +1685,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
/* We have the descriptor so launch a connection to the HS. */
base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
log_info(LD_REND, "Descriptor is here. Great.");
- if (connection_ap_handshake_attach_circuit(conn) < 0) {
- if (!base_conn->marked_for_close)
- connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
- return -1;
- }
+ connection_ap_mark_as_pending_circuit(conn);
return 0;
}
@@ -2324,12 +2441,7 @@ connection_ap_make_link(connection_t *partner,
control_event_stream_status(conn, STREAM_EVENT_NEW, 0);
/* attaching to a dirty circuit is fine */
- if (connection_ap_handshake_attach_circuit(conn) < 0) {
- if (!base_conn->marked_for_close)
- connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
- return NULL;
- }
-
+ connection_ap_mark_as_pending_circuit(conn);
log_info(LD_APP,"... application connection created and linked.");
return conn;
}
@@ -3478,3 +3590,12 @@ circuit_clear_isolation(origin_circuit_t *circ)
circ->socks_username_len = circ->socks_password_len = 0;
}
+/** Free all storage held in module-scoped variables for connection_edge.c */
+void
+connection_edge_free_all(void)
+{
+ untried_pending_connections = 0;
+ smartlist_free(pending_entry_connections);
+ pending_entry_connections = NULL;
+}
+
diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h
index 7c0b9c0767..521e759aed 100644
--- a/src/or/connection_edge.h
+++ b/src/or/connection_edge.h
@@ -64,7 +64,9 @@ int connection_edge_is_rendezvous_stream(edge_connection_t *conn);
int connection_ap_can_use_exit(const entry_connection_t *conn,
const node_t *exit);
void connection_ap_expire_beginning(void);
-void connection_ap_attach_pending(void);
+void connection_ap_rescan_and_attach_pending(void);
+void connection_ap_attach_pending(int retry);
+void connection_ap_mark_as_pending_circuit(entry_connection_t *entry_conn);
void connection_ap_fail_onehop(const char *failed_digest,
cpath_build_state_t *build_state);
void circuit_discard_optional_exit_enclaves(extend_info_t *info);
@@ -100,6 +102,8 @@ int connection_edge_update_circuit_isolation(const entry_connection_t *conn,
void circuit_clear_isolation(origin_circuit_t *circ);
streamid_t get_unique_stream_id_by_circ(origin_circuit_t *circ);
+void connection_edge_free_all(void);
+
/** @name Begin-cell flags
*
* These flags are used in RELAY_BEGIN cells to change the default behavior
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index c454d3f076..73e4d19369 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -1450,17 +1450,12 @@ connection_tls_continue_handshake(or_connection_t *conn)
{
int result;
check_no_tls_errors();
- again:
- if (conn->base_.state == OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING) {
- // log_notice(LD_OR, "Renegotiate with %p", conn->tls);
- result = tor_tls_renegotiate(conn->tls);
- // log_notice(LD_OR, "Result: %d", result);
- } else {
- tor_assert(conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING);
- // log_notice(LD_OR, "Continue handshake with %p", conn->tls);
- result = tor_tls_handshake(conn->tls);
- // log_notice(LD_OR, "Result: %d", result);
- }
+
+ tor_assert(conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING);
+ // log_notice(LD_OR, "Continue handshake with %p", conn->tls);
+ result = tor_tls_handshake(conn->tls);
+ // log_notice(LD_OR, "Result: %d", result);
+
switch (result) {
CASE_TOR_TLS_ERROR_ANY:
log_info(LD_OR,"tls error [%s]. breaking connection.",
@@ -1469,23 +1464,10 @@ connection_tls_continue_handshake(or_connection_t *conn)
case TOR_TLS_DONE:
if (! tor_tls_used_v1_handshake(conn->tls)) {
if (!tor_tls_is_server(conn->tls)) {
- if (conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING) {
- if (tor_tls_received_v3_certificate(conn->tls)) {
- log_info(LD_OR, "Client got a v3 cert! Moving on to v3 "
- "handshake with ciphersuite %s",
- tor_tls_get_ciphersuite_name(conn->tls));
- return connection_or_launch_v3_or_handshake(conn);
- } else {
- log_debug(LD_OR, "Done with initial SSL handshake (client-side)."
- " Requesting renegotiation.");
- connection_or_change_state(conn,
- OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING);
- goto again;
- }
- }
- // log_notice(LD_OR,"Done. state was %d.", conn->base_.state);
+ tor_assert(conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING);
+ return connection_or_launch_v3_or_handshake(conn);
} else {
- /* v2/v3 handshake, but not a client. */
+ /* v2/v3 handshake, but we are not a client. */
log_debug(LD_OR, "Done with initial SSL handshake (server-side). "
"Expecting renegotiation or VERSIONS cell");
tor_tls_set_renegotiate_callback(conn->tls,
@@ -1498,6 +1480,7 @@ connection_tls_continue_handshake(or_connection_t *conn)
return 0;
}
}
+ tor_assert(tor_tls_is_server(conn->tls));
return connection_tls_finish_handshake(conn);
case TOR_TLS_WANTWRITE:
connection_start_writing(TO_CONN(conn));
@@ -1533,22 +1516,8 @@ connection_or_handle_event_cb(struct bufferevent *bufev, short event,
if (! tor_tls_used_v1_handshake(conn->tls)) {
if (!tor_tls_is_server(conn->tls)) {
if (conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING) {
- if (tor_tls_received_v3_certificate(conn->tls)) {
- log_info(LD_OR, "Client got a v3 cert!");
- if (connection_or_launch_v3_or_handshake(conn) < 0)
- connection_or_close_for_error(conn, 0);
- return;
- } else {
- connection_or_change_state(conn,
- OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING);
- tor_tls_unblock_renegotiation(conn->tls);
- if (bufferevent_ssl_renegotiate(conn->base_.bufev)<0) {
- log_warn(LD_OR, "Start_renegotiating went badly.");
- connection_or_close_for_error(conn, 0);
- }
- tor_tls_unblock_renegotiation(conn->tls);
- return; /* ???? */
- }
+ if (connection_or_launch_v3_or_handshake(conn) < 0)
+ connection_or_close_for_error(conn, 0);
}
} else {
const int handshakes = tor_tls_get_num_server_handshakes(conn->tls);
@@ -1800,6 +1769,8 @@ connection_tls_finish_handshake(or_connection_t *conn)
char digest_rcvd[DIGEST_LEN];
int started_here = connection_or_nonopen_was_started_here(conn);
+ tor_assert(!started_here);
+
log_debug(LD_HANDSHAKE,"%s tls handshake on %p with %s done, using "
"ciphersuite %s. verifying.",
started_here?"outgoing":"incoming",
@@ -1815,10 +1786,8 @@ connection_tls_finish_handshake(or_connection_t *conn)
if (tor_tls_used_v1_handshake(conn->tls)) {
conn->link_proto = 1;
- if (!started_here) {
- connection_or_init_conn_from_address(conn, &conn->base_.addr,
- conn->base_.port, digest_rcvd, 0);
- }
+ connection_or_init_conn_from_address(conn, &conn->base_.addr,
+ conn->base_.port, digest_rcvd, 0);
tor_tls_block_renegotiation(conn->tls);
rep_hist_note_negotiated_link_proto(1, started_here);
return connection_or_set_state_open(conn);
@@ -1826,10 +1795,8 @@ connection_tls_finish_handshake(or_connection_t *conn)
connection_or_change_state(conn, OR_CONN_STATE_OR_HANDSHAKING_V2);
if (connection_init_or_handshake_state(conn, started_here) < 0)
return -1;
- if (!started_here) {
- connection_or_init_conn_from_address(conn, &conn->base_.addr,
- conn->base_.port, digest_rcvd, 0);
- }
+ connection_or_init_conn_from_address(conn, &conn->base_.addr,
+ conn->base_.port, digest_rcvd, 0);
return connection_or_send_versions(conn, 0);
}
}
@@ -1844,7 +1811,6 @@ static int
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(get_circuit_build_times_mutable());
diff --git a/src/or/control.c b/src/or/control.c
index c89fdde138..f2eab7b352 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -1927,6 +1927,22 @@ getinfo_helper_dir(control_connection_t *control_conn,
*errmsg = "Not found in cache";
return -1;
}
+ } else if (!strcmpstart(question, "hs/service/desc/id/")) {
+ rend_cache_entry_t *e = NULL;
+
+ question += strlen("hs/service/desc/id/");
+ if (strlen(question) != REND_SERVICE_ID_LEN_BASE32) {
+ *errmsg = "Invalid address";
+ return -1;
+ }
+
+ if (!rend_cache_lookup_v2_desc_as_service(question, &e)) {
+ /* Descriptor found in cache */
+ *answer = tor_strdup(e->desc);
+ } else {
+ *errmsg = "Not found in cache";
+ return -1;
+ }
} else if (!strcmpstart(question, "md/id/")) {
const node_t *node = node_get_by_hex_id(question+strlen("md/id/"));
const microdesc_t *md = NULL;
@@ -2481,6 +2497,8 @@ static const getinfo_item_t getinfo_items[] = {
PREFIX("extra-info/digest/", dir, "Extra-info documents by digest."),
PREFIX("hs/client/desc/id", dir,
"Hidden Service descriptor in client's cache by onion."),
+ PREFIX("hs/service/desc/id/", dir,
+ "Hidden Service descriptor in services's cache by onion."),
PREFIX("net/listeners/", listeners, "Bound addresses by type"),
ITEM("ns/all", networkstatus,
"Brief summary of router status (v2 directory format)"),
@@ -2544,6 +2562,12 @@ static const getinfo_item_t getinfo_items[] = {
"v3 Networkstatus consensus as retrieved from a DirPort."),
ITEM("exit-policy/default", policies,
"The default value appended to the configured exit policy."),
+ ITEM("exit-policy/reject-private/default", policies,
+ "The default rules appended to the configured exit policy by"
+ " ExitPolicyRejectPrivate."),
+ ITEM("exit-policy/reject-private/relay", policies,
+ "The relay-specific rules appended to the configured exit policy by"
+ " ExitPolicyRejectPrivate."),
ITEM("exit-policy/full", policies, "The entire exit policy of onion router"),
ITEM("exit-policy/ipv4", policies, "IPv4 parts of exit policy"),
ITEM("exit-policy/ipv6", policies, "IPv6 parts of exit policy"),
@@ -6232,6 +6256,31 @@ get_desc_id_from_query(const rend_data_t *rend_data, const char *hsdir_fp)
return desc_id;
}
+/** send HS_DESC CREATED event when a local service generates a descriptor.
+ *
+ * <b>service_id</b> is the descriptor onion address.
+ * <b>desc_id_base32</b> is the descriptor ID.
+ * <b>replica</b> is the the descriptor replica number.
+ */
+void
+control_event_hs_descriptor_created(const char *service_id,
+ const char *desc_id_base32,
+ int replica)
+{
+ if (!service_id || !desc_id_base32) {
+ log_warn(LD_BUG, "Called with service_digest==%p, "
+ "desc_id_base32==%p", service_id, desc_id_base32);
+ return;
+ }
+
+ send_control_event(EVENT_HS_DESC,
+ "650 HS_DESC CREATED %s UNKNOWN UNKNOWN %s "
+ "REPLICA=%d\r\n",
+ service_id,
+ desc_id_base32,
+ replica);
+}
+
/** send HS_DESC upload event.
*
* <b>service_id</b> is the descriptor onion address.
diff --git a/src/or/control.h b/src/or/control.h
index fdf7903cb8..1f8e2bcdc6 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -117,6 +117,9 @@ MOCK_DECL(const char *, node_describe_longname_by_id,(const char *id_digest));
void control_event_hs_descriptor_requested(const rend_data_t *rend_query,
const char *desc_id_base32,
const char *hs_dir);
+void control_event_hs_descriptor_created(const char *service_id,
+ const char *desc_id_base32,
+ int replica);
void control_event_hs_descriptor_upload(const char *service_id,
const char *desc_id_base32,
const char *hs_dir);
diff --git a/src/or/directory.c b/src/or/directory.c
index 9461606f1b..0a09e833cd 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -991,10 +991,7 @@ directory_initiate_command_rend(const tor_addr_t *_addr,
switch (connection_connect(TO_CONN(conn), conn->base_.address, &addr,
dir_port, &socket_error)) {
case -1:
- connection_dir_request_failed(conn); /* retry if we want */
- /* XXX we only pass 'conn' above, not 'resource', 'payload',
- * etc. So in many situations it can't retry! -RD */
- connection_free(TO_CONN(conn));
+ connection_mark_for_close(TO_CONN(conn));
return;
case 1:
/* start flushing conn */
@@ -2614,7 +2611,7 @@ choose_compression_level(ssize_t n_bytes)
* service descriptor. On finding one, write a response into
* conn-\>outbuf. If the request is unrecognized, send a 400.
* Always return 0. */
-static int
+STATIC int
directory_handle_command_get(dir_connection_t *conn, const char *headers,
const char *req_body, size_t req_body_len)
{
@@ -2874,7 +2871,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
});
if (global_write_bucket_low(TO_CONN(conn), estimated_len, 2)) {
- write_http_status_line(conn, 503, "Directory busy, try again later.");
+ write_http_status_line(conn, 503, "Directory busy, try again later");
goto vote_done;
}
write_http_response_header(conn, body_len ? body_len : -1, compressed,
@@ -3071,7 +3068,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
len += c->cache_info.signed_descriptor_len);
if (global_write_bucket_low(TO_CONN(conn), compressed?len/2:len, 2)) {
- write_http_status_line(conn, 503, "Directory busy, try again later.");
+ write_http_status_line(conn, 503, "Directory busy, try again later");
goto keys_done;
}
@@ -3398,7 +3395,7 @@ connection_dir_finished_flushing(dir_connection_t *conn)
tor_assert(conn->base_.type == CONN_TYPE_DIR);
/* Note that we have finished writing the directory response. For direct
- * connections this means we're done, for tunneled connections its only
+ * connections this means we're done; for tunneled connections it's only
* an intermediate step. */
if (conn->dirreq_id)
geoip_change_dirreq_state(conn->dirreq_id, DIRREQ_TUNNELED,
diff --git a/src/or/directory.h b/src/or/directory.h
index 4899eb5c8c..427183cac9 100644
--- a/src/or/directory.h
+++ b/src/or/directory.h
@@ -127,6 +127,10 @@ STATIC int purpose_needs_anonymity(uint8_t dir_purpose,
uint8_t router_purpose);
STATIC dirinfo_type_t dir_fetch_type(int dir_purpose, int router_purpose,
const char *resource);
+STATIC int directory_handle_command_get(dir_connection_t *conn,
+ const char *headers,
+ const char *req_body,
+ size_t req_body_len);
#endif
#endif
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index d8e6ee2229..0449e9d8d9 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -3373,8 +3373,8 @@ dirvote_free_all(void)
* ==== */
/** Return the body of the consensus that we're currently trying to build. */
-const char *
-dirvote_get_pending_consensus(consensus_flavor_t flav)
+MOCK_IMPL(const char *,
+dirvote_get_pending_consensus, (consensus_flavor_t flav))
{
tor_assert(((int)flav) >= 0 && (int)flav < N_CONSENSUS_FLAVORS);
return pending_consensuses[flav].body;
@@ -3382,8 +3382,8 @@ dirvote_get_pending_consensus(consensus_flavor_t flav)
/** Return the signatures that we know for the consensus that we're currently
* trying to build. */
-const char *
-dirvote_get_pending_detached_signatures(void)
+MOCK_IMPL(const char *,
+dirvote_get_pending_detached_signatures, (void))
{
return pending_consensus_signatures;
}
diff --git a/src/or/dirvote.h b/src/or/dirvote.h
index dca8540870..966d163088 100644
--- a/src/or/dirvote.h
+++ b/src/or/dirvote.h
@@ -136,8 +136,10 @@ int dirvote_add_signatures(const char *detached_signatures_body,
const char **msg_out);
/* Item access */
-const char *dirvote_get_pending_consensus(consensus_flavor_t flav);
-const char *dirvote_get_pending_detached_signatures(void);
+MOCK_DECL(const char*, dirvote_get_pending_consensus,
+ (consensus_flavor_t flav));
+MOCK_DECL(const char*, dirvote_get_pending_detached_signatures, (void));
+
#define DGV_BY_ID 1
#define DGV_INCLUDE_PENDING 2
#define DGV_INCLUDE_PREVIOUS 4
diff --git a/src/or/dns.c b/src/or/dns.c
index d71246d61e..f98181756e 100644
--- a/src/or/dns.c
+++ b/src/or/dns.c
@@ -107,13 +107,9 @@ static void dns_found_answer(const char *address, uint8_t query_type,
const tor_addr_t *addr,
const char *hostname,
uint32_t ttl);
-static int launch_resolve(cached_resolve_t *resolve);
static void add_wildcarded_test_address(const char *address);
static int configure_nameservers(int force);
static int answer_is_wildcarded(const char *ip);
-static int set_exitconn_info_from_resolve(edge_connection_t *exitconn,
- const cached_resolve_t *resolve,
- char **hostname_out);
static int evdns_err_is_transient(int err);
static void inform_pending_connections(cached_resolve_t *resolve);
static void make_pending_resolve_cached(cached_resolve_t *cached);
@@ -859,10 +855,10 @@ dns_resolve_impl,(edge_connection_t *exitconn, int is_resolve,
* Return -2 on a transient error, -1 on a permenent error, and 1 on
* a successful lookup.
*/
-static int
-set_exitconn_info_from_resolve(edge_connection_t *exitconn,
- const cached_resolve_t *resolve,
- char **hostname_out)
+MOCK_IMPL(STATIC int,
+set_exitconn_info_from_resolve,(edge_connection_t *exitconn,
+ const cached_resolve_t *resolve,
+ char **hostname_out))
{
int ipv4_ok, ipv6_ok, answer_with_ipv4, r;
uint32_t begincell_flags;
@@ -1664,8 +1660,8 @@ launch_one_resolve(const char *address, uint8_t query_type,
/** For eventdns: start resolving as necessary to find the target for
* <b>exitconn</b>. Returns -1 on error, -2 on transient error,
* 0 on "resolve launched." */
-static int
-launch_resolve(cached_resolve_t *resolve)
+MOCK_IMPL(STATIC int,
+launch_resolve,(cached_resolve_t *resolve))
{
tor_addr_t a;
int r;
@@ -2118,5 +2114,18 @@ assert_cache_ok_(void)
}
});
}
+
#endif
+cached_resolve_t
+*dns_get_cache_entry(cached_resolve_t *query)
+{
+ return HT_FIND(cache_map, &cache_root, query);
+}
+
+void
+dns_insert_cache_entry(cached_resolve_t *new_entry)
+{
+ HT_INSERT(cache_map, &cache_root, new_entry);
+}
+
diff --git a/src/or/dns.h b/src/or/dns.h
index 6af7796dbb..c2778b216c 100644
--- a/src/or/dns.h
+++ b/src/or/dns.h
@@ -42,6 +42,18 @@ uint8_t answer_type,const cached_resolve_t *resolved));
MOCK_DECL(STATIC void,send_resolved_hostname_cell,(edge_connection_t *conn,
const char *hostname));
+
+cached_resolve_t *dns_get_cache_entry(cached_resolve_t *query);
+void dns_insert_cache_entry(cached_resolve_t *new_entry);
+
+MOCK_DECL(STATIC int,
+set_exitconn_info_from_resolve,(edge_connection_t *exitconn,
+ const cached_resolve_t *resolve,
+ char **hostname_out));
+
+MOCK_DECL(STATIC int,
+launch_resolve,(cached_resolve_t *resolve));
+
#endif
#endif
diff --git a/src/or/geoip.c b/src/or/geoip.c
index 120ce479cc..a868daea47 100644
--- a/src/or/geoip.c
+++ b/src/or/geoip.c
@@ -18,7 +18,6 @@
#include "geoip.h"
#include "routerlist.h"
-static void clear_geoip_db(void);
static void init_geoip_countries(void);
/** An entry from the GeoIP IPv4 file: maps an IPv4 range to a country. */
@@ -970,7 +969,7 @@ geoip_get_dirreq_history(dirreq_type_t type)
&ent->completion_time);
if (time_diff == 0)
time_diff = 1; /* Avoid DIV/0; "instant" answers are impossible
- * by law of nature or something, but a milisecond
+ * by law of nature or something, but a millisecond
* is a bit greater than "instantly" */
bytes_per_second = (uint32_t)(1000 * ent->response_size / time_diff);
dltimes[ent_sl_idx] = bytes_per_second;
@@ -1207,9 +1206,9 @@ geoip_format_dirreq_stats(time_t now)
{
char t[ISO_TIME_LEN+1];
int i;
- char *v3_ips_string, *v3_reqs_string, *v3_direct_dl_string,
- *v3_tunneled_dl_string;
- char *result;
+ char *v3_ips_string = NULL, *v3_reqs_string = NULL,
+ *v3_direct_dl_string = NULL, *v3_tunneled_dl_string = NULL;
+ char *result = NULL;
if (!start_of_dirreq_stats_interval)
return NULL; /* Not initialized. */
@@ -1666,7 +1665,7 @@ getinfo_helper_geoip(control_connection_t *control_conn,
}
/** Release all storage held by the GeoIP databases and country list. */
-static void
+STATIC void
clear_geoip_db(void)
{
if (geoip_countries) {
diff --git a/src/or/geoip.h b/src/or/geoip.h
index 8a3486c7ac..3f1bba01f8 100644
--- a/src/or/geoip.h
+++ b/src/or/geoip.h
@@ -18,6 +18,7 @@
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);
+STATIC void clear_geoip_db(void);
#endif
int should_record_bridge_info(const or_options_t *options);
int geoip_load_file(sa_family_t family, const char *filename);
diff --git a/src/or/include.am b/src/or/include.am
index d0e955f495..264c4ae802 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -63,6 +63,7 @@ LIBTOR_A_SOURCES = \
src/or/onion_fast.c \
src/or/onion_tap.c \
src/or/transports.c \
+ src/or/periodic.c \
src/or/policies.c \
src/or/reasons.c \
src/or/relay.c \
@@ -173,6 +174,7 @@ ORHEADERS = \
src/or/onion_tap.h \
src/or/or.h \
src/or/transports.h \
+ src/or/periodic.h \
src/or/policies.h \
src/or/reasons.h \
src/or/relay.h \
diff --git a/src/or/main.c b/src/or/main.c
index 0f8d7ff3fa..1469fd1da1 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -44,6 +44,7 @@
#include "nodelist.h"
#include "ntmain.h"
#include "onion.h"
+#include "periodic.h"
#include "policies.h"
#include "transports.h"
#include "relay.h"
@@ -1227,39 +1228,85 @@ get_signewnym_epoch(void)
return newnym_epoch;
}
-typedef struct {
- time_t last_rotated_x509_certificate;
- time_t check_v3_certificate;
- time_t check_listeners;
- time_t download_networkstatus;
- time_t try_getting_descriptors;
- time_t reset_descriptor_failures;
- time_t add_entropy;
- time_t write_bridge_status_file;
- time_t downrate_stability;
- time_t save_stability;
- time_t clean_caches;
- time_t recheck_bandwidth;
- time_t check_for_expired_networkstatus;
- time_t write_stats_files;
- time_t write_bridge_stats;
- time_t check_port_forwarding;
- time_t launch_reachability_tests;
- time_t retry_dns_init;
- time_t next_heartbeat;
- time_t check_descriptor;
- /** When do we next launch DNS wildcarding checks? */
- time_t check_for_correct_dns;
- /** When do we next make sure our Ed25519 keys aren't about to expire? */
- time_t check_ed_keys;
-
-} time_to_t;
-
-static time_to_t time_to = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+/** True iff we have initialized all the members of <b>periodic_events</b>.
+ * Used to prevent double-initialization. */
+static int periodic_events_initialized = 0;
+
+/* Declare all the timer callback functions... */
+#undef CALLBACK
+#define CALLBACK(name) \
+ static int name ## _callback(time_t, const or_options_t *)
+CALLBACK(rotate_onion_key);
+CALLBACK(check_ed_keys);
+CALLBACK(launch_descriptor_fetches);
+CALLBACK(reset_descriptor_failures);
+CALLBACK(rotate_x509_certificate);
+CALLBACK(add_entropy);
+CALLBACK(launch_reachability_tests);
+CALLBACK(downrate_stability);
+CALLBACK(save_stability);
+CALLBACK(check_authority_cert);
+CALLBACK(check_expired_networkstatus);
+CALLBACK(write_stats_file);
+CALLBACK(record_bridge_stats);
+CALLBACK(clean_caches);
+CALLBACK(rend_cache_failure_clean);
+CALLBACK(retry_dns);
+CALLBACK(check_descriptor);
+CALLBACK(check_for_reachability_bw);
+CALLBACK(fetch_networkstatus);
+CALLBACK(retry_listeners);
+CALLBACK(expire_old_ciruits_serverside);
+CALLBACK(check_dns_honesty);
+CALLBACK(write_bridge_ns);
+CALLBACK(check_fw_helper_app);
+CALLBACK(heartbeat);
+
+#undef CALLBACK
+
+/* Now we declare an array of periodic_event_item_t for each periodic event */
+#define CALLBACK(name) PERIODIC_EVENT(name)
+
+static periodic_event_item_t periodic_events[] = {
+ CALLBACK(rotate_onion_key),
+ CALLBACK(check_ed_keys),
+ CALLBACK(launch_descriptor_fetches),
+ CALLBACK(reset_descriptor_failures),
+ CALLBACK(rotate_x509_certificate),
+ CALLBACK(add_entropy),
+ CALLBACK(launch_reachability_tests),
+ CALLBACK(downrate_stability),
+ CALLBACK(save_stability),
+ CALLBACK(check_authority_cert),
+ CALLBACK(check_expired_networkstatus),
+ CALLBACK(write_stats_file),
+ CALLBACK(record_bridge_stats),
+ CALLBACK(clean_caches),
+ CALLBACK(rend_cache_failure_clean),
+ CALLBACK(retry_dns),
+ CALLBACK(check_descriptor),
+ CALLBACK(check_for_reachability_bw),
+ CALLBACK(fetch_networkstatus),
+ CALLBACK(retry_listeners),
+ CALLBACK(expire_old_ciruits_serverside),
+ CALLBACK(check_dns_honesty),
+ CALLBACK(write_bridge_ns),
+ CALLBACK(check_fw_helper_app),
+ CALLBACK(heartbeat),
+ END_OF_PERIODIC_EVENTS
};
-
-/** Reset all the time_to's so we'll do all our actions again as if we
+#undef CALLBACK
+
+/* These are pointers to members of periodic_events[] that are used to
+ * implement particular callbacks. We keep them separate here so that we
+ * can access them by name. We also keep them inside periodic_events[]
+ * so that we can implement "reset all timers" in a reasaonable way. */
+static periodic_event_item_t *check_descriptor_event=NULL;
+static periodic_event_item_t *fetch_networkstatus_event=NULL;
+static periodic_event_item_t *launch_descriptor_fetches_event=NULL;
+static periodic_event_item_t *check_dns_honesty_event=NULL;
+
+/** Reset all the periodic events so we'll do all our actions again as if we
* just started up.
* Useful if our clock just moved back a long time from the future,
* so we don't wait until that future arrives again before acting.
@@ -1267,7 +1314,77 @@ static time_to_t time_to = {
void
reset_all_main_loop_timers(void)
{
- memset(&time_to, 0, sizeof(time_to));
+ int i;
+ for (i = 0; periodic_events[i].name; ++i) {
+ periodic_event_reschedule(&periodic_events[i]);
+ }
+}
+
+/** Return the member of periodic_events[] whose name is <b>name</b>.
+ * Return NULL if no such event is found.
+ */
+static periodic_event_item_t *
+find_periodic_event(const char *name)
+{
+ int i;
+ for (i = 0; periodic_events[i].name; ++i) {
+ if (strcmp(name, periodic_events[i].name) == 0)
+ return &periodic_events[i];
+ }
+ return NULL;
+}
+
+/** Helper, run one second after setup:
+ * Initializes all members of periodic_events and starts them running.
+ *
+ * (We do this one second after setup for backward-compatibility reasons;
+ * it might not actually be necessary.) */
+static void
+initialize_periodic_events_cb(evutil_socket_t fd, short events, void *data)
+{
+ (void) fd;
+ (void) events;
+ (void) data;
+ int i;
+ for (i = 0; periodic_events[i].name; ++i) {
+ periodic_event_launch(&periodic_events[i]);
+ }
+}
+
+/** Set up all the members of periodic_events[], and configure them all to be
+ * launched from a callback. */
+STATIC void
+initialize_periodic_events(void)
+{
+ tor_assert(periodic_events_initialized == 0);
+ periodic_events_initialized = 1;
+
+ int i;
+ for (i = 0; periodic_events[i].name; ++i) {
+ periodic_event_setup(&periodic_events[i]);
+ }
+
+#define NAMED_CALLBACK(name) \
+ STMT_BEGIN name ## _event = find_periodic_event( #name ); STMT_END
+
+ NAMED_CALLBACK(check_descriptor);
+ NAMED_CALLBACK(fetch_networkstatus);
+ NAMED_CALLBACK(launch_descriptor_fetches);
+ NAMED_CALLBACK(check_dns_honesty);
+
+ struct timeval one_second = { 1, 0 };
+ event_base_once(tor_libevent_get_base(), -1, EV_TIMEOUT,
+ initialize_periodic_events_cb, NULL,
+ &one_second);
+}
+
+STATIC void
+teardown_periodic_events(void)
+{
+ int i;
+ for (i = 0; periodic_events[i].name; ++i) {
+ periodic_event_destroy(&periodic_events[i]);
+ }
}
/**
@@ -1278,7 +1395,8 @@ reset_all_main_loop_timers(void)
void
reschedule_descriptor_update_check(void)
{
- time_to.check_descriptor = 0;
+ tor_assert(check_descriptor_event);
+ periodic_event_reschedule(check_descriptor_event);
}
/**
@@ -1288,8 +1406,22 @@ reschedule_descriptor_update_check(void)
void
reschedule_directory_downloads(void)
{
- time_to.download_networkstatus = 0;
- time_to.try_getting_descriptors = 0;
+ tor_assert(fetch_networkstatus_event);
+ tor_assert(launch_descriptor_fetches_event);
+
+ periodic_event_reschedule(fetch_networkstatus_event);
+ periodic_event_reschedule(launch_descriptor_fetches_event);
+}
+
+static inline int
+safe_timer_diff(time_t now, time_t next)
+{
+ if (next > now) {
+ tor_assert(next - now <= INT_MAX);
+ return (int)(next - now);
+ } else {
+ return 1;
+ }
}
/** Perform regular maintenance tasks. This function gets run once per
@@ -1298,13 +1430,8 @@ reschedule_directory_downloads(void)
static void
run_scheduled_events(time_t now)
{
- static int should_init_bridge_stats = 1;
const or_options_t *options = get_options();
- int is_server = server_mode(options);
- int i;
- int have_dir_info;
-
/* 0. See if we've been asked to shut down and our timeout has
* expired; or if our bandwidth limits are exhausted and we
* should hibernate; or if it's time to wake up from hibernation.
@@ -1322,12 +1449,98 @@ run_scheduled_events(time_t now)
/* 0c. If we've deferred log messages for the controller, handle them now */
flush_pending_log_callbacks();
+ if (options->UseBridges && !options->DisableNetwork) {
+ fetch_bridge_descriptors(options, now);
+ }
+
+ if (accounting_is_enabled(options)) {
+ accounting_run_housekeeping(now);
+ }
+
+ if (authdir_mode_v3(options)) {
+ dirvote_act(options, now);
+ }
+
+ /* 3a. Every second, we examine pending circuits and prune the
+ * ones which have been pending for more than a few seconds.
+ * We do this before step 4, so it can try building more if
+ * it's not comfortable with the number of available circuits.
+ */
+ /* (If our circuit build timeout can ever become lower than a second (which
+ * it can't, currently), we should do this more often.) */
+ circuit_expire_building();
+
+ /* 3b. Also look at pending streams and prune the ones that 'began'
+ * a long time ago but haven't gotten a 'connected' yet.
+ * Do this before step 4, so we can put them back into pending
+ * state to be picked up by the new circuit.
+ */
+ connection_ap_expire_beginning();
+
+ /* 3c. And expire connections that we've held open for too long.
+ */
+ connection_expire_held_open();
+
+ /* 4. Every second, we try a new circuit if there are no valid
+ * circuits. Every NewCircuitPeriod seconds, we expire circuits
+ * that became dirty more than MaxCircuitDirtiness seconds ago,
+ * and we make a new circ if there are no clean circuits.
+ */
+ const int have_dir_info = router_have_minimum_dir_info();
+ if (have_dir_info && !net_is_disabled()) {
+ circuit_build_needed_circs(now);
+ } else {
+ circuit_expire_old_circs_as_needed(now);
+ }
+
+ /* 5. We do housekeeping for each connection... */
+ connection_or_set_bad_connections(NULL, 0);
+ int i;
+ for (i=0;i<smartlist_len(connection_array);i++) {
+ run_connection_housekeeping(i, now);
+ }
+
+ /* 6. And remove any marked circuits... */
+ circuit_close_all_marked();
+
+ /* 7. And upload service descriptors if necessary. */
+ if (have_completed_a_circuit() && !net_is_disabled()) {
+ rend_consider_services_upload(now);
+ rend_consider_descriptor_republication();
+ }
+
+ /* 8. and blow away any connections that need to die. have to do this now,
+ * because if we marked a conn for close and left its socket -1, then
+ * we'll pass it to poll/select and bad things will happen.
+ */
+ close_closeable_connections();
+
+ /* 8b. And if anything in our state is ready to get flushed to disk, we
+ * flush it. */
+ or_state_save(now);
+
+ /* 8c. Do channel cleanup just like for connections */
+ channel_run_cleanup();
+ channel_listener_run_cleanup();
+
+ /* 11b. check pending unconfigured managed proxies */
+ if (!net_is_disabled() && pt_proxies_configuration_pending())
+ pt_configure_remaining_proxies();
+}
+
+static int
+rotate_onion_key_callback(time_t now, const or_options_t *options)
+{
/* 1a. Every MIN_ONION_KEY_LIFETIME seconds, rotate the onion keys,
* shut down and restart all cpuworkers, and update the directory if
* necessary.
*/
- if (is_server &&
- get_onion_key_set_at()+MIN_ONION_KEY_LIFETIME < now) {
+ if (server_mode(options)) {
+ time_t rotation_time = get_onion_key_set_at()+MIN_ONION_KEY_LIFETIME;
+ if (rotation_time > now) {
+ return safe_timer_diff(now, rotation_time);
+ }
+
log_info(LD_GENERAL,"Rotating onion key.");
rotate_onion_key();
cpuworkers_rotate_keyinfo();
@@ -1336,9 +1549,15 @@ run_scheduled_events(time_t now)
}
if (advertised_server_mode() && !options->DisableNetwork)
router_upload_dir_desc_to_dirservers(0);
+ return MIN_ONION_KEY_LIFETIME;
}
+ return PERIODIC_EVENT_NO_UPDATE;
+}
- if (is_server && time_to.check_ed_keys < now) {
+static int
+check_ed_keys_callback(time_t now, const or_options_t *options)
+{
+ if (server_mode(options)) {
if (should_make_new_ed_keys(options, now)) {
if (load_ed_keys(options, now) < 0 ||
generate_ed_link_cert(options, now)) {
@@ -1347,202 +1566,255 @@ run_scheduled_events(time_t now)
exit(0);
}
}
- time_to.check_ed_keys = now + 30;
+ return 30;
}
+ return PERIODIC_EVENT_NO_UPDATE;
+}
- if (!should_delay_dir_fetches(options, NULL) &&
- time_to.try_getting_descriptors < now) {
- update_all_descriptor_downloads(now);
- update_extrainfo_downloads(now);
- if (router_have_minimum_dir_info())
- time_to.try_getting_descriptors = now + LAZY_DESCRIPTOR_RETRY_INTERVAL;
- else
- time_to.try_getting_descriptors = now + GREEDY_DESCRIPTOR_RETRY_INTERVAL;
- }
+static int
+launch_descriptor_fetches_callback(time_t now, const or_options_t *options)
+{
+ if (should_delay_dir_fetches(options, NULL))
+ return PERIODIC_EVENT_NO_UPDATE;
- if (time_to.reset_descriptor_failures < now) {
- router_reset_descriptor_download_failures();
- time_to.reset_descriptor_failures =
- now + DESCRIPTOR_FAILURE_RESET_INTERVAL;
- }
+ update_all_descriptor_downloads(now);
+ update_extrainfo_downloads(now);
+ if (router_have_minimum_dir_info())
+ return LAZY_DESCRIPTOR_RETRY_INTERVAL;
+ else
+ return GREEDY_DESCRIPTOR_RETRY_INTERVAL;
+}
- if (options->UseBridges && !options->DisableNetwork)
- fetch_bridge_descriptors(options, now);
+static int
+reset_descriptor_failures_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
+ (void)options;
+ router_reset_descriptor_download_failures();
+ return DESCRIPTOR_FAILURE_RESET_INTERVAL;
+}
+
+static int
+rotate_x509_certificate_callback(time_t now, const or_options_t *options)
+{
+ static int first = 1;
+ (void)now;
+ (void)options;
+ if (first) {
+ first = 0;
+ return MAX_SSL_KEY_LIFETIME_INTERNAL;
+ }
/* 1b. Every MAX_SSL_KEY_LIFETIME_INTERNAL seconds, we change our
* TLS context. */
- if (!time_to.last_rotated_x509_certificate)
- time_to.last_rotated_x509_certificate = now;
- if (time_to.last_rotated_x509_certificate +
- MAX_SSL_KEY_LIFETIME_INTERNAL < now) {
- log_info(LD_GENERAL,"Rotating tls context.");
- if (router_initialize_tls_context() < 0) {
- log_warn(LD_BUG, "Error reinitializing TLS context");
- /* XXX is it a bug here, that we just keep going? -RD */
- }
- time_to.last_rotated_x509_certificate = now;
- /* We also make sure to rotate the TLS connections themselves if they've
- * been up for too long -- but that's done via is_bad_for_new_circs in
- * connection_run_housekeeping() above. */
- }
-
- if (time_to.add_entropy < now) {
- if (time_to.add_entropy) {
- /* We already seeded once, so don't die on failure. */
- if (crypto_seed_rng() < 0) {
- log_warn(LD_GENERAL, "Tried to re-seed RNG, but failed. We already "
- "seeded once, though, so we won't exit here.");
- }
- }
-/** How often do we add more entropy to OpenSSL's RNG pool? */
-#define ENTROPY_INTERVAL (60*60)
- time_to.add_entropy = now + ENTROPY_INTERVAL;
+ log_info(LD_GENERAL,"Rotating tls context.");
+ if (router_initialize_tls_context() < 0) {
+ log_warn(LD_BUG, "Error reinitializing TLS context");
+ /* XXX is it a bug here, that we just keep going? -RD */
}
- /* 1c. If we have to change the accounting interval or record
- * bandwidth used in this accounting interval, do so. */
- if (accounting_is_enabled(options))
- accounting_run_housekeeping(now);
+ /* We also make sure to rotate the TLS connections themselves if they've
+ * been up for too long -- but that's done via is_bad_for_new_circs in
+ * run_connection_housekeeping() above. */
+ return MAX_SSL_KEY_LIFETIME_INTERNAL;
+}
- if (time_to.launch_reachability_tests < now &&
- (authdir_mode_tests_reachability(options)) &&
- !net_is_disabled()) {
- time_to.launch_reachability_tests = now + REACHABILITY_TEST_INTERVAL;
+static int
+add_entropy_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
+ (void)options;
+ /* We already seeded once, so don't die on failure. */
+ if (crypto_seed_rng() < 0) {
+ log_warn(LD_GENERAL, "Tried to re-seed RNG, but failed. We already "
+ "seeded once, though, so we won't exit here.");
+ }
+
+ /** How often do we add more entropy to OpenSSL's RNG pool? */
+#define ENTROPY_INTERVAL (60*60)
+ return ENTROPY_INTERVAL;
+}
+
+static int
+launch_reachability_tests_callback(time_t now, const or_options_t *options)
+{
+ if (authdir_mode_tests_reachability(options) &&
+ !net_is_disabled()) {
/* try to determine reachability of the other Tor relays */
dirserv_test_reachability(now);
}
+ return REACHABILITY_TEST_INTERVAL;
+}
+static int
+downrate_stability_callback(time_t now, const or_options_t *options)
+{
+ (void)options;
/* 1d. Periodically, we discount older stability information so that new
* stability info counts more, and save the stability information to disk as
* appropriate. */
- if (time_to.downrate_stability < now)
- time_to.downrate_stability = rep_hist_downrate_old_runs(now);
+ time_t next = rep_hist_downrate_old_runs(now);
+ return safe_timer_diff(now, next);
+}
+
+static int
+save_stability_callback(time_t now, const or_options_t *options)
+{
if (authdir_mode_tests_reachability(options)) {
- if (time_to.save_stability < now) {
- if (time_to.save_stability && rep_hist_record_mtbf_data(now, 1)<0) {
- log_warn(LD_GENERAL, "Couldn't store mtbf data.");
- }
-#define SAVE_STABILITY_INTERVAL (30*60)
- time_to.save_stability = now + SAVE_STABILITY_INTERVAL;
+ if (rep_hist_record_mtbf_data(now, 1)<0) {
+ log_warn(LD_GENERAL, "Couldn't store mtbf data.");
}
}
+#define SAVE_STABILITY_INTERVAL (30*60)
+ return SAVE_STABILITY_INTERVAL;
+}
+static int
+check_authority_cert_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
+ (void)options;
/* 1e. Periodically, if we're a v3 authority, we check whether our cert is
* close to expiring and warn the admin if it is. */
- if (time_to.check_v3_certificate < now) {
- v3_authority_check_key_expiry();
+ v3_authority_check_key_expiry();
#define CHECK_V3_CERTIFICATE_INTERVAL (5*60)
- time_to.check_v3_certificate = now + CHECK_V3_CERTIFICATE_INTERVAL;
- }
+ return CHECK_V3_CERTIFICATE_INTERVAL;
+}
+static int
+check_expired_networkstatus_callback(time_t now, const or_options_t *options)
+{
+ (void)options;
/* 1f. Check whether our networkstatus has expired.
*/
- if (time_to.check_for_expired_networkstatus < now) {
- networkstatus_t *ns = networkstatus_get_latest_consensus();
- /*XXXX RD: This value needs to be the same as REASONABLY_LIVE_TIME in
- * networkstatus_get_reasonably_live_consensus(), but that value is way
- * way too high. Arma: is the bridge issue there resolved yet? -NM */
+ networkstatus_t *ns = networkstatus_get_latest_consensus();
+ /*XXXX RD: This value needs to be the same as REASONABLY_LIVE_TIME in
+ * networkstatus_get_reasonably_live_consensus(), but that value is way
+ * way too high. Arma: is the bridge issue there resolved yet? -NM */
#define NS_EXPIRY_SLOP (24*60*60)
- if (ns && ns->valid_until < now+NS_EXPIRY_SLOP &&
- router_have_minimum_dir_info()) {
- router_dir_info_changed();
- }
-#define CHECK_EXPIRED_NS_INTERVAL (2*60)
- time_to.check_for_expired_networkstatus = now + CHECK_EXPIRED_NS_INTERVAL;
+ if (ns && ns->valid_until < now+NS_EXPIRY_SLOP &&
+ router_have_minimum_dir_info()) {
+ router_dir_info_changed();
}
+#define CHECK_EXPIRED_NS_INTERVAL (2*60)
+ return CHECK_EXPIRED_NS_INTERVAL;
+}
+static int
+write_stats_file_callback(time_t now, const or_options_t *options)
+{
/* 1g. Check whether we should write statistics to disk.
*/
- if (time_to.write_stats_files < now) {
#define CHECK_WRITE_STATS_INTERVAL (60*60)
- time_t next_time_to_write_stats_files = (time_to.write_stats_files > 0 ?
- time_to.write_stats_files : now) + CHECK_WRITE_STATS_INTERVAL;
- if (options->CellStatistics) {
- time_t next_write =
- rep_hist_buffer_stats_write(time_to.write_stats_files);
- if (next_write && next_write < next_time_to_write_stats_files)
- next_time_to_write_stats_files = next_write;
- }
- if (options->DirReqStatistics) {
- time_t next_write = geoip_dirreq_stats_write(time_to.write_stats_files);
- if (next_write && next_write < next_time_to_write_stats_files)
- next_time_to_write_stats_files = next_write;
- }
- if (options->EntryStatistics) {
- time_t next_write = geoip_entry_stats_write(time_to.write_stats_files);
- if (next_write && next_write < next_time_to_write_stats_files)
- next_time_to_write_stats_files = next_write;
- }
- if (options->HiddenServiceStatistics) {
- time_t next_write = rep_hist_hs_stats_write(time_to.write_stats_files);
- if (next_write && next_write < next_time_to_write_stats_files)
- next_time_to_write_stats_files = next_write;
- }
- if (options->ExitPortStatistics) {
- time_t next_write = rep_hist_exit_stats_write(time_to.write_stats_files);
- if (next_write && next_write < next_time_to_write_stats_files)
- next_time_to_write_stats_files = next_write;
- }
- if (options->ConnDirectionStatistics) {
- time_t next_write = rep_hist_conn_stats_write(time_to.write_stats_files);
- if (next_write && next_write < next_time_to_write_stats_files)
- next_time_to_write_stats_files = next_write;
- }
- if (options->BridgeAuthoritativeDir) {
- time_t next_write = rep_hist_desc_stats_write(time_to.write_stats_files);
- if (next_write && next_write < next_time_to_write_stats_files)
- next_time_to_write_stats_files = next_write;
- }
- time_to.write_stats_files = next_time_to_write_stats_files;
+ time_t next_time_to_write_stats_files = now + CHECK_WRITE_STATS_INTERVAL;
+ if (options->CellStatistics) {
+ time_t next_write =
+ rep_hist_buffer_stats_write(now);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
+ }
+ if (options->DirReqStatistics) {
+ time_t next_write = geoip_dirreq_stats_write(now);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
+ }
+ if (options->EntryStatistics) {
+ time_t next_write = geoip_entry_stats_write(now);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
}
+ if (options->HiddenServiceStatistics) {
+ time_t next_write = rep_hist_hs_stats_write(now);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
+ }
+ if (options->ExitPortStatistics) {
+ time_t next_write = rep_hist_exit_stats_write(now);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
+ }
+ if (options->ConnDirectionStatistics) {
+ time_t next_write = rep_hist_conn_stats_write(now);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
+ }
+ if (options->BridgeAuthoritativeDir) {
+ time_t next_write = rep_hist_desc_stats_write(now);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
+ }
+
+ return safe_timer_diff(now, next_time_to_write_stats_files);
+}
+
+static int
+record_bridge_stats_callback(time_t now, const or_options_t *options)
+{
+ static int should_init_bridge_stats = 1;
/* 1h. Check whether we should write bridge statistics to disk.
*/
if (should_record_bridge_info(options)) {
- if (time_to.write_bridge_stats < now) {
- if (should_init_bridge_stats) {
- /* (Re-)initialize bridge statistics. */
+ if (should_init_bridge_stats) {
+ /* (Re-)initialize bridge statistics. */
geoip_bridge_stats_init(now);
- time_to.write_bridge_stats = now + WRITE_STATS_INTERVAL;
should_init_bridge_stats = 0;
- } else {
- /* Possibly write bridge statistics to disk and ask when to write
- * them next time. */
- time_to.write_bridge_stats = geoip_bridge_stats_write(
- time_to.write_bridge_stats);
- }
+ return WRITE_STATS_INTERVAL;
+ } else {
+ /* Possibly write bridge statistics to disk and ask when to write
+ * them next time. */
+ time_t next = geoip_bridge_stats_write(now);
+ return safe_timer_diff(now, next);
}
} else if (!should_init_bridge_stats) {
/* Bridge mode was turned off. Ensure that stats are re-initialized
* next time bridge mode is turned on. */
should_init_bridge_stats = 1;
}
+ return PERIODIC_EVENT_NO_UPDATE;
+}
+static int
+clean_caches_callback(time_t now, const or_options_t *options)
+{
/* Remove old information from rephist and the rend cache. */
- if (time_to.clean_caches < now) {
- rep_history_clean(now - options->RephistTrackTime);
- rend_cache_clean(now);
- rend_cache_clean_v2_descs_as_dir(now, 0);
- microdesc_cache_rebuild(NULL, 0);
+ rep_history_clean(now - options->RephistTrackTime);
+ rend_cache_clean(now, REND_CACHE_TYPE_CLIENT);
+ rend_cache_clean(now, REND_CACHE_TYPE_SERVICE);
+ rend_cache_clean_v2_descs_as_dir(now, 0);
+ microdesc_cache_rebuild(NULL, 0);
#define CLEAN_CACHES_INTERVAL (30*60)
- time_to.clean_caches = now + CLEAN_CACHES_INTERVAL;
- }
+ return CLEAN_CACHES_INTERVAL;
+}
+
+static int
+rend_cache_failure_clean_callback(time_t now, const or_options_t *options)
+{
+ (void)options;
/* We don't keep entries that are more than five minutes old so we try to
* clean it as soon as we can since we want to make sure the client waits
* as little as possible for reachability reasons. */
rend_cache_failure_clean(now);
+ return 30;
+}
+static int
+retry_dns_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
#define RETRY_DNS_INTERVAL (10*60)
/* If we're a server and initializing dns failed, retry periodically. */
- if (time_to.retry_dns_init < now) {
- time_to.retry_dns_init = now + RETRY_DNS_INTERVAL;
- if (is_server && has_dns_init_failed())
- dns_init();
- }
+ if (server_mode(options) && has_dns_init_failed())
+ dns_init();
+ return RETRY_DNS_INTERVAL;
+}
/* 2. Periodically, we consider force-uploading our descriptor
* (if we've passed our internal checks). */
+static int
+check_descriptor_callback(time_t now, const or_options_t *options)
+{
/** How often do we check whether part of our router info has changed in a
* way that would require an upload? That includes checking whether our IP
* address has changed. */
@@ -1550,185 +1822,167 @@ run_scheduled_events(time_t now)
/* 2b. Once per minute, regenerate and upload the descriptor if the old
* one is inaccurate. */
- if (time_to.check_descriptor < now && !options->DisableNetwork) {
- static int dirport_reachability_count = 0;
- time_to.check_descriptor = now + CHECK_DESCRIPTOR_INTERVAL;
+ if (!options->DisableNetwork) {
check_descriptor_bandwidth_changed(now);
check_descriptor_ipaddress_changed(now);
mark_my_descriptor_dirty_if_too_old(now);
consider_publishable_server(0);
- /* also, check religiously for reachability, if it's within the first
- * 20 minutes of our uptime. */
- if (is_server &&
- (have_completed_a_circuit() || !any_predicted_circuits(now)) &&
- !we_are_hibernating()) {
- if (stats_n_seconds_working < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) {
- consider_testing_reachability(1, dirport_reachability_count==0);
- if (++dirport_reachability_count > 5)
- dirport_reachability_count = 0;
- } else if (time_to.recheck_bandwidth < now) {
- /* If we haven't checked for 12 hours and our bandwidth estimate is
- * low, do another bandwidth test. This is especially important for
- * bridges, since they might go long periods without much use. */
- const routerinfo_t *me = router_get_my_routerinfo();
- if (time_to.recheck_bandwidth && me &&
- me->bandwidthcapacity < me->bandwidthrate &&
- me->bandwidthcapacity < 51200) {
- reset_bandwidth_test();
- }
-#define BANDWIDTH_RECHECK_INTERVAL (12*60*60)
- time_to.recheck_bandwidth = now + BANDWIDTH_RECHECK_INTERVAL;
- }
- }
-
/* If any networkstatus documents are no longer recent, we need to
* update all the descriptors' running status. */
/* Remove dead routers. */
+ /* XXXX This doesn't belong here, but it was here in the pre-
+ * XXXX refactoring code. */
routerlist_remove_old_routers();
}
- /* 2c. Every minute (or every second if TestingTorNetwork), check
- * whether we want to download any networkstatus documents. */
+ return CHECK_DESCRIPTOR_INTERVAL;
+}
-/* How often do we check whether we should download network status
- * documents? */
-#define networkstatus_dl_check_interval(o) ((o)->TestingTorNetwork ? 1 : 60)
+static int
+check_for_reachability_bw_callback(time_t now, const or_options_t *options)
+{
+ /* XXXX This whole thing was stuck in the middle of what is now
+ * XXXX check_descriptor_callback. I'm not sure it's right. */
- if (!should_delay_dir_fetches(options, NULL) &&
- time_to.download_networkstatus < now) {
- time_to.download_networkstatus =
- now + networkstatus_dl_check_interval(options);
- update_networkstatus_downloads(now);
+ static int dirport_reachability_count = 0;
+ /* also, check religiously for reachability, if it's within the first
+ * 20 minutes of our uptime. */
+ if (server_mode(options) &&
+ (have_completed_a_circuit() || !any_predicted_circuits(now)) &&
+ !we_are_hibernating()) {
+ if (stats_n_seconds_working < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) {
+ consider_testing_reachability(1, dirport_reachability_count==0);
+ if (++dirport_reachability_count > 5)
+ dirport_reachability_count = 0;
+ return 1;
+ } else {
+ /* If we haven't checked for 12 hours and our bandwidth estimate is
+ * low, do another bandwidth test. This is especially important for
+ * bridges, since they might go long periods without much use. */
+ const routerinfo_t *me = router_get_my_routerinfo();
+ static int first_time = 1;
+ if (!first_time && me &&
+ me->bandwidthcapacity < me->bandwidthrate &&
+ me->bandwidthcapacity < 51200) {
+ reset_bandwidth_test();
+ }
+ first_time = 0;
+#define BANDWIDTH_RECHECK_INTERVAL (12*60*60)
+ return BANDWIDTH_RECHECK_INTERVAL;
+ }
}
+ return CHECK_DESCRIPTOR_INTERVAL;
+}
- /* 2c. Let directory voting happen. */
- if (authdir_mode_v3(options))
- dirvote_act(options, now);
+static int
+fetch_networkstatus_callback(time_t now, const or_options_t *options)
+{
+ /* 2c. Every minute (or every second if TestingTorNetwork), check
+ * whether we want to download any networkstatus documents. */
- /* 3a. Every second, we examine pending circuits and prune the
- * ones which have been pending for more than a few seconds.
- * We do this before step 4, so it can try building more if
- * it's not comfortable with the number of available circuits.
- */
- /* (If our circuit build timeout can ever become lower than a second (which
- * it can't, currently), we should do this more often.) */
- circuit_expire_building();
+ /* How often do we check whether we should download network status
+ * documents? */
+#define networkstatus_dl_check_interval(o) ((o)->TestingTorNetwork ? 1 : 60)
- /* 3b. Also look at pending streams and prune the ones that 'began'
- * a long time ago but haven't gotten a 'connected' yet.
- * Do this before step 4, so we can put them back into pending
- * state to be picked up by the new circuit.
- */
- connection_ap_expire_beginning();
+ if (should_delay_dir_fetches(options, NULL))
+ return PERIODIC_EVENT_NO_UPDATE;
- /* 3c. And expire connections that we've held open for too long.
- */
- connection_expire_held_open();
+ update_networkstatus_downloads(now);
+ return networkstatus_dl_check_interval(options);
+}
+static int
+retry_listeners_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
+ (void)options;
/* 3d. And every 60 seconds, we relaunch listeners if any died. */
- if (!net_is_disabled() && time_to.check_listeners < now) {
+ if (!net_is_disabled()) {
retry_all_listeners(NULL, NULL, 0);
- time_to.check_listeners = now+60;
- }
-
- /* 4. Every second, we try a new circuit if there are no valid
- * circuits. Every NewCircuitPeriod seconds, we expire circuits
- * that became dirty more than MaxCircuitDirtiness seconds ago,
- * and we make a new circ if there are no clean circuits.
- */
- have_dir_info = router_have_minimum_dir_info();
- if (have_dir_info && !net_is_disabled()) {
- circuit_build_needed_circs(now);
- } else {
- circuit_expire_old_circs_as_needed(now);
+ return 60;
}
+ return PERIODIC_EVENT_NO_UPDATE;
+}
- /* every 10 seconds, but not at the same second as other such events */
- if (now % 10 == 5)
- circuit_expire_old_circuits_serverside(now);
-
- /* 5. We do housekeeping for each connection... */
- connection_or_set_bad_connections(NULL, 0);
- for (i=0;i<smartlist_len(connection_array);i++) {
- run_connection_housekeeping(i, now);
- }
-
- /* 6. And remove any marked circuits... */
- circuit_close_all_marked();
-
- /* 7. And upload service descriptors if necessary. */
- if (have_completed_a_circuit() && !net_is_disabled()) {
- rend_consider_services_upload(now);
- rend_consider_descriptor_republication();
- }
-
- /* 8. and blow away any connections that need to die. have to do this now,
- * because if we marked a conn for close and left its socket -1, then
- * we'll pass it to poll/select and bad things will happen.
- */
- close_closeable_connections();
-
- /* 8b. And if anything in our state is ready to get flushed to disk, we
- * flush it. */
- or_state_save(now);
-
- /* 8c. Do channel cleanup just like for connections */
- channel_run_cleanup();
- channel_listener_run_cleanup();
+static int
+expire_old_ciruits_serverside_callback(time_t now, const or_options_t *options)
+{
+ (void)options;
+ /* every 11 seconds, so not usually the same second as other such events */
+ circuit_expire_old_circuits_serverside(now);
+ return 11;
+}
+static int
+check_dns_honesty_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
/* 9. and if we're an exit node, check whether our DNS is telling stories
* to us. */
- if (!net_is_disabled() &&
- public_server_mode(options) &&
- time_to.check_for_correct_dns < now &&
- ! router_my_exit_policy_is_reject_star()) {
- if (!time_to.check_for_correct_dns) {
- time_to.check_for_correct_dns =
- crypto_rand_time_range(now + 60, now + 180);
- } else {
- dns_launch_correctness_checks();
- time_to.check_for_correct_dns = now + 12*3600 +
- crypto_rand_int(12*3600);
- }
+ if (net_is_disabled() ||
+ ! public_server_mode(options) ||
+ router_my_exit_policy_is_reject_star())
+ return PERIODIC_EVENT_NO_UPDATE;
+
+ static int first_time = 1;
+ if (first_time) {
+ /* Don't launch right when we start */
+ first_time = 0;
+ return crypto_rand_int_range(60, 180);
}
+ dns_launch_correctness_checks();
+ return 12*3600 + crypto_rand_int(12*3600);
+}
+
+static int
+write_bridge_ns_callback(time_t now, const or_options_t *options)
+{
/* 10. write bridge networkstatus file to disk */
- if (options->BridgeAuthoritativeDir &&
- time_to.write_bridge_status_file < now) {
+ if (options->BridgeAuthoritativeDir) {
networkstatus_dump_bridge_status_to_file(now);
#define BRIDGE_STATUSFILE_INTERVAL (30*60)
- time_to.write_bridge_status_file = now+BRIDGE_STATUSFILE_INTERVAL;
+ return BRIDGE_STATUSFILE_INTERVAL;
}
+ return PERIODIC_EVENT_NO_UPDATE;
+}
+static int
+check_fw_helper_app_callback(time_t now, const or_options_t *options)
+{
+ if (net_is_disabled() ||
+ ! server_mode(options) ||
+ ! options->PortForwarding) {
+ return PERIODIC_EVENT_NO_UPDATE;
+ }
/* 11. check the port forwarding app */
- if (!net_is_disabled() &&
- time_to.check_port_forwarding < now &&
- options->PortForwarding &&
- is_server) {
+
#define PORT_FORWARDING_CHECK_INTERVAL 5
- smartlist_t *ports_to_forward = get_list_of_ports_to_forward();
- if (ports_to_forward) {
- tor_check_port_forwarding(options->PortForwardingHelper,
- ports_to_forward,
- now);
-
- SMARTLIST_FOREACH(ports_to_forward, char *, cp, tor_free(cp));
- smartlist_free(ports_to_forward);
- }
- time_to.check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL;
- }
+ smartlist_t *ports_to_forward = get_list_of_ports_to_forward();
+ if (ports_to_forward) {
+ tor_check_port_forwarding(options->PortForwardingHelper,
+ ports_to_forward,
+ now);
- /* 11b. check pending unconfigured managed proxies */
- if (!net_is_disabled() && pt_proxies_configuration_pending())
- pt_configure_remaining_proxies();
+ SMARTLIST_FOREACH(ports_to_forward, char *, cp, tor_free(cp));
+ smartlist_free(ports_to_forward);
+ }
+ return PORT_FORWARDING_CHECK_INTERVAL;
+}
+static int
+heartbeat_callback(time_t now, const or_options_t *options)
+{
+ static int first = 1;
/* 12. write the heartbeat message */
- if (options->HeartbeatPeriod &&
- time_to.next_heartbeat <= now) {
- if (time_to.next_heartbeat) /* don't log the first heartbeat */
- log_heartbeat(now);
- time_to.next_heartbeat = now+options->HeartbeatPeriod;
+ if (first) {
+ first = 0; /* Skip the first one. */
+ } else {
+ log_heartbeat(now);
}
+ /* XXXX This isn't such a good way to handle possible changes in the
+ * callback event */
+ return options->HeartbeatPeriod;
}
/** Timer: used to invoke second_elapsed_callback() once per second. */
@@ -1950,7 +2204,10 @@ dns_servers_relaunch_checks(void)
{
if (server_mode(get_options())) {
dns_reset_correctness_checks();
- time_to.check_for_correct_dns = 0;
+ if (periodic_events_initialized) {
+ tor_assert(check_dns_honesty_event);
+ periodic_event_reschedule(check_dns_honesty_event);
+ }
}
}
@@ -2044,6 +2301,13 @@ do_main_loop(void)
{
time_t now;
+ /* initialize the periodic events first, so that code that depends on the
+ * events being present does not assert.
+ */
+ if (! periodic_events_initialized) {
+ initialize_periodic_events();
+ }
+
/* initialize dns resolve map, spawn workers if needed */
if (dns_init() < 0) {
if (get_options()->ServerDNSAllowBrokenConfig)
@@ -2254,6 +2518,11 @@ run_main_loop_once(void)
}
}
+ /* This will be pretty fast if nothing new is pending. Note that this gets
+ * called once per libevent loop, which will make it happen once per group
+ * of events that fire, or once per second. */
+ connection_ap_attach_pending(0);
+
return 1;
}
@@ -2828,6 +3097,7 @@ tor_free_all(int postfork)
channel_tls_free_all();
channel_free_all();
connection_free_all();
+ connection_edge_free_all();
scheduler_free_all();
memarea_clear_freelist();
nodelist_free_all();
@@ -2854,6 +3124,7 @@ tor_free_all(int postfork)
smartlist_free(closeable_connection_lst);
smartlist_free(active_linked_connection_lst);
periodic_timer_free(second_timer);
+ teardown_periodic_events();
#ifndef USE_BUFFEREVENTS
periodic_timer_free(refill_timer);
#endif
diff --git a/src/or/main.h b/src/or/main.h
index 447d3f4eca..37e93d79d3 100644
--- a/src/or/main.h
+++ b/src/or/main.h
@@ -78,6 +78,8 @@ int tor_init(int argc, char **argv);
#ifdef MAIN_PRIVATE
STATIC void init_connection_lists(void);
STATIC void close_closeable_connections(void);
+STATIC void initialize_periodic_events(void);
+STATIC void teardown_periodic_events(void);
#endif
#endif
diff --git a/src/or/or.h b/src/or/or.h
index 4496cbcec3..5d02ed7037 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -2892,6 +2892,14 @@ typedef struct circuit_t {
* where this circuit was marked.) */
const char *marked_for_close_file; /**< For debugging: in which file was this
* circuit marked for close? */
+ /** For what reason (See END_CIRC_REASON...) is this circuit being closed?
+ * This field is set in circuit_mark_for_close and used later in
+ * circuit_about_to_free. */
+ uint16_t marked_for_close_reason;
+ /** As marked_for_close_reason, but reflects the underlying reason for
+ * closing this circuit.
+ */
+ uint16_t marked_for_close_orig_reason;
/** Unique ID for measuring tunneled network status requests. */
uint64_t dirreq_id;
@@ -3424,6 +3432,7 @@ typedef struct {
* each log message occurs? */
int TruncateLogFile; /**< Boolean: Should we truncate the log file
before we start writing? */
+ char *SyslogIdentityTag; /**< Identity tag to add for syslog logging. */
char *DebugLogFile; /**< Where to send verbose log messages. */
char *DataDirectory; /**< OR only: where to store long-term data. */
@@ -3807,7 +3816,7 @@ typedef struct {
* hibernate." */
/** How do we determine when our AccountingMax has been reached?
* "max" for when in or out reaches AccountingMax
- * "sum for when in plus out reaches AccountingMax */
+ * "sum" for when in plus out reaches AccountingMax */
char *AccountingRule_option;
enum { ACCT_MAX, ACCT_SUM } AccountingRule;
@@ -4014,7 +4023,7 @@ typedef struct {
char *ConsensusParams;
/** Authority only: minimum number of measured bandwidths we must see
- * before we only beliee measured bandwidths to assign flags. */
+ * before we only believe measured bandwidths to assign flags. */
int MinMeasuredBWsForAuthToIgnoreAdvertised;
/** The length of time that we think an initial consensus should be fresh.
diff --git a/src/or/periodic.c b/src/or/periodic.c
new file mode 100644
index 0000000000..109717f738
--- /dev/null
+++ b/src/or/periodic.c
@@ -0,0 +1,120 @@
+/* Copyright (c) 2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+#include "compat_libevent.h"
+#include "config.h"
+#include "periodic.h"
+
+#ifdef HAVE_EVENT2_EVENT_H
+#include <event2/event.h>
+#else
+#include <event.h>
+#endif
+
+/** We disable any interval greater than this number of seconds, on the
+ * grounds that it is probably an absolute time mistakenly passed in as a
+ * relative time.
+ */
+static const int MAX_INTERVAL = 10 * 365 * 86400;
+
+/** Set the event <b>event</b> to run in <b>next_interval</b> seconds from
+ * now. */
+static void
+periodic_event_set_interval(periodic_event_item_t *event,
+ time_t next_interval)
+{
+ tor_assert(next_interval < MAX_INTERVAL);
+ struct timeval tv;
+ tv.tv_sec = next_interval;
+ tv.tv_usec = 0;
+ event_add(event->ev, &tv);
+}
+
+/** Wraps dispatches for periodic events, <b>data</b> will be a pointer to the
+ * event that needs to be called */
+static void
+periodic_event_dispatch(evutil_socket_t fd, short what, void *data)
+{
+ (void)fd;
+ (void)what;
+ periodic_event_item_t *event = data;
+
+ time_t now = time(NULL);
+ const or_options_t *options = get_options();
+ log_debug(LD_GENERAL, "Dispatching %s", event->name);
+ int r = event->fn(now, options);
+ int next_interval = 0;
+
+ /* update the last run time if action was taken */
+ if (r==0) {
+ log_err(LD_BUG, "Invalid return value for periodic event from %s.",
+ event->name);
+ tor_assert(r != 0);
+ } else if (r > 0) {
+ event->last_action_time = now;
+ /* If the event is meant to happen after ten years, that's likely
+ * a bug, and somebody gave an absolute time rather than an interval.
+ */
+ tor_assert(r < MAX_INTERVAL);
+ next_interval = r;
+ } else {
+ /* no action was taken, it is likely a precondition failed,
+ * we should reschedule for next second incase the precondition
+ * passes then */
+ next_interval = 1;
+ }
+
+ log_debug(LD_GENERAL, "Scheduling %s for %d seconds", event->name,
+ next_interval);
+ struct timeval tv = { next_interval , 0 };
+ event_add(event->ev, &tv);
+}
+
+/** Schedules <b>event</b> to run as soon as possible from now. */
+void
+periodic_event_reschedule(periodic_event_item_t *event)
+{
+ periodic_event_set_interval(event, 1);
+}
+
+/** Initializes the libevent backend for a periodic event. */
+void
+periodic_event_setup(periodic_event_item_t *event)
+{
+ if (event->ev) { /* Already setup? This is a bug */
+ log_err(LD_BUG, "Initial dispatch should only be done once.");
+ tor_assert(0);
+ }
+
+ event->ev = tor_event_new(tor_libevent_get_base(),
+ -1, 0,
+ periodic_event_dispatch,
+ event);
+ tor_assert(event->ev);
+}
+
+/** Handles initial dispatch for periodic events. It should happen 1 second
+ * after the events are created to mimic behaviour before #3199's refactor */
+void
+periodic_event_launch(periodic_event_item_t *event)
+{
+ if (! event->ev) { /* Not setup? This is a bug */
+ log_err(LD_BUG, "periodic_event_launch without periodic_event_setup");
+ tor_assert(0);
+ }
+
+ // Initial dispatch
+ periodic_event_dispatch(-1, EV_TIMEOUT, event);
+}
+
+/** Release all storage associated with <b>event</b> */
+void
+periodic_event_destroy(periodic_event_item_t *event)
+{
+ if (!event)
+ return;
+ tor_event_free(event->ev);
+ event->last_action_time = 0;
+}
+
diff --git a/src/or/periodic.h b/src/or/periodic.h
new file mode 100644
index 0000000000..bab0c91916
--- /dev/null
+++ b/src/or/periodic.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_PERIODIC_H
+#define TOR_PERIODIC_H
+
+#define PERIODIC_EVENT_NO_UPDATE (-1)
+
+/** Callback function for a periodic event to take action. The return value
+* influences the next time the function will get called. Return
+* PERIODIC_EVENT_NO_UPDATE to not update <b>last_action_time</b> and be polled
+* again in the next second. If a positive value is returned it will update the
+* interval time. */
+typedef int (*periodic_event_helper_t)(time_t now,
+ const or_options_t *options);
+
+struct event;
+
+/** A single item for the periodic-events-function table. */
+typedef struct periodic_event_item_t {
+ periodic_event_helper_t fn; /**< The function to run the event */
+ time_t last_action_time; /**< The last time the function did something */
+ struct event *ev; /**< Libevent callback we're using to implement this */
+ const char *name; /**< Name of the function -- for debug */
+} periodic_event_item_t;
+
+/** events will get their interval from first execution */
+#define PERIODIC_EVENT(fn) { fn##_callback, 0, NULL, #fn }
+#define END_OF_PERIODIC_EVENTS { NULL, 0, NULL, NULL }
+
+void periodic_event_launch(periodic_event_item_t *event);
+void periodic_event_setup(periodic_event_item_t *event);
+void periodic_event_destroy(periodic_event_item_t *event);
+void periodic_event_reschedule(periodic_event_item_t *event);
+
+#endif
+
diff --git a/src/or/policies.c b/src/or/policies.c
index 9c858ec1b0..ec11375636 100644
--- a/src/or/policies.c
+++ b/src/or/policies.c
@@ -8,6 +8,8 @@
* \brief Code to parse and use address policies and exit policies.
**/
+#define POLICIES_PRIVATE
+
#include "or.h"
#include "config.h"
#include "dirserv.h"
@@ -62,14 +64,15 @@ static const char *private_nets[] = {
NULL
};
-static int policies_parse_exit_policy_internal(config_line_t *cfg,
- smartlist_t **dest,
- int ipv6_exit,
- int rejectprivate,
- uint32_t local_address,
- tor_addr_t *ipv6_local_address,
- int reject_interface_addresses,
- int add_default_policy);
+static int policies_parse_exit_policy_internal(
+ config_line_t *cfg,
+ smartlist_t **dest,
+ int ipv6_exit,
+ int rejectprivate,
+ const smartlist_t *configured_addresses,
+ int reject_interface_addresses,
+ int reject_configured_port_addresses,
+ int add_default_policy);
/** Replace all "private" entries in *<b>policy</b> with their expanded
* equivalents. */
@@ -443,7 +446,7 @@ validate_addr_policies(const or_options_t *options, char **msg)
smartlist_t *addr_policy=NULL;
*msg = NULL;
- if (policies_parse_exit_policy_from_options(options,0,NULL,0,&addr_policy)) {
+ if (policies_parse_exit_policy_from_options(options,0,NULL,&addr_policy)) {
REJECT("Error in ExitPolicy entry.");
}
@@ -864,7 +867,7 @@ addr_policy_intersects(addr_policy_t *a, addr_policy_t *b)
/** Add the exit policy described by <b>more</b> to <b>policy</b>.
*/
-static void
+STATIC void
append_exit_policy_string(smartlist_t **policy, const char *more)
{
config_line_t tmp;
@@ -881,6 +884,9 @@ append_exit_policy_string(smartlist_t **policy, const char *more)
void
addr_policy_append_reject_addr(smartlist_t **dest, const tor_addr_t *addr)
{
+ tor_assert(dest);
+ tor_assert(addr);
+
addr_policy_t p, *add;
memset(&p, 0, sizeof(p));
p.policy_type = ADDR_POLICY_REJECT;
@@ -893,6 +899,70 @@ addr_policy_append_reject_addr(smartlist_t **dest, const tor_addr_t *addr)
if (!*dest)
*dest = smartlist_new();
smartlist_add(*dest, add);
+ log_debug(LD_CONFIG, "Adding a reject ExitPolicy 'reject %s:*'",
+ fmt_addr(addr));
+}
+
+/* Is addr public for the purposes of rejection? */
+static int
+tor_addr_is_public_for_reject(const tor_addr_t *addr)
+{
+ return !tor_addr_is_null(addr) && !tor_addr_is_internal(addr, 0);
+}
+
+/* Add "reject <b>addr</b>:*" to <b>dest</b>, creating the list as needed.
+ * Filter the address, only adding an IPv4 reject rule if ipv4_rules
+ * is true, and similarly for ipv6_rules. Check each address returns true for
+ * tor_addr_is_public_for_reject before adding it.
+ */
+static void
+addr_policy_append_reject_addr_filter(smartlist_t **dest,
+ const tor_addr_t *addr,
+ int ipv4_rules,
+ int ipv6_rules)
+{
+ tor_assert(dest);
+ tor_assert(addr);
+
+ /* Only reject IP addresses which are public */
+ if (tor_addr_is_public_for_reject(addr)) {
+
+ /* Reject IPv4 addresses and IPv6 addresses based on the filters */
+ int is_ipv4 = tor_addr_is_v4(addr);
+ if ((is_ipv4 && ipv4_rules) || (!is_ipv4 && ipv6_rules)) {
+ addr_policy_append_reject_addr(dest, addr);
+ }
+ }
+}
+
+/** Add "reject addr:*" to <b>dest</b>, for each addr in addrs, creating the
+ * list as needed. */
+void
+addr_policy_append_reject_addr_list(smartlist_t **dest,
+ const smartlist_t *addrs)
+{
+ tor_assert(dest);
+ tor_assert(addrs);
+
+ SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, addr) {
+ addr_policy_append_reject_addr(dest, addr);
+ } SMARTLIST_FOREACH_END(addr);
+}
+
+/** Add "reject addr:*" to <b>dest</b>, for each addr in addrs, creating the
+ * list as needed. Filter using */
+static void
+addr_policy_append_reject_addr_list_filter(smartlist_t **dest,
+ const smartlist_t *addrs,
+ int ipv4_rules,
+ int ipv6_rules)
+{
+ tor_assert(dest);
+ tor_assert(addrs);
+
+ SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, addr) {
+ addr_policy_append_reject_addr_filter(dest, addr, ipv4_rules, ipv6_rules);
+ } SMARTLIST_FOREACH_END(addr);
}
/** Detect and excise "dead code" from the policy *<b>dest</b>. */
@@ -979,6 +1049,76 @@ exit_policy_remove_redundancies(smartlist_t *dest)
}
}
+/** Reject private helper for policies_parse_exit_policy_internal: rejects
+ * publicly routable addresses on this exit relay.
+ *
+ * Add reject entries to the linked list *dest:
+ * - if configured_addresses is non-NULL, add entries that reject each
+ * tor_addr_t* in the list as a destination.
+ * - if reject_interface_addresses is true, add entries that reject each
+ * public IPv4 and IPv6 address of each interface on this machine.
+ * - if reject_configured_port_addresses is true, add entries that reject
+ * each IPv4 and IPv6 address configured for a port.
+ *
+ * IPv6 entries are only added if ipv6_exit is true. (All IPv6 addresses are
+ * already blocked by policies_parse_exit_policy_internal if ipv6_exit is
+ * false.)
+ *
+ * The list *dest is created as needed.
+ */
+void
+policies_parse_exit_policy_reject_private(
+ smartlist_t **dest,
+ int ipv6_exit,
+ const smartlist_t *configured_addresses,
+ int reject_interface_addresses,
+ int reject_configured_port_addresses)
+{
+ tor_assert(dest);
+
+ /* Reject configured addresses, if they are from public netblocks. */
+ if (configured_addresses) {
+ addr_policy_append_reject_addr_list_filter(dest, configured_addresses,
+ 1, ipv6_exit);
+ }
+
+ /* Reject configured port addresses, if they are from public netblocks. */
+ if (reject_configured_port_addresses) {
+ const smartlist_t *port_addrs = get_configured_ports();
+
+ SMARTLIST_FOREACH_BEGIN(port_addrs, port_cfg_t *, port) {
+
+ /* Only reject port IP addresses, not port unix sockets */
+ if (!port->is_unix_addr) {
+ addr_policy_append_reject_addr_filter(dest, &port->addr, 1, ipv6_exit);
+ }
+ } SMARTLIST_FOREACH_END(port);
+ }
+
+ /* Reject local addresses from public netblocks on any interface. */
+ if (reject_interface_addresses) {
+ smartlist_t *public_addresses = NULL;
+
+ /* Reject public IPv4 addresses on any interface */
+ public_addresses = get_interface_address6_list(LOG_INFO, AF_INET, 0);
+ addr_policy_append_reject_addr_list_filter(dest, public_addresses, 1, 0);
+ free_interface_address6_list(public_addresses);
+
+ /* Don't look for IPv6 addresses if we're configured as IPv4-only */
+ if (ipv6_exit) {
+ /* Reject public IPv6 addresses on any interface */
+ public_addresses = get_interface_address6_list(LOG_INFO, AF_INET6, 0);
+ addr_policy_append_reject_addr_list_filter(dest, public_addresses, 0, 1);
+ free_interface_address6_list(public_addresses);
+ }
+ }
+
+ /* If addresses were added multiple times, remove all but one of them. */
+ if (*dest) {
+ exit_policy_remove_redundancies(*dest);
+ }
+}
+
#define DEFAULT_EXIT_POLICY \
"reject *:25,reject *:119,reject *:135-139,reject *:445," \
"reject *:563,reject *:1214,reject *:4661-4666," \
@@ -986,16 +1126,12 @@ exit_policy_remove_redundancies(smartlist_t *dest)
/** Parse the exit policy <b>cfg</b> into the linked list *<b>dest</b>.
*
- * If <b>ipv6_exit</b> is true, prepend "reject *6:*" to the policy.
+ * If <b>ipv6_exit</b> is false, prepend "reject *6:*" to the policy.
*
* If <b>rejectprivate</b> is true:
* - prepend "reject private:*" to the policy.
- * - if local_address is non-zero, treat it as a host-order IPv4 address,
- * and prepend an entry that rejects it as a destination.
- * - if ipv6_local_address is non-NULL, prepend an entry that rejects it as
- * a destination.
- * - if reject_interface_addresses is true, prepend entries that reject each
- * public IPv4 and IPv6 address of each interface on this machine.
+ * - prepend entries that reject publicly routable addresses on this exit
+ * relay by calling policies_parse_exit_policy_reject_private
*
* If cfg doesn't end in an absolute accept or reject and if
* <b>add_default_policy</b> is true, add the default exit
@@ -1008,12 +1144,13 @@ exit_policy_remove_redundancies(smartlist_t *dest)
* see router_add_exit_policy.
*/
static int
-policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest,
+policies_parse_exit_policy_internal(config_line_t *cfg,
+ smartlist_t **dest,
int ipv6_exit,
int rejectprivate,
- uint32_t local_address,
- tor_addr_t *ipv6_local_address,
+ const smartlist_t *configured_addresses,
int reject_interface_addresses,
+ int reject_configured_port_addresses,
int add_default_policy)
{
if (!ipv6_exit) {
@@ -1022,69 +1159,12 @@ policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest,
if (rejectprivate) {
/* Reject IPv4 and IPv6 reserved private netblocks */
append_exit_policy_string(dest, "reject private:*");
- /* Reject our local IPv4 address */
- if (local_address) {
- char buf[POLICY_BUF_LEN];
- tor_snprintf(buf, sizeof(buf), "reject %s:*", fmt_addr32(local_address));
- append_exit_policy_string(dest, buf);
- log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for our published "
- "IPv4 address", buf);
- }
- /* Reject our local IPv6 address */
- if (ipv6_exit && ipv6_local_address != NULL) {
- if (tor_addr_is_v4(ipv6_local_address)) {
- log_warn(LD_CONFIG, "IPv4 address '%s' provided as our IPv6 local "
- "address", fmt_addr(ipv6_local_address));
- } else {
- char buf6[POLICY_BUF_LEN];
- tor_snprintf(buf6, sizeof(buf6), "reject [%s]:*",
- fmt_addr(ipv6_local_address));
- append_exit_policy_string(dest, buf6);
- log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for our "
- "published IPv6 address", buf6);
- }
- }
- /* Reject local addresses from public netblocks on any interface,
- * but don't reject our published addresses twice */
- if (reject_interface_addresses) {
- smartlist_t *public_addresses = NULL;
- char bufif[POLICY_BUF_LEN];
-
- /* Reject public IPv4 addresses on any interface,
- * but don't reject our published IPv4 address twice */
- public_addresses = get_interface_address6_list(LOG_INFO, AF_INET, 0);
- SMARTLIST_FOREACH_BEGIN(public_addresses, tor_addr_t *, a) {
- if (!tor_addr_eq_ipv4h(a, local_address)) {
- tor_snprintf(bufif, sizeof(bufif), "reject %s:*",
- fmt_addr(a));
- append_exit_policy_string(dest, bufif);
- log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for a local "
- "interface's public IPv4 address", bufif);
- }
- } SMARTLIST_FOREACH_END(a);
- free_interface_address6_list(public_addresses);
-
- if (ipv6_exit) {
- /* Reject public IPv6 addresses on any interface,
- * but don't reject our published IPv6 address (if any) twice */
- public_addresses = get_interface_address6_list(LOG_INFO, AF_INET6, 0);
- SMARTLIST_FOREACH_BEGIN(public_addresses, tor_addr_t *, a) {
- /* if we don't have an IPv6 local address, we won't have rejected
- * it above. This could happen if a future release does IPv6
- * autodiscovery, and we are waiting to discover our external IPv6
- * address */
- if (ipv6_local_address == NULL
- || !tor_addr_eq(ipv6_local_address, a)) {
- tor_snprintf(bufif, sizeof(bufif), "reject6 [%s]:*",
- fmt_addr(a));
- append_exit_policy_string(dest, bufif);
- log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for a local "
- "interface's public IPv6 address", bufif);
- }
- } SMARTLIST_FOREACH_END(a);
- free_interface_address6_list(public_addresses);
- }
- }
+ /* Reject IPv4 and IPv6 publicly routable addresses on this exit relay */
+ policies_parse_exit_policy_reject_private(
+ dest, ipv6_exit,
+ configured_addresses,
+ reject_interface_addresses,
+ reject_configured_port_addresses);
}
if (parse_addr_policy(cfg, dest, -1))
return -1;
@@ -1167,12 +1247,8 @@ policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest,
* If <b>EXIT_POLICY_REJECT_PRIVATE</b> bit is set in <b>options</b>:
* - prepend an entry that rejects all destinations in all netblocks
* reserved for private use.
- * - if local_address is non-zero, treat it as a host-order IPv4 address,
- * and prepend an entry that rejects it as a destination.
- * - if ipv6_local_address is non-NULL, prepend an entry that rejects it as
- * a destination.
- * - if reject_interface_addresses is true, prepend entries that reject each
- * public IPv4 and IPv6 address of each interface on this machine.
+ * - prepend entries that reject publicly routable addresses on this exit
+ * relay by calling policies_parse_exit_policy_internal
*
* If <b>EXIT_POLICY_ADD_DEFAULT</b> bit is set in <b>options</b>, append
* default exit policy entries to <b>result</b> smartlist.
@@ -1180,9 +1256,7 @@ policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest,
int
policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
exit_policy_parser_cfg_t options,
- uint32_t local_address,
- tor_addr_t *ipv6_local_address,
- int reject_interface_addresses)
+ const smartlist_t *configured_addresses)
{
int ipv6_enabled = (options & EXIT_POLICY_IPV6_ENABLED) ? 1 : 0;
int reject_private = (options & EXIT_POLICY_REJECT_PRIVATE) ? 1 : 0;
@@ -1190,12 +1264,50 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
return policies_parse_exit_policy_internal(cfg,dest,ipv6_enabled,
reject_private,
- local_address,
- ipv6_local_address,
- reject_interface_addresses,
+ configured_addresses,
+ reject_private,
+ reject_private,
add_default);
}
+/** Helper function that adds addr to a smartlist as long as it is non-NULL
+ * and not tor_addr_is_null(). */
+static void
+policies_add_addr_to_smartlist(smartlist_t *addr_list, const tor_addr_t *addr)
+{
+ if (addr && !tor_addr_is_null(addr)) {
+ smartlist_add(addr_list, (void *)addr);
+ }
+}
+
+/** Helper function that adds ipv4h_addr to a smartlist as a tor_addr_t *,
+ * by converting it to a tor_addr_t and passing it to
+ * policies_add_addr_to_smartlist. */
+static void
+policies_add_ipv4h_to_smartlist(smartlist_t *addr_list, uint32_t ipv4h_addr)
+{
+ if (ipv4h_addr) {
+ tor_addr_t ipv4_tor_addr;
+ tor_addr_from_ipv4h(&ipv4_tor_addr, ipv4h_addr);
+ policies_add_addr_to_smartlist(addr_list, (void *)&ipv4_tor_addr);
+ }
+}
+
+/** Helper function that adds or_options->OutboundBindAddressIPv[4|6]_ to a
+ * smartlist as a tor_addr_t *, as long as or_options is non-NULL,
+ * by passing them to policies_add_addr_to_smartlist. */
+static void
+policies_add_outbound_addresses_to_smartlist(smartlist_t *addr_list,
+ const or_options_t *or_options)
+{
+ if (or_options) {
+ policies_add_addr_to_smartlist(addr_list,
+ &or_options->OutboundBindAddressIPv4_);
+ policies_add_addr_to_smartlist(addr_list,
+ &or_options->OutboundBindAddressIPv6_);
+ }
+}
+
/** Parse <b>ExitPolicy</b> member of <b>or_options</b> into <b>result</b>
* smartlist.
* If <b>or_options->IPv6Exit</b> is false, prepend an entry that
@@ -1205,11 +1317,13 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
* - prepend an entry that rejects all destinations in all netblocks reserved
* for private use.
* - if local_address is non-zero, treat it as a host-order IPv4 address, and
- * prepend an entry that rejects it as a destination.
- * - if ipv6_local_address is non-NULL, prepend an entry that rejects it as a
- * destination.
- * - if reject_interface_addresses is true, prepend entries that reject each
- * public IPv4 and IPv6 address of each interface on this machine.
+ * add it to the list of configured addresses.
+ * - if ipv6_local_address is non-NULL, and not the null tor_addr_t, add it
+ * to the list of configured addresses.
+ * - if or_options->OutboundBindAddressIPv4_ is not the null tor_addr_t, add
+ * it to the list of configured addresses.
+ * - if or_options->OutboundBindAddressIPv6_ is not the null tor_addr_t, add
+ * it to the list of configured addresses.
*
* If <b>or_options->BridgeRelay</b> is false, append entries of default
* Tor exit policy into <b>result</b> smartlist.
@@ -1220,18 +1334,21 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
int
policies_parse_exit_policy_from_options(const or_options_t *or_options,
uint32_t local_address,
- tor_addr_t *ipv6_local_address,
- int reject_interface_addresses,
+ const tor_addr_t *ipv6_local_address,
smartlist_t **result)
{
exit_policy_parser_cfg_t parser_cfg = 0;
+ smartlist_t *configured_addresses = smartlist_new();
+ int rv = 0;
+ /* Short-circuit for non-exit relays */
if (or_options->ExitRelay == 0) {
append_exit_policy_string(result, "reject *4:*");
append_exit_policy_string(result, "reject *6:*");
return 0;
}
+ /* Configure the parser */
if (or_options->IPv6Exit) {
parser_cfg |= EXIT_POLICY_IPV6_ENABLED;
}
@@ -1244,10 +1361,20 @@ policies_parse_exit_policy_from_options(const or_options_t *or_options,
parser_cfg |= EXIT_POLICY_ADD_DEFAULT;
}
- return policies_parse_exit_policy(or_options->ExitPolicy,result,
- parser_cfg,local_address,
- ipv6_local_address,
- reject_interface_addresses);
+ /* Add the configured addresses to the tor_addr_t* list */
+ policies_add_ipv4h_to_smartlist(configured_addresses, local_address);
+ policies_add_addr_to_smartlist(configured_addresses, ipv6_local_address);
+ policies_add_outbound_addresses_to_smartlist(configured_addresses,
+ or_options);
+
+ rv = policies_parse_exit_policy(or_options->ExitPolicy, result, parser_cfg,
+ configured_addresses);
+
+ /* We don't need to free the pointers in this list, they are either constant
+ * or locally scoped. */
+ smartlist_free(configured_addresses);
+
+ return rv;
}
/** Add "reject *:*" to the end of the policy in *<b>dest</b>, allocating
@@ -1934,6 +2061,53 @@ compare_tor_addr_to_node_policy(const tor_addr_t *addr, uint16_t port,
}
}
+/**
+ * Given <b>policy_list</b>, a list of addr_policy_t, produce a string
+ * representation of the list.
+ * If <b>include_ipv4</b> is true, include IPv4 entries.
+ * If <b>include_ipv6</b> is true, include IPv6 entries.
+ */
+char *
+policy_dump_to_string(const smartlist_t *policy_list,
+ int include_ipv4,
+ int include_ipv6)
+{
+ smartlist_t *policy_string_list;
+ char *policy_string = NULL;
+
+ policy_string_list = smartlist_new();
+
+ SMARTLIST_FOREACH_BEGIN(policy_list, addr_policy_t *, tmpe) {
+ char *pbuf;
+ int bytes_written_to_pbuf;
+ if ((tor_addr_family(&tmpe->addr) == AF_INET6) && (!include_ipv6)) {
+ continue; /* Don't include IPv6 parts of address policy */
+ }
+ if ((tor_addr_family(&tmpe->addr) == AF_INET) && (!include_ipv4)) {
+ continue; /* Don't include IPv4 parts of address policy */
+ }
+
+ pbuf = tor_malloc(POLICY_BUF_LEN);
+ bytes_written_to_pbuf = policy_write_item(pbuf,POLICY_BUF_LEN, tmpe, 1);
+
+ if (bytes_written_to_pbuf < 0) {
+ log_warn(LD_BUG, "policy_dump_to_string ran out of room!");
+ tor_free(pbuf);
+ goto done;
+ }
+
+ smartlist_add(policy_string_list,pbuf);
+ } SMARTLIST_FOREACH_END(tmpe);
+
+ policy_string = smartlist_join_strings(policy_string_list, "\n", 0, NULL);
+
+ done:
+ SMARTLIST_FOREACH(policy_string_list, char *, str, tor_free(str));
+ smartlist_free(policy_string_list);
+
+ return policy_string;
+}
+
/** Implementation for GETINFO control command: knows the answer for questions
* about "exit-policy/..." */
int
@@ -1945,6 +2119,56 @@ getinfo_helper_policies(control_connection_t *conn,
(void) errmsg;
if (!strcmp(question, "exit-policy/default")) {
*answer = tor_strdup(DEFAULT_EXIT_POLICY);
+ } else if (!strcmp(question, "exit-policy/reject-private/default")) {
+ smartlist_t *private_policy_strings;
+ const char **priv = private_nets;
+
+ private_policy_strings = smartlist_new();
+
+ while (*priv != NULL) {
+ /* IPv6 addresses are in "[]" and contain ":",
+ * IPv4 addresses are not in "[]" and contain "." */
+ smartlist_add_asprintf(private_policy_strings, "reject %s:*", *priv);
+ priv++;
+ }
+
+ *answer = smartlist_join_strings(private_policy_strings,
+ ",", 0, NULL);
+
+ SMARTLIST_FOREACH(private_policy_strings, char *, str, tor_free(str));
+ smartlist_free(private_policy_strings);
+ } else if (!strcmp(question, "exit-policy/reject-private/relay")) {
+ const or_options_t *options = get_options();
+ const routerinfo_t *me = router_get_my_routerinfo();
+
+ if (!me) {
+ *errmsg = "router_get_my_routerinfo returned NULL";
+ return -1;
+ }
+
+ if (!options->ExitPolicyRejectPrivate) {
+ *answer = tor_strdup("");
+ return 0;
+ }
+
+ smartlist_t *private_policy_list = smartlist_new();
+ smartlist_t *configured_addresses = smartlist_new();
+
+ /* Add the configured addresses to the tor_addr_t* list */
+ policies_add_ipv4h_to_smartlist(configured_addresses, me->addr);
+ policies_add_addr_to_smartlist(configured_addresses, &me->ipv6_addr);
+ policies_add_outbound_addresses_to_smartlist(configured_addresses,
+ options);
+
+ policies_parse_exit_policy_reject_private(
+ &private_policy_list,
+ options->IPv6Exit,
+ configured_addresses,
+ 1, 1);
+ *answer = policy_dump_to_string(private_policy_list, 1, 1);
+
+ addr_policy_list_free(private_policy_list);
+ addr_policy_list_free(configured_addresses);
} else if (!strcmpstart(question, "exit-policy/")) {
const routerinfo_t *me = router_get_my_routerinfo();
diff --git a/src/or/policies.h b/src/or/policies.h
index f200d7babe..bb56bf42b8 100644
--- a/src/or/policies.h
+++ b/src/or/policies.h
@@ -44,26 +44,34 @@ addr_policy_t *addr_policy_get_canonical_entry(addr_policy_t *ent);
int cmp_addr_policies(smartlist_t *a, smartlist_t *b);
MOCK_DECL(addr_policy_result_t, compare_tor_addr_to_addr_policy,
(const tor_addr_t *addr, uint16_t port, const smartlist_t *policy));
-
addr_policy_result_t compare_tor_addr_to_node_policy(const tor_addr_t *addr,
uint16_t port, const node_t *node);
-int policies_parse_exit_policy_from_options(const or_options_t *or_options,
- uint32_t local_address,
- tor_addr_t *ipv6_local_address,
- int reject_interface_addresses,
- smartlist_t **result);
+int policies_parse_exit_policy_from_options(
+ const or_options_t *or_options,
+ uint32_t local_address,
+ const tor_addr_t *ipv6_local_address,
+ smartlist_t **result);
int policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
exit_policy_parser_cfg_t options,
- uint32_t local_address,
- tor_addr_t *ipv6_local_address,
- int reject_interface_addresses);
+ const smartlist_t *configured_addresses);
+void policies_parse_exit_policy_reject_private(
+ smartlist_t **dest,
+ int ipv6_exit,
+ const smartlist_t *configured_addresses,
+ int reject_interface_addresses,
+ int reject_configured_port_addresses);
void policies_exit_policy_append_reject_star(smartlist_t **dest);
void addr_policy_append_reject_addr(smartlist_t **dest,
const tor_addr_t *addr);
+void addr_policy_append_reject_addr_list(smartlist_t **dest,
+ const smartlist_t *addrs);
void policies_set_node_exitpolicy_to_reject_all(node_t *exitrouter);
int exit_policy_is_general_exit(smartlist_t *policy);
int policy_is_reject_star(const smartlist_t *policy, sa_family_t family);
+char * policy_dump_to_string(const smartlist_t *policy_list,
+ int include_ipv4,
+ int include_ipv6);
int getinfo_helper_policies(control_connection_t *conn,
const char *question, char **answer,
const char **errmsg);
@@ -84,5 +92,9 @@ addr_policy_result_t compare_tor_addr_to_short_policy(
const tor_addr_t *addr, uint16_t port,
const short_policy_t *policy);
+#ifdef POLICIES_PRIVATE
+STATIC void append_exit_policy_string(smartlist_t **policy, const char *more);
+#endif
+
#endif
diff --git a/src/or/rendcache.c b/src/or/rendcache.c
index d4bdd68698..790e0c246d 100644
--- a/src/or/rendcache.c
+++ b/src/or/rendcache.c
@@ -3,9 +3,10 @@
/**
* \file rendcache.c
- * \brief Hidden service desriptor cache.
+ * \brief Hidden service descriptor cache.
**/
+#define RENDCACHE_PRIVATE
#include "rendcache.h"
#include "config.h"
@@ -15,11 +16,14 @@
/** Map from service id (as generated by rend_get_service_id) to
* rend_cache_entry_t. */
-static strmap_t *rend_cache = NULL;
+STATIC strmap_t *rend_cache = NULL;
+
+/** Map from service id to rend_cache_entry_t; only for hidden services. */
+static strmap_t *rend_cache_local_service = NULL;
/** Map from descriptor id to rend_cache_entry_t; only for hidden service
* directories. */
-static digestmap_t *rend_cache_v2_dir = NULL;
+STATIC digestmap_t *rend_cache_v2_dir = NULL;
/** (Client side only) Map from service id to rend_cache_failure_t. This
* cache is used to track intro point(IP) failures so we know when to keep
@@ -46,10 +50,10 @@ static digestmap_t *rend_cache_v2_dir = NULL;
* This scheme allows us to not realy on the descriptor's timestamp (which
* is rounded down to the hour) to know if we have a newer descriptor. We
* only rely on the usability of intro points from an internal state. */
-static strmap_t *rend_cache_failure = NULL;
+STATIC strmap_t *rend_cache_failure = NULL;
/** DOCDOC */
-static size_t rend_cache_total_allocation = 0;
+STATIC size_t rend_cache_total_allocation = 0;
/** Initializes the service descriptor cache.
*/
@@ -58,11 +62,12 @@ rend_cache_init(void)
{
rend_cache = strmap_new();
rend_cache_v2_dir = digestmap_new();
+ rend_cache_local_service = strmap_new();
rend_cache_failure = strmap_new();
}
/** Return the approximate number of bytes needed to hold <b>e</b>. */
-static size_t
+STATIC size_t
rend_cache_entry_allocation(const rend_cache_entry_t *e)
{
if (!e)
@@ -80,7 +85,7 @@ rend_cache_get_total_allocation(void)
}
/** Decrement the total bytes attributed to the rendezvous cache by n. */
-static void
+STATIC void
rend_cache_decrement_allocation(size_t n)
{
static int have_underflowed = 0;
@@ -97,7 +102,7 @@ rend_cache_decrement_allocation(size_t n)
}
/** Increase the total bytes attributed to the rendezvous cache by n. */
-static void
+STATIC void
rend_cache_increment_allocation(size_t n)
{
static int have_overflowed = 0;
@@ -113,7 +118,7 @@ rend_cache_increment_allocation(size_t n)
}
/** Helper: free a rend cache failure intro object. */
-static void
+STATIC void
rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t *entry)
{
if (entry == NULL) {
@@ -130,7 +135,7 @@ rend_cache_failure_intro_entry_free_(void *entry)
/** Allocate a rend cache failure intro object and return it. <b>failure</b>
* is set into the object. This function can not fail. */
-static rend_cache_failure_intro_t *
+STATIC rend_cache_failure_intro_t *
rend_cache_failure_intro_entry_new(rend_intro_point_failure_t failure)
{
rend_cache_failure_intro_t *entry = tor_malloc(sizeof(*entry));
@@ -140,7 +145,7 @@ rend_cache_failure_intro_entry_new(rend_intro_point_failure_t failure)
}
/** Helper: free a rend cache failure object. */
-static void
+STATIC void
rend_cache_failure_entry_free(rend_cache_failure_t *entry)
{
if (entry == NULL) {
@@ -156,7 +161,7 @@ rend_cache_failure_entry_free(rend_cache_failure_t *entry)
/** Helper: deallocate a rend_cache_failure_t. (Used with strmap_free(),
* which requires a function pointer whose argument is void*). */
-static void
+STATIC void
rend_cache_failure_entry_free_(void *entry)
{
rend_cache_failure_entry_free(entry);
@@ -164,7 +169,7 @@ rend_cache_failure_entry_free_(void *entry)
/** Allocate a rend cache failure object and return it. This function can
* not fail. */
-static rend_cache_failure_t *
+STATIC rend_cache_failure_t *
rend_cache_failure_entry_new(void)
{
rend_cache_failure_t *entry = tor_malloc(sizeof(*entry));
@@ -174,7 +179,7 @@ rend_cache_failure_entry_new(void)
/** Remove failure cache entry for the service ID in the given descriptor
* <b>desc</b>. */
-static void
+STATIC void
rend_cache_failure_remove(rend_service_descriptor_t *desc)
{
char service_id[REND_SERVICE_ID_LEN_BASE32 + 1];
@@ -194,7 +199,7 @@ rend_cache_failure_remove(rend_service_descriptor_t *desc)
}
/** Helper: free storage held by a single service descriptor cache entry. */
-static void
+STATIC void
rend_cache_entry_free(rend_cache_entry_t *e)
{
if (!e)
@@ -222,9 +227,11 @@ rend_cache_free_all(void)
{
strmap_free(rend_cache, rend_cache_entry_free_);
digestmap_free(rend_cache_v2_dir, rend_cache_entry_free_);
+ strmap_free(rend_cache_local_service, rend_cache_entry_free_);
strmap_free(rend_cache_failure, rend_cache_failure_entry_free_);
rend_cache = NULL;
rend_cache_v2_dir = NULL;
+ rend_cache_local_service = NULL;
rend_cache_failure = NULL;
rend_cache_total_allocation = 0;
}
@@ -258,24 +265,33 @@ rend_cache_failure_clean(time_t now)
} STRMAP_FOREACH_END;
}
-/** Removes all old entries from the service descriptor cache.
+/** Removes all old entries from the client or service descriptor cache.
*/
void
-rend_cache_clean(time_t now)
+rend_cache_clean(time_t now, rend_cache_type_t cache_type)
{
strmap_iter_t *iter;
const char *key;
void *val;
rend_cache_entry_t *ent;
time_t cutoff = now - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW;
- for (iter = strmap_iter_init(rend_cache); !strmap_iter_done(iter); ) {
+ strmap_t *cache = NULL;
+
+ if (cache_type == REND_CACHE_TYPE_CLIENT) {
+ cache = rend_cache;
+ } else if (cache_type == REND_CACHE_TYPE_SERVICE) {
+ cache = rend_cache_local_service;
+ }
+ tor_assert(cache);
+
+ for (iter = strmap_iter_init(cache); !strmap_iter_done(iter); ) {
strmap_iter_get(iter, &key, &val);
ent = (rend_cache_entry_t*)val;
if (ent->parsed->timestamp < cutoff) {
- iter = strmap_iter_next_rmv(rend_cache, iter);
+ iter = strmap_iter_next_rmv(cache, iter);
rend_cache_entry_free(ent);
} else {
- iter = strmap_iter_next(rend_cache, iter);
+ iter = strmap_iter_next(cache, iter);
}
}
}
@@ -308,7 +324,7 @@ rend_cache_failure_purge(void)
* <b>identity</b> and service ID <b>service_id</b>. If found, the intro
* failure is set in <b>intro_entry</b> else it stays untouched. Return 1
* iff found else 0. */
-static int
+STATIC int
cache_failure_intro_lookup(const uint8_t *identity, const char *service_id,
rend_cache_failure_intro_t **intro_entry)
{
@@ -352,7 +368,7 @@ cache_failure_intro_dup(const rend_cache_failure_intro_t *entry)
/** Add an intro point failure to the failure cache using the relay
* <b>identity</b> and service ID <b>service_id</b>. Record the
* <b>failure</b> in that object. */
-static void
+STATIC void
cache_failure_intro_add(const uint8_t *identity, const char *service_id,
rend_intro_point_failure_t failure)
{
@@ -379,7 +395,7 @@ cache_failure_intro_add(const uint8_t *identity, const char *service_id,
* descriptor and kept into the failure cache. Then, each intro points that
* are NOT in the descriptor but in the failure cache for the given
* <b>service_id</b> are removed from the failure cache. */
-static void
+STATIC void
validate_intro_point_failure(const rend_service_descriptor_t *desc,
const char *service_id)
{
@@ -535,6 +551,42 @@ rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **e)
return ret;
}
+/*
+ * Lookup the v2 service descriptor with the service ID <b>query</b> in the
+ * local service descriptor cache. Return 0 if found and if <b>e</b> is
+ * non NULL, set it with the entry found. Else, a negative value is returned
+ * and <b>e</b> is untouched.
+ * -EINVAL means that <b>query</b> is not a valid service id.
+ * -ENOENT means that no entry in the cache was found. */
+int
+rend_cache_lookup_v2_desc_as_service(const char *query, rend_cache_entry_t **e)
+{
+ int ret = 0;
+ rend_cache_entry_t *entry = NULL;
+
+ tor_assert(rend_cache_local_service);
+ tor_assert(query);
+
+ if (!rend_valid_service_id(query)) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ /* Lookup descriptor and return. */
+ entry = strmap_get_lc(rend_cache_local_service, query);
+ if (!entry) {
+ ret = -ENOENT;
+ goto end;
+ }
+
+ if (e) {
+ *e = entry;
+ }
+
+ end:
+ return ret;
+}
+
/** Lookup the v2 service descriptor with base32-encoded <b>desc_id</b> and
* copy the pointer to it to *<b>desc</b>. Return 1 on success, 0 on
* well-formed-but-not-found, and -1 on failure.
@@ -660,7 +712,6 @@ rend_cache_store_v2_desc_as_dir(const char *desc)
log_info(LD_REND, "Successfully stored service descriptor with desc ID "
"'%s' and len %d.",
safe_str(desc_id_base32), (int)encoded_size);
-
/* Statistics: Note down this potentially new HS. */
if (options->HiddenServiceStatistics) {
rep_hist_stored_maybe_new_hs(e->parsed->pk);
@@ -687,6 +738,80 @@ rend_cache_store_v2_desc_as_dir(const char *desc)
return RCS_OKAY;
}
+/** Parse the v2 service descriptor in <b>desc</b> and store it to the
+* local service rend cache. Don't attempt to decrypt the included list of
+* introduction points.
+*
+* If we have a newer descriptor with the same ID, ignore this one.
+* If we have an older descriptor with the same ID, replace it.
+*
+* Return an appropriate rend_cache_store_status_t.
+*/
+rend_cache_store_status_t
+rend_cache_store_v2_desc_as_service(const char *desc)
+{
+ rend_service_descriptor_t *parsed = NULL;
+ char desc_id[DIGEST_LEN];
+ char *intro_content = NULL;
+ size_t intro_size;
+ size_t encoded_size;
+ const char *next_desc;
+ char service_id[REND_SERVICE_ID_LEN_BASE32+1];
+ rend_cache_entry_t *e;
+ rend_cache_store_status_t retval = RCS_BADDESC;
+ tor_assert(rend_cache_local_service);
+ tor_assert(desc);
+
+ /* Parse the descriptor. */
+ if (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content,
+ &intro_size, &encoded_size,
+ &next_desc, desc, 0) < 0) {
+ log_warn(LD_REND, "Could not parse descriptor.");
+ goto err;
+ }
+ /* Compute service ID from public key. */
+ if (rend_get_service_id(parsed->pk, service_id)<0) {
+ log_warn(LD_REND, "Couldn't compute service ID.");
+ goto err;
+ }
+
+ /* Do we already have a newer descriptor? Allow new descriptors with a
+ rounded timestamp equal to or newer than the current descriptor */
+ e = (rend_cache_entry_t*) strmap_get_lc(rend_cache_local_service,
+ service_id);
+ if (e && e->parsed->timestamp > parsed->timestamp) {
+ log_info(LD_REND, "We already have a newer service descriptor for "
+ "service ID %s.", safe_str_client(service_id));
+ goto okay;
+ }
+ /* We don't care about the introduction points. */
+ tor_free(intro_content);
+ if (!e) {
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ strmap_set_lc(rend_cache_local_service, service_id, e);
+ } else {
+ rend_cache_decrement_allocation(rend_cache_entry_allocation(e));
+ rend_service_descriptor_free(e->parsed);
+ tor_free(e->desc);
+ }
+ e->parsed = parsed;
+ e->desc = tor_malloc_zero(encoded_size + 1);
+ strlcpy(e->desc, desc, encoded_size + 1);
+ e->len = encoded_size;
+ rend_cache_increment_allocation(rend_cache_entry_allocation(e));
+ log_debug(LD_REND,"Successfully stored rend desc '%s', len %d.",
+ safe_str_client(service_id), (int)encoded_size);
+ return RCS_OKAY;
+
+ okay:
+ retval = RCS_OKAY;
+
+ err:
+ rend_service_descriptor_free(parsed);
+ tor_free(intro_content);
+ return retval;
+}
+
/** Parse the v2 service descriptor in <b>desc</b>, decrypt the included list
* of introduction points with <b>descriptor_cookie</b> (which may also be
* <b>NULL</b> if decryption is not necessary), and store the descriptor to
diff --git a/src/or/rendcache.h b/src/or/rendcache.h
index 0512058054..decb040ee7 100644
--- a/src/or/rendcache.h
+++ b/src/or/rendcache.h
@@ -48,14 +48,21 @@ typedef struct rend_cache_failure_t {
digestmap_t *intro_failures;
} rend_cache_failure_t;
+typedef enum {
+ REND_CACHE_TYPE_CLIENT = 1,
+ REND_CACHE_TYPE_SERVICE = 2,
+} rend_cache_type_t;
+
void rend_cache_init(void);
-void rend_cache_clean(time_t now);
+void rend_cache_clean(time_t now, rend_cache_type_t cache_type);
void rend_cache_failure_clean(time_t now);
void rend_cache_clean_v2_descs_as_dir(time_t now, size_t min_to_remove);
void rend_cache_purge(void);
void rend_cache_free_all(void);
int rend_cache_lookup_entry(const char *query, int version,
rend_cache_entry_t **entry_out);
+int rend_cache_lookup_v2_desc_as_service(const char *query,
+ rend_cache_entry_t **entry_out);
int rend_cache_lookup_v2_desc_as_dir(const char *query, const char **desc);
/** Return value from rend_cache_store_v2_desc_as_{dir,client}. */
typedef enum {
@@ -65,6 +72,8 @@ typedef enum {
} rend_cache_store_status_t;
rend_cache_store_status_t rend_cache_store_v2_desc_as_dir(const char *desc);
+rend_cache_store_status_t rend_cache_store_v2_desc_as_service(
+ const char *desc);
rend_cache_store_status_t rend_cache_store_v2_desc_as_client(const char *desc,
const char *desc_id_base32,
const rend_data_t *rend_query,
@@ -76,5 +85,31 @@ void rend_cache_intro_failure_note(rend_intro_point_failure_t failure,
const char *service_id);
void rend_cache_failure_purge(void);
+#ifdef RENDCACHE_PRIVATE
+
+STATIC size_t rend_cache_entry_allocation(const rend_cache_entry_t *e);
+STATIC void rend_cache_entry_free(rend_cache_entry_t *e);
+STATIC void rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t
+ *entry);
+STATIC void rend_cache_failure_entry_free(rend_cache_failure_t *entry);
+STATIC int cache_failure_intro_lookup(const uint8_t *identity,
+ const char *service_id,
+ rend_cache_failure_intro_t
+ **intro_entry);
+STATIC void rend_cache_decrement_allocation(size_t n);
+STATIC void rend_cache_increment_allocation(size_t n);
+STATIC rend_cache_failure_intro_t *rend_cache_failure_intro_entry_new(
+ rend_intro_point_failure_t failure);
+STATIC rend_cache_failure_t *rend_cache_failure_entry_new(void);
+STATIC void rend_cache_failure_remove(rend_service_descriptor_t *desc);
+STATIC void cache_failure_intro_add(const uint8_t *identity,
+ const char *service_id,
+ rend_intro_point_failure_t failure);
+STATIC void validate_intro_point_failure(const rend_service_descriptor_t *desc,
+ const char *service_id);
+
+STATIC void rend_cache_failure_entry_free_(void *entry);
+#endif
+
#endif /* TOR_RENDCACHE_H */
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index 11e940ce85..3e1c4f3613 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -52,7 +52,7 @@ rend_client_introcirc_has_opened(origin_circuit_t *circ)
tor_assert(circ->cpath);
log_info(LD_REND,"introcirc is open");
- connection_ap_attach_pending();
+ connection_ap_attach_pending(1);
}
/** Send the establish-rendezvous cell along a rendezvous circuit. if
@@ -1103,7 +1103,7 @@ rend_client_rendezvous_acked(origin_circuit_t *circ, const uint8_t *request,
* than trying to attach them all. See comments bug 743. */
/* If we already have the introduction circuit built, make sure we send
* the INTRODUCE cell _now_ */
- connection_ap_attach_pending();
+ connection_ap_attach_pending(1);
return 0;
}
@@ -1222,12 +1222,7 @@ rend_client_desc_trynow(const char *query)
base_conn->timestamp_lastread = now;
base_conn->timestamp_lastwritten = now;
- if (connection_ap_handshake_attach_circuit(conn) < 0) {
- /* it will never work */
- log_warn(LD_REND,"Rendezvous attempt failed. Closing.");
- if (!base_conn->marked_for_close)
- connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
- }
+ connection_ap_mark_as_pending_circuit(conn);
} else { /* 404, or fetch didn't get that far */
log_notice(LD_REND,"Closing stream for '%s.onion': hidden service is "
"unavailable (try again later).",
diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c
index 1e6c6dabda..8c02b67556 100644
--- a/src/or/rendcommon.c
+++ b/src/or/rendcommon.c
@@ -11,6 +11,7 @@
#include "or.h"
#include "circuitbuild.h"
#include "config.h"
+#include "control.h"
#include "rendclient.h"
#include "rendcommon.h"
#include "rendmid.h"
@@ -453,6 +454,7 @@ rend_encode_v2_descriptors(smartlist_t *descs_out,
smartlist_t *client_cookies)
{
char service_id[DIGEST_LEN];
+ char service_id_base32[REND_SERVICE_ID_LEN_BASE32+1];
uint32_t time_period;
char *ipos_base64 = NULL, *ipos = NULL, *ipos_encrypted = NULL,
*descriptor_cookie = NULL;
@@ -647,6 +649,11 @@ rend_encode_v2_descriptors(smartlist_t *descs_out,
goto err;
}
smartlist_add(descs_out, enc);
+ /* Add the uploaded descriptor to the local service's descriptor cache */
+ rend_cache_store_v2_desc_as_service(enc->desc_str);
+ base32_encode(service_id_base32, sizeof(service_id_base32),
+ service_id, REND_SERVICE_ID_LEN);
+ control_event_hs_descriptor_created(service_id_base32, desc_id_base32, k);
}
log_info(LD_REND, "Successfully encoded a v2 descriptor and "
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 77d8b716a2..15d98bfde5 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -3203,39 +3203,72 @@ upload_service_descriptor(rend_service_t *service)
rendpostperiod = get_options()->RendPostPeriod;
- /* Upload descriptor? */
- if (get_options()->PublishHidServDescriptors) {
- networkstatus_t *c = networkstatus_get_latest_consensus();
- if (c && smartlist_len(c->routerstatus_list) > 0) {
- int seconds_valid, i, j, num_descs;
- smartlist_t *descs = smartlist_new();
- smartlist_t *client_cookies = smartlist_new();
- /* Either upload a single descriptor (including replicas) or one
- * descriptor for each authorized client in case of authorization
- * type 'stealth'. */
- num_descs = service->auth_type == REND_STEALTH_AUTH ?
- smartlist_len(service->clients) : 1;
- for (j = 0; j < num_descs; j++) {
- crypto_pk_t *client_key = NULL;
- rend_authorized_client_t *client = NULL;
- smartlist_clear(client_cookies);
- switch (service->auth_type) {
- case REND_NO_AUTH:
- /* Do nothing here. */
- break;
- case REND_BASIC_AUTH:
- SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *,
- cl, smartlist_add(client_cookies, cl->descriptor_cookie));
- break;
- case REND_STEALTH_AUTH:
- client = smartlist_get(service->clients, j);
- client_key = client->client_key;
- smartlist_add(client_cookies, client->descriptor_cookie);
- break;
- }
- /* Encode the current descriptor. */
+ networkstatus_t *c = networkstatus_get_latest_consensus();
+ if (c && smartlist_len(c->routerstatus_list) > 0) {
+ int seconds_valid, i, j, num_descs;
+ smartlist_t *descs = smartlist_new();
+ smartlist_t *client_cookies = smartlist_new();
+ /* Either upload a single descriptor (including replicas) or one
+ * descriptor for each authorized client in case of authorization
+ * type 'stealth'. */
+ num_descs = service->auth_type == REND_STEALTH_AUTH ?
+ smartlist_len(service->clients) : 1;
+ for (j = 0; j < num_descs; j++) {
+ crypto_pk_t *client_key = NULL;
+ rend_authorized_client_t *client = NULL;
+ smartlist_clear(client_cookies);
+ switch (service->auth_type) {
+ case REND_NO_AUTH:
+ /* Do nothing here. */
+ break;
+ case REND_BASIC_AUTH:
+ SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *,
+ cl, smartlist_add(client_cookies, cl->descriptor_cookie));
+ break;
+ case REND_STEALTH_AUTH:
+ client = smartlist_get(service->clients, j);
+ client_key = client->client_key;
+ smartlist_add(client_cookies, client->descriptor_cookie);
+ break;
+ }
+ /* Encode the current descriptor. */
+ seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
+ now, 0,
+ service->auth_type,
+ client_key,
+ client_cookies);
+ if (seconds_valid < 0) {
+ log_warn(LD_BUG, "Internal error: couldn't encode service "
+ "descriptor; not uploading.");
+ smartlist_free(descs);
+ smartlist_free(client_cookies);
+ return;
+ }
+ rend_get_service_id(service->desc->pk, serviceid);
+ if (get_options()->PublishHidServDescriptors) {
+ /* Post the current descriptors to the hidden service directories. */
+ log_info(LD_REND, "Launching upload for hidden service %s",
+ serviceid);
+ directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
+ seconds_valid);
+ }
+ /* Free memory for descriptors. */
+ for (i = 0; i < smartlist_len(descs); i++)
+ rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
+ smartlist_clear(descs);
+ /* Update next upload time. */
+ if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS
+ > rendpostperiod)
+ service->next_upload_time = now + rendpostperiod;
+ else if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS)
+ service->next_upload_time = now + seconds_valid + 1;
+ else
+ service->next_upload_time = now + seconds_valid -
+ REND_TIME_PERIOD_OVERLAPPING_V2_DESCS + 1;
+ /* Post also the next descriptors, if necessary. */
+ if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) {
seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
- now, 0,
+ now, 1,
service->auth_type,
client_key,
client_cookies);
@@ -3246,51 +3279,23 @@ upload_service_descriptor(rend_service_t *service)
smartlist_free(client_cookies);
return;
}
- /* Post the current descriptors to the hidden service directories. */
- rend_get_service_id(service->desc->pk, serviceid);
- log_info(LD_REND, "Launching upload for hidden service %s",
- serviceid);
- directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
- seconds_valid);
+ if (get_options()->PublishHidServDescriptors) {
+ directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
+ seconds_valid);
+ }
/* Free memory for descriptors. */
for (i = 0; i < smartlist_len(descs); i++)
rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
smartlist_clear(descs);
- /* Update next upload time. */
- if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS
- > rendpostperiod)
- service->next_upload_time = now + rendpostperiod;
- else if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS)
- service->next_upload_time = now + seconds_valid + 1;
- else
- service->next_upload_time = now + seconds_valid -
- REND_TIME_PERIOD_OVERLAPPING_V2_DESCS + 1;
- /* Post also the next descriptors, if necessary. */
- if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) {
- seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
- now, 1,
- service->auth_type,
- client_key,
- client_cookies);
- if (seconds_valid < 0) {
- log_warn(LD_BUG, "Internal error: couldn't encode service "
- "descriptor; not uploading.");
- smartlist_free(descs);
- smartlist_free(client_cookies);
- return;
- }
- directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
- seconds_valid);
- /* Free memory for descriptors. */
- for (i = 0; i < smartlist_len(descs); i++)
- rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
- smartlist_clear(descs);
- }
}
- smartlist_free(descs);
- smartlist_free(client_cookies);
- uploaded = 1;
+ }
+ smartlist_free(descs);
+ smartlist_free(client_cookies);
+ uploaded = 1;
+ if (get_options()->PublishHidServDescriptors) {
log_info(LD_REND, "Successfully uploaded v2 rend descriptors!");
+ } else {
+ log_info(LD_REND, "Successfully stored created v2 rend descriptors!");
}
}
@@ -3635,9 +3640,6 @@ rend_consider_services_upload(time_t now)
MIN_REND_INITIAL_POST_DELAY_TESTING :
MIN_REND_INITIAL_POST_DELAY);
- if (!get_options()->PublishHidServDescriptors)
- return;
-
for (i=0; i < smartlist_len(rend_service_list); ++i) {
service = smartlist_get(rend_service_list, i);
if (!service->next_upload_time) { /* never been uploaded yet */
diff --git a/src/or/rephist.c b/src/or/rephist.c
index fe0997c891..4f7aaae279 100644
--- a/src/or/rephist.c
+++ b/src/or/rephist.c
@@ -3026,21 +3026,21 @@ rep_hist_stored_maybe_new_hs(const crypto_pk_t *pubkey)
/* The number of cells that are supposed to be hidden from the adversary
* by adding noise from the Laplace distribution. This value, divided by
- * EPSILON, is Laplace parameter b. */
+ * EPSILON, is Laplace parameter b. It must be greather than 0. */
#define REND_CELLS_DELTA_F 2048
/* Security parameter for obfuscating number of cells with a value between
- * 0 and 1. Smaller values obfuscate observations more, but at the same
+ * ]0.0, 1.0]. Smaller values obfuscate observations more, but at the same
* time make statistics less usable. */
#define REND_CELLS_EPSILON 0.3
/* The number of cells that are supposed to be hidden from the adversary
* by rounding up to the next multiple of this number. */
#define REND_CELLS_BIN_SIZE 1024
-/* The number of service identities that are supposed to be hidden from
- * the adversary by adding noise from the Laplace distribution. This
- * value, divided by EPSILON, is Laplace parameter b. */
+/* The number of service identities that are supposed to be hidden from the
+ * adversary by adding noise from the Laplace distribution. This value,
+ * divided by EPSILON, is Laplace parameter b. It must be greater than 0. */
#define ONIONS_SEEN_DELTA_F 8
/* Security parameter for obfuscating number of service identities with a
- * value between 0 and 1. Smaller values obfuscate observations more, but
+ * value between ]0.0, 1.0]. Smaller values obfuscate observations more, but
* at the same time make statistics less usable. */
#define ONIONS_SEEN_EPSILON 0.3
/* The number of service identities that are supposed to be hidden from
diff --git a/src/or/replaycache.c b/src/or/replaycache.c
index 569e0736cb..82e5c44d3d 100644
--- a/src/or/replaycache.c
+++ b/src/or/replaycache.c
@@ -23,7 +23,7 @@ replaycache_free(replaycache_t *r)
return;
}
- if (r->digests_seen) digestmap_free(r->digests_seen, tor_free_);
+ if (r->digests_seen) digest256map_free(r->digests_seen, tor_free_);
tor_free(r);
}
@@ -54,7 +54,7 @@ replaycache_new(time_t horizon, time_t interval)
r->scrub_interval = interval;
r->scrubbed = 0;
r->horizon = horizon;
- r->digests_seen = digestmap_new();
+ r->digests_seen = digest256map_new();
err:
return r;
@@ -69,7 +69,7 @@ replaycache_add_and_test_internal(
time_t *elapsed)
{
int rv = 0;
- char digest[DIGEST_LEN];
+ uint8_t digest[DIGEST256_LEN];
time_t *access_time;
/* sanity check */
@@ -80,10 +80,10 @@ replaycache_add_and_test_internal(
}
/* compute digest */
- crypto_digest(digest, (const char *)data, len);
+ crypto_digest256((char *)digest, (const char *)data, len, DIGEST_SHA256);
/* check map */
- access_time = digestmap_get(r->digests_seen, digest);
+ access_time = digest256map_get(r->digests_seen, digest);
/* seen before? */
if (access_time != NULL) {
@@ -114,7 +114,7 @@ replaycache_add_and_test_internal(
/* No, so no hit and update the digest map with the current time */
access_time = tor_malloc(sizeof(*access_time));
*access_time = present;
- digestmap_set(r->digests_seen, digest, access_time);
+ digest256map_set(r->digests_seen, digest, access_time);
}
/* now scrub the cache if it's time */
@@ -130,8 +130,8 @@ replaycache_add_and_test_internal(
STATIC void
replaycache_scrub_if_needed_internal(time_t present, replaycache_t *r)
{
- digestmap_iter_t *itr = NULL;
- const char *digest;
+ digest256map_iter_t *itr = NULL;
+ const uint8_t *digest;
void *valp;
time_t *access_time;
@@ -149,19 +149,19 @@ replaycache_scrub_if_needed_internal(time_t present, replaycache_t *r)
if (r->horizon == 0) return;
/* okay, scrub time */
- itr = digestmap_iter_init(r->digests_seen);
- while (!digestmap_iter_done(itr)) {
- digestmap_iter_get(itr, &digest, &valp);
+ itr = digest256map_iter_init(r->digests_seen);
+ while (!digest256map_iter_done(itr)) {
+ digest256map_iter_get(itr, &digest, &valp);
access_time = (time_t *)valp;
/* 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);
+ itr = digest256map_iter_next_rmv(r->digests_seen, itr);
/* Free the value removed */
tor_free(access_time);
} else {
/* Just advance the iterator */
- itr = digestmap_iter_next(r->digests_seen, itr);
+ itr = digest256map_iter_next(r->digests_seen, itr);
}
}
diff --git a/src/or/replaycache.h b/src/or/replaycache.h
index 9b9daf3831..9c409f2fd7 100644
--- a/src/or/replaycache.h
+++ b/src/or/replaycache.h
@@ -26,7 +26,7 @@ struct replaycache_s {
/*
* Digest map: keys are digests, values are times the digest was last seen
*/
- digestmap_t *digests_seen;
+ digest256map_t *digests_seen;
};
#endif /* REPLAYCACHE_PRIVATE */
diff --git a/src/or/router.c b/src/or/router.c
index 8fdad9a5fa..90203458b2 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -269,8 +269,8 @@ client_identity_key_is_set(void)
/** Return the key certificate for this v3 (voting) authority, or NULL
* if we have no such certificate. */
-authority_cert_t *
-get_my_v3_authority_cert(void)
+MOCK_IMPL(authority_cert_t *,
+get_my_v3_authority_cert, (void))
{
return authority_key_certificate;
}
@@ -1714,8 +1714,8 @@ router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port)
/** Return true iff my exit policy is reject *:*. Return -1 if we don't
* have a descriptor */
-int
-router_my_exit_policy_is_reject_star(void)
+MOCK_IMPL(int,
+router_my_exit_policy_is_reject_star,(void))
{
if (!router_get_my_routerinfo()) /* make sure desc_routerinfo exists */
return -1;
@@ -1922,7 +1922,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
/* DNS is screwed up; don't claim to be an exit. */
policies_exit_policy_append_reject_star(&ri->exit_policy);
} else {
- policies_parse_exit_policy_from_options(options,ri->addr,&ri->ipv6_addr,1,
+ policies_parse_exit_policy_from_options(options,ri->addr,&ri->ipv6_addr,
&ri->exit_policy);
}
ri->policy_is_reject_star =
@@ -2728,44 +2728,13 @@ router_dump_exit_policy_to_string(const routerinfo_t *router,
int include_ipv4,
int include_ipv6)
{
- smartlist_t *exit_policy_strings;
- char *policy_string = NULL;
-
if ((!router->exit_policy) || (router->policy_is_reject_star)) {
return tor_strdup("reject *:*");
}
- exit_policy_strings = smartlist_new();
-
- SMARTLIST_FOREACH_BEGIN(router->exit_policy, addr_policy_t *, tmpe) {
- char *pbuf;
- int bytes_written_to_pbuf;
- if ((tor_addr_family(&tmpe->addr) == AF_INET6) && (!include_ipv6)) {
- continue; /* Don't include IPv6 parts of address policy */
- }
- if ((tor_addr_family(&tmpe->addr) == AF_INET) && (!include_ipv4)) {
- continue; /* Don't include IPv4 parts of address policy */
- }
-
- pbuf = tor_malloc(POLICY_BUF_LEN);
- bytes_written_to_pbuf = policy_write_item(pbuf,POLICY_BUF_LEN, tmpe, 1);
-
- if (bytes_written_to_pbuf < 0) {
- log_warn(LD_BUG, "router_dump_exit_policy_to_string ran out of room!");
- tor_free(pbuf);
- goto done;
- }
-
- smartlist_add(exit_policy_strings,pbuf);
- } SMARTLIST_FOREACH_END(tmpe);
-
- policy_string = smartlist_join_strings(exit_policy_strings, "\n", 0, NULL);
-
- done:
- SMARTLIST_FOREACH(exit_policy_strings, char *, str, tor_free(str));
- smartlist_free(exit_policy_strings);
-
- return policy_string;
+ return policy_dump_to_string(router->exit_policy,
+ include_ipv4,
+ include_ipv6);
}
/** Copy the primary (IPv4) OR port (IP address and TCP port) for
diff --git a/src/or/router.h b/src/or/router.h
index d8fcf0a9ad..85f43d804d 100644
--- a/src/or/router.h
+++ b/src/or/router.h
@@ -22,7 +22,7 @@ int server_identity_key_is_set(void);
void set_client_identity_key(crypto_pk_t *k);
crypto_pk_t *get_tlsclient_identity_key(void);
int client_identity_key_is_set(void);
-authority_cert_t *get_my_v3_authority_cert(void);
+MOCK_DECL(authority_cert_t *, get_my_v3_authority_cert, (void));
crypto_pk_t *get_my_v3_authority_signing_key(void);
authority_cert_t *get_my_v3_legacy_cert(void);
crypto_pk_t *get_my_v3_legacy_signing_key(void);
@@ -80,7 +80,7 @@ void check_descriptor_ipaddress_changed(time_t now);
void router_new_address_suggestion(const char *suggestion,
const dir_connection_t *d_conn);
int router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port);
-int router_my_exit_policy_is_reject_star(void);
+MOCK_DECL(int, router_my_exit_policy_is_reject_star,(void));
MOCK_DECL(const routerinfo_t *, router_get_my_routerinfo, (void));
extrainfo_t *router_get_my_extrainfo(void);
const char *router_get_my_descriptor(void);
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index 03729bda5c..8f6a440d16 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -1363,10 +1363,10 @@ router_get_trusteddirserver_by_digest(const char *digest)
dir_server_t *
router_get_fallback_dirserver_by_digest(const char *digest)
{
- if (!trusted_dir_servers)
+ if (!fallback_dir_servers)
return NULL;
- SMARTLIST_FOREACH(trusted_dir_servers, dir_server_t *, ds,
+ SMARTLIST_FOREACH(fallback_dir_servers, dir_server_t *, ds,
{
if (tor_memeq(ds->digest, digest, DIGEST_LEN))
return ds;
@@ -5190,8 +5190,8 @@ hid_serv_acting_as_directory(void)
/** Return true if this node is responsible for storing the descriptor ID
* in <b>query</b> and false otherwise. */
-int
-hid_serv_responsible_for_desc_id(const char *query)
+MOCK_IMPL(int, hid_serv_responsible_for_desc_id,
+ (const char *query))
{
const routerinfo_t *me;
routerstatus_t *last_rs;
diff --git a/src/or/routerlist.h b/src/or/routerlist.h
index 200533fe91..100ab5848f 100644
--- a/src/or/routerlist.h
+++ b/src/or/routerlist.h
@@ -201,7 +201,7 @@ void refresh_all_country_info(void);
int hid_serv_get_responsible_directories(smartlist_t *responsible_dirs,
const char *id);
int hid_serv_acting_as_directory(void);
-int hid_serv_responsible_for_desc_id(const char *id);
+MOCK_DECL(int, hid_serv_responsible_for_desc_id, (const char *id));
void list_pending_microdesc_downloads(digest256map_t *result);
void launch_descriptor_downloads(int purpose,
diff --git a/src/or/routerset.c b/src/or/routerset.c
index 3be55d3404..debe9ec6e1 100644
--- a/src/or/routerset.c
+++ b/src/or/routerset.c
@@ -107,10 +107,12 @@ routerset_parse(routerset_t *target, const char *s, const char *description)
description);
smartlist_add(target->country_names, countryname);
added_countries = 1;
- } else if ((strchr(nick,'.') || strchr(nick, '*')) &&
- (p = router_parse_addr_policy_item_from_string(
+ } else if ((strchr(nick,'.') || strchr(nick, ':') || strchr(nick, '*'))
+ && (p = router_parse_addr_policy_item_from_string(
nick, ADDR_POLICY_REJECT,
&malformed_list))) {
+ /* IPv4 addresses contain '.', IPv6 addresses contain ':',
+ * and wildcard addresses contain '*'. */
log_debug(LD_CONFIG, "Adding address %s to %s", nick, description);
smartlist_add(target->policies, p);
} else if (malformed_list) {
diff --git a/src/or/statefile.c b/src/or/statefile.c
index dd1894beb7..7481cd71cb 100644
--- a/src/or/statefile.c
+++ b/src/or/statefile.c
@@ -372,6 +372,19 @@ or_state_load(void)
new_state = or_state_new();
} else if (contents) {
log_info(LD_GENERAL, "Loaded state from \"%s\"", fname);
+ /* Warn the user if their clock has been set backwards,
+ * they could be tricked into using old consensuses */
+ if (new_state->LastWritten > time(NULL)) {
+ char last_written_str[ISO_TIME_LEN+1];
+ char now_str[ISO_TIME_LEN+1];
+ format_iso_time(last_written_str, new_state->LastWritten),
+ format_iso_time(now_str, time(NULL));
+ log_warn(LD_GENERAL, "Your system clock has been set back in time. "
+ "Tor needs an accurate clock to know when the consensus "
+ "expires. You might have an empty clock battery or bad NTP "
+ "server. Clock time is %s, state file time is %s.",
+ now_str, last_written_str);
+ }
} else {
log_info(LD_GENERAL, "Initialized state");
}
diff --git a/src/test/include.am b/src/test/include.am
index a37fe23db8..d0a819fb7f 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -49,6 +49,8 @@ src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
# matters a lot there, and is quite hard to debug if you forget to do it.
src_test_test_SOURCES = \
+ src/test/log_test_helpers.c \
+ src/test/rend_test_helpers.c \
src/test/test.c \
src/test/test_accounting.c \
src/test/test_addr.c \
@@ -61,6 +63,7 @@ src_test_test_SOURCES = \
src/test/test_checkdir.c \
src/test/test_circuitlist.c \
src/test/test_circuitmux.c \
+ src/test/test_compat_libevent.c \
src/test/test_config.c \
src/test/test_containers.c \
src/test/test_controller.c \
@@ -68,6 +71,7 @@ src_test_test_SOURCES = \
src/test/test_crypto.c \
src/test/test_data.c \
src/test/test_dir.c \
+ src/test/test_dir_handle_get.c \
src/test/test_entryconn.c \
src/test/test_entrynodes.c \
src/test/test_guardfraction.c \
@@ -82,9 +86,11 @@ src_test_test_SOURCES = \
src/test/test_oom.c \
src/test/test_options.c \
src/test/test_policy.c \
+ src/test/test_procmon.c \
src/test/test_pt.c \
src/test/test_relay.c \
src/test/test_relaycell.c \
+ src/test/test_rendcache.c \
src/test/test_replay.c \
src/test/test_routerkeys.c \
src/test/test_routerlist.c \
@@ -93,9 +99,12 @@ src_test_test_SOURCES = \
src/test/test_socks.c \
src/test/test_status.c \
src/test/test_threads.c \
+ src/test/test_tortls.c \
src/test/test_util.c \
+ src/test/test_util_format.c \
+ src/test/test_util_process.c \
src/test/test_helpers.c \
- src/test/test_dns.c \
+ src/test/test_dns.c \
src/test/testing_common.c \
src/ext/tinytest.c
@@ -161,13 +170,16 @@ src_test_test_workqueue_LDADD = src/or/libtor-testing.a \
noinst_HEADERS+= \
src/test/fakechans.h \
+ src/test/log_test_helpers.h \
+ src/test/rend_test_helpers.h \
src/test/test.h \
src/test/test_helpers.h \
src/test/test_descriptors.inc \
src/test/example_extrainfo.inc \
src/test/failing_routerdescs.inc \
src/test/ed25519_vectors.inc \
- src/test/test_descriptors.inc
+ src/test/test_descriptors.inc \
+ src/test/vote_descriptors.inc
noinst_PROGRAMS+= src/test/test-ntor-cl
src_test_test_ntor_cl_SOURCES = src/test/test_ntor_cl.c
diff --git a/src/test/log_test_helpers.c b/src/test/log_test_helpers.c
new file mode 100644
index 0000000000..51b5f9b7b1
--- /dev/null
+++ b/src/test/log_test_helpers.c
@@ -0,0 +1,111 @@
+/* Copyright (c) 2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+#define LOG_PRIVATE
+#include "torlog.h"
+#include "log_test_helpers.h"
+
+static smartlist_t *saved_logs = NULL;
+
+int
+setup_capture_of_logs(int new_level)
+{
+ int previous_log = log_global_min_severity_;
+ log_global_min_severity_ = new_level;
+ mock_clean_saved_logs();
+ MOCK(logv, mock_saving_logv);
+ return previous_log;
+}
+
+void
+teardown_capture_of_logs(int prev)
+{
+ UNMOCK(logv);
+ log_global_min_severity_ = prev;
+ mock_clean_saved_logs();
+}
+
+void
+mock_clean_saved_logs(void)
+{
+ if (!saved_logs)
+ return;
+ SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m,
+ { tor_free(m->generated_msg); tor_free(m); });
+ smartlist_free(saved_logs);
+ saved_logs = NULL;
+}
+
+static mock_saved_log_entry_t *
+mock_get_log_entry(int ix)
+{
+ int saved_log_count = mock_saved_log_number();
+ if (ix < 0) {
+ ix = saved_log_count + ix;
+ }
+
+ if (saved_log_count <= ix)
+ return NULL;
+
+ return smartlist_get(saved_logs, ix);
+}
+
+const char *
+mock_saved_log_at(int ix)
+{
+ mock_saved_log_entry_t *ent = mock_get_log_entry(ix);
+ if (ent)
+ return ent->generated_msg;
+ else
+ return "";
+}
+
+int
+mock_saved_severity_at(int ix)
+{
+ mock_saved_log_entry_t *ent = mock_get_log_entry(ix);
+ if (ent)
+ return ent->severity;
+ else
+ return -1;
+}
+
+int
+mock_saved_log_number(void)
+{
+ if (!saved_logs)
+ return 0;
+ return smartlist_len(saved_logs);
+}
+
+const smartlist_t *
+mock_saved_logs(void)
+{
+ return saved_logs;
+}
+
+void
+mock_saving_logv(int severity, log_domain_mask_t domain,
+ const char *funcname, const char *suffix,
+ const char *format, va_list ap)
+{
+ (void)domain;
+ char *buf = tor_malloc_zero(10240);
+ int n;
+ n = tor_vsnprintf(buf,10240,format,ap);
+ tor_assert(n < 10240-1);
+ buf[n]='\n';
+ buf[n+1]='\0';
+
+ mock_saved_log_entry_t *e = tor_malloc_zero(sizeof(mock_saved_log_entry_t));
+ e->severity = severity;
+ e->funcname = funcname;
+ e->suffix = suffix;
+ e->format = format;
+ e->generated_msg = tor_strdup(buf);
+ tor_free(buf);
+
+ if (!saved_logs)
+ saved_logs = smartlist_new();
+ smartlist_add(saved_logs, e);
+}
+
diff --git a/src/test/log_test_helpers.h b/src/test/log_test_helpers.h
new file mode 100644
index 0000000000..af8e8a60e7
--- /dev/null
+++ b/src/test/log_test_helpers.h
@@ -0,0 +1,31 @@
+/* Copyright (c) 2014-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+
+#ifndef TOR_LOG_TEST_HELPERS_H
+#define TOR_LOG_TEST_HELPERS_H
+
+typedef struct mock_saved_log_entry_t {
+ int severity;
+ const char *funcname;
+ const char *suffix;
+ const char *format;
+ char *generated_msg;
+ struct mock_saved_log_entry_t *next;
+} mock_saved_log_entry_t;
+
+void mock_saving_logv(int severity, log_domain_mask_t domain,
+ const char *funcname, const char *suffix,
+ const char *format, va_list ap)
+ CHECK_PRINTF(5, 0);
+void mock_clean_saved_logs(void);
+const smartlist_t *mock_saved_logs(void);
+int setup_capture_of_logs(int new_level);
+void teardown_capture_of_logs(int prev);
+const char *mock_saved_log_at(int ix);
+int mock_saved_severity_at(int ix);
+int mock_saved_log_number(void);
+
+#endif
+
diff --git a/src/test/rend_test_helpers.c b/src/test/rend_test_helpers.c
new file mode 100644
index 0000000000..f16d67fa1a
--- /dev/null
+++ b/src/test/rend_test_helpers.c
@@ -0,0 +1,73 @@
+/* Copyright (c) 2014-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+#include "test.h"
+#include "rendcommon.h"
+#include "rend_test_helpers.h"
+
+void
+generate_desc(int time_diff, rend_encoded_v2_service_descriptor_t **desc,
+ char **service_id, int intro_points)
+{
+ rend_service_descriptor_t *generated = NULL;
+ smartlist_t *descs = smartlist_new();
+ time_t now;
+
+ now = time(NULL) + time_diff;
+ create_descriptor(&generated, service_id, intro_points);
+ generated->timestamp = now;
+
+ rend_encode_v2_descriptors(descs, generated, now, 0, REND_NO_AUTH, NULL,
+ NULL);
+ tor_assert(smartlist_len(descs) > 1);
+ *desc = smartlist_get(descs, 0);
+ smartlist_set(descs, 0, NULL);
+
+ SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
+ rend_encoded_v2_service_descriptor_free(d));
+ smartlist_free(descs);
+ rend_service_descriptor_free(generated);
+}
+
+void
+create_descriptor(rend_service_descriptor_t **generated, char **service_id,
+ int intro_points)
+{
+ crypto_pk_t *pk1 = NULL;
+ crypto_pk_t *pk2 = NULL;
+ int i;
+
+ *service_id = tor_malloc(REND_SERVICE_ID_LEN_BASE32+1);
+ pk1 = pk_generate(0);
+ pk2 = pk_generate(1);
+
+ *generated = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ (*generated)->pk = crypto_pk_dup_key(pk1);
+ rend_get_service_id((*generated)->pk, *service_id);
+
+ (*generated)->version = 2;
+ (*generated)->protocols = 42;
+ (*generated)->intro_nodes = smartlist_new();
+
+ for (i = 0; i < intro_points; i++) {
+ rend_intro_point_t *intro = tor_malloc_zero(sizeof(rend_intro_point_t));
+ crypto_pk_t *okey = pk_generate(2 + i);
+ intro->extend_info = tor_malloc_zero(sizeof(extend_info_t));
+ intro->extend_info->onion_key = okey;
+ crypto_pk_get_digest(intro->extend_info->onion_key,
+ intro->extend_info->identity_digest);
+ intro->extend_info->nickname[0] = '$';
+ base16_encode(intro->extend_info->nickname + 1,
+ sizeof(intro->extend_info->nickname) - 1,
+ intro->extend_info->identity_digest, DIGEST_LEN);
+ tor_addr_from_ipv4h(&intro->extend_info->addr, crypto_rand_int(65536));
+ intro->extend_info->port = 1 + crypto_rand_int(65535);
+ intro->intro_key = crypto_pk_dup_key(pk2);
+ smartlist_add((*generated)->intro_nodes, intro);
+ }
+
+ crypto_pk_free(pk1);
+ crypto_pk_free(pk2);
+}
+
diff --git a/src/test/rend_test_helpers.h b/src/test/rend_test_helpers.h
new file mode 100644
index 0000000000..1ef03747d7
--- /dev/null
+++ b/src/test/rend_test_helpers.h
@@ -0,0 +1,15 @@
+/* Copyright (c) 2014-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+
+#ifndef TOR_REND_TEST_HELPERS_H
+#define TOR_REND_TEST_HELPERS_H
+
+void generate_desc(int time_diff, rend_encoded_v2_service_descriptor_t **desc,
+ char **service_id, int intro_points);
+void create_descriptor(rend_service_descriptor_t **generated,
+ char **service_id, int intro_points);
+
+#endif
+
diff --git a/src/test/test.c b/src/test/test.c
index e10e260266..1c4c2921db 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -28,6 +28,7 @@
#define ROUTER_PRIVATE
#define CIRCUITSTATS_PRIVATE
#define CIRCUITLIST_PRIVATE
+#define MAIN_PRIVATE
#define STATEFILE_PRIVATE
/*
@@ -47,8 +48,10 @@ double fabs(double x);
#include "connection_edge.h"
#include "geoip.h"
#include "rendcommon.h"
+#include "rendcache.h"
#include "test.h"
#include "torgzip.h"
+#include "main.h"
#include "memarea.h"
#include "onion.h"
#include "onion_ntor.h"
@@ -316,6 +319,13 @@ test_circuit_timeout(void *arg)
int i, runs;
double close_ms;
(void)arg;
+ tor_libevent_cfg cfg;
+
+ memset(&cfg, 0, sizeof(cfg));
+
+ tor_libevent_initialize(&cfg);
+ initialize_periodic_events();
+
circuit_build_times_init(&initial);
circuit_build_times_init(&estimate);
circuit_build_times_init(&final);
@@ -455,6 +465,7 @@ test_circuit_timeout(void *arg)
circuit_build_times_free_timeouts(&estimate);
circuit_build_times_free_timeouts(&final);
or_state_free(state);
+ teardown_periodic_events();
}
/** Test encoding and parsing of rendezvous service descriptors. */
@@ -494,6 +505,9 @@ test_rend_fns(void *arg)
tt_str_op(address6,OP_EQ, "abcdefghijklmnop");
tt_assert(BAD_HOSTNAME == parse_extended_hostname(address7));
+ /* Initialize the service cache. */
+ rend_cache_init();
+
pk1 = pk_generate(0);
pk2 = pk_generate(1);
generated = tor_malloc_zero(sizeof(rend_service_descriptor_t));
@@ -1105,8 +1119,8 @@ static struct testcase_t test_array[] = {
{ "bad_onion_handshake", test_bad_onion_handshake, 0, NULL, NULL },
ENT(onion_queues),
{ "ntor_handshake", test_ntor_handshake, 0, NULL, NULL },
- ENT(circuit_timeout),
- ENT(rend_fns),
+ FORK(circuit_timeout),
+ FORK(rend_fns),
ENT(geoip),
FORK(geoip_with_pt),
FORK(stats),
@@ -1125,12 +1139,14 @@ extern struct testcase_t channeltls_tests[];
extern struct testcase_t checkdir_tests[];
extern struct testcase_t circuitlist_tests[];
extern struct testcase_t circuitmux_tests[];
+extern struct testcase_t compat_libevent_tests[];
extern struct testcase_t config_tests[];
extern struct testcase_t container_tests[];
extern struct testcase_t controller_tests[];
extern struct testcase_t controller_event_tests[];
extern struct testcase_t crypto_tests[];
extern struct testcase_t dir_tests[];
+extern struct testcase_t dir_handle_get_tests[];
extern struct testcase_t entryconn_tests[];
extern struct testcase_t entrynodes_tests[];
extern struct testcase_t guardfraction_tests[];
@@ -1145,9 +1161,11 @@ extern struct testcase_t nodelist_tests[];
extern struct testcase_t oom_tests[];
extern struct testcase_t options_tests[];
extern struct testcase_t policy_tests[];
+extern struct testcase_t procmon_tests[];
extern struct testcase_t pt_tests[];
extern struct testcase_t relay_tests[];
extern struct testcase_t relaycell_tests[];
+extern struct testcase_t rend_cache_tests[];
extern struct testcase_t replaycache_tests[];
extern struct testcase_t router_tests[];
extern struct testcase_t routerkeys_tests[];
@@ -1157,7 +1175,10 @@ extern struct testcase_t scheduler_tests[];
extern struct testcase_t socks_tests[];
extern struct testcase_t status_tests[];
extern struct testcase_t thread_tests[];
+extern struct testcase_t tortls_tests[];
extern struct testcase_t util_tests[];
+extern struct testcase_t util_format_tests[];
+extern struct testcase_t util_process_tests[];
extern struct testcase_t dns_tests[];
struct testgroup_t testgroups[] = {
@@ -1173,12 +1194,14 @@ struct testgroup_t testgroups[] = {
{ "checkdir/", checkdir_tests },
{ "circuitlist/", circuitlist_tests },
{ "circuitmux/", circuitmux_tests },
+ { "compat/libevent/", compat_libevent_tests },
{ "config/", config_tests },
{ "container/", container_tests },
{ "control/", controller_tests },
{ "control/event/", controller_event_tests },
{ "crypto/", crypto_tests },
{ "dir/", dir_tests },
+ { "dir_handle_get/", dir_handle_get_tests },
{ "dir/md/", microdesc_tests },
{ "entryconn/", entryconn_tests },
{ "entrynodes/", entrynodes_tests },
@@ -1192,9 +1215,11 @@ struct testgroup_t testgroups[] = {
{ "oom/", oom_tests },
{ "options/", options_tests },
{ "policy/" , policy_tests },
+ { "procmon/", procmon_tests },
{ "pt/", pt_tests },
{ "relay/" , relay_tests },
{ "relaycell/", relaycell_tests },
+ { "rend_cache/", rend_cache_tests },
{ "replaycache/", replaycache_tests },
{ "routerkeys/", routerkeys_tests },
{ "routerlist/", routerlist_tests },
@@ -1202,8 +1227,11 @@ struct testgroup_t testgroups[] = {
{ "scheduler/", scheduler_tests },
{ "socks/", socks_tests },
{ "status/" , status_tests },
+ { "tortls/", tortls_tests },
{ "util/", util_tests },
+ { "util/format/", util_format_tests },
{ "util/logging/", logging_tests },
+ { "util/process/", util_process_tests },
{ "util/thread/", thread_tests },
{ "dns/", dns_tests },
END_OF_GROUPS
diff --git a/src/test/test_address.c b/src/test/test_address.c
index 3e73c3e27b..9f3d81c92c 100644
--- a/src/test/test_address.c
+++ b/src/test/test_address.c
@@ -119,6 +119,21 @@ smartlist_contains_internal_tor_addr(smartlist_t *smartlist)
}
/** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
+ * that is NULL or the null tor_addr_t. Otherwise, return 0.
+ */
+static int
+smartlist_contains_null_tor_addr(smartlist_t *smartlist)
+{
+ SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) {
+ if (tor_addr == NULL || tor_addr_is_null(tor_addr)) {
+ return 1;
+ }
+ } SMARTLIST_FOREACH_END(tor_addr);
+
+ return 0;
+}
+
+/** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
* that is an IPv4 address. Otherwise, return 0.
*/
static int
@@ -268,8 +283,18 @@ test_address_get_if_addrs_ifaddrs(void *arg)
results = get_interface_addresses_ifaddrs(LOG_ERR);
- tt_int_op(smartlist_len(results),>=,1);
- tt_assert(smartlist_contains_localhost_tor_addr(results));
+ tt_assert(results);
+ /* Some FreeBSD jails don't have localhost IP address. Instead, they only
+ * have the address assigned to the jail (whatever that may be).
+ * And a jail without a network connection might not have any addresses at
+ * all. */
+ tt_assert(!smartlist_contains_null_tor_addr(results));
+
+ /* If there are addresses, they must be IPv4 or IPv6 */
+ if (smartlist_len(results) > 0) {
+ tt_assert(smartlist_contains_ipv4_tor_addr(results)
+ || smartlist_contains_ipv6_tor_addr(results));
+ }
done:
SMARTLIST_FOREACH(results, tor_addr_t *, t, tor_free(t));
@@ -293,6 +318,13 @@ test_address_get_if_addrs_win32(void *arg)
tt_int_op(smartlist_len(results),>=,1);
tt_assert(smartlist_contains_localhost_tor_addr(results));
+ tt_assert(!smartlist_contains_null_tor_addr(results));
+
+ /* If there are addresses, they must be IPv4 or IPv6 */
+ if (smartlist_len(results) > 0) {
+ tt_assert(smartlist_contains_ipv4_tor_addr(results)
+ || smartlist_contains_ipv6_tor_addr(results));
+ }
done:
SMARTLIST_FOREACH(results, tor_addr_t *, t, tor_free(t));
@@ -481,10 +513,24 @@ test_address_get_if_addrs_ioctl(void *arg)
result = get_interface_addresses_ioctl(LOG_ERR);
+ /* On an IPv6-only system, this will fail and return NULL
tt_assert(result);
- tt_int_op(smartlist_len(result),>=,1);
+ */
- tt_assert(smartlist_contains_localhost_tor_addr(result));
+ /* Some FreeBSD jails don't have localhost IP address. Instead, they only
+ * have the address assigned to the jail (whatever that may be).
+ * And a jail without a network connection might not have any addresses at
+ * all. */
+ if (result) {
+ tt_assert(!smartlist_contains_null_tor_addr(result));
+
+ /* If there are addresses, they must be IPv4 or IPv6.
+ * (AIX supports IPv6 from SIOCGIFCONF.) */
+ if (smartlist_len(result) > 0) {
+ tt_assert(smartlist_contains_ipv4_tor_addr(result)
+ || smartlist_contains_ipv6_tor_addr(result));
+ }
+ }
done:
if (result) {
@@ -696,12 +742,13 @@ test_address_get_if_addrs_list_internal(void *arg)
tt_assert(!smartlist_contains_localhost_tor_addr(results));
tt_assert(!smartlist_contains_multicast_tor_addr(results));
/* The list may or may not contain internal addresses */
+ tt_assert(!smartlist_contains_null_tor_addr(results));
- /* Allow unit tests to pass on IPv6-only machines */
+ /* if there are any addresses, they must be IPv4 */
if (smartlist_len(results) > 0) {
- tt_assert(smartlist_contains_ipv4_tor_addr(results)
- || smartlist_contains_ipv6_tor_addr(results));
+ tt_assert(smartlist_contains_ipv4_tor_addr(results));
}
+ tt_assert(!smartlist_contains_ipv6_tor_addr(results));
done:
free_interface_address_list(results);
@@ -724,6 +771,7 @@ test_address_get_if_addrs_list_no_internal(void *arg)
tt_assert(!smartlist_contains_localhost_tor_addr(results));
tt_assert(!smartlist_contains_multicast_tor_addr(results));
tt_assert(!smartlist_contains_internal_tor_addr(results));
+ tt_assert(!smartlist_contains_null_tor_addr(results));
/* if there are any addresses, they must be IPv4 */
if (smartlist_len(results) > 0) {
@@ -752,6 +800,7 @@ test_address_get_if_addrs6_list_internal(void *arg)
tt_assert(!smartlist_contains_localhost_tor_addr(results));
tt_assert(!smartlist_contains_multicast_tor_addr(results));
/* The list may or may not contain internal addresses */
+ tt_assert(!smartlist_contains_null_tor_addr(results));
/* if there are any addresses, they must be IPv6 */
tt_assert(!smartlist_contains_ipv4_tor_addr(results));
@@ -780,7 +829,9 @@ test_address_get_if_addrs6_list_no_internal(void *arg)
tt_assert(!smartlist_contains_localhost_tor_addr(results));
tt_assert(!smartlist_contains_multicast_tor_addr(results));
tt_assert(!smartlist_contains_internal_tor_addr(results));
+ tt_assert(!smartlist_contains_null_tor_addr(results));
+ /* if there are any addresses, they must be IPv6 */
tt_assert(!smartlist_contains_ipv4_tor_addr(results));
if (smartlist_len(results) > 0) {
tt_assert(smartlist_contains_ipv6_tor_addr(results));
@@ -848,7 +899,7 @@ test_address_get_if_addrs_internal_fail(void *arg)
rv = get_interface_address(LOG_ERR, &ipv4h_addr);
tt_assert(rv == -1);
-done:
+ done:
UNMOCK(get_interface_addresses_raw);
UNMOCK(get_interface_address6_via_udp_socket_hack);
free_interface_address6_list(results1);
@@ -876,7 +927,7 @@ test_address_get_if_addrs_no_internal_fail(void *arg)
tt_assert(results2 != NULL);
tt_int_op(smartlist_len(results2),==,0);
-done:
+ done:
UNMOCK(get_interface_addresses_raw);
UNMOCK(get_interface_address6_via_udp_socket_hack);
free_interface_address6_list(results1);
@@ -935,6 +986,118 @@ test_address_get_if_addrs6(void *arg)
return;
}
+static void
+test_address_tor_addr_to_in6(void *ignored)
+{
+ (void)ignored;
+ tor_addr_t *a = tor_malloc_zero(sizeof(tor_addr_t));
+ const struct in6_addr *res;
+ uint8_t expected[16] = {42, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15};
+
+ a->family = AF_INET;
+ res = tor_addr_to_in6(a);
+ tt_assert(!res);
+
+ a->family = AF_INET6;
+ memcpy(a->addr.in6_addr.s6_addr, expected, 16);
+ res = tor_addr_to_in6(a);
+ tt_assert(res);
+ tt_mem_op(res->s6_addr, OP_EQ, expected, 16);
+
+ done:
+ tor_free(a);
+}
+
+static void
+test_address_tor_addr_to_in(void *ignored)
+{
+ (void)ignored;
+ tor_addr_t *a = tor_malloc_zero(sizeof(tor_addr_t));
+ const struct in_addr *res;
+
+ a->family = AF_INET6;
+ res = tor_addr_to_in(a);
+ tt_assert(!res);
+
+ a->family = AF_INET;
+ a->addr.in_addr.s_addr = 44;
+ res = tor_addr_to_in(a);
+ tt_assert(res);
+ tt_int_op(res->s_addr, OP_EQ, 44);
+
+ done:
+ tor_free(a);
+}
+
+static void
+test_address_tor_addr_to_ipv4n(void *ignored)
+{
+ (void)ignored;
+ tor_addr_t *a = tor_malloc_zero(sizeof(tor_addr_t));
+ uint32_t res;
+
+ a->family = AF_INET6;
+ res = tor_addr_to_ipv4n(a);
+ tt_assert(!res);
+
+ a->family = AF_INET;
+ a->addr.in_addr.s_addr = 43;
+ res = tor_addr_to_ipv4n(a);
+ tt_assert(res);
+ tt_int_op(res, OP_EQ, 43);
+
+ done:
+ tor_free(a);
+}
+
+static void
+test_address_tor_addr_to_mapped_ipv4h(void *ignored)
+{
+ (void)ignored;
+ tor_addr_t *a = tor_malloc_zero(sizeof(tor_addr_t));
+ uint32_t res;
+ uint8_t toset[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 42};
+
+ a->family = AF_INET;
+ res = tor_addr_to_mapped_ipv4h(a);
+ tt_assert(!res);
+
+ a->family = AF_INET6;
+
+ memcpy(a->addr.in6_addr.s6_addr, toset, 16);
+ res = tor_addr_to_mapped_ipv4h(a);
+ tt_assert(res);
+ tt_int_op(res, OP_EQ, 42);
+
+ done:
+ tor_free(a);
+}
+
+static void
+test_address_tor_addr_eq_ipv4h(void *ignored)
+{
+ (void)ignored;
+ tor_addr_t *a = tor_malloc_zero(sizeof(tor_addr_t));
+ int res;
+
+ a->family = AF_INET6;
+ res = tor_addr_eq_ipv4h(a, 42);
+ tt_assert(!res);
+
+ a->family = AF_INET;
+ a->addr.in_addr.s_addr = 52;
+ res = tor_addr_eq_ipv4h(a, 42);
+ tt_assert(!res);
+
+ a->addr.in_addr.s_addr = 52;
+ res = tor_addr_eq_ipv4h(a, ntohl(52));
+ tt_assert(res);
+
+ done:
+ tor_free(a);
+}
+
#define ADDRESS_TEST(name, flags) \
{ #name, test_address_ ## name, flags, NULL, NULL }
@@ -961,6 +1124,11 @@ struct testcase_t address_tests[] = {
ADDRESS_TEST(get_if_addrs_ioctl, TT_FORK),
ADDRESS_TEST(ifreq_to_smartlist, 0),
#endif
+ ADDRESS_TEST(tor_addr_to_in6, 0),
+ ADDRESS_TEST(tor_addr_to_in, 0),
+ ADDRESS_TEST(tor_addr_to_ipv4n, 0),
+ ADDRESS_TEST(tor_addr_to_mapped_ipv4h, 0),
+ ADDRESS_TEST(tor_addr_eq_ipv4h, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_compat_libevent.c b/src/test/test_compat_libevent.c
new file mode 100644
index 0000000000..96502df308
--- /dev/null
+++ b/src/test/test_compat_libevent.c
@@ -0,0 +1,238 @@
+/* Copyright (c) 2010-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define COMPAT_LIBEVENT_PRIVATE
+#include "orconfig.h"
+#include "or.h"
+
+#include "test.h"
+
+#include "compat_libevent.h"
+
+#ifdef HAVE_EVENT2_EVENT_H
+#include <event2/event.h>
+#include <event2/thread.h>
+#ifdef USE_BUFFEREVENTS
+#include <event2/bufferevent.h>
+#endif
+#else
+#include <event.h>
+#endif
+
+#include "log_test_helpers.h"
+
+#define NS_MODULE compat_libevent
+
+static void
+test_compat_libevent_logging_callback(void *ignored)
+{
+ (void)ignored;
+ int previous_log = setup_capture_of_logs(LOG_DEBUG);
+
+ libevent_logging_callback(_EVENT_LOG_DEBUG, "hello world");
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_str_op(mock_saved_log_at(0), OP_EQ,
+ "Message from libevent: hello world\n");
+ tt_int_op(mock_saved_severity_at(0), OP_EQ, LOG_DEBUG);
+
+ mock_clean_saved_logs();
+ libevent_logging_callback(_EVENT_LOG_MSG, "hello world another time");
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_str_op(mock_saved_log_at(0), OP_EQ,
+ "Message from libevent: hello world another time\n");
+ tt_int_op(mock_saved_severity_at(0), OP_EQ, LOG_INFO);
+
+ mock_clean_saved_logs();
+ libevent_logging_callback(_EVENT_LOG_WARN, "hello world a third time");
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_str_op(mock_saved_log_at(0), OP_EQ,
+ "Warning from libevent: hello world a third time\n");
+ tt_int_op(mock_saved_severity_at(0), OP_EQ, LOG_WARN);
+
+ mock_clean_saved_logs();
+ libevent_logging_callback(_EVENT_LOG_ERR, "hello world a fourth time");
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_str_op(mock_saved_log_at(0), OP_EQ,
+ "Error from libevent: hello world a fourth time\n");
+ tt_int_op(mock_saved_severity_at(0), OP_EQ, LOG_ERR);
+
+ mock_clean_saved_logs();
+ libevent_logging_callback(42, "hello world a fifth time");
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_str_op(mock_saved_log_at(0), OP_EQ,
+ "Message [42] from libevent: hello world a fifth time\n");
+ tt_int_op(mock_saved_severity_at(0), OP_EQ, LOG_WARN);
+
+ mock_clean_saved_logs();
+ libevent_logging_callback(_EVENT_LOG_DEBUG,
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ );
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_str_op(mock_saved_log_at(0), OP_EQ, "Message from libevent: "
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789\n");
+ tt_int_op(mock_saved_severity_at(0), OP_EQ, LOG_DEBUG);
+
+ mock_clean_saved_logs();
+ libevent_logging_callback(42, "xxx\n");
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_str_op(mock_saved_log_at(0), OP_EQ, "Message [42] from libevent: xxx\n");
+ tt_int_op(mock_saved_severity_at(0), OP_EQ, LOG_WARN);
+
+ suppress_libevent_log_msg("something");
+ mock_clean_saved_logs();
+ libevent_logging_callback(_EVENT_LOG_MSG, "hello there");
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_str_op(mock_saved_log_at(0), OP_EQ,
+ "Message from libevent: hello there\n");
+ tt_int_op(mock_saved_severity_at(0), OP_EQ, LOG_INFO);
+
+ mock_clean_saved_logs();
+ libevent_logging_callback(_EVENT_LOG_MSG, "hello there something else");
+ tt_int_op(mock_saved_log_number(), OP_EQ, 0);
+
+ // No way of verifying the result of this, it seems =/
+ configure_libevent_logging();
+
+ done:
+ suppress_libevent_log_msg(NULL);
+ teardown_capture_of_logs(previous_log);
+}
+
+static void
+test_compat_libevent_le_versions_compatibility(void *ignored)
+{
+ (void)ignored;
+ int res;
+
+ res = le_versions_compatibility(LE_OTHER);
+ tt_int_op(res, OP_EQ, 0);
+
+ res = le_versions_compatibility(V_OLD(0,9,'c'));
+ tt_int_op(res, OP_EQ, 1);
+
+ res = le_versions_compatibility(V(1,3,98));
+ tt_int_op(res, OP_EQ, 2);
+
+ res = le_versions_compatibility(V(1,4,98));
+ tt_int_op(res, OP_EQ, 3);
+
+ res = le_versions_compatibility(V(1,5,0));
+ tt_int_op(res, OP_EQ, 4);
+
+ res = le_versions_compatibility(V(2,0,0));
+ tt_int_op(res, OP_EQ, 4);
+
+ res = le_versions_compatibility(V(2,0,2));
+ tt_int_op(res, OP_EQ, 5);
+
+ done:
+ (void)0;
+}
+
+static void
+test_compat_libevent_tor_decode_libevent_version(void *ignored)
+{
+ (void)ignored;
+ le_version_t res;
+
+ res = tor_decode_libevent_version("SOMETHING WRONG");
+ tt_int_op(res, OP_EQ, LE_OTHER);
+
+ res = tor_decode_libevent_version("1.4.11");
+ tt_int_op(res, OP_EQ, V(1,4,11));
+
+ res = tor_decode_libevent_version("1.4.12b-stable");
+ tt_int_op(res, OP_EQ, V(1,4,12));
+
+ res = tor_decode_libevent_version("1.4.17b_stable");
+ tt_int_op(res, OP_EQ, V(1,4,17));
+
+ res = tor_decode_libevent_version("1.4.12!stable");
+ tt_int_op(res, OP_EQ, LE_OTHER);
+
+ res = tor_decode_libevent_version("1.4.12b!stable");
+ tt_int_op(res, OP_EQ, LE_OTHER);
+
+ res = tor_decode_libevent_version("1.4.13-");
+ tt_int_op(res, OP_EQ, V(1,4,13));
+
+ res = tor_decode_libevent_version("1.4.14_");
+ tt_int_op(res, OP_EQ, V(1,4,14));
+
+ res = tor_decode_libevent_version("1.4.15c-");
+ tt_int_op(res, OP_EQ, V(1,4,15));
+
+ res = tor_decode_libevent_version("1.4.16c_");
+ tt_int_op(res, OP_EQ, V(1,4,16));
+
+ res = tor_decode_libevent_version("1.4.17-s");
+ tt_int_op(res, OP_EQ, V(1,4,17));
+
+ res = tor_decode_libevent_version("1.5");
+ tt_int_op(res, OP_EQ, V(1,5,0));
+
+ res = tor_decode_libevent_version("1.2");
+ tt_int_op(res, OP_EQ, V(1,2,0));
+
+ res = tor_decode_libevent_version("1.2-");
+ tt_int_op(res, OP_EQ, LE_OTHER);
+
+ res = tor_decode_libevent_version("1.6e");
+ tt_int_op(res, OP_EQ, V_OLD(1,6,'e'));
+
+ done:
+ (void)0;
+}
+
+#if defined(LIBEVENT_VERSION)
+#define HEADER_VERSION LIBEVENT_VERSION
+#elif defined(_EVENT_VERSION)
+#define HEADER_VERSION _EVENT_VERSION
+#endif
+
+static void
+test_compat_libevent_header_version(void *ignored)
+{
+ (void)ignored;
+ const char *res;
+
+ res = tor_libevent_get_header_version_str();
+ tt_str_op(res, OP_EQ, HEADER_VERSION);
+
+ done:
+ (void)0;
+}
+
+struct testcase_t compat_libevent_tests[] = {
+ { "logging_callback", test_compat_libevent_logging_callback,
+ TT_FORK, NULL, NULL },
+ { "le_versions_compatibility",
+ test_compat_libevent_le_versions_compatibility, 0, NULL, NULL },
+ { "tor_decode_libevent_version",
+ test_compat_libevent_tor_decode_libevent_version, 0, NULL, NULL },
+ { "header_version", test_compat_libevent_header_version, 0, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c
index dbaec61ee9..fcce2c52cb 100644
--- a/src/test/test_crypto.c
+++ b/src/test/test_crypto.c
@@ -302,6 +302,13 @@ test_crypto_sha(void *arg)
"96177A9CB410FF61F20015AD");
tt_int_op(i, OP_EQ, 0);
+ /* Test SHA-512 with a test vector from the specification. */
+ i = crypto_digest512(data, "abc", 3, DIGEST_SHA512);
+ test_memeq_hex(data, "ddaf35a193617abacc417349ae20413112e6fa4e89a97"
+ "ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3"
+ "feebbd454d4423643ce80e2a9ac94fa54ca49f");
+ tt_int_op(i, OP_EQ, 0);
+
/* Test HMAC-SHA256 with test cases from wikipedia and RFC 4231 */
/* Case empty (wikipedia) */
@@ -410,6 +417,27 @@ test_crypto_sha(void *arg)
crypto_digest_get_digest(d1, d_out1, sizeof(d_out1));
crypto_digest256(d_out2, "abcdef", 6, DIGEST_SHA256);
tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST_LEN);
+ crypto_digest_free(d1);
+ crypto_digest_free(d2);
+
+ /* Incremental digest code with sha512 */
+ d1 = crypto_digest512_new(DIGEST_SHA512);
+ tt_assert(d1);
+ crypto_digest_add_bytes(d1, "abcdef", 6);
+ d2 = crypto_digest_dup(d1);
+ tt_assert(d2);
+ crypto_digest_add_bytes(d2, "ghijkl", 6);
+ crypto_digest_get_digest(d2, d_out1, sizeof(d_out1));
+ crypto_digest512(d_out2, "abcdefghijkl", 12, DIGEST_SHA512);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST_LEN);
+ crypto_digest_assign(d2, d1);
+ crypto_digest_add_bytes(d2, "mno", 3);
+ crypto_digest_get_digest(d2, d_out1, sizeof(d_out1));
+ crypto_digest512(d_out2, "abcdefmno", 9, DIGEST_SHA512);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST_LEN);
+ crypto_digest_get_digest(d1, d_out1, sizeof(d_out1));
+ crypto_digest512(d_out2, "abcdef", 6, DIGEST_SHA512);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST_LEN);
done:
if (d1)
diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c
new file mode 100644
index 0000000000..2e5a50a2f6
--- /dev/null
+++ b/src/test/test_dir_handle_get.c
@@ -0,0 +1,2549 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define RENDCOMMON_PRIVATE
+#define GEOIP_PRIVATE
+#define CONNECTION_PRIVATE
+#define CONFIG_PRIVATE
+#define RENDCACHE_PRIVATE
+
+#include "or.h"
+#include "config.h"
+#include "connection.h"
+#include "directory.h"
+#include "test.h"
+#include "connection.h"
+#include "rendcommon.h"
+#include "rendcache.h"
+#include "router.h"
+#include "routerlist.h"
+#include "rend_test_helpers.h"
+#include "microdesc.h"
+#include "test_helpers.h"
+#include "nodelist.h"
+#include "entrynodes.h"
+#include "routerparse.h"
+#include "networkstatus.h"
+#include "geoip.h"
+#include "dirserv.h"
+#include "torgzip.h"
+#include "dirvote.h"
+
+#ifdef _WIN32
+/* For mkdir() */
+#include <direct.h>
+#else
+#include <dirent.h>
+#endif
+
+#include "vote_descriptors.inc"
+
+#define NS_MODULE dir_handle_get
+
+static void
+connection_write_to_buf_mock(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);
+}
+
+#define GET(path) "GET " path " HTTP/1.0\r\n\r\n"
+#define NOT_FOUND "HTTP/1.0 404 Not found\r\n\r\n"
+#define BAD_REQUEST "HTTP/1.0 400 Bad request\r\n\r\n"
+#define SERVER_BUSY "HTTP/1.0 503 Directory busy, try again later\r\n\r\n"
+#define NOT_ENOUGH_CONSENSUS_SIGNATURES "HTTP/1.0 404 " \
+ "Consensus not signed by sufficient number of requested authorities\r\n\r\n"
+
+static tor_addr_t MOCK_TOR_ADDR;
+
+static void
+test_dir_handle_get_bad_request(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(directory_handle_command_get(conn, "", NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(header, OP_EQ, BAD_REQUEST);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_v1_command_not_found(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ // no frontpage configured
+ tt_ptr_op(get_dirportfrontpage(), OP_EQ, NULL);
+
+ /* V1 path */
+ tt_int_op(directory_handle_command_get(conn, GET("/tor/"), NULL, 0),
+ OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static const char*
+mock_get_dirportfrontpage(void)
+{
+ return "HELLO FROM FRONTPAGE";
+}
+
+static void
+test_dir_handle_get_v1_command(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0, body_len = 0;
+ const char *exp_body = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+ MOCK(get_dirportfrontpage, mock_get_dirportfrontpage);
+
+ exp_body = get_dirportfrontpage();
+ body_len = strlen(exp_body);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(directory_handle_command_get(conn, GET("/tor/"), NULL, 0),
+ OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, body_len+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/html\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 20\r\n"));
+
+ tt_int_op(body_used, OP_EQ, strlen(body));
+ tt_str_op(body, OP_EQ, exp_body);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ UNMOCK(get_dirportfrontpage);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+}
+
+static void
+test_dir_handle_get_not_found(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ /* Unrecognized path */
+ tt_int_op(directory_handle_command_get(conn, GET("/anything"), NULL, 0),
+ OP_EQ, 0);
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_robots_txt(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ tt_int_op(directory_handle_command_get(conn, GET("/tor/robots.txt"),
+ NULL, 0), OP_EQ, 0);
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, 29, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 28\r\n"));
+
+ tt_int_op(body_used, OP_EQ, strlen(body));
+ tt_str_op(body, OP_EQ, "User-agent: *\r\nDisallow: /\r\n");
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+}
+
+static void
+test_dir_handle_get_bytes_txt(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0, body_len = 0;
+ char buff[30];
+ char *exp_body = NULL;
+ (void) data;
+
+ exp_body = directory_dump_request_log();
+ body_len = strlen(exp_body);
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ tt_int_op(directory_handle_command_get(conn, GET("/tor/bytes.txt"), NULL, 0),
+ OP_EQ, 0);
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, body_len+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Pragma: no-cache\r\n"));
+
+ sprintf(buff, "Content-Length: %ld\r\n", (long) body_len);
+ tt_assert(strstr(header, buff));
+
+ tt_int_op(body_used, OP_EQ, strlen(body));
+ tt_str_op(body, OP_EQ, exp_body);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ tor_free(exp_body);
+}
+
+#define RENDEZVOUS2_GET(descid) GET("/tor/rendezvous2/" descid)
+static void
+test_dir_handle_get_rendezvous2_not_found_if_not_encrypted(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ // connection is not encrypted
+ tt_assert(!connection_dir_is_encrypted(conn))
+
+ tt_int_op(directory_handle_command_get(conn, RENDEZVOUS2_GET(), NULL, 0),
+ OP_EQ, 0);
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_rendezvous2_on_encrypted_conn_with_invalid_desc_id(
+ void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ // connection is encrypted
+ TO_CONN(conn)->linked = 1;
+ tt_assert(connection_dir_is_encrypted(conn));
+
+ tt_int_op(directory_handle_command_get(conn,
+ RENDEZVOUS2_GET("invalid-desc-id"), NULL, 0), OP_EQ, 0);
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(header, OP_EQ, BAD_REQUEST);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_rendezvous2_on_encrypted_conn_not_well_formed(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ // connection is encrypted
+ TO_CONN(conn)->linked = 1;
+ tt_assert(connection_dir_is_encrypted(conn));
+
+ //TODO: this cant be reached because rend_valid_descriptor_id() prevents this
+ //case to happen. This test is the same as
+ //test_dir_handle_get_rendezvous2_on_encrypted_conn_with_invalid_desc_id
+ //We should refactor to remove the case from the switch.
+
+ const char *req = RENDEZVOUS2_GET("1bababababababababababababababab");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(header, OP_EQ, BAD_REQUEST);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_rendezvous2_not_found(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ rend_cache_init();
+
+ // connection is encrypted
+ TO_CONN(conn)->linked = 1;
+ tt_assert(connection_dir_is_encrypted(conn));
+
+ const char *req = RENDEZVOUS2_GET("3xqunszqnaolrrfmtzgaki7mxelgvkje");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ rend_cache_free_all();
+}
+
+NS_DECL(const routerinfo_t *, router_get_my_routerinfo, (void));
+NS_DECL(int, hid_serv_responsible_for_desc_id, (const char *id));
+
+static routerinfo_t *mock_routerinfo;
+static int hid_serv_responsible_for_desc_id_response;
+
+static const routerinfo_t *
+NS(router_get_my_routerinfo)(void)
+{
+ if (!mock_routerinfo) {
+ mock_routerinfo = tor_malloc_zero(sizeof(routerinfo_t));
+ }
+
+ return mock_routerinfo;
+}
+
+static int
+NS(hid_serv_responsible_for_desc_id)(const char *id)
+{
+ (void)id;
+ return hid_serv_responsible_for_desc_id_response;
+}
+
+static void
+test_dir_handle_get_rendezvous2_on_encrypted_conn_success(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ char buff[30];
+ char req[70];
+ rend_encoded_v2_service_descriptor_t *desc_holder = NULL;
+ char *service_id = NULL;
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ size_t body_len = 0;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+ NS_MOCK(router_get_my_routerinfo);
+ NS_MOCK(hid_serv_responsible_for_desc_id);
+
+ rend_cache_init();
+ hid_serv_responsible_for_desc_id_response = 1;
+
+ /* create a valid rend service descriptor */
+ #define RECENT_TIME -10
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+
+ tt_int_op(rend_cache_store_v2_desc_as_dir(desc_holder->desc_str),
+ OP_EQ, RCS_OKAY);
+
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ // connection is encrypted
+ TO_CONN(conn)->linked = 1;
+ tt_assert(connection_dir_is_encrypted(conn));
+
+ sprintf(req, RENDEZVOUS2_GET("%s"), desc_id_base32);
+
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ body_len = strlen(desc_holder->desc_str);
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, body_len+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Pragma: no-cache\r\n"));
+ sprintf(buff, "Content-Length: %ld\r\n", (long) body_len);
+ tt_assert(strstr(header, buff));
+
+ tt_int_op(body_used, OP_EQ, strlen(body));
+ tt_str_op(body, OP_EQ, desc_holder->desc_str);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ NS_UNMOCK(router_get_my_routerinfo);
+ NS_UNMOCK(hid_serv_responsible_for_desc_id);
+ tor_free(mock_routerinfo->cache_info.signed_descriptor_body);
+ tor_free(mock_routerinfo);
+
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_cache_free_all();
+}
+
+#define MICRODESC_GET(digest) GET("/tor/micro/d/" digest)
+static void
+test_dir_handle_get_micro_d_not_found(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ #define B64_256_1 "8/Pz8/u7vz8/Pz+7vz8/Pz+7u/Pz8/P7u/Pz8/P7u78"
+ #define B64_256_2 "zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMw"
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = MICRODESC_GET(B64_256_1 "-" B64_256_2);
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static or_options_t *mock_options = NULL;
+static void
+init_mock_options(void)
+{
+ mock_options = malloc(sizeof(or_options_t));
+ memset(mock_options, 0, sizeof(or_options_t));
+ mock_options->TestingTorNetwork = 1;
+}
+
+static const or_options_t *
+mock_get_options(void)
+{
+ tor_assert(mock_options);
+ return mock_options;
+}
+
+static const char microdesc[] =
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMjlHH/daN43cSVRaHBwgUfnszzAhg98EvivJ9Qxfv51mvQUxPjQ07es\n"
+ "gV/3n8fyh3Kqr/ehi9jxkdgSRfSnmF7giaHL1SLZ29kA7KtST+pBvmTpDtHa3ykX\n"
+ "Xorc7hJvIyTZoc1HU+5XSynj3gsBE5IGK1ZRzrNS688LnuZMVp1tAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n";
+
+static void
+test_dir_handle_get_micro_d(void *data)
+{
+ dir_connection_t *conn = NULL;
+ microdesc_cache_t *mc = NULL ;
+ smartlist_t *list = NULL;
+ char digest[DIGEST256_LEN];
+ char digest_base64[128];
+ char path[80];
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* SETUP */
+ init_mock_options();
+ const char *fn = get_fname("dir_handle_datadir_test1");
+ mock_options->DataDirectory = tor_strdup(fn);
+
+#ifdef _WIN32
+ tt_int_op(0, OP_EQ, mkdir(mock_options->DataDirectory));
+#else
+ tt_int_op(0, OP_EQ, mkdir(mock_options->DataDirectory, 0700));
+#endif
+
+ /* Add microdesc to cache */
+ crypto_digest256(digest, microdesc, strlen(microdesc), DIGEST_SHA256);
+ base64_encode_nopad(digest_base64, sizeof(digest_base64),
+ (uint8_t *) digest, DIGEST256_LEN);
+
+ mc = get_microdesc_cache();
+ list = microdescs_add_to_cache(mc, microdesc, NULL, SAVED_NOWHERE, 0,
+ time(NULL), NULL);
+ tt_int_op(1, OP_EQ, smartlist_len(list));
+
+ /* Make the request */
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ sprintf(path, MICRODESC_GET("%s"), digest_base64);
+ tt_int_op(directory_handle_command_get(conn, path, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(microdesc)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+
+ tt_int_op(body_used, OP_EQ, strlen(body));
+ tt_str_op(body, OP_EQ, microdesc);
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(connection_write_to_buf_impl_);
+
+ or_options_free(mock_options); mock_options = NULL;
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ smartlist_free(list);
+ microdesc_free_all();
+}
+
+static void
+test_dir_handle_get_micro_d_server_busy(void *data)
+{
+ dir_connection_t *conn = NULL;
+ microdesc_cache_t *mc = NULL ;
+ smartlist_t *list = NULL;
+ char digest[DIGEST256_LEN];
+ char digest_base64[128];
+ char path[80];
+ char *header = NULL;
+ (void) data;
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* SETUP */
+ init_mock_options();
+ const char *fn = get_fname("dir_handle_datadir_test2");
+ mock_options->DataDirectory = tor_strdup(fn);
+
+#ifdef _WIN32
+ tt_int_op(0, OP_EQ, mkdir(mock_options->DataDirectory));
+#else
+ tt_int_op(0, OP_EQ, mkdir(mock_options->DataDirectory, 0700));
+#endif
+
+ /* Add microdesc to cache */
+ crypto_digest256(digest, microdesc, strlen(microdesc), DIGEST_SHA256);
+ base64_encode_nopad(digest_base64, sizeof(digest_base64),
+ (uint8_t *) digest, DIGEST256_LEN);
+
+ mc = get_microdesc_cache();
+ list = microdescs_add_to_cache(mc, microdesc, NULL, SAVED_NOWHERE, 0,
+ time(NULL), NULL);
+ tt_int_op(1, OP_EQ, smartlist_len(list));
+
+ //Make it busy
+ mock_options->CountPrivateBandwidth = 1;
+
+ /* Make the request */
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ sprintf(path, MICRODESC_GET("%s"), digest_base64);
+ tt_int_op(directory_handle_command_get(conn, path, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(connection_write_to_buf_impl_);
+
+ or_options_free(mock_options); mock_options = NULL;
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ smartlist_free(list);
+ microdesc_free_all();
+}
+
+#define BRIDGES_PATH "/tor/networkstatus-bridges"
+static void
+test_dir_handle_get_networkstatus_bridges_not_found_without_auth(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* SETUP */
+ init_mock_options();
+ mock_options->BridgeAuthoritativeDir = 1;
+ mock_options->BridgePassword_AuthDigest_ = tor_strdup("digest");
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ TO_CONN(conn)->linked = 1;
+
+ const char *req = GET(BRIDGES_PATH);
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(connection_write_to_buf_impl_);
+ or_options_free(mock_options); mock_options = NULL;
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_networkstatus_bridges(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* SETUP */
+ init_mock_options();
+ mock_options->BridgeAuthoritativeDir = 1;
+ mock_options->BridgePassword_AuthDigest_ = tor_malloc(DIGEST256_LEN);
+ crypto_digest256(mock_options->BridgePassword_AuthDigest_,
+ "abcdefghijklm12345", 18, DIGEST_SHA256);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ TO_CONN(conn)->linked = 1;
+
+ const char *req = "GET " BRIDGES_PATH " HTTP/1.0\r\n"
+ "Authorization: Basic abcdefghijklm12345\r\n\r\n";
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 0\r\n"));
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(connection_write_to_buf_impl_);
+ or_options_free(mock_options); mock_options = NULL;
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_networkstatus_bridges_not_found_wrong_auth(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* SETUP */
+ init_mock_options();
+ mock_options->BridgeAuthoritativeDir = 1;
+ mock_options->BridgePassword_AuthDigest_ = tor_malloc(DIGEST256_LEN);
+ crypto_digest256(mock_options->BridgePassword_AuthDigest_,
+ "abcdefghijklm12345", 18, DIGEST_SHA256);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ TO_CONN(conn)->linked = 1;
+
+ const char *req = "GET " BRIDGES_PATH " HTTP/1.0\r\n"
+ "Authorization: Basic NOTSAMEDIGEST\r\n\r\n";
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(connection_write_to_buf_impl_);
+ or_options_free(mock_options); mock_options = NULL;
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+#define SERVER_DESC_GET(id) GET("/tor/server/" id)
+static void
+test_dir_handle_get_server_descriptors_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = SERVER_DESC_GET("invalid");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+ tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_SERVER_BY_FP);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ or_options_free(mock_options); mock_options = NULL;
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_server_descriptors_all(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ /* Setup fake routerlist. */
+ helper_setup_fake_routerlist();
+
+ //TODO: change to router_get_my_extrainfo when testing "extra" path
+ NS_MOCK(router_get_my_routerinfo);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ // We are one of the routers
+ routerlist_t *our_routerlist = router_get_routerlist();
+ tt_int_op(smartlist_len(our_routerlist->routers), OP_GE, 1);
+ mock_routerinfo = smartlist_get(our_routerlist->routers, 0);
+ set_server_identity_key(mock_routerinfo->identity_pkey);
+
+ /* Treat "all" requests as if they were unencrypted */
+ mock_routerinfo->cache_info.send_unencrypted = 1;
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = SERVER_DESC_GET("all");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ //TODO: Is this a BUG?
+ //It requires strlen(signed_descriptor_len)+1 as body_len but returns a body
+ //which is smaller than that by annotation_len bytes
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used,
+ mock_routerinfo->cache_info.signed_descriptor_len+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+
+ //TODO: Is this a BUG?
+ //This is what should be expected: tt_int_op(body_used, OP_EQ, strlen(body));
+ tt_int_op(body_used, OP_EQ,
+ mock_routerinfo->cache_info.signed_descriptor_len);
+
+ tt_str_op(body, OP_EQ, mock_routerinfo->cache_info.signed_descriptor_body +
+ mock_routerinfo->cache_info.annotations_len);
+ tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE);
+
+ done:
+ NS_UNMOCK(router_get_my_routerinfo);
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+
+ routerlist_free_all();
+ nodelist_free_all();
+ entry_guards_free_all();
+}
+
+static char
+TEST_DESCRIPTOR[] =
+"@uploaded-at 2014-06-08 19:20:11\n"
+"@source \"127.0.0.1\"\n"
+"router test000a 127.0.0.1 5000 0 7000\n"
+"platform Tor 0.2.5.3-alpha-dev on Linux\n"
+"protocols Link 1 2 Circuit 1\n"
+"published 2014-06-08 19:20:11\n"
+"fingerprint C7E7 CCB8 179F 8CC3 7F5C 8A04 2B3A 180B 934B 14BA\n"
+"uptime 0\n"
+"bandwidth 1073741824 1073741824 0\n"
+"extra-info-digest 67A152A4C7686FB07664F872620635F194D76D95\n"
+"caches-extra-info\n"
+"onion-key\n"
+"-----BEGIN RSA PUBLIC KEY-----\n"
+"MIGJAoGBAOuBUIEBARMkkka/TGyaQNgUEDLP0KG7sy6KNQTNOlZHUresPr/vlVjo\n"
+"HPpLMfu9M2z18c51YX/muWwY9x4MyQooD56wI4+AqXQcJRwQfQlPn3Ay82uZViA9\n"
+"DpBajRieLlKKkl145KjArpD7F5BVsqccvjErgFYXvhhjSrx7BVLnAgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n"
+"signing-key\n"
+"-----BEGIN RSA PUBLIC KEY-----\n"
+"MIGJAoGBAN6NLnSxWQnFXxqZi5D3b0BMgV6y9NJLGjYQVP+eWtPZWgqyv4zeYsqv\n"
+"O9y6c5lvxyUxmNHfoAbe/s8f2Vf3/YaC17asAVSln4ktrr3e9iY74a9RMWHv1Gzk\n"
+"3042nMcqj3PEhRN0PoLkcOZNjjmNbaqki6qy9bWWZDNTdo+uI44dAgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n"
+"hidden-service-dir\n"
+"contact auth0@test.test\n"
+"ntor-onion-key pK4bs08ERYN591jj7ca17Rn9Q02TIEfhnjR6hSq+fhU=\n"
+"reject *:*\n"
+"router-signature\n"
+"-----BEGIN SIGNATURE-----\n"
+"rx88DuM3Y7tODlHNDDEVzKpwh3csaG1or+T4l2Xs1oq3iHHyPEtB6QTLYrC60trG\n"
+"aAPsj3DEowGfjga1b248g2dtic8Ab+0exfjMm1RHXfDam5TXXZU3A0wMyoHjqHuf\n"
+"eChGPgFNUvEc+5YtD27qEDcUjcinYztTs7/dzxBT4PE=\n"
+"-----END SIGNATURE-----\n";
+
+static void
+test_dir_handle_get_server_descriptors_authority(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ crypto_pk_t *identity_pkey = pk_generate(0);
+ (void) data;
+
+ NS_MOCK(router_get_my_routerinfo);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* init mock */
+ router_get_my_routerinfo();
+ crypto_pk_get_digest(identity_pkey,
+ mock_routerinfo->cache_info.identity_digest);
+
+ // the digest is mine (the channel is unnecrypted, so we must allow sending)
+ set_server_identity_key(identity_pkey);
+ mock_routerinfo->cache_info.send_unencrypted = 1;
+
+ /* Setup descriptor */
+ long annotation_len = strstr(TEST_DESCRIPTOR, "router ") - TEST_DESCRIPTOR;
+ mock_routerinfo->cache_info.signed_descriptor_body =
+ tor_strdup(TEST_DESCRIPTOR);
+ mock_routerinfo->cache_info.signed_descriptor_len =
+ strlen(TEST_DESCRIPTOR) - annotation_len;;
+ mock_routerinfo->cache_info.annotations_len = annotation_len;
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = SERVER_DESC_GET("authority");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ //TODO: Is this a BUG?
+ //It requires strlen(TEST_DESCRIPTOR)+1 as body_len but returns a body which
+ //is smaller than that by annotation_len bytes
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(TEST_DESCRIPTOR)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+
+ tt_int_op(body_used, OP_EQ, strlen(body));
+
+ tt_str_op(body, OP_EQ, TEST_DESCRIPTOR + annotation_len);
+ tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE);
+
+ done:
+ NS_UNMOCK(router_get_my_routerinfo);
+ UNMOCK(connection_write_to_buf_impl_);
+ tor_free(mock_routerinfo->cache_info.signed_descriptor_body);
+ tor_free(mock_routerinfo);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ crypto_pk_free(identity_pkey);
+}
+
+static void
+test_dir_handle_get_server_descriptors_fp(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ crypto_pk_t *identity_pkey = pk_generate(0);
+ (void) data;
+
+ NS_MOCK(router_get_my_routerinfo);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* init mock */
+ router_get_my_routerinfo();
+ crypto_pk_get_digest(identity_pkey,
+ mock_routerinfo->cache_info.identity_digest);
+
+ // the digest is mine (the channel is unnecrypted, so we must allow sending)
+ set_server_identity_key(identity_pkey);
+ mock_routerinfo->cache_info.send_unencrypted = 1;
+
+ /* Setup descriptor */
+ long annotation_len = strstr(TEST_DESCRIPTOR, "router ") - TEST_DESCRIPTOR;
+ mock_routerinfo->cache_info.signed_descriptor_body =
+ tor_strdup(TEST_DESCRIPTOR);
+ mock_routerinfo->cache_info.signed_descriptor_len =
+ strlen(TEST_DESCRIPTOR) - annotation_len;
+ mock_routerinfo->cache_info.annotations_len = annotation_len;
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ #define HEX1 "Fe0daff89127389bc67558691231234551193EEE"
+ #define HEX2 "Deadbeef99999991111119999911111111f00ba4"
+ const char *hex_digest = hex_str(mock_routerinfo->cache_info.identity_digest,
+ DIGEST_LEN);
+
+ char req[155];
+ sprintf(req, SERVER_DESC_GET("fp/%s+" HEX1 "+" HEX2), hex_digest);
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ //TODO: Is this a BUG?
+ //It requires strlen(TEST_DESCRIPTOR)+1 as body_len but returns a body which
+ //is smaller than that by annotation_len bytes
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(TEST_DESCRIPTOR)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+
+ tt_int_op(body_used, OP_EQ, strlen(body));
+
+ tt_str_op(body, OP_EQ, TEST_DESCRIPTOR + annotation_len);
+ tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE);
+
+ done:
+ NS_UNMOCK(router_get_my_routerinfo);
+ UNMOCK(connection_write_to_buf_impl_);
+ tor_free(mock_routerinfo->cache_info.signed_descriptor_body);
+ tor_free(mock_routerinfo);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ crypto_pk_free(identity_pkey);
+}
+
+#define HEX1 "Fe0daff89127389bc67558691231234551193EEE"
+#define HEX2 "Deadbeef99999991111119999911111111f00ba4"
+
+static void
+test_dir_handle_get_server_descriptors_d(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ crypto_pk_t *identity_pkey = pk_generate(0);
+ (void) data;
+
+ /* Setup fake routerlist. */
+ helper_setup_fake_routerlist();
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* Get one router's signed_descriptor_digest */
+ routerlist_t *our_routerlist = router_get_routerlist();
+ tt_int_op(smartlist_len(our_routerlist->routers), OP_GE, 1);
+ routerinfo_t *router = smartlist_get(our_routerlist->routers, 0);
+ const char *hex_digest = hex_str(router->cache_info.signed_descriptor_digest,
+ DIGEST_LEN);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ char req_header[155];
+ sprintf(req_header, SERVER_DESC_GET("d/%s+" HEX1 "+" HEX2), hex_digest);
+ tt_int_op(directory_handle_command_get(conn, req_header, NULL, 0), OP_EQ, 0);
+
+ //TODO: Is this a BUG?
+ //It requires strlen(signed_descriptor_len)+1 as body_len but returns a body
+ //which is smaller than that by annotation_len bytes
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used,
+ router->cache_info.signed_descriptor_len+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+
+ //TODO: Is this a BUG?
+ //This is what should be expected:
+ //tt_int_op(body_used, OP_EQ, strlen(body));
+ tt_int_op(body_used, OP_EQ, router->cache_info.signed_descriptor_len);
+
+ tt_str_op(body, OP_EQ, router->cache_info.signed_descriptor_body +
+ router->cache_info.annotations_len);
+ tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ tor_free(mock_routerinfo);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ crypto_pk_free(identity_pkey);
+
+ routerlist_free_all();
+ nodelist_free_all();
+ entry_guards_free_all();
+}
+
+static void
+test_dir_handle_get_server_descriptors_busy(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ crypto_pk_t *identity_pkey = pk_generate(0);
+ (void) data;
+
+ /* Setup fake routerlist. */
+ helper_setup_fake_routerlist();
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ //Make it busy
+ MOCK(get_options, mock_get_options);
+ init_mock_options();
+ mock_options->CountPrivateBandwidth = 1;
+
+ /* Get one router's signed_descriptor_digest */
+ routerlist_t *our_routerlist = router_get_routerlist();
+ tt_int_op(smartlist_len(our_routerlist->routers), OP_GE, 1);
+ routerinfo_t *router = smartlist_get(our_routerlist->routers, 0);
+ const char *hex_digest = hex_str(router->cache_info.signed_descriptor_digest,
+ DIGEST_LEN);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ #define HEX1 "Fe0daff89127389bc67558691231234551193EEE"
+ #define HEX2 "Deadbeef99999991111119999911111111f00ba4"
+ char req_header[155];
+ sprintf(req_header, SERVER_DESC_GET("d/%s+" HEX1 "+" HEX2), hex_digest);
+ tt_int_op(directory_handle_command_get(conn, req_header, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+ tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE);
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(connection_write_to_buf_impl_);
+ tor_free(mock_routerinfo);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ crypto_pk_free(identity_pkey);
+
+ routerlist_free_all();
+ nodelist_free_all();
+ entry_guards_free_all();
+}
+
+static void
+test_dir_handle_get_server_keys_bad_req(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(BAD_REQUEST, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_server_keys_all_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/all");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+#define TEST_CERTIFICATE AUTHORITY_CERT_3
+#define TEST_SIGNING_KEY AUTHORITY_SIGNKEY_A_DIGEST
+extern const char AUTHORITY_CERT_3[];
+extern const char AUTHORITY_SIGNKEY_A_DIGEST[];
+
+static const char TEST_CERT_IDENT_KEY[] =
+ "D867ACF56A9D229B35C25F0090BC9867E906BE69";
+
+static void
+test_dir_handle_get_server_keys_all(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+
+ dir_server_t *ds = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ /* create a trusted ds */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, "", NULL,
+ V3_DIRINFO, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+
+ /* ds v3_identity_digest is the certificate's identity_key */
+ base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+ TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+ tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/all");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(TEST_CERTIFICATE)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 1883\r\n"));
+
+ tt_str_op(TEST_CERTIFICATE, OP_EQ, body);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+
+ clear_dir_servers();
+ routerlist_free_all();
+}
+
+static void
+test_dir_handle_get_server_keys_authority_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/authority");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static authority_cert_t * mock_cert = NULL;
+
+static authority_cert_t *
+get_my_v3_authority_cert_m(void)
+{
+ tor_assert(mock_cert);
+ return mock_cert;
+}
+
+static void
+test_dir_handle_get_server_keys_authority(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, NULL);
+
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/authority");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(TEST_CERTIFICATE)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 1883\r\n"));
+
+ tt_str_op(TEST_CERTIFICATE, OP_EQ, body);
+
+ done:
+ UNMOCK(get_my_v3_authority_cert);
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ authority_cert_free(mock_cert); mock_cert = NULL;
+}
+
+static void
+test_dir_handle_get_server_keys_fp_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/fp/somehex");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_server_keys_fp(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ dir_server_t *ds = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ /* create a trusted ds */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, "", NULL,
+ V3_DIRINFO, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+
+ /* ds v3_identity_digest is the certificate's identity_key */
+ base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+ TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+
+ tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ char req[71];
+ sprintf(req, GET("/tor/keys/fp/%s"), TEST_CERT_IDENT_KEY);
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(TEST_CERTIFICATE)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 1883\r\n"));
+
+ tt_str_op(TEST_CERTIFICATE, OP_EQ, body);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ clear_dir_servers();
+ routerlist_free_all();
+}
+
+static void
+test_dir_handle_get_server_keys_sk_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/sk/somehex");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_server_keys_sk(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, NULL);
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ char req[71];
+ sprintf(req, GET("/tor/keys/sk/%s"), TEST_SIGNING_KEY);
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(TEST_CERTIFICATE)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 1883\r\n"));
+
+ tt_str_op(TEST_CERTIFICATE, OP_EQ, body);
+
+ done:
+ UNMOCK(get_my_v3_authority_cert);
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ authority_cert_free(mock_cert); mock_cert = NULL;
+ tor_free(header);
+ tor_free(body);
+}
+
+static void
+test_dir_handle_get_server_keys_fpsk_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/fp-sk/somehex");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_server_keys_fpsk(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ dir_server_t *ds = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ /* create a trusted ds */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, "", NULL,
+ V3_DIRINFO, 1.0);
+ tt_assert(ds);
+
+ /* ds v3_identity_digest is the certificate's identity_key */
+ base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+ TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+ dir_server_add(ds);
+
+ tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ char req[115];
+ sprintf(req, GET("/tor/keys/fp-sk/%s-%s"),
+ TEST_CERT_IDENT_KEY, TEST_SIGNING_KEY);
+
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(TEST_CERTIFICATE)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 1883\r\n"));
+
+ tt_str_op(TEST_CERTIFICATE, OP_EQ, body);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+
+ clear_dir_servers();
+ routerlist_free_all();
+}
+
+static void
+test_dir_handle_get_server_keys_busy(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ dir_server_t *ds = NULL;
+ (void) data;
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ /* create a trusted ds */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, "", NULL,
+ V3_DIRINFO, 1.0);
+ tt_assert(ds);
+
+ /* ds v3_identity_digest is the certificate's identity_key */
+ base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+ TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+ dir_server_add(ds);
+
+ tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* setup busy server */
+ init_mock_options();
+ mock_options->CountPrivateBandwidth = 1;
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ char req[71];
+ sprintf(req, GET("/tor/keys/fp/%s"), TEST_CERT_IDENT_KEY);
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ or_options_free(mock_options); mock_options = NULL;
+
+ clear_dir_servers();
+ routerlist_free_all();
+}
+
+static networkstatus_t *mock_ns_val = NULL;
+static networkstatus_t *
+mock_ns_get_by_flavor(consensus_flavor_t f)
+{
+ (void)f;
+ return mock_ns_val;
+}
+
+static void
+test_dir_handle_get_status_vote_current_consensus_ns_not_enough_sigs(void* d)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *stats = NULL;
+ (void) d;
+
+ /* init mock */
+ mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t));
+ mock_ns_val->flavor = FLAV_NS;
+ mock_ns_val->voters = smartlist_new();
+
+ /* init mock */
+ init_mock_options();
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+ MOCK(networkstatus_get_latest_consensus_by_flavor, mock_ns_get_by_flavor);
+
+ /* start gathering stats */
+ mock_options->DirReqStatistics = 1;
+ geoip_dirreq_stats_init(time(NULL));
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/current/consensus-ns/" HEX1 "+" HEX2), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(NOT_ENOUGH_CONSENSUS_SIGNATURES, OP_EQ, header);
+
+ stats = geoip_format_dirreq_stats(time(NULL));
+ tt_assert(stats);
+ tt_assert(strstr(stats, "not-enough-sigs=8"));
+
+ done:
+ UNMOCK(networkstatus_get_latest_consensus_by_flavor);
+ UNMOCK(connection_write_to_buf_impl_);
+ UNMOCK(get_options);
+
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(stats);
+ smartlist_free(mock_ns_val->voters);
+ tor_free(mock_ns_val);
+ or_options_free(mock_options); mock_options = NULL;
+}
+
+static void
+test_dir_handle_get_status_vote_current_consensus_ns_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *stats = NULL;
+ (void) data;
+
+ init_mock_options();
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* start gathering stats */
+ mock_options->DirReqStatistics = 1;
+ geoip_dirreq_stats_init(time(NULL));
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/current/consensus-ns"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ stats = geoip_format_dirreq_stats(time(NULL));
+ tt_assert(stats);
+ tt_assert(strstr(stats, "not-found=8"));
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ UNMOCK(get_options);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(stats);
+ or_options_free(mock_options); mock_options = NULL;
+}
+
+NS_DECL(int, geoip_get_country_by_addr, (const tor_addr_t *addr));
+
+int
+NS(geoip_get_country_by_addr)(const tor_addr_t *addr)
+{
+ (void)addr;
+ CALLED(geoip_get_country_by_addr)++;
+ return 1;
+}
+
+static void
+status_vote_current_consensus_ns_test(char **header, char **body,
+ size_t *body_len)
+{
+ digests_t digests;
+ dir_connection_t *conn = NULL;
+
+ #define NETWORK_STATUS "some network status string"
+ dirserv_set_cached_consensus_networkstatus(NETWORK_STATUS, "ns", &digests,
+ time(NULL));
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ tt_assert(mock_options);
+ mock_options->DirReqStatistics = 1;
+ geoip_dirreq_stats_init(time(NULL));
+
+ /* init geoip database */
+ geoip_parse_entry("10,50,AB", AF_INET);
+ tt_str_op("ab", OP_EQ, geoip_get_country_name(1));
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ TO_CONN(conn)->address = tor_strdup("127.0.0.1");
+
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/current/consensus-ns"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, header, MAX_HEADERS_SIZE,
+ body, body_len, strlen(NETWORK_STATUS)+7, 0);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+}
+
+static void
+test_dir_handle_get_status_vote_current_consensus_ns(void* data)
+{
+ char *header = NULL;
+ char *body = NULL, *comp_body = NULL;
+ size_t body_used = 0, comp_body_used = 0;
+ char *stats = NULL, *hist = NULL;
+ (void) data;
+
+ dirserv_free_all();
+ clear_geoip_db();
+
+ NS_MOCK(geoip_get_country_by_addr);
+ MOCK(get_options, mock_get_options);
+
+ init_mock_options();
+
+ status_vote_current_consensus_ns_test(&header, &comp_body, &comp_body_used);
+ tt_assert(header);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Pragma: no-cache\r\n"));
+
+ compress_method_t compression = detect_compression_method(comp_body,
+ comp_body_used);
+ tt_int_op(ZLIB_METHOD, OP_EQ, compression);
+
+ tor_gzip_uncompress(&body, &body_used, comp_body, comp_body_used,
+ compression, 0, LOG_PROTOCOL_WARN);
+
+ tt_str_op(NETWORK_STATUS, OP_EQ, body);
+ tt_int_op(strlen(NETWORK_STATUS), OP_EQ, body_used);
+
+ stats = geoip_format_dirreq_stats(time(NULL));
+ tt_assert(stats);
+
+ tt_assert(strstr(stats, "ok=8"));
+ tt_assert(strstr(stats, "dirreq-v3-ips ab=8"));
+ tt_assert(strstr(stats, "dirreq-v3-reqs ab=8"));
+ tt_assert(strstr(stats, "dirreq-v3-direct-dl"
+ " complete=0,timeout=0,running=4"));
+
+ hist = geoip_get_request_history();
+ tt_assert(hist);
+ tt_str_op("ab=8", OP_EQ, hist);
+
+ done:
+ NS_UNMOCK(geoip_get_country_by_addr);
+ UNMOCK(get_options);
+ tor_free(header);
+ tor_free(comp_body);
+ tor_free(body);
+ tor_free(stats);
+ tor_free(hist);
+ or_options_free(mock_options); mock_options = NULL;
+
+ dirserv_free_all();
+ clear_geoip_db();
+}
+
+static void
+test_dir_handle_get_status_vote_current_consensus_ns_busy(void* data)
+{
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ char *stats = NULL;
+ (void) data;
+
+ dirserv_free_all();
+ clear_geoip_db();
+
+ MOCK(get_options, mock_get_options);
+
+ // Make it busy
+ init_mock_options();
+ mock_options->CountPrivateBandwidth = 1;
+
+ status_vote_current_consensus_ns_test(&header, &body, &body_used);
+ tt_assert(header);
+
+ tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+ stats = geoip_format_dirreq_stats(time(NULL));
+ tt_assert(stats);
+ tt_assert(strstr(stats, "busy=8"));
+
+ done:
+ UNMOCK(get_options);
+ tor_free(header);
+ tor_free(body);
+ or_options_free(mock_options); mock_options = NULL;
+
+ tor_free(stats);
+ dirserv_free_all();
+ clear_geoip_db();
+}
+
+static void
+test_dir_handle_get_status_vote_current_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/current/" HEX1), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+#define VOTE_DIGEST "312A4890D4D832597ABBD3089C782DBBFB81E48D"
+
+static void
+status_vote_current_d_test(char **header, char **body, size_t *body_l)
+{
+ dir_connection_t *conn = NULL;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/current/d/" VOTE_DIGEST), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, header, MAX_HEADERS_SIZE,
+ body, body_l, strlen(VOTE_BODY_V3)+1, 0);
+ tt_assert(header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+}
+
+static void
+status_vote_next_d_test(char **header, char **body, size_t *body_l)
+{
+ dir_connection_t *conn = NULL;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/next/d/" VOTE_DIGEST), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, header, MAX_HEADERS_SIZE,
+ body, body_l, strlen(VOTE_BODY_V3)+1, 0);
+ tt_assert(header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+}
+
+static void
+test_dir_handle_get_status_vote_current_d_not_found(void* data)
+{
+ char *header = NULL;
+ (void) data;
+
+ status_vote_current_d_test(&header, NULL, NULL);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_status_vote_next_d_not_found(void* data)
+{
+ char *header = NULL;
+ (void) data;
+
+ status_vote_next_d_test(&header, NULL, NULL);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_status_vote_d(void* data)
+{
+ char *header = NULL, *body = NULL;
+ size_t body_used = 0;
+ dir_server_t *ds = NULL;
+ (void) data;
+
+ clear_dir_servers();
+ dirvote_free_all();
+
+ /* create a trusted ds */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, "", NULL,
+ V3_DIRINFO, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+
+ /* ds v3_identity_digest is the certificate's identity_key */
+ base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+ TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+
+ init_mock_options();
+ mock_options->AuthoritativeDir = 1;
+ mock_options->V3AuthoritativeDir = 1;
+ mock_options->TestingV3AuthVotingStartOffset = 0;
+ mock_options->TestingV3AuthInitialVotingInterval = 1;
+ mock_options->TestingV3AuthInitialVoteDelay = 1;
+ mock_options->TestingV3AuthInitialDistDelay = 1;
+
+ time_t now = 1441223455 -1;
+ dirvote_recalculate_timing(mock_options, now);
+
+ const char *msg_out = NULL;
+ int status_out = 0;
+ struct pending_vote_t *pv = dirvote_add_vote(VOTE_BODY_V3, &msg_out,
+ &status_out);
+ tt_assert(pv);
+
+ status_vote_current_d_test(&header, &body, &body_used);
+
+ tt_assert(header);
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 4135\r\n"));
+
+ tt_str_op(VOTE_BODY_V3, OP_EQ, body);
+
+ tor_free(header);
+ tor_free(body);
+
+ status_vote_next_d_test(&header, &body, &body_used);
+
+ tt_assert(header);
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 4135\r\n"));
+
+ tt_str_op(VOTE_BODY_V3, OP_EQ, body);
+
+ done:
+ tor_free(header);
+ tor_free(body);
+ or_options_free(mock_options); mock_options = NULL;
+
+ clear_dir_servers();
+ dirvote_free_all();
+}
+
+static void
+test_dir_handle_get_status_vote_next_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/next/" HEX1), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+status_vote_next_consensus_test(char **header, char **body, size_t *body_used)
+{
+ dir_connection_t *conn = NULL;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/next/consensus"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, header, MAX_HEADERS_SIZE,
+ body, body_used, 18, 0);
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus_not_found(void* data)
+{
+ char *header = NULL, *body = NULL;
+ size_t body_used;
+ (void) data;
+
+ status_vote_next_consensus_test(&header, &body, &body_used);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ tor_free(header);
+ tor_free(body);
+}
+
+static void
+test_dir_handle_get_status_vote_current_authority_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/current/authority"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_status_vote_next_authority_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/next/authority"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+NS_DECL(const char*,
+dirvote_get_pending_consensus, (consensus_flavor_t flav));
+
+const char*
+NS(dirvote_get_pending_consensus)(consensus_flavor_t flav)
+{
+ (void)flav;
+ return "pending consensus";
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus(void* data)
+{
+ char *header = NULL, *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ NS_MOCK(dirvote_get_pending_consensus);
+
+ status_vote_next_consensus_test(&header, &body, &body_used);
+ tt_assert(header);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 17\r\n"));
+
+ tt_str_op("pending consensus", OP_EQ, body);
+
+ done:
+ NS_UNMOCK(dirvote_get_pending_consensus);
+ tor_free(header);
+ tor_free(body);
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus_busy(void* data)
+{
+ char *header = NULL, *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ MOCK(get_options, mock_get_options);
+ NS_MOCK(dirvote_get_pending_consensus);
+
+ //Make it busy
+ init_mock_options();
+ mock_options->CountPrivateBandwidth = 1;
+
+ status_vote_next_consensus_test(&header, &body, &body_used);
+
+ tt_assert(header);
+ tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+ done:
+ NS_UNMOCK(dirvote_get_pending_consensus);
+ UNMOCK(get_options);
+ tor_free(header);
+ tor_free(body);
+ or_options_free(mock_options); mock_options = NULL;
+}
+
+static void
+status_vote_next_consensus_signatures_test(char **header, char **body,
+ size_t *body_used)
+{
+ dir_connection_t *conn = NULL;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/next/consensus-signatures"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, header, MAX_HEADERS_SIZE,
+ body, body_used, 22, 0);
+
+ done:
+ connection_free_(TO_CONN(conn));
+ UNMOCK(connection_write_to_buf_impl_);
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus_signatures_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL, *body = NULL;
+ size_t body_used;
+ (void) data;
+
+ status_vote_next_consensus_signatures_test(&header, &body, &body_used);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+}
+
+NS_DECL(const char*,
+dirvote_get_pending_detached_signatures, (void));
+
+const char*
+NS(dirvote_get_pending_detached_signatures)(void)
+{
+ return "pending detached sigs";
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus_signatures(void* data)
+{
+ char *header = NULL, *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ NS_MOCK(dirvote_get_pending_detached_signatures);
+
+ status_vote_next_consensus_signatures_test(&header, &body, &body_used);
+ tt_assert(header);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 21\r\n"));
+
+ tt_str_op("pending detached sigs", OP_EQ, body);
+
+ done:
+ NS_UNMOCK(dirvote_get_pending_detached_signatures);
+ tor_free(header);
+ tor_free(body);
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus_signatures_busy(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL, *body = NULL;
+ size_t body_used;
+ (void) data;
+
+ NS_MOCK(dirvote_get_pending_detached_signatures);
+ MOCK(get_options, mock_get_options);
+
+ //Make it busy
+ init_mock_options();
+ mock_options->CountPrivateBandwidth = 1;
+
+ status_vote_next_consensus_signatures_test(&header, &body, &body_used);
+
+ tt_assert(header);
+ tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+ done:
+ UNMOCK(get_options);
+ NS_UNMOCK(dirvote_get_pending_detached_signatures);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ or_options_free(mock_options); mock_options = NULL;
+}
+
+static void
+test_dir_handle_get_status_vote_next_authority(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL, *body = NULL;
+ const char *msg_out = NULL;
+ int status_out = 0;
+ size_t body_used = 0;
+ dir_server_t *ds = NULL;
+ (void) data;
+
+ clear_dir_servers();
+ routerlist_free_all();
+ dirvote_free_all();
+
+ mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, NULL);
+
+ /* create a trusted ds */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, "", NULL,
+ V3_DIRINFO, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+
+ /* ds v3_identity_digest is the certificate's identity_key */
+ base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+ TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+ tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+ init_mock_options();
+ mock_options->AuthoritativeDir = 1;
+ mock_options->V3AuthoritativeDir = 1;
+ mock_options->TestingV3AuthVotingStartOffset = 0;
+ mock_options->TestingV3AuthInitialVotingInterval = 1;
+ mock_options->TestingV3AuthInitialVoteDelay = 1;
+ mock_options->TestingV3AuthInitialDistDelay = 1;
+
+ time_t now = 1441223455 -1;
+ dirvote_recalculate_timing(mock_options, now);
+
+ struct pending_vote_t *vote = dirvote_add_vote(VOTE_BODY_V3, &msg_out,
+ &status_out);
+ tt_assert(vote);
+
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/next/authority"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(VOTE_BODY_V3)+1, 0);
+
+ tt_assert(header);
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 4135\r\n"));
+
+ tt_str_op(VOTE_BODY_V3, OP_EQ, body);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ UNMOCK(get_my_v3_authority_cert);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ authority_cert_free(mock_cert); mock_cert = NULL;
+ or_options_free(mock_options); mock_options = NULL;
+
+ clear_dir_servers();
+ routerlist_free_all();
+ dirvote_free_all();
+}
+
+static void
+test_dir_handle_get_status_vote_current_authority(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL, *body = NULL;
+ const char *msg_out = NULL;
+ int status_out = 0;
+ size_t body_used = 0;
+
+ dir_server_t *ds = NULL;
+ (void) data;
+
+ clear_dir_servers();
+ routerlist_free_all();
+ dirvote_free_all();
+
+ mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, NULL);
+
+ /* create a trusted ds */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, "", NULL,
+ V3_DIRINFO, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+
+ /* ds v3_identity_digest is the certificate's identity_key */
+ base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+ TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+
+ tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+ init_mock_options();
+ mock_options->AuthoritativeDir = 1;
+ mock_options->V3AuthoritativeDir = 1;
+ mock_options->TestingV3AuthVotingStartOffset = 0;
+ mock_options->TestingV3AuthInitialVotingInterval = 1;
+ mock_options->TestingV3AuthInitialVoteDelay = 1;
+ mock_options->TestingV3AuthInitialDistDelay = 1;
+
+ time_t now = 1441223455;
+ dirvote_recalculate_timing(mock_options, now-1);
+
+ struct pending_vote_t *vote = dirvote_add_vote(VOTE_BODY_V3, &msg_out,
+ &status_out);
+ tt_assert(vote);
+
+ // move the pending vote to previous vote
+ dirvote_act(mock_options, now+1);
+
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/current/authority"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(VOTE_BODY_V3)+1, 0);
+
+ tt_assert(header);
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 4135\r\n"));
+
+ tt_str_op(VOTE_BODY_V3, OP_EQ, body);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ UNMOCK(get_my_v3_authority_cert);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ authority_cert_free(mock_cert); mock_cert = NULL;
+ or_options_free(mock_options); mock_options = NULL;
+
+ clear_dir_servers();
+ routerlist_free_all();
+ dirvote_free_all();
+}
+
+#define DIR_HANDLE_CMD(name,flags) \
+ { #name, test_dir_handle_get_##name, (flags), NULL, NULL }
+
+struct testcase_t dir_handle_get_tests[] = {
+ DIR_HANDLE_CMD(not_found, 0),
+ DIR_HANDLE_CMD(bad_request, 0),
+ DIR_HANDLE_CMD(v1_command_not_found, 0),
+ DIR_HANDLE_CMD(v1_command, 0),
+ DIR_HANDLE_CMD(robots_txt, 0),
+ DIR_HANDLE_CMD(bytes_txt, 0),
+ DIR_HANDLE_CMD(rendezvous2_not_found_if_not_encrypted, 0),
+ DIR_HANDLE_CMD(rendezvous2_not_found, 0),
+ DIR_HANDLE_CMD(rendezvous2_on_encrypted_conn_with_invalid_desc_id, 0),
+ DIR_HANDLE_CMD(rendezvous2_on_encrypted_conn_not_well_formed, 0),
+ DIR_HANDLE_CMD(rendezvous2_on_encrypted_conn_success, 0),
+ DIR_HANDLE_CMD(micro_d_not_found, 0),
+ DIR_HANDLE_CMD(micro_d_server_busy, 0),
+ DIR_HANDLE_CMD(micro_d, 0),
+ DIR_HANDLE_CMD(networkstatus_bridges_not_found_without_auth, 0),
+ DIR_HANDLE_CMD(networkstatus_bridges_not_found_wrong_auth, 0),
+ DIR_HANDLE_CMD(networkstatus_bridges, 0),
+ DIR_HANDLE_CMD(server_descriptors_not_found, 0),
+ DIR_HANDLE_CMD(server_descriptors_busy, TT_FORK),
+ DIR_HANDLE_CMD(server_descriptors_all, TT_FORK),
+ DIR_HANDLE_CMD(server_descriptors_authority, TT_FORK),
+ DIR_HANDLE_CMD(server_descriptors_fp, TT_FORK),
+ DIR_HANDLE_CMD(server_descriptors_d, TT_FORK),
+ DIR_HANDLE_CMD(server_keys_bad_req, 0),
+ DIR_HANDLE_CMD(server_keys_busy, 0),
+ DIR_HANDLE_CMD(server_keys_all_not_found, 0),
+ DIR_HANDLE_CMD(server_keys_all, 0),
+ DIR_HANDLE_CMD(server_keys_authority_not_found, 0),
+ DIR_HANDLE_CMD(server_keys_authority, 0),
+ DIR_HANDLE_CMD(server_keys_fp_not_found, 0),
+ DIR_HANDLE_CMD(server_keys_fp, 0),
+ DIR_HANDLE_CMD(server_keys_sk_not_found, 0),
+ DIR_HANDLE_CMD(server_keys_sk, 0),
+ DIR_HANDLE_CMD(server_keys_fpsk_not_found, 0),
+ DIR_HANDLE_CMD(server_keys_fpsk, 0),
+ DIR_HANDLE_CMD(status_vote_current_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_next_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_current_authority_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_current_authority, 0),
+ DIR_HANDLE_CMD(status_vote_next_authority_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_next_authority, 0),
+ DIR_HANDLE_CMD(status_vote_current_consensus_ns_not_enough_sigs, 0),
+ DIR_HANDLE_CMD(status_vote_current_consensus_ns_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_current_consensus_ns_busy, 0),
+ DIR_HANDLE_CMD(status_vote_current_consensus_ns, 0),
+ DIR_HANDLE_CMD(status_vote_current_d_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_next_d_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_d, 0),
+ DIR_HANDLE_CMD(status_vote_next_consensus_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_next_consensus_busy, 0),
+ DIR_HANDLE_CMD(status_vote_next_consensus, 0),
+ DIR_HANDLE_CMD(status_vote_next_consensus_signatures_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_next_consensus_signatures_busy, 0),
+ DIR_HANDLE_CMD(status_vote_next_consensus_signatures, 0),
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_dns.c b/src/test/test_dns.c
index ad81914ccb..b40a4825a6 100644
--- a/src/test/test_dns.c
+++ b/src/test/test_dns.c
@@ -5,9 +5,14 @@
#include "dns.h"
#include "connection.h"
+#include "router.h"
+
+#define NS_MODULE dns
+
+#define NS_SUBMODULE clip_ttl
static void
-test_dns_clip_ttl(void *arg)
+NS(test_main)(void *arg)
{
(void)arg;
@@ -21,8 +26,12 @@ test_dns_clip_ttl(void *arg)
return;
}
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE expiry_ttl
+
static void
-test_dns_expiry_ttl(void *arg)
+NS(test_main)(void *arg)
{
(void)arg;
@@ -36,6 +45,10 @@ test_dns_expiry_ttl(void *arg)
return;
}
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE resolve
+
static int resolve_retval = 0;
static int resolve_made_conn_pending = 0;
static char *resolved_name = NULL;
@@ -43,6 +56,11 @@ static cached_resolve_t *cache_entry = NULL;
static int n_fake_impl = 0;
+NS_DECL(int, dns_resolve_impl, (edge_connection_t *exitconn, int is_resolve,
+ or_circuit_t *oncirc, char **hostname_out,
+ int *made_connection_pending_out,
+ cached_resolve_t **resolve_out));
+
/** This will be our configurable substitute for <b>dns_resolve_impl</b> in
* dns.c. It will return <b>resolve_retval</b>,
* and set <b>resolve_made_conn_pending</b> to
@@ -52,10 +70,10 @@ static int n_fake_impl = 0;
* 1.
*/
static int
-dns_resolve_fake_impl(edge_connection_t *exitconn, int is_resolve,
- or_circuit_t *oncirc, char **hostname_out,
- int *made_connection_pending_out,
- cached_resolve_t **resolve_out)
+NS(dns_resolve_impl)(edge_connection_t *exitconn, int is_resolve,
+ or_circuit_t *oncirc, char **hostname_out,
+ int *made_connection_pending_out,
+ cached_resolve_t **resolve_out)
{
(void)oncirc;
(void)exitconn;
@@ -82,8 +100,8 @@ static uint8_t last_answer_type = 0;
static cached_resolve_t *last_resolved;
static void
-send_resolved_cell_replacement(edge_connection_t *conn, uint8_t answer_type,
- const cached_resolve_t *resolved)
+NS(send_resolved_cell)(edge_connection_t *conn, uint8_t answer_type,
+ const cached_resolve_t *resolved)
{
conn_for_resolved_cell = conn;
@@ -98,8 +116,8 @@ static int n_send_resolved_hostname_cell_replacement = 0;
static char *last_resolved_hostname = NULL;
static void
-send_resolved_hostname_cell_replacement(edge_connection_t *conn,
- const char *hostname)
+NS(send_resolved_hostname_cell)(edge_connection_t *conn,
+ const char *hostname)
{
conn_for_resolved_cell = conn;
@@ -112,7 +130,7 @@ send_resolved_hostname_cell_replacement(edge_connection_t *conn,
static int n_dns_cancel_pending_resolve_replacement = 0;
static void
-dns_cancel_pending_resolve_replacement(const char *address)
+NS(dns_cancel_pending_resolve)(const char *address)
{
(void) address;
n_dns_cancel_pending_resolve_replacement++;
@@ -122,7 +140,7 @@ static int n_connection_free = 0;
static connection_t *last_freed_conn = NULL;
static void
-connection_free_replacement(connection_t *conn)
+NS(connection_free)(connection_t *conn)
{
n_connection_free++;
@@ -130,7 +148,7 @@ connection_free_replacement(connection_t *conn)
}
static void
-test_dns_resolve_outer(void *arg)
+NS(test_main)(void *arg)
{
(void) arg;
int retval;
@@ -149,9 +167,9 @@ test_dns_resolve_outer(void *arg)
memset(exitconn,0,sizeof(edge_connection_t));
memset(nextconn,0,sizeof(edge_connection_t));
- MOCK(dns_resolve_impl,dns_resolve_fake_impl);
- MOCK(send_resolved_cell,send_resolved_cell_replacement);
- MOCK(send_resolved_hostname_cell,send_resolved_hostname_cell_replacement);
+ NS_MOCK(dns_resolve_impl);
+ NS_MOCK(send_resolved_cell);
+ NS_MOCK(send_resolved_hostname_cell);
/*
* CASE 1: dns_resolve_impl returns 1 and sets a hostname. purpose is
@@ -264,8 +282,8 @@ test_dns_resolve_outer(void *arg)
* on exitconn with type being RESOLVED_TYPE_ERROR.
*/
- MOCK(dns_cancel_pending_resolve,dns_cancel_pending_resolve_replacement);
- MOCK(connection_free,connection_free_replacement);
+ NS_MOCK(dns_cancel_pending_resolve);
+ NS_MOCK(connection_free);
exitconn->on_circuit = &(on_circuit->base_);
exitconn->base_.purpose = EXIT_PURPOSE_RESOLVE;
@@ -288,11 +306,11 @@ test_dns_resolve_outer(void *arg)
tt_assert(last_freed_conn == TO_CONN(exitconn));
done:
- UNMOCK(dns_resolve_impl);
- UNMOCK(send_resolved_cell);
- UNMOCK(send_resolved_hostname_cell);
- UNMOCK(dns_cancel_pending_resolve);
- UNMOCK(connection_free);
+ NS_UNMOCK(dns_resolve_impl);
+ NS_UNMOCK(send_resolved_cell);
+ NS_UNMOCK(send_resolved_hostname_cell);
+ NS_UNMOCK(dns_cancel_pending_resolve);
+ NS_UNMOCK(connection_free);
tor_free(on_circuit);
tor_free(exitconn);
tor_free(nextconn);
@@ -302,10 +320,443 @@ test_dns_resolve_outer(void *arg)
return;
}
+#undef NS_SUBMODULE
+
+/** Create an <b>edge_connection_t</b> instance that is considered a
+ * valid exit connection by asserts in dns_resolve_impl.
+ */
+static edge_connection_t *
+create_valid_exitconn(void)
+{
+ edge_connection_t *exitconn = tor_malloc_zero(sizeof(edge_connection_t));
+ TO_CONN(exitconn)->type = CONN_TYPE_EXIT;
+ TO_CONN(exitconn)->magic = EDGE_CONNECTION_MAGIC;
+ TO_CONN(exitconn)->purpose = EXIT_PURPOSE_RESOLVE;
+ TO_CONN(exitconn)->state = EXIT_CONN_STATE_RESOLVING;
+ exitconn->base_.s = TOR_INVALID_SOCKET;
+
+ return exitconn;
+}
+
+#define NS_SUBMODULE ASPECT(resolve_impl, addr_is_ip_no_need_to_resolve)
+
+/*
+ * Given that <b>exitconn->base_.address</b> is IP address string, we
+ * want dns_resolve_impl() to parse it and store in
+ * <b>exitconn->base_.addr</b>. We expect dns_resolve_impl to return 1.
+ * Lastly, we want it to set the TTL value to default one for DNS queries.
+ */
+
+static void
+NS(test_main)(void *arg)
+{
+ int retval;
+ int made_pending;
+ const tor_addr_t *resolved_addr;
+ tor_addr_t addr_to_compare;
+
+ (void)arg;
+
+ tor_addr_parse(&addr_to_compare, "8.8.8.8");
+
+ or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
+
+ edge_connection_t *exitconn = create_valid_exitconn();
+
+ TO_CONN(exitconn)->address = tor_strdup("8.8.8.8");
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ NULL);
+
+ resolved_addr = &(exitconn->base_.addr);
+
+ tt_int_op(retval,==,1);
+ tt_assert(tor_addr_eq(resolved_addr, (const tor_addr_t *)&addr_to_compare));
+ tt_int_op(exitconn->address_ttl,==,DEFAULT_DNS_TTL);
+
+ done:
+ tor_free(on_circ);
+ tor_free(TO_CONN(exitconn)->address);
+ tor_free(exitconn);
+ return;
+}
+
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE ASPECT(resolve_impl, non_exit)
+
+/** Given that Tor instance is not configured as an exit node, we want
+ * dns_resolve_impl() to fail with return value -1.
+ */
+static int
+NS(router_my_exit_policy_is_reject_star)(void)
+{
+ return 1;
+}
+
+static void
+NS(test_main)(void *arg)
+{
+ int retval;
+ int made_pending;
+
+ edge_connection_t *exitconn = create_valid_exitconn();
+ or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
+
+ (void)arg;
+
+ TO_CONN(exitconn)->address = tor_strdup("torproject.org");
+
+ NS_MOCK(router_my_exit_policy_is_reject_star);
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ NULL);
+
+ tt_int_op(retval,==,-1);
+
+ done:
+ tor_free(TO_CONN(exitconn)->address);
+ tor_free(exitconn);
+ tor_free(on_circ);
+ NS_UNMOCK(router_my_exit_policy_is_reject_star);
+ return;
+}
+
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE ASPECT(resolve_impl, addr_is_invalid_dest)
+
+/** Given that address is not a valid destination (as judged by
+ * address_is_invalid_destination() function), we want dns_resolve_impl()
+ * function to fail with return value -1.
+ */
+
+static int
+NS(router_my_exit_policy_is_reject_star)(void)
+{
+ return 0;
+}
+
+static void
+NS(test_main)(void *arg)
+{
+ int retval;
+ int made_pending;
+
+ edge_connection_t *exitconn = create_valid_exitconn();
+ or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
+
+ (void)arg;
+
+ NS_MOCK(router_my_exit_policy_is_reject_star);
+
+ TO_CONN(exitconn)->address = tor_strdup("invalid#@!.org");
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ NULL);
+
+ tt_int_op(retval,==,-1);
+
+ done:
+ NS_UNMOCK(router_my_exit_policy_is_reject_star);
+ tor_free(TO_CONN(exitconn)->address);
+ tor_free(exitconn);
+ tor_free(on_circ);
+ return;
+}
+
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE ASPECT(resolve_impl, malformed_ptr)
+
+/** Given that address is a malformed PTR name, we want dns_resolve_impl to
+ * fail.
+ */
+
+static int
+NS(router_my_exit_policy_is_reject_star)(void)
+{
+ return 0;
+}
+
+static void
+NS(test_main)(void *arg)
+{
+ int retval;
+ int made_pending;
+
+ edge_connection_t *exitconn = create_valid_exitconn();
+ or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
+
+ (void)arg;
+
+ TO_CONN(exitconn)->address = tor_strdup("127.0.0.1.in-addr.arpa");
+
+ NS_MOCK(router_my_exit_policy_is_reject_star);
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ NULL);
+
+ tt_int_op(retval,==,-1);
+
+ tor_free(TO_CONN(exitconn)->address);
+
+ TO_CONN(exitconn)->address =
+ tor_strdup("z01234567890123456789.in-addr.arpa");
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ NULL);
+
+ tt_int_op(retval,==,-1);
+
+ done:
+ NS_UNMOCK(router_my_exit_policy_is_reject_star);
+ tor_free(TO_CONN(exitconn)->address);
+ tor_free(exitconn);
+ tor_free(on_circ);
+ return;
+}
+
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE ASPECT(resolve_impl, cache_hit_pending)
+
+/* Given that there is already a pending resolve for the given address,
+ * we want dns_resolve_impl to append our exit connection to list
+ * of pending connections for the pending DNS request and return 0.
+ */
+
+static int
+NS(router_my_exit_policy_is_reject_star)(void)
+{
+ return 0;
+}
+
+static void
+NS(test_main)(void *arg)
+{
+ int retval;
+ int made_pending = 0;
+
+ pending_connection_t *pending_conn = NULL;
+
+ edge_connection_t *exitconn = create_valid_exitconn();
+ or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
+
+ cached_resolve_t *cache_entry = tor_malloc_zero(sizeof(cached_resolve_t));
+ cache_entry->magic = CACHED_RESOLVE_MAGIC;
+ cache_entry->state = CACHE_STATE_PENDING;
+ cache_entry->minheap_idx = -1;
+ cache_entry->expire = time(NULL) + 60 * 60;
+
+ (void)arg;
+
+ TO_CONN(exitconn)->address = tor_strdup("torproject.org");
+
+ strlcpy(cache_entry->address, TO_CONN(exitconn)->address,
+ sizeof(cache_entry->address));
+
+ NS_MOCK(router_my_exit_policy_is_reject_star);
+
+ dns_init();
+
+ dns_insert_cache_entry(cache_entry);
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ NULL);
+
+ tt_int_op(retval,==,0);
+ tt_int_op(made_pending,==,1);
+
+ pending_conn = cache_entry->pending_connections;
+
+ tt_assert(pending_conn != NULL);
+ tt_assert(pending_conn->conn == exitconn);
+
+ done:
+ NS_UNMOCK(router_my_exit_policy_is_reject_star);
+ tor_free(on_circ);
+ tor_free(TO_CONN(exitconn)->address);
+ tor_free(cache_entry->pending_connections);
+ tor_free(cache_entry);
+ return;
+}
+
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE ASPECT(resolve_impl, cache_hit_cached)
+
+/* Given that a finished DNS resolve is available in our cache, we want
+ * dns_resolve_impl() return it to called via resolve_out and pass the
+ * handling to set_exitconn_info_from_resolve function.
+ */
+static int
+NS(router_my_exit_policy_is_reject_star)(void)
+{
+ return 0;
+}
+
+static edge_connection_t *last_exitconn = NULL;
+static cached_resolve_t *last_resolve = NULL;
+
+static int
+NS(set_exitconn_info_from_resolve)(edge_connection_t *exitconn,
+ const cached_resolve_t *resolve,
+ char **hostname_out)
+{
+ last_exitconn = exitconn;
+ last_resolve = (cached_resolve_t *)resolve;
+
+ (void)hostname_out;
+
+ return 0;
+}
+
+static void
+NS(test_main)(void *arg)
+{
+ int retval;
+ int made_pending = 0;
+
+ edge_connection_t *exitconn = create_valid_exitconn();
+ or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
+
+ cached_resolve_t *resolve_out = NULL;
+
+ cached_resolve_t *cache_entry = tor_malloc_zero(sizeof(cached_resolve_t));
+ cache_entry->magic = CACHED_RESOLVE_MAGIC;
+ cache_entry->state = CACHE_STATE_CACHED;
+ cache_entry->minheap_idx = -1;
+ cache_entry->expire = time(NULL) + 60 * 60;
+
+ (void)arg;
+
+ TO_CONN(exitconn)->address = tor_strdup("torproject.org");
+
+ strlcpy(cache_entry->address, TO_CONN(exitconn)->address,
+ sizeof(cache_entry->address));
+
+ NS_MOCK(router_my_exit_policy_is_reject_star);
+ NS_MOCK(set_exitconn_info_from_resolve);
+
+ dns_init();
+
+ dns_insert_cache_entry(cache_entry);
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ &resolve_out);
+
+ tt_int_op(retval,==,0);
+ tt_int_op(made_pending,==,0);
+ tt_assert(resolve_out == cache_entry);
+
+ tt_assert(last_exitconn == exitconn);
+ tt_assert(last_resolve == cache_entry);
+
+ done:
+ NS_UNMOCK(router_my_exit_policy_is_reject_star);
+ NS_UNMOCK(set_exitconn_info_from_resolve);
+ tor_free(on_circ);
+ tor_free(TO_CONN(exitconn)->address);
+ tor_free(cache_entry->pending_connections);
+ tor_free(cache_entry);
+ return;
+}
+
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE ASPECT(resolve_impl, cache_miss)
+
+/* Given that there are neither pending nor pre-cached resolve for a given
+ * address, we want dns_resolve_impl() to create a new cached_resolve_t
+ * object, mark it as pending, insert it into the cache, attach the exit
+ * connection to list of pending connections and call launch_resolve()
+ * with the cached_resolve_t object it created.
+ */
+static int
+NS(router_my_exit_policy_is_reject_star)(void)
+{
+ return 0;
+}
+
+static cached_resolve_t *last_launched_resolve = NULL;
+
+static int
+NS(launch_resolve)(cached_resolve_t *resolve)
+{
+ last_launched_resolve = resolve;
+
+ return 0;
+}
+
+static void
+NS(test_main)(void *arg)
+{
+ int retval;
+ int made_pending = 0;
+
+ pending_connection_t *pending_conn = NULL;
+
+ edge_connection_t *exitconn = create_valid_exitconn();
+ or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
+
+ cached_resolve_t *cache_entry = NULL;
+ cached_resolve_t query;
+
+ (void)arg;
+
+ TO_CONN(exitconn)->address = tor_strdup("torproject.org");
+
+ strlcpy(query.address, TO_CONN(exitconn)->address, sizeof(query.address));
+
+ NS_MOCK(router_my_exit_policy_is_reject_star);
+ NS_MOCK(launch_resolve);
+
+ dns_init();
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ NULL);
+
+ tt_int_op(retval,==,0);
+ tt_int_op(made_pending,==,1);
+
+ cache_entry = dns_get_cache_entry(&query);
+
+ tt_assert(cache_entry);
+
+ pending_conn = cache_entry->pending_connections;
+
+ tt_assert(pending_conn != NULL);
+ tt_assert(pending_conn->conn == exitconn);
+
+ tt_assert(last_launched_resolve == cache_entry);
+ tt_str_op(cache_entry->address,==,TO_CONN(exitconn)->address);
+
+ done:
+ NS_UNMOCK(router_my_exit_policy_is_reject_star);
+ NS_UNMOCK(launch_resolve);
+ tor_free(on_circ);
+ tor_free(TO_CONN(exitconn)->address);
+ tor_free(cache_entry->pending_connections);
+ tor_free(cache_entry);
+ return;
+}
+
+#undef NS_SUBMODULE
+
struct testcase_t dns_tests[] = {
- { "clip_ttl", test_dns_clip_ttl, 0, NULL, NULL },
- { "expiry_ttl", test_dns_expiry_ttl, 0, NULL, NULL },
- { "resolve_outer", test_dns_resolve_outer, TT_FORK, NULL, NULL },
+ TEST_CASE(clip_ttl),
+ TEST_CASE(expiry_ttl),
+ TEST_CASE(resolve),
+ TEST_CASE_ASPECT(resolve_impl, addr_is_ip_no_need_to_resolve),
+ TEST_CASE_ASPECT(resolve_impl, non_exit),
+ TEST_CASE_ASPECT(resolve_impl, addr_is_invalid_dest),
+ TEST_CASE_ASPECT(resolve_impl, malformed_ptr),
+ TEST_CASE_ASPECT(resolve_impl, cache_hit_pending),
+ TEST_CASE_ASPECT(resolve_impl, cache_hit_cached),
+ TEST_CASE_ASPECT(resolve_impl, cache_miss),
END_OF_TESTCASES
};
+#undef NS_MODULE
+
diff --git a/src/test/test_policy.c b/src/test/test_policy.c
index 37c36fed99..082f930551 100644
--- a/src/test/test_policy.c
+++ b/src/test/test_policy.c
@@ -2,8 +2,11 @@
/* See LICENSE for licensing information */
#include "or.h"
+#define CONFIG_PRIVATE
+#include "config.h"
#include "router.h"
#include "routerparse.h"
+#define POLICIES_PRIVATE
#include "policies.h"
#include "test.h"
@@ -49,7 +52,7 @@ test_policy_summary_helper(const char *policy_str,
r = policies_parse_exit_policy(&line, &policy,
EXIT_POLICY_IPV6_ENABLED |
- EXIT_POLICY_ADD_DEFAULT, 0, NULL, 0);
+ EXIT_POLICY_ADD_DEFAULT, NULL);
tt_int_op(r,OP_EQ, 0);
summary = policy_summarize(policy, AF_INET);
@@ -80,7 +83,8 @@ test_policies_general(void *arg)
*policy7 = NULL, *policy8 = NULL, *policy9 = NULL,
*policy10 = NULL, *policy11 = NULL, *policy12 = NULL;
addr_policy_t *p;
- tor_addr_t tar;
+ tor_addr_t tar, tar2;
+ smartlist_t *addr_list = NULL;
config_line_t line;
smartlist_t *sm = NULL;
char *policy_str = NULL;
@@ -115,17 +119,22 @@ test_policies_general(void *arg)
tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy2,
EXIT_POLICY_IPV6_ENABLED |
EXIT_POLICY_REJECT_PRIVATE |
- EXIT_POLICY_ADD_DEFAULT, 0,
- NULL, 0));
+ EXIT_POLICY_ADD_DEFAULT, NULL));
tt_assert(policy2);
- tor_addr_parse(&tar, "[2000::1234]");
+ tor_addr_from_ipv4h(&tar, 0x0306090cu);
+ tor_addr_parse(&tar2, "[2000::1234]");
+ addr_list = smartlist_new();
+ smartlist_add(addr_list, &tar);
+ smartlist_add(addr_list, &tar2);
tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy12,
EXIT_POLICY_IPV6_ENABLED |
EXIT_POLICY_REJECT_PRIVATE |
EXIT_POLICY_ADD_DEFAULT,
- 0x0306090cu, &tar, 1));
+ addr_list));
+ smartlist_free(addr_list);
+ addr_list = NULL;
tt_assert(policy12);
@@ -206,15 +215,15 @@ test_policies_general(void *arg)
tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy8,
EXIT_POLICY_IPV6_ENABLED |
EXIT_POLICY_REJECT_PRIVATE |
- EXIT_POLICY_ADD_DEFAULT, 0,
- NULL, 0));
+ EXIT_POLICY_ADD_DEFAULT,
+ NULL));
tt_assert(policy8);
tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy9,
EXIT_POLICY_REJECT_PRIVATE |
- EXIT_POLICY_ADD_DEFAULT, 0,
- NULL, 0));
+ EXIT_POLICY_ADD_DEFAULT,
+ NULL));
tt_assert(policy9);
@@ -268,8 +277,7 @@ test_policies_general(void *arg)
line.next = NULL;
tt_int_op(0, OP_EQ, policies_parse_exit_policy(&line,&policy,
EXIT_POLICY_IPV6_ENABLED |
- EXIT_POLICY_ADD_DEFAULT, 0,
- NULL, 0));
+ EXIT_POLICY_ADD_DEFAULT, NULL));
tt_assert(policy);
//test_streq(policy->string, "accept *:80");
@@ -489,6 +497,324 @@ test_policies_general(void *arg)
short_policy_free(short_parsed);
}
+/** Helper: Check that policy_list contains address */
+static int
+test_policy_has_address_helper(const smartlist_t *policy_list,
+ const tor_addr_t *addr)
+{
+ int found = 0;
+
+ tt_assert(policy_list);
+ tt_assert(addr);
+
+ SMARTLIST_FOREACH_BEGIN(policy_list, addr_policy_t*, p) {
+ if (tor_addr_eq(&p->addr, addr)) {
+ found = 1;
+ }
+ } SMARTLIST_FOREACH_END(p);
+
+ return found;
+
+ done:
+ return 0;
+}
+
+#define TEST_IPV4_ADDR (0x01020304)
+#define TEST_IPV6_ADDR ("2002::abcd")
+
+/** Run unit tests for rejecting the configured addresses on this exit relay
+ * using policies_parse_exit_policy_reject_private */
+static void
+test_policies_reject_exit_address(void *arg)
+{
+ smartlist_t *policy = NULL;
+ tor_addr_t ipv4_addr, ipv6_addr;
+ smartlist_t *ipv4_list, *ipv6_list, *both_list, *dupl_list;
+ (void)arg;
+
+ tor_addr_from_ipv4h(&ipv4_addr, TEST_IPV4_ADDR);
+ tor_addr_parse(&ipv6_addr, TEST_IPV6_ADDR);
+
+ ipv4_list = smartlist_new();
+ ipv6_list = smartlist_new();
+ both_list = smartlist_new();
+ dupl_list = smartlist_new();
+
+ smartlist_add(ipv4_list, &ipv4_addr);
+ smartlist_add(both_list, &ipv4_addr);
+ smartlist_add(dupl_list, &ipv4_addr);
+ smartlist_add(dupl_list, &ipv4_addr);
+ smartlist_add(dupl_list, &ipv4_addr);
+
+ smartlist_add(ipv6_list, &ipv6_addr);
+ smartlist_add(both_list, &ipv6_addr);
+ smartlist_add(dupl_list, &ipv6_addr);
+ smartlist_add(dupl_list, &ipv6_addr);
+
+ /* IPv4-Only Exits */
+
+ /* test that IPv4 addresses are rejected on an IPv4-only exit */
+ policies_parse_exit_policy_reject_private(&policy, 0, ipv4_list, 0, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 1);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* test that IPv6 addresses are NOT rejected on an IPv4-only exit
+ * (all IPv6 addresses are rejected by policies_parse_exit_policy_internal
+ * on IPv4-only exits, so policies_parse_exit_policy_reject_private doesn't
+ * need to do anything) */
+ policies_parse_exit_policy_reject_private(&policy, 0, ipv6_list, 0, 0);
+ tt_assert(policy == NULL);
+
+ /* test that only IPv4 addresses are rejected on an IPv4-only exit */
+ policies_parse_exit_policy_reject_private(&policy, 0, both_list, 0, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 1);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* Test that lists with duplicate entries produce the same results */
+ policies_parse_exit_policy_reject_private(&policy, 0, dupl_list, 0, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 1);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* IPv4/IPv6 Exits */
+
+ /* test that IPv4 addresses are rejected on an IPv4/IPv6 exit */
+ policies_parse_exit_policy_reject_private(&policy, 1, ipv4_list, 0, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 1);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* test that IPv6 addresses are rejected on an IPv4/IPv6 exit */
+ policies_parse_exit_policy_reject_private(&policy, 1, ipv6_list, 0, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 1);
+ tt_assert(test_policy_has_address_helper(policy, &ipv6_addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* test that IPv4 and IPv6 addresses are rejected on an IPv4/IPv6 exit */
+ policies_parse_exit_policy_reject_private(&policy, 1, both_list, 0, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 2);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
+ tt_assert(test_policy_has_address_helper(policy, &ipv6_addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* Test that lists with duplicate entries produce the same results */
+ policies_parse_exit_policy_reject_private(&policy, 1, dupl_list, 0, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 2);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
+ tt_assert(test_policy_has_address_helper(policy, &ipv6_addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ done:
+ addr_policy_list_free(policy);
+ smartlist_free(ipv4_list);
+ smartlist_free(ipv6_list);
+ smartlist_free(both_list);
+ smartlist_free(dupl_list);
+}
+
+static smartlist_t *test_configured_ports = NULL;
+const smartlist_t *mock_get_configured_ports(void);
+
+/** Returns test_configured_ports */
+const smartlist_t *
+mock_get_configured_ports(void)
+{
+ return test_configured_ports;
+}
+
+/** Run unit tests for rejecting publicly routable configured port addresses
+ * on this exit relay using policies_parse_exit_policy_reject_private */
+static void
+test_policies_reject_port_address(void *arg)
+{
+ smartlist_t *policy = NULL;
+ port_cfg_t *ipv4_port = NULL;
+ port_cfg_t *ipv6_port = NULL;
+ (void)arg;
+
+ test_configured_ports = smartlist_new();
+
+ ipv4_port = port_cfg_new(0);
+ tor_addr_from_ipv4h(&ipv4_port->addr, TEST_IPV4_ADDR);
+ smartlist_add(test_configured_ports, ipv4_port);
+
+ ipv6_port = port_cfg_new(0);
+ tor_addr_parse(&ipv6_port->addr, TEST_IPV6_ADDR);
+ smartlist_add(test_configured_ports, ipv6_port);
+
+ MOCK(get_configured_ports, mock_get_configured_ports);
+
+ /* test that an IPv4 port is rejected on an IPv4-only exit, but an IPv6 port
+ * is NOT rejected (all IPv6 addresses are rejected by
+ * policies_parse_exit_policy_internal on IPv4-only exits, so
+ * policies_parse_exit_policy_reject_private doesn't need to do anything
+ * with IPv6 addresses on IPv4-only exits) */
+ policies_parse_exit_policy_reject_private(&policy, 0, NULL, 0, 1);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 1);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_port->addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* test that IPv4 and IPv6 ports are rejected on an IPv4/IPv6 exit */
+ policies_parse_exit_policy_reject_private(&policy, 1, NULL, 0, 1);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 2);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_port->addr));
+ tt_assert(test_policy_has_address_helper(policy, &ipv6_port->addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ done:
+ addr_policy_list_free(policy);
+ if (test_configured_ports) {
+ SMARTLIST_FOREACH(test_configured_ports,
+ port_cfg_t *, p, port_cfg_free(p));
+ smartlist_free(test_configured_ports);
+ test_configured_ports = NULL;
+ }
+ UNMOCK(get_configured_ports);
+}
+
+smartlist_t *mock_ipv4_addrs = NULL;
+smartlist_t *mock_ipv6_addrs = NULL;
+
+/* mock get_interface_address6_list, returning a deep copy of the template
+ * address list ipv4_interface_address_list or ipv6_interface_address_list */
+static smartlist_t *
+mock_get_interface_address6_list(int severity,
+ sa_family_t family,
+ int include_internal)
+{
+ (void)severity;
+ (void)include_internal;
+ smartlist_t *clone_list = smartlist_new();
+ smartlist_t *template_list = NULL;
+
+ if (family == AF_INET) {
+ template_list = mock_ipv4_addrs;
+ } else if (family == AF_INET6) {
+ template_list = mock_ipv6_addrs;
+ } else {
+ return NULL;
+ }
+
+ tt_assert(template_list);
+
+ SMARTLIST_FOREACH_BEGIN(template_list, tor_addr_t *, src_addr) {
+ tor_addr_t *dest_addr = malloc(sizeof(tor_addr_t));
+ memset(dest_addr, 0, sizeof(*dest_addr));
+ tor_addr_copy_tight(dest_addr, src_addr);
+ smartlist_add(clone_list, dest_addr);
+ } SMARTLIST_FOREACH_END(src_addr);
+
+ return clone_list;
+
+ done:
+ free_interface_address6_list(clone_list);
+ return NULL;
+}
+
+/** Run unit tests for rejecting publicly routable interface addresses on this
+ * exit relay using policies_parse_exit_policy_reject_private */
+static void
+test_policies_reject_interface_address(void *arg)
+{
+ smartlist_t *policy = NULL;
+ smartlist_t *public_ipv4_addrs =
+ get_interface_address6_list(LOG_INFO, AF_INET, 0);
+ smartlist_t *public_ipv6_addrs =
+ get_interface_address6_list(LOG_INFO, AF_INET6, 0);
+ tor_addr_t ipv4_addr, ipv6_addr;
+ (void)arg;
+
+ /* test that no addresses are rejected when none are supplied/requested */
+ policies_parse_exit_policy_reject_private(&policy, 0, NULL, 0, 0);
+ tt_assert(policy == NULL);
+
+ /* test that only IPv4 interface addresses are rejected on an IPv4-only exit
+ * (and allow for duplicates)
+ */
+ policies_parse_exit_policy_reject_private(&policy, 0, NULL, 1, 0);
+ if (policy) {
+ tt_assert(smartlist_len(policy) <= smartlist_len(public_ipv4_addrs));
+ addr_policy_list_free(policy);
+ policy = NULL;
+ }
+
+ /* test that IPv4 and IPv6 interface addresses are rejected on an IPv4/IPv6
+ * exit (and allow for duplicates) */
+ policies_parse_exit_policy_reject_private(&policy, 1, NULL, 1, 0);
+ if (policy) {
+ tt_assert(smartlist_len(policy) <= (smartlist_len(public_ipv4_addrs)
+ + smartlist_len(public_ipv6_addrs)));
+ addr_policy_list_free(policy);
+ policy = NULL;
+ }
+
+ /* Now do it all again, but mocked */
+ tor_addr_from_ipv4h(&ipv4_addr, TEST_IPV4_ADDR);
+ mock_ipv4_addrs = smartlist_new();
+ smartlist_add(mock_ipv4_addrs, (void *)&ipv4_addr);
+
+ tor_addr_parse(&ipv6_addr, TEST_IPV6_ADDR);
+ mock_ipv6_addrs = smartlist_new();
+ smartlist_add(mock_ipv6_addrs, (void *)&ipv6_addr);
+
+ MOCK(get_interface_address6_list, mock_get_interface_address6_list);
+
+ /* test that no addresses are rejected when none are supplied/requested */
+ policies_parse_exit_policy_reject_private(&policy, 0, NULL, 0, 0);
+ tt_assert(policy == NULL);
+
+ /* test that only IPv4 interface addresses are rejected on an IPv4-only exit
+ */
+ policies_parse_exit_policy_reject_private(&policy, 0, NULL, 1, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == smartlist_len(mock_ipv4_addrs));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* test that IPv4 and IPv6 interface addresses are rejected on an IPv4/IPv6
+ * exit */
+ policies_parse_exit_policy_reject_private(&policy, 1, NULL, 1, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == (smartlist_len(mock_ipv4_addrs)
+ + smartlist_len(mock_ipv6_addrs)));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ done:
+ addr_policy_list_free(policy);
+ free_interface_address6_list(public_ipv4_addrs);
+ free_interface_address6_list(public_ipv6_addrs);
+
+ UNMOCK(get_interface_address6_list);
+ /* we don't use free_interface_address6_list on these lists because their
+ * address pointers are stack-based */
+ smartlist_free(mock_ipv4_addrs);
+ smartlist_free(mock_ipv6_addrs);
+}
+
+#undef TEST_IPV4_ADDR
+#undef TEST_IPV6_ADDR
+
static void
test_dump_exit_policy_to_string(void *arg)
{
@@ -578,10 +904,152 @@ test_dump_exit_policy_to_string(void *arg)
tor_free(ep);
}
+static routerinfo_t *mock_desc_routerinfo = NULL;
+static const routerinfo_t *
+mock_router_get_my_routerinfo(void)
+{
+ return mock_desc_routerinfo;
+}
+
+#define DEFAULT_POLICY_STRING "reject *:*"
+#define TEST_IPV4_ADDR (0x02040608)
+#define TEST_IPV6_ADDR ("2003::ef01")
+
+static or_options_t mock_options;
+
+static const or_options_t *
+mock_get_options(void)
+{
+ return &mock_options;
+}
+
+/** Run unit tests for generating summary lines of exit policies */
+static void
+test_policies_getinfo_helper_policies(void *arg)
+{
+ (void)arg;
+ int rv = 0;
+ size_t ipv4_len = 0, ipv6_len = 0;
+ char *answer = NULL;
+ const char *errmsg = NULL;
+ routerinfo_t mock_my_routerinfo;
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/default", &answer, &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ tt_assert(strlen(answer) > 0);
+ tor_free(answer);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/default",
+ &answer, &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ tt_assert(strlen(answer) > 0);
+ tor_free(answer);
+
+ memset(&mock_my_routerinfo, 0, sizeof(routerinfo_t));
+ MOCK(router_get_my_routerinfo, mock_router_get_my_routerinfo);
+ mock_my_routerinfo.exit_policy = smartlist_new();
+ mock_desc_routerinfo = &mock_my_routerinfo;
+
+ memset(&mock_options, 0, sizeof(or_options_t));
+ MOCK(get_options, mock_get_options);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay",
+ &answer, &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ tt_assert(strlen(answer) == 0);
+ tor_free(answer);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/ipv4", &answer,
+ &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ ipv4_len = strlen(answer);
+ tt_assert(ipv4_len == 0 || ipv4_len == strlen(DEFAULT_POLICY_STRING));
+ tt_assert(ipv4_len == 0 || !strcasecmp(answer, DEFAULT_POLICY_STRING));
+ tor_free(answer);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/ipv6", &answer,
+ &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ ipv6_len = strlen(answer);
+ tt_assert(ipv6_len == 0 || ipv6_len == strlen(DEFAULT_POLICY_STRING));
+ tt_assert(ipv6_len == 0 || !strcasecmp(answer, DEFAULT_POLICY_STRING));
+ tor_free(answer);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/full", &answer,
+ &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ /* It's either empty or it's the default */
+ tt_assert(strlen(answer) == 0 || !strcasecmp(answer, DEFAULT_POLICY_STRING));
+ tor_free(answer);
+
+ mock_my_routerinfo.addr = TEST_IPV4_ADDR;
+ tor_addr_parse(&mock_my_routerinfo.ipv6_addr, TEST_IPV6_ADDR);
+ append_exit_policy_string(&mock_my_routerinfo.exit_policy, "accept *4:*");
+ append_exit_policy_string(&mock_my_routerinfo.exit_policy, "reject *6:*");
+
+ mock_options.IPv6Exit = 1;
+ mock_options.ExitPolicyRejectPrivate = 1;
+ tor_addr_from_ipv4h(&mock_options.OutboundBindAddressIPv4_, TEST_IPV4_ADDR);
+ tor_addr_parse(&mock_options.OutboundBindAddressIPv6_, TEST_IPV6_ADDR);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay",
+ &answer, &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ tt_assert(strlen(answer) > 0);
+ tor_free(answer);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/ipv4", &answer,
+ &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ ipv4_len = strlen(answer);
+ tt_assert(ipv4_len > 0);
+ tor_free(answer);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/ipv6", &answer,
+ &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ ipv6_len = strlen(answer);
+ tt_assert(ipv6_len > 0);
+ tor_free(answer);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/full", &answer,
+ &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ tt_assert(strlen(answer) > 0);
+ tt_assert(strlen(answer) == ipv4_len + ipv6_len + 1);
+ tor_free(answer);
+
+ done:
+ tor_free(answer);
+ UNMOCK(get_options);
+ UNMOCK(router_get_my_routerinfo);
+ smartlist_free(mock_my_routerinfo.exit_policy);
+}
+
+#undef DEFAULT_POLICY_STRING
+#undef TEST_IPV4_ADDR
+#undef TEST_IPV6_ADDR
+
struct testcase_t policy_tests[] = {
{ "router_dump_exit_policy_to_string", test_dump_exit_policy_to_string, 0,
NULL, NULL },
{ "general", test_policies_general, 0, NULL, NULL },
+ { "getinfo_helper_policies", test_policies_getinfo_helper_policies, 0, NULL,
+ NULL },
+ { "reject_exit_address", test_policies_reject_exit_address, 0, NULL, NULL },
+ { "reject_interface_address", test_policies_reject_interface_address, 0,
+ NULL, NULL },
+ { "reject_port_address", test_policies_reject_port_address, 0, NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_procmon.c b/src/test/test_procmon.c
new file mode 100644
index 0000000000..2855178788
--- /dev/null
+++ b/src/test/test_procmon.c
@@ -0,0 +1,58 @@
+/* Copyright (c) 2010-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define PROCMON_PRIVATE
+#include "orconfig.h"
+#include "or.h"
+#include "test.h"
+
+#include "procmon.h"
+
+#include "log_test_helpers.h"
+
+#define NS_MODULE procmon
+
+struct event_base;
+
+static void
+test_procmon_tor_process_monitor_new(void *ignored)
+{
+ (void)ignored;
+ tor_process_monitor_t *res;
+ const char *msg;
+
+ res = tor_process_monitor_new(NULL, "probably invalid", 0, NULL, NULL, &msg);
+ tt_assert(!res);
+ tt_str_op(msg, OP_EQ, "invalid PID");
+
+ res = tor_process_monitor_new(NULL, "243443535345454", 0, NULL, NULL, &msg);
+ tt_assert(!res);
+ tt_str_op(msg, OP_EQ, "invalid PID");
+
+ res = tor_process_monitor_new(tor_libevent_get_base(), "43", 0,
+ NULL, NULL, &msg);
+ tt_assert(res);
+ tt_assert(!msg);
+ tor_process_monitor_free(res);
+
+ res = tor_process_monitor_new(tor_libevent_get_base(), "44 hello", 0,
+ NULL, NULL, &msg);
+ tt_assert(res);
+ tt_assert(!msg);
+ tor_process_monitor_free(res);
+
+ res = tor_process_monitor_new(tor_libevent_get_base(), "45:hello", 0,
+ NULL, NULL, &msg);
+ tt_assert(res);
+ tt_assert(!msg);
+
+ done:
+ tor_process_monitor_free(res);
+}
+
+struct testcase_t procmon_tests[] = {
+ { "tor_process_monitor_new", test_procmon_tor_process_monitor_new,
+ TT_FORK, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_rendcache.c b/src/test/test_rendcache.c
new file mode 100644
index 0000000000..11f11147cb
--- /dev/null
+++ b/src/test/test_rendcache.c
@@ -0,0 +1,1333 @@
+/* Copyright (c) 2010-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "or.h"
+
+#include "test.h"
+#define RENDCACHE_PRIVATE
+#include "rendcache.h"
+#include "router.h"
+#include "routerlist.h"
+#include "config.h"
+#include <openssl/rsa.h>
+#include "rend_test_helpers.h"
+
+#define NS_MODULE rend_cache
+
+static const int RECENT_TIME = -10;
+static const int TIME_IN_THE_PAST = -(REND_CACHE_MAX_AGE + \
+ REND_CACHE_MAX_SKEW + 10);
+static const int TIME_IN_THE_FUTURE = REND_CACHE_MAX_SKEW + 10;
+
+extern strmap_t *rend_cache;
+extern digestmap_t *rend_cache_v2_dir;
+extern strmap_t *rend_cache_failure;
+extern size_t rend_cache_total_allocation;
+
+static rend_data_t *
+mock_rend_data(const char *onion_address)
+{
+ rend_data_t *rend_query = tor_malloc_zero(sizeof(rend_data_t));
+
+ strlcpy(rend_query->onion_address, onion_address,
+ sizeof(rend_query->onion_address));
+ rend_query->auth_type = REND_NO_AUTH;
+ rend_query->hsdirs_fp = smartlist_new();
+ smartlist_add(rend_query->hsdirs_fp, tor_memdup("aaaaaaaaaaaaaaaaaaaaaaaa",
+ DIGEST_LEN));
+
+ return rend_query;
+}
+
+static void
+test_rend_cache_lookup_entry(void *data)
+{
+ int ret;
+ rend_data_t *mock_rend_query = NULL;
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ rend_cache_entry_t *entry = NULL;
+ rend_encoded_v2_service_descriptor_t *desc_holder = NULL;
+ char *service_id = NULL;
+ (void)data;
+
+ rend_cache_init();
+
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+
+ ret = rend_cache_lookup_entry("abababababababab", 0, NULL);
+ tt_int_op(ret, OP_EQ, -ENOENT);
+
+ ret = rend_cache_lookup_entry("invalid query", 2, NULL);
+ tt_int_op(ret, OP_EQ, -EINVAL);
+
+ ret = rend_cache_lookup_entry("abababababababab", 2, NULL);
+ tt_int_op(ret, OP_EQ, -ENOENT);
+
+ ret = rend_cache_lookup_entry("abababababababab", 4224, NULL);
+ tt_int_op(ret, OP_EQ, -ENOENT);
+
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+ rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32,
+ mock_rend_query, NULL);
+
+ ret = rend_cache_lookup_entry(service_id, 2, NULL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ ret = rend_cache_lookup_entry(service_id, 2, &entry);
+ tt_assert(entry);
+ tt_int_op(entry->len, OP_EQ, strlen(desc_holder->desc_str));
+ tt_str_op(entry->desc, OP_EQ, desc_holder->desc_str);
+
+ done:
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_cache_free_all();
+ rend_data_free(mock_rend_query);
+}
+
+static void
+test_rend_cache_store_v2_desc_as_client(void *data)
+{
+ rend_cache_store_status_t ret;
+ rend_data_t *mock_rend_query;
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ rend_cache_entry_t *entry = NULL;
+ rend_encoded_v2_service_descriptor_t *desc_holder = NULL;
+ char *service_id = NULL;
+ char client_cookie[REND_DESC_COOKIE_LEN];
+ (void)data;
+
+ rend_cache_init();
+
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+
+ // Test success
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ &entry);
+
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+ tt_assert(entry);
+ tt_int_op(entry->len, OP_EQ, strlen(desc_holder->desc_str));
+ tt_str_op(entry->desc, OP_EQ, desc_holder->desc_str);
+
+ // Test various failure modes
+
+ // TODO: a too long desc_id_base32 argument crashes the function
+ /* ret = rend_cache_store_v2_desc_as_client( */
+ /* desc_holder->desc_str, */
+ /* "3TOOLONG3TOOLONG3TOOLONG3TOOLONG3TOOLONG3TOOLONG", */
+ /* &mock_rend_query, NULL); */
+ /* tt_int_op(ret, OP_EQ, RCS_BADDESC); */
+
+ // Test bad base32 failure
+ // This causes an assertion failure if we're running with assertions.
+ // But when doing coverage, we can test it.
+#ifdef TOR_COVERAGE
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ "!xqunszqnaolrrfmtzgaki7mxelgvkj", mock_rend_query, NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+#endif
+
+ // Test invalid descriptor
+ ret = rend_cache_store_v2_desc_as_client("invalid descriptor",
+ "3xqunszqnaolrrfmtzgaki7mxelgvkje", mock_rend_query, NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+
+ // TODO: it doesn't seem to be possible to test invalid service ID condition.
+ // that means it is likely not possible to have that condition without
+ // earlier conditions failing first (such as signature checking of the desc)
+
+ rend_cache_free_all();
+
+ // Test mismatch between service ID and onion address
+ rend_cache_init();
+ strncpy(mock_rend_query->onion_address, "abc", REND_SERVICE_ID_LEN_BASE32+1);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32,
+ mock_rend_query, NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+ rend_cache_free_all();
+ rend_data_free(mock_rend_query);
+
+ // Test incorrect descriptor ID
+ rend_cache_init();
+ mock_rend_query = mock_rend_data(service_id);
+ desc_id_base32[0]++;
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+ desc_id_base32[0]--;
+ rend_cache_free_all();
+
+ // Test too old descriptor
+ rend_cache_init();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_data_free(mock_rend_query);
+
+ generate_desc(TIME_IN_THE_PAST, &desc_holder, &service_id, 3);
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32,
+ mock_rend_query, NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+ rend_cache_free_all();
+
+ // Test too new descriptor (in the future)
+ rend_cache_init();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_data_free(mock_rend_query);
+
+ generate_desc(TIME_IN_THE_FUTURE, &desc_holder, &service_id, 3);
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+ rend_cache_free_all();
+
+ // Test when a descriptor is already in the cache
+ rend_cache_init();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_data_free(mock_rend_query);
+
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+
+ rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32,
+ mock_rend_query, NULL);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ &entry);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+ tt_assert(entry);
+ rend_cache_free_all();
+
+ // Test unsuccessful decrypting of introduction points
+ rend_cache_init();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_data_free(mock_rend_query);
+
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+ mock_rend_query = mock_rend_data(service_id);
+ mock_rend_query->auth_type = REND_BASIC_AUTH;
+ client_cookie[0] = 'A';
+ memcpy(mock_rend_query->descriptor_cookie, client_cookie,
+ REND_DESC_COOKIE_LEN);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+ rend_cache_free_all();
+
+ // Test successful run when we have REND_BASIC_AUTH but not cookie
+ rend_cache_init();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_data_free(mock_rend_query);
+
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+ mock_rend_query = mock_rend_data(service_id);
+ mock_rend_query->auth_type = REND_BASIC_AUTH;
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ rend_cache_free_all();
+
+ // Test when we have no introduction points
+ rend_cache_init();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_data_free(mock_rend_query);
+
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 0);
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+ rend_cache_free_all();
+
+ // Test when we have too many intro points
+ rend_cache_init();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_data_free(mock_rend_query);
+
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, MAX_INTRO_POINTS+1);
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+
+ done:
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_cache_free_all();
+ rend_data_free(mock_rend_query);
+}
+
+static void
+test_rend_cache_store_v2_desc_as_client_with_different_time(void *data)
+{
+ rend_cache_store_status_t ret;
+ rend_data_t *mock_rend_query;
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ rend_service_descriptor_t *generated = NULL;
+ smartlist_t *descs = smartlist_new();
+ time_t t;
+ char *service_id = NULL;
+ rend_encoded_v2_service_descriptor_t *desc_holder_newer;
+ rend_encoded_v2_service_descriptor_t *desc_holder_older;
+
+ t = time(NULL);
+ rend_cache_init();
+
+ create_descriptor(&generated, &service_id, 3);
+
+ generated->timestamp = t + RECENT_TIME;
+ rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0,
+ REND_NO_AUTH, NULL, NULL);
+ desc_holder_newer = ((rend_encoded_v2_service_descriptor_t *)
+ smartlist_get(descs, 0));
+ smartlist_set(descs, 0, NULL);
+
+ SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
+ rend_encoded_v2_service_descriptor_free(d));
+ smartlist_free(descs);
+ descs = smartlist_new();
+
+ generated->timestamp = (t + RECENT_TIME) - 20;
+ rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0,
+ REND_NO_AUTH, NULL, NULL);
+ desc_holder_older = ((rend_encoded_v2_service_descriptor_t *)
+ smartlist_get(descs, 0));
+ smartlist_set(descs, 0, NULL);
+ (void)data;
+
+ // Test when a descriptor is already in the cache and it is newer than the
+ // one we submit
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32),
+ desc_holder_newer->desc_id, DIGEST_LEN);
+ rend_cache_store_v2_desc_as_client(desc_holder_newer->desc_str,
+ desc_id_base32, mock_rend_query, NULL);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder_older->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ rend_cache_free_all();
+
+ // Test when an old descriptor is in the cache and we submit a newer one
+ rend_cache_init();
+ rend_cache_store_v2_desc_as_client(desc_holder_older->desc_str,
+ desc_id_base32, mock_rend_query, NULL);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder_newer->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ done:
+ rend_encoded_v2_service_descriptor_free(desc_holder_newer);
+ rend_encoded_v2_service_descriptor_free(desc_holder_older);
+ SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
+ rend_encoded_v2_service_descriptor_free(d));
+ smartlist_free(descs);
+ rend_service_descriptor_free(generated);
+ tor_free(service_id);
+ rend_cache_free_all();
+ rend_data_free(mock_rend_query);
+}
+
+#define NS_SUBMODULE lookup_v2_desc_as_dir
+NS_DECL(const routerinfo_t *, router_get_my_routerinfo, (void));
+NS_DECL(int, hid_serv_responsible_for_desc_id, (const char *id));
+
+static routerinfo_t *mock_routerinfo;
+static int hid_serv_responsible_for_desc_id_response;
+
+static const routerinfo_t *
+NS(router_get_my_routerinfo)(void)
+{
+ if (!mock_routerinfo) {
+ mock_routerinfo = tor_malloc(sizeof(routerinfo_t));
+ }
+
+ return mock_routerinfo;
+}
+
+static int
+NS(hid_serv_responsible_for_desc_id)(const char *id)
+{
+ (void)id;
+ return hid_serv_responsible_for_desc_id_response;
+}
+
+static void
+test_rend_cache_lookup_v2_desc_as_dir(void *data)
+{
+ int ret;
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ rend_encoded_v2_service_descriptor_t *desc_holder = NULL;
+ char *service_id = NULL;
+ const char *ret_desc = NULL;
+
+ (void)data;
+
+ NS_MOCK(router_get_my_routerinfo);
+ NS_MOCK(hid_serv_responsible_for_desc_id);
+
+ rend_cache_init();
+
+ // Test invalid base32
+ ret = rend_cache_lookup_v2_desc_as_dir("!bababababababab", NULL);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test non-existent descriptor but well formed
+ ret = rend_cache_lookup_v2_desc_as_dir("3xqunszqnaolrrfmtzgaki7mxelgvkje",
+ NULL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test existing descriptor
+ hid_serv_responsible_for_desc_id_response = 1;
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+ rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+ ret = rend_cache_lookup_v2_desc_as_dir(desc_id_base32, &ret_desc);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_assert(ret_desc);
+
+ done:
+ NS_UNMOCK(router_get_my_routerinfo);
+ NS_UNMOCK(hid_serv_responsible_for_desc_id);
+ tor_free(mock_routerinfo);
+ rend_cache_free_all();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+}
+
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE store_v2_desc_as_dir
+NS_DECL(const routerinfo_t *, router_get_my_routerinfo, (void));
+NS_DECL(int, hid_serv_responsible_for_desc_id, (const char *id));
+
+static const routerinfo_t *
+NS(router_get_my_routerinfo)(void)
+{
+ return mock_routerinfo;
+}
+
+static int
+NS(hid_serv_responsible_for_desc_id)(const char *id)
+{
+ (void)id;
+ return hid_serv_responsible_for_desc_id_response;
+}
+
+static void
+test_rend_cache_store_v2_desc_as_dir(void *data)
+{
+ (void)data;
+ rend_cache_store_status_t ret;
+ rend_encoded_v2_service_descriptor_t *desc_holder = NULL;
+ char *service_id = NULL;
+
+ NS_MOCK(router_get_my_routerinfo);
+ NS_MOCK(hid_serv_responsible_for_desc_id);
+
+ rend_cache_init();
+
+ // Test when we are not an HS dir
+ mock_routerinfo = NULL;
+ ret = rend_cache_store_v2_desc_as_dir("");
+ tt_int_op(ret, OP_EQ, RCS_NOTDIR);
+
+ // Test when we can't parse the descriptor
+ mock_routerinfo = tor_malloc(sizeof(routerinfo_t));
+ hid_serv_responsible_for_desc_id_response = 1;
+ ret = rend_cache_store_v2_desc_as_dir("unparseable");
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+
+ // Test when we are not responsible for an HS
+ hid_serv_responsible_for_desc_id_response = 0;
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+
+ // Test when we have an old descriptor
+ hid_serv_responsible_for_desc_id_response = 1;
+ generate_desc(TIME_IN_THE_PAST, &desc_holder, &service_id, 3);
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+
+ // Test when we have a descriptor in the future
+ generate_desc(TIME_IN_THE_FUTURE, &desc_holder, &service_id, 3);
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+
+ // Test when two descriptors
+ generate_desc(TIME_IN_THE_FUTURE, &desc_holder, &service_id, 3);
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+
+ // Test when asking for hidden service statistics HiddenServiceStatistics
+ rend_cache_purge();
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+ get_options_mutable()->HiddenServiceStatistics = 1;
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ done:
+ NS_UNMOCK(router_get_my_routerinfo);
+ NS_UNMOCK(hid_serv_responsible_for_desc_id);
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_cache_free_all();
+ tor_free(mock_routerinfo);
+}
+
+static void
+test_rend_cache_store_v2_desc_as_dir_with_different_time(void *data)
+{
+ (void)data;
+
+ rend_cache_store_status_t ret;
+ rend_service_descriptor_t *generated = NULL;
+ smartlist_t *descs = smartlist_new();
+ time_t t;
+ char *service_id = NULL;
+ rend_encoded_v2_service_descriptor_t *desc_holder_newer;
+ rend_encoded_v2_service_descriptor_t *desc_holder_older;
+
+ NS_MOCK(router_get_my_routerinfo);
+ NS_MOCK(hid_serv_responsible_for_desc_id);
+
+ rend_cache_init();
+
+ t = time(NULL);
+
+ create_descriptor(&generated, &service_id, 3);
+ generated->timestamp = t + RECENT_TIME;
+ rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0,
+ REND_NO_AUTH, NULL, NULL);
+ desc_holder_newer = ((rend_encoded_v2_service_descriptor_t *)
+ smartlist_get(descs, 0));
+ smartlist_set(descs, 0, NULL);
+ SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
+ rend_encoded_v2_service_descriptor_free(d));
+ smartlist_free(descs);
+ descs = smartlist_new();
+
+ generated->timestamp = (t + RECENT_TIME) - 20;
+ rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0,
+ REND_NO_AUTH, NULL, NULL);
+ desc_holder_older = ((rend_encoded_v2_service_descriptor_t *)
+ smartlist_get(descs, 0));
+ smartlist_set(descs, 0, NULL);
+
+ // Test when we have a newer descriptor stored
+ mock_routerinfo = tor_malloc(sizeof(routerinfo_t));
+ hid_serv_responsible_for_desc_id_response = 1;
+ rend_cache_store_v2_desc_as_dir(desc_holder_newer->desc_str);
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder_older->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ // Test when we have an old descriptor stored
+ rend_cache_purge();
+ rend_cache_store_v2_desc_as_dir(desc_holder_older->desc_str);
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder_newer->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ done:
+ NS_UNMOCK(router_get_my_routerinfo);
+ NS_UNMOCK(hid_serv_responsible_for_desc_id);
+ rend_cache_free_all();
+ rend_service_descriptor_free(generated);
+ tor_free(service_id);
+ SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
+ rend_encoded_v2_service_descriptor_free(d));
+ smartlist_free(descs);
+ rend_encoded_v2_service_descriptor_free(desc_holder_newer);
+ rend_encoded_v2_service_descriptor_free(desc_holder_older);
+ tor_free(mock_routerinfo);
+}
+
+static void
+test_rend_cache_store_v2_desc_as_dir_with_different_content(void *data)
+{
+ (void)data;
+
+ rend_cache_store_status_t ret;
+ rend_service_descriptor_t *generated = NULL;
+ smartlist_t *descs = smartlist_new();
+ time_t t;
+ char *service_id = NULL;
+ rend_encoded_v2_service_descriptor_t *desc_holder_one = NULL;
+ rend_encoded_v2_service_descriptor_t *desc_holder_two = NULL;
+
+ NS_MOCK(router_get_my_routerinfo);
+ NS_MOCK(hid_serv_responsible_for_desc_id);
+
+ rend_cache_init();
+
+ t = time(NULL);
+
+ create_descriptor(&generated, &service_id, 3);
+ generated->timestamp = t + RECENT_TIME;
+ rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0,
+ REND_NO_AUTH, NULL, NULL);
+ desc_holder_one = ((rend_encoded_v2_service_descriptor_t *)
+ smartlist_get(descs, 0));
+ smartlist_set(descs, 0, NULL);
+
+ SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
+ rend_encoded_v2_service_descriptor_free(d));
+ smartlist_free(descs);
+ descs = smartlist_new();
+
+ generated->timestamp = t + RECENT_TIME;
+ generated->protocols = 41;
+ rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0,
+ REND_NO_AUTH, NULL, NULL);
+ desc_holder_two = ((rend_encoded_v2_service_descriptor_t *)
+ smartlist_get(descs, 0));
+ smartlist_set(descs, 0, NULL);
+
+ // Test when we have another descriptor stored, with a different descriptor
+ mock_routerinfo = tor_malloc(sizeof(routerinfo_t));
+ hid_serv_responsible_for_desc_id_response = 1;
+ rend_cache_store_v2_desc_as_dir(desc_holder_one->desc_str);
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder_two->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ done:
+ NS_UNMOCK(router_get_my_routerinfo);
+ NS_UNMOCK(hid_serv_responsible_for_desc_id);
+ rend_cache_free_all();
+ rend_service_descriptor_free(generated);
+ tor_free(service_id);
+ SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
+ rend_encoded_v2_service_descriptor_free(d));
+ smartlist_free(descs);
+ rend_encoded_v2_service_descriptor_free(desc_holder_one);
+ rend_encoded_v2_service_descriptor_free(desc_holder_two);
+}
+
+#undef NS_SUBMODULE
+
+static void
+test_rend_cache_init(void *data)
+{
+ (void)data;
+
+ tt_assert_msg(!rend_cache, "rend_cache should be NULL when starting");
+ tt_assert_msg(!rend_cache_v2_dir, "rend_cache_v2_dir should be NULL "
+ "when starting");
+ tt_assert_msg(!rend_cache_failure, "rend_cache_failure should be NULL when "
+ "starting");
+
+ rend_cache_init();
+
+ tt_assert_msg(rend_cache, "rend_cache should not be NULL after initing");
+ tt_assert_msg(rend_cache_v2_dir, "rend_cache_v2_dir should not be NULL "
+ "after initing");
+ tt_assert_msg(rend_cache_failure, "rend_cache_failure should not be NULL "
+ "after initing");
+
+ tt_int_op(strmap_size(rend_cache), OP_EQ, 0);
+ tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
+ tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 0);
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_decrement_allocation(void *data)
+{
+ (void)data;
+
+ // Test when the cache has enough allocations
+ rend_cache_total_allocation = 10;
+ rend_cache_decrement_allocation(3);
+ tt_int_op(rend_cache_total_allocation, OP_EQ, 7);
+
+ // Test when there are not enough allocations
+ rend_cache_total_allocation = 1;
+ rend_cache_decrement_allocation(2);
+ tt_int_op(rend_cache_total_allocation, OP_EQ, 0);
+
+ // And again
+ rend_cache_decrement_allocation(2);
+ tt_int_op(rend_cache_total_allocation, OP_EQ, 0);
+
+ done:
+ (void)0;
+}
+
+static void
+test_rend_cache_increment_allocation(void *data)
+{
+ (void)data;
+
+ // Test when the cache is not overflowing
+ rend_cache_total_allocation = 5;
+ rend_cache_increment_allocation(3);
+ tt_int_op(rend_cache_total_allocation, OP_EQ, 8);
+
+ // Test when there are too many allocations
+ rend_cache_total_allocation = SIZE_MAX-1;
+ rend_cache_increment_allocation(2);
+ tt_u64_op(rend_cache_total_allocation, OP_EQ, SIZE_MAX);
+
+ // And again
+ rend_cache_increment_allocation(2);
+ tt_u64_op(rend_cache_total_allocation, OP_EQ, SIZE_MAX);
+
+ done:
+ (void)0;
+}
+
+static void
+test_rend_cache_failure_intro_entry_new(void *data)
+{
+ time_t now;
+ rend_cache_failure_intro_t *entry;
+ rend_intro_point_failure_t failure;
+
+ (void)data;
+
+ failure = INTRO_POINT_FAILURE_TIMEOUT;
+ now = time(NULL);
+ entry = rend_cache_failure_intro_entry_new(failure);
+
+ tt_int_op(entry->failure_type, OP_EQ, INTRO_POINT_FAILURE_TIMEOUT);
+ tt_int_op(entry->created_ts, OP_GE, now-5);
+ tt_int_op(entry->created_ts, OP_LE, now+5);
+
+ done:
+ tor_free(entry);
+}
+
+static void
+test_rend_cache_failure_intro_lookup(void *data)
+{
+ (void)data;
+ int ret;
+ rend_cache_failure_t *failure;
+ rend_cache_failure_intro_t *ip;
+ rend_cache_failure_intro_t *entry;
+
+ rend_cache_init();
+
+ failure = rend_cache_failure_entry_new();
+ ip = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+ digestmap_set(failure->intro_failures, "ip1", ip);
+ strmap_set_lc(rend_cache_failure, "foo1", failure);
+
+ // Test not found
+ ret = cache_failure_intro_lookup((const uint8_t *)"foo1", "foo2", NULL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test found with no intro failures in it
+ ret = cache_failure_intro_lookup((const uint8_t *)"ip2", "foo1", NULL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test found
+ ret = cache_failure_intro_lookup((const uint8_t *)"ip1", "foo1", NULL);
+ tt_int_op(ret, OP_EQ, 1);
+
+ // Test found and asking for entry
+ cache_failure_intro_lookup((const uint8_t *)"ip1", "foo1", &entry);
+ tt_assert(entry);
+ tt_assert(entry == ip);
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_clean(void *data)
+{
+ rend_cache_entry_t *one, *two;
+ rend_service_descriptor_t *desc_one, *desc_two;
+ strmap_iter_t *iter = NULL;
+ const char *key;
+ void *val;
+
+ (void)data;
+
+ rend_cache_init();
+
+ // Test with empty rendcache
+ rend_cache_clean(time(NULL), REND_CACHE_TYPE_CLIENT);
+ tt_int_op(strmap_size(rend_cache), OP_EQ, 0);
+
+ // Test with two old entries
+ one = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ two = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ desc_one = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ desc_two = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ one->parsed = desc_one;
+ two->parsed = desc_two;
+
+ desc_one->timestamp = time(NULL) + TIME_IN_THE_PAST;
+ desc_two->timestamp = (time(NULL) + TIME_IN_THE_PAST) - 10;
+ desc_one->pk = pk_generate(0);
+ desc_two->pk = pk_generate(1);
+
+ strmap_set_lc(rend_cache, "foo1", one);
+ strmap_set_lc(rend_cache, "foo2", two);
+
+ rend_cache_clean(time(NULL), REND_CACHE_TYPE_CLIENT);
+ tt_int_op(strmap_size(rend_cache), OP_EQ, 0);
+
+ // Test with one old entry and one newer entry
+ one = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ two = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ desc_one = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ desc_two = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ one->parsed = desc_one;
+ two->parsed = desc_two;
+
+ desc_one->timestamp = (time(NULL) + TIME_IN_THE_PAST) - 10;
+ desc_two->timestamp = time(NULL) - 100;
+ desc_one->pk = pk_generate(0);
+ desc_two->pk = pk_generate(1);
+
+ strmap_set_lc(rend_cache, "foo1", one);
+ strmap_set_lc(rend_cache, "foo2", two);
+
+ rend_cache_clean(time(NULL), REND_CACHE_TYPE_CLIENT);
+ tt_int_op(strmap_size(rend_cache), OP_EQ, 1);
+
+ iter = strmap_iter_init(rend_cache);
+ strmap_iter_get(iter, &key, &val);
+ tt_str_op(key, OP_EQ, "foo2");
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_failure_entry_new(void *data)
+{
+ rend_cache_failure_t *failure;
+
+ (void)data;
+
+ failure = rend_cache_failure_entry_new();
+ tt_assert(failure);
+ tt_int_op(digestmap_size(failure->intro_failures), OP_EQ, 0);
+
+ done:
+ rend_cache_failure_entry_free(failure);
+}
+
+static void
+test_rend_cache_failure_entry_free(void *data)
+{
+ (void)data;
+
+ // Test that it can deal with a NULL argument
+ rend_cache_failure_entry_free(NULL);
+
+ /* done: */
+ /* (void)0; */
+}
+
+static void
+test_rend_cache_failure_clean(void *data)
+{
+ rend_cache_failure_t *failure;
+ rend_cache_failure_intro_t *ip_one, *ip_two;
+
+ (void)data;
+
+ rend_cache_init();
+
+ // Test with empty failure cache
+ rend_cache_failure_clean(time(NULL));
+ tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 0);
+
+ // Test with one empty failure entry
+ failure = rend_cache_failure_entry_new();
+ strmap_set_lc(rend_cache_failure, "foo1", failure);
+ rend_cache_failure_clean(time(NULL));
+ tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 0);
+
+ // Test with one new intro point
+ failure = rend_cache_failure_entry_new();
+ ip_one = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+ digestmap_set(failure->intro_failures, "ip1", ip_one);
+ strmap_set_lc(rend_cache_failure, "foo1", failure);
+ rend_cache_failure_clean(time(NULL));
+ tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 1);
+
+ // Test with one old intro point
+ rend_cache_failure_purge();
+ failure = rend_cache_failure_entry_new();
+ ip_one = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+ ip_one->created_ts = time(NULL) - 7*60;
+ digestmap_set(failure->intro_failures, "ip1", ip_one);
+ strmap_set_lc(rend_cache_failure, "foo1", failure);
+ rend_cache_failure_clean(time(NULL));
+ tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 0);
+
+ // Test with one old intro point and one new one
+ rend_cache_failure_purge();
+ failure = rend_cache_failure_entry_new();
+ ip_one = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+ ip_one->created_ts = time(NULL) - 7*60;
+ digestmap_set(failure->intro_failures, "ip1", ip_one);
+ ip_two = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+ ip_two->created_ts = time(NULL) - 2*60;
+ digestmap_set(failure->intro_failures, "ip2", ip_two);
+ strmap_set_lc(rend_cache_failure, "foo1", failure);
+ rend_cache_failure_clean(time(NULL));
+ tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 1);
+ tt_int_op(digestmap_size(failure->intro_failures), OP_EQ, 1);
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_failure_remove(void *data)
+{
+ rend_service_descriptor_t *desc;
+ (void)data;
+
+ rend_cache_init();
+
+ // Test that it deals well with a NULL desc
+ rend_cache_failure_remove(NULL);
+
+ // Test a descriptor that isn't in the cache
+ desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ desc->pk = pk_generate(0);
+ rend_cache_failure_remove(desc);
+
+ // There seems to not exist any way of getting rend_cache_failure_remove()
+ // to fail because of a problem with rend_get_service_id from here
+ rend_cache_free_all();
+
+ rend_service_descriptor_free(desc);
+ /* done: */
+ /* (void)0; */
+}
+
+static void
+test_rend_cache_free_all(void *data)
+{
+ rend_cache_failure_t *failure;
+ rend_cache_entry_t *one;
+ rend_service_descriptor_t *desc_one;
+
+ (void)data;
+
+ rend_cache_init();
+
+ failure = rend_cache_failure_entry_new();
+ strmap_set_lc(rend_cache_failure, "foo1", failure);
+
+ one = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ desc_one = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ one->parsed = desc_one;
+ desc_one->timestamp = time(NULL) + TIME_IN_THE_PAST;
+ desc_one->pk = pk_generate(0);
+ strmap_set_lc(rend_cache, "foo1", one);
+
+ rend_cache_free_all();
+
+ tt_assert(!rend_cache);
+ tt_assert(!rend_cache_v2_dir);
+ tt_assert(!rend_cache_failure);
+ tt_assert(!rend_cache_total_allocation);
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_entry_free(void *data)
+{
+ (void)data;
+ rend_cache_entry_t *e;
+
+ // Handles NULL correctly
+ rend_cache_entry_free(NULL);
+
+ // Handles NULL descriptor correctly
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ rend_cache_entry_free(e);
+
+ // Handles non-NULL descriptor correctly
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ e->desc = (char *)malloc(10);
+ rend_cache_entry_free(e);
+
+ /* done: */
+ /* (void)0; */
+}
+
+static void
+test_rend_cache_purge(void *data)
+{
+ strmap_t *our_rend_cache;
+
+ (void)data;
+
+ // Deals with a NULL rend_cache
+ rend_cache_purge();
+ tt_assert(rend_cache);
+ tt_int_op(strmap_size(rend_cache), OP_EQ, 0);
+
+ // Deals with existing rend_cache
+ rend_cache_free_all();
+ rend_cache_init();
+
+ our_rend_cache = rend_cache;
+ rend_cache_purge();
+ tt_assert(rend_cache);
+ tt_assert(strmap_size(rend_cache) == 0);
+ tt_assert(rend_cache == our_rend_cache);
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_failure_intro_add(void *data)
+{
+ (void)data;
+ rend_cache_failure_t *fail_entry;
+ rend_cache_failure_intro_t *entry;
+
+ rend_cache_init();
+
+ // Adds non-existing entry
+ cache_failure_intro_add((const uint8_t *)"foo1", "foo2",
+ INTRO_POINT_FAILURE_TIMEOUT);
+ fail_entry = strmap_get_lc(rend_cache_failure, "foo2");
+ tt_assert(fail_entry);
+ tt_int_op(digestmap_size(fail_entry->intro_failures), OP_EQ, 1);
+ entry = digestmap_get(fail_entry->intro_failures, "foo1");
+ tt_assert(entry);
+
+ // Adds existing entry
+ cache_failure_intro_add((const uint8_t *)"foo1", "foo2",
+ INTRO_POINT_FAILURE_TIMEOUT);
+ fail_entry = strmap_get_lc(rend_cache_failure, "foo2");
+ tt_assert(fail_entry);
+ tt_int_op(digestmap_size(fail_entry->intro_failures), OP_EQ, 1);
+ entry = digestmap_get(fail_entry->intro_failures, "foo1");
+ tt_assert(entry);
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_intro_failure_note(void *data)
+{
+ (void)data;
+ rend_cache_failure_t *fail_entry;
+ rend_cache_failure_intro_t *entry;
+
+ rend_cache_init();
+
+ // Test not found
+ rend_cache_intro_failure_note(INTRO_POINT_FAILURE_TIMEOUT,
+ (const uint8_t *)"foo1", "foo2");
+ fail_entry = strmap_get_lc(rend_cache_failure, "foo2");
+ tt_assert(fail_entry);
+ tt_int_op(digestmap_size(fail_entry->intro_failures), OP_EQ, 1);
+ entry = digestmap_get(fail_entry->intro_failures, "foo1");
+ tt_assert(entry);
+ tt_int_op(entry->failure_type, OP_EQ, INTRO_POINT_FAILURE_TIMEOUT);
+
+ // Test found
+ rend_cache_intro_failure_note(INTRO_POINT_FAILURE_UNREACHABLE,
+ (const uint8_t *)"foo1", "foo2");
+ tt_int_op(entry->failure_type, OP_EQ, INTRO_POINT_FAILURE_UNREACHABLE);
+
+ done:
+ rend_cache_free_all();
+}
+
+#define NS_SUBMODULE clean_v2_descs_as_dir
+NS_DECL(int, hid_serv_responsible_for_desc_id, (const char *id));
+
+static int
+NS(hid_serv_responsible_for_desc_id)(const char *id)
+{
+ (void)id;
+ return hid_serv_responsible_for_desc_id_response;
+}
+
+static void
+test_rend_cache_clean_v2_descs_as_dir(void *data)
+{
+ rend_cache_entry_t *e;
+ time_t now;
+ rend_service_descriptor_t *desc;
+ now = time(NULL);
+
+ (void)data;
+
+ NS_MOCK(hid_serv_responsible_for_desc_id);
+ rend_cache_init();
+
+ // Test running with an empty cache
+ rend_cache_clean_v2_descs_as_dir(now, 0);
+ tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
+
+ // Test with only one new entry
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ e->last_served = now;
+ desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ desc->timestamp = now;
+ desc->pk = pk_generate(0);
+ e->parsed = desc;
+ digestmap_set(rend_cache_v2_dir, "abcde", e);
+
+ hid_serv_responsible_for_desc_id_response = 1;
+ rend_cache_clean_v2_descs_as_dir(now, 0);
+ tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 1);
+
+ // Test with one old entry
+ desc->timestamp = now - (REND_CACHE_MAX_AGE + REND_CACHE_MAX_SKEW + 1000);
+ rend_cache_clean_v2_descs_as_dir(now, 0);
+ tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
+
+ // Test with one entry that is not under the responsibility of this
+ // hidden service
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ e->last_served = now;
+ desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ desc->timestamp = now;
+ desc->pk = pk_generate(0);
+ e->parsed = desc;
+ digestmap_set(rend_cache_v2_dir, "abcde", e);
+
+ hid_serv_responsible_for_desc_id_response = 0;
+ rend_cache_clean_v2_descs_as_dir(now, 0);
+ tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
+
+ // Test with one entry that has an old last served
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ e->last_served = now - (REND_CACHE_MAX_AGE + REND_CACHE_MAX_SKEW + 1000);
+ desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ desc->timestamp = now;
+ desc->pk = pk_generate(0);
+ e->parsed = desc;
+ digestmap_set(rend_cache_v2_dir, "abcde", e);
+
+ hid_serv_responsible_for_desc_id_response = 1;
+ rend_cache_clean_v2_descs_as_dir(now, 0);
+ tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
+
+ // Test a run through asking for a large force_remove
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ e->last_served = now;
+ desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ desc->timestamp = now;
+ desc->pk = pk_generate(0);
+ e->parsed = desc;
+ digestmap_set(rend_cache_v2_dir, "abcde", e);
+
+ hid_serv_responsible_for_desc_id_response = 1;
+ rend_cache_clean_v2_descs_as_dir(now, 20000);
+ tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 1);
+
+ done:
+ NS_UNMOCK(hid_serv_responsible_for_desc_id);
+ rend_cache_free_all();
+}
+
+#undef NS_SUBMODULE
+
+static void
+test_rend_cache_entry_allocation(void *data)
+{
+ (void)data;
+
+ size_t ret;
+ rend_cache_entry_t *e = NULL;
+
+ // Handles a null argument
+ ret = rend_cache_entry_allocation(NULL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Handles a non-null argument
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ ret = rend_cache_entry_allocation(e);
+ tt_int_op(ret, OP_GT, sizeof(rend_cache_entry_t));
+
+ done:
+ tor_free(e);
+}
+
+static void
+test_rend_cache_failure_intro_entry_free(void *data)
+{
+ (void)data;
+ rend_cache_failure_intro_t *entry;
+
+ // Handles a null argument
+ rend_cache_failure_intro_entry_free(NULL);
+
+ // Handles a non-null argument
+ entry = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+ rend_cache_failure_intro_entry_free(entry);
+}
+
+static void
+test_rend_cache_failure_purge(void *data)
+{
+ (void)data;
+
+ // Handles a null failure cache
+ strmap_free(rend_cache_failure, rend_cache_failure_entry_free_);
+ rend_cache_failure = NULL;
+
+ rend_cache_failure_purge();
+
+ tt_ptr_op(rend_cache_failure, OP_NE, NULL);
+ tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 0);
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_validate_intro_point_failure(void *data)
+{
+ (void)data;
+ rend_service_descriptor_t *desc = NULL;
+ char *service_id = NULL;
+ rend_intro_point_t *intro = NULL;
+ const uint8_t *identity = NULL;
+ rend_cache_failure_t *failure;
+ rend_cache_failure_intro_t *ip;
+
+ rend_cache_init();
+
+ create_descriptor(&desc, &service_id, 3);
+ desc->timestamp = time(NULL) + RECENT_TIME;
+
+ intro = (rend_intro_point_t *)smartlist_get(desc->intro_nodes, 0);
+ identity = (uint8_t *) intro->extend_info->identity_digest;
+
+ failure = rend_cache_failure_entry_new();
+ ip = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+ digestmap_set(failure->intro_failures, (char *)identity, ip);
+ strmap_set_lc(rend_cache_failure, service_id, failure);
+
+ // Test when we have an intro point in our cache
+ validate_intro_point_failure(desc, service_id);
+ tt_int_op(smartlist_len(desc->intro_nodes), OP_EQ, 2);
+
+ done:
+ rend_cache_free_all();
+ rend_service_descriptor_free(desc);
+ tor_free(service_id);
+}
+
+struct testcase_t rend_cache_tests[] = {
+ { "init", test_rend_cache_init, 0, NULL, NULL },
+ { "decrement_allocation", test_rend_cache_decrement_allocation, 0,
+ NULL, NULL },
+ { "increment_allocation", test_rend_cache_increment_allocation, 0,
+ NULL, NULL },
+ { "clean", test_rend_cache_clean, TT_FORK, NULL, NULL },
+ { "clean_v2_descs_as_dir", test_rend_cache_clean_v2_descs_as_dir, 0,
+ NULL, NULL },
+ { "entry_allocation", test_rend_cache_entry_allocation, 0, NULL, NULL },
+ { "entry_free", test_rend_cache_entry_free, 0, NULL, NULL },
+ { "failure_intro_entry_free", test_rend_cache_failure_intro_entry_free, 0,
+ NULL, NULL },
+ { "free_all", test_rend_cache_free_all, 0, NULL, NULL },
+ { "purge", test_rend_cache_purge, 0, NULL, NULL },
+ { "failure_clean", test_rend_cache_failure_clean, 0, NULL, NULL },
+ { "failure_entry_new", test_rend_cache_failure_entry_new, 0, NULL, NULL },
+ { "failure_entry_free", test_rend_cache_failure_entry_free, 0, NULL, NULL },
+ { "failure_intro_add", test_rend_cache_failure_intro_add, 0, NULL, NULL },
+ { "failure_intro_entry_new", test_rend_cache_failure_intro_entry_new, 0,
+ NULL, NULL },
+ { "failure_intro_lookup", test_rend_cache_failure_intro_lookup, 0,
+ NULL, NULL },
+ { "failure_purge", test_rend_cache_failure_purge, 0, NULL, NULL },
+ { "failure_remove", test_rend_cache_failure_remove, 0, NULL, NULL },
+ { "intro_failure_note", test_rend_cache_intro_failure_note, 0, NULL, NULL },
+ { "lookup", test_rend_cache_lookup_entry, 0, NULL, NULL },
+ { "lookup_v2_desc_as_dir", test_rend_cache_lookup_v2_desc_as_dir, 0,
+ NULL, NULL },
+ { "store_v2_desc_as_client", test_rend_cache_store_v2_desc_as_client, 0,
+ NULL, NULL },
+ { "store_v2_desc_as_client_with_different_time",
+ test_rend_cache_store_v2_desc_as_client_with_different_time, 0,
+ NULL, NULL },
+ { "store_v2_desc_as_dir", test_rend_cache_store_v2_desc_as_dir, 0,
+ NULL, NULL },
+ { "store_v2_desc_as_dir_with_different_time",
+ test_rend_cache_store_v2_desc_as_dir_with_different_time, 0, NULL, NULL },
+ { "store_v2_desc_as_dir_with_different_content",
+ test_rend_cache_store_v2_desc_as_dir_with_different_content, 0,
+ NULL, NULL },
+ { "validate_intro_point_failure",
+ test_rend_cache_validate_intro_point_failure, 0, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_replay.c b/src/test/test_replay.c
index a02c160365..7a0f098776 100644
--- a/src/test/test_replay.c
+++ b/src/test/test_replay.c
@@ -17,6 +17,20 @@ static const char *test_buffer =
" occaecat cupidatat non proident, sunt in culpa qui officia deserunt"
" mollit anim id est laborum.";
+static const char *test_buffer_2 =
+ "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis"
+ " praesentium voluptatum deleniti atque corrupti quos dolores et quas"
+ " molestias excepturi sint occaecati cupiditate non provident, similique"
+ " sunt in culpa qui officia deserunt mollitia animi, id est laborum et"
+ " dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio."
+ " Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil"
+ " impedit quo minus id quod maxime placeat facere possimus, omnis voluptas"
+ " assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut"
+ " officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates"
+ " repudiandae sint et molestiae non recusandae. Itaque earum rerum hic"
+ " tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias"
+ " consequatur aut perferendis doloribus asperiores repellat.";
+
static void
test_replaycache_alloc(void *arg)
{
@@ -83,6 +97,12 @@ test_replaycache_miss(void *arg)
strlen(test_buffer), NULL);
tt_int_op(result,OP_EQ, 0);
+ /* make sure a different buffer misses as well */
+ result =
+ replaycache_add_and_test_internal(1200, NULL, test_buffer_2,
+ strlen(test_buffer_2), NULL);
+ tt_int_op(result,OP_EQ, 0);
+
/* poke the bad-parameter error case too */
result =
replaycache_add_and_test_internal(1200, NULL, test_buffer,
@@ -115,6 +135,18 @@ test_replaycache_hit(void *arg)
strlen(test_buffer), NULL);
tt_int_op(result,OP_EQ, 1);
+ /* make sure a different buffer misses then hits as well */
+
+ result =
+ replaycache_add_and_test_internal(1200, r, test_buffer_2,
+ strlen(test_buffer_2), NULL);
+ tt_int_op(result,OP_EQ, 0);
+
+ result =
+ replaycache_add_and_test_internal(1300, r, test_buffer_2,
+ strlen(test_buffer_2), NULL);
+ tt_int_op(result,OP_EQ, 1);
+
done:
if (r) replaycache_free(r);
@@ -245,7 +277,7 @@ test_replaycache_scrub(void *arg)
/* Make sure we hit the aging-out case too */
replaycache_scrub_if_needed_internal(1500, r);
/* Assert that we aged it */
- tt_int_op(digestmap_size(r->digests_seen),OP_EQ, 0);
+ tt_int_op(digest256map_size(r->digests_seen),OP_EQ, 0);
done:
if (r) replaycache_free(r);
diff --git a/src/test/test_routerset.c b/src/test/test_routerset.c
index 90dfb28c6b..74b39c0486 100644
--- a/src/test/test_routerset.c
+++ b/src/test/test_routerset.c
@@ -423,10 +423,10 @@ NS(test_main)(void *arg)
}
#undef NS_SUBMODULE
-#define NS_SUBMODULE ASPECT(routerset_parse, policy)
+#define NS_SUBMODULE ASPECT(routerset_parse, policy_wildcard)
/*
- * Structural test for routerset_parse, when given a valid policy.
+ * Structural test for routerset_parse, when given a valid wildcard policy.
*/
NS_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string,
@@ -470,6 +470,100 @@ NS(router_parse_addr_policy_item_from_string)(const char *s,
}
#undef NS_SUBMODULE
+#define NS_SUBMODULE ASPECT(routerset_parse, policy_ipv4)
+
+/*
+ * Structural test for routerset_parse, when given a valid IPv4 address
+ * literal policy.
+ */
+
+NS_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string,
+ (const char *s, int assume_action, int *bogus));
+
+addr_policy_t *NS(mock_addr_policy);
+
+static void
+NS(test_main)(void *arg)
+{
+ routerset_t *set;
+ const char *s;
+ int r;
+ (void)arg;
+
+ NS_MOCK(router_parse_addr_policy_item_from_string);
+ NS(mock_addr_policy) = tor_malloc_zero(sizeof(addr_policy_t));
+
+ set = routerset_new();
+ s = "127.0.0.1";
+ r = routerset_parse(set, s, "");
+ tt_int_op(r, OP_EQ, 0);
+ tt_int_op(smartlist_len(set->policies), OP_NE, 0);
+ tt_int_op(CALLED(router_parse_addr_policy_item_from_string), OP_EQ, 1);
+
+ done:
+ routerset_free(set);
+}
+
+addr_policy_t *
+NS(router_parse_addr_policy_item_from_string)(const char *s, int assume_action,
+ int *bogus)
+{
+ (void)s;
+ (void)assume_action;
+ CALLED(router_parse_addr_policy_item_from_string)++;
+ *bogus = 0;
+
+ return NS(mock_addr_policy);
+}
+
+#undef NS_SUBMODULE
+#define NS_SUBMODULE ASPECT(routerset_parse, policy_ipv6)
+
+/*
+ * Structural test for routerset_parse, when given a valid IPv6 address
+ * literal policy.
+ */
+
+NS_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string,
+ (const char *s, int assume_action, int *bad));
+
+addr_policy_t *NS(mock_addr_policy);
+
+static void
+NS(test_main)(void *arg)
+{
+ routerset_t *set;
+ const char *s;
+ int r;
+ (void)arg;
+
+ NS_MOCK(router_parse_addr_policy_item_from_string);
+ NS(mock_addr_policy) = tor_malloc_zero(sizeof(addr_policy_t));
+
+ set = routerset_new();
+ s = "::1";
+ r = routerset_parse(set, s, "");
+ tt_int_op(r, OP_EQ, 0);
+ tt_int_op(smartlist_len(set->policies), OP_NE, 0);
+ tt_int_op(CALLED(router_parse_addr_policy_item_from_string), OP_EQ, 1);
+
+ done:
+ routerset_free(set);
+}
+
+addr_policy_t *
+NS(router_parse_addr_policy_item_from_string)(const char *s,
+ int assume_action, int *bad)
+{
+ (void)s;
+ (void)assume_action;
+ CALLED(router_parse_addr_policy_item_from_string)++;
+ *bad = 0;
+
+ return NS(mock_addr_policy);
+}
+
+#undef NS_SUBMODULE
#define NS_SUBMODULE ASPECT(routerset_union, source_bad)
/*
@@ -2109,7 +2203,9 @@ struct testcase_t routerset_tests[] = {
TEST_CASE_ASPECT(routerset_parse, valid_hexdigest),
TEST_CASE_ASPECT(routerset_parse, valid_nickname),
TEST_CASE_ASPECT(routerset_parse, get_countryname),
- TEST_CASE_ASPECT(routerset_parse, policy),
+ TEST_CASE_ASPECT(routerset_parse, policy_wildcard),
+ TEST_CASE_ASPECT(routerset_parse, policy_ipv4),
+ TEST_CASE_ASPECT(routerset_parse, policy_ipv6),
TEST_CASE(routerset_subtract_nodes),
TEST_CASE_ASPECT(routerset_subtract_nodes, null_routerset),
TEST_CASE(routerset_to_string),
diff --git a/src/test/test_threads.c b/src/test/test_threads.c
index 35f5dc8ea3..fe88c9470f 100644
--- a/src/test/test_threads.c
+++ b/src/test/test_threads.c
@@ -73,6 +73,8 @@ thread_test_func_(void* _s)
++thread_fns_failed;
tor_mutex_release(thread_test_mutex_);
+ tor_free(mycount);
+
tor_mutex_release(m);
spawn_exit();
diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c
new file mode 100644
index 0000000000..b1d91a61c7
--- /dev/null
+++ b/src/test/test_tortls.c
@@ -0,0 +1,2846 @@
+/* Copyright (c) 2010-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define TORTLS_PRIVATE
+#define LOG_PRIVATE
+#include "orconfig.h"
+
+#ifdef _WIN32
+#include <winsock2.h>
+#endif
+
+#ifdef __GNUC__
+#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+#endif
+
+#if __GNUC__ && GCC_VERSION >= 402
+#if GCC_VERSION >= 406
+#pragma GCC diagnostic push
+#endif
+/* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in
+ * srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+
+#include <openssl/opensslv.h>
+
+#include <openssl/ssl.h>
+#include <openssl/ssl3.h>
+#include <openssl/err.h>
+#include <openssl/asn1t.h>
+#include <openssl/x509.h>
+#include <openssl/rsa.h>
+#include <openssl/evp.h>
+#include <openssl/bn.h>
+
+#if __GNUC__ && GCC_VERSION >= 402
+#if GCC_VERSION >= 406
+#pragma GCC diagnostic pop
+#else
+#pragma GCC diagnostic warning "-Wredundant-decls"
+#endif
+#endif
+
+#include "or.h"
+#include "torlog.h"
+#include "config.h"
+#include "tortls.h"
+
+#include "test.h"
+#include "log_test_helpers.h"
+#define NS_MODULE tortls
+
+extern tor_tls_context_t *server_tls_context;
+extern tor_tls_context_t *client_tls_context;
+
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) \
+ && !defined(LIBRESSL_VERSION_NUMBER)
+#define OPENSSL_OPAQUE
+#define SSL_STATE_STR "before SSL initialization"
+#else
+#define SSL_STATE_STR "before/accept initialization"
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static SSL_METHOD *
+give_me_a_test_method(void)
+{
+ SSL_METHOD *method = tor_malloc_zero(sizeof(SSL_METHOD));
+ memcpy(method, TLSv1_method(), sizeof(SSL_METHOD));
+ return method;
+}
+
+static int
+fake_num_ciphers(void)
+{
+ return 0;
+}
+#endif
+
+static void
+test_tortls_errno_to_tls_error(void *data)
+{
+ (void) data;
+ tt_int_op(tor_errno_to_tls_error(SOCK_ERRNO(ECONNRESET)),OP_EQ,
+ TOR_TLS_ERROR_CONNRESET);
+ tt_int_op(tor_errno_to_tls_error(SOCK_ERRNO(ETIMEDOUT)),OP_EQ,
+ TOR_TLS_ERROR_TIMEOUT);
+ tt_int_op(tor_errno_to_tls_error(SOCK_ERRNO(EHOSTUNREACH)),OP_EQ,
+ TOR_TLS_ERROR_NO_ROUTE);
+ tt_int_op(tor_errno_to_tls_error(SOCK_ERRNO(ENETUNREACH)),OP_EQ,
+ TOR_TLS_ERROR_NO_ROUTE);
+ tt_int_op(tor_errno_to_tls_error(SOCK_ERRNO(ECONNREFUSED)),OP_EQ,
+ TOR_TLS_ERROR_CONNREFUSED);
+ tt_int_op(tor_errno_to_tls_error(0),OP_EQ,TOR_TLS_ERROR_MISC);
+ done:
+ (void)1;
+}
+
+static void
+test_tortls_err_to_string(void *data)
+{
+ (void) data;
+ tt_str_op(tor_tls_err_to_string(1),OP_EQ,"[Not an error.]");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_ERROR_MISC),OP_EQ,"misc error");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_ERROR_IO),OP_EQ,"unexpected close");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_ERROR_CONNREFUSED),OP_EQ,
+ "connection refused");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_ERROR_CONNRESET),OP_EQ,
+ "connection reset");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_ERROR_NO_ROUTE),OP_EQ,
+ "host unreachable");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_ERROR_TIMEOUT),OP_EQ,
+ "connection timed out");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_CLOSE),OP_EQ,"closed");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_WANTREAD),OP_EQ,"want to read");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_WANTWRITE),OP_EQ,"want to write");
+ tt_str_op(tor_tls_err_to_string(-100),OP_EQ,"(unknown error code)");
+ done:
+ (void)1;
+}
+
+static int
+mock_tls_cert_matches_key(const tor_tls_t *tls, const tor_x509_cert_t *cert)
+{
+ (void) tls;
+ (void) cert; // XXXX look at this.
+ return 1;
+}
+
+static void
+test_tortls_tor_tls_new(void *data)
+{
+ (void) data;
+ MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key);
+ crypto_pk_t *key1 = NULL, *key2 = NULL;
+ SSL_METHOD *method = NULL;
+
+ key1 = pk_generate(2);
+ key2 = pk_generate(3);
+
+ tor_tls_t *tls = NULL;
+ tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER,
+ key1, key2, 86400), OP_EQ, 0);
+ tls = tor_tls_new(-1, 0);
+ tt_want(tls);
+ tor_tls_free(tls); tls = NULL;
+
+ client_tls_context->ctx = NULL;
+ tls = tor_tls_new(-1, 0);
+ tt_assert(!tls);
+
+#ifndef OPENSSL_OPAQUE
+ method = give_me_a_test_method();
+ SSL_CTX *ctx = SSL_CTX_new(method);
+ method->num_ciphers = fake_num_ciphers;
+ client_tls_context->ctx = ctx;
+ tls = tor_tls_new(-1, 0);
+ tt_assert(!tls);
+#endif
+
+ done:
+ UNMOCK(tor_tls_cert_matches_key);
+ crypto_pk_free(key1);
+ crypto_pk_free(key2);
+ tor_tls_free(tls);
+ tor_free(method);
+ tor_tls_free_all();
+}
+
+#define NS_MODULE tortls
+NS_DECL(void, logv, (int severity, log_domain_mask_t domain,
+ const char *funcname, const char *suffix,
+ const char *format, va_list ap));
+
+static void
+NS(logv)(int severity, log_domain_mask_t domain,
+ const char *funcname, const char *suffix, const char *format,
+ va_list ap)
+{
+ (void) severity;
+ (void) domain;
+ (void) funcname;
+ (void) suffix;
+ (void) format;
+ (void) ap; // XXXX look at this.
+ CALLED(logv)++;
+}
+
+static void
+test_tortls_tor_tls_get_error(void *data)
+{
+ (void) data;
+ MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key);
+ crypto_pk_t *key1 = NULL, *key2 = NULL;
+ key1 = pk_generate(2);
+ key2 = pk_generate(3);
+
+ tor_tls_t *tls = NULL;
+ tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER,
+ key1, key2, 86400), OP_EQ, 0);
+ tls = tor_tls_new(-1, 0);
+ NS_MOCK(logv);
+ tt_int_op(CALLED(logv), OP_EQ, 0);
+ tor_tls_get_error(tls, 0, 0,
+ (const char *)"test", 0, 0);
+ tt_int_op(CALLED(logv), OP_EQ, 1);
+
+ done:
+ UNMOCK(tor_tls_cert_matches_key);
+ NS_UNMOCK(logv);
+ crypto_pk_free(key1);
+ crypto_pk_free(key2);
+ tor_tls_free(tls);
+}
+
+static void
+test_tortls_get_state_description(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ char *buf;
+ SSL_CTX *ctx;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(SSLv23_method());
+
+ buf = tor_malloc_zero(1000);
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ tor_tls_get_state_description(NULL, buf, 20);
+ tt_str_op(buf, OP_EQ, "(No SSL object)");
+
+ SSL_free(tls->ssl);
+ tls->ssl = NULL;
+ tor_tls_get_state_description(tls, buf, 20);
+ tt_str_op(buf, OP_EQ, "(No SSL object)");
+
+ tls->ssl = SSL_new(ctx);
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in HANDSHAKE");
+
+ tls->state = TOR_TLS_ST_OPEN;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in OPEN");
+
+ tls->state = TOR_TLS_ST_GOTCLOSE;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in GOTCLOSE");
+
+ tls->state = TOR_TLS_ST_SENTCLOSE;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in SENTCLOSE");
+
+ tls->state = TOR_TLS_ST_CLOSED;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in CLOSED");
+
+ tls->state = TOR_TLS_ST_RENEGOTIATE;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in RENEGOTIATE");
+
+ tls->state = TOR_TLS_ST_BUFFEREVENT;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR);
+
+ tls->state = 7;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in unknown TLS state");
+
+ done:
+ SSL_CTX_free(ctx);
+ SSL_free(tls->ssl);
+ tor_free(buf);
+ tor_free(tls);
+}
+
+extern int tor_tls_object_ex_data_index;
+
+static void
+test_tortls_get_by_ssl(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ tor_tls_t *res;
+ SSL_CTX *ctx;
+ SSL *ssl;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
+ ctx = SSL_CTX_new(SSLv23_method());
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->magic = TOR_TLS_MAGIC;
+
+ ssl = SSL_new(ctx);
+
+ res = tor_tls_get_by_ssl(ssl);
+ tt_assert(!res);
+
+ SSL_set_ex_data(ssl, tor_tls_object_ex_data_index, tls);
+
+ res = tor_tls_get_by_ssl(ssl);
+ tt_assert(res == tls);
+
+ done:
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+ tor_free(tls);
+}
+
+static void
+test_tortls_allocate_tor_tls_object_ex_data_index(void *ignored)
+{
+ (void)ignored;
+ int first;
+
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
+ first = tor_tls_object_ex_data_index;
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+ tt_int_op(first, OP_EQ, tor_tls_object_ex_data_index);
+
+ done:
+ (void)0;
+}
+
+static void
+test_tortls_log_one_error(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ SSL_CTX *ctx;
+ SSL *ssl = NULL;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(SSLv23_method());
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ int previous_log = setup_capture_of_logs(LOG_INFO);
+
+ tor_tls_log_one_error(NULL, 0, LOG_WARN, 0, "something");
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_str_op(mock_saved_log_at(0), OP_EQ, "TLS error while something: "
+ "(null) (in (null):(null):---)\n");
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, 0, LOG_WARN, 0, NULL);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_str_op(mock_saved_log_at(0), OP_EQ, "TLS error: (null) "
+ "(in (null):(null):---)\n");
+
+ mock_clean_saved_logs();
+ tls->address = tor_strdup("127.hello");
+ tor_tls_log_one_error(tls, 0, LOG_WARN, 0, NULL);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_str_op(mock_saved_log_at(0), OP_EQ, "TLS error with 127.hello: (null) "
+ "(in (null):(null):---)\n");
+ tor_free(tls->address);
+
+ mock_clean_saved_logs();
+ tls->address = tor_strdup("127.hello");
+ tor_tls_log_one_error(tls, 0, LOG_WARN, 0, "blarg");
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_str_op(mock_saved_log_at(0), OP_EQ, "TLS error while blarg with "
+ "127.hello: (null) (in (null):(null):---)\n");
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, 3), LOG_WARN, 0, NULL);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_str_op(mock_saved_log_at(0), OP_EQ, "TLS error with 127.hello: "
+ "BN lib (in unknown library:(null):---)\n");
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_HTTP_REQUEST),
+ LOG_WARN, 0, NULL);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_int_op(mock_saved_severity_at(0), OP_EQ, LOG_INFO);
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_HTTPS_PROXY_REQUEST),
+ LOG_WARN, 0, NULL);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_int_op(mock_saved_severity_at(0), OP_EQ, LOG_INFO);
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_RECORD_LENGTH_MISMATCH),
+ LOG_WARN, 0, NULL);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_int_op(mock_saved_severity_at(0), OP_EQ, LOG_INFO);
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_RECORD_TOO_LARGE),
+ LOG_WARN, 0, NULL);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_int_op(mock_saved_severity_at(0), OP_EQ, LOG_INFO);
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_UNKNOWN_PROTOCOL),
+ LOG_WARN, 0, NULL);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_int_op(mock_saved_severity_at(0), OP_EQ, LOG_INFO);
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_UNSUPPORTED_PROTOCOL),
+ LOG_WARN, 0, NULL);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_int_op(mock_saved_severity_at(0), OP_EQ, LOG_INFO);
+
+ tls->ssl = SSL_new(ctx);
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, 0, LOG_WARN, 0, NULL);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_str_op(mock_saved_log_at(0), OP_EQ, "TLS error with 127.hello: (null)"
+ " (in (null):(null):" SSL_STATE_STR ")\n");
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+ if (tls && tls->ssl)
+ SSL_free(tls->ssl);
+ if (tls)
+ tor_free(tls->address);
+ tor_free(tls);
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_get_error(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ int ret;
+ SSL_CTX *ctx;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(SSLv23_method());
+ int previous_log = setup_capture_of_logs(LOG_INFO);
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = SSL_new(ctx);
+ SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL);
+
+ ret = tor_tls_get_error(tls, 0, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_IO);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_str_op(mock_saved_log_at(0), OP_EQ, "TLS error: unexpected close while"
+ " something (before/accept initialization)\n");
+
+ mock_clean_saved_logs();
+ ret = tor_tls_get_error(tls, 2, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 0);
+
+ mock_clean_saved_logs();
+ ret = tor_tls_get_error(tls, 0, 1, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, -11);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 0);
+
+ mock_clean_saved_logs();
+ ERR_clear_error();
+ ERR_put_error(ERR_LIB_BN, 2, -1, "somewhere.c", 99);
+ ret = tor_tls_get_error(tls, 0, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_str_op(mock_saved_log_at(0), OP_EQ, "TLS error while something: (null)"
+ " (in bignum routines:(null):before/accept initialization)\n");
+
+ mock_clean_saved_logs();
+ ERR_clear_error();
+ tls->ssl->rwstate = SSL_READING;
+ SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_READ;
+ ret = tor_tls_get_error(tls, -1, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, TOR_TLS_WANTREAD);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 0);
+
+ mock_clean_saved_logs();
+ ERR_clear_error();
+ tls->ssl->rwstate = SSL_READING;
+ SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_WRITE;
+ ret = tor_tls_get_error(tls, -1, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, TOR_TLS_WANTWRITE);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 0);
+
+ mock_clean_saved_logs();
+ ERR_clear_error();
+ tls->ssl->rwstate = 0;
+ tls->ssl->shutdown = SSL_RECEIVED_SHUTDOWN;
+ tls->ssl->s3->warn_alert =SSL_AD_CLOSE_NOTIFY;
+ ret = tor_tls_get_error(tls, 0, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, TOR_TLS_CLOSE);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+
+ mock_clean_saved_logs();
+ ret = tor_tls_get_error(tls, 0, 2, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, -10);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 0);
+
+ mock_clean_saved_logs();
+ ERR_put_error(ERR_LIB_SYS, 2, -1, "somewhere.c", 99);
+ ret = tor_tls_get_error(tls, -1, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, -9);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 2);
+ tt_str_op(mock_saved_log_at(1), OP_EQ,
+ "TLS error while something: (null) (in system library:"
+ "connect:before/accept initialization)\n");
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ SSL_free(tls->ssl);
+ tor_free(tls);
+ SSL_CTX_free(ctx);
+}
+#endif
+
+static void
+test_tortls_always_accept_verify_cb(void *ignored)
+{
+ (void)ignored;
+ int ret;
+
+ ret = always_accept_verify_cb(0, NULL);
+ tt_int_op(ret, OP_EQ, 1);
+
+ done:
+ (void)0;
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_x509_cert_free(void *ignored)
+{
+ (void)ignored;
+ tor_x509_cert_t *cert;
+
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ tor_x509_cert_free(cert);
+
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ cert->cert = tor_malloc_zero(sizeof(X509));
+ cert->encoded = tor_malloc_zero(1);
+ tor_x509_cert_free(cert);
+}
+#endif
+
+static void
+test_tortls_x509_cert_get_id_digests(void *ignored)
+{
+ (void)ignored;
+ tor_x509_cert_t *cert;
+ digests_t *d;
+ const digests_t *res;
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ d = tor_malloc_zero(sizeof(digests_t));
+ d->d[0][0] = 42;
+
+ res = tor_x509_cert_get_id_digests(cert);
+ tt_assert(!res);
+
+ cert->pkey_digests_set = 1;
+ cert->pkey_digests = *d;
+ res = tor_x509_cert_get_id_digests(cert);
+ tt_int_op(res->d[0][0], OP_EQ, 42);
+
+ done:
+ tor_free(cert);
+ tor_free(d);
+}
+
+#ifndef OPENSSL_OPAQUE
+static int
+fixed_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+ (void) a; (void) b;
+ return 1;
+}
+
+static void
+fake_x509_free(X509 *cert)
+{
+ if (cert) {
+ if (cert->cert_info) {
+ if (cert->cert_info->key) {
+ if (cert->cert_info->key->pkey) {
+ tor_free(cert->cert_info->key->pkey);
+ }
+ tor_free(cert->cert_info->key);
+ }
+ tor_free(cert->cert_info);
+ }
+ tor_free(cert);
+ }
+}
+
+static void
+test_tortls_cert_matches_key(void *ignored)
+{
+ (void)ignored;
+ int res;
+ tor_tls_t *tls;
+ tor_x509_cert_t *cert;
+ X509 *one = NULL, *two = NULL;
+ EVP_PKEY_ASN1_METHOD *meth = EVP_PKEY_asn1_new(999, 0, NULL, NULL);
+ EVP_PKEY_asn1_set_public(meth, NULL, NULL, fixed_pub_cmp, NULL, NULL, NULL);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ one = tor_malloc_zero(sizeof(X509));
+ one->references = 1;
+ two = tor_malloc_zero(sizeof(X509));
+ two->references = 1;
+
+ res = tor_tls_cert_matches_key(tls, cert);
+ tt_int_op(res, OP_EQ, 0);
+
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+ tls->ssl->session->peer = one;
+ res = tor_tls_cert_matches_key(tls, cert);
+ tt_int_op(res, OP_EQ, 0);
+
+ cert->cert = two;
+ res = tor_tls_cert_matches_key(tls, cert);
+ tt_int_op(res, OP_EQ, 0);
+
+ one->cert_info = tor_malloc_zero(sizeof(X509_CINF));
+ one->cert_info->key = tor_malloc_zero(sizeof(X509_PUBKEY));
+ one->cert_info->key->pkey = tor_malloc_zero(sizeof(EVP_PKEY));
+ one->cert_info->key->pkey->references = 1;
+ one->cert_info->key->pkey->ameth = meth;
+ one->cert_info->key->pkey->type = 1;
+
+ two->cert_info = tor_malloc_zero(sizeof(X509_CINF));
+ two->cert_info->key = tor_malloc_zero(sizeof(X509_PUBKEY));
+ two->cert_info->key->pkey = tor_malloc_zero(sizeof(EVP_PKEY));
+ two->cert_info->key->pkey->references = 1;
+ two->cert_info->key->pkey->ameth = meth;
+ two->cert_info->key->pkey->type = 2;
+
+ res = tor_tls_cert_matches_key(tls, cert);
+ tt_int_op(res, OP_EQ, 0);
+
+ one->cert_info->key->pkey->type = 1;
+ two->cert_info->key->pkey->type = 1;
+ res = tor_tls_cert_matches_key(tls, cert);
+ tt_int_op(res, OP_EQ, 1);
+
+ done:
+ EVP_PKEY_asn1_free(meth);
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ tor_free(cert);
+ fake_x509_free(one);
+ fake_x509_free(two);
+}
+
+static void
+test_tortls_cert_get_key(void *ignored)
+{
+ (void)ignored;
+ tor_x509_cert_t *cert = NULL;
+ crypto_pk_t *res = NULL;
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ X509 *key = NULL;
+ key = tor_malloc_zero(sizeof(X509));
+ key->references = 1;
+
+ res = tor_tls_cert_get_key(cert);
+ tt_assert(!res);
+
+ cert->cert = key;
+ key->cert_info = tor_malloc_zero(sizeof(X509_CINF));
+ key->cert_info->key = tor_malloc_zero(sizeof(X509_PUBKEY));
+ key->cert_info->key->pkey = tor_malloc_zero(sizeof(EVP_PKEY));
+ key->cert_info->key->pkey->references = 1;
+ key->cert_info->key->pkey->type = 2;
+ res = tor_tls_cert_get_key(cert);
+ tt_assert(!res);
+
+ done:
+ fake_x509_free(key);
+ tor_free(cert);
+ crypto_pk_free(res);
+}
+#endif
+
+static void
+test_tortls_get_my_client_auth_key(void *ignored)
+{
+ (void)ignored;
+ crypto_pk_t *ret;
+ crypto_pk_t *expected;
+ tor_tls_context_t *ctx;
+ RSA *k = tor_malloc_zero(sizeof(RSA));
+
+ ctx = tor_malloc_zero(sizeof(tor_tls_context_t));
+ expected = crypto_new_pk_from_rsa_(k);
+ ctx->auth_key = expected;
+
+ client_tls_context = NULL;
+ ret = tor_tls_get_my_client_auth_key();
+ tt_assert(!ret);
+
+ client_tls_context = ctx;
+ ret = tor_tls_get_my_client_auth_key();
+ tt_assert(ret == expected);
+
+ done:
+ tor_free(expected);
+ tor_free(k);
+ tor_free(ctx);
+}
+
+static void
+test_tortls_get_my_certs(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_context_t *ctx;
+ const tor_x509_cert_t *link_cert_out = NULL;
+ const tor_x509_cert_t *id_cert_out = NULL;
+
+ ctx = tor_malloc_zero(sizeof(tor_tls_context_t));
+
+ client_tls_context = NULL;
+ ret = tor_tls_get_my_certs(0, NULL, NULL);
+ tt_int_op(ret, OP_EQ, -1);
+
+ server_tls_context = NULL;
+ ret = tor_tls_get_my_certs(1, NULL, NULL);
+ tt_int_op(ret, OP_EQ, -1);
+
+ client_tls_context = ctx;
+ ret = tor_tls_get_my_certs(0, NULL, NULL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ client_tls_context = ctx;
+ ret = tor_tls_get_my_certs(0, &link_cert_out, &id_cert_out);
+ tt_int_op(ret, OP_EQ, 0);
+
+ server_tls_context = ctx;
+ ret = tor_tls_get_my_certs(1, &link_cert_out, &id_cert_out);
+ tt_int_op(ret, OP_EQ, 0);
+
+ done:
+ (void)1;
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_get_ciphersuite_name(void *ignored)
+{
+ (void)ignored;
+ const char *ret;
+ tor_tls_t *ctx;
+ ctx = tor_malloc_zero(sizeof(tor_tls_t));
+ ctx->ssl = tor_malloc_zero(sizeof(SSL));
+
+ ret = tor_tls_get_ciphersuite_name(ctx);
+ tt_str_op(ret, OP_EQ, "(NONE)");
+
+ done:
+ tor_free(ctx->ssl);
+ tor_free(ctx);
+}
+
+static SSL_CIPHER *
+get_cipher_by_name(const char *name)
+{
+ int i;
+ const SSL_METHOD *method = SSLv23_method();
+ int num = method->num_ciphers();
+ for (i = 0; i < num; ++i) {
+ const SSL_CIPHER *cipher = method->get_cipher(i);
+ const char *ciphername = SSL_CIPHER_get_name(cipher);
+ if (!strcmp(ciphername, name)) {
+ return (SSL_CIPHER *)cipher;
+ }
+ }
+
+ return NULL;
+}
+
+static SSL_CIPHER *
+get_cipher_by_id(uint16_t id)
+{
+ int i;
+ const SSL_METHOD *method = SSLv23_method();
+ int num = method->num_ciphers();
+ for (i = 0; i < num; ++i) {
+ const SSL_CIPHER *cipher = method->get_cipher(i);
+ if (id == (SSL_CIPHER_get_id(cipher) & 0xffff)) {
+ return (SSL_CIPHER *)cipher;
+ }
+ }
+
+ return NULL;
+}
+
+extern uint16_t v2_cipher_list[];
+
+static void
+test_tortls_classify_client_ciphers(void *ignored)
+{
+ (void)ignored;
+ int i;
+ int ret;
+ SSL_CTX *ctx;
+ SSL *ssl;
+ tor_tls_t *tls;
+ STACK_OF(SSL_CIPHER) *ciphers;
+ SSL_CIPHER *tmp_cipher;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->magic = TOR_TLS_MAGIC;
+
+ ctx = SSL_CTX_new(TLSv1_method());
+ ssl = SSL_new(ctx);
+ tls->ssl = ssl;
+
+ ciphers = sk_SSL_CIPHER_new_null();
+
+ ret = tor_tls_classify_client_ciphers(ssl, NULL);
+ tt_int_op(ret, OP_EQ, -1);
+
+ SSL_set_ex_data(ssl, tor_tls_object_ex_data_index, tls);
+ tls->client_cipher_list_type = 42;
+
+ ret = tor_tls_classify_client_ciphers(ssl, NULL);
+ tt_int_op(ret, OP_EQ, 42);
+
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 1);
+
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, SSL_get_ciphers(ssl));
+ tt_int_op(ret, OP_EQ, 3);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 3);
+
+ SSL_CIPHER *one = get_cipher_by_name(TLS1_TXT_DHE_RSA_WITH_AES_128_SHA),
+ *two = get_cipher_by_name(TLS1_TXT_DHE_RSA_WITH_AES_256_SHA),
+ *three = get_cipher_by_name(SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA),
+ *four = NULL;
+ sk_SSL_CIPHER_push(ciphers, one);
+ sk_SSL_CIPHER_push(ciphers, two);
+ sk_SSL_CIPHER_push(ciphers, three);
+ sk_SSL_CIPHER_push(ciphers, four);
+
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 1);
+
+ sk_SSL_CIPHER_zero(ciphers);
+
+ one = get_cipher_by_name("ECDH-RSA-AES256-GCM-SHA384");
+ one->id = 0x00ff;
+ two = get_cipher_by_name("ECDH-RSA-AES128-GCM-SHA256");
+ two->id = 0x0000;
+ sk_SSL_CIPHER_push(ciphers, one);
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 3);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 3);
+
+ sk_SSL_CIPHER_push(ciphers, two);
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 3);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 3);
+
+ one->id = 0xC00A;
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 3);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 3);
+
+ sk_SSL_CIPHER_zero(ciphers);
+ for (i=0; v2_cipher_list[i]; i++) {
+ tmp_cipher = get_cipher_by_id(v2_cipher_list[i]);
+ tt_assert(tmp_cipher);
+ sk_SSL_CIPHER_push(ciphers, tmp_cipher);
+ }
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 2);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 2);
+
+ done:
+ sk_SSL_CIPHER_free(ciphers);
+ SSL_free(tls->ssl);
+ tor_free(tls);
+ SSL_CTX_free(ctx);
+}
+#endif
+
+static void
+test_tortls_client_is_using_v2_ciphers(void *ignored)
+{
+ (void)ignored;
+
+#ifdef HAVE_SSL_GET_CLIENT_CIPHERS
+ tt_skip();
+ done:
+ (void)1;
+#else
+ int ret;
+ SSL_CTX *ctx;
+ SSL *ssl;
+ SSL_SESSION *sess;
+ STACK_OF(SSL_CIPHER) *ciphers;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(TLSv1_method());
+ ssl = SSL_new(ctx);
+ sess = SSL_SESSION_new();
+
+ ret = tor_tls_client_is_using_v2_ciphers(ssl);
+ tt_int_op(ret, OP_EQ, -1);
+
+ ssl->session = sess;
+ ret = tor_tls_client_is_using_v2_ciphers(ssl);
+ tt_int_op(ret, OP_EQ, 0);
+
+ ciphers = sk_SSL_CIPHER_new_null();
+ SSL_CIPHER *one = get_cipher_by_name("ECDH-RSA-AES256-GCM-SHA384");
+ one->id = 0x00ff;
+ sk_SSL_CIPHER_push(ciphers, one);
+ sess->ciphers = ciphers;
+ ret = tor_tls_client_is_using_v2_ciphers(ssl);
+ tt_int_op(ret, OP_EQ, 1);
+ done:
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+#endif
+}
+
+#ifndef OPENSSL_OPAQUE
+static X509 *fixed_try_to_extract_certs_from_tls_cert_out_result = NULL;
+static X509 *fixed_try_to_extract_certs_from_tls_id_cert_out_result = NULL;
+
+static void
+fixed_try_to_extract_certs_from_tls(int severity, tor_tls_t *tls,
+ X509 **cert_out, X509 **id_cert_out)
+{
+ (void) severity;
+ (void) tls;
+ *cert_out = fixed_try_to_extract_certs_from_tls_cert_out_result;
+ *id_cert_out = fixed_try_to_extract_certs_from_tls_id_cert_out_result;
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static const char* notCompletelyValidCertString =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICVjCCAb8CAg37MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG\n"
+ "A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE\n"
+ "MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl\n"
+ "YiBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBmcmFuazRkZC5jb20wHhcNMTIw\n"
+ "ODIyMDUyNzIzWhcNMTcwODIxMDUyNzIzWjBKMQswCQYDVQQGEwJKUDEOMAwGA1UE\n"
+ "CAwFVG9reW8xETAPBgNVBAoMCEZyYW5rNEREMRgwFgYDVQQDDA93d3cuZXhhbXBs\n"
+ "ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMYBBrx5PlP0WNI/ZdzD\n"
+ "+6Pktmurn+F2kQYbtc7XQh8/LTBvCo+P6iZoLEmUA9e7EXLRxgU1CVqeAi7QcAn9\n"
+ "MwBlc8ksFJHB0rtf9pmf8Oza9E0Bynlq/4/Kb1x+d+AyhL7oK9tQwB24uHOueHi1\n"
+ "C/iVv8CSWKiYe6hzN1txYe8rAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAASPdjigJ\n"
+ "kXCqKWpnZ/Oc75EUcMi6HztaW8abUMlYXPIgkV2F7YanHOB7K4f7OOLjiz8DTPFf\n"
+ "jC9UeuErhaA/zzWi8ewMTFZW/WshOrm3fNvcMrMLKtH534JKvcdMg6qIdjTFINIr\n"
+ "evnAhf0cwULaebn+lMs8Pdl7y37+sfluVok=\n"
+ "-----END CERTIFICATE-----\n";
+#endif
+
+static const char* validCertString = "-----BEGIN CERTIFICATE-----\n"
+ "MIIDpTCCAY0CAg3+MA0GCSqGSIb3DQEBBQUAMF4xCzAJBgNVBAYTAlVTMREwDwYD\n"
+ "VQQIDAhJbGxpbm9pczEQMA4GA1UEBwwHQ2hpY2FnbzEUMBIGA1UECgwLVG9yIFRl\n"
+ "c3RpbmcxFDASBgNVBAMMC1RvciBUZXN0aW5nMB4XDTE1MDkwNjEzMzk1OVoXDTQz\n"
+ "MDEyMjEzMzk1OVowVjELMAkGA1UEBhMCVVMxEDAOBgNVBAcMB0NoaWNhZ28xFDAS\n"
+ "BgNVBAoMC1RvciBUZXN0aW5nMR8wHQYDVQQDDBZ0ZXN0aW5nLnRvcnByb2plY3Qu\n"
+ "b3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDoT6uyVVhWyOF3wkHjjYbd\n"
+ "nKaykyRv4JVtKQdZ4OpEErmX1zw4MmyzpQNV6iR4bQnWiyLfzyVJMZDIC/WILBfX\n"
+ "w2Pza/yuLgUvDc3twMuhOACzOQVO8PrEF/aVv2+hbCCy2udXvKhnYn+CCXl3ozc8\n"
+ "XcKYvujTXDyvGWY3xwAjlQIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQCUvnhzQWuQ\n"
+ "MrN+pERkE+zcTI/9dGS90rUMMLgu8VDNqTa0TUQh8uO0EQ6uDvI8Js6e8tgwS0BR\n"
+ "UBahqb7ZHv+rejGCBr5OudqD+x4STiiuPNJVs86JTLN8SpM9CHjIBH5WCCN2KOy3\n"
+ "mevNoRcRRyYJzSFULCunIK6FGulszigMYGscrO4oiTkZiHPh9KvWT40IMiHfL+Lw\n"
+ "EtEWiLex6064LcA2YQ1AMuSZyCexks63lcfaFmQbkYOKqXa1oLkIRuDsOaSVjTfe\n"
+ "vec+X6jvf12cFTKS5WIeqkKF2Irt+dJoiHEGTe5RscUMN/f+gqHPzfFz5dR23sxo\n"
+ "g+HC6MZHlFkLAOx3wW6epPS8A/m1mw3zMPoTnb2U2YYt8T0dJMMlUn/7Y1sEAa+a\n"
+ "dSTMaeUf6VnJ//11m454EZl1to9Z7oJOgqmFffSrdD4BGIWe8f7hhW6L1Enmqe/J\n"
+ "BKL3wbzZh80O1W0bndAwhnEEhlzneFY84cbBo9pmVxpODHkUcStpr5Z7pBDrcL21\n"
+ "Ss/aB/1YrsVXhdvJdOGxl3Mnl9dUY57CympLGlT8f0pPS6GAKOelECOhFMHmJd8L\n"
+ "dj3XQSmKtYHevZ6IvuMXSlB/fJvSjSlkCuLo5+kJoaqPuRu+i/S1qxeRy3CBwmnE\n"
+ "LdSNdcX4N79GQJ996PA8+mUCQG7YRtK+WA==\n"
+ "-----END CERTIFICATE-----\n";
+
+static const char* caCertString = "-----BEGIN CERTIFICATE-----\n"
+ "MIIFjzCCA3egAwIBAgIJAKd5WgyfPMYRMA0GCSqGSIb3DQEBCwUAMF4xCzAJBgNV\n"
+ "BAYTAlVTMREwDwYDVQQIDAhJbGxpbm9pczEQMA4GA1UEBwwHQ2hpY2FnbzEUMBIG\n"
+ "A1UECgwLVG9yIFRlc3RpbmcxFDASBgNVBAMMC1RvciBUZXN0aW5nMB4XDTE1MDkw\n"
+ "NjEzMzc0MVoXDTQzMDEyMjEzMzc0MVowXjELMAkGA1UEBhMCVVMxETAPBgNVBAgM\n"
+ "CElsbGlub2lzMRAwDgYDVQQHDAdDaGljYWdvMRQwEgYDVQQKDAtUb3IgVGVzdGlu\n"
+ "ZzEUMBIGA1UEAwwLVG9yIFRlc3RpbmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw\n"
+ "ggIKAoICAQCpLMUEiLW5leUgBZoEJms2V7lZRhIAjnJBhVMHD0e3UubNknmaQoxf\n"
+ "ARz3rvqOaRd0JlV+qM9qE0DjiYcCVP1cAfqAo9d83uS1vwY3YMVJzADlaIiHfyVW\n"
+ "uEgBy0vvkeUBqaua24dYlcwsemOiXYLu41yM1wkcGHW1AhBNHppY6cznb8TyLgNM\n"
+ "2x3SGUdzc5XMyAFx51faKGBA3wjs+Hg1PLY7d30nmCgEOBavpm5I1disM/0k+Mcy\n"
+ "YmAKEo/iHJX/rQzO4b9znP69juLlR8PDBUJEVIG/CYb6+uw8MjjUyiWXYoqfVmN2\n"
+ "hm/lH8b6rXw1a2Aa3VTeD0DxaWeacMYHY/i01fd5n7hCoDTRNdSw5KJ0L3Z0SKTu\n"
+ "0lzffKzDaIfyZGlpW5qdouACkWYzsaitQOePVE01PIdO30vUfzNTFDfy42ccx3Di\n"
+ "59UCu+IXB+eMtrBfsok0Qc63vtF1linJgjHW1z/8ujk8F7/qkOfODhk4l7wngc2A\n"
+ "EmwWFIFoGaiTEZHB9qteXr4unbXZ0AHpM02uGGwZEGohjFyebEb73M+J57WKKAFb\n"
+ "PqbLcGUksL1SHNBNAJcVLttX55sO4nbidOS/kA3m+F1R04MBTyQF9qA6YDDHqdI3\n"
+ "h/3pw0Z4fxVouTYT4/NfRnX4JTP4u+7Mpcoof28VME0qWqD1LnRhFQIDAQABo1Aw\n"
+ "TjAdBgNVHQ4EFgQUMoAgIXH7pZ3QMRwTjT+DM9Yo/v0wHwYDVR0jBBgwFoAUMoAg\n"
+ "IXH7pZ3QMRwTjT+DM9Yo/v0wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC\n"
+ "AgEAUJxacjXR9sT+Xs6ISFiUsyd0T6WVKMnV46xrYJHirGfx+krWHrjxMY+ZtxYD\n"
+ "DBDGlo11Qc4v6QrclNf5QUBfIiGQsP9Cm6hHcQ+Tpg9HHCgSqG1YNPwCPReCR4br\n"
+ "BLvLfrfkcBL2IWM0PdQdCze+59DBfipsULD2mEn9fjYRXQEwb2QWtQ9qRc20Yb/x\n"
+ "Q4b/+CvUodLkaq7B8MHz0BV8HHcBoph6DYaRmO/N+hPauIuSp6XyaGYcEefGKVKj\n"
+ "G2+fcsdyXsoijNdL8vNKwm4j2gVwCBnw16J00yfFoV46YcbfqEdJB2je0XSvwXqt\n"
+ "14AOTngxso2h9k9HLtrfpO1ZG/B5AcCMs1lzbZ2fp5DPHtjvvmvA2RJqgo3yjw4W\n"
+ "4DHAuTglYFlC3mDHNfNtcGP20JvepcQNzNP2UzwcpOc94hfKikOFw+gf9Vf1qd0y\n"
+ "h/Sk6OZHn2+JVUPiWHIQV98Vtoh4RmUZDJD+b55ia3fQGTGzt4z1XFzQYSva5sfs\n"
+ "wocS/papthqWldQU7x+3wofNd5CNU1x6WKXG/yw30IT/4F8ADJD6GeygNT8QJYvt\n"
+ "u/8lAkbOy6B9xGmSvr0Kk1oq9P2NshA6kalxp1Oz/DTNDdL4AeBXV3JmM6WWCjGn\n"
+ "Yy1RT69d0rwYc5u/vnqODz1IjvT90smsrkBumGt791FAFeg=\n"
+ "-----END CERTIFICATE-----\n";
+
+static X509 *
+read_cert_from(const char *str)
+{
+ BIO *bio = BIO_new(BIO_s_mem());
+ BIO_write(bio, str, (int) strlen(str));
+ X509 *res = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ return res;
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_verify(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ crypto_pk_t *k = NULL;
+ X509 *cert1 = NULL, *cert2 = NULL, *invalidCert = NULL,
+ *validCert = NULL, *caCert = NULL;
+
+ cert1 = tor_malloc_zero(sizeof(X509));
+ cert1->references = 10;
+
+ cert2 = tor_malloc_zero(sizeof(X509));
+ cert2->references = 10;
+
+ validCert = read_cert_from(validCertString);
+ caCert = read_cert_from(caCertString);
+ invalidCert = read_cert_from(notCompletelyValidCertString);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ ret = tor_tls_verify(LOG_WARN, tls, &k);
+ tt_int_op(ret, OP_EQ, -1);
+
+ MOCK(try_to_extract_certs_from_tls, fixed_try_to_extract_certs_from_tls);
+
+ fixed_try_to_extract_certs_from_tls_cert_out_result = cert1;
+ ret = tor_tls_verify(LOG_WARN, tls, &k);
+ tt_int_op(ret, OP_EQ, -1);
+
+ fixed_try_to_extract_certs_from_tls_id_cert_out_result = cert2;
+ ret = tor_tls_verify(LOG_WARN, tls, &k);
+ tt_int_op(ret, OP_EQ, -1);
+
+ fixed_try_to_extract_certs_from_tls_cert_out_result = invalidCert;
+ fixed_try_to_extract_certs_from_tls_id_cert_out_result = invalidCert;
+
+ ret = tor_tls_verify(LOG_WARN, tls, &k);
+ tt_int_op(ret, OP_EQ, -1);
+
+ fixed_try_to_extract_certs_from_tls_cert_out_result = validCert;
+ fixed_try_to_extract_certs_from_tls_id_cert_out_result = caCert;
+
+ ret = tor_tls_verify(LOG_WARN, tls, &k);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(k);
+
+ done:
+ UNMOCK(try_to_extract_certs_from_tls);
+ tor_free(cert1);
+ tor_free(cert2);
+ tor_free(tls);
+ tor_free(k);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_check_lifetime(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ X509 *validCert = read_cert_from(validCertString);
+ time_t now = time(NULL);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ ret = tor_tls_check_lifetime(LOG_WARN, tls, 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+ tls->ssl->session->peer = validCert;
+ ret = tor_tls_check_lifetime(LOG_WARN, tls, 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ validCert->cert_info->validity->notBefore = ASN1_TIME_set(NULL, now-10);
+ validCert->cert_info->validity->notAfter = ASN1_TIME_set(NULL, now+60);
+
+ ret = tor_tls_check_lifetime(LOG_WARN, tls, 0, -1000);
+ tt_int_op(ret, OP_EQ, -1);
+
+ ret = tor_tls_check_lifetime(LOG_WARN, tls, -1000, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ done:
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static int fixed_ssl_pending_result = 0;
+
+static int
+fixed_ssl_pending(const SSL *ignored)
+{
+ (void)ignored;
+ return fixed_ssl_pending_result;
+}
+
+static void
+test_tortls_get_pending_bytes(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ SSL_METHOD *method;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ method = tor_malloc_zero(sizeof(SSL_METHOD));
+ method->ssl_pending = fixed_ssl_pending;
+ tls->ssl->method = method;
+
+ fixed_ssl_pending_result = 42;
+ ret = tor_tls_get_pending_bytes(tls);
+ tt_int_op(ret, OP_EQ, 42);
+
+ done:
+ tor_free(method);
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif
+
+static void
+test_tortls_get_forced_write_size(void *ignored)
+{
+ (void)ignored;
+ long ret;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ tls->wantwrite_n = 43;
+ ret = tor_tls_get_forced_write_size(tls);
+ tt_int_op(ret, OP_EQ, 43);
+
+ done:
+ tor_free(tls);
+}
+
+extern uint64_t total_bytes_written_over_tls;
+extern uint64_t total_bytes_written_by_tls;
+
+static void
+test_tortls_get_write_overhead_ratio(void *ignored)
+{
+ (void)ignored;
+ double ret;
+
+ total_bytes_written_over_tls = 0;
+ ret = tls_get_write_overhead_ratio();
+ tt_int_op(ret, OP_EQ, 1.0);
+
+ total_bytes_written_by_tls = 10;
+ total_bytes_written_over_tls = 1;
+ ret = tls_get_write_overhead_ratio();
+ tt_int_op(ret, OP_EQ, 10.0);
+
+ total_bytes_written_by_tls = 10;
+ total_bytes_written_over_tls = 2;
+ ret = tls_get_write_overhead_ratio();
+ tt_int_op(ret, OP_EQ, 5.0);
+
+ done:
+ (void)0;
+}
+
+static void
+test_tortls_used_v1_handshake(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ // These tests assume both V2 handshake server and client are enabled
+ tls->wasV2Handshake = 0;
+ ret = tor_tls_used_v1_handshake(tls);
+ tt_int_op(ret, OP_EQ, 1);
+
+ tls->wasV2Handshake = 1;
+ ret = tor_tls_used_v1_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+
+ done:
+ tor_free(tls);
+}
+
+static void
+test_tortls_get_num_server_handshakes(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ tls->server_handshake_count = 3;
+ ret = tor_tls_get_num_server_handshakes(tls);
+ tt_int_op(ret, OP_EQ, 3);
+
+ done:
+ tor_free(tls);
+}
+
+static void
+test_tortls_server_got_renegotiate(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ tls->got_renegotiate = 1;
+ ret = tor_tls_server_got_renegotiate(tls);
+ tt_int_op(ret, OP_EQ, 1);
+
+ done:
+ tor_free(tls);
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_SSL_SESSION_get_master_key(void *ignored)
+{
+ (void)ignored;
+ size_t ret;
+ tor_tls_t *tls;
+ uint8_t *out;
+ out = tor_malloc_zero(1);
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+ tls->ssl->session->master_key_length = 1;
+
+#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY
+ tls->ssl->session->master_key[0] = 43;
+ ret = SSL_SESSION_get_master_key(tls->ssl->session, out, 0);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_int_op(out[0], OP_EQ, 0);
+
+ ret = SSL_SESSION_get_master_key(tls->ssl->session, out, 1);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_int_op(out[0], OP_EQ, 43);
+
+ done:
+#endif
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ tor_free(out);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_get_tlssecrets(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ uint8_t *secret_out = tor_malloc_zero(DIGEST256_LEN);;
+ tor_tls_t *tls;
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+ tls->ssl->session->master_key_length = 1;
+ tls->ssl->s3 = tor_malloc_zero(sizeof(SSL3_STATE));
+
+ ret = tor_tls_get_tlssecrets(tls, secret_out);
+ tt_int_op(ret, OP_EQ, 0);
+
+ done:
+ tor_free(secret_out);
+ tor_free(tls->ssl->s3);
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_get_buffer_sizes(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ size_t rbuf_c=-1, rbuf_b=-1, wbuf_c=-1, wbuf_b=-1;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->s3 = tor_malloc_zero(sizeof(SSL3_STATE));
+
+ tls->ssl->s3->rbuf.buf = NULL;
+ tls->ssl->s3->rbuf.len = 1;
+ tls->ssl->s3->rbuf.offset = 0;
+ tls->ssl->s3->rbuf.left = 42;
+
+ tls->ssl->s3->wbuf.buf = NULL;
+ tls->ssl->s3->wbuf.len = 2;
+ tls->ssl->s3->wbuf.offset = 0;
+ tls->ssl->s3->wbuf.left = 43;
+
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)
+ ret = tor_tls_get_buffer_sizes(NULL, NULL, NULL, NULL, NULL);
+ tt_int_op(ret, OP_EQ, -1);
+#else
+ ret = tor_tls_get_buffer_sizes(tls, &rbuf_c, &rbuf_b, &wbuf_c, &wbuf_b);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(rbuf_c, OP_EQ, 0);
+ tt_int_op(wbuf_c, OP_EQ, 0);
+ tt_int_op(rbuf_b, OP_EQ, 42);
+ tt_int_op(wbuf_b, OP_EQ, 43);
+
+ tls->ssl->s3->rbuf.buf = tor_malloc_zero(1);
+ tls->ssl->s3->wbuf.buf = tor_malloc_zero(1);
+ ret = tor_tls_get_buffer_sizes(tls, &rbuf_c, &rbuf_b, &wbuf_c, &wbuf_b);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(rbuf_c, OP_EQ, 1);
+ tt_int_op(wbuf_c, OP_EQ, 2);
+
+#endif
+
+ done:
+ tor_free(tls->ssl->s3->rbuf.buf);
+ tor_free(tls->ssl->s3->wbuf.buf);
+ tor_free(tls->ssl->s3);
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif
+
+static void
+test_tortls_evaluate_ecgroup_for_tls(void *ignored)
+{
+ (void)ignored;
+ int ret;
+
+ ret = evaluate_ecgroup_for_tls(NULL);
+ tt_int_op(ret, OP_EQ, 1);
+
+ ret = evaluate_ecgroup_for_tls("foobar");
+ tt_int_op(ret, OP_EQ, 0);
+
+ ret = evaluate_ecgroup_for_tls("P256");
+ tt_int_op(ret, OP_EQ, 1);
+
+ ret = evaluate_ecgroup_for_tls("P224");
+ // tt_int_op(ret, OP_EQ, 1); This varies between machines
+
+ done:
+ (void)0;
+}
+
+#ifndef OPENSSL_OPAQUE
+typedef struct cert_pkey_st_local
+{
+ X509 *x509;
+ EVP_PKEY *privatekey;
+ const EVP_MD *digest;
+} CERT_PKEY_local;
+
+typedef struct sess_cert_st_local
+{
+ STACK_OF(X509) *cert_chain;
+ int peer_cert_type;
+ CERT_PKEY_local *peer_key;
+ CERT_PKEY_local peer_pkeys[8];
+ int references;
+} SESS_CERT_local;
+
+static void
+test_tortls_try_to_extract_certs_from_tls(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ X509 *cert = NULL, *id_cert = NULL, *c1 = NULL, *c2 = NULL;
+ SESS_CERT_local *sess = NULL;
+
+ c1 = read_cert_from(validCertString);
+ c2 = read_cert_from(caCertString);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+ sess = tor_malloc_zero(sizeof(SESS_CERT_local));
+ tls->ssl->session->sess_cert = (void *)sess;
+
+ try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert);
+ tt_assert(!cert);
+ tt_assert(!id_cert);
+
+ tls->ssl->session->peer = c1;
+ try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert);
+ tt_assert(cert == c1);
+ tt_assert(!id_cert);
+
+ sess->cert_chain = sk_X509_new_null();
+ try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert);
+ tt_assert(cert == c1);
+ tt_assert(!id_cert);
+
+ sk_X509_push(sess->cert_chain, c1);
+ sk_X509_push(sess->cert_chain, c2);
+ try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert);
+ tt_assert(cert == c1);
+ tt_assert(id_cert);
+
+ done:
+ tor_free(sess);
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_get_peer_cert(void *ignored)
+{
+ (void)ignored;
+ tor_x509_cert_t *ret;
+ tor_tls_t *tls;
+ X509 *cert = NULL;
+
+ cert = read_cert_from(validCertString);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+
+ ret = tor_tls_get_peer_cert(tls);
+ tt_assert(!ret);
+
+ tls->ssl->session->peer = cert;
+ ret = tor_tls_get_peer_cert(tls);
+ tt_assert(ret);
+ tt_assert(ret->cert == cert);
+
+ done:
+ tor_x509_cert_free(ret);
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ X509_free(cert);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_peer_has_cert(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ X509 *cert = NULL;
+
+ cert = read_cert_from(validCertString);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+
+ ret = tor_tls_peer_has_cert(tls);
+ tt_assert(!ret);
+
+ tls->ssl->session->peer = cert;
+ ret = tor_tls_peer_has_cert(tls);
+ tt_assert(ret);
+
+ done:
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ X509_free(cert);
+}
+#endif
+
+static void
+test_tortls_is_server(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ int ret;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->isServer = 1;
+ ret = tor_tls_is_server(tls);
+ tt_int_op(ret, OP_EQ, 1);
+
+ done:
+ tor_free(tls);
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_session_secret_cb(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ SSL_CTX *ctx;
+ STACK_OF(SSL_CIPHER) *ciphers = NULL;
+ SSL_CIPHER *one;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ tls->magic = TOR_TLS_MAGIC;
+
+ ctx = SSL_CTX_new(TLSv1_method());
+ tls->ssl = SSL_new(ctx);
+ SSL_set_ex_data(tls->ssl, tor_tls_object_ex_data_index, tls);
+
+ SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL);
+
+ tor_tls_session_secret_cb(tls->ssl, NULL, NULL, NULL, NULL, NULL);
+ tt_assert(!tls->ssl->tls_session_secret_cb);
+
+ one = get_cipher_by_name("ECDH-RSA-AES256-GCM-SHA384");
+ one->id = 0x00ff;
+ ciphers = sk_SSL_CIPHER_new_null();
+ sk_SSL_CIPHER_push(ciphers, one);
+
+ tls->client_cipher_list_type = 0;
+ tor_tls_session_secret_cb(tls->ssl, NULL, NULL, ciphers, NULL, NULL);
+ tt_assert(!tls->ssl->tls_session_secret_cb);
+
+ done:
+ sk_SSL_CIPHER_free(ciphers);
+ SSL_free(tls->ssl);
+ SSL_CTX_free(ctx);
+ tor_free(tls);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+/* TODO: It seems block_renegotiation and unblock_renegotiation and
+ * using different blags. This might not be correct */
+static void
+test_tortls_block_renegotiation(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->s3 = tor_malloc_zero(sizeof(SSL3_STATE));
+ tls->ssl->s3->flags = 0x0010;
+
+ tor_tls_block_renegotiation(tls);
+
+ tt_assert(!(SSL_get_options(tls->ssl) & 0x0010));
+
+ done:
+ tor_free(tls->ssl->s3);
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+
+static void
+test_tortls_unblock_renegotiation(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tor_tls_unblock_renegotiation(tls);
+
+ tt_assert(SSL_get_options(tls->ssl) & 0x00040000L);
+
+ done:
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_assert_renegotiation_unblocked(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tor_tls_unblock_renegotiation(tls);
+ tor_tls_assert_renegotiation_unblocked(tls);
+ /* No assertion here - this test will fail if tor_assert is turned on
+ * and things are bad. */
+
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif
+
+static void
+test_tortls_set_logged_address(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ tor_tls_set_logged_address(tls, "foo bar");
+
+ tt_str_op(tls->address, OP_EQ, "foo bar");
+
+ tor_tls_set_logged_address(tls, "foo bar 2");
+ tt_str_op(tls->address, OP_EQ, "foo bar 2");
+
+ done:
+ tor_free(tls->address);
+ tor_free(tls);
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+example_cb(tor_tls_t *t, void *arg)
+{
+ (void)t;
+ (void)arg;
+}
+
+static void
+test_tortls_set_renegotiate_callback(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ const char *arg = "hello";
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+
+ tor_tls_set_renegotiate_callback(tls, example_cb, (void*)arg);
+ tt_assert(tls->negotiated_callback == example_cb);
+ tt_assert(tls->callback_arg == arg);
+ tt_assert(!tls->got_renegotiate);
+
+ /* Assumes V2_HANDSHAKE_SERVER */
+ tt_assert(tls->ssl->info_callback == tor_tls_server_info_callback);
+
+ tor_tls_set_renegotiate_callback(tls, NULL, (void*)arg);
+ tt_assert(tls->ssl->info_callback == tor_tls_debug_state_callback);
+
+ done:
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static SSL_CIPHER *fixed_cipher1 = NULL;
+static SSL_CIPHER *fixed_cipher2 = NULL;
+static const SSL_CIPHER *
+fake_get_cipher(unsigned ncipher)
+{
+
+ switch (ncipher) {
+ case 1:
+ return fixed_cipher1;
+ case 2:
+ return fixed_cipher2;
+ default:
+ return NULL;
+ }
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_find_cipher_by_id(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ SSL *ssl;
+ SSL_CTX *ctx;
+ const SSL_METHOD *m = TLSv1_method();
+ SSL_METHOD *empty_method = tor_malloc_zero(sizeof(SSL_METHOD));
+
+ fixed_cipher1 = tor_malloc_zero(sizeof(SSL_CIPHER));
+ fixed_cipher2 = tor_malloc_zero(sizeof(SSL_CIPHER));
+ fixed_cipher2->id = 0xC00A;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(m);
+ ssl = SSL_new(ctx);
+
+ ret = find_cipher_by_id(ssl, NULL, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ ret = find_cipher_by_id(ssl, m, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ ret = find_cipher_by_id(ssl, m, 0xFFFF);
+ tt_int_op(ret, OP_EQ, 0);
+
+ ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ ret = find_cipher_by_id(ssl, empty_method, 0xFFFF);
+#ifdef HAVE_SSL_CIPHER_FIND
+ tt_int_op(ret, OP_EQ, 0);
+#else
+ tt_int_op(ret, OP_EQ, 1);
+#endif
+
+ empty_method->get_cipher = fake_get_cipher;
+ ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ empty_method->get_cipher = m->get_cipher;
+ empty_method->num_ciphers = m->num_ciphers;
+ ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ empty_method->get_cipher = fake_get_cipher;
+ empty_method->num_ciphers = m->num_ciphers;
+ ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ empty_method->num_ciphers = fake_num_ciphers;
+ ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
+#ifdef HAVE_SSL_CIPHER_FIND
+ tt_int_op(ret, OP_EQ, 1);
+#else
+ tt_int_op(ret, OP_EQ, 0);
+#endif
+
+ done:
+ tor_free(empty_method);
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+ tor_free(fixed_cipher1);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_debug_state_callback(void *ignored)
+{
+ (void)ignored;
+ SSL *ssl;
+ char *buf = tor_malloc_zero(1000);
+ int n;
+
+ int previous_log = setup_capture_of_logs(LOG_DEBUG);
+
+ ssl = tor_malloc_zero(sizeof(SSL));
+
+ tor_tls_debug_state_callback(ssl, 32, 45);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ n = snprintf(buf, 1000, "SSL %p is now in state unknown"
+ " state [type=32,val=45].\n", ssl);
+ buf[n]='\0';
+ if (strcasecmp(mock_saved_log_at(0), buf))
+ tt_str_op(mock_saved_log_at(0), OP_EQ, buf);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ tor_free(buf);
+ tor_free(ssl);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_server_info_callback(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ SSL_CTX *ctx;
+ SSL *ssl;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(TLSv1_method());
+ ssl = SSL_new(ctx);
+
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->magic = TOR_TLS_MAGIC;
+ tls->ssl = ssl;
+
+ tor_tls_server_info_callback(NULL, 0, 0);
+
+ SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_A);
+ mock_clean_saved_logs();
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_str_op(mock_saved_log_at(0), OP_EQ,
+ "Couldn't look up the tls for an SSL*. How odd!\n");
+
+ SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_B);
+ mock_clean_saved_logs();
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 1);
+ tt_str_op(mock_saved_log_at(0), OP_EQ,
+ "Couldn't look up the tls for an SSL*. How odd!\n");
+
+ SSL_set_state(ssl, 99);
+ mock_clean_saved_logs();
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 0);
+
+ SSL_set_ex_data(tls->ssl, tor_tls_object_ex_data_index, tls);
+ SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_B);
+ tls->negotiated_callback = 0;
+ tls->server_handshake_count = 120;
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ tt_int_op(tls->server_handshake_count, OP_EQ, 121);
+
+ tls->server_handshake_count = 127;
+ tls->negotiated_callback = (void *)1;
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ tt_int_op(tls->server_handshake_count, OP_EQ, 127);
+ tt_int_op(tls->got_renegotiate, OP_EQ, 1);
+
+ tls->ssl->session = SSL_SESSION_new();
+ tls->wasV2Handshake = 0;
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ tt_int_op(tls->wasV2Handshake, OP_EQ, 0);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+ tor_free(tls);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static int fixed_ssl_read_result_index;
+static int fixed_ssl_read_result[5];
+static int fixed_ssl_shutdown_result;
+
+static int
+fixed_ssl_read(SSL *s, void *buf, int len)
+{
+ (void)s;
+ (void)buf;
+ (void)len;
+ return fixed_ssl_read_result[fixed_ssl_read_result_index++];
+}
+
+static int
+fixed_ssl_shutdown(SSL *s)
+{
+ (void)s;
+ return fixed_ssl_shutdown_result;
+}
+
+static int fixed_ssl_state_to_set;
+static tor_tls_t *fixed_tls;
+
+static int
+setting_version_ssl_shutdown(SSL *s)
+{
+ s->version = SSL2_VERSION;
+ return fixed_ssl_shutdown_result;
+}
+
+static int
+setting_version_and_state_ssl_shutdown(SSL *s)
+{
+ fixed_tls->state = fixed_ssl_state_to_set;
+ s->version = SSL2_VERSION;
+ return fixed_ssl_shutdown_result;
+}
+
+static int
+dummy_handshake_func(SSL *s)
+{
+ (void)s;
+ return 1;
+}
+
+static void
+test_tortls_shutdown(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ SSL_METHOD *method = give_me_a_test_method();
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->method = method;
+ method->ssl_read = fixed_ssl_read;
+ method->ssl_shutdown = fixed_ssl_shutdown;
+
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, -9);
+
+ tls->state = TOR_TLS_ST_SENTCLOSE;
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = -1;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, -9);
+
+ tls->ssl->handshake_func = dummy_handshake_func;
+
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = 42;
+ fixed_ssl_read_result[2] = 0;
+ fixed_ssl_shutdown_result = 1;
+ ERR_clear_error();
+ tls->ssl->version = SSL2_VERSION;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_DONE);
+ tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED);
+
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = 42;
+ fixed_ssl_read_result[2] = 0;
+ fixed_ssl_shutdown_result = 0;
+ ERR_clear_error();
+ tls->ssl->version = 0;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_DONE);
+ tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED);
+
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = 42;
+ fixed_ssl_read_result[2] = 0;
+ fixed_ssl_shutdown_result = 0;
+ ERR_clear_error();
+ tls->ssl->version = 0;
+ method->ssl_shutdown = setting_version_ssl_shutdown;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = 42;
+ fixed_ssl_read_result[2] = 0;
+ fixed_ssl_shutdown_result = 0;
+ fixed_tls = tls;
+ fixed_ssl_state_to_set = TOR_TLS_ST_GOTCLOSE;
+ ERR_clear_error();
+ tls->ssl->version = 0;
+ method->ssl_shutdown = setting_version_and_state_ssl_shutdown;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = 42;
+ fixed_ssl_read_result[2] = 0;
+ fixed_ssl_read_result[3] = -1;
+ fixed_ssl_shutdown_result = 0;
+ fixed_tls = tls;
+ fixed_ssl_state_to_set = 0;
+ ERR_clear_error();
+ tls->ssl->version = 0;
+ method->ssl_shutdown = setting_version_and_state_ssl_shutdown;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ tor_free(method);
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+
+static int negotiated_callback_called;
+
+static void
+negotiated_callback_setter(tor_tls_t *t, void *arg)
+{
+ (void)t;
+ (void)arg;
+ negotiated_callback_called++;
+}
+
+static void
+test_tortls_read(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ char buf[100];
+ SSL_METHOD *method = give_me_a_test_method();
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->state = TOR_TLS_ST_OPEN;
+
+ ret = tor_tls_read(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, -9);
+
+ /* These tests assume that V2_HANDSHAKE_SERVER is set */
+ tls->ssl->handshake_func = dummy_handshake_func;
+ tls->ssl->method = method;
+ method->ssl_read = fixed_ssl_read;
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 42;
+ tls->state = TOR_TLS_ST_OPEN;
+ ERR_clear_error();
+ ret = tor_tls_read(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, 42);
+
+ tls->state = TOR_TLS_ST_OPEN;
+ tls->got_renegotiate = 1;
+ fixed_ssl_read_result_index = 0;
+ ERR_clear_error();
+ ret = tor_tls_read(tls, buf, 10);
+ tt_int_op(tls->got_renegotiate, OP_EQ, 0);
+
+ tls->state = TOR_TLS_ST_OPEN;
+ tls->got_renegotiate = 1;
+ negotiated_callback_called = 0;
+ tls->negotiated_callback = negotiated_callback_setter;
+ fixed_ssl_read_result_index = 0;
+ ERR_clear_error();
+ ret = tor_tls_read(tls, buf, 10);
+ tt_int_op(negotiated_callback_called, OP_EQ, 1);
+
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 0;
+ tls->ssl->version = SSL2_VERSION;
+ ERR_clear_error();
+ ret = tor_tls_read(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, TOR_TLS_CLOSE);
+ tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED);
+
+ // TODO: fill up
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ tor_free(method);
+}
+
+static int fixed_ssl_write_result;
+
+static int
+fixed_ssl_write(SSL *s, const void *buf, int len)
+{
+ (void)s;
+ (void)buf;
+ (void)len;
+ return fixed_ssl_write_result;
+}
+
+static void
+test_tortls_write(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ SSL_METHOD *method = give_me_a_test_method();
+ char buf[100];
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->state = TOR_TLS_ST_OPEN;
+
+ ret = tor_tls_write(tls, buf, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ ret = tor_tls_write(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, -9);
+
+ tls->ssl->method = method;
+ tls->wantwrite_n = 1;
+ ret = tor_tls_write(tls, buf, 10);
+ tt_int_op(tls->wantwrite_n, OP_EQ, 0);
+
+ method->ssl_write = fixed_ssl_write;
+ tls->ssl->handshake_func = dummy_handshake_func;
+ fixed_ssl_write_result = 1;
+ ERR_clear_error();
+ ret = tor_tls_write(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, 1);
+
+ fixed_ssl_write_result = -1;
+ ERR_clear_error();
+ tls->ssl->rwstate = SSL_READING;
+ SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL);
+ SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_READ;
+ ret = tor_tls_write(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, TOR_TLS_WANTREAD);
+
+ ERR_clear_error();
+ tls->ssl->rwstate = SSL_READING;
+ SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL);
+ SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_WRITE;
+ ret = tor_tls_write(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, TOR_TLS_WANTWRITE);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ tor_free(method);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static int fixed_ssl_accept_result;
+static int fixed_ssl_connect_result;
+
+static int
+setting_error_ssl_accept(SSL *ssl)
+{
+ (void)ssl;
+ ERR_put_error(ERR_LIB_BN, 2, -1, "somewhere.c", 99);
+ ERR_put_error(ERR_LIB_SYS, 2, -1, "somewhere.c", 99);
+ return fixed_ssl_accept_result;
+}
+
+static int
+setting_error_ssl_connect(SSL *ssl)
+{
+ (void)ssl;
+ ERR_put_error(ERR_LIB_BN, 2, -1, "somewhere.c", 99);
+ ERR_put_error(ERR_LIB_SYS, 2, -1, "somewhere.c", 99);
+ return fixed_ssl_connect_result;
+}
+
+static int
+fixed_ssl_accept(SSL *ssl)
+{
+ (void) ssl;
+ return fixed_ssl_accept_result;
+}
+
+static void
+test_tortls_handshake(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ SSL_CTX *ctx;
+ SSL_METHOD *method = give_me_a_test_method();
+ int previous_log = setup_capture_of_logs(LOG_INFO);
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(TLSv1_method());
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = SSL_new(ctx);
+ tls->state = TOR_TLS_ST_HANDSHAKE;
+
+ ret = tor_tls_handshake(tls);
+ tt_int_op(ret, OP_EQ, -9);
+
+ tls->isServer = 1;
+ tls->state = TOR_TLS_ST_HANDSHAKE;
+ ret = tor_tls_handshake(tls);
+ tt_int_op(ret, OP_EQ, -9);
+
+ tls->ssl->method = method;
+ method->ssl_accept = fixed_ssl_accept;
+ fixed_ssl_accept_result = 2;
+ ERR_clear_error();
+ tls->state = TOR_TLS_ST_HANDSHAKE;
+ ret = tor_tls_handshake(tls);
+ tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_OPEN);
+
+ method->ssl_accept = setting_error_ssl_accept;
+ fixed_ssl_accept_result = 1;
+ ERR_clear_error();
+ mock_clean_saved_logs();
+ tls->state = TOR_TLS_ST_HANDSHAKE;
+ ret = tor_tls_handshake(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 2);
+ /* This fails on jessie. Investigate why! */
+#if 0
+ tt_str_op(mock_saved_log_at(0), OP_EQ,
+ "TLS error while handshaking: (null) (in bignum routines:"
+ "(null):SSLv3 write client hello B)\n");
+ tt_str_op(mock_saved_log_at(1), OP_EQ,
+ "TLS error while handshaking: (null) (in system library:"
+ "connect:SSLv3 write client hello B)\n");
+#endif
+ tt_int_op(mock_saved_severity_at(0), OP_EQ, LOG_INFO);
+ tt_int_op(mock_saved_severity_at(1), OP_EQ, LOG_INFO);
+
+ tls->isServer = 0;
+ method->ssl_connect = setting_error_ssl_connect;
+ fixed_ssl_connect_result = 1;
+ ERR_clear_error();
+ mock_clean_saved_logs();
+ tls->state = TOR_TLS_ST_HANDSHAKE;
+ ret = tor_tls_handshake(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 2);
+#if 0
+ /* See above */
+ tt_str_op(mock_saved_log_at(0), OP_EQ, "TLS error while handshaking: "
+ "(null) (in bignum routines:(null):SSLv3 write client hello B)\n");
+ tt_str_op(mock_saved_log_at(1), OP_EQ, "TLS error while handshaking: "
+ "(null) (in system library:connect:SSLv3 write client hello B)\n");
+#endif
+ tt_int_op(mock_saved_severity_at(0), OP_EQ, LOG_WARN);
+ tt_int_op(mock_saved_severity_at(1), OP_EQ, LOG_WARN);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ SSL_free(tls->ssl);
+ SSL_CTX_free(ctx);
+ tor_free(tls);
+ tor_free(method);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_finish_handshake(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ SSL_CTX *ctx;
+ SSL_METHOD *method = give_me_a_test_method();
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ X509 *c1 = read_cert_from(validCertString);
+ SESS_CERT_local *sess = NULL;
+
+ ctx = SSL_CTX_new(method);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = SSL_new(ctx);
+ tls->state = TOR_TLS_ST_OPEN;
+
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+
+ tls->isServer = 1;
+ tls->wasV2Handshake = 0;
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tls->wasV2Handshake, OP_EQ, 1);
+
+ tls->wasV2Handshake = 1;
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tls->wasV2Handshake, OP_EQ, 1);
+
+ tls->wasV2Handshake = 1;
+ tls->ssl->session = SSL_SESSION_new();
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tls->wasV2Handshake, OP_EQ, 0);
+
+ tls->isServer = 0;
+
+ sess = tor_malloc_zero(sizeof(SESS_CERT_local));
+ tls->ssl->session->sess_cert = (void *)sess;
+ sess->cert_chain = sk_X509_new_null();
+ sk_X509_push(sess->cert_chain, c1);
+ tls->ssl->session->peer = c1;
+ tls->wasV2Handshake = 0;
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tls->wasV2Handshake, OP_EQ, 1);
+
+ method->num_ciphers = fake_num_ciphers;
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, -9);
+
+ done:
+ if (sess)
+ sk_X509_free(sess->cert_chain);
+ if (tls->ssl && tls->ssl->session) {
+ tor_free(tls->ssl->session->sess_cert);
+ }
+ SSL_free(tls->ssl);
+ tor_free(tls);
+ SSL_CTX_free(ctx);
+ tor_free(method);
+}
+#endif
+
+static int fixed_crypto_pk_new_result_index;
+static crypto_pk_t *fixed_crypto_pk_new_result[5];
+
+static crypto_pk_t *
+fixed_crypto_pk_new(void)
+{
+ return fixed_crypto_pk_new_result[fixed_crypto_pk_new_result_index++];
+}
+
+#ifndef OPENSSL_OPAQUE
+static int fixed_crypto_pk_generate_key_with_bits_result_index;
+static int fixed_crypto_pk_generate_key_with_bits_result[5];
+static int fixed_tor_tls_create_certificate_result_index;
+static X509 *fixed_tor_tls_create_certificate_result[5];
+static int fixed_tor_x509_cert_new_result_index;
+static tor_x509_cert_t *fixed_tor_x509_cert_new_result[5];
+
+static int
+fixed_crypto_pk_generate_key_with_bits(crypto_pk_t *env, int bits)
+{
+ (void)env;
+ (void)bits;
+ return fixed_crypto_pk_generate_key_with_bits_result[
+ fixed_crypto_pk_generate_key_with_bits_result_index++];
+}
+
+static X509 *
+fixed_tor_tls_create_certificate(crypto_pk_t *rsa,
+ crypto_pk_t *rsa_sign,
+ const char *cname,
+ const char *cname_sign,
+ unsigned int cert_lifetime)
+{
+ (void)rsa;
+ (void)rsa_sign;
+ (void)cname;
+ (void)cname_sign;
+ (void)cert_lifetime;
+ return fixed_tor_tls_create_certificate_result[
+ fixed_tor_tls_create_certificate_result_index++];
+}
+
+static tor_x509_cert_t *
+fixed_tor_x509_cert_new(X509 *x509_cert)
+{
+ (void) x509_cert;
+ return fixed_tor_x509_cert_new_result[
+ fixed_tor_x509_cert_new_result_index++];
+}
+
+static void
+test_tortls_context_new(void *ignored)
+{
+ (void)ignored;
+ tor_tls_context_t *ret;
+ crypto_pk_t *pk1, *pk2, *pk3, *pk4, *pk5, *pk6, *pk7, *pk8, *pk9, *pk10,
+ *pk11, *pk12, *pk13, *pk14, *pk15, *pk16, *pk17, *pk18;
+
+ pk1 = crypto_pk_new();
+ pk2 = crypto_pk_new();
+ pk3 = crypto_pk_new();
+ pk4 = crypto_pk_new();
+ pk5 = crypto_pk_new();
+ pk6 = crypto_pk_new();
+ pk7 = crypto_pk_new();
+ pk8 = crypto_pk_new();
+ pk9 = crypto_pk_new();
+ pk10 = crypto_pk_new();
+ pk11 = crypto_pk_new();
+ pk12 = crypto_pk_new();
+ pk13 = crypto_pk_new();
+ pk14 = crypto_pk_new();
+ pk15 = crypto_pk_new();
+ pk16 = crypto_pk_new();
+ pk17 = crypto_pk_new();
+ pk18 = crypto_pk_new();
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = NULL;
+ MOCK(crypto_pk_new, fixed_crypto_pk_new);
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ MOCK(crypto_pk_generate_key_with_bits,
+ fixed_crypto_pk_generate_key_with_bits);
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk1;
+ fixed_crypto_pk_new_result[1] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result[0] = -1;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk2;
+ fixed_crypto_pk_new_result[1] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result[0] = 0;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk3;
+ fixed_crypto_pk_new_result[1] = pk4;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result[0] = 0;
+ fixed_crypto_pk_generate_key_with_bits_result[1] = -1;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ MOCK(tor_tls_create_certificate, fixed_tor_tls_create_certificate);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk5;
+ fixed_crypto_pk_new_result[1] = pk6;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_crypto_pk_generate_key_with_bits_result[1] = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = NULL;
+ fixed_tor_tls_create_certificate_result[1] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[2] = tor_malloc_zero(sizeof(X509));
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk7;
+ fixed_crypto_pk_new_result[1] = pk8;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[1] = NULL;
+ fixed_tor_tls_create_certificate_result[2] = tor_malloc_zero(sizeof(X509));
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk9;
+ fixed_crypto_pk_new_result[1] = pk10;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[1] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[2] = NULL;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ MOCK(tor_x509_cert_new, fixed_tor_x509_cert_new);
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk11;
+ fixed_crypto_pk_new_result[1] = pk12;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[1] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[2] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_x509_cert_new_result_index = 0;
+ fixed_tor_x509_cert_new_result[0] = NULL;
+ fixed_tor_x509_cert_new_result[1] = NULL;
+ fixed_tor_x509_cert_new_result[2] = NULL;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk13;
+ fixed_crypto_pk_new_result[1] = pk14;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[1] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[2] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_x509_cert_new_result_index = 0;
+ fixed_tor_x509_cert_new_result[0] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ fixed_tor_x509_cert_new_result[1] = NULL;
+ fixed_tor_x509_cert_new_result[2] = NULL;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk15;
+ fixed_crypto_pk_new_result[1] = pk16;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[1] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[2] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_x509_cert_new_result_index = 0;
+ fixed_tor_x509_cert_new_result[0] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ fixed_tor_x509_cert_new_result[1] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ fixed_tor_x509_cert_new_result[2] = NULL;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk17;
+ fixed_crypto_pk_new_result[1] = pk18;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[1] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[2] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_x509_cert_new_result_index = 0;
+ fixed_tor_x509_cert_new_result[0] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ fixed_tor_x509_cert_new_result[1] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ fixed_tor_x509_cert_new_result[2] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ done:
+ UNMOCK(tor_x509_cert_new);
+ UNMOCK(tor_tls_create_certificate);
+ UNMOCK(crypto_pk_generate_key_with_bits);
+ UNMOCK(crypto_pk_new);
+}
+#endif
+
+static int fixed_crypto_pk_get_evp_pkey_result_index = 0;
+static EVP_PKEY *fixed_crypto_pk_get_evp_pkey_result[5];
+static int fixed_crypto_rand_result;
+
+static EVP_PKEY *
+fixed_crypto_pk_get_evp_pkey_(crypto_pk_t *env, int private)
+{
+ (void) env;
+ (void) private;
+ return fixed_crypto_pk_get_evp_pkey_result[
+ fixed_crypto_pk_get_evp_pkey_result_index++];
+}
+
+static int
+fixed_crypto_rand(char *to, size_t n)
+{
+ (void)to;
+ (void)n;
+ return fixed_crypto_rand_result;
+}
+
+static void
+test_tortls_create_certificate(void *ignored)
+{
+ (void)ignored;
+ X509 *ret;
+ crypto_pk_t *pk1, *pk2;
+
+ pk1 = crypto_pk_new();
+ pk2 = crypto_pk_new();
+
+ MOCK(crypto_pk_get_evp_pkey_, fixed_crypto_pk_get_evp_pkey_);
+ fixed_crypto_pk_get_evp_pkey_result_index = 0;
+ fixed_crypto_pk_get_evp_pkey_result[0] = NULL;
+ ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_get_evp_pkey_result_index = 0;
+ fixed_crypto_pk_get_evp_pkey_result[0] = tor_malloc_zero(sizeof(EVP_PKEY));
+ fixed_crypto_pk_get_evp_pkey_result[1] = NULL;
+ ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_get_evp_pkey_result_index = 0;
+ fixed_crypto_pk_get_evp_pkey_result[0] = tor_malloc_zero(sizeof(EVP_PKEY));
+ fixed_crypto_pk_get_evp_pkey_result[1] = tor_malloc_zero(sizeof(EVP_PKEY));
+ ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1);
+ tt_assert(!ret);
+
+ MOCK(crypto_rand, fixed_crypto_rand);
+ fixed_crypto_rand_result = -1;
+ fixed_crypto_pk_get_evp_pkey_result_index = 0;
+ fixed_crypto_pk_get_evp_pkey_result[0] = tor_malloc_zero(sizeof(EVP_PKEY));
+ fixed_crypto_pk_get_evp_pkey_result[1] = tor_malloc_zero(sizeof(EVP_PKEY));
+ ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1);
+ tt_assert(!ret);
+
+ done:
+ UNMOCK(crypto_rand);
+ UNMOCK(crypto_pk_get_evp_pkey_);
+ crypto_pk_free(pk1);
+ crypto_pk_free(pk2);
+}
+
+static void
+test_tortls_cert_new(void *ignored)
+{
+ (void)ignored;
+ tor_x509_cert_t *ret;
+ X509 *cert = read_cert_from(validCertString);
+
+ ret = tor_x509_cert_new(NULL);
+ tt_assert(!ret);
+
+ ret = tor_x509_cert_new(cert);
+ tt_assert(ret);
+ tor_x509_cert_free(ret);
+ ret = NULL;
+
+#if 0
+ cert = read_cert_from(validCertString);
+ /* XXX this doesn't do what you think: it alters a copy of the pubkey. */
+ X509_get_pubkey(cert)->type = EVP_PKEY_DSA;
+ ret = tor_x509_cert_new(cert);
+ tt_assert(ret);
+#endif
+
+#ifndef OPENSSL_OPAQUE
+ cert = read_cert_from(validCertString);
+ X509_CINF_free(cert->cert_info);
+ cert->cert_info = NULL;
+ ret = tor_x509_cert_new(cert);
+ tt_assert(ret);
+#endif
+
+ done:
+ tor_x509_cert_free(ret);
+}
+
+static void
+test_tortls_cert_is_valid(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_x509_cert_t *cert = NULL, *scert = NULL;
+
+ scert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(scert);
+
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0);
+ tt_int_op(ret, OP_EQ, 1);
+
+#ifndef OPENSSL_OPAQUE
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ ASN1_TIME_free(cert->cert->cert_info->validity->notAfter);
+ cert->cert->cert_info->validity->notAfter =
+ ASN1_TIME_set(NULL, time(NULL)-1000000);
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ X509_PUBKEY_free(cert->cert->cert_info->key);
+ cert->cert->cert_info->key = NULL;
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 1);
+ tt_int_op(ret, OP_EQ, 0);
+#endif
+
+#if 0
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ /* This doesn't actually change the key in the cert. XXXXXX */
+ BN_one(EVP_PKEY_get1_RSA(X509_get_pubkey(cert->cert))->n);
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 1);
+ tt_int_op(ret, OP_EQ, 0);
+
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ /* This doesn't actually change the key in the cert. XXXXXX */
+ X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC;
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 1);
+ tt_int_op(ret, OP_EQ, 0);
+
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ /* This doesn't actually change the key in the cert. XXXXXX */
+ X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC;
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0);
+ tt_int_op(ret, OP_EQ, 1);
+
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ /* This doesn't actually change the key in the cert. XXXXXX */
+ X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC;
+ X509_get_pubkey(cert->cert)->ameth = NULL;
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0);
+ tt_int_op(ret, OP_EQ, 0);
+#endif
+
+ done:
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+}
+
+static void
+test_tortls_context_init_one(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_context_t *old = NULL;
+
+ MOCK(crypto_pk_new, fixed_crypto_pk_new);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = NULL;
+ ret = tor_tls_context_init_one(&old, NULL, 0, 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ done:
+ UNMOCK(crypto_pk_new);
+}
+
+#define LOCAL_TEST_CASE(name, flags) \
+ { #name, test_tortls_##name, (flags|TT_FORK), NULL, NULL }
+
+#ifdef OPENSSL_OPAQUE
+#define INTRUSIVE_TEST_CASE(name, flags) \
+ { #name, NULL, TT_SKIP, NULL, NULL }
+#else
+#define INTRUSIVE_TEST_CASE(name, flags) LOCAL_TEST_CASE(name, flags)
+#endif
+
+struct testcase_t tortls_tests[] = {
+ LOCAL_TEST_CASE(errno_to_tls_error, 0),
+ LOCAL_TEST_CASE(err_to_string, 0),
+ LOCAL_TEST_CASE(tor_tls_new, TT_FORK),
+ LOCAL_TEST_CASE(tor_tls_get_error, 0),
+ LOCAL_TEST_CASE(get_state_description, TT_FORK),
+ LOCAL_TEST_CASE(get_by_ssl, TT_FORK),
+ LOCAL_TEST_CASE(allocate_tor_tls_object_ex_data_index, TT_FORK),
+ LOCAL_TEST_CASE(log_one_error, TT_FORK),
+ INTRUSIVE_TEST_CASE(get_error, TT_FORK),
+ LOCAL_TEST_CASE(always_accept_verify_cb, 0),
+ INTRUSIVE_TEST_CASE(x509_cert_free, 0),
+ LOCAL_TEST_CASE(x509_cert_get_id_digests, 0),
+ INTRUSIVE_TEST_CASE(cert_matches_key, 0),
+ INTRUSIVE_TEST_CASE(cert_get_key, 0),
+ LOCAL_TEST_CASE(get_my_client_auth_key, TT_FORK),
+ LOCAL_TEST_CASE(get_my_certs, TT_FORK),
+ INTRUSIVE_TEST_CASE(get_ciphersuite_name, 0),
+ INTRUSIVE_TEST_CASE(classify_client_ciphers, 0),
+ LOCAL_TEST_CASE(client_is_using_v2_ciphers, 0),
+ INTRUSIVE_TEST_CASE(verify, 0),
+ INTRUSIVE_TEST_CASE(check_lifetime, 0),
+ INTRUSIVE_TEST_CASE(get_pending_bytes, 0),
+ LOCAL_TEST_CASE(get_forced_write_size, 0),
+ LOCAL_TEST_CASE(get_write_overhead_ratio, TT_FORK),
+ LOCAL_TEST_CASE(used_v1_handshake, TT_FORK),
+ LOCAL_TEST_CASE(get_num_server_handshakes, 0),
+ LOCAL_TEST_CASE(server_got_renegotiate, 0),
+ INTRUSIVE_TEST_CASE(SSL_SESSION_get_master_key, 0),
+ INTRUSIVE_TEST_CASE(get_tlssecrets, 0),
+ INTRUSIVE_TEST_CASE(get_buffer_sizes, 0),
+ LOCAL_TEST_CASE(evaluate_ecgroup_for_tls, 0),
+ INTRUSIVE_TEST_CASE(try_to_extract_certs_from_tls, 0),
+ INTRUSIVE_TEST_CASE(get_peer_cert, 0),
+ INTRUSIVE_TEST_CASE(peer_has_cert, 0),
+ INTRUSIVE_TEST_CASE(shutdown, 0),
+ INTRUSIVE_TEST_CASE(finish_handshake, 0),
+ INTRUSIVE_TEST_CASE(handshake, 0),
+ INTRUSIVE_TEST_CASE(write, 0),
+ INTRUSIVE_TEST_CASE(read, 0),
+ INTRUSIVE_TEST_CASE(server_info_callback, 0),
+ LOCAL_TEST_CASE(is_server, 0),
+ INTRUSIVE_TEST_CASE(assert_renegotiation_unblocked, 0),
+ INTRUSIVE_TEST_CASE(block_renegotiation, 0),
+ INTRUSIVE_TEST_CASE(unblock_renegotiation, 0),
+ INTRUSIVE_TEST_CASE(set_renegotiate_callback, 0),
+ LOCAL_TEST_CASE(set_logged_address, 0),
+ INTRUSIVE_TEST_CASE(find_cipher_by_id, 0),
+ INTRUSIVE_TEST_CASE(session_secret_cb, 0),
+ INTRUSIVE_TEST_CASE(debug_state_callback, 0),
+ INTRUSIVE_TEST_CASE(context_new, 0),
+ LOCAL_TEST_CASE(create_certificate, 0),
+ LOCAL_TEST_CASE(cert_new, 0),
+ LOCAL_TEST_CASE(cert_is_valid, 0),
+ LOCAL_TEST_CASE(context_init_one, 0),
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_util.c b/src/test/test_util.c
index 0a5783e9f5..4cf2f9bda1 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -19,6 +19,7 @@
#endif
#include <math.h>
#include <ctype.h>
+#include <float.h>
/* XXXX this is a minimal wrapper to make the unit tests compile with the
* changed tor_timegm interface. */
@@ -4097,6 +4098,9 @@ test_util_round_to_next_multiple_of(void *arg)
tt_u64_op(round_uint64_to_next_multiple_of(99,7), ==, 105);
tt_u64_op(round_uint64_to_next_multiple_of(99,9), ==, 99);
+ tt_u64_op(round_uint64_to_next_multiple_of(UINT64_MAX,2), ==,
+ UINT64_MAX);
+
tt_i64_op(round_int64_to_next_multiple_of(0,1), ==, 0);
tt_i64_op(round_int64_to_next_multiple_of(0,7), ==, 0);
@@ -4110,7 +4114,27 @@ test_util_round_to_next_multiple_of(void *arg)
tt_i64_op(round_int64_to_next_multiple_of(INT64_MIN,2), ==, INT64_MIN);
tt_i64_op(round_int64_to_next_multiple_of(INT64_MAX,2), ==,
- INT64_MAX-INT64_MAX%2);
+ INT64_MAX);
+
+ tt_int_op(round_uint32_to_next_multiple_of(0,1), ==, 0);
+ tt_int_op(round_uint32_to_next_multiple_of(0,7), ==, 0);
+
+ tt_int_op(round_uint32_to_next_multiple_of(99,1), ==, 99);
+ tt_int_op(round_uint32_to_next_multiple_of(99,7), ==, 105);
+ tt_int_op(round_uint32_to_next_multiple_of(99,9), ==, 99);
+
+ tt_int_op(round_uint32_to_next_multiple_of(UINT32_MAX,2), ==,
+ UINT32_MAX);
+
+ tt_uint_op(round_to_next_multiple_of(0,1), ==, 0);
+ tt_uint_op(round_to_next_multiple_of(0,7), ==, 0);
+
+ tt_uint_op(round_to_next_multiple_of(99,1), ==, 99);
+ tt_uint_op(round_to_next_multiple_of(99,7), ==, 105);
+ tt_uint_op(round_to_next_multiple_of(99,9), ==, 99);
+
+ tt_uint_op(round_to_next_multiple_of(UINT_MAX,2), ==,
+ UINT_MAX);
done:
;
}
@@ -4143,6 +4167,7 @@ test_util_laplace(void *arg)
*/
tt_i64_op(INT64_MIN + 20, ==,
add_laplace_noise(20, 0.0, delta_f, epsilon));
+
tt_i64_op(-60, ==, add_laplace_noise(20, 0.1, delta_f, epsilon));
tt_i64_op(-14, ==, add_laplace_noise(20, 0.25, delta_f, epsilon));
tt_i64_op(20, ==, add_laplace_noise(20, 0.5, delta_f, epsilon));
@@ -4150,6 +4175,143 @@ test_util_laplace(void *arg)
tt_i64_op(100, ==, add_laplace_noise(20, 0.9, delta_f, epsilon));
tt_i64_op(215, ==, add_laplace_noise(20, 0.99, delta_f, epsilon));
+ /* Test extreme values of signal with maximally negative values of noise
+ * 1.0000000000000002 is the smallest number > 1
+ * 0.0000000000000002 is the double epsilon (error when calculating near 1)
+ * this is approximately 1/(2^52)
+ * per https://en.wikipedia.org/wiki/Double_precision
+ * (let's not descend into the world of subnormals)
+ * >>> laplace.ppf([0, 0.0000000000000002], loc = 0, scale = 1)
+ * array([ -inf, -35.45506713])
+ */
+ const double noscale_df = 1.0, noscale_eps = 1.0;
+
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(0, 0.0, noscale_df, noscale_eps));
+
+ /* is it clipped to INT64_MIN? */
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(-1, 0.0, noscale_df, noscale_eps));
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(INT64_MIN, 0.0,
+ noscale_df, noscale_eps));
+ /* ... even when scaled? */
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(0, 0.0, delta_f, epsilon));
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(0, 0.0,
+ DBL_MAX, 1));
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(INT64_MIN, 0.0,
+ DBL_MAX, 1));
+
+ /* does it play nice with INT64_MAX? */
+ tt_i64_op((INT64_MIN + INT64_MAX), ==,
+ add_laplace_noise(INT64_MAX, 0.0,
+ noscale_df, noscale_eps));
+
+ /* do near-zero fractional values work? */
+ const double min_dbl_error = 0.0000000000000002;
+
+ tt_i64_op(-35, ==,
+ add_laplace_noise(0, min_dbl_error,
+ noscale_df, noscale_eps));
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(INT64_MIN, min_dbl_error,
+ noscale_df, noscale_eps));
+ tt_i64_op((-35 + INT64_MAX), ==,
+ add_laplace_noise(INT64_MAX, min_dbl_error,
+ noscale_df, noscale_eps));
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(0, min_dbl_error,
+ DBL_MAX, 1));
+ tt_i64_op((INT64_MAX + INT64_MIN), ==,
+ add_laplace_noise(INT64_MAX, min_dbl_error,
+ DBL_MAX, 1));
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(INT64_MIN, min_dbl_error,
+ DBL_MAX, 1));
+
+ /* does it play nice with INT64_MAX? */
+ tt_i64_op((INT64_MAX - 35), ==,
+ add_laplace_noise(INT64_MAX, min_dbl_error,
+ noscale_df, noscale_eps));
+
+ /* Test extreme values of signal with maximally positive values of noise
+ * 1.0000000000000002 is the smallest number > 1
+ * 0.9999999999999998 is the greatest number < 1 by calculation
+ * per https://en.wikipedia.org/wiki/Double_precision
+ * >>> laplace.ppf([1.0, 0.9999999999999998], loc = 0, scale = 1)
+ * array([inf, 35.35050621])
+ * but the function rejects p == 1.0, so we just use max_dbl_lt_one
+ */
+ const double max_dbl_lt_one = 0.9999999999999998;
+
+ /* do near-one fractional values work? */
+ tt_i64_op(35, ==,
+ add_laplace_noise(0, max_dbl_lt_one, noscale_df, noscale_eps));
+
+ /* is it clipped to INT64_MAX? */
+ tt_i64_op(INT64_MAX, ==,
+ add_laplace_noise(INT64_MAX - 35, max_dbl_lt_one,
+ noscale_df, noscale_eps));
+ tt_i64_op(INT64_MAX, ==,
+ add_laplace_noise(INT64_MAX - 34, max_dbl_lt_one,
+ noscale_df, noscale_eps));
+ tt_i64_op(INT64_MAX, ==,
+ add_laplace_noise(INT64_MAX, max_dbl_lt_one,
+ noscale_df, noscale_eps));
+ /* ... even when scaled? */
+ tt_i64_op(INT64_MAX, ==,
+ add_laplace_noise(INT64_MAX, max_dbl_lt_one,
+ delta_f, epsilon));
+ tt_i64_op((INT64_MIN + INT64_MAX), ==,
+ add_laplace_noise(INT64_MIN, max_dbl_lt_one,
+ DBL_MAX, 1));
+ tt_i64_op(INT64_MAX, ==,
+ add_laplace_noise(INT64_MAX, max_dbl_lt_one,
+ DBL_MAX, 1));
+ /* does it play nice with INT64_MIN? */
+ tt_i64_op((INT64_MIN + 35), ==,
+ add_laplace_noise(INT64_MIN, max_dbl_lt_one,
+ noscale_df, noscale_eps));
+
+ done:
+ ;
+}
+
+static void
+test_util_clamp_double_to_int64(void *arg)
+{
+ (void)arg;
+
+ tt_i64_op(INT64_MIN, ==, clamp_double_to_int64(-INFINITY));
+ tt_i64_op(INT64_MIN, ==,
+ clamp_double_to_int64(-1.0 * pow(2.0, 64.0) - 1.0));
+ tt_i64_op(INT64_MIN, ==,
+ clamp_double_to_int64(-1.0 * pow(2.0, 63.0) - 1.0));
+ tt_i64_op(((uint64_t) -1) << 53, ==,
+ clamp_double_to_int64(-1.0 * pow(2.0, 53.0)));
+ tt_i64_op((((uint64_t) -1) << 53) + 1, ==,
+ clamp_double_to_int64(-1.0 * pow(2.0, 53.0) + 1.0));
+ tt_i64_op(-1, ==, clamp_double_to_int64(-1.0));
+ tt_i64_op(0, ==, clamp_double_to_int64(-0.9));
+ tt_i64_op(0, ==, clamp_double_to_int64(-0.1));
+ tt_i64_op(0, ==, clamp_double_to_int64(0.0));
+ tt_i64_op(0, ==, clamp_double_to_int64(NAN));
+ tt_i64_op(0, ==, clamp_double_to_int64(0.1));
+ tt_i64_op(0, ==, clamp_double_to_int64(0.9));
+ tt_i64_op(1, ==, clamp_double_to_int64(1.0));
+ tt_i64_op((((int64_t) 1) << 53) - 1, ==,
+ clamp_double_to_int64(pow(2.0, 53.0) - 1.0));
+ tt_i64_op(((int64_t) 1) << 53, ==,
+ clamp_double_to_int64(pow(2.0, 53.0)));
+ tt_i64_op(INT64_MAX, ==,
+ clamp_double_to_int64(pow(2.0, 63.0)));
+ tt_i64_op(INT64_MAX, ==,
+ clamp_double_to_int64(pow(2.0, 64.0)));
+ tt_i64_op(INT64_MAX, ==, clamp_double_to_int64(INFINITY));
+
done:
;
}
@@ -4180,9 +4342,14 @@ fd_is_nonblocking(tor_socket_t fd)
}
#endif
+#define ERRNO_IS_EPROTO(e) (e == SOCK_ERRNO(EPROTONOSUPPORT))
+#define SOCK_ERR_IS_EPROTO(s) ERRNO_IS_EPROTO(tor_socket_errno(s))
+
+/* Test for tor_open_socket*, using IPv4 or IPv6 depending on arg. */
static void
test_util_socket(void *arg)
{
+ const int domain = !strcmp(arg, "4") ? AF_INET : AF_INET6;
tor_socket_t fd1 = TOR_INVALID_SOCKET;
tor_socket_t fd2 = TOR_INVALID_SOCKET;
tor_socket_t fd3 = TOR_INVALID_SOCKET;
@@ -4193,15 +4360,19 @@ test_util_socket(void *arg)
(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);
+ fd1 = tor_open_socket_with_extensions(domain, SOCK_STREAM, 0, 0, 0);
+ if (SOCK_ERR_IS_EPROTO(fd1)) {
+ /* Assume we're on an IPv4-only or IPv6-only system, and give up now. */
+ goto done;
+ }
+ fd2 = tor_open_socket_with_extensions(domain, SOCK_STREAM, 0, 0, 1);
tt_assert(SOCKET_OK(fd1));
tt_assert(SOCKET_OK(fd2));
tt_int_op(get_n_open_sockets(), OP_EQ, 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);
+ //fd3 = tor_open_socket_with_extensions(domain, SOCK_STREAM, 0, 1, 0);
+ //fd4 = tor_open_socket_with_extensions(domain, SOCK_STREAM, 0, 1, 1);
+ fd3 = tor_open_socket(domain, SOCK_STREAM, 0);
+ fd4 = tor_open_socket_nonblocking(domain, SOCK_STREAM, 0);
tt_assert(SOCKET_OK(fd3));
tt_assert(SOCKET_OK(fd4));
tt_int_op(get_n_open_sockets(), OP_EQ, n + 4);
@@ -4250,8 +4421,20 @@ test_util_socketpair(void *arg)
int n = get_n_open_sockets();
tor_socket_t fds[2] = {TOR_INVALID_SOCKET, TOR_INVALID_SOCKET};
const int family = AF_UNIX;
+ int socketpair_result = 0;
+
+ socketpair_result = tor_socketpair_fn(family, SOCK_STREAM, 0, fds);
+ /* If there is no 127.0.0.1 or ::1, tor_ersatz_socketpair will and must fail.
+ * Otherwise, we risk exposing a socketpair on a routable IP address. (Some
+ * BSD jails use a routable address for localhost. Fortunately, they have
+ * the real AF_UNIX socketpair.) */
+ if (ersatz && ERRNO_IS_EPROTO(-socketpair_result)) {
+ /* In my testing, an IPv6-only FreeBSD jail without ::1 returned EINVAL.
+ * Assume we're on a machine without 127.0.0.1 or ::1 and give up now. */
+ goto done;
+ }
+ tt_int_op(0, OP_EQ, socketpair_result);
- tt_int_op(0, OP_EQ, 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(), OP_EQ, n + 2);
@@ -4271,6 +4454,8 @@ test_util_socketpair(void *arg)
tor_close_socket(fds[1]);
}
+#undef SOCKET_EPROTO
+
static void
test_util_max_mem(void *arg)
{
@@ -4441,6 +4626,7 @@ struct testcase_t util_tests[] = {
UTIL_TEST(di_map, 0),
UTIL_TEST(round_to_next_multiple_of, 0),
UTIL_TEST(laplace, 0),
+ UTIL_TEST(clamp_double_to_int64, 0),
UTIL_TEST(find_str_at_start_of_line, 0),
UTIL_TEST(string_is_C_identifier, 0),
UTIL_TEST(asprintf, 0),
@@ -4473,7 +4659,10 @@ struct testcase_t util_tests[] = {
UTIL_TEST(write_chunks_to_file, 0),
UTIL_TEST(mathlog, 0),
UTIL_TEST(weak_random, 0),
- UTIL_TEST(socket, TT_FORK),
+ { "socket_ipv4", test_util_socket, TT_FORK, &passthrough_setup,
+ (void*)"4" },
+ { "socket_ipv6", test_util_socket, TT_FORK,
+ &passthrough_setup, (void*)"6" },
{ "socketpair", test_util_socketpair, TT_FORK, &passthrough_setup,
(void*)"0" },
{ "socketpair_ersatz", test_util_socketpair, TT_FORK,
diff --git a/src/test/test_util_format.c b/src/test/test_util_format.c
new file mode 100644
index 0000000000..705dfcf605
--- /dev/null
+++ b/src/test/test_util_format.c
@@ -0,0 +1,262 @@
+/* Copyright (c) 2010-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "or.h"
+
+#include "test.h"
+
+#define UTIL_FORMAT_PRIVATE
+#include "util_format.h"
+
+#define NS_MODULE util_format
+
+static void
+test_util_format_base64_encode(void *ignored)
+{
+ (void)ignored;
+ int res;
+ int i;
+ char *src;
+ char *dst;
+
+ src = tor_malloc_zero(256);
+ dst = tor_malloc_zero(1000);
+
+ for (i=0;i<256;i++) {
+ src[i] = (char)i;
+ }
+
+ res = base64_encode(NULL, 1, src, 1, 0);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base64_encode(dst, 1, NULL, 1, 0);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base64_encode(dst, 1, src, 10, 0);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base64_encode(dst, SSIZE_MAX-1, src, 1, 0);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base64_encode(dst, SSIZE_MAX-1, src, 10, 0);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base64_encode(dst, 1000, src, 256, 0);
+ tt_int_op(res, OP_EQ, 344);
+ tt_str_op(dst, OP_EQ, "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh"
+ "8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZH"
+ "SElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3"
+ "BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeY"
+ "mZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wM"
+ "HCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp"
+ "6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==");
+
+ res = base64_encode(dst, 1000, src, 256, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 350);
+ tt_str_op(dst, OP_EQ,
+ "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v\n"
+ "MDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5f\n"
+ "YGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6P\n"
+ "kJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/\n"
+ "wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v\n"
+ "8PHy8/T19vf4+fr7/P3+/w==\n");
+
+ res = base64_encode(dst, 1000, src+1, 255, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 346);
+
+ for (i = 0;i<50;i++) {
+ src[i] = 0;
+ }
+ src[50] = 255;
+ src[51] = 255;
+ src[52] = 255;
+ src[53] = 255;
+
+ res = base64_encode(dst, 1000, src, 54, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 74);
+
+ res = base64_encode(dst, 1000, src+1, 53, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 74);
+
+ res = base64_encode(dst, 1000, src+2, 52, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 74);
+
+ res = base64_encode(dst, 1000, src+3, 51, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 70);
+
+ res = base64_encode(dst, 1000, src+4, 50, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 70);
+
+ res = base64_encode(dst, 1000, src+5, 49, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 70);
+
+ res = base64_encode(dst, 1000, src+6, 48, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 65);
+
+ res = base64_encode(dst, 1000, src+7, 47, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 65);
+
+ res = base64_encode(dst, 1000, src+8, 46, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 65);
+
+ done:
+ tor_free(src);
+ tor_free(dst);
+}
+
+static void
+test_util_format_base64_decode_nopad(void *ignored)
+{
+ (void)ignored;
+ int res;
+ int i;
+ char *src;
+ uint8_t *dst, *real_dst;
+ uint8_t expected[] = {0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65};
+ char real_src[] = "ZXhhbXBsZQ";
+
+ src = tor_malloc_zero(256);
+ dst = tor_malloc_zero(1000);
+ real_dst = tor_malloc_zero(10);
+
+ for (i=0;i<256;i++) {
+ src[i] = (char)i;
+ }
+
+ res = base64_decode_nopad(dst, 1, src, SIZE_T_CEILING);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base64_decode_nopad(dst, 1, src, 5);
+ tt_int_op(res, OP_EQ, -1);
+
+ const char *s = "SGVsbG8gd29ybGQ";
+ res = base64_decode_nopad(dst, 1000, s, strlen(s));
+ tt_int_op(res, OP_EQ, 11);
+ tt_mem_op(dst, OP_EQ, "Hello world", 11);
+
+ s = "T3BhIG11bmRv";
+ res = base64_decode_nopad(dst, 9, s, strlen(s));
+ tt_int_op(res, OP_EQ, 9);
+ tt_mem_op(dst, OP_EQ, "Opa mundo", 9);
+
+ res = base64_decode_nopad(real_dst, 10, real_src, 10);
+ tt_int_op(res, OP_EQ, 7);
+ tt_mem_op(real_dst, OP_EQ, expected, 7);
+
+ done:
+ tor_free(src);
+ tor_free(dst);
+ tor_free(real_dst);
+}
+
+static void
+test_util_format_base64_decode(void *ignored)
+{
+ (void)ignored;
+ int res;
+ int i;
+ char *src;
+ char *dst, *real_dst;
+ uint8_t expected[] = {0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65};
+ char real_src[] = "ZXhhbXBsZQ==";
+
+ src = tor_malloc_zero(256);
+ dst = tor_malloc_zero(1000);
+ real_dst = tor_malloc_zero(10);
+
+ for (i=0;i<256;i++) {
+ src[i] = (char)i;
+ }
+
+ res = base64_decode(dst, 1, src, SIZE_T_CEILING);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base64_decode(dst, SIZE_T_CEILING+1, src, 10);
+ tt_int_op(res, OP_EQ, -1);
+
+ const char *s = "T3BhIG11bmRv";
+ res = base64_decode(dst, 9, s, strlen(s));
+ tt_int_op(res, OP_EQ, 9);
+ tt_mem_op(dst, OP_EQ, "Opa mundo", 9);
+
+ memset(dst, 0, 1000);
+ res = base64_decode(dst, 100, s, strlen(s));
+ tt_int_op(res, OP_EQ, 9);
+ tt_mem_op(dst, OP_EQ, "Opa mundo", 9);
+
+ s = "SGVsbG8gd29ybGQ=";
+ res = base64_decode(dst, 100, s, strlen(s));
+ tt_int_op(res, OP_EQ, 11);
+ tt_mem_op(dst, OP_EQ, "Hello world", 11);
+
+ res = base64_decode(real_dst, 10, real_src, 10);
+ tt_int_op(res, OP_EQ, 7);
+ tt_mem_op(real_dst, OP_EQ, expected, 7);
+
+ done:
+ tor_free(src);
+ tor_free(dst);
+ tor_free(real_dst);
+}
+
+static void
+test_util_format_base16_decode(void *ignored)
+{
+ (void)ignored;
+ int res;
+ int i;
+ char *src;
+ char *dst, *real_dst;
+ char expected[] = {0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65};
+ char real_src[] = "6578616D706C65";
+
+ src = tor_malloc_zero(256);
+ dst = tor_malloc_zero(1000);
+ real_dst = tor_malloc_zero(10);
+
+ for (i=0;i<256;i++) {
+ src[i] = (char)i;
+ }
+
+ res = base16_decode(dst, 3, src, 3);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base16_decode(dst, 1, src, 10);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base16_decode(dst, SIZE_T_CEILING+2, src, 10);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base16_decode(dst, 1000, "", 0);
+ tt_int_op(res, OP_EQ, 0);
+
+ res = base16_decode(dst, 1000, "aabc", 4);
+ tt_int_op(res, OP_EQ, 0);
+ tt_mem_op(dst, OP_EQ, "\xaa\xbc", 2);
+
+ res = base16_decode(dst, 1000, "aabcd", 6);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base16_decode(dst, 1000, "axxx", 4);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base16_decode(real_dst, 10, real_src, 14);
+ tt_int_op(res, OP_EQ, 0);
+ tt_mem_op(real_dst, OP_EQ, expected, 7);
+
+ done:
+ tor_free(src);
+ tor_free(dst);
+ tor_free(real_dst);
+}
+
+struct testcase_t util_format_tests[] = {
+ { "base64_encode", test_util_format_base64_encode, 0, NULL, NULL },
+ { "base64_decode_nopad", test_util_format_base64_decode_nopad, 0,
+ NULL, NULL },
+ { "base64_decode", test_util_format_base64_decode, 0, NULL, NULL },
+ { "base16_decode", test_util_format_base16_decode, 0, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_util_process.c b/src/test/test_util_process.c
new file mode 100644
index 0000000000..cb1d5b2ebb
--- /dev/null
+++ b/src/test/test_util_process.c
@@ -0,0 +1,84 @@
+/* Copyright (c) 2010-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define UTIL_PROCESS_PRIVATE
+#include "orconfig.h"
+#include "or.h"
+
+#include "test.h"
+
+#include "util_process.h"
+
+#include "log_test_helpers.h"
+
+#ifndef _WIN32
+#define NS_MODULE util_process
+
+static void
+temp_callback(int r, void *s)
+{
+ (void)r;
+ (void)s;
+}
+
+static void
+test_util_process_set_waitpid_callback(void *ignored)
+{
+ (void)ignored;
+ waitpid_callback_t *res1 = NULL, *res2 = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+ pid_t pid = (pid_t)42;
+
+ res1 = set_waitpid_callback(pid, temp_callback, NULL);
+ tt_assert(res1);
+
+ res2 = set_waitpid_callback(pid, temp_callback, NULL);
+ tt_assert(res2);
+ tt_str_op(mock_saved_log_at(0), OP_EQ,
+ "Replaced a waitpid monitor on pid 42. That should be "
+ "impossible.\n");
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ clear_waitpid_callback(res1);
+ clear_waitpid_callback(res2);
+}
+
+static void
+test_util_process_clear_waitpid_callback(void *ignored)
+{
+ (void)ignored;
+ waitpid_callback_t *res;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+ pid_t pid = (pid_t)43;
+
+ clear_waitpid_callback(NULL);
+
+ res = set_waitpid_callback(pid, temp_callback, NULL);
+ clear_waitpid_callback(res);
+ tt_int_op(mock_saved_log_number(), OP_EQ, 0);
+
+#if 0
+ /* No. This is use-after-free. We don't _do_ that. XXXX */
+ clear_waitpid_callback(res);
+ tt_str_op(mock_saved_log_at(0), OP_EQ,
+ "Couldn't remove waitpid monitor for pid 43.\n");
+#endif
+
+ done:
+ teardown_capture_of_logs(previous_log);
+}
+#endif /* _WIN32 */
+
+#ifndef _WIN32
+#define TEST(name) { #name, test_util_process_##name, 0, NULL, NULL }
+#else
+#define TEST(name) { #name, NULL, TT_SKIP, NULL, NULL }
+#endif
+
+struct testcase_t util_process_tests[] = {
+ TEST(set_waitpid_callback),
+ TEST(clear_waitpid_callback),
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/vote_descriptors.inc b/src/test/vote_descriptors.inc
new file mode 100644
index 0000000000..c5ce21f744
--- /dev/null
+++ b/src/test/vote_descriptors.inc
@@ -0,0 +1,94 @@
+const char* VOTE_BODY_V3 =
+"network-status-version 3\n"
+"vote-status vote\n"
+"consensus-methods 13 14 15 16 17 18 19 20 21\n"
+"published 2015-09-02 19:34:15\n"
+"valid-after 2015-09-02 19:50:55\n"
+"fresh-until 2015-09-02 20:07:38\n"
+"valid-until 2015-09-02 20:24:15\n"
+"voting-delay 100 250\n"
+"client-versions 0.1.2.14,0.1.2.17\n"
+"server-versions 0.1.2.10,0.1.2.15,0.1.2.16\n"
+"known-flags Authority Exit Fast Guard MadeOfCheese MadeOfTin Running Stable V2Dir Valid\n"
+"flag-thresholds stable-uptime=0 stable-mtbf=0 fast-speed=0 guard-wfu=0.000% guard-tk=0 guard-bw-inc-exits=0 guard-bw-exc-exits=0 enough-mtbf=0 ignoring-advertised-bws=0\n"
+"params circuitwindow=80 foo=660\n"
+"dir-source Voter3 D867ACF56A9D229B35C25F0090BC9867E906BE69 3.4.5.6 3.4.5.6 80 9000\n"
+"contact voter@example.com\n"
+"legacy-dir-key 4141414141414141414141414141414141414141\n"
+"dir-key-certificate-version 3\n"
+"fingerprint D867ACF56A9D229B35C25F0090BC9867E906BE69\n"
+"dir-key-published 2008-12-12 18:07:24\n"
+"dir-key-expires 2009-12-12 18:07:24\n"
+"dir-identity-key\n"
+"-----BEGIN RSA PUBLIC KEY-----\n"
+"MIIBigKCAYEAveMpKlw8oD1YqFqpJchuwSR82BDhutbqgHiez3QO9FmzOctJpV+Y\n"
+"mpTYIJLS/qC+4GBKFF1VK0C4SoBrS3zri0qdXdE+vBGcyrxrjMklpxoqSKRY2011\n"
+"4eqYPghKlo5RzuqteBclGCHyNxWjUJeRKDWgvh+U/gr2uYM6fRm5q0fCzg4aECE7\n"
+"VP6fDGZrMbQI8jHpiMSoC9gkUASNEa6chLInlnP8/H5qUEW4TB9CN/q095pefuwL\n"
+"P+F+1Nz5hnM7fa5XmeMB8iM4RriUmOQlLBZgpQBMpEfWMIPcR9F1Gh3MxERqqUcH\n"
+"tmij+IZdeXg9OkCXykcabaYIhZD3meErn9Tax4oA/THduLfgli9zM0ExwzH1OooN\n"
+"L8rIcJ+2eBo3bQiQUbdYW71sl9w7nSPtircbJUa1mUvWYLPWQxFliPiQSetgJLMj\n"
+"VQqtPmV2hvN2Xk3lLfJO50qMTK7w7Gsaw8UtV4YDM1Hcjp/hQaIB1xfwhXgl+eUU\n"
+"btUa4c+cUTjHAgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n"
+"dir-signing-key\n"
+"-----BEGIN RSA PUBLIC KEY-----\n"
+"MIGJAoGBALPSUInyuEu6NV3NjozplaniIEBzQXEjv1x9/+mqnwZABpYVmuy9A8nx\n"
+"eoyY3sZFsnYwNW/IZjAgG23pEmevu3F+L4myMjjaa6ORl3MgRYQ4gmuFqpefrGdm\n"
+"ywRCleh2JerkQ4VxOuq10dn/abITzLyaZzMw30KXWp5pxKXOLtxFAgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n"
+"dir-key-crosscert\n"
+"-----BEGIN ID SIGNATURE-----\n"
+"FTBJNR/Hlt4T53yUMp1r/QCSMCpkHJCbYBT0R0pvYqhqFfYN5qHRSICRXaFFImIF\n"
+"0DGWmwRza6DxPKNzkm5/b7I0de9zJW1jNNdQAQK5xppAtQcAafRdu8cBonnmh9KX\n"
+"k1NrAK/X00FYywju3yl/SxCn1GddVNkHYexEudmJMPM=\n"
+"-----END ID SIGNATURE-----\n"
+"dir-key-certification\n"
+"-----BEGIN SIGNATURE-----\n"
+"pjWguLFBfELZDc6DywL6Do21SCl7LcutfpM92MEn4WYeSNcTXNR6lRX7reOEJk4e\n"
+"NwEaMt+Hl7slgeR5wjnW3OmMmRPZK9bquNWbfD+sAOV9bRFZTpXIdleAQFPlwvMF\n"
+"z/Gzwspzn4i2Yh6hySShrctMmW8YL3OM8LsBXzBhp/rG2uHlsxmIsc13DA6HWt61\n"
+"ffY72uNE6KckDGsQ4wPGP9q69y6g+X+TNio1KPbsILbePv6EjbO+rS8FiS4njPlg\n"
+"SPYry1RaUvxzxTkswIzdE1tjJrUiqpbWlTGxrH9N4OszoLm45Pc784KLULrjKIoi\n"
+"Q+vRsGrcMBAa+kDowWU6H1ryKR7KOhzRTcf2uqLE/W3ezaRwmOG+ETmoVFwbhk2X\n"
+"OlbXEM9fWP+INvFkr6Z93VYL2jGkCjV7e3xXmre/Lb92fUcYi6t5dwzfV8gJnIoG\n"
+"eCHd0K8NrQK0ipVk/7zcPDKOPeo9Y5aj/f6X/pDHtb+Dd5sT+l82G/Tqy4DIYUYR\n"
+"-----END SIGNATURE-----\n"
+"r router2 AwMDAwMDAwMDAwMDAwMDAwMDAwM Tk5OTk5OTk5OTk5OTk5OTk5OTk4 2015-09-02 19:09:15 153.0.136.1 443 8000\n"
+"s Running V2Dir\n"
+"v 0.1.2.14\n"
+"w Bandwidth=30 Measured=30\n"
+"p reject 1-65535\n"
+"id ed25519 none\n"
+"m 9,10,11,12,13,14,15,16,17 sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa0\n"
+"r router1 BQUFBQUFBQUFBQUFBQUFBQUFBQU TU1NTU1NTU1NTU1NTU1NTU1NTU0 2015-09-02 19:17:35 153.0.153.1 443 0\n"
+"a [1:2:3::4]:4711\n"
+"s Exit Fast Guard Running Stable Valid\n"
+"v 0.2.0.5\n"
+"w Bandwidth=120 Measured=120\n"
+"p reject 1-65535\n"
+"id ed25519 none\n"
+"m 9,10,11,12,13,14,15,16,17 sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa1\n"
+"r router3 MzMzMzMzMzMzMzMzMzMzMzMzMzM T09PT09PT09PT09PT09PT09PT08 2015-09-02 19:17:35 170.0.153.1 400 9999\n"
+"s Authority Exit Fast Guard Running Stable V2Dir Valid\n"
+"v 0.1.0.3\n"
+"w Bandwidth=120\n"
+"p reject 1-65535\n"
+"id ed25519 none\n"
+"m 9,10,11,12,13,14,15,16,17 "
+"sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa2\n"
+"r router4 NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ Ly8vLy8vLy8vLy8vLy8vLy8vLy8 2015-09-02 19:17:35 192.0.2.3 500 1999\n"
+"s Running V2Dir\n"
+"v 0.1.6.3\n"
+"w Bandwidth=30\n"
+"p reject 1-65535\n"
+"id ed25519 none\n"
+"m 9,10,11,12,13,14,15,16,17 sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa3\n"
+"directory-footer\n"
+"directory-signature D867ACF56A9D229B35C25F0090BC9867E906BE69 CBF56A83368A5150F1A9AAADAFB4D77F8C4170E2\n"
+"-----BEGIN SIGNATURE-----\n"
+"AHiWcHe+T3XbnlQqvqSAk6RY3XmEy1+hM2u9Xk6BNi7BpQkEQM1f0vzRpgn5Dnf2\n"
+"TXQWGUq9Z7jdSVnzWT3xqPA4zjw6eZkj+DKUtwq+oEDZGlf8eHTFmr0NAWfwZbk9\n"
+"NAjbMTUXUP37N2XAZwkoCWwFCrrfMwXrL7OhZbj7ifo=\n"
+"-----END SIGNATURE-----\n";
+
diff --git a/src/trunnel/README b/src/trunnel/README
new file mode 100644
index 0000000000..e24aea0764
--- /dev/null
+++ b/src/trunnel/README
@@ -0,0 +1,21 @@
+This directory contains code for use with, and code made by, the
+automatic code generation tool "Trunnel".
+
+Trunnel generates binary parsers and formatters for simple data
+structures. It aims for human-readable, obviously-correct outputs over
+maximum efficiency or flexibility.
+
+The .trunnel files are the inputs here; the .c and .h files are the outputs.
+
+To add a new structure:
+ - Add a new .trunnel file or expand an existing one to describe the format
+ of the structure.
+ - Regenerate the .c and .h files. To do this, you run
+ "scripts/codegen/run_trunnel.sh". You'll need trunnel installed.
+ - Add the .trunnel, .c, and .h files to include.am
+
+For the Trunnel source code, and more documentation about using Trunnel,
+see https://gitweb.torproject.org/trunnel.git , especially
+ https://gitweb.torproject.org/trunnel.git/tree/README
+and https://gitweb.torproject.org/trunnel.git/tree/doc/trunnel.md
+
diff --git a/src/trunnel/include.am b/src/trunnel/include.am
index 9bf37fe58b..b1448b7cb2 100644
--- a/src/trunnel/include.am
+++ b/src/trunnel/include.am
@@ -36,3 +36,7 @@ src_trunnel_libor_trunnel_testing_a_CPPFLAGS = -DTRUNNEL_LOCAL_H $(AM_CPPFLAGS)
src_trunnel_libor_trunnel_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
noinst_HEADERS+= $(TRUNNELHEADERS)
+
+EXTRA_DIST += \
+ src/trunnel/README
+
diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h
index 11b5546787..c0b14e5304 100644
--- a/src/win32/orconfig.h
+++ b/src/win32/orconfig.h
@@ -232,7 +232,7 @@
#define USING_TWOS_COMPLEMENT
/* Version number of package */
-#define VERSION "0.2.7.5-dev"
+#define VERSION "0.2.8.0-alpha-dev"