aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/Makefile.am4
-rw-r--r--src/common/address.c7
-rw-r--r--src/common/address.h11
-rw-r--r--src/common/aes.c208
-rw-r--r--src/common/aes.h2
-rw-r--r--src/common/compat.c59
-rw-r--r--src/common/compat.h13
-rw-r--r--src/common/compat_libevent.c54
-rw-r--r--src/common/compat_libevent.h6
-rw-r--r--src/common/container.h28
-rw-r--r--src/common/crypto.c376
-rw-r--r--src/common/crypto.h2
-rw-r--r--src/common/tortls.c184
-rw-r--r--src/common/tortls.h5
-rw-r--r--src/common/util.c272
-rw-r--r--src/common/util.h84
-rw-r--r--src/or/Makefile.am9
-rw-r--r--src/or/circuitbuild.c143
-rw-r--r--src/or/circuitbuild.h6
-rw-r--r--src/or/circuituse.c7
-rw-r--r--src/or/config.c1030
-rw-r--r--src/or/config.h16
-rw-r--r--src/or/connection.c169
-rw-r--r--src/or/connection.h3
-rw-r--r--src/or/connection_edge.c109
-rw-r--r--src/or/connection_edge.h3
-rw-r--r--src/or/connection_or.c72
-rw-r--r--src/or/control.c14
-rw-r--r--src/or/directory.c10
-rw-r--r--src/or/directory.h2
-rw-r--r--src/or/dirserv.c10
-rw-r--r--src/or/dirvote.c32
-rw-r--r--src/or/dirvote.h3
-rw-r--r--src/or/dns.c7
-rw-r--r--src/or/hibernate.c11
-rw-r--r--src/or/main.c67
-rw-r--r--src/or/nodelist.c113
-rw-r--r--src/or/nodelist.h13
-rw-r--r--src/or/or.h111
-rw-r--r--src/or/relay.c1
-rw-r--r--src/or/rendclient.c2
-rw-r--r--src/or/rendcommon.c5
-rw-r--r--src/or/rendservice.c353
-rw-r--r--src/or/router.c146
-rw-r--r--src/or/router.h10
-rw-r--r--src/or/routerlist.c6
-rw-r--r--src/or/routerparse.c38
-rw-r--r--src/or/transports.c330
-rw-r--r--src/or/transports.h7
-rw-r--r--src/test/Makefile.am1
-rw-r--r--src/test/test.c3
-rw-r--r--src/test/test_config.c170
-rw-r--r--src/test/test_crypto.c33
-rw-r--r--src/test/test_dir.c79
-rw-r--r--src/test/test_pt.c2
-rw-r--r--src/test/test_util.c62
-rw-r--r--src/tools/tor-gencert.c2
57 files changed, 3401 insertions, 1124 deletions
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 2244fe58d..2920e73d2 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -56,9 +56,9 @@ noinst_HEADERS = \
common_sha1.i: $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS)
if test "@SHA1SUM@" != none; then \
- @SHA1SUM@ $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS) | @SED@ -n 's/^\(.*\)$$/"\1\\n"/p' > common_sha1.i; \
+ (cd "$(srcdir)" && @SHA1SUM@ $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS)) | @SED@ -n 's/^\(.*\)$$/"\1\\n"/p' > common_sha1.i; \
elif test "@OPENSSL@" != none; then \
- @OPENSSL@ sha1 $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS) | @SED@ -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > common_sha1.i; \
+ (cd "$(srcdir)" && @OPENSSL@ sha1 $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS)) | @SED@ -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > common_sha1.i; \
else \
rm common_sha1.i; \
touch common_sha1.i; \
diff --git a/src/common/address.c b/src/common/address.c
index f40e428cc..ab056f427 100644
--- a/src/common/address.c
+++ b/src/common/address.c
@@ -962,8 +962,11 @@ char *
tor_dup_addr(const tor_addr_t *addr)
{
char buf[TOR_ADDR_BUF_LEN];
- tor_addr_to_str(buf, addr, sizeof(buf), 0);
- return tor_strdup(buf);
+ if (tor_addr_to_str(buf, addr, sizeof(buf), 0)) {
+ return tor_strdup(buf);
+ } else {
+ return tor_strdup("<unknown address type>");
+ }
}
/** Return a string representing the address <b>addr</b>. This string is
diff --git a/src/common/address.h b/src/common/address.h
index 359b0264d..4568c32bf 100644
--- a/src/common/address.h
+++ b/src/common/address.h
@@ -31,6 +31,13 @@ typedef struct tor_addr_t
} addr;
} tor_addr_t;
+/** Holds an IP address and a TCP/UDP port. */
+typedef struct tor_addr_port_t
+{
+ tor_addr_t addr;
+ uint16_t port;
+} tor_addr_port_t;
+
static INLINE const struct in6_addr *tor_addr_to_in6(const tor_addr_t *a);
static INLINE uint32_t tor_addr_to_ipv4n(const tor_addr_t *a);
static INLINE uint32_t tor_addr_to_ipv4h(const tor_addr_t *a);
@@ -149,7 +156,7 @@ int tor_addr_compare_masked(const tor_addr_t *addr1, const tor_addr_t *addr2,
unsigned int tor_addr_hash(const tor_addr_t *addr);
int tor_addr_is_v4(const tor_addr_t *addr);
-int tor_addr_is_internal(const tor_addr_t *ip, int for_listening) ATTR_PURE;
+int tor_addr_is_internal(const tor_addr_t *ip, int for_listening);
/** Longest length that can be required for a reverse lookup name. */
/* 32 nybbles, 32 dots, 8 characters of "ip6.arpa", 1 NUL: 73 characters. */
@@ -185,7 +192,7 @@ int tor_addr_port_split(int severity, const char *addrport,
char **address_out, uint16_t *port_out);
/* IPv4 helpers */
-int is_internal_IP(uint32_t ip, int for_listening) ATTR_PURE;
+int is_internal_IP(uint32_t ip, int for_listening);
int addr_port_lookup(int severity, const char *addrport, char **address,
uint32_t *addr, uint16_t *port_out);
int parse_port_range(const char *port, uint16_t *port_min_out,
diff --git a/src/common/aes.c b/src/common/aes.c
index 9c03b8085..cec689981 100644
--- a/src/common/aes.c
+++ b/src/common/aes.c
@@ -14,34 +14,39 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
+#include <openssl/aes.h>
+#include <openssl/evp.h>
+#include <openssl/engine.h>
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+/* See comments about which counter mode implementation to use below. */
+#include <openssl/modes.h>
+#define USE_OPENSSL_CTR
+#endif
#include "compat.h"
#include "aes.h"
#include "util.h"
#include "torlog.h"
-/* We have 2 strategies for getting AES: Via OpenSSL's AES_encrypt function,
- * via OpenSSL's EVP_EncryptUpdate function. */
-
-/** Defined iff we're using OpenSSL's AES functions for AES. */
-#undef USE_OPENSSL_AES
-/** Defined iff we're using OpenSSL's EVP code for AES. */
-#undef USE_OPENSSL_EVP
-
-/* Here we pick which to use, if none is force-defined above */
-#if (!defined(USE_OPENSSL_AES) && \
- !defined(USE_OPENSSL_EVP))
-
-#define USE_OPENSSL_EVP
-
+#ifdef ANDROID
+/* Android's OpenSSL seems to have removed all of its Engine support. */
+#define DISABLE_ENGINES
#endif
-/* Include OpenSSL headers as needed. */
-#ifdef USE_OPENSSL_AES
-# include <openssl/aes.h>
-#endif
-#ifdef USE_OPENSSL_EVP
-# include <openssl/evp.h>
-#endif
+/* We have 2 strategies for getting AES: Via OpenSSL's AES_encrypt function,
+ * via OpenSSL's EVP_EncryptUpdate function.
+ *
+ * If there's any hardware acceleration in play, we want to be using EVP_* so
+ * we can get it. Otherwise, we'll want AES_*, which seems to be about 5%
+ * faster than indirecting through the EVP layer.
+ */
+
+/* We have 2 strategies for counter mode: use our own, or use OpenSSL's.
+ *
+ * Here we have a counter mode that's faster than the one shipping with
+ * OpenSSL pre-1.0 (by about 10%!). But OpenSSL 1.0.0 added a counter mode
+ * implementation faster than the one here (by about 7%). So we pick which
+ * one to used based on the Openssl version above.
+ */
/*======================================================================*/
/* Interface to AES code, and counter implementation */
@@ -49,13 +54,12 @@
/** Implements an AES counter-mode cipher. */
struct aes_cnt_cipher {
/** This next element (however it's defined) is the AES key. */
-#if defined(USE_OPENSSL_EVP)
- EVP_CIPHER_CTX key;
-#elif defined(USE_OPENSSL_AES)
- AES_KEY key;
-#endif
+ union {
+ EVP_CIPHER_CTX evp;
+ AES_KEY aes;
+ } key;
-#if !defined(WORDS_BIGENDIAN)
+#if !defined(WORDS_BIGENDIAN) && !defined(USE_OPENSSL_CTR)
#define USING_COUNTER_VARS
/** These four values, together, implement a 128-bit counter, with
* counter0 as the low-order word and counter3 as the high-order word. */
@@ -77,9 +81,51 @@ struct aes_cnt_cipher {
/** The encrypted value of ctr_buf. */
uint8_t buf[16];
/** Our current stream position within buf. */
+#ifdef USE_OPENSSL_CTR
+ unsigned int pos;
+#else
uint8_t pos;
+#endif
+
+ /** True iff we're using the evp implementation of this cipher. */
+ uint8_t using_evp;
};
+/** True if we should prefer the EVP implementation for AES, either because
+ * we're testing it or because we have hardware acceleration configured */
+static int should_use_EVP = 0;
+
+/** Check whether we should use the EVP interface for AES. If <b>force_val</b>
+ * is nonnegative, we use use EVP iff it is true. Otherwise, we use EVP
+ * if there is an engine enabled for aes-ecb. */
+int
+evaluate_evp_for_aes(int force_val)
+{
+ ENGINE *e;
+
+ if (force_val >= 0) {
+ should_use_EVP = force_val;
+ return 0;
+ }
+#ifdef DISABLE_ENGINES
+ should_use_EVP = 0;
+#else
+ e = ENGINE_get_cipher_engine(NID_aes_128_ecb);
+
+ if (e) {
+ log_notice(LD_CRYPTO, "AES engine \"%s\" found; using EVP_* functions.",
+ ENGINE_get_name(e));
+ should_use_EVP = 1;
+ } else {
+ log_notice(LD_CRYPTO, "No AES engine found; using AES_* functions.");
+ should_use_EVP = 0;
+ }
+#endif
+
+ return 0;
+}
+
+#ifndef USE_OPENSSL_CTR
#if !defined(USING_COUNTER_VARS)
#define COUNTER(c, n) ((c)->ctr_buf.buf32[3-(n)])
#else
@@ -100,16 +146,15 @@ _aes_fill_buf(aes_cnt_cipher_t *cipher)
* None of these issues are insurmountable in principle.
*/
-#if defined(USE_OPENSSL_EVP)
- {
+ if (cipher->using_evp) {
int outl=16, inl=16;
- EVP_EncryptUpdate(&cipher->key, cipher->buf, &outl,
+ EVP_EncryptUpdate(&cipher->key.evp, cipher->buf, &outl,
cipher->ctr_buf.buf, inl);
+ } else {
+ AES_encrypt(cipher->ctr_buf.buf, cipher->buf, &cipher->key.aes);
}
-#elif defined(USE_OPENSSL_AES)
- AES_encrypt(cipher->ctr_buf.buf, cipher->buf, &cipher->key);
-#endif
}
+#endif
/**
* Return a newly allocated counter-mode AES128 cipher implementation.
@@ -129,18 +174,21 @@ aes_new_cipher(void)
void
aes_set_key(aes_cnt_cipher_t *cipher, const char *key, int key_bits)
{
-#if defined(USE_OPENSSL_EVP)
- const EVP_CIPHER *c;
- switch (key_bits) {
- case 128: c = EVP_aes_128_ecb(); break;
- case 192: c = EVP_aes_192_ecb(); break;
- case 256: c = EVP_aes_256_ecb(); break;
- default: tor_assert(0);
+ if (should_use_EVP) {
+ const EVP_CIPHER *c;
+ switch (key_bits) {
+ case 128: c = EVP_aes_128_ecb(); break;
+ case 192: c = EVP_aes_192_ecb(); break;
+ case 256: c = EVP_aes_256_ecb(); break;
+ default: tor_assert(0);
+ }
+ EVP_EncryptInit(&cipher->key.evp, c, (const unsigned char*)key, NULL);
+ cipher->using_evp = 1;
+ } else {
+ AES_set_encrypt_key((const unsigned char *)key, key_bits, &cipher->key.aes);
+ cipher->using_evp = 0;
}
- EVP_EncryptInit(&cipher->key, c, (const unsigned char*)key, NULL);
-#elif defined(USE_OPENSSL_AES)
- AES_set_encrypt_key((const unsigned char *)key, key_bits, &(cipher->key));
-#endif
+
#ifdef USING_COUNTER_VARS
cipher->counter0 = 0;
cipher->counter1 = 0;
@@ -151,7 +199,12 @@ aes_set_key(aes_cnt_cipher_t *cipher, const char *key, int key_bits)
memset(cipher->ctr_buf.buf, 0, sizeof(cipher->ctr_buf.buf));
cipher->pos = 0;
+
+#ifdef USE_OPENSSL_CTR
+ memset(cipher->buf, 0, sizeof(cipher->buf));
+#else
_aes_fill_buf(cipher);
+#endif
}
/** Release storage held by <b>cipher</b>
@@ -161,9 +214,9 @@ aes_free_cipher(aes_cnt_cipher_t *cipher)
{
if (!cipher)
return;
-#ifdef USE_OPENSSL_EVP
- EVP_CIPHER_CTX_cleanup(&cipher->key);
-#endif
+ if (cipher->using_evp) {
+ EVP_CIPHER_CTX_cleanup(&cipher->key.evp);
+ }
memset(cipher, 0, sizeof(aes_cnt_cipher_t));
tor_free(cipher);
}
@@ -176,6 +229,18 @@ aes_free_cipher(aes_cnt_cipher_t *cipher)
#define UPDATE_CTR_BUF(c, n)
#endif
+#ifdef USE_OPENSSL_CTR
+/* Helper function to use EVP with openssl's counter-mode wrapper. */
+static void evp_block128_fn(const uint8_t in[16],
+ uint8_t out[16],
+ const void *key)
+{
+ EVP_CIPHER_CTX *ctx = (void*)key;
+ int inl=16, outl=16;
+ EVP_EncryptUpdate(ctx, out, &outl, in, inl);
+}
+#endif
+
/** Encrypt <b>len</b> bytes from <b>input</b>, storing the result in
* <b>output</b>. Uses the key in <b>cipher</b>, and advances the counter
* by <b>len</b> bytes as it encrypts.
@@ -184,20 +249,29 @@ void
aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len,
char *output)
{
- /* This function alone is up to 5% of our runtime in some profiles; anything
- * we could do to make it faster would be great.
- *
- * Experimenting suggests that unrolling the inner loop into a switch
- * statement doesn't help. What does seem to help is making the input and
- * output buffers word aligned, and never crypting anything besides an
- * integer number of words at a time -- it shaves maybe 4-5% of the per-byte
- * encryption time measured by bench_aes. We can't do that with the current
- * Tor protocol, though: Tor really likes to crypt things in 509-byte
- * chunks.
- *
- * If we were really ambitous, we'd force len to be a multiple of the block
- * size, and shave maybe another 4-5% off.
- */
+#ifdef USE_OPENSSL_CTR
+ if (cipher->using_evp) {
+ /* In openssl 1.0.0, there's an if'd out EVP_aes_128_ctr in evp.h. If
+ * it weren't disabled, it might be better just to use that.
+ */
+ CRYPTO_ctr128_encrypt((const unsigned char *)input,
+ (unsigned char *)output,
+ len,
+ &cipher->key.evp,
+ cipher->ctr_buf.buf,
+ cipher->buf,
+ &cipher->pos,
+ evp_block128_fn);
+ } else {
+ AES_ctr128_encrypt((const unsigned char *)input,
+ (unsigned char *)output,
+ len,
+ &cipher->key.aes,
+ cipher->ctr_buf.buf,
+ cipher->buf,
+ &cipher->pos);
+ }
+#else
int c = cipher->pos;
if (PREDICT_UNLIKELY(!len)) return;
@@ -220,6 +294,7 @@ aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len,
UPDATE_CTR_BUF(cipher, 0);
_aes_fill_buf(cipher);
}
+#endif
}
/** Encrypt <b>len</b> bytes from <b>input</b>, storing the results in place.
@@ -229,11 +304,9 @@ aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len,
void
aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len)
{
-
- /* XXXX This function is up to 5% of our runtime in some profiles;
- * we should look into unrolling some of the loops; taking advantage
- * of alignment, using a bigger buffer, and so on. Not till after 0.1.2.x,
- * though. */
+#ifdef USE_OPENSSL_CTR
+ aes_crypt(cipher, data, len, data);
+#else
int c = cipher->pos;
if (PREDICT_UNLIKELY(!len)) return;
@@ -256,6 +329,7 @@ aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len)
UPDATE_CTR_BUF(cipher, 0);
_aes_fill_buf(cipher);
}
+#endif
}
/** Reset the 128-bit counter of <b>cipher</b> to the 16-bit big-endian value
@@ -272,6 +346,8 @@ aes_set_iv(aes_cnt_cipher_t *cipher, const char *iv)
cipher->pos = 0;
memcpy(cipher->ctr_buf.buf, iv, 16);
+#ifndef USE_OPENSSL_CTR
_aes_fill_buf(cipher);
+#endif
}
diff --git a/src/common/aes.h b/src/common/aes.h
index b2591942b..221e84615 100644
--- a/src/common/aes.h
+++ b/src/common/aes.h
@@ -24,5 +24,7 @@ void aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len,
void aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len);
void aes_set_iv(aes_cnt_cipher_t *cipher, const char *iv);
+int evaluate_evp_for_aes(int force_value);
+
#endif
diff --git a/src/common/compat.c b/src/common/compat.c
index 9a2c9d764..ea95f9f08 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -58,6 +58,14 @@
#endif
#endif
+/* Includes for the process attaching prevention */
+#if defined(HAVE_SYS_PRCTL_H) && defined(__linux__)
+#include <sys/prctl.h>
+#elif defined(__APPLE__)
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#endif
+
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
@@ -1519,6 +1527,57 @@ switch_id(const char *user)
#endif
}
+/* We only use the linux prctl for now. There is no Win32 support; this may
+ * also work on various BSD systems and Mac OS X - send testing feedback!
+ *
+ * On recent Gnu/Linux kernels it is possible to create a system-wide policy
+ * that will prevent non-root processes from attaching to other processes
+ * unless they are the parent process; thus gdb can attach to programs that
+ * they execute but they cannot attach to other processes running as the same
+ * user. The system wide policy may be set with the sysctl
+ * kernel.yama.ptrace_scope or by inspecting
+ * /proc/sys/kernel/yama/ptrace_scope and it is 1 by default on Ubuntu 11.04.
+ *
+ * This ptrace scope will be ignored on Gnu/Linux for users with
+ * CAP_SYS_PTRACE and so it is very likely that root will still be able to
+ * attach to the Tor process.
+ */
+/** Attempt to disable debugger attachment: return 0 on success, -1 on
+ * failure. */
+int
+tor_disable_debugger_attach(void)
+{
+ int r, attempted;
+ r = -1;
+ attempted = 0;
+ log_debug(LD_CONFIG,
+ "Attemping to disable debugger attachment to Tor for "
+ "unprivileged users.");
+#if defined(__linux__) && defined(HAVE_SYS_PRCTL_H) && defined(HAVE_PRCTL)
+#ifdef PR_SET_DUMPABLE
+ attempted = 1;
+ r = prctl(PR_SET_DUMPABLE, 0);
+#endif
+#endif
+#if defined(__APPLE__) && defined(PT_DENY_ATTACH)
+ if (r < 0) {
+ attempted = 1;
+ r = ptrace(PT_DENY_ATTACH, 0, 0, 0);
+ }
+#endif
+
+ // XXX: TODO - Mac OS X has dtrace and this may be disabled.
+ // XXX: TODO - Windows probably has something similar
+ if (r == 0) {
+ log_debug(LD_CONFIG,"Debugger attachment disabled for "
+ "unprivileged users.");
+ } else if (attempted) {
+ log_warn(LD_CONFIG, "Unable to disable ptrace attach: %s",
+ strerror(errno));
+ }
+ return r;
+}
+
#ifdef HAVE_PWD_H
/** Allocate and return a string containing the home directory for the
* user <b>username</b>. Only works on posix-like systems. */
diff --git a/src/common/compat.h b/src/common/compat.h
index b005dd297..db541623d 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -135,7 +135,6 @@ extern INLINE double U64_TO_DBL(uint64_t x) {
/* GCC has several useful attributes. */
#if defined(__GNUC__) && __GNUC__ >= 3
#define ATTR_NORETURN __attribute__((noreturn))
-#define ATTR_PURE __attribute__((pure))
#define ATTR_CONST __attribute__((const))
#define ATTR_MALLOC __attribute__((malloc))
#define ATTR_NORETURN __attribute__((noreturn))
@@ -168,7 +167,6 @@ extern INLINE double U64_TO_DBL(uint64_t x) {
#define PREDICT_UNLIKELY(exp) __builtin_expect(!!(exp), 0)
#else
#define ATTR_NORETURN
-#define ATTR_PURE
#define ATTR_CONST
#define ATTR_MALLOC
#define ATTR_NORETURN
@@ -271,9 +269,9 @@ int tor_asprintf(char **strp, const char *fmt, ...)
int tor_vasprintf(char **strp, const char *fmt, va_list args);
const void *tor_memmem(const void *haystack, size_t hlen, const void *needle,
- size_t nlen) ATTR_PURE ATTR_NONNULL((1,3));
+ size_t nlen) ATTR_NONNULL((1,3));
static const void *tor_memstr(const void *haystack, size_t hlen,
- const char *needle) ATTR_PURE ATTR_NONNULL((1,3));
+ const char *needle) ATTR_NONNULL((1,3));
static INLINE const void *
tor_memstr(const void *haystack, size_t hlen, const char *needle)
{
@@ -546,9 +544,9 @@ long tor_weak_random(void);
/* ===== OS compatibility */
const char *get_uname(void);
-uint16_t get_uint16(const void *cp) ATTR_PURE ATTR_NONNULL((1));
-uint32_t get_uint32(const void *cp) ATTR_PURE ATTR_NONNULL((1));
-uint64_t get_uint64(const void *cp) ATTR_PURE ATTR_NONNULL((1));
+uint16_t get_uint16(const void *cp) ATTR_NONNULL((1));
+uint32_t get_uint32(const void *cp) ATTR_NONNULL((1));
+uint64_t get_uint64(const void *cp) ATTR_NONNULL((1));
void set_uint16(void *cp, uint16_t v) ATTR_NONNULL((1));
void set_uint32(void *cp, uint32_t v) ATTR_NONNULL((1));
void set_uint64(void *cp, uint64_t v) ATTR_NONNULL((1));
@@ -566,6 +564,7 @@ set_uint8(void *cp, uint8_t v)
typedef unsigned long rlim_t;
#endif
int set_max_file_descriptors(rlim_t limit, int *max);
+int tor_disable_debugger_attach(void);
int switch_id(const char *user);
#ifdef HAVE_PWD_H
char *get_user_homedir(const char *username);
diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c
index 0cedef8d5..67f465927 100644
--- a/src/common/compat_libevent.c
+++ b/src/common/compat_libevent.c
@@ -558,6 +558,60 @@ tor_check_libevent_header_compatibility(void)
#endif
}
+struct tor_libevent_action_t {
+ struct event *ev;
+ void (*cb)(void *arg);
+ void *arg;
+};
+
+/** Callback for tor_run_in_libevent_loop */
+static void
+run_runnable_cb(evutil_socket_t s, short what, void *arg)
+{
+ tor_libevent_action_t *r = arg;
+ void (*cb)(void *) = r->cb;
+ void *cb_arg = r->arg;
+ (void)what;
+ (void)s;
+ tor_event_free(r->ev);
+ tor_free(r);
+
+ cb(cb_arg);
+}
+
+/** Cause cb(arg) to run later on this iteration of the libevent loop, or in
+ * the next iteration of the libevent loop. This is useful for when you're
+ * deep inside a no-reentrant code and there's some function you want to call
+ * without worrying about whether it might cause reeentrant invocation.
+ */
+tor_libevent_action_t *
+tor_run_in_libevent_loop(void (*cb)(void *arg), void *arg)
+{
+ tor_libevent_action_t *r = tor_malloc(sizeof(tor_libevent_action_t));
+ r->cb = cb;
+ r->arg = arg;
+ r->ev = tor_event_new(tor_libevent_get_base(), -1, EV_TIMEOUT,
+ run_runnable_cb, r);
+ if (!r->ev) {
+ tor_free(r);
+ return NULL;
+ }
+ /* Make the event active immediately. */
+ event_active(r->ev, EV_TIMEOUT, 1);
+
+ return r;
+}
+
+/**
+ * Cancel <b>action</b> without running it.
+ */
+void
+tor_cancel_libevent_action(tor_libevent_action_t *action)
+{
+ tor_event_free(action->ev);
+ tor_free(action);
+}
+
/*
If possible, we're going to try to use Libevent's periodic timer support,
since it does a pretty good job of making sure that periodic events get
diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h
index 024729717..4076cc0e0 100644
--- a/src/common/compat_libevent.h
+++ b/src/common/compat_libevent.h
@@ -44,8 +44,12 @@ void tor_event_free(struct event *ev);
#define tor_evdns_add_server_port evdns_add_server_port
#endif
-typedef struct periodic_timer_t periodic_timer_t;
+typedef struct tor_libevent_action_t tor_libevent_action_t;
+tor_libevent_action_t *tor_run_in_libevent_loop(void (*cb)(void *arg),
+ void *arg);
+void tor_cancel_libevent_action(tor_libevent_action_t *action);
+typedef struct periodic_timer_t periodic_timer_t;
periodic_timer_t *periodic_timer_new(struct event_base *base,
const struct timeval *tv,
void (*cb)(periodic_timer_t *timer, void *data),
diff --git a/src/common/container.h b/src/common/container.h
index 4a6eba789..fe071cc1b 100644
--- a/src/common/container.h
+++ b/src/common/container.h
@@ -35,19 +35,14 @@ void smartlist_remove(smartlist_t *sl, const void *element);
void *smartlist_pop_last(smartlist_t *sl);
void smartlist_reverse(smartlist_t *sl);
void smartlist_string_remove(smartlist_t *sl, const char *element);
-int smartlist_isin(const smartlist_t *sl, const void *element) ATTR_PURE;
-int smartlist_string_isin(const smartlist_t *sl, const char *element)
- ATTR_PURE;
-int smartlist_string_pos(const smartlist_t *, const char *elt) ATTR_PURE;
-int smartlist_string_isin_case(const smartlist_t *sl, const char *element)
- ATTR_PURE;
-int smartlist_string_num_isin(const smartlist_t *sl, int num) ATTR_PURE;
-int smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2)
- ATTR_PURE;
-int smartlist_digest_isin(const smartlist_t *sl, const char *element)
- ATTR_PURE;
-int smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2)
- ATTR_PURE;
+int smartlist_isin(const smartlist_t *sl, const void *element);
+int smartlist_string_isin(const smartlist_t *sl, const char *element);
+int smartlist_string_pos(const smartlist_t *, const char *elt);
+int smartlist_string_isin_case(const smartlist_t *sl, const char *element);
+int smartlist_string_num_isin(const smartlist_t *sl, int num);
+int smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2);
+int smartlist_digest_isin(const smartlist_t *sl, const char *element);
+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);
@@ -55,14 +50,14 @@ void smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2);
#ifdef DEBUG_SMARTLIST
/** Return the number of items in sl.
*/
-static INLINE int smartlist_len(const smartlist_t *sl) ATTR_PURE;
+static INLINE int smartlist_len(const smartlist_t *sl);
static INLINE int smartlist_len(const smartlist_t *sl) {
tor_assert(sl);
return (sl)->num_used;
}
/** Return the <b>idx</b>th element of sl.
*/
-static INLINE void *smartlist_get(const smartlist_t *sl, int idx) ATTR_PURE;
+static INLINE void *smartlist_get(const smartlist_t *sl, int idx);
static INLINE void *smartlist_get(const smartlist_t *sl, int idx) {
tor_assert(sl);
tor_assert(idx>=0);
@@ -114,8 +109,7 @@ void smartlist_uniq_strings(smartlist_t *sl);
void smartlist_uniq_digests(smartlist_t *sl);
void smartlist_uniq_digests256(smartlist_t *sl);
void *smartlist_bsearch(smartlist_t *sl, const void *key,
- int (*compare)(const void *key, const void **member))
- ATTR_PURE;
+ int (*compare)(const void *key, const void **member));
int smartlist_bsearch_idx(const smartlist_t *sl, const void *key,
int (*compare)(const void *key, const void **member),
int *found_out);
diff --git a/src/common/crypto.c b/src/common/crypto.c
index f2ef83352..62b0bcec6 100644
--- a/src/common/crypto.c
+++ b/src/common/crypto.c
@@ -276,6 +276,9 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
} else {
log_info(LD_CRYPTO, "NOT using OpenSSL engine support.");
}
+
+ evaluate_evp_for_aes(-1);
+
return crypto_seed_rng(1);
}
return 0;
@@ -288,37 +291,6 @@ crypto_thread_cleanup(void)
ERR_remove_state(0);
}
-/** Uninitialize the crypto library. Return 0 on success, -1 on failure.
- */
-int
-crypto_global_cleanup(void)
-{
- EVP_cleanup();
- ERR_remove_state(0);
- ERR_free_strings();
-
-#ifndef DISABLE_ENGINES
- ENGINE_cleanup();
-#endif
-
- CONF_modules_unload(1);
- CRYPTO_cleanup_all_ex_data();
-#ifdef TOR_IS_MULTITHREADED
- if (_n_openssl_mutexes) {
- int n = _n_openssl_mutexes;
- tor_mutex_t **ms = _openssl_mutexes;
- int i;
- _openssl_mutexes = NULL;
- _n_openssl_mutexes = 0;
- for (i=0;i<n;++i) {
- tor_mutex_free(ms[i]);
- }
- tor_free(ms);
- }
-#endif
- return 0;
-}
-
/** used by tortls.c: wrap an RSA* in a crypto_pk_env_t. */
crypto_pk_env_t *
_crypto_new_pk_env_rsa(RSA *rsa)
@@ -1809,6 +1781,9 @@ crypto_hmac_sha256(char *hmac_out,
/* DH */
+/** Our DH 'g' parameter */
+#define DH_GENERATOR 2
+
/** Shared P parameter for our circuit-crypto DH key exchanges. */
static BIGNUM *dh_param_p = NULL;
/** Shared P parameter for our TLS DH key exchanges. */
@@ -1816,49 +1791,303 @@ static BIGNUM *dh_param_p_tls = NULL;
/** Shared G parameter for our DH key exchanges. */
static BIGNUM *dh_param_g = NULL;
+/** Generate and return a reasonable and safe DH parameter p. */
+static BIGNUM *
+crypto_generate_dynamic_dh_modulus(void)
+{
+ BIGNUM *dynamic_dh_modulus;
+ DH *dh_parameters;
+ int r, dh_codes;
+ char *s;
+
+ dynamic_dh_modulus = BN_new();
+ tor_assert(dynamic_dh_modulus);
+
+ dh_parameters = DH_generate_parameters(DH_BYTES*8, DH_GENERATOR, NULL, NULL);
+ tor_assert(dh_parameters);
+
+ r = DH_check(dh_parameters, &dh_codes);
+ tor_assert(r && !dh_codes);
+
+ BN_copy(dynamic_dh_modulus, dh_parameters->p);
+ tor_assert(dynamic_dh_modulus);
+
+ DH_free(dh_parameters);
+
+ { /* log the dynamic DH modulus: */
+ s = BN_bn2hex(dynamic_dh_modulus);
+ tor_assert(s);
+ log_info(LD_OR, "Dynamic DH modulus generated: [%s]", s);
+ OPENSSL_free(s);
+ }
+
+ return dynamic_dh_modulus;
+}
+
+/** Store our dynamic DH modulus (and its group parameters) to
+ <b>fname</b> for future use. */
+static int
+crypto_store_dynamic_dh_modulus(const char *fname)
+{
+ int len, new_len;
+ DH *dh = NULL;
+ unsigned char *dh_string_repr = NULL, *cp = NULL;
+ char *base64_encoded_dh = NULL;
+ int retval = -1;
+
+ tor_assert(fname);
+
+ if (!dh_param_p_tls) {
+ log_info(LD_CRYPTO, "Tried to store a DH modulus that does not exist.");
+ goto done;
+ }
+
+ if (!(dh = DH_new()))
+ goto done;
+ if (!(dh->p = BN_dup(dh_param_p_tls)))
+ goto done;
+ if (!(dh->g = BN_new()))
+ goto done;
+ 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)) {
+ log_warn(LD_CRYPTO, "Error occured while DER encoding DH modulus (2).");
+ goto done;
+ }
+
+ base64_encoded_dh = tor_malloc_zero(len * 2); /* should be enough */
+ new_len = base64_encode(base64_encoded_dh, len * 2,
+ (char *)dh_string_repr, len);
+ if (new_len < 0) {
+ log_warn(LD_CRYPTO, "Error occured while base64-encoding DH modulus.");
+ goto done;
+ }
+
+ if (write_bytes_to_new_file(fname, base64_encoded_dh, new_len, 0) < 0) {
+ log_info(LD_CRYPTO, "'%s' was already occupied.", fname);
+ goto done;
+ }
+
+ retval = 0;
+
+ done:
+ if (dh)
+ DH_free(dh);
+ tor_free(dh_string_repr);
+ tor_free(base64_encoded_dh);
+
+ return retval;
+}
+
+/** Return the dynamic DH modulus stored in <b>fname</b>. If there is no
+ dynamic DH modulus stored in <b>fname</b>, return NULL. */
+static BIGNUM *
+crypto_get_stored_dynamic_dh_modulus(const char *fname)
+{
+ int retval;
+ char *contents = NULL;
+ int dh_codes;
+ char *fname_new = NULL;
+ DH *stored_dh = NULL;
+ BIGNUM *dynamic_dh_modulus = NULL;
+ int length = 0;
+ unsigned char *base64_decoded_dh = NULL;
+ const unsigned char *cp = NULL;
+
+ tor_assert(fname);
+
+ contents = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL);
+ if (!contents) {
+ log_info(LD_CRYPTO, "Could not open file '%s'", fname);
+ goto done; /*usually means that ENOENT. don't try to move file to broken.*/
+ }
+
+ /* 'fname' contains the DH parameters stored in base64-ed DER
+ format. We are only interested in the DH modulus. */
+
+ cp = base64_decoded_dh = tor_malloc_zero(strlen(contents));
+ length = base64_decode((char *)base64_decoded_dh, strlen(contents),
+ contents, strlen(contents));
+ if (length < 0) {
+ log_warn(LD_CRYPTO, "Stored dynamic DH modulus seems corrupted (base64).");
+ goto err;
+ }
+
+ stored_dh = d2i_DHparams(NULL, &cp, length);
+ if ((!stored_dh) || (cp - base64_decoded_dh != length)) {
+ log_warn(LD_CRYPTO, "Stored dynamic DH modulus seems corrupted (d2i).");
+ goto err;
+ }
+
+ { /* check the cryptographic qualities of the stored dynamic DH modulus: */
+ retval = DH_check(stored_dh, &dh_codes);
+ if (!retval || dh_codes) {
+ log_warn(LD_CRYPTO, "Stored dynamic DH modulus is not a safe prime.");
+ goto err;
+ }
+
+ retval = DH_size(stored_dh);
+ if (retval < DH_BYTES) {
+ log_warn(LD_CRYPTO, "Stored dynamic DH modulus is smaller "
+ "than '%d' bits.", DH_BYTES*8);
+ goto err;
+ }
+
+ if (!BN_is_word(stored_dh->g, 2)) {
+ log_warn(LD_CRYPTO, "Stored dynamic DH parameters do not use '2' "
+ "as the group generator.");
+ goto err;
+ }
+ }
+
+ { /* log the dynamic DH modulus: */
+ char *s = BN_bn2hex(stored_dh->p);
+ tor_assert(s);
+ log_info(LD_OR, "Found stored dynamic DH modulus: [%s]", s);
+ OPENSSL_free(s);
+ }
+
+ goto done;
+
+ err:
+
+ { /* move broken prime to $filename.broken */
+ fname_new = tor_malloc(strlen(fname) + 8);
+
+ /* no can do if these functions return error */
+ strlcpy(fname_new, fname, strlen(fname) + 8);
+ strlcat(fname_new, ".broken", strlen(fname) + 8);
+
+ log_warn(LD_CRYPTO, "Moving broken dynamic DH prime to '%s'.", fname_new);
+
+ if (replace_file(fname, fname_new))
+ log_notice(LD_CRYPTO, "Error while moving '%s' to '%s'.",
+ fname, fname_new);
+
+ tor_free(fname_new);
+ }
+
+ if (stored_dh) {
+ DH_free(stored_dh);
+ stored_dh = NULL;
+ }
+
+ done:
+ tor_free(contents);
+ tor_free(base64_decoded_dh);
+
+ if (stored_dh) {
+ dynamic_dh_modulus = BN_dup(stored_dh->p);
+ DH_free(stored_dh);
+ }
+
+ return dynamic_dh_modulus;
+}
+
+/** Set the global TLS Diffie-Hellman modulus.
+ * If <b>dynamic_dh_modulus_fname</b> is set, try to read a dynamic DH modulus
+ * off it and use it as the DH modulus. If that's not possible,
+ * generate a new dynamic DH modulus.
+ * If <b>dynamic_dh_modulus_fname</b> is NULL, use the Apache mod_ssl DH
+ * modulus. */
+void
+crypto_set_tls_dh_prime(const char *dynamic_dh_modulus_fname)
+{
+ BIGNUM *tls_prime = NULL;
+ int store_dh_prime_afterwards = 0;
+ int r;
+
+ /* If the space is occupied, free the previous TLS DH prime */
+ if (dh_param_p_tls) {
+ BN_free(dh_param_p_tls);
+ dh_param_p_tls = NULL;
+ }
+
+ if (dynamic_dh_modulus_fname) { /* use dynamic DH modulus: */
+ log_info(LD_OR, "Using stored dynamic DH modulus.");
+ tls_prime = crypto_get_stored_dynamic_dh_modulus(dynamic_dh_modulus_fname);
+
+ if (!tls_prime) {
+ log_notice(LD_OR, "Generating fresh dynamic DH modulus. "
+ "This might take a while...");
+ tls_prime = crypto_generate_dynamic_dh_modulus();
+
+ store_dh_prime_afterwards++;
+ }
+ } else { /* use the static DH prime modulus used by Apache in mod_ssl: */
+ tls_prime = BN_new();
+ tor_assert(tls_prime);
+
+ /* This is the 1024-bit safe prime that Apache uses for its DH stuff; see
+ * modules/ssl/ssl_engine_dh.c; Apache also uses a generator of 2 with this
+ * prime.
+ */
+ r =BN_hex2bn(&tls_prime,
+ "D67DE440CBBBDC1936D693D34AFD0AD50C84D239A45F520BB88174CB98"
+ "BCE951849F912E639C72FB13B4B4D7177E16D55AC179BA420B2A29FE324A"
+ "467A635E81FF5901377BEDDCFD33168A461AAD3B72DAE8860078045B07A7"
+ "DBCA7874087D1510EA9FCC9DDD330507DD62DB88AEAA747DE0F4D6E2BD68"
+ "B0E7393E0F24218EB3");
+ tor_assert(r);
+ }
+
+ tor_assert(tls_prime);
+
+ dh_param_p_tls = tls_prime;
+
+ if (store_dh_prime_afterwards)
+ /* save the new dynamic DH modulus to disk. */
+ if (crypto_store_dynamic_dh_modulus(dynamic_dh_modulus_fname)) {
+ log_notice(LD_CRYPTO, "Failed while storing dynamic DH modulus. "
+ "Make sure your data directory is sane.");
+ }
+}
+
/** Initialize dh_param_p and dh_param_g if they are not already
* set. */
static void
init_dh_param(void)
{
- BIGNUM *p, *p2, *g;
+ BIGNUM *circuit_dh_prime, *generator;
int r;
- if (dh_param_p && dh_param_g && dh_param_p_tls)
+ if (dh_param_p && dh_param_g)
return;
- p = BN_new();
- p2 = BN_new();
- g = BN_new();
- tor_assert(p);
- tor_assert(p2);
- tor_assert(g);
+ circuit_dh_prime = BN_new();
+ generator = BN_new();
+ tor_assert(circuit_dh_prime && generator);
+
+ /* Set our generator for all DH parameters */
+ r = BN_set_word(generator, DH_GENERATOR);
+ tor_assert(r);
/* This is from rfc2409, section 6.2. It's a safe prime, and
supposedly it equals:
2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }.
*/
- r = BN_hex2bn(&p,
+ r = BN_hex2bn(&circuit_dh_prime,
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
"8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
"302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
"A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
"49286651ECE65381FFFFFFFFFFFFFFFF");
tor_assert(r);
- /* This is the 1024-bit safe prime that Apache uses for its DH stuff; see
- * modules/ssl/ssl_engine_dh.c */
- r = BN_hex2bn(&p2,
- "D67DE440CBBBDC1936D693D34AFD0AD50C84D239A45F520BB88174CB98"
- "BCE951849F912E639C72FB13B4B4D7177E16D55AC179BA420B2A29FE324A"
- "467A635E81FF5901377BEDDCFD33168A461AAD3B72DAE8860078045B07A7"
- "DBCA7874087D1510EA9FCC9DDD330507DD62DB88AEAA747DE0F4D6E2BD68"
- "B0E7393E0F24218EB3");
- tor_assert(r);
- r = BN_set_word(g, 2);
- tor_assert(r);
- dh_param_p = p;
- dh_param_p_tls = p2;
- dh_param_g = g;
+ /* Set the new values as the global DH parameters. */
+ dh_param_p = circuit_dh_prime;
+ dh_param_g = generator;
+
+ /* Should be already set by config.c. */
+ tor_assert(dh_param_p_tls);
}
/** Number of bits to use when choosing the x or y value in a Diffie-Hellman
@@ -2830,5 +3059,44 @@ setup_openssl_threading(void)
return 0;
}
#endif
+
+/** Uninitialize the crypto library. Return 0 on success, -1 on failure.
+ */
+int
+crypto_global_cleanup(void)
+{
+ EVP_cleanup();
+ ERR_remove_state(0);
+ ERR_free_strings();
+
+ if (dh_param_p)
+ BN_free(dh_param_p);
+ if (dh_param_p_tls)
+ BN_free(dh_param_p_tls);
+ if (dh_param_g)
+ BN_free(dh_param_g);
+
+#ifndef DISABLE_ENGINES
+ ENGINE_cleanup();
+#endif
+
+ CONF_modules_unload(1);
+ CRYPTO_cleanup_all_ex_data();
+#ifdef TOR_IS_MULTITHREADED
+ if (_n_openssl_mutexes) {
+ int n = _n_openssl_mutexes;
+ tor_mutex_t **ms = _openssl_mutexes;
+ int i;
+ _openssl_mutexes = NULL;
+ _n_openssl_mutexes = 0;
+ for (i=0;i<n;++i) {
+ tor_mutex_free(ms[i]);
+ }
+ tor_free(ms);
+ }
+#endif
+ return 0;
+}
+
/** @} */
diff --git a/src/common/crypto.h b/src/common/crypto.h
index 80c10296a..771c49c2d 100644
--- a/src/common/crypto.h
+++ b/src/common/crypto.h
@@ -91,6 +91,8 @@ int crypto_global_cleanup(void);
crypto_pk_env_t *crypto_new_pk_env(void);
void crypto_free_pk_env(crypto_pk_env_t *env);
+void crypto_set_tls_dh_prime(const char *dynamic_dh_modulus_fname);
+
/* convenience function: wraps crypto_create_crypto_env, set_key, and init. */
crypto_cipher_env_t *crypto_create_init_cipher(const char *key,
int encrypt_mode);
diff --git a/src/common/tortls.c b/src/common/tortls.c
index 554deb7bd..18f268470 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -52,7 +52,6 @@
#include <event2/bufferevent_ssl.h>
#include <event2/buffer.h>
#include <event2/event.h>
-#include "compat_libevent.h"
#endif
#define CRYPTO_PRIVATE /* to import prototypes from crypto.h */
@@ -64,6 +63,7 @@
#include "torlog.h"
#include "container.h"
#include <string.h>
+#include "compat_libevent.h"
/* Enable the "v2" TLS handshake.
*/
@@ -146,7 +146,7 @@ struct tor_tls_t {
/** True iff we should call negotiated_callback when we're done reading. */
unsigned int got_renegotiate:1;
/** Incremented every time we start the server side of a handshake. */
- uint8_t server_handshake_count;
+ unsigned int server_handshake_count:2;
size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last
* time. */
/** Last values retrieved from BIO_number_read()/write(); see
@@ -157,6 +157,11 @@ struct tor_tls_t {
/** If set, a callback to invoke whenever the client tries to renegotiate
* the handshake. */
void (*negotiated_callback)(tor_tls_t *tls, void *arg);
+
+ /** Callback to invoke whenever a client tries to renegotiate more
+ than once. */
+ void (*excess_renegotiations_callback)(void *);
+
/** Argument to pass to negotiated_callback. */
void *callback_arg;
};
@@ -580,7 +585,13 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa,
const char *cname_sign,
unsigned int cert_lifetime)
{
+ /* OpenSSL generates self-signed certificates with random 64-bit serial
+ * numbers, so let's do that too. */
+#define SERIAL_NUMBER_SIZE 8
+
time_t start_time, end_time;
+ BIGNUM *serial_number = NULL;
+ unsigned char serial_tmp[SERIAL_NUMBER_SIZE];
EVP_PKEY *sign_pkey = NULL, *pkey=NULL;
X509 *x509 = NULL;
X509_NAME *name = NULL, *name_issuer=NULL;
@@ -601,8 +612,15 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa,
goto error;
if (!(X509_set_version(x509, 2)))
goto error;
- if (!(ASN1_INTEGER_set(X509_get_serialNumber(x509), (long)start_time)))
- goto error;
+
+ { /* our serial number is 8 random bytes. */
+ if (crypto_rand((char *)serial_tmp, sizeof(serial_tmp)) < 0)
+ goto error;
+ if (!(serial_number = BN_bin2bn(serial_tmp, sizeof(serial_tmp), NULL)))
+ goto error;
+ if (!(BN_to_ASN1_INTEGER(serial_number, X509_get_serialNumber(x509))))
+ goto error;
+ }
if (!(name = tor_x509_name_new(cname)))
goto error;
@@ -635,11 +653,15 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa,
EVP_PKEY_free(sign_pkey);
if (pkey)
EVP_PKEY_free(pkey);
+ if (serial_number)
+ BN_free(serial_number);
if (name)
X509_NAME_free(name);
if (name_issuer)
X509_NAME_free(name_issuer);
return x509;
+
+#undef SERIAL_NUMBER_SIZE
}
/** List of ciphers that servers should select from.*/
@@ -1297,55 +1319,42 @@ tor_tls_client_is_using_v2_ciphers(const SSL *ssl, const char *address)
return 1;
}
+/** We got an SSL ClientHello message. This might mean that the
+ * client wants to initiate a renegotiation and appropriate actions
+ * must be taken. */
static void
-tor_tls_debug_state_callback(const SSL *ssl, int type, int val)
-{
- log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].",
- ssl, ssl_state_to_string(ssl->state), type, val);
-}
-
-/** Invoked when we're accepting a connection on <b>ssl</b>, and the connection
- * changes state. We use this:
- * <ul><li>To alter the state of the handshake partway through, so we
- * do not send or request extra certificates in v2 handshakes.</li>
- * <li>To detect renegotiation</li></ul>
- */
-static void
-tor_tls_server_info_callback(const SSL *ssl, int type, int val)
+tor_tls_got_client_hello(tor_tls_t *tls)
{
- tor_tls_t *tls;
- (void) val;
+ if (tls->server_handshake_count < 3)
+ ++tls->server_handshake_count;
- tor_tls_debug_state_callback(ssl, type, val);
+ if (tls->server_handshake_count == 2) {
+ if (!tls->negotiated_callback) {
+ log_warn(LD_BUG, "Got a renegotiation request but we don't"
+ " have a renegotiation callback set!");
+ }
- if (type != SSL_CB_ACCEPT_LOOP)
- return;
- if (ssl->state != SSL3_ST_SW_SRVR_HELLO_A)
- return;
+ tls->got_renegotiate = 1;
+ } else if (tls->server_handshake_count > 2 &&
+ tls->excess_renegotiations_callback) {
+ /* We got more than one renegotiation requests. The Tor protocol
+ needs just one renegotiation; more than that probably means
+ They are trying to DoS us and we have to stop them. */
- tls = tor_tls_get_by_ssl(ssl);
- if (tls) {
- /* Check whether we're watching for renegotiates. If so, this is one! */
- if (tls->negotiated_callback)
- tls->got_renegotiate = 1;
- if (tls->server_handshake_count < 127) /*avoid any overflow possibility*/
- ++tls->server_handshake_count;
- } else {
- log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!");
- return;
+ tls->excess_renegotiations_callback(tls->callback_arg);
}
/* Now check the cipher list. */
- if (tor_tls_client_is_using_v2_ciphers(ssl, ADDR(tls))) {
+ if (tor_tls_client_is_using_v2_ciphers(tls->ssl, ADDR(tls))) {
/*XXXX_TLS keep this from happening more than once! */
/* Yes, we're casting away the const from ssl. This is very naughty of us.
* Let's hope openssl doesn't notice! */
/* Set SSL_MODE_NO_AUTO_CHAIN to keep from sending back any extra certs. */
- SSL_set_mode((SSL*) ssl, SSL_MODE_NO_AUTO_CHAIN);
+ SSL_set_mode((SSL*) tls->ssl, SSL_MODE_NO_AUTO_CHAIN);
/* Don't send a hello request. */
- SSL_set_verify((SSL*) ssl, SSL_VERIFY_NONE, NULL);
+ SSL_set_verify((SSL*) tls->ssl, SSL_VERIFY_NONE, NULL);
if (tls) {
tls->wasV2Handshake = 1;
@@ -1360,6 +1369,34 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
}
#endif
+/** This is a callback function for SSL_set_info_callback() and it
+ * will be called in every SSL state change.
+ * It logs the SSL state change, and executes any actions that must be
+ * taken. */
+static void
+tor_tls_state_changed_callback(const SSL *ssl, int type, int val)
+{
+ log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].",
+ ssl, ssl_state_to_string(ssl->state), type, val);
+
+#ifdef V2_HANDSHAKE_SERVER
+ if (type == SSL_CB_ACCEPT_LOOP &&
+ ssl->state == SSL3_ST_SW_SRVR_HELLO_A) {
+
+ /* Call tor_tls_got_client_hello() for every SSL ClientHello we
+ receive. */
+
+ tor_tls_t *tls = tor_tls_get_by_ssl(ssl);
+ if (!tls) {
+ log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!");
+ return;
+ }
+
+ tor_tls_got_client_hello(tls);
+ }
+#endif
+}
+
/** Replace *<b>ciphers</b> with a new list of SSL ciphersuites: specifically,
* a list designed to mimic a common web browser. Some of the ciphers in the
* list won't actually be implemented by OpenSSL: that's okay so long as the
@@ -1501,14 +1538,8 @@ tor_tls_new(int sock, int isServer)
log_warn(LD_NET, "Newly created BIO has read count %lu, write count %lu",
result->last_read_count, result->last_write_count);
}
-#ifdef V2_HANDSHAKE_SERVER
- if (isServer) {
- SSL_set_info_callback(result->ssl, tor_tls_server_info_callback);
- } else
-#endif
- {
- SSL_set_info_callback(result->ssl, tor_tls_debug_state_callback);
- }
+
+ SSL_set_info_callback(result->ssl, tor_tls_state_changed_callback);
/* Not expected to get called. */
tls_log_errors(NULL, LOG_WARN, LD_NET, "creating tor_tls_t object");
@@ -1526,25 +1557,22 @@ tor_tls_set_logged_address(tor_tls_t *tls, const char *address)
tls->address = tor_strdup(address);
}
-/** Set <b>cb</b> to be called with argument <b>arg</b> whenever <b>tls</b>
- * next gets a client-side renegotiate in the middle of a read. Do not
- * invoke this function until <em>after</em> initial handshaking is done!
+/** Set <b>cb</b> to be called with argument <b>arg</b> whenever
+ * <b>tls</b> next gets a client-side renegotiate in the middle of a
+ * read. Set <b>cb2</b> to be called with argument <b>arg</b> whenever
+ * <b>tls</b> gets excess renegotiation requests. Do not invoke this
+ * function until <em>after</em> initial handshaking is done!
*/
void
-tor_tls_set_renegotiate_callback(tor_tls_t *tls,
+tor_tls_set_renegotiate_callbacks(tor_tls_t *tls,
void (*cb)(tor_tls_t *, void *arg),
+ void (*cb2)(void *),
void *arg)
{
tls->negotiated_callback = cb;
+ tls->excess_renegotiations_callback = cb2;
tls->callback_arg = arg;
tls->got_renegotiate = 0;
-#ifdef V2_HANDSHAKE_SERVER
- if (cb) {
- SSL_set_info_callback(tls->ssl, tor_tls_server_info_callback);
- } else {
- SSL_set_info_callback(tls->ssl, tor_tls_debug_state_callback);
- }
-#endif
}
/** If this version of openssl requires it, turn on renegotiation on
@@ -1564,16 +1592,6 @@ tor_tls_unblock_renegotiation(tor_tls_t *tls)
}
}
-/** If this version of openssl supports it, turn off renegotiation on
- * <b>tls</b>. (Our protocol never requires this for security, but it's nice
- * to use belt-and-suspenders here.)
- */
-void
-tor_tls_block_renegotiation(tor_tls_t *tls)
-{
- tls->ssl->s3->flags &= ~SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
-}
-
void
tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls)
{
@@ -1611,6 +1629,7 @@ tor_tls_free(tor_tls_t *tls)
SSL_free(tls->ssl);
tls->ssl = NULL;
tls->negotiated_callback = NULL;
+ tls->excess_renegotiations_callback = NULL;
if (tls->context)
tor_tls_context_decref(tls->context);
tor_free(tls->address);
@@ -1632,19 +1651,30 @@ tor_tls_read(tor_tls_t *tls, char *cp, size_t len)
tor_assert(tls->state == TOR_TLS_ST_OPEN);
tor_assert(len<INT_MAX);
r = SSL_read(tls->ssl, cp, (int)len);
- if (r > 0) {
+ if (r > 0) /* return the number of characters read */
+ return r;
+
+ /* If we got here, SSL_read() did not go as expected. */
+
+ err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading", LOG_DEBUG, LD_NET);
+
#ifdef V2_HANDSHAKE_SERVER
- if (tls->got_renegotiate) {
- /* Renegotiation happened! */
- log_info(LD_NET, "Got a TLS renegotiation from %s", ADDR(tls));
- if (tls->negotiated_callback)
- tls->negotiated_callback(tls, tls->callback_arg);
- tls->got_renegotiate = 0;
+ if (tls->got_renegotiate) {
+ if (tls->server_handshake_count != 2) {
+ log_warn(LD_BUG, "We did not notice renegotiation in a timely "
+ "fashion (%u)!", tls->server_handshake_count);
}
-#endif
+
+ /* Renegotiation happened! */
+ log_info(LD_NET, "Got a TLS renegotiation from %s", ADDR(tls));
+ if (tls->negotiated_callback)
+ tls->negotiated_callback(tls, tls->callback_arg);
+ tls->got_renegotiate = 0;
+
return r;
}
- err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading", LOG_DEBUG, LD_NET);
+#endif
+
if (err == _TOR_TLS_ZERORETURN || err == TOR_TLS_CLOSE) {
log_debug(LD_NET,"read returned r=%d; TLS is closed",r);
tls->state = TOR_TLS_ST_CLOSED;
@@ -1681,6 +1711,7 @@ tor_tls_write(tor_tls_t *tls, const char *cp, size_t n)
}
r = SSL_write(tls->ssl, cp, (int)n);
err = tor_tls_get_error(tls, r, 0, "writing", LOG_INFO, LD_NET);
+
if (err == TOR_TLS_DONE) {
return r;
}
@@ -1745,7 +1776,6 @@ tor_tls_finish_handshake(tor_tls_t *tls)
{
int r = TOR_TLS_DONE;
if (tls->isServer) {
- SSL_set_info_callback(tls->ssl, NULL);
SSL_set_verify(tls->ssl, SSL_VERIFY_PEER, always_accept_verify_cb);
/* There doesn't seem to be a clear OpenSSL API to clear mode flags. */
tls->ssl->mode &= ~SSL_MODE_NO_AUTO_CHAIN;
diff --git a/src/common/tortls.h b/src/common/tortls.h
index 673f18dfe..9f86e3712 100644
--- a/src/common/tortls.h
+++ b/src/common/tortls.h
@@ -13,6 +13,7 @@
#include "crypto.h"
#include "compat.h"
+#include "compat_libevent.h"
/* Opaque structure to hold a TLS connection. */
typedef struct tor_tls_t tor_tls_t;
@@ -60,8 +61,9 @@ int tor_tls_context_init(int is_public_server,
unsigned int key_lifetime);
tor_tls_t *tor_tls_new(int sock, int is_server);
void tor_tls_set_logged_address(tor_tls_t *tls, const char *address);
-void tor_tls_set_renegotiate_callback(tor_tls_t *tls,
+void tor_tls_set_renegotiate_callbacks(tor_tls_t *tls,
void (*cb)(tor_tls_t *, void *arg),
+ void (*cb2)(void *),
void *arg);
int tor_tls_is_server(tor_tls_t *tls);
void tor_tls_free(tor_tls_t *tls);
@@ -77,7 +79,6 @@ int tor_tls_handshake(tor_tls_t *tls);
int tor_tls_finish_handshake(tor_tls_t *tls);
int tor_tls_renegotiate(tor_tls_t *tls);
void tor_tls_unblock_renegotiation(tor_tls_t *tls);
-void tor_tls_block_renegotiation(tor_tls_t *tls);
void tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls);
int tor_tls_shutdown(tor_tls_t *tls);
int tor_tls_get_pending_bytes(tor_tls_t *tls);
diff --git a/src/common/util.c b/src/common/util.c
index c44a4aa3b..6d488d996 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -2137,13 +2137,12 @@ write_chunks_to_file(const char *fname, const smartlist_t *chunks, int bin)
return write_chunks_to_file_impl(fname, chunks, flags);
}
-/** As write_str_to_file, but does not assume a NUL-terminated
- * string. Instead, we write <b>len</b> bytes, starting at <b>str</b>. */
-int
-write_bytes_to_file(const char *fname, const char *str, size_t len,
- int bin)
+/** Write <b>len</b> bytes, starting at <b>str</b>, to <b>fname</b>
+ using the open() flags passed in <b>flags</b>. */
+static int
+write_bytes_to_file_impl(const char *fname, const char *str, size_t len,
+ int flags)
{
- int flags = OPEN_FLAGS_REPLACE|(bin?O_BINARY:O_TEXT);
int r;
sized_chunk_t c = { str, len };
smartlist_t *chunks = smartlist_create();
@@ -2153,20 +2152,35 @@ write_bytes_to_file(const char *fname, const char *str, size_t len,
return r;
}
+/** As write_str_to_file, but does not assume a NUL-terminated
+ * string. Instead, we write <b>len</b> bytes, starting at <b>str</b>. */
+int
+write_bytes_to_file(const char *fname, const char *str, size_t len,
+ int bin)
+{
+ return write_bytes_to_file_impl(fname, str, len,
+ OPEN_FLAGS_REPLACE|(bin?O_BINARY:O_TEXT));
+}
+
/** As write_bytes_to_file, but if the file already exists, append the bytes
* to the end of the file instead of overwriting it. */
int
append_bytes_to_file(const char *fname, const char *str, size_t len,
int bin)
{
- int flags = OPEN_FLAGS_APPEND|(bin?O_BINARY:O_TEXT);
- int r;
- sized_chunk_t c = { str, len };
- smartlist_t *chunks = smartlist_create();
- smartlist_add(chunks, &c);
- r = write_chunks_to_file_impl(fname, chunks, flags);
- smartlist_free(chunks);
- return r;
+ return write_bytes_to_file_impl(fname, str, len,
+ OPEN_FLAGS_APPEND|(bin?O_BINARY:O_TEXT));
+}
+
+/** Like write_str_to_file(), but also return -1 if there was a file
+ already residing in <b>fname</b>. */
+int
+write_bytes_to_new_file(const char *fname, const char *str, size_t len,
+ int bin)
+{
+ return write_bytes_to_file_impl(fname, str, len,
+ OPEN_FLAGS_DONT_REPLACE|
+ (bin?O_BINARY:O_TEXT));
}
/** Read the contents of <b>filename</b> into a newly allocated
@@ -3159,28 +3173,72 @@ format_helper_exit_status(unsigned char child_state, int saved_errno,
/* Maximum number of file descriptors, if we cannot get it via sysconf() */
#define DEFAULT_MAX_FD 256
-/** Terminate process running at PID <b>pid</b>.
+/** Terminate the process of <b>process_handle</b>.
* Code borrowed from Python's os.kill. */
int
-tor_terminate_process(pid_t pid)
+tor_terminate_process(process_handle_t *process_handle)
{
#ifdef MS_WINDOWS
- HANDLE handle;
- /* If the signal is outside of what GenerateConsoleCtrlEvent can use,
- attempt to open and terminate the process. */
- handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
- if (!handle)
- return -1;
+ if (tor_get_exit_code(process_handle, 0, NULL) == PROCESS_EXIT_RUNNING) {
+ HANDLE handle;
+ /* If the signal is outside of what GenerateConsoleCtrlEvent can use,
+ attempt to open and terminate the process. */
+ handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE,
+ process_handle->pid.dwProcessId);
+ if (!handle)
+ return -1;
- if (!TerminateProcess(handle, 0))
- return -1;
- else
- return 0;
+ if (!TerminateProcess(handle, 0))
+ return -1;
+ else
+ return 0;
+ }
#else /* Unix */
- return kill(pid, SIGTERM);
+ return kill(process_handle->pid, SIGTERM);
+#endif
+
+ return -1;
+}
+
+/** Return the Process ID of <b>process_handle</b>. */
+int
+tor_process_get_pid(process_handle_t *process_handle)
+{
+#ifdef MS_WINDOWS
+ return (int) process_handle->pid.dwProcessId;
+#else
+ return (int) process_handle->pid;
+#endif
+}
+
+#ifdef MS_WINDOWS
+HANDLE
+tor_process_get_stdout_pipe(process_handle_t *process_handle)
+{
+ return process_handle->stdout_pipe;
+}
+#else
+FILE *
+tor_process_get_stdout_pipe(process_handle_t *process_handle)
+{
+ return process_handle->stdout_handle;
+}
+#endif
+
+static process_handle_t *
+process_handle_new(void)
+{
+ process_handle_t *out = tor_malloc_zero(sizeof(process_handle_t));
+
+#ifndef MS_WINDOWS
+ out->stdout_pipe = -1;
+ out->stderr_pipe = -1;
#endif
+
+ return out;
}
+/*DOCDOC*/
#define CHILD_STATE_INIT 0
#define CHILD_STATE_PIPE 1
#define CHILD_STATE_MAXFD 2
@@ -3213,14 +3271,20 @@ tor_terminate_process(pid_t pid)
*/
int
tor_spawn_background(const char *const filename, const char **argv,
+#ifdef MS_WINDOWS
+ LPVOID envp,
+#else
const char **envp,
- process_handle_t *process_handle)
+#endif
+ process_handle_t **process_handle_out)
{
#ifdef MS_WINDOWS
HANDLE stdout_pipe_read = NULL;
HANDLE stdout_pipe_write = NULL;
HANDLE stderr_pipe_read = NULL;
HANDLE stderr_pipe_write = NULL;
+ process_handle_t *process_handle;
+ int status;
STARTUPINFO siStartInfo;
BOOL retval = FALSE;
@@ -3230,30 +3294,26 @@ tor_spawn_background(const char *const filename, const char **argv,
(void)envp; // Unused on Windows
- /* process_handle must not be NULL */
- tor_assert(process_handle != NULL);
-
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
/* TODO: should we set explicit security attributes? (#2046, comment 5) */
saAttr.lpSecurityDescriptor = NULL;
/* Assume failure to start process */
- memset(process_handle, 0, sizeof(process_handle_t));
- process_handle->status = PROCESS_STATUS_ERROR;
+ status = PROCESS_STATUS_ERROR;
/* Set up pipe for stdout */
if (!CreatePipe(&stdout_pipe_read, &stdout_pipe_write, &saAttr, 0)) {
log_warn(LD_GENERAL,
"Failed to create pipe for stdout communication with child process: %s",
format_win32_error(GetLastError()));
- return process_handle->status;
+ return status;
}
if (!SetHandleInformation(stdout_pipe_read, HANDLE_FLAG_INHERIT, 0)) {
log_warn(LD_GENERAL,
"Failed to configure pipe for stdout communication with child "
"process: %s", format_win32_error(GetLastError()));
- return process_handle->status;
+ return status;
}
/* Set up pipe for stderr */
@@ -3261,13 +3321,13 @@ tor_spawn_background(const char *const filename, const char **argv,
log_warn(LD_GENERAL,
"Failed to create pipe for stderr communication with child process: %s",
format_win32_error(GetLastError()));
- return process_handle->status;
+ return status;
}
if (!SetHandleInformation(stderr_pipe_read, HANDLE_FLAG_INHERIT, 0)) {
log_warn(LD_GENERAL,
"Failed to configure pipe for stderr communication with child "
"process: %s", format_win32_error(GetLastError()));
- return process_handle->status;
+ return status;
}
/* Create the child process */
@@ -3276,6 +3336,9 @@ tor_spawn_background(const char *const filename, const char **argv,
*/
joined_argv = tor_join_win_cmdline(argv);
+ process_handle = process_handle_new();
+ process_handle->status = status;
+
ZeroMemory(&(process_handle->pid), sizeof(PROCESS_INFORMATION));
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
@@ -3295,7 +3358,7 @@ tor_spawn_background(const char *const filename, const char **argv,
/*(TODO: set CREATE_NEW CONSOLE/PROCESS_GROUP to make GetExitCodeProcess()
* work?) */
0, // creation flags
- NULL, // use parent's environment
+ envp, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&(process_handle->pid)); // receives PROCESS_INFORMATION
@@ -3306,22 +3369,25 @@ tor_spawn_background(const char *const filename, const char **argv,
log_warn(LD_GENERAL,
"Failed to create child process %s: %s", filename?filename:argv[0],
format_win32_error(GetLastError()));
+ tor_free(process_handle);
} else {
/* TODO: Close hProcess and hThread in process_handle->pid? */
process_handle->stdout_pipe = stdout_pipe_read;
process_handle->stderr_pipe = stderr_pipe_read;
- process_handle->status = PROCESS_STATUS_RUNNING;
+ status = process_handle->status = PROCESS_STATUS_RUNNING;
}
/* TODO: Close pipes on exit */
-
- return process_handle->status;
+ *process_handle_out = process_handle;
+ return status;
#else // MS_WINDOWS
pid_t pid;
int stdout_pipe[2];
int stderr_pipe[2];
int fd, retval;
ssize_t nbytes;
+ process_handle_t *process_handle;
+ int status;
const char *error_message = SPAWN_ERROR_MESSAGE;
size_t error_message_length;
@@ -3334,9 +3400,7 @@ tor_spawn_background(const char *const filename, const char **argv,
static int max_fd = -1;
- /* Assume failure to start */
- memset(process_handle, 0, sizeof(process_handle_t));
- process_handle->status = PROCESS_STATUS_ERROR;
+ status = PROCESS_STATUS_ERROR;
/* We do the strlen here because strlen() is not signal handler safe,
and we are not allowed to use unsafe functions between fork and exec */
@@ -3350,7 +3414,7 @@ tor_spawn_background(const char *const filename, const char **argv,
log_warn(LD_GENERAL,
"Failed to set up pipe for stdout communication with child process: %s",
strerror(errno));
- return process_handle->status;
+ return status;
}
retval = pipe(stderr_pipe);
@@ -3358,7 +3422,7 @@ tor_spawn_background(const char *const filename, const char **argv,
log_warn(LD_GENERAL,
"Failed to set up pipe for stderr communication with child process: %s",
strerror(errno));
- return process_handle->status;
+ return status;
}
child_state = CHILD_STATE_MAXFD;
@@ -3448,7 +3512,7 @@ tor_spawn_background(const char *const filename, const char **argv,
_exit(255);
/* Never reached, but avoids compiler warning */
- return process_handle->status;
+ return status;
}
/* In parent */
@@ -3459,9 +3523,11 @@ tor_spawn_background(const char *const filename, const char **argv,
close(stdout_pipe[1]);
close(stderr_pipe[0]);
close(stderr_pipe[1]);
- return process_handle->status;
+ return status;
}
+ process_handle = process_handle_new();
+ process_handle->status = status;
process_handle->pid = pid;
/* TODO: If the child process forked but failed to exec, waitpid it */
@@ -3485,7 +3551,7 @@ tor_spawn_background(const char *const filename, const char **argv,
strerror(errno));
}
- process_handle->status = PROCESS_STATUS_RUNNING;
+ status = process_handle->status = PROCESS_STATUS_RUNNING;
/* Set stdout/stderr pipes to be non-blocking */
fcntl(process_handle->stdout_pipe, F_SETFL, O_NONBLOCK);
fcntl(process_handle->stderr_pipe, F_SETFL, O_NONBLOCK);
@@ -3493,10 +3559,52 @@ tor_spawn_background(const char *const filename, const char **argv,
process_handle->stdout_handle = fdopen(process_handle->stdout_pipe, "r");
process_handle->stderr_handle = fdopen(process_handle->stderr_pipe, "r");
+ *process_handle_out = process_handle;
return process_handle->status;
#endif // MS_WINDOWS
}
+/** Destroy all resources allocated by the process handle in
+ * <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)
+{
+ if (!process_handle)
+ return;
+
+ if (also_terminate_process) {
+ if (tor_terminate_process(process_handle) < 0) {
+ log_notice(LD_GENERAL, "Failed to terminate process with PID '%d'",
+ tor_process_get_pid(process_handle));
+ } else {
+ log_info(LD_GENERAL, "Terminated process with PID '%d'",
+ tor_process_get_pid(process_handle));
+ }
+ }
+
+ process_handle->status = PROCESS_STATUS_NOTRUNNING;
+
+#ifdef MS_WINDOWS
+ if (process_handle->stdout_pipe)
+ CloseHandle(process_handle->stdout_pipe);
+
+ if (process_handle->stderr_pipe)
+ CloseHandle(process_handle->stderr_pipe);
+#else
+ if (process_handle->stdout_handle)
+ fclose(process_handle->stdout_handle);
+
+ if (process_handle->stderr_handle)
+ fclose(process_handle->stderr_handle);
+#endif
+
+ memset(process_handle, 0x0f, sizeof(process_handle_t));
+ tor_free(process_handle);
+}
+
/** Get the exit code of a process specified by <b>process_handle</b> and store
* it in <b>exit_code</b>, if set to a non-NULL value. If <b>block</b> is set
* to true, the call will block until the process has exited. Otherwise if
@@ -3508,7 +3616,7 @@ tor_spawn_background(const char *const filename, const char **argv,
* probably not work in Tor, because waitpid() is called in main.c to reap any
* terminated child processes.*/
int
-tor_get_exit_code(const process_handle_t process_handle,
+tor_get_exit_code(const process_handle_t *process_handle,
int block, int *exit_code)
{
#ifdef MS_WINDOWS
@@ -3517,14 +3625,14 @@ tor_get_exit_code(const process_handle_t process_handle,
if (block) {
/* Wait for the process to exit */
- retval = WaitForSingleObject(process_handle.pid.hProcess, INFINITE);
+ retval = WaitForSingleObject(process_handle->pid.hProcess, INFINITE);
if (retval != WAIT_OBJECT_0) {
log_warn(LD_GENERAL, "WaitForSingleObject() failed (%d): %s",
(int)retval, format_win32_error(GetLastError()));
return PROCESS_EXIT_ERROR;
}
} else {
- retval = WaitForSingleObject(process_handle.pid.hProcess, 0);
+ retval = WaitForSingleObject(process_handle->pid.hProcess, 0);
if (WAIT_TIMEOUT == retval) {
/* Process has not exited */
return PROCESS_EXIT_RUNNING;
@@ -3536,7 +3644,7 @@ tor_get_exit_code(const process_handle_t process_handle,
}
if (exit_code != NULL) {
- success = GetExitCodeProcess(process_handle.pid.hProcess,
+ success = GetExitCodeProcess(process_handle->pid.hProcess,
(PDWORD)exit_code);
if (!success) {
log_warn(LD_GENERAL, "GetExitCodeProcess() failed: %s",
@@ -3548,19 +3656,19 @@ tor_get_exit_code(const process_handle_t process_handle,
int stat_loc;
int retval;
- retval = waitpid(process_handle.pid, &stat_loc, block?0:WNOHANG);
+ retval = waitpid(process_handle->pid, &stat_loc, block?0:WNOHANG);
if (!block && 0 == retval) {
/* Process has not exited */
return PROCESS_EXIT_RUNNING;
- } else if (retval != process_handle.pid) {
- log_warn(LD_GENERAL, "waitpid() failed for PID %d: %s", process_handle.pid,
- strerror(errno));
+ } else if (retval != process_handle->pid) {
+ log_warn(LD_GENERAL, "waitpid() failed for PID %d: %s",
+ process_handle->pid, strerror(errno));
return PROCESS_EXIT_ERROR;
}
if (!WIFEXITED(stat_loc)) {
log_warn(LD_GENERAL, "Process %d did not exit normally",
- process_handle.pid);
+ process_handle->pid);
return PROCESS_EXIT_ERROR;
}
@@ -3662,7 +3770,6 @@ tor_read_all_handle(FILE *h, char *buf, size_t count,
if (NULL == retval) {
if (feof(h)) {
log_debug(LD_GENERAL, "fgets() reached end of file");
- fclose(h);
if (eof)
*eof = 1;
break;
@@ -3675,7 +3782,6 @@ tor_read_all_handle(FILE *h, char *buf, size_t count,
} else {
log_warn(LD_GENERAL, "fgets() from handle failed: %s",
strerror(errno));
- fclose(h);
return -1;
}
}
@@ -3835,12 +3941,10 @@ log_from_pipe(FILE *stream, int severity, const char *executable,
r = get_string_from_pipe(stream, buf, sizeof(buf) - 1);
if (r == IO_STREAM_CLOSED) {
- fclose(stream);
return 1;
} else if (r == IO_STREAM_EAGAIN) {
return 0;
} else if (r == IO_STREAM_TERM) {
- fclose(stream);
return -1;
}
@@ -3948,7 +4052,7 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
/* Static variables are initialized to zero, so child_handle.status=0
* which corresponds to it not running on startup */
- static process_handle_t child_handle;
+ static process_handle_t *child_handle=NULL;
static time_t time_to_run_helper = 0;
int stdout_status, stderr_status, retval;
@@ -3974,46 +4078,50 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
argv[9] = NULL;
/* Start the child, if it is not already running */
- if (child_handle.status != PROCESS_STATUS_RUNNING &&
+ if ((!child_handle || child_handle->status != PROCESS_STATUS_RUNNING) &&
time_to_run_helper < now) {
+ int status;
+
/* Assume tor-fw-helper will succeed, start it later*/
time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_SUCCESS;
+ if (child_handle) {
+ tor_process_handle_destroy(child_handle, 1);
+ child_handle = NULL;
+ }
+
#ifdef MS_WINDOWS
/* Passing NULL as lpApplicationName makes Windows search for the .exe */
- tor_spawn_background(NULL, argv, NULL, &child_handle);
+ status = tor_spawn_background(NULL, argv, NULL, &child_handle);
#else
- tor_spawn_background(filename, argv, NULL, &child_handle);
+ status = tor_spawn_background(filename, argv, NULL, &child_handle);
#endif
- if (PROCESS_STATUS_ERROR == child_handle.status) {
+
+ if (PROCESS_STATUS_ERROR == status) {
log_warn(LD_GENERAL, "Failed to start port forwarding helper %s",
filename);
time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_FAIL;
return;
}
-#ifdef MS_WINDOWS
- log_info(LD_GENERAL,
- "Started port forwarding helper (%s)", filename);
-#else
+
log_info(LD_GENERAL,
- "Started port forwarding helper (%s) with pid %d", filename,
- child_handle.pid);
-#endif
+ "Started port forwarding helper (%s) with pid '%d'",
+ filename, tor_process_get_pid(child_handle));
}
/* If child is running, read from its stdout and stderr) */
- if (PROCESS_STATUS_RUNNING == child_handle.status) {
+ if (child_handle && PROCESS_STATUS_RUNNING == child_handle->status) {
/* Read from stdout/stderr and log result */
retval = 0;
#ifdef MS_WINDOWS
- stdout_status = log_from_handle(child_handle.stdout_pipe, LOG_INFO);
- stderr_status = log_from_handle(child_handle.stderr_pipe, LOG_WARN);
+ stdout_status = log_from_handle(child_handle->stdout_pipe, LOG_INFO);
+ stderr_status = log_from_handle(child_handle->stderr_pipe, LOG_WARN);
/* If we got this far (on Windows), the process started */
retval = 0;
#else
- stdout_status = log_from_pipe(child_handle.stdout_handle,
+ stdout_status = log_from_pipe(child_handle->stdout_handle,
LOG_INFO, filename, &retval);
- stderr_status = log_from_pipe(child_handle.stderr_handle,
+ stderr_status = log_from_pipe(child_handle->stderr_handle,
LOG_WARN, filename, &retval);
#endif
if (retval) {
@@ -4026,7 +4134,7 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
/* There was a failure */
retval = -1;
#ifdef MS_WINDOWS
- else if (tor_get_exit_code(child_handle, 0, NULL) !=
+ else if (!child_handle || tor_get_exit_code(child_handle, 0, NULL) !=
PROCESS_EXIT_RUNNING) {
/* process has exited or there was an error */
/* TODO: Do something with the process return value */
@@ -4049,10 +4157,10 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
if (0 != retval) {
if (1 == retval) {
log_info(LD_GENERAL, "Port forwarding helper terminated");
- child_handle.status = PROCESS_STATUS_NOTRUNNING;
+ child_handle->status = PROCESS_STATUS_NOTRUNNING;
} else {
log_warn(LD_GENERAL, "Failed to read from port forwarding helper");
- child_handle.status = PROCESS_STATUS_ERROR;
+ child_handle->status = PROCESS_STATUS_ERROR;
}
/* TODO: The child might not actually be finished (maybe it failed or
diff --git a/src/common/util.h b/src/common/util.h
index 77ed1ca5e..3a68f3993 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -173,19 +173,15 @@ int n_bits_set_u8(uint8_t v);
#define HEX_CHARACTERS "0123456789ABCDEFabcdef"
void tor_strlower(char *s) ATTR_NONNULL((1));
void tor_strupper(char *s) ATTR_NONNULL((1));
-int tor_strisprint(const char *s) ATTR_PURE ATTR_NONNULL((1));
-int tor_strisnonupper(const char *s) ATTR_PURE ATTR_NONNULL((1));
-int strcmp_opt(const char *s1, const char *s2) ATTR_PURE;
-int strcmpstart(const char *s1, const char *s2) ATTR_PURE ATTR_NONNULL((1,2));
-int strcmp_len(const char *s1, const char *s2, size_t len)
- ATTR_PURE ATTR_NONNULL((1,2));
-int strcasecmpstart(const char *s1, const char *s2)
- ATTR_PURE ATTR_NONNULL((1,2));
-int strcmpend(const char *s1, const char *s2) ATTR_PURE ATTR_NONNULL((1,2));
-int strcasecmpend(const char *s1, const char *s2)
- ATTR_PURE ATTR_NONNULL((1,2));
-int fast_memcmpstart(const void *mem, size_t memlen,
- const char *prefix) ATTR_PURE;
+int tor_strisprint(const char *s) ATTR_NONNULL((1));
+int tor_strisnonupper(const char *s) ATTR_NONNULL((1));
+int strcmp_opt(const char *s1, const char *s2);
+int strcmpstart(const char *s1, const char *s2) ATTR_NONNULL((1,2));
+int strcmp_len(const char *s1, const char *s2, size_t len) ATTR_NONNULL((1,2));
+int strcasecmpstart(const char *s1, const char *s2) ATTR_NONNULL((1,2));
+int strcmpend(const char *s1, const char *s2) ATTR_NONNULL((1,2));
+int strcasecmpend(const char *s1, const char *s2) ATTR_NONNULL((1,2));
+int fast_memcmpstart(const void *mem, size_t memlen, const char *prefix);
void tor_strstrip(char *s, const char *strip) ATTR_NONNULL((1,2));
long tor_parse_long(const char *s, int base, long min,
@@ -197,19 +193,19 @@ double tor_parse_double(const char *s, double min, double max, int *ok,
uint64_t tor_parse_uint64(const char *s, int base, uint64_t min,
uint64_t max, int *ok, char **next);
const char *hex_str(const char *from, size_t fromlen) ATTR_NONNULL((1));
-const char *eat_whitespace(const char *s) ATTR_PURE;
-const char *eat_whitespace_eos(const char *s, const char *eos) ATTR_PURE;
-const char *eat_whitespace_no_nl(const char *s) ATTR_PURE;
-const char *eat_whitespace_eos_no_nl(const char *s, const char *eos) ATTR_PURE;
-const char *find_whitespace(const char *s) ATTR_PURE;
-const char *find_whitespace_eos(const char *s, const char *eos) ATTR_PURE;
-const char *find_str_at_start_of_line(const char *haystack, const char *needle)
- ATTR_PURE;
+const char *eat_whitespace(const char *s);
+const char *eat_whitespace_eos(const char *s, const char *eos);
+const char *eat_whitespace_no_nl(const char *s);
+const char *eat_whitespace_eos_no_nl(const char *s, const char *eos);
+const char *find_whitespace(const char *s);
+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 tor_mem_is_zero(const char *mem, size_t len) ATTR_PURE;
-int tor_digest_is_zero(const char *digest) ATTR_PURE;
-int tor_digest256_is_zero(const char *digest) ATTR_PURE;
+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);
struct smartlist_t;
@@ -315,6 +311,7 @@ int check_private_dir(const char *dirname, cpd_check_t check,
const char *effective_user);
#define OPEN_FLAGS_REPLACE (O_WRONLY|O_CREAT|O_TRUNC)
#define OPEN_FLAGS_APPEND (O_WRONLY|O_CREAT|O_APPEND)
+#define OPEN_FLAGS_DONT_REPLACE (O_CREAT|O_EXCL|O_APPEND|O_WRONLY)
typedef struct open_file_t open_file_t;
int start_writing_to_file(const char *fname, int open_flags, int mode,
open_file_t **data_out);
@@ -336,6 +333,8 @@ int write_chunks_to_file(const char *fname, const struct smartlist_t *chunks,
int bin);
int append_bytes_to_file(const char *fname, const char *str, size_t len,
int bin);
+int write_bytes_to_new_file(const char *fname, const char *str, size_t len,
+ int bin);
/** Flag for read_file_to_str: open the file in binary mode. */
#define RFTS_BIN 1
@@ -349,7 +348,7 @@ const char *parse_config_line_from_str(const char *line,
char **key_out, char **value_out);
char *expand_filename(const char *filename);
struct smartlist_t *tor_listdir(const char *dirname);
-int path_is_relative(const char *filename) ATTR_PURE;
+int path_is_relative(const char *filename);
/* Process helpers */
void start_daemon(void);
@@ -360,10 +359,14 @@ void write_pidfile(char *filename);
void tor_check_port_forwarding(const char *filename,
int dir_port, int or_port, time_t now);
-int tor_terminate_process(pid_t pid);
-typedef struct process_handle_s process_handle_t;
+typedef struct process_handle_t process_handle_t;
int tor_spawn_background(const char *const filename, const char **argv,
- const char **envp, process_handle_t *process_handle);
+#ifdef MS_WINDOWS
+ LPVOID envp,
+#else
+ const char **envp,
+#endif
+ process_handle_t **process_handle_out);
#define SPAWN_ERROR_MESSAGE "ERR: Failed to spawn background process - code "
@@ -371,16 +374,16 @@ int tor_spawn_background(const char *const filename, const char **argv,
HANDLE load_windows_system_library(const TCHAR *library_name);
#endif
-#ifdef UTIL_PRIVATE
-/* Prototypes for private functions only used by util.c (and unit tests) */
-
/* Values of process_handle_t.status. PROCESS_STATUS_NOTRUNNING must be
* 0 because tor_check_port_forwarding depends on this being the initial
* statue of the static instance of process_handle_t */
#define PROCESS_STATUS_NOTRUNNING 0
#define PROCESS_STATUS_RUNNING 1
#define PROCESS_STATUS_ERROR -1
-struct process_handle_s {
+
+#ifdef UTIL_PRIVATE
+/*DOCDOC*/
+struct process_handle_t {
int status;
#ifdef MS_WINDOWS
HANDLE stdout_pipe;
@@ -394,12 +397,13 @@ struct process_handle_s {
pid_t pid;
#endif // MS_WINDOWS
};
+#endif
/* Return values of tor_get_exit_code() */
#define PROCESS_EXIT_RUNNING 1
#define PROCESS_EXIT_EXITED 0
#define PROCESS_EXIT_ERROR -1
-int tor_get_exit_code(const process_handle_t process_handle,
+int tor_get_exit_code(const process_handle_t *process_handle,
int block, int *exit_code);
int tor_split_lines(struct smartlist_t *sl, char *buf, int len);
#ifdef MS_WINDOWS
@@ -416,6 +420,20 @@ ssize_t tor_read_all_from_process_stderr(
const process_handle_t *process_handle, char *buf, size_t count);
char *tor_join_win_cmdline(const char *argv[]);
+int tor_process_get_pid(process_handle_t *process_handle);
+#ifdef MS_WINDOWS
+HANDLE tor_process_get_stdout_pipe(process_handle_t *process_handle);
+#else
+FILE *tor_process_get_stdout_pipe(process_handle_t *process_handle);
+#endif
+
+int tor_terminate_process(process_handle_t *process_handle);
+void tor_process_handle_destroy(process_handle_t *process_handle,
+ int also_terminate_process);
+
+#ifdef UTIL_PRIVATE
+/* Prototypes for private functions only used by util.c (and unit tests) */
+
void format_helper_exit_status(unsigned char child_state,
int saved_errno, char *hex_errno);
diff --git a/src/or/Makefile.am b/src/or/Makefile.am
index 67adf504d..a5682081a 100644
--- a/src/or/Makefile.am
+++ b/src/or/Makefile.am
@@ -126,8 +126,9 @@ tor_main.o: micro-revision.i
micro-revision.i: FORCE
@rm -f micro-revision.tmp; \
- if test -d ../../.git && test -x "`which git 2>&1;true`"; then \
- HASH="`git rev-parse --short=16 HEAD`"; \
+ if test -d "$(top_srcdir)/.git" && \
+ test -x "`which git 2>&1;true`"; then \
+ HASH="`cd "$(top_srcdir)" && git rev-parse --short=16 HEAD`"; \
echo \"$$HASH\" > micro-revision.tmp; \
fi; \
if test ! -f micro-revision.tmp ; then \
@@ -141,10 +142,10 @@ micro-revision.i: FORCE
or_sha1.i: $(tor_SOURCES) $(libtor_a_SOURCES)
if test "@SHA1SUM@" != none; then \
- @SHA1SUM@ $(tor_SOURCES) $(libtor_a_SOURCES) | \
+ (cd "$(srcdir)" && @SHA1SUM@ $(tor_SOURCES) $(libtor_a_SOURCES)) | \
@SED@ -n 's/^\(.*\)$$/"\1\\n"/p' > or_sha1.i; \
elif test "@OPENSSL@" != none; then \
- @OPENSSL@ sha1 $(tor_SOURCES) $(libtor_a_SOURCES) | \
+ (cd "$(srcdir)" && @OPENSSL@ sha1 $(tor_SOURCES) $(libtor_a_SOURCES)) | \
@SED@ -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > or_sha1.i; \
else \
rm or_sha1.i; \
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index b04bd1012..7934a2e7f 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -3012,7 +3012,7 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit)
log_warn(LD_CIRC,"failed to choose an exit server");
return -1;
}
- exit = extend_info_from_node(node);
+ exit = extend_info_from_node(node, 0);
tor_assert(exit);
}
state->chosen_exit = exit;
@@ -3254,14 +3254,19 @@ onion_extend_cpath(origin_circuit_t *circ)
} else if (cur_len == 0) { /* picking first node */
const node_t *r = choose_good_entry_server(purpose, state);
if (r) {
- info = extend_info_from_node(r);
+ /* If we're extending to a bridge, use the preferred address
+ rather than the primary, for potentially extending to an IPv6
+ bridge. */
+ int use_pref_addr = (r->ri != NULL &&
+ r->ri->purpose == ROUTER_PURPOSE_BRIDGE);
+ info = extend_info_from_node(r, use_pref_addr);
tor_assert(info);
}
} else {
const node_t *r =
choose_good_middle_server(purpose, state, circ->cpath, cur_len);
if (r) {
- info = extend_info_from_node(r);
+ info = extend_info_from_node(r, 0);
tor_assert(info);
}
}
@@ -3320,28 +3325,36 @@ extend_info_alloc(const char *nickname, const char *digest,
return info;
}
-/** Allocate and return a new extend_info_t that can be used to build a
- * circuit to or through the router <b>r</b>. */
+/** Allocate and return a new extend_info_t that can be used to build
+ * a circuit to or through the router <b>r</b>. Use the primary
+ * address of the router unless <b>for_direct_connect</b> is true, in
+ * which case the preferred address is used instead. */
extend_info_t *
-extend_info_from_router(const routerinfo_t *r)
+extend_info_from_router(const routerinfo_t *r, int for_direct_connect)
{
- tor_addr_t addr;
+ tor_addr_port_t ap;
tor_assert(r);
- tor_addr_from_ipv4h(&addr, r->addr);
+
+ if (for_direct_connect)
+ router_get_pref_orport(r, &ap);
+ else
+ router_get_prim_orport(r, &ap);
return extend_info_alloc(r->nickname, r->cache_info.identity_digest,
- r->onion_pkey, &addr, r->or_port);
+ r->onion_pkey, &ap.addr, ap.port);
}
-/** Allocate and return a new extend_info that can be used to build a ircuit
- * to or through the node <b>node</b>. May return NULL if there is not
- * enough info about <b>node</b> to extend to it--for example, if there
- * is no routerinfo_t or microdesc_t.
+/** Allocate and return a new extend_info that can be used to build a
+ * ircuit to or through the node <b>node</b>. Use the primary address
+ * of the node unless <b>for_direct_connect</b> is true, in which case
+ * the preferred address is used instead. May return NULL if there is
+ * not enough info about <b>node</b> to extend to it--for example, if
+ * there is no routerinfo_t or microdesc_t.
**/
extend_info_t *
-extend_info_from_node(const node_t *node)
+extend_info_from_node(const node_t *node, int for_direct_connect)
{
if (node->ri) {
- return extend_info_from_router(node->ri);
+ return extend_info_from_router(node->ri, for_direct_connect);
} else if (node->rs && node->md) {
tor_addr_t addr;
tor_addr_from_ipv4h(&addr, node->rs->addr);
@@ -4841,10 +4854,11 @@ get_configured_bridge_by_addr_port_digest(const tor_addr_t *addr,
static bridge_info_t *
get_configured_bridge_by_routerinfo(const routerinfo_t *ri)
{
- tor_addr_t addr;
- tor_addr_from_ipv4h(&addr, ri->addr);
- return get_configured_bridge_by_addr_port_digest(&addr,
- ri->or_port, ri->cache_info.identity_digest);
+ tor_addr_port_t ap;
+
+ router_get_pref_orport(ri, &ap);
+ return get_configured_bridge_by_addr_port_digest(&ap.addr, ap.port,
+ ri->cache_info.identity_digest);
}
/** Return 1 if <b>ri</b> is one of our known bridges, else 0. */
@@ -4858,18 +4872,31 @@ routerinfo_is_a_configured_bridge(const routerinfo_t *ri)
int
node_is_a_configured_bridge(const node_t *node)
{
- tor_addr_t addr;
- uint16_t orport;
+ int retval = 0; /* Negative. */
+ smartlist_t *orports = NULL;
+
if (!node)
- return 0;
- if (node_get_addr(node, &addr) < 0)
- return 0;
- orport = node_get_orport(node);
- if (orport == 0)
- return 0;
+ goto out;
+
+ orports = node_get_all_orports(node);
+ if (orports == NULL)
+ goto out;
- return get_configured_bridge_by_addr_port_digest(
- &addr, orport, node->identity) != NULL;
+ SMARTLIST_FOREACH_BEGIN(orports, tor_addr_port_t *, orport) {
+ if (get_configured_bridge_by_addr_port_digest(&orport->addr, orport->port,
+ node->identity) != NULL) {
+ retval = 1;
+ goto out;
+ }
+ } SMARTLIST_FOREACH_END(orport);
+
+ out:
+ if (orports != NULL) {
+ SMARTLIST_FOREACH(orports, tor_addr_port_t *, p, tor_free(p));
+ smartlist_free(orports);
+ orports = NULL;
+ }
+ return retval;
}
/** We made a connection to a router at <b>addr</b>:<b>port</b>
@@ -5123,18 +5150,52 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node)
routerinfo_t *ri = node->ri;
tor_addr_from_ipv4h(&addr, ri->addr);
- if (!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) &&
- bridge->port == ri->or_port) {
+ if ((!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) &&
+ bridge->port == ri->or_port) ||
+ (!tor_addr_compare(&bridge->addr, &ri->ipv6_addr, CMP_EXACT) &&
+ bridge->port == ri->ipv6_orport)) {
/* they match, so no need to do anything */
} else {
- ri->addr = tor_addr_to_ipv4h(&bridge->addr);
- tor_free(ri->address);
- ri->address = tor_dup_ip(ri->addr);
- ri->or_port = bridge->port;
- log_info(LD_DIR,
- "Adjusted bridge routerinfo for '%s' to match configured "
- "address %s:%d.",
- ri->nickname, ri->address, ri->or_port);
+ if (tor_addr_family(&bridge->addr) == AF_INET) {
+ ri->addr = tor_addr_to_ipv4h(&bridge->addr);
+ tor_free(ri->address);
+ ri->address = tor_dup_ip(ri->addr);
+ ri->or_port = bridge->port;
+ log_info(LD_DIR,
+ "Adjusted bridge routerinfo for '%s' to match configured "
+ "address %s:%d.",
+ ri->nickname, ri->address, ri->or_port);
+ } else if (tor_addr_family(&bridge->addr) == AF_INET6) {
+ tor_addr_copy(&ri->ipv6_addr, &bridge->addr);
+ ri->ipv6_orport = bridge->port;
+ log_info(LD_DIR,
+ "Adjusted bridge routerinfo for '%s' to match configured "
+ "address %s:%d.",
+ ri->nickname, fmt_addr(&ri->ipv6_addr), ri->ipv6_orport);
+ } else {
+ log_err(LD_BUG, "Address family not supported: %d.",
+ tor_addr_family(&bridge->addr));
+ return;
+ }
+ }
+
+ /* Indicate that we prefer connecting to this bridge over the
+ protocol that the bridge address indicates. Last bridge
+ descriptor handled wins. */
+ ri->ipv6_preferred = tor_addr_family(&bridge->addr) == AF_INET6;
+
+ /* XXXipv6 we lack support for falling back to another address for
+ the same relay, warn the user */
+ if (!tor_addr_is_null(&ri->ipv6_addr))
+ {
+ tor_addr_port_t ap;
+ router_get_pref_orport(ri, &ap);
+ log_notice(LD_CONFIG,
+ "Bridge '%s' has both an IPv4 and an IPv6 address. "
+ "Will prefer using its %s address (%s:%d).",
+ ri->nickname,
+ ri->ipv6_preferred ? "IPv6" : "IPv4",
+ fmt_addr(&ap.addr), ap.port);
}
}
if (node->rs) {
@@ -5179,8 +5240,8 @@ learned_bridge_descriptor(routerinfo_t *ri, int from_cache)
rewrite_node_address_for_bridge(bridge, node);
add_an_entry_guard(node, 1, 1);
- log_notice(LD_DIR, "new bridge descriptor '%s' (%s)", ri->nickname,
- from_cache ? "cached" : "fresh");
+ log_notice(LD_DIR, "new bridge descriptor '%s' (%s): %s", ri->nickname,
+ from_cache ? "cached" : "fresh", router_describe(ri));
/* set entry->made_contact so if it goes down we don't drop it from
* our entry node list */
entry_guard_register_connect_status(ri->cache_info.identity_digest,
diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h
index 1052db615..8df274870 100644
--- a/src/or/circuitbuild.h
+++ b/src/or/circuitbuild.h
@@ -59,8 +59,10 @@ void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop);
extend_info_t *extend_info_alloc(const char *nickname, const char *digest,
crypto_pk_env_t *onion_key,
const tor_addr_t *addr, uint16_t port);
-extend_info_t *extend_info_from_router(const routerinfo_t *r);
-extend_info_t *extend_info_from_node(const node_t *node);
+extend_info_t *extend_info_from_router(const routerinfo_t *r,
+ int for_direct_connect);
+extend_info_t *extend_info_from_node(const node_t *node,
+ int for_direct_connect);
extend_info_t *extend_info_dup(extend_info_t *info);
void extend_info_free(extend_info_t *info);
const node_t *build_state_get_exit_node(cpath_build_state_t *state);
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index f9931d88d..fbcfe2445 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -1439,7 +1439,10 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
int opt = conn->chosen_exit_optional;
r = node_get_by_nickname(conn->chosen_exit_name, 1);
if (r && node_has_descriptor(r)) {
- extend_info = extend_info_from_node(r);
+ /* We might want to connect to an IPv6 bridge for loading
+ descriptors so we use the preferred address rather than
+ the primary. */
+ extend_info = extend_info_from_node(r, conn->want_onehop ? 1 : 0);
} else {
log_debug(LD_DIR, "considering %d, %s",
want_onehop, conn->chosen_exit_name);
@@ -1690,7 +1693,7 @@ consider_recording_trackhost(const entry_connection_t *conn,
addressmap_register(conn->socks_request->address, new_address,
time(NULL) + options->TrackHostExitsExpire,
- ADDRMAPSRC_TRACKEXIT);
+ ADDRMAPSRC_TRACKEXIT, 0, 0);
}
/** Attempt to attach the connection <b>conn</b> to <b>circ</b>, and send a
diff --git a/src/or/config.c b/src/or/config.c
index adfde8138..525ff16b7 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -220,7 +220,7 @@ static config_var_t _option_vars[] = {
V(ConstrainedSockSize, MEMUNIT, "8192"),
V(ContactInfo, STRING, NULL),
V(ControlListenAddress, LINELIST, NULL),
- V(ControlPort, PORT, "0"),
+ V(ControlPort, LINELIST, NULL),
V(ControlPortFileGroupReadable,BOOL, "0"),
V(ControlPortWriteToFile, FILENAME, NULL),
V(ControlSocket, LINELIST, NULL),
@@ -231,12 +231,13 @@ static config_var_t _option_vars[] = {
V(CountPrivateBandwidth, BOOL, "0"),
V(DataDirectory, FILENAME, NULL),
OBSOLETE("DebugLogFile"),
+ V(DisableNetwork, BOOL, "0"),
V(DirAllowPrivateAddresses, BOOL, NULL),
V(TestingAuthDirTimeToLearnReachability, INTERVAL, "30 minutes"),
V(DirListenAddress, LINELIST, NULL),
OBSOLETE("DirFetchPeriod"),
V(DirPolicy, LINELIST, NULL),
- V(DirPort, PORT, "0"),
+ V(DirPort, LINELIST, NULL),
V(DirPortFrontPage, FILENAME, NULL),
OBSOLETE("DirPostPeriod"),
OBSOLETE("DirRecordUsageByCountry"),
@@ -246,7 +247,9 @@ static config_var_t _option_vars[] = {
V(DirReqStatistics, BOOL, "1"),
VAR("DirServer", LINELIST, DirServers, NULL),
V(DisableAllSwap, BOOL, "0"),
+ V(DisableDebuggerAttachment, BOOL, "1"),
V(DisableIOCP, BOOL, "1"),
+ V(DynamicDHGroups, BOOL, "1"),
V(DNSPort, LINELIST, NULL),
V(DNSListenAddress, LINELIST, NULL),
V(DownloadExtraInfo, BOOL, "0"),
@@ -340,7 +343,7 @@ static config_var_t _option_vars[] = {
V(NumCPUs, UINT, "0"),
V(NumEntryGuards, UINT, "3"),
V(ORListenAddress, LINELIST, NULL),
- V(ORPort, PORT, "0"),
+ V(ORPort, LINELIST, NULL),
V(OutboundBindAddress, STRING, NULL),
OBSOLETE("PathlenCoinWeight"),
V(PerConnBWBurst, MEMUNIT, "0"),
@@ -406,6 +409,7 @@ static config_var_t _option_vars[] = {
V(UseEntryGuards, BOOL, "1"),
V(UseMicrodescriptors, AUTOBOOL, "auto"),
V(User, STRING, NULL),
+ V(UserspaceIOCPBuffers, BOOL, "0"),
VAR("V1AuthoritativeDirectory",BOOL, V1AuthoritativeDir, "0"),
VAR("V2AuthoritativeDirectory",BOOL, V2AuthoritativeDir, "0"),
VAR("V3AuthoritativeDirectory",BOOL, V3AuthoritativeDir, "0"),
@@ -597,8 +601,11 @@ static int parse_dir_server_line(const char *line,
dirinfo_type_t required_type,
int validate_only);
static void port_cfg_free(port_cfg_t *port);
-static int parse_client_ports(const or_options_t *options, int validate_only,
+static int parse_ports(const or_options_t *options, int validate_only,
char **msg_out, int *n_ports_out);
+static int check_server_ports(const smartlist_t *ports,
+ const or_options_t *options);
+
static int validate_data_directory(or_options_t *options);
static int write_configuration_file(const char *fname,
const or_options_t *options);
@@ -611,9 +618,6 @@ static int or_state_validate(or_state_t *old_options, or_state_t *options,
static int or_state_load(void);
static int options_init_logs(or_options_t *options, int validate_only);
-static int is_listening_on_low_port(int port_option,
- const config_line_t *listen_options);
-
static uint64_t config_parse_memunit(const char *s, int *ok);
static int config_parse_msec_interval(const char *s, int *ok);
static int config_parse_interval(const char *s, int *ok);
@@ -660,16 +664,20 @@ static const config_format_t state_format = {
/** Command-line and config-file options. */
static or_options_t *global_options = NULL;
+/** DOCDOC */
+static or_options_t *global_default_options = NULL;
/** Name of most recently read torrc file. */
static char *torrc_fname = NULL;
+/** DOCDOC */
+static char *torrc_defaults_fname;
/** Persistent serialized state. */
static or_state_t *global_state = NULL;
/** Configuration Options set by command line. */
static config_line_t *global_cmdline_options = NULL;
/** Contents of most recently read DirPortFrontPage file. */
static char *global_dirfrontpagecontents = NULL;
-/** List of port_cfg_t for client-level (SOCKS, DNS, Trans, NATD) ports. */
-static smartlist_t *configured_client_ports = NULL;
+/** List of port_cfg_t for all configured ports. */
+static smartlist_t *configured_ports = NULL;
/** Return the contents of our frontpage string, or NULL if not configured. */
const char *
@@ -805,6 +813,8 @@ config_free_all(void)
{
or_options_free(global_options);
global_options = NULL;
+ or_options_free(global_default_options);
+ global_default_options = NULL;
config_free(&state_format, global_state);
global_state = NULL;
@@ -812,14 +822,15 @@ config_free_all(void)
config_free_lines(global_cmdline_options);
global_cmdline_options = NULL;
- if (configured_client_ports) {
- SMARTLIST_FOREACH(configured_client_ports,
+ if (configured_ports) {
+ SMARTLIST_FOREACH(configured_ports,
port_cfg_t *, p, tor_free(p));
- smartlist_free(configured_client_ports);
- configured_client_ports = NULL;
+ smartlist_free(configured_ports);
+ configured_ports = NULL;
}
tor_free(torrc_fname);
+ tor_free(torrc_defaults_fname);
tor_free(_version);
tor_free(global_dirfrontpagecontents);
}
@@ -1065,7 +1076,7 @@ options_act_reversible(const or_options_t *old_options, char **msg)
#endif
if (running_tor) {
- int n_client_ports=0;
+ int n_ports=0;
/* We need to set the connection limit before we can open the listeners. */
if (set_max_file_descriptors((unsigned)options->ConnLimit,
&options->_ConnLimit) < 0) {
@@ -1081,10 +1092,10 @@ options_act_reversible(const or_options_t *old_options, char **msg)
libevent_initialized = 1;
}
- /* Adjust the client port configuration so we can launch listeners. */
- if (parse_client_ports(options, 0, msg, &n_client_ports)) {
+ /* Adjust the port configuration so we can launch listeners. */
+ if (parse_ports(options, 0, msg, &n_ports)) {
if (!*msg)
- *msg = tor_strdup("Unexpected problem parsing client port config");
+ *msg = tor_strdup("Unexpected problem parsing port config");
goto rollback;
}
@@ -1092,13 +1103,22 @@ options_act_reversible(const or_options_t *old_options, char **msg)
consider_hibernation(time(NULL));
/* Launch the listeners. (We do this before we setuid, so we can bind to
- * ports under 1024.) We don't want to rebind if we're hibernating. */
+ * ports under 1024.) We don't want to rebind if we're hibernating. If
+ * networking is disabled, this will close all but the control listeners,
+ * but disable those. */
if (!we_are_hibernating()) {
if (retry_all_listeners(replaced_listeners, new_listeners) < 0) {
*msg = tor_strdup("Failed to bind one of the listener ports.");
goto rollback;
}
}
+ if (options->DisableNetwork) {
+ /* Aggressively close non-controller stuff, NOW */
+ log_notice(LD_NET, "DisableNetwork is set. Tor will not make or accept "
+ "non-control network connections. Shutting down all existing "
+ "connections.");
+ connection_mark_all_noncontrol_connections();
+ }
}
#if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H)
@@ -1267,6 +1287,24 @@ get_effective_bwburst(const or_options_t *options)
return (uint32_t)bw;
}
+/** Return True if any changes from <b>old_options</b> to
+ * <b>new_options</b> needs us to refresh our TLS context. */
+static int
+options_transition_requires_fresh_tls_context(const or_options_t *old_options,
+ const or_options_t *new_options)
+{
+ tor_assert(new_options);
+
+ if (!old_options)
+ return 0;
+
+ if ((old_options->DynamicDHGroups != new_options->DynamicDHGroups)) {
+ return 1;
+ }
+
+ return 0;
+}
+
/** Fetch the active option list, and take actions based on it. All of the
* things we do should survive being done repeatedly. If present,
* <b>old_options</b> contains the previous value of the options.
@@ -1286,6 +1324,14 @@ options_act(const or_options_t *old_options)
const int transition_affects_workers =
old_options && options_transition_affects_workers(old_options, options);
+ /* disable ptrace and later, other basic debugging techniques */
+ if (options->DisableDebuggerAttachment) {
+ tor_disable_debugger_attach();
+ } else {
+ log_notice(LD_CONFIG,"Debugger attachment enabled "
+ "for unprivileged users.");
+ }
+
if (running_tor && !have_lockfile()) {
if (try_locking(options, 1) < 0)
return -1;
@@ -1384,6 +1430,29 @@ options_act(const or_options_t *old_options)
finish_daemon(options->DataDirectory);
}
+ /* If needed, generate a new TLS DH prime according to the current torrc. */
+ if (server_mode(options)) {
+ if (!old_options) {
+ if (options->DynamicDHGroups) {
+ char *fname = get_datadir_fname2("keys", "dynamic_dh_params");
+ crypto_set_tls_dh_prime(fname);
+ tor_free(fname);
+ } else {
+ crypto_set_tls_dh_prime(NULL);
+ }
+ } else {
+ if (options->DynamicDHGroups && !old_options->DynamicDHGroups) {
+ char *fname = get_datadir_fname2("keys", "dynamic_dh_params");
+ crypto_set_tls_dh_prime(fname);
+ tor_free(fname);
+ } else if (!options->DynamicDHGroups && old_options->DynamicDHGroups) {
+ crypto_set_tls_dh_prime(NULL);
+ }
+ }
+ } else { /* clients don't need a dynamic DH prime. */
+ crypto_set_tls_dh_prime(NULL);
+ }
+
/* We want to reinit keys as needed before we do much of anything else:
keys are important, and other things can depend on them. */
if (transition_affects_workers ||
@@ -1393,6 +1462,13 @@ options_act(const or_options_t *old_options)
log_warn(LD_BUG,"Error initializing keys; exiting");
return -1;
}
+ } else if (old_options &&
+ options_transition_requires_fresh_tls_context(old_options,
+ options)) {
+ if (router_initialize_tls_context() < 0) {
+ log_warn(LD_BUG,"Error initializing TLS context.");
+ return -1;
+ }
}
/* Write our PID to the PID file. If we do not have write permissions we
@@ -1502,7 +1578,7 @@ options_act(const or_options_t *old_options)
int was_relay = 0;
if (options->BridgeRelay) {
time_t int_start = time(NULL);
- if (old_options->ORPort == options->ORPort) {
+ if (config_lines_eq(old_options->ORPort, options->ORPort)) {
int_start += RELAY_BRIDGE_STATS_DELAY;
was_relay = 1;
}
@@ -1741,7 +1817,11 @@ config_get_commandlines(int argc, char **argv, config_line_t **result)
int i = 1;
while (i < argc) {
+ unsigned command = CONFIG_LINE_NORMAL;
+ int want_arg = 1;
+
if (!strcmp(argv[i],"-f") ||
+ !strcmp(argv[i],"--defaults-torrc") ||
!strcmp(argv[i],"--hash-password")) {
i += 2; /* command-line option with argument. ignore them. */
continue;
@@ -1758,13 +1838,6 @@ config_get_commandlines(int argc, char **argv, config_line_t **result)
continue;
}
- if (i == argc-1) {
- log_warn(LD_CONFIG,"Command-line option '%s' with no value. Failing.",
- argv[i]);
- config_free_lines(front);
- return -1;
- }
-
*new = tor_malloc_zero(sizeof(config_line_t));
s = argv[i];
@@ -1773,15 +1846,33 @@ config_get_commandlines(int argc, char **argv, config_line_t **result)
s++;
if (*s == '-')
s++;
+ /* Figure out the command, if any. */
+ if (*s == '+') {
+ s++;
+ command = CONFIG_LINE_APPEND;
+ } else if (*s == '/') {
+ s++;
+ command = CONFIG_LINE_CLEAR;
+ /* A 'clear' command has no argument. */
+ want_arg = 0;
+ }
+
+ if (want_arg && i == argc-1) {
+ log_warn(LD_CONFIG,"Command-line option '%s' with no value. Failing.",
+ argv[i]);
+ config_free_lines(front);
+ return -1;
+ }
(*new)->key = tor_strdup(expand_abbrev(&options_format, s, 1, 1));
- (*new)->value = tor_strdup(argv[i+1]);
+ (*new)->value = want_arg ? tor_strdup(argv[i+1]) : tor_strdup("");
+ (*new)->command = command;
(*new)->next = NULL;
log(LOG_DEBUG, LD_CONFIG, "command line: parsed keyword '%s', value '%s'",
(*new)->key, (*new)->value);
new = &((*new)->next);
- i += 2;
+ i += want_arg ? 2 : 1;
}
*result = front;
return 0;
@@ -1796,7 +1887,7 @@ config_line_append(config_line_t **lst,
{
config_line_t *newline;
- newline = tor_malloc(sizeof(config_line_t));
+ newline = tor_malloc_zero(sizeof(config_line_t));
newline->key = tor_strdup(key);
newline->value = tor_strdup(val);
newline->next = NULL;
@@ -1809,9 +1900,12 @@ config_line_append(config_line_t **lst,
/** Helper: parse the config string and strdup into key/value
* strings. Set *result to the list, or NULL if parsing the string
* failed. Return 0 on success, -1 on failure. Warn and ignore any
- * misformatted lines. */
+ * misformatted lines.
+ *
+ * If <b>extended</b> is set, then treat keys beginning with / and with + as
+ * indicating "clear" and "append" respectively. */
int
-config_get_lines(const char *string, config_line_t **result)
+config_get_lines(const char *string, config_line_t **result, int extended)
{
config_line_t *list = NULL, **next;
char *k, *v;
@@ -1827,13 +1921,30 @@ config_get_lines(const char *string, config_line_t **result)
return -1;
}
if (k && v) {
+ unsigned command = CONFIG_LINE_NORMAL;
+ if (extended) {
+ if (k[0] == '+') {
+ char *k_new = tor_strdup(k+1);
+ tor_free(k);
+ k = k_new;
+ command = CONFIG_LINE_APPEND;
+ } else if (k[0] == '/') {
+ char *k_new = tor_strdup(k+1);
+ tor_free(k);
+ k = k_new;
+ tor_free(v);
+ v = tor_strdup("");
+ command = CONFIG_LINE_CLEAR;
+ }
+ }
/* This list can get long, so we keep a pointer to the end of it
* rather than using config_line_append over and over and getting
* n^2 performance. */
- *next = tor_malloc(sizeof(config_line_t));
+ *next = tor_malloc_zero(sizeof(config_line_t));
(*next)->key = k;
(*next)->value = v;
(*next)->next = NULL;
+ (*next)->command = command;
next = &((*next)->next);
} else {
tor_free(k);
@@ -2061,7 +2172,19 @@ config_assign_value(const config_format_t *fmt, or_options_t *options,
case CONFIG_TYPE_LINELIST:
case CONFIG_TYPE_LINELIST_S:
- config_line_append((config_line_t**)lvalue, c->key, c->value);
+ {
+ config_line_t *lastval = *(config_line_t**)lvalue;
+ if (lastval && lastval->fragile) {
+ if (c->command != CONFIG_LINE_APPEND) {
+ config_free_lines(lastval);
+ *(config_line_t**)lvalue = NULL;
+ } else {
+ lastval->fragile = 0;
+ }
+ }
+
+ config_line_append((config_line_t**)lvalue, c->key, c->value);
+ }
break;
case CONFIG_TYPE_OBSOLETE:
log_warn(LD_CONFIG, "Skipping obsolete configuration option '%s'", c->key);
@@ -2077,6 +2200,28 @@ config_assign_value(const config_format_t *fmt, or_options_t *options,
return 0;
}
+/** Mark every linelist in <b>options<b> "fragile", so that fresh assignments
+ * to it will replace old ones. */
+static void
+config_mark_lists_fragile(const config_format_t *fmt, or_options_t *options)
+{
+ int i;
+ tor_assert(fmt);
+ tor_assert(options);
+
+ for (i = 0; fmt->vars[i].name; ++i) {
+ const config_var_t *var = &fmt->vars[i];
+ config_line_t *list;
+ if (var->type != CONFIG_TYPE_LINELIST &&
+ var->type != CONFIG_TYPE_LINELIST_V)
+ continue;
+
+ list = *(config_line_t **)STRUCT_VAR_P(options, var->var_offset);
+ if (list)
+ list->fragile = 1;
+ }
+}
+
/** If <b>c</b> is a syntactically valid configuration line, update
* <b>options</b> with its value and return 0. Otherwise return -1 for bad
* key, -2 for bad value.
@@ -2119,8 +2264,9 @@ config_assign_line(const config_format_t *fmt, or_options_t *options,
if (!strlen(c->value)) {
/* reset or clear it, then return */
if (!clear_first) {
- if (var->type == CONFIG_TYPE_LINELIST ||
- var->type == CONFIG_TYPE_LINELIST_S) {
+ if ((var->type == CONFIG_TYPE_LINELIST ||
+ var->type == CONFIG_TYPE_LINELIST_S) &&
+ c->command != CONFIG_LINE_CLEAR) {
/* We got an empty linelist from the torrc or command line.
As a special case, call this an error. Warn and ignore. */
log_warn(LD_CONFIG,
@@ -2130,6 +2276,8 @@ config_assign_line(const config_format_t *fmt, or_options_t *options,
}
}
return 0;
+ } else if (c->command == CONFIG_LINE_CLEAR && !clear_first) {
+ option_reset(fmt, options, var, use_defaults);
}
if (options_seen && (var->type != CONFIG_TYPE_LINELIST &&
@@ -2224,7 +2372,7 @@ config_lines_dup(const config_line_t *inp)
config_line_t *result = NULL;
config_line_t **next_out = &result;
while (inp) {
- *next_out = tor_malloc(sizeof(config_line_t));
+ *next_out = tor_malloc_zero(sizeof(config_line_t));
(*next_out)->key = tor_strdup(inp->key);
(*next_out)->value = tor_strdup(inp->value);
inp = inp->next;
@@ -2461,6 +2609,12 @@ config_assign(const config_format_t *fmt, void *options, config_line_t *list,
list = list->next;
}
bitarray_free(options_seen);
+
+ /** Now we're done assigning a group of options to the configuration.
+ * Subsequent group assignments should _replace_ linelists, not extend
+ * them. */
+ config_mark_lists_fragile(fmt, options);
+
return 0;
}
@@ -2903,37 +3057,6 @@ options_init(or_options_t *options)
config_init(&options_format, options);
}
-/* Check if the port number given in <b>port_option</b> in combination with
- * the specified port in <b>listen_options</b> will result in Tor actually
- * opening a low port (meaning a port lower than 1024). Return 1 if
- * it is, or 0 if it isn't or the concept of a low port isn't applicable for
- * the platform we're on. */
-static int
-is_listening_on_low_port(int port_option,
- const config_line_t *listen_options)
-{
-#ifdef MS_WINDOWS
- (void) port_option;
- (void) listen_options;
- return 0; /* No port is too low for windows. */
-#else
- const config_line_t *l;
- uint16_t p;
- if (port_option == 0)
- return 0; /* We're not listening */
- if (listen_options == NULL)
- return (port_option < 1024);
-
- for (l = listen_options; l; l = l->next) {
- addr_port_lookup(LOG_WARN, l->value, NULL, NULL, &p);
- if (p<1024) {
- return 1;
- }
- }
- return 0;
-#endif
-}
-
/** Set all vars in the configuration object <b>options</b> to their default
* values. */
static void
@@ -2956,24 +3079,30 @@ config_init(const config_format_t *fmt, void *options)
* Else, if comment_defaults, write default values as comments.
*/
static char *
-config_dump(const config_format_t *fmt, const void *options, int minimal,
+config_dump(const config_format_t *fmt, const void *default_options,
+ const void *options, int minimal,
int comment_defaults)
{
smartlist_t *elements;
- or_options_t *defaults;
+ const or_options_t *defaults = default_options;
+ void *defaults_tmp = NULL;
config_line_t *line, *assigned;
char *result;
int i;
char *msg = NULL;
- defaults = config_alloc(fmt);
- config_init(fmt, defaults);
+ if (defaults == NULL) {
+ defaults = defaults_tmp = config_alloc(fmt);
+ config_init(fmt, defaults_tmp);
+ }
/* XXX use a 1 here so we don't add a new log line while dumping */
- if (fmt->validate_fn(NULL,defaults, 1, &msg) < 0) {
- log_err(LD_BUG, "Failed to validate default config.");
- tor_free(msg);
- tor_assert(0);
+ if (default_options == NULL) {
+ if (fmt->validate_fn(NULL, defaults_tmp, 1, &msg) < 0) {
+ log_err(LD_BUG, "Failed to validate default config.");
+ tor_free(msg);
+ tor_assert(0);
+ }
}
elements = smartlist_create();
@@ -3015,7 +3144,8 @@ config_dump(const config_format_t *fmt, const void *options, int minimal,
result = smartlist_join_strings(elements, "", 0, NULL);
SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
smartlist_free(elements);
- config_free(fmt, defaults);
+ if (defaults_tmp)
+ config_free(fmt, defaults_tmp);
return result;
}
@@ -3026,7 +3156,8 @@ config_dump(const config_format_t *fmt, const void *options, int minimal,
char *
options_dump(const or_options_t *options, int minimal)
{
- return config_dump(&options_format, options, minimal, 0);
+ return config_dump(&options_format, global_default_options,
+ options, minimal, 0);
}
/** Return 0 if every element of sl is a string holding a decimal
@@ -3151,7 +3282,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
int i;
config_line_t *cl;
const char *uname = get_uname();
- int n_client_ports=0;
+ int n_ports=0;
#define REJECT(arg) \
STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END
#define COMPLAIN(arg) STMT_BEGIN log(LOG_WARN, LD_CONFIG, arg); STMT_END
@@ -3169,13 +3300,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
"for details.", uname);
}
- if (options->ORPort == 0 && options->ORListenAddress != NULL)
- REJECT("ORPort must be defined if ORListenAddress is defined.");
-
- if (options->DirPort == 0 && options->DirListenAddress != NULL)
- REJECT("DirPort must be defined if DirListenAddress is defined.");
-
- if (parse_client_ports(options, 1, msg, &n_client_ports) < 0)
+ if (parse_ports(options, 1, msg, &n_ports) < 0)
return -1;
if (validate_data_directory(options)<0)
@@ -3222,7 +3347,9 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("Can't use a relative path to torrc when RunAsDaemon is set.");
#endif
- if (n_client_ports == 0 && options->ORPort == 0 && !options->RendConfigLines)
+ /* XXXX require that the only port not be DirPort? */
+ /* XXXX require that at least one port be listened-upon. */
+ if (n_ports == 0 && !options->RendConfigLines)
log(LOG_WARN, LD_CONFIG,
"SocksPort, TransPort, NATDPort, DNSPort, and ORPort are all "
"undefined, and there aren't any hidden services configured. "
@@ -3238,19 +3365,6 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("TokenBucketRefillInterval must be between 1 and 1000 inclusive.");
}
- if (options->AccountingMax &&
- (is_listening_on_low_port(options->ORPort, options->ORListenAddress) ||
- is_listening_on_low_port(options->DirPort, options->DirListenAddress)))
- {
- log(LOG_WARN, LD_CONFIG,
- "You have set AccountingMax to use hibernation. You have also "
- "chosen a low DirPort or OrPort. This combination can make Tor stop "
- "working when it tries to re-attach the port after a period of "
- "hibernation. Please choose a different port or turn off "
- "hibernation unless you know this combination will work on your "
- "platform.");
- }
-
if (options->ExcludeExitNodes || options->ExcludeNodes) {
options->_ExcludeExitNodesUnion = routerset_new();
routerset_union(options->_ExcludeExitNodesUnion,options->ExcludeExitNodes);
@@ -3504,7 +3618,8 @@ options_validate(or_options_t *old_options, or_options_t *options,
if (options->BridgeRelay && options->DirPort) {
log_warn(LD_CONFIG, "Can't set a DirPort on a bridge relay; disabling "
"DirPort");
- options->DirPort = 0;
+ config_free_lines(options->DirPort);
+ options->DirPort = NULL;
}
if (options->MinUptimeHidServDirectoryV2 < 0) {
@@ -3745,39 +3860,6 @@ options_validate(or_options_t *old_options, or_options_t *options,
}
}
- if (options->ControlListenAddress) {
- int all_are_local = 1;
- config_line_t *ln;
- for (ln = options->ControlListenAddress; ln; ln = ln->next) {
- if (strcmpstart(ln->value, "127."))
- all_are_local = 0;
- }
- if (!all_are_local) {
- if (!options->HashedControlPassword &&
- !options->HashedControlSessionPassword &&
- !options->CookieAuthentication) {
- log_warn(LD_CONFIG,
- "You have a ControlListenAddress set to accept "
- "unauthenticated connections from a non-local address. "
- "This means that programs not running on your computer "
- "can reconfigure your Tor, without even having to guess a "
- "password. That's so bad that I'm closing your ControlPort "
- "for you. If you need to control your Tor remotely, try "
- "enabling authentication and using a tool like stunnel or "
- "ssh to encrypt remote access.");
- options->ControlPort = 0;
- } else {
- log_warn(LD_CONFIG, "You have a ControlListenAddress set to accept "
- "connections from a non-local address. This means that "
- "programs not running on your computer can reconfigure your "
- "Tor. That's pretty bad, since the controller "
- "protocol isn't encrypted! Maybe you should just listen on "
- "127.0.0.1 and use a tool like stunnel or ssh to encrypt "
- "remote connections to your control port.");
- }
- }
- }
-
if (options->ControlPort && !options->HashedControlPassword &&
!options->HashedControlSessionPassword &&
!options->CookieAuthentication) {
@@ -4000,8 +4082,9 @@ options_validate(or_options_t *old_options, or_options_t *options,
}
});
- if (options->BridgeRelay == 1 && options->ORPort == 0)
- REJECT("BridgeRelay is 1, ORPort is 0. This is an invalid combination.");
+ if (options->BridgeRelay == 1 && ! options->ORPort)
+ REJECT("BridgeRelay is 1, ORPort is not set. This is an invalid "
+ "combination.");
return 0;
#undef REJECT
@@ -4092,7 +4175,7 @@ options_transition_affects_workers(const or_options_t *old_options,
{
if (!opt_streq(old_options->DataDirectory, new_options->DataDirectory) ||
old_options->NumCPUs != new_options->NumCPUs ||
- old_options->ORPort != new_options->ORPort ||
+ !config_lines_eq(old_options->ORPort, new_options->ORPort) ||
old_options->ServerDNSSearchDomains !=
new_options->ServerDNSSearchDomains ||
old_options->_SafeLogging != new_options->_SafeLogging ||
@@ -4122,9 +4205,10 @@ options_transition_affects_descriptor(const or_options_t *old_options,
!config_lines_eq(old_options->ExitPolicy,new_options->ExitPolicy) ||
old_options->ExitPolicyRejectPrivate !=
new_options->ExitPolicyRejectPrivate ||
- old_options->ORPort != new_options->ORPort ||
- old_options->DirPort != new_options->DirPort ||
+ !config_lines_eq(old_options->ORPort, new_options->ORPort) ||
+ !config_lines_eq(old_options->DirPort, new_options->DirPort) ||
old_options->ClientOnly != new_options->ClientOnly ||
+ old_options->DisableNetwork != new_options->DisableNetwork ||
old_options->_PublishServerDescriptor !=
new_options->_PublishServerDescriptor ||
get_effective_bwrate(old_options) != get_effective_bwrate(new_options) ||
@@ -4198,17 +4282,25 @@ get_windows_conf_root(void)
}
#endif
-/** Return the default location for our torrc file. */
+/** Return the default location for our torrc file.
+ * DOCDOC defaults_file */
static const char *
-get_default_conf_file(void)
+get_default_conf_file(int defaults_file)
{
#ifdef MS_WINDOWS
- static char path[MAX_PATH+1];
- strlcpy(path, get_windows_conf_root(), MAX_PATH);
- strlcat(path,"\\torrc",MAX_PATH);
- return path;
+ if (defaults_file) {
+ static char defaults_path[MAX_PATH+1];
+ tor_snprintf(defaults_path, MAX_PATH, "%s\\torrc-defaults",
+ get_windows_conf_root());
+ return defaults_path;
+ } else {
+ static char path[MAX_PATH+1];
+ tor_snprintf(path, MAX_PATH, "%s\\torrc",
+ get_windows_conf_root());
+ return path;
+ }
#else
- return (CONFDIR "/torrc");
+ return defaults_file ? CONFDIR "/torrc-defaults" : CONFDIR "/torrc";
#endif
}
@@ -4241,37 +4333,46 @@ check_nickname_list(const char *lst, const char *name, char **msg)
return r;
}
-/** Learn config file name from command line arguments, or use the default */
+/** Learn config file name from command line arguments, or use the default,
+ * DOCDOC defaults_file */
static char *
find_torrc_filename(int argc, char **argv,
+ int defaults_file,
int *using_default_torrc, int *ignore_missing_torrc)
{
char *fname=NULL;
int i;
+ const char *fname_opt = defaults_file ? "--defaults-torrc" : "-f";
+ const char *ignore_opt = defaults_file ? NULL : "--ignore-missing-torrc";
+
+ if (defaults_file)
+ *ignore_missing_torrc = 1;
for (i = 1; i < argc; ++i) {
- if (i < argc-1 && !strcmp(argv[i],"-f")) {
+ if (i < argc-1 && !strcmp(argv[i],fname_opt)) {
if (fname) {
- log(LOG_WARN, LD_CONFIG, "Duplicate -f options on command line.");
+ log(LOG_WARN, LD_CONFIG, "Duplicate %s options on command line.",
+ fname_opt);
tor_free(fname);
}
fname = expand_filename(argv[i+1]);
*using_default_torrc = 0;
++i;
- } else if (!strcmp(argv[i],"--ignore-missing-torrc")) {
+ } else if (ignore_opt && !strcmp(argv[i],ignore_opt)) {
*ignore_missing_torrc = 1;
}
}
if (*using_default_torrc) {
/* didn't find one, try CONFDIR */
- const char *dflt = get_default_conf_file();
+ const char *dflt = get_default_conf_file(defaults_file);
if (dflt && file_status(dflt) == FN_FILE) {
fname = tor_strdup(dflt);
} else {
#ifndef MS_WINDOWS
- char *fn;
- fn = expand_filename("~/.torrc");
+ char *fn = NULL;
+ if (!defaults_file)
+ fn = expand_filename("~/.torrc");
if (fn && file_status(fn) == FN_FILE) {
fname = fn;
} else {
@@ -4286,31 +4387,34 @@ find_torrc_filename(int argc, char **argv,
return fname;
}
-/** Load torrc from disk, setting torrc_fname if successful */
+/** Load torrc from disk, setting torrc_fname if successful.
+ * DOCDOC defaults_file */
static char *
-load_torrc_from_disk(int argc, char **argv)
+load_torrc_from_disk(int argc, char **argv, int defaults_file)
{
char *fname=NULL;
char *cf = NULL;
int using_default_torrc = 1;
int ignore_missing_torrc = 0;
+ char **fname_var = defaults_file ? &torrc_defaults_fname : &torrc_fname;
- fname = find_torrc_filename(argc, argv,
+ fname = find_torrc_filename(argc, argv, defaults_file,
&using_default_torrc, &ignore_missing_torrc);
tor_assert(fname);
log(LOG_DEBUG, LD_CONFIG, "Opening config file \"%s\"", fname);
- tor_free(torrc_fname);
- torrc_fname = fname;
+ tor_free(*fname_var);
+ *fname_var = fname;
/* Open config file */
if (file_status(fname) != FN_FILE ||
!(cf = read_file_to_str(fname,0,NULL))) {
- if (using_default_torrc == 1 || ignore_missing_torrc ) {
- log(LOG_NOTICE, LD_CONFIG, "Configuration file \"%s\" not present, "
- "using reasonable defaults.", fname);
+ if (using_default_torrc == 1 || ignore_missing_torrc) {
+ if (!defaults_file)
+ log(LOG_NOTICE, LD_CONFIG, "Configuration file \"%s\" not present, "
+ "using reasonable defaults.", fname);
tor_free(fname); /* sets fname to NULL */
- torrc_fname = NULL;
+ *fname_var = NULL;
cf = tor_strdup("");
} else {
log(LOG_WARN, LD_CONFIG,
@@ -4324,7 +4428,7 @@ load_torrc_from_disk(int argc, char **argv)
return cf;
err:
tor_free(fname);
- torrc_fname = NULL;
+ *fname_var = NULL;
return NULL;
}
@@ -4335,8 +4439,9 @@ load_torrc_from_disk(int argc, char **argv)
int
options_init_from_torrc(int argc, char **argv)
{
- char *cf=NULL;
- int i, retval, command;
+ char *cf=NULL, *cf_defaults=NULL;
+ int i, command;
+ int retval = -1;
static char **backup_argv;
static int backup_argc;
char *command_arg = NULL;
@@ -4395,24 +4500,24 @@ options_init_from_torrc(int argc, char **argv)
if (command == CMD_HASH_PASSWORD) {
cf = tor_strdup("");
} else {
- cf = load_torrc_from_disk(argc, argv);
+ cf_defaults = load_torrc_from_disk(argc, argv, 1);
+ cf = load_torrc_from_disk(argc, argv, 0);
if (!cf)
goto err;
}
- retval = options_init_from_string(cf, command, command_arg, &errmsg);
- tor_free(cf);
- if (retval < 0)
- goto err;
-
- return 0;
+ retval = options_init_from_string(cf_defaults, cf, command, command_arg,
+ &errmsg);
err:
+
+ tor_free(cf);
+ tor_free(cf_defaults);
if (errmsg) {
log(LOG_WARN,LD_CONFIG,"%s", errmsg);
tor_free(errmsg);
}
- return -1;
+ return retval < 0 ? -1 : 0;
}
/** Load the options from the configuration in <b>cf</b>, validate
@@ -4425,13 +4530,13 @@ options_init_from_torrc(int argc, char **argv)
* * -4 for error while setting the new options
*/
setopt_err_t
-options_init_from_string(const char *cf,
+options_init_from_string(const char *cf_defaults, const char *cf,
int command, const char *command_arg,
char **msg)
{
- or_options_t *oldoptions, *newoptions;
+ or_options_t *oldoptions, *newoptions, *newdefaultoptions=NULL;
config_line_t *cl;
- int retval;
+ int retval, i;
setopt_err_t err = SETOPT_ERR_MISC;
tor_assert(msg);
@@ -4444,17 +4549,24 @@ options_init_from_string(const char *cf,
newoptions->command = command;
newoptions->command_arg = command_arg;
- /* get config lines, assign them */
- retval = config_get_lines(cf, &cl);
- if (retval < 0) {
- err = SETOPT_ERR_PARSE;
- goto err;
- }
- retval = config_assign(&options_format, newoptions, cl, 0, 0, msg);
- config_free_lines(cl);
- if (retval < 0) {
- err = SETOPT_ERR_PARSE;
- goto err;
+ for (i = 0; i < 2; ++i) {
+ const char *body = i==0 ? cf_defaults : cf;
+ if (!body)
+ continue;
+ /* get config lines, assign them */
+ retval = config_get_lines(body, &cl, 1);
+ if (retval < 0) {
+ err = SETOPT_ERR_PARSE;
+ goto err;
+ }
+ retval = config_assign(&options_format, newoptions, cl, 0, 0, msg);
+ config_free_lines(cl);
+ if (retval < 0) {
+ err = SETOPT_ERR_PARSE;
+ goto err;
+ }
+ if (i==0)
+ newdefaultoptions = options_dup(&options_format, newoptions);
}
/* Go through command-line variables too */
@@ -4489,6 +4601,8 @@ options_init_from_string(const char *cf,
/* Clear newoptions and re-initialize them with new defaults. */
config_free(&options_format, newoptions);
+ config_free(&options_format, newdefaultoptions);
+ newdefaultoptions = NULL;
newoptions = tor_malloc_zero(sizeof(or_options_t));
newoptions->_magic = OR_OPTIONS_MAGIC;
options_init(newoptions);
@@ -4496,22 +4610,24 @@ options_init_from_string(const char *cf,
newoptions->command_arg = command_arg;
/* Assign all options a second time. */
- retval = config_get_lines(cf, &cl);
- if (retval < 0) {
- err = SETOPT_ERR_PARSE;
- goto err;
- }
- retval = config_assign(&options_format, newoptions, cl, 0, 0, msg);
- config_free_lines(cl);
- if (retval < 0) {
- err = SETOPT_ERR_PARSE;
- goto err;
- }
- retval = config_assign(&options_format, newoptions,
- global_cmdline_options, 0, 0, msg);
- if (retval < 0) {
- err = SETOPT_ERR_PARSE;
- goto err;
+ for (i = 0; i < 2; ++i) {
+ const char *body = i==0 ? cf_defaults : cf;
+ if (!body)
+ continue;
+ /* get config lines, assign them */
+ retval = config_get_lines(body, &cl, 1);
+ if (retval < 0) {
+ err = SETOPT_ERR_PARSE;
+ goto err;
+ }
+ retval = config_assign(&options_format, newoptions, cl, 0, 0, msg);
+ config_free_lines(cl);
+ if (retval < 0) {
+ err = SETOPT_ERR_PARSE;
+ goto err;
+ }
+ if (i==0)
+ newdefaultoptions = options_dup(&options_format, newoptions);
}
}
@@ -4530,11 +4646,14 @@ options_init_from_string(const char *cf,
err = SETOPT_ERR_SETTING;
goto err; /* frees and replaces old options */
}
+ config_free(&options_format, global_default_options);
+ global_default_options = newdefaultoptions;
return SETOPT_OK;
err:
config_free(&options_format, newoptions);
+ config_free(&options_format, newdefaultoptions);
if (*msg) {
char *old_msg = *msg;
tor_asprintf(msg, "Failed to parse/validate config: %s", old_msg);
@@ -4546,12 +4665,14 @@ options_init_from_string(const char *cf,
/** Return the location for our configuration file.
*/
const char *
-get_torrc_fname(void)
+get_torrc_fname(int defaults_fname)
{
- if (torrc_fname)
- return torrc_fname;
+ const char *fname = defaults_fname ? torrc_defaults_fname : torrc_fname;
+
+ if (fname)
+ return fname;
else
- return get_default_conf_file();
+ return get_default_conf_file(defaults_fname);
}
/** Adjust the address map based on the MapAddress elements in the
@@ -4567,24 +4688,60 @@ config_register_addressmaps(const or_options_t *options)
addressmap_clear_configured();
elts = smartlist_create();
for (opt = options->AddressMap; opt; opt = opt->next) {
+ int from_wildcard = 0, to_wildcard = 0;
smartlist_split_string(elts, opt->value, NULL,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2);
- if (smartlist_len(elts) >= 2) {
- from = smartlist_get(elts,0);
- to = smartlist_get(elts,1);
- if (address_is_invalid_destination(to, 1)) {
- log_warn(LD_CONFIG,
- "Skipping invalid argument '%s' to MapAddress", to);
- } else {
- addressmap_register(from, tor_strdup(to), 0, ADDRMAPSRC_TORRC);
- if (smartlist_len(elts)>2) {
- log_warn(LD_CONFIG,"Ignoring extra arguments to MapAddress.");
- }
- }
- } else {
+ if (smartlist_len(elts) < 2) {
log_warn(LD_CONFIG,"MapAddress '%s' has too few arguments. Ignoring.",
opt->value);
+ goto cleanup;
+ }
+
+ from = smartlist_get(elts,0);
+ to = smartlist_get(elts,1);
+
+ if (to[0] == '.' || from[0] == '.') {
+ log_warn(LD_CONFIG,"MapAddress '%s' is ambiguous - address starts with a"
+ "'.'. Ignoring.",opt->value);
+ goto cleanup;
+ }
+
+ if (!strcmp(to, "*") || !strcmp(from, "*")) {
+ log_warn(LD_CONFIG,"MapAddress '%s' is unsupported - can't remap from "
+ "or to *. Ignoring.",opt->value);
+ goto cleanup;
+ }
+ /* Detect asterisks in expressions of type: '*.example.com' */
+ if (!strncmp(from,"*.",2)) {
+ from += 2;
+ from_wildcard = 1;
+ }
+ if (!strncmp(to,"*.",2)) {
+ to += 2;
+ to_wildcard = 1;
+ }
+
+ if (to_wildcard && !from_wildcard) {
+ log_warn(LD_CONFIG,
+ "Skipping invalid argument '%s' to MapAddress: "
+ "can only use wildcard (i.e. '*.') if 'from' address "
+ "uses wildcard also", opt->value);
+ goto cleanup;
+ }
+
+ if (address_is_invalid_destination(to, 1)) {
+ log_warn(LD_CONFIG,
+ "Skipping invalid argument '%s' to MapAddress", opt->value);
+ goto cleanup;
}
+
+ addressmap_register(from, tor_strdup(to), 0, ADDRMAPSRC_TORRC,
+ from_wildcard, to_wildcard);
+
+ if (smartlist_len(elts) > 2)
+ log_warn(LD_CONFIG,"Ignoring extra arguments to MapAddress.");
+
+ cleanup:
SMARTLIST_FOREACH(elts, char*, cp, tor_free(cp));
smartlist_clear(elts);
}
@@ -5156,7 +5313,7 @@ parse_dir_server_line(const char *line, dirinfo_type_t required_type,
* clause once Tor 0.1.2.17 is obsolete. */
log_warn(LD_CONFIG, "Dangerous dirserver line. To correct, erase your "
"torrc file (%s), or reinstall Tor and use the default torrc.",
- get_torrc_fname());
+ get_torrc_fname(0));
goto err;
}
if (base16_decode(digest, DIGEST_LEN, fingerprint, HEX_DIGEST_LEN)<0) {
@@ -5220,12 +5377,53 @@ warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname)
} SMARTLIST_FOREACH_END(port);
}
-#define CL_PORT_NO_OPTIONS (1u<<0)
+/** DOCDOC */
+static void
+warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid)
+{
+ int warned = 0;
+ SMARTLIST_FOREACH_BEGIN(ports, port_cfg_t *, port) {
+ if (port->type != CONN_TYPE_CONTROL_LISTENER)
+ continue;
+ if (port->is_unix_addr)
+ continue;
+ if (!tor_addr_is_loopback(&port->addr)) {
+ if (forbid) {
+ if (!warned)
+ log_warn(LD_CONFIG,
+ "You have a ControlPort set to accept "
+ "unauthenticated connections from a non-local address. "
+ "This means that programs not running on your computer "
+ "can reconfigure your Tor, without even having to guess a "
+ "password. That's so bad that I'm closing your ControlPort "
+ "for you. If you need to control your Tor remotely, try "
+ "enabling authentication and using a tool like stunnel or "
+ "ssh to encrypt remote access.");
+ warned = 1;
+ port_cfg_free(port);
+ SMARTLIST_DEL_CURRENT(ports, port);
+ } else {
+ log_warn(LD_CONFIG, "You have a ControlPort set to accept "
+ "connections from a non-local address. This means that "
+ "programs not running on your computer can reconfigure your "
+ "Tor. That's pretty bad, since the controller "
+ "protocol isn't encrypted! Maybe you should just listen on "
+ "127.0.0.1 and use a tool like stunnel or ssh to encrypt "
+ "remote connections to your control port.");
+ return; /* No point in checking the rest */
+ }
+ }
+ } SMARTLIST_FOREACH_END(port);
+}
+
+#define CL_PORT_NO_OPTIONS (1u<<0)
#define CL_PORT_WARN_NONLOCAL (1u<<1)
#define CL_PORT_ALLOW_EXTRA_LISTENADDR (1u<<2)
+#define CL_PORT_SERVER_OPTIONS (1u<<3)
+#define CL_PORT_FORBID_NONLOCAL (1u<<4)
/**
- * Parse port configuration for a single client port type.
+ * Parse port configuration for a single port type.
*
* Read entries of the "FooPort" type from the list <b>ports</b>, and
* entries of the "FooListenAddress" type from the list
@@ -5245,17 +5443,22 @@ warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname)
* isolation options in the FooPort entries.
*
* If CL_PORT_WARN_NONLOCAL is set in <b>flags</b>, warn if any of the
- * ports are not on a local address.
+ * ports are not on a local address. If CL_PORT_FORBID_NONLOCAL is set,
+ * this is a contrl port with no password set: don't even allow it.
*
* Unless CL_PORT_ALLOW_EXTRA_LISTENADDR is set in <b>flags</b>, warn
* if FooListenAddress is set but FooPort is 0.
*
+ * If CL_PORT_SERVER_OPTIONS is set in <b>flags</b>, do not allow stream
+ * isolation options in the FooPort entries; instead allow the
+ * server-port option set.
+ *
* On success, if <b>out</b> is given, add a new port_cfg_t entry to
* <b>out</b> for every port that the client should listen on. Return 0
* on success, -1 on failure.
*/
static int
-parse_client_port_config(smartlist_t *out,
+parse_port_config(smartlist_t *out,
const config_line_t *ports,
const config_line_t *listenaddrs,
const char *portname,
@@ -5266,8 +5469,11 @@ parse_client_port_config(smartlist_t *out,
{
smartlist_t *elts;
int retval = -1;
- const unsigned allow_client_options = !(flags & CL_PORT_NO_OPTIONS);
+ const unsigned is_control = (listener_type == CONN_TYPE_CONTROL_LISTENER);
+ const unsigned allow_no_options = flags & CL_PORT_NO_OPTIONS;
+ const unsigned use_server_options = flags & CL_PORT_SERVER_OPTIONS;
const unsigned warn_nonlocal = flags & CL_PORT_WARN_NONLOCAL;
+ const unsigned forbid_nonlocal = flags & CL_PORT_FORBID_NONLOCAL;
const unsigned allow_spurious_listenaddr =
flags & CL_PORT_ALLOW_EXTRA_LISTENADDR;
@@ -5303,6 +5509,17 @@ parse_client_port_config(smartlist_t *out,
return -1;
}
+ if (use_server_options && out) {
+ /* Add a no_listen port. */
+ port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t));
+ cfg->type = listener_type;
+ cfg->port = mainport;
+ tor_addr_make_unspec(&cfg->addr); /* Server ports default to 0.0.0.0 */
+ cfg->no_listen = 1;
+ cfg->ipv4_only = 1;
+ smartlist_add(out, cfg);
+ }
+
for (; listenaddrs; listenaddrs = listenaddrs->next) {
tor_addr_t addr;
uint16_t port = 0;
@@ -5318,12 +5535,17 @@ parse_client_port_config(smartlist_t *out,
tor_addr_copy(&cfg->addr, &addr);
cfg->session_group = SESSION_GROUP_UNSET;
cfg->isolation_flags = ISO_DEFAULT;
+ cfg->no_advertise = 1;
smartlist_add(out, cfg);
}
}
- if (warn_nonlocal && out)
- warn_nonlocal_client_ports(out, portname);
+ if (warn_nonlocal && out) {
+ if (is_control)
+ warn_nonlocal_controller_ports(out, forbid_nonlocal);
+ else
+ warn_nonlocal_client_ports(out, portname);
+ }
return 0;
} /* end if (listenaddrs) */
@@ -5355,6 +5577,8 @@ parse_client_port_config(smartlist_t *out,
char *addrport;
uint16_t ptmp=0;
int ok;
+ int no_listen = 0, no_advertise = 0, all_addrs = 0,
+ ipv4_only = 0, ipv6_only = 0;
smartlist_split_string(elts, ports->value, NULL,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
@@ -5363,7 +5587,7 @@ parse_client_port_config(smartlist_t *out,
goto err;
}
- if (!allow_client_options && smartlist_len(elts) > 1) {
+ if (allow_no_options && smartlist_len(elts) > 1) {
log_warn(LD_CONFIG, "Too many options on %sPort line", portname);
goto err;
}
@@ -5402,56 +5626,107 @@ parse_client_port_config(smartlist_t *out,
}
/* Now parse the rest of the options, if any. */
- SMARTLIST_FOREACH_BEGIN(elts, char *, elt) {
- int no = 0, isoflag = 0;
- const char *elt_orig = elt;
- if (elt_sl_idx == 0)
- continue; /* Skip addr:port */
- if (!strcasecmpstart(elt, "SessionGroup=")) {
- int group = (int)tor_parse_long(elt+strlen("SessionGroup="),
- 10, 0, INT_MAX, &ok, NULL);
- if (!ok) {
- log_warn(LD_CONFIG, "Invalid %sPort option '%s'",
+ if (use_server_options) {
+ /* This is a server port; parse advertising options */
+ SMARTLIST_FOREACH_BEGIN(elts, char *, elt) {
+ if (elt_sl_idx == 0)
+ continue; /* Skip addr:port */
+
+ if (!strcasecmp(elt, "NoAdvertise")) {
+ no_advertise = 1;
+ } else if (!strcasecmp(elt, "NoListen")) {
+ no_listen = 1;
+#if 0
+ /* not implemented yet. */
+ } else if (!strcasecmp(elt, "AllAddrs")) {
+
+ all_addrs = 1;
+#endif
+ } else if (!strcasecmp(elt, "IPv4Only")) {
+ ipv4_only = 1;
+ } else if (!strcasecmp(elt, "IPv6Only")) {
+ ipv6_only = 1;
+ } else {
+ log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'",
portname, escaped(elt));
- goto err;
- }
- if (sessiongroup >= 0) {
- log_warn(LD_CONFIG, "Multiple SessionGroup options on %sPort",
- portname);
- goto err;
}
- sessiongroup = group;
- continue;
- }
+ } SMARTLIST_FOREACH_END(elt);
- if (!strcasecmpstart(elt, "No")) {
- no = 1;
- elt += 2;
+ if (no_advertise && no_listen) {
+ log_warn(LD_CONFIG, "Tried to set both NoListen and NoAdvertise "
+ "on %sPort line '%s'",
+ portname, escaped(ports->value));
+ goto err;
}
- if (!strcasecmpend(elt, "s"))
- elt[strlen(elt)-1] = '\0'; /* kill plurals. */
-
- if (!strcasecmp(elt, "IsolateDestPort")) {
- isoflag = ISO_DESTPORT;
- } else if (!strcasecmp(elt, "IsolateDestAddr")) {
- isoflag = ISO_DESTADDR;
- } else if (!strcasecmp(elt, "IsolateSOCKSAuth")) {
- isoflag = ISO_SOCKSAUTH;
- } else if (!strcasecmp(elt, "IsolateClientProtocol")) {
- isoflag = ISO_CLIENTPROTO;
- } else if (!strcasecmp(elt, "IsolateClientAddr")) {
- isoflag = ISO_CLIENTADDR;
- } else {
- log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'",
- portname, escaped(elt_orig));
+ if (ipv4_only && ipv6_only) {
+ log_warn(LD_CONFIG, "Tried to set both IPv4Only and IPv6Only "
+ "on %sPort line '%s'",
+ portname, escaped(ports->value));
+ goto err;
}
-
- if (no) {
- isolation &= ~isoflag;
- } else {
- isolation |= isoflag;
+ if (ipv4_only && tor_addr_family(&addr) == AF_INET6) {
+ log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv6",
+ portname);
+ goto err;
+ }
+ if (ipv6_only && tor_addr_family(&addr) == AF_INET) {
+ log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv4",
+ portname);
+ goto err;
}
- } SMARTLIST_FOREACH_END(elt);
+ } else {
+ /* This is a client port; parse isolation options */
+ SMARTLIST_FOREACH_BEGIN(elts, char *, elt) {
+ int no = 0, isoflag = 0;
+ const char *elt_orig = elt;
+ if (elt_sl_idx == 0)
+ continue; /* Skip addr:port */
+ if (!strcasecmpstart(elt, "SessionGroup=")) {
+ int group = (int)tor_parse_long(elt+strlen("SessionGroup="),
+ 10, 0, INT_MAX, &ok, NULL);
+ if (!ok) {
+ log_warn(LD_CONFIG, "Invalid %sPort option '%s'",
+ portname, escaped(elt));
+ goto err;
+ }
+ if (sessiongroup >= 0) {
+ log_warn(LD_CONFIG, "Multiple SessionGroup options on %sPort",
+ portname);
+ goto err;
+ }
+ sessiongroup = group;
+ continue;
+ }
+
+ if (!strcasecmpstart(elt, "No")) {
+ no = 1;
+ elt += 2;
+ }
+ if (!strcasecmpend(elt, "s"))
+ elt[strlen(elt)-1] = '\0'; /* kill plurals. */
+
+ if (!strcasecmp(elt, "IsolateDestPort")) {
+ isoflag = ISO_DESTPORT;
+ } else if (!strcasecmp(elt, "IsolateDestAddr")) {
+ isoflag = ISO_DESTADDR;
+ } else if (!strcasecmp(elt, "IsolateSOCKSAuth")) {
+ isoflag = ISO_SOCKSAUTH;
+ } else if (!strcasecmp(elt, "IsolateClientProtocol")) {
+ isoflag = ISO_CLIENTPROTO;
+ } else if (!strcasecmp(elt, "IsolateClientAddr")) {
+ isoflag = ISO_CLIENTADDR;
+ } else {
+ log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'",
+ portname, escaped(elt_orig));
+ }
+
+ if (no) {
+ isolation &= ~isoflag;
+ } else {
+ isolation |= isoflag;
+ }
+ } SMARTLIST_FOREACH_END(elt);
+ }
if (out && port) {
port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t));
@@ -5460,14 +5735,24 @@ parse_client_port_config(smartlist_t *out,
tor_addr_copy(&cfg->addr, &addr);
cfg->session_group = sessiongroup;
cfg->isolation_flags = isolation;
+ cfg->no_listen = no_listen;
+ cfg->no_listen = no_advertise;
+ cfg->all_addrs = all_addrs;
+ cfg->ipv4_only = ipv4_only;
+ cfg->ipv6_only = ipv6_only;
+
smartlist_add(out, cfg);
}
SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp));
smartlist_clear(elts);
}
- if (warn_nonlocal && out)
- warn_nonlocal_client_ports(out, portname);
+ if (warn_nonlocal && out) {
+ if (is_control)
+ warn_nonlocal_controller_ports(out, forbid_nonlocal);
+ else
+ warn_nonlocal_client_ports(out, portname);
+ }
retval = 0;
err:
@@ -5476,6 +5761,27 @@ parse_client_port_config(smartlist_t *out,
return retval;
}
+/** DOCDOC */
+static int
+parse_socket_config(smartlist_t *out, const config_line_t *cfg,
+ int listener_type)
+{
+
+ if (!out)
+ return 0;
+
+ for ( ; cfg; cfg = cfg->next) {
+ size_t len = strlen(cfg->value);
+ port_cfg_t *port = tor_malloc_zero(sizeof(port_cfg_t) + len + 1);
+ port->is_unix_addr = 1;
+ memcpy(port->unix_addr, cfg->value, len+1);
+ port->type = listener_type;
+ smartlist_add(out, port);
+ }
+
+ return 0;
+}
+
/** Parse all client port types (Socks, DNS, Trans, NATD) from
* <b>options</b>. On success, set *<b>n_ports_out</b> to the number of
* ports that are listed and return 0. On failure, set *<b>msg</b> to a
@@ -5485,8 +5791,8 @@ parse_client_port_config(smartlist_t *out,
* new list of ports parsed from <b>options</b>.
**/
static int
-parse_client_ports(const or_options_t *options, int validate_only,
- char **msg, int *n_ports_out)
+parse_ports(const or_options_t *options, int validate_only,
+ char **msg, int *n_ports_out)
{
smartlist_t *ports;
int retval = -1;
@@ -5495,7 +5801,7 @@ parse_client_ports(const or_options_t *options, int validate_only,
*n_ports_out = 0;
- if (parse_client_port_config(ports,
+ if (parse_port_config(ports,
options->SocksPort, options->SocksListenAddress,
"Socks", CONN_TYPE_AP_LISTENER,
"127.0.0.1", 9050,
@@ -5503,7 +5809,7 @@ parse_client_ports(const or_options_t *options, int validate_only,
*msg = tor_strdup("Invalid SocksPort/SocksListenAddress configuration");
goto err;
}
- if (parse_client_port_config(ports,
+ if (parse_port_config(ports,
options->DNSPort, options->DNSListenAddress,
"DNS", CONN_TYPE_AP_DNS_LISTENER,
"127.0.0.1", 0,
@@ -5511,7 +5817,7 @@ parse_client_ports(const or_options_t *options, int validate_only,
*msg = tor_strdup("Invalid DNSPort/DNSListenAddress configuration");
goto err;
}
- if (parse_client_port_config(ports,
+ if (parse_port_config(ports,
options->TransPort, options->TransListenAddress,
"Trans", CONN_TYPE_AP_TRANS_LISTENER,
"127.0.0.1", 0,
@@ -5519,7 +5825,7 @@ parse_client_ports(const or_options_t *options, int validate_only,
*msg = tor_strdup("Invalid TransPort/TransListenAddress configuration");
goto err;
}
- if (parse_client_port_config(ports,
+ if (parse_port_config(ports,
options->NATDPort, options->NATDListenAddress,
"NATD", CONN_TYPE_AP_NATD_LISTENER,
"127.0.0.1", 0,
@@ -5527,16 +5833,63 @@ parse_client_ports(const or_options_t *options, int validate_only,
*msg = tor_strdup("Invalid NatdPort/NatdListenAddress configuration");
goto err;
}
+ {
+ unsigned control_port_flags = CL_PORT_NO_OPTIONS | CL_PORT_WARN_NONLOCAL;
+ const int any_passwords = (options->HashedControlPassword ||
+ options->HashedControlSessionPassword ||
+ options->CookieAuthentication);
+ if (! any_passwords)
+ control_port_flags |= CL_PORT_FORBID_NONLOCAL;
+
+ if (parse_port_config(ports,
+ options->ControlPort, options->ControlListenAddress,
+ "Control", CONN_TYPE_CONTROL_LISTENER,
+ "127.0.0.1", 0,
+ control_port_flags) < 0) {
+ *msg = tor_strdup("Invalid ControlPort/ControlListenAddress "
+ "configuration");
+ goto err;
+ }
+ if (parse_socket_config(ports,
+ options->ControlSocket,
+ CONN_TYPE_CONTROL_LISTENER) < 0) {
+ *msg = tor_strdup("Invalid ControlSocket configuration");
+ goto err;
+ }
+ }
+ if (! options->ClientOnly) {
+ if (parse_port_config(ports,
+ options->ORPort, options->ORListenAddress,
+ "OR", CONN_TYPE_OR_LISTENER,
+ "0.0.0.0", 0,
+ CL_PORT_SERVER_OPTIONS) < 0) {
+ *msg = tor_strdup("Invalid ORPort/ORListenAddress configuration");
+ goto err;
+ }
+ if (parse_port_config(ports,
+ options->DirPort, options->DirListenAddress,
+ "Dir", CONN_TYPE_DIR_LISTENER,
+ "0.0.0.0", 0,
+ CL_PORT_SERVER_OPTIONS) < 0) {
+ *msg = tor_strdup("Invalid DirPort/DirListenAddress configuration");
+ goto err;
+ }
+ }
+
+ if (check_server_ports(ports, options) < 0) {
+ *msg = tor_strdup("Misconfigured server ports");
+ goto err;
+ }
*n_ports_out = smartlist_len(ports);
if (!validate_only) {
- if (configured_client_ports) {
- SMARTLIST_FOREACH(configured_client_ports,
+ if (configured_ports) {
+ SMARTLIST_FOREACH(configured_ports,
port_cfg_t *, p, port_cfg_free(p));
- smartlist_free(configured_client_ports);
+ smartlist_free(configured_ports);
}
- configured_client_ports = ports;
+ configured_ports = ports;
ports = NULL; /* prevent free below. */
}
@@ -5549,14 +5902,107 @@ parse_client_ports(const or_options_t *options, int validate_only,
return retval;
}
+/** DOCDOC */
+static int
+check_server_ports(const smartlist_t *ports,
+ const or_options_t *options)
+{
+ int n_orport_advertised = 0;
+ int n_orport_advertised_ipv4 = 0;
+ int n_orport_listeners = 0;
+ int n_dirport_advertised = 0;
+ int n_dirport_listeners = 0;
+ int n_low_port = 0;
+ int r = 0;
+
+ SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) {
+ if (port->type == CONN_TYPE_DIR_LISTENER) {
+ if (! port->no_advertise)
+ ++n_dirport_advertised;
+ if (! port->no_listen)
+ ++n_dirport_listeners;
+ } else if (port->type == CONN_TYPE_OR_LISTENER) {
+ if (! port->no_advertise) {
+ ++n_orport_advertised;
+ if (tor_addr_family(&port->addr) == AF_INET ||
+ (tor_addr_family(&port->addr) == AF_UNSPEC && !port->ipv6_only))
+ ++n_orport_advertised_ipv4;
+ }
+ if (! port->no_listen)
+ ++n_orport_listeners;
+ } else {
+ continue;
+ }
+#ifndef MS_WINDOWS
+ if (!port->no_advertise && port->port < 1024)
+ ++n_low_port;
+#endif
+ } SMARTLIST_FOREACH_END(port);
+
+ if (n_orport_advertised && !n_orport_listeners) {
+ log_warn(LD_CONFIG, "We are advertising an ORPort, but not actually "
+ "listening on one.");
+ r = -1;
+ }
+ if (n_dirport_advertised && !n_dirport_listeners) {
+ log_warn(LD_CONFIG, "We are advertising a DirPort, but not actually "
+ "listening on one.");
+ r = -1;
+ }
+ if (n_dirport_advertised > 1) {
+ log_warn(LD_CONFIG, "Can't advertise more than one DirPort.");
+ r = -1;
+ }
+ if (n_orport_advertised && !n_orport_advertised_ipv4 &&
+ !options->BridgeRelay) {
+ log_warn(LD_CONFIG, "Configured non-bridge only to listen on an IPv6 "
+ "address.");
+ r = -1;
+ }
+
+ if (n_low_port && options->AccountingMax) {
+ log(LOG_WARN, LD_CONFIG,
+ "You have set AccountingMax to use hibernation. You have also "
+ "chosen a low DirPort or OrPort. This combination can make Tor stop "
+ "working when it tries to re-attach the port after a period of "
+ "hibernation. Please choose a different port or turn off "
+ "hibernation unless you know this combination will work on your "
+ "platform.");
+ }
+
+ return r;
+}
+
/** Return a list of port_cfg_t for client ports parsed from the
* options. */
const smartlist_t *
-get_configured_client_ports(void)
+get_configured_ports(void)
+{
+ if (!configured_ports)
+ configured_ports = smartlist_create();
+ return configured_ports;
+}
+
+/** Return the first advertised port of type <b>listener_type</b> in
+ <b>address_family</b>. */
+int
+get_first_advertised_port_by_type_af(int listener_type, int address_family)
{
- if (!configured_client_ports)
- configured_client_ports = smartlist_create();
- return configured_client_ports;
+ if (!configured_ports)
+ return 0;
+ SMARTLIST_FOREACH_BEGIN(configured_ports, const port_cfg_t *, cfg) {
+ if (cfg->type == listener_type &&
+ !cfg->no_advertise &&
+ (tor_addr_family(&cfg->addr) == address_family ||
+ tor_addr_family(&cfg->addr) == AF_UNSPEC)) {
+ if (tor_addr_family(&cfg->addr) != AF_UNSPEC ||
+ (address_family == AF_INET && !cfg->ipv6_only) ||
+ (address_family == AF_INET6 && !cfg->ipv4_only)) {
+ return cfg->port;
+ }
+ }
+ } SMARTLIST_FOREACH_END(cfg);
+ return 0;
}
/** Adjust the value of options->DataDirectory, or fill it in if it's
@@ -5715,7 +6161,7 @@ options_save_current(void)
* If we try falling back to datadirectory or something, we have a better
* chance of saving the configuration, but a better chance of doing
* something the user never expected. */
- return write_configuration_file(get_torrc_fname(), get_options());
+ return write_configuration_file(get_torrc_fname(0), get_options());
}
/** Mapping from a unit name to a multiplier for converting that unit into a
@@ -5934,7 +6380,7 @@ init_libevent(const or_options_t *options)
suppress_libevent_log_msg(NULL);
tor_check_libevent_version(tor_libevent_get_method(),
- get_options()->ORPort != 0,
+ get_options()->ORPort != NULL,
&badness);
if (badness) {
const char *v = tor_libevent_get_version_str();
@@ -6181,7 +6627,7 @@ or_state_load(void)
if (contents) {
config_line_t *lines=NULL;
int assign_retval;
- if (config_get_lines(contents, &lines)<0)
+ if (config_get_lines(contents, &lines, 0)<0)
goto done;
assign_retval = config_assign(&state_format, new_state,
lines, 0, 0, &errmsg);
@@ -6285,7 +6731,7 @@ or_state_save(time_t now)
tor_free(global_state->TorVersion);
tor_asprintf(&global_state->TorVersion, "Tor %s", get_version());
- state = config_dump(&state_format, global_state, 1, 0);
+ state = config_dump(&state_format, NULL, global_state, 1, 0);
format_local_iso_time(tbuf, now);
tor_asprintf(&contents,
"# Tor state file last generated on %s local time\n"
diff --git a/src/or/config.h b/src/or/config.h
index 76f6841d7..2d94192d3 100644
--- a/src/or/config.h
+++ b/src/or/config.h
@@ -23,24 +23,24 @@ const char *escaped_safe_str_client(const char *address);
const char *escaped_safe_str(const char *address);
const char *get_version(void);
-int config_get_lines(const char *string, config_line_t **result);
+int config_get_lines(const char *string, config_line_t **result, int extended);
void config_free_lines(config_line_t *front);
setopt_err_t options_trial_assign(config_line_t *list, int use_defaults,
int clear_first, char **msg);
int resolve_my_address(int warn_severity, const or_options_t *options,
uint32_t *addr, char **hostname_out);
-int is_local_addr(const tor_addr_t *addr) ATTR_PURE;
+int is_local_addr(const tor_addr_t *addr);
void options_init(or_options_t *options);
char *options_dump(const or_options_t *options, int minimal);
int options_init_from_torrc(int argc, char **argv);
-setopt_err_t options_init_from_string(const char *cf,
+setopt_err_t options_init_from_string(const char *cf_defaults, const char *cf,
int command, const char *command_arg, char **msg);
int option_is_recognized(const char *key);
const char *option_get_canonical_name(const char *key);
config_line_t *option_get_assignment(const or_options_t *options,
const char *key);
int options_save_current(void);
-const char *get_torrc_fname(void);
+const char *get_torrc_fname(int defaults_fname);
char *options_get_datadir_fname2_suffix(const or_options_t *options,
const char *sub1, const char *sub2,
const char *suffix);
@@ -64,7 +64,13 @@ or_state_t *get_or_state(void);
int did_last_state_file_write_fail(void);
int or_state_save(time_t now);
-const smartlist_t *get_configured_client_ports(void);
+const smartlist_t *get_configured_ports(void);
+int get_first_advertised_port_by_type_af(int listener_type,
+ int address_family);
+#define get_primary_or_port() \
+ (get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER, AF_INET))
+#define get_primary_dir_port() \
+ (get_first_advertised_port_by_type_af(CONN_TYPE_DIR_LISTENER, AF_INET))
int options_need_geoip_info(const or_options_t *options,
const char **reason_out);
diff --git a/src/or/connection.c b/src/or/connection.c
index a52bf4807..7b95acac6 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -880,7 +880,8 @@ connection_create_listener(const struct sockaddr *listensockaddr,
return NULL;
}
- if (listensockaddr->sa_family == AF_INET) {
+ if (listensockaddr->sa_family == AF_INET ||
+ listensockaddr->sa_family == AF_INET6) {
int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER);
if (is_tcp)
start_reading = 1;
@@ -890,7 +891,7 @@ connection_create_listener(const struct sockaddr *listensockaddr,
log_notice(LD_NET, "Opening %s on %s:%d",
conn_type_to_string(type), fmt_addr(&addr), usePort);
- s = tor_open_socket(PF_INET,
+ s = tor_open_socket(tor_addr_family(&addr),
is_tcp ? SOCK_STREAM : SOCK_DGRAM,
is_tcp ? IPPROTO_TCP: IPPROTO_UDP);
if (!SOCKET_OK(s)) {
@@ -1318,6 +1319,24 @@ connection_connect(connection_t *conn, const char *address,
else
protocol_family = PF_INET;
+ if (get_options()->DisableNetwork) {
+ /* We should never even try to connect anyplace if DisableNetwork is set.
+ * Warn if we do, and refuse to make the connection. */
+ static ratelim_t disablenet_violated = RATELIM_INIT(30*60);
+ char *m;
+#ifdef MS_WINDOWS
+ *socket_error = WSAENETUNREACH;
+#else
+ *socket_error = ENETUNREACH;
+#endif
+ if ((m = rate_limit_log(&disablenet_violated, approx_time()))) {
+ log_warn(LD_BUG, "Tried to open a socket with DisableNetwork set.%s", m);
+ tor_free(m);
+ }
+ tor_fragile_assert();
+ return -1;
+ }
+
s = tor_open_socket(protocol_family,SOCK_STREAM,IPPROTO_TCP);
if (s < 0) {
*socket_error = tor_socket_errno(-1);
@@ -1796,6 +1815,9 @@ retry_listener_ports(smartlist_t *old_conns,
(conn->socket_family == AF_UNIX && ! wanted->is_unix_addr))
continue;
+ if (wanted->no_listen)
+ continue; /* We don't want to open a listener for this one */
+
if (wanted->is_unix_addr) {
if (conn->socket_family == AF_UNIX &&
!strcmp(wanted->unix_addr, conn->address)) {
@@ -1834,6 +1856,8 @@ retry_listener_ports(smartlist_t *old_conns,
connection_t *conn;
int real_port = port->port == CFG_AUTO_PORT ? 0 : port->port;
tor_assert(real_port <= UINT16_MAX);
+ if (port->no_listen)
+ continue;
if (port->is_unix_addr) {
listensockaddr = (struct sockaddr *)
@@ -1870,82 +1894,6 @@ retry_listener_ports(smartlist_t *old_conns,
return r;
}
-/**
- * Launch any configured listener connections of type <b>type</b>. (A
- * listener is configured if <b>port_option</b> is non-zero. If any
- * ListenAddress configuration options are given in <b>cfg</b>, create a
- * connection binding to each one. Otherwise, create a single
- * connection binding to the address <b>default_addr</b>.)
- *
- * We assume that we're starting with a list of existing listener connection_t
- * pointers in <b>old_conns</b>: we do not launch listeners that are already
- * in that list. Instead, we just remove them from the list.
- *
- * All new connections we launch are added to <b>new_conns</b>.
- */
-static int
-retry_listeners(smartlist_t *old_conns,
- int type, const config_line_t *cfg,
- int port_option, const char *default_addr,
- smartlist_t *new_conns,
- int is_sockaddr_un)
-{
- smartlist_t *ports = smartlist_create();
- tor_addr_t dflt_addr;
- int retval = 0;
-
- if (default_addr) {
- tor_addr_parse(&dflt_addr, default_addr);
- } else {
- tor_addr_make_unspec(&dflt_addr);
- }
-
- if (port_option) {
- if (!cfg) {
- port_cfg_t *port = tor_malloc_zero(sizeof(port_cfg_t));
- tor_addr_copy(&port->addr, &dflt_addr);
- port->port = port_option;
- port->type = type;
- smartlist_add(ports, port);
- } else {
- const config_line_t *c;
- for (c = cfg; c; c = c->next) {
- port_cfg_t *port;
- tor_addr_t addr;
- uint16_t portval = 0;
- if (is_sockaddr_un) {
- size_t len = strlen(c->value);
- port = tor_malloc_zero(sizeof(port_cfg_t) + len + 1);
- port->is_unix_addr = 1;
- memcpy(port->unix_addr, c->value, len+1);
- } else {
- if (tor_addr_port_lookup(c->value, &addr, &portval) < 0) {
- log_warn(LD_CONFIG, "Can't parse/resolve %s %s",
- c->key, c->value);
- retval = -1;
- continue;
- }
- port = tor_malloc_zero(sizeof(port_cfg_t));
- tor_addr_copy(&port->addr, &addr);
- }
- port->type = type;
- port->port = portval ? portval : port_option;
- smartlist_add(ports, port);
- }
- }
- }
-
- if (retval == -1)
- goto cleanup;
-
- retval = retry_listener_ports(old_conns, ports, new_conns);
-
- cleanup:
- SMARTLIST_FOREACH(ports, port_cfg_t *, p, tor_free(p));
- smartlist_free(ports);
- return retval;
-}
-
/** Launch listeners for each port you should have open. Only launch
* listeners who are not already open, and only close listeners we no longer
* want.
@@ -1968,35 +1916,10 @@ retry_all_listeners(smartlist_t *replaced_conns,
smartlist_add(listeners, conn);
} SMARTLIST_FOREACH_END(conn);
- if (! options->ClientOnly) {
- if (retry_listeners(listeners,
- CONN_TYPE_OR_LISTENER, options->ORListenAddress,
- options->ORPort, "0.0.0.0",
- new_conns, 0) < 0)
- retval = -1;
- if (retry_listeners(listeners,
- CONN_TYPE_DIR_LISTENER, options->DirListenAddress,
- options->DirPort, "0.0.0.0",
- new_conns, 0) < 0)
- retval = -1;
- }
-
if (retry_listener_ports(listeners,
- get_configured_client_ports(),
+ get_configured_ports(),
new_conns) < 0)
retval = -1;
- if (retry_listeners(listeners,
- CONN_TYPE_CONTROL_LISTENER,
- options->ControlListenAddress,
- options->ControlPort, "127.0.0.1",
- new_conns, 0) < 0)
- retval = -1;
- if (retry_listeners(listeners,
- CONN_TYPE_CONTROL_LISTENER,
- options->ControlSocket,
- options->ControlSocket ? 1 : 0, NULL,
- new_conns, 1) < 0)
- retval = -1;
/* Any members that were still in 'listeners' don't correspond to
* any configured port. Kill 'em. */
@@ -2013,6 +1936,7 @@ retry_all_listeners(smartlist_t *replaced_conns,
smartlist_free(listeners);
+ /* XXXprop186 should take all advertised ports into account */
if (old_or_port != router_get_advertised_or_port(options) ||
old_dir_port != router_get_advertised_dir_port(options, 0)) {
/* Our chosen ORPort or DirPort is not what it used to be: the
@@ -2025,6 +1949,43 @@ retry_all_listeners(smartlist_t *replaced_conns,
return retval;
}
+/** Mark every listener of type other than CONTROL_LISTENER to be closed. */
+void
+connection_mark_all_noncontrol_listeners(void)
+{
+ SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) {
+ if (conn->marked_for_close)
+ continue;
+ if (conn->type == CONN_TYPE_CONTROL_LISTENER)
+ continue;
+ if (connection_is_listener(conn))
+ connection_mark_for_close(conn);
+ } SMARTLIST_FOREACH_END(conn);
+}
+
+/** Mark every external conection not used for controllers for close. */
+void
+connection_mark_all_noncontrol_connections(void)
+{
+ SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) {
+ if (conn->marked_for_close)
+ continue;
+ switch (conn->type) {
+ case CONN_TYPE_CPUWORKER:
+ case CONN_TYPE_CONTROL_LISTENER:
+ case CONN_TYPE_CONTROL:
+ break;
+ case CONN_TYPE_AP:
+ connection_mark_unattached_ap(TO_ENTRY_CONN(conn),
+ END_STREAM_REASON_HIBERNATING);
+ break;
+ default:
+ connection_mark_for_close(conn);
+ break;
+ }
+ } SMARTLIST_FOREACH_END(conn);
+}
+
/** Return 1 if we should apply rate limiting to <b>conn</b>, and 0
* otherwise.
* Right now this just checks if it's an internal IP address or an
diff --git a/src/or/connection.h b/src/or/connection.h
index 9f1148972..c4b8bf8ab 100644
--- a/src/or/connection.h
+++ b/src/or/connection.h
@@ -66,6 +66,9 @@ int get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type,
int retry_all_listeners(smartlist_t *replaced_conns,
smartlist_t *new_conns);
+void connection_mark_all_noncontrol_listeners(void);
+void connection_mark_all_noncontrol_connections(void);
+
ssize_t connection_bucket_write_limit(connection_t *conn, time_t now);
int global_write_bucket_low(connection_t *conn, size_t attempt, int priority);
void connection_bucket_init(void);
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index dc15721b6..aba9ba272 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -803,11 +803,18 @@ connection_ap_detach_retriable(entry_connection_t *conn,
* the configuration file, "1" for mappings set from the control
* interface, and other values for DNS and TrackHostExit mappings that can
* expire.)
+ *
+ * A mapping may be 'wildcarded'. If "src_wildcard" is true, then
+ * any address that ends with a . followed by the key for this entry will
+ * get remapped by it. If "dst_wildcard" is also true, then only the
+ * matching suffix of such addresses will get replaced by new_address.
*/
typedef struct {
char *new_address;
time_t expires;
addressmap_entry_source_t source:3;
+ unsigned src_wildcard:1;
+ unsigned dst_wildcard:1;
short num_resolve_failures;
} addressmap_entry_t;
@@ -1054,6 +1061,37 @@ addressmap_free_all(void)
virtaddress_reversemap = NULL;
}
+/** Try to find a match for AddressMap expressions that use
+ * wildcard notation such as '*.c.d *.e.f' (so 'a.c.d' will map to 'a.e.f') or
+ * '*.c.d a.b.c' (so 'a.c.d' will map to a.b.c).
+ * Return the matching entry in AddressMap or NULL if no match is found.
+ * For expressions such as '*.c.d *.e.f', truncate <b>address</b> 'a.c.d'
+ * to 'a' before we return the matching AddressMap entry.
+ *
+ * This function does not handle the case where a pattern of the form "*.c.d"
+ * matches the address c.d -- that's done by the main addressmap_rewrite
+ * function.
+ */
+static addressmap_entry_t *
+addressmap_match_superdomains(char *address)
+{
+ addressmap_entry_t *val;
+ char *cp;
+
+ cp = address;
+ while ((cp = strchr(cp, '.'))) {
+ /* cp now points to a suffix of address that begins with a . */
+ val = strmap_get_lc(addressmap, cp+1);
+ if (val && val->src_wildcard) {
+ if (val->dst_wildcard)
+ *cp = '\0';
+ return val;
+ }
+ ++cp;
+ }
+ return NULL;
+}
+
/** Look at address, and rewrite it until it doesn't want any
* more rewrites; but don't get into an infinite loop.
* Don't write more than maxlen chars into address. Return true if the
@@ -1066,25 +1104,49 @@ addressmap_rewrite(char *address, size_t maxlen, time_t *expires_out)
{
addressmap_entry_t *ent;
int rewrites;
- char *cp;
time_t expires = TIME_MAX;
for (rewrites = 0; rewrites < 16; rewrites++) {
+ int exact_match = 0;
+ char *addr_orig = tor_strdup(escaped_safe_str_client(address));
+
ent = strmap_get(addressmap, address);
if (!ent || !ent->new_address) {
+ ent = addressmap_match_superdomains(address);
+ } else {
+ if (ent->src_wildcard && !ent->dst_wildcard &&
+ !strcasecmp(address, ent->new_address)) {
+ /* This is a rule like *.example.com example.com, and we just got
+ * "example.com" */
+ tor_free(addr_orig);
+ if (expires_out)
+ *expires_out = expires;
+ return rewrites > 0;
+ }
+
+ exact_match = 1;
+ }
+
+ if (!ent || !ent->new_address) {
+ tor_free(addr_orig);
if (expires_out)
*expires_out = expires;
return (rewrites > 0); /* done, no rewrite needed */
}
- cp = tor_strdup(escaped_safe_str_client(ent->new_address));
+ if (ent->dst_wildcard && !exact_match) {
+ strlcat(address, ".", maxlen);
+ strlcat(address, ent->new_address, maxlen);
+ } else {
+ strlcpy(address, ent->new_address, maxlen);
+ }
+
log_info(LD_APP, "Addressmap: rewriting %s to %s",
- escaped_safe_str_client(address), cp);
+ addr_orig, escaped_safe_str_client(address));
if (ent->expires > 1 && ent->expires < expires)
expires = ent->expires;
- tor_free(cp);
- strlcpy(address, ent->new_address, maxlen);
+ tor_free(addr_orig);
}
log_warn(LD_CONFIG,
"Loop detected: we've rewritten %s 16 times! Using it as-is.",
@@ -1148,17 +1210,34 @@ addressmap_have_mapping(const char *address, int update_expiry)
* <b>new_address</b> should be a newly dup'ed string, which we'll use or
* free as appropriate. We will leave address alone.
*
- * If <b>new_address</b> is NULL, or equal to <b>address</b>, remove
- * any mappings that exist from <b>address</b>.
- */
+ * If <b>wildcard_addr</b> is true, then the mapping will match any address
+ * equal to <b>address</b>, or any address ending with a period followed by
+ * <b>address</b>. If <b>wildcard_addr</b> and <b>wildcard_new_addr</b> are
+ * both true, the mapping will rewrite addresses that end with
+ * ".<b>address</b>" into ones that end with ".<b>new_address</b>."
+ *
+ * If <b>new_address</b> is NULL, or <b>new_address</b> is equal to
+ * <b>address</b> and <b>wildcard_addr</b> is equal to
+ * <b>wildcard_new_addr</b>, remove any mappings that exist from
+ * <b>address</b>.
+ *
+ *
+ * It is an error to set <b>wildcard_new_addr</b> if <b>wildcard_addr</b> is
+ * not set. */
void
addressmap_register(const char *address, char *new_address, time_t expires,
- addressmap_entry_source_t source)
+ addressmap_entry_source_t source,
+ const int wildcard_addr,
+ const int wildcard_new_addr)
{
addressmap_entry_t *ent;
+ if (wildcard_new_addr)
+ tor_assert(wildcard_addr);
+
ent = strmap_get(addressmap, address);
- if (!new_address || !strcasecmp(address,new_address)) {
+ if (!new_address || (!strcasecmp(address,new_address) &&
+ wildcard_addr == wildcard_new_addr)) {
/* Remove the mapping, if any. */
tor_free(new_address);
if (ent) {
@@ -1193,6 +1272,8 @@ addressmap_register(const char *address, char *new_address, time_t expires,
ent->expires = expires==2 ? 1 : expires;
ent->num_resolve_failures = 0;
ent->source = source;
+ ent->src_wildcard = wildcard_addr ? 1 : 0;
+ ent->dst_wildcard = wildcard_new_addr ? 1 : 0;
log_info(LD_CONFIG, "Addressmap: (re)mapped '%s' to '%s'",
safe_str_client(address),
@@ -1277,7 +1358,7 @@ client_dns_set_addressmap_impl(const char *address, const char *name,
"%s", name);
}
addressmap_register(extendedaddress, tor_strdup(extendedval),
- time(NULL) + ttl, ADDRMAPSRC_DNS);
+ time(NULL) + ttl, ADDRMAPSRC_DNS, 0, 0);
}
/** Record the fact that <b>address</b> resolved to <b>val</b>.
@@ -1529,7 +1610,7 @@ addressmap_register_virtual_address(int type, char *new_address)
log_info(LD_APP, "Registering map from %s to %s", *addrp, new_address);
if (vent_needs_to_be_added)
strmap_set(virtaddress_reversemap, new_address, vent);
- addressmap_register(*addrp, new_address, 2, ADDRMAPSRC_AUTOMAP);
+ addressmap_register(*addrp, new_address, 2, ADDRMAPSRC_AUTOMAP, 0, 0);
#if 0
{
@@ -2603,7 +2684,7 @@ connection_ap_make_link(connection_t *partner,
want_onehop ? "direct" : "anonymized",
safe_str_client(address), port);
- conn = entry_connection_new(CONN_TYPE_AP, AF_INET);
+ conn = entry_connection_new(CONN_TYPE_AP, tor_addr_family(&partner->addr));
base_conn = ENTRY_TO_CONN(conn);
base_conn->linked = 1; /* so that we can add it safely below. */
@@ -3209,7 +3290,7 @@ connection_exit_connect_dir(edge_connection_t *exitconn)
exitconn->_base.state = EXIT_CONN_STATE_OPEN;
- dirconn = dir_connection_new(AF_INET);
+ dirconn = dir_connection_new(tor_addr_family(&exitconn->_base.addr));
tor_addr_copy(&dirconn->_base.addr, &exitconn->_base.addr);
dirconn->_base.port = 0;
diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h
index 830667e60..47c9c45b1 100644
--- a/src/or/connection_edge.h
+++ b/src/or/connection_edge.h
@@ -78,7 +78,8 @@ int addressmap_rewrite(char *address, size_t maxlen, time_t *expires_out);
int addressmap_have_mapping(const char *address, int update_timeout);
void addressmap_register(const char *address, char *new_address,
- time_t expires, addressmap_entry_source_t source);
+ time_t expires, addressmap_entry_source_t source,
+ int address_wildcard, int new_address_wildcard);
int parse_virtual_addr_network(const char *val, int validate_only,
char **msg);
int client_dns_incr_failures(const char *address);
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index cce99e4d6..470f6d2a3 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -42,6 +42,7 @@ static int connection_or_check_valid_tls_handshake(or_connection_t *conn,
char *digest_rcvd_out);
static void connection_or_tls_renegotiated_cb(tor_tls_t *tls, void *_conn);
+static void connection_or_close_connection_cb(void *_conn);
#ifdef USE_BUFFEREVENTS
static void connection_or_handle_event_cb(struct bufferevent *bufev,
@@ -492,6 +493,9 @@ connection_or_about_to_close(or_connection_t *or_conn)
time_t now = time(NULL);
connection_t *conn = TO_CONN(or_conn);
+ if (or_conn->pending_action)
+ tor_cancel_libevent_action(or_conn->pending_action);
+
/* Remember why we're closing this connection. */
if (conn->state != OR_CONN_STATE_OPEN) {
/* Inform any pending (not attached) circs that they should
@@ -626,7 +630,9 @@ connection_or_update_token_buckets(smartlist_t *conns,
/** If we don't necessarily know the router we're connecting to, but we
* have an addr/port/id_digest, then fill in as much as we can. Start
- * by checking to see if this describes a router we know. */
+ * by checking to see if this describes a router we know.
+ * <b>started_here</b> is 1 if we are the initiator of <b>conn</b> and
+ * 0 if it's an incoming connection. */
void
connection_or_init_conn_from_address(or_connection_t *conn,
const tor_addr_t *addr, uint16_t port,
@@ -641,10 +647,11 @@ connection_or_init_conn_from_address(or_connection_t *conn,
tor_addr_copy(&conn->_base.addr, addr);
tor_addr_copy(&conn->real_addr, addr);
if (r) {
- tor_addr_t node_addr;
- node_get_addr(r, &node_addr);
- /* XXXX proposal 118 will make this more complex. */
- if (tor_addr_eq(&conn->_base.addr, &node_addr))
+ tor_addr_port_t node_ap;
+ node_get_pref_orport(r, &node_ap);
+ /* XXXX proposal 186 is making this more complex. For now, a conn
+ is canonical when it uses the _preferred_ address. */
+ if (tor_addr_eq(&conn->_base.addr, &node_ap.addr))
conn->is_canonical = 1;
if (!started_here) {
/* Override the addr/port, so our log messages will make sense.
@@ -657,12 +664,12 @@ connection_or_init_conn_from_address(or_connection_t *conn,
* right IP address and port 56244, that wouldn't be as helpful. now we
* log the "right" port too, so we know if it's moria1 or moria2.
*/
- tor_addr_copy(&conn->_base.addr, &node_addr);
- conn->_base.port = node_get_orport(r);
+ tor_addr_copy(&conn->_base.addr, &node_ap.addr);
+ conn->_base.port = node_ap.port;
}
conn->nickname = tor_strdup(node_get_nickname(r));
tor_free(conn->_base.address);
- conn->_base.address = tor_dup_addr(&node_addr);
+ conn->_base.address = tor_dup_addr(&node_ap.addr);
} else {
const char *n;
/* If we're an authoritative directory server, we may know a
@@ -1029,7 +1036,7 @@ connection_or_connect(const tor_addr_t *_addr, uint16_t port,
return NULL;
}
- conn = or_connection_new(AF_INET);
+ conn = or_connection_new(tor_addr_family(&addr));
/* set up conn so it's got all the data we need to remember */
connection_or_init_conn_from_address(conn, &addr, port, id_digest, 1);
@@ -1096,12 +1103,16 @@ connection_tls_start_handshake(or_connection_t *conn, int receiving)
conn->_base.state = OR_CONN_STATE_TLS_HANDSHAKING;
tor_assert(!conn->tls);
conn->tls = tor_tls_new(conn->_base.s, receiving);
- tor_tls_set_logged_address(conn->tls, // XXX client and relay?
- escaped_safe_str(conn->_base.address));
if (!conn->tls) {
log_warn(LD_BUG,"tor_tls_new failed. Closing.");
return -1;
}
+ tor_tls_set_logged_address(conn->tls, // XXX client and relay?
+ escaped_safe_str(conn->_base.address));
+ tor_tls_set_renegotiate_callbacks(conn->tls,
+ connection_or_tls_renegotiated_cb,
+ connection_or_close_connection_cb,
+ conn);
#ifdef USE_BUFFEREVENTS
if (connection_type_uses_bufferevent(TO_CONN(conn))) {
const int filtering = get_options()->_UseFilteringSSLBufferevents;
@@ -1146,10 +1157,6 @@ connection_or_tls_renegotiated_cb(tor_tls_t *tls, void *_conn)
or_connection_t *conn = _conn;
(void)tls;
- /* Don't invoke this again. */
- tor_tls_set_renegotiate_callback(tls, NULL, NULL);
- tor_tls_block_renegotiation(tls);
-
if (connection_tls_finish_handshake(conn) < 0) {
/* XXXX_TLS double-check that it's ok to do this from inside read. */
/* XXXX_TLS double-check that this verifies certificates. */
@@ -1157,6 +1164,34 @@ connection_or_tls_renegotiated_cb(tor_tls_t *tls, void *_conn)
}
}
+/*DOCDOC*/
+static void
+close_connection_libevent_cb(void *_conn)
+{
+ or_connection_t *or_conn = _conn;
+ connection_t *conn = TO_CONN(or_conn);
+
+ or_conn->pending_action = NULL;
+
+ connection_stop_reading(conn);
+ if (!conn->marked_for_close)
+ connection_mark_for_close(conn);
+}
+
+/* DOCDOC */
+static void
+connection_or_close_connection_cb(void *_conn)
+{
+ /* We can't close their connection from in here since it's an OpenSSL
+ callback, so we set a libevent event that triggers in the next event
+ loop and closes the connection. */
+ or_connection_t *or_conn = _conn;
+ if (or_conn->_base.marked_for_close || or_conn->pending_action)
+ return;
+ or_conn->pending_action =
+ tor_run_in_libevent_loop(close_connection_libevent_cb, or_conn);
+}
+
/** Move forward with the tls handshake. If it finishes, hand
* <b>conn</b> to connection_tls_finish_handshake().
*
@@ -1203,9 +1238,6 @@ connection_tls_continue_handshake(or_connection_t *conn)
/* v2/v3 handshake, but not a client. */
log_debug(LD_OR, "Done with initial SSL handshake (server-side). "
"Expecting renegotiation or VERSIONS cell");
- tor_tls_set_renegotiate_callback(conn->tls,
- connection_or_tls_renegotiated_cb,
- conn);
conn->_base.state = OR_CONN_STATE_TLS_SERVER_RENEGOTIATING;
connection_stop_writing(TO_CONN(conn));
connection_start_reading(TO_CONN(conn));
@@ -1266,9 +1298,6 @@ connection_or_handle_event_cb(struct bufferevent *bufev, short event,
} else if (tor_tls_get_num_server_handshakes(conn->tls) == 1) {
/* v2 or v3 handshake, as a server. Only got one handshake, so
* wait for the next one. */
- tor_tls_set_renegotiate_callback(conn->tls,
- connection_or_tls_renegotiated_cb,
- conn);
conn->_base.state = OR_CONN_STATE_TLS_SERVER_RENEGOTIATING;
/* return 0; */
return; /* ???? */
@@ -1536,7 +1565,6 @@ connection_tls_finish_handshake(or_connection_t *conn)
connection_or_init_conn_from_address(conn, &conn->_base.addr,
conn->_base.port, digest_rcvd, 0);
}
- tor_tls_block_renegotiation(conn->tls);
return connection_or_set_state_open(conn);
} else {
conn->_base.state = OR_CONN_STATE_OR_HANDSHAKING_V2;
diff --git a/src/or/control.c b/src/or/control.c
index 109eb8857..20caabf0c 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -737,7 +737,7 @@ control_setconf_helper(control_connection_t *conn, uint32_t len, char *body,
SMARTLIST_FOREACH(entries, char *, cp, tor_free(cp));
smartlist_free(entries);
- if (config_get_lines(config, &lines) < 0) {
+ if (config_get_lines(config, &lines, 0) < 0) {
log_warn(LD_CONTROL,"Controller gave us config lines we can't parse.");
connection_write_str_to_buf("551 Couldn't parse configuration\r\n",
conn);
@@ -883,7 +883,7 @@ handle_control_loadconf(control_connection_t *conn, uint32_t len,
const char *msg = NULL;
(void) len;
- retval = options_init_from_string(body, CMD_RUN_TOR, NULL, &errstring);
+ retval = options_init_from_string(NULL, body, CMD_RUN_TOR, NULL, &errstring);
if (retval != SETOPT_OK)
log_warn(LD_CONTROL,
@@ -1331,7 +1331,8 @@ handle_control_mapaddress(control_connection_t *conn, uint32_t len,
smartlist_add(reply, ans);
}
} else {
- addressmap_register(from, tor_strdup(to), 1, ADDRMAPSRC_CONTROLLER);
+ addressmap_register(from, tor_strdup(to), 1,
+ ADDRMAPSRC_CONTROLLER, 0, 0);
tor_snprintf(ans, anslen, "250-%s", line);
smartlist_add(reply, ans);
}
@@ -1378,7 +1379,9 @@ getinfo_helper_misc(control_connection_t *conn, const char *question,
if (!strcmp(question, "version")) {
*answer = tor_strdup(get_version());
} else if (!strcmp(question, "config-file")) {
- *answer = tor_strdup(get_torrc_fname());
+ *answer = tor_strdup(get_torrc_fname(0));
+ } else if (!strcmp(question, "config-defaults-file")) {
+ *answer = tor_strdup(get_torrc_fname(1));
} else if (!strcmp(question, "config-text")) {
*answer = options_dump(get_options(), 1);
} else if (!strcmp(question, "info/names")) {
@@ -2024,6 +2027,7 @@ typedef struct getinfo_item_t {
static const getinfo_item_t getinfo_items[] = {
ITEM("version", misc, "The current version of Tor."),
ITEM("config-file", misc, "Current location of the \"torrc\" file."),
+ ITEM("config-defaults-file", misc, "Current location of the defaults file."),
ITEM("config-text", misc,
"Return the string that would be written by a saveconf command."),
ITEM("accounting/bytes", accounting,
@@ -2399,7 +2403,7 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
/* now circ refers to something that is ready to be extended */
SMARTLIST_FOREACH(nodes, const node_t *, node,
{
- extend_info_t *info = extend_info_from_node(node);
+ extend_info_t *info = extend_info_from_node(node, 0);
tor_assert(info); /* True, since node_has_descriptor(node) == true */
circuit_append_new_exit(circ, info);
extend_info_free(info);
diff --git a/src/or/directory.c b/src/or/directory.c
index 073b88717..d4abe0243 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -923,7 +923,7 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr,
return;
}
- conn = dir_connection_new(AF_INET);
+ conn = dir_connection_new(tor_addr_family(&addr));
/* set up conn so it's got all the data we need to remember */
tor_addr_copy(&conn->_base.addr, &addr);
@@ -1623,9 +1623,11 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
if (!reason) reason = tor_strdup("[no reason given]");
log_debug(LD_DIR,
- "Received response from directory server '%s:%d': %d %s",
+ "Received response from directory server '%s:%d': %d %s "
+ "(purpose: %d)",
conn->_base.address, conn->_base.port, status_code,
- escaped(reason));
+ escaped(reason),
+ conn->_base.purpose);
/* now check if it's got any hints for us about our IP address. */
if (conn->dirconn_direct) {
@@ -3733,7 +3735,7 @@ download_status_reset(download_status_t *dls)
const int *schedule;
size_t schedule_len;
- find_dl_schedule_and_len(dls, get_options()->DirPort,
+ find_dl_schedule_and_len(dls, get_options()->DirPort != NULL,
&schedule, &schedule_len);
dls->n_download_failures = 0;
diff --git a/src/or/directory.h b/src/or/directory.h
index 8c63bb5df..5050f700d 100644
--- a/src/or/directory.h
+++ b/src/or/directory.h
@@ -80,7 +80,7 @@ time_t download_status_increment_failure(download_status_t *dls,
* the optional status code <b>sc</b>. */
#define download_status_failed(dls, sc) \
download_status_increment_failure((dls), (sc), NULL, \
- get_options()->DirPort, time(NULL))
+ get_options()->DirPort!=NULL, time(NULL))
void download_status_reset(download_status_t *dls);
static int download_status_is_ready(download_status_t *dls, time_t now,
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index be62459b1..f4bbca850 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -232,7 +232,7 @@ dirserv_load_fingerprint_file(void)
}
tor_free(fname);
- result = config_get_lines(cf, &front);
+ result = config_get_lines(cf, &front, 0);
tor_free(cf);
if (result < 0) {
log_warn(LD_CONFIG, "Error reading from fingerprint file");
@@ -1212,7 +1212,7 @@ directory_fetches_from_authorities(const or_options_t *options)
return 1; /* we don't know our IP address; ask an authority. */
refuseunknown = ! router_my_exit_policy_is_reject_star() &&
should_refuse_unknown_exits(options);
- if (options->DirPort == 0 && !refuseunknown)
+ if (options->DirPort == NULL && !refuseunknown)
return 0;
if (!server_mode(options) || !advertised_server_mode())
return 0;
@@ -1248,7 +1248,7 @@ directory_fetches_dir_info_later(const or_options_t *options)
int
directory_caches_v2_dir_info(const or_options_t *options)
{
- return options->DirPort != 0;
+ return options->DirPort != NULL;
}
/** Return 1 if we want to keep descriptors, networkstatuses, etc around
@@ -1273,7 +1273,7 @@ directory_caches_dir_info(const or_options_t *options)
int
directory_permits_begindir_requests(const or_options_t *options)
{
- return options->BridgeRelay != 0 || options->DirPort != 0;
+ return options->BridgeRelay != 0 || options->DirPort != NULL;
}
/** Return 1 if we want to allow controllers to ask us directory
@@ -1282,7 +1282,7 @@ directory_permits_begindir_requests(const or_options_t *options)
int
directory_permits_controller_requests(const or_options_t *options)
{
- return options->DirPort != 0;
+ return options->DirPort != NULL;
}
/** Return 1 if we have no need to fetch new descriptors. This generally
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index bf34c62af..01e2358c4 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -50,7 +50,7 @@ static int dirvote_publish_consensus(void);
static char *make_consensus_method_list(int low, int high, const char *sep);
/** The highest consensus method that we currently support. */
-#define MAX_SUPPORTED_CONSENSUS_METHOD 11
+#define MAX_SUPPORTED_CONSENSUS_METHOD 12
/** Lowest consensus method that contains a 'directory-footer' marker */
#define MIN_METHOD_FOR_FOOTER 9
@@ -64,6 +64,10 @@ static char *make_consensus_method_list(int low, int high, const char *sep);
/** Lowest consensus method that generates microdescriptors */
#define MIN_METHOD_FOR_MICRODESC 8
+/** Lowest consensus method that ensures a majority of authorities voted
+ * for a param. */
+#define MIN_METHOD_FOR_MAJORITY_PARAMS 12
+
/* =====
* Voting
* =====*/
@@ -608,11 +612,16 @@ compute_consensus_versions_list(smartlist_t *lst, int n_versioning)
return result;
}
+/** Minimum number of directory authorities voting for a parameter to
+ * include it in the consensus, if consensus method 12 or later is to be
+ * used. See proposal 178 for details. */
+#define MIN_VOTES_FOR_PARAM 3
+
/** Helper: given a list of valid networkstatus_t, return a new string
* containing the contents of the consensus network parameter set.
*/
/* private */ char *
-dirvote_compute_params(smartlist_t *votes)
+dirvote_compute_params(smartlist_t *votes, int method, int total_authorities)
{
int i;
int32_t *vals;
@@ -669,11 +678,17 @@ dirvote_compute_params(smartlist_t *votes)
next_param = smartlist_get(param_list, param_sl_idx+1);
if (!next_param || strncmp(next_param, param, cur_param_len)) {
/* We've reached the end of a series. */
- int32_t median = median_int32(vals, i);
- char *out_string = tor_malloc(64+cur_param_len);
- memcpy(out_string, param, cur_param_len);
- tor_snprintf(out_string+cur_param_len,64, "%ld", (long)median);
- smartlist_add(output, out_string);
+ /* Make sure enough authorities voted on this param, unless the
+ * the consensus method we use is too old for that. */
+ if (method < MIN_METHOD_FOR_MAJORITY_PARAMS ||
+ i > total_authorities/2 ||
+ i >= MIN_VOTES_FOR_PARAM) {
+ int32_t median = median_int32(vals, i);
+ char *out_string = tor_malloc(64+cur_param_len);
+ memcpy(out_string, param, cur_param_len);
+ tor_snprintf(out_string+cur_param_len,64, "%ld", (long)median);
+ smartlist_add(output, out_string);
+ }
i = 0;
if (next_param) {
@@ -1496,7 +1511,8 @@ networkstatus_compute_consensus(smartlist_t *votes,
}
if (consensus_method >= MIN_METHOD_FOR_PARAMS) {
- params = dirvote_compute_params(votes);
+ params = dirvote_compute_params(votes, consensus_method,
+ total_authorities);
if (params) {
smartlist_add(chunks, tor_strdup("params "));
smartlist_add(chunks, params);
diff --git a/src/or/dirvote.h b/src/or/dirvote.h
index d19635173..1f4dc362b 100644
--- a/src/or/dirvote.h
+++ b/src/or/dirvote.h
@@ -84,7 +84,8 @@ document_signature_t *voter_get_sig_by_algorithm(
#ifdef DIRVOTE_PRIVATE
char *format_networkstatus_vote(crypto_pk_env_t *private_key,
networkstatus_t *v3_ns);
-char *dirvote_compute_params(smartlist_t *votes);
+char *dirvote_compute_params(smartlist_t *votes, int method,
+ int total_authorities);
#endif
#endif
diff --git a/src/or/dns.c b/src/or/dns.c
index 8ed953690..beb110acb 100644
--- a/src/or/dns.c
+++ b/src/or/dns.c
@@ -1395,6 +1395,10 @@ launch_resolve(edge_connection_t *exitconn)
int r;
int options = get_options()->ServerDNSSearchDomains ? 0
: DNS_QUERY_NO_SEARCH;
+
+ if (get_options()->DisableNetwork)
+ return -1;
+
/* What? Nameservers not configured? Sounds like a bug. */
if (!nameservers_configured) {
log_warn(LD_EXIT, "(Harmless.) Nameservers not configured, but resolve "
@@ -1601,6 +1605,9 @@ launch_test_addresses(int fd, short event, void *args)
(void)event;
(void)args;
+ if (options->DisableNetwork)
+ return;
+
log_info(LD_EXIT, "Launching checks to see whether our nameservers like to "
"hijack *everything*.");
/* This situation is worse than the failure-hijacking situation. When this
diff --git a/src/or/hibernate.c b/src/or/hibernate.c
index 6fd2b4f19..ce64581d1 100644
--- a/src/or/hibernate.c
+++ b/src/or/hibernate.c
@@ -735,7 +735,6 @@ hibernate_soft_limit_reached(void)
static void
hibernate_begin(hibernate_state_t new_state, time_t now)
{
- connection_t *conn;
const or_options_t *options = get_options();
if (new_state == HIBERNATE_STATE_EXITING &&
@@ -756,15 +755,7 @@ hibernate_begin(hibernate_state_t new_state, time_t now)
}
/* close listeners. leave control listener(s). */
- while ((conn = connection_get_by_type(CONN_TYPE_OR_LISTENER)) ||
- (conn = connection_get_by_type(CONN_TYPE_AP_LISTENER)) ||
- (conn = connection_get_by_type(CONN_TYPE_AP_TRANS_LISTENER)) ||
- (conn = connection_get_by_type(CONN_TYPE_AP_DNS_LISTENER)) ||
- (conn = connection_get_by_type(CONN_TYPE_AP_NATD_LISTENER)) ||
- (conn = connection_get_by_type(CONN_TYPE_DIR_LISTENER))) {
- log_info(LD_NET,"Closing listener type %d", conn->type);
- connection_mark_for_close(conn);
- }
+ connection_mark_all_noncontrol_listeners();
/* XXX kill intro point circs */
/* XXX upload rendezvous service descriptors with no intro points */
diff --git a/src/or/main.c b/src/or/main.c
index 266356a96..d3eb721d8 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -196,6 +196,26 @@ free_old_inbuf(connection_t *conn)
}
#endif
+#ifdef MS_WINDOWS
+/** Remove the kernel-space send and receive buffers for <b>s</b>. For use
+ * with IOCP only. */
+static int
+set_buffer_lengths_to_zero(tor_socket_t s)
+{
+ int zero = 0;
+ int r = 0;
+ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&zero, sizeof(zero))) {
+ log_warn(LD_NET, "Unable to clear SO_SNDBUF");
+ r = -1;
+ }
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&zero, sizeof(zero))) {
+ log_warn(LD_NET, "Unable to clear SO_RCVBUF");
+ r = -1;
+ }
+ return r;
+}
+#endif
+
/** Add <b>conn</b> to the array of connections that we can poll on. The
* connection's socket must be set; the connection starts out
* non-reading and non-writing.
@@ -216,6 +236,14 @@ connection_add_impl(connection_t *conn, int is_connecting)
#ifdef USE_BUFFEREVENTS
if (connection_type_uses_bufferevent(conn)) {
if (SOCKET_OK(conn->s) && !conn->linked) {
+
+#ifdef MS_WINDOWS
+ if (tor_libevent_using_iocp_bufferevents() &&
+ get_options()->UserspaceIOCPBuffers) {
+ set_buffer_lengths_to_zero(conn->s);
+ }
+#endif
+
conn->bufev = bufferevent_socket_new(
tor_libevent_get_base(),
conn->s,
@@ -906,7 +934,7 @@ directory_info_has_arrived(time_t now, int from_cache)
update_extrainfo_downloads(now);
}
- if (server_mode(options) && !we_are_hibernating() && !from_cache &&
+ if (server_mode(options) && !net_is_disabled() && !from_cache &&
(can_complete_circuit || !any_predicted_circuits(now)))
consider_testing_reachability(1, 1);
}
@@ -1133,11 +1161,11 @@ run_scheduled_events(time_t now)
if (router_rebuild_descriptor(1)<0) {
log_info(LD_CONFIG, "Couldn't rebuild router descriptor");
}
- if (advertised_server_mode())
+ if (advertised_server_mode() & !options->DisableNetwork)
router_upload_dir_desc_to_dirservers(0);
}
- if (time_to_try_getting_descriptors < now) {
+ if (!options->DisableNetwork && time_to_try_getting_descriptors < now) {
update_all_descriptor_downloads(now);
update_extrainfo_downloads(now);
if (router_have_minimum_dir_info())
@@ -1161,10 +1189,7 @@ run_scheduled_events(time_t now)
last_rotated_x509_certificate = now;
if (last_rotated_x509_certificate+MAX_SSL_KEY_LIFETIME_INTERNAL < now) {
log_info(LD_GENERAL,"Rotating tls context.");
- if (tor_tls_context_init(public_server_mode(options),
- get_tlsclient_identity_key(),
- is_server ? get_server_identity_key() : NULL,
- MAX_SSL_KEY_LIFETIME_ADVERTISED) < 0) {
+ if (router_initialize_tls_context() < 0) {
log_warn(LD_BUG, "Error reinitializing TLS context");
/* XXX is it a bug here, that we just keep going? -RD */
}
@@ -1191,7 +1216,7 @@ run_scheduled_events(time_t now)
if (time_to_launch_reachability_tests < now &&
(authdir_mode_tests_reachability(options)) &&
- !we_are_hibernating()) {
+ !net_is_disabled()) {
time_to_launch_reachability_tests = now + REACHABILITY_TEST_INTERVAL;
/* try to determine reachability of the other Tor relays */
dirserv_test_reachability(now);
@@ -1327,7 +1352,7 @@ run_scheduled_events(time_t now)
/* 2b. Once per minute, regenerate and upload the descriptor if the old
* one is inaccurate. */
- if (time_to_check_descriptor < now) {
+ if (time_to_check_descriptor < now && !options->DisableNetwork) {
static int dirport_reachability_count = 0;
time_to_check_descriptor = now + CHECK_DESCRIPTOR_INTERVAL;
check_descriptor_bandwidth_changed(now);
@@ -1402,7 +1427,7 @@ run_scheduled_events(time_t now)
connection_expire_held_open();
/** 3d. And every 60 seconds, we relaunch listeners if any died. */
- if (!we_are_hibernating() && time_to_check_listeners < now) {
+ if (!net_is_disabled() && time_to_check_listeners < now) {
retry_all_listeners(NULL, NULL);
time_to_check_listeners = now+60;
}
@@ -1413,7 +1438,7 @@ run_scheduled_events(time_t now)
* and we make a new circ if there are no clean circuits.
*/
have_dir_info = router_have_minimum_dir_info();
- if (have_dir_info && !we_are_hibernating())
+ if (have_dir_info && !net_is_disabled())
circuit_build_needed_circs(now);
/* every 10 seconds, but not at the same second as other such events */
@@ -1444,7 +1469,7 @@ run_scheduled_events(time_t now)
circuit_close_all_marked();
/** 7. And upload service descriptors if necessary. */
- if (can_complete_circuit && !we_are_hibernating()) {
+ if (can_complete_circuit && !net_is_disabled()) {
rend_consider_services_upload(now);
rend_consider_descriptor_republication();
}
@@ -1461,7 +1486,8 @@ run_scheduled_events(time_t now)
/** 9. and if we're a server, check whether our DNS is telling stories to
* us. */
- if (public_server_mode(options) && time_to_check_for_correct_dns < now) {
+ if (!net_is_disabled() &&
+ public_server_mode(options) && time_to_check_for_correct_dns < now) {
if (!time_to_check_for_correct_dns) {
time_to_check_for_correct_dns = now + 60 + crypto_rand_int(120);
} else {
@@ -1480,19 +1506,21 @@ run_scheduled_events(time_t now)
}
/** 11. check the port forwarding app */
- if (time_to_check_port_forwarding < now &&
+ if (!net_is_disabled() &&
+ time_to_check_port_forwarding < now &&
options->PortForwarding &&
is_server) {
#define PORT_FORWARDING_CHECK_INTERVAL 5
+ /* XXXXX this should take a list of ports, not just two! */
tor_check_port_forwarding(options->PortForwardingHelper,
- options->DirPort,
- options->ORPort,
+ get_primary_dir_port(),
+ get_primary_or_port(),
now);
time_to_check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL;
}
/** 11b. check pending unconfigured managed proxies */
- if (pt_proxies_configuration_pending())
+ if (!net_is_disabled() && pt_proxies_configuration_pending())
pt_configure_remaining_proxies();
/** 11c. validate pluggable transports configuration if we need to */
@@ -1564,7 +1592,7 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg)
control_event_stream_bandwidth_used();
if (server_mode(options) &&
- !we_are_hibernating() &&
+ !net_is_disabled() &&
seconds_elapsed > 0 &&
can_complete_circuit &&
stats_n_seconds_working / TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT !=
@@ -1765,7 +1793,8 @@ do_hup(void)
/* retry appropriate downloads */
router_reset_status_download_failures();
router_reset_descriptor_download_failures();
- update_networkstatus_downloads(time(NULL));
+ if (!options->DisableNetwork)
+ update_networkstatus_downloads(time(NULL));
/* We'll retry routerstatus downloads in about 10 seconds; no need to
* force a retry there. */
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
index b93b919c1..eafc9b8b7 100644
--- a/src/or/nodelist.c
+++ b/src/or/nodelist.c
@@ -646,24 +646,70 @@ node_exit_policy_rejects_all(const node_t *node)
return 1;
}
-/** Copy the address for <b>node</b> into *<b>addr_out</b>. */
-int
-node_get_addr(const node_t *node, tor_addr_t *addr_out)
+/** Return list of tor_addr_port_t with all OR ports (in the sense IP
+ * addr + TCP port) for <b>node</b>. Caller must free all elements
+ * using tor_free() and free the list using smartlist_free().
+ *
+ * XXX this is potentially a memory fragmentation hog -- if on
+ * critical path consider the option of having the caller allocate the
+ * memory
+ */
+smartlist_t *
+node_get_all_orports(const node_t *node)
+{
+ smartlist_t *sl = smartlist_create();
+
+ if (node->ri != NULL) {
+ if (node->ri->addr != 0) {
+ tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t));
+ tor_addr_from_ipv4h(&ap->addr, node->ri->addr);
+ ap->port = node->ri->or_port;
+ smartlist_add(sl, ap);
+ }
+ if (!tor_addr_is_null(&node->ri->ipv6_addr)) {
+ tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t));
+ tor_addr_copy(&ap->addr, &node->ri->ipv6_addr);
+ ap->port = node->ri->or_port;
+ smartlist_add(sl, ap);
+ }
+ } else if (node->rs != NULL) {
+ tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t));
+ tor_addr_from_ipv4h(&ap->addr, node->rs->addr);
+ ap->port = node->rs->or_port;
+ smartlist_add(sl, ap);
+ }
+
+ return sl;
+}
+
+/** Copy the primary (IPv4) OR port (IP address and TCP port) for
+ * <b>node</b> into *<b>ap_out</b>. */
+void
+node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out)
{
if (node->ri) {
- tor_addr_from_ipv4h(addr_out, node->ri->addr);
- return 0;
- } else if (node->rs) {
- tor_addr_from_ipv4h(addr_out, node->rs->addr);
- return 0;
+ router_get_prim_orport(node->ri, ap_out);
}
- return -1;
+ else if (node->rs) {
+ tor_addr_from_ipv4h(&ap_out->addr, node->rs->addr);
+ ap_out->port = node->rs->or_port;
+ }
+}
+
+/** Wrapper around node_get_prim_orport for backward
+ compatibility. */
+void
+node_get_addr(const node_t *node, tor_addr_t *addr_out)
+{
+ tor_addr_port_t ap;
+ node_get_prim_orport(node, &ap);
+ tor_addr_copy(addr_out, &ap.addr);
}
/** Return the host-order IPv4 address for <b>node</b>, or 0 if it doesn't
* seem to have one. */
uint32_t
-node_get_addr_ipv4h(const node_t *node)
+node_get_prim_addr_ipv4h(const node_t *node)
{
if (node->ri) {
return node->ri->addr;
@@ -673,9 +719,38 @@ node_get_addr_ipv4h(const node_t *node)
return 0;
}
-/** Copy a string representation of the IP address for <b>node</b> into the
- * <b>len</b>-byte buffer at <b>buf</b>.
- */
+/** Copy the preferred OR port (IP address and TCP port) for
+ * <b>node</b> into <b>ap_out</b>. */
+void
+node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out)
+{
+ if (node->ri) {
+ router_get_pref_orport(node->ri, ap_out);
+ } else if (node->rs) {
+ /* No IPv6 in routerstatus_t yet. XXXprop186 ok for private
+ bridges but needs fixing */
+ tor_addr_from_ipv4h(&ap_out->addr, node->rs->addr);
+ ap_out->port = node->rs->or_port;
+ }
+}
+
+/** Copy the preferred IPv6 OR port (address and TCP port) for
+ * <b>node</b> into *<b>ap_out</b>. */
+void
+node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out)
+{
+ if (node->ri) {
+ router_get_pref_ipv6_orport(node->ri, ap_out);
+ } else if (node->rs) {
+ /* No IPv6 in routerstatus_t yet. XXXprop186 ok for private
+ bridges but needs fixing */
+ tor_addr_make_unspec(&ap_out->addr);
+ ap_out->port = 0;
+ }
+}
+
+/** Copy a string representation of an IP address for <b>node</b> into
+ * the <b>len</b>-byte buffer at <b>buf</b>. */
void
node_get_address_string(const node_t *node, char *buf, size_t len)
{
@@ -701,18 +776,6 @@ node_get_declared_uptime(const node_t *node)
return -1;
}
-/** Return <b>node</b>'s declared or_port */
-uint16_t
-node_get_orport(const node_t *node)
-{
- if (node->ri)
- return node->ri->or_port;
- else if (node->rs)
- return node->rs->or_port;
- else
- return 0;
-}
-
/** Return <b>node</b>'s platform string, or NULL if we don't know it. */
const char *
node_get_platform(const node_t *node)
diff --git a/src/or/nodelist.h b/src/or/nodelist.h
index bd2e63953..1b7549dad 100644
--- a/src/or/nodelist.h
+++ b/src/or/nodelist.h
@@ -37,10 +37,13 @@ int node_get_purpose(const node_t *node);
(node_get_purpose((node)) == ROUTER_PURPOSE_BRIDGE)
int node_is_me(const node_t *node);
int node_exit_policy_rejects_all(const node_t *node);
-int node_get_addr(const node_t *node, tor_addr_t *addr_out);
-uint32_t node_get_addr_ipv4h(const node_t *node);
+smartlist_t *node_get_all_orports(const node_t *node);
+void node_get_prim_orport(const node_t *node, tor_addr_port_t *addr_port_out);
+void node_get_pref_orport(const node_t *node, tor_addr_port_t *addr_port_out);
+void node_get_pref_ipv6_orport(const node_t *node,
+ tor_addr_port_t *addr_port_out);
+uint32_t node_get_prim_addr_ipv4h(const node_t *node);
int node_allows_single_hop_exits(const node_t *node);
-uint16_t node_get_orport(const node_t *node);
const char *node_get_nickname(const node_t *node);
const char *node_get_platform(const node_t *node);
void node_get_address_string(const node_t *node, char *cp, size_t len);
@@ -50,6 +53,10 @@ const smartlist_t *node_get_declared_family(const node_t *node);
smartlist_t *nodelist_get_list(void);
+/* Temporary during transition to multiple addresses. */
+void node_get_addr(const node_t *node, tor_addr_t *addr_out);
+#define node_get_addr_ipv4h(n) node_get_prim_addr_ipv4h((n))
+
/* XXXX These need to move out of routerlist.c */
void nodelist_refresh_countries(void);
void node_set_country(node_t *node);
diff --git a/src/or/or.h b/src/or/or.h
index 8117ee1f8..eb9f060e5 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -793,10 +793,10 @@ typedef struct rend_data_t {
char rend_cookie[REND_COOKIE_LEN];
} rend_data_t;
-/** Time interval for tracking possible replays of INTRODUCE2 cells.
- * Incoming cells with timestamps half of this interval in the past or
- * future are dropped immediately. */
-#define REND_REPLAY_TIME_INTERVAL (60 * 60)
+/** Time interval for tracking replays of DH public keys received in
+ * INTRODUCE2 cells. Used only to avoid launching multiple
+ * simultaneous attempts to connect to the same rendezvous point. */
+#define REND_REPLAY_TIME_INTERVAL (5 * 60)
/** Used to indicate which way a cell is going on a circuit. */
typedef enum {
@@ -1276,6 +1276,8 @@ typedef struct or_connection_t {
unsigned active_circuit_pqueue_last_recalibrated;
struct or_connection_t *next_with_same_id; /**< Next connection with same
* identity digest as this one. */
+
+ tor_libevent_action_t *pending_action;
} or_connection_t;
/** Subtype of connection_t for an "edge connection" -- that is, an entry (ap)
@@ -1724,6 +1726,13 @@ typedef struct {
uint16_t or_port; /**< Port for TLS connections. */
uint16_t dir_port; /**< Port for HTTP directory connections. */
+ /* DOCDOC */
+ /* XXXXX187 Actually these should probably be part of a list of addresses,
+ * not just a special case. Use abstractions to access these; don't do it
+ * directly. */
+ tor_addr_t ipv6_addr;
+ uint16_t ipv6_orport;
+
crypto_pk_env_t *onion_pkey; /**< Public RSA key for onions. */
crypto_pk_env_t *identity_pkey; /**< Public RSA key for signing. */
@@ -1755,6 +1764,8 @@ typedef struct {
/** True if, after we have added this router, we should re-launch
* tests for it. */
unsigned int needs_retest_if_added:1;
+ /** True if ipv6_addr:ipv6_orport is preferred. */
+ unsigned int ipv6_preferred:1;
/** Tor can use this router for general positions in circuits; we got it
* from a directory server as usual, or we're an authority and a server
@@ -2835,16 +2846,37 @@ typedef struct port_cfg_t {
int session_group; /**< A session group, or -1 if this port is not in a
* session group. */
+ /* Server port types (or, dir) only: */
+ unsigned int no_advertise : 1;
+ unsigned int no_listen : 1;
+ unsigned int all_addrs : 1;
+ unsigned int ipv4_only : 1;
+ unsigned int ipv6_only : 1;
+
/* Unix sockets only: */
/** Path for an AF_UNIX address */
char unix_addr[FLEXIBLE_ARRAY_MEMBER];
} port_cfg_t;
+/** Ordinary configuration line. */
+#define CONFIG_LINE_NORMAL 0
+/** Appends to previous configuration for the same option, even if we
+ * would ordinary replace it. */
+#define CONFIG_LINE_APPEND 1
+/* Removes all previous configuration for an option. */
+#define CONFIG_LINE_CLEAR 2
+
/** A linked list of lines in a config file. */
typedef struct config_line_t {
char *key;
char *value;
struct config_line_t *next;
+ /** What special treatment (if any) does this line require? */
+ unsigned int command:2;
+ /** If true, subsequent assignments to this linelist should replace
+ * it, not extend it. Set only on the first item in a linelist in an
+ * or_options_t. */
+ unsigned int fragile:1;
} config_line_t;
typedef struct routerset_t routerset_t;
@@ -2877,6 +2909,8 @@ typedef struct {
char *Address; /**< OR only: configured address for this onion router. */
char *PidFile; /**< Where to store PID of Tor process. */
+ int DynamicDHGroups; /**< Dynamic generation of prime moduli for use in DH.*/
+
routerset_t *ExitNodes; /**< Structure containing nicknames, digests,
* country codes and IP address patterns of ORs to
* consider as exits. */
@@ -2934,17 +2968,18 @@ typedef struct {
int DirAllowPrivateAddresses;
char *User; /**< Name of user to run Tor as. */
char *Group; /**< Name of group to run Tor as. */
- int ORPort; /**< Port to listen on for OR connections. */
+ config_line_t *ORPort; /**< Ports to listen on for OR connections. */
config_line_t *SocksPort; /**< Ports to listen on for SOCKS connections. */
/** Ports to listen on for transparent pf/netfilter connections. */
config_line_t *TransPort;
config_line_t *NATDPort; /**< Ports to listen on for transparent natd
* connections. */
- int ControlPort; /**< Port to listen on for control connections. */
+ config_line_t *ControlPort; /**< Port to listen on for control
+ * connections. */
config_line_t *ControlSocket; /**< List of Unix Domain Sockets to listen on
* for control connections. */
int ControlSocketsGroupWritable; /**< Boolean: Are control sockets g+rw? */
- int DirPort; /**< Port to listen on for directory connections. */
+ config_line_t *DirPort; /**< Port to listen on for directory connections. */
config_line_t *DNSPort; /**< Port to listen on for DNS requests. */
int AssumeReachable; /**< Whether to publish our descriptor regardless. */
int AuthoritativeDir; /**< Boolean: is this an authoritative directory? */
@@ -3257,6 +3292,8 @@ typedef struct {
disclaimer. This allows a server administrator to show
that they're running Tor and anyone visiting their server
will know this without any specialized knowledge. */
+ int DisableDebuggerAttachment; /**< Currently Linux only specific attempt to
+ disable ptrace; needs BSD testing. */
/** Boolean: if set, we start even if our resolv.conf file is missing
* or broken. */
int ServerDNSAllowBrokenConfig;
@@ -3441,6 +3478,15 @@ typedef struct {
* never use it. If -1, we do what the consensus says. */
int OptimisticData;
+ /** If 1, and we are using IOCP, we set the kernel socket SNDBUF and RCVBUF
+ * to 0 to try to save kernel memory and avoid the dread "Out of buffers"
+ * issue. */
+ int UserspaceIOCPBuffers;
+
+ /** If 1, we accept and launch no external network connections, except on
+ * control ports. */
+ int DisableNetwork;
+
} or_options_t;
/** Persistent state for an onion router, as saved to disk. */
@@ -4026,6 +4072,26 @@ typedef struct rend_encoded_v2_service_descriptor_t {
* introduction point. See also rend_intro_point_t.unreachable_count. */
#define MAX_INTRO_POINT_REACHABILITY_FAILURES 5
+/** The maximum number of distinct INTRODUCE2 cells which a hidden
+ * service's introduction point will receive before it begins to
+ * expire.
+ *
+ * XXX023 Is this number at all sane? */
+#define INTRO_POINT_LIFETIME_INTRODUCTIONS 16384
+
+/** The minimum number of seconds that an introduction point will last
+ * before expiring due to old age. (If it receives
+ * INTRO_POINT_LIFETIME_INTRODUCTIONS INTRODUCE2 cells, it may expire
+ * sooner.)
+ *
+ * XXX023 Should this be configurable? */
+#define INTRO_POINT_LIFETIME_MIN_SECONDS 18*60*60
+/** The maximum number of seconds that an introduction point will last
+ * before expiring due to old age.
+ *
+ * XXX023 Should this be configurable? */
+#define INTRO_POINT_LIFETIME_MAX_SECONDS 24*60*60
+
/** Introduction point information. Used both in rend_service_t (on
* the service side) and in rend_service_descriptor_t (on both the
* client and service side). */
@@ -4045,6 +4111,37 @@ typedef struct rend_intro_point_t {
* circuit to this intro point for some reason other than our
* circuit-build timeout. See also MAX_INTRO_POINT_REACHABILITY_FAILURES. */
unsigned int unreachable_count : 3;
+
+ /** (Service side only) Flag indicating that this intro point was
+ * included in the last HS descriptor we generated. */
+ unsigned int listed_in_last_desc : 1;
+
+ /** (Service side only) A digestmap recording the INTRODUCE2 cells
+ * this intro point's circuit has received. Each key is the digest
+ * of the RSA-encrypted part of a received INTRODUCE2 cell; each
+ * value is a pointer to the time_t at which the cell was received.
+ * This digestmap is used to prevent replay attacks. */
+ digestmap_t *accepted_intro_rsa_parts;
+
+ /** (Service side only) The time at which this intro point was first
+ * published, or -1 if this intro point has not yet been
+ * published. */
+ time_t time_published;
+
+ /** (Service side only) The time at which this intro point should
+ * (start to) expire, or -1 if we haven't decided when this intro
+ * point should expire. */
+ time_t time_to_expire;
+
+ /** (Service side only) The time at which we decided that this intro
+ * point should start expiring, or -1 if this intro point is not yet
+ * expiring.
+ *
+ * This field also serves as a flag to indicate that we have decided
+ * to expire this intro point, in case intro_point_should_expire_now
+ * flaps (perhaps due to a clock jump; perhaps due to other
+ * weirdness, or even a (present or future) bug). */
+ time_t time_expiring;
} rend_intro_point_t;
/** Information used to connect to a hidden service. Used on both the
diff --git a/src/or/relay.c b/src/or/relay.c
index ac3114bda..6cf4b73a5 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -924,6 +924,7 @@ connection_edge_process_relay_cell_not_open(
}
circuit_log_path(LOG_INFO,LD_APP,TO_ORIGIN_CIRCUIT(circ));
/* don't send a socks reply to transparent conns */
+ tor_assert(entry_conn->socks_request != NULL);
if (!entry_conn->socks_request->has_finished)
connection_ap_handshake_socks_reply(entry_conn, NULL, 0, 0);
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index e7ef313ab..5429b6c7e 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -1067,7 +1067,7 @@ rend_client_get_random_intro_impl(const rend_cache_entry_t *entry,
smartlist_del(usable_nodes, i);
goto again;
}
- new_extend_info = extend_info_from_node(node);
+ new_extend_info = extend_info_from_node(node, 0);
if (!new_extend_info) {
log_info(LD_REND, "We don't have a descriptor for the intro-point relay "
"'%s'; trying another.",
diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c
index 94bb00221..d09e6566c 100644
--- a/src/or/rendcommon.c
+++ b/src/or/rendcommon.c
@@ -440,6 +440,11 @@ rend_intro_point_free(rend_intro_point_t *intro)
extend_info_free(intro->extend_info);
crypto_free_pk_env(intro->intro_key);
+
+ if (intro->accepted_intro_rsa_parts != NULL) {
+ digestmap_free(intro->accepted_intro_rsa_parts, _tor_free);
+ }
+
tor_free(intro);
}
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 34d255ac1..a360d5ce5 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -26,6 +26,10 @@
static origin_circuit_t *find_intro_circuit(rend_intro_point_t *intro,
const char *pk_digest);
+static rend_intro_point_t *find_intro_point(origin_circuit_t *circ);
+
+static int intro_point_should_expire_now(rend_intro_point_t *intro,
+ time_t now);
/** Represents the mapping from a virtual port of a rendezvous service to
* a real port on some IP.
@@ -36,8 +40,10 @@ typedef struct rend_service_port_config_t {
tor_addr_t real_addr;
} rend_service_port_config_t;
-/** Try to maintain this many intro points per service if possible. */
-#define NUM_INTRO_POINTS 3
+/** Try to maintain this many intro points per service by default. */
+#define NUM_INTRO_POINTS_DEFAULT 3
+/** Maintain no more than this many intro points per hidden service. */
+#define NUM_INTRO_POINTS_MAX 10
/** If we can't build our intro circuits, don't retry for this long. */
#define INTRO_CIRC_RETRY_PERIOD (60*5)
@@ -51,6 +57,10 @@ typedef struct rend_service_port_config_t {
* rendezvous point before giving up? */
#define MAX_REND_TIMEOUT 30
+/** How many seconds should we wait for new HS descriptors to reach
+ * our clients before we close an expiring intro point? */
+#define INTRO_POINT_EXPIRATION_GRACE_PERIOD 5*60
+
/** Represents a single hidden service running at this OP. */
typedef struct rend_service_t {
/* Fields specified in config file */
@@ -72,17 +82,24 @@ typedef struct rend_service_t {
* introduction points. */
int n_intro_circuits_launched; /**< Count of intro circuits we have
* established in this period. */
+ unsigned int n_intro_points_wanted; /**< Number of intro points this
+ * service wants to have open. */
rend_service_descriptor_t *desc; /**< Current hidden service descriptor. */
time_t desc_is_dirty; /**< Time at which changes to the hidden service
* descriptor content occurred, or 0 if it's
* up-to-date. */
time_t next_upload_time; /**< Scheduled next hidden service descriptor
* upload time. */
- /** Map from digests of Diffie-Hellman values INTRODUCE2 to time_t of when
- * they were received; used to prevent replays. */
- digestmap_t *accepted_intros;
- /** Time at which we last removed expired values from accepted_intros. */
- time_t last_cleaned_accepted_intros;
+ /** Map from digests of Diffie-Hellman values INTRODUCE2 to time_t
+ * of when they were received. Clients may send INTRODUCE1 cells
+ * for the same rendezvous point through two or more different
+ * introduction points; when they do, this digestmap keeps us from
+ * launching multiple simultaneous attempts to connect to the same
+ * rend point. */
+ digestmap_t *accepted_intro_dh_parts;
+ /** Time at which we last removed expired values from
+ * accepted_intro_dh_parts. */
+ time_t last_cleaned_accepted_intro_dh_parts;
} rend_service_t;
/** A list of rend_service_t's for services run on this OP.
@@ -142,7 +159,7 @@ rend_service_free(rend_service_t *service)
rend_authorized_client_free(c););
smartlist_free(service->clients);
}
- digestmap_free(service->accepted_intros, _tor_free);
+ digestmap_free(service->accepted_intro_dh_parts, _tor_free);
tor_free(service);
}
@@ -319,6 +336,7 @@ rend_config_services(const or_options_t *options, int validate_only)
service->directory = tor_strdup(line->value);
service->ports = smartlist_create();
service->intro_period_started = time(NULL);
+ service->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT;
continue;
}
if (!service) {
@@ -542,16 +560,38 @@ rend_service_update_descriptor(rend_service_t *service)
for (i = 0; i < smartlist_len(service->intro_nodes); ++i) {
rend_intro_point_t *intro_svc = smartlist_get(service->intro_nodes, i);
rend_intro_point_t *intro_desc;
+
+ /* This intro point won't be listed in the descriptor... */
+ intro_svc->listed_in_last_desc = 0;
+
+ if (intro_svc->time_expiring != -1) {
+ /* This intro point is expiring. Don't list it. */
+ continue;
+ }
+
circ = find_intro_circuit(intro_svc, service->pk_digest);
- if (!circ || circ->_base.purpose != CIRCUIT_PURPOSE_S_INTRO)
+ if (!circ || circ->_base.purpose != CIRCUIT_PURPOSE_S_INTRO) {
+ /* This intro point's circuit isn't finished yet. Don't list it. */
continue;
+ }
+
+ /* ...unless this intro point is listed in the descriptor. */
+ intro_svc->listed_in_last_desc = 1;
- /* We have an entirely established intro circuit. */
+ /* We have an entirely established intro circuit. Publish it in
+ * our descriptor. */
intro_desc = tor_malloc_zero(sizeof(rend_intro_point_t));
intro_desc->extend_info = extend_info_dup(intro_svc->extend_info);
if (intro_svc->intro_key)
intro_desc->intro_key = crypto_pk_dup_key(intro_svc->intro_key);
smartlist_add(d->intro_nodes, intro_desc);
+
+ if (intro_svc->time_published == -1) {
+ /* We are publishing this intro point in a descriptor for the
+ * first time -- note the current time in the service's copy of
+ * the intro point. */
+ intro_svc->time_published = time(NULL);
+ }
}
}
@@ -857,15 +897,16 @@ rend_check_authorization(rend_service_t *service,
/** Remove elements from <b>service</b>'s replay cache that are old enough to
* be noticed by timestamp checking. */
static void
-clean_accepted_intros(rend_service_t *service, time_t now)
+clean_accepted_intro_dh_parts(rend_service_t *service, time_t now)
{
const time_t cutoff = now - REND_REPLAY_TIME_INTERVAL;
- service->last_cleaned_accepted_intros = now;
- if (!service->accepted_intros)
+ service->last_cleaned_accepted_intro_dh_parts = now;
+ if (!service->accepted_intro_dh_parts)
return;
- DIGESTMAP_FOREACH_MODIFY(service->accepted_intros, digest, time_t *, t) {
+ DIGESTMAP_FOREACH_MODIFY(service->accepted_intro_dh_parts, digest,
+ time_t *, t) {
if (*t < cutoff) {
tor_free(t);
MAP_DEL_CURRENT(digest);
@@ -890,6 +931,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
char buf[RELAY_PAYLOAD_SIZE];
char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */
rend_service_t *service;
+ rend_intro_point_t *intro_point;
int r, i, v3_shift = 0;
size_t len, keylen;
crypto_dh_env_t *dh = NULL;
@@ -939,7 +981,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
service = rend_service_get_by_pk_digest(
circuit->rend_data->rend_pk_digest);
if (!service) {
- log_warn(LD_REND, "Got an INTRODUCE2 cell for an unrecognized service %s.",
+ log_warn(LD_BUG, "Internal error: Got an INTRODUCE2 cell on an intro "
+ "circ for an unrecognized service %s.",
escaped(serviceid));
return -1;
}
@@ -964,17 +1007,26 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
return -1;
}
- if (!service->accepted_intros)
- service->accepted_intros = digestmap_new();
+ intro_point = find_intro_point(circuit);
+ if (intro_point == NULL) {
+ log_warn(LD_BUG, "Internal error: Got an INTRODUCE2 cell on an intro circ "
+ "(for service %s) with no corresponding rend_intro_point_t.",
+ escaped(serviceid));
+ return -1;
+ }
+
+ if (!service->accepted_intro_dh_parts)
+ service->accepted_intro_dh_parts = digestmap_new();
+
+ if (!intro_point->accepted_intro_rsa_parts)
+ intro_point->accepted_intro_rsa_parts = digestmap_new();
{
char pkpart_digest[DIGEST_LEN];
- /* Check for replay of PK-encrypted portion. It is slightly naughty to
- use the same digestmap to check for this and for g^x replays, but
- collisions are tremendously unlikely.
- */
+ /* Check for replay of PK-encrypted portion. */
crypto_digest(pkpart_digest, (char*)request+DIGEST_LEN, keylen);
- access_time = digestmap_get(service->accepted_intros, pkpart_digest);
+ access_time = digestmap_get(intro_point->accepted_intro_rsa_parts,
+ pkpart_digest);
if (access_time != NULL) {
log_warn(LD_REND, "Possible replay detected! We received an "
"INTRODUCE2 cell with same PK-encrypted part %d seconds ago. "
@@ -983,7 +1035,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
}
access_time = tor_malloc(sizeof(time_t));
*access_time = now;
- digestmap_set(service->accepted_intros, pkpart_digest, access_time);
+ digestmap_set(intro_point->accepted_intro_rsa_parts,
+ pkpart_digest, access_time);
}
/* Next N bytes is encrypted with service key */
@@ -999,7 +1052,6 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
len = r;
if (*buf == 3) {
/* Version 3 INTRODUCE2 cell. */
- time_t ts = 0;
v3_shift = 1;
auth_type = buf[1];
switch (auth_type) {
@@ -1021,17 +1073,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
log_info(LD_REND, "Unknown authorization type '%d'", auth_type);
}
- /* Check timestamp. */
- ts = ntohl(get_uint32(buf+1+v3_shift));
+ /* Skip the timestamp field. We no longer use it. */
v3_shift += 4;
- if ((now - ts) < -1 * REND_REPLAY_TIME_INTERVAL / 2 ||
- (now - ts) > REND_REPLAY_TIME_INTERVAL / 2) {
- /* This is far more likely to mean that a client's clock is
- * skewed than that a replay attack is in progress. */
- log_info(LD_REND, "INTRODUCE2 cell is too %s. Discarding.",
- (now - ts) < 0 ? "old" : "new");
- return -1;
- }
}
if (*buf == 2 || *buf == 3) {
/* Version 2 INTRODUCE2 cell. */
@@ -1101,7 +1144,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
goto err;
}
- extend_info = extend_info_from_node(node);
+ extend_info = extend_info_from_node(node, 0);
}
if (len != REND_COOKIE_LEN+DH_KEY_LEN) {
@@ -1130,7 +1173,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
/* Check whether there is a past request with the same Diffie-Hellman,
* part 1. */
- access_time = digestmap_get(service->accepted_intros, diffie_hellman_hash);
+ access_time = digestmap_get(service->accepted_intro_dh_parts,
+ diffie_hellman_hash);
if (access_time != NULL) {
/* A Tor client will send a new INTRODUCE1 cell with the same rend
* cookie and DH public key as its previous one if its intro circ
@@ -1152,9 +1196,11 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
* one hour. */
access_time = tor_malloc(sizeof(time_t));
*access_time = now;
- digestmap_set(service->accepted_intros, diffie_hellman_hash, access_time);
- if (service->last_cleaned_accepted_intros + REND_REPLAY_TIME_INTERVAL < now)
- clean_accepted_intros(service, now);
+ digestmap_set(service->accepted_intro_dh_parts,
+ diffie_hellman_hash, access_time);
+ if (service->last_cleaned_accepted_intro_dh_parts + REND_REPLAY_TIME_INTERVAL
+ < now)
+ clean_accepted_intro_dh_parts(service, now);
/* If the service performs client authorization, check included auth data. */
if (service->clients) {
@@ -1416,7 +1462,8 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
/* If we already have enough introduction circuits for this service,
* redefine this one as a general circuit or close it, depending. */
- if (count_established_intro_points(serviceid) > NUM_INTRO_POINTS) {
+ if (count_established_intro_points(serviceid) >
+ (int)service->n_intro_points_wanted) { /* XXX023 remove cast */
const or_options_t *options = get_options();
if (options->ExcludeNodes) {
/* XXXX in some future version, we can test whether the transition is
@@ -1658,6 +1705,35 @@ find_intro_circuit(rend_intro_point_t *intro, const char *pk_digest)
return NULL;
}
+/** Return a pointer to the rend_intro_point_t corresponding to the
+ * service-side introduction circuit <b>circ</b>. */
+static rend_intro_point_t *
+find_intro_point(origin_circuit_t *circ)
+{
+ const char *serviceid;
+ rend_service_t *service = NULL;
+
+ tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
+ TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_INTRO);
+ tor_assert(circ->rend_data);
+ serviceid = circ->rend_data->onion_address;
+
+ SMARTLIST_FOREACH(rend_service_list, rend_service_t *, s,
+ if (tor_memeq(s->service_id, serviceid, REND_SERVICE_ID_LEN_BASE32)) {
+ service = s;
+ break;
+ });
+
+ if (service == NULL) return NULL;
+
+ SMARTLIST_FOREACH(service->intro_nodes, rend_intro_point_t *, intro_point,
+ if (crypto_pk_cmp_keys(intro_point->intro_key, circ->intro_key) == 0) {
+ return intro_point;
+ });
+
+ return NULL;
+}
+
/** Determine the responsible hidden service directories for the
* rend_encoded_v2_service_descriptor_t's in <b>descs</b> and upload them;
* <b>service_id</b> and <b>seconds_valid</b> are only passed for logging
@@ -1861,6 +1937,52 @@ upload_service_descriptor(rend_service_t *service)
service->desc_is_dirty = 0;
}
+/** Return non-zero iff <b>intro</b> should 'expire' now (i.e. we
+ * should stop publishing it in new descriptors and eventually close
+ * it). */
+static int
+intro_point_should_expire_now(rend_intro_point_t *intro,
+ time_t now)
+{
+ tor_assert(intro != NULL);
+
+ if (intro->time_published == -1) {
+ /* Don't expire an intro point if we haven't even published it yet. */
+ return 0;
+ }
+
+ if (intro->time_expiring != -1) {
+ /* We've already started expiring this intro point. *Don't* let
+ * this function's result 'flap'. */
+ return 1;
+ }
+
+ if (digestmap_size(intro->accepted_intro_rsa_parts) >=
+ INTRO_POINT_LIFETIME_INTRODUCTIONS) {
+ /* This intro point has been used too many times. Expire it now. */
+ return 1;
+ }
+
+ if (intro->time_to_expire == -1) {
+ /* This intro point has been published, but we haven't picked an
+ * expiration time for it. Pick one now. */
+ int intro_point_lifetime_seconds =
+ INTRO_POINT_LIFETIME_MIN_SECONDS +
+ crypto_rand_int(INTRO_POINT_LIFETIME_MAX_SECONDS -
+ INTRO_POINT_LIFETIME_MIN_SECONDS);
+
+ /* Start the expiration timer now, rather than when the intro
+ * point was first published. There shouldn't be much of a time
+ * difference. */
+ intro->time_to_expire = now + intro_point_lifetime_seconds;
+
+ return 0;
+ }
+
+ /* This intro point has a time to expire set already. Use it. */
+ return (now >= intro->time_to_expire);
+}
+
/** For every service, check how many intro points it currently has, and:
* - Pick new intro points as necessary.
* - Launch circuits to any new intro points.
@@ -1872,7 +1994,9 @@ rend_services_introduce(void)
const node_t *node;
rend_service_t *service;
rend_intro_point_t *intro;
- int changed, prev_intro_nodes;
+ int intro_point_set_changed, prev_intro_nodes;
+ unsigned int n_intro_points_unexpired;
+ unsigned int n_intro_points_to_open;
smartlist_t *intro_nodes;
time_t now;
const or_options_t *options = get_options();
@@ -1885,7 +2009,16 @@ rend_services_introduce(void)
service = smartlist_get(rend_service_list, i);
tor_assert(service);
- changed = 0;
+
+ /* intro_point_set_changed becomes non-zero iff the set of intro
+ * points to be published in service's descriptor has changed. */
+ intro_point_set_changed = 0;
+
+ /* n_intro_points_unexpired collects the number of non-expiring
+ * intro points we have, so that we know how many new intro
+ * circuits we need to launch for this service. */
+ n_intro_points_unexpired = 0;
+
if (now > service->intro_period_started+INTRO_CIRC_RETRY_PERIOD) {
/* One period has elapsed; we can try building circuits again. */
service->intro_period_started = now;
@@ -1899,59 +2032,116 @@ rend_services_introduce(void)
/* Find out which introduction points we have in progress for this
service. */
- for (j=0; j < smartlist_len(service->intro_nodes); ++j) {
- intro = smartlist_get(service->intro_nodes, j);
+ SMARTLIST_FOREACH_BEGIN(service->intro_nodes, rend_intro_point_t *,
+ intro) {
+ origin_circuit_t *intro_circ =
+ find_intro_circuit(intro, service->pk_digest);
+
+ if (intro->time_expiring + INTRO_POINT_EXPIRATION_GRACE_PERIOD > now) {
+ /* This intro point has completely expired. Remove it, and
+ * mark the circuit for close if it's still alive. */
+ if (intro_circ != NULL) {
+ circuit_mark_for_close(TO_CIRCUIT(intro_circ),
+ END_CIRC_REASON_FINISHED);
+ }
+ rend_intro_point_free(intro);
+ intro = NULL; /* SMARTLIST_DEL_CURRENT takes a name, not a value. */
+ SMARTLIST_DEL_CURRENT(service->intro_nodes, intro);
+ /* We don't need to set intro_point_set_changed here, because
+ * this intro point wouldn't have been published in a current
+ * descriptor anyway. */
+ continue;
+ }
+
node = node_get_by_id(intro->extend_info->identity_digest);
- if (!node || !find_intro_circuit(intro, service->pk_digest)) {
- log_info(LD_REND,"Giving up on %s as intro point for %s.",
+ if (!node || !intro_circ) {
+ int removing_this_intro_point_changes_the_intro_point_set = 1;
+ log_info(LD_REND, "Giving up on %s as intro point for %s"
+ " (circuit disappeared).",
safe_str_client(extend_info_describe(intro->extend_info)),
safe_str_client(service->service_id));
- if (service->desc) {
- SMARTLIST_FOREACH(service->desc->intro_nodes, rend_intro_point_t *,
- dintro, {
- if (tor_memeq(dintro->extend_info->identity_digest,
- intro->extend_info->identity_digest, DIGEST_LEN)) {
- log_info(LD_REND, "The intro point we are giving up on was "
- "included in the last published descriptor. "
- "Marking current descriptor as dirty.");
- service->desc_is_dirty = now;
- }
- });
+ if (intro->time_expiring != -1) {
+ log_info(LD_REND, "We were already expiring the intro point; "
+ "no need to mark the HS descriptor as dirty over this.");
+ removing_this_intro_point_changes_the_intro_point_set = 0;
+ } else if (intro->listed_in_last_desc) {
+ log_info(LD_REND, "The intro point we are giving up on was "
+ "included in the last published descriptor. "
+ "Marking current descriptor as dirty.");
+ service->desc_is_dirty = now;
}
rend_intro_point_free(intro);
- smartlist_del(service->intro_nodes,j--);
- changed = 1;
+ intro = NULL; /* SMARTLIST_DEL_CURRENT takes a name, not a value. */
+ SMARTLIST_DEL_CURRENT(service->intro_nodes, intro);
+ if (removing_this_intro_point_changes_the_intro_point_set)
+ intro_point_set_changed = 1;
}
+
+ if (intro != NULL && intro_point_should_expire_now(intro, now)) {
+ log_info(LD_REND, "Expiring %s as intro point for %s.",
+ safe_str_client(extend_info_describe(intro->extend_info)),
+ safe_str_client(service->service_id));
+
+ /* The polite (and generally Right) way to expire an intro
+ * point is to establish a new one to replace it, publish a
+ * new descriptor that doesn't list any expiring intro points,
+ * and *then*, once our upload attempts for the new descriptor
+ * have ended (whether in success or failure), close the
+ * expiring intro points.
+ *
+ * Unfortunately, we can't find out when the new descriptor
+ * has actually been uploaded, so we'll have to settle for a
+ * five-minute timer. Start it. XXX023 This sucks. */
+ intro->time_expiring = now;
+
+ intro_point_set_changed = 1;
+ }
+
+ if (intro != NULL && intro->time_expiring == -1)
+ ++n_intro_points_unexpired;
+
if (node)
smartlist_add(intro_nodes, (void*)node);
- }
-
- /* We have enough intro points, and the intro points we thought we had were
- * all connected.
- */
- if (!changed && smartlist_len(service->intro_nodes) >= NUM_INTRO_POINTS) {
- /* We have all our intro points! Start a fresh period and reset the
- * circuit count. */
+ } SMARTLIST_FOREACH_END(intro);
+
+ if (!intro_point_set_changed &&
+ (n_intro_points_unexpired >= service->n_intro_points_wanted)) {
+ /* We have enough intro circuits in progress, and none of our
+ * intro circuits have died since the last call to
+ * rend_services_introduce! Start a fresh period and reset the
+ * circuit count.
+ *
+ * XXXX WTF? */
service->intro_period_started = now;
service->n_intro_circuits_launched = 0;
continue;
}
- /* Remember how many introduction circuits we started with. */
+ /* Remember how many introduction circuits we started with.
+ *
+ * prev_intro_nodes serves a different purpose than
+ * n_intro_points_unexpired -- this variable tells us where our
+ * previously-created intro points end and our new ones begin in
+ * the intro-point list, so we don't have to launch the circuits
+ * at the same time as we create the intro points they correspond
+ * to. XXXX This is daft. */
prev_intro_nodes = smartlist_len(service->intro_nodes);
+
/* We have enough directory information to start establishing our
- * intro points. We want to end up with three intro points, but if
- * we're just starting, we launch five and pick the first three that
- * complete.
+ * intro points. We want to end up with n_intro_points_wanted
+ * intro points, but if we're just starting, we launch two extra
+ * circuits and use the first n_intro_points_wanted that complete.
*
* The ones after the first three will be converted to 'general'
* internal circuits in rend_service_intro_has_opened(), and then
* we'll drop them from the list of intro points next time we
* go through the above "find out which introduction points we have
* in progress" loop. */
-#define NUM_INTRO_POINTS_INIT (NUM_INTRO_POINTS + 2)
- for (j=prev_intro_nodes; j < (prev_intro_nodes == 0 ?
- NUM_INTRO_POINTS_INIT : NUM_INTRO_POINTS); ++j) {
+ n_intro_points_to_open = (service->n_intro_points_wanted +
+ (prev_intro_nodes == 0 ? 2 : 0));
+ for (j = (int)n_intro_points_unexpired;
+ j < (int)n_intro_points_to_open;
+ ++j) { /* XXXX remove casts */
router_crn_flags_t flags = CRN_NEED_UPTIME|CRN_NEED_DESC;
if (get_options()->_AllowInvalid & ALLOW_INVALID_INTRODUCTION)
flags |= CRN_ALLOW_INVALID;
@@ -1959,16 +2149,21 @@ rend_services_introduce(void)
options->ExcludeNodes, flags);
if (!node) {
log_warn(LD_REND,
- "Could only establish %d introduction points for %s.",
- smartlist_len(service->intro_nodes), service->service_id);
+ "Could only establish %d introduction points for %s; "
+ "wanted %u.",
+ smartlist_len(service->intro_nodes), service->service_id,
+ n_intro_points_to_open);
break;
}
- changed = 1;
+ intro_point_set_changed = 1;
smartlist_add(intro_nodes, (void*)node);
intro = tor_malloc_zero(sizeof(rend_intro_point_t));
- intro->extend_info = extend_info_from_node(node);
+ intro->extend_info = extend_info_from_node(node, 0);
intro->intro_key = crypto_new_pk_env();
tor_assert(!crypto_pk_generate_key(intro->intro_key));
+ intro->time_published = -1;
+ intro->time_to_expire = -1;
+ intro->time_expiring = -1;
smartlist_add(service->intro_nodes, intro);
log_info(LD_REND, "Picked router %s as an intro point for %s.",
safe_str_client(node_describe(node)),
@@ -1976,7 +2171,7 @@ rend_services_introduce(void)
}
/* If there's no need to launch new circuits, stop here. */
- if (!changed)
+ if (!intro_point_set_changed)
continue;
/* Establish new introduction points. */
diff --git a/src/or/router.c b/src/or/router.c
index b6b96a5ff..a08a2009a 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -484,6 +484,16 @@ v3_authority_check_key_expiry(void)
last_warned = now;
}
+int
+router_initialize_tls_context(void)
+{
+ return tor_tls_context_init(public_server_mode(get_options()),
+ get_tlsclient_identity_key(),
+ server_mode(get_options()) ?
+ get_server_identity_key() : NULL,
+ MAX_SSL_KEY_LIFETIME_ADVERTISED);
+}
+
/** Initialize all OR private keys, and the TLS context, as necessary.
* On OPs, this only initializes the tls context. Return 0 on success,
* or -1 if Tor should die.
@@ -530,10 +540,7 @@ init_keys(void)
}
set_client_identity_key(prkey);
/* Create a TLS context. */
- if (tor_tls_context_init(0,
- get_tlsclient_identity_key(),
- NULL,
- MAX_SSL_KEY_LIFETIME_ADVERTISED) < 0) {
+ if (router_initialize_tls_context() < 0) {
log_err(LD_GENERAL,"Error creating TLS context for Tor client.");
return -1;
}
@@ -626,13 +633,11 @@ init_keys(void)
tor_free(keydir);
/* 3. Initialize link key and TLS context. */
- if (tor_tls_context_init(public_server_mode(options),
- get_tlsclient_identity_key(),
- get_server_identity_key(),
- MAX_SSL_KEY_LIFETIME_ADVERTISED) < 0) {
+ if (router_initialize_tls_context() < 0) {
log_err(LD_GENERAL,"Error initializing TLS context");
return -1;
}
+
/* 4. Build our router descriptor. */
/* Must be called after keys are initialized. */
mydesc = router_get_my_descriptor();
@@ -780,7 +785,7 @@ check_whether_dirport_reachable(void)
const or_options_t *options = get_options();
return !options->DirPort ||
options->AssumeReachable ||
- we_are_hibernating() ||
+ net_is_disabled() ||
can_reach_dir_port;
}
@@ -806,7 +811,7 @@ decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port)
return 0;
if (authdir_mode(options)) /* always publish */
return dir_port;
- if (we_are_hibernating())
+ if (net_is_disabled())
return 0;
if (!check_whether_dirport_reachable())
return 0;
@@ -888,7 +893,8 @@ consider_testing_reachability(int test_or, int test_dir)
log_info(LD_CIRC, "Testing %s of my ORPort: %s:%d.",
!orport_reachable ? "reachability" : "bandwidth",
me->address, me->or_port);
- ei = extend_info_from_router(me);
+ /* XXX IPv6 self testing IPv6 orports will need pref_addr */
+ ei = extend_info_from_router(me, 0);
circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei,
CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL);
extend_info_free(ei);
@@ -974,6 +980,14 @@ router_perform_bandwidth_test(int num_circs, time_t now)
}
}
+/** Return true iff our network is in some sense disabled: either we're
+ * hibernating, entering hibernation, or */
+int
+net_is_disabled(void)
+{
+ return get_options()->DisableNetwork || we_are_hibernating();
+}
+
/** Return true iff we believe ourselves to be an authoritative
* directory server.
*/
@@ -1070,7 +1084,7 @@ int
server_mode(const or_options_t *options)
{
if (options->ClientOnly) return 0;
- return (options->ORPort != 0 || options->ORListenAddress);
+ return (options->ORPort || options->ORListenAddress);
}
/** Return true iff we are trying to be a non-bridge server.
@@ -1121,7 +1135,14 @@ int
proxy_mode(const or_options_t *options)
{
(void)options;
- return smartlist_len(get_configured_client_ports()) > 0;
+ SMARTLIST_FOREACH_BEGIN(get_configured_ports(), const port_cfg_t *, p) {
+ if (p->type == CONN_TYPE_AP_LISTENER ||
+ p->type == CONN_TYPE_AP_TRANS_LISTENER ||
+ p->type == CONN_TYPE_AP_DNS_LISTENER ||
+ p->type == CONN_TYPE_AP_NATD_LISTENER)
+ return 1;
+ } SMARTLIST_FOREACH_END(p);
+ return 0;
}
/** Decide if we're a publishable server. We are a publishable server if:
@@ -1180,17 +1201,21 @@ consider_publishable_server(int force)
/** Return the port that we should advertise as our ORPort; this is either
* the one configured in the ORPort option, or the one we actually bound to
- * if ORPort is "auto". */
+ * if ORPort is "auto".
+ */
uint16_t
router_get_advertised_or_port(const or_options_t *options)
{
- if (options->ORPort == CFG_AUTO_PORT) {
+ int port = get_primary_or_port();
+ (void)options;
+
+ if (port == CFG_AUTO_PORT) {
connection_t *c = connection_get_by_type(CONN_TYPE_OR_LISTENER);
if (c)
return c->port;
return 0;
}
- return options->ORPort;
+ return port;
}
/** Return the port that we should advertise as our DirPort;
@@ -1201,15 +1226,18 @@ router_get_advertised_or_port(const or_options_t *options)
uint16_t
router_get_advertised_dir_port(const or_options_t *options, uint16_t dirport)
{
- if (!options->DirPort)
+ int dirport_configured = get_primary_dir_port();
+ (void)options;
+
+ if (!dirport_configured)
return dirport;
- if (options->DirPort == CFG_AUTO_PORT) {
+ if (dirport_configured == CFG_AUTO_PORT) {
connection_t *c = connection_get_by_type(CONN_TYPE_DIR_LISTENER);
if (c)
return c->port;
return 0;
}
- return options->DirPort;
+ return dirport_configured;
}
/*
@@ -1466,6 +1494,24 @@ router_rebuild_descriptor(int force)
ri->cache_info.published_on = time(NULL);
ri->onion_pkey = crypto_pk_dup_key(get_onion_key()); /* must invoke from
* main thread */
+ if (options->BridgeRelay) {
+ /* For now, only bridges advertise an ipv6 or-address. And only one. */
+ const port_cfg_t *ipv6_orport = NULL;
+ SMARTLIST_FOREACH_BEGIN(get_configured_ports(), const port_cfg_t *, p) {
+ if (p->type == CONN_TYPE_OR_LISTENER &&
+ ! p->no_advertise &&
+ ! p->ipv4_only &&
+ tor_addr_family(&p->addr) == AF_INET6 &&
+ ! tor_addr_is_internal(&p->addr, 1)) {
+ ipv6_orport = p;
+ break;
+ }
+ } SMARTLIST_FOREACH_END(p);
+ if (ipv6_orport) {
+ tor_addr_copy(&ri->ipv6_addr, &ipv6_orport->addr);
+ ri->ipv6_orport = ipv6_orport->port;
+ }
+ }
ri->identity_pkey = crypto_pk_dup_key(get_server_identity_key());
if (crypto_pk_get_digest(ri->identity_pkey,
ri->cache_info.identity_digest)<0) {
@@ -1875,6 +1921,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
int result=0;
addr_policy_t *tmpe;
char *family_line;
+ char *extra_or_address = NULL;
const or_options_t *options = get_options();
/* Make sure the identity key matches the one in the routerinfo. */
@@ -1927,9 +1974,22 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
router->cache_info.extra_info_digest, DIGEST_LEN);
}
+ if (router->ipv6_orport &&
+ tor_addr_family(&router->ipv6_addr) == AF_INET6) {
+ char addr[TOR_ADDR_BUF_LEN];
+ const char *a;
+ a = tor_addr_to_str(addr, &router->ipv6_addr, sizeof(addr), 1);
+ if (a) {
+ tor_asprintf(&extra_or_address,
+ "or-address %s:%d\n", a, router->ipv6_orport);
+ log_notice(LD_OR, "My line is <%s>", extra_or_address);
+ }
+ }
+
/* Generate the easy portion of the router descriptor. */
result = tor_snprintf(s, maxlen,
"router %s %s %d 0 %d\n"
+ "%s"
"platform %s\n"
"opt protocols Link 1 2 Circuit 1\n"
"published %s\n"
@@ -1944,6 +2004,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
router->address,
router->or_port,
decide_to_advertise_dirport(options, router->dir_port),
+ extra_or_address ? extra_or_address : "",
router->platform,
published,
fingerprint,
@@ -1964,6 +2025,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
tor_free(family_line);
tor_free(onion_pkey);
tor_free(identity_pkey);
+ tor_free(extra_or_address);
if (result < 0) {
log_warn(LD_BUG,"descriptor snprintf #1 ran out of room!");
@@ -2059,6 +2121,52 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
return (int)written+1;
}
+/** Copy the primary (IPv4) OR port (IP address and TCP port) for
+ * <b>router</b> into *<b>ap_out</b>. */
+void
+router_get_prim_orport(const routerinfo_t *router, tor_addr_port_t *ap_out)
+{
+ tor_assert(ap_out != NULL);
+ tor_addr_from_ipv4h(&ap_out->addr, router->addr);
+ ap_out->port = router->or_port;
+}
+
+/** Return 1 if we prefer the IPv6 address and OR TCP port of
+ * <b>router</b>, else 0.
+ *
+ * We prefer the IPv6 address if the router has one and
+ * i) the routerinfo_t says so
+ * or
+ * ii) the router has no IPv4 address. */
+int
+router_ipv6_preferred(const routerinfo_t *router)
+{
+ return (!tor_addr_is_null(&router->ipv6_addr)
+ && (router->ipv6_preferred || router->addr == 0));
+}
+
+/** Copy the preferred OR port (IP address and TCP port) for
+ * <b>router</b> into *<b>addr_out</b>. */
+void
+router_get_pref_orport(const routerinfo_t *router, tor_addr_port_t *ap_out)
+{
+ if (router_ipv6_preferred(router))
+ router_get_pref_ipv6_orport(router, ap_out);
+ else
+ router_get_prim_orport(router, ap_out);
+}
+
+/** Copy the preferred IPv6 OR port (IP address and TCP port) for
+ * <b>router</b> into *<b>ap_out</b>. */
+void
+router_get_pref_ipv6_orport(const routerinfo_t *router,
+ tor_addr_port_t *ap_out)
+{
+ tor_assert(ap_out != NULL);
+ tor_addr_copy(&ap_out->addr, &router->ipv6_addr);
+ ap_out->port = router->ipv6_orport;
+}
+
/** Load the contents of <b>filename</b>, find the last line starting with
* <b>end_line</b>, ensure that its timestamp is not more than 25 hours in
* the past or more than 1 hour in the future with respect to <b>now</b>,
diff --git a/src/or/router.h b/src/or/router.h
index f9d156cb0..d426b25da 100644
--- a/src/or/router.h
+++ b/src/or/router.h
@@ -30,6 +30,7 @@ crypto_pk_env_t *init_key_from_file(const char *fname, int generate,
int severity);
void v3_authority_check_key_expiry(void);
+int router_initialize_tls_context(void);
int init_keys(void);
int check_whether_orport_reachable(void);
@@ -39,6 +40,8 @@ void router_orport_found_reachable(void);
void router_dirport_found_reachable(void);
void router_perform_bandwidth_test(int num_circs, time_t now);
+int net_is_disabled(void);
+
int authdir_mode(const or_options_t *options);
int authdir_mode_v1(const or_options_t *options);
int authdir_mode_v2(const or_options_t *options);
@@ -82,6 +85,13 @@ int router_pick_published_address(const or_options_t *options, uint32_t *addr);
int router_rebuild_descriptor(int force);
int router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
crypto_pk_env_t *ident_key);
+void router_get_prim_orport(const routerinfo_t *router,
+ tor_addr_port_t *addr_port_out);
+void router_get_pref_orport(const routerinfo_t *router,
+ tor_addr_port_t *addr_port_out);
+void router_get_pref_ipv6_orport(const routerinfo_t *router,
+ tor_addr_port_t *addr_port_out);
+int router_ipv6_preferred(const routerinfo_t *router);
int extrainfo_dump_to_string(char **s, extrainfo_t *extrainfo,
crypto_pk_env_t *ident_key);
int is_legal_nickname(const char *s);
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index d97b978f4..689df99c5 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -3244,7 +3244,7 @@ router_set_status(const char *digest, int up)
log_debug(LD_DIR,"Marking router %s as %s.",
node_describe(node), up ? "up" : "down");
#endif
- if (!up && node_is_me(node) && !we_are_hibernating())
+ if (!up && node_is_me(node) && !net_is_disabled())
log_warn(LD_NET, "We just marked ourself as down. Are your external "
"addresses reachable?");
node->is_running = up;
@@ -4009,6 +4009,8 @@ signed_desc_digest_is_recognized(signed_descriptor_t *desc)
void
update_all_descriptor_downloads(time_t now)
{
+ if (get_options()->DisableNetwork)
+ return;
update_router_descriptor_downloads(now);
update_microdesc_downloads(now);
launch_dummy_descriptor_download_as_needed(now, get_options());
@@ -4021,6 +4023,8 @@ routerlist_retry_directory_downloads(time_t now)
{
router_reset_status_download_failures();
router_reset_descriptor_download_failures();
+ if (get_options()->DisableNetwork)
+ return;
update_networkstatus_downloads(now);
update_all_descriptor_downloads(now);
}
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 4ea7b964c..678b11971 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -64,6 +64,7 @@ typedef enum {
K_DIR_OPTIONS,
K_CLIENT_VERSIONS,
K_SERVER_VERSIONS,
+ K_OR_ADDRESS,
K_P,
K_R,
K_S,
@@ -286,6 +287,7 @@ static token_rule_t routerdesc_token_table[] = {
T01("family", K_FAMILY, ARGS, NO_OBJ ),
T01("caches-extra-info", K_CACHES_EXTRA_INFO, NO_ARGS, NO_OBJ ),
+ T0N("or-address", K_OR_ADDRESS, GE(1), NO_OBJ ),
T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
T1( "bandwidth", K_BANDWIDTH, GE(3), NO_OBJ ),
@@ -541,6 +543,7 @@ static int router_get_hashes_impl(const char *s, size_t s_len,
const char *start_str, const char *end_str,
char end_char);
static void token_clear(directory_token_t *tok);
+static smartlist_t *find_all_by_keyword(smartlist_t *s, directory_keyword k);
static smartlist_t *find_all_exitpolicy(smartlist_t *s);
static directory_token_t *_find_by_keyword(smartlist_t *s,
directory_keyword keyword,
@@ -1506,6 +1509,27 @@ router_parse_entry_from_string(const char *s, const char *end,
"older Tors.");
goto err;
}
+ {
+ smartlist_t *or_addresses = find_all_by_keyword(tokens, K_OR_ADDRESS);
+ if (or_addresses) {
+ SMARTLIST_FOREACH_BEGIN(or_addresses, directory_token_t *, t) {
+ tor_addr_t a;
+ maskbits_t bits;
+ uint16_t port_min, port_max;
+ /* XXXX Prop186 the full spec allows much more than this. */
+ if (tor_addr_parse_mask_ports(t->args[0], &a, &bits, &port_min,
+ &port_max) == AF_INET6 &&
+ bits == 128 &&
+ port_min == port_max) {
+ /* Okay, this is one we can understand. */
+ tor_addr_copy(&router->ipv6_addr, &a);
+ router->ipv6_orport = port_min;
+ break;
+ }
+ } SMARTLIST_FOREACH_END(t);
+ smartlist_free(or_addresses);
+ }
+ }
exit_policy_tokens = find_all_exitpolicy(tokens);
if (!smartlist_len(exit_policy_tokens)) {
log_warn(LD_DIR, "No exit policy tokens in descriptor.");
@@ -4134,6 +4158,20 @@ _find_by_keyword(smartlist_t *s, directory_keyword keyword,
return tok;
}
+/** DOCDOC */
+static smartlist_t *
+find_all_by_keyword(smartlist_t *s, directory_keyword k)
+{
+ smartlist_t *out = NULL;
+ SMARTLIST_FOREACH(s, directory_token_t *, t,
+ if (t->tp == k) {
+ if (!out)
+ out = smartlist_create();
+ smartlist_add(out, t);
+ });
+ return out;
+}
+
/** Return a newly allocated smartlist of all accept or reject tokens in
* <b>s</b>.
*/
diff --git a/src/or/transports.c b/src/or/transports.c
index 6e8200f40..eaaf1a3bf 100644
--- a/src/or/transports.c
+++ b/src/or/transports.c
@@ -12,12 +12,20 @@
#include "circuitbuild.h"
#include "transports.h"
#include "util.h"
+#include "router.h"
-static void set_managed_proxy_environment(char ***envp,
+#ifdef MS_WINDOWS
+static void set_managed_proxy_environment(LPVOID *envp,
const managed_proxy_t *mp);
+#else
+static int set_managed_proxy_environment(char ***envp,
+ const managed_proxy_t *mp);
+#endif
+
static INLINE int proxy_configuration_finished(const managed_proxy_t *mp);
-static void managed_proxy_destroy(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 void configure_proxy(managed_proxy_t *mp);
@@ -229,12 +237,10 @@ proxy_prepare_for_restart(managed_proxy_t *mp)
transport_t *t_tmp = NULL;
tor_assert(mp->conf_state == PT_PROTO_COMPLETED);
- tor_assert(mp->pid);
- /* kill the old obfsproxy process */
- tor_terminate_process(mp->pid);
- mp->pid = 0;
- fclose(mp->_stdout);
+ /* destroy the process handle and terminate the process. */
+ tor_process_handle_destroy(mp->process_handle, 1);
+ mp->process_handle = NULL;
/* destroy all its old transports. we no longer use them. */
SMARTLIST_FOREACH_BEGIN(mp->transports, const char *, t_name) {
@@ -256,52 +262,52 @@ proxy_prepare_for_restart(managed_proxy_t *mp)
static int
launch_managed_proxy(managed_proxy_t *mp)
{
- (void) mp;
- (void) set_managed_proxy_environment;
- return -1;
-#if 0
- /* XXXX023 we must reenable this code for managed proxies to work.
- * "All it needs" is revision to work with the new tor_spawn_background
- * API. */
- char **envp=NULL;
- int pid;
- process_handle_t proc;
- FILE *stdout_read = NULL;
- int stdout_pipe=-1, stderr_pipe=-1;
+ int retval;
+
+#ifdef MS_WINDOWS
+
+ LPVOID envp=NULL;
- /* prepare the environment variables for the managed proxy */
set_managed_proxy_environment(&envp, mp);
+ tor_assert(envp);
- pid = tor_spawn_background(mp->argv[0], (const char **)mp->argv,
- (const char **)envp, &proc);
- if (pid < 0) {
- log_warn(LD_GENERAL, "Managed proxy at '%s' failed at launch.",
- mp->argv[0]);
+ /* Passing NULL as lpApplicationName makes Windows search for the .exe */
+ retval = tor_spawn_background(NULL, (const char **)mp->argv, envp,
+ &mp->process_handle);
+
+ tor_free(envp);
+
+#else
+
+ char **envp=NULL;
+
+ /* prepare the environment variables for the managed proxy */
+ if (set_managed_proxy_environment(&envp, mp) < 0) {
+ log_warn(LD_GENERAL, "Could not setup the environment of "
+ "the managed proxy at '%s'.", mp->argv[0]);
+ free_execve_args(envp);
return -1;
}
+ retval = tor_spawn_background(mp->argv[0], (const char **)mp->argv,
+ (const char **)envp, &mp->process_handle);
+
/* free the memory allocated by set_managed_proxy_environment(). */
free_execve_args(envp);
- /* Set stdout/stderr pipes to be non-blocking */
-#ifdef _WIN32
- {
- u_long nonblocking = 1;
- ioctlsocket(stdout_pipe, FIONBIO, &nonblocking);
- }
-#else
- fcntl(stdout_pipe, F_SETFL, O_NONBLOCK);
#endif
- /* Open the buffered IO streams */
- stdout_read = fdopen(stdout_pipe, "r");
+ if (retval == PROCESS_STATUS_ERROR) {
+ log_warn(LD_GENERAL, "Managed proxy at '%s' failed at launch.",
+ mp->argv[0]);
+ return -1;
+ }
- log_info(LD_CONFIG, "Managed proxy has spawned at PID %d.", pid);
+ log_info(LD_CONFIG, "Managed proxy at '%s' has spawned with PID '%d'.",
+ mp->argv[0], tor_process_get_pid(mp->process_handle));
mp->conf_state = PT_PROTO_LAUNCHED;
- mp->_stdout = stdout_read;
- mp->pid = pid;
-#endif
+
return 0;
}
@@ -314,7 +320,8 @@ pt_configure_remaining_proxies(void)
log_debug(LD_CONFIG, "Configuring remaining managed proxies (%d)!",
unconfigured_proxies_n);
SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) {
- tor_assert(mp->conf_state != PT_PROTO_BROKEN);
+ tor_assert(mp->conf_state != PT_PROTO_BROKEN ||
+ mp->conf_state != PT_PROTO_FAILED_LAUNCH);
if (mp->got_hup) {
mp->got_hup = 0;
@@ -343,6 +350,65 @@ pt_configure_remaining_proxies(void)
} SMARTLIST_FOREACH_END(mp);
}
+#ifdef MS_WINDOWS
+
+/** Attempt to continue configuring managed proxy <b>mp</b>. */
+static void
+configure_proxy(managed_proxy_t *mp)
+{
+ int pos;
+ char stdout_buf[200];
+ smartlist_t *lines = NULL;
+
+ /* if we haven't launched the proxy yet, do it now */
+ if (mp->conf_state == PT_PROTO_INFANT) {
+ if (launch_managed_proxy(mp) < 0) { /* launch fail */
+ mp->conf_state = PT_PROTO_FAILED_LAUNCH;
+ handle_finished_proxy(mp);
+ }
+ return;
+ }
+
+ tor_assert(mp->conf_state != PT_PROTO_INFANT);
+ tor_assert(mp->process_handle);
+
+ pos = tor_read_all_handle(tor_process_get_stdout_pipe(mp->process_handle),
+ stdout_buf, sizeof(stdout_buf) - 1, NULL);
+ if (pos < 0) {
+ log_notice(LD_GENERAL, "Failed to read data from managed proxy");
+ mp->conf_state = PT_PROTO_BROKEN;
+ goto done;
+ }
+
+ if (pos == 0) /* proxy has nothing interesting to say. */
+ return;
+
+ /* End with a null even if there isn't a \r\n at the end */
+ /* TODO: What if this is a partial line? */
+ stdout_buf[pos] = '\0';
+
+ /* Split up the buffer */
+ lines = smartlist_create();
+ tor_split_lines(lines, stdout_buf, pos);
+
+ /* Handle lines. */
+ SMARTLIST_FOREACH_BEGIN(lines, const char *, line) {
+ handle_proxy_line(line, mp);
+ if (proxy_configuration_finished(mp))
+ goto done;
+ } SMARTLIST_FOREACH_END(line);
+
+ done:
+ /* if the proxy finished configuring, exit the loop. */
+ if (proxy_configuration_finished(mp))
+ handle_finished_proxy(mp);
+
+ if (lines)
+ smartlist_free(lines);
+}
+
+#else /* MS_WINDOWS */
+
/** Attempt to continue configuring managed proxy <b>mp</b>. */
static void
configure_proxy(managed_proxy_t *mp)
@@ -352,15 +418,19 @@ configure_proxy(managed_proxy_t *mp)
/* if we haven't launched the proxy yet, do it now */
if (mp->conf_state == PT_PROTO_INFANT) {
- launch_managed_proxy(mp);
+ if (launch_managed_proxy(mp) < 0) { /* launch fail */
+ mp->conf_state = PT_PROTO_FAILED_LAUNCH;
+ handle_finished_proxy(mp);
+ }
return;
}
tor_assert(mp->conf_state != PT_PROTO_INFANT);
+ tor_assert(mp->process_handle);
while (1) {
- r = get_string_from_pipe(mp->_stdout, stdout_buf,
- sizeof(stdout_buf) - 1);
+ r = get_string_from_pipe(tor_process_get_stdout_pipe(mp->process_handle),
+ stdout_buf, sizeof(stdout_buf) - 1);
if (r == IO_STREAM_OKAY) { /* got a line; handle it! */
handle_proxy_line((const char *)stdout_buf, mp);
@@ -382,6 +452,8 @@ configure_proxy(managed_proxy_t *mp)
}
}
+#endif /* MS_WINDOWS */
+
/** Register server managed proxy <b>mp</b> transports to state */
static void
register_server_proxy(managed_proxy_t *mp)
@@ -394,6 +466,10 @@ register_server_proxy(managed_proxy_t *mp)
tor_assert(mp->conf_state != PT_PROTO_COMPLETED);
SMARTLIST_FOREACH_BEGIN(mp->transports, transport_t *, t) {
save_transport_to_state(t->name, &t->addr, t->port);
+ /* LOG_WARN so that the bridge operator can easily find the
+ transport's port in the log file and send it to the users. */
+ log_warn(LD_GENERAL, "Registered server transport '%s' at '%s:%d'",
+ t->name, fmt_addr(&t->addr), (int)t->port);
smartlist_add(sm_tmp, tor_strdup(t->name));
} SMARTLIST_FOREACH_END(t);
@@ -453,7 +529,8 @@ register_proxy(managed_proxy_t *mp)
/** Free memory allocated by managed proxy <b>mp</b>. */
static void
-managed_proxy_destroy(managed_proxy_t *mp)
+managed_proxy_destroy(managed_proxy_t *mp,
+ int also_terminate_process)
{
if (mp->conf_state != PT_PROTO_COMPLETED)
SMARTLIST_FOREACH(mp->transports, transport_t *, t, transport_free(t));
@@ -470,15 +547,11 @@ managed_proxy_destroy(managed_proxy_t *mp)
/* remove it from the list of managed proxies */
smartlist_remove(managed_proxy_list, mp);
- /* close its stdout stream */
- if (mp->_stdout)
- fclose(mp->_stdout);
-
/* free the argv */
free_execve_args(mp->argv);
- if (mp->pid)
- tor_terminate_process(mp->pid);
+ tor_process_handle_destroy(mp->process_handle, also_terminate_process);
+ mp->process_handle = NULL;
tor_free(mp);
}
@@ -489,7 +562,10 @@ handle_finished_proxy(managed_proxy_t *mp)
{
switch (mp->conf_state) {
case PT_PROTO_BROKEN: /* if broken: */
- managed_proxy_destroy(mp); /* annihilate it. */
+ managed_proxy_destroy(mp, 1); /* annihilate it. */
+ break;
+ case PT_PROTO_FAILED_LAUNCH: /* if it failed before launching: */
+ managed_proxy_destroy(mp, 0); /* destroy it but don't terminate */
break;
case PT_PROTO_CONFIGURED: /* if configured correctly: */
register_proxy(mp); /* register its transports */
@@ -515,7 +591,8 @@ static INLINE int
proxy_configuration_finished(const managed_proxy_t *mp)
{
return (mp->conf_state == PT_PROTO_CONFIGURED ||
- mp->conf_state == PT_PROTO_BROKEN);
+ mp->conf_state == PT_PROTO_BROKEN ||
+ mp->conf_state == PT_PROTO_FAILED_LAUNCH);
}
/** This function is called when a proxy sends an {S,C}METHODS DONE message. */
@@ -537,7 +614,7 @@ handle_methods_done(const managed_proxy_t *mp)
void
handle_proxy_line(const char *line, managed_proxy_t *mp)
{
- log_debug(LD_GENERAL, "Got a line from managed proxy: %s\n", line);
+ log_debug(LD_GENERAL, "Got a line from managed proxy: %s", line);
if (strlen(line) < SMALLEST_MANAGED_LINE_SIZE) {
log_warn(LD_GENERAL, "Managed proxy configuration line is too small. "
@@ -614,14 +691,16 @@ handle_proxy_line(const char *line, managed_proxy_t *mp)
return;
} else if (!strcmpstart(line, SPAWN_ERROR_MESSAGE)) {
log_warn(LD_GENERAL, "Could not launch managed proxy executable!");
- goto err;
+ mp->conf_state = PT_PROTO_FAILED_LAUNCH;
+ return;
}
log_warn(LD_CONFIG, "Unknown line received by managed proxy. (%s)", line);
err:
mp->conf_state = PT_PROTO_BROKEN;
- return;
+ log_warn(LD_CONFIG, "Managed proxy at '%s' failed the configuration protocol"
+ " and will be destroyed.", mp->argv ? mp->argv[0] : "");
}
/** Parses an ENV-ERROR <b>line</b> and warns the user accordingly. */
@@ -858,8 +937,115 @@ get_bindaddr_for_proxy(const managed_proxy_t *mp)
return bindaddr;
}
-/** Prepare the <b>envp</b> of managed proxy <b>mp</b> */
+#ifdef MS_WINDOWS
+
+/** Prepare the environment <b>envp</b> of managed proxy <b>mp</b>.
+ * <b>envp</b> is allocated on the heap and should be freed by the
+ * caller after its use. */
static void
+set_managed_proxy_environment(LPVOID *envp, const managed_proxy_t *mp)
+{
+ const or_options_t *options = get_options();
+ extern char **environ;
+
+ LPVOID tmp=NULL;
+
+ char *state_tmp=NULL;
+ char *state_env=NULL;
+ char *transports_to_launch=NULL;
+ char *transports_env=NULL;
+ char *bindaddr_tmp=NULL;
+ char *bindaddr_env=NULL;
+ char *orport_env=NULL;
+
+ char version_env[31]; /* XXX temp */
+ char extended_env[43]; /* XXX temp */
+
+ int env_size = 0;
+
+ /* A smartlist carrying all the env. variables that the managed
+ proxy should inherit. */
+ smartlist_t *envs = smartlist_create();
+
+ /* Copy the whole environment of the Tor process.
+ It should also copy PATH and HOME of the Tor process.*/
+ char **environ_tmp = environ;
+ while (*environ_tmp)
+ smartlist_add(envs, *environ_tmp++);
+
+ /* Create the TOR_PT_* environment variables. */
+ state_tmp = get_datadir_fname("pt_state/"); /* XXX temp */
+ tor_asprintf(&state_env, "TOR_PT_STATE_LOCATION=%s", state_tmp);
+
+ strcpy(version_env, "TOR_PT_MANAGED_TRANSPORT_VER=1");
+
+ transports_to_launch =
+ smartlist_join_strings(mp->transports_to_launch, ",", 0, NULL);
+
+ tor_asprintf(&transports_env,
+ mp->is_server ?
+ "TOR_PT_SERVER_TRANSPORTS=%s" : "TOR_PT_CLIENT_TRANSPORTS=%s",
+ transports_to_launch);
+
+ smartlist_add(envs, state_env);
+ smartlist_add(envs, version_env);
+ smartlist_add(envs, transports_env);
+
+ if (mp->is_server) {
+ tor_asprintf(&orport_env, "TOR_PT_ORPORT=127.0.0.1:%d", options->ORPort);
+
+ bindaddr_tmp = get_bindaddr_for_proxy(mp);
+ tor_asprintf(&bindaddr_env, "TOR_PT_SERVER_BINDADDR=%s", bindaddr_tmp);
+
+ strcpy(extended_env, "TOR_PT_EXTENDED_SERVER_PORT=127.0.0.1:4200");
+
+ smartlist_add(envs, orport_env);
+ smartlist_add(envs, extended_env);
+ smartlist_add(envs, bindaddr_env);
+ }
+
+ /* It seems like some versions of Windows need a sorted lpEnvironment
+ block. */
+ smartlist_sort_strings(envs);
+
+ /* An environment block consists of a null-terminated block of
+ null-terminated strings: */
+
+ /* Calculate the block's size. */
+ SMARTLIST_FOREACH(envs, const char *, s,
+ env_size += strlen(s) + 1);
+ env_size += 1; /* space for last NUL */
+
+ *envp = tor_malloc(env_size);
+ tmp = *envp;
+
+ /* Create the block. */
+ SMARTLIST_FOREACH_BEGIN(envs, const char *, s) {
+ memcpy(tmp, s, strlen(s)); /* copy the env. variable string */
+ tmp += strlen(s);
+ memset(tmp, '\0', 1); /* append NUL at the end of the string */
+ tmp += 1;
+ } SMARTLIST_FOREACH_END(s);
+ memset(tmp, '\0', 1); /* last NUL */
+
+ /* Finally, free the whole mess. */
+ tor_free(state_tmp);
+ tor_free(state_env);
+ tor_free(transports_to_launch);
+ tor_free(transports_env);
+ tor_free(bindaddr_tmp);
+ tor_free(bindaddr_env);
+ tor_free(orport_env);
+
+ smartlist_free(envs);
+}
+
+#else /* MS_WINDOWS */
+
+/** Prepare the environment <b>envp</b> of managed proxy <b>mp</b>.
+ * <b>envp</b> is allocated on the heap and should be freed by the
+ * caller after its use. */
+static int
set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp)
{
const or_options_t *options = get_options();
@@ -867,7 +1053,10 @@ set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp)
char *state_loc=NULL;
char *transports_to_launch=NULL;
char *bindaddr=NULL;
+ char *home_env=NULL;
+ char *path_env=NULL;
+ int r = -1;
int n_envs = mp->is_server ? ENVIRON_SIZE_SERVER : ENVIRON_SIZE_CLIENT;
/* allocate enough space for our env. vars and a NULL pointer */
@@ -878,15 +1067,21 @@ set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp)
transports_to_launch =
smartlist_join_strings(mp->transports_to_launch, ",", 0, NULL);
- tor_asprintf(tmp++, "HOME=%s", getenv("HOME"));
- tor_asprintf(tmp++, "PATH=%s", getenv("PATH"));
+ home_env = getenv("HOME");
+ path_env = getenv("PATH");
+ if (!home_env || !path_env)
+ goto done;
+
+ tor_asprintf(tmp++, "HOME=%s", home_env);
+ tor_asprintf(tmp++, "PATH=%s", path_env);
tor_asprintf(tmp++, "TOR_PT_STATE_LOCATION=%s", state_loc);
tor_asprintf(tmp++, "TOR_PT_MANAGED_TRANSPORT_VER=1"); /* temp */
if (mp->is_server) {
bindaddr = get_bindaddr_for_proxy(mp);
/* XXX temp */
- tor_asprintf(tmp++, "TOR_PT_ORPORT=127.0.0.1:%d", options->ORPort);
+ tor_asprintf(tmp++, "TOR_PT_ORPORT=127.0.0.1:%d",
+ router_get_advertised_or_port(options));
tor_asprintf(tmp++, "TOR_PT_SERVER_BINDADDR=%s", bindaddr);
tor_asprintf(tmp++, "TOR_PT_SERVER_TRANSPORTS=%s", transports_to_launch);
/* XXX temp*/
@@ -896,11 +1091,18 @@ set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp)
}
*tmp = NULL;
+ r = 0;
+
+ done:
tor_free(state_loc);
tor_free(transports_to_launch);
tor_free(bindaddr);
+
+ return r;
}
+#endif /* MS_WINDOWS */
+
/** Create and return a new managed proxy for <b>transport</b> using
* <b>proxy_argv</b>. If <b>is_server</b> is true, it's a server
* managed proxy. */
@@ -995,9 +1197,9 @@ pt_prepare_proxy_list_for_config_read(void)
SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) {
/* Destroy unconfigured proxies. */
if (mp->conf_state != PT_PROTO_COMPLETED) {
- managed_proxy_destroy(mp);
- unconfigured_proxies_n--;
- continue;
+ managed_proxy_destroy(mp, 1);
+ unconfigured_proxies_n--;
+ continue;
}
tor_assert(mp->conf_state == PT_PROTO_COMPLETED);
@@ -1024,7 +1226,7 @@ sweep_proxy_list(void)
SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) {
if (mp->marked_for_removal) {
SMARTLIST_DEL_CURRENT(managed_proxy_list, mp);
- managed_proxy_destroy(mp);
+ managed_proxy_destroy(mp, 1);
}
} SMARTLIST_FOREACH_END(mp);
}
@@ -1039,7 +1241,7 @@ pt_free_all(void)
free them. Otherwise, it hasn't registered its transports yet
and we should free them here. */
SMARTLIST_FOREACH(managed_proxy_list, managed_proxy_t *, mp,
- managed_proxy_destroy(mp));
+ managed_proxy_destroy(mp, 1));
smartlist_free(managed_proxy_list);
managed_proxy_list=NULL;
diff --git a/src/or/transports.h b/src/or/transports.h
index 4a9338759..314af2b3a 100644
--- a/src/or/transports.h
+++ b/src/or/transports.h
@@ -36,7 +36,8 @@ enum pt_proto_state {
PT_PROTO_ACCEPTING_METHODS, /* accepting methods */
PT_PROTO_CONFIGURED, /* configured successfully */
PT_PROTO_COMPLETED, /* configure and registered its transports */
- PT_PROTO_BROKEN
+ PT_PROTO_BROKEN, /* broke during the protocol */
+ PT_PROTO_FAILED_LAUNCH /* failed while launching */
};
/** Structure containing information of a managed proxy. */
@@ -47,8 +48,8 @@ typedef struct {
int is_server; /* is it a server proxy? */
- FILE *_stdout; /* a stream to its stdout
- (closed in managed_proxy_destroy()) */
+ /* A pointer to the process handle of this managed proxy. */
+ process_handle_t *process_handle;
int pid; /* The Process ID this managed proxy is using. */
diff --git a/src/test/Makefile.am b/src/test/Makefile.am
index ffe1f942e..73de30071 100644
--- a/src/test/Makefile.am
+++ b/src/test/Makefile.am
@@ -21,6 +21,7 @@ test_SOURCES = \
test_microdesc.c \
test_pt.c \
test_util.c \
+ test_config.c \
tinytest.c
bench_SOURCES = \
diff --git a/src/test/test.c b/src/test/test.c
index d4edf1484..5fcc31c47 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -1844,6 +1844,7 @@ extern struct testcase_t util_tests[];
extern struct testcase_t dir_tests[];
extern struct testcase_t microdesc_tests[];
extern struct testcase_t pt_tests[];
+extern struct testcase_t config_tests[];
static struct testgroup_t testgroups[] = {
{ "", test_array },
@@ -1855,6 +1856,7 @@ static struct testgroup_t testgroups[] = {
{ "dir/", dir_tests },
{ "dir/md/", microdesc_tests },
{ "pt/", pt_tests },
+ { "config/", config_tests },
END_OF_GROUPS
};
@@ -1907,6 +1909,7 @@ main(int c, const char **v)
printf("Can't initialize crypto subsystem; exiting.\n");
return 1;
}
+ crypto_set_tls_dh_prime(NULL);
rep_hist_init();
network_init();
setup_directory();
diff --git a/src/test/test_config.c b/src/test/test_config.c
new file mode 100644
index 000000000..4ce0fa845
--- /dev/null
+++ b/src/test/test_config.c
@@ -0,0 +1,170 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2010, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "or.h"
+#include "config.h"
+#include "connection_edge.h"
+#include "test.h"
+
+static void
+test_config_addressmap(void *arg)
+{
+ char buf[1024];
+ char address[256];
+ time_t expires = TIME_MAX;
+ (void)arg;
+
+ strlcpy(buf, "MapAddress .invalidwildcard.com *.torserver.exit\n" // invalid
+ "MapAddress *invalidasterisk.com *.torserver.exit\n" // invalid
+ "MapAddress *.google.com *.torserver.exit\n"
+ "MapAddress *.yahoo.com *.google.com.torserver.exit\n"
+ "MapAddress *.cn.com www.cnn.com\n"
+ "MapAddress *.cnn.com www.cnn.com\n"
+ "MapAddress ex.com www.cnn.com\n"
+ "MapAddress ey.com *.cnn.com\n"
+ "MapAddress www.torproject.org 1.1.1.1\n"
+ "MapAddress other.torproject.org "
+ "this.torproject.org.otherserver.exit\n"
+ "MapAddress test.torproject.org 2.2.2.2\n"
+ "MapAddress www.google.com 3.3.3.3\n"
+ "MapAddress www.example.org 4.4.4.4\n"
+ "MapAddress 4.4.4.4 7.7.7.7\n"
+ "MapAddress 4.4.4.4 5.5.5.5\n"
+ "MapAddress www.infiniteloop.org 6.6.6.6\n"
+ "MapAddress 6.6.6.6 www.infiniteloop.org\n"
+ , sizeof(buf));
+
+ config_get_lines(buf, &(get_options_mutable()->AddressMap), 0);
+ config_register_addressmaps(get_options());
+
+ /* MapAddress .invalidwildcard.com .torserver.exit - no match */
+ strlcpy(address, "www.invalidwildcard.com", sizeof(address));
+ test_assert(!addressmap_rewrite(address, sizeof(address), &expires));
+
+ /* MapAddress *invalidasterisk.com .torserver.exit - no match */
+ strlcpy(address, "www.invalidasterisk.com", sizeof(address));
+ test_assert(!addressmap_rewrite(address, sizeof(address), &expires));
+
+ /* Where no mapping for FQDN match on top-level domain */
+ /* MapAddress .google.com .torserver.exit */
+ strlcpy(address, "reader.google.com", sizeof(address));
+ test_assert(addressmap_rewrite(address, sizeof(address), &expires));
+ test_streq(address, "reader.torserver.exit");
+
+ /* MapAddress *.yahoo.com *.google.com.torserver.exit */
+ strlcpy(address, "reader.yahoo.com", sizeof(address));
+ test_assert(addressmap_rewrite(address, sizeof(address), &expires));
+ test_streq(address, "reader.google.com.torserver.exit");
+
+ /*MapAddress *.cnn.com www.cnn.com */
+ strlcpy(address, "cnn.com", sizeof(address));
+ test_assert(addressmap_rewrite(address, sizeof(address), &expires));
+ test_streq(address, "www.cnn.com");
+
+ /* MapAddress .cn.com www.cnn.com */
+ strlcpy(address, "www.cn.com", sizeof(address));
+ test_assert(addressmap_rewrite(address, sizeof(address), &expires));
+ test_streq(address, "www.cnn.com");
+
+ /* MapAddress ex.com www.cnn.com - no match */
+ strlcpy(address, "www.ex.com", sizeof(address));
+ test_assert(!addressmap_rewrite(address, sizeof(address), &expires));
+
+ /* MapAddress ey.com *.cnn.com - invalid expression */
+ strlcpy(address, "ey.com", sizeof(address));
+ test_assert(!addressmap_rewrite(address, sizeof(address), &expires));
+
+ /* Where mapping for FQDN match on FQDN */
+ strlcpy(address, "www.google.com", sizeof(address));
+ test_assert(addressmap_rewrite(address, sizeof(address), &expires));
+ test_streq(address, "3.3.3.3");
+
+ strlcpy(address, "www.torproject.org", sizeof(address));
+ test_assert(addressmap_rewrite(address, sizeof(address), &expires));
+ test_streq(address, "1.1.1.1");
+
+ strlcpy(address, "other.torproject.org", sizeof(address));
+ test_assert(addressmap_rewrite(address, sizeof(address), &expires));
+ test_streq(address, "this.torproject.org.otherserver.exit");
+
+ strlcpy(address, "test.torproject.org", sizeof(address));
+ test_assert(addressmap_rewrite(address, sizeof(address), &expires));
+ test_streq(address, "2.2.2.2");
+
+ /* Test a chain of address mappings and the order in which they were added:
+ "MapAddress www.example.org 4.4.4.4"
+ "MapAddress 4.4.4.4 7.7.7.7"
+ "MapAddress 4.4.4.4 5.5.5.5"
+ */
+ strlcpy(address, "www.example.org", sizeof(address));
+ test_assert(addressmap_rewrite(address, sizeof(address), &expires));
+ test_streq(address, "5.5.5.5");
+
+ /* Test infinite address mapping results in no change */
+ strlcpy(address, "www.infiniteloop.org", sizeof(address));
+ test_assert(addressmap_rewrite(address, sizeof(address), &expires));
+ test_streq(address, "www.infiniteloop.org");
+
+ /* Test we don't find false positives */
+ strlcpy(address, "www.example.com", sizeof(address));
+ test_assert(!addressmap_rewrite(address, sizeof(address), &expires));
+
+ /* Test top-level-domain matching a bit harder */
+ addressmap_clear_configured();
+ strlcpy(buf, "MapAddress *.com *.torserver.exit\n"
+ "MapAddress *.torproject.org 1.1.1.1\n"
+ "MapAddress *.net 2.2.2.2\n"
+ , sizeof(buf));
+ config_get_lines(buf, &(get_options_mutable()->AddressMap), 0);
+ config_register_addressmaps(get_options());
+
+ strlcpy(address, "www.abc.com", sizeof(address));
+ test_assert(addressmap_rewrite(address, sizeof(address), &expires));
+ test_streq(address, "www.abc.torserver.exit");
+
+ strlcpy(address, "www.def.com", sizeof(address));
+ test_assert(addressmap_rewrite(address, sizeof(address), &expires));
+ test_streq(address, "www.def.torserver.exit");
+
+ strlcpy(address, "www.torproject.org", sizeof(address));
+ test_assert(addressmap_rewrite(address, sizeof(address), &expires));
+ test_streq(address, "1.1.1.1");
+
+ strlcpy(address, "test.torproject.org", sizeof(address));
+ test_assert(addressmap_rewrite(address, sizeof(address), &expires));
+ test_streq(address, "1.1.1.1");
+
+ strlcpy(address, "torproject.net", sizeof(address));
+ test_assert(addressmap_rewrite(address, sizeof(address), &expires));
+ test_streq(address, "2.2.2.2");
+
+ /* We don't support '*' as a mapping directive */
+ addressmap_clear_configured();
+ strlcpy(buf, "MapAddress * *.torserver.exit\n", sizeof(buf));
+ config_get_lines(buf, &(get_options_mutable()->AddressMap), 0);
+ config_register_addressmaps(get_options());
+
+ strlcpy(address, "www.abc.com", sizeof(address));
+ test_assert(!addressmap_rewrite(address, sizeof(address), &expires));
+
+ strlcpy(address, "www.def.net", sizeof(address));
+ test_assert(!addressmap_rewrite(address, sizeof(address), &expires));
+
+ strlcpy(address, "www.torproject.org", sizeof(address));
+ test_assert(!addressmap_rewrite(address, sizeof(address), &expires));
+
+done:
+ ;
+}
+
+#define CONFIG_TEST(name, flags) \
+ { #name, test_config_ ## name, flags, NULL, NULL }
+
+struct testcase_t config_tests[] = {
+ CONFIG_TEST(addressmap, 0),
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c
index 1b338a29a..cad8c2f55 100644
--- a/src/test/test_crypto.c
+++ b/src/test/test_crypto.c
@@ -7,6 +7,7 @@
#define CRYPTO_PRIVATE
#include "or.h"
#include "test.h"
+#include "aes.h"
/** Run unit tests for Diffie-Hellman functionality. */
static void
@@ -95,13 +96,16 @@ test_crypto_rng(void)
/** Run unit tests for our AES functionality */
static void
-test_crypto_aes(void)
+test_crypto_aes(void *arg)
{
char *data1 = NULL, *data2 = NULL, *data3 = NULL;
crypto_cipher_env_t *env1 = NULL, *env2 = NULL;
int i, j;
char *mem_op_hex_tmp=NULL;
+ int use_evp = !strcmp(arg,"evp");
+ evaluate_evp_for_aes(use_evp);
+
data1 = tor_malloc(1024);
data2 = tor_malloc(1024);
data3 = tor_malloc(1024);
@@ -670,7 +674,7 @@ test_crypto_s2k(void)
/** Test AES-CTR encryption and decryption with IV. */
static void
-test_crypto_aes_iv(void)
+test_crypto_aes_iv(void *arg)
{
crypto_cipher_env_t *cipher;
char *plain, *encrypted1, *encrypted2, *decrypted1, *decrypted2;
@@ -678,6 +682,9 @@ test_crypto_aes_iv(void)
char key1[16], key2[16];
ssize_t encrypted_size, decrypted_size;
+ int use_evp = !strcmp(arg,"evp");
+ evaluate_evp_for_aes(use_evp);
+
plain = tor_malloc(4095);
encrypted1 = tor_malloc(4095 + 1 + 16);
encrypted2 = tor_malloc(4095 + 1 + 16);
@@ -851,18 +858,36 @@ test_crypto_base32_decode(void)
;
}
+static void *
+pass_data_setup_fn(const struct testcase_t *testcase)
+{
+ return testcase->setup_data;
+}
+static int
+pass_data_cleanup_fn(const struct testcase_t *testcase, void *ptr)
+{
+ (void)ptr;
+ (void)testcase;
+ return 1;
+}
+static const struct testcase_setup_t pass_data = {
+ pass_data_setup_fn, pass_data_cleanup_fn
+};
+
#define CRYPTO_LEGACY(name) \
{ #name, legacy_test_helper, 0, &legacy_setup, test_crypto_ ## name }
struct testcase_t crypto_tests[] = {
CRYPTO_LEGACY(formats),
CRYPTO_LEGACY(rng),
- CRYPTO_LEGACY(aes),
+ { "aes_AES", test_crypto_aes, TT_FORK, &pass_data, (void*)"aes" },
+ { "aes_EVP", test_crypto_aes, TT_FORK, &pass_data, (void*)"evp" },
CRYPTO_LEGACY(sha),
CRYPTO_LEGACY(pk),
CRYPTO_LEGACY(dh),
CRYPTO_LEGACY(s2k),
- CRYPTO_LEGACY(aes_iv),
+ { "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"aes" },
+ { "aes_iv_EVP", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"evp" },
CRYPTO_LEGACY(base32_decode),
END_OF_TESTCASES
};
diff --git a/src/test/test_dir.c b/src/test/test_dir.c
index 205d7b577..046a1f25b 100644
--- a/src/test/test_dir.c
+++ b/src/test/test_dir.c
@@ -96,6 +96,8 @@ test_dir_formats(void)
r1->cache_info.published_on = 0;
r1->or_port = 9000;
r1->dir_port = 9003;
+ tor_addr_parse(&r1->ipv6_addr, "1:2:3:4::");
+ r1->ipv6_orport = 9999;
r1->onion_pkey = crypto_pk_dup_key(pk1);
r1->identity_pkey = crypto_pk_dup_key(pk2);
r1->bandwidthrate = 1000;
@@ -141,6 +143,7 @@ test_dir_formats(void)
test_assert(router_dump_router_to_string(buf, 2048, r1, pk2)>0);
strlcpy(buf2, "router Magri 18.244.0.1 9000 0 9003\n"
+ "or-address [1:2:3:4::]:9999\n"
"platform Tor "VERSION" on ", sizeof(buf2));
strlcat(buf2, get_uname(), sizeof(buf2));
strlcat(buf2, "\n"
@@ -620,13 +623,81 @@ test_dir_param_voting(void)
test_eq(0, networkstatus_get_param(&vote4, "foobar", 0, -100, 8));
smartlist_add(votes, &vote1);
+
+ /* Do the first tests without adding all the other votes, for
+ * networks without many dirauths. */
+
+ res = dirvote_compute_params(votes, 11, 6);
+ test_streq(res, "ab=90 abcd=20 cw=50 x-yz=-99");
+ tor_free(res);
+
+ res = dirvote_compute_params(votes, 12, 2);
+ test_streq(res, "");
+ tor_free(res);
+
+ res = dirvote_compute_params(votes, 12, 1);
+ test_streq(res, "ab=90 abcd=20 cw=50 x-yz=-99");
+ tor_free(res);
+
smartlist_add(votes, &vote2);
+
+ res = dirvote_compute_params(votes, 11, 2);
+ test_streq(res, "ab=27 abcd=20 cw=5 x-yz=-99");
+ tor_free(res);
+
+ res = dirvote_compute_params(votes, 12, 2);
+ test_streq(res, "ab=27 cw=5 x-yz=-99");
+ tor_free(res);
+
+ res = dirvote_compute_params(votes, 12, 3);
+ test_streq(res, "ab=27 cw=5 x-yz=-99");
+ tor_free(res);
+
+ res = dirvote_compute_params(votes, 12, 6);
+ test_streq(res, "");
+ tor_free(res);
+
smartlist_add(votes, &vote3);
+
+ res = dirvote_compute_params(votes, 11, 3);
+ test_streq(res, "ab=27 abcd=20 c=60 cw=50 x-yz=-9 zzzzz=101");
+ tor_free(res);
+
+ res = dirvote_compute_params(votes, 12, 3);
+ test_streq(res, "ab=27 abcd=20 cw=50 x-yz=-9");
+ tor_free(res);
+
+ res = dirvote_compute_params(votes, 12, 5);
+ test_streq(res, "cw=50 x-yz=-9");
+ tor_free(res);
+
+ res = dirvote_compute_params(votes, 12, 9);
+ test_streq(res, "cw=50 x-yz=-9");
+ tor_free(res);
+
smartlist_add(votes, &vote4);
- res = dirvote_compute_params(votes);
- test_streq(res,
- "ab=90 abcd=20 c=1 cw=50 x-yz=-9 zzzzz=101");
+ res = dirvote_compute_params(votes, 11, 4);
+ test_streq(res, "ab=90 abcd=20 c=1 cw=50 x-yz=-9 zzzzz=101");
+ tor_free(res);
+
+ res = dirvote_compute_params(votes, 12, 4);
+ test_streq(res, "ab=90 abcd=20 cw=50 x-yz=-9");
+ tor_free(res);
+
+ res = dirvote_compute_params(votes, 12, 5);
+ test_streq(res, "ab=90 abcd=20 cw=50 x-yz=-9");
+ tor_free(res);
+
+ /* Test that the special-cased "at least three dirauths voted for
+ * this param" logic works as expected. */
+ res = dirvote_compute_params(votes, 12, 6);
+ test_streq(res, "ab=90 abcd=20 cw=50 x-yz=-9");
+ tor_free(res);
+
+ res = dirvote_compute_params(votes, 12, 10);
+ test_streq(res, "ab=90 abcd=20 cw=50 x-yz=-9");
+ tor_free(res);
done:
tor_free(res);
@@ -1049,7 +1120,7 @@ test_dir_v3_networkstatus(void)
"Running:Stable:V2Dir:Valid");
tor_free(cp);
cp = smartlist_join_strings(con->net_params, ":", 0, NULL);
- test_streq(cp, "bar=2000000000:circuitwindow=80:foo=660");
+ test_streq(cp, "circuitwindow=80:foo=660");
tor_free(cp);
test_eq(4, smartlist_len(con->voters)); /*3 voters, 1 legacy key.*/
diff --git a/src/test/test_pt.c b/src/test/test_pt.c
index f97b21fa0..45f441106 100644
--- a/src/test/test_pt.c
+++ b/src/test/test_pt.c
@@ -91,7 +91,7 @@ test_pt_protocol(void)
{
char line[200];
- managed_proxy_t *mp = tor_malloc(sizeof(managed_proxy_t));
+ managed_proxy_t *mp = tor_malloc_zero(sizeof(managed_proxy_t));
mp->conf_state = PT_PROTO_LAUNCHED;
mp->transports = smartlist_create();
diff --git a/src/test/test_util.c b/src/test/test_util.c
index 64bf52e7b..c762a8d7b 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -1388,27 +1388,29 @@ run_util_spawn_background(const char *argv[], const char *expected_out,
{
int retval, exit_code;
ssize_t pos;
- process_handle_t process_handle;
+ process_handle_t *process_handle=NULL;
char stdout_buf[100], stderr_buf[100];
+ int status;
/* Start the program */
#ifdef MS_WINDOWS
- tor_spawn_background(NULL, argv, NULL, &process_handle);
+ status = tor_spawn_background(NULL, argv, NULL, &process_handle);
#else
- tor_spawn_background(argv[0], argv, NULL, &process_handle);
+ status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
#endif
- tt_int_op(process_handle.status, ==, expected_status);
-
- /* If the process failed to start, don't bother continuing */
- if (process_handle.status == PROCESS_STATUS_ERROR)
+ tt_int_op(status, ==, expected_status);
+ if (status == PROCESS_STATUS_ERROR)
return;
- tt_int_op(process_handle.stdout_pipe, >, 0);
- tt_int_op(process_handle.stderr_pipe, >, 0);
+ tt_assert(process_handle != NULL);
+ tt_int_op(process_handle->status, ==, expected_status);
+
+ tt_int_op(process_handle->stdout_pipe, >, 0);
+ tt_int_op(process_handle->stderr_pipe, >, 0);
/* Check stdout */
- pos = tor_read_all_from_process_stdout(&process_handle, stdout_buf,
+ pos = tor_read_all_from_process_stdout(process_handle, stdout_buf,
sizeof(stdout_buf) - 1);
tt_assert(pos >= 0);
stdout_buf[pos] = '\0';
@@ -1422,7 +1424,7 @@ run_util_spawn_background(const char *argv[], const char *expected_out,
// TODO: Make test-child exit with something other than 0
/* Check stderr */
- pos = tor_read_all_from_process_stderr(&process_handle, stderr_buf,
+ pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
sizeof(stderr_buf) - 1);
tt_assert(pos >= 0);
stderr_buf[pos] = '\0';
@@ -1430,7 +1432,8 @@ run_util_spawn_background(const char *argv[], const char *expected_out,
tt_int_op(pos, ==, strlen(expected_err));
done:
- ;
+ if (process_handle)
+ tor_process_handle_destroy(process_handle, 1);
}
/** Check that we can launch a process and read the output */
@@ -1489,7 +1492,8 @@ test_util_spawn_background_partial_read(void *ptr)
int retval, exit_code;
ssize_t pos = -1;
- process_handle_t process_handle;
+ process_handle_t *process_handle=NULL;
+ int status;
char stdout_buf[100], stderr_buf[100];
#ifdef MS_WINDOWS
const char *argv[] = {"test-child.exe", "--test", NULL};
@@ -1510,21 +1514,23 @@ test_util_spawn_background_partial_read(void *ptr)
/* Start the program */
#ifdef MS_WINDOWS
- tor_spawn_background(NULL, argv, NULL, &process_handle);
+ status = tor_spawn_background(NULL, argv, NULL, &process_handle);
#else
- tor_spawn_background(argv[0], argv, NULL, &process_handle);
+ status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
#endif
- tt_int_op(process_handle.status, ==, expected_status);
+ tt_int_op(status, ==, expected_status);
+ tt_assert(process_handle);
+ tt_int_op(process_handle->status, ==, expected_status);
/* Check stdout */
- for (expected_out_ctr =0; expected_out[expected_out_ctr] != NULL;) {
+ for (expected_out_ctr = 0; expected_out[expected_out_ctr] != NULL;) {
#ifdef MS_WINDOWS
- pos = tor_read_all_handle(process_handle.stdout_pipe, stdout_buf,
+ pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
sizeof(stdout_buf) - 1, NULL);
#else
/* Check that we didn't read the end of file last time */
tt_assert(!eof);
- pos = tor_read_all_handle(process_handle.stdout_handle, stdout_buf,
+ pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf,
sizeof(stdout_buf) - 1, NULL, &eof);
#endif
log_info(LD_GENERAL, "tor_read_all_handle() returned %d", (int)pos);
@@ -1542,16 +1548,16 @@ test_util_spawn_background_partial_read(void *ptr)
/* The process should have exited without writing more */
#ifdef MS_WINDOWS
- pos = tor_read_all_handle(process_handle.stdout_pipe, stdout_buf,
+ pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
sizeof(stdout_buf) - 1,
- &process_handle);
+ process_handle);
tt_int_op(pos, ==, 0);
#else
if (!eof) {
/* We should have got all the data, but maybe not the EOF flag */
- pos = tor_read_all_handle(process_handle.stdout_handle, stdout_buf,
+ pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf,
sizeof(stdout_buf) - 1,
- &process_handle, &eof);
+ process_handle, &eof);
tt_int_op(pos, ==, 0);
tt_assert(eof);
}
@@ -1566,7 +1572,7 @@ test_util_spawn_background_partial_read(void *ptr)
// TODO: Make test-child exit with something other than 0
/* Check stderr */
- pos = tor_read_all_from_process_stderr(&process_handle, stderr_buf,
+ pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
sizeof(stderr_buf) - 1);
tt_assert(pos >= 0);
stderr_buf[pos] = '\0';
@@ -1574,7 +1580,7 @@ test_util_spawn_background_partial_read(void *ptr)
tt_int_op(pos, ==, strlen(expected_err));
done:
- ;
+ tor_process_handle_destroy(process_handle, 1);
}
/**
@@ -1654,7 +1660,7 @@ test_util_split_lines(void *ptr)
int i, j;
char *orig_line;
- smartlist_t *sl;
+ smartlist_t *sl=NULL;
(void)ptr;
@@ -1683,10 +1689,12 @@ test_util_split_lines(void *ptr)
tt_assert(tests[i].split_line[j] == NULL);
tor_free(orig_line);
smartlist_free(sl);
+ sl = NULL;
}
done:
- ;
+ tor_free(orig_line);
+ smartlist_free(sl);
}
static void
diff --git a/src/tools/tor-gencert.c b/src/tools/tor-gencert.c
index 974a58bec..57f82b1bc 100644
--- a/src/tools/tor-gencert.c
+++ b/src/tools/tor-gencert.c
@@ -153,7 +153,7 @@ parse_commandline(int argc, char **argv)
}
months_lifetime = atoi(argv[++i]);
if (months_lifetime > 24 || months_lifetime < 0) {
- fprintf(stderr, "Lifetime (in months) was out of range.");
+ fprintf(stderr, "Lifetime (in months) was out of range.\n");
return 1;
}
} else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--reuse")) {