diff options
Diffstat (limited to 'src')
93 files changed, 4350 insertions, 917 deletions
diff --git a/src/common/compat.c b/src/common/compat.c index 69eb0643d0..0e943f38fe 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -1746,6 +1746,15 @@ get_user_homedir(const char *username) * actually examine the filesystem; does a purely syntactic modification. * * The parent of the root director is considered to be iteself. + * + * Path separators are the forward slash (/) everywhere and additionally + * the backslash (\) on Win32. + * + * Cuts off any number of trailing path separators but otherwise ignores + * them for purposes of finding the parent directory. + * + * Returns 0 if a parent directory was successfully found, -1 otherwise (fname + * did not have any path separators or only had them at the end). * */ int get_parent_directory(char *fname) diff --git a/src/common/compat.h b/src/common/compat.h index 8ab7190526..258fc99020 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -84,13 +84,19 @@ /* ===== Compiler compatibility */ -/* GCC can check printf types on arbitrary functions. */ +/* GCC can check printf and scanf types on arbitrary functions. */ #ifdef __GNUC__ #define CHECK_PRINTF(formatIdx, firstArg) \ __attribute__ ((format(printf, formatIdx, firstArg))) #else #define CHECK_PRINTF(formatIdx, firstArg) #endif +#ifdef __GNUC__ +#define CHECK_SCANF(formatIdx, firstArg) \ + __attribute__ ((format(scanf, formatIdx, firstArg))) +#else +#define CHECK_SCANF(formatIdx, firstArg) +#endif /* inline is __inline on windows. */ #ifdef _WIN32 diff --git a/src/common/container.c b/src/common/container.c index eec497a3e6..476dc82913 100644 --- a/src/common/container.c +++ b/src/common/container.c @@ -243,6 +243,25 @@ smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2) return 1; } +/** Return true iff the two lists contain the same int pointer values in + * the same order, or if they are both NULL. */ +int +smartlist_ints_eq(const smartlist_t *sl1, const smartlist_t *sl2) +{ + if (sl1 == NULL) + return sl2 == NULL; + if (sl2 == NULL) + return 0; + if (smartlist_len(sl1) != smartlist_len(sl2)) + return 0; + SMARTLIST_FOREACH(sl1, int *, cp1, { + int *cp2 = smartlist_get(sl2, cp1_sl_idx); + if (*cp1 != *cp2) + return 0; + }); + return 1; +} + /** Return true iff <b>sl</b> has some element E such that * tor_memeq(E,<b>element</b>,DIGEST_LEN) */ diff --git a/src/common/container.h b/src/common/container.h index 1a68b8f67b..1bcc540665 100644 --- a/src/common/container.h +++ b/src/common/container.h @@ -42,6 +42,7 @@ int smartlist_contains_string_case(const smartlist_t *sl, const char *element); int smartlist_contains_int_as_string(const smartlist_t *sl, int num); int smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2); int smartlist_contains_digest(const smartlist_t *sl, const char *element); +int smartlist_ints_eq(const smartlist_t *sl1, const smartlist_t *sl2); int smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2); void smartlist_intersect(smartlist_t *sl1, const smartlist_t *sl2); void smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2); diff --git a/src/common/crypto.c b/src/common/crypto.c index 0ababeaea5..730ce08286 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -114,7 +114,6 @@ crypto_get_rsa_padding_overhead(int padding) switch (padding) { case RSA_PKCS1_OAEP_PADDING: return PKCS1_OAEP_PADDING_OVERHEAD; - case RSA_PKCS1_PADDING: return PKCS1_PADDING_OVERHEAD; default: tor_assert(0); return -1; } } @@ -126,7 +125,6 @@ crypto_get_rsa_padding(int padding) { switch (padding) { - case PK_PKCS1_PADDING: return RSA_PKCS1_PADDING; case PK_PKCS1_OAEP_PADDING: return RSA_PKCS1_OAEP_PADDING; default: tor_assert(0); return -1; } @@ -1154,22 +1152,21 @@ int crypto_pk_asn1_encode(crypto_pk_t *pk, char *dest, size_t dest_len) { int len; - unsigned char *buf, *cp; - len = i2d_RSAPublicKey(pk->key, NULL); - if (len < 0 || (size_t)len > dest_len || dest_len > SIZE_T_CEILING) + unsigned char *buf = NULL; + + len = i2d_RSAPublicKey(pk->key, &buf); + if (len < 0 || buf == NULL) return -1; - cp = buf = tor_malloc(len+1); - len = i2d_RSAPublicKey(pk->key, &cp); - if (len < 0) { - crypto_log_errors(LOG_WARN,"encoding public key"); - tor_free(buf); + + if ((size_t)len > dest_len || dest_len > SIZE_T_CEILING) { + OPENSSL_free(buf); return -1; } /* We don't encode directly into 'dest', because that would be illegal * type-punning. (C99 is smarter than me, C99 is smarter than me...) */ memcpy(dest,buf,len); - tor_free(buf); + OPENSSL_free(buf); return len; } @@ -1200,24 +1197,17 @@ crypto_pk_asn1_decode(const char *str, size_t len) int crypto_pk_get_digest(crypto_pk_t *pk, char *digest_out) { - unsigned char *buf, *bufp; + unsigned char *buf = NULL; int len; - len = i2d_RSAPublicKey(pk->key, NULL); - if (len < 0) + len = i2d_RSAPublicKey(pk->key, &buf); + if (len < 0 || buf == NULL) return -1; - buf = bufp = tor_malloc(len+1); - len = i2d_RSAPublicKey(pk->key, &bufp); - if (len < 0) { - crypto_log_errors(LOG_WARN,"encoding public key"); - tor_free(buf); - return -1; - } if (crypto_digest(digest_out, (char*)buf, len) < 0) { - tor_free(buf); + OPENSSL_free(buf); return -1; } - tor_free(buf); + OPENSSL_free(buf); return 0; } @@ -1226,31 +1216,24 @@ crypto_pk_get_digest(crypto_pk_t *pk, char *digest_out) int crypto_pk_get_all_digests(crypto_pk_t *pk, digests_t *digests_out) { - unsigned char *buf, *bufp; + unsigned char *buf = NULL; int len; - len = i2d_RSAPublicKey(pk->key, NULL); - if (len < 0) - return -1; - buf = bufp = tor_malloc(len+1); - len = i2d_RSAPublicKey(pk->key, &bufp); - if (len < 0) { - crypto_log_errors(LOG_WARN,"encoding public key"); - tor_free(buf); + len = i2d_RSAPublicKey(pk->key, &buf); + if (len < 0 || buf == NULL) return -1; - } if (crypto_digest_all(digests_out, (char*)buf, len) < 0) { - tor_free(buf); + OPENSSL_free(buf); return -1; } - tor_free(buf); + OPENSSL_free(buf); return 0; } /** Copy <b>in</b> to the <b>outlen</b>-byte buffer <b>out</b>, adding spaces * every four spaces. */ -/* static */ void -add_spaces_to_fp(char *out, size_t outlen, const char *in) +void +crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in) { int n = 0; char *end = out+outlen; @@ -1287,7 +1270,7 @@ crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out, int add_space) } base16_encode(hexdigest,sizeof(hexdigest),digest,DIGEST_LEN); if (add_space) { - add_spaces_to_fp(fp_out, FINGERPRINT_LEN+1, hexdigest); + crypto_add_spaces_to_fp(fp_out, FINGERPRINT_LEN+1, hexdigest); } else { strncpy(fp_out, hexdigest, HEX_DIGEST_LEN+1); } @@ -1637,21 +1620,6 @@ crypto_digest_smartlist(char *digest_out, size_t len_out, crypto_digest_free(d); } -/** Compute the HMAC-SHA-1 of the <b>msg_len</b> bytes in <b>msg</b>, using - * the <b>key</b> of length <b>key_len</b>. Store the DIGEST_LEN-byte result - * in <b>hmac_out</b>. - */ -void -crypto_hmac_sha1(char *hmac_out, - const char *key, size_t key_len, - const char *msg, size_t msg_len) -{ - tor_assert(key_len < INT_MAX); - tor_assert(msg_len < INT_MAX); - HMAC(EVP_sha1(), key, (int)key_len, (unsigned char*)msg, (int)msg_len, - (unsigned char*)hmac_out, NULL); -} - /** Compute the HMAC-SHA-256 of the <b>msg_len</b> bytes in <b>msg</b>, using * the <b>key</b> of length <b>key_len</b>. Store the DIGEST256_LEN-byte * result in <b>hmac_out</b>. @@ -1720,7 +1688,7 @@ crypto_store_dynamic_dh_modulus(const char *fname) { int len, new_len; DH *dh = NULL; - unsigned char *dh_string_repr = NULL, *cp = NULL; + unsigned char *dh_string_repr = NULL; char *base64_encoded_dh = NULL; char *file_string = NULL; int retval = -1; @@ -1744,15 +1712,8 @@ crypto_store_dynamic_dh_modulus(const char *fname) if (!BN_set_word(dh->g, DH_GENERATOR)) goto done; - len = i2d_DHparams(dh, NULL); - if (len < 0) { - log_warn(LD_CRYPTO, "Error occured while DER encoding DH modulus (1)."); - goto done; - } - - cp = dh_string_repr = tor_malloc_zero(len+1); - len = i2d_DHparams(dh, &cp); - if ((len < 0) || ((cp - dh_string_repr) != len)) { + len = i2d_DHparams(dh, &dh_string_repr); + if ((len < 0) || (dh_string_repr == NULL)) { log_warn(LD_CRYPTO, "Error occured while DER encoding DH modulus (2)."); goto done; } @@ -1779,7 +1740,8 @@ crypto_store_dynamic_dh_modulus(const char *fname) done: if (dh) DH_free(dh); - tor_free(dh_string_repr); + if (dh_string_repr) + OPENSSL_free(dh_string_repr); tor_free(base64_encoded_dh); tor_free(file_string); diff --git a/src/common/crypto.h b/src/common/crypto.h index 2fbca4c260..651d553a4e 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -15,6 +15,7 @@ #include <stdio.h> #include "torint.h" +#include "testsupport.h" /* Macro to create an arbitrary OpenSSL version number as used by @@ -69,13 +70,9 @@ * signs removed. */ #define BASE64_DIGEST256_LEN 43 -/** Constant used to indicate PKCS1 padding for public-key encryption */ -#define PK_PKCS1_PADDING 60001 /** Constant used to indicate OAEP padding for public-key encryption */ #define PK_PKCS1_OAEP_PADDING 60002 -/** Number of bytes added for PKCS1 padding. */ -#define PKCS1_PADDING_OVERHEAD 11 /** Number of bytes added for PKCS1-OAEP padding. */ #define PKCS1_OAEP_PADDING_OVERHEAD 42 @@ -221,9 +218,6 @@ void crypto_digest_get_digest(crypto_digest_t *digest, crypto_digest_t *crypto_digest_dup(const crypto_digest_t *digest); void crypto_digest_assign(crypto_digest_t *into, const crypto_digest_t *from); -void crypto_hmac_sha1(char *hmac_out, - const char *key, size_t key_len, - const char *msg, size_t msg_len); void crypto_hmac_sha256(char *hmac_out, const char *key, size_t key_len, const char *msg, size_t msg_len); @@ -290,7 +284,6 @@ void secret_to_key(char *key_out, size_t key_out_len, const char *secret, /** OpenSSL-based utility functions. */ void memwipe(void *mem, uint8_t byte, size_t sz); -#ifdef CRYPTO_PRIVATE /* Prototypes for private functions only used by tortls.c, crypto.c, and the * unit tests. */ struct rsa_st; @@ -301,9 +294,8 @@ crypto_pk_t *crypto_new_pk_from_rsa_(struct rsa_st *rsa); struct evp_pkey_st *crypto_pk_get_evp_pkey_(crypto_pk_t *env, int private); struct dh_st *crypto_dh_get_dh_(crypto_dh_t *dh); -/* Prototypes for private functions only used by crypto.c and test.c*/ -void add_spaces_to_fp(char *out, size_t outlen, const char *in); -#endif + +void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in); #endif diff --git a/src/common/crypto_curve25519.c b/src/common/crypto_curve25519.c index 88c723f37c..9e83440e16 100644 --- a/src/common/crypto_curve25519.c +++ b/src/common/crypto_curve25519.c @@ -29,7 +29,7 @@ int curve25519_donna(uint8_t *mypublic, #endif #endif -int +STATIC int curve25519_impl(uint8_t *output, const uint8_t *secret, const uint8_t *basepoint) { diff --git a/src/common/crypto_curve25519.h b/src/common/crypto_curve25519.h index 652f1883c6..f9d533ba22 100644 --- a/src/common/crypto_curve25519.h +++ b/src/common/crypto_curve25519.h @@ -4,6 +4,7 @@ #ifndef TOR_CRYPTO_CURVE25519_H #define TOR_CRYPTO_CURVE25519_H +#include "testsupport.h" #include "torint.h" /** Length of a curve25519 public key when encoded. */ @@ -52,8 +53,8 @@ int curve25519_keypair_read_from_file(curve25519_keypair_t *keypair_out, const char *fname); #ifdef CRYPTO_CURVE25519_PRIVATE -int curve25519_impl(uint8_t *output, const uint8_t *secret, - const uint8_t *basepoint); +STATIC int curve25519_impl(uint8_t *output, const uint8_t *secret, + const uint8_t *basepoint); #endif #endif diff --git a/src/common/crypto_format.c b/src/common/crypto_format.c index 93932f839c..be669c8d2b 100644 --- a/src/common/crypto_format.c +++ b/src/common/crypto_format.c @@ -3,7 +3,6 @@ /* Formatting and parsing code for crypto-related data structures. */ -#define CRYPTO_CURVE25519_PRIVATE #include "orconfig.h" #ifdef HAVE_SYS_STAT_H #include <sys/stat.h> diff --git a/src/common/include.am b/src/common/include.am index b796ebfae8..032befd209 100644 --- a/src/common/include.am +++ b/src/common/include.am @@ -1,5 +1,15 @@ -noinst_LIBRARIES+= src/common/libor.a src/common/libor-crypto.a src/common/libor-event.a +noinst_LIBRARIES += \ + src/common/libor.a \ + src/common/libor-crypto.a \ + src/common/libor-event.a + +if UNITTESTS_ENABLED +noinst_LIBRARIES += \ + src/common/libor-testing.a \ + src/common/libor-crypto-testing.a \ + src/common/libor-event-testing.a +endif EXTRA_DIST+= \ src/common/common_sha1.i \ @@ -14,9 +24,13 @@ else libor_extra_source= endif +src_common_libcurve25519_donna_a_CFLAGS= + if BUILD_CURVE25519_DONNA src_common_libcurve25519_donna_a_SOURCES=\ src/ext/curve25519_donna/curve25519-donna.c +src_common_libcurve25519_donna_a_CFLAGS+=\ + @F_OMIT_FRAME_POINTER@ noinst_LIBRARIES+=src/common/libcurve25519_donna.a LIBDONNA=src/common/libcurve25519_donna.a else @@ -30,13 +44,11 @@ LIBDONNA= endif endif -src_common_libcurve25519_donna_a_CFLAGS = - if CURVE25519_ENABLED libcrypto_extra_source=src/common/crypto_curve25519.c endif -src_common_libor_a_SOURCES = \ +LIBOR_A_SOURCES = \ src/common/address.c \ src/common/compat.c \ src/common/container.c \ @@ -47,9 +59,10 @@ src_common_libor_a_SOURCES = \ src/common/procmon.c \ src/common/util.c \ src/common/util_codedigest.c \ + src/common/sandbox.c \ $(libor_extra_source) -src_common_libor_crypto_a_SOURCES = \ +LIBOR_CRYPTO_A_SOURCES = \ src/common/aes.c \ src/common/crypto.c \ src/common/crypto_format.c \ @@ -57,7 +70,23 @@ src_common_libor_crypto_a_SOURCES = \ src/common/tortls.c \ $(libcrypto_extra_source) -src_common_libor_event_a_SOURCES = src/common/compat_libevent.c +LIBOR_EVENT_A_SOURCES = src/common/compat_libevent.c + +src_common_libor_a_SOURCES = $(LIBOR_A_SOURCES) +src_common_libor_crypto_a_SOURCES = $(LIBOR_CRYPTO_A_SOURCES) +src_common_libor_event_a_SOURCES = $(LIBOR_EVENT_A_SOURCES) + +src_common_libor_testing_a_SOURCES = $(LIBOR_A_SOURCES) +src_common_libor_crypto_testing_a_SOURCES = $(LIBOR_CRYPTO_A_SOURCES) +src_common_libor_event_testing_a_SOURCES = $(LIBOR_EVENT_A_SOURCES) + +src_common_libor_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS) +src_common_libor_crypto_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS) +src_common_libor_event_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS) +src_common_libor_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) +src_common_libor_crypto_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) +src_common_libor_event_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) + COMMONHEADERS = \ src/common/address.h \ @@ -72,6 +101,8 @@ COMMONHEADERS = \ src/common/memarea.h \ src/common/mempool.h \ src/common/procmon.h \ + src/common/sandbox.h \ + src/common/testsupport.h \ src/common/torgzip.h \ src/common/torint.h \ src/common/torlog.h \ diff --git a/src/common/log.c b/src/common/log.c index e196a11287..303fba93a1 100644 --- a/src/common/log.c +++ b/src/common/log.c @@ -36,6 +36,10 @@ #include "torlog.h" #include "container.h" +/** Given a severity, yields an index into log_severity_list_t.masks to use + * for that severity. */ +#define SEVERITY_MASK_IDX(sev) ((sev) - LOG_ERR) + /** @{ */ /** The string we stick at the end of a log message when it is too long, * and its length. */ @@ -1138,6 +1142,22 @@ get_min_log_level(void) return min; } +/** Return the fd of a file log that is receiving ERR messages, or -1 if + * no such log exists. */ +int +get_err_logging_fd(void) +{ + const logfile_t *lf; + for (lf = logfiles; lf; lf = lf->next) { + if (lf->is_temporary || lf->is_syslog || !lf->filename || + lf->callback || lf->seems_dead || lf->fd < 0) + continue; + if (lf->severities->masks[LOG_ERR] & LD_GENERAL) + return lf->fd; + } + return -1; +} + /** Switch all logs to output at most verbose level. */ void switch_logs_debug(void) diff --git a/src/common/sandbox.c b/src/common/sandbox.c new file mode 100644 index 0000000000..dbb1657cdb --- /dev/null +++ b/src/common/sandbox.c @@ -0,0 +1,331 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file sandbox.c + * \brief Code to enable sandboxing. + **/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "orconfig.h" +#include "sandbox.h" +#include "torlog.h" +#include "util.h" + +#if defined(HAVE_SECCOMP_H) && defined(__linux__) +#define USE_LIBSECCOMP +#endif + +#define DEBUGGING_CLOSE + +#if defined(USE_LIBSECCOMP) + +#include <sys/syscall.h> +#include <seccomp.h> +#include <signal.h> +#include <unistd.h> + +/** Variable used for storing all syscall numbers that will be allowed with the + * stage 1 general Tor sandbox. + */ +static int general_filter[] = { + SCMP_SYS(access), + SCMP_SYS(brk), + SCMP_SYS(clock_gettime), + SCMP_SYS(close), + SCMP_SYS(clone), + SCMP_SYS(epoll_create), + SCMP_SYS(epoll_ctl), + SCMP_SYS(epoll_wait), + SCMP_SYS(execve), + SCMP_SYS(fcntl), +#ifdef __NR_fcntl64 + /* Older libseccomp versions don't define PNR entries for all of these, + * so we need to ifdef them here.*/ + SCMP_SYS(fcntl64), +#endif + SCMP_SYS(flock), + SCMP_SYS(fstat), +#ifdef __NR_fstat64 + SCMP_SYS(fstat64), +#endif + SCMP_SYS(futex), + SCMP_SYS(getdents64), + SCMP_SYS(getegid), +#ifdef __NR_getegid32 + SCMP_SYS(getegid32), +#endif + SCMP_SYS(geteuid), +#ifdef __NR_geteuid32 + SCMP_SYS(geteuid32), +#endif + SCMP_SYS(getgid), +#ifdef __NR_getgid32 + SCMP_SYS(getgid32), +#endif + SCMP_SYS(getrlimit), + SCMP_SYS(gettimeofday), + SCMP_SYS(getuid), +#ifdef __NR_getuid32 + SCMP_SYS(getuid32), +#endif + SCMP_SYS(lseek), +#ifdef __NR__llseek + SCMP_SYS(_llseek), +#endif + SCMP_SYS(mkdir), + SCMP_SYS(mlockall), + SCMP_SYS(mmap), +#ifdef __NR_mmap2 + SCMP_SYS(mmap2), +#endif + SCMP_SYS(mprotect), + SCMP_SYS(mremap), + SCMP_SYS(munmap), + SCMP_SYS(open), + SCMP_SYS(openat), + SCMP_SYS(poll), + SCMP_SYS(prctl), + SCMP_SYS(read), + SCMP_SYS(rename), + SCMP_SYS(rt_sigaction), + SCMP_SYS(rt_sigprocmask), + SCMP_SYS(rt_sigreturn), +#ifdef __NR_sigreturn + SCMP_SYS(sigreturn), +#endif + SCMP_SYS(set_robust_list), + SCMP_SYS(set_thread_area), + SCMP_SYS(set_tid_address), + SCMP_SYS(stat), +#ifdef __NR_stat64 + SCMP_SYS(stat64), +#endif + SCMP_SYS(time), + SCMP_SYS(uname), + SCMP_SYS(write), + SCMP_SYS(exit_group), + SCMP_SYS(exit), + + // socket syscalls + SCMP_SYS(accept4), + SCMP_SYS(bind), + SCMP_SYS(connect), + SCMP_SYS(getsockname), + SCMP_SYS(getsockopt), + SCMP_SYS(listen), +#if __NR_recv >= 0 + /* This is a kludge; It's necessary on 64-bit with libseccomp 1.0.0; I + * don't know if other 64-bit or other versions require it. */ + SCMP_SYS(recv), +#endif + SCMP_SYS(recvmsg), +#if __NR_send >= 0 + SCMP_SYS(send), +#endif + SCMP_SYS(sendto), + SCMP_SYS(setsockopt), + SCMP_SYS(socket), + SCMP_SYS(socketpair), + + // TODO: remove when accept4 is fixed +#ifdef __NR_socketcall + SCMP_SYS(socketcall), +#endif + + SCMP_SYS(recvfrom), + SCMP_SYS(unlink) +}; + +/** + * Function responsible for setting up and enabling a global syscall filter. + * The function is a prototype developed for stage 1 of sandboxing Tor. + * Returns 0 on success. + */ +static int +install_glob_syscall_filter(void) +{ + int rc = 0, i, filter_size; + scmp_filter_ctx ctx; + + ctx = seccomp_init(SCMP_ACT_TRAP); + if (ctx == NULL) { + log_err(LD_BUG,"(Sandbox) failed to initialise libseccomp context"); + rc = -1; + goto end; + } + + if (general_filter != NULL) { + filter_size = sizeof(general_filter) / sizeof(general_filter[0]); + } else { + filter_size = 0; + } + + // add general filters + for (i = 0; i < filter_size; i++) { + rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, general_filter[i], 0); + if (rc != 0) { + log_err(LD_BUG,"(Sandbox) failed to add syscall index %d, " + "received libseccomp error %d", i, rc); + goto end; + } + } + + rc = seccomp_load(ctx); + + end: + seccomp_release(ctx); + return (rc < 0 ? -rc : rc); +} + +/** Additional file descriptor to use when logging seccomp2 failures */ +static int sigsys_debugging_fd = -1; + +/** Use the file descriptor <b>fd</b> to log seccomp2 failures. */ +static void +sigsys_set_debugging_fd(int fd) +{ + sigsys_debugging_fd = fd; +} + +/** + * Function called when a SIGSYS is caught by the application. It notifies the + * user that an error has occurred and either terminates or allows the + * application to continue execution, based on the DEBUGGING_CLOSE symbol. + */ +static void +sigsys_debugging(int nr, siginfo_t *info, void *void_context) +{ + ucontext_t *ctx = (ucontext_t *) (void_context); + char message[256]; + int rv = 0, syscall, length, err; + (void) nr; + + if (info->si_code != SYS_SECCOMP) + return; + + if (!ctx) + return; + + syscall = ctx->uc_mcontext.gregs[REG_SYSCALL]; + + strlcpy(message, "\n\n(Sandbox) Caught a bad syscall attempt (syscall 0x", + sizeof(message)); + (void) format_hex_number_sigsafe(syscall, message+strlen(message), + sizeof(message)-strlen(message)); + strlcat(message, ")\n", sizeof(message)); + length = strlen(message); + + err = 0; + if (sigsys_debugging_fd >= 0) { + rv = write(sigsys_debugging_fd, message, length); + err += rv != length; + } + + rv = write(STDOUT_FILENO, message, length); + err += rv != length; + + if (err) + _exit(2); + +#if defined(DEBUGGING_CLOSE) + _exit(1); +#endif // DEBUGGING_CLOSE +} + +/** + * Function that adds a handler for SIGSYS, which is the signal thrown + * when the application is issuing a syscall which is not allowed. The + * main purpose of this function is to help with debugging by identifying + * filtered syscalls. + */ +static int +install_sigsys_debugging(void) +{ + struct sigaction act; + sigset_t mask; + + memset(&act, 0, sizeof(act)); + sigemptyset(&mask); + sigaddset(&mask, SIGSYS); + + act.sa_sigaction = &sigsys_debugging; + act.sa_flags = SA_SIGINFO; + if (sigaction(SIGSYS, &act, NULL) < 0) { + log_err(LD_BUG,"(Sandbox) Failed to register SIGSYS signal handler"); + return -1; + } + + if (sigprocmask(SIG_UNBLOCK, &mask, NULL)) { + log_err(LD_BUG,"(Sandbox) Failed call to sigprocmask()"); + return -2; + } + + return 0; +} +#endif // USE_LIBSECCOMP + +#ifdef USE_LIBSECCOMP +/** + * Initialises the syscall sandbox filter for any linux architecture, taking + * into account various available features for different linux flavours. + */ +static int +initialise_libseccomp_sandbox(void) +{ + if (install_sigsys_debugging()) + return -1; + + if (install_glob_syscall_filter()) + return -2; + + return 0; +} + +#endif // USE_LIBSECCOMP + +/** + * Enables the stage 1 general sandbox. It applies a syscall filter which does + * not restrict any Tor features. The filter is representative for the whole + * application. + */ +int +tor_global_sandbox(void) +{ + +#if defined(USE_LIBSECCOMP) + return initialise_libseccomp_sandbox(); + +#elif defined(_WIN32) + log_warn(LD_BUG,"Windows sandboxing is not implemented. The feature is " + "currently disabled."); + return 0; + +#elif defined(TARGET_OS_MAC) + log_warn(LD_BUG,"Mac OSX sandboxing is not implemented. The feature is " + "currently disabled"); + return 0; +#else + log_warn(LD_BUG,"Sandboxing is not implemented for your platform. The " + "feature is currently disabled"); + return 0; +#endif +} + +/** Use <b>fd</b> to log non-survivable sandbox violations. */ +void +sandbox_set_debugging_fd(int fd) +{ +#ifdef USE_LIBSECCOMP + sigsys_set_debugging_fd(fd); +#else + (void)fd; +#endif +} + diff --git a/src/common/sandbox.h b/src/common/sandbox.h new file mode 100644 index 0000000000..bd6f0cfb47 --- /dev/null +++ b/src/common/sandbox.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file sandbox.h + * \brief Header file for sandbox.c. + **/ + +#ifndef SANDBOX_H_ +#define SANDBOX_H_ + +#ifndef SYS_SECCOMP + +/** + * Used by SIGSYS signal handler to check if the signal was issued due to a + * seccomp2 filter violation. + */ +#define SYS_SECCOMP 1 + +#endif + +/** + * Linux definitions + */ +#ifdef __linux__ + +#define __USE_GNU +#include <sys/ucontext.h> + +/** + * Linux 32 bit definitions + */ +#if defined(__i386__) + +#define REG_SYSCALL REG_EAX + +/** + * Linux 64 bit definitions + */ +#elif defined(__x86_64__) + +#define REG_SYSCALL REG_RAX + +#endif + +#endif // __linux__ + +void sandbox_set_debugging_fd(int fd); +int tor_global_sandbox(void); + +#endif /* SANDBOX_H_ */ + diff --git a/src/common/testsupport.h b/src/common/testsupport.h new file mode 100644 index 0000000000..4a4f50b69b --- /dev/null +++ b/src/common/testsupport.h @@ -0,0 +1,80 @@ +/* Copyright (c) 2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_TESTSUPPORT_H +#define TOR_TESTSUPPORT_H + +#ifdef TOR_UNIT_TESTS +#define STATIC +#else +#define STATIC static +#endif + +/** Quick and dirty macros to implement test mocking. + * + * To use them, suppose that you have a function you'd like to mock + * with the signature "void writebuf(size_t n, char *buf)". You can then + * declare the function as: + * + * MOCK_DECL(void, writebuf, (size_t n, char *buf)); + * + * and implement it as: + * + * MOCK_IMPL(void + * writebuf,(size_t n, char *buf) + * { + * ... + * } + * + * For the non-testing build, this will expand simply into: + * + * void writebuf(size_t n, char *buf); + * void + * writebuf(size_t n, char *buf) + * { + * ... + * } + * + * But for the testing case, it will expand into: + * + * void writebuf__real(size_t n, char *buf); + * extern void (*writebuf)(size_t n, char *buf); + * + * void (*writebuf)(size_t n, char *buf) = writebuf__real; + * void + * writebuf__real(size_t n, char *buf) + * { + * ... + * } + * + * This is not a great mocking system! It is deliberately "the simplest + * thing that could work", and pays for its simplicity in its lack of + * features, and in its uglification of the Tor code. Replacing it with + * something clever would be a fine thing. + * + * @{ */ +#ifdef TOR_UNIT_TESTS +#define MOCK_DECL(rv, funcname, arglist) \ + rv funcname ##__real arglist; \ + extern rv(*funcname) arglist +#define MOCK_IMPL(rv, funcname, arglist) \ + rv(*funcname) arglist = funcname ##__real; \ + rv funcname ##__real arglist +#define MOCK(func, replacement) \ + do { \ + (func) = (replacement); \ + } while (0) +#define UNMOCK(func) \ + do { \ + func = func ##__real; \ + } while (0) +#else +#define MOCK_DECL(rv, funcname, arglist) \ + rv funcname arglist +#define MOCK_IMPL(rv, funcname, arglist) \ + rv funcname arglist +#endif +/** @} */ + +#endif + diff --git a/src/common/torlog.h b/src/common/torlog.h index 8675d7b6e7..ecd7e121eb 100644 --- a/src/common/torlog.h +++ b/src/common/torlog.h @@ -114,12 +114,6 @@ typedef struct log_severity_list_t { log_domain_mask_t masks[LOG_DEBUG-LOG_ERR+1]; } log_severity_list_t; -#ifdef LOG_PRIVATE -/** Given a severity, yields an index into log_severity_list_t.masks to use - * for that severity. */ -#define SEVERITY_MASK_IDX(sev) ((sev) - LOG_ERR) -#endif - /** Callback type used for add_callback_log. */ typedef void (*log_callback)(int severity, uint32_t domain, const char *msg); @@ -142,6 +136,7 @@ int get_min_log_level(void); void switch_logs_debug(void); void logs_free_all(void); void add_temp_log(int min_severity); +int get_err_logging_fd(void); void close_temp_logs(void); void rollback_log_changes(void); void mark_logs_temp(void); diff --git a/src/common/tortls.c b/src/common/tortls.c index b7e5bc1a5f..df706b0012 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -48,9 +48,6 @@ #include "compat_libevent.h" #endif -#define CRYPTO_PRIVATE /* to import prototypes from crypto.h */ -#define TORTLS_PRIVATE - #include "crypto.h" #include "tortls.h" #include "util.h" @@ -806,24 +803,24 @@ tor_cert_new(X509 *x509_cert) tor_cert_t *cert; EVP_PKEY *pkey; RSA *rsa; - int length, length2; - unsigned char *cp; + int length; + unsigned char *buf = NULL; if (!x509_cert) return NULL; - length = i2d_X509(x509_cert, NULL); + length = i2d_X509(x509_cert, &buf); cert = tor_malloc_zero(sizeof(tor_cert_t)); - if (length <= 0) { + if (length <= 0 || buf == NULL) { tor_free(cert); log_err(LD_CRYPTO, "Couldn't get length of encoded x509 certificate"); X509_free(x509_cert); return NULL; } cert->encoded_len = (size_t) length; - cp = cert->encoded = tor_malloc(length); - length2 = i2d_X509(x509_cert, &cp); - tor_assert(length2 == length); + cert->encoded = tor_malloc(length); + memcpy(cert->encoded, buf, length); + OPENSSL_free(buf); cert->cert = x509_cert; @@ -979,31 +976,6 @@ tor_tls_cert_get_key(tor_cert_t *cert) return result; } -/** Return true iff <b>a</b> and <b>b</b> represent the same public key. */ -static int -pkey_eq(EVP_PKEY *a, EVP_PKEY *b) -{ - /* We'd like to do this, but openssl 0.9.7 doesn't have it: - return EVP_PKEY_cmp(a,b) == 1; - */ - unsigned char *a_enc=NULL, *b_enc=NULL, *a_ptr, *b_ptr; - int a_len1, b_len1, a_len2, b_len2, result; - a_len1 = i2d_PublicKey(a, NULL); - b_len1 = i2d_PublicKey(b, NULL); - if (a_len1 != b_len1) - return 0; - a_ptr = a_enc = tor_malloc(a_len1); - b_ptr = b_enc = tor_malloc(b_len1); - a_len2 = i2d_PublicKey(a, &a_ptr); - b_len2 = i2d_PublicKey(b, &b_ptr); - tor_assert(a_len2 == a_len1); - tor_assert(b_len2 == b_len1); - result = tor_memeq(a_enc, b_enc, a_len1); - tor_free(a_enc); - tor_free(b_enc); - return result; -} - /** Return true iff the other side of <b>tls</b> has authenticated to us, and * the key certified in <b>cert</b> is the same as the key they used to do it. */ @@ -1019,7 +991,7 @@ tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert) link_key = X509_get_pubkey(peercert); cert_key = X509_get_pubkey(cert->cert); - result = link_key && cert_key && pkey_eq(cert_key, link_key); + result = link_key && cert_key && EVP_PKEY_cmp(cert_key, link_key) == 1; X509_free(peercert); if (link_key) diff --git a/src/common/util.c b/src/common/util.c index db160fdf0a..fc247acbeb 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -879,6 +879,39 @@ tor_digest_is_zero(const char *digest) return tor_memeq(digest, ZERO_DIGEST, DIGEST_LEN); } +/** Return true if <b>string</b> is a valid '<key>=[<value>]' string. + * <value> is optional, to indicate the empty string. Log at logging + * <b>severity</b> if something ugly happens. */ +int +string_is_key_value(int severity, const char *string) +{ + /* position of equal sign in string */ + const char *equal_sign_pos = NULL; + + tor_assert(string); + + if (strlen(string) < 2) { /* "x=" is shortest args string */ + tor_log(severity, LD_GENERAL, "'%s' is too short to be a k=v value.", + escaped(string)); + return 0; + } + + equal_sign_pos = strchr(string, '='); + if (!equal_sign_pos) { + tor_log(severity, LD_GENERAL, "'%s' is not a k=v value.", escaped(string)); + return 0; + } + + /* validate that the '=' is not in the beginning of the string. */ + if (equal_sign_pos == string) { + tor_log(severity, LD_GENERAL, "'%s' is not a valid k=v value.", + escaped(string)); + return 0; + } + + return 1; +} + /** Return true iff the DIGEST256_LEN bytes in digest are all zero. */ int tor_digest256_is_zero(const char *digest) @@ -1190,6 +1223,43 @@ escaped(const char *s) return escaped_val_; } +/** Return a newly allocated string equal to <b>string</b>, except that every + * character in <b>chars_to_escape</b> is preceded by a backslash. */ +char * +tor_escape_str_for_pt_args(const char *string, const char *chars_to_escape) +{ + char *new_string = NULL; + char *new_cp = NULL; + size_t length, new_length; + + tor_assert(string); + + length = strlen(string); + + if (!length) /* If we were given the empty string, return the same. */ + return tor_strdup(""); + /* (new_length > SIZE_MAX) => ((length * 2) + 1 > SIZE_MAX) => + (length*2 > SIZE_MAX - 1) => (length > (SIZE_MAX - 1)/2) */ + if (length > (SIZE_MAX - 1)/2) /* check for overflow */ + return NULL; + + /* this should be enough even if all characters must be escaped */ + new_length = (length * 2) + 1; + + new_string = new_cp = tor_malloc(new_length); + + while (*string) { + if (strchr(chars_to_escape, *string)) + *new_cp++ = '\\'; + + *new_cp++ = *string++; + } + + *new_cp = '\0'; /* NUL-terminate the new string */ + + return new_string; +} + /* ===== * Time * ===== */ @@ -3309,13 +3379,13 @@ tor_join_win_cmdline(const char *argv[]) } /** - * Helper function to output hex numbers, called by - * format_helper_exit_status(). This writes the hexadecimal digits of x into - * buf, up to max_len digits, and returns the actual number of digits written. - * If there is insufficient space, it will write nothing and return 0. + * Helper function to output hex numbers from within a signal handler. + * + * Writes the nul-terminated hexadecimal digits of <b>x</b> into a buffer + * <b>buf</b> of size <b>buf_len</b>, and return the actual number of digits + * written, not counting the terminal NUL. * - * This function DOES NOT add a terminating NUL character to its output: be - * careful! + * If there is insufficient space, write nothing and return 0. * * This accepts an unsigned int because format_helper_exit_status() needs to * call it with a signed int and an unsigned char, and since the C standard @@ -3330,15 +3400,14 @@ tor_join_win_cmdline(const char *argv[]) * arbitrary C functions. */ int -format_hex_number_for_helper_exit_status(unsigned int x, char *buf, - int max_len) +format_hex_number_sigsafe(unsigned int x, char *buf, int buf_len) { int len; unsigned int tmp; char *cur; /* Sanity check */ - if (!buf || max_len <= 0) + if (!buf || buf_len <= 1) return 0; /* How many chars do we need for x? */ @@ -3354,7 +3423,7 @@ format_hex_number_for_helper_exit_status(unsigned int x, char *buf, } /* Bail if we would go past the end of the buffer */ - if (len > max_len) + if (len+1 > buf_len) return 0; /* Point to last one */ @@ -3366,10 +3435,13 @@ format_hex_number_for_helper_exit_status(unsigned int x, char *buf, x >>= 4; } while (x != 0 && cur >= buf); + buf[len] = '\0'; + /* Return len */ return len; } +#ifndef _WIN32 /** Format <b>child_state</b> and <b>saved_errno</b> as a hex string placed in * <b>hex_errno</b>. Called between fork and _exit, so must be signal-handler * safe. @@ -3385,7 +3457,7 @@ format_hex_number_for_helper_exit_status(unsigned int x, char *buf, * On success return the number of characters added to hex_errno, not counting * the terminating NUL; return -1 on error. */ -int +STATIC int format_helper_exit_status(unsigned char child_state, int saved_errno, char *hex_errno) { @@ -3416,8 +3488,8 @@ format_helper_exit_status(unsigned char child_state, int saved_errno, cur = hex_errno; /* Emit child_state */ - written = format_hex_number_for_helper_exit_status(child_state, - cur, left); + written = format_hex_number_sigsafe(child_state, cur, left); + if (written <= 0) goto err; @@ -3446,8 +3518,7 @@ format_helper_exit_status(unsigned char child_state, int saved_errno, } /* Emit unsigned_errno */ - written = format_hex_number_for_helper_exit_status(unsigned_errno, - cur, left); + written = format_hex_number_sigsafe(unsigned_errno, cur, left); if (written <= 0) goto err; @@ -3478,6 +3549,7 @@ format_helper_exit_status(unsigned char child_state, int saved_errno, done: return res; } +#endif /* Maximum number of file descriptors, if we cannot get it via sysconf() */ #define DEFAULT_MAX_FD 256 @@ -3894,9 +3966,9 @@ tor_spawn_background(const char *const filename, const char **argv, * <b>process_handle</b>. * If <b>also_terminate_process</b> is true, also terminate the * process of the process handle. */ -void -tor_process_handle_destroy(process_handle_t *process_handle, - int also_terminate_process) +MOCK_IMPL(void, +tor_process_handle_destroy,(process_handle_t *process_handle, + int also_terminate_process)) { if (!process_handle) return; @@ -4405,9 +4477,9 @@ stream_status_to_string(enum stream_status stream_status) /** Return a smartlist containing lines outputted from * <b>handle</b>. Return NULL on error, and set * <b>stream_status_out</b> appropriately. */ -smartlist_t * -tor_get_lines_from_handle(HANDLE *handle, - enum stream_status *stream_status_out) +MOCK_IMPL(smartlist_t *, +tor_get_lines_from_handle, (HANDLE *handle, + enum stream_status *stream_status_out)) { int pos; char stdout_buf[600] = {0}; @@ -4495,8 +4567,8 @@ log_from_handle(HANDLE *pipe, int severity) /** Return a smartlist containing lines outputted from * <b>handle</b>. Return NULL on error, and set * <b>stream_status_out</b> appropriately. */ -smartlist_t * -tor_get_lines_from_handle(FILE *handle, enum stream_status *stream_status_out) +MOCK_IMPL(smartlist_t *, +tor_get_lines_from_handle,(FILE *handle, enum stream_status *stream_status_out)) { enum stream_status stream_status; char stdout_buf[400]; diff --git a/src/common/util.h b/src/common/util.h index 96a02dd775..dd7a169a1b 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -15,6 +15,7 @@ #include "torint.h" #include "compat.h" #include "di_ops.h" +#include "testsupport.h" #include <stdio.h> #include <stdlib.h> #ifdef _WIN32 @@ -222,23 +223,22 @@ const char *find_whitespace_eos(const char *s, const char *eos); const char *find_str_at_start_of_line(const char *haystack, const char *needle); int string_is_C_identifier(const char *string); +int string_is_key_value(int severity, const char *string); int tor_mem_is_zero(const char *mem, size_t len); int tor_digest_is_zero(const char *digest); int tor_digest256_is_zero(const char *digest); char *esc_for_log(const char *string) ATTR_MALLOC; const char *escaped(const char *string); + +char *tor_escape_str_for_pt_args(const char *string, + const char *chars_to_escape); + struct smartlist_t; -int tor_vsscanf(const char *buf, const char *pattern, va_list ap) -#ifdef __GNUC__ - __attribute__((format(scanf, 2, 0))) -#endif - ; +int tor_vsscanf(const char *buf, const char *pattern, va_list ap) \ + CHECK_SCANF(2, 0); int tor_sscanf(const char *buf, const char *pattern, ...) -#ifdef __GNUC__ - __attribute__((format(scanf, 2, 3))) -#endif - ; + CHECK_SCANF(2, 3); void smartlist_add_asprintf(struct smartlist_t *sl, const char *pattern, ...) CHECK_PRINTF(2, 3); @@ -492,18 +492,21 @@ FILE *tor_process_get_stdout_pipe(process_handle_t *process_handle); #endif #ifdef _WIN32 -struct smartlist_t * -tor_get_lines_from_handle(HANDLE *handle, - enum stream_status *stream_status); +MOCK_DECL(struct smartlist_t *, +tor_get_lines_from_handle,(HANDLE *handle, + enum stream_status *stream_status)); #else -struct smartlist_t * -tor_get_lines_from_handle(FILE *handle, - enum stream_status *stream_status); +MOCK_DECL(struct smartlist_t *, +tor_get_lines_from_handle,(FILE *handle, + enum stream_status *stream_status)); #endif -int tor_terminate_process(process_handle_t *process_handle); -void tor_process_handle_destroy(process_handle_t *process_handle, - int also_terminate_process); +int +tor_terminate_process(process_handle_t *process_handle); + +MOCK_DECL(void, +tor_process_handle_destroy,(process_handle_t *process_handle, + int also_terminate_process)); /* ===== Insecure rng */ typedef struct tor_weak_rng_t { @@ -519,12 +522,13 @@ int32_t tor_weak_random_range(tor_weak_rng_t *rng, int32_t top); * <b>n</b> */ #define tor_weak_random_one_in_n(rng, n) (0==tor_weak_random_range((rng),(n))) +int format_hex_number_sigsafe(unsigned int x, char *buf, int max_len); + #ifdef UTIL_PRIVATE /* Prototypes for private functions only used by util.c (and unit tests) */ -int format_hex_number_for_helper_exit_status(unsigned int x, char *buf, - int max_len); -int format_helper_exit_status(unsigned char child_state, +#ifndef _WIN32 +STATIC int format_helper_exit_status(unsigned char child_state, int saved_errno, char *hex_errno); /* Space for hex values of child state, a slash, saved_errno (with @@ -533,6 +537,8 @@ int format_helper_exit_status(unsigned char child_state, 1 + sizeof(int) * 2 + 1) #endif +#endif + const char *libor_get_digests(void); #endif diff --git a/src/config/deanonymind.py b/src/config/deanonymind.py index c86dadca99..31d0658eea 100755 --- a/src/config/deanonymind.py +++ b/src/config/deanonymind.py @@ -156,23 +156,34 @@ def apply_manual_changes(assignments, manual_assignments): entry['end_num'] == manual_entry['end_num']: if len(manual_entry['country_code']) != 2: print '-%s' % (line, ) # only remove, don't replace - else: + del manual_dict[start_num] + elif entry['country_code'] != \ + manual_entry['country_code']: new_line = format_line_with_other_country(entry, manual_entry) print '-%s\n+%s' % (line, new_line, ) result.append(new_line) - del manual_dict[start_num] + del manual_dict[start_num] + else: + print ('Warning: not applying ineffective manual ' + 'change:\n %s\n %s' % (line, manual_line, )) + result.append(line) else: - print ('Warning: only partial match between ' - 'original/automatically replaced assignment and ' - 'manual assignment:\n %s\n %s\nNot applying ' - 'manual change.' % (line, manual_line, )) + print ('Warning: not applying manual change that is only ' + 'a partial match:\n %s\n %s' % + (line, manual_line, )) result.append(line) + elif 'country_code' in entry and \ + entry['country_code'] == 'A1': + print ('Warning: no manual replacement for A1 entry:\n %s' + % (line, )) + result.append(line) else: result.append(line) if len(manual_dict) > 0: - print ('Warning: could not apply all manual assignments: %s' % - ('\n '.join(manual_dict.values())), ) + print 'Warning: could not apply all manual assignments:' + for line in manual_dict.values(): + print ' %s' % (line, ) return result def write_file(path, assignments, long_format=True): diff --git a/src/config/geoip-manual b/src/config/geoip-manual index 99c897ff42..ff9be5691c 100644 --- a/src/config/geoip-manual +++ b/src/config/geoip-manual @@ -3,11 +3,10 @@ # directory to process this file when producing a new geoip file. See # README.geoip in the same directory for details. -# Remove MaxMind entry 0.116.0.0-0.119.255.255 which MaxMind says is AT, -# but which is part of reserved range 0.0.0.0/8. -KL 2012-06-13 -# Disabled, because MaxMind apparently removed this range from their -# database. -KL 2013-02-08 -#"0.116.0.0","0.119.255.255","7602176","7864319","","" +# GB, because previous MaxMind entry 31.6.16.0-31.6.25.255 is GB, and RIR +# delegation files say entire range 31.6.0.0-31.6.63.255 is GB. +# -KL 2013-03-07 +"31.6.26.0","31.6.27.255","520493568","520494079","GB","United Kingdom" # NL, because previous MaxMind entry 31.171.128.0-31.171.133.255 is NL, # and RIR delegation files say 31.171.128.0-31.171.135.255 is NL. @@ -20,6 +19,17 @@ # -KL 2012-11-27 "37.139.64.0","37.139.64.0","629882880","629882880","EU","Europe" +# US, because next MaxMind entry 38.99.145.0-38.99.149.255 is US and +# RIR delegation files say entire range 38.0.0.0-38.255.255.255 is US. +# -KL 2013-05-13 +"38.99.144.0","38.99.144.255","644059136","644059391","US","United States" + +# GB, because RIR delegation files say exactly this range +# 46.16.32.0-46.16.39.255 is GB, even though neither previous nor next +# MaxMind range is GB. Both previous and next MaxMind ranges match RIR +# delegation files, too. -KL 2013-03-07 +"46.16.32.0","46.16.39.255","772808704","772810751","GB","United Kingdom" + # CH, because previous MaxMind entry 46.19.141.0-46.19.142.255 is CH, and # RIR delegation files say 46.19.136.0-46.19.143.255 is CH. # -KL 2012-11-27 @@ -30,17 +40,15 @@ # -KL 2012-11-27 "46.166.128.0","46.166.128.255","782663680","782663935","GB","United Kingdom" -# US, though could as well be CA. Previous MaxMind entry -# 64.237.32.52-64.237.34.127 is US, next MaxMind entry -# 64.237.34.144-64.237.34.151 is CA, and RIR delegation files say the -# entire block 64.237.32.0-64.237.63.255 is US. -KL 2012-11-27 -"64.237.34.128","64.237.34.143","1089282688","1089282703","US","United States" +# GB, because previous MaxMind entry 46.166.129.0-46.166.134.255 is GB, +# and RIR delegation files say entire range 46.166.128.0-46.166.191.255 is +# GB. -KL 2013-03-07 +"46.166.135.0","46.166.139.255","782665472","782666751","GB","United Kingdom" -# US, though could as well be UY. Previous MaxMind entry -# 67.15.170.0-67.15.182.255 is US, next MaxMind entry -# 67.15.183.128-67.15.183.159 is UY, and RIR delegation files say the -# entire block 67.15.0.0-67.15.255.255 is US. -KL 2012-11-27 -"67.15.183.0","67.15.183.127","1125103360","1125103487","US","United States" +# Removing, because RIR delegation files don't even have an entry for this +# single-address range, and there's no previous or next range in MaxMind. +# -KL 2013-03-07 +"64.185.237.110","64.185.237.110","1085926766","1085926766","","" # US, because next MaxMind entry 67.43.145.0-67.43.155.255 is US, and RIR # delegation files say 67.43.144.0-67.43.159.255 is US. @@ -61,6 +69,24 @@ # US. -KL 2012-11-27 "70.232.245.60","70.232.245.255","1189672252","1189672447","US","United States" +# SE, because previous MaxMind entry 80.67.11.200-80.67.13.255 is SE, and +# RIR delegation files say entire range 80.67.0.0-80.67.15.255 is SE. +# -KL 2013-03-07 +"80.67.14.0","80.67.15.255","1346571776","1346572287","SE","Sweden" + +# NL, because previous MaxMind entry 81.171.56.0-81.171.80.255 is NL, and +# RIR delegation files say entire range 81.171.64.0-81.171.127.255 is NL. +# -KL 2013-03-07 +"81.171.81.0","81.171.81.127","1370181888","1370182015","NL","Netherlands" + +# GB, because RIPE NCC says 85.237.192.0-85.237.223.255 is GB. +"85.237.222.0","85.237.222.255","1441652224","1441652479","GB","United Kingdom" + +# BE, because next MaxMind entry 86.39.147.0-86.39.148.31 is BE, and RIR +# delegation files say entire range 86.39.128.0-86.39.255.255 is BE. +# -KL 2013-04-08 +"86.39.146.0","86.39.146.255","1445433856","1445434111","BE","Belgium" + # GB, despite neither previous (GE) nor next (LV) MaxMind entry being GB, # but because RIR delegation files agree with both previous and next # MaxMind entry and say GB for 91.228.0.0-91.228.3.255. -KL 2012-11-27 @@ -77,9 +103,14 @@ # -KL 2012-11-27 "91.238.214.0","91.238.215.255","1542379008","1542379519","GB","United Kingdom" -# US, because next MaxMind entry 173.0.16.0-173.0.65.255 is US, and RIR -# delegation files say 173.0.0.0-173.0.15.255 is US. -KL 2012-11-27 -"173.0.0.0","173.0.15.255","2902458368","2902462463","US","United States" +# NL, because next MaxMind entry 176.56.173.0-176.56.173.63 is NL, and RIR +# delegation files say 176.56.160.0-176.56.191.255 is NL. -KL 2013-05-13 +"176.56.172.0","176.56.172.255","2956504064","2956504319","NL","Netherlands" + +# NL, despite neither previous (RU) nor next (GB) MaxMind entry being NL, +# but because RIR delegation files say entire range +# 176.56.160.0-176.56.191.255 is NL. -KL 2013-05-13 +"176.56.174.0","176.56.174.255","2956504576","2956504831","NL","Netherlands" # US, because next MaxMind entry 176.67.84.0-176.67.84.79 is US, and RIR # delegation files say 176.67.80.0-176.67.87.255 is US. -KL 2012-11-27 @@ -90,6 +121,12 @@ # -KL 2012-11-27 "176.67.86.0","176.67.87.255","2957202944","2957203455","US","United States" +# GB, because RIR delegation files say exactly this range +# 185.25.84.0-185.25.87.255 is GB, even though neither previous nor next +# MaxMind range is GB. Both previous and next MaxMind ranges match RIR +# delegation files, too. -KL 2013-05-13 +"185.25.84.0","185.25.87.255","3105444864","3105445887","GB","United Kingdom" + # EU, despite neither previous (RU) nor next (UA) MaxMind entry being EU, # but because RIR delegation files agree with both previous and next # MaxMind entry and say EU for 193.200.150.0-193.200.150.255. @@ -101,11 +138,29 @@ # -KL 2012-11-27 "199.96.87.128","199.96.87.255","3344979840","3344979967","US","United States" +# US, because next MaxMind entry 199.101.193.0-199.101.195.255 is US, and, +# together with next entries, matches RIR delegation file entry +# 199.101.192.0-199.101.199.255 which is US. -KL 2013-05-13 +"199.101.192.0","199.101.192.255","3345334272","3345334527","US","United States" + +# US, because ARIN says 199.255.208.0-199.255.215.255 is US. +"199.255.213.0","199.255.215.255","3355432192","3355432959","US","United States" + +# US, because next MaxMind entry 204.12.162.0-204.12.197.119 is US, and +# RIR delegation files say 204.12.160.0-204.12.191.255 is US. +# -KL 2013-05-13 +"204.12.160.0","204.12.161.255","3423379456","3423379967","US","United States" + # US, because previous MaxMind entry 209.58.176.144-209.59.31.255 is US, # and RIR delegation files say 209.59.32.0-209.59.63.255 is US. # -KL 2012-11-27 "209.59.32.0","209.59.63.255","3510312960","3510321151","US","United States" +# EU, despite neither previous (RU) nor next (SE) MaxMind entry being EU, +# but because RIR delegation files agree with previous MaxMind entry and +# say EU for 217.15.160.0-217.15.175.255. -KL 2013-05-13 +"217.15.160.0","217.15.164.255","3641679872","3641681151","EU","Europe" + # FR, because previous MaxMind entry 217.15.166.0-217.15.166.255 is FR, # and RIR delegation files contain a block 217.15.160.0-217.15.175.255 # which, however, is EU, not FR. But merging with next MaxMind entry diff --git a/src/or/addressmap.c b/src/or/addressmap.c index 79e4b7c5e2..9bc79bd84b 100644 --- a/src/or/addressmap.c +++ b/src/or/addressmap.c @@ -798,7 +798,7 @@ address_is_in_virtual_range(const char *address) /** Return a random address conforming to the virtual address configuration * in <b>conf</b>. */ -/* private */ void +STATIC void get_random_virtual_addr(const virtual_addr_conf_t *conf, tor_addr_t *addr_out) { uint8_t tmp[4]; diff --git a/src/or/addressmap.h b/src/or/addressmap.h index 40210ee990..417832b31f 100644 --- a/src/or/addressmap.h +++ b/src/or/addressmap.h @@ -7,6 +7,8 @@ #ifndef TOR_ADDRESSMAP_H #define TOR_ADDRESSMAP_H +#include "testsupport.h" + void addressmap_init(void); void addressmap_clear_excluded_trackexithosts(const or_options_t *options); void addressmap_clear_invalid_automaps(const or_options_t *options); @@ -52,8 +54,8 @@ typedef struct virtual_addr_conf_t { maskbits_t bits; } virtual_addr_conf_t; -void get_random_virtual_addr(const virtual_addr_conf_t *conf, - tor_addr_t *addr_out); +STATIC void get_random_virtual_addr(const virtual_addr_conf_t *conf, + tor_addr_t *addr_out); #endif #endif diff --git a/src/or/buffers.c b/src/or/buffers.c index c4c847ec87..cc5890416f 100644 --- a/src/or/buffers.c +++ b/src/or/buffers.c @@ -1294,7 +1294,7 @@ buf_matches_at_pos(const buf_pos_t *pos, const char *s, size_t n) /** Return the first position in <b>buf</b> at which the <b>n</b>-character * string <b>s</b> occurs, or -1 if it does not occur. */ -/*private*/ int +STATIC int buf_find_string_offset(const buf_t *buf, const char *s, size_t n) { buf_pos_t pos; diff --git a/src/or/buffers.h b/src/or/buffers.h index c947f0ba98..910494a874 100644 --- a/src/or/buffers.h +++ b/src/or/buffers.h @@ -12,6 +12,8 @@ #ifndef TOR_BUFFERS_H #define TOR_BUFFERS_H +#include "testsupport.h" + buf_t *buf_new(void); buf_t *buf_new_with_capacity(size_t size); void buf_free(buf_t *buf); @@ -89,7 +91,7 @@ int generic_buffer_set_to_copy(generic_buffer_t **output, void assert_buf_ok(buf_t *buf); #ifdef BUFFERS_PRIVATE -int buf_find_string_offset(const buf_t *buf, const char *s, size_t n); +STATIC int buf_find_string_offset(const buf_t *buf, const char *s, size_t n); #endif #endif diff --git a/src/or/channel.c b/src/or/channel.c index 602797d0dc..05f269b8fa 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -122,6 +122,8 @@ static cell_queue_entry_t * cell_queue_entry_new_fixed(cell_t *cell); static cell_queue_entry_t * cell_queue_entry_new_var(var_cell_t *var_cell); +static int is_destroy_cell(channel_t *chan, + const cell_queue_entry_t *q, circid_t *circid_out); /* Functions to maintain the digest map */ static void channel_add_to_digest_map(channel_t *chan); @@ -801,6 +803,7 @@ channel_free(channel_t *chan) /* Get rid of cmux */ if (chan->cmux) { circuitmux_detach_all_circuits(chan->cmux); + circuitmux_mark_destroyed_circids_usable(chan->cmux, chan); circuitmux_free(chan->cmux); chan->cmux = NULL; } @@ -1685,6 +1688,13 @@ channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q) chan->timestamp_last_added_nonpadding = approx_time(); } + { + circid_t circ_id; + if (is_destroy_cell(chan, q, &circ_id)) { + channel_note_destroy_not_pending(chan, circ_id); + } + } + /* Can we send it right out? If so, try */ if (TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue) && chan->state == CHANNEL_STATE_OPEN) { @@ -2607,6 +2617,54 @@ channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell) } } +/** If <b>packed_cell</b> on <b>chan</b> is a destroy cell, then set + * *<b>circid_out</b> to its circuit ID, and return true. Otherwise, return + * false. */ +/* XXXX Move this function. */ +int +packed_cell_is_destroy(channel_t *chan, + const packed_cell_t *packed_cell, + circid_t *circid_out) +{ + if (chan->wide_circ_ids) { + if (packed_cell->body[4] == CELL_DESTROY) { + *circid_out = ntohl(get_uint32(packed_cell->body)); + return 1; + } + } else { + if (packed_cell->body[2] == CELL_DESTROY) { + *circid_out = ntohs(get_uint16(packed_cell->body)); + return 1; + } + } + return 0; +} + +/** DOCDOC */ +static int +is_destroy_cell(channel_t *chan, + const cell_queue_entry_t *q, circid_t *circid_out) +{ + *circid_out = 0; + switch (q->type) { + case CELL_QUEUE_FIXED: + if (q->u.fixed.cell->command == CELL_DESTROY) { + *circid_out = q->u.fixed.cell->circ_id; + return 1; + } + break; + case CELL_QUEUE_VAR: + if (q->u.var.var_cell->command == CELL_DESTROY) { + *circid_out = q->u.var.var_cell->circ_id; + return 1; + } + break; + case CELL_QUEUE_PACKED: + return packed_cell_is_destroy(chan, q->u.packed.packed_cell, circid_out); + } + return 0; +} + /** * Send destroy cell on a channel * @@ -2618,25 +2676,20 @@ channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell) int channel_send_destroy(circid_t circ_id, channel_t *chan, int reason) { - cell_t cell; - tor_assert(chan); /* Check to make sure we can send on this channel first */ if (!(chan->state == CHANNEL_STATE_CLOSING || chan->state == CHANNEL_STATE_CLOSED || - chan->state == CHANNEL_STATE_ERROR)) { - memset(&cell, 0, sizeof(cell_t)); - cell.circ_id = circ_id; - cell.command = CELL_DESTROY; - cell.payload[0] = (uint8_t) reason; + chan->state == CHANNEL_STATE_ERROR) && + chan->cmux) { + channel_note_destroy_pending(chan, circ_id); + circuitmux_append_destroy_cell(chan, chan->cmux, circ_id, reason); log_debug(LD_OR, "Sending destroy (circID %u) on channel %p " "(global ID " U64_FORMAT ")", (unsigned)circ_id, chan, U64_PRINTF_ARG(chan->global_identifier)); - - channel_write_cell(chan, &cell); } else { log_warn(LD_BUG, "Someone called channel_send_destroy() for circID %u " diff --git a/src/or/channel.h b/src/or/channel.h index 0933ec8d39..83d7e900ff 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -477,5 +477,9 @@ uint64_t channel_count_xmitted(channel_t *chan); uint64_t channel_listener_count_accepted(channel_listener_t *chan_l); +int packed_cell_is_destroy(channel_t *chan, + const packed_cell_t *packed_cell, + circid_t *circid_out); + #endif diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index daeaa37b1e..85bacce485 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -8,7 +8,7 @@ * \file circuitlist.c * \brief Manage the global circuit list. **/ - +#define CIRCUITLIST_PRIVATE #include "or.h" #include "channel.h" #include "circuitbuild.h" @@ -41,7 +41,6 @@ circuit_t *global_circuitlist=NULL; /** A list of all the circuits in CIRCUIT_STATE_CHAN_WAIT. */ static smartlist_t *circuits_pending_chans = NULL; -static void circuit_free(circuit_t *circ); static void circuit_free_cpath(crypt_path_t *cpath); static void circuit_free_cpath_node(crypt_path_t *victim); static void cpath_ref_decref(crypt_path_reference_t *cpath_ref); @@ -207,18 +206,123 @@ circuit_set_circid_chan_helper(circuit_t *circ, int direction, } } +/** Mark that circuit id <b>id</b> shouldn't be used on channel <b>chan</b>, + * even if there is no circuit on the channel. We use this to keep the + * circuit id from getting re-used while we have queued but not yet sent + * a destroy cell. */ +void +channel_mark_circid_unusable(channel_t *chan, circid_t id) +{ + chan_circid_circuit_map_t search; + chan_circid_circuit_map_t *ent; + + /* See if there's an entry there. That wouldn't be good. */ + memset(&search, 0, sizeof(search)); + search.chan = chan; + search.circ_id = id; + ent = HT_FIND(chan_circid_map, &chan_circid_map, &search); + + if (ent && ent->circuit) { + /* we have a problem. */ + log_warn(LD_BUG, "Tried to mark %u unusable on %p, but there was already " + "a circuit there.", (unsigned)id, chan); + } else if (ent) { + /* It's already marked. */ + } else { + ent = tor_malloc_zero(sizeof(chan_circid_circuit_map_t)); + ent->chan = chan; + ent->circ_id = id; + /* leave circuit at NULL */ + HT_INSERT(chan_circid_map, &chan_circid_map, ent); + } +} + +/** Mark that a circuit id <b>id</b> can be used again on <b>chan</b>. + * We use this to re-enable the circuit ID after we've sent a destroy cell. + */ +void +channel_mark_circid_usable(channel_t *chan, circid_t id) +{ + chan_circid_circuit_map_t search; + chan_circid_circuit_map_t *ent; + + /* See if there's an entry there. That wouldn't be good. */ + memset(&search, 0, sizeof(search)); + search.chan = chan; + search.circ_id = id; + ent = HT_REMOVE(chan_circid_map, &chan_circid_map, &search); + if (ent && ent->circuit) { + log_warn(LD_BUG, "Tried to mark %u usable on %p, but there was already " + "a circuit there.", (unsigned)id, chan); + return; + } + if (_last_circid_chan_ent == ent) + _last_circid_chan_ent = NULL; + tor_free(ent); +} + +/** Called to indicate that a DESTROY is pending on <b>chan</b> with + * circuit ID <b>id</b>, but hasn't been sent yet. */ +void +channel_note_destroy_pending(channel_t *chan, circid_t id) +{ + circuit_t *circ = circuit_get_by_circid_channel_even_if_marked(id,chan); + if (circ) { + if (circ->n_chan == chan && circ->n_circ_id == id) { + circ->n_delete_pending = 1; + } else { + or_circuit_t *orcirc = TO_OR_CIRCUIT(circ); + if (orcirc->p_chan == chan && orcirc->p_circ_id == id) { + circ->p_delete_pending = 1; + } + } + return; + } + channel_mark_circid_unusable(chan, id); +} + +/** Called to indicate that a DESTROY is no longer pending on <b>chan</b> with + * circuit ID <b>id</b> -- typically, because it has been sent. */ +void +channel_note_destroy_not_pending(channel_t *chan, circid_t id) +{ + circuit_t *circ = circuit_get_by_circid_channel_even_if_marked(id,chan); + if (circ) { + if (circ->n_chan == chan && circ->n_circ_id == id) { + circ->n_delete_pending = 0; + } else { + or_circuit_t *orcirc = TO_OR_CIRCUIT(circ); + if (orcirc->p_chan == chan && orcirc->p_circ_id == id) { + circ->p_delete_pending = 0; + } + } + /* XXXX this shouldn't happen; log a bug here. */ + return; + } + channel_mark_circid_usable(chan, id); +} + /** Set the p_conn field of a circuit <b>circ</b>, along * with the corresponding circuit ID, and add the circuit as appropriate * to the (chan,id)-\>circuit map. */ void -circuit_set_p_circid_chan(or_circuit_t *circ, circid_t id, +circuit_set_p_circid_chan(or_circuit_t *or_circ, circid_t id, channel_t *chan) { - circuit_set_circid_chan_helper(TO_CIRCUIT(circ), CELL_DIRECTION_IN, - id, chan); + circuit_t *circ = TO_CIRCUIT(or_circ); + channel_t *old_chan = or_circ->p_chan; + circid_t old_id = or_circ->p_circ_id; + + circuit_set_circid_chan_helper(circ, CELL_DIRECTION_IN, id, chan); if (chan) - tor_assert(bool_eq(circ->p_chan_cells.n, circ->next_active_on_p_chan)); + tor_assert(bool_eq(or_circ->p_chan_cells.n, + or_circ->next_active_on_p_chan)); + + if (circ->p_delete_pending && old_chan) { + channel_mark_circid_unusable(old_chan, old_id); + circ->p_delete_pending = 0; + } } /** Set the n_conn field of a circuit <b>circ</b>, along @@ -228,10 +332,18 @@ void circuit_set_n_circid_chan(circuit_t *circ, circid_t id, channel_t *chan) { + channel_t *old_chan = circ->n_chan; + circid_t old_id = circ->n_circ_id; + circuit_set_circid_chan_helper(circ, CELL_DIRECTION_OUT, id, chan); if (chan) tor_assert(bool_eq(circ->n_chan_cells.n, circ->next_active_on_n_chan)); + + if (circ->n_delete_pending && old_chan) { + channel_mark_circid_unusable(old_chan, old_id); + circ->n_delete_pending = 0; + } } /** Change the state of <b>circ</b> to <b>state</b>, adding it to or removing @@ -570,6 +682,7 @@ init_circuit_base(circuit_t *circ) circ->package_window = circuit_initial_package_window(); circ->deliver_window = CIRCWINDOW_START; + cell_queue_init(&circ->n_chan_cells); circuit_add(circ); } @@ -615,6 +728,7 @@ or_circuit_new(circid_t p_circ_id, channel_t *p_chan) circuit_set_p_circid_chan(circ, p_circ_id, p_chan); circ->remaining_relay_early_cells = MAX_RELAY_EARLY_CELLS_PER_CIRCUIT; + cell_queue_init(&circ->p_chan_cells); init_circuit_base(TO_CIRCUIT(circ)); @@ -623,7 +737,7 @@ or_circuit_new(circid_t p_circ_id, channel_t *p_chan) /** Deallocate space associated with circ. */ -static void +STATIC void circuit_free(circuit_t *circ) { void *mem; @@ -928,9 +1042,13 @@ circuit_get_by_global_id(uint32_t id) * - circ-\>n_circ_id or circ-\>p_circ_id is equal to <b>circ_id</b>, and * - circ is attached to <b>chan</b>, either as p_chan or n_chan. * Return NULL if no such circuit exists. + * + * If <b>found_entry_out</b> is provided, set it to true if we have a + * placeholder entry for circid/chan, and leave it unset otherwise. */ static INLINE circuit_t * -circuit_get_by_circid_channel_impl(circid_t circ_id, channel_t *chan) +circuit_get_by_circid_channel_impl(circid_t circ_id, channel_t *chan, + int *found_entry_out) { chan_circid_circuit_map_t search; chan_circid_circuit_map_t *found; @@ -951,15 +1069,21 @@ circuit_get_by_circid_channel_impl(circid_t circ_id, channel_t *chan) " circ_id %u, channel ID " U64_FORMAT " (%p)", found->circuit, (unsigned)circ_id, U64_PRINTF_ARG(chan->global_identifier), chan); + if (found_entry_out) + *found_entry_out = 1; return found->circuit; } log_debug(LD_CIRC, - "circuit_get_by_circid_channel_impl() found nothing for" + "circuit_get_by_circid_channel_impl() found %s for" " circ_id %u, channel ID " U64_FORMAT " (%p)", + found ? "placeholder" : "nothing", (unsigned)circ_id, U64_PRINTF_ARG(chan->global_identifier), chan); + if (found_entry_out) + *found_entry_out = found ? 1 : 0; + return NULL; /* The rest of this checks for bugs. Disabled by default. */ /* We comment it out because coverity complains otherwise. @@ -993,7 +1117,7 @@ circuit_get_by_circid_channel_impl(circid_t circ_id, channel_t *chan) circuit_t * circuit_get_by_circid_channel(circid_t circ_id, channel_t *chan) { - circuit_t *circ = circuit_get_by_circid_channel_impl(circ_id, chan); + circuit_t *circ = circuit_get_by_circid_channel_impl(circ_id, chan, NULL); if (!circ || circ->marked_for_close) return NULL; else @@ -1009,7 +1133,7 @@ circuit_t * circuit_get_by_circid_channel_even_if_marked(circid_t circ_id, channel_t *chan) { - return circuit_get_by_circid_channel_impl(circ_id, chan); + return circuit_get_by_circid_channel_impl(circ_id, chan, NULL); } /** Return true iff the circuit ID <b>circ_id</b> is currently used by a @@ -1017,7 +1141,9 @@ circuit_get_by_circid_channel_even_if_marked(circid_t circ_id, int circuit_id_in_use_on_channel(circid_t circ_id, channel_t *chan) { - return circuit_get_by_circid_channel_impl(circ_id, chan) != NULL; + int found = 0; + return circuit_get_by_circid_channel_impl(circ_id, chan, &found) != NULL + || found; } /** Return the circuit that a given edge connection is using. */ @@ -1514,7 +1640,7 @@ marked_circuit_free_cells(circuit_t *circ) } /** Return the number of cells used by the circuit <b>c</b>'s cell queues. */ -static size_t +STATIC size_t n_cells_in_circ_queues(const circuit_t *c) { size_t n = c->n_chan_cells.n; @@ -1689,15 +1815,16 @@ assert_circuit_ok(const circuit_t *c) /* We use the _impl variant here to make sure we don't fail on marked * circuits, which would not be returned by the regular function. */ circuit_t *c2 = circuit_get_by_circid_channel_impl(c->n_circ_id, - c->n_chan); + c->n_chan, NULL); tor_assert(c == c2); } } if (or_circ && or_circ->p_chan) { if (or_circ->p_circ_id) { /* ibid */ - circuit_t *c2 = circuit_get_by_circid_channel_impl(or_circ->p_circ_id, - or_circ->p_chan); + circuit_t *c2 = + circuit_get_by_circid_channel_impl(or_circ->p_circ_id, + or_circ->p_chan, NULL); tor_assert(c == c2); } } diff --git a/src/or/circuitlist.h b/src/or/circuitlist.h index 874f68cd22..4e56f5264f 100644 --- a/src/or/circuitlist.h +++ b/src/or/circuitlist.h @@ -12,6 +12,8 @@ #ifndef TOR_CIRCUITLIST_H #define TOR_CIRCUITLIST_H +#include "testsupport.h" + circuit_t * circuit_get_global_list_(void); const char *circuit_state_to_string(int state); const char *circuit_purpose_to_controller_string(uint8_t purpose); @@ -23,6 +25,8 @@ void circuit_set_p_circid_chan(or_circuit_t *circ, circid_t id, channel_t *chan); void circuit_set_n_circid_chan(circuit_t *circ, circid_t id, channel_t *chan); +void channel_mark_circid_unusable(channel_t *chan, circid_t id); +void channel_mark_circid_usable(channel_t *chan, circid_t id); void circuit_set_state(circuit_t *circ, uint8_t state); void circuit_close_all_marked(void); int32_t circuit_initial_package_window(void); @@ -63,5 +67,13 @@ void assert_circuit_ok(const circuit_t *c); void circuit_free_all(void); void circuits_handle_oom(size_t current_allocation); +void channel_note_destroy_pending(channel_t *chan, circid_t id); +void channel_note_destroy_not_pending(channel_t *chan, circid_t id); + +#ifdef CIRCUITLIST_PRIVATE +STATIC void circuit_free(circuit_t *circ); +STATIC size_t n_cells_in_circ_queues(const circuit_t *c); +#endif + #endif diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index 545cfd0650..47b423066a 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -10,6 +10,7 @@ #include "channel.h" #include "circuitlist.h" #include "circuitmux.h" +#include "relay.h" /* * Private typedefs for circuitmux.c @@ -115,6 +116,22 @@ struct circuitmux_s { */ struct circuit_t *active_circuits_head, *active_circuits_tail; + /** List of queued destroy cells */ + cell_queue_t destroy_cell_queue; + /** Boolean: True iff the last cell to circuitmux_get_first_active_circuit + * returned the destroy queue. Used to force alternation between + * destroy/non-destroy cells. + * + * XXXX There is no reason to think that alternating is a particularly good + * approach -- it's just designed to prevent destroys from starving other + * cells completely. + */ + unsigned int last_cell_was_destroy : 1; + /** Destroy counter: increment this when a destroy gets queued, decrement + * when we unqueue it, so we can test to make sure they don't starve. + */ + int64_t destroy_ctr; + /* * Circuitmux policy; if this is non-NULL, it can override the built- * in round-robin active circuits behavior. This is how EWMA works in @@ -193,6 +210,11 @@ static void circuitmux_assert_okay_pass_one(circuitmux_t *cmux); static void circuitmux_assert_okay_pass_two(circuitmux_t *cmux); static void circuitmux_assert_okay_pass_three(circuitmux_t *cmux); +/* Static global variables */ + +/** Count the destroy balance to debug destroy queue logic */ +static int64_t global_destroy_ctr = 0; + /* Function definitions */ /** @@ -361,6 +383,7 @@ circuitmux_alloc(void) rv = tor_malloc_zero(sizeof(*rv)); rv->chanid_circid_map = tor_malloc_zero(sizeof(*( rv->chanid_circid_map))); HT_INIT(chanid_circid_muxinfo_map, rv->chanid_circid_map); + cell_queue_init(&rv->destroy_cell_queue); return rv; } @@ -476,6 +499,31 @@ circuitmux_detach_all_circuits(circuitmux_t *cmux) cmux->n_cells = 0; } +/** Reclaim all circuit IDs currently marked as unusable on <b>chan</b> because + * of pending destroy cells in <b>cmux</b>. + * + * This function must be called AFTER circuits are unlinked from the (channel, + * circuid-id) map with circuit_unlink_all_from_channel(), but before calling + * circuitmux_free(). + */ +void +circuitmux_mark_destroyed_circids_usable(circuitmux_t *cmux, channel_t *chan) +{ + packed_cell_t *cell; + int n_bad = 0; + TOR_SIMPLEQ_FOREACH(cell, &cmux->destroy_cell_queue.head, next) { + circid_t circid = 0; + if (packed_cell_is_destroy(chan, cell, &circid)) { + channel_mark_circid_usable(chan, circid); + } else { + ++n_bad; + } + } + if (n_bad) + log_warn(LD_BUG, "%d cell(s) on destroy queue did not look like a " + "DESTROY cell.", n_bad); +} + /** * Free a circuitmux_t; the circuits must be detached first with * circuitmux_detach_all_circuits(). @@ -508,6 +556,30 @@ circuitmux_free(circuitmux_t *cmux) tor_free(cmux->chanid_circid_map); } + /* + * We're throwing away some destroys; log the counter and + * adjust the global counter by the queue size. + */ + if (cmux->destroy_cell_queue.n > 0) { + cmux->destroy_ctr -= cmux->destroy_cell_queue.n; + global_destroy_ctr -= cmux->destroy_cell_queue.n; + log_debug(LD_CIRC, + "Freeing cmux at %p with %u queued destroys; the last cmux " + "destroy balance was "I64_FORMAT", global is "I64_FORMAT, + cmux, cmux->destroy_cell_queue.n, + I64_PRINTF_ARG(cmux->destroy_ctr), + I64_PRINTF_ARG(global_destroy_ctr)); + } else { + log_debug(LD_CIRC, + "Freeing cmux at %p with no queued destroys, the cmux destroy " + "balance was "I64_FORMAT", global is "I64_FORMAT, + cmux, + I64_PRINTF_ARG(cmux->destroy_ctr), + I64_PRINTF_ARG(global_destroy_ctr)); + } + + cell_queue_clear(&cmux->destroy_cell_queue); + tor_free(cmux); } @@ -816,7 +888,7 @@ circuitmux_num_cells(circuitmux_t *cmux) { tor_assert(cmux); - return cmux->n_cells; + return cmux->n_cells + cmux->destroy_cell_queue.n; } /** @@ -851,9 +923,9 @@ circuitmux_num_circuits(circuitmux_t *cmux) * Attach a circuit to a circuitmux, for the specified direction. */ -void -circuitmux_attach_circuit(circuitmux_t *cmux, circuit_t *circ, - cell_direction_t direction) +MOCK_IMPL(void, +circuitmux_attach_circuit,(circuitmux_t *cmux, circuit_t *circ, + cell_direction_t direction)) { channel_t *chan = NULL; uint64_t channel_id; @@ -1000,8 +1072,8 @@ circuitmux_attach_circuit(circuitmux_t *cmux, circuit_t *circ, * no-op if not attached. */ -void -circuitmux_detach_circuit(circuitmux_t *cmux, circuit_t *circ) +MOCK_IMPL(void, +circuitmux_detach_circuit,(circuitmux_t *cmux, circuit_t *circ)) { chanid_circid_muxinfo_t search, *hashent = NULL; /* @@ -1368,16 +1440,36 @@ circuitmux_set_num_cells(circuitmux_t *cmux, circuit_t *circ, /** * Pick a circuit to send from, using the active circuits list or a * circuitmux policy if one is available. This is called from channel.c. + * + * If we would rather send a destroy cell, return NULL and set + * *<b>destroy_queue_out</b> to the destroy queue. + * + * If we have nothing to send, set *<b>destroy_queue_out</b> to NULL and + * return NULL. */ circuit_t * -circuitmux_get_first_active_circuit(circuitmux_t *cmux) +circuitmux_get_first_active_circuit(circuitmux_t *cmux, + cell_queue_t **destroy_queue_out) { circuit_t *circ = NULL; tor_assert(cmux); + tor_assert(destroy_queue_out); + + *destroy_queue_out = NULL; + + if (cmux->destroy_cell_queue.n && + (!cmux->last_cell_was_destroy || cmux->n_active_circuits == 0)) { + /* We have destroy cells to send, and either we just sent a relay cell, + * or we have no relay cells to send. */ + + /* XXXX We should let the cmux policy have some say in this eventually. */ + /* XXXX Alternating is not a terribly brilliant approach here. */ + *destroy_queue_out = &cmux->destroy_cell_queue; - if (cmux->n_active_circuits > 0) { + cmux->last_cell_was_destroy = 1; + } else if (cmux->n_active_circuits > 0) { /* We also must have a cell available for this to be the case */ tor_assert(cmux->n_cells > 0); /* Do we have a policy-provided circuit selector? */ @@ -1389,7 +1481,11 @@ circuitmux_get_first_active_circuit(circuitmux_t *cmux) tor_assert(cmux->active_circuits_head); circ = cmux->active_circuits_head; } - } else tor_assert(cmux->n_cells == 0); + cmux->last_cell_was_destroy = 0; + } else { + tor_assert(cmux->n_cells == 0); + tor_assert(cmux->destroy_cell_queue.n == 0); + } return circ; } @@ -1463,6 +1559,26 @@ circuitmux_notify_xmit_cells(circuitmux_t *cmux, circuit_t *circ, circuitmux_assert_okay_paranoid(cmux); } +/** + * Notify the circuitmux that a destroy was sent, so we can update + * the counter. + */ + +void +circuitmux_notify_xmit_destroy(circuitmux_t *cmux) +{ + tor_assert(cmux); + + --(cmux->destroy_ctr); + --(global_destroy_ctr); + log_debug(LD_CIRC, + "Cmux at %p sent a destroy, cmux counter is now "I64_FORMAT", " + "global counter is now "I64_FORMAT, + cmux, + I64_PRINTF_ARG(cmux->destroy_ctr), + I64_PRINTF_ARG(global_destroy_ctr)); +} + /* * Circuitmux consistency checking assertions */ @@ -1743,3 +1859,40 @@ circuitmux_assert_okay_pass_three(circuitmux_t *cmux) } } +/*DOCDOC */ +void +circuitmux_append_destroy_cell(channel_t *chan, + circuitmux_t *cmux, + circid_t circ_id, + uint8_t reason) +{ + cell_t cell; + memset(&cell, 0, sizeof(cell_t)); + cell.circ_id = circ_id; + cell.command = CELL_DESTROY; + cell.payload[0] = (uint8_t) reason; + + cell_queue_append_packed_copy(&cmux->destroy_cell_queue, &cell, + chan->wide_circ_ids, 0); + + /* Destroy entering the queue, update counters */ + ++(cmux->destroy_ctr); + ++global_destroy_ctr; + log_debug(LD_CIRC, + "Cmux at %p queued a destroy for circ %u, cmux counter is now " + I64_FORMAT", global counter is now "I64_FORMAT, + cmux, circ_id, + I64_PRINTF_ARG(cmux->destroy_ctr), + I64_PRINTF_ARG(global_destroy_ctr)); + + /* XXXX Duplicate code from append_cell_to_circuit_queue */ + if (!channel_has_queued_writes(chan)) { + /* There is no data at all waiting to be sent on the outbuf. Add a + * cell, so that we can notice when it gets flushed, flushed_some can + * get called, and we can start putting more data onto the buffer then. + */ + log_debug(LD_GENERAL, "Primed a buffer."); + channel_flush_from_first_active_circuit(chan, 1); + } +} + diff --git a/src/or/circuitmux.h b/src/or/circuitmux.h index 25644ffab7..ee2f5d1535 100644 --- a/src/or/circuitmux.h +++ b/src/or/circuitmux.h @@ -10,6 +10,7 @@ #define TOR_CIRCUITMUX_H #include "or.h" +#include "testsupport.h" typedef struct circuitmux_policy_s circuitmux_policy_t; typedef struct circuitmux_policy_data_s circuitmux_policy_data_t; @@ -120,17 +121,27 @@ unsigned int circuitmux_num_circuits(circuitmux_t *cmux); unsigned int circuitmux_num_active_circuits(circuitmux_t *cmux); /* Channel interface */ -circuit_t * circuitmux_get_first_active_circuit(circuitmux_t *cmux); +circuit_t * circuitmux_get_first_active_circuit(circuitmux_t *cmux, + cell_queue_t **destroy_queue_out); void circuitmux_notify_xmit_cells(circuitmux_t *cmux, circuit_t *circ, unsigned int n_cells); +void circuitmux_notify_xmit_destroy(circuitmux_t *cmux); /* Circuit interface */ -void circuitmux_attach_circuit(circuitmux_t *cmux, circuit_t *circ, - cell_direction_t direction); -void circuitmux_detach_circuit(circuitmux_t *cmux, circuit_t *circ); +MOCK_DECL(void, circuitmux_attach_circuit, (circuitmux_t *cmux, + circuit_t *circ, + cell_direction_t direction)); +MOCK_DECL(void, circuitmux_detach_circuit, + (circuitmux_t *cmux, circuit_t *circ)); void circuitmux_clear_num_cells(circuitmux_t *cmux, circuit_t *circ); void circuitmux_set_num_cells(circuitmux_t *cmux, circuit_t *circ, unsigned int n_cells); +void circuitmux_append_destroy_cell(channel_t *chan, + circuitmux_t *cmux, circid_t circ_id, + uint8_t reason); +void circuitmux_mark_destroyed_circids_usable(circuitmux_t *cmux, + channel_t *chan); + #endif /* TOR_CIRCUITMUX_H */ diff --git a/src/or/circuitstats.c b/src/or/circuitstats.c index 1d7812bf2b..95129f9a43 100644 --- a/src/or/circuitstats.c +++ b/src/or/circuitstats.c @@ -29,9 +29,13 @@ /* XXXX024 Make this static; add accessor functions. */ circuit_build_times_t circ_times; +#ifdef TOR_UNIT_TESTS /** If set, we're running the unit tests: we should avoid clobbering * our state file or accessing get_options() or get_or_state() */ static int unit_tests = 0; +#else +#define unit_tests 0 +#endif /** * This function decides if CBT learning should be disabled. It returns @@ -438,7 +442,7 @@ circuit_build_times_get_initial_timeout(void) * Leave estimated parameters, timeout and network liveness intact * for future use. */ -void +STATIC void circuit_build_times_reset(circuit_build_times_t *cbt) { memset(cbt->circuit_build_times, 0, sizeof(cbt->circuit_build_times)); @@ -949,7 +953,7 @@ circuit_build_times_parse_state(circuit_build_times_t *cbt, * an acceptable approximation because we are only concerned with the * accuracy of the CDF of the tail. */ -int +STATIC int circuit_build_times_update_alpha(circuit_build_times_t *cbt) { build_time_t *x=cbt->circuit_build_times; @@ -1033,7 +1037,7 @@ circuit_build_times_update_alpha(circuit_build_times_t *cbt) * * Return value is in milliseconds. */ -double +STATIC double circuit_build_times_calculate_timeout(circuit_build_times_t *cbt, double quantile) { @@ -1050,6 +1054,7 @@ circuit_build_times_calculate_timeout(circuit_build_times_t *cbt, return ret; } +#ifdef TOR_UNIT_TESTS /** Pareto CDF */ double circuit_build_times_cdf(circuit_build_times_t *cbt, double x) @@ -1060,7 +1065,9 @@ circuit_build_times_cdf(circuit_build_times_t *cbt, double x) tor_assert(0 <= ret && ret <= 1.0); return ret; } +#endif +#ifdef TOR_UNIT_TESTS /** * Generate a synthetic time using our distribution parameters. * @@ -1093,7 +1100,9 @@ circuit_build_times_generate_sample(circuit_build_times_t *cbt, tor_assert(ret > 0); return ret; } +#endif +#ifdef TOR_UNIT_TESTS /** * Estimate an initial alpha parameter by solving the quantile * function with a quantile point and a specific timeout value. @@ -1114,6 +1123,7 @@ circuit_build_times_initial_alpha(circuit_build_times_t *cbt, (tor_mathlog(cbt->Xm)-tor_mathlog(timeout_ms)); tor_assert(cbt->alpha > 0); } +#endif /** * Returns true if we need circuits to be built @@ -1282,7 +1292,7 @@ circuit_build_times_network_check_live(circuit_build_times_t *cbt) * to restart the process of building test circuits and estimating a * new timeout. */ -int +STATIC int circuit_build_times_network_check_changed(circuit_build_times_t *cbt) { int total_build_times = cbt->total_build_times; @@ -1546,6 +1556,8 @@ circuit_build_times_set_timeout(circuit_build_times_t *cbt) cbt->total_build_times); } } + +#ifdef TOR_UNIT_TESTS /** Make a note that we're running unit tests (rather than running Tor * itself), so we avoid clobbering our state file. */ void @@ -1553,4 +1565,5 @@ circuitbuild_running_unit_tests(void) { unit_tests = 1; } +#endif diff --git a/src/or/circuitstats.h b/src/or/circuitstats.h index 87dce99f4f..53ba2b2d2e 100644 --- a/src/or/circuitstats.h +++ b/src/or/circuitstats.h @@ -38,19 +38,23 @@ double circuit_build_times_timeout_rate(const circuit_build_times_t *cbt); double circuit_build_times_close_rate(const circuit_build_times_t *cbt); #ifdef CIRCUITSTATS_PRIVATE -double circuit_build_times_calculate_timeout(circuit_build_times_t *cbt, +STATIC double circuit_build_times_calculate_timeout(circuit_build_times_t *cbt, double quantile); +STATIC int circuit_build_times_update_alpha(circuit_build_times_t *cbt); +STATIC void circuit_build_times_reset(circuit_build_times_t *cbt); + +/* Network liveness functions */ +STATIC int circuit_build_times_network_check_changed( + circuit_build_times_t *cbt); +#endif + +#ifdef TOR_UNIT_TESTS build_time_t circuit_build_times_generate_sample(circuit_build_times_t *cbt, double q_lo, double q_hi); +double circuit_build_times_cdf(circuit_build_times_t *cbt, double x); void circuit_build_times_initial_alpha(circuit_build_times_t *cbt, double quantile, double time_ms); -int circuit_build_times_update_alpha(circuit_build_times_t *cbt); -double circuit_build_times_cdf(circuit_build_times_t *cbt, double x); void circuitbuild_running_unit_tests(void); -void circuit_build_times_reset(circuit_build_times_t *cbt); - -/* Network liveness functions */ -int circuit_build_times_network_check_changed(circuit_build_times_t *cbt); #endif /* Network liveness functions */ @@ -58,8 +62,5 @@ void circuit_build_times_network_is_live(circuit_build_times_t *cbt); int circuit_build_times_network_check_live(circuit_build_times_t *cbt); void circuit_build_times_network_circ_success(circuit_build_times_t *cbt); -/* DOCDOC circuit_build_times_get_bw_scale */ -int circuit_build_times_get_bw_scale(networkstatus_t *ns); - #endif diff --git a/src/or/config.c b/src/or/config.c index 72ceea395e..5e2de0792c 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -10,7 +10,6 @@ **/ #define CONFIG_PRIVATE - #include "or.h" #include "addressmap.h" #include "channel.h" @@ -40,6 +39,7 @@ #include "rendservice.h" #include "rephist.h" #include "router.h" +#include "sandbox.h" #include "util.h" #include "routerlist.h" #include "routerset.h" @@ -281,6 +281,7 @@ static config_var_t option_vars_[] = { V(IPv6Exit, BOOL, "0"), VAR("ServerTransportPlugin", LINELIST, ServerTransportPlugin, NULL), V(ServerTransportListenAddr, LINELIST, NULL), + V(ServerTransportOptions, LINELIST, NULL), V(Socks4Proxy, STRING, NULL), V(Socks5Proxy, STRING, NULL), V(Socks5ProxyUsername, STRING, NULL), @@ -370,6 +371,7 @@ static config_var_t option_vars_[] = { V(RunAsDaemon, BOOL, "0"), // V(RunTesting, BOOL, "0"), OBSOLETE("RunTesting"), // currently unused + V(Sandbox, BOOL, "0"), V(SafeLogging, STRING, "1"), V(SafeSocks, BOOL, "0"), V(ServerDNSAllowBrokenConfig, BOOL, "1"), @@ -405,7 +407,7 @@ static config_var_t option_vars_[] = { V(UseEntryGuards, BOOL, "1"), V(UseEntryGuardsAsDirGuards, BOOL, "1"), V(UseMicrodescriptors, AUTOBOOL, "auto"), - V(UseNTorHandshake, AUTOBOOL, "auto"), + V(UseNTorHandshake, AUTOBOOL, "1"), V(User, STRING, NULL), V(UserspaceIOCPBuffers, BOOL, "0"), VAR("V1AuthoritativeDirectory",BOOL, V1AuthoritativeDir, "0"), @@ -414,6 +416,7 @@ static config_var_t option_vars_[] = { V(TestingV3AuthInitialVotingInterval, INTERVAL, "30 minutes"), V(TestingV3AuthInitialVoteDelay, INTERVAL, "5 minutes"), V(TestingV3AuthInitialDistDelay, INTERVAL, "5 minutes"), + V(TestingV3AuthVotingStartOffset, INTERVAL, "0"), V(V3AuthVotingInterval, INTERVAL, "1 hour"), V(V3AuthVoteDelay, INTERVAL, "5 minutes"), V(V3AuthDistDelay, INTERVAL, "5 minutes"), @@ -434,6 +437,23 @@ static config_var_t option_vars_[] = { VAR("__OwningControllerProcess",STRING,OwningControllerProcess, NULL), V(MinUptimeHidServDirectoryV2, INTERVAL, "25 hours"), V(VoteOnHidServDirectoriesV2, BOOL, "1"), + V(TestingServerDownloadSchedule, CSV_INTERVAL, "0, 0, 0, 60, 60, 120, " + "300, 900, 2147483647"), + V(TestingClientDownloadSchedule, CSV_INTERVAL, "0, 0, 60, 300, 600, " + "2147483647"), + V(TestingServerConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 60, " + "300, 600, 1800, 1800, 1800, 1800, " + "1800, 3600, 7200"), + V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 60, " + "300, 600, 1800, 3600, 3600, 3600, " + "10800, 21600, 43200"), + V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "3600, 900, 900, 3600"), + V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "10 minutes"), + V(TestingDirConnectionMaxStall, INTERVAL, "5 minutes"), + V(TestingConsensusMaxDownloadTries, UINT, "8"), + V(TestingDescriptorMaxDownloadTries, UINT, "8"), + V(TestingMicrodescMaxDownloadTries, UINT, "8"), + V(TestingCertMaxDownloadTries, UINT, "8"), VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "0"), { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } @@ -459,9 +479,25 @@ static const config_var_t testing_tor_network_defaults[] = { V(TestingV3AuthInitialVotingInterval, INTERVAL, "5 minutes"), V(TestingV3AuthInitialVoteDelay, INTERVAL, "20 seconds"), V(TestingV3AuthInitialDistDelay, INTERVAL, "20 seconds"), + V(TestingV3AuthVotingStartOffset, INTERVAL, "0"), V(TestingAuthDirTimeToLearnReachability, INTERVAL, "0 minutes"), V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "0 minutes"), V(MinUptimeHidServDirectoryV2, INTERVAL, "0 minutes"), + V(TestingServerDownloadSchedule, CSV_INTERVAL, "0, 0, 0, 5, 10, 15, " + "20, 30, 60"), + V(TestingClientDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, 15, 20, " + "30, 60"), + V(TestingServerConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, " + "15, 20, 30, 60"), + V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, " + "15, 20, 30, 60"), + V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "60, 30, 30, 60"), + V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "5 seconds"), + V(TestingDirConnectionMaxStall, INTERVAL, "30 seconds"), + V(TestingConsensusMaxDownloadTries, UINT, "80"), + V(TestingDescriptorMaxDownloadTries, UINT, "80"), + V(TestingMicrodescMaxDownloadTries, UINT, "80"), + V(TestingCertMaxDownloadTries, UINT, "80"), VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "1"), { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } @@ -474,9 +510,6 @@ static const config_var_t testing_tor_network_defaults[] = { #ifdef _WIN32 static char *get_windows_conf_root(void); #endif -static int options_validate(or_options_t *old_options, - or_options_t *options, - int from_setconf, char **msg); static int options_act_reversible(const or_options_t *old_options, char **msg); static int options_act(const or_options_t *old_options); static int options_transition_allowed(const or_options_t *old, @@ -488,7 +521,6 @@ static int options_transition_affects_descriptor( const or_options_t *old_options, const or_options_t *new_options); static int check_nickname_list(const char *lst, const char *name, char **msg); -static int parse_bridge_line(const char *line, int validate_only); static int parse_client_transport_line(const char *line, int validate_only); static int parse_server_transport_line(const char *line, int validate_only); @@ -521,7 +553,7 @@ static void config_maybe_load_geoip_files_(const or_options_t *options, #define OR_OPTIONS_MAGIC 9090909 /** Configuration format for or_options_t. */ -static config_format_t options_format = { +STATIC config_format_t options_format = { sizeof(or_options_t), OR_OPTIONS_MAGIC, STRUCT_OFFSET(or_options_t, magic_), @@ -677,7 +709,7 @@ get_short_version(void) /** Release additional memory allocated in options */ -static void +STATIC void or_options_free(or_options_t *options) { if (!options) @@ -971,6 +1003,7 @@ options_act_reversible(const or_options_t *old_options, char **msg) int set_conn_limit = 0; int r = -1; int logs_marked = 0; + int old_min_log_level = get_min_log_level(); /* Daemonize _first_, since we only want to open most of this stuff in * the subprocess. Libevent bases can't be reliably inherited across @@ -1109,6 +1142,8 @@ options_act_reversible(const or_options_t *old_options, char **msg) goto rollback; } + sandbox_set_debugging_fd(get_err_logging_fd()); + commit: r = 0; if (logs_marked) { @@ -1119,6 +1154,13 @@ options_act_reversible(const or_options_t *old_options, char **msg) control_adjust_event_log_severity(); tor_free(severity); } + if (get_min_log_level() >= LOG_INFO && + get_min_log_level() != old_min_log_level) { + log_warn(LD_GENERAL, "Your log may contain sensitive information - you're " + "logging above \"notice\". Please log safely. Don't log unless " + "it serves an important reason. Overwrite the log afterwards."); + } + SMARTLIST_FOREACH(replaced_listeners, connection_t *, conn, { log_notice(LD_NET, "Closing old %s on %s:%d", @@ -1301,14 +1343,23 @@ options_act(const or_options_t *old_options) } #endif + if (options->SafeLogging_ != SAFELOG_SCRUB_ALL && + (!old_options || old_options->SafeLogging_ != options->SafeLogging_)) { + log_warn(LD_GENERAL, "Your log may contain sensitive information - you " + "disabled SafeLogging. Please log safely. Don't log unless it " + "serves an important reason. Overwrite the log afterwards."); + } + if (options->Bridges) { mark_bridge_list(); for (cl = options->Bridges; cl; cl = cl->next) { - if (parse_bridge_line(cl->value, 0)<0) { + bridge_line_t *bridge_line = parse_bridge_line(cl->value); + if (!bridge_line) { log_warn(LD_BUG, "Previously validated Bridge line could not be added!"); return -1; } + bridge_add_from_config(bridge_line); } sweep_bridge_list(); } @@ -1851,7 +1902,8 @@ options_trial_assign(config_line_t *list, int use_defaults, return r; } - if (options_validate(get_options_mutable(), trial_options, 1, msg) < 0) { + if (options_validate(get_options_mutable(), trial_options, + global_default_options, 1, msg) < 0) { config_free(&options_format, trial_options); return SETOPT_ERR_PARSE; /*XXX make this a separate return value. */ } @@ -2281,10 +2333,11 @@ compute_publishserverdescriptor(or_options_t *options) * */ #define RECOMMENDED_MIN_CIRCUIT_BUILD_TIMEOUT (10) -/** Return 0 if every setting in <b>options</b> is reasonable, and a - * permissible transition from <b>old_options</b>. Else return -1. - * Should have no side effects, except for normalizing the contents of - * <b>options</b>. +/** Return 0 if every setting in <b>options</b> is reasonable, is a + * permissible transition from <b>old_options</b>, and none of the + * testing-only settings differ from <b>default_options</b> unless in + * testing mode. Else return -1. Should have no side effects, except for + * normalizing the contents of <b>options</b>. * * On error, tor_strdup an error explanation into *<b>msg</b>. * @@ -2293,9 +2346,9 @@ compute_publishserverdescriptor(or_options_t *options) * Log line should stay empty. If it's 0, then give us a default log * if there are no logs defined. */ -static int +STATIC int options_validate(or_options_t *old_options, or_options_t *options, - int from_setconf, char **msg) + or_options_t *default_options, int from_setconf, char **msg) { int i; config_line_t *cl; @@ -2962,14 +3015,14 @@ options_validate(or_options_t *old_options, or_options_t *options, size_t len; len = strlen(options->Socks5ProxyUsername); - if (len < 1 || len > 255) + if (len < 1 || len > MAX_SOCKS5_AUTH_FIELD_SIZE) REJECT("Socks5ProxyUsername must be between 1 and 255 characters."); if (!options->Socks5ProxyPassword) REJECT("Socks5ProxyPassword must be included with Socks5ProxyUsername."); len = strlen(options->Socks5ProxyPassword); - if (len < 1 || len > 255) + if (len < 1 || len > MAX_SOCKS5_AUTH_FIELD_SIZE) REJECT("Socks5ProxyPassword must be between 1 and 255 characters."); } else if (options->Socks5ProxyPassword) REJECT("Socks5ProxyPassword must be included with Socks5ProxyUsername."); @@ -3053,8 +3106,10 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("If you set UseBridges, you must set TunnelDirConns."); for (cl = options->Bridges; cl; cl = cl->next) { - if (parse_bridge_line(cl->value, 1)<0) - REJECT("Bridge line did not parse. See logs for details."); + bridge_line_t *bridge_line = parse_bridge_line(cl->value); + if (!bridge_line) + REJECT("Bridge line did not parse. See logs for details."); + bridge_line_free(bridge_line); } for (cl = options->ClientTransportPlugin; cl; cl = cl->next) { @@ -3090,6 +3145,19 @@ options_validate(or_options_t *old_options, or_options_t *options, "ServerTransportListenAddr line will be ignored."); } + for (cl = options->ServerTransportOptions; cl; cl = cl->next) { + /** If get_options_from_transport_options_line() fails with + 'transport' being NULL, it means that something went wrong + while parsing the ServerTransportOptions line. */ + smartlist_t *options_sl = + get_options_from_transport_options_line(cl->value, NULL); + if (!options_sl) + REJECT("ServerTransportOptions did not parse. See logs for details."); + + SMARTLIST_FOREACH(options_sl, char *, cp, tor_free(cp)); + smartlist_free(options_sl); + } + if (options->ConstrainedSockets) { /* If the user wants to constrain socket buffer use, make sure the desired * limit is between MIN|MAX_TCPSOCK_BUFFER in k increments. */ @@ -3182,35 +3250,46 @@ options_validate(or_options_t *old_options, or_options_t *options, "ignore you."); } - /*XXXX checking for defaults manually like this is a bit fragile.*/ - - /* Keep changes to hard-coded values synchronous to man page and default - * values table. */ - if (options->TestingV3AuthInitialVotingInterval != 30*60 && - !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) { - REJECT("TestingV3AuthInitialVotingInterval may only be changed in testing " - "Tor networks!"); - } else if (options->TestingV3AuthInitialVotingInterval < MIN_VOTE_INTERVAL) { +#define CHECK_DEFAULT(arg) \ + STMT_BEGIN \ + if (!options->TestingTorNetwork && \ + !options->UsingTestNetworkDefaults_ && \ + !config_is_same(&options_format,options, \ + default_options,#arg)) { \ + REJECT(#arg " may only be changed in testing Tor " \ + "networks!"); \ + } STMT_END + CHECK_DEFAULT(TestingV3AuthInitialVotingInterval); + CHECK_DEFAULT(TestingV3AuthInitialVoteDelay); + CHECK_DEFAULT(TestingV3AuthInitialDistDelay); + CHECK_DEFAULT(TestingV3AuthVotingStartOffset); + CHECK_DEFAULT(TestingAuthDirTimeToLearnReachability); + CHECK_DEFAULT(TestingEstimatedDescriptorPropagationTime); + CHECK_DEFAULT(TestingServerDownloadSchedule); + CHECK_DEFAULT(TestingClientDownloadSchedule); + CHECK_DEFAULT(TestingServerConsensusDownloadSchedule); + CHECK_DEFAULT(TestingClientConsensusDownloadSchedule); + CHECK_DEFAULT(TestingBridgeDownloadSchedule); + CHECK_DEFAULT(TestingClientMaxIntervalWithoutRequest); + CHECK_DEFAULT(TestingDirConnectionMaxStall); + CHECK_DEFAULT(TestingConsensusMaxDownloadTries); + CHECK_DEFAULT(TestingDescriptorMaxDownloadTries); + CHECK_DEFAULT(TestingMicrodescMaxDownloadTries); + CHECK_DEFAULT(TestingCertMaxDownloadTries); +#undef CHECK_DEFAULT + + if (options->TestingV3AuthInitialVotingInterval < MIN_VOTE_INTERVAL) { REJECT("TestingV3AuthInitialVotingInterval is insanely low."); } else if (((30*60) % options->TestingV3AuthInitialVotingInterval) != 0) { REJECT("TestingV3AuthInitialVotingInterval does not divide evenly into " "30 minutes."); } - if (options->TestingV3AuthInitialVoteDelay != 5*60 && - !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) { - - REJECT("TestingV3AuthInitialVoteDelay may only be changed in testing " - "Tor networks!"); - } else if (options->TestingV3AuthInitialVoteDelay < MIN_VOTE_SECONDS) { + if (options->TestingV3AuthInitialVoteDelay < MIN_VOTE_SECONDS) { REJECT("TestingV3AuthInitialVoteDelay is way too low."); } - if (options->TestingV3AuthInitialDistDelay != 5*60 && - !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) { - REJECT("TestingV3AuthInitialDistDelay may only be changed in testing " - "Tor networks!"); - } else if (options->TestingV3AuthInitialDistDelay < MIN_DIST_SECONDS) { + if (options->TestingV3AuthInitialDistDelay < MIN_DIST_SECONDS) { REJECT("TestingV3AuthInitialDistDelay is way too low."); } @@ -3221,26 +3300,61 @@ options_validate(or_options_t *old_options, or_options_t *options, "must be less than half TestingV3AuthInitialVotingInterval"); } - if (options->TestingAuthDirTimeToLearnReachability != 30*60 && - !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) { - REJECT("TestingAuthDirTimeToLearnReachability may only be changed in " - "testing Tor networks!"); - } else if (options->TestingAuthDirTimeToLearnReachability < 0) { + if (options->TestingV3AuthVotingStartOffset > + MIN(options->TestingV3AuthInitialVotingInterval, + options->V3AuthVotingInterval)) { + REJECT("TestingV3AuthVotingStartOffset is higher than the voting " + "interval."); + } + + if (options->TestingAuthDirTimeToLearnReachability < 0) { REJECT("TestingAuthDirTimeToLearnReachability must be non-negative."); } else if (options->TestingAuthDirTimeToLearnReachability > 2*60*60) { COMPLAIN("TestingAuthDirTimeToLearnReachability is insanely high."); } - if (options->TestingEstimatedDescriptorPropagationTime != 10*60 && - !options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) { - REJECT("TestingEstimatedDescriptorPropagationTime may only be changed in " - "testing Tor networks!"); - } else if (options->TestingEstimatedDescriptorPropagationTime < 0) { + if (options->TestingEstimatedDescriptorPropagationTime < 0) { REJECT("TestingEstimatedDescriptorPropagationTime must be non-negative."); } else if (options->TestingEstimatedDescriptorPropagationTime > 60*60) { COMPLAIN("TestingEstimatedDescriptorPropagationTime is insanely high."); } + if (options->TestingClientMaxIntervalWithoutRequest < 1) { + REJECT("TestingClientMaxIntervalWithoutRequest is way too low."); + } else if (options->TestingClientMaxIntervalWithoutRequest > 3600) { + COMPLAIN("TestingClientMaxIntervalWithoutRequest is insanely high."); + } + + if (options->TestingDirConnectionMaxStall < 5) { + REJECT("TestingDirConnectionMaxStall is way too low."); + } else if (options->TestingDirConnectionMaxStall > 3600) { + COMPLAIN("TestingDirConnectionMaxStall is insanely high."); + } + + if (options->TestingConsensusMaxDownloadTries < 2) { + REJECT("TestingConsensusMaxDownloadTries must be greater than 1."); + } else if (options->TestingConsensusMaxDownloadTries > 800) { + COMPLAIN("TestingConsensusMaxDownloadTries is insanely high."); + } + + if (options->TestingDescriptorMaxDownloadTries < 2) { + REJECT("TestingDescriptorMaxDownloadTries must be greater than 1."); + } else if (options->TestingDescriptorMaxDownloadTries > 800) { + COMPLAIN("TestingDescriptorMaxDownloadTries is insanely high."); + } + + if (options->TestingMicrodescMaxDownloadTries < 2) { + REJECT("TestingMicrodescMaxDownloadTries must be greater than 1."); + } else if (options->TestingMicrodescMaxDownloadTries > 800) { + COMPLAIN("TestingMicrodescMaxDownloadTries is insanely high."); + } + + if (options->TestingCertMaxDownloadTries < 2) { + REJECT("TestingCertMaxDownloadTries must be greater than 1."); + } else if (options->TestingCertMaxDownloadTries > 800) { + COMPLAIN("TestingCertMaxDownloadTries is insanely high."); + } + if (options->TestingTorNetwork) { log_warn(LD_CONFIG, "TestingTorNetwork is set. This will make your node " "almost unusable in the public Tor network, and is " @@ -3860,7 +3974,8 @@ options_init_from_string(const char *cf_defaults, const char *cf, } /* Validate newoptions */ - if (options_validate(oldoptions, newoptions, 0, msg) < 0) { + if (options_validate(oldoptions, newoptions, newdefaultoptions, + 0, msg) < 0) { err = SETOPT_ERR_PARSE; /*XXX make this a separate return value.*/ goto err; } @@ -4117,21 +4232,72 @@ options_init_logs(or_options_t *options, int validate_only) return ok?0:-1; } +/** Given a smartlist of SOCKS arguments to be passed to a transport + * proxy in <b>args</b>, validate them and return -1 if they are + * corrupted. Return 0 if they seem OK. */ +static int +validate_transport_socks_arguments(const smartlist_t *args) +{ + char *socks_string = NULL; + size_t socks_string_len; + + tor_assert(args); + tor_assert(smartlist_len(args) > 0); + + SMARTLIST_FOREACH_BEGIN(args, const char *, s) { + if (!string_is_key_value(LOG_WARN, s)) { /* items should be k=v items */ + log_warn(LD_CONFIG, "'%s' is not a k=v item.", s); + return -1; + } + } SMARTLIST_FOREACH_END(s); + + socks_string = pt_stringify_socks_args(args); + if (!socks_string) + return -1; + + socks_string_len = strlen(socks_string); + tor_free(socks_string); + + if (socks_string_len > MAX_SOCKS5_AUTH_SIZE_TOTAL) { + log_warn(LD_CONFIG, "SOCKS arguments can't be more than %u bytes (%lu).", + MAX_SOCKS5_AUTH_SIZE_TOTAL, + (unsigned long) socks_string_len); + return -1; + } + + return 0; +} + +/** Deallocate a bridge_line_t structure. */ +/* private */ void +bridge_line_free(bridge_line_t *bridge_line) +{ + if (!bridge_line) + return; + + if (bridge_line->socks_args) { + SMARTLIST_FOREACH(bridge_line->socks_args, char*, s, tor_free(s)); + smartlist_free(bridge_line->socks_args); + } + tor_free(bridge_line->transport_name); + tor_free(bridge_line); +} + /** Read the contents of a Bridge line from <b>line</b>. Return 0 * if the line is well-formed, and -1 if it isn't. If * <b>validate_only</b> is 0, and the line is well-formed, then add - * the bridge described in the line to our internal bridge list. */ -static int -parse_bridge_line(const char *line, int validate_only) + * the bridge described in the line to our internal bridge list. + * + * Bridge line format: + * Bridge [transport] IP:PORT [id-fingerprint] [k=v] [k=v] ... + */ +/* private */ bridge_line_t * +parse_bridge_line(const char *line) { smartlist_t *items = NULL; - int r; char *addrport=NULL, *fingerprint=NULL; - char *transport_name=NULL; - char *field1=NULL; - tor_addr_t addr; - uint16_t port = 0; - char digest[DIGEST_LEN]; + char *field=NULL; + bridge_line_t *bridge_line = tor_malloc_zero(sizeof(bridge_line_t)); items = smartlist_new(); smartlist_split_string(items, line, NULL, @@ -4141,68 +4307,102 @@ parse_bridge_line(const char *line, int validate_only) goto err; } - /* field1 is either a transport name or addrport */ - field1 = smartlist_get(items, 0); + /* first field is either a transport name or addrport */ + field = smartlist_get(items, 0); smartlist_del_keeporder(items, 0); - if (!(strstr(field1, ".") || strstr(field1, ":"))) { - /* new-style bridge line */ - transport_name = field1; + if (string_is_C_identifier(field)) { + /* It's a transport name. */ + bridge_line->transport_name = field; if (smartlist_len(items) < 1) { log_warn(LD_CONFIG, "Too few items to Bridge line."); goto err; } - addrport = smartlist_get(items, 0); + addrport = smartlist_get(items, 0); /* Next field is addrport then. */ smartlist_del_keeporder(items, 0); } else { - addrport = field1; + addrport = field; } - if (tor_addr_port_lookup(addrport, &addr, &port)<0) { + /* Parse addrport. */ + if (tor_addr_port_lookup(addrport, + &bridge_line->addr, &bridge_line->port)<0) { log_warn(LD_CONFIG, "Error parsing Bridge address '%s'", addrport); goto err; } - if (!port) { + if (!bridge_line->port) { log_info(LD_CONFIG, "Bridge address '%s' has no port; using default port 443.", addrport); - port = 443; + bridge_line->port = 443; } + /* If transports are enabled, next field could be a fingerprint or a + socks argument. If transports are disabled, next field must be + a fingerprint. */ if (smartlist_len(items)) { - fingerprint = smartlist_join_strings(items, "", 0, NULL); + if (bridge_line->transport_name) { /* transports enabled: */ + field = smartlist_get(items, 0); + smartlist_del_keeporder(items, 0); + + /* If it's a key=value pair, then it's a SOCKS argument for the + transport proxy... */ + if (string_is_key_value(LOG_DEBUG, field)) { + bridge_line->socks_args = smartlist_new(); + smartlist_add(bridge_line->socks_args, field); + } else { /* ...otherwise, it's the bridge fingerprint. */ + fingerprint = field; + } + + } else { /* transports disabled: */ + fingerprint = smartlist_join_strings(items, "", 0, NULL); + } + } + + /* Handle fingerprint, if it was provided. */ + if (fingerprint) { if (strlen(fingerprint) != HEX_DIGEST_LEN) { log_warn(LD_CONFIG, "Key digest for Bridge is wrong length."); goto err; } - if (base16_decode(digest, DIGEST_LEN, fingerprint, HEX_DIGEST_LEN)<0) { + if (base16_decode(bridge_line->digest, DIGEST_LEN, + fingerprint, HEX_DIGEST_LEN)<0) { log_warn(LD_CONFIG, "Unable to decode Bridge key digest."); goto err; } } - if (!validate_only) { - log_debug(LD_DIR, "Bridge at %s (transport: %s) (%s)", - fmt_addrport(&addr, port), - transport_name ? transport_name : "no transport", - fingerprint ? fingerprint : "no key listed"); - bridge_add_from_config(&addr, port, - fingerprint ? digest : NULL, transport_name); + /* If we are using transports, any remaining items in the smartlist + should be k=v values. */ + if (bridge_line->transport_name && smartlist_len(items)) { + if (!bridge_line->socks_args) + bridge_line->socks_args = smartlist_new(); + + /* append remaining items of 'items' to 'socks_args' */ + smartlist_add_all(bridge_line->socks_args, items); + smartlist_clear(items); + + tor_assert(smartlist_len(bridge_line->socks_args) > 0); + } + + if (bridge_line->socks_args) { + if (validate_transport_socks_arguments(bridge_line->socks_args) < 0) + goto err; } - r = 0; goto done; err: - r = -1; + bridge_line_free(bridge_line); + bridge_line = NULL; done: SMARTLIST_FOREACH(items, char*, s, tor_free(s)); smartlist_free(items); tor_free(addrport); - tor_free(transport_name); tor_free(fingerprint); - return r; + + return bridge_line; } /** Read the contents of a ClientTransportPlugin line from @@ -4392,6 +4592,63 @@ get_bindaddr_from_transport_listen_line(const char *line,const char *transport) return addrport; } +/** Given a ServerTransportOptions <b>line</b>, return a smartlist + * with the options. Return NULL if the line was not well-formed. + * + * If <b>transport</b> is set, return NULL if the line is not + * referring to <b>transport</b>. + * + * The returned smartlist and its strings are allocated on the heap + * and it's the responsibility of the caller to free it. */ +smartlist_t * +get_options_from_transport_options_line(const char *line,const char *transport) +{ + smartlist_t *items = smartlist_new(); + smartlist_t *options = smartlist_new(); + const char *parsed_transport = NULL; + + smartlist_split_string(items, line, NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); + + if (smartlist_len(items) < 2) { + log_warn(LD_CONFIG,"Too few arguments on ServerTransportOptions line."); + goto err; + } + + parsed_transport = smartlist_get(items, 0); + /* If 'transport' is given, check if it matches the one on the line */ + if (transport && strcmp(transport, parsed_transport)) + goto err; + + SMARTLIST_FOREACH_BEGIN(items, const char *, option) { + if (option_sl_idx == 0) /* skip the transport field (first field)*/ + continue; + + /* validate that it's a k=v value */ + if (!string_is_key_value(LOG_WARN, option)) { + log_warn(LD_CONFIG, "%s is not a k=v value.", escaped(option)); + goto err; + } + + /* add it to the options smartlist */ + smartlist_add(options, tor_strdup(option)); + log_debug(LD_CONFIG, "Added %s to the list of options", escaped(option)); + } SMARTLIST_FOREACH_END(option); + + goto done; + + err: + SMARTLIST_FOREACH(options, char*, s, tor_free(s)); + smartlist_free(options); + options = NULL; + + done: + SMARTLIST_FOREACH(items, char*, s, tor_free(s)); + smartlist_free(items); + + return options; +} + /** Given the name of a pluggable transport in <b>transport</b>, check * the configuration file to see if the user has explicitly asked for * it to listen on a specific port. Return a <address:port> string if @@ -4412,6 +4669,26 @@ get_transport_bindaddr_from_config(const char *transport) return NULL; } +/** Given the name of a pluggable transport in <b>transport</b>, check + * the configuration file to see if the user has asked us to pass any + * parameters to the pluggable transport. Return a smartlist + * containing the parameters, otherwise NULL. */ +smartlist_t * +get_options_for_server_transport(const char *transport) +{ + config_line_t *cl; + const or_options_t *options = get_options(); + + for (cl = options->ServerTransportOptions; cl; cl = cl->next) { + smartlist_t *options_sl = + get_options_from_transport_options_line(cl->value, transport); + if (options_sl) + return options_sl; + } + + return NULL; +} + /** Read the contents of a ServerTransportPlugin line from * <b>line</b>. Return 0 if the line is well-formed, and -1 if it * isn't. @@ -5886,6 +6163,43 @@ options_get_datadir_fname2_suffix(const or_options_t *options, return fname; } +/** Check wether the data directory has a private subdirectory + * <b>subdir</b>. If not, try to create it. Return 0 on success, + * -1 otherwise. */ +int +check_or_create_data_subdir(const char *subdir) +{ + char *statsdir = get_datadir_fname(subdir); + int return_val = 0; + + if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) { + log_warn(LD_HIST, "Unable to create %s/ directory!", subdir); + return_val = -1; + } + tor_free(statsdir); + return return_val; +} + +/** Create a file named <b>fname</b> with contents <b>str</b> in the + * subdirectory <b>subdir</b> of the data directory. <b>descr</b> + * should be a short description of the file's content and will be + * used for the warning message, if it's present and the write process + * fails. Return 0 on success, -1 otherwise.*/ +int +write_to_data_subdir(const char* subdir, const char* fname, + const char* str, const char* descr) +{ + char *filename = get_datadir_fname2(subdir, fname); + int return_val = 0; + + if (write_str_to_file(filename, str, 0) < 0) { + log_warn(LD_HIST, "Unable to write %s to disk!", descr ? descr : fname); + return_val = -1; + } + tor_free(filename); + return return_val; +} + /** Given a file name check to see whether the file exists but has not been * modified for a very long time. If so, remove it. */ void @@ -5975,6 +6289,7 @@ getinfo_helper_config(control_connection_t *conn, case CONFIG_TYPE_ISOTIME: type = "Time"; break; case CONFIG_TYPE_ROUTERSET: type = "RouterList"; break; case CONFIG_TYPE_CSV: type = "CommaList"; break; + case CONFIG_TYPE_CSV_INTERVAL: type = "TimeIntervalCommaList"; break; case CONFIG_TYPE_LINELIST: type = "LineList"; break; case CONFIG_TYPE_LINELIST_S: type = "Dependant"; break; case CONFIG_TYPE_LINELIST_V: type = "Virtual"; break; diff --git a/src/or/config.h b/src/or/config.h index ef4acac514..16a8a350d5 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -59,6 +59,10 @@ char *options_get_datadir_fname2_suffix(const or_options_t *options, #define get_datadir_fname_suffix(sub1, suffix) \ get_datadir_fname2_suffix((sub1), NULL, (suffix)) +int check_or_create_data_subdir(const char *subdir); +int write_to_data_subdir(const char* subdir, const char* fname, + const char* str, const char* descr); + int get_num_cpus(const or_options_t *options); const smartlist_t *get_configured_ports(void); @@ -86,10 +90,7 @@ uint32_t get_effective_bwburst(const or_options_t *options); char *get_transport_bindaddr_from_config(const char *transport); -#ifdef CONFIG_PRIVATE -/* Used only by config.c and test.c */ or_options_t *options_new(void); -#endif void config_register_addressmaps(const or_options_t *options); /* XXXX024 move to connection_edge.h */ @@ -98,5 +99,34 @@ int addressmap_register_auto(const char *from, const char *to, addressmap_entry_source_t addrmap_source, const char **msg); +/** Represents the information stored in a torrc Bridge line. */ +typedef struct bridge_line_t { + tor_addr_t addr; /* The IP address of the bridge. */ + uint16_t port; /* The TCP port of the bridge. */ + char *transport_name; /* The name of the pluggable transport that + should be used to connect to the bridge. */ + char digest[DIGEST_LEN]; /* The bridge's identity key digest. */ + smartlist_t *socks_args; /* SOCKS arguments for the pluggable + transport proxy. */ +} bridge_line_t; + +void bridge_line_free(bridge_line_t *bridge_line); +bridge_line_t *parse_bridge_line(const char *line); +smartlist_t *get_options_from_transport_options_line(const char *line, + const char *transport); +smartlist_t *get_options_for_server_transport(const char *transport); + +#ifdef CONFIG_PRIVATE +#ifdef TOR_UNIT_TESTS +extern struct config_format_t options_format; +#endif + +STATIC void or_options_free(or_options_t *options); +STATIC int options_validate(or_options_t *old_options, + or_options_t *options, + or_options_t *default_options, + int from_setconf, char **msg); +#endif + #endif diff --git a/src/or/confparse.c b/src/or/confparse.c index 8863d92409..41055791ef 100644 --- a/src/or/confparse.c +++ b/src/or/confparse.c @@ -223,6 +223,8 @@ config_assign_value(const config_format_t *fmt, void *options, int i, ok; const config_var_t *var; void *lvalue; + int *csv_int; + smartlist_t *csv_str; CONFIG_CHECK(fmt, options); @@ -357,6 +359,36 @@ config_assign_value(const config_format_t *fmt, void *options, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); break; + case CONFIG_TYPE_CSV_INTERVAL: + if (*(smartlist_t**)lvalue) { + SMARTLIST_FOREACH(*(smartlist_t**)lvalue, int *, cp, tor_free(cp)); + smartlist_clear(*(smartlist_t**)lvalue); + } else { + *(smartlist_t**)lvalue = smartlist_new(); + } + csv_str = smartlist_new(); + smartlist_split_string(csv_str, c->value, ",", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + SMARTLIST_FOREACH_BEGIN(csv_str, char *, str) + { + i = config_parse_interval(str, &ok); + if (!ok) { + tor_asprintf(msg, + "Interval in '%s %s' is malformed or out of bounds.", + c->key, c->value); + SMARTLIST_FOREACH(csv_str, char *, cp, tor_free(cp)); + smartlist_clear(csv_str); + return -1; + } + csv_int = tor_malloc_zero(sizeof(int)); + *csv_int = i; + smartlist_add(*(smartlist_t**)lvalue, csv_int); + } + SMARTLIST_FOREACH_END(str); + SMARTLIST_FOREACH(csv_str, char *, cp, tor_free(cp)); + smartlist_clear(csv_str); + break; + case CONFIG_TYPE_LINELIST: case CONFIG_TYPE_LINELIST_S: { @@ -555,6 +587,7 @@ config_get_assigned_option(const config_format_t *fmt, const void *options, const config_var_t *var; const void *value; config_line_t *result; + smartlist_t *csv_str; tor_assert(options && key); CONFIG_CHECK(fmt, options); @@ -637,6 +670,20 @@ config_get_assigned_option(const config_format_t *fmt, const void *options, else result->value = tor_strdup(""); break; + case CONFIG_TYPE_CSV_INTERVAL: + if (*(smartlist_t**)value) { + csv_str = smartlist_new(); + SMARTLIST_FOREACH_BEGIN(*(smartlist_t**)value, int *, i) + { + smartlist_add_asprintf(csv_str, "%d", *i); + } + SMARTLIST_FOREACH_END(i); + result->value = smartlist_join_strings(csv_str, ",", 0, NULL); + SMARTLIST_FOREACH(csv_str, char *, cp, tor_free(cp)); + smartlist_free(csv_str); + } else + result->value = tor_strdup(""); + break; case CONFIG_TYPE_OBSOLETE: log_fn(LOG_INFO, LD_CONFIG, "You asked me for the value of an obsolete config option '%s'.", @@ -826,6 +873,13 @@ config_clear(const config_format_t *fmt, void *options, *(smartlist_t **)lvalue = NULL; } break; + case CONFIG_TYPE_CSV_INTERVAL: + if (*(smartlist_t**)lvalue) { + SMARTLIST_FOREACH(*(smartlist_t **)lvalue, int *, cp, tor_free(cp)); + smartlist_free(*(smartlist_t **)lvalue); + *(smartlist_t **)lvalue = NULL; + } + break; case CONFIG_TYPE_LINELIST: case CONFIG_TYPE_LINELIST_S: config_free_lines(*(config_line_t **)lvalue); @@ -1072,20 +1126,36 @@ static struct unit_table_t memory_units[] = { { "kbytes", 1<<10 }, { "kilobyte", 1<<10 }, { "kilobytes", 1<<10 }, + { "kilobits", 1<<7 }, + { "kilobit", 1<<7 }, + { "kbits", 1<<7 }, + { "kbit", 1<<7 }, { "m", 1<<20 }, { "mb", 1<<20 }, { "mbyte", 1<<20 }, { "mbytes", 1<<20 }, { "megabyte", 1<<20 }, { "megabytes", 1<<20 }, + { "megabits", 1<<17 }, + { "megabit", 1<<17 }, + { "mbits", 1<<17 }, + { "mbit", 1<<17 }, { "gb", 1<<30 }, { "gbyte", 1<<30 }, { "gbytes", 1<<30 }, { "gigabyte", 1<<30 }, { "gigabytes", 1<<30 }, + { "gigabits", 1<<27 }, + { "gigabit", 1<<27 }, + { "gbits", 1<<27 }, + { "gbit", 1<<27 }, { "tb", U64_LITERAL(1)<<40 }, { "terabyte", U64_LITERAL(1)<<40 }, { "terabytes", U64_LITERAL(1)<<40 }, + { "terabits", U64_LITERAL(1)<<37 }, + { "terabit", U64_LITERAL(1)<<37 }, + { "tbits", U64_LITERAL(1)<<37 }, + { "tbit", U64_LITERAL(1)<<37 }, { NULL, 0 }, }; diff --git a/src/or/confparse.h b/src/or/confparse.h index 1b987f3bf9..924ee0d945 100644 --- a/src/or/confparse.h +++ b/src/or/confparse.h @@ -26,6 +26,9 @@ typedef enum config_type_t { CONFIG_TYPE_ISOTIME, /**< An ISO-formatted time relative to UTC. */ CONFIG_TYPE_CSV, /**< A list of strings, separated by commas and * optional whitespace. */ + CONFIG_TYPE_CSV_INTERVAL, /**< A list of strings, separated by commas and + * optional whitespace, representing intervals in + * seconds, with optional units */ CONFIG_TYPE_LINELIST, /**< Uninterpreted config lines */ CONFIG_TYPE_LINELIST_S, /**< Uninterpreted, context-sensitive config lines, * mixed with other keywords. */ @@ -73,7 +76,7 @@ typedef int (*validate_fn_t)(void*,void*,int,char**); /** Information on the keys, value types, key-to-struct-member mappings, * variable descriptions, validation functions, and abbreviations for a * configuration or storage format. */ -typedef struct { +typedef struct config_format_t { size_t size; /**< Size of the struct that everything gets parsed into. */ uint32_t magic; /**< Required 'magic value' to make sure we have a struct * of the right type. */ diff --git a/src/or/connection.c b/src/or/connection.c index 6e754a0f7a..6a3cc7bec4 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -44,6 +44,7 @@ #include "router.h" #include "transports.h" #include "routerparse.h" +#include "transports.h" #ifdef USE_BUFFEREVENTS #include <event2/event.h> @@ -876,8 +877,11 @@ check_location_for_unix_socket(const or_options_t *options, const char *path) int r = -1; char *p = tor_strdup(path); cpd_check_t flags = CPD_CHECK_MODE_ONLY; - if (get_parent_directory(p)<0) + if (get_parent_directory(p)<0 || p[0] != '/') { + log_warn(LD_GENERAL, "Bad unix socket address '%s'. Tor does not support " + "relative paths for unix sockets.", path); goto done; + } if (options->ControlSocketsGroupWritable) flags |= CPD_GROUP_OK; @@ -939,8 +943,8 @@ connection_listener_new(const struct sockaddr *listensockaddr, const port_cfg_t *port_cfg) { listener_connection_t *lis_conn; - connection_t *conn; - tor_socket_t s; /* the socket we're going to make */ + connection_t *conn = NULL; + tor_socket_t s = TOR_INVALID_SOCKET; /* the socket we're going to make */ or_options_t const *options = get_options(); #if defined(HAVE_PWD_H) && defined(HAVE_SYS_UN_H) struct passwd *pw = NULL; @@ -988,7 +992,7 @@ connection_listener_new(const struct sockaddr *listensockaddr, /* We need to set IPV6_V6ONLY so that this socket can't get used for * IPv4 connections. */ if (setsockopt(s,IPPROTO_IPV6, IPV6_V6ONLY, - (void*)&one, sizeof(one))<0) { + (void*)&one, sizeof(one)) < 0) { int e = tor_socket_errno(s); log_warn(LD_NET, "Error setting IPV6_V6ONLY flag: %s", tor_socket_strerror(e)); @@ -1004,7 +1008,6 @@ connection_listener_new(const struct sockaddr *listensockaddr, helpfulhint = ". Is Tor already running?"; log_warn(LD_NET, "Could not bind to %s:%u: %s%s", address, usePort, tor_socket_strerror(e), helpfulhint); - tor_close_socket(s); goto err; } @@ -1012,7 +1015,6 @@ connection_listener_new(const struct sockaddr *listensockaddr, if (listen(s,SOMAXCONN) < 0) { log_warn(LD_NET, "Could not listen on %s:%u: %s", address, usePort, tor_socket_strerror(tor_socket_errno(s))); - tor_close_socket(s); goto err; } } @@ -1061,7 +1063,6 @@ connection_listener_new(const struct sockaddr *listensockaddr, if (bind(s, listensockaddr, (socklen_t)sizeof(struct sockaddr_un)) == -1) { log_warn(LD_NET,"Bind to %s failed: %s.", address, tor_socket_strerror(tor_socket_errno(s))); - tor_close_socket(s); goto err; } #ifdef HAVE_PWD_H @@ -1070,12 +1071,10 @@ connection_listener_new(const struct sockaddr *listensockaddr, if (pw == NULL) { log_warn(LD_NET,"Unable to chown() %s socket: user %s not found.", address, options->User); - tor_close_socket(s); goto err; } else if (chown(address, pw->pw_uid, pw->pw_gid) < 0) { log_warn(LD_NET,"Unable to chown() %s socket: %s.", address, strerror(errno)); - tor_close_socket(s); goto err; } } @@ -1085,35 +1084,32 @@ connection_listener_new(const struct sockaddr *listensockaddr, * platforms. */ if (chmod(address, 0660) < 0) { log_warn(LD_FS,"Unable to make %s group-writable.", address); - tor_close_socket(s); goto err; } } - if (listen(s,SOMAXCONN) < 0) { + if (listen(s, SOMAXCONN) < 0) { log_warn(LD_NET, "Could not listen on %s: %s", address, tor_socket_strerror(tor_socket_errno(s))); - tor_close_socket(s); goto err; } #else (void)options; #endif /* HAVE_SYS_UN_H */ } else { - log_err(LD_BUG,"Got unexpected address family %d.", - listensockaddr->sa_family); - tor_assert(0); + log_err(LD_BUG, "Got unexpected address family %d.", + listensockaddr->sa_family); + tor_assert(0); } - if (set_socket_nonblocking(s) == -1) { - tor_close_socket(s); + if (set_socket_nonblocking(s) == -1) goto err; - } lis_conn = listener_connection_new(type, listensockaddr->sa_family); conn = TO_CONN(lis_conn); conn->socket_family = listensockaddr->sa_family; conn->s = s; + s = TOR_INVALID_SOCKET; /* Prevent double-close */ conn->address = tor_strdup(address); conn->port = gotPort; tor_addr_copy(&conn->addr, &addr); @@ -1149,7 +1145,6 @@ connection_listener_new(const struct sockaddr *listensockaddr, if (connection_add(conn) < 0) { /* no space, forget it */ log_warn(LD_NET,"connection_add for listener failed. Giving up."); - connection_free(conn); goto err; } @@ -1168,6 +1163,11 @@ connection_listener_new(const struct sockaddr *listensockaddr, return conn; err: + if (SOCKET_OK(s)) + tor_close_socket(s); + if (conn) + connection_free(conn); + return NULL; } @@ -1580,6 +1580,32 @@ connection_proxy_state_to_string(int state) return states[state]; } +/** Returns the global proxy type used by tor. Use this function for + * logging or high-level purposes, don't use it to fill the + * <b>proxy_type</b> field of or_connection_t; use the actual proxy + * protocol instead.*/ +static int +get_proxy_type(void) +{ + const or_options_t *options = get_options(); + + if (options->HTTPSProxy) + return PROXY_CONNECT; + else if (options->Socks4Proxy) + return PROXY_SOCKS4; + else if (options->Socks5Proxy) + return PROXY_SOCKS5; + else if (options->ClientTransportPlugin) + return PROXY_PLUGGABLE; + else + return PROXY_NONE; +} + +/* One byte for the version, one for the command, two for the + port, and four for the addr... and, one more for the + username NUL: */ +#define SOCKS4_STANDARD_BUFFER_SIZE (1 + 1 + 2 + 4 + 1) + /** Write a proxy request of <b>type</b> (socks4, socks5, https) to conn * for conn->addr:conn->port, authenticating with the auth details given * in the configuration (if available). SOCKS 5 and HTTP CONNECT proxies @@ -1634,17 +1660,45 @@ connection_proxy_connect(connection_t *conn, int type) } case PROXY_SOCKS4: { - unsigned char buf[9]; + unsigned char *buf; uint16_t portn; uint32_t ip4addr; + size_t buf_size = 0; + char *socks_args_string = NULL; - /* Send a SOCKS4 connect request with empty user id */ + /* Send a SOCKS4 connect request */ if (tor_addr_family(&conn->addr) != AF_INET) { log_warn(LD_NET, "SOCKS4 client is incompatible with IPv6"); return -1; } + { /* If we are here because we are trying to connect to a + pluggable transport proxy, check if we have any SOCKS + arguments to transmit. If we do, compress all arguments to + a single string in 'socks_args_string': */ + + if (get_proxy_type() == PROXY_PLUGGABLE) { + socks_args_string = + pt_get_socks_args_for_proxy_addrport(&conn->addr, conn->port); + if (socks_args_string) + log_debug(LD_NET, "Sending out '%s' as our SOCKS argument string.", + socks_args_string); + } + } + + { /* Figure out the buffer size we need for the SOCKS message: */ + + buf_size = SOCKS4_STANDARD_BUFFER_SIZE; + + /* If we have a SOCKS argument string, consider its size when + calculating the buffer size: */ + if (socks_args_string) + buf_size += strlen(socks_args_string); + } + + buf = tor_malloc_zero(buf_size); + ip4addr = tor_addr_to_ipv4n(&conn->addr); portn = htons(conn->port); @@ -1652,9 +1706,23 @@ connection_proxy_connect(connection_t *conn, int type) buf[1] = SOCKS_COMMAND_CONNECT; /* command */ memcpy(buf + 2, &portn, 2); /* port */ memcpy(buf + 4, &ip4addr, 4); /* addr */ - buf[8] = 0; /* userid (empty) */ - connection_write_to_buf((char *)buf, sizeof(buf), conn); + /* Next packet field is the userid. If we have pluggable + transport SOCKS arguments, we have to embed them + there. Otherwise, we use an empty userid. */ + if (socks_args_string) { /* place the SOCKS args string: */ + tor_assert(strlen(socks_args_string) > 0); + tor_assert(buf_size >= + SOCKS4_STANDARD_BUFFER_SIZE + strlen(socks_args_string)); + strlcpy((char *)buf + 8, socks_args_string, buf_size - 8); + tor_free(socks_args_string); + } else { + buf[8] = 0; /* no userid */ + } + + connection_write_to_buf((char *)buf, buf_size, conn); + tor_free(buf); + conn->proxy_state = PROXY_SOCKS4_WANT_CONNECT_OK; break; } @@ -1666,8 +1734,13 @@ connection_proxy_connect(connection_t *conn, int type) buf[0] = 5; /* version */ + /* We have to use SOCKS5 authentication, if we have a + Socks5ProxyUsername or if we want to pass arguments to our + pluggable transport proxy: */ + if ((options->Socks5ProxyUsername) || + (get_proxy_type() == PROXY_PLUGGABLE && + (get_socks_args_by_bridge_addrport(&conn->addr, conn->port)))) { /* number of auth methods */ - if (options->Socks5ProxyUsername) { buf[1] = 2; buf[2] = 0x00; /* no authentication */ buf[3] = 0x02; /* rfc1929 Username/Passwd auth */ @@ -1861,15 +1934,49 @@ connection_read_proxy_handshake(connection_t *conn) unsigned char buf[1024]; size_t reqsize, usize, psize; const char *user, *pass; + char *socks_args_string = NULL; + + if (get_proxy_type() == PROXY_PLUGGABLE) { + socks_args_string = + pt_get_socks_args_for_proxy_addrport(&conn->addr, conn->port); + if (!socks_args_string) { + log_warn(LD_NET, "Could not create SOCKS args string."); + ret = -1; + break; + } + + log_debug(LD_NET, "SOCKS5 arguments: %s", socks_args_string); + tor_assert(strlen(socks_args_string) > 0); + tor_assert(strlen(socks_args_string) <= MAX_SOCKS5_AUTH_SIZE_TOTAL); + + if (strlen(socks_args_string) > MAX_SOCKS5_AUTH_FIELD_SIZE) { + user = socks_args_string; + usize = MAX_SOCKS5_AUTH_FIELD_SIZE; + pass = socks_args_string + MAX_SOCKS5_AUTH_FIELD_SIZE; + psize = strlen(socks_args_string) - MAX_SOCKS5_AUTH_FIELD_SIZE; + } else { + user = socks_args_string; + usize = strlen(socks_args_string); + pass = "\0"; + psize = 1; + } + } else if (get_options()->Socks5ProxyUsername) { + user = get_options()->Socks5ProxyUsername; + pass = get_options()->Socks5ProxyPassword; + tor_assert(user && pass); + usize = strlen(user); + psize = strlen(pass); + } else { + log_err(LD_BUG, "We entered %s for no reason!", __func__); + tor_fragile_assert(); + ret = -1; + break; + } - user = get_options()->Socks5ProxyUsername; - pass = get_options()->Socks5ProxyPassword; - tor_assert(user && pass); - - /* XXX len of user and pass must be <= 255 !!! */ - usize = strlen(user); - psize = strlen(pass); - tor_assert(usize <= 255 && psize <= 255); + /* Username and password lengths should have been checked + above and during torrc parsing. */ + tor_assert(usize <= MAX_SOCKS5_AUTH_FIELD_SIZE && + psize <= MAX_SOCKS5_AUTH_FIELD_SIZE); reqsize = 3 + usize + psize; buf[0] = 1; /* negotiation version */ @@ -1878,6 +1985,9 @@ connection_read_proxy_handshake(connection_t *conn) buf[2 + usize] = psize; memcpy(buf + 3 + usize, pass, psize); + if (socks_args_string) + tor_free(socks_args_string); + connection_write_to_buf((char *)buf, reqsize, conn); conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_RFC1929_OK; @@ -3288,8 +3398,8 @@ connection_outbuf_too_full(connection_t *conn) /** Try to flush more bytes onto <b>conn</b>-\>s. * - * This function gets called either from conn_write() in main.c - * when poll() has declared that conn wants to write, or below + * This function gets called either from conn_write_callback() in main.c + * when libevent tells us that conn wants to write, or below * from connection_write_to_buf() when an entire TLS record is ready. * * Update <b>conn</b>-\>timestamp_lastwritten to now, and call flush_buf @@ -4366,7 +4476,7 @@ get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type, options->Bridges) { const transport_t *transport = NULL; int r; - r = find_transport_by_bridge_addrport(&conn->addr, conn->port, &transport); + r = get_transport_by_bridge_addrport(&conn->addr, conn->port, &transport); if (r<0) return -1; if (transport) { /* transport found */ @@ -4381,24 +4491,6 @@ get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type, return 0; } -/** Returns the global proxy type used by tor. */ -static int -get_proxy_type(void) -{ - const or_options_t *options = get_options(); - - if (options->HTTPSProxy) - return PROXY_CONNECT; - else if (options->Socks4Proxy) - return PROXY_SOCKS4; - else if (options->Socks5Proxy) - return PROXY_SOCKS5; - else if (options->ClientTransportPlugin) - return PROXY_PLUGGABLE; - else - return PROXY_NONE; -} - /** Log a failed connection to a proxy server. * <b>conn</b> is the connection we use the proxy server for. */ void diff --git a/src/or/connection.h b/src/or/connection.h index c78fe6e652..3e656ec06e 100644 --- a/src/or/connection.h +++ b/src/or/connection.h @@ -89,6 +89,14 @@ int connection_connect(connection_t *conn, const char *address, const tor_addr_t *addr, uint16_t port, int *socket_error); +/** Maximum size of information that we can fit into SOCKS5 username + or password fields. */ +#define MAX_SOCKS5_AUTH_FIELD_SIZE 255 + +/** Total maximum size of information that we can fit into SOCKS5 + username and password fields. */ +#define MAX_SOCKS5_AUTH_SIZE_TOTAL 2*MAX_SOCKS5_AUTH_FIELD_SIZE + int connection_proxy_connect(connection_t *conn, int type); int connection_read_proxy_handshake(connection_t *conn); void log_failed_proxy_connection(connection_t *conn); diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index bb7ffb9a40..33585a0944 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -407,7 +407,7 @@ connection_edge_finished_flushing(edge_connection_t *conn) * that the name resolution that led us to <b>addr</b> will be valid for * <b>ttl</b> seconds. Return -1 on error, or the number of bytes used on * success. */ -/* private */int +STATIC int connected_cell_format_payload(uint8_t *payload_out, const tor_addr_t *addr, uint32_t ttl) @@ -2265,7 +2265,7 @@ connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply, * Return -1 in the case where want to send a RELAY_END cell, and < -1 when * we don't. **/ -/* static */ int +STATIC int begin_cell_parse(const cell_t *cell, begin_cell_t *bcell, uint8_t *end_reason_out) { diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h index ea284cbcfd..e3a95ad9ed 100644 --- a/src/or/connection_edge.h +++ b/src/or/connection_edge.h @@ -12,6 +12,8 @@ #ifndef TOR_CONNECTION_EDGE_H #define TOR_CONNECTION_EDGE_H +#include "testsupport.h" + #define connection_mark_unattached_ap(conn, endreason) \ connection_mark_unattached_ap_((conn), (endreason), __LINE__, SHORT_FILE__) @@ -130,9 +132,9 @@ typedef struct begin_cell_t { unsigned is_begindir : 1; } begin_cell_t; -int begin_cell_parse(const cell_t *cell, begin_cell_t *bcell, +STATIC int begin_cell_parse(const cell_t *cell, begin_cell_t *bcell, uint8_t *end_reason_out); -int connected_cell_format_payload(uint8_t *payload_out, +STATIC int connected_cell_format_payload(uint8_t *payload_out, const tor_addr_t *addr, uint32_t ttl); #endif diff --git a/src/or/control.c b/src/or/control.c index a88de12d69..3f8d47c554 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -334,7 +334,7 @@ connection_write_str_to_buf(const char *s, control_connection_t *conn) * the end. Replace all LF characters sequences with CRLF. Return the number * of bytes in *<b>out</b>. */ -/* static */ size_t +STATIC size_t write_escaped_data(const char *data, size_t len, char **out) { size_t sz_out = len+8; @@ -382,7 +382,7 @@ write_escaped_data(const char *data, size_t len, char **out) * that appears at the start of a line, and replacing all CRLF sequences * with LF. Return the number of * bytes in *<b>out</b>. */ -/* static */ size_t +STATIC size_t read_escaped_data(const char *data, size_t len, char **out) { char *outp; @@ -1572,7 +1572,8 @@ munge_extrainfo_into_routerinfo(const char *ri_body, if (!(cp = tor_memstr(ei_body, ei_len, kwd))) continue; ++cp; - eol = memchr(cp, '\n', ei_len - (cp-ei_body)); + if (!(eol = memchr(cp, '\n', ei_len - (cp-ei_body)))) + continue; memcpy(outp, cp, eol-cp+1); outp += eol-cp+1; } diff --git a/src/or/control.h b/src/or/control.h index 61062da2c4..d0f682067e 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -91,8 +91,8 @@ void control_event_clients_seen(const char *controller_str); #ifdef CONTROL_PRIVATE /* Used only by control.c and test.c */ -size_t write_escaped_data(const char *data, size_t len, char **out); -size_t read_escaped_data(const char *data, size_t len, char **out); +STATIC size_t write_escaped_data(const char *data, size_t len, char **out); +STATIC size_t read_escaped_data(const char *data, size_t len, char **out); #endif #endif diff --git a/src/or/directory.c b/src/or/directory.c index b4381ac0de..88d6717791 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -3723,57 +3723,27 @@ dir_networkstatus_download_failed(smartlist_t *failed, int status_code) } SMARTLIST_FOREACH_END(fp); } -/** Schedule for when servers should download things in general. */ -static const int server_dl_schedule[] = { - 0, 0, 0, 60, 60, 60*2, 60*5, 60*15, INT_MAX -}; -/** Schedule for when clients should download things in general. */ -static const int client_dl_schedule[] = { - 0, 0, 60, 60*5, 60*10, INT_MAX -}; -/** Schedule for when servers should download consensuses. */ -static const int server_consensus_dl_schedule[] = { - 0, 0, 60, 60*5, 60*10, 60*30, 60*30, 60*30, 60*30, 60*30, 60*60, 60*60*2 -}; -/** Schedule for when clients should download consensuses. */ -static const int client_consensus_dl_schedule[] = { - 0, 0, 60, 60*5, 60*10, 60*30, 60*60, 60*60, 60*60, 60*60*3, 60*60*6, 60*60*12 -}; -/** Schedule for when clients should download bridge descriptors. */ -static const int bridge_dl_schedule[] = { - 60*60, 15*60, 15*60, 60*60 -}; - -/** Decide which download schedule we want to use, and then return a - * pointer to it along with a pointer to its length. Helper function for - * download_status_increment_failure() and download_status_reset(). */ -static void -find_dl_schedule_and_len(download_status_t *dls, int server, - const int **schedule, size_t *schedule_len) +/** Decide which download schedule we want to use based on descriptor type + * in <b>dls</b> and whether we are acting as directory <b>server</b>, and + * then return a list of int pointers defining download delays in seconds. + * Helper function for download_status_increment_failure() and + * download_status_reset(). */ +static const smartlist_t * +find_dl_schedule_and_len(download_status_t *dls, int server) { switch (dls->schedule) { case DL_SCHED_GENERIC: - if (server) { - *schedule = server_dl_schedule; - *schedule_len = sizeof(server_dl_schedule)/sizeof(int); - } else { - *schedule = client_dl_schedule; - *schedule_len = sizeof(client_dl_schedule)/sizeof(int); - } - break; + if (server) + return get_options()->TestingServerDownloadSchedule; + else + return get_options()->TestingClientDownloadSchedule; case DL_SCHED_CONSENSUS: - if (server) { - *schedule = server_consensus_dl_schedule; - *schedule_len = sizeof(server_consensus_dl_schedule)/sizeof(int); - } else { - *schedule = client_consensus_dl_schedule; - *schedule_len = sizeof(client_consensus_dl_schedule)/sizeof(int); - } - break; + if (server) + return get_options()->TestingServerConsensusDownloadSchedule; + else + return get_options()->TestingClientConsensusDownloadSchedule; case DL_SCHED_BRIDGE: - *schedule = bridge_dl_schedule; - *schedule_len = sizeof(bridge_dl_schedule)/sizeof(int); - break; + return get_options()->TestingBridgeDownloadSchedule; default: tor_assert(0); } @@ -3787,8 +3757,7 @@ time_t download_status_increment_failure(download_status_t *dls, int status_code, const char *item, int server, time_t now) { - const int *schedule; - size_t schedule_len; + const smartlist_t *schedule; int increment; tor_assert(dls); if (status_code != 503 || server) { @@ -3796,14 +3765,14 @@ download_status_increment_failure(download_status_t *dls, int status_code, ++dls->n_download_failures; } - find_dl_schedule_and_len(dls, server, &schedule, &schedule_len); + schedule = find_dl_schedule_and_len(dls, server); - if (dls->n_download_failures < schedule_len) - increment = schedule[dls->n_download_failures]; + if (dls->n_download_failures < smartlist_len(schedule)) + increment = *(int *)smartlist_get(schedule, dls->n_download_failures); else if (dls->n_download_failures == IMPOSSIBLE_TO_DOWNLOAD) increment = INT_MAX; else - increment = schedule[schedule_len-1]; + increment = *(int *)smartlist_get(schedule, smartlist_len(schedule) - 1); if (increment < INT_MAX) dls->next_attempt_at = now+increment; @@ -3836,14 +3805,11 @@ download_status_increment_failure(download_status_t *dls, int status_code, void download_status_reset(download_status_t *dls) { - const int *schedule; - size_t schedule_len; - - find_dl_schedule_and_len(dls, get_options()->DirPort_set, - &schedule, &schedule_len); + const smartlist_t *schedule = find_dl_schedule_and_len( + dls, get_options()->DirPort_set); dls->n_download_failures = 0; - dls->next_attempt_at = time(NULL) + schedule[0]; + dls->next_attempt_at = time(NULL) + *(int *)smartlist_get(schedule, 0); } /** Return the number of failures on <b>dls</b> since the last success (if @@ -3888,7 +3854,8 @@ dir_routerdesc_download_failed(smartlist_t *failed, int status_code, } else { dls = router_get_dl_status_by_descriptor_digest(digest); } - if (!dls || dls->n_download_failures >= MAX_ROUTERDESC_DOWNLOAD_FAILURES) + if (!dls || dls->n_download_failures >= + get_options()->TestingDescriptorMaxDownloadTries) continue; download_status_increment_failure(dls, status_code, cp, server, now); } SMARTLIST_FOREACH_END(cp); @@ -3919,7 +3886,8 @@ dir_microdesc_download_failed(smartlist_t *failed, if (!rs) continue; dls = &rs->dl_status; - if (dls->n_download_failures >= MAX_MICRODESC_DOWNLOAD_FAILURES) + if (dls->n_download_failures >= + get_options()->TestingMicrodescMaxDownloadTries) continue; { char buf[BASE64_DIGEST256_LEN+1]; diff --git a/src/or/dirserv.c b/src/or/dirserv.c index 3e46153a55..3243ac47c4 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -1907,7 +1907,7 @@ router_counts_toward_thresholds(const node_t *node, time_t now, * the Weighted Fractional Uptime history, and use them to set thresholds for * the Stable, Fast, and Guard flags. Update the fields stable_uptime, * stable_mtbf, enough_mtbf_info, guard_wfu, guard_tk, fast_bandwidth, - * guard_bandwidh_including_exits, guard_bandwidth_excluding_exits, + * guard_bandwidth_including_exits, and guard_bandwidth_excluding_exits. * * Also, set the is_exit flag of each router appropriately. */ static void @@ -2082,7 +2082,7 @@ static digestmap_t *mbw_cache = NULL; /** Store a measured bandwidth cache entry when reading the measured * bandwidths file. */ -void +STATIC void dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line, time_t as_of) { @@ -2112,7 +2112,7 @@ dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line, } /** Clear and free the measured bandwidth cache */ -void +STATIC void dirserv_clear_measured_bw_cache(void) { if (mbw_cache) { @@ -2123,7 +2123,7 @@ dirserv_clear_measured_bw_cache(void) } /** Scan the measured bandwidth cache and remove expired entries */ -void +STATIC void dirserv_expire_measured_bw_cache(time_t now) { @@ -2145,7 +2145,7 @@ dirserv_expire_measured_bw_cache(time_t now) } /** Get the current size of the measured bandwidth cache */ -int +STATIC int dirserv_get_measured_bw_cache_size(void) { if (mbw_cache) return digestmap_size(mbw_cache); @@ -2155,7 +2155,7 @@ dirserv_get_measured_bw_cache_size(void) /** Query the cache by identity digest, return value indicates whether * we found it. The bw_out and as_of_out pointers receive the cached * bandwidth value and the time it was cached if not NULL. */ -int +STATIC int dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_kb_out, time_t *as_of_out) { @@ -2176,7 +2176,7 @@ dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_kb_out, } /** Predicate wrapper for dirserv_query_measured_bw_cache() */ -int +STATIC int dirserv_has_measured_bw(const char *node_id) { return dirserv_query_measured_bw_cache_kb(node_id, NULL, NULL); @@ -2754,7 +2754,7 @@ clear_status_flags_on_sybil(routerstatus_t *rs) * into a measured_bw_line_t output structure. Returns -1 on failure * or 0 on success. */ -int +STATIC int measured_bw_line_parse(measured_bw_line_t *out, const char *orig_line) { char *line = tor_strdup(orig_line); @@ -2835,7 +2835,7 @@ measured_bw_line_parse(measured_bw_line_t *out, const char *orig_line) * of bandwidth statuses. Returns true if a line is found, * false otherwise. */ -int +STATIC int measured_bw_line_apply(measured_bw_line_t *parsed_line, smartlist_t *routerstatuses) { @@ -3093,7 +3093,8 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, else last_consensus_interval = options->TestingV3AuthInitialVotingInterval; v3_out->valid_after = - dirvote_get_start_of_next_interval(now, (int)last_consensus_interval); + dirvote_get_start_of_next_interval(now, (int)last_consensus_interval, + options->TestingV3AuthVotingStartOffset); format_iso_time(tbuf, v3_out->valid_after); log_notice(LD_DIR,"Choosing valid-after time in vote as %s: " "consensus_set=%d, last_interval=%d", @@ -3167,7 +3168,7 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, /** For v2 authoritative directories only: Replace the contents of * <b>the_v2_networkstatus</b> with a newly generated network status * object. */ -cached_dir_t * +STATIC cached_dir_t * generate_v2_networkstatus_opinion(void) { cached_dir_t *r = NULL; diff --git a/src/or/dirserv.h b/src/or/dirserv.h index f9d36d760f..7221fc9957 100644 --- a/src/or/dirserv.h +++ b/src/or/dirserv.h @@ -12,6 +12,8 @@ #ifndef TOR_DIRSERV_H #define TOR_DIRSERV_H +#include "testsupport.h" + /** What fraction (1 over this number) of the relay ID space do we * (as a directory authority) launch connections to at each reachability * test? */ @@ -119,20 +121,21 @@ cached_dir_t *new_cached_dir(char *s, time_t published); /* Put the MAX_MEASUREMENT_AGE #define here so unit tests can see it */ #define MAX_MEASUREMENT_AGE (3*24*60*60) /* 3 days */ -int measured_bw_line_parse(measured_bw_line_t *out, const char *line); +STATIC int measured_bw_line_parse(measured_bw_line_t *out, const char *line); -int measured_bw_line_apply(measured_bw_line_t *parsed_line, +STATIC int measured_bw_line_apply(measured_bw_line_t *parsed_line, smartlist_t *routerstatuses); -void dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line, +STATIC void dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line, time_t as_of); -void dirserv_clear_measured_bw_cache(void); -void dirserv_expire_measured_bw_cache(time_t now); -int dirserv_get_measured_bw_cache_size(void); -int dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_out, - time_t *as_of_out); -int dirserv_has_measured_bw(const char *node_id); -cached_dir_t *generate_v2_networkstatus_opinion(void); +STATIC void dirserv_clear_measured_bw_cache(void); +STATIC void dirserv_expire_measured_bw_cache(time_t now); +STATIC int dirserv_get_measured_bw_cache_size(void); +STATIC int dirserv_query_measured_bw_cache_kb(const char *node_id, + long *bw_out, + time_t *as_of_out); +STATIC int dirserv_has_measured_bw(const char *node_id); +STATIC cached_dir_t *generate_v2_networkstatus_opinion(void); #endif int dirserv_read_measured_bandwidths(const char *from_file, diff --git a/src/or/dirvote.c b/src/or/dirvote.c index e0af66e22d..12ceba8549 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -60,7 +60,7 @@ static char *make_consensus_method_list(int low, int high, const char *sep); /** Return a new string containing the string representation of the vote in * <b>v3_ns</b>, signed with our v3 signing key <b>private_signing_key</b>. * For v3 authorities. */ -char * +STATIC char * format_networkstatus_vote(crypto_pk_t *private_signing_key, networkstatus_t *v3_ns) { @@ -587,7 +587,7 @@ compute_consensus_versions_list(smartlist_t *lst, int n_versioning) /** Helper: given a list of valid networkstatus_t, return a new string * containing the contents of the consensus network parameter set. */ -/* private */ char * +STATIC char * dirvote_compute_params(smartlist_t *votes, int method, int total_authorities) { int i; @@ -2533,12 +2533,13 @@ dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out) timing_out->dist_delay = options->V3AuthDistDelay; } -/** Return the start of the next interval of size <b>interval</b> (in seconds) - * after <b>now</b>. Midnight always starts a fresh interval, and if the last - * interval of a day would be truncated to less than half its size, it is - * rolled into the previous interval. */ +/** Return the start of the next interval of size <b>interval</b> (in + * seconds) after <b>now</b>, plus <b>offset</b>. Midnight always + * starts a fresh interval, and if the last interval of a day would be + * truncated to less than half its size, it is rolled into the + * previous interval. */ time_t -dirvote_get_start_of_next_interval(time_t now, int interval) +dirvote_get_start_of_next_interval(time_t now, int interval, int offset) { struct tm tm; time_t midnight_today=0; @@ -2566,6 +2567,10 @@ dirvote_get_start_of_next_interval(time_t now, int interval) if (next + interval/2 > midnight_tomorrow) next = midnight_tomorrow; + next += offset; + if (next - interval > now) + next -= interval; + return next; } @@ -2629,8 +2634,10 @@ dirvote_recalculate_timing(const or_options_t *options, time_t now) vote_delay = dist_delay = interval / 4; start = voting_schedule.interval_starts = - dirvote_get_start_of_next_interval(now,interval); - end = dirvote_get_start_of_next_interval(start+1, interval); + dirvote_get_start_of_next_interval(now,interval, + options->TestingV3AuthVotingStartOffset); + end = dirvote_get_start_of_next_interval(start+1, interval, + options->TestingV3AuthVotingStartOffset); tor_assert(end > start); diff --git a/src/or/dirvote.h b/src/or/dirvote.h index b236452122..3a4951a95f 100644 --- a/src/or/dirvote.h +++ b/src/or/dirvote.h @@ -12,10 +12,12 @@ #ifndef TOR_DIRVOTE_H #define TOR_DIRVOTE_H +#include "testsupport.h" + /** Lowest allowable value for VoteSeconds. */ -#define MIN_VOTE_SECONDS 20 +#define MIN_VOTE_SECONDS 2 /** Lowest allowable value for DistSeconds. */ -#define MIN_DIST_SECONDS 20 +#define MIN_DIST_SECONDS 2 /** Smallest allowable voting interval. */ #define MIN_VOTE_INTERVAL 300 @@ -86,7 +88,9 @@ authority_cert_t *authority_cert_dup(authority_cert_t *cert); /* vote scheduling */ void dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out); -time_t dirvote_get_start_of_next_interval(time_t now, int interval); +time_t dirvote_get_start_of_next_interval(time_t now, + int interval, + int offset); void dirvote_recalculate_timing(const or_options_t *options, time_t now); void dirvote_act(const or_options_t *options, time_t now); @@ -134,9 +138,9 @@ document_signature_t *voter_get_sig_by_algorithm( digest_algorithm_t alg); #ifdef DIRVOTE_PRIVATE -char *format_networkstatus_vote(crypto_pk_t *private_key, +STATIC char *format_networkstatus_vote(crypto_pk_t *private_key, networkstatus_t *v3_ns); -char *dirvote_compute_params(smartlist_t *votes, int method, +STATIC char *dirvote_compute_params(smartlist_t *votes, int method, int total_authorities); #endif diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c index f1af75aefb..cadc70ec7a 100644 --- a/src/or/entrynodes.c +++ b/src/or/entrynodes.c @@ -54,6 +54,10 @@ typedef struct { /** When should we next try to fetch a descriptor for this bridge? */ download_status_t fetch_status; + + /** A smartlist of k=v values to be passed to the SOCKS proxy, if + transports are used for this bridge. */ + smartlist_t *socks_args; } bridge_info_t; /** A list of our chosen entry guards. */ @@ -1583,6 +1587,11 @@ bridge_free(bridge_info_t *bridge) return; tor_free(bridge->transport_name); + if (bridge->socks_args) { + SMARTLIST_FOREACH(bridge->socks_args, char*, s, tor_free(s)); + smartlist_free(bridge->socks_args); + } + tor_free(bridge); } @@ -1761,30 +1770,51 @@ bridge_resolve_conflicts(const tor_addr_t *addr, uint16_t port, } SMARTLIST_FOREACH_END(bridge); } -/** Remember a new bridge at <b>addr</b>:<b>port</b>. If <b>digest</b> - * is set, it tells us the identity key too. If we already had the - * bridge in our list, unmark it, and don't actually add anything new. - * If <b>transport_name</b> is non-NULL - the bridge is associated with a - * pluggable transport - we assign the transport to the bridge. */ +/** Register the bridge information in <b>bridge_line</b> to the + * bridge subsystem. Steals reference of <b>bridge_line</b>. */ void -bridge_add_from_config(const tor_addr_t *addr, uint16_t port, - const char *digest, const char *transport_name) +bridge_add_from_config(bridge_line_t *bridge_line) { bridge_info_t *b; - bridge_resolve_conflicts(addr, port, digest, transport_name); + { /* Log the bridge we are about to register: */ + log_debug(LD_GENERAL, "Registering bridge at %s (transport: %s) (%s)", + fmt_addrport(&bridge_line->addr, bridge_line->port), + bridge_line->transport_name ? + bridge_line->transport_name : "no transport", + tor_digest_is_zero(bridge_line->digest) ? + "no key listed" : hex_str(bridge_line->digest, DIGEST_LEN)); + + if (bridge_line->socks_args) { /* print socks arguments */ + int i = 0; + + tor_assert(smartlist_len(bridge_line->socks_args) > 0); + + log_debug(LD_GENERAL, "Bridge uses %d SOCKS arguments:", + smartlist_len(bridge_line->socks_args)); + SMARTLIST_FOREACH(bridge_line->socks_args, const char *, arg, + log_debug(LD_CONFIG, "%d: %s", ++i, arg)); + } + } + + bridge_resolve_conflicts(&bridge_line->addr, + bridge_line->port, + bridge_line->digest, + bridge_line->transport_name); b = tor_malloc_zero(sizeof(bridge_info_t)); - tor_addr_copy(&b->addr, addr); - b->port = port; - if (digest) - memcpy(b->identity, digest, DIGEST_LEN); - if (transport_name) - b->transport_name = tor_strdup(transport_name); + tor_addr_copy(&b->addr, &bridge_line->addr); + b->port = bridge_line->port; + memcpy(b->identity, bridge_line->digest, DIGEST_LEN); + if (bridge_line->transport_name) + b->transport_name = bridge_line->transport_name; b->fetch_status.schedule = DL_SCHED_BRIDGE; + b->socks_args = bridge_line->socks_args; if (!bridge_list) bridge_list = smartlist_new(); + tor_free(bridge_line); /* Deallocate bridge_line now. */ + smartlist_add(bridge_list, b); } @@ -1845,7 +1875,7 @@ find_transport_name_by_bridge_addrport(const tor_addr_t *addr, uint16_t port) * transport, but the transport could not be found. */ int -find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port, +get_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port, const transport_t **transport) { *transport = NULL; @@ -1872,6 +1902,17 @@ find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port, return 0; } +/** Return a smartlist containing all the SOCKS arguments that we + * should pass to the SOCKS proxy. */ +const smartlist_t * +get_socks_args_by_bridge_addrport(const tor_addr_t *addr, uint16_t port) +{ + bridge_info_t *bridge = get_configured_bridge_by_addr_port_digest(addr, + port, + NULL); + return bridge ? bridge->socks_args : NULL; +} + /** We need to ask <b>bridge</b> for its server descriptor. */ static void launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge) diff --git a/src/or/entrynodes.h b/src/or/entrynodes.h index 52b8dc00e4..533f2027aa 100644 --- a/src/or/entrynodes.h +++ b/src/or/entrynodes.h @@ -97,9 +97,8 @@ int routerinfo_is_a_configured_bridge(const routerinfo_t *ri); int node_is_a_configured_bridge(const node_t *node); void learned_router_identity(const tor_addr_t *addr, uint16_t port, const char *digest); -void bridge_add_from_config(const tor_addr_t *addr, uint16_t port, - const char *digest, - const char *transport_name); +struct bridge_line_t; +void bridge_add_from_config(struct bridge_line_t *bridge_line); void retry_bridge_descriptor_fetch_directly(const char *digest); void fetch_bridge_descriptors(const or_options_t *options, time_t now); void learned_bridge_descriptor(routerinfo_t *ri, int from_cache); @@ -109,13 +108,17 @@ int entries_known_but_down(const or_options_t *options); void entries_retry_all(const or_options_t *options); int any_bridge_supports_microdescriptors(void); +const smartlist_t *get_socks_args_by_bridge_addrport(const tor_addr_t *addr, + uint16_t port); + +int any_bridges_dont_support_microdescriptors(void); void entry_guards_free_all(void); const char *find_transport_name_by_bridge_addrport(const tor_addr_t *addr, uint16_t port); struct transport_t; -int find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port, +int get_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port, const struct transport_t **transport); int validate_pluggable_transports_config(void); diff --git a/src/or/geoip.c b/src/or/geoip.c index e2e98e8ec4..020075f80f 100644 --- a/src/or/geoip.c +++ b/src/or/geoip.c @@ -120,7 +120,7 @@ geoip_add_entry(const tor_addr_t *low, const tor_addr_t *high, /** Add an entry to the GeoIP table indicated by <b>family</b>, * parsing it from <b>line</b>. The format is as for geoip_load_file(). */ -/*private*/ int +STATIC int geoip_parse_entry(const char *line, sa_family_t family) { tor_addr_t low_addr, high_addr; @@ -363,7 +363,7 @@ geoip_load_file(sa_family_t family, const char *filename) * be less than geoip_get_n_countries(). To decode it, call * geoip_get_country_name(). */ -int +STATIC int geoip_get_country_by_ipv4(uint32_t ipaddr) { geoip_ipv4_entry_t *ent; @@ -379,7 +379,7 @@ geoip_get_country_by_ipv4(uint32_t ipaddr) * 0 for the 'unknown country'. The return value will always be less than * geoip_get_n_countries(). To decode it, call geoip_get_country_name(). */ -int +STATIC int geoip_get_country_by_ipv6(const struct in6_addr *addr) { geoip_ipv6_entry_t *ent; @@ -1132,7 +1132,7 @@ geoip_format_dirreq_stats(time_t now) time_t geoip_dirreq_stats_write(time_t now) { - char *statsdir = NULL, *filename = NULL, *str = NULL; + char *str = NULL; if (!start_of_dirreq_stats_interval) return 0; /* Not initialized. */ @@ -1146,21 +1146,13 @@ geoip_dirreq_stats_write(time_t now) str = geoip_format_dirreq_stats(now); /* Write dirreq-stats string to disk. */ - statsdir = get_datadir_fname("stats"); - if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) { - log_warn(LD_HIST, "Unable to create stats/ directory!"); - goto done; + if (!check_or_create_data_subdir("stats")) { + write_to_data_subdir("stats", "dirreq-stats", str, "dirreq statistics"); + /* Reset measurement interval start. */ + geoip_reset_dirreq_stats(now); } - filename = get_datadir_fname2("stats", "dirreq-stats"); - if (write_str_to_file(filename, str, 0) < 0) - log_warn(LD_HIST, "Unable to write dirreq statistics to disk!"); - - /* Reset measurement interval start. */ - geoip_reset_dirreq_stats(now); done: - tor_free(statsdir); - tor_free(filename); tor_free(str); return start_of_dirreq_stats_interval + WRITE_STATS_INTERVAL; } @@ -1297,7 +1289,7 @@ format_bridge_stats_controller(time_t now) time_t geoip_bridge_stats_write(time_t now) { - char *filename = NULL, *val = NULL, *statsdir = NULL; + char *val = NULL; /* Check if 24 hours have passed since starting measurements. */ if (now < start_of_bridge_stats_interval + WRITE_STATS_INTERVAL) @@ -1317,24 +1309,20 @@ geoip_bridge_stats_write(time_t now) start_of_bridge_stats_interval = now; /* Write it to disk. */ - statsdir = get_datadir_fname("stats"); - if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) - goto done; - filename = get_datadir_fname2("stats", "bridge-stats"); - - write_str_to_file(filename, bridge_stats_extrainfo, 0); - - /* Tell the controller, "hey, there are clients!" */ - { - char *controller_str = format_bridge_stats_controller(now); - if (controller_str) - control_event_clients_seen(controller_str); - tor_free(controller_str); + if (!check_or_create_data_subdir("stats")) { + write_to_data_subdir("stats", "bridge-stats", + bridge_stats_extrainfo, "bridge statistics"); + + /* Tell the controller, "hey, there are clients!" */ + { + char *controller_str = format_bridge_stats_controller(now); + if (controller_str) + control_event_clients_seen(controller_str); + tor_free(controller_str); + } } - done: - tor_free(filename); - tor_free(statsdir); + done: return start_of_bridge_stats_interval + WRITE_STATS_INTERVAL; } @@ -1436,7 +1424,7 @@ geoip_format_entry_stats(time_t now) time_t geoip_entry_stats_write(time_t now) { - char *statsdir = NULL, *filename = NULL, *str = NULL; + char *str = NULL; if (!start_of_entry_stats_interval) return 0; /* Not initialized. */ @@ -1450,21 +1438,14 @@ geoip_entry_stats_write(time_t now) str = geoip_format_entry_stats(now); /* Write entry-stats string to disk. */ - statsdir = get_datadir_fname("stats"); - if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) { - log_warn(LD_HIST, "Unable to create stats/ directory!"); - goto done; - } - filename = get_datadir_fname2("stats", "entry-stats"); - if (write_str_to_file(filename, str, 0) < 0) - log_warn(LD_HIST, "Unable to write entry statistics to disk!"); + if (!check_or_create_data_subdir("stats")) { + write_to_data_subdir("stats", "entry-stats", str, "entry statistics"); - /* Reset measurement interval start. */ - geoip_reset_entry_stats(now); + /* Reset measurement interval start. */ + geoip_reset_entry_stats(now); + } done: - tor_free(statsdir); - tor_free(filename); tor_free(str); return start_of_entry_stats_interval + WRITE_STATS_INTERVAL; } diff --git a/src/or/geoip.h b/src/or/geoip.h index ebefee5f4e..d67007298e 100644 --- a/src/or/geoip.h +++ b/src/or/geoip.h @@ -12,10 +12,12 @@ #ifndef TOR_GEOIP_H #define TOR_GEOIP_H +#include "testsupport.h" + #ifdef GEOIP_PRIVATE -int geoip_parse_entry(const char *line, sa_family_t family); -int geoip_get_country_by_ipv4(uint32_t ipaddr); -int geoip_get_country_by_ipv6(const struct in6_addr *addr); +STATIC int geoip_parse_entry(const char *line, sa_family_t family); +STATIC int geoip_get_country_by_ipv4(uint32_t ipaddr); +STATIC int geoip_get_country_by_ipv6(const struct in6_addr *addr); #endif int should_record_bridge_info(const or_options_t *options); int geoip_load_file(sa_family_t family, const char *filename); diff --git a/src/or/hibernate.c b/src/or/hibernate.c index a412571331..22c0f253da 100644 --- a/src/or/hibernate.c +++ b/src/or/hibernate.c @@ -1010,6 +1010,7 @@ getinfo_helper_accounting(control_connection_t *conn, return 0; } +#ifdef TOR_UNIT_TESTS /** * Manually change the hibernation state. Private; used only by the unit * tests. @@ -1019,4 +1020,5 @@ hibernate_set_state_for_testing_(hibernate_state_t newstate) { hibernate_state = newstate; } +#endif diff --git a/src/or/hibernate.h b/src/or/hibernate.h index d2d6989e10..18832fbc6c 100644 --- a/src/or/hibernate.h +++ b/src/or/hibernate.h @@ -45,8 +45,10 @@ typedef enum { HIBERNATE_STATE_INITIAL=5 } hibernate_state_t; +#ifdef TOR_UNIT_TESTS void hibernate_set_state_for_testing_(hibernate_state_t newstate); #endif +#endif #endif diff --git a/src/or/include.am b/src/or/include.am index 65dbeff53e..f5002e6986 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -1,5 +1,13 @@ bin_PROGRAMS+= src/or/tor -noinst_LIBRARIES+= src/or/libtor.a +noinst_LIBRARIES += \ + src/or/libtor.a +if UNITTESTS_ENABLED +noinst_LIBRARIES += \ + src/or/libtor-testing.a +endif +if COVERAGE_ENABLED +noinst_PROGRAMS+= src/or/tor-cov +endif if BUILD_NT_SERVICES tor_platform_source=src/or/ntmain.c @@ -21,7 +29,7 @@ else onion_ntor_source= endif -src_or_libtor_a_SOURCES = \ +LIBTOR_A_SOURCES = \ src/or/addressmap.c \ src/or/buffers.c \ src/or/channel.c \ @@ -77,6 +85,9 @@ src_or_libtor_a_SOURCES = \ $(onion_ntor_source) \ src/or/config_codedigest.c +src_or_libtor_a_SOURCES = $(LIBTOR_A_SOURCES) +src_or_libtor_testing_a_SOURCES = $(LIBTOR_A_SOURCES) + #libtor_a_LIBADD = ../common/libor.a ../common/libor-crypto.a \ # ../common/libor-event.a @@ -90,6 +101,9 @@ AM_CPPFLAGS += -DSHARE_DATADIR="\"$(datadir)\"" \ -DLOCALSTATEDIR="\"$(localstatedir)\"" \ -DBINDIR="\"$(bindir)\"" +src_or_libtor_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS) +src_or_libtor_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) + # -L flags need to go in LDFLAGS. -l flags need to go in LDADD. # This seems to matter nowhere but on windows, but I assure you that it # matters a lot there, and is quite hard to debug if you forget to do it. @@ -102,6 +116,18 @@ src_or_tor_LDADD = src/or/libtor.a src/common/libor.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ +if COVERAGE_ENABLED +src_or_tor_cov_SOURCES = src/or/tor_main.c +src_or_tor_cov_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS) +src_or_tor_cov_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) +src_or_tor_cov_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@ +src_or_tor_cov_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \ + src/common/libor-crypto-testing.a $(LIBDONNA) \ + src/common/libor-event-testing.a \ + @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \ + @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ +endif + ORHEADERS = \ src/or/addressmap.h \ src/or/buffers.h \ diff --git a/src/or/main.c b/src/or/main.c index bd23141b97..d1728250a4 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -10,7 +10,6 @@ * connections, implements main loop, and drives scheduled events. **/ -#define MAIN_PRIVATE #include "or.h" #include "addressmap.h" #include "buffers.h" @@ -57,6 +56,7 @@ #include <openssl/crypto.h> #endif #include "memarea.h" +#include "../common/sandbox.h" #ifdef HAVE_EVENT2_EVENT_H #include <event2/event.h> @@ -155,8 +155,6 @@ int can_complete_circuit=0; /** How often do we 'forgive' undownloadable router descriptors and attempt * to download them again? */ #define DESCRIPTOR_FAILURE_RESET_INTERVAL (60*60) -/** How long do we let a directory connection stall before expiring it? */ -#define DIR_CONN_MAX_STALL (5*60) /** Decides our behavior when no logs are configured/before any * logs have been configured. For 0, we log notice to stdout as normal. @@ -1028,9 +1026,11 @@ run_connection_housekeeping(int i, time_t now) * if a server or received if a client) for 5 min */ if (conn->type == CONN_TYPE_DIR && ((DIR_CONN_IS_SERVER(conn) && - conn->timestamp_lastwritten + DIR_CONN_MAX_STALL < now) || + conn->timestamp_lastwritten + + options->TestingDirConnectionMaxStall < now) || (!DIR_CONN_IS_SERVER(conn) && - conn->timestamp_lastread + DIR_CONN_MAX_STALL < now))) { + conn->timestamp_lastread + + options->TestingDirConnectionMaxStall < now))) { log_info(LD_DIR,"Expiring wedged directory conn (fd %d, purpose %d)", (int)conn->s, conn->purpose); /* This check is temporary; it's to let us know whether we should consider @@ -1153,6 +1153,7 @@ run_scheduled_events(time_t now) static time_t time_to_check_v3_certificate = 0; static time_t time_to_check_listeners = 0; static time_t time_to_check_descriptor = 0; + static time_t time_to_download_networkstatus = 0; static time_t time_to_shrink_memory = 0; static time_t time_to_try_getting_descriptors = 0; static time_t time_to_reset_descriptor_failures = 0; @@ -1442,10 +1443,18 @@ run_scheduled_events(time_t now) networkstatus_v2_list_clean(now); /* Remove dead routers. */ routerlist_remove_old_routers(); + } - /* Also, once per minute, check whether we want to download any - * networkstatus documents. - */ + /* 2c. Every minute (or every second if TestingTorNetwork), check + * whether we want to download any networkstatus documents. */ + +/* How often do we check whether we should download network status + * documents? */ +#define networkstatus_dl_check_interval(o) ((o)->TestingTorNetwork ? 1 : 60) + + if (time_to_download_networkstatus < now && !options->DisableNetwork) { + time_to_download_networkstatus = + now + networkstatus_dl_check_interval(options); update_networkstatus_downloads(now); } @@ -1865,7 +1874,7 @@ do_hup(void) } /** Tor main loop. */ -/* static */ int +int do_main_loop(void) { int loop_result; @@ -2292,7 +2301,7 @@ handle_signals(int is_parent) /** Main entry point for the Tor command-line client. */ -/* static */ int +int tor_init(int argc, char *argv[]) { char buf[256]; @@ -2558,7 +2567,7 @@ tor_cleanup(void) } /** Read/create keys as needed, and echo our fingerprint to stdout. */ -/* static */ int +static int do_list_fingerprint(void) { char buf[FINGERPRINT_LEN+1]; @@ -2588,7 +2597,7 @@ do_list_fingerprint(void) /** Entry point for password hashing: take the desired password from * the command line, and print its salted hash to stdout. **/ -/* static */ void +static void do_hash_password(void) { @@ -2695,6 +2704,14 @@ tor_main(int argc, char *argv[]) #endif if (tor_init(argc, argv)<0) return -1; + + if (get_options()->Sandbox) { + if (tor_global_sandbox()) { + log_err(LD_BUG,"Failed to create syscall sandbox filter"); + return -1; + } + } + switch (get_options()->command) { case CMD_RUN_TOR: #ifdef NT_SERVICE diff --git a/src/or/main.h b/src/or/main.h index 338449b6a6..85621a32bf 100644 --- a/src/or/main.h +++ b/src/or/main.h @@ -66,12 +66,8 @@ void tor_free_all(int postfork); int tor_main(int argc, char *argv[]); -#ifdef MAIN_PRIVATE int do_main_loop(void); -int do_list_fingerprint(void); -void do_hash_password(void); int tor_init(int argc, char **argv); -#endif #endif diff --git a/src/or/microdesc.c b/src/or/microdesc.c index b93bd83af5..8c763c6729 100644 --- a/src/or/microdesc.c +++ b/src/or/microdesc.c @@ -139,7 +139,7 @@ get_microdesc_cache(void) * ending at <b>eos</b>, and store them in <b>cache</b>. If <b>no_save</b>, * mark them as non-writable to disk. If <b>where</b> is SAVED_IN_CACHE, * leave their bodies as pointers to the mmap'd cache. If where is - * <b>SAVED_NOWHERE</b>, do not allow annotations. If listed_at is positive, + * <b>SAVED_NOWHERE</b>, do not allow annotations. If listed_at is not -1, * set the last_listed field of every microdesc to listed_at. If * requested_digests is non-null, then it contains a list of digests we mean * to allow, so we should reject any non-requested microdesc with a different @@ -159,7 +159,7 @@ microdescs_add_to_cache(microdesc_cache_t *cache, descriptors = microdescs_parse_from_string(s, eos, allow_annotations, copy_body); - if (listed_at > 0) { + if (listed_at != (time_t)-1) { SMARTLIST_FOREACH(descriptors, microdesc_t *, md, md->last_listed = listed_at); } @@ -687,7 +687,7 @@ microdesc_list_missing_digest256(networkstatus_t *ns, microdesc_cache_t *cache, continue; if (downloadable_only && !download_status_is_ready(&rs->dl_status, now, - MAX_MICRODESC_DOWNLOAD_FAILURES)) + get_options()->TestingMicrodescMaxDownloadTries)) continue; if (skip && digestmap_get(skip, rs->descriptor_digest)) continue; diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index 8715841544..3f995a9f6c 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -1203,8 +1203,6 @@ we_want_to_fetch_flavor(const or_options_t *options, int flavor) return flavor == usable_consensus_flavor(); } -/** How many times will we try to fetch a consensus before we give up? */ -#define CONSENSUS_NETWORKSTATUS_MAX_DL_TRIES 8 /** How long will we hang onto a possibly live consensus for which we're * fetching certs before we check whether there is a better one? */ #define DELAY_WHILE_FETCHING_CERTS (20*60) @@ -1238,7 +1236,7 @@ update_consensus_networkstatus_downloads(time_t now) resource = networkstatus_get_flavor_name(i); if (!download_status_is_ready(&consensus_dl_status[i], now, - CONSENSUS_NETWORKSTATUS_MAX_DL_TRIES)) + options->TestingConsensusMaxDownloadTries)) continue; /* We failed downloading a consensus too recently. */ if (connection_dir_get_by_purpose_and_resource( DIR_PURPOSE_FETCH_CONSENSUS, resource)) diff --git a/src/or/ntmain.c b/src/or/ntmain.c index 8b67b86822..2fa074d0be 100644 --- a/src/or/ntmain.c +++ b/src/or/ntmain.c @@ -3,7 +3,6 @@ * Copyright (c) 2007-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ -#define MAIN_PRIVATE #include "or.h" #include "config.h" #include "main.h" diff --git a/src/or/or.h b/src/or/or.h index 3dc96b9a9d..a6a4bcc1ba 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -99,6 +99,7 @@ #include "ht.h" #include "replaycache.h" #include "crypto_curve25519.h" +#include "tor_queue.h" /* These signals are defined to help handle_control_signal work. */ @@ -238,7 +239,9 @@ typedef enum { #define PROXY_SOCKS5 3 /* !!!! If there is ever a PROXY_* type over 2, we must grow the proxy_type * field in or_connection_t */ -/* pluggable transports proxy type */ + +/* Pluggable transport proxy type. Don't use this in or_connection_t, + * instead use the actual underlying proxy type (see above). */ #define PROXY_PLUGGABLE 4 /* Proxy client handshake states */ @@ -823,9 +826,15 @@ typedef enum { /** Maximum number of queued cells on a circuit for which we are the * midpoint before we give up and kill it. This must be >= circwindow * to avoid killing innocent circuits, and >= circwindow*2 to give - * leaky-pipe a chance for being useful someday. + * leaky-pipe a chance of working someday. The ORCIRC_MAX_MIDDLE_KILL_THRESH + * ratio controls the margin of error between emitting a warning and + * killing the circuit. + */ +#define ORCIRC_MAX_MIDDLE_CELLS (CIRCWINDOW_START_MAX*2) +/** Ratio of hard (circuit kill) to soft (warning) thresholds for the + * ORCIRC_MAX_MIDDLE_CELLS tests. */ -#define ORCIRC_MAX_MIDDLE_CELLS (21*(CIRCWINDOW_START_MAX)/10) +#define ORCIRC_MAX_MIDDLE_KILL_THRESH (1.1f) /* Cell commands. These values are defined in tor-spec.txt. */ #define CELL_PADDING 0 @@ -1075,7 +1084,8 @@ typedef struct var_cell_t { /** A cell as packed for writing to the network. */ typedef struct packed_cell_t { - struct packed_cell_t *next; /**< Next cell queued on this circuit. */ + /** Next cell queued on this circuit. */ + TOR_SIMPLEQ_ENTRY(packed_cell_t) next; char body[CELL_MAX_NETWORK_SIZE]; /**< Cell as packed for network. */ } packed_cell_t; @@ -1097,8 +1107,8 @@ typedef struct insertion_time_queue_t { /** A queue of cells on a circuit, waiting to be added to the * or_connection_t's outbuf. */ typedef struct cell_queue_t { - packed_cell_t *head; /**< The first cell, or NULL if the queue is empty. */ - packed_cell_t *tail; /**< The last cell, or NULL if the queue is empty. */ + /** Linked list of packed_cell_t*/ + TOR_SIMPLEQ_HEAD(cell_simpleq, packed_cell_t) head; int n; /**< The number of cells in the queue. */ insertion_time_queue_t *insertion_times; /**< Insertion times of cells. */ } cell_queue_t; @@ -2286,14 +2296,6 @@ typedef struct node_t { } node_t; -/** How many times will we try to download a router's descriptor before giving - * up? */ -#define MAX_ROUTERDESC_DOWNLOAD_FAILURES 8 - -/** How many times will we try to download a microdescriptor before giving - * up? */ -#define MAX_MICRODESC_DOWNLOAD_FAILURES 8 - /** Contents of a v2 (non-consensus, non-vote) network status object. */ typedef struct networkstatus_v2_t { /** When did we receive the network-status document? */ @@ -2503,10 +2505,6 @@ typedef struct desc_store_t { * filename for a temporary file when rebuilding the store, and .new to this * filename for the journal. */ const char *fname_base; - /** Alternative (obsolete) value for fname_base: if the file named by - * fname_base isn't present, we read from here instead, but we never write - * here. */ - const char *fname_alt_base; /** Human-readable description of what this store contains. */ const char *description; @@ -2796,6 +2794,13 @@ typedef struct circuit_t { * allowing n_streams to add any more cells. (OR circuit only.) */ unsigned int streams_blocked_on_p_chan : 1; + /** True iff we have queued a delete backwards on this circuit, but not put + * it on the output buffer. */ + unsigned int p_delete_pending : 1; + /** True iff we have queued a delete forwards on this circuit, but not put + * it on the output buffer. */ + unsigned int n_delete_pending : 1; + uint8_t state; /**< Current status of this circuit. */ uint8_t purpose; /**< Why are we creating this circuit? */ @@ -3176,6 +3181,12 @@ typedef struct or_circuit_t { * exit-ward queues of this circuit; reset every time when writing * buffer stats to disk. */ uint64_t total_cell_waiting_time; + + /** Maximum cell queue size for a middle relay; this is stored per circuit + * so append_cell_to_circuit_queue() can adjust it if it changes. If set + * to zero, it is initialized to the default value. + */ + uint32_t max_middle_cells; } or_circuit_t; /** Convert a circuit subtype to a circuit_t. */ @@ -3491,6 +3502,9 @@ typedef struct { /** List of TCP/IP addresses that transports should listen at. */ config_line_t *ServerTransportListenAddr; + /** List of options that must be passed to pluggable transports. */ + config_line_t *ServerTransportOptions; + int BridgeRelay; /**< Boolean: are we acting as a bridge relay? We make * this explicit so we can change how we behave in the * future. */ @@ -3750,6 +3764,7 @@ typedef struct { SAFELOG_SCRUB_ALL, SAFELOG_SCRUB_RELAY, SAFELOG_SCRUB_NONE } SafeLogging_; + int Sandbox; /**< Boolean: should sandboxing be enabled? */ int SafeSocks; /**< Boolean: should we outright refuse application * connections that use socks4 or socks5-with-local-dns? */ #define LOG_PROTOCOL_WARN (get_options()->ProtocolWarnings ? \ @@ -3921,6 +3936,10 @@ typedef struct { * signatures. Only altered on testing networks.*/ int TestingV3AuthInitialDistDelay; + /** Offset in seconds added to the starting time for consensus + voting. Only altered on testing networks. */ + int TestingV3AuthVotingStartOffset; + /** If an authority has been around for less than this amount of time, it * does not believe its reachability information is accurate. Only * altered on testing networks. */ @@ -3931,6 +3950,51 @@ typedef struct { * networks. */ int TestingEstimatedDescriptorPropagationTime; + /** Schedule for when servers should download things in general. Only + * altered on testing networks. */ + smartlist_t *TestingServerDownloadSchedule; + + /** Schedule for when clients should download things in general. Only + * altered on testing networks. */ + smartlist_t *TestingClientDownloadSchedule; + + /** Schedule for when servers should download consensuses. Only altered + * on testing networks. */ + smartlist_t *TestingServerConsensusDownloadSchedule; + + /** Schedule for when clients should download consensuses. Only altered + * on testing networks. */ + smartlist_t *TestingClientConsensusDownloadSchedule; + + /** Schedule for when clients should download bridge descriptors. Only + * altered on testing networks. */ + smartlist_t *TestingBridgeDownloadSchedule; + + /** When directory clients have only a few descriptors to request, they + * batch them until they have more, or until this amount of time has + * passed. Only altered on testing networks. */ + int TestingClientMaxIntervalWithoutRequest; + + /** How long do we let a directory connection stall before expiring + * it? Only altered on testing networks. */ + int TestingDirConnectionMaxStall; + + /** How many times will we try to fetch a consensus before we give + * up? Only altered on testing networks. */ + int TestingConsensusMaxDownloadTries; + + /** How many times will we try to download a router's descriptor before + * giving up? Only altered on testing networks. */ + int TestingDescriptorMaxDownloadTries; + + /** How many times will we try to download a microdescriptor before + * giving up? Only altered on testing networks. */ + int TestingMicrodescMaxDownloadTries; + + /** How many times will we try to fetch a certificate before giving + * up? Only altered on testing networks. */ + int TestingCertMaxDownloadTries; + /** If true, we take part in a testing network. Change the defaults of a * couple of other configuration options and allow to change the values * of certain configuration options. */ diff --git a/src/or/relay.c b/src/or/relay.c index 3138c5e8d1..297f0f69e0 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -58,6 +58,9 @@ static void adjust_exit_policy_from_exitpolicy_failure(origin_circuit_t *circ, entry_connection_t *conn, node_t *node, const tor_addr_t *addr); +#if 0 +static int get_max_middle_cells(void); +#endif /** Stop reading on edge connections when we have this many cells * waiting on the appropriate queue. */ @@ -966,7 +969,7 @@ remap_event_helper(entry_connection_t *conn, const tor_addr_t *new_addr) * <b>addr_out</b> to the address we're connected to, and <b>ttl_out</b> to * the ttl of that address, in seconds, and return 0. On failure, return * -1. */ -int +STATIC int connected_cell_parse(const relay_header_t *rh, const cell_t *cell, tor_addr_t *addr_out, int *ttl_out) { @@ -1494,7 +1497,8 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, if (layer_hint) { if (layer_hint->package_window + CIRCWINDOW_INCREMENT > CIRCWINDOW_START_MAX) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + static struct ratelim_t exit_warn_ratelim = RATELIM_INIT(600); + log_fn_ratelim(&exit_warn_ratelim, LOG_WARN, LD_PROTOCOL, "Unexpected sendme cell from exit relay. " "Closing circ."); return -END_CIRC_REASON_TORPROTOCOL; @@ -1506,7 +1510,8 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, } else { if (circ->package_window + CIRCWINDOW_INCREMENT > CIRCWINDOW_START_MAX) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + static struct ratelim_t client_warn_ratelim = RATELIM_INIT(600); + log_fn_ratelim(&client_warn_ratelim, LOG_WARN, LD_PROTOCOL, "Unexpected sendme cell from client. " "Closing circ (window %d).", circ->package_window); @@ -2084,7 +2089,7 @@ packed_cell_free_unchecked(packed_cell_t *cell) } /** Allocate and return a new packed_cell_t. */ -static INLINE packed_cell_t * +STATIC packed_cell_t * packed_cell_new(void) { ++total_cells_allocated; @@ -2095,6 +2100,8 @@ packed_cell_new(void) void packed_cell_free(packed_cell_t *cell) { + if (!cell) + return; packed_cell_free_unchecked(cell); } @@ -2124,7 +2131,6 @@ packed_cell_copy(const cell_t *cell, int wide_circ_ids) { packed_cell_t *c = packed_cell_new(); cell_pack(c, cell, wide_circ_ids); - c->next = NULL; return c; } @@ -2132,25 +2138,18 @@ packed_cell_copy(const cell_t *cell, int wide_circ_ids) void cell_queue_append(cell_queue_t *queue, packed_cell_t *cell) { - if (queue->tail) { - tor_assert(!queue->tail->next); - queue->tail->next = cell; - } else { - queue->head = cell; - } - queue->tail = cell; - cell->next = NULL; + TOR_SIMPLEQ_INSERT_TAIL(&queue->head, cell, next); ++queue->n; } /** Append a newly allocated copy of <b>cell</b> to the end of <b>queue</b> */ void cell_queue_append_packed_copy(cell_queue_t *queue, const cell_t *cell, - int wide_circ_ids) + int wide_circ_ids, int use_stats) { packed_cell_t *copy = packed_cell_copy(cell, wide_circ_ids); /* Remember the time when this cell was put in the queue. */ - if (get_options()->CellStatistics) { + if (get_options()->CellStatistics && use_stats) { struct timeval now; uint32_t added; insertion_time_queue_t *it_queue = queue->insertion_times; @@ -2182,18 +2181,24 @@ cell_queue_append_packed_copy(cell_queue_t *queue, const cell_t *cell, cell_queue_append(queue, copy); } +/** Initialize <b>queue</b> as an empty cell queue. */ +void +cell_queue_init(cell_queue_t *queue) +{ + memset(queue, 0, sizeof(cell_queue_t)); + TOR_SIMPLEQ_INIT(&queue->head); +} + /** Remove and free every cell in <b>queue</b>. */ void cell_queue_clear(cell_queue_t *queue) { - packed_cell_t *cell, *next; - cell = queue->head; - while (cell) { - next = cell->next; + packed_cell_t *cell; + while ((cell = TOR_SIMPLEQ_FIRST(&queue->head))) { + TOR_SIMPLEQ_REMOVE_HEAD(&queue->head, next); packed_cell_free_unchecked(cell); - cell = next; } - queue->head = queue->tail = NULL; + TOR_SIMPLEQ_INIT(&queue->head); queue->n = 0; if (queue->insertion_times) { while (queue->insertion_times->first) { @@ -2207,17 +2212,13 @@ cell_queue_clear(cell_queue_t *queue) /** Extract and return the cell at the head of <b>queue</b>; return NULL if * <b>queue</b> is empty. */ -static INLINE packed_cell_t * +STATIC packed_cell_t * cell_queue_pop(cell_queue_t *queue) { - packed_cell_t *cell = queue->head; + packed_cell_t *cell = TOR_SIMPLEQ_FIRST(&queue->head); if (!cell) return NULL; - queue->head = cell->next; - if (cell == queue->tail) { - tor_assert(!queue->head); - queue->tail = NULL; - } + TOR_SIMPLEQ_REMOVE_HEAD(&queue->head, next); --queue->n; return cell; } @@ -2368,7 +2369,7 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) { circuitmux_t *cmux = NULL; int n_flushed = 0; - cell_queue_t *queue; + cell_queue_t *queue, *destroy_queue=NULL; circuit_t *circ; or_circuit_t *or_circ; int streams_blocked; @@ -2381,7 +2382,18 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) /* Main loop: pick a circuit, send a cell, update the cmux */ while (n_flushed < max) { - circ = circuitmux_get_first_active_circuit(cmux); + circ = circuitmux_get_first_active_circuit(cmux, &destroy_queue); + if (destroy_queue) { + /* this code is duplicated from some of the logic below. Ugly! XXXX */ + tor_assert(destroy_queue->n > 0); + cell = cell_queue_pop(destroy_queue); + channel_write_packed_cell(chan, cell); + /* Update the cmux destroy counter */ + circuitmux_notify_xmit_destroy(cmux); + cell = NULL; + ++n_flushed; + continue; + } /* If it returns NULL, no cells left to send */ if (!circ) break; assert_cmux_ok_paranoid(chan); @@ -2482,6 +2494,20 @@ channel_flush_from_first_active_circuit(channel_t *chan, int max) return n_flushed; } +#if 0 +/** Indicate the current preferred cap for middle circuits; zero disables + * the cap. Right now it's just a constant, ORCIRC_MAX_MIDDLE_CELLS, but + * the logic in append_cell_to_circuit_queue() is written to be correct + * if we want to base it on a consensus param or something that might change + * in the future. + */ +static int +get_max_middle_cells(void) +{ + return ORCIRC_MAX_MIDDLE_CELLS; +} +#endif + /** Add <b>cell</b> to the queue of <b>circ</b> writing to <b>chan</b> * transmitting in <b>direction</b>. */ void @@ -2492,6 +2518,9 @@ append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, or_circuit_t *orcirc = NULL; cell_queue_t *queue; int streams_blocked; +#if 0 + uint32_t tgt_max_middle_cells, p_len, n_len, tmp, hard_max_middle_cells; +#endif if (circ->marked_for_close) return; @@ -2513,28 +2542,81 @@ append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, if ((circ->n_chan != NULL) && CIRCUIT_IS_ORCIRC(circ)) { orcirc = TO_OR_CIRCUIT(circ); if (orcirc->p_chan) { - if (queue->n + 1 >= ORCIRC_MAX_MIDDLE_CELLS) { - /* Queueing this cell would put queue over the cap */ - log_warn(LD_CIRC, - "Got a cell exceeding the cap of %u in the %s direction " - "on middle circ ID %u on chan ID " U64_FORMAT - "; killing the circuit.", - ORCIRC_MAX_MIDDLE_CELLS, - (direction == CELL_DIRECTION_OUT) ? "n" : "p", - (direction == CELL_DIRECTION_OUT) ? - circ->n_circ_id : orcirc->p_circ_id, - U64_PRINTF_ARG( + /* We are a middle circuit if we have both n_chan and p_chan */ + /* We'll need to know the current preferred maximum */ + tgt_max_middle_cells = get_max_middle_cells(); + if (tgt_max_middle_cells > 0) { + /* Do we need to initialize middle_max_cells? */ + if (orcirc->max_middle_cells == 0) { + orcirc->max_middle_cells = tgt_max_middle_cells; + } else { + if (tgt_max_middle_cells > orcirc->max_middle_cells) { + /* If we want to increase the cap, we can do so right away */ + orcirc->max_middle_cells = tgt_max_middle_cells; + } else if (tgt_max_middle_cells < orcirc->max_middle_cells) { + /* + * If we're shrinking the cap, we can't shrink past either queue; + * compare tgt_max_middle_cells rather than tgt_max_middle_cells * + * ORCIRC_MAX_MIDDLE_KILL_THRESH so the queues don't shrink enough + * to generate spurious warnings, either. + */ + n_len = circ->n_chan_cells.n; + p_len = orcirc->p_chan_cells.n; + tmp = tgt_max_middle_cells; + if (tmp < n_len) tmp = n_len; + if (tmp < p_len) tmp = p_len; + orcirc->max_middle_cells = tmp; + } + /* else no change */ + } + } else { + /* tgt_max_middle_cells == 0 indicates we should disable the cap */ + orcirc->max_middle_cells = 0; + } + + /* Now we know orcirc->max_middle_cells is set correctly */ + if (orcirc->max_middle_cells > 0) { + hard_max_middle_cells = + (uint32_t)(((double)orcirc->max_middle_cells) * + ORCIRC_MAX_MIDDLE_KILL_THRESH); + + if ((unsigned)queue->n + 1 >= hard_max_middle_cells) { + /* Queueing this cell would put queue over the kill theshold */ + log_warn(LD_CIRC, + "Got a cell exceeding the hard cap of %u in the " + "%s direction on middle circ ID %u on chan ID " + U64_FORMAT "; killing the circuit.", + hard_max_middle_cells, + (direction == CELL_DIRECTION_OUT) ? "n" : "p", (direction == CELL_DIRECTION_OUT) ? - circ->n_chan->global_identifier : - orcirc->p_chan->global_identifier)); - circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT); - return; + circ->n_circ_id : orcirc->p_circ_id, + U64_PRINTF_ARG( + (direction == CELL_DIRECTION_OUT) ? + circ->n_chan->global_identifier : + orcirc->p_chan->global_identifier)); + circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT); + return; + } else if ((unsigned)queue->n + 1 == orcirc->max_middle_cells) { + /* Only use ==, not >= for this test so we don't spam the log */ + log_warn(LD_CIRC, + "While trying to queue a cell, reached the soft cap of %u " + "in the %s direction on middle circ ID %u " + "on chan ID " U64_FORMAT ".", + orcirc->max_middle_cells, + (direction == CELL_DIRECTION_OUT) ? "n" : "p", + (direction == CELL_DIRECTION_OUT) ? + circ->n_circ_id : orcirc->p_circ_id, + U64_PRINTF_ARG( + (direction == CELL_DIRECTION_OUT) ? + circ->n_chan->global_identifier : + orcirc->p_chan->global_identifier)); + } } } } #endif - cell_queue_append_packed_copy(queue, cell, chan->wide_circ_ids); + cell_queue_append_packed_copy(queue, cell, chan->wide_circ_ids, 1); if (PREDICT_UNLIKELY(cell_queues_check_size())) { /* We ran the OOM handler */ diff --git a/src/or/relay.h b/src/or/relay.h index 1fef10a7da..e1b5e381ed 100644 --- a/src/or/relay.h +++ b/src/or/relay.h @@ -51,10 +51,11 @@ size_t packed_cell_mem_cost(void); /* For channeltls.c */ void packed_cell_free(packed_cell_t *cell); +void cell_queue_init(cell_queue_t *queue); void cell_queue_clear(cell_queue_t *queue); void cell_queue_append(cell_queue_t *queue, packed_cell_t *cell); void cell_queue_append_packed_copy(cell_queue_t *queue, const cell_t *cell, - int wide_circ_ids); + int wide_circ_ids, int use_stats); void append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, cell_t *cell, cell_direction_t direction, @@ -75,11 +76,14 @@ void circuit_clear_cell_queue(circuit_t *circ, channel_t *chan); void stream_choice_seed_weak_rng(void); -#ifdef RELAY_PRIVATE int relay_crypt(circuit_t *circ, cell_t *cell, cell_direction_t cell_direction, crypt_path_t **layer_hint, char *recognized); -int connected_cell_parse(const relay_header_t *rh, const cell_t *cell, + +#ifdef RELAY_PRIVATE +STATIC int connected_cell_parse(const relay_header_t *rh, const cell_t *cell, tor_addr_t *addr_out, int *ttl_out); +STATIC packed_cell_t *packed_cell_new(void); +STATIC packed_cell_t *cell_queue_pop(cell_queue_t *queue); #endif #endif diff --git a/src/or/rephist.c b/src/or/rephist.c index 55f321d5ff..c84322a679 100644 --- a/src/or/rephist.c +++ b/src/or/rephist.c @@ -2313,7 +2313,7 @@ rep_hist_format_exit_stats(time_t now) time_t rep_hist_exit_stats_write(time_t now) { - char *statsdir = NULL, *filename = NULL, *str = NULL; + char *str = NULL; if (!start_of_exit_stats_interval) return 0; /* Not initialized. */ @@ -2329,19 +2329,12 @@ rep_hist_exit_stats_write(time_t now) rep_hist_reset_exit_stats(now); /* Try to write to disk. */ - statsdir = get_datadir_fname("stats"); - if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) { - log_warn(LD_HIST, "Unable to create stats/ directory!"); - goto done; + if (!check_or_create_data_subdir("stats")) { + write_to_data_subdir("stats", "exit-stats", str, "exit port statistics"); } - filename = get_datadir_fname2("stats", "exit-stats"); - if (write_str_to_file(filename, str, 0) < 0) - log_warn(LD_HIST, "Unable to write exit port statistics to disk!"); done: tor_free(str); - tor_free(statsdir); - tor_free(filename); return start_of_exit_stats_interval + WRITE_STATS_INTERVAL; } @@ -2598,7 +2591,7 @@ time_t rep_hist_buffer_stats_write(time_t now) { circuit_t *circ; - char *statsdir = NULL, *filename = NULL, *str = NULL; + char *str = NULL; if (!start_of_buffer_stats_interval) return 0; /* Not initialized. */ @@ -2617,19 +2610,12 @@ rep_hist_buffer_stats_write(time_t now) rep_hist_reset_buffer_stats(now); /* Try to write to disk. */ - statsdir = get_datadir_fname("stats"); - if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) { - log_warn(LD_HIST, "Unable to create stats/ directory!"); - goto done; + if (!check_or_create_data_subdir("stats")) { + write_to_data_subdir("stats", "buffer-stats", str, "buffer statistics"); } - filename = get_datadir_fname2("stats", "buffer-stats"); - if (write_str_to_file(filename, str, 0) < 0) - log_warn(LD_HIST, "Unable to write buffer stats to disk!"); done: tor_free(str); - tor_free(filename); - tor_free(statsdir); return start_of_buffer_stats_interval + WRITE_STATS_INTERVAL; } @@ -2741,7 +2727,7 @@ rep_hist_format_desc_stats(time_t now) time_t rep_hist_desc_stats_write(time_t now) { - char *statsdir = NULL, *filename = NULL, *str = NULL; + char *filename = NULL, *str = NULL; if (!start_of_served_descs_stats_interval) return 0; /* We're not collecting stats. */ @@ -2751,10 +2737,8 @@ rep_hist_desc_stats_write(time_t now) str = rep_hist_format_desc_stats(now); tor_assert(str != NULL); - statsdir = get_datadir_fname("stats"); - if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) { - log_warn(LD_HIST, "Unable to create stats/ directory!"); - goto done; + if (check_or_create_data_subdir("stats") < 0) { + goto done; } filename = get_datadir_fname2("stats", "served-desc-stats"); if (append_bytes_to_file(filename, str, strlen(str), 0) < 0) @@ -2763,7 +2747,6 @@ rep_hist_desc_stats_write(time_t now) rep_hist_reset_desc_stats(now); done: - tor_free(statsdir); tor_free(filename); tor_free(str); return start_of_served_descs_stats_interval + WRITE_STATS_INTERVAL; @@ -2981,7 +2964,7 @@ rep_hist_format_conn_stats(time_t now) time_t rep_hist_conn_stats_write(time_t now) { - char *statsdir = NULL, *filename = NULL, *str = NULL; + char *str = NULL; if (!start_of_conn_stats_interval) return 0; /* Not initialized. */ @@ -2995,19 +2978,12 @@ rep_hist_conn_stats_write(time_t now) rep_hist_reset_conn_stats(now); /* Try to write to disk. */ - statsdir = get_datadir_fname("stats"); - if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) { - log_warn(LD_HIST, "Unable to create stats/ directory!"); - goto done; + if (!check_or_create_data_subdir("stats")) { + write_to_data_subdir("stats", "conn-stats", str, "connection statistics"); } - filename = get_datadir_fname2("stats", "conn-stats"); - if (write_str_to_file(filename, str, 0) < 0) - log_warn(LD_HIST, "Unable to write conn stats to disk!"); done: tor_free(str); - tor_free(filename); - tor_free(statsdir); return start_of_conn_stats_interval + WRITE_STATS_INTERVAL; } diff --git a/src/or/replaycache.c b/src/or/replaycache.c index 59b98489b7..122efb7030 100644 --- a/src/or/replaycache.c +++ b/src/or/replaycache.c @@ -63,7 +63,7 @@ replaycache_new(time_t horizon, time_t interval) /** See documentation for replaycache_add_and_test() */ -int +STATIC int replaycache_add_and_test_internal( time_t present, replaycache_t *r, const void *data, int len, time_t *elapsed) @@ -127,14 +127,13 @@ replaycache_add_and_test_internal( /** See documentation for replaycache_scrub_if_needed() */ -void +STATIC void replaycache_scrub_if_needed_internal(time_t present, replaycache_t *r) { digestmap_iter_t *itr = NULL; const char *digest; void *valp; time_t *access_time; - char scrub_this; /* sanity check */ if (!r || !(r->digests_seen)) { @@ -152,20 +151,10 @@ replaycache_scrub_if_needed_internal(time_t present, replaycache_t *r) /* okay, scrub time */ itr = digestmap_iter_init(r->digests_seen); while (!digestmap_iter_done(itr)) { - scrub_this = 0; digestmap_iter_get(itr, &digest, &valp); access_time = (time_t *)valp; - if (access_time) { - /* aged out yet? */ - if (*access_time < present - r->horizon) scrub_this = 1; - } else { - /* Buh? Get rid of it, anyway */ - log_info(LD_BUG, "replaycache_scrub_if_needed_internal() saw a NULL" - " entry in the digestmap."); - scrub_this = 1; - } - - if (scrub_this) { + /* aged out yet? */ + if (*access_time < present - r->horizon) { /* Advance the iterator and remove this one */ itr = digestmap_iter_next_rmv(r->digests_seen, itr); /* Free the value removed */ diff --git a/src/or/replaycache.h b/src/or/replaycache.h index de20cab627..c60c408103 100644 --- a/src/or/replaycache.h +++ b/src/or/replaycache.h @@ -45,10 +45,10 @@ replaycache_t * replaycache_new(time_t horizon, time_t interval); * testing. For everything else, use the wrappers below instead. */ -int replaycache_add_and_test_internal( +STATIC int replaycache_add_and_test_internal( time_t present, replaycache_t *r, const void *data, int len, time_t *elapsed); -void replaycache_scrub_if_needed_internal( +STATIC void replaycache_scrub_if_needed_internal( time_t present, replaycache_t *r); #endif /* REPLAYCACHE_PRIVATE */ diff --git a/src/or/router.c b/src/or/router.c index 6069da8f09..1063eda044 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -2249,7 +2249,7 @@ router_guess_address_from_dir_headers(uint32_t *guess) * string describing the version of Tor and the operating system we're * currently running on. */ -void +STATIC void get_platform_str(char *platform, size_t len) { tor_snprintf(platform, len, "Tor %s on %s", diff --git a/src/or/router.h b/src/or/router.h index 60095d087b..1079ec78c2 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -12,6 +12,8 @@ #ifndef TOR_ROUTER_H #define TOR_ROUTER_H +#include "testsupport.h" + crypto_pk_t *get_onion_key(void); time_t get_onion_key_set_at(void); void set_server_identity_key(crypto_pk_t *k); @@ -146,7 +148,7 @@ smartlist_t *router_get_all_orports(const routerinfo_t *ri); #ifdef ROUTER_PRIVATE /* Used only by router.c and test.c */ -void get_platform_str(char *platform, size_t len); +STATIC void get_platform_str(char *platform, size_t len); #endif #endif diff --git a/src/or/routerlist.c b/src/or/routerlist.c index c2220f4ca9..46da17e03b 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -629,9 +629,6 @@ authority_cert_dl_looks_uncertain(const char *id_digest) return n_failures >= N_AUTH_CERT_DL_FAILURES_TO_BUG_USER; } -/** How many times will we try to fetch a certificate before giving up? */ -#define MAX_CERT_DL_FAILURES 8 - /** Try to download any v3 authority certificates that we may be missing. If * <b>status</b> is provided, try to get all the ones that were used to sign * <b>status</b>. Additionally, try to have a non-expired certificate for @@ -703,7 +700,7 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now) } SMARTLIST_FOREACH_END(cert); if (!found && download_status_is_ready(&(cl->dl_status_by_id), now, - MAX_CERT_DL_FAILURES) && + get_options()->TestingCertMaxDownloadTries) && !digestmap_get(pending_id, ds->v3_identity_digest)) { log_info(LD_DIR, "No current certificate known for authority %s " @@ -765,7 +762,7 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now) } if (download_status_is_ready_by_sk_in_cl( cl, sig->signing_key_digest, - now, MAX_CERT_DL_FAILURES) && + now, get_options()->TestingCertMaxDownloadTries) && !fp_pair_map_get_by_digests(pending_cert, voter->identity_digest, sig->signing_key_digest)) { @@ -1126,32 +1123,18 @@ router_rebuild_store(int flags, desc_store_t *store) static int router_reload_router_list_impl(desc_store_t *store) { - char *fname = NULL, *altname = NULL, *contents = NULL; + char *fname = NULL, *contents = NULL; struct stat st; - int read_from_old_location = 0; int extrainfo = (store->type == EXTRAINFO_STORE); - time_t now = time(NULL); store->journal_len = store->store_len = 0; fname = get_datadir_fname(store->fname_base); - if (store->fname_alt_base) - altname = get_datadir_fname(store->fname_alt_base); if (store->mmap) /* get rid of it first */ tor_munmap_file(store->mmap); store->mmap = NULL; store->mmap = tor_mmap_file(fname); - if (!store->mmap && altname && file_status(altname) == FN_FILE) { - read_from_old_location = 1; - log_notice(LD_DIR, "Couldn't read %s; trying to load routers from old " - "location %s.", fname, altname); - if ((store->mmap = tor_mmap_file(altname))) - read_from_old_location = 1; - } - if (altname && !read_from_old_location) { - remove_file_if_very_old(altname, now); - } if (store->mmap) { store->store_len = store->mmap->size; if (extrainfo) @@ -1168,14 +1151,6 @@ router_reload_router_list_impl(desc_store_t *store) fname = get_datadir_fname_suffix(store->fname_base, ".new"); if (file_status(fname) == FN_FILE) contents = read_file_to_str(fname, RFTS_BIN|RFTS_IGNORE_MISSING, &st); - if (read_from_old_location) { - tor_free(altname); - altname = get_datadir_fname_suffix(store->fname_alt_base, ".new"); - if (!contents) - contents = read_file_to_str(altname, RFTS_BIN|RFTS_IGNORE_MISSING, &st); - else - remove_file_if_very_old(altname, now); - } if (contents) { if (extrainfo) router_load_extrainfo_from_string(contents, NULL,SAVED_IN_JOURNAL, @@ -1188,9 +1163,8 @@ router_reload_router_list_impl(desc_store_t *store) } tor_free(fname); - tor_free(altname); - if (store->journal_len || read_from_old_location) { + if (store->journal_len) { /* Always clear the journal on startup.*/ router_rebuild_store(RRS_FORCE, store); } else if (!extrainfo) { @@ -1827,7 +1801,7 @@ router_get_advertised_bandwidth_capped(const routerinfo_t *router) * doubles, convert them to uint64_t, and try to scale them linearly so as to * much of the range of uint64_t. If <b>total_out</b> is provided, set it to * the sum of all elements in the array _before_ scaling. */ -/* private */ void +STATIC void scale_array_elements_to_u64(u64_dbl_t *entries, int n_entries, uint64_t *total_out) { @@ -1870,7 +1844,7 @@ gt_i64_timei(uint64_t a, uint64_t b) * value, and return the index of that element. If all elements are 0, choose * an index at random. Return -1 on error. */ -/* private */ int +STATIC int choose_array_element_by_weight(const u64_dbl_t *entries, int n_entries) { int i, i_chosen=-1, n_chosen=0; @@ -2570,19 +2544,6 @@ router_is_named(const routerinfo_t *router) tor_memeq(digest, router->cache_info.identity_digest, DIGEST_LEN)); } -/** Return true iff the digest of <b>router</b>'s identity key, - * encoded in hexadecimal, matches <b>hexdigest</b> (which is - * optionally prefixed with a single dollar sign). Return false if - * <b>hexdigest</b> is malformed, or it doesn't match. */ -static INLINE int -router_hex_digest_matches(const routerinfo_t *router, const char *hexdigest) -{ - return hex_digest_nickname_matches(hexdigest, - router->cache_info.identity_digest, - router->nickname, - router_is_named(router)); -} - /** Return true iff <b>digest</b> is the digest of the identity key of a * trusted directory matching at least one bit of <b>type</b>. If <b>type</b> * is zero, any authority is okay. */ @@ -2777,7 +2738,6 @@ router_get_routerlist(void) routerlist->extra_info_map = eimap_new(); routerlist->desc_store.fname_base = "cached-descriptors"; - routerlist->desc_store.fname_alt_base = "cached-routers"; routerlist->extrainfo_store.fname_base = "cached-extrainfo"; routerlist->desc_store.type = ROUTER_STORE; @@ -4487,12 +4447,8 @@ initiate_descriptor_downloads(const routerstatus_t *source, * try to split our requests into at least this many requests. */ #define MIN_REQUESTS 3 /** If we want fewer than this many descriptors, wait until we - * want more, or until MAX_CLIENT_INTERVAL_WITHOUT_REQUEST has - * passed. */ + * want more, or until TestingClientMaxIntervalWithoutRequest has passed. */ #define MAX_DL_TO_DELAY 16 -/** When directory clients have only a few servers to request, they batch - * them until they have more, or until this amount of time has passed. */ -#define MAX_CLIENT_INTERVAL_WITHOUT_REQUEST (10*60) /** Given a <b>purpose</b> (FETCH_MICRODESC or FETCH_SERVERDESC) and a list of * router descriptor digests or microdescriptor digest256s in @@ -4524,7 +4480,7 @@ launch_descriptor_downloads(int purpose, should_delay = 0; } else { should_delay = (last_descriptor_download_attempted + - MAX_CLIENT_INTERVAL_WITHOUT_REQUEST) > now; + options->TestingClientMaxIntervalWithoutRequest) > now; if (!should_delay && n_downloadable) { if (last_descriptor_download_attempted) { log_info(LD_DIR, @@ -4797,7 +4753,7 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote, continue; /* We have an in-progress download. */ } if (!download_status_is_ready(&rs->dl_status, now, - MAX_ROUTERDESC_DOWNLOAD_FAILURES)) { + options->TestingDescriptorMaxDownloadTries)) { ++n_delayed; /* Not ready for retry. */ continue; } @@ -4957,7 +4913,7 @@ update_extrainfo_downloads(time_t now) continue; } if (!download_status_is_ready(&sd->ei_dl_status, now, - MAX_ROUTERDESC_DOWNLOAD_FAILURES)) { + options->TestingDescriptorMaxDownloadTries)) { ++n_delay; continue; } diff --git a/src/or/routerlist.h b/src/or/routerlist.h index ce0f0f2e34..0162297ca7 100644 --- a/src/or/routerlist.h +++ b/src/or/routerlist.h @@ -11,6 +11,8 @@ #ifndef TOR_ROUTERLIST_H #define TOR_ROUTERLIST_H +#include "testsupport.h" + int get_n_authorities(dirinfo_type_t type); int trusted_dirs_reload_certs(void); @@ -206,9 +208,10 @@ typedef union u64_dbl_t { double dbl; } u64_dbl_t; -int choose_array_element_by_weight(const u64_dbl_t *entries, int n_entries); -void scale_array_elements_to_u64(u64_dbl_t *entries, int n_entries, - uint64_t *total_out); +STATIC int choose_array_element_by_weight(const u64_dbl_t *entries, + int n_entries); +STATIC void scale_array_elements_to_u64(u64_dbl_t *entries, int n_entries, + uint64_t *total_out); #endif #endif diff --git a/src/or/statefile.c b/src/or/statefile.c index bcb7b07417..aac8850b16 100644 --- a/src/or/statefile.c +++ b/src/or/statefile.c @@ -117,8 +117,8 @@ static const config_format_t state_format = { static or_state_t *global_state = NULL; /** Return the persistent state struct for this Tor. */ -or_state_t * -get_or_state(void) +MOCK_IMPL(or_state_t *, +get_or_state, (void)) { tor_assert(global_state); return global_state; diff --git a/src/or/statefile.h b/src/or/statefile.h index dcdee6c604..762b0f5ee1 100644 --- a/src/or/statefile.h +++ b/src/or/statefile.h @@ -7,7 +7,7 @@ #ifndef TOR_STATEFILE_H #define TOR_STATEFILE_H -or_state_t *get_or_state(void); +MOCK_DECL(or_state_t *,get_or_state,(void)); int did_last_state_file_write_fail(void); int or_state_save(time_t now); diff --git a/src/or/transports.c b/src/or/transports.c index 3749d6bb21..62cc1a864f 100644 --- a/src/or/transports.c +++ b/src/or/transports.c @@ -95,18 +95,14 @@ #include "util.h" #include "router.h" #include "statefile.h" +#include "entrynodes.h" static process_environment_t * create_managed_proxy_environment(const managed_proxy_t *mp); static INLINE int proxy_configuration_finished(const managed_proxy_t *mp); -static void managed_proxy_destroy(managed_proxy_t *mp, - int also_terminate_process); - static void handle_finished_proxy(managed_proxy_t *mp); -static int configure_proxy(managed_proxy_t *mp); - static void parse_method_error(const char *line, int is_server_method); #define parse_server_method_error(l) parse_method_error(l, 1) #define parse_client_method_error(l) parse_method_error(l, 0) @@ -136,7 +132,8 @@ static smartlist_t *transport_list = NULL; SOCKS version <b>socks_ver</b>. */ static transport_t * transport_new(const tor_addr_t *addr, uint16_t port, - const char *name, int socks_ver) + const char *name, int socks_ver, + const char *extra_info_args) { transport_t *t = tor_malloc_zero(sizeof(transport_t)); @@ -144,6 +141,8 @@ transport_new(const tor_addr_t *addr, uint16_t port, t->port = port; t->name = tor_strdup(name); t->socks_version = socks_ver; + if (extra_info_args) + t->extra_info_args = tor_strdup(extra_info_args); return t; } @@ -156,6 +155,7 @@ transport_free(transport_t *transport) return; tor_free(transport->name); + tor_free(transport->extra_info_args); tor_free(transport); } @@ -323,7 +323,7 @@ int transport_add_from_config(const tor_addr_t *addr, uint16_t port, const char *name, int socks_ver) { - transport_t *t = transport_new(addr, port, name, socks_ver); + transport_t *t = transport_new(addr, port, name, socks_ver, NULL); int r = transport_add(t); @@ -589,7 +589,7 @@ pt_configure_remaining_proxies(void) * Return 1 if the transport configuration finished, and return 0 * otherwise (if we still have more configuring to do for this * proxy). */ -static int +STATIC int configure_proxy(managed_proxy_t *mp) { int configuration_finished = 0; @@ -699,7 +699,7 @@ register_proxy(const managed_proxy_t *mp) } /** Free memory allocated by managed proxy <b>mp</b>. */ -static void +STATIC void managed_proxy_destroy(managed_proxy_t *mp, int also_terminate_process) { @@ -713,7 +713,8 @@ managed_proxy_destroy(managed_proxy_t *mp, smartlist_free(mp->transports_to_launch); /* remove it from the list of managed proxies */ - smartlist_remove(managed_proxy_list, mp); + if (managed_proxy_list) + smartlist_remove(managed_proxy_list, mp); /* free the argv */ free_execve_args(mp->argv); @@ -750,7 +751,6 @@ handle_finished_proxy(managed_proxy_t *mp) } unconfigured_proxies_n--; - tor_assert(unconfigured_proxies_n >= 0); } /** Return true if the configuration of the managed proxy <b>mp</b> is @@ -781,7 +781,7 @@ handle_methods_done(const managed_proxy_t *mp) /** Handle a configuration protocol <b>line</b> received from a * managed proxy <b>mp</b>. */ -void +STATIC void handle_proxy_line(const char *line, managed_proxy_t *mp) { log_info(LD_GENERAL, "Got a line from managed proxy '%s': (%s)", @@ -882,7 +882,7 @@ handle_proxy_line(const char *line, managed_proxy_t *mp) } /** Parses an ENV-ERROR <b>line</b> and warns the user accordingly. */ -void +STATIC void parse_env_error(const char *line) { /* (Length of the protocol string) plus (a space) and (the first char of @@ -898,7 +898,7 @@ parse_env_error(const char *line) /** Handles a VERSION <b>line</b>. Updates the configuration protocol * version in <b>mp</b>. */ -int +STATIC int parse_version(const char *line, managed_proxy_t *mp) { if (strlen(line) < (strlen(PROTO_NEG_SUCCESS) + 2)) { @@ -939,14 +939,14 @@ parse_method_error(const char *line, int is_server) /** Parses an SMETHOD <b>line</b> and if well-formed it registers the * new transport in <b>mp</b>. */ -int +STATIC int parse_smethod_line(const char *line, managed_proxy_t *mp) { int r; smartlist_t *items = NULL; char *method_name=NULL; - + char *args_string=NULL; char *addrport=NULL; tor_addr_t tor_addr; char *address=NULL; @@ -963,6 +963,9 @@ parse_smethod_line(const char *line, managed_proxy_t *mp) goto err; } + /* Example of legit SMETHOD line: + SMETHOD obfs2 0.0.0.0:25612 ARGS:secret=supersekrit,key=superkey */ + tor_assert(!strcmp(smartlist_get(items,0),PROTO_SMETHOD)); method_name = smartlist_get(items,1); @@ -990,7 +993,19 @@ parse_smethod_line(const char *line, managed_proxy_t *mp) goto err; } - transport = transport_new(&tor_addr, port, method_name, PROXY_NONE); + if (smartlist_len(items) > 3) { + /* Seems like there are also some [options] in the SMETHOD line. + Let's see if we can parse them. */ + char *options_string = smartlist_get(items, 3); + log_debug(LD_CONFIG, "Got options_string: %s", options_string); + if (!strcmpstart(options_string, "ARGS:")) { + args_string = options_string+strlen("ARGS:"); + log_debug(LD_CONFIG, "Got ARGS: %s", args_string); + } + } + + transport = transport_new(&tor_addr, port, method_name, + PROXY_NONE, args_string); if (!transport) goto err; @@ -1016,7 +1031,7 @@ parse_smethod_line(const char *line, managed_proxy_t *mp) /** Parses a CMETHOD <b>line</b>, and if well-formed it registers * the new transport in <b>mp</b>. */ -int +STATIC int parse_cmethod_line(const char *line, managed_proxy_t *mp) { int r; @@ -1082,7 +1097,7 @@ parse_cmethod_line(const char *line, managed_proxy_t *mp) goto err; } - transport = transport_new(&tor_addr, port, method_name, socks_ver); + transport = transport_new(&tor_addr, port, method_name, socks_ver, NULL); if (!transport) goto err; @@ -1105,6 +1120,50 @@ parse_cmethod_line(const char *line, managed_proxy_t *mp) return r; } +/** Return a newly allocated string that tor should place in + * TOR_PT_SERVER_TRANSPORT_OPTIONS while configuring the server + * manged proxy in <b>mp</b>. Return NULL if no such options are found. */ +STATIC char * +get_transport_options_for_server_proxy(const managed_proxy_t *mp) +{ + char *options_string = NULL; + smartlist_t *string_sl = smartlist_new(); + + tor_assert(mp->is_server); + + /** Loop over the transports of the proxy. If we have options for + any of them, format them appropriately and place them in our + smartlist. Finally, join our smartlist to get the final + string. */ + SMARTLIST_FOREACH_BEGIN(mp->transports_to_launch, const char *, transport) { + smartlist_t *options_tmp_sl = NULL; + options_tmp_sl = get_options_for_server_transport(transport); + if (!options_tmp_sl) + continue; + + /** Loop over the options of this transport, escape them, and + place them in the smartlist. */ + SMARTLIST_FOREACH_BEGIN(options_tmp_sl, const char *, options) { + char *escaped_opts = tor_escape_str_for_pt_args(options, ":;\\"); + smartlist_add_asprintf(string_sl, "%s:%s", + transport, escaped_opts); + tor_free(escaped_opts); + } SMARTLIST_FOREACH_END(options); + + SMARTLIST_FOREACH(options_tmp_sl, char *, c, tor_free(c)); + smartlist_free(options_tmp_sl); + } SMARTLIST_FOREACH_END(transport); + + if (smartlist_len(string_sl)) { + options_string = smartlist_join_strings(string_sl, ";", 0, NULL); + } + + SMARTLIST_FOREACH(string_sl, char *, t, tor_free(t)); + smartlist_free(string_sl); + + return options_string; +} + /** Return the string that tor should place in TOR_PT_SERVER_BINDADDR * while configuring the server managed proxy in <b>mp</b>. The * string is stored in the heap, and it's the the responsibility of @@ -1186,6 +1245,16 @@ create_managed_proxy_environment(const managed_proxy_t *mp) tor_free(bindaddr_tmp); } + { + char *server_transport_options = + get_transport_options_for_server_proxy(mp); + if (server_transport_options) { + smartlist_add_asprintf(envs, "TOR_PT_SERVER_TRANSPORT_OPTIONS=%s", + server_transport_options); + tor_free(server_transport_options); + } + } + /* XXX024 Remove the '=' here once versions of obfsproxy which * assert that this env var exists are sufficiently dead. * @@ -1216,7 +1285,7 @@ create_managed_proxy_environment(const managed_proxy_t *mp) * <b>proxy_argv</b>. * * Requires that proxy_argv have at least one element. */ -static managed_proxy_t * +STATIC managed_proxy_t * managed_proxy_create(const smartlist_t *transport_list, char **proxy_argv, int is_server) { @@ -1390,6 +1459,8 @@ pt_get_extra_info_descriptor_string(void) tor_assert(mp->transports); SMARTLIST_FOREACH_BEGIN(mp->transports, const transport_t *, t) { + char *transport_args = NULL; + /* If the transport proxy returned "0.0.0.0" as its address, and * we know our external IP address, use it. Otherwise, use the * returned address. */ @@ -1405,9 +1476,16 @@ pt_get_extra_info_descriptor_string(void) addrport = fmt_addrport(&t->addr, t->port); } + /* If this transport has any arguments with it, prepend a space + to them so that we can add them to the transport line. */ + if (t->extra_info_args) + tor_asprintf(&transport_args, " %s", t->extra_info_args); + smartlist_add_asprintf(string_chunks, - "transport %s %s", - t->name, addrport); + "transport %s %s%s", + t->name, addrport, + transport_args ? transport_args : ""); + tor_free(transport_args); } SMARTLIST_FOREACH_END(t); } SMARTLIST_FOREACH_END(mp); @@ -1426,6 +1504,57 @@ pt_get_extra_info_descriptor_string(void) return the_string; } +/** Stringify the SOCKS arguments in <b>socks_args</b> according to + * 180_pluggable_transport.txt. The string is allocated on the heap + * and it's the responsibility of the caller to free it after use. */ +char * +pt_stringify_socks_args(const smartlist_t *socks_args) +{ + /* tmp place to store escaped socks arguments, so that we can + concatenate them up afterwards */ + smartlist_t *sl_tmp = NULL; + char *escaped_string = NULL; + char *new_string = NULL; + + tor_assert(socks_args); + tor_assert(smartlist_len(socks_args) > 0); + + sl_tmp = smartlist_new(); + + SMARTLIST_FOREACH_BEGIN(socks_args, const char *, s) { + /* Escape ';' and '\'. */ + escaped_string = tor_escape_str_for_pt_args(s, ";\\"); + if (!escaped_string) + goto done; + + smartlist_add(sl_tmp, escaped_string); + } SMARTLIST_FOREACH_END(s); + + new_string = smartlist_join_strings(sl_tmp, ";", 0, NULL); + + done: + SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s)); + smartlist_free(sl_tmp); + + return new_string; +} + +/** Return a string of the SOCKS arguments that we should pass to the + * pluggable transports proxy in <b>addr</b>:<b>port</b> according to + * 180_pluggable_transport.txt. The string is allocated on the heap + * and it's the responsibility of the caller to free it after use. */ +char * +pt_get_socks_args_for_proxy_addrport(const tor_addr_t *addr, uint16_t port) +{ + const smartlist_t *socks_args = NULL; + + socks_args = get_socks_args_by_bridge_addrport(addr, port); + if (!socks_args) + return NULL; + + return pt_stringify_socks_args(socks_args); +} + /** The tor config was read. * Destroy all managed proxies that were marked by a previous call to * prepare_proxy_list_for_config_read() and are not used by the new diff --git a/src/or/transports.h b/src/or/transports.h index 6ee82f4556..7b524f2073 100644 --- a/src/or/transports.h +++ b/src/or/transports.h @@ -25,6 +25,9 @@ typedef struct transport_t { /** Boolean: We are re-parsing our transport list, and we are going to remove * this one if we don't find it in the list of configured transports. */ unsigned marked_for_removal : 1; + /** Arguments for this transport that must be written to the + extra-info descriptor. */ + char *extra_info_args; } transport_t; void mark_transport_list(void); @@ -55,6 +58,10 @@ void pt_prepare_proxy_list_for_config_read(void); void sweep_proxy_list(void); smartlist_t *get_transport_proxy_ports(void); +char *pt_stringify_socks_args(const smartlist_t *socks_args); + +char *pt_get_socks_args_for_proxy_addrport(const tor_addr_t *addr, + uint16_t port); #ifdef PT_PRIVATE /** State of the managed proxy configuration protocol. */ @@ -100,12 +107,21 @@ typedef struct { smartlist_t *transports; } managed_proxy_t; -int parse_cmethod_line(const char *line, managed_proxy_t *mp); -int parse_smethod_line(const char *line, managed_proxy_t *mp); +STATIC int parse_cmethod_line(const char *line, managed_proxy_t *mp); +STATIC int parse_smethod_line(const char *line, managed_proxy_t *mp); + +STATIC int parse_version(const char *line, managed_proxy_t *mp); +STATIC void parse_env_error(const char *line); +STATIC void handle_proxy_line(const char *line, managed_proxy_t *mp); +STATIC char *get_transport_options_for_server_proxy(const managed_proxy_t *mp); + +STATIC void managed_proxy_destroy(managed_proxy_t *mp, + int also_terminate_process); + +STATIC managed_proxy_t *managed_proxy_create(const smartlist_t *transport_list, + char **proxy_argv, int is_server); -int parse_version(const char *line, managed_proxy_t *mp); -void parse_env_error(const char *line); -void handle_proxy_line(const char *line, managed_proxy_t *mp); +STATIC int configure_proxy(managed_proxy_t *mp); #endif diff --git a/src/test/bench.c b/src/test/bench.c index 5a8d21d173..ca01d3c3e6 100644 --- a/src/test/bench.c +++ b/src/test/bench.c @@ -15,7 +15,6 @@ const char tor_git_revision[] = ""; #include "orconfig.h" #define RELAY_PRIVATE -#define CONFIG_PRIVATE #include "or.h" #include "onion_tap.h" @@ -204,6 +203,7 @@ bench_onion_ntor(void) for (i = 0; i < iters; ++i) { onion_skin_ntor_create(nodeid, &keypair1.pubkey, &state, os); ntor_handshake_state_free(state); + state = NULL; } end = perftime(); printf("Client-side, part 1: %f usec.\n", NANOCOUNT(start, end, iters)/1e3); diff --git a/src/test/include.am b/src/test/include.am index 112d1a79d8..fb704d7185 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -1,11 +1,15 @@ TESTS+= src/test/test -noinst_PROGRAMS+= src/test/test src/test/test-child src/test/bench +noinst_PROGRAMS+= src/test/bench +if UNITTESTS_ENABLED +noinst_PROGRAMS+= src/test/test src/test/test-child +endif src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \ -DLOCALSTATEDIR="\"$(localstatedir)\"" \ -DBINDIR="\"$(bindir)\"" \ - -I"$(top_srcdir)/src/or" -I"$(top_srcdir)/src/ext" + -I"$(top_srcdir)/src/or" -I"$(top_srcdir)/src/ext" \ + -DTOR_UNIT_TESTS # -L flags need to go in LDFLAGS. -l flags need to go in LDADD. # This seems to matter nowhere but on Windows, but I assure you that it @@ -15,18 +19,24 @@ src_test_test_SOURCES = \ src/test/test.c \ src/test/test_addr.c \ src/test/test_cell_formats.c \ + src/test/test_circuitlist.c \ + src/test/test_circuitmux.c \ src/test/test_containers.c \ src/test/test_crypto.c \ + src/test/test_cell_queue.c \ src/test/test_data.c \ src/test/test_dir.c \ src/test/test_introduce.c \ src/test/test_microdesc.c \ + src/test/test_options.c \ src/test/test_pt.c \ src/test/test_replay.c \ src/test/test_util.c \ src/test/test_config.c \ src/ext/tinytest.c +src_test_test_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) + src_test_test_CPPFLAGS= $(src_test_AM_CPPFLAGS) src_test_bench_SOURCES = \ @@ -36,9 +46,9 @@ src_test_bench_CPPFLAGS= $(src_test_AM_CPPFLAGS) src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ @TOR_LDFLAGS_libevent@ -src_test_test_LDADD = src/or/libtor.a src/common/libor.a \ - src/common/libor-crypto.a $(LIBDONNA) \ - src/common/libor-event.a \ +src_test_test_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \ + src/common/libor-crypto-testing.a $(LIBDONNA) \ + src/common/libor-event-testing.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ diff --git a/src/test/test-network.sh b/src/test/test-network.sh new file mode 100755 index 0000000000..9146ae3ce4 --- /dev/null +++ b/src/test/test-network.sh @@ -0,0 +1,25 @@ +#! /bin/sh + +# NOTE: Requires Chutney in $CHUTNEY_PATH. + +TOR_DIR=$(pwd)/src/or +NETWORK_FLAVOUR=basic +CHUTNEY_NETWORK=networks/$NETWORK_FLAVOUR +myname=$(basename $0) + +[ -d "$CHUTNEY_PATH" ] && [ -x "$CHUTNEY_PATH/chutney" ] || { + echo "$myname: missing 'chutney' in CHUTNEY_PATH ($CHUTNEY_PATH)" + exit 1 +} +cd "$CHUTNEY_PATH" +PATH=$TOR_DIR:$PATH # For picking up the right tor binary. +./tools/bootstrap-network.sh $NETWORK_FLAVOUR || exit 2 + +# Sleep some, waiting for the network to bootstrap. +# TODO: Add chutney command 'bootstrap-status' and use that instead. +BOOTSTRAP_TIME=18 +echo -n "$myname: sleeping for $BOOTSTRAP_TIME seconds" +n=$BOOTSTRAP_TIME; while [ $n -gt 0 ]; do + sleep 1; n=$(expr $n - 1); echo -n . +done; echo "" +./chutney verify $CHUTNEY_NETWORK diff --git a/src/test/test.c b/src/test/test.c index 3ff39e6293..60fbfb132c 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -29,7 +29,6 @@ const char tor_git_revision[] = ""; /* These macros pull in declarations for some functions and structures that * are typically file-private. */ #define BUFFERS_PRIVATE -#define CONFIG_PRIVATE #define GEOIP_PRIVATE #define ROUTER_PRIVATE #define CIRCUITSTATS_PRIVATE @@ -840,43 +839,130 @@ test_onion_handshake(void) crypto_dh_t *c_dh = NULL; char c_buf[TAP_ONIONSKIN_CHALLENGE_LEN]; char c_keys[40]; - /* server-side */ char s_buf[TAP_ONIONSKIN_REPLY_LEN]; char s_keys[40]; - + int i; /* shared */ - crypto_pk_t *pk = NULL; + crypto_pk_t *pk = NULL, *pk2 = NULL; pk = pk_generate(0); + pk2 = pk_generate(1); /* client handshake 1. */ memset(c_buf, 0, TAP_ONIONSKIN_CHALLENGE_LEN); test_assert(! onion_skin_TAP_create(pk, &c_dh, c_buf)); - /* server handshake */ - memset(s_buf, 0, TAP_ONIONSKIN_REPLY_LEN); - memset(s_keys, 0, 40); - test_assert(! onion_skin_TAP_server_handshake(c_buf, pk, NULL, - s_buf, s_keys, 40)); + for (i = 1; i <= 3; ++i) { + crypto_pk_t *k1, *k2; + if (i==1) { + /* server handshake: only one key known. */ + k1 = pk; k2 = NULL; + } else if (i==2) { + /* server handshake: try the right key first. */ + k1 = pk; k2 = pk2; + } else { + /* server handshake: try the right key second. */ + k1 = pk2; k2 = pk; + } - /* client handshake 2 */ - memset(c_keys, 0, 40); - test_assert(! onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40)); + memset(s_buf, 0, TAP_ONIONSKIN_REPLY_LEN); + memset(s_keys, 0, 40); + test_assert(! onion_skin_TAP_server_handshake(c_buf, k1, k2, + s_buf, s_keys, 40)); - if (memcmp(c_keys, s_keys, 40)) { - puts("Aiiiie"); - exit(1); + /* client handshake 2 */ + memset(c_keys, 0, 40); + test_assert(! onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40)); + + test_memeq(c_keys, s_keys, 40); + memset(s_buf, 0, 40); + test_memneq(c_keys, s_buf, 40); } - test_memeq(c_keys, s_keys, 40); - memset(s_buf, 0, 40); - test_memneq(c_keys, s_buf, 40); + done: + crypto_dh_free(c_dh); + crypto_pk_free(pk); + crypto_pk_free(pk2); +} + +static void +test_bad_onion_handshake(void *arg) +{ + char junk_buf[TAP_ONIONSKIN_CHALLENGE_LEN]; + char junk_buf2[TAP_ONIONSKIN_CHALLENGE_LEN]; + /* client-side */ + crypto_dh_t *c_dh = NULL; + char c_buf[TAP_ONIONSKIN_CHALLENGE_LEN]; + char c_keys[40]; + /* server-side */ + char s_buf[TAP_ONIONSKIN_REPLY_LEN]; + char s_keys[40]; + /* shared */ + crypto_pk_t *pk = NULL, *pk2 = NULL; + + (void)arg; + + pk = pk_generate(0); + pk2 = pk_generate(1); + + /* Server: Case 1: the encrypted data is degenerate. */ + memset(junk_buf, 0, sizeof(junk_buf)); + crypto_pk_public_hybrid_encrypt(pk, junk_buf2, TAP_ONIONSKIN_CHALLENGE_LEN, + junk_buf, DH_KEY_LEN, PK_PKCS1_OAEP_PADDING, 1); + tt_int_op(-1, ==, + onion_skin_TAP_server_handshake(junk_buf2, pk, NULL, + s_buf, s_keys, 40)); + + /* Server: Case 2: the encrypted data is not long enough. */ + memset(junk_buf, 0, sizeof(junk_buf)); + memset(junk_buf2, 0, sizeof(junk_buf2)); + crypto_pk_public_encrypt(pk, junk_buf2, sizeof(junk_buf2), + junk_buf, 48, PK_PKCS1_OAEP_PADDING); + tt_int_op(-1, ==, + onion_skin_TAP_server_handshake(junk_buf2, pk, NULL, + s_buf, s_keys, 40)); + + /* client handshake 1: do it straight. */ + memset(c_buf, 0, TAP_ONIONSKIN_CHALLENGE_LEN); + test_assert(! onion_skin_TAP_create(pk, &c_dh, c_buf)); + + /* Server: Case 3: we just don't have the right key. */ + tt_int_op(-1, ==, + onion_skin_TAP_server_handshake(c_buf, pk2, NULL, + s_buf, s_keys, 40)); + + /* Server: Case 4: The RSA-encrypted portion is corrupt. */ + c_buf[64] ^= 33; + tt_int_op(-1, ==, + onion_skin_TAP_server_handshake(c_buf, pk, NULL, + s_buf, s_keys, 40)); + c_buf[64] ^= 33; + + /* (Let the server procede) */ + tt_int_op(0, ==, + onion_skin_TAP_server_handshake(c_buf, pk, NULL, + s_buf, s_keys, 40)); + + /* Client: Case 1: The server sent back junk. */ + s_buf[64] ^= 33; + tt_int_op(-1, ==, + onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40)); + s_buf[64] ^= 33; + + /* Let the client finish; make sure it can. */ + tt_int_op(0, ==, + onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40)); + test_memeq(s_keys, c_keys, 40); + + /* Client: Case 2: The server sent back a degenerate DH. */ + memset(s_buf, 0, sizeof(s_buf)); + tt_int_op(-1, ==, + onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40)); done: - if (c_dh) - crypto_dh_free(c_dh); - if (pk) - crypto_pk_free(pk); + crypto_dh_free(c_dh); + crypto_pk_free(pk); + crypto_pk_free(pk2); } #ifdef CURVE25519_ENABLED @@ -2005,6 +2091,7 @@ static struct testcase_t test_array[] = { ENT(buffers), { "buffer_copy", test_buffer_copy, 0, NULL, NULL }, ENT(onion_handshake), + { "bad_onion_handshake", test_bad_onion_handshake, 0, NULL, NULL }, #ifdef CURVE25519_ENABLED { "ntor_handshake", test_ntor_handshake, 0, NULL, NULL }, #endif @@ -2045,6 +2132,10 @@ extern struct testcase_t config_tests[]; extern struct testcase_t introduce_tests[]; extern struct testcase_t replaycache_tests[]; extern struct testcase_t cell_format_tests[]; +extern struct testcase_t circuitlist_tests[]; +extern struct testcase_t circuitmux_tests[]; +extern struct testcase_t cell_queue_tests[]; +extern struct testcase_t options_tests[]; static struct testgroup_t testgroups[] = { { "", test_array }, @@ -2054,12 +2145,16 @@ static struct testgroup_t testgroups[] = { { "container/", container_tests }, { "util/", util_tests }, { "cellfmt/", cell_format_tests }, + { "cellqueue/", cell_queue_tests }, { "dir/", dir_tests }, { "dir/md/", microdesc_tests }, { "pt/", pt_tests }, { "config/", config_tests }, { "replaycache/", replaycache_tests }, { "introduce/", introduce_tests }, + { "circuitlist/", circuitlist_tests }, + { "circuitmux/", circuitmux_tests }, + { "options/", options_tests }, END_OF_GROUPS }; diff --git a/src/test/test_addr.c b/src/test/test_addr.c index fec85a4696..4bc602df84 100644 --- a/src/test/test_addr.c +++ b/src/test/test_addr.c @@ -44,6 +44,10 @@ test_addr_basic(void) test_eq(u32, 0x7f000001u); test_eq(u16, 0); tor_free(cp); + + test_assert(addr_port_lookup(LOG_WARN, "localhost:3", &cp, &u32, NULL)); + tor_free(cp); + test_eq(0, addr_mask_get_bits(0x0u)); test_eq(32, addr_mask_get_bits(0xFFFFFFFFu)); test_eq(16, addr_mask_get_bits(0xFFFF0000u)); @@ -217,11 +221,12 @@ test_addr_ip6_helpers(void) /* ==== Converting to and from sockaddr_t. */ sin = (struct sockaddr_in *)&sa_storage; sin->sin_family = AF_INET; - sin->sin_port = 9090; + sin->sin_port = htons(9090); sin->sin_addr.s_addr = htonl(0x7f7f0102); /*127.127.1.2*/ - tor_addr_from_sockaddr(&t1, (struct sockaddr *)sin, NULL); + tor_addr_from_sockaddr(&t1, (struct sockaddr *)sin, &port1); test_eq(tor_addr_family(&t1), AF_INET); test_eq(tor_addr_to_ipv4h(&t1), 0x7f7f0102); + tt_int_op(port1, ==, 9090); memset(&sa_storage, 0, sizeof(sa_storage)); test_eq(sizeof(struct sockaddr_in), @@ -235,8 +240,9 @@ test_addr_ip6_helpers(void) sin6->sin6_family = AF_INET6; sin6->sin6_port = htons(7070); sin6->sin6_addr.s6_addr[0] = 128; - tor_addr_from_sockaddr(&t1, (struct sockaddr *)sin6, NULL); + tor_addr_from_sockaddr(&t1, (struct sockaddr *)sin6, &port1); test_eq(tor_addr_family(&t1), AF_INET6); + tt_int_op(port1, ==, 7070); p1 = tor_addr_to_str(buf, &t1, sizeof(buf), 0); test_streq(p1, "8000::"); @@ -464,6 +470,9 @@ test_addr_ip6_helpers(void) test_eq(0, i); i = tor_addr_parse_PTR_name(&t1, "Foobar.baz", AF_UNSPEC, 1); test_eq(0, i); + i = tor_addr_parse_PTR_name(&t1, "9999999999999999999999999999.in-addr.arpa", + AF_UNSPEC, 1); + test_eq(-1, i); i = tor_addr_parse_PTR_name(&t1, "1.0.168.192.in-addr.arpa", AF_UNSPEC, 1); test_eq(1, i); @@ -844,6 +853,90 @@ test_virtaddrmap(void *data) } static void +test_addr_localname(void *arg) +{ + (void)arg; + tt_assert(tor_addr_hostname_is_local("localhost")); + tt_assert(tor_addr_hostname_is_local("LOCALHOST")); + tt_assert(tor_addr_hostname_is_local("LocalHost")); + tt_assert(tor_addr_hostname_is_local("local")); + tt_assert(tor_addr_hostname_is_local("LOCAL")); + tt_assert(tor_addr_hostname_is_local("here.now.local")); + tt_assert(tor_addr_hostname_is_local("here.now.LOCAL")); + + tt_assert(!tor_addr_hostname_is_local(" localhost")); + tt_assert(!tor_addr_hostname_is_local("www.torproject.org")); + done: + ; +} + +static void +test_addr_dup_ip(void *arg) +{ + char *v = NULL; + (void)arg; +#define CHECK(ip, s) do { \ + v = tor_dup_ip(ip); \ + tt_str_op(v,==,(s)); \ + tor_free(v); \ + } while (0) + + CHECK(0xffffffff, "255.255.255.255"); + CHECK(0x00000000, "0.0.0.0"); + CHECK(0x7f000001, "127.0.0.1"); + CHECK(0x01020304, "1.2.3.4"); + +#undef CHECK + done: + tor_free(v); +} + +static void +test_addr_sockaddr_to_str(void *arg) +{ + char *v = NULL; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + struct sockaddr_storage ss; +#ifdef HAVE_SYS_UN_H + struct sockaddr_un sun; +#endif +#define CHECK(sa, s) do { \ + v = tor_sockaddr_to_str((const struct sockaddr*) &(sa)); \ + tt_str_op(v,==,(s)); \ + tor_free(v); \ + } while (0) + (void)arg; + + memset(&ss,0,sizeof(ss)); + ss.ss_family = AF_UNSPEC; + CHECK(ss, "unspec"); + + memset(&sin,0,sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(0x7f808001); + sin.sin_port = htons(1234); + CHECK(sin, "127.128.128.1:1234"); + +#ifdef HAVE_SYS_UN_H + memset(&sun,0,sizeof(sun)); + sun.sun_family = AF_UNIX; + strlcpy(sun.sun_path, "/here/is/a/path", sizeof(sun.sun_path)); + CHECK(sun, "unix:/here/is/a/path"); +#endif + + memset(&sin6,0,sizeof(sin6)); + sin6.sin6_family = AF_INET6; + memcpy(sin6.sin6_addr.s6_addr, "\x20\x00\x00\x00\x00\x00\x00\x00" + "\x00\x1a\x2b\x3c\x4d\x5e\x00\x01", 16); + sin6.sin6_port = htons(1234); + CHECK(sin6, "[2000::1a:2b3c:4d5e:1]:1234"); + + done: + tor_free(v); +} + +static void test_addr_is_loopback(void *data) { static const struct loopback_item { @@ -886,6 +979,9 @@ struct testcase_t addr_tests[] = { ADDR_LEGACY(ip6_helpers), ADDR_LEGACY(parse), { "virtaddr", test_virtaddrmap, 0, NULL, NULL }, + { "localname", test_addr_localname, 0, NULL, NULL }, + { "dup_ip", test_addr_dup_ip, 0, NULL, NULL }, + { "sockaddr_to_str", test_addr_sockaddr_to_str, 0, NULL, NULL }, { "is_loopback", test_addr_is_loopback, 0, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_cell_queue.c b/src/test/test_cell_queue.c new file mode 100644 index 0000000000..cf2d11ad5d --- /dev/null +++ b/src/test/test_cell_queue.c @@ -0,0 +1,146 @@ +/* Copyright (c) 2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CIRCUITLIST_PRIVATE +#define RELAY_PRIVATE +#include "or.h" +#include "circuitlist.h" +#include "relay.h" +#include "test.h" + +static void +test_cq_manip(void *arg) +{ + packed_cell_t *pc1=NULL, *pc2=NULL, *pc3=NULL, *pc4=NULL, *pc_tmp=NULL; + cell_queue_t cq; + cell_t cell; + (void) arg; + + init_cell_pool(); + cell_queue_init(&cq); + tt_int_op(cq.n, ==, 0); + + pc1 = packed_cell_new(); + pc2 = packed_cell_new(); + pc3 = packed_cell_new(); + pc4 = packed_cell_new(); + tt_assert(pc1 && pc2 && pc3 && pc4); + + tt_ptr_op(NULL, ==, cell_queue_pop(&cq)); + + /* Add and remove a singleton. */ + cell_queue_append(&cq, pc1); + tt_int_op(cq.n, ==, 1); + tt_ptr_op(pc1, ==, cell_queue_pop(&cq)); + tt_int_op(cq.n, ==, 0); + + /* Add and remove four items */ + cell_queue_append(&cq, pc4); + cell_queue_append(&cq, pc3); + cell_queue_append(&cq, pc2); + cell_queue_append(&cq, pc1); + tt_int_op(cq.n, ==, 4); + tt_ptr_op(pc4, ==, cell_queue_pop(&cq)); + tt_ptr_op(pc3, ==, cell_queue_pop(&cq)); + tt_ptr_op(pc2, ==, cell_queue_pop(&cq)); + tt_ptr_op(pc1, ==, cell_queue_pop(&cq)); + tt_int_op(cq.n, ==, 0); + tt_ptr_op(NULL, ==, cell_queue_pop(&cq)); + + /* Try a packed copy (wide, then narrow, which is a bit of a cheat, since a + * real cell queue has only one type.) */ + memset(&cell, 0, sizeof(cell)); + cell.circ_id = 0x12345678; + cell.command = 10; + strlcpy((char*)cell.payload, "Lorax ipsum gruvvulus thneed amet, snergelly " + "once-ler lerkim, sed do barbaloot tempor gluppitus ut labore et " + "truffula magna aliqua.", + sizeof(cell.payload)); + cell_queue_append_packed_copy(&cq, &cell, 1 /*wide*/, 0 /*stats*/); + cell.circ_id = 0x2013; + cell_queue_append_packed_copy(&cq, &cell, 0 /*wide*/, 0 /*stats*/); + tt_int_op(cq.n, ==, 2); + + pc_tmp = cell_queue_pop(&cq); + tt_int_op(cq.n, ==, 1); + tt_ptr_op(pc_tmp, !=, NULL); + test_mem_op(pc_tmp->body, ==, "\x12\x34\x56\x78\x0a", 5); + test_mem_op(pc_tmp->body+5, ==, cell.payload, sizeof(cell.payload)); + packed_cell_free(pc_tmp); + + pc_tmp = cell_queue_pop(&cq); + tt_int_op(cq.n, ==, 0); + tt_ptr_op(pc_tmp, !=, NULL); + test_mem_op(pc_tmp->body, ==, "\x20\x13\x0a", 3); + test_mem_op(pc_tmp->body+3, ==, cell.payload, sizeof(cell.payload)); + packed_cell_free(pc_tmp); + pc_tmp = NULL; + + tt_ptr_op(NULL, ==, cell_queue_pop(&cq)); + + /* Now make sure cell_queue_clear works. */ + cell_queue_append(&cq, pc2); + cell_queue_append(&cq, pc1); + tt_int_op(cq.n, ==, 2); + cell_queue_clear(&cq); + pc2 = pc1 = NULL; /* prevent double-free */ + tt_int_op(cq.n, ==, 0); + + done: + packed_cell_free(pc1); + packed_cell_free(pc2); + packed_cell_free(pc3); + packed_cell_free(pc4); + packed_cell_free(pc_tmp); + + cell_queue_clear(&cq); + free_cell_pool(); +} + +static void +test_circuit_n_cells(void *arg) +{ + packed_cell_t *pc1=NULL, *pc2=NULL, *pc3=NULL, *pc4=NULL, *pc5=NULL; + origin_circuit_t *origin_c=NULL; + or_circuit_t *or_c=NULL; + + (void)arg; + + init_cell_pool(); + + pc1 = packed_cell_new(); + pc2 = packed_cell_new(); + pc3 = packed_cell_new(); + pc4 = packed_cell_new(); + pc5 = packed_cell_new(); + tt_assert(pc1 && pc2 && pc3 && pc4 && pc5); + + or_c = or_circuit_new(0, NULL); + origin_c = origin_circuit_new(); + origin_c->base_.purpose = CIRCUIT_PURPOSE_C_GENERAL; + + tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(or_c)), ==, 0); + cell_queue_append(&or_c->p_chan_cells, pc1); + tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(or_c)), ==, 1); + cell_queue_append(&or_c->base_.n_chan_cells, pc2); + cell_queue_append(&or_c->base_.n_chan_cells, pc3); + tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(or_c)), ==, 3); + + tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(origin_c)), ==, 0); + cell_queue_append(&origin_c->base_.n_chan_cells, pc4); + cell_queue_append(&origin_c->base_.n_chan_cells, pc5); + tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(origin_c)), ==, 2); + + done: + circuit_free(TO_CIRCUIT(or_c)); + circuit_free(TO_CIRCUIT(origin_c)); + + free_cell_pool(); +} + +struct testcase_t cell_queue_tests[] = { + { "basic", test_cq_manip, TT_FORK, NULL, NULL, }, + { "circ_n_cells", test_circuit_n_cells, TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_circuitlist.c b/src/test/test_circuitlist.c new file mode 100644 index 0000000000..720b407659 --- /dev/null +++ b/src/test/test_circuitlist.c @@ -0,0 +1,168 @@ +/* Copyright (c) 2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define TOR_CHANNEL_INTERNAL_ +#define CIRCUITLIST_PRIVATE +#include "or.h" +#include "channel.h" +#include "circuitlist.h" +#include "test.h" + +static channel_t * +new_fake_channel(void) +{ + channel_t *chan = tor_malloc_zero(sizeof(channel_t)); + channel_init(chan); + return chan; +} + +static struct { + int ncalls; + void *cmux; + void *circ; + cell_direction_t dir; +} cam; + +static void +circuitmux_attach_mock(circuitmux_t *cmux, circuit_t *circ, + cell_direction_t dir) +{ + ++cam.ncalls; + cam.cmux = cmux; + cam.circ = circ; + cam.dir = dir; +} + +static struct { + int ncalls; + void *cmux; + void *circ; +} cdm; + +static void +circuitmux_detach_mock(circuitmux_t *cmux, circuit_t *circ) +{ + ++cdm.ncalls; + cdm.cmux = cmux; + cdm.circ = circ; +} + +#define GOT_CMUX_ATTACH(mux_, circ_, dir_) do { \ + tt_int_op(cam.ncalls, ==, 1); \ + tt_ptr_op(cam.cmux, ==, (mux_)); \ + tt_ptr_op(cam.circ, ==, (circ_)); \ + tt_ptr_op(cam.dir, ==, (dir_)); \ + memset(&cam, 0, sizeof(cam)); \ + } while (0) + +#define GOT_CMUX_DETACH(mux_, circ_) do { \ + tt_int_op(cdm.ncalls, ==, 1); \ + tt_ptr_op(cdm.cmux, ==, (mux_)); \ + tt_ptr_op(cdm.circ, ==, (circ_)); \ + memset(&cdm, 0, sizeof(cdm)); \ + } while (0) + +static void +test_clist_maps(void *arg) +{ + channel_t *ch1 = new_fake_channel(); + channel_t *ch2 = new_fake_channel(); + channel_t *ch3 = new_fake_channel(); + or_circuit_t *or_c1=NULL, *or_c2=NULL; + + (void) arg; + + MOCK(circuitmux_attach_circuit, circuitmux_attach_mock); + MOCK(circuitmux_detach_circuit, circuitmux_detach_mock); + memset(&cam, 0, sizeof(cam)); + memset(&cdm, 0, sizeof(cdm)); + + ch1->cmux = (void*)0x1001; + ch2->cmux = (void*)0x1002; + ch3->cmux = (void*)0x1003; + + or_c1 = or_circuit_new(100, ch2); + tt_assert(or_c1); + GOT_CMUX_ATTACH(ch2->cmux, or_c1, CELL_DIRECTION_IN); + tt_int_op(or_c1->p_circ_id, ==, 100); + tt_ptr_op(or_c1->p_chan, ==, ch2); + + or_c2 = or_circuit_new(100, ch1); + tt_assert(or_c2); + GOT_CMUX_ATTACH(ch1->cmux, or_c2, CELL_DIRECTION_IN); + tt_int_op(or_c2->p_circ_id, ==, 100); + tt_ptr_op(or_c2->p_chan, ==, ch1); + + circuit_set_n_circid_chan(TO_CIRCUIT(or_c1), 200, ch1); + GOT_CMUX_ATTACH(ch1->cmux, or_c1, CELL_DIRECTION_OUT); + + circuit_set_n_circid_chan(TO_CIRCUIT(or_c2), 200, ch2); + GOT_CMUX_ATTACH(ch2->cmux, or_c2, CELL_DIRECTION_OUT); + + tt_ptr_op(circuit_get_by_circid_channel(200, ch1), ==, TO_CIRCUIT(or_c1)); + tt_ptr_op(circuit_get_by_circid_channel(200, ch2), ==, TO_CIRCUIT(or_c2)); + tt_ptr_op(circuit_get_by_circid_channel(100, ch2), ==, TO_CIRCUIT(or_c1)); + /* Try the same thing again, to test the "fast" path. */ + tt_ptr_op(circuit_get_by_circid_channel(100, ch2), ==, TO_CIRCUIT(or_c1)); + tt_assert(circuit_id_in_use_on_channel(100, ch2)); + tt_assert(! circuit_id_in_use_on_channel(101, ch2)); + + /* Try changing the circuitid and channel of that circuit. */ + circuit_set_p_circid_chan(or_c1, 500, ch3); + GOT_CMUX_DETACH(ch2->cmux, TO_CIRCUIT(or_c1)); + GOT_CMUX_ATTACH(ch3->cmux, TO_CIRCUIT(or_c1), CELL_DIRECTION_IN); + tt_ptr_op(circuit_get_by_circid_channel(100, ch2), ==, NULL); + tt_assert(! circuit_id_in_use_on_channel(100, ch2)); + tt_ptr_op(circuit_get_by_circid_channel(500, ch3), ==, TO_CIRCUIT(or_c1)); + + /* Now let's see about destroy handling. */ + tt_assert(! circuit_id_in_use_on_channel(205, ch2)); + tt_assert(circuit_id_in_use_on_channel(200, ch2)); + channel_note_destroy_pending(ch2, 200); + channel_note_destroy_pending(ch2, 205); + channel_note_destroy_pending(ch1, 100); + tt_assert(circuit_id_in_use_on_channel(205, ch2)) + tt_assert(circuit_id_in_use_on_channel(200, ch2)); + tt_assert(circuit_id_in_use_on_channel(100, ch1)); + + tt_assert(TO_CIRCUIT(or_c2)->n_delete_pending != 0); + tt_ptr_op(circuit_get_by_circid_channel(200, ch2), ==, TO_CIRCUIT(or_c2)); + tt_ptr_op(circuit_get_by_circid_channel(100, ch1), ==, TO_CIRCUIT(or_c2)); + + /* Okay, now free ch2 and make sure that the circuit ID is STILL not + * usable, because we haven't declared the destroy to be nonpending */ + tt_int_op(cdm.ncalls, ==, 0); + circuit_free(TO_CIRCUIT(or_c2)); + or_c2 = NULL; /* prevent free */ + tt_int_op(cdm.ncalls, ==, 2); + memset(&cdm, 0, sizeof(cdm)); + tt_assert(circuit_id_in_use_on_channel(200, ch2)); + tt_assert(circuit_id_in_use_on_channel(100, ch1)); + tt_ptr_op(circuit_get_by_circid_channel(200, ch2), ==, NULL); + tt_ptr_op(circuit_get_by_circid_channel(100, ch1), ==, NULL); + + /* Now say that the destroy is nonpending */ + channel_note_destroy_not_pending(ch2, 200); + tt_ptr_op(circuit_get_by_circid_channel(200, ch2), ==, NULL); + channel_note_destroy_not_pending(ch1, 100); + tt_ptr_op(circuit_get_by_circid_channel(100, ch1), ==, NULL); + tt_assert(! circuit_id_in_use_on_channel(200, ch2)); + tt_assert(! circuit_id_in_use_on_channel(100, ch1)); + + done: + tor_free(ch1); + tor_free(ch2); + tor_free(ch3); + if (or_c1) + circuit_free(TO_CIRCUIT(or_c1)); + if (or_c2) + circuit_free(TO_CIRCUIT(or_c2)); + UNMOCK(circuitmux_attach_circuit); + UNMOCK(circuitmux_detach_circuit); +} + +struct testcase_t circuitlist_tests[] = { + { "maps", test_clist_maps, TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_circuitmux.c b/src/test/test_circuitmux.c new file mode 100644 index 0000000000..0f592001cb --- /dev/null +++ b/src/test/test_circuitmux.c @@ -0,0 +1,84 @@ +/* Copyright (c) 2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define TOR_CHANNEL_INTERNAL_ +#define CIRCUITMUX_PRIVATE +#define RELAY_PRIVATE +#include "or.h" +#include "channel.h" +#include "circuitmux.h" +#include "relay.h" +#include "test.h" + +/* XXXX duplicated function from test_circuitlist.c */ +static channel_t * +new_fake_channel(void) +{ + channel_t *chan = tor_malloc_zero(sizeof(channel_t)); + channel_init(chan); + return chan; +} + +static int +has_queued_writes(channel_t *c) +{ + (void) c; + return 1; +} + +/** Test destroy cell queue with no interference from other queues. */ +static void +test_cmux_destroy_cell_queue(void *arg) +{ + circuitmux_t *cmux = NULL; + channel_t *ch = NULL; + circuit_t *circ = NULL; + cell_queue_t *cq = NULL; + packed_cell_t *pc = NULL; + + init_cell_pool(); + (void) arg; + + cmux = circuitmux_alloc(); + tt_assert(cmux); + ch = new_fake_channel(); + ch->has_queued_writes = has_queued_writes; + ch->wide_circ_ids = 1; + + circ = circuitmux_get_first_active_circuit(cmux, &cq); + tt_assert(!circ); + tt_assert(!cq); + + circuitmux_append_destroy_cell(ch, cmux, 100, 10); + circuitmux_append_destroy_cell(ch, cmux, 190, 6); + circuitmux_append_destroy_cell(ch, cmux, 30, 1); + + tt_int_op(circuitmux_num_cells(cmux), ==, 3); + + circ = circuitmux_get_first_active_circuit(cmux, &cq); + tt_assert(!circ); + tt_assert(cq); + + tt_int_op(cq->n, ==, 3); + + pc = cell_queue_pop(cq); + tt_assert(pc); + test_mem_op(pc->body, ==, "\x00\x00\x00\x64\x04\x0a\x00\x00\x00", 9); + packed_cell_free(pc); + pc = NULL; + + tt_int_op(circuitmux_num_cells(cmux), ==, 2); + + done: + circuitmux_free(cmux); + channel_free(ch); + packed_cell_free(pc); + + free_cell_pool(); +} + +struct testcase_t circuitmux_tests[] = { + { "destroy_cell_queue", test_cmux_destroy_cell_queue, TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_config.c b/src/test/test_config.c index e20fe73295..3848d352d5 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -10,6 +10,8 @@ #include "confparse.h" #include "connection_edge.h" #include "test.h" +#include "util.h" +#include "address.h" static void test_config_addressmap(void *arg) @@ -169,11 +171,353 @@ test_config_addressmap(void *arg) ; } +static int +is_private_dir(const char* path) +{ + struct stat st; + int r = stat(path, &st); + if (r) { + return 0; + } +#if !defined (_WIN32) || defined (WINCE) + if ((st.st_mode & (S_IFDIR | 0777)) != (S_IFDIR | 0700)) { + return 0; + } +#endif + return 1; +} + +static void +test_config_check_or_create_data_subdir(void *arg) +{ + or_options_t *options = get_options_mutable(); + char *datadir = options->DataDirectory = tor_strdup(get_fname("datadir-0")); + const char *subdir = "test_stats"; + char *subpath = get_datadir_fname(subdir); + struct stat st; + int r; +#if !defined (_WIN32) || defined (WINCE) + unsigned group_permission; +#endif + (void)arg; + +#if defined (_WIN32) && !defined (WINCE) + tt_int_op(mkdir(options->DataDirectory), ==, 0); +#else + tt_int_op(mkdir(options->DataDirectory, 0700), ==, 0); +#endif + + r = stat(subpath, &st); + + // The subdirectory shouldn't exist yet, + // but should be created by the call to check_or_create_data_subdir. + test_assert(r && (errno == ENOENT)); + test_assert(!check_or_create_data_subdir(subdir)); + test_assert(is_private_dir(subpath)); + + // The check should return 0, if the directory already exists + // and is private to the user. + test_assert(!check_or_create_data_subdir(subdir)); + +#if !defined (_WIN32) || defined (WINCE) + group_permission = st.st_mode | 0070; + r = chmod(subpath, group_permission); + + if (r) { + test_fail_msg("Changing permissions for the subdirectory failed."); + } + + // If the directory exists, but its mode is too permissive + // a call to check_or_create_data_subdir should reset the mode. + test_assert(!is_private_dir(subpath)); + test_assert(!check_or_create_data_subdir(subdir)); + test_assert(is_private_dir(subpath)); +#endif + + done: + rmdir(subpath); + tor_free(datadir); + tor_free(subpath); +} + +static void +test_config_write_to_data_subdir(void *arg) +{ + or_options_t* options = get_options_mutable(); + char *datadir = options->DataDirectory = tor_strdup(get_fname("datadir-1")); + const char* subdir = "test_stats"; + const char* fname = "test_file"; + const char* str = + "Lorem ipsum dolor sit amet, consetetur sadipscing\n" + "elitr, sed diam nonumy eirmod\n" + "tempor invidunt ut labore et dolore magna aliquyam\n" + "erat, sed diam voluptua.\n" + "At vero eos et accusam et justo duo dolores et ea\n" + "rebum. Stet clita kasd gubergren,\n" + "no sea takimata sanctus est Lorem ipsum dolor sit amet.\n" + "Lorem ipsum dolor sit amet,\n" + "consetetur sadipscing elitr, sed diam nonumy eirmod\n" + "tempor invidunt ut labore et dolore\n" + "magna aliquyam erat, sed diam voluptua. At vero eos et\n" + "accusam et justo duo dolores et\n" + "ea rebum. Stet clita kasd gubergren, no sea takimata\n" + "sanctus est Lorem ipsum dolor sit amet."; + char* filepath = get_datadir_fname2(subdir, fname); + (void)arg; + +#if defined (_WIN32) && !defined (WINCE) + tt_int_op(mkdir(options->DataDirectory), ==, 0); +#else + tt_int_op(mkdir(options->DataDirectory, 0700), ==, 0); +#endif + + // Write attempt shoudl fail, if subdirectory doesn't exist. + test_assert(write_to_data_subdir(subdir, fname, str, NULL)); + test_assert(! check_or_create_data_subdir(subdir)); + + // Content of file after write attempt should be + // equal to the original string. + test_assert(!write_to_data_subdir(subdir, fname, str, NULL)); + test_streq(read_file_to_str(filepath, 0, NULL), str); + + // A second write operation should overwrite the old content. + test_assert(!write_to_data_subdir(subdir, fname, str, NULL)); + test_streq(read_file_to_str(filepath, 0, NULL), str); + + done: + (void) unlink(filepath); + rmdir(options->DataDirectory); + tor_free(datadir); + tor_free(filepath); +} + +/* Test helper function: Make sure that a bridge line gets parsed + * properly. Also make sure that the resulting bridge_line_t structure + * has its fields set correctly. */ +static void +good_bridge_line_test(const char *string, const char *test_addrport, + const char *test_digest, const char *test_transport, + const smartlist_t *test_socks_args) +{ + char *tmp = NULL; + bridge_line_t *bridge_line = parse_bridge_line(string); + test_assert(bridge_line); + + /* test addrport */ + tmp = tor_strdup(fmt_addrport(&bridge_line->addr, bridge_line->port)); + test_streq(test_addrport, tmp); + tor_free(tmp); + + /* If we were asked to validate a digest, but we did not get a + digest after parsing, we failed. */ + if (test_digest && tor_digest_is_zero(bridge_line->digest)) + test_assert(0); + + /* If we were not asked to validate a digest, and we got a digest + after parsing, we failed again. */ + if (!test_digest && !tor_digest_is_zero(bridge_line->digest)) + test_assert(0); + + /* If we were asked to validate a digest, and we got a digest after + parsing, make sure it's correct. */ + if (test_digest) { + tmp = tor_strdup(hex_str(bridge_line->digest, DIGEST_LEN)); + tor_strlower(tmp); + test_streq(test_digest, tmp); + tor_free(tmp); + } + + /* If we were asked to validate a transport name, make sure tha it + matches with the transport name that was parsed. */ + if (test_transport && !bridge_line->transport_name) + test_assert(0); + if (!test_transport && bridge_line->transport_name) + test_assert(0); + if (test_transport) + test_streq(test_transport, bridge_line->transport_name); + + /* Validate the SOCKS argument smartlist. */ + if (test_socks_args && !bridge_line->socks_args) + test_assert(0); + if (!test_socks_args && bridge_line->socks_args) + test_assert(0); + if (test_socks_args) + test_assert(smartlist_strings_eq(test_socks_args, + bridge_line->socks_args)); + + done: + tor_free(tmp); + bridge_line_free(bridge_line); +} + +/* Test helper function: Make sure that a bridge line is + * unparseable. */ +static void +bad_bridge_line_test(const char *string) +{ + bridge_line_t *bridge_line = parse_bridge_line(string); + test_assert(!bridge_line); + + done: + bridge_line_free(bridge_line); +} + +static void +test_config_parse_bridge_line(void *arg) +{ + (void) arg; + good_bridge_line_test("192.0.2.1:4123", + "192.0.2.1:4123", NULL, NULL, NULL); + + good_bridge_line_test("192.0.2.1", + "192.0.2.1:443", NULL, NULL, NULL); + + good_bridge_line_test("transport [::1]", + "[::1]:443", NULL, "transport", NULL); + + good_bridge_line_test("transport 192.0.2.1:12 " + "4352e58420e68f5e40bf7c74faddccd9d1349413", + "192.0.2.1:12", + "4352e58420e68f5e40bf7c74faddccd9d1349413", + "transport", NULL); + + { + smartlist_t *sl_tmp = smartlist_new(); + smartlist_add_asprintf(sl_tmp, "twoandtwo=five"); + + good_bridge_line_test("transport 192.0.2.1:12 " + "4352e58420e68f5e40bf7c74faddccd9d1349413 twoandtwo=five", + "192.0.2.1:12", "4352e58420e68f5e40bf7c74faddccd9d1349413", + "transport", sl_tmp); + + SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s)); + smartlist_free(sl_tmp); + } + + { + smartlist_t *sl_tmp = smartlist_new(); + smartlist_add_asprintf(sl_tmp, "twoandtwo=five"); + smartlist_add_asprintf(sl_tmp, "z=z"); + + good_bridge_line_test("transport 192.0.2.1:12 twoandtwo=five z=z", + "192.0.2.1:12", NULL, "transport", sl_tmp); + + SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s)); + smartlist_free(sl_tmp); + } + + good_bridge_line_test("192.0.2.1:1231 " + "4352e58420e68f5e40bf7c74faddccd9d1349413", + "192.0.2.1:1231", + "4352e58420e68f5e40bf7c74faddccd9d1349413", + NULL, NULL); + + /* Empty line */ + bad_bridge_line_test(""); + /* bad transport name */ + bad_bridge_line_test("tr$n_sp0r7 190.20.2.2"); + /* weird ip address */ + bad_bridge_line_test("a.b.c.d"); + /* invalid fpr */ + bad_bridge_line_test("2.2.2.2:1231 4352e58420e68f5e40bf7c74faddccd9d1349"); + /* no k=v in the end */ + bad_bridge_line_test("obfs2 2.2.2.2:1231 " + "4352e58420e68f5e40bf7c74faddccd9d1349413 what"); + /* no addrport */ + bad_bridge_line_test("asdw"); + /* huge k=v value that can't fit in SOCKS fields */ + bad_bridge_line_test( + "obfs2 2.2.2.2:1231 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aa=b"); +} + +static void +test_config_parse_transport_options_line(void *arg) +{ + smartlist_t *options_sl = NULL, *sl_tmp = NULL; + + (void) arg; + + { /* too small line */ + options_sl = get_options_from_transport_options_line("valley", NULL); + test_assert(!options_sl); + } + + { /* no k=v values */ + options_sl = get_options_from_transport_options_line("hit it!", NULL); + test_assert(!options_sl); + } + + { /* correct line, but wrong transport specified */ + options_sl = + get_options_from_transport_options_line("trebuchet k=v", "rook"); + test_assert(!options_sl); + } + + { /* correct -- no transport specified */ + sl_tmp = smartlist_new(); + smartlist_add_asprintf(sl_tmp, "ladi=dadi"); + smartlist_add_asprintf(sl_tmp, "weliketo=party"); + + options_sl = + get_options_from_transport_options_line("rook ladi=dadi weliketo=party", + NULL); + test_assert(options_sl); + test_assert(smartlist_strings_eq(options_sl, sl_tmp)); + + SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s)); + smartlist_free(sl_tmp); + sl_tmp = NULL; + SMARTLIST_FOREACH(options_sl, char *, s, tor_free(s)); + smartlist_free(options_sl); + options_sl = NULL; + } + + { /* correct -- correct transport specified */ + sl_tmp = smartlist_new(); + smartlist_add_asprintf(sl_tmp, "ladi=dadi"); + smartlist_add_asprintf(sl_tmp, "weliketo=party"); + + options_sl = + get_options_from_transport_options_line("rook ladi=dadi weliketo=party", + "rook"); + test_assert(options_sl); + test_assert(smartlist_strings_eq(options_sl, sl_tmp)); + SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s)); + smartlist_free(sl_tmp); + sl_tmp = NULL; + SMARTLIST_FOREACH(options_sl, char *, s, tor_free(s)); + smartlist_free(options_sl); + options_sl = NULL; + } + + done: + if (options_sl) { + SMARTLIST_FOREACH(options_sl, char *, s, tor_free(s)); + smartlist_free(options_sl); + } + if (sl_tmp) { + SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s)); + smartlist_free(sl_tmp); + } +} + #define CONFIG_TEST(name, flags) \ { #name, test_config_ ## name, flags, NULL, NULL } struct testcase_t config_tests[] = { CONFIG_TEST(addressmap, 0), + CONFIG_TEST(parse_bridge_line, 0), + CONFIG_TEST(parse_transport_options_line, 0), + CONFIG_TEST(check_or_create_data_subdir, TT_FORK), + CONFIG_TEST(write_to_data_subdir, TT_FORK), END_OF_TESTCASES }; diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index f92bfd673e..9dc43b1d27 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -4,7 +4,6 @@ /* See LICENSE for licensing information */ #include "orconfig.h" -#define CRYPTO_PRIVATE #define CRYPTO_CURVE25519_PRIVATE #include "or.h" #include "test.h" @@ -14,6 +13,10 @@ #include "crypto_curve25519.h" #endif +extern const char AUTHORITY_SIGNKEY_1[]; +extern const char AUTHORITY_SIGNKEY_1_DIGEST[]; +extern const char AUTHORITY_SIGNKEY_1_DIGEST256[]; + /** Run unit tests for Diffie-Hellman functionality. */ static void test_crypto_dh(void) @@ -269,34 +272,6 @@ test_crypto_sha(void) "96177A9CB410FF61F20015AD"); tt_int_op(i, ==, 0); - /* Test HMAC-SHA-1 with test cases from RFC2202. */ - - /* Case 1. */ - memset(key, 0x0b, 20); - crypto_hmac_sha1(digest, key, 20, "Hi There", 8); - test_streq(hex_str(digest, 20), - "B617318655057264E28BC0B6FB378C8EF146BE00"); - /* Case 2. */ - crypto_hmac_sha1(digest, "Jefe", 4, "what do ya want for nothing?", 28); - test_streq(hex_str(digest, 20), - "EFFCDF6AE5EB2FA2D27416D5F184DF9C259A7C79"); - - /* Case 4. */ - base16_decode(key, 25, - "0102030405060708090a0b0c0d0e0f10111213141516171819", 50); - memset(data, 0xcd, 50); - crypto_hmac_sha1(digest, key, 25, data, 50); - test_streq(hex_str(digest, 20), - "4C9007F4026250C6BC8414F9BF50C86C2D7235DA"); - - /* Case 5. */ - memset(key, 0xaa, 80); - crypto_hmac_sha1(digest, key, 80, - "Test Using Larger Than Block-Size Key - Hash Key First", - 54); - test_streq(hex_str(digest, 20), - "AA4AE5E15272D00E95705637CE8A3B55ED402112"); - /* Test HMAC-SHA256 with test cases from wikipedia and RFC 4231 */ /* Case empty (wikipedia) */ @@ -422,7 +397,7 @@ test_crypto_pk(void) char *encoded = NULL; char data1[1024], data2[1024], data3[1024]; size_t size; - int i, j, p, len; + int i, len; /* Public-key ciphers */ pk1 = pk_generate(0); @@ -506,19 +481,16 @@ test_crypto_pk(void) /* Try with hybrid encryption wrappers. */ crypto_rand(data1, 1024); - for (i = 0; i < 2; ++i) { - for (j = 85; j < 140; ++j) { - memset(data2,0,1024); - memset(data3,0,1024); - p = (i==0)?PK_PKCS1_PADDING:PK_PKCS1_OAEP_PADDING; - len = crypto_pk_public_hybrid_encrypt(pk1,data2,sizeof(data2), - data1,j,p,0); - test_assert(len>=0); - len = crypto_pk_private_hybrid_decrypt(pk1,data3,sizeof(data3), - data2,len,p,1); - test_eq(len,j); - test_memeq(data1,data3,j); - } + for (i = 85; i < 140; ++i) { + memset(data2,0,1024); + memset(data3,0,1024); + len = crypto_pk_public_hybrid_encrypt(pk1,data2,sizeof(data2), + data1,i,PK_PKCS1_OAEP_PADDING,0); + test_assert(len>=0); + len = crypto_pk_private_hybrid_decrypt(pk1,data3,sizeof(data3), + data2,len,PK_PKCS1_OAEP_PADDING,1); + test_eq(len,i); + test_memeq(data1,data3,i); } /* Try copy_full */ @@ -536,6 +508,35 @@ test_crypto_pk(void) tor_free(encoded); } +/** Sanity check for crypto pk digests */ +static void +test_crypto_digests(void) +{ + crypto_pk_t *k = NULL; + ssize_t r; + digests_t pkey_digests; + char digest[DIGEST_LEN]; + + k = crypto_pk_new(); + test_assert(k); + r = crypto_pk_read_private_key_from_string(k, AUTHORITY_SIGNKEY_1, -1); + test_assert(!r); + + r = crypto_pk_get_digest(k, digest); + test_assert(r == 0); + test_memeq(hex_str(digest, DIGEST_LEN), + AUTHORITY_SIGNKEY_1_DIGEST, HEX_DIGEST_LEN); + + r = crypto_pk_get_all_digests(k, &pkey_digests); + + test_memeq(hex_str(pkey_digests.d[DIGEST_SHA1], DIGEST_LEN), + AUTHORITY_SIGNKEY_1_DIGEST, HEX_DIGEST_LEN); + test_memeq(hex_str(pkey_digests.d[DIGEST_SHA256], DIGEST256_LEN), + AUTHORITY_SIGNKEY_1_DIGEST256, HEX_DIGEST256_LEN); + done: + crypto_pk_free(k); +} + /** Run unit tests for misc crypto formatting functionality (base64, base32, * fingerprints, etc) */ static void @@ -630,7 +631,7 @@ test_crypto_formats(void) data1 = tor_strdup("ABCD1234ABCD56780000ABCD1234ABCD56780000"); test_eq(strlen(data1), 40); data2 = tor_malloc(FINGERPRINT_LEN+1); - add_spaces_to_fp(data2, FINGERPRINT_LEN+1, data1); + crypto_add_spaces_to_fp(data2, FINGERPRINT_LEN+1, data1); test_streq(data2, "ABCD 1234 ABCD 5678 0000 ABCD 1234 ABCD 5678 0000"); tor_free(data1); tor_free(data2); @@ -1134,6 +1135,7 @@ struct testcase_t crypto_tests[] = { { "aes_EVP", test_crypto_aes, TT_FORK, &pass_data, (void*)"evp" }, CRYPTO_LEGACY(sha), CRYPTO_LEGACY(pk), + CRYPTO_LEGACY(digests), CRYPTO_LEGACY(dh), CRYPTO_LEGACY(s2k), { "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"aes" }, diff --git a/src/test/test_data.c b/src/test/test_data.c index 5f0f7cba01..3c68b1294b 100644 --- a/src/test/test_data.c +++ b/src/test/test_data.c @@ -63,6 +63,11 @@ const char AUTHORITY_SIGNKEY_1[] = "Yx4lqK0ca5IkTp3HevwnlWaJgbaOTUspCVshzJBhDA==\n" "-----END RSA PRIVATE KEY-----\n"; +const char AUTHORITY_SIGNKEY_1_DIGEST[] = + "CBF56A83368A5150F1A9AAADAFB4D77F8C4170E2"; +const char AUTHORITY_SIGNKEY_1_DIGEST256[] = + "AF7C5468DBE3BA54A052726038D7F15F3C4CA511B1952645B3D96D83A8DFB51C"; + /** Second of 3 example authority certificates for unit testing. */ const char AUTHORITY_CERT_2[] = "dir-key-certificate-version 3\n" diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 56ac3b34c7..6c2915d094 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -2317,9 +2317,9 @@ test_dir_v2_dir(void *arg) /* Make a directory so there's somewhere to store the thing */ #ifdef _WIN32 - mkdir(get_fname("cached-status")); + tt_int_op(mkdir(get_fname("cached-status")), ==, 0); #else - mkdir(get_fname("cached-status"), 0700); + tt_int_op(mkdir(get_fname("cached-status"), 0700), ==, 0); #endif v2 = generate_v2_networkstatus_opinion(); diff --git a/src/test/test_options.c b/src/test/test_options.c new file mode 100644 index 0000000000..6beff2567e --- /dev/null +++ b/src/test/test_options.c @@ -0,0 +1,169 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CONFIG_PRIVATE +#include "or.h" +#include "confparse.h" +#include "config.h" +#include "test.h" + +typedef struct { + int severity; + uint32_t domain; + char *msg; +} logmsg_t; + +static smartlist_t *messages = NULL; + +static void +log_cback(int severity, uint32_t domain, const char *msg) +{ + logmsg_t *x = tor_malloc(sizeof(*x)); + x->severity = severity; + x->domain = domain; + x->msg = tor_strdup(msg); + if (!messages) + messages = smartlist_new(); + smartlist_add(messages, x); +} + +static void +setup_log_callback(void) +{ + log_severity_list_t lst; + memset(&lst, 0, sizeof(lst)); + lst.masks[LOG_ERR - LOG_ERR] = ~0; + lst.masks[LOG_WARN - LOG_ERR] = ~0; + lst.masks[LOG_NOTICE - LOG_ERR] = ~0; + add_callback_log(&lst, log_cback); +} + +static char * +dump_logs(void) +{ + smartlist_t *msgs; + char *out; + if (! messages) + return tor_strdup(""); + msgs = smartlist_new(); + SMARTLIST_FOREACH_BEGIN(messages, logmsg_t *, x) { + smartlist_add_asprintf(msgs, "[%s] %s", + log_level_to_string(x->severity), x->msg); + } SMARTLIST_FOREACH_END(x); + out = smartlist_join_strings(msgs, "", 0, NULL); + SMARTLIST_FOREACH(msgs, char *, cp, tor_free(cp)); + smartlist_free(msgs); + return out; +} + +static void +clear_log_messages(void) +{ + if (!messages) + return; + SMARTLIST_FOREACH(messages, logmsg_t *, m, + { tor_free(m->msg); tor_free(m); }); + smartlist_free(messages); + messages = NULL; +} + +static void +test_options_validate_impl(const char *configuration, + const char *expect_errmsg, + int expect_log_severity, + const char *expect_log) +{ + or_options_t *opt = options_new(); + or_options_t *dflt; + config_line_t *cl=NULL; + char *msg=NULL; + int r; + opt->command = CMD_RUN_TOR; + options_init(opt); + + dflt = config_dup(&options_format, opt); + clear_log_messages(); + + r = config_get_lines(configuration, &cl, 1); + tt_int_op(r, ==, 0); + + r = config_assign(&options_format, opt, cl, 0, 0, &msg); + tt_int_op(r, ==, 0); + + r = options_validate(NULL, opt, dflt, 0, &msg); + if (expect_errmsg && !msg) { + TT_DIE(("Expected error message <%s> from <%s>, but got none.", + expect_errmsg, configuration)); + } else if (expect_errmsg && !strstr(msg, expect_errmsg)) { + TT_DIE(("Expected error message <%s> from <%s>, but got <%s>.", + expect_errmsg, configuration, msg)); + } else if (!expect_errmsg && msg) { + TT_DIE(("Expected no error message from <%s> but got <%s>.", + configuration, msg)); + } + tt_int_op((r == 0), ==, (msg == NULL)); + + if (expect_log) { + int found = 0; + if (messages) { + SMARTLIST_FOREACH_BEGIN(messages, logmsg_t *, m) { + if (m->severity == expect_log_severity && + strstr(m->msg, expect_log)) { + found = 1; + break; + } + } SMARTLIST_FOREACH_END(m); + } + if (!found) { + tor_free(msg); + msg = dump_logs(); + TT_DIE(("Expected log message [%s] %s from <%s>, but got <%s>.", + log_level_to_string(expect_log_severity), expect_log, + configuration, msg)); + } + } + + done: + config_free_lines(cl); + or_options_free(opt); + or_options_free(dflt); + tor_free(msg); + clear_log_messages(); +} + +#define WANT_ERR(config, msg) \ + test_options_validate_impl((config), (msg), 0, NULL) +#define WANT_LOG(config, severity, msg) \ + test_options_validate_impl((config), NULL, (severity), (msg)) +#define WANT_ERR_LOG(config, msg, severity, logmsg) \ + test_options_validate_impl((config), (msg), (severity), (logmsg)) +#define OK(config) \ + test_options_validate_impl((config), NULL, 0, NULL) + +static void +test_options_validate(void *arg) +{ + (void)arg; + setup_log_callback(); + + WANT_ERR_LOG("ServerTransportOptions trebuchet", + "ServerTransportOptions did not parse", + LOG_WARN, "Too few arguments"); + OK("ServerTransportOptions trebuchet sling=snappy"); + OK("ServerTransportOptions trebuchet sling="); + WANT_ERR_LOG("ServerTransportOptions trebuchet slingsnappy", + "ServerTransportOptions did not parse", + LOG_WARN, "\"slingsnappy\" is not a k=v"); + +// done: + clear_log_messages(); + return; +} + +struct testcase_t options_tests[] = { + { "validate", test_options_validate, TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_pt.c b/src/test/test_pt.c index 80707f4379..f969457d0e 100644 --- a/src/test/test_pt.c +++ b/src/test/test_pt.c @@ -5,9 +5,14 @@ #include "orconfig.h" #define PT_PRIVATE +#define UTIL_PRIVATE #include "or.h" +#include "config.h" +#include "confparse.h" #include "transports.h" #include "circuitbuild.h" +#include "util.h" +#include "statefile.h" #include "test.h" static void @@ -22,64 +27,102 @@ static void test_pt_parsing(void) { char line[200]; + transport_t *transport = NULL; + tor_addr_t test_addr; managed_proxy_t *mp = tor_malloc(sizeof(managed_proxy_t)); mp->conf_state = PT_PROTO_INFANT; mp->transports = smartlist_new(); /* incomplete cmethod */ - strcpy(line,"CMETHOD trebuchet"); + strlcpy(line,"CMETHOD trebuchet",sizeof(line)); test_assert(parse_cmethod_line(line, mp) < 0); reset_mp(mp); /* wrong proxy type */ - strcpy(line,"CMETHOD trebuchet dog 127.0.0.1:1999"); + strlcpy(line,"CMETHOD trebuchet dog 127.0.0.1:1999",sizeof(line)); test_assert(parse_cmethod_line(line, mp) < 0); reset_mp(mp); /* wrong addrport */ - strcpy(line,"CMETHOD trebuchet socks4 abcd"); + strlcpy(line,"CMETHOD trebuchet socks4 abcd",sizeof(line)); test_assert(parse_cmethod_line(line, mp) < 0); reset_mp(mp); /* correct line */ - strcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999"); + strlcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999",sizeof(line)); test_assert(parse_cmethod_line(line, mp) == 0); - test_assert(smartlist_len(mp->transports)); + test_assert(smartlist_len(mp->transports) == 1); + transport = smartlist_get(mp->transports, 0); + /* test registered address of transport */ + tor_addr_parse(&test_addr, "127.0.0.1"); + test_assert(tor_addr_eq(&test_addr, &transport->addr)); + /* test registered port of transport */ + test_assert(transport->port == 1999); + /* test registered SOCKS version of transport */ + test_assert(transport->socks_version == PROXY_SOCKS5); + /* test registered name of transport */ + test_streq(transport->name, "trebuchet"); reset_mp(mp); /* incomplete smethod */ - strcpy(line,"SMETHOD trebuchet"); + strlcpy(line,"SMETHOD trebuchet",sizeof(line)); test_assert(parse_smethod_line(line, mp) < 0); reset_mp(mp); /* wrong addr type */ - strcpy(line,"SMETHOD trebuchet abcd"); + strlcpy(line,"SMETHOD trebuchet abcd",sizeof(line)); test_assert(parse_smethod_line(line, mp) < 0); reset_mp(mp); /* cowwect */ - strcpy(line,"SMETHOD trebuchy 127.0.0.1:1999"); + strlcpy(line,"SMETHOD trebuchy 127.0.0.2:2999",sizeof(line)); test_assert(parse_smethod_line(line, mp) == 0); + test_assert(smartlist_len(mp->transports) == 1); + transport = smartlist_get(mp->transports, 0); + /* test registered address of transport */ + tor_addr_parse(&test_addr, "127.0.0.2"); + test_assert(tor_addr_eq(&test_addr, &transport->addr)); + /* test registered port of transport */ + test_assert(transport->port == 2999); + /* test registered name of transport */ + test_streq(transport->name, "trebuchy"); reset_mp(mp); + /* Include some arguments. Good ones. */ + strlcpy(line,"SMETHOD trebuchet 127.0.0.1:9999 " + "ARGS:counterweight=3,sling=snappy", + sizeof(line)); + test_assert(parse_smethod_line(line, mp) == 0); + tt_int_op(1, ==, smartlist_len(mp->transports)); + { + const transport_t *transport = smartlist_get(mp->transports, 0); + tt_assert(transport); + tt_str_op(transport->name, ==, "trebuchet"); + tt_int_op(transport->port, ==, 9999); + tt_str_op(fmt_addr(&transport->addr), ==, "127.0.0.1"); + tt_str_op(transport->extra_info_args, ==, + "counterweight=3,sling=snappy"); + } + reset_mp(mp); + /* unsupported version */ - strcpy(line,"VERSION 666"); + strlcpy(line,"VERSION 666",sizeof(line)); test_assert(parse_version(line, mp) < 0); /* incomplete VERSION */ - strcpy(line,"VERSION "); + strlcpy(line,"VERSION ",sizeof(line)); test_assert(parse_version(line, mp) < 0); /* correct VERSION */ - strcpy(line,"VERSION 1"); + strlcpy(line,"VERSION 1",sizeof(line)); test_assert(parse_version(line, mp) == 0); done: @@ -87,6 +130,58 @@ test_pt_parsing(void) } static void +test_pt_get_transport_options(void *arg) +{ + char **execve_args; + smartlist_t *transport_list = smartlist_new(); + managed_proxy_t *mp; + or_options_t *options = get_options_mutable(); + char *opt_str = NULL; + config_line_t *cl = NULL; + (void)arg; + + execve_args = tor_malloc(sizeof(char*)*2); + execve_args[0] = tor_strdup("cheeseshop"); + execve_args[1] = NULL; + + mp = managed_proxy_create(transport_list, execve_args, 1); + tt_ptr_op(mp, !=, NULL); + opt_str = get_transport_options_for_server_proxy(mp); + tt_ptr_op(opt_str, ==, NULL); + + smartlist_add(mp->transports_to_launch, tor_strdup("gruyere")); + smartlist_add(mp->transports_to_launch, tor_strdup("roquefort")); + smartlist_add(mp->transports_to_launch, tor_strdup("stnectaire")); + + tt_assert(options); + + cl = tor_malloc_zero(sizeof(config_line_t)); + cl->value = tor_strdup("gruyere melty=10 hardness=se;ven"); + options->ServerTransportOptions = cl; + + cl = tor_malloc_zero(sizeof(config_line_t)); + cl->value = tor_strdup("stnectaire melty=4 hardness=three"); + cl->next = options->ServerTransportOptions; + options->ServerTransportOptions = cl; + + cl = tor_malloc_zero(sizeof(config_line_t)); + cl->value = tor_strdup("pepperjack melty=12 hardness=five"); + cl->next = options->ServerTransportOptions; + options->ServerTransportOptions = cl; + + opt_str = get_transport_options_for_server_proxy(mp); + tt_str_op(opt_str, ==, + "gruyere:melty=10;gruyere:hardness=se\\;ven;" + "stnectaire:melty=4;stnectaire:hardness=three"); + + done: + tor_free(opt_str); + config_free_lines(cl); + managed_proxy_destroy(mp, 0); + smartlist_free(transport_list); +} + +static void test_pt_protocol(void) { char line[200]; @@ -99,32 +194,32 @@ test_pt_protocol(void) /* various wrong protocol runs: */ - strcpy(line,"VERSION 1"); + strlcpy(line,"VERSION 1",sizeof(line)); handle_proxy_line(line, mp); test_assert(mp->conf_state == PT_PROTO_ACCEPTING_METHODS); - strcpy(line,"VERSION 1"); + strlcpy(line,"VERSION 1",sizeof(line)); handle_proxy_line(line, mp); test_assert(mp->conf_state == PT_PROTO_BROKEN); reset_mp(mp); - strcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999"); + strlcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999",sizeof(line)); handle_proxy_line(line, mp); test_assert(mp->conf_state == PT_PROTO_BROKEN); reset_mp(mp); /* correct protocol run: */ - strcpy(line,"VERSION 1"); + strlcpy(line,"VERSION 1",sizeof(line)); handle_proxy_line(line, mp); test_assert(mp->conf_state == PT_PROTO_ACCEPTING_METHODS); - strcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999"); + strlcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999",sizeof(line)); handle_proxy_line(line, mp); test_assert(mp->conf_state == PT_PROTO_ACCEPTING_METHODS); - strcpy(line,"CMETHODS DONE"); + strlcpy(line,"CMETHODS DONE",sizeof(line)); handle_proxy_line(line, mp); test_assert(mp->conf_state == PT_PROTO_CONFIGURED); @@ -132,12 +227,147 @@ test_pt_protocol(void) tor_free(mp); } +static void +test_pt_get_extrainfo_string(void *arg) +{ + managed_proxy_t *mp1 = NULL, *mp2 = NULL; + char **argv1, **argv2; + smartlist_t *t1 = smartlist_new(), *t2 = smartlist_new(); + int r; + char *s = NULL; + (void) arg; + + argv1 = tor_malloc_zero(sizeof(char*)*3); + argv1[0] = tor_strdup("ewige"); + argv1[1] = tor_strdup("Blumenkraft"); + argv1[2] = NULL; + argv2 = tor_malloc_zero(sizeof(char*)*4); + argv2[0] = tor_strdup("und"); + argv2[1] = tor_strdup("ewige"); + argv2[2] = tor_strdup("Schlangenkraft"); + argv2[3] = NULL; + + mp1 = managed_proxy_create(t1, argv1, 1); + mp2 = managed_proxy_create(t2, argv2, 1); + + r = parse_smethod_line("SMETHOD hagbard 127.0.0.1:5555", mp1); + tt_int_op(r, ==, 0); + r = parse_smethod_line("SMETHOD celine 127.0.0.1:1723 ARGS:card=no-enemy", + mp2); + tt_int_op(r, ==, 0); + + /* Force these proxies to look "completed" or they won't generate output. */ + mp1->conf_state = mp2->conf_state = PT_PROTO_COMPLETED; + + s = pt_get_extra_info_descriptor_string(); + tt_assert(s); + tt_str_op(s, ==, + "transport hagbard 127.0.0.1:5555\n" + "transport celine 127.0.0.1:1723 card=no-enemy\n"); + + done: + /* XXXX clean up better */ + smartlist_free(t1); + smartlist_free(t2); + tor_free(s); +} + +#ifdef _WIN32 +#define STDIN_HANDLE HANDLE +#else +#define STDIN_HANDLE FILE +#endif + +static smartlist_t * +tor_get_lines_from_handle_replacement(STDIN_HANDLE *handle, + enum stream_status *stream_status_out) +{ + static int times_called = 0; + smartlist_t *retval_sl = smartlist_new(); + + (void) handle; + (void) stream_status_out; + + /* Generate some dummy CMETHOD lines the first 5 times. The 6th + time, send 'CMETHODS DONE' to finish configuring the proxy. */ + if (times_called++ != 5) { + smartlist_add_asprintf(retval_sl, "CMETHOD mock%d socks5 127.0.0.1:555%d", + times_called, times_called); + } else { + smartlist_add(retval_sl, tor_strdup("CMETHODS DONE")); + } + + return retval_sl; +} + +/* NOP mock */ +static void +tor_process_handle_destroy_replacement(process_handle_t *process_handle, + int also_terminate_process) +{ + (void) process_handle; + (void) also_terminate_process; +} + +static or_state_t *dummy_state = NULL; + +static or_state_t * +get_or_state_replacement(void) +{ + return dummy_state; +} + +/* Test the configure_proxy() function. */ +static void +test_pt_configure_proxy(void *arg) +{ + int i; + managed_proxy_t *mp = NULL; + (void) arg; + + dummy_state = tor_malloc_zero(sizeof(or_state_t)); + + MOCK(tor_get_lines_from_handle, + tor_get_lines_from_handle_replacement); + MOCK(tor_process_handle_destroy, + tor_process_handle_destroy_replacement); + MOCK(get_or_state, + get_or_state_replacement); + + mp = tor_malloc(sizeof(managed_proxy_t)); + mp->conf_state = PT_PROTO_ACCEPTING_METHODS; + mp->transports = smartlist_new(); + mp->transports_to_launch = smartlist_new(); + mp->process_handle = tor_malloc_zero(sizeof(process_handle_t)); + mp->argv = tor_malloc_zero(sizeof(char*)*2); + mp->argv[0] = tor_strdup("<testcase>"); + + /* Test the return value of configure_proxy() by calling it some + times while it is uninitialized and then finally finalizing its + configuration. */ + for (i = 0 ; i < 5 ; i++) { + test_assert(configure_proxy(mp) == 0); + } + test_assert(configure_proxy(mp) == 1); + + done: + tor_free(dummy_state); + UNMOCK(tor_get_lines_from_handle); + UNMOCK(tor_process_handle_destroy); +} + #define PT_LEGACY(name) \ { #name, legacy_test_helper, 0, &legacy_setup, test_pt_ ## name } struct testcase_t pt_tests[] = { PT_LEGACY(parsing), PT_LEGACY(protocol), + { "get_transport_options", test_pt_get_transport_options, TT_FORK, + NULL, NULL }, + { "get_extrainfo_string", test_pt_get_extrainfo_string, TT_FORK, + NULL, NULL }, + { "configure_proxy",test_pt_configure_proxy, TT_FORK, + NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_replay.c b/src/test/test_replay.c index de841ad594..200bf4dd3e 100644 --- a/src/test/test_replay.c +++ b/src/test/test_replay.c @@ -32,6 +32,40 @@ test_replaycache_alloc(void) } static void +test_replaycache_badalloc(void) +{ + replaycache_t *r = NULL; + + /* Negative horizon should fail */ + r = replaycache_new(-600, 300); + test_assert(r == NULL); + /* Negative interval should get adjusted to zero */ + r = replaycache_new(600, -300); + test_assert(r != NULL); + test_eq(r->scrub_interval, 0); + replaycache_free(r); + /* Negative horizon and negative interval should still fail */ + r = replaycache_new(-600, -300); + test_assert(r == NULL); + + done: + if (r) replaycache_free(r); + + return; +} + +static void +test_replaycache_free_null(void) +{ + replaycache_free(NULL); + /* Assert that we're here without horrible death */ + test_assert(1); + + done: + return; +} + +static void test_replaycache_miss(void) { replaycache_t *r = NULL; @@ -45,6 +79,12 @@ test_replaycache_miss(void) (int)strlen(test_buffer), NULL); test_eq(result, 0); + /* poke the bad-parameter error case too */ + result = + replaycache_add_and_test_internal(1200, NULL, test_buffer, + (int)strlen(test_buffer), NULL); + test_eq(result, 0); + done: if (r) replaycache_free(r); @@ -163,16 +203,146 @@ test_replaycache_noexpire(void) return; } +static void +test_replaycache_scrub(void) +{ + replaycache_t *r = NULL; + int result; + + r = replaycache_new(600, 300); + test_assert(r != NULL); + + /* Set up like in test_replaycache_hit() */ + result = + replaycache_add_and_test_internal(100, r, test_buffer, + (int)strlen(test_buffer), NULL); + test_eq(result, 0); + + result = + replaycache_add_and_test_internal(200, r, test_buffer, + (int)strlen(test_buffer), NULL); + test_eq(result, 1); + + /* + * Poke a few replaycache_scrub_if_needed_internal() error cases that + * can't happen through replaycache_add_and_test_internal() + */ + + /* Null cache */ + replaycache_scrub_if_needed_internal(300, NULL); + /* Assert we're still here */ + test_assert(1); + + /* Make sure we hit the aging-out case too */ + replaycache_scrub_if_needed_internal(1500, r); + /* Assert that we aged it */ + test_eq(digestmap_size(r->digests_seen), 0); + + done: + if (r) replaycache_free(r); + + return; +} + +static void +test_replaycache_future(void) +{ + replaycache_t *r = NULL; + int result; + time_t elapsed = 0; + + r = replaycache_new(600, 300); + test_assert(r != NULL); + + /* Set up like in test_replaycache_hit() */ + result = + replaycache_add_and_test_internal(100, r, test_buffer, + (int)strlen(test_buffer), &elapsed); + test_eq(result, 0); + /* elapsed should still be 0, since it wasn't written */ + test_eq(elapsed, 0); + + result = + replaycache_add_and_test_internal(200, r, test_buffer, + (int)strlen(test_buffer), &elapsed); + test_eq(result, 1); + /* elapsed should be the time since the last hit */ + test_eq(elapsed, 100); + + /* + * Now let's turn the clock back to get coverage on the cache entry from the + * future not-supposed-to-happen case. + */ + result = + replaycache_add_and_test_internal(150, r, test_buffer, + (int)strlen(test_buffer), &elapsed); + /* We should still get a hit */ + test_eq(result, 1); + /* ...but it shouldn't let us see a negative elapsed time */ + test_eq(elapsed, 0); + + done: + if (r) replaycache_free(r); + + return; +} + +static void +test_replaycache_realtime(void) +{ + replaycache_t *r = NULL; + /* + * Negative so we fail if replaycache_add_test_and_elapsed() doesn't + * write to elapsed. + */ + time_t elapsed = -1; + int result; + + /* Test the realtime as well as *_internal() entry points */ + r = replaycache_new(600, 300); + test_assert(r != NULL); + + /* This should miss */ + result = + replaycache_add_and_test(r, test_buffer, (int)strlen(test_buffer)); + test_eq(result, 0); + + /* This should hit */ + result = + replaycache_add_and_test(r, test_buffer, (int)strlen(test_buffer)); + test_eq(result, 1); + + /* This should hit and return a small elapsed time */ + result = + replaycache_add_test_and_elapsed(r, test_buffer, + (int)strlen(test_buffer), &elapsed); + test_eq(result, 1); + test_assert(elapsed >= 0); + test_assert(elapsed <= 5); + + /* Scrub it to exercise that entry point too */ + replaycache_scrub_if_needed(r); + + done: + if (r) replaycache_free(r); + return; +} + #define REPLAYCACHE_LEGACY(name) \ { #name, legacy_test_helper, 0, &legacy_setup, test_replaycache_ ## name } struct testcase_t replaycache_tests[] = { REPLAYCACHE_LEGACY(alloc), + REPLAYCACHE_LEGACY(badalloc), + REPLAYCACHE_LEGACY(free_null), REPLAYCACHE_LEGACY(miss), REPLAYCACHE_LEGACY(hit), REPLAYCACHE_LEGACY(age), REPLAYCACHE_LEGACY(elapsed), REPLAYCACHE_LEGACY(noexpire), + REPLAYCACHE_LEGACY(scrub), + REPLAYCACHE_LEGACY(future), + REPLAYCACHE_LEGACY(realtime), END_OF_TESTCASES }; diff --git a/src/test/test_util.c b/src/test/test_util.c index 6e1ee713d8..f7513c0f31 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -796,6 +796,64 @@ test_util_expand_filename(void) } #endif +/** Test tor_escape_str_for_pt_args(). */ +static void +test_util_escape_string_socks(void) +{ + char *escaped_string = NULL; + + /** Simple backslash escape. */ + escaped_string = tor_escape_str_for_pt_args("This is a backslash: \\",";\\"); + test_assert(escaped_string); + test_streq(escaped_string, "This is a backslash: \\\\"); + tor_free(escaped_string); + + /** Simple semicolon escape. */ + escaped_string = tor_escape_str_for_pt_args("First rule:Do not use ;",";\\"); + test_assert(escaped_string); + test_streq(escaped_string, "First rule:Do not use \\;"); + tor_free(escaped_string); + + /** Empty string. */ + escaped_string = tor_escape_str_for_pt_args("", ";\\"); + test_assert(escaped_string); + test_streq(escaped_string, ""); + tor_free(escaped_string); + + /** Escape all characters. */ + escaped_string = tor_escape_str_for_pt_args(";\\;\\", ";\\"); + test_assert(escaped_string); + test_streq(escaped_string, "\\;\\\\\\;\\\\"); + tor_free(escaped_string); + + escaped_string = tor_escape_str_for_pt_args(";", ";\\"); + test_assert(escaped_string); + test_streq(escaped_string, "\\;"); + tor_free(escaped_string); + + done: + tor_free(escaped_string); +} + +static void +test_util_string_is_key_value(void *ptr) +{ + (void)ptr; + test_assert(string_is_key_value(LOG_WARN, "key=value")); + test_assert(string_is_key_value(LOG_WARN, "k=v")); + test_assert(string_is_key_value(LOG_WARN, "key=")); + test_assert(string_is_key_value(LOG_WARN, "x=")); + test_assert(string_is_key_value(LOG_WARN, "xx=")); + test_assert(!string_is_key_value(LOG_WARN, "=value")); + test_assert(!string_is_key_value(LOG_WARN, "=x")); + test_assert(!string_is_key_value(LOG_WARN, "=")); + + /* ??? */ + /* test_assert(!string_is_key_value(LOG_WARN, "===")); */ + done: + ; +} + /** Test basic string functionality. */ static void test_util_strmisc(void) @@ -2223,6 +2281,7 @@ test_util_load_win_lib(void *ptr) } #endif +#ifndef _WIN32 static void clear_hex_errno(char *hex_errno) { @@ -2266,6 +2325,7 @@ test_util_exit_status(void *ptr) done: ; } +#endif #ifndef _WIN32 /** Check that fgets waits until a full line, and not return a partial line, on @@ -2567,14 +2627,14 @@ test_util_spawn_background_partial_read(void *ptr) } /** - * Test for format_hex_number_for_helper_exit_status() + * Test for format_hex_number_sigsafe() */ static void test_util_format_hex_number(void *ptr) { int i, len; - char buf[HEX_ERRNO_SIZE + 1]; + char buf[33]; const struct { const char *str; unsigned int x; @@ -2583,6 +2643,8 @@ test_util_format_hex_number(void *ptr) {"1", 1}, {"273A", 0x273a}, {"FFFF", 0xffff}, + {"7FFFFFFF", 0x7fffffff}, + {"FFFFFFFF", 0xffffffff}, #if UINT_MAX >= 0xffffffff {"31BC421D", 0x31bc421d}, {"FFFFFFFF", 0xffffffff}, @@ -2593,19 +2655,23 @@ test_util_format_hex_number(void *ptr) (void)ptr; for (i = 0; test_data[i].str != NULL; ++i) { - len = format_hex_number_for_helper_exit_status(test_data[i].x, - buf, HEX_ERRNO_SIZE); + len = format_hex_number_sigsafe(test_data[i].x, buf, sizeof(buf)); test_neq(len, 0); - buf[len] = '\0'; + test_eq(len, strlen(buf)); test_streq(buf, test_data[i].str); } + test_eq(4, format_hex_number_sigsafe(0xffff, buf, 5)); + test_streq(buf, "FFFF"); + test_eq(0, format_hex_number_sigsafe(0xffff, buf, 4)); + test_eq(0, format_hex_number_sigsafe(0, buf, 1)); + done: return; } /** - * Test that we can properly format q Windows command line + * Test that we can properly format a Windows command line */ static void test_util_join_win_cmdline(void *ptr) @@ -2816,7 +2882,7 @@ test_util_eat_whitespace(void *ptr) (void)ptr; /* Try one leading ws */ - strcpy(str, "fuubaar"); + strlcpy(str, "fuubaar", sizeof(str)); for (i = 0; i < sizeof(ws); ++i) { str[0] = ws[i]; test_eq_ptr(str + 1, eat_whitespace(str)); @@ -2831,14 +2897,14 @@ test_util_eat_whitespace(void *ptr) test_eq_ptr(str, eat_whitespace_eos_no_nl(str, str + strlen(str))); /* Empty string */ - strcpy(str, ""); + strlcpy(str, "", sizeof(str)); test_eq_ptr(str, eat_whitespace(str)); test_eq_ptr(str, eat_whitespace_eos(str, str)); test_eq_ptr(str, eat_whitespace_no_nl(str)); test_eq_ptr(str, eat_whitespace_eos_no_nl(str, str)); /* Only ws */ - strcpy(str, " \t\r\n"); + strlcpy(str, " \t\r\n", sizeof(str)); test_eq_ptr(str + strlen(str), eat_whitespace(str)); test_eq_ptr(str + strlen(str), eat_whitespace_eos(str, str + strlen(str))); test_eq_ptr(str + strlen(str) - 1, @@ -2846,7 +2912,7 @@ test_util_eat_whitespace(void *ptr) test_eq_ptr(str + strlen(str) - 1, eat_whitespace_eos_no_nl(str, str + strlen(str))); - strcpy(str, " \t\r "); + strlcpy(str, " \t\r ", sizeof(str)); test_eq_ptr(str + strlen(str), eat_whitespace(str)); test_eq_ptr(str + strlen(str), eat_whitespace_eos(str, str + strlen(str))); @@ -2855,7 +2921,7 @@ test_util_eat_whitespace(void *ptr) eat_whitespace_eos_no_nl(str, str + strlen(str))); /* Multiple ws */ - strcpy(str, "fuubaar"); + strlcpy(str, "fuubaar", sizeof(str)); for (i = 0; i < sizeof(ws); ++i) str[i] = ws[i]; test_eq_ptr(str + sizeof(ws), eat_whitespace(str)); @@ -2865,28 +2931,28 @@ test_util_eat_whitespace(void *ptr) eat_whitespace_eos_no_nl(str, str + strlen(str))); /* Eat comment */ - strcpy(str, "# Comment \n No Comment"); + strlcpy(str, "# Comment \n No Comment", sizeof(str)); test_streq("No Comment", eat_whitespace(str)); test_streq("No Comment", eat_whitespace_eos(str, str + strlen(str))); test_eq_ptr(str, eat_whitespace_no_nl(str)); test_eq_ptr(str, eat_whitespace_eos_no_nl(str, str + strlen(str))); /* Eat comment & ws mix */ - strcpy(str, " # \t Comment \n\t\nNo Comment"); + strlcpy(str, " # \t Comment \n\t\nNo Comment", sizeof(str)); test_streq("No Comment", eat_whitespace(str)); test_streq("No Comment", eat_whitespace_eos(str, str + strlen(str))); test_eq_ptr(str + 1, eat_whitespace_no_nl(str)); test_eq_ptr(str + 1, eat_whitespace_eos_no_nl(str, str + strlen(str))); /* Eat entire comment */ - strcpy(str, "#Comment"); + strlcpy(str, "#Comment", sizeof(str)); test_eq_ptr(str + strlen(str), eat_whitespace(str)); test_eq_ptr(str + strlen(str), eat_whitespace_eos(str, str + strlen(str))); test_eq_ptr(str, eat_whitespace_no_nl(str)); test_eq_ptr(str, eat_whitespace_eos_no_nl(str, str + strlen(str))); /* Blank line, then comment */ - strcpy(str, " \t\n # Comment"); + strlcpy(str, " \t\n # Comment", sizeof(str)); test_eq_ptr(str + strlen(str), eat_whitespace(str)); test_eq_ptr(str + strlen(str), eat_whitespace_eos(str, str + strlen(str))); test_eq_ptr(str + 2, eat_whitespace_no_nl(str)); @@ -3211,6 +3277,42 @@ test_util_mathlog(void *arg) ; } +static void +test_util_round_to_next_multiple_of(void *arg) +{ + (void)arg; + + test_assert(round_uint64_to_next_multiple_of(0,1) == 0); + test_assert(round_uint64_to_next_multiple_of(0,7) == 0); + + test_assert(round_uint64_to_next_multiple_of(99,1) == 99); + test_assert(round_uint64_to_next_multiple_of(99,7) == 105); + test_assert(round_uint64_to_next_multiple_of(99,9) == 99); + + done: + ; +} + +static void +test_util_strclear(void *arg) +{ + static const char *vals[] = { "", "a", "abcdef", "abcdefgh", NULL }; + int i; + char *v = NULL; + (void)arg; + + for (i = 0; vals[i]; ++i) { + size_t n; + v = tor_strdup(vals[i]); + n = strlen(v); + tor_strclear(v); + tt_assert(tor_mem_is_zero(v, n+1)); + tor_free(v); + } + done: + tor_free(v); +} + #define UTIL_LEGACY(name) \ { #name, legacy_test_helper, 0, &legacy_setup, test_util_ ## name } @@ -3227,6 +3329,8 @@ struct testcase_t util_tests[] = { #ifndef _WIN32 UTIL_LEGACY(expand_filename), #endif + UTIL_LEGACY(escape_string_socks), + UTIL_LEGACY(string_is_key_value), UTIL_LEGACY(strmisc), UTIL_LEGACY(pow2), UTIL_LEGACY(gzip), @@ -3240,6 +3344,8 @@ struct testcase_t util_tests[] = { UTIL_LEGACY(path_is_relative), UTIL_LEGACY(strtok), UTIL_LEGACY(di_ops), + UTIL_TEST(round_to_next_multiple_of, 0), + UTIL_TEST(strclear, 0), UTIL_TEST(find_str_at_start_of_line, 0), UTIL_TEST(string_is_C_identifier, 0), UTIL_TEST(asprintf, 0), @@ -3248,8 +3354,8 @@ struct testcase_t util_tests[] = { #ifdef _WIN32 UTIL_TEST(load_win_lib, 0), #endif - UTIL_TEST(exit_status, 0), #ifndef _WIN32 + UTIL_TEST(exit_status, 0), UTIL_TEST(fgets_eagain, TT_SKIP), #endif UTIL_TEST(spawn_background_ok, 0), diff --git a/src/tools/tor-checkkey.c b/src/tools/tor-checkkey.c index a3860ca4b7..d50f12ed2a 100644 --- a/src/tools/tor-checkkey.c +++ b/src/tools/tor-checkkey.c @@ -1,8 +1,6 @@ /* Copyright (c) 2008-2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ -#define CRYPTO_PRIVATE - #include "orconfig.h" #include <stdio.h> diff --git a/src/tools/tor-fw-helper/tor-fw-helper.c b/src/tools/tor-fw-helper/tor-fw-helper.c index bb6e70aaa3..84cc21e346 100644 --- a/src/tools/tor-fw-helper/tor-fw-helper.c +++ b/src/tools/tor-fw-helper/tor-fw-helper.c @@ -496,6 +496,6 @@ main(int argc, char **argv) smartlist_free(tor_fw_options.ports_to_forward); } - exit(r); + exit(0); } diff --git a/src/tools/tor-gencert.c b/src/tools/tor-gencert.c index 3809b22d43..25beb2aae1 100644 --- a/src/tools/tor-gencert.c +++ b/src/tools/tor-gencert.c @@ -27,8 +27,6 @@ #include <assert.h> #endif -#define CRYPTO_PRIVATE - #include "compat.h" #include "../common/util.h" #include "../common/torlog.h" diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h index f5d5cf4460..43f68c3b08 100644 --- a/src/win32/orconfig.h +++ b/src/win32/orconfig.h @@ -241,7 +241,7 @@ #define USING_TWOS_COMPLEMENT /* Version number of package */ -#define VERSION "0.2.4.10-alpha-dev" +#define VERSION "0.2.5.0-alpha-dev" |