summaryrefslogtreecommitdiff
path: root/src/or
diff options
context:
space:
mode:
Diffstat (limited to 'src/or')
-rw-r--r--src/or/addressmap.c11
-rw-r--r--src/or/addressmap.h2
-rw-r--r--src/or/buffers.c77
-rw-r--r--src/or/buffers.h4
-rw-r--r--src/or/channel.c55
-rw-r--r--src/or/channel.h8
-rw-r--r--src/or/channeltls.c53
-rw-r--r--src/or/channeltls.h2
-rw-r--r--src/or/circpathbias.c11
-rw-r--r--src/or/circpathbias.h2
-rw-r--r--src/or/circuitbuild.c92
-rw-r--r--src/or/circuitbuild.h3
-rw-r--r--src/or/circuitlist.c134
-rw-r--r--src/or/circuitlist.h4
-rw-r--r--src/or/circuitmux.c22
-rw-r--r--src/or/circuitmux.h2
-rw-r--r--src/or/circuitmux_ewma.c10
-rw-r--r--src/or/circuitmux_ewma.h2
-rw-r--r--src/or/circuitstats.c9
-rw-r--r--src/or/circuitstats.h2
-rw-r--r--src/or/circuituse.c61
-rw-r--r--src/or/circuituse.h2
-rw-r--r--src/or/command.c2
-rw-r--r--src/or/command.h2
-rw-r--r--src/or/config.c507
-rw-r--r--src/or/config.h45
-rw-r--r--src/or/confparse.c9
-rw-r--r--src/or/confparse.h2
-rw-r--r--src/or/connection.c453
-rw-r--r--src/or/connection.h79
-rw-r--r--src/or/connection_edge.c239
-rw-r--r--src/or/connection_edge.h23
-rw-r--r--src/or/connection_or.c132
-rw-r--r--src/or/connection_or.h5
-rw-r--r--src/or/control.c106
-rw-r--r--src/or/control.h10
-rw-r--r--src/or/cpuworker.c2
-rw-r--r--src/or/cpuworker.h2
-rw-r--r--src/or/dircollate.c81
-rw-r--r--src/or/dircollate.h26
-rw-r--r--src/or/directory.c1071
-rw-r--r--src/or/directory.h75
-rw-r--r--src/or/dirserv.c145
-rw-r--r--src/or/dirserv.h10
-rw-r--r--src/or/dirvote.c54
-rw-r--r--src/or/dirvote.h16
-rw-r--r--src/or/dns.c37
-rw-r--r--src/or/dns.h14
-rw-r--r--src/or/dnsserv.c19
-rw-r--r--src/or/dnsserv.h2
-rw-r--r--src/or/entrynodes.c82
-rw-r--r--src/or/entrynodes.h5
-rw-r--r--src/or/eventdns_tor.h5
-rw-r--r--src/or/ext_orport.c15
-rw-r--r--src/or/ext_orport.h2
-rw-r--r--src/or/fallback_dirs.inc242
-rw-r--r--src/or/fp_pair.c14
-rw-r--r--src/or/fp_pair.h2
-rw-r--r--src/or/geoip.c19
-rw-r--r--src/or/geoip.h3
-rw-r--r--src/or/hibernate.c75
-rw-r--r--src/or/hibernate.h3
-rw-r--r--src/or/include.am14
-rw-r--r--src/or/keypin.c129
-rw-r--r--src/or/keypin.h5
-rw-r--r--src/or/main.c1139
-rw-r--r--src/or/main.h6
-rw-r--r--src/or/microdesc.c18
-rw-r--r--src/or/microdesc.h2
-rw-r--r--src/or/networkstatus.c401
-rw-r--r--src/or/networkstatus.h14
-rw-r--r--src/or/nodelist.c349
-rw-r--r--src/or/nodelist.h14
-rw-r--r--src/or/ntmain.c8
-rw-r--r--src/or/ntmain.h2
-rw-r--r--src/or/onion.c4
-rw-r--r--src/or/onion.h2
-rw-r--r--src/or/onion_fast.c10
-rw-r--r--src/or/onion_fast.h2
-rw-r--r--src/or/onion_ntor.c8
-rw-r--r--src/or/onion_ntor.h2
-rw-r--r--src/or/onion_tap.c2
-rw-r--r--src/or/onion_tap.h2
-rw-r--r--src/or/or.h332
-rw-r--r--src/or/periodic.c126
-rw-r--r--src/or/periodic.h37
-rw-r--r--src/or/policies.c1217
-rw-r--r--src/or/policies.h86
-rw-r--r--src/or/reasons.c2
-rw-r--r--src/or/reasons.h2
-rw-r--r--src/or/relay.c20
-rw-r--r--src/or/relay.h2
-rw-r--r--src/or/rendcache.c265
-rw-r--r--src/or/rendcache.h56
-rw-r--r--src/or/rendclient.c59
-rw-r--r--src/or/rendclient.h2
-rw-r--r--src/or/rendcommon.c92
-rw-r--r--src/or/rendcommon.h7
-rw-r--r--src/or/rendmid.c11
-rw-r--r--src/or/rendmid.h2
-rw-r--r--src/or/rendservice.c206
-rw-r--r--src/or/rendservice.h2
-rw-r--r--src/or/rephist.c64
-rw-r--r--src/or/rephist.h2
-rw-r--r--src/or/replaycache.c28
-rw-r--r--src/or/replaycache.h4
-rw-r--r--src/or/router.c274
-rw-r--r--src/or/router.h12
-rw-r--r--src/or/routerkeys.c138
-rw-r--r--src/or/routerkeys.h4
-rw-r--r--src/or/routerlist.c563
-rw-r--r--src/or/routerlist.h26
-rw-r--r--src/or/routerparse.c161
-rw-r--r--src/or/routerparse.h7
-rw-r--r--src/or/routerset.c43
-rw-r--r--src/or/routerset.h3
-rw-r--r--src/or/scheduler.c2
-rw-r--r--src/or/scheduler.h2
-rw-r--r--src/or/statefile.c16
-rw-r--r--src/or/statefile.h2
-rw-r--r--src/or/status.c24
-rw-r--r--src/or/status.h2
-rw-r--r--src/or/tor_main.c8
-rw-r--r--src/or/torcert.c17
-rw-r--r--src/or/torcert.h2
-rw-r--r--src/or/transports.c10
-rw-r--r--src/or/transports.h2
127 files changed, 7899 insertions, 2379 deletions
diff --git a/src/or/addressmap.c b/src/or/addressmap.c
index 9c29fb2acb..047a863ef5 100644
--- a/src/or/addressmap.c
+++ b/src/or/addressmap.c
@@ -1,9 +1,18 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+/**
+ * \file addressmap.c
+ *
+ * \brief The addressmap module manages the processes by which we rewrite
+ * addresses in client requess. It handles the MapAddress controller and
+ * torrc commands, and the TrackHostExits feature, and the client-side DNS
+ * cache (deprecated).
+ */
+
#define ADDRESSMAP_PRIVATE
#include "or.h"
diff --git a/src/or/addressmap.h b/src/or/addressmap.h
index ff108df024..67648d0518 100644
--- a/src/or/addressmap.h
+++ b/src/or/addressmap.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_ADDRESSMAP_H
diff --git a/src/or/buffers.c b/src/or/buffers.c
index 85fcbc64e8..cdc71ab9db 100644
--- a/src/or/buffers.c
+++ b/src/or/buffers.c
@@ -1,14 +1,15 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file buffers.c
* \brief Implements a generic interface buffer. Buffers are
* fairly opaque string holders that can read to or flush from:
- * memory, file descriptors, or TLS connections.
+ * memory, file descriptors, or TLS connections. Buffers are implemented
+ * as linked lists of memory chunks.
**/
#define BUFFERS_PRIVATE
#include "or.h"
@@ -78,7 +79,7 @@ static int parse_socks_client(const uint8_t *data, size_t datalen,
/** Return the next character in <b>chunk</b> onto which data can be appended.
* If the chunk is full, this might be off the end of chunk->mem. */
-static INLINE char *
+static inline char *
CHUNK_WRITE_PTR(chunk_t *chunk)
{
return chunk->data + chunk->datalen;
@@ -86,7 +87,7 @@ CHUNK_WRITE_PTR(chunk_t *chunk)
/** Return the number of bytes that can be written onto <b>chunk</b> without
* running out of space. */
-static INLINE size_t
+static inline size_t
CHUNK_REMAINING_CAPACITY(const chunk_t *chunk)
{
return (chunk->mem + chunk->memlen) - (chunk->data + chunk->datalen);
@@ -94,7 +95,7 @@ CHUNK_REMAINING_CAPACITY(const chunk_t *chunk)
/** Move all bytes stored in <b>chunk</b> to the front of <b>chunk</b>->mem,
* to free up space at the end. */
-static INLINE void
+static inline void
chunk_repack(chunk_t *chunk)
{
if (chunk->datalen && chunk->data != &chunk->mem[0]) {
@@ -106,7 +107,7 @@ chunk_repack(chunk_t *chunk)
/** Keep track of total size of allocated chunks for consistency asserts */
static size_t total_bytes_allocated_in_chunks = 0;
static void
-chunk_free_unchecked(chunk_t *chunk)
+buf_chunk_free_unchecked(chunk_t *chunk)
{
if (!chunk)
return;
@@ -118,7 +119,7 @@ chunk_free_unchecked(chunk_t *chunk)
total_bytes_allocated_in_chunks -= CHUNK_ALLOC_SIZE(chunk->memlen);
tor_free(chunk);
}
-static INLINE chunk_t *
+static inline chunk_t *
chunk_new_with_alloc_size(size_t alloc)
{
chunk_t *ch;
@@ -136,7 +137,7 @@ chunk_new_with_alloc_size(size_t alloc)
/** Expand <b>chunk</b> until it can hold <b>sz</b> bytes, and return a
* new pointer to <b>chunk</b>. Old pointers are no longer valid. */
-static INLINE chunk_t *
+static inline chunk_t *
chunk_grow(chunk_t *chunk, size_t sz)
{
off_t offset;
@@ -165,7 +166,7 @@ chunk_grow(chunk_t *chunk, size_t sz)
/** Return the allocation size we'd like to use to hold <b>target</b>
* bytes. */
-static INLINE size_t
+static inline size_t
preferred_chunk_size(size_t target)
{
size_t sz = MIN_CHUNK_ALLOC;
@@ -178,13 +179,10 @@ preferred_chunk_size(size_t target)
/** Collapse data from the first N chunks from <b>buf</b> into buf->head,
* growing it as necessary, until buf->head has the first <b>bytes</b> bytes
* of data from the buffer, or until buf->head has all the data in <b>buf</b>.
- *
- * If <b>nulterminate</b> is true, ensure that there is a 0 byte in
- * buf->head->mem right after all the data. */
+ */
STATIC void
-buf_pullup(buf_t *buf, size_t bytes, int nulterminate)
+buf_pullup(buf_t *buf, size_t bytes)
{
- /* XXXX nothing uses nulterminate; remove it. */
chunk_t *dest, *src;
size_t capacity;
if (!buf->head)
@@ -194,17 +192,9 @@ buf_pullup(buf_t *buf, size_t bytes, int nulterminate)
if (buf->datalen < bytes)
bytes = buf->datalen;
- if (nulterminate) {
- capacity = bytes + 1;
- if (buf->head->datalen >= bytes && CHUNK_REMAINING_CAPACITY(buf->head)) {
- *CHUNK_WRITE_PTR(buf->head) = '\0';
- return;
- }
- } else {
- capacity = bytes;
- if (buf->head->datalen >= bytes)
- return;
- }
+ capacity = bytes;
+ if (buf->head->datalen >= bytes)
+ return;
if (buf->head->memlen >= capacity) {
/* We don't need to grow the first chunk, but we might need to repack it.*/
@@ -238,7 +228,7 @@ buf_pullup(buf_t *buf, size_t bytes, int nulterminate)
dest->next = src->next;
if (buf->tail == src)
buf->tail = dest;
- chunk_free_unchecked(src);
+ buf_chunk_free_unchecked(src);
} else {
memcpy(CHUNK_WRITE_PTR(dest), src->data, n);
dest->datalen += n;
@@ -248,11 +238,6 @@ buf_pullup(buf_t *buf, size_t bytes, int nulterminate)
}
}
- if (nulterminate) {
- tor_assert(CHUNK_REMAINING_CAPACITY(buf->head));
- *CHUNK_WRITE_PTR(buf->head) = '\0';
- }
-
check();
}
@@ -271,7 +256,7 @@ buf_get_first_chunk_data(const buf_t *buf, const char **cp, size_t *sz)
#endif
/** Remove the first <b>n</b> bytes from buf. */
-static INLINE void
+static inline void
buf_remove_from_front(buf_t *buf, size_t n)
{
tor_assert(buf->datalen >= n);
@@ -289,7 +274,7 @@ buf_remove_from_front(buf_t *buf, size_t n)
buf->head = victim->next;
if (buf->tail == victim)
buf->tail = NULL;
- chunk_free_unchecked(victim);
+ buf_chunk_free_unchecked(victim);
}
}
check();
@@ -329,7 +314,7 @@ buf_clear(buf_t *buf)
buf->datalen = 0;
for (chunk = buf->head; chunk; chunk = next) {
next = chunk->next;
- chunk_free_unchecked(chunk);
+ buf_chunk_free_unchecked(chunk);
}
buf->head = buf->tail = NULL;
}
@@ -468,7 +453,7 @@ buf_get_total_allocation(void)
* <b>chunk</b> (which must be on <b>buf</b>). If we get an EOF, set
* *<b>reached_eof</b> to 1. Return -1 on error, 0 on eof or blocking,
* and the number of bytes read otherwise. */
-static INLINE int
+static inline int
read_to_chunk(buf_t *buf, chunk_t *chunk, tor_socket_t fd, size_t at_most,
int *reached_eof, int *socket_error)
{
@@ -504,7 +489,7 @@ read_to_chunk(buf_t *buf, chunk_t *chunk, tor_socket_t fd, size_t at_most,
/** As read_to_chunk(), but return (negative) error code on error, blocking,
* or TLS, and the number of bytes read otherwise. */
-static INLINE int
+static inline int
read_to_chunk_tls(buf_t *buf, chunk_t *chunk, tor_tls_t *tls,
size_t at_most)
{
@@ -627,7 +612,7 @@ read_to_buf_tls(tor_tls_t *tls, size_t at_most, buf_t *buf)
* the bytes written from *<b>buf_flushlen</b>. Return the number of bytes
* written on success, 0 on blocking, -1 on failure.
*/
-static INLINE int
+static inline int
flush_chunk(tor_socket_t s, buf_t *buf, chunk_t *chunk, size_t sz,
size_t *buf_flushlen)
{
@@ -662,7 +647,7 @@ flush_chunk(tor_socket_t s, buf_t *buf, chunk_t *chunk, size_t sz,
* bytes written from *<b>buf_flushlen</b>. Return the number of bytes
* written on success, and a TOR_TLS error code on failure or blocking.
*/
-static INLINE int
+static inline int
flush_chunk_tls(tor_tls_t *tls, buf_t *buf, chunk_t *chunk,
size_t sz, size_t *buf_flushlen)
{
@@ -813,7 +798,7 @@ write_to_buf(const char *string, size_t string_len, buf_t *buf)
/** Helper: copy the first <b>string_len</b> bytes from <b>buf</b>
* onto <b>string</b>.
*/
-static INLINE void
+static inline void
peek_from_buf(char *string, size_t string_len, const buf_t *buf)
{
chunk_t *chunk;
@@ -858,7 +843,7 @@ fetch_from_buf(char *string, size_t string_len, buf_t *buf)
/** True iff the cell command <b>command</b> is one that implies a
* variable-length cell in Tor link protocol <b>linkproto</b>. */
-static INLINE int
+static inline int
cell_command_is_var_length(uint8_t command, int linkproto)
{
/* If linkproto is v2 (2), CELL_VERSIONS is the only variable-length cells
@@ -1099,7 +1084,7 @@ buf_find_pos_of_char(char ch, buf_pos_t *out)
/** Advance <b>pos</b> by a single character, if there are any more characters
* in the buffer. Returns 0 on success, -1 on failure. */
-static INLINE int
+static inline int
buf_pos_inc(buf_pos_t *pos)
{
++pos->pos;
@@ -1203,7 +1188,7 @@ fetch_from_buf_http(buf_t *buf,
/* Okay, we have a full header. Make sure it all appears in the first
* chunk. */
if ((int)buf->head->datalen < crlf_offset + 4)
- buf_pullup(buf, crlf_offset+4, 0);
+ buf_pullup(buf, crlf_offset+4);
headerlen = crlf_offset + 4;
headers = buf->head->data;
@@ -1451,7 +1436,7 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
do {
n_drain = 0;
- buf_pullup(buf, want_length, 0);
+ buf_pullup(buf, want_length);
tor_assert(buf->head && buf->head->datalen >= 2);
want_length = 0;
@@ -1870,7 +1855,7 @@ parse_socks(const char *data, size_t datalen, socks_request_t *req,
*want_length_out = SOCKS4_NETWORK_LEN;
return 0; /* not yet */
}
- // buf_pullup(buf, 1280, 0);
+ // buf_pullup(buf, 1280);
req->command = (unsigned char) *(data+1);
if (req->command != SOCKS_COMMAND_CONNECT &&
req->command != SOCKS_COMMAND_RESOLVE) {
@@ -1961,7 +1946,7 @@ parse_socks(const char *data, size_t datalen, socks_request_t *req,
log_warn(LD_PROTOCOL,
"Your application (using socks4 to port %d) gave Tor "
"a malformed hostname: %s. Rejecting the connection.",
- req->port, escaped(req->address));
+ req->port, escaped_safe_str_client(req->address));
return -1;
}
if (authend != authstart) {
@@ -2038,7 +2023,7 @@ fetch_from_buf_socks_client(buf_t *buf, int state, char **reason)
if (buf->datalen < 2)
return 0;
- buf_pullup(buf, MAX_SOCKS_MESSAGE_LEN, 0);
+ buf_pullup(buf, MAX_SOCKS_MESSAGE_LEN);
tor_assert(buf->head && buf->head->datalen >= 2);
r = parse_socks_client((uint8_t*)buf->head->data, buf->head->datalen,
diff --git a/src/or/buffers.h b/src/or/buffers.h
index 6d0c68500b..2b43ea14b1 100644
--- a/src/or/buffers.h
+++ b/src/or/buffers.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -101,7 +101,7 @@ void assert_buf_ok(buf_t *buf);
#ifdef BUFFERS_PRIVATE
STATIC int buf_find_string_offset(const buf_t *buf, const char *s, size_t n);
-STATIC void buf_pullup(buf_t *buf, size_t bytes, int nulterminate);
+STATIC void buf_pullup(buf_t *buf, size_t bytes);
void buf_get_first_chunk_data(const buf_t *buf, const char **cp, size_t *sz);
#define DEBUG_CHUNK_ALLOC
diff --git a/src/or/channel.c b/src/or/channel.c
index af095026e4..f3939399b0 100644
--- a/src/or/channel.c
+++ b/src/or/channel.c
@@ -1,9 +1,13 @@
-/* * Copyright (c) 2012-2015, The Tor Project, Inc. */
+/* * Copyright (c) 2012-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file channel.c
- * \brief OR-to-OR channel abstraction layer
+ *
+ * \brief OR/OP-to-OR channel abstraction layer. A channel's job is to
+ * transfer cells from Tor instance to Tor instance.
+ * Currently, there is only one implementation of the channel abstraction: in
+ * channeltls.c.
**/
/*
@@ -127,13 +131,13 @@ typedef struct channel_idmap_entry_s {
TOR_LIST_HEAD(channel_list_s, channel_s) channel_list;
} channel_idmap_entry_t;
-static INLINE unsigned
+static inline unsigned
channel_idmap_hash(const channel_idmap_entry_t *ent)
{
return (unsigned) siphash24g(ent->digest, DIGEST_LEN);
}
-static INLINE int
+static inline int
channel_idmap_eq(const channel_idmap_entry_t *a,
const channel_idmap_entry_t *b)
{
@@ -2652,6 +2656,11 @@ channel_process_cells(channel_t *chan)
/*
* Process cells until we're done or find one we have no current handler
* for.
+ *
+ * We must free the cells here after calling the handler, since custody
+ * of the buffer was given to the channel layer when they were queued;
+ * see comments on memory management in channel_queue_cell() and in
+ * channel_queue_var_cell() below.
*/
while (NULL != (q = TOR_SIMPLEQ_FIRST(&chan->incoming_queue))) {
tor_assert(q);
@@ -2669,6 +2678,7 @@ channel_process_cells(channel_t *chan)
q->u.fixed.cell, chan,
U64_PRINTF_ARG(chan->global_identifier));
chan->cell_handler(chan, q->u.fixed.cell);
+ tor_free(q->u.fixed.cell);
tor_free(q);
} else if (q->type == CELL_QUEUE_VAR &&
chan->var_cell_handler) {
@@ -2681,6 +2691,7 @@ channel_process_cells(channel_t *chan)
q->u.var.var_cell, chan,
U64_PRINTF_ARG(chan->global_identifier));
chan->var_cell_handler(chan, q->u.var.var_cell);
+ tor_free(q->u.var.var_cell);
tor_free(q);
} else {
/* Can't handle this one */
@@ -2701,6 +2712,7 @@ channel_queue_cell(channel_t *chan, cell_t *cell)
{
int need_to_queue = 0;
cell_queue_entry_t *q;
+ cell_t *cell_copy = NULL;
tor_assert(chan);
tor_assert(cell);
@@ -2728,8 +2740,19 @@ channel_queue_cell(channel_t *chan, cell_t *cell)
U64_PRINTF_ARG(chan->global_identifier));
chan->cell_handler(chan, cell);
} else {
- /* Otherwise queue it and then process the queue if possible. */
- q = cell_queue_entry_new_fixed(cell);
+ /*
+ * Otherwise queue it and then process the queue if possible.
+ *
+ * We queue a copy, not the original pointer - it might have been on the
+ * stack in connection_or_process_cells_from_inbuf() (or another caller
+ * if we ever have a subclass other than channel_tls_t), or be freed
+ * there after we return. This is the uncommon case; the non-copying
+ * fast path occurs in the if (!need_to_queue) case above when the
+ * upper layer has installed cell handlers.
+ */
+ cell_copy = tor_malloc_zero(sizeof(cell_t));
+ memcpy(cell_copy, cell, sizeof(cell_t));
+ q = cell_queue_entry_new_fixed(cell_copy);
log_debug(LD_CHANNEL,
"Queueing incoming cell_t %p for channel %p "
"(global ID " U64_FORMAT ")",
@@ -2755,6 +2778,7 @@ channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell)
{
int need_to_queue = 0;
cell_queue_entry_t *q;
+ var_cell_t *cell_copy = NULL;
tor_assert(chan);
tor_assert(var_cell);
@@ -2783,8 +2807,18 @@ channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell)
U64_PRINTF_ARG(chan->global_identifier));
chan->var_cell_handler(chan, var_cell);
} else {
- /* Otherwise queue it and then process the queue if possible. */
- q = cell_queue_entry_new_var(var_cell);
+ /*
+ * Otherwise queue it and then process the queue if possible.
+ *
+ * We queue a copy, not the original pointer - it might have been on the
+ * stack in connection_or_process_cells_from_inbuf() (or another caller
+ * if we ever have a subclass other than channel_tls_t), or be freed
+ * there after we return. This is the uncommon case; the non-copying
+ * fast path occurs in the if (!need_to_queue) case above when the
+ * upper layer has installed cell handlers.
+ */
+ cell_copy = var_cell_copy(var_cell);
+ q = cell_queue_entry_new_var(cell_copy);
log_debug(LD_CHANNEL,
"Queueing incoming var_cell_t %p for channel %p "
"(global ID " U64_FORMAT ")",
@@ -2834,7 +2868,7 @@ channel_assert_counter_consistency(void)
(n_channel_bytes_in_queues + n_channel_bytes_passed_to_lower_layer));
}
-/** DOCDOC */
+/* DOCDOC */
static int
is_destroy_cell(channel_t *chan,
const cell_queue_entry_t *q, circid_t *circid_out)
@@ -3069,6 +3103,7 @@ channel_free_list(smartlist_t *channels, int mark_for_close)
if (curr->cmux) {
circuitmux_detach_all_circuits(curr->cmux, NULL);
}
+ SMARTLIST_DEL_CURRENT(channels, curr);
channel_unregister(curr);
if (mark_for_close) {
if (!CHANNEL_CONDEMNED(curr)) {
@@ -3475,7 +3510,7 @@ channel_dump_statistics, (channel_t *chan, int severity))
have_remote_addr = channel_get_addr_if_possible(chan, &remote_addr);
if (have_remote_addr) {
char *actual = tor_strdup(channel_get_actual_remote_descr(chan));
- remote_addr_str = tor_dup_addr(&remote_addr);
+ remote_addr_str = tor_addr_to_str_dup(&remote_addr);
tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " says its remote address"
" is %s, and gives a canonical description of \"%s\" and an "
diff --git a/src/or/channel.h b/src/or/channel.h
index 2b38ca7e19..a8c337e107 100644
--- a/src/or/channel.h
+++ b/src/or/channel.h
@@ -1,4 +1,4 @@
-/* * Copyright (c) 2012-2015, The Tor Project, Inc. */
+/* * Copyright (c) 2012-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -18,7 +18,7 @@ typedef void (*channel_cell_handler_fn_ptr)(channel_t *, cell_t *);
typedef void (*channel_var_cell_handler_fn_ptr)(channel_t *, var_cell_t *);
struct cell_queue_entry_s;
-TOR_SIMPLEQ_HEAD(chan_cell_queue, cell_queue_entry_s) incoming_queue;
+TOR_SIMPLEQ_HEAD(chan_cell_queue, cell_queue_entry_s);
typedef struct chan_cell_queue chan_cell_queue_t;
/**
@@ -182,7 +182,7 @@ struct channel_s {
* space should we use?
*/
circ_id_type_bitfield_t circ_id_type:2;
- /** DOCDOC*/
+ /* DOCDOC */
unsigned wide_circ_ids:1;
/** For how many circuits are we n_chan? What about p_chan? */
@@ -531,7 +531,7 @@ channel_t * channel_next_with_digest(channel_t *chan);
CHANNEL_IS_OPEN(chan) || \
CHANNEL_IS_MAINT(chan))
-static INLINE int
+static inline int
channel_is_in_state(channel_t *chan, channel_state_t state)
{
return chan->state == state;
diff --git a/src/or/channeltls.c b/src/or/channeltls.c
index c90f569233..2128b0924d 100644
--- a/src/or/channeltls.c
+++ b/src/or/channeltls.c
@@ -1,9 +1,11 @@
-/* * Copyright (c) 2012-2015, The Tor Project, Inc. */
+/* * Copyright (c) 2012-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file channeltls.c
- * \brief channel_t concrete subclass using or_connection_t
+ *
+ * \brief A concrete subclass of channel_t using or_connection_t to transfer
+ * cells between Tor instances.
**/
/*
@@ -552,7 +554,7 @@ channel_tls_get_remote_descr_method(channel_t *chan, int flags)
break;
case GRD_FLAG_ORIGINAL:
/* Actual address with port */
- addr_str = tor_dup_addr(&(tlschan->conn->real_addr));
+ addr_str = tor_addr_to_str_dup(&(tlschan->conn->real_addr));
tor_snprintf(buf, MAX_DESCR_LEN + 1,
"%s:%u", addr_str, conn->port);
tor_free(addr_str);
@@ -565,7 +567,7 @@ channel_tls_get_remote_descr_method(channel_t *chan, int flags)
break;
case GRD_FLAG_ORIGINAL|GRD_FLAG_ADDR_ONLY:
/* Actual address, no port */
- addr_str = tor_dup_addr(&(tlschan->conn->real_addr));
+ addr_str = tor_addr_to_str_dup(&(tlschan->conn->real_addr));
strlcpy(buf, addr_str, sizeof(buf));
tor_free(addr_str);
answer = buf;
@@ -1009,6 +1011,11 @@ channel_tls_time_process_cell(cell_t *cell, channel_tls_t *chan, int *time,
* for cell types specific to the handshake for this transport protocol and
* handles them, and queues all other cells to the channel_t layer, which
* eventually will hand them off to command.c.
+ *
+ * The channel layer itself decides whether the cell should be queued or
+ * can be handed off immediately to the upper-layer code. It is responsible
+ * for copying in the case that it queues; we merely pass pointers through
+ * which we get from connection_or_process_cells_from_inbuf().
*/
void
@@ -1106,6 +1113,12 @@ channel_tls_handle_cell(cell_t *cell, or_connection_t *conn)
* related and live below the channel_t layer, so no variable-length
* cells ever get delivered in the current implementation, but I've left
* the mechanism in place for future use.
+ *
+ * If we were handing them off to the upper layer, the channel_t queueing
+ * code would be responsible for memory management, and we'd just be passing
+ * pointers through from connection_or_process_cells_from_inbuf(). That
+ * caller always frees them after this function returns, so this function
+ * should never free var_cell.
*/
void
@@ -1663,30 +1676,9 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
#define NETINFO_NOTICE_SKEW 3600
if (labs(apparent_skew) > NETINFO_NOTICE_SKEW &&
router_get_by_id_digest(chan->conn->identity_digest)) {
- char dbuf[64];
- int severity;
- /*XXXX be smarter about when everybody says we are skewed. */
- if (router_digest_is_trusted_dir(chan->conn->identity_digest))
- severity = LOG_WARN;
- else
- severity = LOG_INFO;
- format_time_interval(dbuf, sizeof(dbuf), apparent_skew);
- log_fn(severity, LD_GENERAL,
- "Received NETINFO cell with skewed time from "
- "server at %s:%d. It seems that our clock is %s by %s, or "
- "that theirs is %s. Tor requires an accurate clock to work: "
- "please check your time and date settings.",
- chan->conn->base_.address,
- (int)(chan->conn->base_.port),
- apparent_skew > 0 ? "ahead" : "behind",
- dbuf,
- apparent_skew > 0 ? "behind" : "ahead");
- if (severity == LOG_WARN) /* only tell the controller if an authority */
- control_event_general_status(LOG_WARN,
- "CLOCK_SKEW SKEW=%ld SOURCE=OR:%s:%d",
- apparent_skew,
- chan->conn->base_.address,
- chan->conn->base_.port);
+ int trusted = router_digest_is_trusted_dir(chan->conn->identity_digest);
+ clock_skew_warning(TO_CONN(chan->conn), apparent_skew, trusted, LD_GENERAL,
+ "NETINFO cell", "OR");
}
/* XXX maybe act on my_apparent_addr, if the source is sufficiently
@@ -1840,7 +1832,8 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
chan->conn->handshake_state->authenticated = 1;
{
- const digests_t *id_digests = tor_x509_cert_get_id_digests(id_cert);
+ const common_digests_t *id_digests =
+ tor_x509_cert_get_id_digests(id_cert);
crypto_pk_t *identity_rcvd;
if (!id_digests)
ERR("Couldn't compute digests for key in ID cert");
@@ -2130,7 +2123,7 @@ channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan)
{
crypto_pk_t *identity_rcvd =
tor_tls_cert_get_key(chan->conn->handshake_state->id_cert);
- const digests_t *id_digests =
+ const common_digests_t *id_digests =
tor_x509_cert_get_id_digests(chan->conn->handshake_state->id_cert);
/* This must exist; we checked key type when reading the cert. */
diff --git a/src/or/channeltls.h b/src/or/channeltls.h
index a0df9faac2..a4d9c7a095 100644
--- a/src/or/channeltls.h
+++ b/src/or/channeltls.h
@@ -1,4 +1,4 @@
-/* * Copyright (c) 2012-2015, The Tor Project, Inc. */
+/* * Copyright (c) 2012-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/circpathbias.c b/src/or/circpathbias.c
index a0115cc6ec..552947eba2 100644
--- a/src/or/circpathbias.c
+++ b/src/or/circpathbias.c
@@ -1,9 +1,18 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+/**
+ * \file circpathbias.c
+ *
+ * \brief Code to track success/failure rates of circuits built through
+ * different tor nodes, in an attempt to detect attacks where
+ * an attacker deliberately causes circuits to fail until the client
+ * choses a path they like.
+ */
+
#include "or.h"
#include "channel.h"
#include "circpathbias.h"
diff --git a/src/or/circpathbias.h b/src/or/circpathbias.h
index 9e973850d5..ce76689d5f 100644
--- a/src/or/circpathbias.h
+++ b/src/or/circpathbias.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index 0688398f6d..e6fe3f0c37 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -1,12 +1,14 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file circuitbuild.c
- * \brief The actual details of building circuits.
+ *
+ * \brief Implements the details of building circuits (by chosing paths,
+ * constructing/sending create/extend cells, and so on).
**/
#define CIRCUITBUILD_PRIVATE
@@ -45,10 +47,6 @@
#include "routerset.h"
#include "crypto.h"
-#ifndef MIN
-#define MIN(a,b) ((a)<(b)?(a):(b))
-#endif
-
static channel_t * channel_connect_for_circuit(const tor_addr_t *addr,
uint16_t port,
const char *id_digest);
@@ -493,11 +491,26 @@ circuit_handle_first_hop(origin_circuit_t *circ)
int err_reason = 0;
const char *msg = NULL;
int should_launch = 0;
+ const or_options_t *options = get_options();
firsthop = onion_next_hop_in_cpath(circ->cpath);
tor_assert(firsthop);
tor_assert(firsthop->extend_info);
+ /* Some bridges are on private addresses. Others pass a dummy private
+ * address to the pluggable transport, which ignores it.
+ * Deny the connection if:
+ * - the address is internal, and
+ * - we're not connecting to a configured bridge, and
+ * - we're not configured to allow extends to private addresses. */
+ if (tor_addr_is_internal(&firsthop->extend_info->addr, 0) &&
+ !extend_info_is_a_configured_bridge(firsthop->extend_info) &&
+ !options->ExtendAllowPrivateAddresses) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Client asked me to connect directly to a private address");
+ return -END_CIRC_REASON_TORPROTOCOL;
+ }
+
/* now see if we're already connected to the first OR in 'route' */
log_debug(LD_CIRC,"Looking for firsthop '%s'",
fmt_addrport(&firsthop->extend_info->addr,
@@ -737,7 +750,7 @@ inform_testing_reachability(void)
/** Return true iff we should send a create_fast cell to start building a given
* circuit */
-static INLINE int
+static inline int
should_use_create_fast_for_circuit(origin_circuit_t *circ)
{
const or_options_t *options = get_options();
@@ -1770,7 +1783,7 @@ pick_tor2web_rendezvous_node(router_crn_flags_t flags,
router_add_running_nodes_to_smartlist(all_live_nodes,
allow_invalid,
0, 0, 0,
- need_desc);
+ need_desc, 0);
/* Filter all_live_nodes to only add live *and* whitelisted RPs to
* the list whitelisted_live_rps. */
@@ -2136,7 +2149,9 @@ choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state)
const node_t *choice;
smartlist_t *excluded;
const or_options_t *options = get_options();
- router_crn_flags_t flags = CRN_NEED_GUARD|CRN_NEED_DESC;
+ /* If possible, choose an entry server with a preferred address,
+ * otherwise, choose one with an allowed address */
+ router_crn_flags_t flags = CRN_NEED_GUARD|CRN_NEED_DESC|CRN_PREF_ADDR;
const node_t *node;
if (state && options->UseEntryGuards &&
@@ -2153,14 +2168,6 @@ choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state)
* family. */
nodelist_add_node_and_family(excluded, node);
}
- if (firewall_is_fascist_or()) {
- /* Exclude all ORs that we can't reach through our firewall */
- smartlist_t *nodes = nodelist_get_list();
- SMARTLIST_FOREACH(nodes, const node_t *, node, {
- if (!fascist_firewall_allows_node(node))
- smartlist_add(excluded, (void*)node);
- });
- }
/* and exclude current entry guards and their families,
* unless we're in a test network, and excluding guards
* would exclude all nodes (i.e. we're in an incredibly small tor network,
@@ -2239,9 +2246,11 @@ onion_extend_cpath(origin_circuit_t *circ)
if (r) {
/* If we're a client, use the preferred address rather than the
primary address, for potentially connecting to an IPv6 OR
- port. */
- info = extend_info_from_node(r, server_mode(get_options()) == 0);
- tor_assert(info);
+ port. Servers always want the primary (IPv4) address. */
+ int client = (server_mode(get_options()) == 0);
+ info = extend_info_from_node(r, client);
+ /* Clients can fail to find an allowed address */
+ tor_assert(info || client);
}
} else {
const node_t *r =
@@ -2316,33 +2325,43 @@ extend_info_new(const char *nickname, const char *digest,
* <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.
+ * routerinfo_t or microdesc_t, or if for_direct_connect is true and none of
+ * the node's addresses are allowed by tor's firewall and IP version config.
**/
extend_info_t *
extend_info_from_node(const node_t *node, int for_direct_connect)
{
tor_addr_port_t ap;
+ int valid_addr = 0;
if (node->ri == NULL && (node->rs == NULL || node->md == NULL))
return NULL;
+ /* Choose a preferred address first, but fall back to an allowed address.
+ * choose_address returns 1 on success, but get_prim_orport returns 0. */
if (for_direct_connect)
- node_get_pref_orport(node, &ap);
+ valid_addr = fascist_firewall_choose_address_node(node,
+ FIREWALL_OR_CONNECTION,
+ 0, &ap);
else
- node_get_prim_orport(node, &ap);
+ valid_addr = !node_get_prim_orport(node, &ap);
- log_debug(LD_CIRC, "using %s for %s",
- fmt_addrport(&ap.addr, ap.port),
- node->ri ? node->ri->nickname : node->rs->nickname);
+ if (valid_addr)
+ log_debug(LD_CIRC, "using %s for %s",
+ fmt_addrport(&ap.addr, ap.port),
+ node->ri ? node->ri->nickname : node->rs->nickname);
+ else
+ log_warn(LD_CIRC, "Could not choose valid address for %s",
+ node->ri ? node->ri->nickname : node->rs->nickname);
- if (node->ri)
+ if (valid_addr && node->ri)
return extend_info_new(node->ri->nickname,
node->identity,
node->ri->onion_pkey,
node->ri->onion_curve25519_pkey,
&ap.addr,
ap.port);
- else if (node->rs && node->md)
+ else if (valid_addr && node->rs && node->md)
return extend_info_new(node->rs->nickname,
node->identity,
node->md->onion_pkey,
@@ -2403,3 +2422,20 @@ build_state_get_exit_nickname(cpath_build_state_t *state)
return state->chosen_exit->nickname;
}
+/** Return true iff the given address can be used to extend to. */
+int
+extend_info_addr_is_allowed(const tor_addr_t *addr)
+{
+ tor_assert(addr);
+
+ /* Check if we have a private address and if we can extend to it. */
+ if ((tor_addr_is_internal(addr, 0) || tor_addr_is_multicast(addr)) &&
+ !get_options()->ExtendAllowPrivateAddresses) {
+ goto disallow;
+ }
+ /* Allowed! */
+ return 1;
+ disallow:
+ return 0;
+}
+
diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h
index 01563791b7..7f5fd511a9 100644
--- a/src/or/circuitbuild.h
+++ b/src/or/circuitbuild.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -53,6 +53,7 @@ extend_info_t *extend_info_new(const char *nickname, const char *digest,
extend_info_t *extend_info_from_node(const node_t *r, int for_direct_connect);
extend_info_t *extend_info_dup(extend_info_t *info);
void extend_info_free(extend_info_t *info);
+int extend_info_addr_is_allowed(const tor_addr_t *addr);
const node_t *build_state_get_exit_node(cpath_build_state_t *state);
const char *build_state_get_exit_nickname(cpath_build_state_t *state);
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index 716024df6a..1efb7ef4d0 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -1,12 +1,13 @@
/* Copyright 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file circuitlist.c
- * \brief Manage the global circuit list.
+ *
+ * \brief Manage the global circuit list, and looking up circuits within it.
**/
#define CIRCUITLIST_PRIVATE
#include "or.h"
@@ -44,11 +45,17 @@ static smartlist_t *global_circuitlist = NULL;
/** A list of all the circuits in CIRCUIT_STATE_CHAN_WAIT. */
static smartlist_t *circuits_pending_chans = NULL;
+/** A list of all the circuits that have been marked with
+ * circuit_mark_for_close and which are waiting for circuit_about_to_free. */
+static smartlist_t *circuits_pending_close = NULL;
+
static void circuit_free_cpath_node(crypt_path_t *victim);
static void cpath_ref_decref(crypt_path_reference_t *cpath_ref);
//static void circuit_set_rend_token(or_circuit_t *circ, int is_rend_circ,
// const uint8_t *token);
static void circuit_clear_rend_token(or_circuit_t *circ);
+static void circuit_about_to_free_atexit(circuit_t *circ);
+static void circuit_about_to_free(circuit_t *circ);
/********* END VARIABLES ************/
@@ -66,7 +73,7 @@ typedef struct chan_circid_circuit_map_t {
/** Helper for hash tables: compare the channel and circuit ID for a and
* b, and return less than, equal to, or greater than zero appropriately.
*/
-static INLINE int
+static inline int
chan_circid_entries_eq_(chan_circid_circuit_map_t *a,
chan_circid_circuit_map_t *b)
{
@@ -75,7 +82,7 @@ chan_circid_entries_eq_(chan_circid_circuit_map_t *a,
/** Helper: return a hash based on circuit ID and the pointer value of
* chan in <b>a</b>. */
-static INLINE unsigned int
+static inline unsigned int
chan_circid_entry_hash_(chan_circid_circuit_map_t *a)
{
/* Try to squeze the siphash input into 8 bytes to save any extra siphash
@@ -451,16 +458,27 @@ circuit_count_pending_on_channel(channel_t *chan)
void
circuit_close_all_marked(void)
{
+ if (circuits_pending_close == NULL)
+ return;
+
smartlist_t *lst = circuit_get_global_list();
- SMARTLIST_FOREACH_BEGIN(lst, circuit_t *, circ) {
- /* Fix up index if SMARTLIST_DEL_CURRENT just moved this one. */
- circ->global_circuitlist_idx = circ_sl_idx;
- if (circ->marked_for_close) {
- circ->global_circuitlist_idx = -1;
- circuit_free(circ);
- SMARTLIST_DEL_CURRENT(lst, circ);
+ SMARTLIST_FOREACH_BEGIN(circuits_pending_close, circuit_t *, circ) {
+ tor_assert(circ->marked_for_close);
+
+ /* Remove it from the circuit list. */
+ int idx = circ->global_circuitlist_idx;
+ smartlist_del(lst, idx);
+ if (idx < smartlist_len(lst)) {
+ circuit_t *replacement = smartlist_get(lst, idx);
+ replacement->global_circuitlist_idx = idx;
}
+ circ->global_circuitlist_idx = -1;
+
+ circuit_about_to_free(circ);
+ circuit_free(circ);
} SMARTLIST_FOREACH_END(circ);
+
+ smartlist_clear(circuits_pending_close);
}
/** Return the head of the global linked list of circuits. */
@@ -738,6 +756,18 @@ or_circuit_new(circid_t p_circ_id, channel_t *p_chan)
return circ;
}
+/** Free all storage held in circ->testing_cell_stats */
+void
+circuit_clear_testing_cell_stats(circuit_t *circ)
+{
+ if (!circ || !circ->testing_cell_stats)
+ return;
+ SMARTLIST_FOREACH(circ->testing_cell_stats, testing_cell_stats_entry_t *,
+ ent, tor_free(ent));
+ smartlist_free(circ->testing_cell_stats);
+ circ->testing_cell_stats = NULL;
+}
+
/** Deallocate space associated with circ.
*/
STATIC void
@@ -749,6 +779,8 @@ circuit_free(circuit_t *circ)
if (!circ)
return;
+ circuit_clear_testing_cell_stats(circ);
+
if (CIRCUIT_IS_ORIGIN(circ)) {
origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
mem = ocirc;
@@ -885,6 +917,7 @@ circuit_free_all(void)
}
}
tmp->global_circuitlist_idx = -1;
+ circuit_about_to_free_atexit(tmp);
circuit_free(tmp);
SMARTLIST_DEL_CURRENT(lst, tmp);
} SMARTLIST_FOREACH_END(tmp);
@@ -895,6 +928,9 @@ circuit_free_all(void)
smartlist_free(circuits_pending_chans);
circuits_pending_chans = NULL;
+ smartlist_free(circuits_pending_close);
+ circuits_pending_close = NULL;
+
{
chan_circid_circuit_map_t **elt, **next, *c;
for (elt = HT_START(chan_circid_map, &chan_circid_map);
@@ -1030,7 +1066,7 @@ circuit_get_by_global_id(uint32_t id)
* If <b>found_entry_out</b> is provided, set it to true if we have a
* placeholder entry for circid/chan, and leave it unset otherwise.
*/
-static INLINE circuit_t *
+static inline circuit_t *
circuit_get_by_circid_channel_impl(circid_t circ_id, channel_t *chan,
int *found_entry_out)
{
@@ -1703,6 +1739,65 @@ circuit_mark_for_close_, (circuit_t *circ, int reason, int line,
reason = END_CIRC_REASON_NONE;
}
+ circ->marked_for_close = line;
+ circ->marked_for_close_file = file;
+ circ->marked_for_close_reason = reason;
+ circ->marked_for_close_orig_reason = orig_reason;
+
+ if (!CIRCUIT_IS_ORIGIN(circ)) {
+ or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
+ if (or_circ->rend_splice) {
+ if (!or_circ->rend_splice->base_.marked_for_close) {
+ /* do this after marking this circuit, to avoid infinite recursion. */
+ circuit_mark_for_close(TO_CIRCUIT(or_circ->rend_splice), reason);
+ }
+ or_circ->rend_splice = NULL;
+ }
+ }
+
+ if (circuits_pending_close == NULL)
+ circuits_pending_close = smartlist_new();
+
+ smartlist_add(circuits_pending_close, circ);
+}
+
+/** Called immediately before freeing a marked circuit <b>circ</b> from
+ * circuit_free_all() while shutting down Tor; this is a safe-at-shutdown
+ * version of circuit_about_to_free(). It's important that it at least
+ * do circuitmux_detach_circuit() when appropriate.
+ */
+static void
+circuit_about_to_free_atexit(circuit_t *circ)
+{
+
+ if (circ->n_chan) {
+ circuit_clear_cell_queue(circ, circ->n_chan);
+ circuitmux_detach_circuit(circ->n_chan->cmux, circ);
+ circuit_set_n_circid_chan(circ, 0, NULL);
+ }
+
+ if (! CIRCUIT_IS_ORIGIN(circ)) {
+ or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
+
+ if (or_circ->p_chan) {
+ circuit_clear_cell_queue(circ, or_circ->p_chan);
+ circuitmux_detach_circuit(or_circ->p_chan->cmux, circ);
+ circuit_set_p_circid_chan(or_circ, 0, NULL);
+ }
+ }
+}
+
+/** Called immediately before freeing a marked circuit <b>circ</b>.
+ * Disconnects the circuit from other data structures, launches events
+ * as appropriate, and performs other housekeeping.
+ */
+static void
+circuit_about_to_free(circuit_t *circ)
+{
+
+ int reason = circ->marked_for_close_reason;
+ int orig_reason = circ->marked_for_close_orig_reason;
+
if (circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) {
onion_pending_remove(TO_OR_CIRCUIT(circ));
}
@@ -1726,6 +1821,7 @@ circuit_mark_for_close_, (circuit_t *circ, int reason, int line,
(circ->state == CIRCUIT_STATE_OPEN)?CIRC_EVENT_CLOSED:CIRC_EVENT_FAILED,
orig_reason);
}
+
if (circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
int timed_out = (reason == END_CIRC_REASON_TIMEOUT);
@@ -1810,20 +1906,6 @@ circuit_mark_for_close_, (circuit_t *circ, int reason, int line,
connection_edge_destroy(circ->n_circ_id, conn);
ocirc->p_streams = NULL;
}
-
- circ->marked_for_close = line;
- circ->marked_for_close_file = file;
-
- if (!CIRCUIT_IS_ORIGIN(circ)) {
- or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
- if (or_circ->rend_splice) {
- if (!or_circ->rend_splice->base_.marked_for_close) {
- /* do this after marking this circuit, to avoid infinite recursion. */
- circuit_mark_for_close(TO_CIRCUIT(or_circ->rend_splice), reason);
- }
- or_circ->rend_splice = NULL;
- }
- }
}
/** Given a marked circuit <b>circ</b>, aggressively free its cell queues to
diff --git a/src/or/circuitlist.h b/src/or/circuitlist.h
index 4e600da57d..2707b426ab 100644
--- a/src/or/circuitlist.h
+++ b/src/or/circuitlist.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -71,6 +71,8 @@ void assert_circuit_ok(const circuit_t *c);
void circuit_free_all(void);
void circuits_handle_oom(size_t current_allocation);
+void circuit_clear_testing_cell_stats(circuit_t *circ);
+
void channel_note_destroy_pending(channel_t *chan, circid_t id);
MOCK_DECL(void, channel_note_destroy_not_pending,
(channel_t *chan, circid_t id));
diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c
index a77bffac90..cc1c4cd401 100644
--- a/src/or/circuitmux.c
+++ b/src/or/circuitmux.c
@@ -1,4 +1,4 @@
-/* * Copyright (c) 2012-2015, The Tor Project, Inc. */
+/* * Copyright (c) 2012-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -186,10 +186,10 @@ struct chanid_circid_muxinfo_t {
* Static function declarations
*/
-static INLINE int
+static inline int
chanid_circid_entries_eq(chanid_circid_muxinfo_t *a,
chanid_circid_muxinfo_t *b);
-static INLINE unsigned int
+static inline unsigned int
chanid_circid_entry_hash(chanid_circid_muxinfo_t *a);
static chanid_circid_muxinfo_t *
circuitmux_find_map_entry(circuitmux_t *cmux, circuit_t *circ);
@@ -199,12 +199,12 @@ circuitmux_make_circuit_active(circuitmux_t *cmux, circuit_t *circ,
static void
circuitmux_make_circuit_inactive(circuitmux_t *cmux, circuit_t *circ,
cell_direction_t direction);
-static INLINE void
+static inline void
circuitmux_move_active_circ_to_tail(circuitmux_t *cmux, circuit_t *circ,
cell_direction_t direction);
-static INLINE circuit_t **
+static inline circuit_t **
circuitmux_next_active_circ_p(circuitmux_t *cmux, circuit_t *circ);
-static INLINE circuit_t **
+static inline circuit_t **
circuitmux_prev_active_circ_p(circuitmux_t *cmux, circuit_t *circ);
static void circuitmux_assert_okay_pass_one(circuitmux_t *cmux);
static void circuitmux_assert_okay_pass_two(circuitmux_t *cmux);
@@ -226,7 +226,7 @@ static int64_t global_destroy_ctr = 0;
* used by circuitmux_notify_xmit_cells().
*/
-static INLINE void
+static inline void
circuitmux_move_active_circ_to_tail(circuitmux_t *cmux, circuit_t *circ,
cell_direction_t direction)
{
@@ -306,7 +306,7 @@ circuitmux_move_active_circ_to_tail(circuitmux_t *cmux, circuit_t *circ,
circuitmux_assert_okay_paranoid(cmux);
}
-static INLINE circuit_t **
+static inline circuit_t **
circuitmux_next_active_circ_p(circuitmux_t *cmux, circuit_t *circ)
{
tor_assert(cmux);
@@ -319,7 +319,7 @@ circuitmux_next_active_circ_p(circuitmux_t *cmux, circuit_t *circ)
}
}
-static INLINE circuit_t **
+static inline circuit_t **
circuitmux_prev_active_circ_p(circuitmux_t *cmux, circuit_t *circ)
{
tor_assert(cmux);
@@ -338,7 +338,7 @@ circuitmux_prev_active_circ_p(circuitmux_t *cmux, circuit_t *circ)
* than zero appropriately.
*/
-static INLINE int
+static inline int
chanid_circid_entries_eq(chanid_circid_muxinfo_t *a,
chanid_circid_muxinfo_t *b)
{
@@ -349,7 +349,7 @@ chanid_circid_entries_eq(chanid_circid_muxinfo_t *a,
* Helper: return a hash based on circuit ID and channel ID in a.
*/
-static INLINE unsigned int
+static inline unsigned int
chanid_circid_entry_hash(chanid_circid_muxinfo_t *a)
{
return (((unsigned int)(a->circ_id) << 8) ^
diff --git a/src/or/circuitmux.h b/src/or/circuitmux.h
index 837e3961bf..00745ac4a1 100644
--- a/src/or/circuitmux.h
+++ b/src/or/circuitmux.h
@@ -1,4 +1,4 @@
-/* * Copyright (c) 2012-2015, The Tor Project, Inc. */
+/* * Copyright (c) 2012-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/circuitmux_ewma.c b/src/or/circuitmux_ewma.c
index 1c0318de06..b784a140ac 100644
--- a/src/or/circuitmux_ewma.c
+++ b/src/or/circuitmux_ewma.c
@@ -1,4 +1,4 @@
-/* * Copyright (c) 2012-2015, The Tor Project, Inc. */
+/* * Copyright (c) 2012-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -115,7 +115,7 @@ TO_EWMA_POL_CIRC_DATA(circuitmux_policy_circ_data_t *);
* if the cast is impossible.
*/
-static INLINE ewma_policy_data_t *
+static inline ewma_policy_data_t *
TO_EWMA_POL_DATA(circuitmux_policy_data_t *pol)
{
if (!pol) return NULL;
@@ -130,7 +130,7 @@ TO_EWMA_POL_DATA(circuitmux_policy_data_t *pol)
* and assert if the cast is impossible.
*/
-static INLINE ewma_policy_circ_data_t *
+static inline ewma_policy_circ_data_t *
TO_EWMA_POL_CIRC_DATA(circuitmux_policy_circ_data_t *pol)
{
if (!pol) return NULL;
@@ -147,7 +147,7 @@ static int compare_cell_ewma_counts(const void *p1, const void *p2);
static unsigned cell_ewma_tick_from_timeval(const struct timeval *now,
double *remainder_out);
static circuit_t * cell_ewma_to_circuit(cell_ewma_t *ewma);
-static INLINE double get_scale_factor(unsigned from_tick, unsigned to_tick);
+static inline double get_scale_factor(unsigned from_tick, unsigned to_tick);
static cell_ewma_t * pop_first_cell_ewma(ewma_policy_data_t *pol);
static void remove_cell_ewma(ewma_policy_data_t *pol, cell_ewma_t *ewma);
static void scale_single_cell_ewma(cell_ewma_t *ewma, unsigned cur_tick);
@@ -644,7 +644,7 @@ cell_ewma_set_scale_factor(const or_options_t *options,
/** Return the multiplier necessary to convert the value of a cell sent in
* 'from_tick' to one sent in 'to_tick'. */
-static INLINE double
+static inline double
get_scale_factor(unsigned from_tick, unsigned to_tick)
{
/* This math can wrap around, but that's okay: unsigned overflow is
diff --git a/src/or/circuitmux_ewma.h b/src/or/circuitmux_ewma.h
index 3feef834dd..58aac1e196 100644
--- a/src/or/circuitmux_ewma.h
+++ b/src/or/circuitmux_ewma.h
@@ -1,4 +1,4 @@
-/* * Copyright (c) 2012-2015, The Tor Project, Inc. */
+/* * Copyright (c) 2012-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/circuitstats.c b/src/or/circuitstats.c
index 3ced5afad5..9ac2d565b5 100644
--- a/src/or/circuitstats.c
+++ b/src/or/circuitstats.c
@@ -1,9 +1,16 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+/**
+ * \file circuitstats.c
+ *
+ * \brief Maintains and analyzes statistics about circuit built times, so we
+ * can tell how long we may need to wait for a fast circuit to be constructed.
+ */
+
#define CIRCUITSTATS_PRIVATE
#include "or.h"
diff --git a/src/or/circuitstats.h b/src/or/circuitstats.h
index fe05a24e97..72b160983f 100644
--- a/src/or/circuitstats.h
+++ b/src/or/circuitstats.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index a3b71974ca..b5959944f1 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -92,7 +92,7 @@ circuit_is_acceptable(const origin_circuit_t *origin_circ,
/* decide if this circ is suitable for this conn */
/* for rend circs, circ->cpath->prev is not the last router in the
- * circuit, it's the magical extra bob hop. so just check the nickname
+ * circuit, it's the magical extra service hop. so just check the nickname
* of the one we meant to finish at.
*/
build_state = origin_circ->build_state;
@@ -1067,7 +1067,7 @@ circuit_predict_and_launch_new(void)
if (rep_hist_get_predicted_internal(now, &hidserv_needs_uptime,
&hidserv_needs_capacity) &&
((num_uptime_internal<2 && hidserv_needs_uptime) ||
- num_internal<2)
+ num_internal<3)
&& router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN) {
if (hidserv_needs_uptime)
flags |= CIRCLAUNCH_NEED_UPTIME;
@@ -1123,7 +1123,7 @@ circuit_build_needed_circs(time_t now)
* don't require an exit circuit, review in #13814.
* This allows HSs to function in a consensus without exits. */
if (router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN)
- connection_ap_attach_pending();
+ connection_ap_rescan_and_attach_pending();
/* make sure any hidden services have enough intro points
* HS intro point streams only require an internal circuit */
@@ -1475,7 +1475,7 @@ circuit_has_opened(origin_circuit_t *circ)
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
rend_client_rendcirc_has_opened(circ);
/* Start building an intro circ if we don't have one yet. */
- connection_ap_attach_pending();
+ connection_ap_attach_pending(1);
/* This isn't a call to circuit_try_attaching_streams because a
* circuit in _C_ESTABLISH_REND state isn't connected to its
* hidden service yet, thus we can't attach streams to it yet,
@@ -1493,11 +1493,11 @@ circuit_has_opened(origin_circuit_t *circ)
circuit_try_attaching_streams(circ);
break;
case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
- /* at Bob, waiting for introductions */
+ /* at the service, waiting for introductions */
rend_service_intro_has_opened(circ);
break;
case CIRCUIT_PURPOSE_S_CONNECT_REND:
- /* at Bob, connecting to rend point */
+ /* at the service, connecting to rend point */
rend_service_rendezvous_has_opened(circ);
break;
case CIRCUIT_PURPOSE_TESTING:
@@ -1537,14 +1537,14 @@ void
circuit_try_attaching_streams(origin_circuit_t *circ)
{
/* Attach streams to this circuit if we can. */
- connection_ap_attach_pending();
+ connection_ap_attach_pending(1);
/* The call to circuit_try_clearing_isolation_state here will do
* nothing and return 0 if we didn't attach any streams to circ
* above. */
if (circuit_try_clearing_isolation_state(circ)) {
/* Maybe *now* we can attach some streams to this circuit. */
- connection_ap_attach_pending();
+ connection_ap_attach_pending(1);
}
}
@@ -1617,32 +1617,32 @@ circuit_build_failed(origin_circuit_t *circ)
circuit_testing_failed(circ, failed_at_last_hop);
break;
case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
- /* at Bob, waiting for introductions */
+ /* at the service, waiting for introductions */
if (circ->base_.state != CIRCUIT_STATE_OPEN) {
circuit_increment_failure_count();
}
- /* no need to care here, because bob will rebuild intro
+ /* no need to care here, because the service will rebuild intro
* points periodically. */
break;
case CIRCUIT_PURPOSE_C_INTRODUCING:
- /* at Alice, connecting to intro point */
- /* Don't increment failure count, since Bob may have picked
+ /* at the client, connecting to intro point */
+ /* Don't increment failure count, since the service may have picked
* the introduction point maliciously */
- /* Alice will pick a new intro point when this one dies, if
+ /* The client will pick a new intro point when this one dies, if
* the stream in question still cares. No need to act here. */
break;
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
- /* at Alice, waiting for Bob */
+ /* at the client, waiting for the service */
circuit_increment_failure_count();
- /* Alice will pick a new rend point when this one dies, if
+ /* the client will pick a new rend point when this one dies, if
* the stream in question still cares. No need to act here. */
break;
case CIRCUIT_PURPOSE_S_CONNECT_REND:
- /* at Bob, connecting to rend point */
- /* Don't increment failure count, since Alice may have picked
+ /* at the service, connecting to rend point */
+ /* Don't increment failure count, since the client may have picked
* the rendezvous point maliciously */
log_info(LD_REND,
- "Couldn't connect to Alice's chosen rend point %s "
+ "Couldn't connect to the client's chosen rend point %s "
"(%s hop failed).",
escaped(build_state_get_exit_nickname(circ->build_state)),
failed_at_last_hop?"last":"non-last");
@@ -1674,7 +1674,7 @@ circuit_launch(uint8_t purpose, int flags)
return circuit_launch_by_extend_info(purpose, NULL, flags);
}
-/** DOCDOC */
+/* DOCDOC */
static int
have_enough_path_info(int need_exit)
{
@@ -1986,6 +1986,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
"No intro points for '%s': re-fetching service descriptor.",
safe_str_client(rend_data->onion_address));
rend_client_refetch_v2_renddesc(rend_data);
+ connection_ap_mark_as_non_pending_circuit(conn);
ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_RENDDESC_WAIT;
return 0;
}
@@ -2005,8 +2006,13 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
if (r && node_has_descriptor(r)) {
/* We might want to connect to an IPv6 bridge for loading
descriptors so we use the preferred address rather than
- the primary. */
+ the primary. */
extend_info = extend_info_from_node(r, conn->want_onehop ? 1 : 0);
+ if (!extend_info) {
+ log_warn(LD_CIRC,"Could not make a one-hop connection to %s. "
+ "Discarding this circuit.", conn->chosen_exit_name);
+ return -1;
+ }
} else {
log_debug(LD_DIR, "considering %d, %s",
want_onehop, conn->chosen_exit_name);
@@ -2240,7 +2246,7 @@ consider_recording_trackhost(const entry_connection_t *conn,
char fp[HEX_DIGEST_LEN+1];
/* Search the addressmap for this conn's destination. */
- /* If he's not in the address map.. */
+ /* If they're not in the address map.. */
if (!options->TrackHostExits ||
addressmap_have_mapping(conn->socks_request->address,
options->TrackHostExitsExpire))
@@ -2284,8 +2290,15 @@ connection_ap_handshake_attach_chosen_circuit(entry_connection_t *conn,
base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
- if (!circ->base_.timestamp_dirty)
- circ->base_.timestamp_dirty = time(NULL);
+ if (!circ->base_.timestamp_dirty ||
+ ((conn->entry_cfg.isolation_flags & ISO_SOCKSAUTH) &&
+ (conn->entry_cfg.socks_iso_keep_alive) &&
+ (conn->socks_request->usernamelen ||
+ conn->socks_request->passwordlen))) {
+ /* When stream isolation is in use and controlled by an application
+ * we are willing to keep using the stream. */
+ circ->base_.timestamp_dirty = approx_time();
+ }
pathbias_count_use_attempt(circ);
diff --git a/src/or/circuituse.h b/src/or/circuituse.h
index a59f478ac8..5973978c45 100644
--- a/src/or/circuituse.h
+++ b/src/or/circuituse.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/command.c b/src/or/command.c
index af6e0533d8..5ad92bed1e 100644
--- a/src/or/command.c
+++ b/src/or/command.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/command.h b/src/or/command.h
index bea96261bb..12cda6a463 100644
--- a/src/or/command.h
+++ b/src/or/command.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/config.c b/src/or/config.c
index 6e782de0e0..2e14ba69dc 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -162,6 +162,7 @@ static config_var_t option_vars_[] = {
V(AuthDirInvalidCCs, CSV, ""),
V(AuthDirFastGuarantee, MEMUNIT, "100 KB"),
V(AuthDirGuardBWGuarantee, MEMUNIT, "2 MB"),
+ V(AuthDirPinKeys, BOOL, "0"),
V(AuthDirReject, LINELIST, NULL),
V(AuthDirRejectCCs, CSV, ""),
OBSOLETE("AuthDirRejectUnlisted"),
@@ -189,10 +190,12 @@ static config_var_t option_vars_[] = {
V(CircuitPriorityHalflife, DOUBLE, "-100.0"), /*negative:'Use default'*/
V(ClientDNSRejectInternalAddresses, BOOL,"1"),
V(ClientOnly, BOOL, "0"),
- V(ClientPreferIPv6ORPort, BOOL, "0"),
+ V(ClientPreferIPv6ORPort, AUTOBOOL, "auto"),
+ V(ClientPreferIPv6DirPort, AUTOBOOL, "auto"),
V(ClientRejectInternalAddresses, BOOL, "1"),
V(ClientTransportPlugin, LINELIST, NULL),
V(ClientUseIPv6, BOOL, "0"),
+ V(ClientUseIPv4, BOOL, "1"),
V(ConsensusParams, STRING, NULL),
V(ConnLimit, UINT, "1000"),
V(ConnDirectionStatistics, BOOL, "0"),
@@ -211,6 +214,7 @@ static config_var_t option_vars_[] = {
V(CookieAuthFile, STRING, NULL),
V(CountPrivateBandwidth, BOOL, "0"),
V(DataDirectory, FILENAME, NULL),
+ V(DataDirectoryGroupReadable, BOOL, "0"),
V(DisableNetwork, BOOL, "0"),
V(DirAllowPrivateAddresses, BOOL, "0"),
V(TestingAuthDirTimeToLearnReachability, INTERVAL, "30 minutes"),
@@ -220,6 +224,7 @@ static config_var_t option_vars_[] = {
V(DirPortFrontPage, FILENAME, NULL),
VAR("DirReqStatistics", BOOL, DirReqStatistics_option, "1"),
VAR("DirAuthority", LINELIST, DirAuthorities, NULL),
+ V(DirCache, BOOL, "1"),
V(DirAuthorityFallbackRate, DOUBLE, "1.0"),
V(DisableAllSwap, BOOL, "0"),
V(DisableDebuggerAttachment, BOOL, "1"),
@@ -250,6 +255,7 @@ static config_var_t option_vars_[] = {
V(ExtORPortCookieAuthFileGroupReadable, BOOL, "0"),
V(ExtraInfoStatistics, BOOL, "1"),
V(FallbackDir, LINELIST, NULL),
+ V(UseDefaultFallbackDirs, BOOL, "1"),
OBSOLETE("FallbackNetworkstatusFile"),
V(FascistFirewall, BOOL, "0"),
@@ -289,7 +295,7 @@ static config_var_t option_vars_[] = {
VAR("HiddenServiceMaxStreams",LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceMaxStreamsCloseCircuit",LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceNumIntroductionPoints", LINELIST_S, RendConfigLines, NULL),
- V(HiddenServiceStatistics, BOOL, "0"),
+ V(HiddenServiceStatistics, BOOL, "1"),
V(HidServAuth, LINELIST, NULL),
V(CloseHSClientCircuitsImmediatelyOnTimeout, BOOL, "0"),
V(CloseHSServiceRendCircuitsImmediatelyOnTimeout, BOOL, "0"),
@@ -307,10 +313,12 @@ static config_var_t option_vars_[] = {
V(Socks5ProxyUsername, STRING, NULL),
V(Socks5ProxyPassword, STRING, NULL),
V(KeepalivePeriod, INTERVAL, "5 minutes"),
+ V(KeepBindCapabilities, AUTOBOOL, "auto"),
VAR("Log", LINELIST, Logs, NULL),
V(LogMessageDomains, BOOL, "0"),
V(LogTimeGranularity, MSEC_INTERVAL, "1 second"),
V(TruncateLogFile, BOOL, "0"),
+ V(SyslogIdentityTag, STRING, NULL),
V(LongLivedPorts, CSV,
"21,22,706,1863,5050,5190,5222,5223,6523,6667,6697,8300"),
VAR("MapAddress", LINELIST, AddressMap, NULL),
@@ -333,6 +341,7 @@ static config_var_t option_vars_[] = {
V(NumCPUs, UINT, "0"),
V(NumDirectoryGuards, UINT, "0"),
V(NumEntryGuards, UINT, "0"),
+ V(OfflineMasterKey, BOOL, "0"),
V(ORListenAddress, LINELIST, NULL),
VPORT(ORPort, LINELIST, NULL),
V(OutboundBindAddress, LINELIST, NULL),
@@ -471,10 +480,40 @@ static config_var_t option_vars_[] = {
V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 60, "
"300, 600, 1800, 3600, 3600, 3600, "
"10800, 21600, 43200"),
+ /* With the ClientBootstrapConsensus*Download* below:
+ * Clients with only authorities will try:
+ * - 3 authorities over 10 seconds, then wait 60 minutes.
+ * Clients with authorities and fallbacks will try:
+ * - 2 authorities and 4 fallbacks over 21 seconds, then wait 60 minutes.
+ * Clients will also retry when an application request arrives.
+ * After a number of failed reqests, clients retry every 3 days + 1 hour.
+ *
+ * Clients used to try 2 authorities over 10 seconds, then wait for
+ * 60 minutes or an application request.
+ *
+ * When clients have authorities and fallbacks available, they use these
+ * schedules: (we stagger the times to avoid thundering herds) */
+ V(ClientBootstrapConsensusAuthorityDownloadSchedule, CSV_INTERVAL,
+ "10, 11, 3600, 10800, 25200, 54000, 111600, 262800" /* 3 days + 1 hour */),
+ V(ClientBootstrapConsensusFallbackDownloadSchedule, CSV_INTERVAL,
+ "0, 1, 4, 11, 3600, 10800, 25200, 54000, 111600, 262800"),
+ /* When clients only have authorities available, they use this schedule: */
+ V(ClientBootstrapConsensusAuthorityOnlyDownloadSchedule, CSV_INTERVAL,
+ "0, 3, 7, 3600, 10800, 25200, 54000, 111600, 262800"),
+ /* We don't want to overwhelm slow networks (or mirrors whose replies are
+ * blocked), but we also don't want to fail if only some mirrors are
+ * blackholed. Clients will try 3 directories simultaneously.
+ * (Relays never use simultaneous connections.) */
+ V(ClientBootstrapConsensusMaxInProgressTries, UINT, "3"),
V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "3600, 900, 900, 3600"),
V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "10 minutes"),
V(TestingDirConnectionMaxStall, INTERVAL, "5 minutes"),
V(TestingConsensusMaxDownloadTries, UINT, "8"),
+ /* Since we try connections rapidly and simultaneously, we can afford
+ * to give up earlier. (This protects against overloading directories.) */
+ V(ClientBootstrapConsensusMaxDownloadTries, UINT, "7"),
+ /* We want to give up much earlier if we're only using authorities. */
+ V(ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries, UINT, "4"),
V(TestingDescriptorMaxDownloadTries, UINT, "8"),
V(TestingMicrodescMaxDownloadTries, UINT, "8"),
V(TestingCertMaxDownloadTries, UINT, "8"),
@@ -498,6 +537,14 @@ static const config_var_t testing_tor_network_defaults[] = {
V(AssumeReachable, BOOL, "1"),
V(AuthDirMaxServersPerAddr, UINT, "0"),
V(AuthDirMaxServersPerAuthAddr,UINT, "0"),
+ V(ClientBootstrapConsensusAuthorityDownloadSchedule, CSV_INTERVAL,
+ "0, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"),
+ V(ClientBootstrapConsensusFallbackDownloadSchedule, CSV_INTERVAL,
+ "0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"),
+ V(ClientBootstrapConsensusAuthorityOnlyDownloadSchedule, CSV_INTERVAL,
+ "0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"),
+ V(ClientBootstrapConsensusMaxDownloadTries, UINT, "80"),
+ V(ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries, UINT, "80"),
V(ClientDNSRejectInternalAddresses, BOOL,"0"),
V(ClientRejectInternalAddresses, BOOL, "0"),
V(CountPrivateBandwidth, BOOL, "1"),
@@ -545,7 +592,6 @@ static const config_var_t testing_tor_network_defaults[] = {
static char *get_windows_conf_root(void);
#endif
static int options_act_reversible(const or_options_t *old_options, char **msg);
-static int options_act(const or_options_t *old_options);
static int options_transition_allowed(const or_options_t *old,
const or_options_t *new,
char **msg);
@@ -556,15 +602,12 @@ static int options_transition_affects_descriptor(
static int check_nickname_list(char **lst, const char *name, char **msg);
static char *get_bindaddr_from_transport_listen_line(const char *line,
const char *transport);
-static int parse_dir_authority_line(const char *line,
- dirinfo_type_t required_type,
- int validate_only);
-static void port_cfg_free(port_cfg_t *port);
static int parse_ports(or_options_t *options, int validate_only,
char **msg_out, int *n_ports_out,
int *world_writable_control_socket);
static int check_server_ports(const smartlist_t *ports,
- const or_options_t *options);
+ const or_options_t *options,
+ int *num_low_ports_out);
static int validate_data_directory(or_options_t *options);
static int write_configuration_file(const char *fname,
@@ -623,15 +666,15 @@ static char *global_dirfrontpagecontents = NULL;
static smartlist_t *configured_ports = NULL;
/** Return the contents of our frontpage string, or NULL if not configured. */
-const char *
-get_dirportfrontpage(void)
+MOCK_IMPL(const char*,
+get_dirportfrontpage, (void))
{
return global_dirfrontpagecontents;
}
-/** Return the currently configured options. */
-or_options_t *
-get_options_mutable(void)
+/** Returns the currently configured options. */
+MOCK_IMPL(or_options_t *,
+get_options_mutable, (void))
{
tor_assert(global_options);
return global_options;
@@ -762,6 +805,7 @@ or_options_free(or_options_t *options)
}
tor_free(options->BridgePassword_AuthDigest_);
tor_free(options->command_arg);
+ tor_free(options->master_key_fname);
config_free(&options_format, options);
}
@@ -790,7 +834,6 @@ config_free_all(void)
tor_free(torrc_fname);
tor_free(torrc_defaults_fname);
- tor_free(the_tor_version);
tor_free(global_dirfrontpagecontents);
tor_free(the_short_tor_version);
@@ -862,6 +905,7 @@ static const char *default_authorities[] = {
"128.31.0.39:9131 9695 DFC3 5FFE B861 329B 9F1A B04C 4639 7020 CE31",
"tor26 orport=443 "
"v3ident=14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 "
+ "ipv6=[2001:858:2:2:aabb:0:563b:1526]:443 "
"86.59.21.38:80 847B 1F85 0344 D787 6491 A548 92F9 0493 4E4E B85D",
"dizum orport=443 "
"v3ident=E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58 "
@@ -870,30 +914,43 @@ static const char *default_authorities[] = {
"82.94.251.203:80 4A0C CD2D DC79 9508 3D73 F5D6 6710 0C8A 5831 F16D",
"gabelmoo orport=443 "
"v3ident=ED03BB616EB2F60BEC80151114BB25CEF515B226 "
+ "ipv6=[2001:638:a000:4140::ffff:189]:443 "
"131.188.40.189:80 F204 4413 DAC2 E02E 3D6B CF47 35A1 9BCA 1DE9 7281",
"dannenberg orport=443 "
- "v3ident=585769C78764D58426B8B52B6651A5A71137189A "
+ "v3ident=0232AF901C31A04EE9848595AF9BB7620D4C5B2E "
"193.23.244.244:80 7BE6 83E6 5D48 1413 21C5 ED92 F075 C553 64AC 7123",
"urras orport=80 "
"v3ident=80550987E1D626E3EBA5E5E75A458DE0626D088C "
- "208.83.223.34:443 0AD3 FA88 4D18 F89E EA2D 89C0 1937 9E0E 7FD9 4417",
+ "208.83.223.34:443 0AD3 FA88 4D18 F89E EA2D 89C0 1937 9E0E 7FD9 4417"
+ /* XX/teor - urras may have an IPv6 address, but it's not in urras'
+ * descriptor as of 11 Dec 2015. See #17813. */,
"maatuska orport=80 "
"v3ident=49015F787433103580E3B66A1707A00E60F2D15B "
+ "ipv6=[2001:67c:289c::9]:80 "
"171.25.193.9:443 BD6A 8292 55CB 08E6 6FBE 7D37 4836 3586 E46B 3810",
"Faravahar orport=443 "
"v3ident=EFCBE720AB3A82B99F9E953CD5BF50F7EEFC7B97 "
"154.35.175.225:80 CF6D 0AAF B385 BE71 B8E1 11FC 5CFF 4B47 9237 33BC",
"longclaw orport=443 "
"v3ident=23D15D965BC35114467363C165C4F724B64B4F66 "
+ "ipv6=[2620:13:4000:8000:60:f3ff:fea1:7cff]:443 "
"199.254.238.52:80 74A9 1064 6BCE EFBC D2E8 74FC 1DC9 9743 0F96 8145",
NULL
};
+/** List of fallback directory authorities. The list is generated by opt-in of
+ * relays that meet certain stability criteria.
+ */
+static const char *default_fallbacks[] = {
+#include "fallback_dirs.inc"
+ NULL
+};
+
/** Add the default directory authorities directly into the trusted dir list,
* but only add them insofar as they share bits with <b>type</b>.
* Each authority's bits are restricted to the bits shared with <b>type</b>.
* If <b>type</b> is ALL_DIRINFO or NO_DIRINFO (zero), add all authorities. */
-static void
+STATIC void
add_default_trusted_dir_authorities(dirinfo_type_t type)
{
int i;
@@ -911,13 +968,10 @@ MOCK_IMPL(void,
add_default_fallback_dir_servers,(void))
{
int i;
- const char *fallback[] = {
- NULL
- };
- for (i=0; fallback[i]; i++) {
- if (parse_dir_fallback_line(fallback[i], 0)<0) {
+ for (i=0; default_fallbacks[i]; i++) {
+ if (parse_dir_fallback_line(default_fallbacks[i], 0)<0) {
log_err(LD_BUG, "Couldn't parse internal FallbackDir line %s",
- fallback[i]);
+ default_fallbacks[i]);
}
}
}
@@ -987,6 +1041,7 @@ consider_adding_dir_servers(const or_options_t *options,
!smartlist_len(router_get_fallback_dir_servers()) || !old_options ||
!config_lines_eq(options->DirAuthorities, old_options->DirAuthorities) ||
!config_lines_eq(options->FallbackDir, old_options->FallbackDir) ||
+ (options->UseDefaultFallbackDirs != old_options->UseDefaultFallbackDirs) ||
!config_lines_eq(options->AlternateBridgeAuthority,
old_options->AlternateBridgeAuthority) ||
!config_lines_eq(options->AlternateDirAuthority,
@@ -1015,8 +1070,8 @@ consider_adding_dir_servers(const or_options_t *options,
type |= V3_DIRINFO | EXTRAINFO_DIRINFO | MICRODESC_DIRINFO;
/* Only add the default fallback directories when the DirAuthorities,
* AlternateDirAuthority, and FallbackDir directory config options
- * are set to their defaults. */
- if (!options->FallbackDir) {
+ * are set to their defaults, and when UseDefaultFallbackDirs is 1. */
+ if (!options->FallbackDir && options->UseDefaultFallbackDirs) {
add_default_fallback_dir_servers();
}
}
@@ -1041,6 +1096,9 @@ consider_adding_dir_servers(const or_options_t *options,
return 0;
}
+/* Helps determine flags to pass to switch_id. */
+static int have_low_ports = -1;
+
/** 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.
@@ -1175,7 +1233,16 @@ options_act_reversible(const or_options_t *old_options, char **msg)
/* Setuid/setgid as appropriate */
if (options->User) {
- if (switch_id(options->User) != 0) {
+ tor_assert(have_low_ports != -1);
+ unsigned switch_id_flags = 0;
+ if (options->KeepBindCapabilities == 1) {
+ switch_id_flags |= SWITCH_ID_KEEP_BINDLOW;
+ switch_id_flags |= SWITCH_ID_WARN_IF_NO_CAPS;
+ }
+ if (options->KeepBindCapabilities == -1 && have_low_ports) {
+ switch_id_flags |= SWITCH_ID_KEEP_BINDLOW;
+ }
+ if (switch_id(options->User, switch_id_flags) != 0) {
/* No need to roll back, since you can't change the value. */
*msg = tor_strdup("Problem with User value. See logs for details.");
goto done;
@@ -1183,16 +1250,30 @@ options_act_reversible(const or_options_t *old_options, char **msg)
}
/* Ensure data directory is private; create if possible. */
+ cpd_check_t cpd_opts = running_tor ? CPD_CREATE : CPD_CHECK;
+ if (options->DataDirectoryGroupReadable)
+ cpd_opts |= CPD_GROUP_READ;
if (check_private_dir(options->DataDirectory,
- running_tor ? CPD_CREATE : CPD_CHECK,
+ cpd_opts,
options->User)<0) {
tor_asprintf(msg,
"Couldn't access/create private data directory \"%s\"",
options->DataDirectory);
+
goto done;
/* No need to roll back, since you can't change the value. */
}
+#ifndef _WIN32
+ if (options->DataDirectoryGroupReadable) {
+ /* Only new dirs created get new opts, also enforce group read. */
+ if (chmod(options->DataDirectory, 0750)) {
+ log_warn(LD_FS,"Unable to make %s group-readable: %s",
+ options->DataDirectory, strerror(errno));
+ }
+ }
+#endif
+
/* Bail out at this point if we're not going to be a client or server:
* we don't run Tor itself. */
if (!running_tor)
@@ -1365,7 +1446,7 @@ options_transition_requires_fresh_tls_context(const or_options_t *old_options,
* Note: We haven't moved all the "act on new configuration" logic
* here yet. Some is still in do_hup() and other places.
*/
-static int
+STATIC int
options_act(const or_options_t *old_options)
{
config_line_t *cl;
@@ -1387,10 +1468,12 @@ options_act(const or_options_t *old_options)
if (options->DisableDebuggerAttachment && !disabled_debugger_attach &&
running_tor) {
int ok = tor_disable_debugger_attach();
+ /* LCOV_EXCL_START the warned_debugger_attach is 0 can't reach inside. */
if (warned_debugger_attach && ok == 1) {
log_notice(LD_CONFIG, "Disabled attaching debuggers for unprivileged "
"users.");
}
+ /* LCOV_EXCL_STOP */
disabled_debugger_attach = (ok == 1);
} else if (!options->DisableDebuggerAttachment &&
!warned_debugger_attach) {
@@ -1417,12 +1500,14 @@ options_act(const or_options_t *old_options)
#endif
#ifdef ENABLE_TOR2WEB_MODE
+/* LCOV_EXCL_START */
if (!options->Tor2webMode) {
log_err(LD_CONFIG, "This copy of Tor was compiled to run in "
"'tor2web mode'. It can only be run with the Tor2webMode torrc "
"option enabled.");
return -1;
}
+/* LCOV_EXCL_STOP */
#else
if (options->Tor2webMode) {
log_err(LD_CONFIG, "This copy of Tor was not compiled to run in "
@@ -1434,7 +1519,7 @@ options_act(const or_options_t *old_options)
#endif
/* If we are a bridge with a pluggable transport proxy but no
- Extended ORPort, inform the user that she is missing out. */
+ Extended ORPort, inform the user that they are missing out. */
if (server_mode(options) && options->ServerTransportPlugin &&
!options->ExtORPort_lines) {
log_notice(LD_CONFIG, "We use pluggable transports but the Extended "
@@ -1686,8 +1771,8 @@ options_act(const or_options_t *old_options)
if (revise_trackexithosts)
addressmap_clear_excluded_trackexithosts(options);
- if (!options->AutomapHostsOnResolve) {
- if (old_options->AutomapHostsOnResolve)
+ if (!options->AutomapHostsOnResolve &&
+ old_options->AutomapHostsOnResolve) {
revise_automap_entries = 1;
} else {
if (!smartlist_strings_eq(old_options->AutomapHostsSuffixes,
@@ -1826,8 +1911,8 @@ options_act(const or_options_t *old_options)
print_notice = 1;
}
if (print_notice)
- log_notice(LD_CONFIG, "Configured to measure statistics. Look for "
- "the *-stats files that will first be written to the "
+ log_notice(LD_CONFIG, "Configured to measure statistics. Look for "
+ "the *-stats files that will first be written to the "
"data directory in 24 hours from now.");
}
@@ -1918,6 +2003,12 @@ static const struct {
{ "--dump-config", ARGUMENT_OPTIONAL },
{ "--list-fingerprint", TAKES_NO_ARGUMENT },
{ "--keygen", TAKES_NO_ARGUMENT },
+ { "--newpass", TAKES_NO_ARGUMENT },
+#if 0
+/* XXXX028: This is not working yet in 0.2.7, so disabling with the
+ * minimal code modification. */
+ { "--master-key", ARGUMENT_NECESSARY },
+#endif
{ "--no-passphrase", TAKES_NO_ARGUMENT },
{ "--passphrase-fd", ARGUMENT_NECESSARY },
{ "--verify-config", TAKES_NO_ARGUMENT },
@@ -2104,7 +2195,7 @@ print_usage(void)
printf(
"Copyright (c) 2001-2004, Roger Dingledine\n"
"Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson\n"
-"Copyright (c) 2007-2015, The Tor Project, Inc.\n\n"
+"Copyright (c) 2007-2016, The Tor Project, Inc.\n\n"
"tor -f <torrc> [args]\n"
"See man page for options, or https://www.torproject.org/ for "
"documentation.\n");
@@ -2602,7 +2693,7 @@ options_validate_cb(void *old_options, void *options, void *default_options,
/** Log a warning message iff <b>filepath</b> is not absolute.
* Warning message must contain option name <b>option</b> and
- * an absolute path that <b>filepath<b> will resolve to.
+ * an absolute path that <b>filepath</b> will resolve to.
*
* In case <b>filepath</b> is absolute, do nothing.
*/
@@ -2705,7 +2796,8 @@ options_validate(or_options_t *old_options, or_options_t *options,
} else {
if (!is_legal_nickname(options->Nickname)) {
tor_asprintf(msg,
- "Nickname '%s' is wrong length or contains illegal characters.",
+ "Nickname '%s', nicknames must be between 1 and 19 characters "
+ "inclusive, and must contain only the characters [a-zA-Z0-9].",
options->Nickname);
return -1;
}
@@ -2770,7 +2862,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
options->TransProxyType_parsed = TPT_TPROXY;
#endif
} else if (!strcasecmp(options->TransProxyType, "ipfw")) {
-#if !defined(__FreeBSD__) && !defined( DARWIN )
+#ifndef KERNEL_MAY_SUPPORT_IPFW
/* Earlier versions of OS X have ipfw */
REJECT("ipfw is a FreeBSD-specific"
"and OS X/Darwin-specific feature.");
@@ -2989,6 +3081,8 @@ options_validate(or_options_t *old_options, or_options_t *options,
}
}
+ /* Terminate Reachable*Addresses with reject *
+ */
for (i=0; i<3; i++) {
config_line_t **linep =
(i==0) ? &options->ReachableAddresses :
@@ -2998,8 +3092,6 @@ options_validate(or_options_t *old_options, or_options_t *options,
continue;
/* We need to end with a reject *:*, not an implicit accept *:* */
for (;;) {
- if (!strcmp((*linep)->value, "reject *:*")) /* already there */
- break;
linep = &((*linep)->next);
if (!*linep) {
*linep = tor_malloc_zero(sizeof(config_line_t));
@@ -3015,11 +3107,29 @@ options_validate(or_options_t *old_options, or_options_t *options,
if ((options->ReachableAddresses ||
options->ReachableORAddresses ||
- options->ReachableDirAddresses) &&
+ options->ReachableDirAddresses ||
+ options->ClientUseIPv4 == 0) &&
server_mode(options))
REJECT("Servers must be able to freely connect to the rest "
"of the Internet, so they must not set Reachable*Addresses "
- "or FascistFirewall.");
+ "or FascistFirewall or FirewallPorts or ClientUseIPv4 0.");
+
+ /* We check if Reachable*Addresses blocks all addresses in
+ * parse_reachable_addresses(). */
+
+#define WARN_PLEASE_USE_IPV6_LOG_MSG \
+ "ClientPreferIPv6%sPort 1 is ignored unless tor is using IPv6. " \
+ "Please set ClientUseIPv6 1, ClientUseIPv4 0, or configure bridges."
+
+ if (!fascist_firewall_use_ipv6(options)
+ && options->ClientPreferIPv6ORPort == 1)
+ log_warn(LD_CONFIG, WARN_PLEASE_USE_IPV6_LOG_MSG, "OR");
+
+ if (!fascist_firewall_use_ipv6(options)
+ && options->ClientPreferIPv6DirPort == 1)
+ log_warn(LD_CONFIG, WARN_PLEASE_USE_IPV6_LOG_MSG, "Dir");
+
+#undef WARN_PLEASE_USE_IPV6_LOG_MSG
if (options->UseBridges &&
server_mode(options))
@@ -3173,6 +3283,21 @@ options_validate(or_options_t *old_options, or_options_t *options,
"http://freehaven.net/anonbib/#hs-attack06 for details.");
}
+ if (options->EntryNodes &&
+ routerset_is_list(options->EntryNodes) &&
+ (routerset_len(options->EntryNodes) == 1) &&
+ (options->RendConfigLines != NULL)) {
+ tor_asprintf(msg,
+ "You have one single EntryNodes and at least one hidden service "
+ "configured. This is bad because it's very easy to locate your "
+ "entry guard which can then lead to the deanonymization of your "
+ "hidden service -- for more details, see "
+ "https://trac.torproject.org/projects/tor/ticket/14917. "
+ "For this reason, the use of one EntryNodes with an hidden "
+ "service is prohibited until a better solution is found.");
+ return -1;
+ }
+
if (!options->LearnCircuitBuildTimeout && options->CircuitBuildTimeout &&
options->CircuitBuildTimeout < RECOMMENDED_MIN_CIRCUIT_BUILD_TIMEOUT) {
log_warn(LD_CONFIG,
@@ -3356,8 +3481,30 @@ options_validate(or_options_t *old_options, or_options_t *options,
options->AccountingRule = ACCT_SUM;
else if (!strcmp(options->AccountingRule_option, "max"))
options->AccountingRule = ACCT_MAX;
+ else if (!strcmp(options->AccountingRule_option, "in"))
+ options->AccountingRule = ACCT_IN;
+ else if (!strcmp(options->AccountingRule_option, "out"))
+ options->AccountingRule = ACCT_OUT;
else
- REJECT("AccountingRule must be 'sum' or 'max'");
+ REJECT("AccountingRule must be 'sum', 'max', 'in', or 'out'");
+ }
+
+ if (options->DirPort_set && !options->DirCache) {
+ REJECT("DirPort configured but DirCache disabled. DirPort requires "
+ "DirCache.");
+ }
+
+ if (options->BridgeRelay && !options->DirCache) {
+ REJECT("We're a bridge but DirCache is disabled. BridgeRelay requires "
+ "DirCache.");
+ }
+
+ if (server_mode(options)) {
+ char *msg = NULL;
+ if (have_enough_mem_for_dircache(options, 0, &msg)) {
+ log_warn(LD_CONFIG, "%s", msg);
+ tor_free(msg);
+ }
}
if (options->HTTPProxy) { /* parse it now */
@@ -3508,6 +3655,13 @@ options_validate(or_options_t *old_options, or_options_t *options,
if (validate_addr_policies(options, msg) < 0)
return -1;
+ /* If FallbackDir is set, we don't UseDefaultFallbackDirs */
+ if (options->UseDefaultFallbackDirs && options->FallbackDir) {
+ log_info(LD_CONFIG, "You have set UseDefaultFallbackDirs 1 and "
+ "FallbackDir(s). Ignoring UseDefaultFallbackDirs, and "
+ "using the FallbackDir(s) you have set.");
+ }
+
if (validate_dir_servers(options, old_options) < 0)
REJECT("Directory authority/fallback line did not parse. See logs "
"for details.");
@@ -3788,11 +3942,41 @@ options_validate(or_options_t *old_options, or_options_t *options,
}
if (options->TestingConsensusMaxDownloadTries < 2) {
- REJECT("TestingConsensusMaxDownloadTries must be greater than 1.");
+ REJECT("TestingConsensusMaxDownloadTries must be greater than 2.");
} else if (options->TestingConsensusMaxDownloadTries > 800) {
COMPLAIN("TestingConsensusMaxDownloadTries is insanely high.");
}
+ if (options->ClientBootstrapConsensusMaxDownloadTries < 2) {
+ REJECT("ClientBootstrapConsensusMaxDownloadTries must be greater "
+ "than 2."
+ );
+ } else if (options->ClientBootstrapConsensusMaxDownloadTries > 800) {
+ COMPLAIN("ClientBootstrapConsensusMaxDownloadTries is insanely "
+ "high.");
+ }
+
+ if (options->ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries
+ < 2) {
+ REJECT("ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries must "
+ "be greater than 2."
+ );
+ } else if (
+ options->ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries
+ > 800) {
+ COMPLAIN("ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries is "
+ "insanely high.");
+ }
+
+ if (options->ClientBootstrapConsensusMaxInProgressTries < 1) {
+ REJECT("ClientBootstrapConsensusMaxInProgressTries must be greater "
+ "than 0.");
+ } else if (options->ClientBootstrapConsensusMaxInProgressTries
+ > 100) {
+ COMPLAIN("ClientBootstrapConsensusMaxInProgressTries is insanely "
+ "high.");
+ }
+
if (options->TestingDescriptorMaxDownloadTries < 2) {
REJECT("TestingDescriptorMaxDownloadTries must be greater than 1.");
} else if (options->TestingDescriptorMaxDownloadTries > 800) {
@@ -3925,6 +4109,52 @@ compute_real_max_mem_in_queues(const uint64_t val, int log_guess)
}
}
+/* If we have less than 300 MB suggest disabling dircache */
+#define DIRCACHE_MIN_MB_BANDWIDTH 300
+#define DIRCACHE_MIN_BANDWIDTH (DIRCACHE_MIN_MB_BANDWIDTH*ONE_MEGABYTE)
+#define STRINGIFY(val) #val
+
+/** Create a warning message for emitting if we are a dircache but may not have
+ * enough system memory, or if we are not a dircache but probably should be.
+ * Return -1 when a message is returned in *msg*, else return 0. */
+STATIC int
+have_enough_mem_for_dircache(const or_options_t *options, size_t total_mem,
+ char **msg)
+{
+ *msg = NULL;
+ /* XXX We should possibly be looking at MaxMemInQueues here
+ * unconditionally. Or we should believe total_mem unconditionally. */
+ if (total_mem == 0) {
+ if (get_total_system_memory(&total_mem) < 0) {
+ total_mem = options->MaxMemInQueues >= SIZE_MAX ?
+ SIZE_MAX : (size_t)options->MaxMemInQueues;
+ }
+ }
+ if (options->DirCache) {
+ if (total_mem < DIRCACHE_MIN_BANDWIDTH) {
+ if (options->BridgeRelay) {
+ *msg = strdup("Running a Bridge with less than "
+ STRINGIFY(DIRCACHE_MIN_MB_BANDWIDTH) " MB of memory is "
+ "not recommended.");
+ } else {
+ *msg = strdup("Being a directory cache (default) with less than "
+ STRINGIFY(DIRCACHE_MIN_MB_BANDWIDTH) " MB of memory is "
+ "not recommended and may consume most of the available "
+ "resources, consider disabling this functionality by "
+ "setting the DirCache option to 0.");
+ }
+ }
+ } else {
+ if (total_mem >= DIRCACHE_MIN_BANDWIDTH) {
+ *msg = strdup("DirCache is disabled and we are configured as a "
+ "relay. This may disqualify us from becoming a guard in the "
+ "future.");
+ }
+ }
+ return *msg == NULL ? 0 : -1;
+}
+#undef STRINGIFY
+
/** Helper: return true iff s1 and s2 are both NULL, or both non-NULL
* equal strings. */
static int
@@ -3972,6 +4202,18 @@ options_transition_allowed(const or_options_t *old,
return -1;
}
+ if (old->KeepBindCapabilities != new_val->KeepBindCapabilities) {
+ *msg = tor_strdup("While Tor is running, changing KeepBindCapabilities is "
+ "not allowed.");
+ return -1;
+ }
+
+ if (!opt_streq(old->SyslogIdentityTag, new_val->SyslogIdentityTag)) {
+ *msg = tor_strdup("While Tor is running, changing "
+ "SyslogIdentityTag is not allowed.");
+ return -1;
+ }
+
if ((old->HardwareAccel != new_val->HardwareAccel)
|| !opt_streq(old->AccelName, new_val->AccelName)
|| !opt_streq(old->AccelDir, new_val->AccelDir)) {
@@ -4020,6 +4262,7 @@ options_transition_allowed(const or_options_t *old,
} \
} while (0)
+ SB_NOCHANGE_STR(Address);
SB_NOCHANGE_STR(PidFile);
SB_NOCHANGE_STR(ServerDNSResolvConfFile);
SB_NOCHANGE_STR(DirPortFrontPage);
@@ -4101,7 +4344,8 @@ options_transition_affects_descriptor(const or_options_t *old_options,
!opt_streq(old_options->MyFamily, new_options->MyFamily) ||
!opt_streq(old_options->AccountingStart, new_options->AccountingStart) ||
old_options->AccountingMax != new_options->AccountingMax ||
- public_server_mode(old_options) != public_server_mode(new_options))
+ public_server_mode(old_options) != public_server_mode(new_options) ||
+ old_options->DirCache != new_options->DirCache)
return 1;
return 0;
@@ -4512,6 +4756,15 @@ options_init_from_torrc(int argc, char **argv)
}
}
+ if (config_line_find(cmdline_only_options, "--newpass")) {
+ if (command == CMD_KEYGEN) {
+ get_options_mutable()->change_key_passphrase = 1;
+ } else {
+ log_err(LD_CONFIG, "--newpass specified without --keygen!");
+ exit(1);
+ }
+ }
+
{
const config_line_t *fd_line = config_line_find(cmdline_only_options,
"--passphrase-fd");
@@ -4537,6 +4790,19 @@ options_init_from_torrc(int argc, char **argv)
}
}
+ {
+ const config_line_t *key_line = config_line_find(cmdline_only_options,
+ "--master-key");
+ if (key_line) {
+ if (command != CMD_KEYGEN) {
+ log_err(LD_CONFIG, "--master-key without --keygen!");
+ exit(1);
+ } else {
+ get_options_mutable()->master_key_fname = tor_strdup(key_line->value);
+ }
+ }
+ }
+
err:
tor_free(cf);
@@ -4891,7 +5157,7 @@ options_init_logs(const or_options_t *old_options, or_options_t *options,
!strcasecmp(smartlist_get(elts,0), "syslog")) {
#ifdef HAVE_SYSLOG_H
if (!validate_only) {
- add_syslog_log(severity);
+ add_syslog_log(severity, options->SyslogIdentityTag);
}
#else
log_warn(LD_CONFIG, "Syslog is not supported on this system. Sorry.");
@@ -5464,13 +5730,14 @@ get_options_for_server_transport(const char *transport)
* (minus whatever bits it's missing) as a valid authority.
* Return 0 on success or filtering out by type,
* or -1 if the line isn't well-formed or if we can't add it. */
-static int
+STATIC int
parse_dir_authority_line(const char *line, dirinfo_type_t required_type,
int validate_only)
{
smartlist_t *items = NULL;
int r;
char *addrport=NULL, *address=NULL, *nickname=NULL, *fingerprint=NULL;
+ tor_addr_port_t ipv6_addrport, *ipv6_addrport_ptr = NULL;
uint16_t dir_port = 0, or_port = 0;
char digest[DIGEST_LEN];
char v3_digest[DIGEST_LEN];
@@ -5527,6 +5794,20 @@ parse_dir_authority_line(const char *line, dirinfo_type_t required_type,
} else {
type |= V3_DIRINFO|EXTRAINFO_DIRINFO|MICRODESC_DIRINFO;
}
+ } else if (!strcasecmpstart(flag, "ipv6=")) {
+ if (ipv6_addrport_ptr) {
+ log_warn(LD_CONFIG, "Redundant ipv6 addr/port on DirAuthority line");
+ } else {
+ if (tor_addr_port_parse(LOG_WARN, flag+strlen("ipv6="),
+ &ipv6_addrport.addr, &ipv6_addrport.port,
+ -1) < 0
+ || tor_addr_family(&ipv6_addrport.addr) != AF_INET6) {
+ log_warn(LD_CONFIG, "Bad ipv6 addr/port %s on DirAuthority line",
+ escaped(flag));
+ goto err;
+ }
+ ipv6_addrport_ptr = &ipv6_addrport;
+ }
} else {
log_warn(LD_CONFIG, "Unrecognized flag '%s' on DirAuthority line",
flag);
@@ -5569,6 +5850,7 @@ parse_dir_authority_line(const char *line, dirinfo_type_t required_type,
log_debug(LD_DIR, "Trusted %d dirserver at %s:%d (%s)", (int)type,
address, (int)dir_port, (char*)smartlist_get(items,0));
if (!(ds = trusted_dir_server_new(nickname, address, dir_port, or_port,
+ ipv6_addrport_ptr,
digest, v3_digest, type, weight)))
goto err;
dir_server_add(ds);
@@ -5606,6 +5888,7 @@ parse_dir_fallback_line(const char *line,
int ok;
char id[DIGEST_LEN];
char *address=NULL;
+ tor_addr_port_t ipv6_addrport, *ipv6_addrport_ptr = NULL;
double weight=1.0;
memset(id, 0, sizeof(id));
@@ -5624,6 +5907,20 @@ parse_dir_fallback_line(const char *line,
} else if (!strcmpstart(cp, "id=")) {
ok = !base16_decode(id, DIGEST_LEN,
cp+strlen("id="), strlen(cp)-strlen("id="));
+ } else if (!strcasecmpstart(cp, "ipv6=")) {
+ if (ipv6_addrport_ptr) {
+ log_warn(LD_CONFIG, "Redundant ipv6 addr/port on FallbackDir line");
+ } else {
+ if (tor_addr_port_parse(LOG_WARN, cp+strlen("ipv6="),
+ &ipv6_addrport.addr, &ipv6_addrport.port,
+ -1) < 0
+ || tor_addr_family(&ipv6_addrport.addr) != AF_INET6) {
+ log_warn(LD_CONFIG, "Bad ipv6 addr/port %s on FallbackDir line",
+ escaped(cp));
+ goto end;
+ }
+ ipv6_addrport_ptr = &ipv6_addrport;
+ }
} else if (!strcmpstart(cp, "weight=")) {
int ok;
const char *wstring = cp + strlen("weight=");
@@ -5665,7 +5962,8 @@ parse_dir_fallback_line(const char *line,
if (!validate_only) {
dir_server_t *ds;
- ds = fallback_dir_server_new(&addr, dirport, orport, id, weight);
+ ds = fallback_dir_server_new(&addr, dirport, orport, ipv6_addrport_ptr,
+ id, weight);
if (!ds) {
log_warn(LD_CONFIG, "Couldn't create FallbackDir %s", escaped(line));
goto end;
@@ -5684,7 +5982,7 @@ parse_dir_fallback_line(const char *line,
}
/** Allocate and return a new port_cfg_t with reasonable defaults. */
-static port_cfg_t *
+STATIC port_cfg_t *
port_cfg_new(size_t namelen)
{
tor_assert(namelen <= SIZE_T_CEILING - sizeof(port_cfg_t) - 1);
@@ -5696,7 +5994,7 @@ port_cfg_new(size_t namelen)
}
/** Free all storage held in <b>port</b> */
-static void
+STATIC void
port_cfg_free(port_cfg_t *port)
{
tor_free(port);
@@ -5750,9 +6048,9 @@ warn_nonlocal_ext_orports(const smartlist_t *ports, const char *portname)
} SMARTLIST_FOREACH_END(port);
}
-/** Given a list of port_cfg_t in <b>ports</b>, warn any controller port there
- * is listening on any non-loopback address. If <b>forbid_nonlocal</b> is
- * true, then emit a stronger warning and remove the port from the list.
+/** Given a list of port_cfg_t in <b>ports</b>, warn if any controller port
+ * there is listening on any non-loopback address. If <b>forbid_nonlocal</b>
+ * is true, then emit a stronger warning and remove the port from the list.
*/
static void
warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid_nonlocal)
@@ -5792,15 +6090,6 @@ warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid_nonlocal)
} SMARTLIST_FOREACH_END(port);
}
-#define CL_PORT_NO_STREAM_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)
-#define CL_PORT_TAKES_HOSTNAMES (1u<<5)
-#define CL_PORT_IS_UNIXSOCKET (1u<<6)
-#define CL_PORT_DFLT_GROUP_WRITABLE (1u<<7)
-
#ifdef HAVE_SYS_UN_H
/** Parse the given <b>addrport</b> and set <b>path_out</b> if a Unix socket
@@ -5888,7 +6177,7 @@ config_parse_unix_port(const char *addrport, char **path_out)
* <b>out</b> for every port that the client should listen on. Return 0
* on success, -1 on failure.
*/
-static int
+STATIC int
parse_port_config(smartlist_t *out,
const config_line_t *ports,
const config_line_t *listenaddrs,
@@ -6022,6 +6311,7 @@ parse_port_config(smartlist_t *out,
int sessiongroup = SESSION_GROUP_UNSET;
unsigned isolation = ISO_DEFAULT;
int prefer_no_auth = 0;
+ int socks_iso_keep_alive = 0;
char *addrport;
uint16_t ptmp=0;
@@ -6031,7 +6321,9 @@ parse_port_config(smartlist_t *out,
ipv4_traffic = 1, ipv6_traffic = 0, prefer_ipv6 = 0,
cache_ipv4 = 1, use_cached_ipv4 = 0,
cache_ipv6 = 0, use_cached_ipv6 = 0,
- prefer_ipv6_automap = 1, world_writable = 0, group_writable = 0;
+ prefer_ipv6_automap = 1, world_writable = 0, group_writable = 0,
+ relax_dirmode_check = 0,
+ has_used_unix_socket_only_option = 0;
smartlist_split_string(elts, ports->value, NULL,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
@@ -6079,6 +6371,7 @@ parse_port_config(smartlist_t *out,
tor_free(addrtmp);
goto err;
}
+ tor_free(addrtmp);
} else {
/* Try parsing integer port before address, because, who knows?
"9050" might be a valid address. */
@@ -6093,7 +6386,7 @@ parse_port_config(smartlist_t *out,
}
port = ptmp;
} else {
- log_warn(LD_CONFIG, "Couldn't parse address '%s' for %sPort",
+ log_warn(LD_CONFIG, "Couldn't parse address %s for %sPort",
escaped(addrport), portname);
goto err;
}
@@ -6183,9 +6476,15 @@ parse_port_config(smartlist_t *out,
if (!strcasecmp(elt, "GroupWritable")) {
group_writable = !no;
+ has_used_unix_socket_only_option = 1;
continue;
} else if (!strcasecmp(elt, "WorldWritable")) {
world_writable = !no;
+ has_used_unix_socket_only_option = 1;
+ continue;
+ } else if (!strcasecmp(elt, "RelaxDirModeCheck")) {
+ relax_dirmode_check = !no;
+ has_used_unix_socket_only_option = 1;
continue;
}
@@ -6231,6 +6530,9 @@ parse_port_config(smartlist_t *out,
} else if (!strcasecmp(elt, "PreferSOCKSNoAuth")) {
prefer_no_auth = ! no;
continue;
+ } else if (!strcasecmp(elt, "KeepAliveIsolateSOCKSAuth")) {
+ socks_iso_keep_alive = ! no;
+ continue;
}
if (!strcasecmpend(elt, "s"))
@@ -6270,9 +6572,17 @@ parse_port_config(smartlist_t *out,
goto err;
}
- if ( (world_writable || group_writable) && ! unix_socket_path) {
- log_warn(LD_CONFIG, "You have a %sPort entry with GroupWritable "
- "or WorldWritable set, but it is not a unix socket.", portname);
+ if ( has_used_unix_socket_only_option && ! unix_socket_path) {
+ log_warn(LD_CONFIG, "You have a %sPort entry with GroupWritable, "
+ "WorldWritable, or RelaxDirModeCheck, but it is not a "
+ "unix socket.", portname);
+ goto err;
+ }
+
+ if (!(isolation & ISO_SOCKSAUTH) && socks_iso_keep_alive) {
+ log_warn(LD_CONFIG, "You have a %sPort entry with both "
+ "NoIsolateSOCKSAuth and KeepAliveIsolateSOCKSAuth set.",
+ portname);
goto err;
}
@@ -6291,6 +6601,7 @@ parse_port_config(smartlist_t *out,
cfg->type = listener_type;
cfg->is_world_writable = world_writable;
cfg->is_group_writable = group_writable;
+ cfg->relax_dirmode_check = relax_dirmode_check;
cfg->entry_cfg.isolation_flags = isolation;
cfg->entry_cfg.session_group = sessiongroup;
cfg->server_cfg.no_advertise = no_advertise;
@@ -6309,6 +6620,7 @@ parse_port_config(smartlist_t *out,
cfg->entry_cfg.socks_prefer_no_auth = prefer_no_auth;
if (! (isolation & ISO_SOCKSAUTH))
cfg->entry_cfg.socks_prefer_no_auth = 1;
+ cfg->entry_cfg.socks_iso_keep_alive = socks_iso_keep_alive;
smartlist_add(out, cfg);
}
@@ -6470,10 +6782,13 @@ parse_ports(or_options_t *options, int validate_only,
}
}
- if (check_server_ports(ports, options) < 0) {
+ int n_low_ports = 0;
+ if (check_server_ports(ports, options, &n_low_ports) < 0) {
*msg = tor_strdup("Misconfigured server ports");
goto err;
}
+ if (have_low_ports < 0)
+ have_low_ports = (n_low_ports > 0);
*n_ports_out = smartlist_len(ports);
@@ -6527,10 +6842,12 @@ parse_ports(or_options_t *options, int validate_only,
}
/** Given a list of <b>port_cfg_t</b> in <b>ports</b>, check them for internal
- * consistency and warn as appropriate. */
+ * consistency and warn as appropriate. Set *<b>n_low_ports_out</b> to the
+ * number of sub-1024 ports we will be binding. */
static int
check_server_ports(const smartlist_t *ports,
- const or_options_t *options)
+ const or_options_t *options,
+ int *n_low_ports_out)
{
int n_orport_advertised = 0;
int n_orport_advertised_ipv4 = 0;
@@ -6593,23 +6910,31 @@ check_server_ports(const smartlist_t *ports,
r = -1;
}
- if (n_low_port && options->AccountingMax) {
+ if (n_low_port && options->AccountingMax &&
+ (!have_capability_support() || options->KeepBindCapabilities == 0)) {
+ const char *extra = "";
+ if (options->KeepBindCapabilities == 0 && have_capability_support())
+ extra = ", and you have disabled KeepBindCapabilities.";
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 "
+ "chosen a low DirPort or OrPort%s."
+ "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.");
+ "platform.", extra);
}
+ if (n_low_ports_out)
+ *n_low_ports_out = n_low_port;
+
return r;
}
/** Return a list of port_cfg_t for client ports parsed from the
* options. */
-const smartlist_t *
-get_configured_ports(void)
+MOCK_IMPL(const smartlist_t *,
+get_configured_ports,(void))
{
if (!configured_ports)
configured_ports = smartlist_new();
@@ -7093,7 +7418,7 @@ getinfo_helper_config(control_connection_t *conn,
smartlist_free(sl);
} else if (!strcmp(question, "config/defaults")) {
smartlist_t *sl = smartlist_new();
- int i, dirauth_lines_seen = 0;
+ int i, dirauth_lines_seen = 0, fallback_lines_seen = 0;
for (i = 0; option_vars_[i].name; ++i) {
const config_var_t *var = &option_vars_[i];
if (var->initvalue != NULL) {
@@ -7104,6 +7429,13 @@ getinfo_helper_config(control_connection_t *conn,
*/
++dirauth_lines_seen;
}
+ if (strcmp(option_vars_[i].name, "FallbackDir") == 0) {
+ /*
+ * Similarly count fallback lines, so that we can decided later
+ * to add the defaults manually.
+ */
+ ++fallback_lines_seen;
+ }
char *val = esc_for_log(var->initvalue);
smartlist_add_asprintf(sl, "%s %s\n",var->name,val);
tor_free(val);
@@ -7129,6 +7461,24 @@ getinfo_helper_config(control_connection_t *conn,
}
}
+ if (fallback_lines_seen == 0 &&
+ get_options()->UseDefaultFallbackDirs == 1) {
+ /*
+ * We didn't see any explicitly configured fallback mirrors,
+ * so add the defaults to the list manually.
+ *
+ * default_fallbacks is included earlier in this file and
+ * is a const char ** NULL-terminated array of fallback config lines.
+ */
+ const char **i;
+
+ for (i = default_fallbacks; *i != NULL; ++i) {
+ char *val = esc_for_log(*i);
+ smartlist_add_asprintf(sl, "FallbackDir %s\n", val);
+ tor_free(val);
+ }
+ }
+
*answer = smartlist_join_strings(sl, "", 0, NULL);
SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
smartlist_free(sl);
@@ -7271,8 +7621,7 @@ init_cookie_authentication(const char *fname, const char *header,
/* Generate the cookie */
*cookie_out = tor_malloc(cookie_len);
- if (crypto_rand((char *)*cookie_out, cookie_len) < 0)
- goto done;
+ crypto_rand((char *)*cookie_out, cookie_len);
/* Create the string that should be written on the file. */
memcpy(cookie_file_str, header, strlen(header));
diff --git a/src/or/config.h b/src/or/config.h
index 0ee1e1a3c4..02121cf95c 100644
--- a/src/or/config.h
+++ b/src/or/config.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -14,9 +14,13 @@
#include "testsupport.h"
-const char *get_dirportfrontpage(void);
-MOCK_DECL(const or_options_t *,get_options,(void));
-or_options_t *get_options_mutable(void);
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(DARWIN)
+#define KERNEL_MAY_SUPPORT_IPFW
+#endif
+
+MOCK_DECL(const char*, get_dirportfrontpage, (void));
+MOCK_DECL(const or_options_t *, get_options, (void));
+MOCK_DECL(or_options_t *, get_options_mutable, (void));
int set_options(or_options_t *new_val, char **msg);
void config_free_all(void);
const char *safe_str_client(const char *address);
@@ -76,7 +80,7 @@ int write_to_data_subdir(const char* subdir, const char* fname,
int get_num_cpus(const or_options_t *options);
-const smartlist_t *get_configured_ports(void);
+MOCK_DECL(const smartlist_t *,get_configured_ports,(void));
int get_first_advertised_port_by_type_af(int listener_type,
int address_family);
#define get_primary_or_port() \
@@ -136,10 +140,23 @@ smartlist_t *get_options_from_transport_options_line(const char *line,
smartlist_t *get_options_for_server_transport(const char *transport);
#ifdef CONFIG_PRIVATE
+
+#define CL_PORT_NO_STREAM_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)
+#define CL_PORT_TAKES_HOSTNAMES (1u<<5)
+#define CL_PORT_IS_UNIXSOCKET (1u<<6)
+#define CL_PORT_DFLT_GROUP_WRITABLE (1u<<7)
+
+STATIC int options_act(const or_options_t *old_options);
#ifdef TOR_UNIT_TESTS
extern struct config_format_t options_format;
#endif
+STATIC port_cfg_t *port_cfg_new(size_t namelen);
+STATIC void port_cfg_free(port_cfg_t *port);
STATIC void or_options_free(or_options_t *options);
STATIC int options_validate(or_options_t *old_options,
or_options_t *options,
@@ -150,10 +167,22 @@ STATIC int parse_transport_line(const or_options_t *options,
int server);
STATIC int consider_adding_dir_servers(const or_options_t *options,
const or_options_t *old_options);
+STATIC void add_default_trusted_dir_authorities(dirinfo_type_t type);
MOCK_DECL(STATIC void, add_default_fallback_dir_servers, (void));
-STATIC int
-parse_dir_fallback_line(const char *line,
- int validate_only);
+STATIC int parse_dir_authority_line(const char *line,
+ dirinfo_type_t required_type,
+ int validate_only);
+STATIC int parse_dir_fallback_line(const char *line, int validate_only);
+STATIC int have_enough_mem_for_dircache(const or_options_t *options,
+ size_t total_mem, char **msg);
+STATIC int parse_port_config(smartlist_t *out,
+ const config_line_t *ports,
+ const config_line_t *listenaddrs,
+ const char *portname,
+ int listener_type,
+ const char *defaultaddr,
+ int defaultport,
+ const unsigned flags);
#endif
#endif
diff --git a/src/or/confparse.c b/src/or/confparse.c
index ac21df25cb..4f446d07c3 100644
--- a/src/or/confparse.c
+++ b/src/or/confparse.c
@@ -1,9 +1,16 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+/**
+ * \file confparse.c
+ *
+ * \brief Back-end for parsing and generating key-value files, used to
+ * implement the torrc file format and the state file.
+ */
+
#include "or.h"
#include "confparse.h"
#include "routerset.h"
diff --git a/src/or/confparse.h b/src/or/confparse.h
index 83c0f75b52..885c615202 100644
--- a/src/or/confparse.h
+++ b/src/or/confparse.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_CONFPARSE_H
diff --git a/src/or/connection.c b/src/or/connection.c
index 78176d3768..1bd1a92e39 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -19,6 +19,7 @@
*/
#define TOR_CHANNEL_INTERNAL_
#define CONNECTION_PRIVATE
+#include "backtrace.h"
#include "channel.h"
#include "channeltls.h"
#include "circuitbuild.h"
@@ -37,6 +38,7 @@
#include "ext_orport.h"
#include "geoip.h"
#include "main.h"
+#include "nodelist.h"
#include "policies.h"
#include "reasons.h"
#include "relay.h"
@@ -44,8 +46,10 @@
#include "rendcommon.h"
#include "rephist.h"
#include "router.h"
+#include "routerlist.h"
#include "transports.h"
#include "routerparse.h"
+#include "sandbox.h"
#include "transports.h"
#ifdef USE_BUFFEREVENTS
@@ -661,9 +665,7 @@ connection_free,(connection_t *conn))
return;
tor_assert(!connection_is_on_closeable_list(conn));
tor_assert(!connection_in_array(conn));
- if (conn->linked_conn) {
- log_err(LD_BUG, "Called with conn->linked_conn still set.");
- tor_fragile_assert();
+ if (BUG(conn->linked_conn)) {
conn->linked_conn->linked_conn = NULL;
if (! conn->linked_conn->marked_for_close &&
conn->linked_conn->reading_from_linked_conn)
@@ -678,6 +680,13 @@ connection_free,(connection_t *conn))
if (conn->type == CONN_TYPE_CONTROL) {
connection_control_closed(TO_CONTROL_CONN(conn));
}
+#if 1
+ /* DEBUGGING */
+ if (conn->type == CONN_TYPE_AP) {
+ connection_ap_warn_and_unmark_if_pending_circ(TO_ENTRY_CONN(conn),
+ "connection_free");
+ }
+#endif
connection_unregister_events(conn);
connection_free_(conn);
}
@@ -1004,6 +1013,10 @@ check_location_for_unix_socket(const or_options_t *options, const char *path,
flags |= CPD_GROUP_OK;
}
+ if (port->relax_dirmode_check) {
+ flags |= CPD_RELAX_DIRMODE_CHECK;
+ }
+
if (check_private_dir(p, flags, options->User) < 0) {
char *escpath, *escdir;
escpath = esc_for_log(path);
@@ -1051,6 +1064,31 @@ make_socket_reuseable(tor_socket_t sock)
#endif
}
+#ifdef _WIN32
+/** Tell the Windows TCP stack to prevent other applications from receiving
+ * traffic from tor's open ports. Return 0 on success, -1 on failure. */
+static int
+make_win32_socket_exclusive(tor_socket_t sock)
+{
+#ifdef SO_EXCLUSIVEADDRUSE
+ int one=1;
+
+ /* Any socket that sets REUSEADDR on win32 can bind to a port _even when
+ * somebody else already has it bound_, and _even if the original socket
+ * didn't set REUSEADDR_. Use EXCLUSIVEADDRUSE to prevent this port-stealing
+ * on win32. */
+ if (setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (void*) &one,
+ (socklen_t)sizeof(one))) {
+ return -1;
+ }
+ return 0;
+#else
+ (void) sock;
+ return 0;
+#endif
+}
+#endif
+
/** Max backlog to pass to listen. We start at */
static int listen_limit = INT_MAX;
@@ -1104,7 +1142,6 @@ connection_listener_new(const struct sockaddr *listensockaddr,
start_reading = 1;
tor_addr_from_sockaddr(&addr, listensockaddr, &usePort);
-
log_notice(LD_NET, "Opening %s on %s",
conn_type_to_string(type), fmt_addrport(&addr, usePort));
@@ -1128,11 +1165,20 @@ connection_listener_new(const struct sockaddr *listensockaddr,
tor_socket_strerror(errno));
}
-#if defined USE_TRANSPARENT && defined(IP_TRANSPARENT)
+#ifdef _WIN32
+ if (make_win32_socket_exclusive(s) < 0) {
+ log_warn(LD_NET, "Error setting SO_EXCLUSIVEADDRUSE flag on %s: %s",
+ conn_type_to_string(type),
+ tor_socket_strerror(errno));
+ }
+#endif
+
+#if defined(USE_TRANSPARENT) && defined(IP_TRANSPARENT)
if (options->TransProxyType_parsed == TPT_TPROXY &&
type == CONN_TYPE_AP_TRANS_LISTENER) {
int one = 1;
- if (setsockopt(s, SOL_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0) {
+ if (setsockopt(s, SOL_IP, IP_TRANSPARENT, (void*)&one,
+ (socklen_t)sizeof(one)) < 0) {
const char *extra = "";
int e = tor_socket_errno(s);
if (e == EPERM)
@@ -1146,16 +1192,11 @@ connection_listener_new(const struct sockaddr *listensockaddr,
#ifdef IPV6_V6ONLY
if (listensockaddr->sa_family == AF_INET6) {
-#ifdef _WIN32
- /* In Redmond, this kind of thing passes for standards-conformance. */
- DWORD one = 1;
-#else
int one = 1;
-#endif
/* We need to set IPV6_V6ONLY so that this socket can't get used for
* IPv4 connections. */
if (setsockopt(s,IPPROTO_IPV6, IPV6_V6ONLY,
- (void*)&one, sizeof(one)) < 0) {
+ (void*)&one, (socklen_t)sizeof(one)) < 0) {
int e = tor_socket_errno(s);
log_warn(LD_NET, "Error setting IPV6_V6ONLY flag: %s",
tor_socket_strerror(e));
@@ -1245,11 +1286,16 @@ connection_listener_new(const struct sockaddr *listensockaddr,
#ifdef HAVE_PWD_H
if (options->User) {
pw = tor_getpwnam(options->User);
+ struct stat st;
if (pw == NULL) {
log_warn(LD_NET,"Unable to chown() %s socket: user %s not found.",
address, options->User);
goto err;
- } else if (chown(address, pw->pw_uid, pw->pw_gid) < 0) {
+ } else if (fstat(s, &st) == 0 &&
+ st.st_uid == pw->pw_uid && st.st_gid == pw->pw_gid) {
+ /* No change needed */
+ } else if (chown(sandbox_intern_string(address),
+ pw->pw_uid, pw->pw_gid) < 0) {
log_warn(LD_NET,"Unable to chown() %s socket: %s.",
address, strerror(errno));
goto err;
@@ -1260,6 +1306,7 @@ connection_listener_new(const struct sockaddr *listensockaddr,
{
unsigned mode;
const char *status;
+ struct stat st;
if (port_cfg->is_world_writable) {
mode = 0666;
status = "world-writable";
@@ -1272,7 +1319,9 @@ connection_listener_new(const struct sockaddr *listensockaddr,
}
/* We need to use chmod; fchmod doesn't work on sockets on all
* platforms. */
- if (chmod(address, mode) < 0) {
+ if (fstat(s, &st) == 0 && (st.st_mode & 0777) == mode) {
+ /* no change needed */
+ } else if (chmod(sandbox_intern_string(address), mode) < 0) {
log_warn(LD_FS,"Unable to make %s %s.", address, status);
goto err;
}
@@ -1437,7 +1486,7 @@ connection_handle_listener_read(connection_t *conn, int new_type)
if (!SOCKET_OK(news)) { /* accept() error */
int e = tor_socket_errno(conn->s);
if (ERRNO_IS_ACCEPT_EAGAIN(e)) {
- return 0; /* he hung up before we could accept(). that's fine. */
+ return 0; /* they hung up before we could accept(). that's fine. */
} else if (ERRNO_IS_RESOURCE_LIMIT(e)) {
warn_too_many_conns();
return 0;
@@ -1513,7 +1562,7 @@ connection_handle_listener_read(connection_t *conn, int new_type)
/* remember the remote address */
tor_addr_copy(&newconn->addr, &addr);
newconn->port = port;
- newconn->address = tor_dup_addr(&addr);
+ newconn->address = tor_addr_to_str_dup(&addr);
if (new_type == CONN_TYPE_AP && conn->socket_family != AF_UNIX) {
log_info(LD_NET, "New SOCKS connection opened from %s.",
@@ -1597,6 +1646,8 @@ connection_init_accepted_conn(connection_t *conn,
break;
case CONN_TYPE_AP_TRANS_LISTENER:
TO_ENTRY_CONN(conn)->is_transparent_ap = 1;
+ /* XXXX028 -- is this correct still, with the addition of
+ * pending_entry_connections ? */
conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
return connection_ap_process_transparent(TO_ENTRY_CONN(conn));
case CONN_TYPE_AP_NATD_LISTENER:
@@ -1616,13 +1667,18 @@ connection_init_accepted_conn(connection_t *conn,
return 0;
}
-static int
-connection_connect_sockaddr(connection_t *conn,
+/** Take conn, make a nonblocking socket; try to connect to
+ * sa, binding to bindaddr if sa is not localhost. If fail, return -1 and if
+ * applicable put your best guess about errno into *<b>socket_error</b>.
+ * If connected return 1, if EAGAIN return 0.
+ */
+MOCK_IMPL(STATIC int,
+connection_connect_sockaddr,(connection_t *conn,
const struct sockaddr *sa,
socklen_t sa_len,
const struct sockaddr *bindaddr,
socklen_t bindaddr_len,
- int *socket_error)
+ int *socket_error))
{
tor_socket_t s;
int inprogress = 0;
@@ -1705,11 +1761,87 @@ connection_connect_sockaddr(connection_t *conn,
return inprogress ? 0 : 1;
}
+/* Log a message if connection attempt is made when IPv4 or IPv6 is disabled.
+ * Log a less severe message if we couldn't conform to ClientPreferIPv6ORPort
+ * or ClientPreferIPv6ORPort. */
+static void
+connection_connect_log_client_use_ip_version(const connection_t *conn)
+{
+ const or_options_t *options = get_options();
+
+ /* Only clients care about ClientUseIPv4/6, bail out early on servers, and
+ * on connections we don't care about */
+ if (server_mode(options) || !conn || conn->type == CONN_TYPE_EXIT) {
+ return;
+ }
+
+ /* We're only prepared to log OR and DIR connections here */
+ if (conn->type != CONN_TYPE_OR && conn->type != CONN_TYPE_DIR) {
+ return;
+ }
+
+ const int must_ipv4 = !fascist_firewall_use_ipv6(options);
+ const int must_ipv6 = (options->ClientUseIPv4 == 0);
+ const int pref_ipv6 = (conn->type == CONN_TYPE_OR
+ ? fascist_firewall_prefer_ipv6_orport(options)
+ : fascist_firewall_prefer_ipv6_dirport(options));
+ tor_addr_t real_addr;
+ tor_addr_make_null(&real_addr, AF_UNSPEC);
+
+ /* OR conns keep the original address in real_addr, as addr gets overwritten
+ * with the descriptor address */
+ if (conn->type == CONN_TYPE_OR) {
+ const or_connection_t *or_conn = TO_OR_CONN((connection_t *)conn);
+ tor_addr_copy(&real_addr, &or_conn->real_addr);
+ } else if (conn->type == CONN_TYPE_DIR) {
+ tor_addr_copy(&real_addr, &conn->addr);
+ }
+
+ /* Check if we broke a mandatory address family restriction */
+ if ((must_ipv4 && tor_addr_family(&real_addr) == AF_INET6)
+ || (must_ipv6 && tor_addr_family(&real_addr) == AF_INET)) {
+ static int logged_backtrace = 0;
+ log_info(LD_BUG, "Outgoing %s connection to %s violated ClientUseIPv%s 0.",
+ conn->type == CONN_TYPE_OR ? "OR" : "Dir",
+ fmt_addr(&real_addr),
+ options->ClientUseIPv4 == 0 ? "4" : "6");
+ if (!logged_backtrace) {
+ log_backtrace(LOG_INFO, LD_BUG, "Address came from");
+ logged_backtrace = 1;
+ }
+ }
+
+ /* Bridges are allowed to break IPv4/IPv6 ORPort preferences to connect to
+ * the node's configured address when ClientPreferIPv6ORPort is auto */
+ if (options->UseBridges && conn->type == CONN_TYPE_OR
+ && options->ClientPreferIPv6ORPort == -1) {
+ return;
+ }
+
+ /* Check if we couldn't satisfy an address family preference */
+ if ((!pref_ipv6 && tor_addr_family(&real_addr) == AF_INET6)
+ || (pref_ipv6 && tor_addr_family(&real_addr) == AF_INET)) {
+ log_info(LD_NET, "Outgoing connection to %s doesn't satisfy "
+ "ClientPreferIPv6%sPort %d, with ClientUseIPv4 %d, and "
+ "fascist_firewall_use_ipv6 %d (ClientUseIPv6 %d and UseBridges "
+ "%d).",
+ fmt_addr(&real_addr),
+ conn->type == CONN_TYPE_OR ? "OR" : "Dir",
+ conn->type == CONN_TYPE_OR ? options->ClientPreferIPv6ORPort
+ : options->ClientPreferIPv6DirPort,
+ options->ClientUseIPv4, fascist_firewall_use_ipv6(options),
+ options->ClientUseIPv6, options->UseBridges);
+ }
+}
+
/** Take conn, make a nonblocking socket; try to connect to
- * addr:port (they arrive in *host order*). If fail, return -1 and if
+ * addr:port (port arrives in *host order*). If fail, return -1 and if
* applicable put your best guess about errno into *<b>socket_error</b>.
* Else assign s to conn-\>s: if connected return 1, if EAGAIN return 0.
*
+ * addr:port can be different to conn->addr:conn->port if connecting through
+ * a proxy.
+ *
* address is used to make the logs useful.
*
* On success, add conn to the list of polled connections.
@@ -1726,6 +1858,10 @@ connection_connect(connection_t *conn, const char *address,
const or_options_t *options = get_options();
int protocol_family;
+ /* Log if we didn't stick to ClientUseIPv4/6 or ClientPreferIPv6OR/DirPort
+ */
+ connection_connect_log_client_use_ip_version(conn);
+
if (tor_addr_family(addr) == AF_INET6)
protocol_family = PF_INET6;
else
@@ -2381,6 +2517,15 @@ retry_listener_ports(smartlist_t *old_conns,
if (port->server_cfg.no_listen)
continue;
+#ifndef _WIN32
+ /* We don't need to be root to create a UNIX socket, so defer until after
+ * setuid. */
+ const or_options_t *options = get_options();
+ if (port->is_unix_addr && !geteuid() && (options->User) &&
+ strcmp(options->User, "root"))
+ continue;
+#endif
+
if (port->is_unix_addr) {
listensockaddr = (struct sockaddr *)
create_unix_sockaddr(port->unix_addr,
@@ -2391,7 +2536,7 @@ retry_listener_ports(smartlist_t *old_conns,
real_port,
listensockaddr,
sizeof(struct sockaddr_storage));
- address = tor_dup_addr(&port->addr);
+ address = tor_addr_to_str_dup(&port->addr);
}
if (listensockaddr) {
@@ -3497,7 +3642,7 @@ connection_read_to_buf(connection_t *conn, ssize_t *max_to_read,
* take us over our read allotment, but really we shouldn't be
* believing that SSL bytes are the same as TCP bytes anyway. */
int r2 = read_to_buf_tls(or_conn->tls, pending, conn->inbuf);
- if (r2<0) {
+ if (BUG(r2<0)) {
log_warn(LD_BUG, "apparently, reading pending bytes can fail.");
return -1;
}
@@ -3585,7 +3730,7 @@ connection_read_to_buf(connection_t *conn, ssize_t *max_to_read,
}
/* Call even if result is 0, since the global read bucket may
- * have reached 0 on a different conn, and this guy needs to
+ * have reached 0 on a different conn, and this connection needs to
* know to stop reading. */
connection_consider_empty_read_buckets(conn);
if (n_written > 0 && connection_is_writing(conn))
@@ -4081,7 +4226,7 @@ connection_handle_write_impl(connection_t *conn, int force)
}
/* Call even if result is 0, since the global write bucket may
- * have reached 0 on a different conn, and this guy needs to
+ * have reached 0 on a different conn, and this connection needs to
* know to stop writing. */
connection_consider_empty_write_buckets(conn);
if (n_read > 0 && connection_is_reading(conn))
@@ -4209,24 +4354,32 @@ connection_write_to_buf_impl_,(const char *string, size_t len,
}
}
+/** Return a connection_t * from get_connection_array() that satisfies test on
+ * var, and that is not marked for close. */
+#define CONN_GET_TEMPLATE(var, test) \
+ STMT_BEGIN \
+ smartlist_t *conns = get_connection_array(); \
+ SMARTLIST_FOREACH(conns, connection_t *, var, \
+ { \
+ if (var && (test) && !var->marked_for_close) \
+ return var; \
+ }); \
+ return NULL; \
+ STMT_END
+
/** Return a connection with given type, address, port, and purpose;
- * or NULL if no such connection exists. */
-connection_t *
-connection_get_by_type_addr_port_purpose(int type,
+ * or NULL if no such connection exists (or if all such connections are marked
+ * for close). */
+MOCK_IMPL(connection_t *,
+connection_get_by_type_addr_port_purpose,(int type,
const tor_addr_t *addr, uint16_t port,
- int purpose)
+ int purpose))
{
- smartlist_t *conns = get_connection_array();
- SMARTLIST_FOREACH(conns, connection_t *, conn,
- {
- if (conn->type == type &&
+ CONN_GET_TEMPLATE(conn,
+ (conn->type == type &&
tor_addr_eq(&conn->addr, addr) &&
conn->port == port &&
- conn->purpose == purpose &&
- !conn->marked_for_close)
- return conn;
- });
- return NULL;
+ conn->purpose == purpose));
}
/** Return the stream with id <b>id</b> if it is not already marked for
@@ -4235,13 +4388,7 @@ connection_get_by_type_addr_port_purpose(int type,
connection_t *
connection_get_by_global_id(uint64_t id)
{
- smartlist_t *conns = get_connection_array();
- SMARTLIST_FOREACH(conns, connection_t *, conn,
- {
- if (conn->global_identifier == id)
- return conn;
- });
- return NULL;
+ CONN_GET_TEMPLATE(conn, conn->global_identifier == id);
}
/** Return a connection of type <b>type</b> that is not marked for close.
@@ -4249,13 +4396,7 @@ connection_get_by_global_id(uint64_t id)
connection_t *
connection_get_by_type(int type)
{
- smartlist_t *conns = get_connection_array();
- SMARTLIST_FOREACH(conns, connection_t *, conn,
- {
- if (conn->type == type && !conn->marked_for_close)
- return conn;
- });
- return NULL;
+ CONN_GET_TEMPLATE(conn, conn->type == type);
}
/** Return a connection of type <b>type</b> that is in state <b>state</b>,
@@ -4264,13 +4405,7 @@ connection_get_by_type(int type)
connection_t *
connection_get_by_type_state(int type, int state)
{
- smartlist_t *conns = get_connection_array();
- SMARTLIST_FOREACH(conns, connection_t *, conn,
- {
- if (conn->type == type && conn->state == state && !conn->marked_for_close)
- return conn;
- });
- return NULL;
+ CONN_GET_TEMPLATE(conn, conn->type == type && conn->state == state);
}
/** Return a connection of type <b>type</b> that has rendquery equal
@@ -4281,55 +4416,141 @@ connection_t *
connection_get_by_type_state_rendquery(int type, int state,
const char *rendquery)
{
- smartlist_t *conns = get_connection_array();
-
tor_assert(type == CONN_TYPE_DIR ||
type == CONN_TYPE_AP || type == CONN_TYPE_EXIT);
tor_assert(rendquery);
- SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
- if (conn->type == type &&
- !conn->marked_for_close &&
- (!state || state == conn->state)) {
- if (type == CONN_TYPE_DIR &&
+ CONN_GET_TEMPLATE(conn,
+ (conn->type == type &&
+ (!state || state == conn->state)) &&
+ (
+ (type == CONN_TYPE_DIR &&
TO_DIR_CONN(conn)->rend_data &&
!rend_cmp_service_ids(rendquery,
TO_DIR_CONN(conn)->rend_data->onion_address))
- return conn;
- else if (CONN_IS_EDGE(conn) &&
+ ||
+ (CONN_IS_EDGE(conn) &&
TO_EDGE_CONN(conn)->rend_data &&
!rend_cmp_service_ids(rendquery,
TO_EDGE_CONN(conn)->rend_data->onion_address))
- return conn;
- }
- } SMARTLIST_FOREACH_END(conn);
- return NULL;
+ ));
}
+#define CONN_FIRST_AND_FREE_TEMPLATE(sl) \
+ STMT_BEGIN \
+ if (smartlist_len(sl) > 0) { \
+ void *first_item = smartlist_get(sl, 0); \
+ smartlist_free(sl); \
+ return first_item; \
+ } else { \
+ smartlist_free(sl); \
+ return NULL; \
+ } \
+ STMT_END
+
/** Return a directory connection (if any one exists) that is fetching
- * the item described by <b>state</b>/<b>resource</b> */
+ * the item described by <b>purpose</b>/<b>resource</b>, otherwise return NULL.
+ */
dir_connection_t *
-connection_dir_get_by_purpose_and_resource(int purpose,
+connection_dir_get_by_purpose_and_resource(
+ int purpose,
const char *resource)
{
- smartlist_t *conns = get_connection_array();
+ smartlist_t *conns = connection_dir_list_by_purpose_and_resource(
+ purpose,
+ resource);
+ CONN_FIRST_AND_FREE_TEMPLATE(conns);
+}
- SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
- dir_connection_t *dirconn;
- if (conn->type != CONN_TYPE_DIR || conn->marked_for_close ||
- conn->purpose != purpose)
- continue;
- dirconn = TO_DIR_CONN(conn);
- if (dirconn->requested_resource == NULL) {
- if (resource == NULL)
- return dirconn;
- } else if (resource) {
- if (0 == strcmp(resource, dirconn->requested_resource))
- return dirconn;
- }
- } SMARTLIST_FOREACH_END(conn);
+/** Return a new smartlist of dir_connection_t * from get_connection_array()
+ * that satisfy conn_test on connection_t *conn_var, and dirconn_test on
+ * dir_connection_t *dirconn_var. conn_var must be of CONN_TYPE_DIR and not
+ * marked for close to be included in the list. */
+#define DIR_CONN_LIST_TEMPLATE(conn_var, conn_test, \
+ dirconn_var, dirconn_test) \
+ STMT_BEGIN \
+ smartlist_t *conns = get_connection_array(); \
+ smartlist_t *dir_conns = smartlist_new(); \
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn_var) { \
+ if (conn_var && (conn_test) \
+ && conn_var->type == CONN_TYPE_DIR \
+ && !conn_var->marked_for_close) { \
+ dir_connection_t *dirconn_var = TO_DIR_CONN(conn_var); \
+ if (dirconn_var && (dirconn_test)) { \
+ smartlist_add(dir_conns, dirconn_var); \
+ } \
+ } \
+ } SMARTLIST_FOREACH_END(conn_var); \
+ return dir_conns; \
+ STMT_END
+
+/** Return a list of directory connections that are fetching the item
+ * described by <b>purpose</b>/<b>resource</b>. If there are none,
+ * return an empty list. This list must be freed using smartlist_free,
+ * but the pointers in it must not be freed.
+ * Note that this list should not be cached, as the pointers in it can be
+ * freed if their connections close. */
+smartlist_t *
+connection_dir_list_by_purpose_and_resource(
+ int purpose,
+ const char *resource)
+{
+ DIR_CONN_LIST_TEMPLATE(conn,
+ conn->purpose == purpose,
+ dirconn,
+ 0 == strcmp_opt(resource,
+ dirconn->requested_resource));
+}
- return NULL;
+/** Return a directory connection (if any one exists) that is fetching
+ * the item described by <b>purpose</b>/<b>resource</b>/<b>state</b>,
+ * otherwise return NULL. */
+dir_connection_t *
+connection_dir_get_by_purpose_resource_and_state(
+ int purpose,
+ const char *resource,
+ int state)
+{
+ smartlist_t *conns =
+ connection_dir_list_by_purpose_resource_and_state(
+ purpose,
+ resource,
+ state);
+ CONN_FIRST_AND_FREE_TEMPLATE(conns);
+}
+
+#undef CONN_FIRST_AND_FREE_TEMPLATE
+
+/** Return a list of directory connections that are fetching the item
+ * described by <b>purpose</b>/<b>resource</b>/<b>state</b>. If there are
+ * none, return an empty list. This list must be freed using smartlist_free,
+ * but the pointers in it must not be freed.
+ * Note that this list should not be cached, as the pointers in it can be
+ * freed if their connections close. */
+smartlist_t *
+connection_dir_list_by_purpose_resource_and_state(
+ int purpose,
+ const char *resource,
+ int state)
+{
+ DIR_CONN_LIST_TEMPLATE(conn,
+ conn->purpose == purpose && conn->state == state,
+ dirconn,
+ 0 == strcmp_opt(resource,
+ dirconn->requested_resource));
+}
+
+#undef DIR_CONN_LIST_TEMPLATE
+
+/** Return an arbitrary active OR connection that isn't <b>this_conn</b>.
+ *
+ * We use this to guess if we should tell the controller that we
+ * didn't manage to connect to any of our bridges. */
+static connection_t *
+connection_get_another_active_or_conn(const or_connection_t *this_conn)
+{
+ CONN_GET_TEMPLATE(conn,
+ conn != TO_CONN(this_conn) && conn->type == CONN_TYPE_OR);
}
/** Return 1 if there are any active OR connections apart from
@@ -4340,23 +4561,18 @@ connection_dir_get_by_purpose_and_resource(int purpose,
int
any_other_active_or_conns(const or_connection_t *this_conn)
{
- smartlist_t *conns = get_connection_array();
- SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
- if (conn == TO_CONN(this_conn)) { /* don't consider this conn */
- continue;
- }
-
- if (conn->type == CONN_TYPE_OR &&
- !conn->marked_for_close) {
- log_debug(LD_DIR, "%s: Found an OR connection: %s",
- __func__, conn->address);
- return 1;
- }
- } SMARTLIST_FOREACH_END(conn);
+ connection_t *conn = connection_get_another_active_or_conn(this_conn);
+ if (conn != NULL) {
+ log_debug(LD_DIR, "%s: Found an OR connection: %s",
+ __func__, conn->address);
+ return 1;
+ }
return 0;
}
+#undef CONN_GET_TEMPLATE
+
/** Return 1 if <b>conn</b> is a listener conn, else return 0. */
int
connection_is_listener(connection_t *conn)
@@ -5012,3 +5228,34 @@ connection_free_all(void)
#endif
}
+/** Log a warning, and possibly emit a control event, that <b>received</b> came
+ * at a skewed time. <b>trusted</b> indicates that the <b>source</b> was one
+ * that we had more faith in and therefore the warning level should have higher
+ * severity.
+ */
+void
+clock_skew_warning(const connection_t *conn, long apparent_skew, int trusted,
+ log_domain_mask_t domain, const char *received,
+ const char *source)
+{
+ char dbuf[64];
+ char *ext_source = NULL;
+ format_time_interval(dbuf, sizeof(dbuf), apparent_skew);
+ if (conn)
+ tor_asprintf(&ext_source, "%s:%s:%d", source, conn->address, conn->port);
+ else
+ ext_source = tor_strdup(source);
+ log_fn(trusted ? LOG_WARN : LOG_INFO, domain,
+ "Received %s with skewed time (%s): "
+ "It seems that our clock is %s by %s, or that theirs is %s%s. "
+ "Tor requires an accurate clock to work: please check your time, "
+ "timezone, and date settings.", received, ext_source,
+ apparent_skew > 0 ? "ahead" : "behind", dbuf,
+ apparent_skew > 0 ? "behind" : "ahead",
+ (!conn || trusted) ? "" : ", or they are sending us the wrong time");
+ if (trusted)
+ control_event_general_status(LOG_WARN, "CLOCK_SKEW SKEW=%ld SOURCE=%s",
+ apparent_skew, ext_source);
+ tor_free(ext_source);
+}
+
diff --git a/src/or/connection.h b/src/or/connection.h
index b6ff3d7bd6..45175cd5a2 100644
--- a/src/or/connection.h
+++ b/src/or/connection.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -146,12 +146,12 @@ static void connection_write_to_buf(const char *string, size_t len,
/* DOCDOC connection_write_to_buf_zlib */
static void connection_write_to_buf_zlib(const char *string, size_t len,
dir_connection_t *conn, int done);
-static INLINE void
+static inline void
connection_write_to_buf(const char *string, size_t len, connection_t *conn)
{
connection_write_to_buf_impl_(string, len, conn, 0);
}
-static INLINE void
+static inline void
connection_write_to_buf_zlib(const char *string, size_t len,
dir_connection_t *conn, int done)
{
@@ -163,7 +163,7 @@ static size_t connection_get_inbuf_len(connection_t *conn);
/* DOCDOC connection_get_outbuf_len */
static size_t connection_get_outbuf_len(connection_t *conn);
-static INLINE size_t
+static inline size_t
connection_get_inbuf_len(connection_t *conn)
{
IF_HAS_BUFFEREVENT(conn, {
@@ -173,7 +173,7 @@ connection_get_inbuf_len(connection_t *conn)
}
}
-static INLINE size_t
+static inline size_t
connection_get_outbuf_len(connection_t *conn)
{
IF_HAS_BUFFEREVENT(conn, {
@@ -186,14 +186,64 @@ connection_get_outbuf_len(connection_t *conn)
connection_t *connection_get_by_global_id(uint64_t id);
connection_t *connection_get_by_type(int type);
-connection_t *connection_get_by_type_addr_port_purpose(int type,
- const tor_addr_t *addr,
- uint16_t port, int purpose);
+MOCK_DECL(connection_t *,connection_get_by_type_addr_port_purpose,(int type,
+ const tor_addr_t *addr,
+ uint16_t port, int purpose));
connection_t *connection_get_by_type_state(int type, int state);
connection_t *connection_get_by_type_state_rendquery(int type, int state,
const char *rendquery);
dir_connection_t *connection_dir_get_by_purpose_and_resource(
- int state, const char *resource);
+ int purpose,
+ const char *resource);
+dir_connection_t *connection_dir_get_by_purpose_resource_and_state(
+ int purpose,
+ const char *resource,
+ int state);
+smartlist_t *connection_dir_list_by_purpose_and_resource(
+ int purpose,
+ const char *resource);
+smartlist_t *connection_dir_list_by_purpose_resource_and_state(
+ int purpose,
+ const char *resource,
+ int state);
+
+#define CONN_LEN_AND_FREE_TEMPLATE(sl) \
+ STMT_BEGIN \
+ int len = smartlist_len(sl); \
+ smartlist_free(sl); \
+ return len; \
+ STMT_END
+
+/** Return a count of directory connections that are fetching the item
+ * described by <b>purpose</b>/<b>resource</b>. */
+static inline int
+connection_dir_count_by_purpose_and_resource(
+ int purpose,
+ const char *resource)
+{
+ smartlist_t *conns = connection_dir_list_by_purpose_and_resource(
+ purpose,
+ resource);
+ CONN_LEN_AND_FREE_TEMPLATE(conns);
+}
+
+/** Return a count of directory connections that are fetching the item
+ * described by <b>purpose</b>/<b>resource</b>/<b>state</b>. */
+static inline int
+connection_dir_count_by_purpose_resource_and_state(
+ int purpose,
+ const char *resource,
+ int state)
+{
+ smartlist_t *conns =
+ connection_dir_list_by_purpose_resource_and_state(
+ purpose,
+ resource,
+ state);
+ CONN_LEN_AND_FREE_TEMPLATE(conns);
+}
+
+#undef CONN_LEN_AND_FREE_TEMPLATE
int any_other_active_or_conns(const or_connection_t *this_conn);
@@ -210,6 +260,10 @@ int connection_or_nonopen_was_started_here(or_connection_t *conn);
void connection_dump_buffer_mem_stats(int severity);
void remove_file_if_very_old(const char *fname, time_t now);
+void clock_skew_warning(const connection_t *conn, long apparent_skew,
+ int trusted, log_domain_mask_t domain,
+ const char *received, const char *source);
+
#ifdef USE_BUFFEREVENTS
int connection_type_uses_bufferevent(connection_t *conn);
void connection_configure_bufferevent_callbacks(connection_t *conn);
@@ -235,6 +289,13 @@ void connection_buckets_note_empty_ts(uint32_t *timestamp_var,
int tokens_before,
size_t tokens_removed,
const struct timeval *tvnow);
+MOCK_DECL(STATIC int,connection_connect_sockaddr,
+ (connection_t *conn,
+ const struct sockaddr *sa,
+ socklen_t sa_len,
+ const struct sockaddr *bindaddr,
+ socklen_t bindaddr_len,
+ int *socket_error));
#endif
#endif
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 3ff02933c3..e58d32e7a5 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -11,6 +11,9 @@
#define CONNECTION_EDGE_PRIVATE
#include "or.h"
+
+#include "backtrace.h"
+
#include "addressmap.h"
#include "buffers.h"
#include "channel.h"
@@ -503,6 +506,16 @@ connection_edge_finished_connecting(edge_connection_t *edge_conn)
return connection_edge_process_inbuf(edge_conn, 1);
}
+/** A list of all the entry_connection_t * objects that are not marked
+ * for close, and are in AP_CONN_STATE_CIRCUIT_WAIT.
+ *
+ * (Right now, we check in several places to make sure that this list is
+ * correct. When it's incorrect, we'll fix it, and log a BUG message.)
+ */
+static smartlist_t *pending_entry_connections = NULL;
+
+static int untried_pending_connections = 0;
+
/** Common code to connection_(ap|exit)_about_to_close. */
static void
connection_edge_about_to_close(edge_connection_t *edge_conn)
@@ -525,6 +538,8 @@ connection_ap_about_to_close(entry_connection_t *entry_conn)
edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(entry_conn);
connection_t *conn = ENTRY_TO_CONN(entry_conn);
+ connection_edge_about_to_close(edge_conn);
+
if (entry_conn->socks_request->has_finished == 0) {
/* since conn gets removed right after this function finishes,
* there's no point trying to send back a reply at this point. */
@@ -543,6 +558,20 @@ connection_ap_about_to_close(entry_connection_t *entry_conn)
conn->marked_for_close_file, conn->marked_for_close);
dnsserv_reject_request(entry_conn);
}
+
+ if (TO_CONN(edge_conn)->state == AP_CONN_STATE_CIRCUIT_WAIT) {
+ smartlist_remove(pending_entry_connections, entry_conn);
+ }
+
+#if 1
+ /* Check to make sure that this isn't in pending_entry_connections if it
+ * didn't actually belong there. */
+ if (TO_CONN(edge_conn)->type == CONN_TYPE_AP) {
+ connection_ap_warn_and_unmark_if_pending_circ(entry_conn,
+ "about_to_close");
+ }
+#endif
+
control_event_stream_bandwidth(edge_conn);
control_event_stream_status(entry_conn, STREAM_EVENT_CLOSED,
edge_conn->end_reason);
@@ -711,26 +740,181 @@ connection_ap_expire_beginning(void)
} SMARTLIST_FOREACH_END(base_conn);
}
-/** Tell any AP streams that are waiting for a new circuit to try again,
- * either attaching to an available circ or launching a new one.
+/**
+ * As connection_ap_attach_pending, but first scans the entire connection
+ * array to see if any elements are missing.
*/
void
-connection_ap_attach_pending(void)
+connection_ap_rescan_and_attach_pending(void)
{
entry_connection_t *entry_conn;
smartlist_t *conns = get_connection_array();
+
+ if (PREDICT_UNLIKELY(NULL == pending_entry_connections))
+ pending_entry_connections = smartlist_new();
+
SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
if (conn->marked_for_close ||
conn->type != CONN_TYPE_AP ||
conn->state != AP_CONN_STATE_CIRCUIT_WAIT)
continue;
+
entry_conn = TO_ENTRY_CONN(conn);
+ tor_assert(entry_conn);
+ if (! smartlist_contains(pending_entry_connections, entry_conn)) {
+ log_warn(LD_BUG, "Found a connection %p that was supposed to be "
+ "in pending_entry_connections, but wasn't. No worries; "
+ "adding it.",
+ pending_entry_connections);
+ untried_pending_connections = 1;
+ connection_ap_mark_as_pending_circuit(entry_conn);
+ }
+
+ } SMARTLIST_FOREACH_END(conn);
+
+ connection_ap_attach_pending(1);
+}
+
+#ifdef DEBUGGING_17659
+#define UNMARK() do { \
+ entry_conn->marked_pending_circ_line = 0; \
+ entry_conn->marked_pending_circ_file = 0; \
+ } while (0)
+#else
+#define UNMARK() do { } while (0)
+#endif
+
+/** Tell any AP streams that are listed as waiting for a new circuit to try
+ * again, either attaching to an available circ or launching a new one.
+ *
+ * If <b>retry</b> is false, only check the list if it contains at least one
+ * streams that we have not yet tried to attach to a circuit.
+ */
+void
+connection_ap_attach_pending(int retry)
+{
+ if (PREDICT_UNLIKELY(!pending_entry_connections)) {
+ return;
+ }
+
+ if (untried_pending_connections == 0 && !retry)
+ return;
+
+ /* Don't allow modifications to pending_entry_connections while we are
+ * iterating over it. */
+ smartlist_t *pending = pending_entry_connections;
+ pending_entry_connections = smartlist_new();
+
+ SMARTLIST_FOREACH_BEGIN(pending,
+ entry_connection_t *, entry_conn) {
+ connection_t *conn = ENTRY_TO_CONN(entry_conn);
+ tor_assert(conn && entry_conn);
+ if (conn->marked_for_close) {
+ UNMARK();
+ continue;
+ }
+ if (conn->magic != ENTRY_CONNECTION_MAGIC) {
+ log_warn(LD_BUG, "%p has impossible magic value %u.",
+ entry_conn, (unsigned)conn->magic);
+ UNMARK();
+ continue;
+ }
+ if (conn->state != AP_CONN_STATE_CIRCUIT_WAIT) {
+ log_warn(LD_BUG, "%p is no longer in circuit_wait. Its current state "
+ "is %s. Why is it on pending_entry_connections?",
+ entry_conn,
+ conn_state_to_string(conn->type, conn->state));
+ UNMARK();
+ continue;
+ }
+
if (connection_ap_handshake_attach_circuit(entry_conn) < 0) {
if (!conn->marked_for_close)
connection_mark_unattached_ap(entry_conn,
END_STREAM_REASON_CANT_ATTACH);
}
- } SMARTLIST_FOREACH_END(conn);
+
+ if (! conn->marked_for_close &&
+ conn->type == CONN_TYPE_AP &&
+ conn->state == AP_CONN_STATE_CIRCUIT_WAIT) {
+ if (!smartlist_contains(pending_entry_connections, entry_conn)) {
+ smartlist_add(pending_entry_connections, entry_conn);
+ continue;
+ }
+ }
+
+ UNMARK();
+ } SMARTLIST_FOREACH_END(entry_conn);
+
+ smartlist_free(pending);
+ untried_pending_connections = 0;
+}
+
+/** Mark <b>entry_conn</b> as needing to get attached to a circuit.
+ *
+ * And <b>entry_conn</b> must be in AP_CONN_STATE_CIRCUIT_WAIT,
+ * should not already be pending a circuit. The circuit will get
+ * launched or the connection will get attached the next time we
+ * call connection_ap_attach_pending().
+ */
+void
+connection_ap_mark_as_pending_circuit_(entry_connection_t *entry_conn,
+ const char *fname, int lineno)
+{
+ connection_t *conn = ENTRY_TO_CONN(entry_conn);
+ tor_assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
+ tor_assert(conn->magic == ENTRY_CONNECTION_MAGIC);
+ if (conn->marked_for_close)
+ return;
+
+ if (PREDICT_UNLIKELY(NULL == pending_entry_connections))
+ pending_entry_connections = smartlist_new();
+
+ if (PREDICT_UNLIKELY(smartlist_contains(pending_entry_connections,
+ entry_conn))) {
+ log_warn(LD_BUG, "What?? pending_entry_connections already contains %p! "
+ "(Called from %s:%d.)",
+ entry_conn, fname, lineno);
+#ifdef DEBUGGING_17659
+ const char *f2 = entry_conn->marked_pending_circ_file;
+ log_warn(LD_BUG, "(Previously called from %s:%d.)\n",
+ f2 ? f2 : "<NULL>",
+ entry_conn->marked_pending_circ_line);
+#endif
+ log_backtrace(LOG_WARN, LD_BUG, "To debug, this may help");
+ return;
+ }
+
+#ifdef DEBUGGING_17659
+ entry_conn->marked_pending_circ_line = (uint16_t) lineno;
+ entry_conn->marked_pending_circ_file = fname;
+#endif
+
+ untried_pending_connections = 1;
+ smartlist_add(pending_entry_connections, entry_conn);
+}
+
+/** Mark <b>entry_conn</b> as no longer waiting for a circuit. */
+void
+connection_ap_mark_as_non_pending_circuit(entry_connection_t *entry_conn)
+{
+ if (PREDICT_UNLIKELY(NULL == pending_entry_connections))
+ return;
+ UNMARK();
+ smartlist_remove(pending_entry_connections, entry_conn);
+}
+
+/* DOCDOC */
+void
+connection_ap_warn_and_unmark_if_pending_circ(entry_connection_t *entry_conn,
+ const char *where)
+{
+ if (pending_entry_connections &&
+ smartlist_contains(pending_entry_connections, entry_conn)) {
+ log_warn(LD_BUG, "What was %p doing in pending_entry_connections in %s?",
+ entry_conn, where);
+ connection_ap_mark_as_non_pending_circuit(entry_conn);
+ }
}
/** Tell any AP streams that are waiting for a one-hop tunnel to
@@ -851,12 +1035,13 @@ connection_ap_detach_retriable(entry_connection_t *conn,
* a tunneled directory connection, then just attach it. */
ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CIRCUIT_WAIT;
circuit_detach_stream(TO_CIRCUIT(circ),ENTRY_TO_EDGE_CONN(conn));
- return connection_ap_handshake_attach_circuit(conn);
+ connection_ap_mark_as_pending_circuit(conn);
} else {
+ CONNECTION_AP_EXPECT_NONPENDING(conn);
ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CONTROLLER_WAIT;
circuit_detach_stream(TO_CIRCUIT(circ),ENTRY_TO_EDGE_CONN(conn));
- return 0;
}
+ return 0;
}
/** Check if <b>conn</b> is using a dangerous port. Then warn and/or
@@ -905,6 +1090,7 @@ connection_ap_rewrite_and_attach_if_allowed(entry_connection_t *conn,
const or_options_t *options = get_options();
if (options->LeaveStreamsUnattached) {
+ CONNECTION_AP_EXPECT_NONPENDING(conn);
ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CONTROLLER_WAIT;
return 0;
}
@@ -1454,10 +1640,12 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
/* If we were given a circuit to attach to, try to attach. Otherwise,
* try to find a good one and attach to that. */
int rv;
- if (circ)
- rv = connection_ap_handshake_attach_chosen_circuit(conn, circ, cpath);
- else
- rv = connection_ap_handshake_attach_circuit(conn);
+ if (circ) {
+ rv = connection_ap_handshake_attach_chosen_circuit(conn, circ, cpath);
+ } else {
+ connection_ap_mark_as_pending_circuit(conn);
+ rv = 0;
+ }
/* If the above function returned 0 then we're waiting for a circuit.
* if it returned 1, we're attached. Both are okay. But if it returned
@@ -1554,6 +1742,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
* Also, a fetch could have been requested if the onion address was not
* found in the cache previously. */
if (refetch_desc || !rend_client_any_intro_points_usable(entry)) {
+ connection_ap_mark_as_non_pending_circuit(conn);
base_conn->state = AP_CONN_STATE_RENDDESC_WAIT;
log_info(LD_REND, "Unknown descriptor %s. Fetching.",
safe_str_client(rend_data->onion_address));
@@ -1564,11 +1753,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
/* We have the descriptor so launch a connection to the HS. */
base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
log_info(LD_REND, "Descriptor is here. Great.");
- if (connection_ap_handshake_attach_circuit(conn) < 0) {
- if (!base_conn->marked_for_close)
- connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
- return -1;
- }
+ connection_ap_mark_as_pending_circuit(conn);
return 0;
}
@@ -2248,7 +2433,7 @@ connection_ap_handshake_send_resolve(entry_connection_t *ap_conn)
if (!base_conn->address) {
/* This might be unnecessary. XXXX */
- base_conn->address = tor_dup_addr(&base_conn->addr);
+ base_conn->address = tor_addr_to_str_dup(&base_conn->addr);
}
base_conn->state = AP_CONN_STATE_RESOLVE_WAIT;
log_info(LD_APP,"Address sent for resolve, ap socket "TOR_SOCKET_T_FORMAT
@@ -2324,12 +2509,7 @@ connection_ap_make_link(connection_t *partner,
control_event_stream_status(conn, STREAM_EVENT_NEW, 0);
/* attaching to a dirty circuit is fine */
- if (connection_ap_handshake_attach_circuit(conn) < 0) {
- if (!base_conn->marked_for_close)
- connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
- return NULL;
- }
-
+ connection_ap_mark_as_pending_circuit(conn);
log_info(LD_APP,"... application connection created and linked.");
return conn;
}
@@ -2771,8 +2951,8 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
return 0;
}
/* Make sure to get the 'real' address of the previous hop: the
- * caller might want to know whether his IP address has changed, and
- * we might already have corrected base_.addr[ess] for the relay's
+ * caller might want to know whether the remote IP address has changed,
+ * and we might already have corrected base_.addr[ess] for the relay's
* canonical IP address. */
if (or_circ && or_circ->p_chan)
address = tor_strdup(channel_get_actual_remote_address(or_circ->p_chan));
@@ -3478,3 +3658,12 @@ circuit_clear_isolation(origin_circuit_t *circ)
circ->socks_username_len = circ->socks_password_len = 0;
}
+/** Free all storage held in module-scoped variables for connection_edge.c */
+void
+connection_edge_free_all(void)
+{
+ untried_pending_connections = 0;
+ smartlist_free(pending_entry_connections);
+ pending_entry_connections = NULL;
+}
+
diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h
index 7c0b9c0767..5dfc8af901 100644
--- a/src/or/connection_edge.h
+++ b/src/or/connection_edge.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -64,7 +64,20 @@ int connection_edge_is_rendezvous_stream(edge_connection_t *conn);
int connection_ap_can_use_exit(const entry_connection_t *conn,
const node_t *exit);
void connection_ap_expire_beginning(void);
-void connection_ap_attach_pending(void);
+void connection_ap_rescan_and_attach_pending(void);
+void connection_ap_attach_pending(int retry);
+void connection_ap_mark_as_pending_circuit_(entry_connection_t *entry_conn,
+ const char *file, int line);
+#define connection_ap_mark_as_pending_circuit(c) \
+ connection_ap_mark_as_pending_circuit_((c), __FILE__, __LINE__)
+void connection_ap_mark_as_non_pending_circuit(entry_connection_t *entry_conn);
+#define CONNECTION_AP_EXPECT_NONPENDING(c) do { \
+ if (ENTRY_TO_CONN(c)->state == AP_CONN_STATE_CIRCUIT_WAIT) { \
+ log_warn(LD_BUG, "At %s:%d: %p was unexpectedly in circuit_wait.", \
+ __FILE__, __LINE__, (c)); \
+ connection_ap_mark_as_non_pending_circuit(c); \
+ } \
+ } while (0)
void connection_ap_fail_onehop(const char *failed_digest,
cpath_build_state_t *build_state);
void circuit_discard_optional_exit_enclaves(extend_info_t *info);
@@ -100,6 +113,12 @@ int connection_edge_update_circuit_isolation(const entry_connection_t *conn,
void circuit_clear_isolation(origin_circuit_t *circ);
streamid_t get_unique_stream_id_by_circ(origin_circuit_t *circ);
+void connection_edge_free_all(void);
+
+void connection_ap_warn_and_unmark_if_pending_circ(
+ entry_connection_t *entry_conn,
+ const char *where);
+
/** @name Begin-cell flags
*
* These flags are used in RELAY_BEGIN cells to change the default behavior
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index a967c93aca..f8be763792 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -488,6 +488,28 @@ var_cell_new(uint16_t payload_len)
return cell;
}
+/**
+ * Copy a var_cell_t
+ */
+
+var_cell_t *
+var_cell_copy(const var_cell_t *src)
+{
+ var_cell_t *copy = NULL;
+ size_t size = 0;
+
+ if (src != NULL) {
+ size = STRUCT_OFFSET(var_cell_t, payload) + src->payload_len;
+ copy = tor_malloc_zero(size);
+ copy->payload_len = src->payload_len;
+ copy->command = src->command;
+ copy->circ_id = src->circ_id;
+ memcpy(copy->payload, src->payload, copy->payload_len);
+ }
+
+ return copy;
+}
+
/** Release all space held by <b>cell</b>. */
void
var_cell_free(var_cell_t *cell)
@@ -912,7 +934,7 @@ connection_or_init_conn_from_address(or_connection_t *conn,
}
conn->nickname = tor_strdup(node_get_nickname(r));
tor_free(conn->base_.address);
- conn->base_.address = tor_dup_addr(&node_ap.addr);
+ conn->base_.address = tor_addr_to_str_dup(&node_ap.addr);
} else {
conn->nickname = tor_malloc(HEX_DIGEST_LEN+2);
conn->nickname[0] = '$';
@@ -920,7 +942,7 @@ connection_or_init_conn_from_address(or_connection_t *conn,
conn->identity_digest, DIGEST_LEN);
tor_free(conn->base_.address);
- conn->base_.address = tor_dup_addr(addr);
+ conn->base_.address = tor_addr_to_str_dup(addr);
}
/*
@@ -1259,11 +1281,9 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port,
switch (connection_connect(TO_CONN(conn), conn->base_.address,
&addr, port, &socket_error)) {
case -1:
- /* If the connection failed immediately, and we're using
- * a proxy, our proxy is down. Don't blame the Tor server. */
- if (conn->base_.proxy_state == PROXY_INFANT)
- entry_guard_register_connect_status(conn->identity_digest,
- 0, 1, time(NULL));
+ /* We failed to establish a connection probably because of a local
+ * error. No need to blame the guard in this case. Notify the networking
+ * system of this failure. */
connection_or_connect_failed(conn,
errno_to_orconn_end_reason(socket_error),
tor_socket_strerror(socket_error));
@@ -1450,17 +1470,12 @@ connection_tls_continue_handshake(or_connection_t *conn)
{
int result;
check_no_tls_errors();
- again:
- if (conn->base_.state == OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING) {
- // log_notice(LD_OR, "Renegotiate with %p", conn->tls);
- result = tor_tls_renegotiate(conn->tls);
- // log_notice(LD_OR, "Result: %d", result);
- } else {
- tor_assert(conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING);
- // log_notice(LD_OR, "Continue handshake with %p", conn->tls);
- result = tor_tls_handshake(conn->tls);
- // log_notice(LD_OR, "Result: %d", result);
- }
+
+ tor_assert(conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING);
+ // log_notice(LD_OR, "Continue handshake with %p", conn->tls);
+ result = tor_tls_handshake(conn->tls);
+ // log_notice(LD_OR, "Result: %d", result);
+
switch (result) {
CASE_TOR_TLS_ERROR_ANY:
log_info(LD_OR,"tls error [%s]. breaking connection.",
@@ -1469,23 +1484,10 @@ connection_tls_continue_handshake(or_connection_t *conn)
case TOR_TLS_DONE:
if (! tor_tls_used_v1_handshake(conn->tls)) {
if (!tor_tls_is_server(conn->tls)) {
- if (conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING) {
- if (tor_tls_received_v3_certificate(conn->tls)) {
- log_info(LD_OR, "Client got a v3 cert! Moving on to v3 "
- "handshake with ciphersuite %s",
- tor_tls_get_ciphersuite_name(conn->tls));
- return connection_or_launch_v3_or_handshake(conn);
- } else {
- log_debug(LD_OR, "Done with initial SSL handshake (client-side)."
- " Requesting renegotiation.");
- connection_or_change_state(conn,
- OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING);
- goto again;
- }
- }
- // log_notice(LD_OR,"Done. state was %d.", conn->base_.state);
+ tor_assert(conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING);
+ return connection_or_launch_v3_or_handshake(conn);
} else {
- /* v2/v3 handshake, but not a client. */
+ /* v2/v3 handshake, but we are not a client. */
log_debug(LD_OR, "Done with initial SSL handshake (server-side). "
"Expecting renegotiation or VERSIONS cell");
tor_tls_set_renegotiate_callback(conn->tls,
@@ -1498,6 +1500,7 @@ connection_tls_continue_handshake(or_connection_t *conn)
return 0;
}
}
+ tor_assert(tor_tls_is_server(conn->tls));
return connection_tls_finish_handshake(conn);
case TOR_TLS_WANTWRITE:
connection_start_writing(TO_CONN(conn));
@@ -1533,22 +1536,8 @@ connection_or_handle_event_cb(struct bufferevent *bufev, short event,
if (! tor_tls_used_v1_handshake(conn->tls)) {
if (!tor_tls_is_server(conn->tls)) {
if (conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING) {
- if (tor_tls_received_v3_certificate(conn->tls)) {
- log_info(LD_OR, "Client got a v3 cert!");
- if (connection_or_launch_v3_or_handshake(conn) < 0)
- connection_or_close_for_error(conn, 0);
- return;
- } else {
- connection_or_change_state(conn,
- OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING);
- tor_tls_unblock_renegotiation(conn->tls);
- if (bufferevent_ssl_renegotiate(conn->base_.bufev)<0) {
- log_warn(LD_OR, "Start_renegotiating went badly.");
- connection_or_close_for_error(conn, 0);
- }
- tor_tls_unblock_renegotiation(conn->tls);
- return; /* ???? */
- }
+ if (connection_or_launch_v3_or_handshake(conn) < 0)
+ connection_or_close_for_error(conn, 0);
}
} else {
const int handshakes = tor_tls_get_num_server_handshakes(conn->tls);
@@ -1612,11 +1601,11 @@ connection_or_nonopen_was_started_here(or_connection_t *conn)
}
/** <b>Conn</b> just completed its handshake. Return 0 if all is well, and
- * return -1 if he is lying, broken, or otherwise something is wrong.
+ * return -1 if they are lying, broken, or otherwise something is wrong.
*
* If we initiated this connection (<b>started_here</b> is true), make sure
* the other side sent a correctly formed certificate. If I initiated the
- * connection, make sure it's the right guy.
+ * connection, make sure it's the right relay by checking the certificate.
*
* Otherwise (if we _didn't_ initiate this connection), it's okay for
* the certificate to be weird or absent.
@@ -1632,7 +1621,7 @@ connection_or_nonopen_was_started_here(or_connection_t *conn)
* 1) Set conn->circ_id_type according to tor-spec.txt.
* 2) If we're an authdirserver and we initiated the connection: drop all
* descriptors that claim to be on that IP/port but that aren't
- * this guy; and note that this guy is reachable.
+ * this relay; and note that this relay is reachable.
* 3) If this is a bridge and we didn't configure its identity
* fingerprint, remember the keyid we just learned.
*/
@@ -1785,7 +1774,7 @@ connection_or_client_used(or_connection_t *conn)
*
* Make sure we are happy with the person we just handshaked with.
*
- * If he initiated the connection, make sure he's not already connected,
+ * If they initiated the connection, make sure they're not already connected,
* then initialize conn from the information in router.
*
* If all is successful, call circuit_n_conn_done() to handle events
@@ -1800,6 +1789,8 @@ connection_tls_finish_handshake(or_connection_t *conn)
char digest_rcvd[DIGEST_LEN];
int started_here = connection_or_nonopen_was_started_here(conn);
+ tor_assert(!started_here);
+
log_debug(LD_HANDSHAKE,"%s tls handshake on %p with %s done, using "
"ciphersuite %s. verifying.",
started_here?"outgoing":"incoming",
@@ -1815,10 +1806,8 @@ connection_tls_finish_handshake(or_connection_t *conn)
if (tor_tls_used_v1_handshake(conn->tls)) {
conn->link_proto = 1;
- if (!started_here) {
- connection_or_init_conn_from_address(conn, &conn->base_.addr,
- conn->base_.port, digest_rcvd, 0);
- }
+ connection_or_init_conn_from_address(conn, &conn->base_.addr,
+ conn->base_.port, digest_rcvd, 0);
tor_tls_block_renegotiation(conn->tls);
rep_hist_note_negotiated_link_proto(1, started_here);
return connection_or_set_state_open(conn);
@@ -1826,10 +1815,8 @@ connection_tls_finish_handshake(or_connection_t *conn)
connection_or_change_state(conn, OR_CONN_STATE_OR_HANDSHAKING_V2);
if (connection_init_or_handshake_state(conn, started_here) < 0)
return -1;
- if (!started_here) {
- connection_or_init_conn_from_address(conn, &conn->base_.addr,
- conn->base_.port, digest_rcvd, 0);
- }
+ connection_or_init_conn_from_address(conn, &conn->base_.addr,
+ conn->base_.port, digest_rcvd, 0);
return connection_or_send_versions(conn, 0);
}
}
@@ -1844,7 +1831,6 @@ static int
connection_or_launch_v3_or_handshake(or_connection_t *conn)
{
tor_assert(connection_or_nonopen_was_started_here(conn));
- tor_assert(tor_tls_received_v3_certificate(conn->tls));
circuit_build_times_network_is_live(get_circuit_build_times_mutable());
@@ -2060,6 +2046,19 @@ connection_or_process_cells_from_inbuf(or_connection_t *conn)
{
var_cell_t *var_cell;
+ /*
+ * Note on memory management for incoming cells: below the channel layer,
+ * we shouldn't need to consider its internal queueing/copying logic. It
+ * is safe to pass cells to it on the stack or on the heap, but in the
+ * latter case we must be sure we free them later.
+ *
+ * The incoming cell queue code in channel.c will (in the common case)
+ * decide it can pass them to the upper layer immediately, in which case
+ * those functions may run directly on the cell pointers we pass here, or
+ * it may decide to queue them, in which case it will allocate its own
+ * buffer and copy the cell.
+ */
+
while (1) {
log_debug(LD_OR,
TOR_SOCKET_T_FORMAT": starting, inbuf_datalen %d "
@@ -2290,8 +2289,7 @@ connection_or_send_auth_challenge_cell(or_connection_t *conn)
auth_challenge_cell_t *ac = auth_challenge_cell_new();
- if (crypto_rand((char*)ac->challenge, sizeof(ac->challenge)) < 0)
- goto done;
+ crypto_rand((char*)ac->challenge, sizeof(ac->challenge));
auth_challenge_cell_add_methods(ac, AUTHTYPE_RSA_SHA256_TLSSECRET);
auth_challenge_cell_set_n_methods(ac,
@@ -2353,7 +2351,7 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
{
const tor_x509_cert_t *id_cert=NULL, *link_cert=NULL;
- const digests_t *my_digests, *their_digests;
+ const common_digests_t *my_digests, *their_digests;
const uint8_t *my_id, *their_id, *client_id, *server_id;
if (tor_tls_get_my_certs(server, &link_cert, &id_cert))
goto err;
diff --git a/src/or/connection_or.h b/src/or/connection_or.h
index 3877fd5a13..e2ec47a4f2 100644
--- a/src/or/connection_or.h
+++ b/src/or/connection_or.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -97,9 +97,10 @@ void cell_pack(packed_cell_t *dest, const cell_t *src, int wide_circ_ids);
int var_cell_pack_header(const var_cell_t *cell, char *hdr_out,
int wide_circ_ids);
var_cell_t *var_cell_new(uint16_t payload_len);
+var_cell_t *var_cell_copy(const var_cell_t *src);
void var_cell_free(var_cell_t *cell);
-/** DOCDOC */
+/* DOCDOC */
#define MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS 4
#endif
diff --git a/src/or/control.c b/src/or/control.c
index de18c9eae8..862c836e40 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -1,5 +1,5 @@
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -192,7 +192,7 @@ static void flush_queued_events_cb(evutil_socket_t fd, short what, void *arg);
/** Given a control event code for a message event, return the corresponding
* log severity. */
-static INLINE int
+static inline int
event_to_log_severity(int event)
{
switch (event) {
@@ -206,7 +206,7 @@ event_to_log_severity(int event)
}
/** Given a log severity, return the corresponding control event code. */
-static INLINE int
+static inline int
log_severity_to_event(int severity)
{
switch (severity) {
@@ -325,7 +325,7 @@ control_event_is_interesting(int event)
/** Append a NUL-terminated string <b>s</b> to the end of
* <b>conn</b>-\>outbuf.
*/
-static INLINE void
+static inline void
connection_write_str_to_buf(const char *s, control_connection_t *conn)
{
size_t len = strlen(s);
@@ -428,7 +428,7 @@ read_escaped_data(const char *data, size_t len, char **out)
/** If the first <b>in_len_max</b> characters in <b>start</b> contain a
* double-quoted string with escaped characters, return the length of that
* string (as encoded, including quotes). Otherwise return -1. */
-static INLINE int
+static inline int
get_escaped_string_length(const char *start, size_t in_len_max,
int *chars_out)
{
@@ -1927,6 +1927,22 @@ getinfo_helper_dir(control_connection_t *control_conn,
*errmsg = "Not found in cache";
return -1;
}
+ } else if (!strcmpstart(question, "hs/service/desc/id/")) {
+ rend_cache_entry_t *e = NULL;
+
+ question += strlen("hs/service/desc/id/");
+ if (strlen(question) != REND_SERVICE_ID_LEN_BASE32) {
+ *errmsg = "Invalid address";
+ return -1;
+ }
+
+ if (!rend_cache_lookup_v2_desc_as_service(question, &e)) {
+ /* Descriptor found in cache */
+ *answer = tor_strdup(e->desc);
+ } else {
+ *errmsg = "Not found in cache";
+ return -1;
+ }
} else if (!strcmpstart(question, "md/id/")) {
const node_t *node = node_get_by_hex_id(question+strlen("md/id/"));
const microdesc_t *md = NULL;
@@ -1995,6 +2011,11 @@ getinfo_helper_dir(control_connection_t *control_conn,
char *filename = get_datadir_fname("cached-consensus");
*answer = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL);
tor_free(filename);
+ if (!*answer) { /* generate an error */
+ *errmsg = "Could not open cached consensus. "
+ "Make sure FetchUselessDescriptors is set to 1.";
+ return -1;
+ }
}
} else if (!strcmp(question, "network-status")) { /* v1 */
routerlist_t *routerlist = router_get_routerlist();
@@ -2481,6 +2502,8 @@ static const getinfo_item_t getinfo_items[] = {
PREFIX("extra-info/digest/", dir, "Extra-info documents by digest."),
PREFIX("hs/client/desc/id", dir,
"Hidden Service descriptor in client's cache by onion."),
+ PREFIX("hs/service/desc/id/", dir,
+ "Hidden Service descriptor in services's cache by onion."),
PREFIX("net/listeners/", listeners, "Bound addresses by type"),
ITEM("ns/all", networkstatus,
"Brief summary of router status (v2 directory format)"),
@@ -2544,6 +2567,12 @@ static const getinfo_item_t getinfo_items[] = {
"v3 Networkstatus consensus as retrieved from a DirPort."),
ITEM("exit-policy/default", policies,
"The default value appended to the configured exit policy."),
+ ITEM("exit-policy/reject-private/default", policies,
+ "The default rules appended to the configured exit policy by"
+ " ExitPolicyRejectPrivate."),
+ ITEM("exit-policy/reject-private/relay", policies,
+ "The relay-specific rules appended to the configured exit policy by"
+ " ExitPolicyRejectPrivate."),
ITEM("exit-policy/full", policies, "The entire exit policy of onion router"),
ITEM("exit-policy/ipv4", policies, "IPv4 parts of exit policy"),
ITEM("exit-policy/ipv6", policies, "IPv6 parts of exit policy"),
@@ -2840,12 +2869,26 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
}
/* now circ refers to something that is ready to be extended */
+ int first_node = zero_circ;
SMARTLIST_FOREACH(nodes, const node_t *, node,
{
- extend_info_t *info = extend_info_from_node(node, 0);
- tor_assert(info); /* True, since node_has_descriptor(node) == true */
+ extend_info_t *info = extend_info_from_node(node, first_node);
+ if (first_node && !info) {
+ log_warn(LD_CONTROL,
+ "controller tried to connect to a node that doesn't have any "
+ "addresses that are allowed by the firewall configuration; "
+ "circuit marked for closing.");
+ circuit_mark_for_close(TO_CIRCUIT(circ), -END_CIRC_REASON_CONNECTFAILED);
+ connection_write_str_to_buf("551 Couldn't start circuit\r\n", conn);
+ goto done;
+ } else {
+ /* True, since node_has_descriptor(node) == true and we are extending
+ * to the node's primary address */
+ tor_assert(info);
+ }
circuit_append_new_exit(circ, info);
extend_info_free(info);
+ first_node = 0;
});
/* now that we've populated the cpath, start extending */
@@ -2987,6 +3030,7 @@ handle_control_attachstream(control_connection_t *conn, uint32_t len,
edge_conn->end_reason = 0;
if (tmpcirc)
circuit_detach_stream(tmpcirc, edge_conn);
+ CONNECTION_AP_EXPECT_NONPENDING(ap_conn);
TO_CONN(edge_conn)->state = AP_CONN_STATE_CONTROLLER_WAIT;
}
@@ -3418,8 +3462,7 @@ handle_control_authchallenge(control_connection_t *conn, uint32_t len,
tor_free(client_nonce);
return -1;
}
- const int fail = crypto_rand(server_nonce, SAFECOOKIE_SERVER_NONCE_LEN);
- tor_assert(!fail);
+ crypto_rand(server_nonce, SAFECOOKIE_SERVER_NONCE_LEN);
/* Now compute and send the server-to-controller response, and the
* server's nonce. */
@@ -5064,7 +5107,7 @@ sum_up_cell_stats_by_command(circuit_t *circ, cell_stats_t *cell_stats)
{
memset(cell_stats, 0, sizeof(cell_stats_t));
SMARTLIST_FOREACH_BEGIN(circ->testing_cell_stats,
- testing_cell_stats_entry_t *, ent) {
+ const testing_cell_stats_entry_t *, ent) {
tor_assert(ent->command <= CELL_COMMAND_MAX_);
if (!ent->removed && !ent->exitward) {
cell_stats->added_cells_appward[ent->command] += 1;
@@ -5077,10 +5120,8 @@ sum_up_cell_stats_by_command(circuit_t *circ, cell_stats_t *cell_stats)
cell_stats->removed_cells_exitward[ent->command] += 1;
cell_stats->total_time_exitward[ent->command] += ent->waiting_time * 10;
}
- tor_free(ent);
} SMARTLIST_FOREACH_END(ent);
- smartlist_free(circ->testing_cell_stats);
- circ->testing_cell_stats = NULL;
+ circuit_clear_testing_cell_stats(circ);
}
/** Helper: append a cell statistics string to <code>event_parts</code>,
@@ -6364,6 +6405,31 @@ get_desc_id_from_query(const rend_data_t *rend_data, const char *hsdir_fp)
return desc_id;
}
+/** send HS_DESC CREATED event when a local service generates a descriptor.
+ *
+ * <b>service_id</b> is the descriptor onion address.
+ * <b>desc_id_base32</b> is the descriptor ID.
+ * <b>replica</b> is the the descriptor replica number.
+ */
+void
+control_event_hs_descriptor_created(const char *service_id,
+ const char *desc_id_base32,
+ int replica)
+{
+ if (!service_id || !desc_id_base32) {
+ log_warn(LD_BUG, "Called with service_digest==%p, "
+ "desc_id_base32==%p", service_id, desc_id_base32);
+ return;
+ }
+
+ send_control_event(EVENT_HS_DESC,
+ "650 HS_DESC CREATED %s UNKNOWN UNKNOWN %s "
+ "REPLICA=%d\r\n",
+ service_id,
+ desc_id_base32,
+ replica);
+}
+
/** send HS_DESC upload event.
*
* <b>service_id</b> is the descriptor onion address.
@@ -6452,6 +6518,7 @@ control_event_hs_descriptor_receive_end(const char *action,
*/
void
control_event_hs_descriptor_upload_end(const char *action,
+ const char *onion_address,
const char *id_digest,
const char *reason)
{
@@ -6468,8 +6535,9 @@ control_event_hs_descriptor_upload_end(const char *action,
}
send_control_event(EVENT_HS_DESC,
- "650 HS_DESC %s UNKNOWN UNKNOWN %s%s\r\n",
+ "650 HS_DESC %s %s UNKNOWN %s%s\r\n",
action,
+ rend_hsaddress_str_or_unknown(onion_address),
node_describe_longname_by_id(id_digest),
reason_field ? reason_field : "");
@@ -6499,14 +6567,17 @@ control_event_hs_descriptor_received(const char *onion_address,
* called when we successfully uploaded a hidden service descriptor.
*/
void
-control_event_hs_descriptor_uploaded(const char *id_digest)
+control_event_hs_descriptor_uploaded(const char *id_digest,
+ const char *onion_address)
{
if (!id_digest) {
log_warn(LD_BUG, "Called with id_digest==%p",
id_digest);
return;
}
- control_event_hs_descriptor_upload_end("UPLOADED", id_digest, NULL);
+
+ control_event_hs_descriptor_upload_end("UPLOADED", onion_address,
+ id_digest, NULL);
}
/** Send HS_DESC event to inform controller that query <b>rend_query</b>
@@ -6568,6 +6639,7 @@ control_event_hs_descriptor_content(const char *onion_address,
*/
void
control_event_hs_descriptor_upload_failed(const char *id_digest,
+ const char *onion_address,
const char *reason)
{
if (!id_digest) {
@@ -6575,7 +6647,7 @@ control_event_hs_descriptor_upload_failed(const char *id_digest,
id_digest);
return;
}
- control_event_hs_descriptor_upload_end("UPLOAD_FAILED",
+ control_event_hs_descriptor_upload_end("UPLOAD_FAILED", onion_address,
id_digest, reason);
}
diff --git a/src/or/control.h b/src/or/control.h
index 9ac8995aac..b3902e64bd 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -117,6 +117,9 @@ MOCK_DECL(const char *, node_describe_longname_by_id,(const char *id_digest));
void control_event_hs_descriptor_requested(const rend_data_t *rend_query,
const char *desc_id_base32,
const char *hs_dir);
+void control_event_hs_descriptor_created(const char *service_id,
+ const char *desc_id_base32,
+ int replica);
void control_event_hs_descriptor_upload(const char *service_id,
const char *desc_id_base32,
const char *hs_dir);
@@ -126,16 +129,19 @@ void control_event_hs_descriptor_receive_end(const char *action,
const char *id_digest,
const char *reason);
void control_event_hs_descriptor_upload_end(const char *action,
+ const char *onion_address,
const char *hs_dir,
const char *reason);
void control_event_hs_descriptor_received(const char *onion_address,
const rend_data_t *rend_data,
const char *id_digest);
-void control_event_hs_descriptor_uploaded(const char *hs_dir);
+void control_event_hs_descriptor_uploaded(const char *hs_dir,
+ const char *onion_address);
void control_event_hs_descriptor_failed(const rend_data_t *rend_data,
const char *id_digest,
const char *reason);
void control_event_hs_descriptor_upload_failed(const char *hs_dir,
+ const char *onion_address,
const char *reason);
void control_event_hs_descriptor_content(const char *onion_address,
const char *desc_id,
diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c
index 76d97e05f2..3109d5a177 100644
--- a/src/or/cpuworker.c
+++ b/src/or/cpuworker.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2003-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/cpuworker.h b/src/or/cpuworker.h
index 70a595e472..62cf0eb164 100644
--- a/src/or/cpuworker.h
+++ b/src/or/cpuworker.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/dircollate.c b/src/or/dircollate.c
index 4c812c40e6..3f9d78f02d 100644
--- a/src/or/dircollate.c
+++ b/src/or/dircollate.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2014, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -17,20 +17,25 @@
static void dircollator_collate_by_rsa(dircollator_t *dc);
static void dircollator_collate_by_ed25519(dircollator_t *dc);
+/** Hashtable entry mapping a pair of digests (actually an ed25519 key and an
+ * RSA SHA1 digest) to an array of vote_routerstatus_t. */
typedef struct ddmap_entry_s {
HT_ENTRY(ddmap_entry_s) node;
uint8_t d[DIGEST_LEN + DIGEST256_LEN];
+ /* The nth member of this array corresponds to the vote_routerstatus_t (if
+ * any) received for this digest pair from the nth voter. */
vote_routerstatus_t *vrs_lst[FLEXIBLE_ARRAY_MEMBER];
} ddmap_entry_t;
-double_digest_map_t *by_both_ids;
-
+/** Release all storage held by e. */
static void
ddmap_entry_free(ddmap_entry_t *e)
{
tor_free(e);
}
+/** Return a new empty ddmap_entry, with <b>n_votes</b> elements in
+ * vrs_list. */
static ddmap_entry_t *
ddmap_entry_new(int n_votes)
{
@@ -50,6 +55,8 @@ ddmap_entry_eq(const ddmap_entry_t *a, const ddmap_entry_t *b)
return fast_memeq(a->d, b->d, sizeof(a->d));
}
+/** Record the RSA identity of <b>ent</b> as <b>rsa_sha1</b>, and the
+ * ed25519 identity as <b>ed25519</b>. */
static void
ddmap_entry_set_digests(ddmap_entry_t *ent,
const uint8_t *rsa_sha1,
@@ -63,6 +70,10 @@ HT_PROTOTYPE(double_digest_map, ddmap_entry_s, node, ddmap_entry_hash,
ddmap_entry_eq);
HT_GENERATE2(double_digest_map, ddmap_entry_s, node, ddmap_entry_hash,
ddmap_entry_eq, 0.6, tor_reallocarray, tor_free_);
+
+/** Helper: add a single vote_routerstatus_t <b>vrs</b> to the collator
+ * <b>dc</b>, indexing it by its RSA key digest, and by the 2-tuple of
+ * its RSA key digest and Ed25519 key. */
static void
dircollator_add_routerstatus(dircollator_t *dc,
int vote_num,
@@ -71,6 +82,8 @@ dircollator_add_routerstatus(dircollator_t *dc,
{
const char *id = vrs->status.identity_digest;
+ vrs->ed25519_reflects_consensus = 0;
+
(void) vote;
vote_routerstatus_t **vrs_lst = digestmap_get(dc->by_rsa_sha1, id);
if (NULL == vrs_lst) {
@@ -82,7 +95,7 @@ dircollator_add_routerstatus(dircollator_t *dc,
const uint8_t *ed = vrs->ed25519_id;
- if (tor_mem_is_zero((char*)ed, DIGEST256_LEN))
+ if (! vrs->has_ed25519_listing)
return;
ddmap_entry_t search, *found;
@@ -99,6 +112,8 @@ dircollator_add_routerstatus(dircollator_t *dc,
vrs_lst[vote_num] = vrs;
}
+/** Create and return a new dircollator object to use when collating
+ * <b>n_votes</b> out of a total of <b>n_authorities</b>. */
dircollator_t *
dircollator_new(int n_votes, int n_authorities)
{
@@ -115,6 +130,7 @@ dircollator_new(int n_votes, int n_authorities)
return dc;
}
+/** Release all storage held by <b>dc</b>. */
void
dircollator_free(dircollator_t *dc)
{
@@ -139,6 +155,10 @@ dircollator_free(dircollator_t *dc)
tor_free(dc);
}
+/** Add a single vote <b>v</b> to a dircollator <b>dc</b>. This function must
+ * be called exactly once for each vote to be used in the consensus. It may
+ * only be called before dircollator_collate().
+ */
void
dircollator_add_vote(dircollator_t *dc, networkstatus_t *v)
{
@@ -153,13 +173,16 @@ dircollator_add_vote(dircollator_t *dc, networkstatus_t *v)
} SMARTLIST_FOREACH_END(vrs);
}
+/** Sort the entries in <b>dc</b> according to <b>consensus_method</b>, so
+ * that the consensus process can iterate over them with
+ * dircollator_n_routers() and dircollator_get_votes_for_router(). */
void
dircollator_collate(dircollator_t *dc, int consensus_method)
{
tor_assert(!dc->is_collated);
dc->all_rsa_sha1_lst = smartlist_new();
- if (consensus_method < MIN_METHOD_FOR_ED25519_ID_VOTING + 10/*XXX*/)
+ if (consensus_method < MIN_METHOD_FOR_ED25519_ID_VOTING)
dircollator_collate_by_rsa(dc);
else
dircollator_collate_by_ed25519(dc);
@@ -168,6 +191,15 @@ dircollator_collate(dircollator_t *dc, int consensus_method)
dc->is_collated = 1;
}
+/**
+ * Collation function for RSA-only consensuses: collate the votes for each
+ * entry in <b>dc</b> by their RSA keys.
+ *
+ * The rule is:
+ * If an RSA identity key is listed by more than half of the authorities,
+ * include that identity, and treat all descriptors with that RSA identity
+ * as describing the same router.
+ */
static void
dircollator_collate_by_rsa(dircollator_t *dc)
{
@@ -189,6 +221,20 @@ dircollator_collate_by_rsa(dircollator_t *dc)
dc->by_collated_rsa_sha1 = dc->by_rsa_sha1;
}
+/**
+ * Collation function for ed25519 consensuses: collate the votes for each
+ * entry in <b>dc</b> by ed25519 key and by RSA key.
+ *
+ * The rule is, approximately:
+ * If a (ed,rsa) identity is listed by more than half of authorities,
+ * include it. And include all (rsa)-only votes about that node as
+ * matching.
+ *
+ * Otherwise, if an (*,rsa) or (rsa) identity is listed by more than
+ * half of the authorities, and no (ed,rsa) pair for the same RSA key
+ * has been already been included based on the rule above, include
+ * that RSA identity.
+ */
static void
dircollator_collate_by_ed25519(dircollator_t *dc)
{
@@ -197,6 +243,7 @@ dircollator_collate_by_ed25519(dircollator_t *dc)
ddmap_entry_t **iter;
+ /* Go over all <ed,rsa> pairs */
HT_FOREACH(iter, double_digest_map, &dc->by_both_ids) {
ddmap_entry_t *ent = *iter;
int n = 0, i;
@@ -205,9 +252,13 @@ dircollator_collate_by_ed25519(dircollator_t *dc)
++n;
}
+ /* If not enough authorties listed this exact <ed,rsa> pair,
+ * don't include it. */
if (n <= total_authorities / 2)
continue;
+ /* Now consider whether there are any other entries with the same
+ * RSA key (but with possibly different or missing ed value). */
vote_routerstatus_t **vrs_lst2 = digestmap_get(dc->by_rsa_sha1,
(char*)ent->d);
tor_assert(vrs_lst2);
@@ -220,13 +271,17 @@ dircollator_collate_by_ed25519(dircollator_t *dc)
}
}
+ /* Record that we have seen this RSA digest. */
digestmap_set(rsa_digests, (char*)ent->d, ent->vrs_lst);
smartlist_add(dc->all_rsa_sha1_lst, ent->d);
}
+ /* Now look over all entries with an RSA digest, looking for RSA digests
+ * we didn't put in yet.
+ */
DIGESTMAP_FOREACH(dc->by_rsa_sha1, k, vote_routerstatus_t **, vrs_lst) {
if (digestmap_get(rsa_digests, k) != NULL)
- continue;
+ continue; /* We already included this RSA digest */
int n = 0, i;
for (i = 0; i < dc->n_votes; ++i) {
@@ -235,7 +290,7 @@ dircollator_collate_by_ed25519(dircollator_t *dc)
}
if (n <= total_authorities / 2)
- continue;
+ continue; /* Not enough votes */
digestmap_set(rsa_digests, k, vrs_lst);
smartlist_add(dc->all_rsa_sha1_lst, (char *)k);
@@ -244,15 +299,27 @@ dircollator_collate_by_ed25519(dircollator_t *dc)
dc->by_collated_rsa_sha1 = rsa_digests;
}
+/** Return the total number of collated router entries. This function may
+ * only be called after dircollator_collate. */
int
dircollator_n_routers(dircollator_t *dc)
{
+ tor_assert(dc->is_collated);
return smartlist_len(dc->all_rsa_sha1_lst);
}
+/** Return an array of vote_routerstatus_t entries for the <b>idx</b>th router
+ * in the collation order. Each array contains n_votes elements, where the
+ * nth element of the array is the vote_routerstatus_t from the nth voter for
+ * this identity (or NULL if there is no such entry).
+ *
+ * The maximum value for <b>idx</b> is dircollator_n_routers().
+ *
+ * This function may only be called after dircollator_collate. */
vote_routerstatus_t **
dircollator_get_votes_for_router(dircollator_t *dc, int idx)
{
+ tor_assert(dc->is_collated);
tor_assert(idx < smartlist_len(dc->all_rsa_sha1_lst));
return digestmap_get(dc->by_collated_rsa_sha1,
smartlist_get(dc->all_rsa_sha1_lst, idx));
diff --git a/src/or/dircollate.h b/src/or/dircollate.h
index cd1e8ac96d..358c730cbb 100644
--- a/src/or/dircollate.h
+++ b/src/or/dircollate.h
@@ -1,12 +1,12 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2014, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
- * \file dirvote.h
- * \brief Header file for dirvote.c.
+ * \file dircollate.h
+ * \brief Header file for dircollate.c.
**/
#ifndef TOR_DIRCOLLATE_H
@@ -30,18 +30,36 @@ vote_routerstatus_t **dircollator_get_votes_for_router(dircollator_t *dc,
#ifdef DIRCOLLATE_PRIVATE
struct ddmap_entry_s;
typedef HT_HEAD(double_digest_map, ddmap_entry_s) double_digest_map_t;
+/** A dircollator keeps track of all the routerstatus entries in a
+ * set of networkstatus votes, and matches them by an appropriate rule. */
struct dircollator_s {
- /**DOCDOC */
+ /** True iff we have run the collation algorithm. */
int is_collated;
+ /** The total number of votes that we received. */
int n_votes;
+ /** The total number of authorities we acknowledge. */
int n_authorities;
+ /** The index which the next vote to be added to this collator should
+ * receive. */
int next_vote_num;
+ /** Map from RSA-SHA1 identity digest to an array of <b>n_votes</b>
+ * vote_routerstatus_t* pointers, such that the i'th member of the
+ * array is the i'th vote's entry for that RSA-SHA1 ID.*/
digestmap_t *by_rsa_sha1;
+ /** Map from <ed, RSA-SHA1> pair to an array similar to that used in
+ * by_rsa_sha1 above. We include <NULL,RSA-SHA1> entries for votes that
+ * say that there is no Ed key. */
struct double_digest_map by_both_ids;
+ /** One of two outputs created by collation: a map from RSA-SHA1
+ * identity digest to an array of the vote_routerstatus_t objects. Entries
+ * only exist in this map for identities that we should include in the
+ * consensus. */
digestmap_t *by_collated_rsa_sha1;
+ /** One of two outputs created by collation: a sorted array of RSA-SHA1
+ * identity digests .*/
smartlist_t *all_rsa_sha1_lst;
};
#endif
diff --git a/src/or/directory.c b/src/or/directory.c
index 9461606f1b..7d1bbf0c8c 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -1,9 +1,10 @@
/* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#include "or.h"
+#include "backtrace.h"
#include "buffers.h"
#include "circuitbuild.h"
#include "config.h"
@@ -82,18 +83,18 @@ static void dir_microdesc_download_failed(smartlist_t *failed,
static void note_client_request(int purpose, int compressed, size_t bytes);
static int client_likes_consensus(networkstatus_t *v, const char *want_url);
-static void directory_initiate_command_rend(const tor_addr_t *addr,
- uint16_t or_port,
- uint16_t dir_port,
- const char *digest,
- uint8_t dir_purpose,
- uint8_t router_purpose,
- dir_indirection_t indirection,
- const char *resource,
- const char *payload,
- size_t payload_len,
- time_t if_modified_since,
- const rend_data_t *rend_query);
+static void directory_initiate_command_rend(
+ const tor_addr_port_t *or_addr_port,
+ const tor_addr_port_t *dir_addr_port,
+ const char *digest,
+ uint8_t dir_purpose,
+ uint8_t router_purpose,
+ dir_indirection_t indirection,
+ const char *resource,
+ const char *payload,
+ size_t payload_len,
+ time_t if_modified_since,
+ const rend_data_t *rend_query);
/********* START VARIABLES **********/
@@ -143,7 +144,7 @@ purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose)
/** Return a newly allocated string describing <b>auth</b>. Only describes
* authority features. */
-static char *
+STATIC char *
authdir_type_to_string(dirinfo_type_t auth)
{
char *result;
@@ -162,7 +163,7 @@ authdir_type_to_string(dirinfo_type_t auth)
}
/** Return a string describing a given directory connection purpose. */
-static const char *
+STATIC const char *
dir_conn_purpose_to_string(int purpose)
{
switch (purpose)
@@ -313,7 +314,6 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
SMARTLIST_FOREACH_BEGIN(dirservers, dir_server_t *, ds) {
routerstatus_t *rs = &(ds->fake_status);
size_t upload_len = payload_len;
- tor_addr_t ds_addr;
if ((type & ds->type) == 0)
continue;
@@ -344,11 +344,12 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
log_info(LD_DIR, "Uploading an extrainfo too (length %d)",
(int) extrainfo_len);
}
- tor_addr_from_ipv4h(&ds_addr, ds->addr);
if (purpose_needs_anonymity(dir_purpose, router_purpose)) {
indirection = DIRIND_ANONYMOUS;
- } else if (!fascist_firewall_allows_address_dir(&ds_addr,ds->dir_port)) {
- if (fascist_firewall_allows_address_or(&ds_addr,ds->or_port))
+ } else if (!fascist_firewall_allows_dir_server(ds,
+ FIREWALL_DIR_CONNECTION,
+ 0)) {
+ if (fascist_firewall_allows_dir_server(ds, FIREWALL_OR_CONNECTION, 0))
indirection = DIRIND_ONEHOP;
else
indirection = DIRIND_ANONYMOUS;
@@ -370,7 +371,7 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
/** Return true iff, according to the values in <b>options</b>, we should be
* using directory guards for direct downloads of directory information. */
-static int
+STATIC int
should_use_directory_guards(const or_options_t *options)
{
/* Public (non-bridge) servers never use directory guards. */
@@ -425,14 +426,17 @@ directory_pick_generic_dirserver(dirinfo_type_t type, int pds_flags,
* Use <b>pds_flags</b> as arguments to router_pick_directory_server()
* or router_pick_trusteddirserver().
*/
-MOCK_IMPL(void, directory_get_from_dirserver, (uint8_t dir_purpose,
- uint8_t router_purpose,
- const char *resource,
- int pds_flags))
+MOCK_IMPL(void, directory_get_from_dirserver, (
+ uint8_t dir_purpose,
+ uint8_t router_purpose,
+ const char *resource,
+ int pds_flags,
+ download_want_authority_t want_authority))
{
const routerstatus_t *rs = NULL;
const or_options_t *options = get_options();
- int prefer_authority = directory_fetches_from_authorities(options);
+ int prefer_authority = (directory_fetches_from_authorities(options)
+ || want_authority == DL_WANT_AUTHORITY);
int require_authority = 0;
int get_via_tor = purpose_needs_anonymity(dir_purpose, router_purpose);
dirinfo_type_t type = dir_fetch_type(dir_purpose, router_purpose, resource);
@@ -493,11 +497,14 @@ MOCK_IMPL(void, directory_get_from_dirserver, (uint8_t dir_purpose,
const node_t *node = choose_random_dirguard(type);
if (node && node->ri) {
/* every bridge has a routerinfo. */
- tor_addr_t addr;
routerinfo_t *ri = node->ri;
- node_get_addr(node, &addr);
- directory_initiate_command(&addr,
- ri->or_port, 0/*no dirport*/,
+ /* clients always make OR connections to bridges */
+ tor_addr_port_t or_ap;
+ /* we are willing to use a non-preferred address if we need to */
+ fascist_firewall_choose_address_node(node, FIREWALL_OR_CONNECTION, 0,
+ &or_ap);
+ directory_initiate_command(&or_ap.addr, or_ap.port,
+ NULL, 0, /*no dirport*/
ri->cache_info.identity_digest,
dir_purpose,
router_purpose,
@@ -606,6 +613,95 @@ dirind_is_anon(dir_indirection_t ind)
return ind == DIRIND_ANON_DIRPORT || ind == DIRIND_ANONYMOUS;
}
+/* Choose reachable OR and Dir addresses and ports from status, copying them
+ * into use_or_ap and use_dir_ap. If indirection is anonymous, then we're
+ * connecting via another relay, so choose the primary IPv4 address and ports.
+ *
+ * status should have at least one reachable address, if we can't choose a
+ * reachable address, warn and return -1. Otherwise, return 0.
+ */
+static int
+directory_choose_address_routerstatus(const routerstatus_t *status,
+ dir_indirection_t indirection,
+ tor_addr_port_t *use_or_ap,
+ tor_addr_port_t *use_dir_ap)
+{
+ tor_assert(status != NULL);
+ tor_assert(use_or_ap != NULL);
+ tor_assert(use_dir_ap != NULL);
+
+ const or_options_t *options = get_options();
+ int have_or = 0, have_dir = 0;
+
+ /* We expect status to have at least one reachable address if we're
+ * connecting to it directly.
+ *
+ * Therefore, we can simply use the other address if the one we want isn't
+ * allowed by the firewall.
+ *
+ * (When Tor uploads and downloads a hidden service descriptor, it uses
+ * DIRIND_ANONYMOUS, except for Tor2Web, which uses DIRIND_ONEHOP.
+ * So this code will only modify the address for Tor2Web's HS descriptor
+ * fetches. Even Single Onion Servers (NYI) use DIRIND_ANONYMOUS, to avoid
+ * HSDirs denying service by rejecting descriptors.)
+ */
+
+ /* Initialise the OR / Dir addresses */
+ tor_addr_make_null(&use_or_ap->addr, AF_UNSPEC);
+ use_or_ap->port = 0;
+ tor_addr_make_null(&use_dir_ap->addr, AF_UNSPEC);
+ use_dir_ap->port = 0;
+
+ /* ORPort connections */
+ if (indirection == DIRIND_ANONYMOUS) {
+ if (status->addr) {
+ /* Since we're going to build a 3-hop circuit and ask the 2nd relay
+ * to extend to this address, always use the primary (IPv4) OR address */
+ tor_addr_from_ipv4h(&use_or_ap->addr, status->addr);
+ use_or_ap->port = status->or_port;
+ have_or = 1;
+ }
+ } else if (indirection == DIRIND_ONEHOP) {
+ /* We use an IPv6 address if we have one and we prefer it.
+ * Use the preferred address and port if they are reachable, otherwise,
+ * use the alternate address and port (if any).
+ */
+ have_or = fascist_firewall_choose_address_rs(status,
+ FIREWALL_OR_CONNECTION, 0,
+ use_or_ap);
+ }
+
+ /* DirPort connections
+ * DIRIND_ONEHOP uses ORPort, but may fall back to the DirPort on relays */
+ if (indirection == DIRIND_DIRECT_CONN ||
+ indirection == DIRIND_ANON_DIRPORT ||
+ (indirection == DIRIND_ONEHOP
+ && !directory_must_use_begindir(options))) {
+ have_dir = fascist_firewall_choose_address_rs(status,
+ FIREWALL_DIR_CONNECTION, 0,
+ use_dir_ap);
+ }
+
+ /* We rejected all addresses in the relay's status. This means we can't
+ * connect to it. */
+ if (!have_or && !have_dir) {
+ static int logged_backtrace = 0;
+ log_info(LD_BUG, "Rejected all OR and Dir addresses from %s when "
+ "launching an outgoing directory connection to: IPv4 %s OR %d "
+ "Dir %d IPv6 %s OR %d Dir %d", routerstatus_describe(status),
+ fmt_addr32(status->addr), status->or_port,
+ status->dir_port, fmt_addr(&status->ipv6_addr),
+ status->ipv6_orport, status->dir_port);
+ if (!logged_backtrace) {
+ log_backtrace(LOG_INFO, LD_BUG, "Addresses came from");
+ logged_backtrace = 1;
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
/** Same as directory_initiate_command_routerstatus(), but accepts
* rendezvous data to fetch a hidden service descriptor. */
void
@@ -621,8 +717,11 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
{
const or_options_t *options = get_options();
const node_t *node;
- tor_addr_t addr;
+ tor_addr_port_t use_or_ap, use_dir_ap;
const int anonymized_connection = dirind_is_anon(indirection);
+
+ tor_assert(status != NULL);
+
node = node_get_by_id(status->identity_digest);
if (!node && anonymized_connection) {
@@ -631,7 +730,6 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
routerstatus_describe(status));
return;
}
- tor_addr_from_ipv4h(&addr, status->addr);
if (options->ExcludeNodes && options->StrictNodes &&
routerset_contains_routerstatus(options->ExcludeNodes, status, -1)) {
@@ -643,13 +741,30 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
return;
}
- directory_initiate_command_rend(&addr,
- status->or_port, status->dir_port,
- status->identity_digest,
- dir_purpose, router_purpose,
- indirection, resource,
- payload, payload_len, if_modified_since,
- rend_query);
+ /* At this point, if we are a clients making a direct connection to a
+ * directory server, we have selected a server that has at least one address
+ * allowed by ClientUseIPv4/6 and Reachable{"",OR,Dir}Addresses. This
+ * selection uses the preference in ClientPreferIPv6{OR,Dir}Port, if
+ * possible. (If UseBridges is set, clients always use IPv6, and prefer it
+ * by default.)
+ *
+ * Now choose an address that we can use to connect to the directory server.
+ */
+ if (directory_choose_address_routerstatus(status, indirection, &use_or_ap,
+ &use_dir_ap) < 0) {
+ return;
+ }
+
+ /* We don't retry the alternate OR/Dir address for the same directory if
+ * the address we choose fails (#6772).
+ * Instead, we'll retry another directory on failure. */
+
+ directory_initiate_command_rend(&use_or_ap, &use_dir_ap,
+ status->identity_digest,
+ dir_purpose, router_purpose,
+ indirection, resource,
+ payload, payload_len, if_modified_since,
+ rend_query);
}
/** Launch a new connection to the directory server <b>status</b> to
@@ -666,15 +781,15 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
* When fetching a rendezvous descriptor, <b>resource</b> is the service ID we
* want to fetch.
*/
-void
-directory_initiate_command_routerstatus(const routerstatus_t *status,
- uint8_t dir_purpose,
- uint8_t router_purpose,
- dir_indirection_t indirection,
- const char *resource,
- const char *payload,
- size_t payload_len,
- time_t if_modified_since)
+MOCK_IMPL(void, directory_initiate_command_routerstatus,
+ (const routerstatus_t *status,
+ uint8_t dir_purpose,
+ uint8_t router_purpose,
+ dir_indirection_t indirection,
+ const char *resource,
+ const char *payload,
+ size_t payload_len,
+ time_t if_modified_since))
{
directory_initiate_command_routerstatus_rend(status, dir_purpose,
router_purpose,
@@ -712,7 +827,7 @@ connection_dir_request_failed(dir_connection_t *conn)
return; /* this was a test fetch. don't retry. */
}
if (!entry_list_is_constrained(get_options()))
- router_set_status(conn->identity_digest, 0); /* don't try him again */
+ router_set_status(conn->identity_digest, 0); /* don't try this one again */
if (conn->base_.purpose == DIR_PURPOSE_FETCH_SERVERDESC ||
conn->base_.purpose == DIR_PURPOSE_FETCH_EXTRAINFO) {
log_info(LD_DIR, "Giving up on serverdesc/extrainfo fetch from "
@@ -851,6 +966,16 @@ connection_dir_download_cert_failed(dir_connection_t *conn, int status)
update_certificate_downloads(time(NULL));
}
+/* Should this tor instance only use begindir for all its directory requests?
+ */
+int
+directory_must_use_begindir(const or_options_t *options)
+{
+ /* Clients, onion services, and bridges must use begindir,
+ * relays and authorities do not have to */
+ return !public_server_mode(options);
+}
+
/** Evaluate the situation and decide if we should use an encrypted
* "begindir-style" connection for this directory request.
* 1) If or_port is 0, or it's a direct conn and or_port is firewalled
@@ -858,40 +983,90 @@ connection_dir_download_cert_failed(dir_connection_t *conn, int status)
* 2) If we prefer to avoid begindir conns, and we're not fetching or
* publishing a bridge relay descriptor, no.
* 3) Else yes.
+ * If returning 0, return in *reason why we can't use begindir.
+ * reason must not be NULL.
*/
static int
directory_command_should_use_begindir(const or_options_t *options,
const tor_addr_t *addr,
int or_port, uint8_t router_purpose,
- dir_indirection_t indirection)
+ dir_indirection_t indirection,
+ const char **reason)
{
(void) router_purpose;
- if (!or_port)
+ tor_assert(reason);
+ *reason = NULL;
+
+ /* Reasons why we can't possibly use begindir */
+ if (!or_port) {
+ *reason = "directory with unknown ORPort";
return 0; /* We don't know an ORPort -- no chance. */
- if (indirection == DIRIND_DIRECT_CONN || indirection == DIRIND_ANON_DIRPORT)
+ }
+ if (indirection == DIRIND_DIRECT_CONN ||
+ indirection == DIRIND_ANON_DIRPORT) {
+ *reason = "DirPort connection";
return 0;
- if (indirection == DIRIND_ONEHOP)
- if (!fascist_firewall_allows_address_or(addr, or_port) ||
- directory_fetches_from_authorities(options))
- return 0; /* We're firewalled or are acting like a relay -- also no. */
+ }
+ if (indirection == DIRIND_ONEHOP) {
+ /* We're firewalled and want a direct OR connection */
+ if (!fascist_firewall_allows_address_addr(addr, or_port,
+ FIREWALL_OR_CONNECTION, 0, 0)) {
+ *reason = "ORPort not reachable";
+ return 0;
+ }
+ }
+ /* Reasons why we want to avoid using begindir */
+ if (indirection == DIRIND_ONEHOP) {
+ if (!directory_must_use_begindir(options)) {
+ *reason = "in relay mode";
+ return 0;
+ }
+ }
+ /* DIRIND_ONEHOP on a client, or DIRIND_ANONYMOUS
+ */
+ *reason = "(using begindir)";
return 1;
}
-/** Helper for directory_initiate_command_routerstatus: send the
- * command to a server whose address is <b>address</b>, whose IP is
- * <b>addr</b>, whose directory port is <b>dir_port</b>, whose tor version
- * <b>supports_begindir</b>, and whose identity key digest is
- * <b>digest</b>. */
+/** Helper for directory_initiate_command_rend: send the
+ * command to a server whose OR address/port is <b>or_addr</b>/<b>or_port</b>,
+ * whose directory address/port is <b>dir_addr</b>/<b>dir_port</b>, whose
+ * identity key digest is <b>digest</b>, with purposes <b>dir_purpose</b> and
+ * <b>router_purpose</b>, making an (in)direct connection as specified in
+ * <b>indirection</b>, with command <b>resource</b>, <b>payload</b> of
+ * <b>payload_len</b>, and asking for a result only <b>if_modified_since</b>.
+ */
void
-directory_initiate_command(const tor_addr_t *_addr,
- uint16_t or_port, uint16_t dir_port,
+directory_initiate_command(const tor_addr_t *or_addr, uint16_t or_port,
+ const tor_addr_t *dir_addr, uint16_t dir_port,
const char *digest,
uint8_t dir_purpose, uint8_t router_purpose,
dir_indirection_t indirection, const char *resource,
const char *payload, size_t payload_len,
time_t if_modified_since)
{
- directory_initiate_command_rend(_addr, or_port, dir_port,
+ tor_addr_port_t or_ap, dir_ap;
+
+ /* Use the null tor_addr and 0 port if the address or port isn't valid. */
+ if (tor_addr_port_is_valid(or_addr, or_port, 0)) {
+ tor_addr_copy(&or_ap.addr, or_addr);
+ or_ap.port = or_port;
+ } else {
+ /* the family doesn't matter here, so make it IPv4 */
+ tor_addr_make_null(&or_ap.addr, AF_INET);
+ or_ap.port = or_port = 0;
+ }
+
+ if (tor_addr_port_is_valid(dir_addr, dir_port, 0)) {
+ tor_addr_copy(&dir_ap.addr, dir_addr);
+ dir_ap.port = dir_port;
+ } else {
+ /* the family doesn't matter here, so make it IPv4 */
+ tor_addr_make_null(&dir_ap.addr, AF_INET);
+ dir_ap.port = dir_port = 0;
+ }
+
+ directory_initiate_command_rend(&or_ap, &dir_ap,
digest, dir_purpose,
router_purpose, indirection,
resource, payload, payload_len,
@@ -911,10 +1086,11 @@ is_sensitive_dir_purpose(uint8_t dir_purpose)
}
/** Same as directory_initiate_command(), but accepts rendezvous data to
- * fetch a hidden service descriptor. */
+ * fetch a hidden service descriptor, and takes its address & port arguments
+ * as tor_addr_port_t. */
static void
-directory_initiate_command_rend(const tor_addr_t *_addr,
- uint16_t or_port, uint16_t dir_port,
+directory_initiate_command_rend(const tor_addr_port_t *or_addr_port,
+ const tor_addr_port_t *dir_addr_port,
const char *digest,
uint8_t dir_purpose, uint8_t router_purpose,
dir_indirection_t indirection,
@@ -923,19 +1099,33 @@ directory_initiate_command_rend(const tor_addr_t *_addr,
time_t if_modified_since,
const rend_data_t *rend_query)
{
+ tor_assert(or_addr_port);
+ tor_assert(dir_addr_port);
+ tor_assert(or_addr_port->port || dir_addr_port->port);
+ tor_assert(digest);
+
dir_connection_t *conn;
const or_options_t *options = get_options();
int socket_error = 0;
- int use_begindir = directory_command_should_use_begindir(options, _addr,
- or_port, router_purpose, indirection);
+ const char *begindir_reason = NULL;
+ /* Should the connection be to a relay's OR port (and inside that we will
+ * send our directory request)? */
+ const int use_begindir = directory_command_should_use_begindir(options,
+ &or_addr_port->addr, or_addr_port->port,
+ router_purpose, indirection,
+ &begindir_reason);
+ /* Will the connection go via a three-hop Tor circuit? Note that this
+ * is separate from whether it will use_begindir. */
const int anonymized_connection = dirind_is_anon(indirection);
- tor_addr_t addr;
- tor_assert(_addr);
- tor_assert(or_port || dir_port);
- tor_assert(digest);
-
- tor_addr_copy(&addr, _addr);
+ /* What is the address we want to make the directory request to? If
+ * we're making a begindir request this is the ORPort of the relay
+ * we're contacting; if not a begindir request, this is its DirPort.
+ * Note that if anonymized_connection is true, we won't be initiating
+ * a connection directly to this address. */
+ tor_addr_t addr;
+ tor_addr_copy(&addr, &(use_begindir ? or_addr_port : dir_addr_port)->addr);
+ uint16_t port = (use_begindir ? or_addr_port : dir_addr_port)->port;
log_debug(LD_DIR, "anonymized %d, use_begindir %d.",
anonymized_connection, use_begindir);
@@ -949,6 +1139,14 @@ directory_initiate_command_rend(const tor_addr_t *_addr,
(void)is_sensitive_dir_purpose;
#endif
+ /* use encrypted begindir connections for everything except relays
+ * this provides better protection for directory fetches */
+ if (!use_begindir && directory_must_use_begindir(options)) {
+ log_warn(LD_BUG, "Client could not use begindir connection: %s",
+ begindir_reason ? begindir_reason : "(NULL)");
+ return;
+ }
+
/* ensure that we don't make direct connections when a SOCKS server is
* configured. */
if (!anonymized_connection && !use_begindir && !options->HTTPProxy &&
@@ -958,12 +1156,32 @@ directory_initiate_command_rend(const tor_addr_t *_addr,
return;
}
+ /* Make sure that the destination addr and port we picked is viable. */
+ if (!port || tor_addr_is_null(&addr)) {
+ static int logged_backtrace = 0;
+ log_warn(LD_DIR,
+ "Cannot make an outgoing %sconnection without %sPort.",
+ use_begindir ? "begindir " : "",
+ use_begindir ? "an OR" : "a Dir");
+ if (!logged_backtrace) {
+ log_backtrace(LOG_INFO, LD_BUG, "Address came from");
+ logged_backtrace = 1;
+ }
+ return;
+ }
+
+ /* ensure we don't make excess connections when we're already downloading
+ * a consensus during bootstrap */
+ if (connection_dir_avoid_extra_connection_for_purpose(dir_purpose)) {
+ return;
+ }
+
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);
- conn->base_.port = use_begindir ? or_port : dir_port;
- conn->base_.address = tor_dup_addr(&addr);
+ conn->base_.port = port;
+ conn->base_.address = tor_addr_to_str_dup(&addr);
memcpy(conn->identity_digest, digest, DIGEST_LEN);
conn->base_.purpose = dir_purpose;
@@ -985,22 +1203,24 @@ directory_initiate_command_rend(const tor_addr_t *_addr,
if (options->HTTPProxy) {
tor_addr_copy(&addr, &options->HTTPProxyAddr);
- dir_port = options->HTTPProxyPort;
+ port = options->HTTPProxyPort;
}
switch (connection_connect(TO_CONN(conn), conn->base_.address, &addr,
- dir_port, &socket_error)) {
+ port, &socket_error)) {
case -1:
- connection_dir_request_failed(conn); /* retry if we want */
- /* XXX we only pass 'conn' above, not 'resource', 'payload',
- * etc. So in many situations it can't retry! -RD */
- connection_free(TO_CONN(conn));
+ connection_mark_for_close(TO_CONN(conn));
return;
case 1:
/* start flushing conn */
conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING;
/* fall through */
case 0:
+ /* Close this connection if there's another consensus connection
+ * downloading (during bootstrap), or connecting (after bootstrap). */
+ if (connection_dir_close_consensus_conn_if_extra(conn)) {
+ return;
+ }
/* queue the command on the outbuf */
directory_send_command(conn, dir_purpose, 1, resource,
payload, payload_len,
@@ -1009,8 +1229,12 @@ directory_initiate_command_rend(const tor_addr_t *_addr,
/* writable indicates finish, readable indicates broken link,
error indicates broken link in windowsland. */
}
- } else { /* we want to connect via a tor connection */
+ } else {
+ /* We will use a Tor circuit (maybe 1-hop, maybe 3-hop, maybe with
+ * begindir, maybe not with begindir) */
+
entry_connection_t *linked_conn;
+
/* Anonymized tunneled connections can never share a circuit.
* One-hop directory connections can share circuits with each other
* but nothing else. */
@@ -1032,7 +1256,7 @@ directory_initiate_command_rend(const tor_addr_t *_addr,
conn->base_.address, conn->base_.port,
digest,
SESSION_GROUP_DIRCONN, iso_flags,
- use_begindir, conn->dirconn_direct);
+ use_begindir, !anonymized_connection);
if (!linked_conn) {
log_warn(LD_NET,"Making tunnel to dirserver failed.");
connection_mark_for_close(TO_CONN(conn));
@@ -1044,6 +1268,11 @@ directory_initiate_command_rend(const tor_addr_t *_addr,
connection_mark_for_close(TO_CONN(conn));
return;
}
+ /* Close this connection if there's another consensus connection
+ * downloading (during bootstrap), or connecting (after bootstrap). */
+ if (connection_dir_close_consensus_conn_if_extra(conn)) {
+ return;
+ }
conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING;
/* queue the command on the outbuf */
directory_send_command(conn, dir_purpose, 0, resource,
@@ -1138,6 +1367,23 @@ directory_get_consensus_url(const char *resource)
return url;
}
+/**
+ * Copies the ipv6 from source to destination, subject to buffer size limit
+ * size. If decorate is true, makes sure the copied address is decorated.
+ */
+static void
+copy_ipv6_address(char* destination, const char* source, size_t len,
+ int decorate) {
+ tor_assert(destination);
+ tor_assert(source);
+
+ if (decorate && source[0] != '[') {
+ tor_snprintf(destination, len, "[%s]", source);
+ } else {
+ strlcpy(destination, source, len);
+ }
+}
+
/** Queue an appropriate HTTP command on conn-\>outbuf. The other args
* are as in directory_initiate_command().
*/
@@ -1149,6 +1395,9 @@ directory_send_command(dir_connection_t *conn,
{
char proxystring[256];
char hoststring[128];
+ /* NEEDS to be the same size hoststring.
+ Will be decorated with brackets around it if it is ipv6. */
+ char decorated_address[128];
smartlist_t *headers = smartlist_new();
char *url;
char request[8192];
@@ -1161,12 +1410,20 @@ directory_send_command(dir_connection_t *conn,
if (resource)
conn->requested_resource = tor_strdup(resource);
+ /* decorate the ip address if it is ipv6 */
+ if (strchr(conn->base_.address, ':')) {
+ copy_ipv6_address(decorated_address, conn->base_.address,
+ sizeof(decorated_address), 1);
+ } else {
+ strlcpy(decorated_address, conn->base_.address, sizeof(decorated_address));
+ }
+
/* come up with a string for which Host: we want */
if (conn->base_.port == 80) {
- strlcpy(hoststring, conn->base_.address, sizeof(hoststring));
+ strlcpy(hoststring, decorated_address, sizeof(hoststring));
} else {
- tor_snprintf(hoststring, sizeof(hoststring),"%s:%d",
- conn->base_.address, conn->base_.port);
+ tor_snprintf(hoststring, sizeof(hoststring), "%s:%d",
+ decorated_address, conn->base_.port);
}
/* Format if-modified-since */
@@ -1574,7 +1831,7 @@ load_downloaded_routers(const char *body, smartlist_t *which,
added = router_load_routers_from_string(body, NULL, SAVED_NOWHERE, which,
descriptor_digests, buf);
- if (general)
+ if (added && general)
control_event_bootstrap(BOOTSTRAP_STATUS_LOADING_DESCRIPTORS,
count_loading_descriptors_progress());
return added;
@@ -1598,7 +1855,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
size_t body_len = 0, orig_len = 0;
int status_code;
time_t date_header = 0;
- long delta;
+ long apparent_skew;
compress_method_t compression;
int plausible;
int skewed = 0;
@@ -1657,28 +1914,15 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
* and the date header. (We used to check now-date_header, but that's
* inaccurate if we spend a lot of time downloading.)
*/
- delta = conn->base_.timestamp_lastwritten - date_header;
- if (labs(delta)>ALLOW_DIRECTORY_TIME_SKEW) {
- char dbuf[64];
+ apparent_skew = conn->base_.timestamp_lastwritten - date_header;
+ if (labs(apparent_skew)>ALLOW_DIRECTORY_TIME_SKEW) {
int trusted = router_digest_is_trusted_dir(conn->identity_digest);
- format_time_interval(dbuf, sizeof(dbuf), delta);
- log_fn(trusted ? LOG_WARN : LOG_INFO,
- LD_HTTP,
- "Received directory with skewed time (server '%s:%d'): "
- "It seems that our clock is %s by %s, or that theirs is %s. "
- "Tor requires an accurate clock to work: please check your time, "
- "timezone, and date settings.",
- conn->base_.address, conn->base_.port,
- delta>0 ? "ahead" : "behind", dbuf,
- delta>0 ? "behind" : "ahead");
+ clock_skew_warning(TO_CONN(conn), apparent_skew, trusted, LD_HTTP,
+ "directory", "DIRSERV");
skewed = 1; /* don't check the recommended-versions line */
- if (trusted)
- control_event_general_status(LOG_WARN,
- "CLOCK_SKEW SKEW=%ld SOURCE=DIRSERV:%s:%d",
- delta, conn->base_.address, conn->base_.port);
} else {
log_debug(LD_HTTP, "Time on received directory is within tolerance; "
- "we are %ld seconds skewed. (That's okay.)", delta);
+ "we are %ld seconds skewed. (That's okay.)", apparent_skew);
}
}
(void) skewed; /* skewed isn't used yet. */
@@ -1788,7 +2032,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
routers_update_all_from_networkstatus(now, 3);
update_microdescs_from_networkstatus(now);
update_microdesc_downloads(now);
- directory_info_has_arrived(now, 0);
+ directory_info_has_arrived(now, 0, 0);
log_info(LD_DIR, "Successfully loaded consensus.");
}
@@ -1824,7 +2068,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
* ones got flushed to disk so it's safe to call this on them */
connection_dir_download_cert_failed(conn, status_code);
} else {
- directory_info_has_arrived(now, 0);
+ directory_info_has_arrived(now, 0, 0);
log_info(LD_DIR, "Successfully loaded certificates from fetch.");
}
} else {
@@ -1938,7 +2182,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
if (load_downloaded_routers(body, which, descriptor_digests,
conn->router_purpose,
conn->base_.address))
- directory_info_has_arrived(now, 0);
+ directory_info_has_arrived(now, 0, 0);
}
}
if (which) { /* mark remaining ones as failed */
@@ -1989,8 +2233,11 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
/* Mark remaining ones as failed. */
dir_microdesc_download_failed(which, status_code);
}
- control_event_bootstrap(BOOTSTRAP_STATUS_LOADING_DESCRIPTORS,
- count_loading_descriptors_progress());
+ if (mds && smartlist_len(mds)) {
+ control_event_bootstrap(BOOTSTRAP_STATUS_LOADING_DESCRIPTORS,
+ count_loading_descriptors_progress());
+ directory_info_has_arrived(now, 0, 1);
+ }
SMARTLIST_FOREACH(which, char *, cp, tor_free(cp));
smartlist_free(which);
smartlist_free(mds);
@@ -2117,41 +2364,33 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
{
rend_cache_entry_t *entry = NULL;
- switch (rend_cache_store_v2_desc_as_client(body,
- conn->requested_resource, conn->rend_data,
- &entry)) {
- case RCS_BADDESC:
- case RCS_NOTDIR: /* Impossible */
- log_warn(LD_REND,"Fetching v2 rendezvous descriptor failed. "
- "Retrying at another directory.");
- /* We'll retry when connection_about_to_close_connection()
- * cleans this dir conn up. */
- SEND_HS_DESC_FAILED_EVENT("BAD_DESC");
- SEND_HS_DESC_FAILED_CONTENT();
- break;
- case RCS_OKAY:
- default:
- {
- char service_id[REND_SERVICE_ID_LEN_BASE32 + 1];
- /* Should never be NULL here for an OKAY returned code. */
- tor_assert(entry);
- rend_get_service_id(entry->parsed->pk, service_id);
-
- /* success. notify pending connections about this. */
- log_info(LD_REND, "Successfully fetched v2 rendezvous "
- "descriptor.");
- control_event_hs_descriptor_received(service_id,
- conn->rend_data,
- conn->identity_digest);
- control_event_hs_descriptor_content(service_id,
- conn->requested_resource,
- conn->identity_digest,
- body);
- conn->base_.purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2;
- rend_client_desc_trynow(service_id);
- memwipe(service_id, 0, sizeof(service_id));
- break;
- }
+ if (rend_cache_store_v2_desc_as_client(body,
+ conn->requested_resource, conn->rend_data, &entry) < 0) {
+ log_warn(LD_REND,"Fetching v2 rendezvous descriptor failed. "
+ "Retrying at another directory.");
+ /* We'll retry when connection_about_to_close_connection()
+ * cleans this dir conn up. */
+ SEND_HS_DESC_FAILED_EVENT("BAD_DESC");
+ SEND_HS_DESC_FAILED_CONTENT();
+ } else {
+ char service_id[REND_SERVICE_ID_LEN_BASE32 + 1];
+ /* Should never be NULL here if we found the descriptor. */
+ tor_assert(entry);
+ rend_get_service_id(entry->parsed->pk, service_id);
+
+ /* success. notify pending connections about this. */
+ log_info(LD_REND, "Successfully fetched v2 rendezvous "
+ "descriptor.");
+ control_event_hs_descriptor_received(service_id,
+ conn->rend_data,
+ conn->identity_digest);
+ control_event_hs_descriptor_content(service_id,
+ conn->requested_resource,
+ conn->identity_digest,
+ body);
+ conn->base_.purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2;
+ rend_client_desc_trynow(service_id);
+ memwipe(service_id, 0, sizeof(service_id));
}
break;
}
@@ -2186,17 +2425,23 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
if (conn->base_.purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2) {
#define SEND_HS_DESC_UPLOAD_FAILED_EVENT(reason) ( \
- control_event_hs_descriptor_upload_failed(conn->identity_digest, \
- reason) )
+ control_event_hs_descriptor_upload_failed( \
+ conn->identity_digest, \
+ conn->rend_data->onion_address, \
+ reason) )
log_info(LD_REND,"Uploaded rendezvous descriptor (status %d "
"(%s))",
status_code, escaped(reason));
+ /* Without the rend data, we'll have a problem identifying what has been
+ * uploaded for which service. */
+ tor_assert(conn->rend_data);
switch (status_code) {
case 200:
log_info(LD_REND,
"Uploading rendezvous descriptor: finished with status "
"200 (%s)", escaped(reason));
- control_event_hs_descriptor_uploaded(conn->identity_digest);
+ control_event_hs_descriptor_uploaded(conn->identity_digest,
+ conn->rend_data->onion_address);
rend_service_desc_has_uploaded(conn->rend_data);
break;
case 400:
@@ -2593,7 +2838,7 @@ client_likes_consensus(networkstatus_t *v, const char *want_url)
/** Return the compression level we should use for sending a compressed
* response of size <b>n_bytes</b>. */
-static zlib_compression_level_t
+STATIC zlib_compression_level_t
choose_compression_level(ssize_t n_bytes)
{
if (! have_been_under_memory_pressure()) {
@@ -2614,7 +2859,7 @@ choose_compression_level(ssize_t n_bytes)
* service descriptor. On finding one, write a response into
* conn-\>outbuf. If the request is unrecognized, send a 400.
* Always return 0. */
-static int
+STATIC int
directory_handle_command_get(dir_connection_t *conn, const char *headers,
const char *req_body, size_t req_body_len)
{
@@ -2770,10 +3015,8 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
}
if (1) {
- struct in_addr in;
tor_addr_t addr;
- if (tor_inet_aton((TO_CONN(conn))->address, &in)) {
- tor_addr_from_ipv4h(&addr, ntohl(in.s_addr));
+ if (tor_addr_parse(&addr, (TO_CONN(conn))->address) >= 0) {
geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS,
&addr, NULL,
time(NULL));
@@ -2874,7 +3117,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
});
if (global_write_bucket_low(TO_CONN(conn), estimated_len, 2)) {
- write_http_status_line(conn, 503, "Directory busy, try again later.");
+ write_http_status_line(conn, 503, "Directory busy, try again later");
goto vote_done;
}
write_http_response_header(conn, body_len ? body_len : -1, compressed,
@@ -3071,7 +3314,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
len += c->cache_info.signed_descriptor_len);
if (global_write_bucket_low(TO_CONN(conn), compressed?len/2:len, 2)) {
- write_http_status_line(conn, 503, "Directory busy, try again later.");
+ write_http_status_line(conn, 503, "Directory busy, try again later");
goto keys_done;
}
@@ -3232,6 +3475,13 @@ directory_handle_command_post(dir_connection_t *conn, const char *headers,
conn->base_.state = DIR_CONN_STATE_SERVER_WRITING;
+ if (!public_server_mode(options)) {
+ log_info(LD_DIR, "Rejected dir post request from %s "
+ "since we're not a public relay.", conn->base_.address);
+ write_http_status_line(conn, 503, "Not acting as a public relay");
+ goto done;
+ }
+
if (parse_http_url(headers, &url) < 0) {
write_http_status_line(conn, 400, "Bad request");
return 0;
@@ -3241,24 +3491,14 @@ directory_handle_command_post(dir_connection_t *conn, const char *headers,
/* Handle v2 rendezvous service publish request. */
if (connection_dir_is_encrypted(conn) &&
!strcmpstart(url,"/tor/rendezvous2/publish")) {
- switch (rend_cache_store_v2_desc_as_dir(body)) {
- case RCS_NOTDIR:
- log_info(LD_REND, "Rejected v2 rend descriptor (length %d) from %s "
- "since we're not currently a hidden service directory.",
- (int)body_len, conn->base_.address);
- write_http_status_line(conn, 503, "Currently not acting as v2 "
- "hidden service directory");
- break;
- case RCS_BADDESC:
- log_warn(LD_REND, "Rejected v2 rend descriptor (length %d) from %s.",
- (int)body_len, conn->base_.address);
- write_http_status_line(conn, 400,
- "Invalid v2 service descriptor rejected");
- break;
- case RCS_OKAY:
- default:
- write_http_status_line(conn, 200, "Service descriptor (v2) stored");
- log_info(LD_REND, "Handled v2 rendezvous descriptor post: accepted");
+ if (rend_cache_store_v2_desc_as_dir(body) < 0) {
+ log_warn(LD_REND, "Rejected v2 rend descriptor (length %d) from %s.",
+ (int)body_len, conn->base_.address);
+ write_http_status_line(conn, 400,
+ "Invalid v2 service descriptor rejected");
+ } else {
+ write_http_status_line(conn, 200, "Service descriptor (v2) stored");
+ log_info(LD_REND, "Handled v2 rendezvous descriptor post: accepted");
}
goto done;
}
@@ -3398,7 +3638,7 @@ connection_dir_finished_flushing(dir_connection_t *conn)
tor_assert(conn->base_.type == CONN_TYPE_DIR);
/* Note that we have finished writing the directory response. For direct
- * connections this means we're done, for tunneled connections its only
+ * connections this means we're done; for tunneled connections it's only
* an intermediate step. */
if (conn->dirreq_id)
geoip_change_dirreq_state(conn->dirreq_id, DIRREQ_TUNNELED,
@@ -3439,8 +3679,227 @@ connection_dir_finished_flushing(dir_connection_t *conn)
return 0;
}
+/* A helper function for connection_dir_close_consensus_conn_if_extra()
+ * and connection_dir_close_extra_consensus_conns() that returns 0 if
+ * we can't have, or don't want to close, excess consensus connections. */
+STATIC int
+connection_dir_would_close_consensus_conn_helper(void)
+{
+ const or_options_t *options = get_options();
+
+ /* we're only interested in closing excess connections if we could
+ * have created any in the first place */
+ if (!networkstatus_consensus_can_use_multiple_directories(options)) {
+ return 0;
+ }
+
+ /* We want to close excess connections downloading a consensus.
+ * If there aren't any excess, we don't have anything to close. */
+ if (!networkstatus_consensus_has_excess_connections()) {
+ return 0;
+ }
+
+ /* If we have excess connections, but none of them are downloading a
+ * consensus, and we are still bootstrapping (that is, we have no usable
+ * consensus), we don't want to close any until one starts downloading. */
+ if (!networkstatus_consensus_is_downloading_usable_flavor()
+ && networkstatus_consensus_is_bootstrapping(time(NULL))) {
+ return 0;
+ }
+
+ /* If we have just stopped bootstrapping (that is, just parsed a consensus),
+ * we might still have some excess connections hanging around. So we still
+ * have to check if we want to close any, even if we've stopped
+ * bootstrapping. */
+ return 1;
+}
+
+/* Check if we would close excess consensus connections. If we would, any
+ * new consensus connection would become excess immediately, so return 1.
+ * Otherwise, return 0. */
+int
+connection_dir_avoid_extra_connection_for_purpose(unsigned int purpose)
+{
+ const or_options_t *options = get_options();
+
+ /* We're not interested in connections that aren't fetching a consensus. */
+ if (purpose != DIR_PURPOSE_FETCH_CONSENSUS) {
+ return 0;
+ }
+
+ /* we're only interested in avoiding excess connections if we could
+ * have created any in the first place */
+ if (!networkstatus_consensus_can_use_multiple_directories(options)) {
+ return 0;
+ }
+
+ /* If there are connections downloading a consensus, and we are still
+ * bootstrapping (that is, we have no usable consensus), we can be sure that
+ * any further connections would be excess. */
+ if (networkstatus_consensus_is_downloading_usable_flavor()
+ && networkstatus_consensus_is_bootstrapping(time(NULL))) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Check if we have more than one consensus download connection attempt, and
+ * close conn:
+ * - if we don't have a consensus, and we're downloading a consensus, and conn
+ * is not downloading a consensus yet;
+ * - if we do have a consensus, and there's more than one consensus connection.
+ *
+ * Post-bootstrap consensus connection attempts are initiated one at a time.
+ * So this function won't close any consensus connection attempts that
+ * are initiated after bootstrap.
+ */
+int
+connection_dir_close_consensus_conn_if_extra(dir_connection_t *conn)
+{
+ tor_assert(conn);
+ tor_assert(conn->base_.type == CONN_TYPE_DIR);
+
+ /* We're not interested in connections that aren't fetching a consensus. */
+ if (conn->base_.purpose != DIR_PURPOSE_FETCH_CONSENSUS) {
+ return 0;
+ }
+
+ /* The connection has already been closed */
+ if (conn->base_.marked_for_close) {
+ return 0;
+ }
+
+ /* Only close this connection if there's another consensus connection
+ * downloading (during bootstrap), or connecting (after bootstrap).
+ * Post-bootstrap consensus connection attempts won't be closed, because
+ * they only occur one at a time. */
+ if (!connection_dir_would_close_consensus_conn_helper()) {
+ return 0;
+ }
+
+ const int we_are_bootstrapping = networkstatus_consensus_is_bootstrapping(
+ time(NULL));
+
+ /* We don't want to check other connections to see if they are downloading,
+ * as this is prone to race-conditions. So leave it for
+ * connection_dir_close_extra_consensus_conns(() to clean up.
+ *
+ * But if conn has just started connecting, or we have a consensus already,
+ * we can be sure it's not needed any more. */
+ if (!we_are_bootstrapping
+ || conn->base_.state == DIR_CONN_STATE_CONNECTING) {
+ connection_close_immediate(&conn->base_);
+ connection_mark_for_close(&conn->base_);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Clean up excess consensus download connection attempts.
+ * During bootstrap, or when the bootstrap consensus has just been downloaded,
+ * if we have more than one active consensus connection:
+ * - if we don't have a consensus, and we're downloading a consensus, keep an
+ * earlier connection, or a connection to a fallback directory, and close
+ * all other connections;
+ * - if we have just downloaded the bootstrap consensus, and have other
+ * consensus connections left over, close all of them.
+ *
+ * Post-bootstrap consensus connection attempts are initiated one at a time.
+ * So this function won't close any consensus connection attempts that
+ * are initiated after bootstrap.
+ */
+void
+connection_dir_close_extra_consensus_conns(void)
+{
+ /* Only cleanup connections if there is more than one consensus connection,
+ * and at least one of those connections is already downloading
+ * (during bootstrap), or connecting (just after the bootstrap consensus is
+ * downloaded).
+ * Post-bootstrap consensus connection attempts won't be cleaned up, because
+ * they only occur one at a time. */
+ if (!connection_dir_would_close_consensus_conn_helper()) {
+ return;
+ }
+
+ int we_are_bootstrapping = networkstatus_consensus_is_bootstrapping(
+ time(NULL));
+
+ const char *usable_resource = networkstatus_get_flavor_name(
+ usable_consensus_flavor());
+ smartlist_t *consens_usable_conns =
+ connection_dir_list_by_purpose_and_resource(
+ DIR_PURPOSE_FETCH_CONSENSUS,
+ usable_resource);
+
+ /* If we want to keep a connection that's downloading, find a connection to
+ * keep, favouring:
+ * - connections opened earlier (they are likely to have progressed further)
+ * - connections to fallbacks (to reduce the load on authorities) */
+ dir_connection_t *kept_download_conn = NULL;
+ int kept_is_authority = 0;
+ if (we_are_bootstrapping) {
+ SMARTLIST_FOREACH_BEGIN(consens_usable_conns,
+ dir_connection_t *, d) {
+ tor_assert(d);
+ int d_is_authority = router_digest_is_trusted_dir(d->identity_digest);
+ /* keep the first connection that is past the connecting state, but
+ * prefer fallbacks. */
+ if (d->base_.state != DIR_CONN_STATE_CONNECTING) {
+ if (!kept_download_conn || (kept_is_authority && !d_is_authority)) {
+ kept_download_conn = d;
+ kept_is_authority = d_is_authority;
+ /* we've found the earliest fallback, and want to keep it regardless
+ * of any other connections */
+ if (!kept_is_authority)
+ break;
+ }
+ }
+ } SMARTLIST_FOREACH_END(d);
+ }
+
+ SMARTLIST_FOREACH_BEGIN(consens_usable_conns,
+ dir_connection_t *, d) {
+ tor_assert(d);
+ /* don't close this connection if it's the one we want to keep */
+ if (kept_download_conn && d == kept_download_conn)
+ continue;
+ /* mark all other connections for close */
+ if (!d->base_.marked_for_close) {
+ connection_close_immediate(&d->base_);
+ connection_mark_for_close(&d->base_);
+ }
+ } SMARTLIST_FOREACH_END(d);
+
+ smartlist_free(consens_usable_conns);
+ consens_usable_conns = NULL;
+
+ /* make sure we've closed all excess connections */
+ const int final_connecting_conn_count =
+ connection_dir_count_by_purpose_resource_and_state(
+ DIR_PURPOSE_FETCH_CONSENSUS,
+ usable_resource,
+ DIR_CONN_STATE_CONNECTING);
+ if (final_connecting_conn_count > 0) {
+ log_warn(LD_BUG, "Expected 0 consensus connections connecting after "
+ "cleanup, got %d.", final_connecting_conn_count);
+ }
+ const int expected_final_conn_count = (we_are_bootstrapping ? 1 : 0);
+ const int final_conn_count =
+ connection_dir_count_by_purpose_and_resource(
+ DIR_PURPOSE_FETCH_CONSENSUS,
+ usable_resource);
+ if (final_conn_count > expected_final_conn_count) {
+ log_warn(LD_BUG, "Expected %d consensus connections after cleanup, got "
+ "%d.", expected_final_conn_count, final_connecting_conn_count);
+ }
+}
+
/** Connected handler for directory connections: begin sending data to the
- * server */
+ * server, and return 0, or, if the connection is an excess bootstrap
+ * connection, close all excess bootstrap connections.
+ * Only used when connections don't immediately connect. */
int
connection_dir_finished_connecting(dir_connection_t *conn)
{
@@ -3451,31 +3910,65 @@ connection_dir_finished_connecting(dir_connection_t *conn)
log_debug(LD_HTTP,"Dir connection to router %s:%u established.",
conn->base_.address,conn->base_.port);
- conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING; /* start flushing conn */
+ /* Close this connection if there's another consensus connection
+ * downloading (during bootstrap), or connecting (after bootstrap). */
+ if (connection_dir_close_consensus_conn_if_extra(conn)) {
+ return -1;
+ }
+
+ /* start flushing conn */
+ conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING;
return 0;
}
/** Decide which download schedule we want to use based on descriptor type
- * in <b>dls</b> and whether we are acting as directory <b>server</b>, and
- * then return a list of int pointers defining download delays in seconds.
- * Helper function for download_status_increment_failure() and
- * download_status_reset(). */
-static const smartlist_t *
-find_dl_schedule_and_len(download_status_t *dls, int server)
+ * in <b>dls</b> and <b>options</b>.
+ * Then return a list of int pointers defining download delays in seconds.
+ * Helper function for download_status_increment_failure(),
+ * download_status_reset(), and download_status_increment_attempt(). */
+STATIC const smartlist_t *
+find_dl_schedule(download_status_t *dls, const or_options_t *options)
{
+ const int dir_server = dir_server_mode(options);
+ const int multi_d = networkstatus_consensus_can_use_multiple_directories(
+ options);
+ const int we_are_bootstrapping = networkstatus_consensus_is_bootstrapping(
+ time(NULL));
+ const int use_fallbacks = networkstatus_consensus_can_use_extra_fallbacks(
+ options);
switch (dls->schedule) {
case DL_SCHED_GENERIC:
- if (server)
- return get_options()->TestingServerDownloadSchedule;
- else
- return get_options()->TestingClientDownloadSchedule;
+ if (dir_server) {
+ return options->TestingServerDownloadSchedule;
+ } else {
+ return options->TestingClientDownloadSchedule;
+ }
case DL_SCHED_CONSENSUS:
- if (server)
- return get_options()->TestingServerConsensusDownloadSchedule;
- else
- return get_options()->TestingClientConsensusDownloadSchedule;
+ if (!multi_d) {
+ return options->TestingServerConsensusDownloadSchedule;
+ } else {
+ if (we_are_bootstrapping) {
+ if (!use_fallbacks) {
+ /* A bootstrapping client without extra fallback directories */
+ return
+ options->ClientBootstrapConsensusAuthorityOnlyDownloadSchedule;
+ } else if (dls->want_authority) {
+ /* A bootstrapping client with extra fallback directories, but
+ * connecting to an authority */
+ return
+ options->ClientBootstrapConsensusAuthorityDownloadSchedule;
+ } else {
+ /* A bootstrapping client connecting to extra fallback directories
+ */
+ return
+ options->ClientBootstrapConsensusFallbackDownloadSchedule;
+ }
+ } else {
+ return options->TestingClientConsensusDownloadSchedule;
+ }
+ }
case DL_SCHED_BRIDGE:
- return get_options()->TestingBridgeDownloadSchedule;
+ return options->TestingBridgeDownloadSchedule;
default:
tor_assert(0);
}
@@ -3484,54 +3977,168 @@ find_dl_schedule_and_len(download_status_t *dls, int server)
return NULL;
}
-/** Called when an attempt to download <b>dls</b> has failed with HTTP status
+/* Find the current delay for dls based on schedule.
+ * Set dls->next_attempt_at based on now, and return the delay.
+ * Helper for download_status_increment_failure and
+ * download_status_increment_attempt. */
+STATIC int
+download_status_schedule_get_delay(download_status_t *dls,
+ const smartlist_t *schedule,
+ time_t now)
+{
+ tor_assert(dls);
+ tor_assert(schedule);
+
+ int delay = INT_MAX;
+ uint8_t dls_schedule_position = (dls->increment_on
+ == DL_SCHED_INCREMENT_ATTEMPT
+ ? dls->n_download_attempts
+ : dls->n_download_failures);
+
+ if (dls_schedule_position < smartlist_len(schedule))
+ delay = *(int *)smartlist_get(schedule, dls_schedule_position);
+ else if (dls_schedule_position == IMPOSSIBLE_TO_DOWNLOAD)
+ delay = INT_MAX;
+ else
+ delay = *(int *)smartlist_get(schedule, smartlist_len(schedule) - 1);
+
+ /* A negative delay makes no sense. Knowing that delay is
+ * non-negative allows us to safely do the wrapping check below. */
+ tor_assert(delay >= 0);
+
+ /* Avoid now+delay overflowing INT_MAX, by comparing with a subtraction
+ * that won't overflow (since delay is non-negative). */
+ if (delay < INT_MAX && now <= INT_MAX - delay) {
+ dls->next_attempt_at = now+delay;
+ } else {
+ dls->next_attempt_at = TIME_MAX;
+ }
+
+ return delay;
+}
+
+/* Log a debug message about item, which increments on increment_action, has
+ * incremented dls_n_download_increments times. The message varies based on
+ * was_schedule_incremented (if not, not_incremented_response is logged), and
+ * the values of increment, dls_next_attempt_at, and now.
+ * Helper for download_status_increment_failure and
+ * download_status_increment_attempt. */
+static void
+download_status_log_helper(const char *item, int was_schedule_incremented,
+ const char *increment_action,
+ const char *not_incremented_response,
+ uint8_t dls_n_download_increments, int increment,
+ time_t dls_next_attempt_at, time_t now)
+{
+ if (item) {
+ if (!was_schedule_incremented)
+ log_debug(LD_DIR, "%s %s %d time(s); I'll try again %s.",
+ item, increment_action, (int)dls_n_download_increments,
+ not_incremented_response);
+ else if (increment == 0)
+ log_debug(LD_DIR, "%s %s %d time(s); I'll try again immediately.",
+ item, increment_action, (int)dls_n_download_increments);
+ else if (dls_next_attempt_at < TIME_MAX)
+ log_debug(LD_DIR, "%s %s %d time(s); I'll try again in %d seconds.",
+ item, increment_action, (int)dls_n_download_increments,
+ (int)(dls_next_attempt_at-now));
+ else
+ log_debug(LD_DIR, "%s %s %d time(s); Giving up for a while.",
+ item, increment_action, (int)dls_n_download_increments);
+ }
+}
+
+/** Determine when a failed download attempt should be retried.
+ * Called when an attempt to download <b>dls</b> has failed with HTTP status
* <b>status_code</b>. Increment the failure count (if the code indicates a
- * real failure) and set <b>dls</b>-\>next_attempt_at to an appropriate time
- * in the future. */
+ * real failure, or if we're a server) and set <b>dls</b>-\>next_attempt_at to
+ * an appropriate time in the future and return it.
+ * If <b>dls->increment_on</b> is DL_SCHED_INCREMENT_ATTEMPT, increment the
+ * failure count, and return a time in the far future for the next attempt (to
+ * avoid an immediate retry). */
time_t
download_status_increment_failure(download_status_t *dls, int status_code,
const char *item, int server, time_t now)
{
- const smartlist_t *schedule;
- int increment;
+ int increment = -1;
tor_assert(dls);
+
+ /* only count the failure if it's permanent, or we're a server */
if (status_code != 503 || server) {
if (dls->n_download_failures < IMPOSSIBLE_TO_DOWNLOAD-1)
++dls->n_download_failures;
}
- schedule = find_dl_schedule_and_len(dls, server);
+ if (dls->increment_on == DL_SCHED_INCREMENT_FAILURE) {
+ /* We don't find out that a failure-based schedule has attempted a
+ * connection until that connection fails.
+ * We'll never find out about successful connections, but this doesn't
+ * matter, because schedules are reset after a successful download.
+ */
+ if (dls->n_download_attempts < IMPOSSIBLE_TO_DOWNLOAD-1)
+ ++dls->n_download_attempts;
- if (dls->n_download_failures < smartlist_len(schedule))
- increment = *(int *)smartlist_get(schedule, dls->n_download_failures);
- else if (dls->n_download_failures == IMPOSSIBLE_TO_DOWNLOAD)
- increment = INT_MAX;
- else
- increment = *(int *)smartlist_get(schedule, smartlist_len(schedule) - 1);
+ /* only return a failure retry time if this schedule increments on failures
+ */
+ const smartlist_t *schedule = find_dl_schedule(dls, get_options());
+ increment = download_status_schedule_get_delay(dls, schedule, now);
+ }
- if (increment < INT_MAX)
- dls->next_attempt_at = now+increment;
- else
- dls->next_attempt_at = TIME_MAX;
+ download_status_log_helper(item, !dls->increment_on, "failed",
+ "concurrently", dls->n_download_failures,
+ increment, dls->next_attempt_at, now);
- if (item) {
- if (increment == 0)
- log_debug(LD_DIR, "%s failed %d time(s); I'll try again immediately.",
- item, (int)dls->n_download_failures);
- else if (dls->next_attempt_at < TIME_MAX)
- log_debug(LD_DIR, "%s failed %d time(s); I'll try again in %d seconds.",
- item, (int)dls->n_download_failures,
- (int)(dls->next_attempt_at-now));
- else
- log_debug(LD_DIR, "%s failed %d time(s); Giving up for a while.",
- item, (int)dls->n_download_failures);
+ if (dls->increment_on == DL_SCHED_INCREMENT_ATTEMPT) {
+ /* stop this schedule retrying on failure, it will launch concurrent
+ * connections instead */
+ return TIME_MAX;
+ } else {
+ return dls->next_attempt_at;
+ }
+}
+
+/** Determine when the next download attempt should be made when using an
+ * attempt-based (potentially concurrent) download schedule.
+ * Called when an attempt to download <b>dls</b> is being initiated.
+ * Increment the attempt count and set <b>dls</b>-\>next_attempt_at to an
+ * appropriate time in the future and return it.
+ * If <b>dls->increment_on</b> is DL_SCHED_INCREMENT_FAILURE, don't increment
+ * the attempts, and return a time in the far future (to avoid launching a
+ * concurrent attempt). */
+time_t
+download_status_increment_attempt(download_status_t *dls, const char *item,
+ time_t now)
+{
+ int delay = -1;
+ tor_assert(dls);
+
+ if (dls->increment_on == DL_SCHED_INCREMENT_FAILURE) {
+ /* this schedule should retry on failure, and not launch any concurrent
+ attempts */
+ log_info(LD_BUG, "Tried to launch an attempt-based connection on a "
+ "failure-based schedule.");
+ return TIME_MAX;
}
+
+ if (dls->n_download_attempts < IMPOSSIBLE_TO_DOWNLOAD-1)
+ ++dls->n_download_attempts;
+
+ const smartlist_t *schedule = find_dl_schedule(dls, get_options());
+ delay = download_status_schedule_get_delay(dls, schedule, now);
+
+ download_status_log_helper(item, dls->increment_on, "attempted",
+ "on failure", dls->n_download_attempts,
+ delay, dls->next_attempt_at, now);
+
return dls->next_attempt_at;
}
/** Reset <b>dls</b> so that it will be considered downloadable
* immediately, and/or to show that we don't need it anymore.
*
+ * Must be called to initialise a download schedule, otherwise the zeroth item
+ * in the schedule will never be used.
+ *
* (We find the zeroth element of the download schedule, and set
* next_attempt_at to be the appropriate offset from 'now'. In most
* cases this means setting it to 'now', so the item will be immediately
@@ -3540,14 +4147,16 @@ download_status_increment_failure(download_status_t *dls, int status_code,
void
download_status_reset(download_status_t *dls)
{
- if (dls->n_download_failures == IMPOSSIBLE_TO_DOWNLOAD)
+ if (dls->n_download_failures == IMPOSSIBLE_TO_DOWNLOAD
+ || dls->n_download_attempts == IMPOSSIBLE_TO_DOWNLOAD)
return; /* Don't reset this. */
- const smartlist_t *schedule = find_dl_schedule_and_len(
- dls, get_options()->DirPort_set);
+ const smartlist_t *schedule = find_dl_schedule(dls, get_options());
dls->n_download_failures = 0;
+ dls->n_download_attempts = 0;
dls->next_attempt_at = time(NULL) + *(int *)smartlist_get(schedule, 0);
+ /* Don't reset dls->want_authority or dls->increment_on */
}
/** Return the number of failures on <b>dls</b> since the last success (if
@@ -3558,6 +4167,22 @@ download_status_get_n_failures(const download_status_t *dls)
return dls->n_download_failures;
}
+/** Return the number of attempts to download <b>dls</b> since the last success
+ * (if any). This can differ from download_status_get_n_failures() due to
+ * outstanding concurrent attempts. */
+int
+download_status_get_n_attempts(const download_status_t *dls)
+{
+ return dls->n_download_attempts;
+}
+
+/** Return the next time to attempt to download <b>dls</b>. */
+time_t
+download_status_get_next_attempt_at(const download_status_t *dls)
+{
+ return dls->next_attempt_at;
+}
+
/** Called when one or more routerdesc (or extrainfo, if <b>was_extrainfo</b>)
* fetches have failed (with uppercase fingerprints listed in <b>failed</b>,
* either as descriptor digests or as identity digests based on
diff --git a/src/or/directory.h b/src/or/directory.h
index 4899eb5c8c..c4edbb5c0f 100644
--- a/src/or/directory.h
+++ b/src/or/directory.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -16,18 +16,20 @@ int directories_have_accepted_server_descriptor(void);
void directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
dirinfo_type_t type, const char *payload,
size_t payload_len, size_t extrainfo_len);
-MOCK_DECL(void, directory_get_from_dirserver, (uint8_t dir_purpose,
- uint8_t router_purpose,
- const char *resource,
- int pds_flags));
+MOCK_DECL(void, directory_get_from_dirserver, (
+ uint8_t dir_purpose,
+ uint8_t router_purpose,
+ const char *resource,
+ int pds_flags,
+ download_want_authority_t want_authority));
void directory_get_from_all_authorities(uint8_t dir_purpose,
uint8_t router_purpose,
const char *resource);
/** Enumeration of ways to connect to a directory server */
typedef enum {
- /** Default: connect over a one-hop Tor circuit but fall back to direct
- * connection */
+ /** Default: connect over a one-hop Tor circuit. Relays fall back to direct
+ * DirPort connections, clients, onion services, and bridges do not */
DIRIND_ONEHOP=0,
/** Connect over a multi-hop anonymizing Tor circuit */
DIRIND_ANONYMOUS=1,
@@ -37,14 +39,18 @@ typedef enum {
DIRIND_ANON_DIRPORT,
} dir_indirection_t;
-void directory_initiate_command_routerstatus(const routerstatus_t *status,
- uint8_t dir_purpose,
- uint8_t router_purpose,
- dir_indirection_t indirection,
- const char *resource,
- const char *payload,
- size_t payload_len,
- time_t if_modified_since);
+int directory_must_use_begindir(const or_options_t *options);
+
+MOCK_DECL(void, directory_initiate_command_routerstatus,
+ (const routerstatus_t *status,
+ uint8_t dir_purpose,
+ uint8_t router_purpose,
+ dir_indirection_t indirection,
+ const char *resource,
+ const char *payload,
+ size_t payload_len,
+ time_t if_modified_since));
+
void directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
uint8_t dir_purpose,
uint8_t router_purpose,
@@ -64,14 +70,17 @@ int connection_dir_process_inbuf(dir_connection_t *conn);
int connection_dir_finished_flushing(dir_connection_t *conn);
int connection_dir_finished_connecting(dir_connection_t *conn);
void connection_dir_about_to_close(dir_connection_t *dir_conn);
-void directory_initiate_command(const tor_addr_t *addr,
- uint16_t or_port, uint16_t dir_port,
+void directory_initiate_command(const tor_addr_t *or_addr, uint16_t or_port,
+ const tor_addr_t *dir_addr, uint16_t dir_port,
const char *digest,
uint8_t dir_purpose, uint8_t router_purpose,
dir_indirection_t indirection,
const char *resource,
const char *payload, size_t payload_len,
time_t if_modified_since);
+int connection_dir_avoid_extra_connection_for_purpose(unsigned int purpose);
+int connection_dir_close_consensus_conn_if_extra(dir_connection_t *conn);
+void connection_dir_close_extra_consensus_conns(void);
#define DSR_HEX (1<<0)
#define DSR_BASE64 (1<<1)
@@ -90,34 +99,41 @@ int router_supports_extrainfo(const char *identity_digest, int is_authority);
time_t download_status_increment_failure(download_status_t *dls,
int status_code, const char *item,
int server, time_t now);
+time_t download_status_increment_attempt(download_status_t *dls,
+ const char *item, time_t now);
/** Increment the failure count of the download_status_t <b>dls</b>, with
* the optional status code <b>sc</b>. */
#define download_status_failed(dls, sc) \
download_status_increment_failure((dls), (sc), NULL, \
- get_options()->DirPort_set, time(NULL))
+ dir_server_mode(get_options()), \
+ time(NULL))
void download_status_reset(download_status_t *dls);
static int download_status_is_ready(download_status_t *dls, time_t now,
int max_failures);
/** Return true iff, as of <b>now</b>, the resource tracked by <b>dls</b> is
* ready to get its download reattempted. */
-static INLINE int
+static inline int
download_status_is_ready(download_status_t *dls, time_t now,
int max_failures)
{
- return (dls->n_download_failures <= max_failures
- && dls->next_attempt_at <= now);
+ int under_failure_limit = (dls->n_download_failures <= max_failures
+ && dls->n_download_attempts <= max_failures);
+ return (under_failure_limit && dls->next_attempt_at <= now);
}
static void download_status_mark_impossible(download_status_t *dl);
/** Mark <b>dl</b> as never downloadable. */
-static INLINE void
+static inline void
download_status_mark_impossible(download_status_t *dl)
{
dl->n_download_failures = IMPOSSIBLE_TO_DOWNLOAD;
+ dl->n_download_attempts = IMPOSSIBLE_TO_DOWNLOAD;
}
int download_status_get_n_failures(const download_status_t *dls);
+int download_status_get_n_attempts(const download_status_t *dls);
+time_t download_status_get_next_attempt_at(const download_status_t *dls);
#ifdef TOR_UNIT_TESTS
/* Used only by directory.c and test_dir.c */
@@ -127,6 +143,21 @@ STATIC int purpose_needs_anonymity(uint8_t dir_purpose,
uint8_t router_purpose);
STATIC dirinfo_type_t dir_fetch_type(int dir_purpose, int router_purpose,
const char *resource);
+STATIC int directory_handle_command_get(dir_connection_t *conn,
+ const char *headers,
+ const char *req_body,
+ size_t req_body_len);
+STATIC int connection_dir_would_close_consensus_conn_helper(void);
+STATIC int download_status_schedule_get_delay(download_status_t *dls,
+ const smartlist_t *schedule,
+ time_t now);
+
+STATIC char* authdir_type_to_string(dirinfo_type_t auth);
+STATIC const char * dir_conn_purpose_to_string(int purpose);
+STATIC int should_use_directory_guards(const or_options_t *options);
+STATIC zlib_compression_level_t choose_compression_level(ssize_t n_bytes);
+STATIC const smartlist_t *find_dl_schedule(download_status_t *dls,
+ const or_options_t *options);
#endif
#endif
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index 58ab009cbf..f012b7bf64 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#define DIRSERV_PRIVATE
@@ -248,6 +248,7 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg,
int severity)
{
char d[DIGEST_LEN];
+ const int key_pinning = get_options()->AuthDirPinKeys;
if (crypto_pk_get_digest(router->identity_pkey, d)) {
log_warn(LD_BUG,"Error computing fingerprint");
@@ -261,14 +262,16 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg,
if (KEYPIN_MISMATCH ==
keypin_check((const uint8_t*)router->cache_info.identity_digest,
router->signing_key_cert->signing_key.pubkey)) {
- if (msg) {
- *msg = "Ed25519 identity key or RSA identity key has changed.";
- }
log_fn(severity, LD_DIR,
"Descriptor from router %s has an Ed25519 key, "
"but the <rsa,ed25519> keys don't match what they were before.",
router_describe(router));
- return FP_REJECT;
+ if (key_pinning) {
+ if (msg) {
+ *msg = "Ed25519 identity key or RSA identity key has changed.";
+ }
+ return FP_REJECT;
+ }
}
} else {
/* No ed25519 key */
@@ -277,13 +280,15 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg,
log_fn(severity, LD_DIR,
"Descriptor from router %s has no Ed25519 key, "
"when we previously knew an Ed25519 for it. Ignoring for now, "
- "since Tor 0.2.6 is under development.",
+ "since Ed25519 keys are fairly new.",
router_describe(router));
#ifdef DISABLE_DISABLING_ED25519
- if (msg) {
- *msg = "Ed25519 identity key has disappeared.";
+ if (key_pinning) {
+ if (msg) {
+ *msg = "Ed25519 identity key has disappeared.";
+ }
+ return FP_REJECT;
}
- return FP_REJECT;
#endif
}
}
@@ -582,6 +587,7 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
char *desc, *nickname;
const size_t desclen = ri->cache_info.signed_descriptor_len +
ri->cache_info.annotations_len;
+ const int key_pinning = get_options()->AuthDirPinKeys;
*msg = NULL;
/* If it's too big, refuse it now. Otherwise we'll cache it all over the
@@ -626,7 +632,8 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
if (ri->signing_key_cert) {
keypin_status = keypin_check_and_add(
(const uint8_t*)ri->cache_info.identity_digest,
- ri->signing_key_cert->signing_key.pubkey);
+ ri->signing_key_cert->signing_key.pubkey,
+ ! key_pinning);
} else {
keypin_status = keypin_check_lone_rsa(
(const uint8_t*)ri->cache_info.identity_digest);
@@ -635,7 +642,7 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
keypin_status = KEYPIN_NOT_FOUND;
#endif
}
- if (keypin_status == KEYPIN_MISMATCH) {
+ if (keypin_status == KEYPIN_MISMATCH && key_pinning) {
log_info(LD_DIRSERV, "Dropping descriptor from %s (source: %s) because "
"its key did not match an older RSA/Ed25519 keypair",
router_describe(ri), source);
@@ -790,7 +797,7 @@ list_single_server_status(const routerinfo_t *desc, int is_live)
}
/* DOCDOC running_long_enough_to_decide_unreachable */
-static INLINE int
+static inline int
running_long_enough_to_decide_unreachable(void)
{
return time_of_process_start
@@ -1084,13 +1091,13 @@ 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_set && !refuseunknown)
+ if (!dir_server_mode(options) && !refuseunknown)
return 0;
if (!server_mode(options) || !advertised_server_mode())
return 0;
me = router_get_my_routerinfo();
- if (!me || (!me->dir_port && !refuseunknown))
- return 0; /* if dirport not advertised, return 0 too */
+ if (!me || (!me->supports_tunnelled_dir_requests && !refuseunknown))
+ return 0; /* if we don't service directory requests, return 0 too */
return 1;
}
@@ -1121,7 +1128,7 @@ directory_fetches_dir_info_later(const or_options_t *options)
int
directory_caches_unknown_auth_certs(const or_options_t *options)
{
- return options->DirPort_set || options->BridgeRelay;
+ return dir_server_mode(options) || options->BridgeRelay;
}
/** Return 1 if we want to keep descriptors, networkstatuses, etc around
@@ -1130,7 +1137,7 @@ directory_caches_unknown_auth_certs(const or_options_t *options)
int
directory_caches_dir_info(const or_options_t *options)
{
- if (options->BridgeRelay || options->DirPort_set)
+ if (options->BridgeRelay || dir_server_mode(options))
return 1;
if (!server_mode(options) || !advertised_server_mode())
return 0;
@@ -1146,7 +1153,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_set;
+ return options->BridgeRelay != 0 || dir_server_mode(options);
}
/** Return 1 if we have no need to fetch new descriptors. This generally
@@ -1223,7 +1230,7 @@ free_cached_dir_(void *_d)
void
dirserv_set_cached_consensus_networkstatus(const char *networkstatus,
const char *flavor_name,
- const digests_t *digests,
+ const common_digests_t *digests,
time_t published)
{
cached_dir_t *new_networkstatus;
@@ -1232,7 +1239,7 @@ dirserv_set_cached_consensus_networkstatus(const char *networkstatus,
cached_consensuses = strmap_new();
new_networkstatus = new_cached_dir(tor_strdup(networkstatus), published);
- memcpy(&new_networkstatus->digests, digests, sizeof(digests_t));
+ memcpy(&new_networkstatus->digests, digests, sizeof(common_digests_t));
old_networkstatus = strmap_set(cached_consensuses, flavor_name,
new_networkstatus);
if (old_networkstatus)
@@ -1295,7 +1302,7 @@ static uint32_t guard_bandwidth_excluding_exits_kb = 0;
/** Helper: estimate the uptime of a router given its stated uptime and the
* amount of time since it last stated its stated uptime. */
-static INLINE long
+static inline long
real_uptime(const routerinfo_t *router, time_t now)
{
if (now < router->cache_info.published_on)
@@ -1343,8 +1350,9 @@ dirserv_thinks_router_is_unreliable(time_t now,
}
/** Return true iff <b>router</b> should be assigned the "HSDir" flag.
+ *
* Right now this means it advertises support for it, it has a high uptime,
- * it has a DirPort open, it has the Stable flag and it's currently
+ * it's a directory cache, it has the Stable and Fast flags, and it's currently
* considered Running.
*
* This function needs to be called after router-\>is_running has
@@ -1371,8 +1379,9 @@ dirserv_thinks_router_is_hs_dir(const routerinfo_t *router,
else
uptime = real_uptime(router, now);
- return (router->wants_to_be_hs_dir && router->dir_port &&
- node->is_stable &&
+ return (router->wants_to_be_hs_dir &&
+ router->supports_tunnelled_dir_requests &&
+ node->is_stable && node->is_fast &&
uptime >= get_options()->MinUptimeHidServDirectoryV2 &&
router_is_active(router, node, now));
}
@@ -1415,7 +1424,7 @@ router_counts_toward_thresholds(const node_t *node, time_t now,
*
* Also, set the is_exit flag of each router appropriately. */
static void
-dirserv_compute_performance_thresholds(routerlist_t *rl,
+dirserv_compute_performance_thresholds(const smartlist_t *routers,
digestmap_t *omit_as_sybil)
{
int n_active, n_active_nonexit, n_familiar;
@@ -1443,18 +1452,18 @@ dirserv_compute_performance_thresholds(routerlist_t *rl,
* sort them and use that to compute thresholds. */
n_active = n_active_nonexit = 0;
/* Uptime for every active router. */
- uptimes = tor_calloc(smartlist_len(rl->routers), sizeof(uint32_t));
+ uptimes = tor_calloc(smartlist_len(routers), sizeof(uint32_t));
/* Bandwidth for every active router. */
- bandwidths_kb = tor_calloc(smartlist_len(rl->routers), sizeof(uint32_t));
+ bandwidths_kb = tor_calloc(smartlist_len(routers), sizeof(uint32_t));
/* Bandwidth for every active non-exit router. */
bandwidths_excluding_exits_kb =
- tor_calloc(smartlist_len(rl->routers), sizeof(uint32_t));
+ tor_calloc(smartlist_len(routers), sizeof(uint32_t));
/* Weighted mean time between failure for each active router. */
- mtbfs = tor_calloc(smartlist_len(rl->routers), sizeof(double));
+ mtbfs = tor_calloc(smartlist_len(routers), sizeof(double));
/* Time-known for each active router. */
- tks = tor_calloc(smartlist_len(rl->routers), sizeof(long));
+ tks = tor_calloc(smartlist_len(routers), sizeof(long));
/* Weighted fractional uptime for each active router. */
- wfus = tor_calloc(smartlist_len(rl->routers), sizeof(double));
+ wfus = tor_calloc(smartlist_len(routers), sizeof(double));
nodelist_assert_ok();
@@ -1589,11 +1598,11 @@ dirserv_compute_performance_thresholds(routerlist_t *rl,
* networkstatus_getinfo_by_purpose().
*/
void
-dirserv_compute_bridge_flag_thresholds(routerlist_t *rl)
+dirserv_compute_bridge_flag_thresholds(const smartlist_t *routers)
{
digestmap_t *omit_as_sybil = digestmap_new();
- dirserv_compute_performance_thresholds(rl, omit_as_sybil);
+ dirserv_compute_performance_thresholds(routers, omit_as_sybil);
digestmap_free(omit_as_sybil, NULL);
}
@@ -1746,16 +1755,13 @@ dirserv_get_bandwidth_for_router_kb(const routerinfo_t *ri)
* how many measured bandwidths we know. This is used to decide whether we
* ever trust advertised bandwidths for purposes of assigning flags. */
static void
-dirserv_count_measured_bws(routerlist_t *rl)
+dirserv_count_measured_bws(const smartlist_t *routers)
{
/* Initialize this first */
routers_with_measured_bw = 0;
- tor_assert(rl);
- tor_assert(rl->routers);
-
/* Iterate over the routerlist and count measured bandwidths */
- SMARTLIST_FOREACH_BEGIN(rl->routers, routerinfo_t *, ri) {
+ SMARTLIST_FOREACH_BEGIN(routers, const routerinfo_t *, ri) {
/* Check if we know a measured bandwidth for this one */
if (dirserv_has_measured_bw(ri->cache_info.identity_digest)) {
++routers_with_measured_bw;
@@ -1914,7 +1920,7 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version,
rs->is_hs_dir?" HSDir":"",
rs->is_flagged_running?" Running":"",
rs->is_stable?" Stable":"",
- (rs->dir_port!=0)?" V2Dir":"",
+ rs->is_v2_dir?" V2Dir":"",
rs->is_valid?" Valid":"");
/* length of "opt v \n" */
@@ -2119,6 +2125,50 @@ get_possible_sybil_list(const smartlist_t *routers)
return omit_as_sybil;
}
+/** If there are entries in <b>routers</b> with exactly the same ed25519 keys,
+ * remove the older one. If they are exactly the same age, remove the one
+ * with the greater descriptor digest. May alter the order of the list. */
+static void
+routers_make_ed_keys_unique(smartlist_t *routers)
+{
+ routerinfo_t *ri2;
+ digest256map_t *by_ed_key = digest256map_new();
+
+ SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) {
+ ri->omit_from_vote = 0;
+ if (ri->signing_key_cert == NULL)
+ continue; /* No ed key */
+ const uint8_t *pk = ri->signing_key_cert->signing_key.pubkey;
+ if ((ri2 = digest256map_get(by_ed_key, pk))) {
+ /* Duplicate; must omit one. Set the omit_from_vote flag in whichever
+ * one has the earlier published_on. */
+ const time_t ri_pub = ri->cache_info.published_on;
+ const time_t ri2_pub = ri2->cache_info.published_on;
+ if (ri2_pub < ri_pub ||
+ (ri2_pub == ri_pub &&
+ memcmp(ri->cache_info.signed_descriptor_digest,
+ ri2->cache_info.signed_descriptor_digest,DIGEST_LEN)<0)) {
+ digest256map_set(by_ed_key, pk, ri);
+ ri2->omit_from_vote = 1;
+ } else {
+ ri->omit_from_vote = 1;
+ }
+ } else {
+ /* Add to map */
+ digest256map_set(by_ed_key, pk, ri);
+ }
+ } SMARTLIST_FOREACH_END(ri);
+
+ digest256map_free(by_ed_key, NULL);
+
+ /* Now remove every router where the omit_from_vote flag got set. */
+ SMARTLIST_FOREACH_BEGIN(routers, const routerinfo_t *, ri) {
+ if (ri->omit_from_vote) {
+ SMARTLIST_DEL_CURRENT(routers, ri);
+ }
+ } SMARTLIST_FOREACH_END(ri);
+}
+
/** Extract status information from <b>ri</b> and from other authority
* functions and store it in <b>rs</b>>.
*
@@ -2150,7 +2200,7 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs,
rs->is_valid = node->is_valid;
- if (node->is_fast &&
+ if (node->is_fast && node->is_stable &&
((options->AuthDirGuardBWGuarantee &&
routerbw_kb >= options->AuthDirGuardBWGuarantee/1000) ||
routerbw_kb >= MIN(guard_bandwidth_including_exits_kb,
@@ -2178,6 +2228,7 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs,
strlcpy(rs->nickname, ri->nickname, sizeof(rs->nickname));
rs->or_port = ri->or_port;
rs->dir_port = ri->dir_port;
+ rs->is_v2_dir = ri->supports_tunnelled_dir_requests;
if (options->AuthDirHasIPv6Connectivity == 1 &&
!tor_addr_is_null(&ri->ipv6_addr) &&
node->last_reachable6 >= now - REACHABLE_TIMEOUT) {
@@ -2808,6 +2859,8 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
routers = smartlist_new();
smartlist_add_all(routers, rl->routers);
+ routers_make_ed_keys_unique(routers);
+ /* After this point, don't use rl->routers; use 'routers' instead. */
routers_sort_by_identity(routers);
omit_as_sybil = get_possible_sybil_list(routers);
@@ -2818,9 +2871,9 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
/* Count how many have measured bandwidths so we know how to assign flags;
* this must come before dirserv_compute_performance_thresholds() */
- dirserv_count_measured_bws(rl);
+ dirserv_count_measured_bws(routers);
- dirserv_compute_performance_thresholds(rl, omit_as_sybil);
+ dirserv_compute_performance_thresholds(routers, omit_as_sybil);
routerstatuses = smartlist_new();
microdescriptors = smartlist_new();
@@ -3093,7 +3146,7 @@ dirserv_get_routerdescs(smartlist_t *descs_out, const char *key,
DSR_HEX|DSR_SORT_UNIQ);
SMARTLIST_FOREACH_BEGIN(digests, const char *, d) {
if (router_digest_is_me(d)) {
- /* make sure desc_routerinfo exists */
+ /* calling router_get_my_routerinfo() to make sure it exists */
const routerinfo_t *ri = router_get_my_routerinfo();
if (ri)
smartlist_add(descs_out, (void*) &(ri->cache_info));
@@ -3125,7 +3178,7 @@ dirserv_get_routerdescs(smartlist_t *descs_out, const char *key,
* router listening at <b>address</b>:<b>or_port</b>, and has yielded
* a certificate with digest <b>digest_rcvd</b>.
*
- * Inform the reachability checker that we could get to this guy.
+ * Inform the reachability checker that we could get to this relay.
*/
void
dirserv_orconn_tls_done(const tor_addr_t *addr,
@@ -3745,7 +3798,9 @@ validate_recommended_package_line(const char *line)
cp = end_of_word + 1;
}
- return (n_entries == 0) ? 0 : 1;
+ /* If we reach this point, we have at least 1 entry. */
+ tor_assert(n_entries > 0);
+ return 1;
}
/** Release all storage used by the directory server. */
diff --git a/src/or/dirserv.h b/src/or/dirserv.h
index d07339bc12..3e735db071 100644
--- a/src/or/dirserv.h
+++ b/src/or/dirserv.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -50,7 +50,7 @@ int list_server_status_v1(smartlist_t *routers, char **router_status_out,
int dirserv_dump_directory_to_string(char **dir_out,
crypto_pk_t *private_key);
char *dirserv_get_flag_thresholds_line(void);
-void dirserv_compute_bridge_flag_thresholds(routerlist_t *rl);
+void dirserv_compute_bridge_flag_thresholds(const smartlist_t *routers);
int directory_fetches_from_authorities(const or_options_t *options);
int directory_fetches_dir_info_early(const or_options_t *options);
@@ -63,9 +63,9 @@ int directory_too_idle_to_fetch_descriptors(const or_options_t *options,
cached_dir_t *dirserv_get_consensus(const char *flavor_name);
void dirserv_set_cached_consensus_networkstatus(const char *consensus,
- const char *flavor_name,
- const digests_t *digests,
- time_t published);
+ const char *flavor_name,
+ const common_digests_t *digests,
+ time_t published);
void dirserv_clear_old_networkstatuses(time_t cutoff);
int dirserv_get_routerdesc_fingerprints(smartlist_t *fps_out, const char *key,
const char **msg,
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index d8e6ee2229..9854af7d7f 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#define DIRVOTE_PRIVATE
@@ -54,7 +54,6 @@ static int dirvote_perform_vote(void);
static void dirvote_clear_votes(int all_votes);
static int dirvote_compute_consensuses(void);
static int dirvote_publish_consensus(void);
-static char *make_consensus_method_list(int low, int high, const char *sep);
/* =====
* Voting
@@ -558,13 +557,20 @@ compute_consensus_method(smartlist_t *votes)
static int
consensus_method_is_supported(int method)
{
+ if (method == MIN_METHOD_FOR_ED25519_ID_IN_MD) {
+ /* This method was broken due to buggy code accidently left in
+ * dircollate.c; do not actually use it.
+ */
+ return 0;
+ }
+
return (method >= MIN_SUPPORTED_CONSENSUS_METHOD) &&
(method <= MAX_SUPPORTED_CONSENSUS_METHOD);
}
/** Return a newly allocated string holding the numbers between low and high
* (inclusive) that are supported consensus methods. */
-static char *
+STATIC char *
make_consensus_method_list(int low, int high, const char *separator)
{
char *list;
@@ -1235,6 +1241,9 @@ networkstatus_compute_consensus(smartlist_t *votes,
smartlist_free(combined_server_versions);
smartlist_free(combined_client_versions);
+ if (consensus_method >= MIN_METHOD_FOR_ED25519_ID_VOTING)
+ smartlist_add(flags, tor_strdup("NoEdConsensus"));
+
smartlist_sort_strings(flags);
smartlist_uniq_strings(flags);
@@ -1532,6 +1541,8 @@ networkstatus_compute_consensus(smartlist_t *votes,
num_bandwidths = 0;
num_mbws = 0;
num_guardfraction_inputs = 0;
+ int ed_consensus = 0;
+ const uint8_t *ed_consensus_val = NULL;
/* Okay, go through all the entries for this digest. */
for (int voter_idx = 0; voter_idx < smartlist_len(votes); ++voter_idx) {
@@ -1573,6 +1584,17 @@ networkstatus_compute_consensus(smartlist_t *votes,
if (rs->status.has_bandwidth)
bandwidths_kb[num_bandwidths++] = rs->status.bandwidth_kb;
+
+ /* Count number for which ed25519 is canonical. */
+ if (rs->ed25519_reflects_consensus) {
+ ++ed_consensus;
+ if (ed_consensus_val) {
+ tor_assert(fast_memeq(ed_consensus_val, rs->ed25519_id,
+ ED25519_PUBKEY_LEN));
+ } else {
+ ed_consensus_val = rs->ed25519_id;
+ }
+ }
}
/* We don't include this router at all unless more than half of
@@ -1580,6 +1602,14 @@ networkstatus_compute_consensus(smartlist_t *votes,
if (n_listing <= total_authorities/2)
continue;
+ if (ed_consensus > 0) {
+ tor_assert(consensus_method >= MIN_METHOD_FOR_ED25519_ID_VOTING);
+ if (ed_consensus <= total_authorities / 2) {
+ log_warn(LD_BUG, "Not enough entries had ed_consensus set; how "
+ "can we have a consensus of %d?", ed_consensus);
+ }
+ }
+
/* The clangalyzer can't figure out that this will never be NULL
* if n_listing is at least 1 */
tor_assert(current_rsa_id);
@@ -1633,6 +1663,10 @@ networkstatus_compute_consensus(smartlist_t *votes,
} else if (!strcmp(fl, "Unnamed")) {
if (is_unnamed)
smartlist_add(chosen_flags, (char*)fl);
+ } else if (!strcmp(fl, "NoEdConsensus") &&
+ consensus_method >= MIN_METHOD_FOR_ED25519_ID_VOTING) {
+ if (ed_consensus <= total_authorities/2)
+ smartlist_add(chosen_flags, (char*)fl);
} else {
if (flag_counts[fl_sl_idx] > n_flag_voters[fl_sl_idx]/2) {
smartlist_add(chosen_flags, (char*)fl);
@@ -2115,14 +2149,14 @@ networkstatus_add_detached_signatures(networkstatus_t *target,
/** Make sure all the digests we know match, and at least one matches. */
{
- digests_t *digests = strmap_get(sigs->digests, flavor);
+ common_digests_t *digests = strmap_get(sigs->digests, flavor);
int n_matches = 0;
int alg;
if (!digests) {
*msg_out = "No digests for given consensus flavor";
return -1;
}
- for (alg = DIGEST_SHA1; alg < N_DIGEST_ALGORITHMS; ++alg) {
+ for (alg = DIGEST_SHA1; alg < N_COMMON_DIGEST_ALGORITHMS; ++alg) {
if (!tor_mem_is_zero(digests->d[alg], DIGEST256_LEN)) {
if (fast_memeq(target->digests.d[alg], digests->d[alg],
DIGEST256_LEN)) {
@@ -2315,7 +2349,7 @@ networkstatus_get_detached_signatures(smartlist_t *consensuses)
/* start with SHA256; we don't include SHA1 for anything but the basic
* consensus. */
- for (alg = DIGEST_SHA256; alg < N_DIGEST_ALGORITHMS; ++alg) {
+ for (alg = DIGEST_SHA256; alg < N_COMMON_DIGEST_ALGORITHMS; ++alg) {
char d[HEX_DIGEST256_LEN+1];
const char *alg_name =
crypto_digest_algorithm_get_name(alg);
@@ -3373,8 +3407,8 @@ dirvote_free_all(void)
* ==== */
/** Return the body of the consensus that we're currently trying to build. */
-const char *
-dirvote_get_pending_consensus(consensus_flavor_t flav)
+MOCK_IMPL(const char *,
+dirvote_get_pending_consensus, (consensus_flavor_t flav))
{
tor_assert(((int)flav) >= 0 && (int)flav < N_CONSENSUS_FLAVORS);
return pending_consensuses[flav].body;
@@ -3382,8 +3416,8 @@ dirvote_get_pending_consensus(consensus_flavor_t flav)
/** Return the signatures that we know for the consensus that we're currently
* trying to build. */
-const char *
-dirvote_get_pending_detached_signatures(void)
+MOCK_IMPL(const char *,
+dirvote_get_pending_detached_signatures, (void))
{
return pending_consensus_signatures;
}
diff --git a/src/or/dirvote.h b/src/or/dirvote.h
index dca8540870..0b1d284060 100644
--- a/src/or/dirvote.h
+++ b/src/or/dirvote.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -55,7 +55,7 @@
#define MIN_SUPPORTED_CONSENSUS_METHOD 13
/** The highest consensus method that we currently support. */
-#define MAX_SUPPORTED_CONSENSUS_METHOD 21
+#define MAX_SUPPORTED_CONSENSUS_METHOD 22
/** Lowest consensus method where microdesc consensuses omit any entry
* with no microdesc. */
@@ -87,11 +87,12 @@
#define MIN_METHOD_FOR_GUARDFRACTION 20
/** Lowest consensus method where authorities may include an "id" line for
- * ed25519 identities in microdescriptors. */
+ * ed25519 identities in microdescriptors. (Broken; see
+ * consensus_method_is_supported() for more info.) */
#define MIN_METHOD_FOR_ED25519_ID_IN_MD 21
/** Lowest consensus method where authorities vote on ed25519 ids and ensure
* ed25519 id consistency. */
-#define MIN_METHOD_FOR_ED25519_ID_VOTING MIN_METHOD_FOR_ED25519_ID_IN_MD
+#define MIN_METHOD_FOR_ED25519_ID_VOTING 22
/** Default bandwidth to clip unmeasured bandwidths to using method >=
* MIN_METHOD_TO_CLIP_UNMEASURED_BW. (This is not a consensus method; do not
@@ -136,8 +137,10 @@ int dirvote_add_signatures(const char *detached_signatures_body,
const char **msg_out);
/* Item access */
-const char *dirvote_get_pending_consensus(consensus_flavor_t flav);
-const char *dirvote_get_pending_detached_signatures(void);
+MOCK_DECL(const char*, dirvote_get_pending_consensus,
+ (consensus_flavor_t flav));
+MOCK_DECL(const char*, dirvote_get_pending_detached_signatures, (void));
+
#define DGV_BY_ID 1
#define DGV_INCLUDE_PENDING 2
#define DGV_INCLUDE_PREVIOUS 4
@@ -175,6 +178,7 @@ STATIC char *format_networkstatus_vote(crypto_pk_t *private_key,
STATIC char *dirvote_compute_params(smartlist_t *votes, int method,
int total_authorities);
STATIC char *compute_consensus_package_lines(smartlist_t *votes);
+STATIC char *make_consensus_method_list(int low, int high, const char *sep);
#endif
#endif
diff --git a/src/or/dns.c b/src/or/dns.c
index d71246d61e..c7adfbc971 100644
--- a/src/or/dns.c
+++ b/src/or/dns.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2003-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -107,13 +107,9 @@ static void dns_found_answer(const char *address, uint8_t query_type,
const tor_addr_t *addr,
const char *hostname,
uint32_t ttl);
-static int launch_resolve(cached_resolve_t *resolve);
static void add_wildcarded_test_address(const char *address);
static int configure_nameservers(int force);
static int answer_is_wildcarded(const char *ip);
-static int set_exitconn_info_from_resolve(edge_connection_t *exitconn,
- const cached_resolve_t *resolve,
- char **hostname_out);
static int evdns_err_is_transient(int err);
static void inform_pending_connections(cached_resolve_t *resolve);
static void make_pending_resolve_cached(cached_resolve_t *cached);
@@ -138,7 +134,7 @@ static int dns_is_broken_for_ipv6 = 0;
/** Function to compare hashed resolves on their addresses; used to
* implement hash tables. */
-static INLINE int
+static inline int
cached_resolves_eq(cached_resolve_t *a, cached_resolve_t *b)
{
/* make this smarter one day? */
@@ -147,7 +143,7 @@ cached_resolves_eq(cached_resolve_t *a, cached_resolve_t *b)
}
/** Hash function for cached_resolve objects */
-static INLINE unsigned int
+static inline unsigned int
cached_resolve_hash(cached_resolve_t *a)
{
return (unsigned) siphash24g((const uint8_t*)a->address, strlen(a->address));
@@ -859,10 +855,10 @@ dns_resolve_impl,(edge_connection_t *exitconn, int is_resolve,
* Return -2 on a transient error, -1 on a permenent error, and 1 on
* a successful lookup.
*/
-static int
-set_exitconn_info_from_resolve(edge_connection_t *exitconn,
- const cached_resolve_t *resolve,
- char **hostname_out)
+MOCK_IMPL(STATIC int,
+set_exitconn_info_from_resolve,(edge_connection_t *exitconn,
+ const cached_resolve_t *resolve,
+ char **hostname_out))
{
int ipv4_ok, ipv6_ok, answer_with_ipv4, r;
uint32_t begincell_flags;
@@ -1130,7 +1126,7 @@ dns_cancel_pending_resolve,(const char *address))
/** Return true iff <b>address</b> is one of the addresses we use to verify
* that well-known sites aren't being hijacked by our DNS servers. */
-static INLINE int
+static inline int
is_test_address(const char *address)
{
const or_options_t *options = get_options();
@@ -1664,8 +1660,8 @@ launch_one_resolve(const char *address, uint8_t query_type,
/** For eventdns: start resolving as necessary to find the target for
* <b>exitconn</b>. Returns -1 on error, -2 on transient error,
* 0 on "resolve launched." */
-static int
-launch_resolve(cached_resolve_t *resolve)
+MOCK_IMPL(STATIC int,
+launch_resolve,(cached_resolve_t *resolve))
{
tor_addr_t a;
int r;
@@ -2118,5 +2114,18 @@ assert_cache_ok_(void)
}
});
}
+
#endif
+cached_resolve_t
+*dns_get_cache_entry(cached_resolve_t *query)
+{
+ return HT_FIND(cache_map, &cache_root, query);
+}
+
+void
+dns_insert_cache_entry(cached_resolve_t *new_entry)
+{
+ HT_INSERT(cache_map, &cache_root, new_entry);
+}
+
diff --git a/src/or/dns.h b/src/or/dns.h
index 6af7796dbb..b14f7dd29c 100644
--- a/src/or/dns.h
+++ b/src/or/dns.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -42,6 +42,18 @@ uint8_t answer_type,const cached_resolve_t *resolved));
MOCK_DECL(STATIC void,send_resolved_hostname_cell,(edge_connection_t *conn,
const char *hostname));
+
+cached_resolve_t *dns_get_cache_entry(cached_resolve_t *query);
+void dns_insert_cache_entry(cached_resolve_t *new_entry);
+
+MOCK_DECL(STATIC int,
+set_exitconn_info_from_resolve,(edge_connection_t *exitconn,
+ const cached_resolve_t *resolve,
+ char **hostname_out));
+
+MOCK_DECL(STATIC int,
+launch_resolve,(cached_resolve_t *resolve));
+
#endif
#endif
diff --git a/src/or/dnsserv.c b/src/or/dnsserv.c
index f7710908bd..edca50f6f9 100644
--- a/src/or/dnsserv.c
+++ b/src/or/dnsserv.c
@@ -1,8 +1,9 @@
-/* Copyright (c) 2007-2015, The Tor Project, Inc. */
+/* Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
- * \file dnsserv.c \brief Implements client-side DNS proxy server code. Note:
+ * \file dnsserv.c
+ * \brief Implements client-side DNS proxy server code. Note:
* this is the DNS Server code, not the Server DNS code. Confused? This code
* runs on client-side, and acts as a DNS server. The code in dns.c, on the
* other hand, runs on Tor servers, and acts as a DNS client.
@@ -87,8 +88,6 @@ evdns_server_callback(struct evdns_server_request *req, void *data_)
for (i = 0; i < req->nquestions; ++i) {
if (req->questions[i]->dns_question_class != EVDNS_CLASS_INET)
continue;
- if (! q)
- q = req->questions[i];
switch (req->questions[i]->type) {
case EVDNS_TYPE_A:
case EVDNS_TYPE_AAAA:
@@ -96,7 +95,7 @@ evdns_server_callback(struct evdns_server_request *req, void *data_)
/* We always pick the first one of these questions, if there is
one. */
if (! supported_q)
- supported_q = q;
+ supported_q = req->questions[i];
break;
default:
break;
@@ -125,12 +124,13 @@ evdns_server_callback(struct evdns_server_request *req, void *data_)
/* Make a new dummy AP connection, and attach the request to it. */
entry_conn = entry_connection_new(CONN_TYPE_AP, AF_INET);
conn = ENTRY_TO_EDGE_CONN(entry_conn);
+ CONNECTION_AP_EXPECT_NONPENDING(entry_conn);
TO_CONN(conn)->state = AP_CONN_STATE_RESOLVE_WAIT;
conn->is_dns_request = 1;
tor_addr_copy(&TO_CONN(conn)->addr, &tor_addr);
TO_CONN(conn)->port = port;
- TO_CONN(conn)->address = tor_dup_addr(&tor_addr);
+ TO_CONN(conn)->address = tor_addr_to_str_dup(&tor_addr);
if (q->type == EVDNS_TYPE_A || q->type == EVDNS_TYPE_AAAA ||
q->type == EVDNS_QTYPE_ALL) {
@@ -199,12 +199,13 @@ dnsserv_launch_request(const char *name, int reverse,
/* Make a new dummy AP connection, and attach the request to it. */
entry_conn = entry_connection_new(CONN_TYPE_AP, AF_INET);
conn = ENTRY_TO_EDGE_CONN(entry_conn);
+ CONNECTION_AP_EXPECT_NONPENDING(entry_conn);
conn->base_.state = AP_CONN_STATE_RESOLVE_WAIT;
tor_addr_copy(&TO_CONN(conn)->addr, &control_conn->base_.addr);
#ifdef AF_UNIX
/*
- * The control connection can be AF_UNIX and if so tor_dup_addr will
+ * The control connection can be AF_UNIX and if so tor_addr_to_str_dup will
* unhelpfully say "<unknown address type>"; say "(Tor_internal)"
* instead.
*/
@@ -213,11 +214,11 @@ dnsserv_launch_request(const char *name, int reverse,
TO_CONN(conn)->address = tor_strdup("(Tor_internal)");
} else {
TO_CONN(conn)->port = control_conn->base_.port;
- TO_CONN(conn)->address = tor_dup_addr(&control_conn->base_.addr);
+ TO_CONN(conn)->address = tor_addr_to_str_dup(&control_conn->base_.addr);
}
#else
TO_CONN(conn)->port = control_conn->base_.port;
- TO_CONN(conn)->address = tor_dup_addr(&control_conn->base_.addr);
+ TO_CONN(conn)->address = tor_addr_to_str_dup(&control_conn->base_.addr);
#endif
if (reverse)
diff --git a/src/or/dnsserv.h b/src/or/dnsserv.h
index 09ad5d7759..ad0e248c83 100644
--- a/src/or/dnsserv.h
+++ b/src/or/dnsserv.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c
index ebf675166b..310a948b35 100644
--- a/src/or/entrynodes.c
+++ b/src/or/entrynodes.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -87,7 +87,7 @@ get_entry_guards(void)
/** Check whether the entry guard <b>e</b> is usable, given the directory
* authorities' opinion about the router (stored in <b>ri</b>) and the user's
- * configuration (in <b>options</b>). Set <b>e</b>-&gt;bad_since
+ * configuration (in <b>options</b>). Set <b>e</b>->bad_since
* accordingly. Return true iff the entry guard's status changes.
*
* If it's not usable, set *<b>reason</b> to a static string explaining why.
@@ -117,6 +117,9 @@ entry_guard_set_status(entry_guard_t *e, const node_t *node,
*reason = "not recommended as a guard";
else if (routerset_contains_node(options->ExcludeNodes, node))
*reason = "excluded";
+ /* We only care about OR connection connectivity for entry guards. */
+ else if (!fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, 0))
+ *reason = "unreachable by config";
else if (e->path_bias_disabled)
*reason = "path-biased";
@@ -268,7 +271,7 @@ entry_is_live(const entry_guard_t *e, entry_is_live_flags_t flags,
*msg = "not fast/stable";
return NULL;
}
- if (!fascist_firewall_allows_node(node)) {
+ if (!fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, 0)) {
*msg = "unreachable by config";
return NULL;
}
@@ -918,7 +921,8 @@ entry_guards_set_from_config(const or_options_t *options)
} else if (routerset_contains_node(options->ExcludeNodes, node)) {
SMARTLIST_DEL_CURRENT(entry_nodes, node);
continue;
- } else if (!fascist_firewall_allows_node(node)) {
+ } else if (!fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION,
+ 0)) {
SMARTLIST_DEL_CURRENT(entry_nodes, node);
continue;
} else if (! node->is_possible_guard) {
@@ -1152,7 +1156,7 @@ choose_random_entry_impl(cpath_build_state_t *state, int for_directory,
} else {
/* Try to have at least 2 choices available. This way we don't
* get stuck with a single live-but-crummy entry and just keep
- * using him.
+ * using it.
* (We might get 2 live-but-crummy entry guards, but so be it.) */
preferred_min = 2;
}
@@ -1791,7 +1795,7 @@ get_configured_bridge_by_orports_digest(const char *digest,
}
/** If we have a bridge configured whose digest matches <b>digest</b>, or a
- * bridge with no known digest whose address matches <b>addr</b>:<b>/port</b>,
+ * bridge with no known digest whose address matches <b>addr</b>:<b>port</b>,
* return that bridge. Else return NULL. If <b>digest</b> is NULL, check for
* address/port matches only. */
static bridge_info_t *
@@ -1814,6 +1818,30 @@ get_configured_bridge_by_addr_port_digest(const tor_addr_t *addr,
return NULL;
}
+/** If we have a bridge configured whose digest matches <b>digest</b>, or a
+ * bridge with no known digest whose address matches <b>addr</b>:<b>port</b>,
+ * return 1. Else return 0. If <b>digest</b> is NULL, check for
+ * address/port matches only. */
+int
+addr_is_a_configured_bridge(const tor_addr_t *addr,
+ uint16_t port,
+ const char *digest)
+{
+ tor_assert(addr);
+ return get_configured_bridge_by_addr_port_digest(addr, port, digest) ? 1 : 0;
+}
+
+/** If we have a bridge configured whose digest matches
+ * <b>ei->identity_digest</b>, or a bridge with no known digest whose address
+ * matches <b>ei->addr</b>:<b>ei->port</b>, return 1. Else return 0.
+ * If <b>ei->onion_key</b> is NULL, check for address/port matches only. */
+int
+extend_info_is_a_configured_bridge(const extend_info_t *ei)
+{
+ const char *digest = ei->onion_key ? ei->identity_digest : NULL;
+ return addr_is_a_configured_bridge(&ei->addr, ei->port, digest);
+}
+
/** Wrapper around get_configured_bridge_by_addr_port_digest() to look
* it up via router descriptor <b>ri</b>. */
static bridge_info_t *
@@ -2116,8 +2144,18 @@ launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge)
return;
}
- directory_initiate_command(&bridge->addr,
- bridge->port, 0/*no dirport*/,
+ /* Until we get a descriptor for the bridge, we only know one address for
+ * it. */
+ if (!fascist_firewall_allows_address_addr(&bridge->addr, bridge->port,
+ FIREWALL_OR_CONNECTION, 0, 0)) {
+ log_notice(LD_CONFIG, "Tried to fetch a descriptor directly from a "
+ "bridge, but that bridge is not reachable through our "
+ "firewall.");
+ return;
+ }
+
+ directory_initiate_command(&bridge->addr, bridge->port,
+ NULL, 0, /*no dirport*/
bridge->identity,
DIR_PURPOSE_FETCH_SERVERDESC,
ROUTER_PURPOSE_BRIDGE,
@@ -2178,7 +2216,9 @@ fetch_bridge_descriptors(const or_options_t *options, time_t now)
!options->UpdateBridgesFromAuthority, !num_bridge_auths);
if (ask_bridge_directly &&
- !fascist_firewall_allows_address_or(&bridge->addr, bridge->port)) {
+ !fascist_firewall_allows_address_addr(&bridge->addr, bridge->port,
+ FIREWALL_OR_CONNECTION, 0,
+ 0)) {
log_notice(LD_DIR, "Bridge at '%s' isn't reachable by our "
"firewall policy. %s.",
fmt_addrport(&bridge->addr, bridge->port),
@@ -2205,7 +2245,7 @@ fetch_bridge_descriptors(const or_options_t *options, time_t now)
log_info(LD_DIR, "Fetching bridge info '%s' from bridge authority.",
resource);
directory_get_from_dirserver(DIR_PURPOSE_FETCH_SERVERDESC,
- ROUTER_PURPOSE_BRIDGE, resource, 0);
+ ROUTER_PURPOSE_BRIDGE, resource, 0, DL_WANT_AUTHORITY);
}
}
SMARTLIST_FOREACH_END(bridge);
@@ -2226,6 +2266,7 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node)
* does so through an address from any source other than node_get_addr().
*/
tor_addr_t addr;
+ const or_options_t *options = get_options();
if (node->ri) {
routerinfo_t *ri = node->ri;
@@ -2258,9 +2299,15 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node)
}
}
- /* Mark which address to use based on which bridge_t we got. */
- node->ipv6_preferred = (tor_addr_family(&bridge->addr) == AF_INET6 &&
- !tor_addr_is_null(&node->ri->ipv6_addr));
+ if (options->ClientPreferIPv6ORPort == -1) {
+ /* Mark which address to use based on which bridge_t we got. */
+ node->ipv6_preferred = (tor_addr_family(&bridge->addr) == AF_INET6 &&
+ !tor_addr_is_null(&node->ri->ipv6_addr));
+ } else {
+ /* Mark which address to use based on user preference */
+ node->ipv6_preferred = (fascist_firewall_prefer_ipv6_orport(options) &&
+ !tor_addr_is_null(&node->ri->ipv6_addr));
+ }
/* XXXipv6 we lack support for falling back to another address for
the same relay, warn the user */
@@ -2269,10 +2316,13 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node)
node_get_pref_orport(node, &ap);
log_notice(LD_CONFIG,
"Bridge '%s' has both an IPv4 and an IPv6 address. "
- "Will prefer using its %s address (%s).",
+ "Will prefer using its %s address (%s) based on %s.",
ri->nickname,
- tor_addr_family(&ap.addr) == AF_INET6 ? "IPv6" : "IPv4",
- fmt_addrport(&ap.addr, ap.port));
+ node->ipv6_preferred ? "IPv6" : "IPv4",
+ fmt_addrport(&ap.addr, ap.port),
+ options->ClientPreferIPv6ORPort == -1 ?
+ "the configured Bridge address" :
+ "ClientPreferIPv6ORPort");
}
}
if (node->rs) {
diff --git a/src/or/entrynodes.h b/src/or/entrynodes.h
index 107a562506..247c80940e 100644
--- a/src/or/entrynodes.h
+++ b/src/or/entrynodes.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -127,6 +127,9 @@ int getinfo_helper_entry_guards(control_connection_t *conn,
void mark_bridge_list(void);
void sweep_bridge_list(void);
+int addr_is_a_configured_bridge(const tor_addr_t *addr, uint16_t port,
+ const char *digest);
+int extend_info_is_a_configured_bridge(const extend_info_t *ei);
int routerinfo_is_a_configured_bridge(const routerinfo_t *ri);
int node_is_a_configured_bridge(const node_t *node);
void learned_router_identity(const tor_addr_t *addr, uint16_t port,
diff --git a/src/or/eventdns_tor.h b/src/or/eventdns_tor.h
index 9d51f0960e..5db09ae043 100644
--- a/src/or/eventdns_tor.h
+++ b/src/or/eventdns_tor.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2007-2015, The Tor Project, Inc. */
+/* Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_EVENTDNS_TOR_H
@@ -12,9 +12,6 @@ typedef unsigned int uint;
#ifndef HAVE_U_CHAR
typedef unsigned char u_char;
#endif
-#ifdef _WIN32
-#define inline __inline
-#endif
#include "torint.h"
/* These are for debugging possible memory leaks. */
diff --git a/src/or/ext_orport.c b/src/or/ext_orport.c
index e8c8aa60a4..8ba3c6afa3 100644
--- a/src/or/ext_orport.c
+++ b/src/or/ext_orport.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2015, The Tor Project, Inc. */
+/* Copyright (c) 2012-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -151,7 +151,7 @@ init_ext_or_cookie_authentication(int is_enabled)
}
/** Read data from <b>conn</b> and see if the client sent us the
- * authentication type that she prefers to use in this session.
+ * authentication type that they prefer to use in this session.
*
* Return -1 if we received corrupted data or if we don't support the
* authentication type. Return 0 if we need more data in
@@ -178,7 +178,7 @@ connection_ext_or_auth_neg_auth_type(connection_t *conn)
return 1;
}
-/** DOCDOC */
+/* DOCDOC */
STATIC int
handle_client_auth_nonce(const char *client_nonce, size_t client_nonce_len,
char **client_hash_out,
@@ -193,8 +193,7 @@ handle_client_auth_nonce(const char *client_nonce, size_t client_nonce_len,
return -1;
/* Get our nonce */
- if (crypto_rand(server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN) < 0)
- return -1;
+ crypto_rand(server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN);
{ /* set up macs */
size_t hmac_s_msg_len = strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST) +
@@ -462,8 +461,8 @@ connection_ext_or_handle_cmd_useraddr(connection_t *conn,
return -1;
{ /* do some logging */
- char *old_address = tor_dup_addr(&conn->addr);
- char *new_address = tor_dup_addr(&addr);
+ char *old_address = tor_addr_to_str_dup(&conn->addr);
+ char *new_address = tor_addr_to_str_dup(&addr);
log_debug(LD_NET, "Received USERADDR."
"We rewrite our address from '%s:%u' to '%s:%u'.",
@@ -479,7 +478,7 @@ connection_ext_or_handle_cmd_useraddr(connection_t *conn,
if (conn->address) {
tor_free(conn->address);
}
- conn->address = tor_dup_addr(&addr);
+ conn->address = tor_addr_to_str_dup(&addr);
return 0;
}
diff --git a/src/or/ext_orport.h b/src/or/ext_orport.h
index 8b2542f937..33d954e8d0 100644
--- a/src/or/ext_orport.h
+++ b/src/or/ext_orport.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef EXT_ORPORT_H
diff --git a/src/or/fallback_dirs.inc b/src/or/fallback_dirs.inc
new file mode 100644
index 0000000000..8e82a3fb2f
--- /dev/null
+++ b/src/or/fallback_dirs.inc
@@ -0,0 +1,242 @@
+/* Whitelist & blacklist excluded 1273 of 1553 candidates. */
+/* Checked IPv4 DirPorts served a consensus within 15.0s. */
+/*
+Final Count: 100 (Eligible 280, Target 356 (1781 * 0.20), Max 100)
+Excluded: 180 (Same Operator 102, Failed/Skipped Download 40, Excess 38)
+Bandwidth Range: 6.0 - 67.2 MB/s
+*/
+/*
+Onionoo Source: details Date: 2016-04-18 01:00:00 Version: 3.1
+URL: https:onionoo.torproject.orgdetails?fields=fingerprint%2Cnickname%2Ccontact%2Clast_changed_address_or_port%2Cconsensus_weight%2Cadvertised_bandwidth%2Cor_addresses%2Cdir_address%2Crecommended_version%2Cflags%2Ceffective_family&flag=V2Dir&type=relay&last_seen_days=-7&first_seen_days=7-
+*/
+/*
+Onionoo Source: uptime Date: 2016-04-18 01:00:00 Version: 3.1
+URL: https:onionoo.torproject.orguptime?first_seen_days=7-&flag=V2Dir&type=relay&last_seen_days=-7
+*/
+"193.171.202.146:9030 orport=9001 id=01A9258A46E97FF8B2CAC7910577862C14F2C524"
+" weight=10",
+"5.9.110.236:9030 orport=9001 id=0756B7CD4DFC8182BE23143FAC0642F515182CEB"
+" ipv6=[2a01:4f8:162:51e2::2]:9001"
+" weight=10",
+"37.187.1.149:9030 orport=9001 id=08DC0F3C6E3D9C527C1FC8745D35DD1B0DE1875D"
+" ipv6=[2001:41d0:a:195::1]:9001"
+" weight=10",
+"5.39.92.199:80 orport=443 id=0BEA4A88D069753218EAAAD6D22EA87B9A1319D6"
+" weight=10",
+"5.196.88.122:9030 orport=9001 id=0C2C599AFCB26F5CFC2C7592435924C1D63D9484"
+" weight=10",
+"178.62.197.82:80 orport=443 id=0D3EBA17E1C78F1E9900BABDB23861D46FCAF163"
+" weight=10",
+"144.76.14.145:110 orport=143 id=14419131033443AE6E21DA82B0D307F7CAE42BDB"
+" ipv6=[2a01:4f8:190:9490::dead]:443"
+" weight=10",
+"178.32.216.146:9030 orport=9001 id=17898F9A2EBC7D69DAF87C00A1BD2FABF3C9E1D2"
+" weight=10",
+"46.101.151.222:80 orport=443 id=1DBAED235E3957DE1ABD25B4206BE71406FB61F8"
+" weight=10",
+"91.219.237.229:80 orport=443 id=1ECD73B936CB6E6B3CD647CC204F108D9DF2C9F7"
+" weight=10",
+"212.47.229.2:9030 orport=9001 id=20462CBA5DA4C2D963567D17D0B7249718114A68"
+" weight=10",
+"185.61.138.18:8080 orport=4443 id=2541759BEC04D37811C2209A88E863320271EC9C"
+" weight=10",
+"51.254.215.121:80 orport=443 id=262B66AD25C79588AD1FC8ED0E966395B47E5C1D"
+" weight=10",
+"194.150.168.79:11112 orport=11111 id=29F1020B94BE25E6BE1AD13E93CE19D2131B487C"
+" weight=10",
+"144.76.26.175:9012 orport=9011 id=2BA2C8E96B2590E1072AECE2BDB5C48921BF8510"
+" weight=10",
+"62.210.124.124:9130 orport=9101 id=2EBD117806EE43C3CC885A8F1E4DC60F207E7D3E"
+" ipv6=[2001:bc8:3f23:100::1]:9101"
+" weight=10",
+"213.61.66.118:9031 orport=9001 id=30648BC64CEDB3020F4A405E4AB2A6347FB8FA22"
+" weight=10",
+"212.83.154.33:8080 orport=8443 id=322C6E3A973BC10FC36DE3037AD27BC89F14723B"
+" weight=10",
+"109.105.109.162:52860 orport=60784 id=32EE911D968BE3E016ECA572BB1ED0A9EE43FC2F"
+" ipv6=[2001:948:7:2::163]:5001"
+" weight=10",
+"146.0.32.144:9030 orport=9001 id=35E8B344F661F4F2E68B17648F35798B44672D7E"
+" weight=10",
+"217.79.190.25:9030 orport=9090 id=361D33C96D0F161275EE67E2C91EE10B276E778B"
+" weight=10",
+"62.210.92.11:9130 orport=9101 id=387B065A38E4DAA16D9D41C2964ECBC4B31D30FF"
+" ipv6=[2001:bc8:338c::1]:9101"
+" weight=10",
+"198.50.191.95:80 orport=443 id=39F096961ED2576975C866D450373A9913AFDC92"
+" weight=10",
+"164.132.77.175:9030 orport=9001 id=3B33F6FCA645AD4E91428A3AF7DC736AD9FB727B"
+" weight=10",
+"176.10.107.180:9030 orport=9001 id=3D7E274A87D9A89AF064C13D1EE4CA1F184F2600"
+" weight=10",
+"37.187.102.186:9030 orport=9001 id=489D94333DF66D57FFE34D9D59CC2D97E2CB0053"
+" ipv6=[2001:41d0:a:26ba::1]:9001"
+" weight=10",
+"188.165.194.195:9030 orport=9001 id=49E7AD01BB96F6FE3AB8C3B15BD2470B150354DF"
+" weight=10",
+"108.53.208.157:80 orport=443 id=4F0DB7E687FC7C0AE55C8F243DA8B0EB27FBF1F2"
+" weight=10",
+"212.51.134.123:9030 orport=9001 id=50586E25BE067FD1F739998550EDDCB1A14CA5B2"
+" ipv6=[2a02:168:6e00:0:3a60:77ff:fe9c:8bd1]:9001"
+" weight=10",
+"5.175.233.86:80 orport=443 id=5525D0429BFE5DC4F1B0E9DE47A4CFA169661E33"
+" weight=10",
+"94.23.204.175:9030 orport=9001 id=5665A3904C89E22E971305EE8C1997BCA4123C69"
+" weight=10",
+"109.163.234.9:80 orport=443 id=5714542DCBEE1DD9864824723638FD44B2122CEA"
+" weight=10",
+"185.21.100.50:9030 orport=9001 id=58ED9C9C35E433EE58764D62892B4FFD518A3CD0"
+" ipv6=[2a00:1158:2:cd00:0:74:6f:72]:443"
+" weight=10",
+"78.142.142.246:80 orport=443 id=5A5E03355C1908EBF424CAF1F3ED70782C0D2F74"
+" weight=10",
+"185.100.85.138:80 orport=46356 id=5C4DF16A0029CC4F67D3E127356E68F219269859"
+" weight=10",
+"178.16.208.62:80 orport=443 id=5CF8AFA5E4B0BB88942A44A3F3AAE08C3BDFD60B"
+" ipv6=[2a00:1c20:4089:1234:a6a4:2926:d0af:dfee]:443"
+" weight=10",
+"95.128.43.164:80 orport=443 id=616081EC829593AF4232550DE6FFAA1D75B37A90"
+" ipv6=[2a02:ec0:209:10::4]:443"
+" weight=10",
+"89.187.142.208:80 orport=443 id=64186650FFE4469EBBE52B644AE543864D32F43C"
+" weight=10",
+"144.76.73.140:9030 orport=9001 id=6A640018EABF3DA9BAD9321AA37C2C87BBE1F907"
+" weight=10",
+"94.126.23.174:9030 orport=9001 id=6FC6F08270D565BE89B7C819DD8E2D487397C073"
+" weight=10",
+"176.31.191.26:9030 orport=9001 id=7350AB9ED7568F22745198359373C04AC783C37C"
+" weight=10",
+"46.101.237.246:9030 orport=9001 id=75F1992FD3F403E9C082A5815EB5D12934CDF46C"
+" ipv6=[2a03:b0c0:3:d0::208:5001]:9050"
+" weight=10",
+"185.11.180.67:80 orport=9001 id=794D8EA8343A4E820320265D05D4FA83AB6D1778"
+" weight=10",
+"62.210.129.246:80 orport=443 id=79E169B25E4C7CE99584F6ED06F379478F23E2B8"
+" weight=10",
+"82.223.21.74:9030 orport=9001 id=7A32C9519D80CA458FC8B034A28F5F6815649A98"
+" ipv6=[2001:470:53e0::cafe]:9050"
+" weight=10",
+"192.160.102.164:80 orport=9001 id=823AA81E277F366505545522CEDC2F529CE4DC3F"
+" weight=10",
+"192.87.28.82:9030 orport=9001 id=844AE9CAD04325E955E2BE1521563B79FE7094B7"
+" weight=10",
+"84.219.173.60:9030 orport=443 id=855BC2DABE24C861CD887DB9B2E950424B49FC34"
+" weight=10",
+"163.172.138.22:80 orport=443 id=8664DC892540F3C789DB37008236C096C871734D"
+" weight=10",
+"185.96.88.29:80 orport=443 id=86C281AD135058238D7A337D546C902BE8505DDE"
+" weight=10",
+"93.180.156.84:9030 orport=9001 id=8844D87E9B038BE3270938F05AF797E1D3C74C0F"
+" weight=10",
+"178.217.184.32:9030 orport=443 id=8B7F47AE1A5D954A3E58ACDE0865D09DBA5B738D"
+" weight=10",
+"151.80.42.103:9030 orport=9001 id=9007C1D8E4F03D506A4A011B907A9E8D04E3C605"
+" ipv6=[2001:41d0:e:f67::114]:9001"
+" weight=10",
+"5.79.68.161:81 orport=443 id=9030DCF419F6E2FBF84F63CBACBA0097B06F557E"
+" ipv6=[2001:1af8:4700:a012:1::1]:443"
+" weight=10",
+"51.255.41.65:9030 orport=9001 id=9231DF741915AA1630031A93026D88726877E93A"
+" weight=10",
+"91.219.237.244:80 orport=443 id=92ECC9E0E2AF81BB954719B189AC362E254AD4A5"
+" weight=10",
+"46.101.102.71:80 orport=443 id=9504CB22EEB25D344DE63CB7A6F2C46F895C3686"
+" ipv6=[2a03:b0c0:3:d0::2ed:7001]:9050"
+" weight=10",
+"85.214.206.219:9030 orport=9001 id=98F8D5F359949E41DE8DF3DBB1975A86E96A84A0"
+" weight=10",
+"81.7.10.93:31336 orport=31337 id=99E246DB480B313A3012BC3363093CC26CD209C7"
+" weight=10",
+"46.28.110.244:80 orport=443 id=9F7D6E6420183C2B76D3CE99624EBC98A21A967E"
+" weight=10",
+"46.165.230.5:80 orport=443 id=A0F06C2FADF88D3A39AA3072B406F09D7095AC9E"
+" weight=10",
+"171.25.193.77:80 orport=443 id=A10C4F666D27364036B562823E5830BC448E046A"
+" ipv6=[2001:67c:289c:3::77]:443"
+" weight=10",
+"176.9.5.116:9030 orport=9001 id=A1EB8D8F1EE28DB98BBB1EAA3B4BEDD303BAB911"
+" weight=10",
+"192.34.63.137:9030 orport=443 id=ABCB4965F1FEE193602B50A365425105C889D3F8"
+" weight=10",
+"195.154.164.243:80 orport=443 id=AC66FFA4AB35A59EBBF5BF4C70008BF24D8A7A5C"
+" ipv6=[2001:bc8:399f:f000::1]:993"
+" weight=10",
+"86.59.119.88:80 orport=443 id=ACD889D86E02EDDAB1AFD81F598C0936238DC6D0"
+" weight=10",
+"163.172.131.88:80 orport=443 id=AD253B49E303C6AB1E048B014392AC569E8A7DAE"
+" ipv6=[2001:bc8:4400:2100::2:1009]:443"
+" weight=10",
+"178.254.44.135:80 orport=443 id=AE6A8C18E7499B586CD36246AC4BCAFFBBF93AB2"
+" weight=10",
+"37.187.7.74:80 orport=443 id=AEA43CB1E47BE5F8051711B2BF01683DB1568E05"
+" ipv6=[2001:41d0:a:74a::1]:443"
+" weight=10",
+"212.129.62.232:80 orport=443 id=B143D439B72D239A419F8DCE07B8A8EB1B486FA7"
+" weight=10",
+"185.66.250.141:9030 orport=9001 id=B1726B94885CE3AC3910CA8B60622B97B98E2529"
+" weight=10",
+"193.11.114.46:9032 orport=9003 id=B83DC1558F0D34353BB992EF93AFEAFDB226A73E"
+" weight=10",
+"178.62.36.64:9030 orport=9001 id=B87C84E38DAECFFFFDE98E5AEE5786AFDC748F2C"
+" weight=10",
+"197.231.221.211:9030 orport=9001 id=BC630CBBB518BE7E9F4E09712AB0269E9DC7D626"
+" weight=10",
+"198.96.155.3:8080 orport=5001 id=BCEDF6C193AA687AE471B8A22EBF6BC57C2D285E"
+" weight=10",
+"148.251.190.229:9030 orport=9010 id=BF0FB582E37F738CD33C3651125F2772705BB8E8"
+" ipv6=[2a01:4f8:211:c68::2]:9010"
+" weight=10",
+"188.138.112.60:1433 orport=1521 id=C414F28FD2BEC1553024299B31D4E726BEB8E788"
+" weight=10",
+"195.154.79.128:80 orport=443 id=C697612CA5AED06B8D829FCC6065B9287212CB2F"
+" weight=10",
+"37.59.46.159:9030 orport=9001 id=CBD0D1BD110EC52963082D839AC6A89D0AE243E7"
+" weight=10",
+"91.121.54.8:9030 orport=9001 id=CBEE0F3303C8C50462A12107CA2AE061831931BC"
+" weight=10",
+"178.62.199.226:80 orport=443 id=CBEFF7BA4A4062045133C053F2D70524D8BBE5BE"
+" ipv6=[2a03:b0c0:2:d0::b7:5001]:443"
+" weight=10",
+"81.7.17.171:80 orport=443 id=CFECDDCA990E3EF7B7EC958B22441386B6B8D820"
+" ipv6=[2a02:180:1:1::517:11ab]:443"
+" weight=10",
+"134.119.3.164:9030 orport=9001 id=D1B8AAA98C65F3DF7D8BB3AF881CAEB84A33D8EE"
+" weight=10",
+"185.13.38.75:9030 orport=9001 id=D2A1703758A0FBBA026988B92C2F88BAB59F9361"
+" weight=10",
+"37.187.115.157:9030 orport=9001 id=D5039E1EBFD96D9A3F9846BF99EC9F75EDDE902A"
+" weight=10",
+"185.14.185.240:9030 orport=443 id=D62FB817B0288085FAC38A6DC8B36DCD85B70260"
+" weight=10",
+"37.221.162.226:9030 orport=9001 id=D64366987CB39F61AD21DBCF8142FA0577B92811"
+" weight=10",
+"193.35.52.53:9030 orport=9001 id=DAA39FC00B196B353C2A271459C305C429AF09E4"
+" weight=10",
+"178.62.173.203:9030 orport=9001 id=DD85503F2D1F52EF9EAD621E942298F46CD2FC10"
+" ipv6=[2a03:b0c0:0:1010::a4:b001]:9001"
+" weight=10",
+"5.34.183.205:80 orport=443 id=DDD7871C1B7FA32CB55061E08869A236E61BDDF8"
+" weight=10",
+"195.191.233.221:80 orport=443 id=DE134FC8E5CC4EC8A5DE66934E70AC9D70267197"
+" weight=10",
+"46.252.26.2:45212 orport=49991 id=E589316576A399C511A9781A73DA4545640B479D"
+" weight=10",
+"176.31.180.157:143 orport=22 id=E781F4EC69671B3F1864AE2753E0890351506329"
+" ipv6=[2001:41d0:8:eb9d::1]:22"
+" weight=10",
+"131.188.40.188:443 orport=80 id=EBE718E1A49EE229071702964F8DB1F318075FF8"
+" weight=10",
+"91.219.236.222:80 orport=443 id=EC413181CEB1C8EDC17608BBB177CD5FD8535E99"
+" weight=10",
+"94.242.246.23:443 orport=9001 id=F65E0196C94DFFF48AFBF2F5F9E3E19AAE583FD0"
+" ipv6=[2a01:608:ffff:ff07::1:23]:9003"
+" weight=10",
+"46.101.143.173:80 orport=443 id=F960DF50F0FD4075AC9B505C1D4FFC8384C490FB"
+" weight=10",
+"195.154.8.111:80 orport=443 id=FCB6695F8F2DC240E974510A4B3A0F2B12AB5B64"
+" weight=10",
+"192.187.124.98:9030 orport=9001 id=FD1871854BFC06D7B02F10742073069F0528B5CC"
+" weight=10",
+"193.11.164.243:9030 orport=9001 id=FFA72BD683BC2FCF988356E6BEC1E490F313FB07"
+" ipv6=[2001:6b0:7:125::243]:9001"
+" weight=10",
diff --git a/src/or/fp_pair.c b/src/or/fp_pair.c
index 42bebcd847..53b311e580 100644
--- a/src/or/fp_pair.c
+++ b/src/or/fp_pair.c
@@ -1,6 +1,14 @@
-/* Copyright (c) 2013-2015, The Tor Project, Inc. */
+/* Copyright (c) 2013-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+/**
+ * \file fp_pair.c
+ *
+ * \brief Manages data structures for associating pairs of fingerprints. Used
+ * to handle combinations of identity/signing-key fingerprints for
+ * authorities.
+ **/
+
#include "or.h"
#include "fp_pair.h"
@@ -21,7 +29,7 @@ struct fp_pair_map_s {
*/
/** Compare fp_pair_entry_t objects by key value. */
-static INLINE int
+static inline int
fp_pair_map_entries_eq(const fp_pair_map_entry_t *a,
const fp_pair_map_entry_t *b)
{
@@ -29,7 +37,7 @@ fp_pair_map_entries_eq(const fp_pair_map_entry_t *a,
}
/** Return a hash value for an fp_pair_entry_t. */
-static INLINE unsigned int
+static inline unsigned int
fp_pair_map_entry_hash(const fp_pair_map_entry_t *a)
{
tor_assert(sizeof(a->key) == DIGEST_LEN*2);
diff --git a/src/or/fp_pair.h b/src/or/fp_pair.h
index 0830ab1f36..b1466581d2 100644
--- a/src/or/fp_pair.h
+++ b/src/or/fp_pair.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015, The Tor Project, Inc. */
+/* Copyright (c) 2013-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/geoip.c b/src/or/geoip.c
index 120ce479cc..b563db0418 100644
--- a/src/or/geoip.c
+++ b/src/or/geoip.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2007-2015, The Tor Project, Inc. */
+/* Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -18,7 +18,6 @@
#include "geoip.h"
#include "routerlist.h"
-static void clear_geoip_db(void);
static void init_geoip_countries(void);
/** An entry from the GeoIP IPv4 file: maps an IPv4 range to a country. */
@@ -483,7 +482,7 @@ static HT_HEAD(clientmap, clientmap_entry_t) client_history =
HT_INITIALIZER();
/** Hashtable helper: compute a hash of a clientmap_entry_t. */
-static INLINE unsigned
+static inline unsigned
clientmap_entry_hash(const clientmap_entry_t *a)
{
unsigned h = (unsigned) tor_addr_hash(&a->addr);
@@ -494,7 +493,7 @@ clientmap_entry_hash(const clientmap_entry_t *a)
return h;
}
/** Hashtable helper: compare two clientmap_entry_t values for equality. */
-static INLINE int
+static inline int
clientmap_entries_eq(const clientmap_entry_t *a, const clientmap_entry_t *b)
{
if (strcmp_opt(a->transport_name, b->transport_name))
@@ -970,7 +969,7 @@ geoip_get_dirreq_history(dirreq_type_t type)
&ent->completion_time);
if (time_diff == 0)
time_diff = 1; /* Avoid DIV/0; "instant" answers are impossible
- * by law of nature or something, but a milisecond
+ * by law of nature or something, but a millisecond
* is a bit greater than "instantly" */
bytes_per_second = (uint32_t)(1000 * ent->response_size / time_diff);
dltimes[ent_sl_idx] = bytes_per_second;
@@ -1207,9 +1206,9 @@ geoip_format_dirreq_stats(time_t now)
{
char t[ISO_TIME_LEN+1];
int i;
- char *v3_ips_string, *v3_reqs_string, *v3_direct_dl_string,
- *v3_tunneled_dl_string;
- char *result;
+ char *v3_ips_string = NULL, *v3_reqs_string = NULL,
+ *v3_direct_dl_string = NULL, *v3_tunneled_dl_string = NULL;
+ char *result = NULL;
if (!start_of_dirreq_stats_interval)
return NULL; /* Not initialized. */
@@ -1280,6 +1279,8 @@ geoip_dirreq_stats_write(time_t now)
/* Generate history string .*/
str = geoip_format_dirreq_stats(now);
+ if (! str)
+ goto done;
/* Write dirreq-stats string to disk. */
if (!check_or_create_data_subdir("stats")) {
@@ -1666,7 +1667,7 @@ getinfo_helper_geoip(control_connection_t *control_conn,
}
/** Release all storage held by the GeoIP databases and country list. */
-static void
+STATIC void
clear_geoip_db(void)
{
if (geoip_countries) {
diff --git a/src/or/geoip.h b/src/or/geoip.h
index 8a3486c7ac..070296dd07 100644
--- a/src/or/geoip.h
+++ b/src/or/geoip.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -18,6 +18,7 @@
STATIC int geoip_parse_entry(const char *line, sa_family_t family);
STATIC int geoip_get_country_by_ipv4(uint32_t ipaddr);
STATIC int geoip_get_country_by_ipv6(const struct in6_addr *addr);
+STATIC void clear_geoip_db(void);
#endif
int should_record_bridge_info(const or_options_t *options);
int geoip_load_file(sa_family_t family, const char *filename);
diff --git a/src/or/hibernate.c b/src/or/hibernate.c
index 356e11f6ec..3666abbcf4 100644
--- a/src/or/hibernate.c
+++ b/src/or/hibernate.c
@@ -1,5 +1,5 @@
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -28,6 +28,7 @@ hibernating, phase 2:
#include "config.h"
#include "connection.h"
#include "connection_edge.h"
+#include "control.h"
#include "hibernate.h"
#include "main.h"
#include "router.h"
@@ -111,11 +112,34 @@ static int cfg_start_day = 0,
cfg_start_min = 0;
/** @} */
+static const char *hibernate_state_to_string(hibernate_state_t state);
static void reset_accounting(time_t now);
static int read_bandwidth_usage(void);
static time_t start_of_accounting_period_after(time_t now);
static time_t start_of_accounting_period_containing(time_t now);
static void accounting_set_wakeup_time(void);
+static void on_hibernate_state_change(hibernate_state_t prev_state);
+
+/**
+ * Return the human-readable name for the hibernation state <b>state</b>
+ */
+static const char *
+hibernate_state_to_string(hibernate_state_t state)
+{
+ static char buf[64];
+ switch (state) {
+ case HIBERNATE_STATE_EXITING: return "EXITING";
+ case HIBERNATE_STATE_LOWBANDWIDTH: return "SOFT";
+ case HIBERNATE_STATE_DORMANT: return "HARD";
+ case HIBERNATE_STATE_INITIAL:
+ case HIBERNATE_STATE_LIVE:
+ return "AWAKE";
+ default:
+ log_warn(LD_BUG, "unknown hibernate state %d", state);
+ tor_snprintf(buf, sizeof(buf), "unknown [%d]", state);
+ return buf;
+ }
+}
/* ************
* Functions for bandwidth accounting.
@@ -412,11 +436,15 @@ configure_accounting(time_t now)
/** Return the relevant number of bytes sent/received this interval
* based on the set AccountingRule */
-static uint64_t
+uint64_t
get_accounting_bytes(void)
{
if (get_options()->AccountingRule == ACCT_SUM)
return n_bytes_read_in_interval+n_bytes_written_in_interval;
+ else if (get_options()->AccountingRule == ACCT_IN)
+ return n_bytes_read_in_interval;
+ else if (get_options()->AccountingRule == ACCT_OUT)
+ return n_bytes_written_in_interval;
else
return MAX(n_bytes_read_in_interval, n_bytes_written_in_interval);
}
@@ -490,7 +518,7 @@ reset_accounting(time_t now)
}
/** Return true iff we should save our bandwidth usage to disk. */
-static INLINE int
+static inline int
time_to_record_bandwidth_usage(time_t now)
{
/* Note every 600 sec */
@@ -931,6 +959,7 @@ consider_hibernation(time_t now)
{
int accounting_enabled = get_options()->AccountingMax != 0;
char buf[ISO_TIME_LEN+1];
+ hibernate_state_t prev_state = hibernate_state;
/* If we're in 'exiting' mode, then we just shut down after the interval
* elapses. */
@@ -986,6 +1015,10 @@ consider_hibernation(time_t now)
hibernate_end_time_elapsed(now);
}
}
+
+ /* Dispatch a controller event if the hibernation state changed. */
+ if (hibernate_state != prev_state)
+ on_hibernate_state_change(prev_state);
}
/** Helper function: called when we get a GETINFO request for an
@@ -1003,14 +1036,10 @@ getinfo_helper_accounting(control_connection_t *conn,
if (!strcmp(question, "accounting/enabled")) {
*answer = tor_strdup(accounting_is_enabled(get_options()) ? "1" : "0");
} else if (!strcmp(question, "accounting/hibernating")) {
- if (hibernate_state == HIBERNATE_STATE_DORMANT)
- *answer = tor_strdup("hard");
- else if (hibernate_state == HIBERNATE_STATE_LOWBANDWIDTH)
- *answer = tor_strdup("soft");
- else
- *answer = tor_strdup("awake");
+ *answer = tor_strdup(hibernate_state_to_string(hibernate_state));
+ tor_strlower(*answer);
} else if (!strcmp(question, "accounting/bytes")) {
- tor_asprintf(answer, U64_FORMAT" "U64_FORMAT,
+ tor_asprintf(answer, U64_FORMAT" "U64_FORMAT,
U64_PRINTF_ARG(n_bytes_read_in_interval),
U64_PRINTF_ARG(n_bytes_written_in_interval));
} else if (!strcmp(question, "accounting/bytes-left")) {
@@ -1022,6 +1051,18 @@ getinfo_helper_accounting(control_connection_t *conn,
total_left = limit - total_bytes;
tor_asprintf(answer, U64_FORMAT" "U64_FORMAT,
U64_PRINTF_ARG(total_left), U64_PRINTF_ARG(total_left));
+ } else if (get_options()->AccountingRule == ACCT_IN) {
+ uint64_t read_left = 0;
+ if (n_bytes_read_in_interval < limit)
+ read_left = limit - n_bytes_read_in_interval;
+ tor_asprintf(answer, U64_FORMAT" "U64_FORMAT,
+ U64_PRINTF_ARG(read_left), U64_PRINTF_ARG(limit));
+ } else if (get_options()->AccountingRule == ACCT_OUT) {
+ uint64_t write_left = 0;
+ if (n_bytes_written_in_interval < limit)
+ write_left = limit - n_bytes_written_in_interval;
+ tor_asprintf(answer, U64_FORMAT" "U64_FORMAT,
+ U64_PRINTF_ARG(limit), U64_PRINTF_ARG(write_left));
} else {
uint64_t read_left = 0, write_left = 0;
if (n_bytes_read_in_interval < limit)
@@ -1046,6 +1087,20 @@ getinfo_helper_accounting(control_connection_t *conn,
return 0;
}
+/**
+ * Helper function: called when the hibernation state changes, and sends a
+ * SERVER_STATUS event to notify interested controllers of the accounting
+ * state change.
+ */
+static void
+on_hibernate_state_change(hibernate_state_t prev_state)
+{
+ (void)prev_state; /* Should we do something with this? */
+ control_event_server_status(LOG_NOTICE,
+ "HIBERNATION_STATUS STATUS=%s",
+ hibernate_state_to_string(hibernate_state));
+}
+
#ifdef TOR_UNIT_TESTS
/**
* Manually change the hibernation state. Private; used only by the unit
diff --git a/src/or/hibernate.h b/src/or/hibernate.h
index b9e619c5ad..fa9da6de39 100644
--- a/src/or/hibernate.h
+++ b/src/or/hibernate.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -19,6 +19,7 @@ MOCK_DECL(int, accounting_is_enabled, (const or_options_t *options));
int accounting_get_interval_length(void);
MOCK_DECL(time_t, accounting_get_end_time, (void));
void configure_accounting(time_t now);
+uint64_t get_accounting_bytes(void);
void accounting_run_housekeeping(time_t now);
void accounting_add_bytes(size_t n_read, size_t n_written, int seconds);
int accounting_record_bandwidth_usage(time_t now, or_state_t *state);
diff --git a/src/or/include.am b/src/or/include.am
index 959b26dcf9..712ae18406 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -63,6 +63,7 @@ LIBTOR_A_SOURCES = \
src/or/onion_fast.c \
src/or/onion_tap.c \
src/or/transports.c \
+ src/or/periodic.c \
src/or/policies.c \
src/or/reasons.c \
src/or/relay.c \
@@ -92,7 +93,8 @@ src_or_libtor_testing_a_SOURCES = $(LIBTOR_A_SOURCES)
src_or_tor_SOURCES = src/or/tor_main.c
AM_CPPFLAGS += -I$(srcdir)/src/or -Isrc/or
-src/or/tor_main.o: micro-revision.i
+src/or/tor_main.$(OBJEXT) \
+ src/or/src_or_tor_cov-tor_main.$(OBJEXT): micro-revision.i
AM_CPPFLAGS += -DSHARE_DATADIR="\"$(datadir)\"" \
-DLOCALSTATEDIR="\"$(localstatedir)\"" \
@@ -108,7 +110,7 @@ src_or_libtor_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
src_or_tor_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@
src_or_tor_LDADD = src/or/libtor.a src/common/libor.a \
- src/common/libor-crypto.a $(LIBDONNA) \
+ src/common/libor-crypto.a $(LIBKECCAK_TINY) $(LIBDONNA) \
src/common/libor-event.a src/trunnel/libor-trunnel.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
@TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@
@@ -119,13 +121,10 @@ src_or_tor_cov_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_or_tor_cov_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
src_or_tor_cov_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@
src_or_tor_cov_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \
- src/common/libor-crypto-testing.a $(LIBDONNA) \
+ src/common/libor-crypto-testing.a $(LIBKECCAK_TINY) $(LIBDONNA) \
src/common/libor-event-testing.a src/trunnel/libor-trunnel-testing.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
@TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@
-export TESTING_TOR_BINARY = $(top_builddir)/src/or/tor-cov
-else
-export TESTING_TOR_BINARY = $(top_builddir)/src/or/tor
endif
ORHEADERS = \
@@ -153,9 +152,11 @@ ORHEADERS = \
src/or/dirserv.h \
src/or/dirvote.h \
src/or/dns.h \
+ src/or/dns_structs.h \
src/or/dnsserv.h \
src/or/eventdns_tor.h \
src/or/ext_orport.h \
+ src/or/fallback_dirs.inc \
src/or/fp_pair.h \
src/or/geoip.h \
src/or/entrynodes.h \
@@ -172,6 +173,7 @@ ORHEADERS = \
src/or/onion_tap.h \
src/or/or.h \
src/or/transports.h \
+ src/or/periodic.h \
src/or/policies.h \
src/or/reasons.h \
src/or/relay.h \
diff --git a/src/or/keypin.c b/src/or/keypin.c
index ebe83b35d2..1f82eccf86 100644
--- a/src/or/keypin.c
+++ b/src/or/keypin.c
@@ -1,6 +1,13 @@
-/* Copyright (c) 2014, The Tor Project, Inc. */
+/* Copyright (c) 2014-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+/**
+ * \file keypin.c
+ *
+ * \brief Functions and structures for associating routers' RSA key
+ * fingerprints with their ED25519 keys.
+ */
+
#define KEYPIN_PRIVATE
#include "orconfig.h"
@@ -48,21 +55,23 @@ static int keypin_journal_append_entry(const uint8_t *rsa_id_digest,
const uint8_t *ed25519_id_key);
static int keypin_check_and_add_impl(const uint8_t *rsa_id_digest,
const uint8_t *ed25519_id_key,
- int do_not_add);
+ const int do_not_add,
+ const int replace);
+static int keypin_add_or_replace_entry_in_map(keypin_ent_t *ent);
static HT_HEAD(rsamap, keypin_ent_st) the_rsa_map = HT_INITIALIZER();
static HT_HEAD(edmap, keypin_ent_st) the_ed_map = HT_INITIALIZER();
/** Hashtable helper: compare two keypin table entries and return true iff
* they have the same RSA key IDs. */
-static INLINE int
+static inline int
keypin_ents_eq_rsa(const keypin_ent_t *a, const keypin_ent_t *b)
{
return tor_memeq(a->rsa_id, b->rsa_id, sizeof(a->rsa_id));
}
/** Hashtable helper: hash a keypin table entries based on its RSA key ID */
-static INLINE unsigned
+static inline unsigned
keypin_ent_hash_rsa(const keypin_ent_t *a)
{
return (unsigned) siphash24g(a->rsa_id, sizeof(a->rsa_id));
@@ -70,14 +79,14 @@ return (unsigned) siphash24g(a->rsa_id, sizeof(a->rsa_id));
/** Hashtable helper: compare two keypin table entries and return true iff
* they have the same ed25519 keys */
-static INLINE int
+static inline int
keypin_ents_eq_ed(const keypin_ent_t *a, const keypin_ent_t *b)
{
return tor_memeq(a->ed25519_key, b->ed25519_key, sizeof(a->ed25519_key));
}
/** Hashtable helper: hash a keypin table entries based on its ed25519 key */
-static INLINE unsigned
+static inline unsigned
keypin_ent_hash_ed(const keypin_ent_t *a)
{
return (unsigned) siphash24g(a->ed25519_key, sizeof(a->ed25519_key));
@@ -100,12 +109,17 @@ HT_GENERATE2(edmap, keypin_ent_st, edmap_node, keypin_ent_hash_ed,
* return KEYPIN_FOUND. If we find an entry that matches one key but
* not the other, return KEYPIN_MISMATCH. If we have no entry for either
* key, add such an entry to the table and return KEYPIN_ADDED.
+ *
+ * If <b>replace_existing_entry</b> is true, then any time we would have said
+ * KEYPIN_FOUND, we instead add this entry anyway and return KEYPIN_ADDED.
*/
int
keypin_check_and_add(const uint8_t *rsa_id_digest,
- const uint8_t *ed25519_id_key)
+ const uint8_t *ed25519_id_key,
+ const int replace_existing_entry)
{
- return keypin_check_and_add_impl(rsa_id_digest, ed25519_id_key, 0);
+ return keypin_check_and_add_impl(rsa_id_digest, ed25519_id_key, 0,
+ replace_existing_entry);
}
/**
@@ -116,7 +130,7 @@ int
keypin_check(const uint8_t *rsa_id_digest,
const uint8_t *ed25519_id_key)
{
- return keypin_check_and_add_impl(rsa_id_digest, ed25519_id_key, 1);
+ return keypin_check_and_add_impl(rsa_id_digest, ed25519_id_key, 1, 0);
}
/**
@@ -125,7 +139,8 @@ keypin_check(const uint8_t *rsa_id_digest,
static int
keypin_check_and_add_impl(const uint8_t *rsa_id_digest,
const uint8_t *ed25519_id_key,
- int do_not_add)
+ const int do_not_add,
+ const int replace)
{
keypin_ent_t search, *ent;
memset(&search, 0, sizeof(search));
@@ -139,18 +154,21 @@ keypin_check_and_add_impl(const uint8_t *rsa_id_digest,
if (tor_memeq(ent->ed25519_key, ed25519_id_key,sizeof(ent->ed25519_key))) {
return KEYPIN_FOUND; /* Match on both keys. Great. */
} else {
- return KEYPIN_MISMATCH; /* Found RSA with different Ed key */
+ if (!replace)
+ return KEYPIN_MISMATCH; /* Found RSA with different Ed key */
}
}
/* See if we know a different RSA key for this ed key */
- ent = HT_FIND(edmap, &the_ed_map, &search);
- if (ent) {
- /* If we got here, then the ed key matches and the RSA doesn't */
- tor_assert(fast_memeq(ent->ed25519_key, ed25519_id_key,
- sizeof(ent->ed25519_key)));
- tor_assert(fast_memneq(ent->rsa_id, rsa_id_digest, sizeof(ent->rsa_id)));
- return KEYPIN_MISMATCH;
+ if (! replace) {
+ ent = HT_FIND(edmap, &the_ed_map, &search);
+ if (ent) {
+ /* If we got here, then the ed key matches and the RSA doesn't */
+ tor_assert(fast_memeq(ent->ed25519_key, ed25519_id_key,
+ sizeof(ent->ed25519_key)));
+ tor_assert(fast_memneq(ent->rsa_id, rsa_id_digest, sizeof(ent->rsa_id)));
+ return KEYPIN_MISMATCH;
+ }
}
/* Okay, this one is new to us. */
@@ -158,7 +176,12 @@ keypin_check_and_add_impl(const uint8_t *rsa_id_digest,
return KEYPIN_NOT_FOUND;
ent = tor_memdup(&search, sizeof(search));
- keypin_add_entry_to_map(ent);
+ int r = keypin_add_or_replace_entry_in_map(ent);
+ if (! replace) {
+ tor_assert(r == 1);
+ } else {
+ tor_assert(r != 0);
+ }
keypin_journal_append_entry(rsa_id_digest, ed25519_id_key);
return KEYPIN_ADDED;
}
@@ -174,6 +197,57 @@ keypin_add_entry_to_map, (keypin_ent_t *ent))
}
/**
+ * Helper: add 'ent' to the maps, replacing any entries that contradict it.
+ * Take ownership of 'ent', freeing it if needed.
+ *
+ * Return 0 if the entry was a duplicate, -1 if there was a conflict,
+ * and 1 if there was no conflict.
+ */
+static int
+keypin_add_or_replace_entry_in_map(keypin_ent_t *ent)
+{
+ int r = 1;
+ keypin_ent_t *ent2 = HT_FIND(rsamap, &the_rsa_map, ent);
+ keypin_ent_t *ent3 = HT_FIND(edmap, &the_ed_map, ent);
+ if (ent2 &&
+ fast_memeq(ent2->ed25519_key, ent->ed25519_key, DIGEST256_LEN)) {
+ /* We already have this mapping stored. Ignore it. */
+ tor_free(ent);
+ return 0;
+ } else if (ent2 || ent3) {
+ /* We have a conflict. (If we had no entry, we would have ent2 == ent3
+ * == NULL. If we had a non-conflicting duplicate, we would have found
+ * it above.)
+ *
+ * We respond by having this entry (ent) supersede all entries that it
+ * contradicts (ent2 and/or ent3). In other words, if we receive
+ * <rsa,ed>, we remove all <rsa,ed'> and all <rsa',ed>, for rsa'!=rsa
+ * and ed'!= ed.
+ */
+ const keypin_ent_t *t;
+ if (ent2) {
+ t = HT_REMOVE(rsamap, &the_rsa_map, ent2);
+ tor_assert(ent2 == t);
+ t = HT_REMOVE(edmap, &the_ed_map, ent2);
+ tor_assert(ent2 == t);
+ }
+ if (ent3 && ent2 != ent3) {
+ t = HT_REMOVE(rsamap, &the_rsa_map, ent3);
+ tor_assert(ent3 == t);
+ t = HT_REMOVE(edmap, &the_ed_map, ent3);
+ tor_assert(ent3 == t);
+ tor_free(ent3);
+ }
+ tor_free(ent2);
+ r = -1;
+ /* Fall through */
+ }
+
+ keypin_add_entry_to_map(ent);
+ return r;
+}
+
+/**
* Check whether we already have an entry in the key pinning table for a
* router with RSA ID digest <b>rsa_id_digest</b>. If we have no such entry,
* return KEYPIN_NOT_FOUND. If we find an entry that matches the RSA key but
@@ -321,22 +395,13 @@ keypin_load_journal_impl(const char *data, size_t size)
continue;
}
- const keypin_ent_t *ent2;
- if ((ent2 = HT_FIND(rsamap, &the_rsa_map, ent))) {
- if (fast_memeq(ent2->ed25519_key, ent->ed25519_key, DIGEST256_LEN)) {
- ++n_duplicates;
- } else {
- ++n_conflicts;
- }
- tor_free(ent);
- continue;
- } else if (HT_FIND(edmap, &the_ed_map, ent)) {
- tor_free(ent);
+ const int r = keypin_add_or_replace_entry_in_map(ent);
+ if (r == 0) {
+ ++n_duplicates;
+ } else if (r == -1) {
++n_conflicts;
- continue;
}
- keypin_add_entry_to_map(ent);
++n_entries;
}
diff --git a/src/or/keypin.h b/src/or/keypin.h
index 2a5b3f1786..673f24d9e3 100644
--- a/src/or/keypin.h
+++ b/src/or/keypin.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014, The Tor Project, Inc. */
+/* Copyright (c) 2014-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_KEYPIN_H
@@ -7,7 +7,8 @@
#include "testsupport.h"
int keypin_check_and_add(const uint8_t *rsa_id_digest,
- const uint8_t *ed25519_id_key);
+ const uint8_t *ed25519_id_key,
+ const int replace_existing_entry);
int keypin_check(const uint8_t *rsa_id_digest,
const uint8_t *ed25519_id_key);
diff --git a/src/or/main.c b/src/or/main.c
index 0b0207b975..fba9799a60 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -44,6 +44,7 @@
#include "nodelist.h"
#include "ntmain.h"
#include "onion.h"
+#include "periodic.h"
#include "policies.h"
#include "transports.h"
#include "relay.h"
@@ -190,32 +191,6 @@ int quiet_level = 0;
*
****************************************************************************/
-#if 0 && defined(USE_BUFFEREVENTS)
-static void
-free_old_inbuf(connection_t *conn)
-{
- if (! conn->inbuf)
- return;
-
- tor_assert(conn->outbuf);
- tor_assert(buf_datalen(conn->inbuf) == 0);
- tor_assert(buf_datalen(conn->outbuf) == 0);
- buf_free(conn->inbuf);
- buf_free(conn->outbuf);
- conn->inbuf = conn->outbuf = NULL;
-
- if (conn->read_event) {
- event_del(conn->read_event);
- tor_event_free(conn->read_event);
- }
- if (conn->write_event) {
- event_del(conn->read_event);
- tor_event_free(conn->write_event);
- }
- conn->read_event = conn->write_event = NULL;
-}
-#endif
-
#if defined(_WIN32) && defined(USE_BUFFEREVENTS)
/** Remove the kernel-space send and receive buffers for <b>s</b>. For use
* with IOCP only. */
@@ -224,11 +199,13 @@ 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))) {
+ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&zero,
+ (socklen_t)sizeof(zero))) {
log_warn(LD_NET, "Unable to clear SO_SNDBUF");
r = -1;
}
- if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&zero, sizeof(zero))) {
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&zero,
+ (socklen_t)sizeof(zero))) {
log_warn(LD_NET, "Unable to clear SO_RCVBUF");
r = -1;
}
@@ -499,8 +476,7 @@ connection_in_array(connection_t *conn)
return smartlist_contains(connection_array, conn);
}
-/** Set <b>*array</b> to an array of all connections, and <b>*n</b>
- * to the length of the array. <b>*array</b> and <b>*n</b> must not
+/** Set <b>*array</b> to an array of all connections. <b>*array</b> must not
* be modified.
*/
smartlist_t *
@@ -567,6 +543,46 @@ connection_is_reading(connection_t *conn)
(conn->read_event && event_pending(conn->read_event, EV_READ, NULL));
}
+/** Check whether <b>conn</b> is correct in having (or not having) a
+ * read/write event (passed in <b>ev</b>). On success, return 0. On failure,
+ * log a warning and return -1. */
+static int
+connection_check_event(connection_t *conn, struct event *ev)
+{
+ int bad;
+
+ if (conn->type == CONN_TYPE_AP && TO_EDGE_CONN(conn)->is_dns_request) {
+ /* DNS requests which we launch through the dnsserv.c module do not have
+ * any underlying socket or any underlying linked connection, so they
+ * shouldn't have any attached events either.
+ */
+ bad = ev != NULL;
+ } else {
+ /* Everytyhing else should have an underlying socket, or a linked
+ * connection (which is also tracked with a read_event/write_event pair).
+ */
+ bad = ev == NULL;
+ }
+
+ if (bad) {
+ log_warn(LD_BUG, "Event missing on connection %p [%s;%s]. "
+ "socket=%d. linked=%d. "
+ "is_dns_request=%d. Marked_for_close=%s:%d",
+ conn,
+ conn_type_to_string(conn->type),
+ conn_state_to_string(conn->type, conn->state),
+ (int)conn->s, (int)conn->linked,
+ (conn->type == CONN_TYPE_AP &&
+ TO_EDGE_CONN(conn)->is_dns_request),
+ conn->marked_for_close_file ? conn->marked_for_close_file : "-",
+ conn->marked_for_close
+ );
+ log_backtrace(LOG_WARN, LD_BUG, "Backtrace attached.");
+ return -1;
+ }
+ return 0;
+}
+
/** Tell the main loop to stop notifying <b>conn</b> of any read events. */
MOCK_IMPL(void,
connection_stop_reading,(connection_t *conn))
@@ -578,7 +594,9 @@ connection_stop_reading,(connection_t *conn))
return;
});
- tor_assert(conn->read_event);
+ if (connection_check_event(conn, conn->read_event) < 0) {
+ return;
+ }
if (conn->linked) {
conn->reading_from_linked_conn = 0;
@@ -603,7 +621,9 @@ connection_start_reading,(connection_t *conn))
return;
});
- tor_assert(conn->read_event);
+ if (connection_check_event(conn, conn->read_event) < 0) {
+ return;
+ }
if (conn->linked) {
conn->reading_from_linked_conn = 1;
@@ -643,7 +663,9 @@ connection_stop_writing,(connection_t *conn))
return;
});
- tor_assert(conn->write_event);
+ if (connection_check_event(conn, conn->write_event) < 0) {
+ return;
+ }
if (conn->linked) {
conn->writing_to_linked_conn = 0;
@@ -669,7 +691,9 @@ connection_start_writing,(connection_t *conn))
return;
});
- tor_assert(conn->write_event);
+ if (connection_check_event(conn, conn->write_event) < 0) {
+ return;
+ }
if (conn->linked) {
conn->writing_to_linked_conn = 1;
@@ -944,18 +968,6 @@ conn_close_if_marked(int i)
* would make much more sense to react in
* connection_handle_read_impl, or to just stop reading in
* mark_and_flush */
-#if 0
-#define MARKED_READING_RATE 180
- static ratelim_t marked_read_lim = RATELIM_INIT(MARKED_READING_RATE);
- char *m;
- if ((m = rate_limit_log(&marked_read_lim, now))) {
- log_warn(LD_BUG, "Marked connection (fd %d, type %s, state %s) "
- "is still reading; that shouldn't happen.%s",
- (int)conn->s, conn_type_to_string(conn->type),
- conn_state_to_string(conn->type, conn->state), m);
- tor_free(m);
- }
-#endif
conn->read_blocked_on_bw = 1;
connection_stop_reading(conn);
}
@@ -1038,12 +1050,12 @@ directory_all_unreachable(time_t now)
/** This function is called whenever we successfully pull down some new
* network statuses or server descriptors. */
void
-directory_info_has_arrived(time_t now, int from_cache)
+directory_info_has_arrived(time_t now, int from_cache, int suppress_logs)
{
const or_options_t *options = get_options();
if (!router_have_minimum_dir_info()) {
- int quiet = from_cache ||
+ int quiet = suppress_logs || from_cache ||
directory_too_idle_to_fetch_descriptors(options, now);
tor_log(quiet ? LOG_INFO : LOG_NOTICE, LD_DIR,
"I learned some more directory information, but not enough to "
@@ -1227,39 +1239,85 @@ get_signewnym_epoch(void)
return newnym_epoch;
}
-typedef struct {
- time_t last_rotated_x509_certificate;
- time_t check_v3_certificate;
- time_t check_listeners;
- time_t download_networkstatus;
- time_t try_getting_descriptors;
- time_t reset_descriptor_failures;
- time_t add_entropy;
- time_t write_bridge_status_file;
- time_t downrate_stability;
- time_t save_stability;
- time_t clean_caches;
- time_t recheck_bandwidth;
- time_t check_for_expired_networkstatus;
- time_t write_stats_files;
- time_t write_bridge_stats;
- time_t check_port_forwarding;
- time_t launch_reachability_tests;
- time_t retry_dns_init;
- time_t next_heartbeat;
- time_t check_descriptor;
- /** When do we next launch DNS wildcarding checks? */
- time_t check_for_correct_dns;
- /** When do we next make sure our Ed25519 keys aren't about to expire? */
- time_t check_ed_keys;
-
-} time_to_t;
-
-static time_to_t time_to = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+/** True iff we have initialized all the members of <b>periodic_events</b>.
+ * Used to prevent double-initialization. */
+static int periodic_events_initialized = 0;
+
+/* Declare all the timer callback functions... */
+#undef CALLBACK
+#define CALLBACK(name) \
+ static int name ## _callback(time_t, const or_options_t *)
+CALLBACK(rotate_onion_key);
+CALLBACK(check_ed_keys);
+CALLBACK(launch_descriptor_fetches);
+CALLBACK(reset_descriptor_failures);
+CALLBACK(rotate_x509_certificate);
+CALLBACK(add_entropy);
+CALLBACK(launch_reachability_tests);
+CALLBACK(downrate_stability);
+CALLBACK(save_stability);
+CALLBACK(check_authority_cert);
+CALLBACK(check_expired_networkstatus);
+CALLBACK(write_stats_file);
+CALLBACK(record_bridge_stats);
+CALLBACK(clean_caches);
+CALLBACK(rend_cache_failure_clean);
+CALLBACK(retry_dns);
+CALLBACK(check_descriptor);
+CALLBACK(check_for_reachability_bw);
+CALLBACK(fetch_networkstatus);
+CALLBACK(retry_listeners);
+CALLBACK(expire_old_ciruits_serverside);
+CALLBACK(check_dns_honesty);
+CALLBACK(write_bridge_ns);
+CALLBACK(check_fw_helper_app);
+CALLBACK(heartbeat);
+
+#undef CALLBACK
+
+/* Now we declare an array of periodic_event_item_t for each periodic event */
+#define CALLBACK(name) PERIODIC_EVENT(name)
+
+static periodic_event_item_t periodic_events[] = {
+ CALLBACK(rotate_onion_key),
+ CALLBACK(check_ed_keys),
+ CALLBACK(launch_descriptor_fetches),
+ CALLBACK(reset_descriptor_failures),
+ CALLBACK(rotate_x509_certificate),
+ CALLBACK(add_entropy),
+ CALLBACK(launch_reachability_tests),
+ CALLBACK(downrate_stability),
+ CALLBACK(save_stability),
+ CALLBACK(check_authority_cert),
+ CALLBACK(check_expired_networkstatus),
+ CALLBACK(write_stats_file),
+ CALLBACK(record_bridge_stats),
+ CALLBACK(clean_caches),
+ CALLBACK(rend_cache_failure_clean),
+ CALLBACK(retry_dns),
+ CALLBACK(check_descriptor),
+ CALLBACK(check_for_reachability_bw),
+ CALLBACK(fetch_networkstatus),
+ CALLBACK(retry_listeners),
+ CALLBACK(expire_old_ciruits_serverside),
+ CALLBACK(check_dns_honesty),
+ CALLBACK(write_bridge_ns),
+ CALLBACK(check_fw_helper_app),
+ CALLBACK(heartbeat),
+ END_OF_PERIODIC_EVENTS
};
-
-/** Reset all the time_to's so we'll do all our actions again as if we
+#undef CALLBACK
+
+/* These are pointers to members of periodic_events[] that are used to
+ * implement particular callbacks. We keep them separate here so that we
+ * can access them by name. We also keep them inside periodic_events[]
+ * so that we can implement "reset all timers" in a reasonable way. */
+static periodic_event_item_t *check_descriptor_event=NULL;
+static periodic_event_item_t *fetch_networkstatus_event=NULL;
+static periodic_event_item_t *launch_descriptor_fetches_event=NULL;
+static periodic_event_item_t *check_dns_honesty_event=NULL;
+
+/** Reset all the periodic events so we'll do all our actions again as if we
* just started up.
* Useful if our clock just moved back a long time from the future,
* so we don't wait until that future arrives again before acting.
@@ -1267,7 +1325,77 @@ static time_to_t time_to = {
void
reset_all_main_loop_timers(void)
{
- memset(&time_to, 0, sizeof(time_to));
+ int i;
+ for (i = 0; periodic_events[i].name; ++i) {
+ periodic_event_reschedule(&periodic_events[i]);
+ }
+}
+
+/** Return the member of periodic_events[] whose name is <b>name</b>.
+ * Return NULL if no such event is found.
+ */
+static periodic_event_item_t *
+find_periodic_event(const char *name)
+{
+ int i;
+ for (i = 0; periodic_events[i].name; ++i) {
+ if (strcmp(name, periodic_events[i].name) == 0)
+ return &periodic_events[i];
+ }
+ return NULL;
+}
+
+/** Helper, run one second after setup:
+ * Initializes all members of periodic_events and starts them running.
+ *
+ * (We do this one second after setup for backward-compatibility reasons;
+ * it might not actually be necessary.) */
+static void
+initialize_periodic_events_cb(evutil_socket_t fd, short events, void *data)
+{
+ (void) fd;
+ (void) events;
+ (void) data;
+ int i;
+ for (i = 0; periodic_events[i].name; ++i) {
+ periodic_event_launch(&periodic_events[i]);
+ }
+}
+
+/** Set up all the members of periodic_events[], and configure them all to be
+ * launched from a callback. */
+STATIC void
+initialize_periodic_events(void)
+{
+ tor_assert(periodic_events_initialized == 0);
+ periodic_events_initialized = 1;
+
+ int i;
+ for (i = 0; periodic_events[i].name; ++i) {
+ periodic_event_setup(&periodic_events[i]);
+ }
+
+#define NAMED_CALLBACK(name) \
+ STMT_BEGIN name ## _event = find_periodic_event( #name ); STMT_END
+
+ NAMED_CALLBACK(check_descriptor);
+ NAMED_CALLBACK(fetch_networkstatus);
+ NAMED_CALLBACK(launch_descriptor_fetches);
+ NAMED_CALLBACK(check_dns_honesty);
+
+ struct timeval one_second = { 1, 0 };
+ event_base_once(tor_libevent_get_base(), -1, EV_TIMEOUT,
+ initialize_periodic_events_cb, NULL,
+ &one_second);
+}
+
+STATIC void
+teardown_periodic_events(void)
+{
+ int i;
+ for (i = 0; periodic_events[i].name; ++i) {
+ periodic_event_destroy(&periodic_events[i]);
+ }
}
/**
@@ -1278,7 +1406,8 @@ reset_all_main_loop_timers(void)
void
reschedule_descriptor_update_check(void)
{
- time_to.check_descriptor = 0;
+ tor_assert(check_descriptor_event);
+ periodic_event_reschedule(check_descriptor_event);
}
/**
@@ -1288,8 +1417,34 @@ reschedule_descriptor_update_check(void)
void
reschedule_directory_downloads(void)
{
- time_to.download_networkstatus = 0;
- time_to.try_getting_descriptors = 0;
+ tor_assert(fetch_networkstatus_event);
+ tor_assert(launch_descriptor_fetches_event);
+
+ periodic_event_reschedule(fetch_networkstatus_event);
+ periodic_event_reschedule(launch_descriptor_fetches_event);
+}
+
+#define LONGEST_TIMER_PERIOD (30 * 86400)
+/** Helper: Return the number of seconds between <b>now</b> and <b>next</b>,
+ * clipped to the range [1 second, LONGEST_TIMER_PERIOD]. */
+static inline int
+safe_timer_diff(time_t now, time_t next)
+{
+ if (next > now) {
+ /* There were no computers at signed TIME_MIN (1902 on 32-bit systems),
+ * and nothing that could run Tor. It's a bug if 'next' is around then.
+ * On 64-bit systems with signed TIME_MIN, TIME_MIN is before the Big
+ * Bang. We cannot extrapolate past a singularity, but there was probably
+ * nothing that could run Tor then, either.
+ **/
+ tor_assert(next > TIME_MIN + LONGEST_TIMER_PERIOD);
+
+ if (next - LONGEST_TIMER_PERIOD > now)
+ return LONGEST_TIMER_PERIOD;
+ return (int)(next - now);
+ } else {
+ return 1;
+ }
}
/** Perform regular maintenance tasks. This function gets run once per
@@ -1298,13 +1453,8 @@ reschedule_directory_downloads(void)
static void
run_scheduled_events(time_t now)
{
- static int should_init_bridge_stats = 1;
const or_options_t *options = get_options();
- int is_server = server_mode(options);
- int i;
- int have_dir_info;
-
/* 0. See if we've been asked to shut down and our timeout has
* expired; or if our bandwidth limits are exhausted and we
* should hibernate; or if it's time to wake up from hibernation.
@@ -1322,12 +1472,109 @@ run_scheduled_events(time_t now)
/* 0c. If we've deferred log messages for the controller, handle them now */
flush_pending_log_callbacks();
+ if (options->UseBridges && !options->DisableNetwork) {
+ fetch_bridge_descriptors(options, now);
+ }
+
+ if (accounting_is_enabled(options)) {
+ accounting_run_housekeeping(now);
+ }
+
+ if (authdir_mode_v3(options)) {
+ dirvote_act(options, now);
+ }
+
+ /* 2d. Cleanup excess consensus bootstrap connections every second.
+ * connection_dir_close_consensus_conn_if_extra() closes some connections
+ * that are clearly excess, but this check is more thorough.
+ * This only closes connections if there is more than one consensus
+ * connection, and at least one of those connections is already downloading
+ * (during bootstrap), or connecting (just after the bootstrap consensus is
+ * downloaded).
+ * It won't close any consensus connections initiated after bootstrap,
+ * because those attempts are made one at a time. */
+ connection_dir_close_extra_consensus_conns();
+
+ /* 3a. Every second, we examine pending circuits and prune the
+ * ones which have been pending for more than a few seconds.
+ * We do this before step 4, so it can try building more if
+ * it's not comfortable with the number of available circuits.
+ */
+ /* (If our circuit build timeout can ever become lower than a second (which
+ * it can't, currently), we should do this more often.) */
+ circuit_expire_building();
+
+ /* 3b. Also look at pending streams and prune the ones that 'began'
+ * a long time ago but haven't gotten a 'connected' yet.
+ * Do this before step 4, so we can put them back into pending
+ * state to be picked up by the new circuit.
+ */
+ connection_ap_expire_beginning();
+
+ /* 3c. And expire connections that we've held open for too long.
+ */
+ connection_expire_held_open();
+
+ /* 4. Every second, we try a new circuit if there are no valid
+ * circuits. Every NewCircuitPeriod seconds, we expire circuits
+ * that became dirty more than MaxCircuitDirtiness seconds ago,
+ * and we make a new circ if there are no clean circuits.
+ */
+ const int have_dir_info = router_have_minimum_dir_info();
+ if (have_dir_info && !net_is_disabled()) {
+ circuit_build_needed_circs(now);
+ } else {
+ circuit_expire_old_circs_as_needed(now);
+ }
+
+ /* 5. We do housekeeping for each connection... */
+ connection_or_set_bad_connections(NULL, 0);
+ int i;
+ for (i=0;i<smartlist_len(connection_array);i++) {
+ run_connection_housekeeping(i, now);
+ }
+
+ /* 6. And remove any marked circuits... */
+ circuit_close_all_marked();
+
+ /* 7. And upload service descriptors if necessary. */
+ if (have_completed_a_circuit() && !net_is_disabled()) {
+ rend_consider_services_upload(now);
+ rend_consider_descriptor_republication();
+ }
+
+ /* 8. and blow away any connections that need to die. have to do this now,
+ * because if we marked a conn for close and left its socket -1, then
+ * we'll pass it to poll/select and bad things will happen.
+ */
+ close_closeable_connections();
+
+ /* 8b. And if anything in our state is ready to get flushed to disk, we
+ * flush it. */
+ or_state_save(now);
+
+ /* 8c. Do channel cleanup just like for connections */
+ channel_run_cleanup();
+ channel_listener_run_cleanup();
+
+ /* 11b. check pending unconfigured managed proxies */
+ if (!net_is_disabled() && pt_proxies_configuration_pending())
+ pt_configure_remaining_proxies();
+}
+
+static int
+rotate_onion_key_callback(time_t now, const or_options_t *options)
+{
/* 1a. Every MIN_ONION_KEY_LIFETIME seconds, rotate the onion keys,
* shut down and restart all cpuworkers, and update the directory if
* necessary.
*/
- if (is_server &&
- get_onion_key_set_at()+MIN_ONION_KEY_LIFETIME < now) {
+ if (server_mode(options)) {
+ time_t rotation_time = get_onion_key_set_at()+MIN_ONION_KEY_LIFETIME;
+ if (rotation_time > now) {
+ return safe_timer_diff(now, rotation_time);
+ }
+
log_info(LD_GENERAL,"Rotating onion key.");
rotate_onion_key();
cpuworkers_rotate_keyinfo();
@@ -1336,9 +1583,15 @@ run_scheduled_events(time_t now)
}
if (advertised_server_mode() && !options->DisableNetwork)
router_upload_dir_desc_to_dirservers(0);
+ return MIN_ONION_KEY_LIFETIME;
}
+ return PERIODIC_EVENT_NO_UPDATE;
+}
- if (is_server && time_to.check_ed_keys < now) {
+static int
+check_ed_keys_callback(time_t now, const or_options_t *options)
+{
+ if (server_mode(options)) {
if (should_make_new_ed_keys(options, now)) {
if (load_ed_keys(options, now) < 0 ||
generate_ed_link_cert(options, now)) {
@@ -1347,199 +1600,255 @@ run_scheduled_events(time_t now)
exit(0);
}
}
- time_to.check_ed_keys = now + 30;
+ return 30;
}
+ return PERIODIC_EVENT_NO_UPDATE;
+}
- if (!should_delay_dir_fetches(options, NULL) &&
- time_to.try_getting_descriptors < now) {
- update_all_descriptor_downloads(now);
- update_extrainfo_downloads(now);
- if (router_have_minimum_dir_info())
- time_to.try_getting_descriptors = now + LAZY_DESCRIPTOR_RETRY_INTERVAL;
- else
- time_to.try_getting_descriptors = now + GREEDY_DESCRIPTOR_RETRY_INTERVAL;
- }
+static int
+launch_descriptor_fetches_callback(time_t now, const or_options_t *options)
+{
+ if (should_delay_dir_fetches(options, NULL))
+ return PERIODIC_EVENT_NO_UPDATE;
- if (time_to.reset_descriptor_failures < now) {
- router_reset_descriptor_download_failures();
- time_to.reset_descriptor_failures =
- now + DESCRIPTOR_FAILURE_RESET_INTERVAL;
- }
+ update_all_descriptor_downloads(now);
+ update_extrainfo_downloads(now);
+ if (router_have_minimum_dir_info())
+ return LAZY_DESCRIPTOR_RETRY_INTERVAL;
+ else
+ return GREEDY_DESCRIPTOR_RETRY_INTERVAL;
+}
- if (options->UseBridges && !options->DisableNetwork)
- fetch_bridge_descriptors(options, now);
+static int
+reset_descriptor_failures_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
+ (void)options;
+ router_reset_descriptor_download_failures();
+ return DESCRIPTOR_FAILURE_RESET_INTERVAL;
+}
+
+static int
+rotate_x509_certificate_callback(time_t now, const or_options_t *options)
+{
+ static int first = 1;
+ (void)now;
+ (void)options;
+ if (first) {
+ first = 0;
+ return MAX_SSL_KEY_LIFETIME_INTERNAL;
+ }
/* 1b. Every MAX_SSL_KEY_LIFETIME_INTERNAL seconds, we change our
* TLS context. */
- if (!time_to.last_rotated_x509_certificate)
- time_to.last_rotated_x509_certificate = now;
- if (time_to.last_rotated_x509_certificate +
- MAX_SSL_KEY_LIFETIME_INTERNAL < now) {
- log_info(LD_GENERAL,"Rotating tls context.");
- if (router_initialize_tls_context() < 0) {
- log_warn(LD_BUG, "Error reinitializing TLS context");
- /* XXX is it a bug here, that we just keep going? -RD */
- }
- time_to.last_rotated_x509_certificate = now;
- /* We also make sure to rotate the TLS connections themselves if they've
- * been up for too long -- but that's done via is_bad_for_new_circs in
- * connection_run_housekeeping() above. */
+ log_info(LD_GENERAL,"Rotating tls context.");
+ if (router_initialize_tls_context() < 0) {
+ log_err(LD_BUG, "Error reinitializing TLS context");
+ tor_assert_unreached();
}
- if (time_to.add_entropy < now) {
- if (time_to.add_entropy) {
- /* We already seeded once, so don't die on failure. */
- crypto_seed_rng();
- }
-/** How often do we add more entropy to OpenSSL's RNG pool? */
-#define ENTROPY_INTERVAL (60*60)
- time_to.add_entropy = now + ENTROPY_INTERVAL;
+ /* We also make sure to rotate the TLS connections themselves if they've
+ * been up for too long -- but that's done via is_bad_for_new_circs in
+ * run_connection_housekeeping() above. */
+ return MAX_SSL_KEY_LIFETIME_INTERNAL;
+}
+
+static int
+add_entropy_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
+ (void)options;
+ /* We already seeded once, so don't die on failure. */
+ if (crypto_seed_rng() < 0) {
+ log_warn(LD_GENERAL, "Tried to re-seed RNG, but failed. We already "
+ "seeded once, though, so we won't exit here.");
}
- /* 1c. If we have to change the accounting interval or record
- * bandwidth used in this accounting interval, do so. */
- if (accounting_is_enabled(options))
- accounting_run_housekeeping(now);
+ /** How often do we add more entropy to OpenSSL's RNG pool? */
+#define ENTROPY_INTERVAL (60*60)
+ return ENTROPY_INTERVAL;
+}
- if (time_to.launch_reachability_tests < now &&
- (authdir_mode_tests_reachability(options)) &&
- !net_is_disabled()) {
- time_to.launch_reachability_tests = now + REACHABILITY_TEST_INTERVAL;
+static int
+launch_reachability_tests_callback(time_t now, const or_options_t *options)
+{
+ if (authdir_mode_tests_reachability(options) &&
+ !net_is_disabled()) {
/* try to determine reachability of the other Tor relays */
dirserv_test_reachability(now);
}
+ return REACHABILITY_TEST_INTERVAL;
+}
+static int
+downrate_stability_callback(time_t now, const or_options_t *options)
+{
+ (void)options;
/* 1d. Periodically, we discount older stability information so that new
* stability info counts more, and save the stability information to disk as
* appropriate. */
- if (time_to.downrate_stability < now)
- time_to.downrate_stability = rep_hist_downrate_old_runs(now);
+ time_t next = rep_hist_downrate_old_runs(now);
+ return safe_timer_diff(now, next);
+}
+
+static int
+save_stability_callback(time_t now, const or_options_t *options)
+{
if (authdir_mode_tests_reachability(options)) {
- if (time_to.save_stability < now) {
- if (time_to.save_stability && rep_hist_record_mtbf_data(now, 1)<0) {
- log_warn(LD_GENERAL, "Couldn't store mtbf data.");
- }
-#define SAVE_STABILITY_INTERVAL (30*60)
- time_to.save_stability = now + SAVE_STABILITY_INTERVAL;
+ if (rep_hist_record_mtbf_data(now, 1)<0) {
+ log_warn(LD_GENERAL, "Couldn't store mtbf data.");
}
}
+#define SAVE_STABILITY_INTERVAL (30*60)
+ return SAVE_STABILITY_INTERVAL;
+}
+static int
+check_authority_cert_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
+ (void)options;
/* 1e. Periodically, if we're a v3 authority, we check whether our cert is
* close to expiring and warn the admin if it is. */
- if (time_to.check_v3_certificate < now) {
- v3_authority_check_key_expiry();
+ v3_authority_check_key_expiry();
#define CHECK_V3_CERTIFICATE_INTERVAL (5*60)
- time_to.check_v3_certificate = now + CHECK_V3_CERTIFICATE_INTERVAL;
- }
+ return CHECK_V3_CERTIFICATE_INTERVAL;
+}
+static int
+check_expired_networkstatus_callback(time_t now, const or_options_t *options)
+{
+ (void)options;
/* 1f. Check whether our networkstatus has expired.
*/
- if (time_to.check_for_expired_networkstatus < now) {
- networkstatus_t *ns = networkstatus_get_latest_consensus();
- /*XXXX RD: This value needs to be the same as REASONABLY_LIVE_TIME in
- * networkstatus_get_reasonably_live_consensus(), but that value is way
- * way too high. Arma: is the bridge issue there resolved yet? -NM */
+ networkstatus_t *ns = networkstatus_get_latest_consensus();
+ /*XXXX RD: This value needs to be the same as REASONABLY_LIVE_TIME in
+ * networkstatus_get_reasonably_live_consensus(), but that value is way
+ * way too high. Arma: is the bridge issue there resolved yet? -NM */
#define NS_EXPIRY_SLOP (24*60*60)
- if (ns && ns->valid_until < now+NS_EXPIRY_SLOP &&
- router_have_minimum_dir_info()) {
- router_dir_info_changed();
- }
-#define CHECK_EXPIRED_NS_INTERVAL (2*60)
- time_to.check_for_expired_networkstatus = now + CHECK_EXPIRED_NS_INTERVAL;
+ if (ns && ns->valid_until < now+NS_EXPIRY_SLOP &&
+ router_have_minimum_dir_info()) {
+ router_dir_info_changed();
}
+#define CHECK_EXPIRED_NS_INTERVAL (2*60)
+ return CHECK_EXPIRED_NS_INTERVAL;
+}
+static int
+write_stats_file_callback(time_t now, const or_options_t *options)
+{
/* 1g. Check whether we should write statistics to disk.
*/
- if (time_to.write_stats_files < now) {
#define CHECK_WRITE_STATS_INTERVAL (60*60)
- time_t next_time_to_write_stats_files = (time_to.write_stats_files > 0 ?
- time_to.write_stats_files : now) + CHECK_WRITE_STATS_INTERVAL;
- if (options->CellStatistics) {
- time_t next_write =
- rep_hist_buffer_stats_write(time_to.write_stats_files);
- if (next_write && next_write < next_time_to_write_stats_files)
- next_time_to_write_stats_files = next_write;
- }
- if (options->DirReqStatistics) {
- time_t next_write = geoip_dirreq_stats_write(time_to.write_stats_files);
- if (next_write && next_write < next_time_to_write_stats_files)
- next_time_to_write_stats_files = next_write;
- }
- if (options->EntryStatistics) {
- time_t next_write = geoip_entry_stats_write(time_to.write_stats_files);
- if (next_write && next_write < next_time_to_write_stats_files)
- next_time_to_write_stats_files = next_write;
- }
- if (options->HiddenServiceStatistics) {
- time_t next_write = rep_hist_hs_stats_write(time_to.write_stats_files);
- if (next_write && next_write < next_time_to_write_stats_files)
- next_time_to_write_stats_files = next_write;
- }
- if (options->ExitPortStatistics) {
- time_t next_write = rep_hist_exit_stats_write(time_to.write_stats_files);
- if (next_write && next_write < next_time_to_write_stats_files)
- next_time_to_write_stats_files = next_write;
- }
- if (options->ConnDirectionStatistics) {
- time_t next_write = rep_hist_conn_stats_write(time_to.write_stats_files);
- if (next_write && next_write < next_time_to_write_stats_files)
- next_time_to_write_stats_files = next_write;
- }
- if (options->BridgeAuthoritativeDir) {
- time_t next_write = rep_hist_desc_stats_write(time_to.write_stats_files);
- if (next_write && next_write < next_time_to_write_stats_files)
- next_time_to_write_stats_files = next_write;
- }
- time_to.write_stats_files = next_time_to_write_stats_files;
+ time_t next_time_to_write_stats_files = now + CHECK_WRITE_STATS_INTERVAL;
+ if (options->CellStatistics) {
+ time_t next_write =
+ rep_hist_buffer_stats_write(now);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
+ }
+ if (options->DirReqStatistics) {
+ time_t next_write = geoip_dirreq_stats_write(now);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
+ }
+ if (options->EntryStatistics) {
+ time_t next_write = geoip_entry_stats_write(now);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
+ }
+ if (options->HiddenServiceStatistics) {
+ time_t next_write = rep_hist_hs_stats_write(now);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
+ }
+ if (options->ExitPortStatistics) {
+ time_t next_write = rep_hist_exit_stats_write(now);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
+ }
+ if (options->ConnDirectionStatistics) {
+ time_t next_write = rep_hist_conn_stats_write(now);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
}
+ if (options->BridgeAuthoritativeDir) {
+ time_t next_write = rep_hist_desc_stats_write(now);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
+ }
+
+ return safe_timer_diff(now, next_time_to_write_stats_files);
+}
+
+static int
+record_bridge_stats_callback(time_t now, const or_options_t *options)
+{
+ static int should_init_bridge_stats = 1;
/* 1h. Check whether we should write bridge statistics to disk.
*/
if (should_record_bridge_info(options)) {
- if (time_to.write_bridge_stats < now) {
- if (should_init_bridge_stats) {
- /* (Re-)initialize bridge statistics. */
+ if (should_init_bridge_stats) {
+ /* (Re-)initialize bridge statistics. */
geoip_bridge_stats_init(now);
- time_to.write_bridge_stats = now + WRITE_STATS_INTERVAL;
should_init_bridge_stats = 0;
- } else {
- /* Possibly write bridge statistics to disk and ask when to write
- * them next time. */
- time_to.write_bridge_stats = geoip_bridge_stats_write(
- time_to.write_bridge_stats);
- }
+ return WRITE_STATS_INTERVAL;
+ } else {
+ /* Possibly write bridge statistics to disk and ask when to write
+ * them next time. */
+ time_t next = geoip_bridge_stats_write(now);
+ return safe_timer_diff(now, next);
}
} else if (!should_init_bridge_stats) {
/* Bridge mode was turned off. Ensure that stats are re-initialized
* next time bridge mode is turned on. */
should_init_bridge_stats = 1;
}
+ return PERIODIC_EVENT_NO_UPDATE;
+}
+static int
+clean_caches_callback(time_t now, const or_options_t *options)
+{
/* Remove old information from rephist and the rend cache. */
- if (time_to.clean_caches < now) {
- rep_history_clean(now - options->RephistTrackTime);
- rend_cache_clean(now);
- rend_cache_clean_v2_descs_as_dir(now, 0);
- microdesc_cache_rebuild(NULL, 0);
+ rep_history_clean(now - options->RephistTrackTime);
+ rend_cache_clean(now, REND_CACHE_TYPE_CLIENT);
+ rend_cache_clean(now, REND_CACHE_TYPE_SERVICE);
+ rend_cache_clean_v2_descs_as_dir(now, 0);
+ microdesc_cache_rebuild(NULL, 0);
#define CLEAN_CACHES_INTERVAL (30*60)
- time_to.clean_caches = now + CLEAN_CACHES_INTERVAL;
- }
+ return CLEAN_CACHES_INTERVAL;
+}
+
+static int
+rend_cache_failure_clean_callback(time_t now, const or_options_t *options)
+{
+ (void)options;
/* We don't keep entries that are more than five minutes old so we try to
* clean it as soon as we can since we want to make sure the client waits
* as little as possible for reachability reasons. */
rend_cache_failure_clean(now);
+ return 30;
+}
+static int
+retry_dns_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
#define RETRY_DNS_INTERVAL (10*60)
/* If we're a server and initializing dns failed, retry periodically. */
- if (time_to.retry_dns_init < now) {
- time_to.retry_dns_init = now + RETRY_DNS_INTERVAL;
- if (is_server && has_dns_init_failed())
- dns_init();
- }
+ if (server_mode(options) && has_dns_init_failed())
+ dns_init();
+ return RETRY_DNS_INTERVAL;
+}
/* 2. Periodically, we consider force-uploading our descriptor
* (if we've passed our internal checks). */
+static int
+check_descriptor_callback(time_t now, const or_options_t *options)
+{
/** How often do we check whether part of our router info has changed in a
* way that would require an upload? That includes checking whether our IP
* address has changed. */
@@ -1547,185 +1856,178 @@ run_scheduled_events(time_t now)
/* 2b. Once per minute, regenerate and upload the descriptor if the old
* one is inaccurate. */
- if (time_to.check_descriptor < now && !options->DisableNetwork) {
- static int dirport_reachability_count = 0;
- time_to.check_descriptor = now + CHECK_DESCRIPTOR_INTERVAL;
+ if (!options->DisableNetwork) {
check_descriptor_bandwidth_changed(now);
check_descriptor_ipaddress_changed(now);
mark_my_descriptor_dirty_if_too_old(now);
consider_publishable_server(0);
- /* also, check religiously for reachability, if it's within the first
- * 20 minutes of our uptime. */
- if (is_server &&
- (have_completed_a_circuit() || !any_predicted_circuits(now)) &&
- !we_are_hibernating()) {
- if (stats_n_seconds_working < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) {
- consider_testing_reachability(1, dirport_reachability_count==0);
- if (++dirport_reachability_count > 5)
- dirport_reachability_count = 0;
- } else if (time_to.recheck_bandwidth < now) {
- /* If we haven't checked for 12 hours and our bandwidth estimate is
- * low, do another bandwidth test. This is especially important for
- * bridges, since they might go long periods without much use. */
- const routerinfo_t *me = router_get_my_routerinfo();
- if (time_to.recheck_bandwidth && me &&
- me->bandwidthcapacity < me->bandwidthrate &&
- me->bandwidthcapacity < 51200) {
- reset_bandwidth_test();
- }
-#define BANDWIDTH_RECHECK_INTERVAL (12*60*60)
- time_to.recheck_bandwidth = now + BANDWIDTH_RECHECK_INTERVAL;
- }
- }
-
/* If any networkstatus documents are no longer recent, we need to
* update all the descriptors' running status. */
/* Remove dead routers. */
+ /* XXXX This doesn't belong here, but it was here in the pre-
+ * XXXX refactoring code. */
routerlist_remove_old_routers();
}
- /* 2c. Every minute (or every second if TestingTorNetwork), check
- * whether we want to download any networkstatus documents. */
+ return CHECK_DESCRIPTOR_INTERVAL;
+}
-/* How often do we check whether we should download network status
- * documents? */
-#define networkstatus_dl_check_interval(o) ((o)->TestingTorNetwork ? 1 : 60)
+static int
+check_for_reachability_bw_callback(time_t now, const or_options_t *options)
+{
+ /* XXXX This whole thing was stuck in the middle of what is now
+ * XXXX check_descriptor_callback. I'm not sure it's right. */
- if (!should_delay_dir_fetches(options, NULL) &&
- time_to.download_networkstatus < now) {
- time_to.download_networkstatus =
- now + networkstatus_dl_check_interval(options);
- update_networkstatus_downloads(now);
+ static int dirport_reachability_count = 0;
+ /* also, check religiously for reachability, if it's within the first
+ * 20 minutes of our uptime. */
+ if (server_mode(options) &&
+ (have_completed_a_circuit() || !any_predicted_circuits(now)) &&
+ !we_are_hibernating()) {
+ if (stats_n_seconds_working < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) {
+ consider_testing_reachability(1, dirport_reachability_count==0);
+ if (++dirport_reachability_count > 5)
+ dirport_reachability_count = 0;
+ return 1;
+ } else {
+ /* If we haven't checked for 12 hours and our bandwidth estimate is
+ * low, do another bandwidth test. This is especially important for
+ * bridges, since they might go long periods without much use. */
+ const routerinfo_t *me = router_get_my_routerinfo();
+ static int first_time = 1;
+ if (!first_time && me &&
+ me->bandwidthcapacity < me->bandwidthrate &&
+ me->bandwidthcapacity < 51200) {
+ reset_bandwidth_test();
+ }
+ first_time = 0;
+#define BANDWIDTH_RECHECK_INTERVAL (12*60*60)
+ return BANDWIDTH_RECHECK_INTERVAL;
+ }
}
+ return CHECK_DESCRIPTOR_INTERVAL;
+}
- /* 2c. Let directory voting happen. */
- if (authdir_mode_v3(options))
- dirvote_act(options, now);
+static int
+fetch_networkstatus_callback(time_t now, const or_options_t *options)
+{
+ /* 2c. Every minute (or every second if TestingTorNetwork, or during
+ * client bootstrap), check whether we want to download any networkstatus
+ * documents. */
- /* 3a. Every second, we examine pending circuits and prune the
- * ones which have been pending for more than a few seconds.
- * We do this before step 4, so it can try building more if
- * it's not comfortable with the number of available circuits.
- */
- /* (If our circuit build timeout can ever become lower than a second (which
- * it can't, currently), we should do this more often.) */
- circuit_expire_building();
+ /* How often do we check whether we should download network status
+ * documents? */
+ const int we_are_bootstrapping = networkstatus_consensus_is_bootstrapping(
+ now);
+ const int prefer_mirrors = !directory_fetches_from_authorities(
+ get_options());
+ int networkstatus_dl_check_interval = 60;
+ /* check more often when testing, or when bootstrapping from mirrors
+ * (connection limits prevent too many connections being made) */
+ if (options->TestingTorNetwork
+ || (we_are_bootstrapping && prefer_mirrors)) {
+ networkstatus_dl_check_interval = 1;
+ }
- /* 3b. Also look at pending streams and prune the ones that 'began'
- * a long time ago but haven't gotten a 'connected' yet.
- * Do this before step 4, so we can put them back into pending
- * state to be picked up by the new circuit.
- */
- connection_ap_expire_beginning();
+ if (should_delay_dir_fetches(options, NULL))
+ return PERIODIC_EVENT_NO_UPDATE;
- /* 3c. And expire connections that we've held open for too long.
- */
- connection_expire_held_open();
+ update_networkstatus_downloads(now);
+ return networkstatus_dl_check_interval;
+}
+static int
+retry_listeners_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
+ (void)options;
/* 3d. And every 60 seconds, we relaunch listeners if any died. */
- if (!net_is_disabled() && time_to.check_listeners < now) {
+ if (!net_is_disabled()) {
retry_all_listeners(NULL, NULL, 0);
- time_to.check_listeners = now+60;
+ return 60;
}
+ return PERIODIC_EVENT_NO_UPDATE;
+}
- /* 4. Every second, we try a new circuit if there are no valid
- * circuits. Every NewCircuitPeriod seconds, we expire circuits
- * that became dirty more than MaxCircuitDirtiness seconds ago,
- * and we make a new circ if there are no clean circuits.
- */
- have_dir_info = router_have_minimum_dir_info();
- if (have_dir_info && !net_is_disabled()) {
- circuit_build_needed_circs(now);
- } else {
- circuit_expire_old_circs_as_needed(now);
- }
-
- /* every 10 seconds, but not at the same second as other such events */
- if (now % 10 == 5)
- circuit_expire_old_circuits_serverside(now);
-
- /* 5. We do housekeeping for each connection... */
- connection_or_set_bad_connections(NULL, 0);
- for (i=0;i<smartlist_len(connection_array);i++) {
- run_connection_housekeeping(i, now);
- }
-
- /* 6. And remove any marked circuits... */
- circuit_close_all_marked();
-
- /* 7. And upload service descriptors if necessary. */
- if (have_completed_a_circuit() && !net_is_disabled()) {
- rend_consider_services_upload(now);
- rend_consider_descriptor_republication();
- }
-
- /* 8. and blow away any connections that need to die. have to do this now,
- * because if we marked a conn for close and left its socket -1, then
- * we'll pass it to poll/select and bad things will happen.
- */
- close_closeable_connections();
-
- /* 8b. And if anything in our state is ready to get flushed to disk, we
- * flush it. */
- or_state_save(now);
-
- /* 8c. Do channel cleanup just like for connections */
- channel_run_cleanup();
- channel_listener_run_cleanup();
+static int
+expire_old_ciruits_serverside_callback(time_t now, const or_options_t *options)
+{
+ (void)options;
+ /* every 11 seconds, so not usually the same second as other such events */
+ circuit_expire_old_circuits_serverside(now);
+ return 11;
+}
+static int
+check_dns_honesty_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
/* 9. and if we're an exit node, check whether our DNS is telling stories
* to us. */
- if (!net_is_disabled() &&
- public_server_mode(options) &&
- time_to.check_for_correct_dns < now &&
- ! router_my_exit_policy_is_reject_star()) {
- if (!time_to.check_for_correct_dns) {
- time_to.check_for_correct_dns =
- crypto_rand_time_range(now + 60, now + 180);
- } else {
- dns_launch_correctness_checks();
- time_to.check_for_correct_dns = now + 12*3600 +
- crypto_rand_int(12*3600);
- }
+ if (net_is_disabled() ||
+ ! public_server_mode(options) ||
+ router_my_exit_policy_is_reject_star())
+ return PERIODIC_EVENT_NO_UPDATE;
+
+ static int first_time = 1;
+ if (first_time) {
+ /* Don't launch right when we start */
+ first_time = 0;
+ return crypto_rand_int_range(60, 180);
}
+ dns_launch_correctness_checks();
+ return 12*3600 + crypto_rand_int(12*3600);
+}
+
+static int
+write_bridge_ns_callback(time_t now, const or_options_t *options)
+{
/* 10. write bridge networkstatus file to disk */
- if (options->BridgeAuthoritativeDir &&
- time_to.write_bridge_status_file < now) {
+ if (options->BridgeAuthoritativeDir) {
networkstatus_dump_bridge_status_to_file(now);
#define BRIDGE_STATUSFILE_INTERVAL (30*60)
- time_to.write_bridge_status_file = now+BRIDGE_STATUSFILE_INTERVAL;
+ return BRIDGE_STATUSFILE_INTERVAL;
}
+ return PERIODIC_EVENT_NO_UPDATE;
+}
+static int
+check_fw_helper_app_callback(time_t now, const or_options_t *options)
+{
+ if (net_is_disabled() ||
+ ! server_mode(options) ||
+ ! options->PortForwarding) {
+ return PERIODIC_EVENT_NO_UPDATE;
+ }
/* 11. check the port forwarding app */
- if (!net_is_disabled() &&
- time_to.check_port_forwarding < now &&
- options->PortForwarding &&
- is_server) {
+
#define PORT_FORWARDING_CHECK_INTERVAL 5
- smartlist_t *ports_to_forward = get_list_of_ports_to_forward();
- if (ports_to_forward) {
- tor_check_port_forwarding(options->PortForwardingHelper,
- ports_to_forward,
- now);
-
- SMARTLIST_FOREACH(ports_to_forward, char *, cp, tor_free(cp));
- smartlist_free(ports_to_forward);
- }
- time_to.check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL;
- }
+ smartlist_t *ports_to_forward = get_list_of_ports_to_forward();
+ if (ports_to_forward) {
+ tor_check_port_forwarding(options->PortForwardingHelper,
+ ports_to_forward,
+ now);
- /* 11b. check pending unconfigured managed proxies */
- if (!net_is_disabled() && pt_proxies_configuration_pending())
- pt_configure_remaining_proxies();
+ SMARTLIST_FOREACH(ports_to_forward, char *, cp, tor_free(cp));
+ smartlist_free(ports_to_forward);
+ }
+ return PORT_FORWARDING_CHECK_INTERVAL;
+}
+static int
+heartbeat_callback(time_t now, const or_options_t *options)
+{
+ static int first = 1;
/* 12. write the heartbeat message */
- if (options->HeartbeatPeriod &&
- time_to.next_heartbeat <= now) {
- if (time_to.next_heartbeat) /* don't log the first heartbeat */
- log_heartbeat(now);
- time_to.next_heartbeat = now+options->HeartbeatPeriod;
+ if (first) {
+ first = 0; /* Skip the first one. */
+ } else {
+ log_heartbeat(now);
}
+ /* XXXX This isn't such a good way to handle possible changes in the
+ * callback event */
+ return options->HeartbeatPeriod;
}
/** Timer: used to invoke second_elapsed_callback() once per second. */
@@ -1795,8 +2097,9 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg)
if (me && !check_whether_orport_reachable()) {
char *address = tor_dup_ip(me->addr);
log_warn(LD_CONFIG,"Your server (%s:%d) has not managed to confirm that "
- "its ORPort is reachable. Please check your firewalls, ports, "
- "address, /etc/hosts file, etc.",
+ "its ORPort is reachable. Relays do not publish descriptors "
+ "until their ORPort and DirPort are reachable. Please check "
+ "your firewalls, ports, address, /etc/hosts file, etc.",
address, me->or_port);
control_event_server_status(LOG_WARN,
"REACHABILITY_FAILED ORADDRESS=%s:%d",
@@ -1808,8 +2111,9 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg)
char *address = tor_dup_ip(me->addr);
log_warn(LD_CONFIG,
"Your server (%s:%d) has not managed to confirm that its "
- "DirPort is reachable. Please check your firewalls, ports, "
- "address, /etc/hosts file, etc.",
+ "DirPort is reachable. Relays do not publish descriptors "
+ "until their ORPort and DirPort are reachable. Please check "
+ "your firewalls, ports, address, /etc/hosts file, etc.",
address, me->dir_port);
control_event_server_status(LOG_WARN,
"REACHABILITY_FAILED DIRADDRESS=%s:%d",
@@ -1919,7 +2223,10 @@ got_libevent_error(void)
void
ip_address_changed(int at_interface)
{
- int server = server_mode(get_options());
+ const or_options_t *options = get_options();
+ int server = server_mode(options);
+ int exit_reject_private = (server && options->ExitRelay
+ && options->ExitPolicyRejectPrivate);
if (at_interface) {
if (! server) {
@@ -1933,10 +2240,15 @@ ip_address_changed(int at_interface)
reset_bandwidth_test();
stats_n_seconds_working = 0;
router_reset_reachability();
- mark_my_descriptor_dirty("IP address changed");
}
}
+ /* Exit relays incorporate interface addresses in their exit policies when
+ * ExitPolicyRejectPrivate is set */
+ if (exit_reject_private || (server && !at_interface)) {
+ mark_my_descriptor_dirty("IP address changed");
+ }
+
dns_servers_relaunch_checks();
}
@@ -1947,7 +2259,10 @@ dns_servers_relaunch_checks(void)
{
if (server_mode(get_options())) {
dns_reset_correctness_checks();
- time_to.check_for_correct_dns = 0;
+ if (periodic_events_initialized) {
+ tor_assert(check_dns_honesty_event);
+ periodic_event_reschedule(check_dns_honesty_event);
+ }
}
}
@@ -2019,6 +2334,14 @@ do_hup(void)
* force a retry there. */
if (server_mode(options)) {
+ /* Maybe we've been given a new ed25519 key or certificate?
+ */
+ time_t now = approx_time();
+ if (load_ed_keys(options, now) < 0 ||
+ generate_ed_link_cert(options, now)) {
+ log_warn(LD_OR, "Problem reloading Ed25519 keys; still using old keys.");
+ }
+
/* Update cpuworker and dnsworker processes, so they get up-to-date
* configuration options. */
cpuworkers_rotate_keyinfo();
@@ -2033,6 +2356,13 @@ do_main_loop(void)
{
time_t now;
+ /* initialize the periodic events first, so that code that depends on the
+ * events being present does not assert.
+ */
+ if (! periodic_events_initialized) {
+ initialize_periodic_events();
+ }
+
/* initialize dns resolve map, spawn workers if needed */
if (dns_init() < 0) {
if (get_options()->ServerDNSAllowBrokenConfig)
@@ -2115,7 +2445,7 @@ do_main_loop(void)
* appropriate.)
*/
now = time(NULL);
- directory_info_has_arrived(now, 1);
+ directory_info_has_arrived(now, 1, 0);
if (server_mode(get_options())) {
/* launch cpuworkers. Need to do this *after* we've read the onion key. */
@@ -2233,9 +2563,7 @@ run_main_loop_once(void)
return -1;
#endif
} else {
- if (ERRNO_IS_EINPROGRESS(e))
- log_warn(LD_BUG,
- "libevent call returned EINPROGRESS? Please report.");
+ tor_assert_nonfatal_once(! ERRNO_IS_EINPROGRESS(e));
log_debug(LD_NET,"libevent call interrupted.");
/* You can't trust the results of this poll(). Go back to the
* top of the big for loop. */
@@ -2243,6 +2571,11 @@ run_main_loop_once(void)
}
}
+ /* This will be pretty fast if nothing new is pending. Note that this gets
+ * called once per libevent loop, which will make it happen once per group
+ * of events that fire, or once per second. */
+ connection_ap_attach_pending(0);
+
return 1;
}
@@ -2817,8 +3150,8 @@ tor_free_all(int postfork)
channel_tls_free_all();
channel_free_all();
connection_free_all();
+ connection_edge_free_all();
scheduler_free_all();
- memarea_clear_freelist();
nodelist_free_all();
microdesc_free_all();
ext_orport_free_all();
@@ -2843,6 +3176,7 @@ tor_free_all(int postfork)
smartlist_free(closeable_connection_lst);
smartlist_free(active_linked_connection_lst);
periodic_timer_free(second_timer);
+ teardown_periodic_events();
#ifndef USE_BUFFEREVENTS
periodic_timer_free(refill_timer);
#endif
@@ -2909,6 +3243,7 @@ do_list_fingerprint(void)
char buf[FINGERPRINT_LEN+1];
crypto_pk_t *k;
const char *nickname = get_options()->Nickname;
+ sandbox_disable_getaddrinfo_cache();
if (!server_mode(get_options())) {
log_err(LD_GENERAL,
"Clients don't have long-term identity keys. Exiting.");
@@ -2982,6 +3317,13 @@ do_dump_config(void)
static void
init_addrinfo(void)
{
+ if (! server_mode(get_options()) ||
+ (get_options()->Address && strlen(get_options()->Address) > 0)) {
+ /* We don't need to seed our own hostname, because we won't be calling
+ * resolve_my_address on it.
+ */
+ return;
+ }
char hname[256];
// host name to sandbox
@@ -3018,6 +3360,8 @@ sandbox_init_filter(void)
OPEN_DATADIR2(name, name2 suffix); \
} while (0)
+ OPEN(options->DataDirectory);
+ OPEN_DATADIR("keys");
OPEN_DATADIR_SUFFIX("cached-certs", ".tmp");
OPEN_DATADIR_SUFFIX("cached-consensus", ".tmp");
OPEN_DATADIR_SUFFIX("unverified-consensus", ".tmp");
@@ -3034,12 +3378,19 @@ sandbox_init_filter(void)
OPEN_DATADIR_SUFFIX("state", ".tmp");
OPEN_DATADIR_SUFFIX("unparseable-desc", ".tmp");
OPEN_DATADIR_SUFFIX("v3-status-votes", ".tmp");
+ OPEN_DATADIR("key-pinning-journal");
OPEN("/dev/srandom");
OPEN("/dev/urandom");
OPEN("/dev/random");
OPEN("/etc/hosts");
OPEN("/proc/meminfo");
+ if (options->BridgeAuthoritativeDir)
+ OPEN_DATADIR_SUFFIX("networkstatus-bridges", ".tmp");
+
+ if (authdir_mode_handles_descs(options, -1))
+ OPEN_DATADIR("approved-routers");
+
if (options->ServerDNSResolvConfFile)
sandbox_cfg_allow_open_filename(&cfg,
tor_strdup(options->ServerDNSResolvConfFile));
@@ -3080,6 +3431,9 @@ sandbox_init_filter(void)
RENAME_SUFFIX("unparseable-desc", ".tmp");
RENAME_SUFFIX("v3-status-votes", ".tmp");
+ if (options->BridgeAuthoritativeDir)
+ RENAME_SUFFIX("networkstatus-bridges", ".tmp");
+
#define STAT_DATADIR(name) \
sandbox_cfg_allow_stat_filename(&cfg, get_datadir_fname(name))
@@ -3133,6 +3487,20 @@ sandbox_init_filter(void)
}
}
+ SMARTLIST_FOREACH_BEGIN(get_configured_ports(), port_cfg_t *, port) {
+ if (!port->is_unix_addr)
+ continue;
+ /* When we open an AF_UNIX address, we want permission to open the
+ * directory that holds it. */
+ char *dirname = tor_strdup(port->unix_addr);
+ if (get_parent_directory(dirname) == 0) {
+ OPEN(dirname);
+ }
+ tor_free(dirname);
+ sandbox_cfg_allow_chmod_filename(&cfg, tor_strdup(port->unix_addr));
+ sandbox_cfg_allow_chown_filename(&cfg, tor_strdup(port->unix_addr));
+ } SMARTLIST_FOREACH_END(port);
+
if (options->DirPortFrontPage) {
sandbox_cfg_allow_open_filename(&cfg,
tor_strdup(options->DirPortFrontPage));
@@ -3148,6 +3516,16 @@ sandbox_init_filter(void)
OPEN_DATADIR2("keys", "secret_onion_key.old");
OPEN_DATADIR2("keys", "secret_onion_key_ntor.old");
+ OPEN_DATADIR2_SUFFIX("keys", "ed25519_master_id_secret_key", ".tmp");
+ OPEN_DATADIR2_SUFFIX("keys", "ed25519_master_id_secret_key_encrypted",
+ ".tmp");
+ OPEN_DATADIR2_SUFFIX("keys", "ed25519_master_id_public_key", ".tmp");
+ OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_secret_key", ".tmp");
+ OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_secret_key_encrypted",
+ ".tmp");
+ OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_public_key", ".tmp");
+ OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_cert", ".tmp");
+
OPEN_DATADIR2_SUFFIX("stats", "bridge-stats", ".tmp");
OPEN_DATADIR2_SUFFIX("stats", "dirreq-stats", ".tmp");
@@ -3175,9 +3553,16 @@ sandbox_init_filter(void)
RENAME_SUFFIX2("stats", "exit-stats", ".tmp");
RENAME_SUFFIX2("stats", "buffer-stats", ".tmp");
RENAME_SUFFIX2("stats", "conn-stats", ".tmp");
+ RENAME_SUFFIX2("stats", "hidserv-stats", ".tmp");
RENAME_SUFFIX("hashed-fingerprint", ".tmp");
RENAME_SUFFIX("router-stability", ".tmp");
+ RENAME_SUFFIX2("keys", "ed25519_master_id_secret_key", ".tmp");
+ RENAME_SUFFIX2("keys", "ed25519_master_id_secret_key_encrypted", ".tmp");
+ RENAME_SUFFIX2("keys", "ed25519_master_id_public_key", ".tmp");
+ RENAME_SUFFIX2("keys", "ed25519_signing_secret_key", ".tmp");
+ RENAME_SUFFIX2("keys", "ed25519_signing_cert", ".tmp");
+
sandbox_cfg_allow_rename(&cfg,
get_datadir_fname2("keys", "secret_onion_key"),
get_datadir_fname2("keys", "secret_onion_key.old"));
diff --git a/src/or/main.h b/src/or/main.h
index 447d3f4eca..ad865b8124 100644
--- a/src/or/main.h
+++ b/src/or/main.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -48,7 +48,7 @@ MOCK_DECL(void,connection_start_writing,(connection_t *conn));
void connection_stop_reading_from_linked_conn(connection_t *conn);
void directory_all_unreachable(time_t now);
-void directory_info_has_arrived(time_t now, int from_cache);
+void directory_info_has_arrived(time_t now, int from_cache, int suppress_logs);
void ip_address_changed(int at_interface);
void dns_servers_relaunch_checks(void);
@@ -78,6 +78,8 @@ int tor_init(int argc, char **argv);
#ifdef MAIN_PRIVATE
STATIC void init_connection_lists(void);
STATIC void close_closeable_connections(void);
+STATIC void initialize_periodic_events(void);
+STATIC void teardown_periodic_events(void);
#endif
#endif
diff --git a/src/or/microdesc.c b/src/or/microdesc.c
index a9bab3ddc6..299042995b 100644
--- a/src/or/microdesc.c
+++ b/src/or/microdesc.c
@@ -1,6 +1,13 @@
-/* Copyright (c) 2009-2015, The Tor Project, Inc. */
+/* Copyright (c) 2009-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+/**
+ * \file microdesc.c
+ *
+ * \brief Implements microdescriptors -- an abbreviated description of
+ * less-frequently-changing router information.
+ */
+
#include "or.h"
#include "circuitbuild.h"
#include "config.h"
@@ -47,14 +54,15 @@ struct microdesc_cache_t {
static microdesc_cache_t *get_microdesc_cache_noload(void);
/** Helper: computes a hash of <b>md</b> to place it in a hash table. */
-static INLINE unsigned int
+static inline unsigned int
microdesc_hash_(microdesc_t *md)
{
return (unsigned) siphash24g(md->digest, sizeof(md->digest));
}
-/** Helper: compares <b>a</b> and </b> for equality for hash-table purposes. */
-static INLINE int
+/** Helper: compares <b>a</b> and <b>b</b> for equality for hash-table
+ * purposes. */
+static inline int
microdesc_eq_(microdesc_t *a, microdesc_t *b)
{
return tor_memeq(a->digest, b->digest, DIGEST256_LEN);
@@ -840,7 +848,7 @@ microdesc_list_missing_digest256(networkstatus_t *ns, microdesc_cache_t *cache,
/** Launch download requests for microdescriptors as appropriate.
*
* Specifically, we should launch download requests if we are configured to
- * download mirodescriptors, and there are some microdescriptors listed the
+ * download mirodescriptors, and there are some microdescriptors listed in the
* current microdesc consensus that we don't have, and either we never asked
* for them, or we failed to download them but we're willing to retry.
*/
diff --git a/src/or/microdesc.h b/src/or/microdesc.h
index 08571e4bd5..0675e233d6 100644
--- a/src/or/microdesc.h
+++ b/src/or/microdesc.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index 71a2c0f121..51c985e85e 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -85,8 +85,30 @@ static time_t time_to_download_next_consensus[N_CONSENSUS_FLAVORS];
/** Download status for the current consensus networkstatus. */
static download_status_t consensus_dl_status[N_CONSENSUS_FLAVORS] =
{
- { 0, 0, DL_SCHED_CONSENSUS },
- { 0, 0, DL_SCHED_CONSENSUS },
+ { 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_ANY_DIRSERVER,
+ DL_SCHED_INCREMENT_FAILURE },
+ { 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_ANY_DIRSERVER,
+ DL_SCHED_INCREMENT_FAILURE },
+ };
+
+#define N_CONSENSUS_BOOTSTRAP_SCHEDULES 2
+#define CONSENSUS_BOOTSTRAP_SOURCE_AUTHORITY 0
+#define CONSENSUS_BOOTSTRAP_SOURCE_ANY_DIRSERVER 1
+
+/* Using DL_SCHED_INCREMENT_ATTEMPT on these schedules means that
+ * download_status_increment_failure won't increment these entries.
+ * However, any bootstrap connection failures that occur after we have
+ * a valid consensus will count against the failure counts on the non-bootstrap
+ * schedules. There should only be one of these, as all the others will have
+ * been cancelled. (This doesn't seem to be a significant issue.) */
+static download_status_t
+ consensus_bootstrap_dl_status[N_CONSENSUS_BOOTSTRAP_SCHEDULES] =
+ {
+ { 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_AUTHORITY,
+ DL_SCHED_INCREMENT_ATTEMPT },
+ /* During bootstrap, DL_WANT_ANY_DIRSERVER means "use fallbacks". */
+ { 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_ANY_DIRSERVER,
+ DL_SCHED_INCREMENT_ATTEMPT },
};
/** True iff we have logged a warning about this OR's version being older than
@@ -97,6 +119,10 @@ static int have_warned_about_old_version = 0;
static int have_warned_about_new_version = 0;
static void routerstatus_list_update_named_server_map(void);
+static void update_consensus_bootstrap_multiple_downloads(
+ time_t now,
+ const or_options_t *options,
+ int we_are_bootstrapping);
/** Forget that we've warned about anything networkstatus-related, so we will
* give fresh warnings if the same behavior happens again. */
@@ -122,6 +148,9 @@ networkstatus_reset_download_failures(void)
for (i=0; i < N_CONSENSUS_FLAVORS; ++i)
download_status_reset(&consensus_dl_status[i]);
+
+ for (i=0; i < N_CONSENSUS_BOOTSTRAP_SCHEDULES; ++i)
+ download_status_reset(&consensus_bootstrap_dl_status[i]);
}
/** Read every cached v3 consensus networkstatus from the disk. */
@@ -734,6 +763,55 @@ we_want_to_fetch_flavor(const or_options_t *options, int flavor)
* fetching certs before we check whether there is a better one? */
#define DELAY_WHILE_FETCHING_CERTS (20*60)
+/* Check if a downloaded consensus flavor should still wait for certificates
+ * to download now.
+ * If so, return 1. If not, fail dls and return 0. */
+static int
+check_consensus_waiting_for_certs(int flavor, time_t now,
+ download_status_t *dls)
+{
+ consensus_waiting_for_certs_t *waiting;
+
+ /* We should always have a known flavor, because we_want_to_fetch_flavor()
+ * filters out unknown flavors. */
+ tor_assert(flavor >= 0 && flavor < N_CONSENSUS_FLAVORS);
+
+ waiting = &consensus_waiting_for_certs[flavor];
+ if (waiting->consensus) {
+ /* XXXX make sure this doesn't delay sane downloads. */
+ if (waiting->set_at + DELAY_WHILE_FETCHING_CERTS > now) {
+ return 1;
+ } else {
+ if (!waiting->dl_failed) {
+ download_status_failed(dls, 0);
+ waiting->dl_failed=1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Return the maximum download tries for a consensus, based on options and
+ * whether we_are_bootstrapping. */
+static int
+consensus_max_download_tries(const or_options_t *options,
+ int we_are_bootstrapping)
+{
+ int use_fallbacks = networkstatus_consensus_can_use_extra_fallbacks(options);
+
+ if (we_are_bootstrapping) {
+ if (use_fallbacks) {
+ return options->ClientBootstrapConsensusMaxDownloadTries;
+ } else {
+ return
+ options->ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries;
+ }
+ }
+
+ return options->TestingConsensusMaxDownloadTries;
+}
+
/** If we want to download a fresh consensus, launch a new download as
* appropriate. */
static void
@@ -741,12 +819,19 @@ update_consensus_networkstatus_downloads(time_t now)
{
int i;
const or_options_t *options = get_options();
+ const int we_are_bootstrapping = networkstatus_consensus_is_bootstrapping(
+ now);
+ const int use_multi_conn =
+ networkstatus_consensus_can_use_multiple_directories(options);
+
+ if (should_delay_dir_fetches(options, NULL))
+ return;
for (i=0; i < N_CONSENSUS_FLAVORS; ++i) {
/* XXXX need some way to download unknown flavors if we are caching. */
const char *resource;
- consensus_waiting_for_certs_t *waiting;
networkstatus_t *c;
+ int max_in_progress_conns = 1;
if (! we_want_to_fetch_flavor(options, i))
continue;
@@ -762,35 +847,167 @@ update_consensus_networkstatus_downloads(time_t now)
resource = networkstatus_get_flavor_name(i);
- /* Let's make sure we remembered to update consensus_dl_status */
- tor_assert(consensus_dl_status[i].schedule == DL_SCHED_CONSENSUS);
+ /* Check if we already have enough connections in progress */
+ if (we_are_bootstrapping) {
+ max_in_progress_conns =
+ options->ClientBootstrapConsensusMaxInProgressTries;
+ }
+ if (connection_dir_count_by_purpose_and_resource(
+ DIR_PURPOSE_FETCH_CONSENSUS,
+ resource)
+ >= max_in_progress_conns) {
+ continue;
+ }
- if (!download_status_is_ready(&consensus_dl_status[i], now,
- options->TestingConsensusMaxDownloadTries))
- continue; /* We failed downloading a consensus too recently. */
- if (connection_dir_get_by_purpose_and_resource(
- DIR_PURPOSE_FETCH_CONSENSUS, resource))
- continue; /* There's an in-progress download.*/
+ /* Check if we want to launch another download for a usable consensus.
+ * Only used during bootstrap. */
+ if (we_are_bootstrapping && use_multi_conn
+ && i == usable_consensus_flavor()) {
+
+ /* Check if we're already downloading a usable consensus */
+ int consens_conn_count =
+ connection_dir_count_by_purpose_and_resource(
+ DIR_PURPOSE_FETCH_CONSENSUS,
+ resource);
+ int connect_consens_conn_count =
+ connection_dir_count_by_purpose_resource_and_state(
+ DIR_PURPOSE_FETCH_CONSENSUS,
+ resource,
+ DIR_CONN_STATE_CONNECTING);
+
+ /* If not all connections are "connecting", then some are
+ * downloading. We want to have at most one downloading at a time. */
+ if (connect_consens_conn_count < consens_conn_count) {
+ continue;
+ }
- waiting = &consensus_waiting_for_certs[i];
- if (waiting->consensus) {
- /* XXXX make sure this doesn't delay sane downloads. */
- if (waiting->set_at + DELAY_WHILE_FETCHING_CERTS > now) {
- continue; /* We're still getting certs for this one. */
- } else {
- if (!waiting->dl_failed) {
- download_status_failed(&consensus_dl_status[i], 0);
- waiting->dl_failed=1;
- }
+ /* Make multiple connections for a bootstrap consensus download. */
+ update_consensus_bootstrap_multiple_downloads(now, options,
+ we_are_bootstrapping);
+ } else {
+ /* Check if we failed downloading a consensus too recently */
+ int max_dl_tries = consensus_max_download_tries(options,
+ we_are_bootstrapping);
+
+ /* Let's make sure we remembered to update consensus_dl_status */
+ tor_assert(consensus_dl_status[i].schedule == DL_SCHED_CONSENSUS);
+
+ if (!download_status_is_ready(&consensus_dl_status[i],
+ now,
+ max_dl_tries)) {
+ continue;
}
+
+ /* Check if we're waiting for certificates to download */
+ if (check_consensus_waiting_for_certs(i, now, &consensus_dl_status[i]))
+ continue;
+
+ /* Try the requested attempt */
+ log_info(LD_DIR, "Launching %s standard networkstatus consensus "
+ "download.", networkstatus_get_flavor_name(i));
+ directory_get_from_dirserver(DIR_PURPOSE_FETCH_CONSENSUS,
+ ROUTER_PURPOSE_GENERAL, resource,
+ PDS_RETRY_IF_NO_SERVERS,
+ consensus_dl_status[i].want_authority);
}
+ }
+}
- log_info(LD_DIR, "Launching %s networkstatus consensus download.",
- networkstatus_get_flavor_name(i));
+/** When we're bootstrapping, launch one or more consensus download
+ * connections, if schedule indicates connection(s) should be made after now.
+ * If is_authority, connect to an authority, otherwise, use a fallback
+ * directory mirror.
+ */
+static void
+update_consensus_bootstrap_attempt_downloads(
+ time_t now,
+ const or_options_t *options,
+ int we_are_bootstrapping,
+ download_status_t *dls,
+ download_want_authority_t want_authority)
+{
+ int max_dl_tries = consensus_max_download_tries(options,
+ we_are_bootstrapping);
+ const char *resource = networkstatus_get_flavor_name(
+ usable_consensus_flavor());
+
+ /* Let's make sure we remembered to update schedule */
+ tor_assert(dls->schedule == DL_SCHED_CONSENSUS);
+
+ /* Allow for multiple connections in the same second, if the schedule value
+ * is 0. */
+ while (download_status_is_ready(dls, now, max_dl_tries)) {
+ log_info(LD_DIR, "Launching %s bootstrap %s networkstatus consensus "
+ "download.", resource, (want_authority == DL_WANT_AUTHORITY
+ ? "authority"
+ : "mirror"));
directory_get_from_dirserver(DIR_PURPOSE_FETCH_CONSENSUS,
ROUTER_PURPOSE_GENERAL, resource,
- PDS_RETRY_IF_NO_SERVERS);
+ PDS_RETRY_IF_NO_SERVERS, want_authority);
+ /* schedule the next attempt */
+ download_status_increment_attempt(dls, resource, now);
+ }
+}
+
+/** If we're bootstrapping, check the connection schedules and see if we want
+ * to make additional, potentially concurrent, consensus download
+ * connections.
+ * Only call when bootstrapping, and when we want to make additional
+ * connections. Only nodes that satisfy
+ * networkstatus_consensus_can_use_multiple_directories make additional
+ * connections.
+ */
+static void
+update_consensus_bootstrap_multiple_downloads(time_t now,
+ const or_options_t *options,
+ int we_are_bootstrapping)
+{
+ const int usable_flavor = usable_consensus_flavor();
+
+ /* make sure we can use multiple connections */
+ if (!networkstatus_consensus_can_use_multiple_directories(options)) {
+ return;
+ }
+
+ /* If we've managed to validate a usable consensus, don't make additional
+ * connections. */
+ if (!we_are_bootstrapping) {
+ return;
+ }
+
+ /* Launch concurrent consensus download attempt(s) based on the mirror and
+ * authority schedules. Try the mirror first - this makes it slightly more
+ * likely that we'll connect to the fallback first, and then end the
+ * authority connection attempt. */
+
+ /* If a consensus download fails because it's waiting for certificates,
+ * we'll fail both the authority and fallback schedules. This is better than
+ * failing only one of the schedules, and having the other continue
+ * unchecked.
+ */
+
+ /* If we don't have or can't use extra fallbacks, don't try them. */
+ if (networkstatus_consensus_can_use_extra_fallbacks(options)) {
+ download_status_t *dls_f =
+ &consensus_bootstrap_dl_status[CONSENSUS_BOOTSTRAP_SOURCE_ANY_DIRSERVER];
+
+ if (!check_consensus_waiting_for_certs(usable_flavor, now, dls_f)) {
+ /* During bootstrap, DL_WANT_ANY_DIRSERVER means "use fallbacks". */
+ update_consensus_bootstrap_attempt_downloads(now, options,
+ we_are_bootstrapping, dls_f,
+ DL_WANT_ANY_DIRSERVER);
+ }
+ }
+
+ /* Now try an authority. */
+ download_status_t *dls_a =
+ &consensus_bootstrap_dl_status[CONSENSUS_BOOTSTRAP_SOURCE_AUTHORITY];
+
+ if (!check_consensus_waiting_for_certs(usable_flavor, now, dls_a)) {
+ update_consensus_bootstrap_attempt_downloads(now, options,
+ we_are_bootstrapping, dls_a,
+ DL_WANT_AUTHORITY);
}
}
@@ -1057,6 +1274,104 @@ networkstatus_get_reasonably_live_consensus(time_t now, int flavor)
return NULL;
}
+/** Check if we're bootstrapping a consensus download. This means that we are
+ * only using the authorities and fallback directory mirrors to download the
+ * consensus flavour we'll use. */
+int
+networkstatus_consensus_is_bootstrapping(time_t now)
+{
+ /* If we don't have a consensus, we must still be bootstrapping */
+ return !networkstatus_get_reasonably_live_consensus(
+ now,
+ usable_consensus_flavor());
+}
+
+/** Check if we can use multiple directories for a consensus download.
+ * Only clients (including bridge relays, which act like clients) benefit
+ * from multiple simultaneous consensus downloads. */
+int
+networkstatus_consensus_can_use_multiple_directories(
+ const or_options_t *options)
+{
+ /* If we are a client, bridge, bridge client, or hidden service */
+ return !public_server_mode(options);
+}
+
+/** Check if we can use fallback directory mirrors for a consensus download.
+ * If we have fallbacks and don't want to fetch from the authorities,
+ * we can use them. */
+int
+networkstatus_consensus_can_use_extra_fallbacks(const or_options_t *options)
+{
+ /* The list length comparisons are a quick way to check if we have any
+ * non-authority fallback directories. If we ever have any authorities that
+ * aren't fallback directories, we will need to change this code. */
+ tor_assert(smartlist_len(router_get_fallback_dir_servers())
+ >= smartlist_len(router_get_trusted_dir_servers()));
+ /* If we don't fetch from the authorities, and we have additional mirrors,
+ * we can use them. */
+ return (!directory_fetches_from_authorities(options)
+ && (smartlist_len(router_get_fallback_dir_servers())
+ > smartlist_len(router_get_trusted_dir_servers())));
+}
+
+/* Check if there is more than 1 consensus connection retrieving the usable
+ * consensus flavor. If so, return 1, if not, return 0.
+ *
+ * During normal operation, Tor only makes one consensus download
+ * connection. But clients can make multiple simultaneous consensus
+ * connections to improve bootstrap speed and reliability.
+ *
+ * If there is more than one connection, we must have connections left
+ * over from bootstrapping. However, some of the connections may have
+ * completed and been cleaned up, so it is not sufficient to check the
+ * return value of this function to see if a client could make multiple
+ * bootstrap connections. Use
+ * networkstatus_consensus_can_use_multiple_directories()
+ * and networkstatus_consensus_is_bootstrapping(). */
+int
+networkstatus_consensus_has_excess_connections(void)
+{
+ const char *usable_resource = networkstatus_get_flavor_name(
+ usable_consensus_flavor());
+ const int consens_conn_usable_count =
+ connection_dir_count_by_purpose_and_resource(
+ DIR_PURPOSE_FETCH_CONSENSUS,
+ usable_resource);
+ /* The maximum number of connections we want downloading a usable consensus
+ * Always 1, whether bootstrapping or not. */
+ const int max_expected_consens_conn_usable_count = 1;
+
+ if (consens_conn_usable_count > max_expected_consens_conn_usable_count) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Is tor currently downloading a consensus of the usable flavor? */
+int
+networkstatus_consensus_is_downloading_usable_flavor(void)
+{
+ const char *usable_resource = networkstatus_get_flavor_name(
+ usable_consensus_flavor());
+ const int consens_conn_usable_count =
+ connection_dir_count_by_purpose_and_resource(
+ DIR_PURPOSE_FETCH_CONSENSUS,
+ usable_resource);
+
+ const int connect_consens_conn_usable_count =
+ connection_dir_count_by_purpose_resource_and_state(
+ DIR_PURPOSE_FETCH_CONSENSUS,
+ usable_resource,
+ DIR_CONN_STATE_CONNECTING);
+ if (connect_consens_conn_usable_count < consens_conn_usable_count) {
+ return 1;
+ }
+
+ return 0;
+}
+
/** Given two router status entries for the same router identity, return 1 if
* if the contents have changed between them. Otherwise, return 0. */
static int
@@ -1147,6 +1462,38 @@ networkstatus_copy_old_consensus_info(networkstatus_t *new_c,
} SMARTLIST_FOREACH_JOIN_END(rs_old, rs_new);
}
+#ifdef TOR_UNIT_TESTS
+/**Accept a <b>flavor</b> consensus <b>c</b> without any additional
+ * validation. This is exclusively for unit tests.
+ * We copy any ancillary information from a pre-existing consensus
+ * and then free the current one and replace it with the newly
+ * provided instance. Returns -1 on unrecognized flavor, 0 otherwise.
+ */
+int
+networkstatus_set_current_consensus_from_ns(networkstatus_t *c,
+ const char *flavor)
+{
+ int flav = networkstatus_parse_flavor_name(flavor);
+ switch (flav) {
+ case FLAV_NS:
+ if (current_ns_consensus) {
+ networkstatus_copy_old_consensus_info(c, current_ns_consensus);
+ networkstatus_vote_free(current_ns_consensus);
+ }
+ current_ns_consensus = c;
+ break;
+ case FLAV_MICRODESC:
+ if (current_md_consensus) {
+ networkstatus_copy_old_consensus_info(c, current_md_consensus);
+ networkstatus_vote_free(current_md_consensus);
+ }
+ current_md_consensus = c;
+ break;
+ }
+ return current_md_consensus ? 0 : -1;
+}
+#endif //TOR_UNIT_TESTS
+
/** Try to replace the current cached v3 networkstatus with the one in
* <b>consensus</b>. If we don't have enough certificates to validate it,
* store it in consensus_waiting_for_certs and launch a certificate fetch.
@@ -1180,7 +1527,7 @@ networkstatus_set_current_consensus(const char *consensus,
const unsigned dl_certs = !(flags & NSSET_DONT_DOWNLOAD_CERTS);
const unsigned accept_obsolete = flags & NSSET_ACCEPT_OBSOLETE;
const unsigned require_flavor = flags & NSSET_REQUIRE_FLAVOR;
- const digests_t *current_digests = NULL;
+ const common_digests_t *current_digests = NULL;
consensus_waiting_for_certs_t *waiting = NULL;
time_t current_valid_after = 0;
int free_consensus = 1; /* Free 'c' at the end of the function */
@@ -1701,7 +2048,7 @@ networkstatus_dump_bridge_status_to_file(time_t now)
char published[ISO_TIME_LEN+1];
format_iso_time(published, now);
- dirserv_compute_bridge_flag_thresholds(rl);
+ dirserv_compute_bridge_flag_thresholds(rl->routers);
thresholds = dirserv_get_flag_thresholds_line();
tor_asprintf(&published_thresholds_and_status,
"published %s\nflag-thresholds %s\n%s",
diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h
index d6e9e37013..f2f8af5c6b 100644
--- a/src/or/networkstatus.h
+++ b/src/or/networkstatus.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -70,6 +70,14 @@ MOCK_DECL(networkstatus_t *,networkstatus_get_latest_consensus_by_flavor,
networkstatus_t *networkstatus_get_live_consensus(time_t now);
networkstatus_t *networkstatus_get_reasonably_live_consensus(time_t now,
int flavor);
+int networkstatus_consensus_is_bootstrapping(time_t now);
+int networkstatus_consensus_can_use_multiple_directories(
+ const or_options_t *options);
+int networkstatus_consensus_can_use_extra_fallbacks(
+ const or_options_t *options);
+int networkstatus_consensus_has_excess_connections(void);
+int networkstatus_consensus_is_downloading_usable_flavor(void);
+
#define NSSET_FROM_CACHE 1
#define NSSET_WAS_WAITING_FOR_CERTS 2
#define NSSET_DONT_DOWNLOAD_CERTS 4
@@ -106,6 +114,10 @@ int networkstatus_get_weight_scale_param(networkstatus_t *ns);
#ifdef NETWORKSTATUS_PRIVATE
STATIC void vote_routerstatus_free(vote_routerstatus_t *rs);
+#ifdef TOR_UNIT_TESTS
+STATIC int networkstatus_set_current_consensus_from_ns(networkstatus_t *c,
+ const char *flavor);
+#endif // TOR_UNIT_TESTS
#endif
#endif
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
index 2f272a1d56..abbb82dfdd 100644
--- a/src/or/nodelist.c
+++ b/src/or/nodelist.c
@@ -1,9 +1,17 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+/**
+ * \file nodelist.c
+ *
+ * \brief Structures and functions for tracking what we know about the routers
+ * on the Tor network, and correlating information from networkstatus,
+ * routerinfo, and microdescs.
+ */
+
#include "or.h"
#include "address.h"
#include "config.h"
@@ -57,13 +65,13 @@ typedef struct nodelist_t {
} nodelist_t;
-static INLINE unsigned int
+static inline unsigned int
node_id_hash(const node_t *node)
{
return (unsigned) siphash24g(node->identity, DIGEST_LEN);
}
-static INLINE unsigned int
+static inline unsigned int
node_id_eq(const node_t *node1, const node_t *node2)
{
return tor_memeq(node1->identity, node2->identity, DIGEST_LEN);
@@ -224,7 +232,6 @@ nodelist_set_consensus(networkstatus_t *ns)
{
const or_options_t *options = get_options();
int authdir = authdir_mode_v3(options);
- int client = !server_mode(options);
init_nodelist();
if (ns->flavor == FLAV_MICRODESC)
@@ -261,7 +268,7 @@ nodelist_set_consensus(networkstatus_t *ns)
node->is_bad_exit = rs->is_bad_exit;
node->is_hs_dir = rs->is_hs_dir;
node->ipv6_preferred = 0;
- if (client && options->ClientPreferIPv6ORPort == 1 &&
+ if (fascist_firewall_prefer_ipv6_orport(options) &&
(tor_addr_is_null(&rs->ipv6_addr) == 0 ||
(node->md && tor_addr_is_null(&node->md->ipv6_addr) == 0)))
node->ipv6_preferred = 1;
@@ -291,7 +298,7 @@ nodelist_set_consensus(networkstatus_t *ns)
}
/** Helper: return true iff a node has a usable amount of information*/
-static INLINE int
+static inline int
node_is_usable(const node_t *node)
{
return (node->rs) || (node->ri);
@@ -644,12 +651,19 @@ node_is_named(const node_t *node)
int
node_is_dir(const node_t *node)
{
- if (node->rs)
- return node->rs->dir_port != 0;
- else if (node->ri)
- return node->ri->dir_port != 0;
- else
+ if (node->rs) {
+ routerstatus_t * rs = node->rs;
+ /* This is true if supports_tunnelled_dir_requests is true which
+ * indicates that we support directory request tunnelled or through the
+ * DirPort. */
+ return rs->is_v2_dir;
+ } else if (node->ri) {
+ routerinfo_t * ri = node->ri;
+ /* Both tunnelled request is supported or DirPort is set. */
+ return ri->supports_tunnelled_dir_requests;
+ } else {
return 0;
+ }
}
/** Return true iff <b>node</b> has either kind of usable descriptor -- that
@@ -754,6 +768,40 @@ node_exit_policy_is_exact(const node_t *node, sa_family_t family)
return 1;
}
+/* Check if the "addr" and port_field fields from r are a valid non-listening
+ * address/port. If so, set valid to true and add a newly allocated
+ * tor_addr_port_t containing "addr" and port_field to sl.
+ * "addr" is an IPv4 host-order address and port_field is a uint16_t.
+ * r is typically a routerinfo_t or routerstatus_t.
+ */
+#define SL_ADD_NEW_IPV4_AP(r, port_field, sl, valid) \
+ STMT_BEGIN \
+ if (tor_addr_port_is_valid_ipv4h((r)->addr, (r)->port_field, 0)) { \
+ valid = 1; \
+ tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); \
+ tor_addr_from_ipv4h(&ap->addr, (r)->addr); \
+ ap->port = (r)->port_field; \
+ smartlist_add((sl), ap); \
+ } \
+ STMT_END
+
+/* Check if the "addr" and port_field fields from r are a valid non-listening
+ * address/port. If so, set valid to true and add a newly allocated
+ * tor_addr_port_t containing "addr" and port_field to sl.
+ * "addr" is a tor_addr_t and port_field is a uint16_t.
+ * r is typically a routerinfo_t or routerstatus_t.
+ */
+#define SL_ADD_NEW_IPV6_AP(r, port_field, sl, valid) \
+ STMT_BEGIN \
+ if (tor_addr_port_is_valid(&(r)->ipv6_addr, (r)->port_field, 0)) { \
+ valid = 1; \
+ tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); \
+ tor_addr_copy(&ap->addr, &(r)->ipv6_addr); \
+ ap->port = (r)->port_field; \
+ smartlist_add((sl), ap); \
+ } \
+ STMT_END
+
/** 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().
@@ -766,30 +814,38 @@ smartlist_t *
node_get_all_orports(const node_t *node)
{
smartlist_t *sl = smartlist_new();
+ int valid = 0;
+ /* Find a valid IPv4 address and port */
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);
+ SL_ADD_NEW_IPV4_AP(node->ri, or_port, sl, valid);
+ }
+
+ /* If we didn't find a valid address/port in the ri, try the rs */
+ if (!valid && node->rs != NULL) {
+ SL_ADD_NEW_IPV4_AP(node->rs, or_port, sl, valid);
+ }
+
+ /* Find a valid IPv6 address and port */
+ valid = 0;
+ if (node->ri != NULL) {
+ SL_ADD_NEW_IPV6_AP(node->ri, ipv6_orport, sl, valid);
+ }
+
+ if (!valid && node->rs != NULL) {
+ SL_ADD_NEW_IPV6_AP(node->rs, ipv6_orport, sl, valid);
+ }
+
+ if (!valid && node->md != NULL) {
+ SL_ADD_NEW_IPV6_AP(node->md, ipv6_orport, sl, valid);
}
return sl;
}
+#undef SL_ADD_NEW_IPV4_AP
+#undef SL_ADD_NEW_IPV6_AP
+
/** Wrapper around node_get_prim_orport for backward
compatibility. */
void
@@ -805,9 +861,13 @@ node_get_addr(const node_t *node, tor_addr_t *addr_out)
uint32_t
node_get_prim_addr_ipv4h(const node_t *node)
{
- if (node->ri) {
+ /* Don't check the ORPort or DirPort, as this function isn't port-specific,
+ * and the node might have a valid IPv4 address, yet have a zero
+ * ORPort or DirPort.
+ */
+ if (node->ri && tor_addr_is_valid_ipv4h(node->ri->addr, 0)) {
return node->ri->addr;
- } else if (node->rs) {
+ } else if (node->rs && tor_addr_is_valid_ipv4h(node->rs->addr, 0)) {
return node->rs->addr;
}
return 0;
@@ -818,13 +878,13 @@ node_get_prim_addr_ipv4h(const node_t *node)
void
node_get_address_string(const node_t *node, char *buf, size_t len)
{
- if (node->ri) {
- strlcpy(buf, fmt_addr32(node->ri->addr), len);
- } else if (node->rs) {
+ uint32_t ipv4_addr = node_get_prim_addr_ipv4h(node);
+
+ if (tor_addr_is_valid_ipv4h(ipv4_addr, 0)) {
tor_addr_t addr;
- tor_addr_from_ipv4h(&addr, node->rs->addr);
+ tor_addr_from_ipv4h(&addr, ipv4_addr);
tor_addr_to_str(buf, &addr, len, 0);
- } else {
+ } else if (len > 0) {
buf[0] = '\0';
}
}
@@ -883,30 +943,83 @@ node_get_declared_family(const node_t *node)
return NULL;
}
+/* Does this node have a valid IPv6 address?
+ * Prefer node_has_ipv6_orport() or node_has_ipv6_dirport() for
+ * checking specific ports. */
+int
+node_has_ipv6_addr(const node_t *node)
+{
+ /* Don't check the ORPort or DirPort, as this function isn't port-specific,
+ * and the node might have a valid IPv6 address, yet have a zero
+ * ORPort or DirPort.
+ */
+ if (node->ri && tor_addr_is_valid(&node->ri->ipv6_addr, 0))
+ return 1;
+ if (node->rs && tor_addr_is_valid(&node->rs->ipv6_addr, 0))
+ return 1;
+ if (node->md && tor_addr_is_valid(&node->md->ipv6_addr, 0))
+ return 1;
+
+ return 0;
+}
+
+/* Does this node have a valid IPv6 ORPort? */
+int
+node_has_ipv6_orport(const node_t *node)
+{
+ tor_addr_port_t ipv6_orport;
+ node_get_pref_ipv6_orport(node, &ipv6_orport);
+ return tor_addr_port_is_valid_ap(&ipv6_orport, 0);
+}
+
+/* Does this node have a valid IPv6 DirPort? */
+int
+node_has_ipv6_dirport(const node_t *node)
+{
+ tor_addr_port_t ipv6_dirport;
+ node_get_pref_ipv6_dirport(node, &ipv6_dirport);
+ return tor_addr_port_is_valid_ap(&ipv6_dirport, 0);
+}
+
/** Return 1 if we prefer the IPv6 address and OR TCP port of
* <b>node</b>, else 0.
*
- * We prefer the IPv6 address if the router has an IPv6 address and
+ * We prefer the IPv6 address if the router has an IPv6 address,
+ * and we can use IPv6 addresses, and:
* i) the node_t says that it prefers IPv6
* or
- * ii) the router has no IPv4 address. */
+ * ii) the router has no IPv4 OR address.
+ *
+ * If you don't have a node, consider looking it up.
+ * If there is no node, use fascist_firewall_prefer_ipv6_orport().
+ */
int
-node_ipv6_preferred(const node_t *node)
+node_ipv6_or_preferred(const node_t *node)
{
+ const or_options_t *options = get_options();
tor_addr_port_t ipv4_addr;
node_assert_ok(node);
- if (node->ipv6_preferred || node_get_prim_orport(node, &ipv4_addr)) {
- if (node->ri)
- return !tor_addr_is_null(&node->ri->ipv6_addr);
- if (node->md)
- return !tor_addr_is_null(&node->md->ipv6_addr);
- if (node->rs)
- return !tor_addr_is_null(&node->rs->ipv6_addr);
+ /* XX/teor - node->ipv6_preferred is set from
+ * fascist_firewall_prefer_ipv6_orport() each time the consensus is loaded.
+ */
+ if (!fascist_firewall_use_ipv6(options)) {
+ return 0;
+ } else if (node->ipv6_preferred || node_get_prim_orport(node, &ipv4_addr)) {
+ return node_has_ipv6_orport(node);
}
return 0;
}
+#define RETURN_IPV4_AP(r, port_field, ap_out) \
+ STMT_BEGIN \
+ if (r && tor_addr_port_is_valid_ipv4h((r)->addr, (r)->port_field, 0)) { \
+ tor_addr_from_ipv4h(&(ap_out)->addr, (r)->addr); \
+ (ap_out)->port = (r)->port_field; \
+ return 0; \
+ } \
+ STMT_END
+
/** Copy the primary (IPv4) OR port (IP address and TCP port) for
* <b>node</b> into *<b>ap_out</b>. Return 0 if a valid address and
* port was copied, else return non-zero.*/
@@ -916,20 +1029,10 @@ node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out)
node_assert_ok(node);
tor_assert(ap_out);
- if (node->ri) {
- if (node->ri->addr == 0 || node->ri->or_port == 0)
- return -1;
- tor_addr_from_ipv4h(&ap_out->addr, node->ri->addr);
- ap_out->port = node->ri->or_port;
- return 0;
- }
- if (node->rs) {
- if (node->rs->addr == 0 || node->rs->or_port == 0)
- return -1;
- tor_addr_from_ipv4h(&ap_out->addr, node->rs->addr);
- ap_out->port = node->rs->or_port;
- return 0;
- }
+ RETURN_IPV4_AP(node->ri, or_port, ap_out);
+ RETURN_IPV4_AP(node->rs, or_port, ap_out);
+ /* Microdescriptors only have an IPv6 address */
+
return -1;
}
@@ -938,21 +1041,12 @@ node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out)
void
node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out)
{
- const or_options_t *options = get_options();
tor_assert(ap_out);
- /* Cheap implementation of config option ClientUseIPv6 -- simply
- don't prefer IPv6 when ClientUseIPv6 is not set and we're not a
- client running with bridges. See #4455 for more on this subject.
-
- Note that this filter is too strict since we're hindering not
- only clients! Erring on the safe side shouldn't be a problem
- though. XXX move this check to where outgoing connections are
- made? -LN */
- if ((options->ClientUseIPv6 || options->UseBridges) &&
- node_ipv6_preferred(node)) {
+ if (node_ipv6_or_preferred(node)) {
node_get_pref_ipv6_orport(node, ap_out);
} else {
+ /* the primary ORPort is always on IPv4 */
node_get_prim_orport(node, ap_out);
}
}
@@ -965,20 +1059,115 @@ node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out)
node_assert_ok(node);
tor_assert(ap_out);
- /* We prefer the microdesc over a potential routerstatus here. They
- are not being synchronised atm so there might be a chance that
- they differ at some point, f.ex. when flipping
- UseMicrodescriptors? -LN */
+ /* Prefer routerstatus over microdesc for consistency with the
+ * fascist_firewall_* functions. Also check if the address or port are valid,
+ * and try another alternative if they are not. */
- if (node->ri) {
+ if (node->ri && tor_addr_port_is_valid(&node->ri->ipv6_addr,
+ node->ri->ipv6_orport, 0)) {
tor_addr_copy(&ap_out->addr, &node->ri->ipv6_addr);
ap_out->port = node->ri->ipv6_orport;
- } else if (node->md) {
+ } else if (node->rs && tor_addr_port_is_valid(&node->rs->ipv6_addr,
+ node->rs->ipv6_orport, 0)) {
+ tor_addr_copy(&ap_out->addr, &node->rs->ipv6_addr);
+ ap_out->port = node->rs->ipv6_orport;
+ } else if (node->md && tor_addr_port_is_valid(&node->md->ipv6_addr,
+ node->md->ipv6_orport, 0)) {
tor_addr_copy(&ap_out->addr, &node->md->ipv6_addr);
ap_out->port = node->md->ipv6_orport;
- } else if (node->rs) {
+ } else {
+ tor_addr_make_null(&ap_out->addr, AF_INET6);
+ ap_out->port = 0;
+ }
+}
+
+/** Return 1 if we prefer the IPv6 address and Dir TCP port of
+ * <b>node</b>, else 0.
+ *
+ * We prefer the IPv6 address if the router has an IPv6 address,
+ * and we can use IPv6 addresses, and:
+ * i) the router has no IPv4 Dir address.
+ * or
+ * ii) our preference is for IPv6 Dir addresses.
+ *
+ * If there is no node, use fascist_firewall_prefer_ipv6_dirport().
+ */
+int
+node_ipv6_dir_preferred(const node_t *node)
+{
+ const or_options_t *options = get_options();
+ tor_addr_port_t ipv4_addr;
+ node_assert_ok(node);
+
+ /* node->ipv6_preferred is set from fascist_firewall_prefer_ipv6_orport(),
+ * so we can't use it to determine DirPort IPv6 preference.
+ * This means that bridge clients will use IPv4 DirPorts by default.
+ */
+ if (!fascist_firewall_use_ipv6(options)) {
+ return 0;
+ } else if (node_get_prim_dirport(node, &ipv4_addr)
+ || fascist_firewall_prefer_ipv6_dirport(get_options())) {
+ return node_has_ipv6_dirport(node);
+ }
+ return 0;
+}
+
+/** Copy the primary (IPv4) Dir port (IP address and TCP port) for
+ * <b>node</b> into *<b>ap_out</b>. Return 0 if a valid address and
+ * port was copied, else return non-zero.*/
+int
+node_get_prim_dirport(const node_t *node, tor_addr_port_t *ap_out)
+{
+ node_assert_ok(node);
+ tor_assert(ap_out);
+
+ RETURN_IPV4_AP(node->ri, dir_port, ap_out);
+ RETURN_IPV4_AP(node->rs, dir_port, ap_out);
+ /* Microdescriptors only have an IPv6 address */
+
+ return -1;
+}
+
+#undef RETURN_IPV4_AP
+
+/** Copy the preferred Dir port (IP address and TCP port) for
+ * <b>node</b> into *<b>ap_out</b>. */
+void
+node_get_pref_dirport(const node_t *node, tor_addr_port_t *ap_out)
+{
+ tor_assert(ap_out);
+
+ if (node_ipv6_dir_preferred(node)) {
+ node_get_pref_ipv6_dirport(node, ap_out);
+ } else {
+ /* the primary DirPort is always on IPv4 */
+ node_get_prim_dirport(node, ap_out);
+ }
+}
+
+/** Copy the preferred IPv6 Dir port (IP address and TCP port) for
+ * <b>node</b> into *<b>ap_out</b>. */
+void
+node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out)
+{
+ node_assert_ok(node);
+ tor_assert(ap_out);
+
+ /* Check if the address or port are valid, and try another alternative if
+ * they are not. Note that microdescriptors have no dir_port. */
+
+ /* Assume IPv4 and IPv6 dirports are the same */
+ if (node->ri && tor_addr_port_is_valid(&node->ri->ipv6_addr,
+ node->ri->dir_port, 0)) {
+ tor_addr_copy(&ap_out->addr, &node->ri->ipv6_addr);
+ ap_out->port = node->ri->dir_port;
+ } else if (node->rs && tor_addr_port_is_valid(&node->rs->ipv6_addr,
+ node->rs->dir_port, 0)) {
tor_addr_copy(&ap_out->addr, &node->rs->ipv6_addr);
- ap_out->port = node->rs->ipv6_orport;
+ ap_out->port = node->rs->dir_port;
+ } else {
+ tor_addr_make_null(&ap_out->addr, AF_INET6);
+ ap_out->port = 0;
}
}
@@ -1021,7 +1210,7 @@ nodelist_refresh_countries(void)
/** Return true iff router1 and router2 have similar enough network addresses
* that we should treat them as being in the same family */
-static INLINE int
+static inline int
addrs_in_same_network_family(const tor_addr_t *a1,
const tor_addr_t *a2)
{
@@ -1045,7 +1234,7 @@ node_nickname_matches(const node_t *node, const char *nickname)
}
/** Return true iff <b>node</b> is named by some nickname in <b>lst</b>. */
-static INLINE int
+static inline int
node_in_nickname_smartlist(const smartlist_t *lst, const node_t *node)
{
if (!lst) return 0;
diff --git a/src/or/nodelist.h b/src/or/nodelist.h
index a131e0dd4e..71a91e107f 100644
--- a/src/or/nodelist.h
+++ b/src/or/nodelist.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -55,10 +55,20 @@ void node_get_address_string(const node_t *node, char *cp, size_t len);
long node_get_declared_uptime(const node_t *node);
time_t node_get_published_on(const node_t *node);
const smartlist_t *node_get_declared_family(const node_t *node);
-int node_ipv6_preferred(const node_t *node);
+
+int node_has_ipv6_addr(const node_t *node);
+int node_has_ipv6_orport(const node_t *node);
+int node_has_ipv6_dirport(const node_t *node);
+/* Deprecated - use node_ipv6_or_preferred or node_ipv6_dir_preferred */
+#define node_ipv6_preferred(node) node_ipv6_or_preferred(node)
+int node_ipv6_or_preferred(const node_t *node);
int node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out);
void node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out);
void node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out);
+int node_ipv6_dir_preferred(const node_t *node);
+int node_get_prim_dirport(const node_t *node, tor_addr_port_t *ap_out);
+void node_get_pref_dirport(const node_t *node, tor_addr_port_t *ap_out);
+void node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out);
int node_has_curve25519_onion_key(const node_t *node);
MOCK_DECL(smartlist_t *, nodelist_get_list, (void));
diff --git a/src/or/ntmain.c b/src/or/ntmain.c
index b31ed869d6..ded0e0d307 100644
--- a/src/or/ntmain.c
+++ b/src/or/ntmain.c
@@ -1,8 +1,14 @@
/* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+/**
+ * \file ntmain.c
+ *
+ * \brief Entry points for running/configuring Tor as Windows Service.
+ */
+
#ifdef _WIN32
#include "or.h"
diff --git a/src/or/ntmain.h b/src/or/ntmain.h
index eb55a296f6..31bf38c62c 100644
--- a/src/or/ntmain.h
+++ b/src/or/ntmain.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/onion.c b/src/or/onion.c
index 4864792511..4bed7ae226 100644
--- a/src/or/onion.c
+++ b/src/or/onion.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -527,7 +527,7 @@ onion_skin_server_handshake(int type,
* <b>rend_authenticator_out</b> to the "KH" field that can be used to
* establish introduction points at this hop, and return 0. On failure,
* return -1, and set *msg_out to an error message if this is worth
- * complaining to the usre about. */
+ * complaining to the user about. */
int
onion_skin_client_handshake(int type,
const onion_handshake_state_t *handshake_state,
diff --git a/src/or/onion.h b/src/or/onion.h
index 45454f480d..0275fa00d2 100644
--- a/src/or/onion.h
+++ b/src/or/onion.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/onion_fast.c b/src/or/onion_fast.c
index 7584112570..1f79860596 100644
--- a/src/or/onion_fast.c
+++ b/src/or/onion_fast.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -30,10 +30,7 @@ fast_onionskin_create(fast_handshake_state_t **handshake_state_out,
{
fast_handshake_state_t *s;
*handshake_state_out = s = tor_malloc(sizeof(fast_handshake_state_t));
- if (crypto_rand((char*)s->state, sizeof(s->state)) < 0) {
- tor_free(s);
- return -1;
- }
+ crypto_rand((char*)s->state, sizeof(s->state));
memcpy(handshake_out, s->state, DIGEST_LEN);
return 0;
}
@@ -56,8 +53,7 @@ fast_server_handshake(const uint8_t *key_in, /* DIGEST_LEN bytes */
size_t out_len;
int r = -1;
- if (crypto_rand((char*)handshake_reply_out, DIGEST_LEN)<0)
- return -1;
+ crypto_rand((char*)handshake_reply_out, DIGEST_LEN);
memcpy(tmp, key_in, DIGEST_LEN);
memcpy(tmp+DIGEST_LEN, handshake_reply_out, DIGEST_LEN);
diff --git a/src/or/onion_fast.h b/src/or/onion_fast.h
index d50d503a73..b9626002c3 100644
--- a/src/or/onion_fast.h
+++ b/src/or/onion_fast.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/onion_ntor.c b/src/or/onion_ntor.c
index 539f06f61f..9f97a4cfbe 100644
--- a/src/or/onion_ntor.c
+++ b/src/or/onion_ntor.c
@@ -1,6 +1,12 @@
-/* Copyright (c) 2012-2015, The Tor Project, Inc. */
+/* Copyright (c) 2012-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+/**
+ * \file onion_ntor.c
+ *
+ * \brief Implementation for the ntor handshake.
+ */
+
#include "orconfig.h"
#define ONION_NTOR_PRIVATE
diff --git a/src/or/onion_ntor.h b/src/or/onion_ntor.h
index 0a39c1de16..f637b437fd 100644
--- a/src/or/onion_ntor.h
+++ b/src/or/onion_ntor.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2015, The Tor Project, Inc. */
+/* Copyright (c) 2012-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_ONION_NTOR_H
diff --git a/src/or/onion_tap.c b/src/or/onion_tap.c
index 487cbeec04..bfd472351f 100644
--- a/src/or/onion_tap.c
+++ b/src/or/onion_tap.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/onion_tap.h b/src/or/onion_tap.h
index c548b3d99f..a2880f6e98 100644
--- a/src/or/onion_tap.h
+++ b/src/or/onion_tap.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/or.h b/src/or/or.h
index 54e05e3bc6..86664d470d 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -458,9 +458,11 @@ typedef enum {
#define CIRCUIT_PURPOSE_OR_MIN_ 1
/** OR-side circuit purpose: normal circuit, at OR. */
#define CIRCUIT_PURPOSE_OR 1
-/** OR-side circuit purpose: At OR, from Bob, waiting for intro from Alices. */
+/** OR-side circuit purpose: At OR, from the service, waiting for intro from
+ * clients. */
#define CIRCUIT_PURPOSE_INTRO_POINT 2
-/** OR-side circuit purpose: At OR, from Alice, waiting for Bob. */
+/** OR-side circuit purpose: At OR, from the client, waiting for the service.
+ */
#define CIRCUIT_PURPOSE_REND_POINT_WAITING 3
/** OR-side circuit purpose: At OR, both circuits have this purpose. */
#define CIRCUIT_PURPOSE_REND_ESTABLISHED 4
@@ -479,43 +481,47 @@ typedef enum {
* to becoming open, or they are open and have sent the
* establish_rendezvous cell but haven't received an ack.
* circuits that are c_rend_ready are open and have received a
- * rend ack, but haven't heard from bob yet. if they have a
+ * rend ack, but haven't heard from the service yet. if they have a
* buildstate->pending_final_cpath then they're expecting a
- * cell from bob, else they're not.
+ * cell from the service, else they're not.
* circuits that are c_rend_ready_intro_acked are open, and
* some intro circ has sent its intro and received an ack.
* circuits that are c_rend_joined are open, have heard from
- * bob, and are talking to him.
+ * the service, and are talking to it.
*/
/** Client-side circuit purpose: Normal circuit, with cpath. */
#define CIRCUIT_PURPOSE_C_GENERAL 5
-/** Client-side circuit purpose: at Alice, connecting to intro point. */
+/** Client-side circuit purpose: at the client, connecting to intro point. */
#define CIRCUIT_PURPOSE_C_INTRODUCING 6
-/** Client-side circuit purpose: at Alice, sent INTRODUCE1 to intro point,
+/** Client-side circuit purpose: at the client, sent INTRODUCE1 to intro point,
* waiting for ACK/NAK. */
#define CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT 7
-/** Client-side circuit purpose: at Alice, introduced and acked, closing. */
+/** Client-side circuit purpose: at the client, introduced and acked, closing.
+ */
#define CIRCUIT_PURPOSE_C_INTRODUCE_ACKED 8
-/** Client-side circuit purpose: at Alice, waiting for ack. */
+/** Client-side circuit purpose: at the client, waiting for ack. */
#define CIRCUIT_PURPOSE_C_ESTABLISH_REND 9
-/** Client-side circuit purpose: at Alice, waiting for Bob. */
+/** Client-side circuit purpose: at the client, waiting for the service. */
#define CIRCUIT_PURPOSE_C_REND_READY 10
-/** Client-side circuit purpose: at Alice, waiting for Bob, INTRODUCE
- * has been acknowledged. */
+/** Client-side circuit purpose: at the client, waiting for the service,
+ * INTRODUCE has been acknowledged. */
#define CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED 11
-/** Client-side circuit purpose: at Alice, rendezvous established. */
+/** Client-side circuit purpose: at the client, rendezvous established. */
#define CIRCUIT_PURPOSE_C_REND_JOINED 12
/** This circuit is used for build time measurement only */
#define CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT 13
#define CIRCUIT_PURPOSE_C_MAX_ 13
-/** Hidden-service-side circuit purpose: at Bob, waiting for introductions. */
+/** Hidden-service-side circuit purpose: at the service, waiting for
+ * introductions. */
#define CIRCUIT_PURPOSE_S_ESTABLISH_INTRO 14
-/** Hidden-service-side circuit purpose: at Bob, successfully established
- * intro. */
+/** Hidden-service-side circuit purpose: at the service, successfully
+ * established intro. */
#define CIRCUIT_PURPOSE_S_INTRO 15
-/** Hidden-service-side circuit purpose: at Bob, connecting to rend point. */
+/** Hidden-service-side circuit purpose: at the service, connecting to rend
+ * point. */
#define CIRCUIT_PURPOSE_S_CONNECT_REND 16
-/** Hidden-service-side circuit purpose: at Bob, rendezvous established. */
+/** Hidden-service-side circuit purpose: at the service, rendezvous
+ * established. */
#define CIRCUIT_PURPOSE_S_REND_JOINED 17
/** A testing circuit; not meant to be used for actual traffic. */
#define CIRCUIT_PURPOSE_TESTING 18
@@ -915,18 +921,18 @@ typedef enum {
#define VAR_CELL_MAX_HEADER_SIZE 7
static int get_cell_network_size(int wide_circ_ids);
-static INLINE int get_cell_network_size(int wide_circ_ids)
+static inline int get_cell_network_size(int wide_circ_ids)
{
return wide_circ_ids ? CELL_MAX_NETWORK_SIZE : CELL_MAX_NETWORK_SIZE - 2;
}
static int get_var_cell_header_size(int wide_circ_ids);
-static INLINE int get_var_cell_header_size(int wide_circ_ids)
+static inline int get_var_cell_header_size(int wide_circ_ids)
{
return wide_circ_ids ? VAR_CELL_MAX_HEADER_SIZE :
VAR_CELL_MAX_HEADER_SIZE - 2;
}
static int get_circ_id_size(int wide_circ_ids);
-static INLINE int get_circ_id_size(int wide_circ_ids)
+static inline int get_circ_id_size(int wide_circ_ids)
{
return wide_circ_ids ? 4 : 2;
}
@@ -1155,6 +1161,8 @@ typedef struct entry_port_cfg_t {
/** When both no-auth and user/pass are advertised by a SOCKS client, select
* no-auth. */
unsigned int socks_prefer_no_auth : 1;
+ /** When ISO_SOCKSAUTH is in use, Keep-Alive circuits indefinitely. */
+ unsigned int socks_iso_keep_alive : 1;
/* Client port types only: */
unsigned int ipv4_traffic : 1;
@@ -1286,21 +1294,26 @@ typedef struct connection_t {
time_t timestamp_created; /**< When was this connection_t created? */
- /* XXXX_IP6 make this IPv6-capable */
int socket_family; /**< Address family of this connection's socket. Usually
- * AF_INET, but it can also be AF_UNIX, or in the future
- * AF_INET6 */
- tor_addr_t addr; /**< IP of the other side of the connection; used to
- * identify routers, along with port. */
- uint16_t port; /**< If non-zero, port on the other end
- * of the connection. */
+ * AF_INET, but it can also be AF_UNIX, or AF_INET6 */
+ tor_addr_t addr; /**< IP that socket "s" is directly connected to;
+ * may be the IP address for a proxy or pluggable transport,
+ * see "address" for the address of the final destination.
+ */
+ uint16_t port; /**< If non-zero, port that socket "s" is directly connected
+ * to; may be the port for a proxy or pluggable transport,
+ * see "address" for the port at the final destination. */
uint16_t marked_for_close; /**< Should we close this conn on the next
* iteration of the main loop? (If true, holds
* the line number where this connection was
* marked.) */
const char *marked_for_close_file; /**< For debugging: in which file were
* we marked for close? */
- char *address; /**< FQDN (or IP) of the guy on the other end.
+ char *address; /**< FQDN (or IP) and port of the final destination for this
+ * connection; this is always the remote address, it is
+ * passed to a proxy or pluggable transport if one in use.
+ * See "addr" and "port" for the address that socket "s" is
+ * directly connected to.
* strdup into this, because free_connection() frees it. */
/** Another connection that's connected to this one in lieu of a socket. */
struct connection_t *linked_conn;
@@ -1350,7 +1363,7 @@ typedef struct listener_connection_t {
* in the v3 handshake. The subject key must be a 1024-bit RSA key; it
* must be signed by the identity key */
#define OR_CERT_TYPE_AUTH_1024 3
-/** DOCDOC */
+/* DOCDOC */
#define OR_CERT_TYPE_RSA_ED_CROSSCERT 7
/**@}*/
@@ -1644,6 +1657,13 @@ typedef struct entry_connection_t {
* request that we're going to try to answer. */
struct evdns_server_request *dns_server_request;
+#define DEBUGGING_17659
+
+#ifdef DEBUGGING_17659
+ uint16_t marked_pending_circ_line;
+ const char *marked_pending_circ_file;
+#endif
+
#define NUM_CIRCUITS_LAUNCHED_THRESHOLD 10
/** Number of times we've launched a circuit to handle this stream. If
* it gets too high, that could indicate an inconsistency between our
@@ -1797,38 +1817,38 @@ static control_connection_t *TO_CONTROL_CONN(connection_t *);
* invalid. */
static listener_connection_t *TO_LISTENER_CONN(connection_t *);
-static INLINE or_connection_t *TO_OR_CONN(connection_t *c)
+static inline or_connection_t *TO_OR_CONN(connection_t *c)
{
tor_assert(c->magic == OR_CONNECTION_MAGIC);
return DOWNCAST(or_connection_t, c);
}
-static INLINE dir_connection_t *TO_DIR_CONN(connection_t *c)
+static inline dir_connection_t *TO_DIR_CONN(connection_t *c)
{
tor_assert(c->magic == DIR_CONNECTION_MAGIC);
return DOWNCAST(dir_connection_t, c);
}
-static INLINE edge_connection_t *TO_EDGE_CONN(connection_t *c)
+static inline edge_connection_t *TO_EDGE_CONN(connection_t *c)
{
tor_assert(c->magic == EDGE_CONNECTION_MAGIC ||
c->magic == ENTRY_CONNECTION_MAGIC);
return DOWNCAST(edge_connection_t, c);
}
-static INLINE entry_connection_t *TO_ENTRY_CONN(connection_t *c)
+static inline entry_connection_t *TO_ENTRY_CONN(connection_t *c)
{
tor_assert(c->magic == ENTRY_CONNECTION_MAGIC);
return (entry_connection_t*) SUBTYPE_P(c, entry_connection_t, edge_.base_);
}
-static INLINE entry_connection_t *EDGE_TO_ENTRY_CONN(edge_connection_t *c)
+static inline entry_connection_t *EDGE_TO_ENTRY_CONN(edge_connection_t *c)
{
tor_assert(c->base_.magic == ENTRY_CONNECTION_MAGIC);
return (entry_connection_t*) SUBTYPE_P(c, entry_connection_t, edge_);
}
-static INLINE control_connection_t *TO_CONTROL_CONN(connection_t *c)
+static inline control_connection_t *TO_CONTROL_CONN(connection_t *c)
{
tor_assert(c->magic == CONTROL_CONNECTION_MAGIC);
return DOWNCAST(control_connection_t, c);
}
-static INLINE listener_connection_t *TO_LISTENER_CONN(connection_t *c)
+static inline listener_connection_t *TO_LISTENER_CONN(connection_t *c)
{
tor_assert(c->magic == LISTENER_CONNECTION_MAGIC);
return DOWNCAST(listener_connection_t, c);
@@ -1920,7 +1940,7 @@ typedef struct cached_dir_t {
size_t dir_len; /**< Length of <b>dir</b> (not counting its NUL). */
size_t dir_z_len; /**< Length of <b>dir_z</b>. */
time_t published; /**< When was this object published. */
- digests_t digests; /**< Digests of this object (networkstatus only) */
+ common_digests_t digests; /**< Digests of this object (networkstatus only) */
int refcnt; /**< Reference count for this cached_dir_t. */
} cached_dir_t;
@@ -1944,8 +1964,8 @@ typedef enum {
} saved_location_t;
#define saved_location_bitfield_t ENUM_BF(saved_location_t)
-/** Enumeration: what kind of download schedule are we using for a given
- * object? */
+/** Enumeration: what directory object is being downloaded?
+ * This determines which schedule is selected to perform the download. */
typedef enum {
DL_SCHED_GENERIC = 0,
DL_SCHED_CONSENSUS = 1,
@@ -1953,15 +1973,74 @@ typedef enum {
} download_schedule_t;
#define download_schedule_bitfield_t ENUM_BF(download_schedule_t)
+/** Enumeration: is the download schedule for downloading from an authority,
+ * or from any available directory mirror?
+ * During bootstrap, "any" means a fallback (or an authority, if there
+ * are no fallbacks).
+ * When we have a valid consensus, "any" means any directory server. */
+typedef enum {
+ DL_WANT_ANY_DIRSERVER = 0,
+ DL_WANT_AUTHORITY = 1,
+} download_want_authority_t;
+#define download_want_authority_bitfield_t \
+ ENUM_BF(download_want_authority_t)
+
+/** Enumeration: do we want to increment the schedule position each time a
+ * connection is attempted (these attempts can be concurrent), or do we want
+ * to increment the schedule position after a connection fails? */
+typedef enum {
+ DL_SCHED_INCREMENT_FAILURE = 0,
+ DL_SCHED_INCREMENT_ATTEMPT = 1,
+} download_schedule_increment_t;
+#define download_schedule_increment_bitfield_t \
+ ENUM_BF(download_schedule_increment_t)
+
/** Information about our plans for retrying downloads for a downloadable
- * object. */
+ * directory object.
+ * Each type of downloadable directory object has a corresponding retry
+ * <b>schedule</b>, which can be different depending on whether the object is
+ * being downloaded from an authority or a mirror (<b>want_authority</b>).
+ * <b>next_attempt_at</b> contains the next time we will attempt to download
+ * the object.
+ * For schedules that <b>increment_on</b> failure, <b>n_download_failures</b>
+ * is used to determine the position in the schedule. (Each schedule is a
+ * smartlist of integer delays, parsed from a CSV option.) Every time a
+ * connection attempt fails, <b>n_download_failures</b> is incremented,
+ * the new delay value is looked up from the schedule, and
+ * <b>next_attempt_at</b> is set delay seconds from the time the previous
+ * connection failed. Therefore, at most one failure-based connection can be
+ * in progress for each download_status_t.
+ * For schedules that <b>increment_on</b> attempt, <b>n_download_attempts</b>
+ * is used to determine the position in the schedule. Every time a
+ * connection attempt is made, <b>n_download_attempts</b> is incremented,
+ * the new delay value is looked up from the schedule, and
+ * <b>next_attempt_at</b> is set delay seconds from the time the previous
+ * connection was attempted. Therefore, multiple concurrent attempted-based
+ * connections can be in progress for each download_status_t.
+ * After an object is successfully downloaded, any other concurrent connections
+ * are terminated. A new schedule which starts at position 0 is used for
+ * subsequent downloads of the same object.
+ */
typedef struct download_status_t {
- time_t next_attempt_at; /**< When should we try downloading this descriptor
+ time_t next_attempt_at; /**< When should we try downloading this object
* again? */
- uint8_t n_download_failures; /**< Number of failures trying to download the
- * most recent descriptor. */
- download_schedule_bitfield_t schedule : 8;
-
+ uint8_t n_download_failures; /**< Number of failed downloads of the most
+ * recent object, since the last success. */
+ uint8_t n_download_attempts; /**< Number of (potentially concurrent) attempts
+ * to download the most recent object, since
+ * the last success. */
+ download_schedule_bitfield_t schedule : 8; /**< What kind of object is being
+ * downloaded? This determines the
+ * schedule used for the download.
+ */
+ download_want_authority_bitfield_t want_authority : 1; /**< Is the download
+ * happening from an authority
+ * or a mirror? This determines
+ * the schedule used for the
+ * download. */
+ download_schedule_increment_bitfield_t increment_on : 1; /**< does this
+ * schedule increment on each attempt,
+ * or after each failure? */
} download_status_t;
/** If n_download_failures is this high, the download can never happen. */
@@ -2079,6 +2158,15 @@ typedef struct {
* tests for it. */
unsigned int needs_retest_if_added:1;
+ /** True iff this router included "tunnelled-dir-server" in its descriptor,
+ * implying it accepts tunnelled directory requests, or it advertised
+ * dir_port > 0. */
+ unsigned int supports_tunnelled_dir_requests:1;
+
+ /** Used during voting to indicate that we should not include an entry for
+ * this routerinfo. Used only during voting. */
+ unsigned int omit_from_vote: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
* uploaded it. */
@@ -2132,7 +2220,7 @@ typedef struct routerstatus_t {
/** Digest of the router's most recent descriptor or microdescriptor.
* If it's a descriptor, we only use the first DIGEST_LEN bytes. */
char descriptor_digest[DIGEST256_LEN];
- uint32_t addr; /**< IPv4 address for this router. */
+ uint32_t addr; /**< IPv4 address for this router, in host order. */
uint16_t or_port; /**< OR port for this router. */
uint16_t dir_port; /**< Directory port for this router. */
tor_addr_t ipv6_addr; /**< IPv6 address for this router. */
@@ -2156,6 +2244,9 @@ typedef struct routerstatus_t {
* an exit node. */
unsigned int is_hs_dir:1; /**< True iff this router is a v2-or-later hidden
* service directory. */
+ unsigned int is_v2_dir:1; /** True iff this router publishes an open DirPort
+ * or it claims to accept tunnelled dir requests.
+ */
/** True iff we know version info for this router. (i.e., a "v" entry was
* included.) We'll replace all these with a big tor_version_t or a char[]
* if the number of traits we care about ever becomes incredibly big. */
@@ -2257,7 +2348,7 @@ typedef struct microdesc_t {
curve25519_public_key_t *onion_curve25519_pkey;
/** Ed25519 identity key, if included. */
ed25519_public_key_t *ed25519_identity_pkey;
- /** As routerinfo_t.ipv6_add */
+ /** As routerinfo_t.ipv6_addr */
tor_addr_t ipv6_addr;
/** As routerinfo_t.ipv6_orport */
uint16_t ipv6_orport;
@@ -2275,7 +2366,7 @@ typedef struct microdesc_t {
* Specifically, a node_t is a Tor router as we are using it: a router that
* we are considering for circuits, connections, and so on. A node_t is a
* thin wrapper around the routerstatus, routerinfo, and microdesc for a
- * single wrapper, and provides a consistent interface for all of them.
+ * single router, and provides a consistent interface for all of them.
*
* Also, a node_t has mutable state. While a routerinfo, a routerstatus,
* and a microdesc have[*] only the information read from a router
@@ -2332,7 +2423,8 @@ typedef struct node_t {
/* Local info: derived. */
- /** True if the IPv6 OR port is preferred over the IPv4 OR port. */
+ /** True if the IPv6 OR port is preferred over the IPv4 OR port.
+ * XX/teor - can this become out of date if the torrc changes? */
unsigned int ipv6_preferred:1;
/** According to the geoip db what country is this router in? */
@@ -2371,8 +2463,13 @@ typedef struct vote_routerstatus_t {
char *version; /**< The version that the authority says this router is
* running. */
unsigned int has_measured_bw:1; /**< The vote had a measured bw */
- unsigned int has_ed25519_listing:1; /** DOCDOC */
- unsigned int ed25519_reflects_consensus:1; /** DOCDOC */
+ /** True iff the vote included an entry for ed25519 ID, or included
+ * "id ed25519 none" to indicate that there was no ed25519 ID. */
+ unsigned int has_ed25519_listing:1;
+ /** True if the Ed25519 listing here is the consensus-opinion for the
+ * Ed25519 listing; false if there was no consensus on Ed25519 key status,
+ * or if this VRS doesn't reflect it. */
+ unsigned int ed25519_reflects_consensus:1;
uint32_t measured_bw_kb; /**< Measured bandwidth (capacity) of the router */
/** The hash or hashes that the authority claims this microdesc has. */
vote_microdesc_hash_t *microdesc;
@@ -2490,7 +2587,7 @@ typedef struct networkstatus_t {
struct authority_cert_t *cert; /**< Vote only: the voter's certificate. */
/** Digests of this document, as signed. */
- digests_t digests;
+ common_digests_t digests;
/** List of router statuses, sorted by identity digest. For a vote,
* the elements are vote_routerstatus_t; for a consensus, the elements
@@ -2877,6 +2974,11 @@ typedef struct circuit_t {
* circuits entered certain states. This usage probably won't
* interfere with this field's primary purpose, but we should
* document it more thoroughly to make sure of that.
+ *
+ * XXX027 The SocksPort option KeepaliveIsolateSOCKSAuth will artificially
+ * adjust this value forward each time a suitable stream is attached to an
+ * already constructed circuit, potentially keeping the circuit alive
+ * indefinitely.
*/
time_t timestamp_dirty;
@@ -2885,6 +2987,14 @@ typedef struct circuit_t {
* where this circuit was marked.) */
const char *marked_for_close_file; /**< For debugging: in which file was this
* circuit marked for close? */
+ /** For what reason (See END_CIRC_REASON...) is this circuit being closed?
+ * This field is set in circuit_mark_for_close and used later in
+ * circuit_about_to_free. */
+ uint16_t marked_for_close_reason;
+ /** As marked_for_close_reason, but reflects the underlying reason for
+ * closing this circuit.
+ */
+ uint16_t marked_for_close_orig_reason;
/** Unique ID for measuring tunneled network status requests. */
uint64_t dirreq_id;
@@ -3274,27 +3384,27 @@ static const origin_circuit_t *CONST_TO_ORIGIN_CIRCUIT(const circuit_t *);
/** Return 1 iff <b>node</b> has Exit flag and no BadExit flag.
* Otherwise, return 0.
*/
-static INLINE int node_is_good_exit(const node_t *node)
+static inline int node_is_good_exit(const node_t *node)
{
return node->is_exit && ! node->is_bad_exit;
}
-static INLINE or_circuit_t *TO_OR_CIRCUIT(circuit_t *x)
+static inline or_circuit_t *TO_OR_CIRCUIT(circuit_t *x)
{
tor_assert(x->magic == OR_CIRCUIT_MAGIC);
return DOWNCAST(or_circuit_t, x);
}
-static INLINE const or_circuit_t *CONST_TO_OR_CIRCUIT(const circuit_t *x)
+static inline const or_circuit_t *CONST_TO_OR_CIRCUIT(const circuit_t *x)
{
tor_assert(x->magic == OR_CIRCUIT_MAGIC);
return DOWNCAST(or_circuit_t, x);
}
-static INLINE origin_circuit_t *TO_ORIGIN_CIRCUIT(circuit_t *x)
+static inline origin_circuit_t *TO_ORIGIN_CIRCUIT(circuit_t *x)
{
tor_assert(x->magic == ORIGIN_CIRCUIT_MAGIC);
return DOWNCAST(origin_circuit_t, x);
}
-static INLINE const origin_circuit_t *CONST_TO_ORIGIN_CIRCUIT(
+static inline const origin_circuit_t *CONST_TO_ORIGIN_CIRCUIT(
const circuit_t *x)
{
tor_assert(x->magic == ORIGIN_CIRCUIT_MAGIC);
@@ -3360,6 +3470,7 @@ typedef struct port_cfg_t {
unsigned is_group_writable : 1;
unsigned is_world_writable : 1;
+ unsigned relax_dirmode_check : 1;
entry_port_cfg_t entry_cfg;
@@ -3417,9 +3528,11 @@ typedef struct {
* each log message occurs? */
int TruncateLogFile; /**< Boolean: Should we truncate the log file
before we start writing? */
+ char *SyslogIdentityTag; /**< Identity tag to add for syslog logging. */
char *DebugLogFile; /**< Where to send verbose log messages. */
char *DataDirectory; /**< OR only: where to store long-term data. */
+ int DataDirectoryGroupReadable; /**< Boolean: Is the DataDirectory g+r? */
char *Nickname; /**< OR only: nickname of this onion router. */
char *Address; /**< OR only: configured address for this onion router. */
char *PidFile; /**< Where to store PID of Tor process. */
@@ -3681,7 +3794,7 @@ typedef struct {
* and try a new circuit if the stream has been
* waiting for this many seconds. If zero, use
* our default internal timeout schedule. */
- int MaxOnionQueueDelay; /**<DOCDOC*/
+ int MaxOnionQueueDelay; /*< DOCDOC */
int NewCircuitPeriod; /**< How long do we use a circuit before building
* a new one? */
int MaxCircuitDirtiness; /**< Never use circs that were first used more than
@@ -3741,6 +3854,8 @@ typedef struct {
/** List of fallback directory servers */
config_line_t *FallbackDir;
+ /** Whether to use the default hard-coded FallbackDirs */
+ int UseDefaultFallbackDirs;
/** Weight to apply to all directory authority rates if considering them
* along with fallbackdirs */
@@ -3783,6 +3898,7 @@ typedef struct {
* number of servers per IP address shared
* with an authority. */
int AuthDirHasIPv6Connectivity; /**< Boolean: are we on IPv6? */
+ int AuthDirPinKeys; /**< Boolean: Do we enforce key-pinning? */
/** If non-zero, always vote the Fast flag for any relay advertising
* this amount of capacity or more. */
@@ -3799,9 +3915,11 @@ typedef struct {
* hibernate." */
/** How do we determine when our AccountingMax has been reached?
* "max" for when in or out reaches AccountingMax
- * "sum for when in plus out reaches AccountingMax */
+ * "sum" for when in plus out reaches AccountingMax
+ * "in" for when in reaches AccountingMax
+ * "out" for when out reaches AccountingMax */
char *AccountingRule_option;
- enum { ACCT_MAX, ACCT_SUM } AccountingRule;
+ enum { ACCT_MAX, ACCT_SUM, ACCT_IN, ACCT_OUT } AccountingRule;
/** Base64-encoded hash of accepted passwords for the control system. */
config_line_t *HashedControlPassword;
@@ -3875,6 +3993,10 @@ typedef struct {
/** Should we fetch our dir info at the start of the consensus period? */
int FetchDirInfoExtraEarly;
+ int DirCache; /**< Cache all directory documents and accept requests via
+ * tunnelled dir conns from clients. If 1, enabled (default);
+ * If 0, disabled. */
+
char *VirtualAddrNetworkIPv4; /**< Address and mask to hand out for virtual
* MAPADDRESS requests for IPv4 addresses */
char *VirtualAddrNetworkIPv6; /**< Address and mask to hand out for virtual
@@ -3975,12 +4097,24 @@ typedef struct {
* over randomly chosen exits. */
int ClientRejectInternalAddresses;
- /** If true, clients may connect over IPv6. XXX we don't really
- enforce this -- clients _may_ set up outgoing IPv6 connections
- even when this option is not set. */
+ /** If true, clients may connect over IPv4. If false, they will avoid
+ * connecting over IPv4. We enforce this for OR and Dir connections. */
+ int ClientUseIPv4;
+ /** If true, clients may connect over IPv6. If false, they will avoid
+ * connecting over IPv4. We enforce this for OR and Dir connections.
+ * Use fascist_firewall_use_ipv6() instead of accessing this value
+ * directly. */
int ClientUseIPv6;
- /** If true, prefer an IPv6 OR port over an IPv4 one. */
+ /** If true, prefer an IPv6 OR port over an IPv4 one for entry node
+ * connections. If auto, bridge clients prefer IPv6, and other clients
+ * prefer IPv4. Use node_ipv6_or_preferred() instead of accessing this value
+ * directly. */
int ClientPreferIPv6ORPort;
+ /** If true, prefer an IPv6 directory port over an IPv4 one for direct
+ * directory connections. If auto, bridge clients prefer IPv6, and other
+ * clients prefer IPv4. Use fascist_firewall_prefer_ipv6_dirport() instead of
+ * accessing this value directly. */
+ int ClientPreferIPv6DirPort;
/** The length of time that we think a consensus should be fresh. */
int V3AuthVotingInterval;
@@ -4006,7 +4140,7 @@ typedef struct {
char *ConsensusParams;
/** Authority only: minimum number of measured bandwidths we must see
- * before we only beliee measured bandwidths to assign flags. */
+ * before we only believe measured bandwidths to assign flags. */
int MinMeasuredBWsForAuthToIgnoreAdvertised;
/** The length of time that we think an initial consensus should be fresh.
@@ -4051,6 +4185,36 @@ typedef struct {
* on testing networks. */
smartlist_t *TestingClientConsensusDownloadSchedule;
+ /** Schedule for when clients should download consensuses from authorities
+ * if they are bootstrapping (that is, they don't have a usable, reasonably
+ * live consensus). Only used by clients fetching from a list of fallback
+ * directory mirrors.
+ *
+ * This schedule is incremented by (potentially concurrent) connection
+ * attempts, unlike other schedules, which are incremented by connection
+ * failures. Only altered on testing networks. */
+ smartlist_t *ClientBootstrapConsensusAuthorityDownloadSchedule;
+
+ /** Schedule for when clients should download consensuses from fallback
+ * directory mirrors if they are bootstrapping (that is, they don't have a
+ * usable, reasonably live consensus). Only used by clients fetching from a
+ * list of fallback directory mirrors.
+ *
+ * This schedule is incremented by (potentially concurrent) connection
+ * attempts, unlike other schedules, which are incremented by connection
+ * failures. Only altered on testing networks. */
+ smartlist_t *ClientBootstrapConsensusFallbackDownloadSchedule;
+
+ /** Schedule for when clients should download consensuses from authorities
+ * if they are bootstrapping (that is, they don't have a usable, reasonably
+ * live consensus). Only used by clients which don't have or won't fetch
+ * from a list of fallback directory mirrors.
+ *
+ * This schedule is incremented by (potentially concurrent) connection
+ * attempts, unlike other schedules, which are incremented by connection
+ * failures. Only altered on testing networks. */
+ smartlist_t *ClientBootstrapConsensusAuthorityOnlyDownloadSchedule;
+
/** Schedule for when clients should download bridge descriptors. Only
* altered on testing networks. */
smartlist_t *TestingBridgeDownloadSchedule;
@@ -4068,6 +4232,21 @@ typedef struct {
* up? Only altered on testing networks. */
int TestingConsensusMaxDownloadTries;
+ /** How many times will a client try to fetch a consensus while
+ * bootstrapping using a list of fallback directories, before it gives up?
+ * Only altered on testing networks. */
+ int ClientBootstrapConsensusMaxDownloadTries;
+
+ /** How many times will a client try to fetch a consensus while
+ * bootstrapping using only a list of authorities, before it gives up?
+ * Only altered on testing networks. */
+ int ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries;
+
+ /** How many simultaneous in-progress connections will we make when trying
+ * to fetch a consensus before we wait for one to complete, timeout, or
+ * error out? Only altered on testing networks. */
+ int ClientBootstrapConsensusMaxInProgressTries;
+
/** How many times will we try to download a router's descriptor before
* giving up? Only altered on testing networks. */
int TestingDescriptorMaxDownloadTries;
@@ -4295,6 +4474,10 @@ typedef struct {
/** How long before auth keys expire will we try to make a new one? */
int TestingAuthKeySlop;
+ /** Force use of offline master key features: never generate a master
+ * ed25519 identity key except from tor --keygen */
+ int OfflineMasterKey;
+
enum {
FORCE_PASSPHRASE_AUTO=0,
FORCE_PASSPHRASE_ON,
@@ -4302,6 +4485,11 @@ typedef struct {
} keygen_force_passphrase;
int use_keygen_passphrase_fd;
int keygen_passphrase_fd;
+ int change_key_passphrase;
+ char *master_key_fname;
+
+ /** Autobool: Do we try to retain capabilities if we can? */
+ int KeepBindCapabilities;
} or_options_t;
/** Persistent state for an onion router, as saved to disk. */
@@ -4374,7 +4562,7 @@ typedef struct {
/** Change the next_write time of <b>state</b> to <b>when</b>, unless the
* state is already scheduled to be written to disk earlier than <b>when</b>.
*/
-static INLINE void or_state_mark_dirty(or_state_t *state, time_t when)
+static inline void or_state_mark_dirty(or_state_t *state, time_t when)
{
if (state->next_write > when)
state->next_write = when;
@@ -4985,9 +5173,13 @@ typedef struct dir_server_t {
char *description;
char *nickname;
char *address; /**< Hostname. */
+ /* XX/teor - why do we duplicate the address and port fields here and in
+ * fake_status? Surely we could just use fake_status (#17867). */
+ tor_addr_t ipv6_addr; /**< IPv6 address if present; AF_UNSPEC if not */
uint32_t addr; /**< IPv4 address. */
uint16_t dir_port; /**< Directory port. */
uint16_t or_port; /**< OR port: Used for tunneling connections. */
+ uint16_t ipv6_orport; /**< OR port corresponding to ipv6_addr. */
double weight; /** Weight used when selecting this node at random */
char digest[DIGEST_LEN]; /**< Digest of identity key. */
char v3_identity_digest[DIGEST_LEN]; /**< Digest of v3 (authority only,
@@ -5068,7 +5260,9 @@ typedef enum {
CRN_ALLOW_INVALID = 1<<3,
/* XXXX not used, apparently. */
CRN_WEIGHT_AS_EXIT = 1<<5,
- CRN_NEED_DESC = 1<<6
+ CRN_NEED_DESC = 1<<6,
+ /* On clients, only provide nodes that satisfy ClientPreferIPv6OR */
+ CRN_PREF_ADDR = 1<<7
} router_crn_flags_t;
/** Return value for router_add_to_routerlist() and dirserv_add_descriptor() */
diff --git a/src/or/periodic.c b/src/or/periodic.c
new file mode 100644
index 0000000000..057fcf672e
--- /dev/null
+++ b/src/or/periodic.c
@@ -0,0 +1,126 @@
+/* Copyright (c) 2015-2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file periodic.c
+ *
+ * \brief Generic backend for handling periodic events.
+ */
+
+#include "or.h"
+#include "compat_libevent.h"
+#include "config.h"
+#include "periodic.h"
+
+#ifdef HAVE_EVENT2_EVENT_H
+#include <event2/event.h>
+#else
+#include <event.h>
+#endif
+
+/** We disable any interval greater than this number of seconds, on the
+ * grounds that it is probably an absolute time mistakenly passed in as a
+ * relative time.
+ */
+static const int MAX_INTERVAL = 10 * 365 * 86400;
+
+/** Set the event <b>event</b> to run in <b>next_interval</b> seconds from
+ * now. */
+static void
+periodic_event_set_interval(periodic_event_item_t *event,
+ time_t next_interval)
+{
+ tor_assert(next_interval < MAX_INTERVAL);
+ struct timeval tv;
+ tv.tv_sec = next_interval;
+ tv.tv_usec = 0;
+ event_add(event->ev, &tv);
+}
+
+/** Wraps dispatches for periodic events, <b>data</b> will be a pointer to the
+ * event that needs to be called */
+static void
+periodic_event_dispatch(evutil_socket_t fd, short what, void *data)
+{
+ (void)fd;
+ (void)what;
+ periodic_event_item_t *event = data;
+
+ time_t now = time(NULL);
+ const or_options_t *options = get_options();
+// log_debug(LD_GENERAL, "Dispatching %s", event->name);
+ int r = event->fn(now, options);
+ int next_interval = 0;
+
+ /* update the last run time if action was taken */
+ if (r==0) {
+ log_err(LD_BUG, "Invalid return value for periodic event from %s.",
+ event->name);
+ tor_assert(r != 0);
+ } else if (r > 0) {
+ event->last_action_time = now;
+ /* If the event is meant to happen after ten years, that's likely
+ * a bug, and somebody gave an absolute time rather than an interval.
+ */
+ tor_assert(r < MAX_INTERVAL);
+ next_interval = r;
+ } else {
+ /* no action was taken, it is likely a precondition failed,
+ * we should reschedule for next second incase the precondition
+ * passes then */
+ next_interval = 1;
+ }
+
+// log_debug(LD_GENERAL, "Scheduling %s for %d seconds", event->name,
+// next_interval);
+ struct timeval tv = { next_interval , 0 };
+ event_add(event->ev, &tv);
+}
+
+/** Schedules <b>event</b> to run as soon as possible from now. */
+void
+periodic_event_reschedule(periodic_event_item_t *event)
+{
+ periodic_event_set_interval(event, 1);
+}
+
+/** Initializes the libevent backend for a periodic event. */
+void
+periodic_event_setup(periodic_event_item_t *event)
+{
+ if (event->ev) { /* Already setup? This is a bug */
+ log_err(LD_BUG, "Initial dispatch should only be done once.");
+ tor_assert(0);
+ }
+
+ event->ev = tor_event_new(tor_libevent_get_base(),
+ -1, 0,
+ periodic_event_dispatch,
+ event);
+ tor_assert(event->ev);
+}
+
+/** Handles initial dispatch for periodic events. It should happen 1 second
+ * after the events are created to mimic behaviour before #3199's refactor */
+void
+periodic_event_launch(periodic_event_item_t *event)
+{
+ if (! event->ev) { /* Not setup? This is a bug */
+ log_err(LD_BUG, "periodic_event_launch without periodic_event_setup");
+ tor_assert(0);
+ }
+
+ // Initial dispatch
+ periodic_event_dispatch(-1, EV_TIMEOUT, event);
+}
+
+/** Release all storage associated with <b>event</b> */
+void
+periodic_event_destroy(periodic_event_item_t *event)
+{
+ if (!event)
+ return;
+ tor_event_free(event->ev);
+ event->last_action_time = 0;
+}
+
diff --git a/src/or/periodic.h b/src/or/periodic.h
new file mode 100644
index 0000000000..021bb4ef5c
--- /dev/null
+++ b/src/or/periodic.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2015-2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_PERIODIC_H
+#define TOR_PERIODIC_H
+
+#define PERIODIC_EVENT_NO_UPDATE (-1)
+
+/** Callback function for a periodic event to take action. The return value
+* influences the next time the function will get called. Return
+* PERIODIC_EVENT_NO_UPDATE to not update <b>last_action_time</b> and be polled
+* again in the next second. If a positive value is returned it will update the
+* interval time. */
+typedef int (*periodic_event_helper_t)(time_t now,
+ const or_options_t *options);
+
+struct event;
+
+/** A single item for the periodic-events-function table. */
+typedef struct periodic_event_item_t {
+ periodic_event_helper_t fn; /**< The function to run the event */
+ time_t last_action_time; /**< The last time the function did something */
+ struct event *ev; /**< Libevent callback we're using to implement this */
+ const char *name; /**< Name of the function -- for debug */
+} periodic_event_item_t;
+
+/** events will get their interval from first execution */
+#define PERIODIC_EVENT(fn) { fn##_callback, 0, NULL, #fn }
+#define END_OF_PERIODIC_EVENTS { NULL, 0, NULL, NULL }
+
+void periodic_event_launch(periodic_event_item_t *event);
+void periodic_event_setup(periodic_event_item_t *event);
+void periodic_event_destroy(periodic_event_item_t *event);
+void periodic_event_reschedule(periodic_event_item_t *event);
+
+#endif
+
diff --git a/src/or/policies.c b/src/or/policies.c
index 560b8cb4c3..2703d7edef 100644
--- a/src/or/policies.c
+++ b/src/or/policies.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -8,9 +8,12 @@
* \brief Code to parse and use address policies and exit policies.
**/
+#define POLICIES_PRIVATE
+
#include "or.h"
#include "config.h"
#include "dirserv.h"
+#include "networkstatus.h"
#include "nodelist.h"
#include "policies.h"
#include "router.h"
@@ -62,12 +65,15 @@ static const char *private_nets[] = {
NULL
};
-static int policies_parse_exit_policy_internal(config_line_t *cfg,
- smartlist_t **dest,
- int ipv6_exit,
- int rejectprivate,
- uint32_t local_address,
- int add_default_policy);
+static int policies_parse_exit_policy_internal(
+ config_line_t *cfg,
+ smartlist_t **dest,
+ int ipv6_exit,
+ int rejectprivate,
+ const smartlist_t *configured_addresses,
+ int reject_interface_addresses,
+ int reject_configured_port_addresses,
+ int add_default_policy);
/** Replace all "private" entries in *<b>policy</b> with their expanded
* equivalents. */
@@ -97,7 +103,7 @@ policy_expand_private(smartlist_t **policy)
if (tor_addr_parse_mask_ports(private_nets[i], 0,
&newpolicy.addr,
&newpolicy.maskbits, &port_min, &port_max)<0) {
- tor_assert(0);
+ tor_assert_unreached();
}
smartlist_add(tmp, addr_policy_get_canonical_entry(&newpolicy));
}
@@ -152,7 +158,7 @@ policy_expand_unspec(smartlist_t **policy)
}
/**
- * Given a linked list of config lines containing "allow" and "deny"
+ * Given a linked list of config lines containing "accept[6]" and "reject[6]"
* tokens, parse them and append the result to <b>dest</b>. Return -1
* if any tokens are malformed (and don't append any), else return 0.
*
@@ -167,6 +173,7 @@ parse_addr_policy(config_line_t *cfg, smartlist_t **dest,
smartlist_t *result;
smartlist_t *entries;
addr_policy_t *item;
+ int malformed_list;
int r = 0;
if (!cfg)
@@ -179,12 +186,22 @@ parse_addr_policy(config_line_t *cfg, smartlist_t **dest,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
SMARTLIST_FOREACH_BEGIN(entries, const char *, ent) {
log_debug(LD_CONFIG,"Adding new entry '%s'",ent);
- item = router_parse_addr_policy_item_from_string(ent, assume_action);
+ malformed_list = 0;
+ item = router_parse_addr_policy_item_from_string(ent, assume_action,
+ &malformed_list);
if (item) {
smartlist_add(result, item);
- } else {
- log_warn(LD_CONFIG,"Malformed policy '%s'.", ent);
+ } else if (malformed_list) {
+ /* the error is so severe the entire list should be discarded */
+ log_warn(LD_CONFIG, "Malformed policy '%s'. Discarding entire policy "
+ "list.", ent);
r = -1;
+ } else {
+ /* the error is minor: don't add the item, but keep processing the
+ * rest of the policies in the list */
+ log_debug(LD_CONFIG, "Ignored policy '%s' due to non-fatal error. "
+ "The remainder of the policy list will be used.",
+ ent);
}
} SMARTLIST_FOREACH_END(ent);
SMARTLIST_FOREACH(entries, char *, ent, tor_free(ent));
@@ -254,16 +271,76 @@ parse_reachable_addresses(void)
"Error parsing ReachableDirAddresses entry; ignoring.");
ret = -1;
}
+
+ /* We ignore ReachableAddresses for relays */
+ if (!server_mode(options)) {
+ if ((reachable_or_addr_policy
+ && policy_is_reject_star(reachable_or_addr_policy, AF_UNSPEC))
+ || (reachable_dir_addr_policy
+ && policy_is_reject_star(reachable_dir_addr_policy, AF_UNSPEC))) {
+ log_warn(LD_CONFIG, "Tor cannot connect to the Internet if "
+ "ReachableAddresses, ReachableORAddresses, or "
+ "ReachableDirAddresses reject all addresses. Please accept "
+ "some addresses in these options.");
+ } else if (options->ClientUseIPv4 == 1
+ && ((reachable_or_addr_policy
+ && policy_is_reject_star(reachable_or_addr_policy, AF_INET))
+ || (reachable_dir_addr_policy
+ && policy_is_reject_star(reachable_dir_addr_policy, AF_INET)))) {
+ log_warn(LD_CONFIG, "You have set ClientUseIPv4 1, but "
+ "ReachableAddresses, ReachableORAddresses, or "
+ "ReachableDirAddresses reject all IPv4 addresses. "
+ "Tor will not connect using IPv4.");
+ } else if (fascist_firewall_use_ipv6(options)
+ && ((reachable_or_addr_policy
+ && policy_is_reject_star(reachable_or_addr_policy, AF_INET6))
+ || (reachable_dir_addr_policy
+ && policy_is_reject_star(reachable_dir_addr_policy, AF_INET6)))) {
+ log_warn(LD_CONFIG, "You have configured tor to use IPv6 "
+ "(ClientUseIPv6 1 or UseBridges 1), but "
+ "ReachableAddresses, ReachableORAddresses, or "
+ "ReachableDirAddresses reject all IPv6 addresses. "
+ "Tor will not connect using IPv6.");
+ }
+ }
+
return ret;
}
-/** Return true iff the firewall options might block any address:port
- * combination.
+/* Return true iff ClientUseIPv4 0 or ClientUseIPv6 0 might block any OR or Dir
+ * address:port combination. */
+static int
+firewall_is_fascist_impl(void)
+{
+ const or_options_t *options = get_options();
+ /* Assume every non-bridge relay has an IPv4 address.
+ * Clients which use bridges may only know the IPv6 address of their
+ * bridge. */
+ return (options->ClientUseIPv4 == 0
+ || (!fascist_firewall_use_ipv6(options)
+ && options->UseBridges == 1));
+}
+
+/** Return true iff the firewall options, including ClientUseIPv4 0 and
+ * ClientUseIPv6 0, might block any OR address:port combination.
+ * Address preferences may still change which address is selected even if
+ * this function returns false.
*/
int
firewall_is_fascist_or(void)
{
- return reachable_or_addr_policy != NULL;
+ return (reachable_or_addr_policy != NULL || firewall_is_fascist_impl());
+}
+
+/** Return true iff the firewall options, including ClientUseIPv4 0 and
+ * ClientUseIPv6 0, might block any Dir address:port combination.
+ * Address preferences may still change which address is selected even if
+ * this function returns false.
+ */
+int
+firewall_is_fascist_dir(void)
+{
+ return (reachable_dir_addr_policy != NULL || firewall_is_fascist_impl());
}
/** Return true iff <b>policy</b> (possibly NULL) will allow a
@@ -301,49 +378,624 @@ addr_policy_permits_address(uint32_t addr, uint16_t port,
return addr_policy_permits_tor_addr(&a, port, policy);
}
-/** Return true iff we think our firewall will let us make an OR connection to
- * addr:port. */
-int
-fascist_firewall_allows_address_or(const tor_addr_t *addr, uint16_t port)
+/** Return true iff we think our firewall will let us make a connection to
+ * addr:port.
+ *
+ * If we are configured as a server, ignore any address family preference and
+ * just use IPv4.
+ * Otherwise:
+ * - return false for all IPv4 addresses:
+ * - if ClientUseIPv4 is 0, or
+ * if pref_only and pref_ipv6 are both true;
+ * - return false for all IPv6 addresses:
+ * - if fascist_firewall_use_ipv6() is 0, or
+ * - if pref_only is true and pref_ipv6 is false.
+ *
+ * Return false if addr is NULL or tor_addr_is_null(), or if port is 0. */
+STATIC int
+fascist_firewall_allows_address(const tor_addr_t *addr,
+ uint16_t port,
+ smartlist_t *firewall_policy,
+ int pref_only, int pref_ipv6)
{
+ const or_options_t *options = get_options();
+ const int client_mode = !server_mode(options);
+
+ if (!addr || tor_addr_is_null(addr) || !port) {
+ return 0;
+ }
+
+ /* Clients stop using IPv4 if it's disabled. In most cases, clients also
+ * stop using IPv4 if it's not preferred.
+ * Servers must have IPv4 enabled and preferred. */
+ if (tor_addr_family(addr) == AF_INET && client_mode &&
+ (!options->ClientUseIPv4 || (pref_only && pref_ipv6))) {
+ return 0;
+ }
+
+ /* Clients and Servers won't use IPv6 unless it's enabled (and in most
+ * cases, IPv6 must also be preferred before it will be used). */
+ if (tor_addr_family(addr) == AF_INET6 &&
+ (!fascist_firewall_use_ipv6(options) || (pref_only && !pref_ipv6))) {
+ return 0;
+ }
+
return addr_policy_permits_tor_addr(addr, port,
- reachable_or_addr_policy);
+ firewall_policy);
}
-/** Return true iff we think our firewall will let us make an OR connection to
- * <b>ri</b>. */
+/** Is this client configured to use IPv6?
+ * Use node_ipv6_or/dir_preferred() when checking a specific node and OR/Dir
+ * port: it supports bridge client per-node IPv6 preferences.
+ */
int
-fascist_firewall_allows_or(const routerinfo_t *ri)
+fascist_firewall_use_ipv6(const or_options_t *options)
{
- /* XXXX proposal 118 */
- tor_addr_t addr;
- tor_addr_from_ipv4h(&addr, ri->addr);
- return fascist_firewall_allows_address_or(&addr, ri->or_port);
+ /* Clients use IPv6 if it's set, or they use bridges, or they don't use
+ * IPv4 */
+ return (options->ClientUseIPv6 == 1 || options->UseBridges == 1
+ || options->ClientUseIPv4 == 0);
}
-/** Return true iff we think our firewall will let us make an OR connection to
- * <b>node</b>. */
+/** Do we prefer to connect to IPv6, ignoring ClientPreferIPv6ORPort and
+ * ClientPreferIPv6DirPort?
+ * If we're unsure, return -1, otherwise, return 1 for IPv6 and 0 for IPv4.
+ */
+static int
+fascist_firewall_prefer_ipv6_impl(const or_options_t *options)
+{
+ /*
+ Cheap implementation of config options ClientUseIPv4 & ClientUseIPv6 --
+ If we're a server or IPv6 is disabled, use IPv4.
+ If IPv4 is disabled, use IPv6.
+ */
+
+ if (server_mode(options) || !fascist_firewall_use_ipv6(options)) {
+ return 0;
+ }
+
+ if (!options->ClientUseIPv4) {
+ return 1;
+ }
+
+ return -1;
+}
+
+/** Do we prefer to connect to IPv6 ORPorts?
+ * Use node_ipv6_or_preferred() whenever possible: it supports bridge client
+ * per-node IPv6 preferences.
+ */
int
-fascist_firewall_allows_node(const node_t *node)
+fascist_firewall_prefer_ipv6_orport(const or_options_t *options)
{
- if (node->ri) {
- return fascist_firewall_allows_or(node->ri);
- } else if (node->rs) {
- tor_addr_t addr;
- tor_addr_from_ipv4h(&addr, node->rs->addr);
- return fascist_firewall_allows_address_or(&addr, node->rs->or_port);
+ int pref_ipv6 = fascist_firewall_prefer_ipv6_impl(options);
+
+ if (pref_ipv6 >= 0) {
+ return pref_ipv6;
+ }
+
+ /* We can use both IPv4 and IPv6 - which do we prefer? */
+ if (options->ClientPreferIPv6ORPort == 1) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/** Do we prefer to connect to IPv6 DirPorts?
+ *
+ * (node_ipv6_dir_preferred() doesn't support bridge client per-node IPv6
+ * preferences. There's no reason to use it instead of this function.)
+ */
+int
+fascist_firewall_prefer_ipv6_dirport(const or_options_t *options)
+{
+ int pref_ipv6 = fascist_firewall_prefer_ipv6_impl(options);
+
+ if (pref_ipv6 >= 0) {
+ return pref_ipv6;
+ }
+
+ /* We can use both IPv4 and IPv6 - which do we prefer? */
+ if (options->ClientPreferIPv6DirPort == 1) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/** Return true iff we think our firewall will let us make a connection to
+ * addr:port. Uses ReachableORAddresses or ReachableDirAddresses based on
+ * fw_connection.
+ * If pref_only is true, return true if addr is in the client's preferred
+ * address family, which is IPv6 if pref_ipv6 is true, and IPv4 otherwise.
+ * If pref_only is false, ignore pref_ipv6, and return true if addr is allowed.
+ */
+int
+fascist_firewall_allows_address_addr(const tor_addr_t *addr, uint16_t port,
+ firewall_connection_t fw_connection,
+ int pref_only, int pref_ipv6)
+{
+ if (fw_connection == FIREWALL_OR_CONNECTION) {
+ return fascist_firewall_allows_address(addr, port,
+ reachable_or_addr_policy,
+ pref_only, pref_ipv6);
+ } else if (fw_connection == FIREWALL_DIR_CONNECTION) {
+ return fascist_firewall_allows_address(addr, port,
+ reachable_dir_addr_policy,
+ pref_only, pref_ipv6);
} else {
+ log_warn(LD_BUG, "Bad firewall_connection_t value %d.",
+ fw_connection);
+ return 0;
+ }
+}
+
+/** Return true iff we think our firewall will let us make a connection to
+ * addr:port (ap). Uses ReachableORAddresses or ReachableDirAddresses based on
+ * fw_connection.
+ * pref_only and pref_ipv6 work as in fascist_firewall_allows_address_addr().
+ */
+static int
+fascist_firewall_allows_address_ap(const tor_addr_port_t *ap,
+ firewall_connection_t fw_connection,
+ int pref_only, int pref_ipv6)
+{
+ tor_assert(ap);
+ return fascist_firewall_allows_address_addr(&ap->addr, ap->port,
+ fw_connection, pref_only,
+ pref_ipv6);
+}
+
+/* Return true iff we think our firewall will let us make a connection to
+ * ipv4h_or_addr:ipv4_or_port. ipv4h_or_addr is interpreted in host order.
+ * Uses ReachableORAddresses or ReachableDirAddresses based on
+ * fw_connection.
+ * pref_only and pref_ipv6 work as in fascist_firewall_allows_address_addr().
+ */
+static int
+fascist_firewall_allows_address_ipv4h(uint32_t ipv4h_or_addr,
+ uint16_t ipv4_or_port,
+ firewall_connection_t fw_connection,
+ int pref_only, int pref_ipv6)
+{
+ tor_addr_t ipv4_or_addr;
+ tor_addr_from_ipv4h(&ipv4_or_addr, ipv4h_or_addr);
+ return fascist_firewall_allows_address_addr(&ipv4_or_addr, ipv4_or_port,
+ fw_connection, pref_only,
+ pref_ipv6);
+}
+
+/** Return true iff we think our firewall will let us make a connection to
+ * ipv4h_addr/ipv6_addr. Uses ipv4_orport/ipv6_orport/ReachableORAddresses or
+ * ipv4_dirport/ipv6_dirport/ReachableDirAddresses based on IPv4/IPv6 and
+ * <b>fw_connection</b>.
+ * pref_only and pref_ipv6 work as in fascist_firewall_allows_address_addr().
+ */
+static int
+fascist_firewall_allows_base(uint32_t ipv4h_addr, uint16_t ipv4_orport,
+ uint16_t ipv4_dirport,
+ const tor_addr_t *ipv6_addr, uint16_t ipv6_orport,
+ uint16_t ipv6_dirport,
+ firewall_connection_t fw_connection,
+ int pref_only, int pref_ipv6)
+{
+ if (fascist_firewall_allows_address_ipv4h(ipv4h_addr,
+ (fw_connection == FIREWALL_OR_CONNECTION
+ ? ipv4_orport
+ : ipv4_dirport),
+ fw_connection,
+ pref_only, pref_ipv6)) {
return 1;
}
+
+ if (fascist_firewall_allows_address_addr(ipv6_addr,
+ (fw_connection == FIREWALL_OR_CONNECTION
+ ? ipv6_orport
+ : ipv6_dirport),
+ fw_connection,
+ pref_only, pref_ipv6)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/** Like fascist_firewall_allows_base(), but takes ri. */
+static int
+fascist_firewall_allows_ri_impl(const routerinfo_t *ri,
+ firewall_connection_t fw_connection,
+ int pref_only, int pref_ipv6)
+{
+ if (!ri) {
+ return 0;
+ }
+
+ /* Assume IPv4 and IPv6 DirPorts are the same */
+ return fascist_firewall_allows_base(ri->addr, ri->or_port, ri->dir_port,
+ &ri->ipv6_addr, ri->ipv6_orport,
+ ri->dir_port, fw_connection, pref_only,
+ pref_ipv6);
+}
+
+/** Like fascist_firewall_allows_rs, but doesn't consult the node. */
+static int
+fascist_firewall_allows_rs_impl(const routerstatus_t *rs,
+ firewall_connection_t fw_connection,
+ int pref_only, int pref_ipv6)
+{
+ if (!rs) {
+ return 0;
+ }
+
+ /* Assume IPv4 and IPv6 DirPorts are the same */
+ return fascist_firewall_allows_base(rs->addr, rs->or_port, rs->dir_port,
+ &rs->ipv6_addr, rs->ipv6_orport,
+ rs->dir_port, fw_connection, pref_only,
+ pref_ipv6);
}
-/** Return true iff we think our firewall will let us make a directory
- * connection to addr:port. */
+/** Like fascist_firewall_allows_base(), but takes rs.
+ * Consults the corresponding node, then falls back to rs if node is NULL.
+ * This should only happen when there's no valid consensus, and rs doesn't
+ * correspond to a bridge client's bridge.
+ */
int
-fascist_firewall_allows_address_dir(const tor_addr_t *addr, uint16_t port)
+fascist_firewall_allows_rs(const routerstatus_t *rs,
+ firewall_connection_t fw_connection, int pref_only)
{
- return addr_policy_permits_tor_addr(addr, port,
- reachable_dir_addr_policy);
+ if (!rs) {
+ return 0;
+ }
+
+ const node_t *node = node_get_by_id(rs->identity_digest);
+
+ if (node) {
+ return fascist_firewall_allows_node(node, fw_connection, pref_only);
+ } else {
+ /* There's no node-specific IPv6 preference, so use the generic IPv6
+ * preference instead. */
+ const or_options_t *options = get_options();
+ int pref_ipv6 = (fw_connection == FIREWALL_OR_CONNECTION
+ ? fascist_firewall_prefer_ipv6_orport(options)
+ : fascist_firewall_prefer_ipv6_dirport(options));
+
+ return fascist_firewall_allows_rs_impl(rs, fw_connection, pref_only,
+ pref_ipv6);
+ }
+}
+
+/** Return true iff we think our firewall will let us make a connection to
+ * ipv6_addr:ipv6_orport based on ReachableORAddresses.
+ * If <b>fw_connection</b> is FIREWALL_DIR_CONNECTION, returns 0.
+ * pref_only and pref_ipv6 work as in fascist_firewall_allows_address_addr().
+ */
+static int
+fascist_firewall_allows_md_impl(const microdesc_t *md,
+ firewall_connection_t fw_connection,
+ int pref_only, int pref_ipv6)
+{
+ if (!md) {
+ return 0;
+ }
+
+ /* Can't check dirport, it doesn't have one */
+ if (fw_connection == FIREWALL_DIR_CONNECTION) {
+ return 0;
+ }
+
+ /* Also can't check IPv4, doesn't have that either */
+ return fascist_firewall_allows_address_addr(&md->ipv6_addr, md->ipv6_orport,
+ fw_connection, pref_only,
+ pref_ipv6);
+}
+
+/** Like fascist_firewall_allows_base(), but takes node, and looks up pref_ipv6
+ * from node_ipv6_or/dir_preferred(). */
+int
+fascist_firewall_allows_node(const node_t *node,
+ firewall_connection_t fw_connection,
+ int pref_only)
+{
+ if (!node) {
+ return 0;
+ }
+
+ node_assert_ok(node);
+
+ const int pref_ipv6 = (fw_connection == FIREWALL_OR_CONNECTION
+ ? node_ipv6_or_preferred(node)
+ : node_ipv6_dir_preferred(node));
+
+ /* Sometimes, the rs is missing the IPv6 address info, and we need to go
+ * all the way to the md */
+ if (node->ri && fascist_firewall_allows_ri_impl(node->ri, fw_connection,
+ pref_only, pref_ipv6)) {
+ return 1;
+ } else if (node->rs && fascist_firewall_allows_rs_impl(node->rs,
+ fw_connection,
+ pref_only,
+ pref_ipv6)) {
+ return 1;
+ } else if (node->md && fascist_firewall_allows_md_impl(node->md,
+ fw_connection,
+ pref_only,
+ pref_ipv6)) {
+ return 1;
+ } else {
+ /* If we know nothing, assume it's unreachable, we'll never get an address
+ * to connect to. */
+ return 0;
+ }
+}
+
+/** Like fascist_firewall_allows_rs(), but takes ds. */
+int
+fascist_firewall_allows_dir_server(const dir_server_t *ds,
+ firewall_connection_t fw_connection,
+ int pref_only)
+{
+ if (!ds) {
+ return 0;
+ }
+
+ /* A dir_server_t always has a fake_status. As long as it has the same
+ * addresses/ports in both fake_status and dir_server_t, this works fine.
+ * (See #17867.)
+ * This function relies on fascist_firewall_choose_address_rs looking up the
+ * node if it can, because that will get the latest info for the relay. */
+ return fascist_firewall_allows_rs(&ds->fake_status, fw_connection,
+ pref_only);
+}
+
+/** If a and b are both valid and allowed by fw_connection,
+ * choose one based on want_a and return it.
+ * Otherwise, return whichever is allowed.
+ * Otherwise, return NULL.
+ * pref_only and pref_ipv6 work as in fascist_firewall_allows_address_addr().
+ */
+static const tor_addr_port_t *
+fascist_firewall_choose_address_impl(const tor_addr_port_t *a,
+ const tor_addr_port_t *b,
+ int want_a,
+ firewall_connection_t fw_connection,
+ int pref_only, int pref_ipv6)
+{
+ const tor_addr_port_t *use_a = NULL;
+ const tor_addr_port_t *use_b = NULL;
+
+ if (fascist_firewall_allows_address_ap(a, fw_connection, pref_only,
+ pref_ipv6)) {
+ use_a = a;
+ }
+
+ if (fascist_firewall_allows_address_ap(b, fw_connection, pref_only,
+ pref_ipv6)) {
+ use_b = b;
+ }
+
+ /* If both are allowed */
+ if (use_a && use_b) {
+ /* Choose a if we want it */
+ return (want_a ? use_a : use_b);
+ } else {
+ /* Choose a if we have it */
+ return (use_a ? use_a : use_b);
+ }
+}
+
+/** If a and b are both valid and preferred by fw_connection,
+ * choose one based on want_a and return it.
+ * Otherwise, return whichever is preferred.
+ * If neither are preferred, and pref_only is false:
+ * - If a and b are both allowed by fw_connection,
+ * choose one based on want_a and return it.
+ * - Otherwise, return whichever is preferred.
+ * Otherwise, return NULL. */
+STATIC const tor_addr_port_t *
+fascist_firewall_choose_address(const tor_addr_port_t *a,
+ const tor_addr_port_t *b,
+ int want_a,
+ firewall_connection_t fw_connection,
+ int pref_only, int pref_ipv6)
+{
+ const tor_addr_port_t *pref = fascist_firewall_choose_address_impl(
+ a, b, want_a,
+ fw_connection,
+ 1, pref_ipv6);
+ if (pref_only || pref) {
+ /* If there is a preferred address, use it. If we can only use preferred
+ * addresses, and neither address is preferred, pref will be NULL, and we
+ * want to return NULL, so return it. */
+ return pref;
+ } else {
+ /* If there's no preferred address, and we can return addresses that are
+ * not preferred, use an address that's allowed */
+ return fascist_firewall_choose_address_impl(a, b, want_a, fw_connection,
+ 0, pref_ipv6);
+ }
+}
+
+/** Copy an address and port into <b>ap</b> that we think our firewall will
+ * let us connect to. Uses ipv4_addr/ipv6_addr and
+ * ipv4_orport/ipv6_orport/ReachableORAddresses or
+ * ipv4_dirport/ipv6_dirport/ReachableDirAddresses based on IPv4/IPv6 and
+ * <b>fw_connection</b>.
+ * If pref_only, only choose preferred addresses. In either case, choose
+ * a preferred address before an address that's not preferred.
+ * If both addresses could be chosen (they are both preferred or both allowed)
+ * choose IPv6 if pref_ipv6 is true, otherwise choose IPv4.
+ * If neither address is chosen, return 0, else return 1. */
+static int
+fascist_firewall_choose_address_base(const tor_addr_t *ipv4_addr,
+ uint16_t ipv4_orport,
+ uint16_t ipv4_dirport,
+ const tor_addr_t *ipv6_addr,
+ uint16_t ipv6_orport,
+ uint16_t ipv6_dirport,
+ firewall_connection_t fw_connection,
+ int pref_only,
+ int pref_ipv6,
+ tor_addr_port_t* ap)
+{
+ const tor_addr_port_t *result = NULL;
+ const int want_ipv4 = !pref_ipv6;
+
+ tor_assert(ipv6_addr);
+ tor_assert(ap);
+
+ tor_addr_port_t ipv4_ap;
+ tor_addr_copy(&ipv4_ap.addr, ipv4_addr);
+ ipv4_ap.port = (fw_connection == FIREWALL_OR_CONNECTION
+ ? ipv4_orport
+ : ipv4_dirport);
+
+ tor_addr_port_t ipv6_ap;
+ tor_addr_copy(&ipv6_ap.addr, ipv6_addr);
+ ipv6_ap.port = (fw_connection == FIREWALL_OR_CONNECTION
+ ? ipv6_orport
+ : ipv6_dirport);
+
+ result = fascist_firewall_choose_address(&ipv4_ap, &ipv6_ap,
+ want_ipv4,
+ fw_connection, pref_only,
+ pref_ipv6);
+
+ if (result) {
+ tor_addr_copy(&ap->addr, &result->addr);
+ ap->port = result->port;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/** Like fascist_firewall_choose_address_base(), but takes a host-order IPv4
+ * address as the first parameter. */
+static int
+fascist_firewall_choose_address_ipv4h(uint32_t ipv4h_addr,
+ uint16_t ipv4_orport,
+ uint16_t ipv4_dirport,
+ const tor_addr_t *ipv6_addr,
+ uint16_t ipv6_orport,
+ uint16_t ipv6_dirport,
+ firewall_connection_t fw_connection,
+ int pref_only,
+ int pref_ipv6,
+ tor_addr_port_t* ap)
+{
+ tor_addr_t ipv4_addr;
+ tor_addr_from_ipv4h(&ipv4_addr, ipv4h_addr);
+ return fascist_firewall_choose_address_base(&ipv4_addr, ipv4_orport,
+ ipv4_dirport, ipv6_addr,
+ ipv6_orport, ipv6_dirport,
+ fw_connection, pref_only,
+ pref_ipv6, ap);
+}
+
+/** Like fascist_firewall_choose_address_base(), but takes <b>rs</b>.
+ * Consults the corresponding node, then falls back to rs if node is NULL.
+ * This should only happen when there's no valid consensus, and rs doesn't
+ * correspond to a bridge client's bridge.
+ */
+int
+fascist_firewall_choose_address_rs(const routerstatus_t *rs,
+ firewall_connection_t fw_connection,
+ int pref_only, tor_addr_port_t* ap)
+{
+ if (!rs) {
+ return 0;
+ }
+
+ tor_assert(ap);
+
+ const node_t *node = node_get_by_id(rs->identity_digest);
+
+ if (node) {
+ return fascist_firewall_choose_address_node(node, fw_connection, pref_only,
+ ap);
+ } else {
+ /* There's no node-specific IPv6 preference, so use the generic IPv6
+ * preference instead. */
+ const or_options_t *options = get_options();
+ int pref_ipv6 = (fw_connection == FIREWALL_OR_CONNECTION
+ ? fascist_firewall_prefer_ipv6_orport(options)
+ : fascist_firewall_prefer_ipv6_dirport(options));
+
+ /* Assume IPv4 and IPv6 DirPorts are the same.
+ * Assume the IPv6 OR and Dir addresses are the same. */
+ return fascist_firewall_choose_address_ipv4h(rs->addr,
+ rs->or_port,
+ rs->dir_port,
+ &rs->ipv6_addr,
+ rs->ipv6_orport,
+ rs->dir_port,
+ fw_connection,
+ pref_only,
+ pref_ipv6,
+ ap);
+ }
+}
+
+/** Like fascist_firewall_choose_address_base(), but takes <b>node</b>, and
+ * looks up the node's IPv6 preference rather than taking an argument
+ * for pref_ipv6. */
+int
+fascist_firewall_choose_address_node(const node_t *node,
+ firewall_connection_t fw_connection,
+ int pref_only, tor_addr_port_t *ap)
+{
+ if (!node) {
+ return 0;
+ }
+
+ node_assert_ok(node);
+
+ const int pref_ipv6_node = (fw_connection == FIREWALL_OR_CONNECTION
+ ? node_ipv6_or_preferred(node)
+ : node_ipv6_dir_preferred(node));
+
+ tor_addr_port_t ipv4_or_ap;
+ node_get_prim_orport(node, &ipv4_or_ap);
+ tor_addr_port_t ipv4_dir_ap;
+ node_get_prim_dirport(node, &ipv4_dir_ap);
+
+ tor_addr_port_t ipv6_or_ap;
+ node_get_pref_ipv6_orport(node, &ipv6_or_ap);
+ tor_addr_port_t ipv6_dir_ap;
+ node_get_pref_ipv6_dirport(node, &ipv6_dir_ap);
+
+ /* Assume the IPv6 OR and Dir addresses are the same. */
+ return fascist_firewall_choose_address_base(&ipv4_or_ap.addr,
+ ipv4_or_ap.port,
+ ipv4_dir_ap.port,
+ &ipv6_or_ap.addr,
+ ipv6_or_ap.port,
+ ipv6_dir_ap.port,
+ fw_connection,
+ pref_only,
+ pref_ipv6_node,
+ ap);
+}
+
+/** Like fascist_firewall_choose_address_rs(), but takes <b>ds</b>. */
+int
+fascist_firewall_choose_address_dir_server(const dir_server_t *ds,
+ firewall_connection_t fw_connection,
+ int pref_only,
+ tor_addr_port_t *ap)
+{
+ if (!ds) {
+ return 0;
+ }
+
+ /* A dir_server_t always has a fake_status. As long as it has the same
+ * addresses/ports in both fake_status and dir_server_t, this works fine.
+ * (See #17867.)
+ * This function relies on fascist_firewall_choose_address_rs looking up the
+ * node if it can, because that will get the latest info for the relay. */
+ return fascist_firewall_choose_address_rs(&ds->fake_status, fw_connection,
+ pref_only, ap);
}
/** Return 1 if <b>addr</b> is permitted to connect to our dir port,
@@ -430,7 +1082,7 @@ validate_addr_policies(const or_options_t *options, char **msg)
smartlist_t *addr_policy=NULL;
*msg = NULL;
- if (policies_parse_exit_policy_from_options(options,0,&addr_policy)) {
+ if (policies_parse_exit_policy_from_options(options,0,NULL,&addr_policy)) {
REJECT("Error in ExitPolicy entry.");
}
@@ -568,6 +1220,8 @@ cmp_single_addr_policy(addr_policy_t *a, addr_policy_t *b)
return r;
if ((r=((int)a->is_private - (int)b->is_private)))
return r;
+ /* refcnt and is_canonical are irrelevant to equality,
+ * they are hash table implementation details */
if ((r=tor_addr_compare(&a->addr, &b->addr, CMP_EXACT)))
return r;
if ((r=((int)a->maskbits - (int)b->maskbits)))
@@ -610,7 +1264,7 @@ typedef struct policy_map_ent_t {
static HT_HEAD(policy_map, policy_map_ent_t) policy_root = HT_INITIALIZER();
/** Return true iff a and b are equal. */
-static INLINE int
+static inline int
policy_eq(policy_map_ent_t *a, policy_map_ent_t *b)
{
return cmp_single_addr_policy(a->policy, b->policy) == 0;
@@ -678,6 +1332,10 @@ compare_known_tor_addr_to_addr_policy(const tor_addr_t *addr, uint16_t port,
/* We know the address and port, and we know the policy, so we can just
* compute an exact match. */
SMARTLIST_FOREACH_BEGIN(policy, addr_policy_t *, tmpe) {
+ if (tmpe->addr.family == AF_UNSPEC) {
+ log_warn(LD_BUG, "Policy contains an AF_UNSPEC address, which only "
+ "matches other AF_UNSPEC addresses.");
+ }
/* Address is known */
if (!tor_addr_compare_masked(addr, &tmpe->addr, tmpe->maskbits,
CMP_EXACT)) {
@@ -705,6 +1363,10 @@ compare_known_tor_addr_to_addr_policy_noport(const tor_addr_t *addr,
int maybe_accept = 0, maybe_reject = 0;
SMARTLIST_FOREACH_BEGIN(policy, addr_policy_t *, tmpe) {
+ if (tmpe->addr.family == AF_UNSPEC) {
+ log_warn(LD_BUG, "Policy contains an AF_UNSPEC address, which only "
+ "matches other AF_UNSPEC addresses.");
+ }
if (!tor_addr_compare_masked(addr, &tmpe->addr, tmpe->maskbits,
CMP_EXACT)) {
if (tmpe->prt_min <= 1 && tmpe->prt_max >= 65535) {
@@ -744,6 +1406,10 @@ compare_unknown_tor_addr_to_addr_policy(uint16_t port,
int maybe_accept = 0, maybe_reject = 0;
SMARTLIST_FOREACH_BEGIN(policy, addr_policy_t *, tmpe) {
+ if (tmpe->addr.family == AF_UNSPEC) {
+ log_warn(LD_BUG, "Policy contains an AF_UNSPEC address, which only "
+ "matches other AF_UNSPEC addresses.");
+ }
if (tmpe->prt_min <= port && port <= tmpe->prt_max) {
if (tmpe->maskbits == 0) {
/* Definitely matches, since it covers all addresses. */
@@ -849,7 +1515,7 @@ addr_policy_intersects(addr_policy_t *a, addr_policy_t *b)
/** Add the exit policy described by <b>more</b> to <b>policy</b>.
*/
-static void
+STATIC void
append_exit_policy_string(smartlist_t **policy, const char *more)
{
config_line_t tmp;
@@ -866,6 +1532,9 @@ append_exit_policy_string(smartlist_t **policy, const char *more)
void
addr_policy_append_reject_addr(smartlist_t **dest, const tor_addr_t *addr)
{
+ tor_assert(dest);
+ tor_assert(addr);
+
addr_policy_t p, *add;
memset(&p, 0, sizeof(p));
p.policy_type = ADDR_POLICY_REJECT;
@@ -878,6 +1547,71 @@ addr_policy_append_reject_addr(smartlist_t **dest, const tor_addr_t *addr)
if (!*dest)
*dest = smartlist_new();
smartlist_add(*dest, add);
+ log_debug(LD_CONFIG, "Adding a reject ExitPolicy 'reject %s:*'",
+ fmt_addr(addr));
+}
+
+/* Is addr public for the purposes of rejection? */
+static int
+tor_addr_is_public_for_reject(const tor_addr_t *addr)
+{
+ return (!tor_addr_is_null(addr) && !tor_addr_is_internal(addr, 0)
+ && !tor_addr_is_multicast(addr));
+}
+
+/* Add "reject <b>addr</b>:*" to <b>dest</b>, creating the list as needed.
+ * Filter the address, only adding an IPv4 reject rule if ipv4_rules
+ * is true, and similarly for ipv6_rules. Check each address returns true for
+ * tor_addr_is_public_for_reject before adding it.
+ */
+static void
+addr_policy_append_reject_addr_filter(smartlist_t **dest,
+ const tor_addr_t *addr,
+ int ipv4_rules,
+ int ipv6_rules)
+{
+ tor_assert(dest);
+ tor_assert(addr);
+
+ /* Only reject IP addresses which are public */
+ if (tor_addr_is_public_for_reject(addr)) {
+
+ /* Reject IPv4 addresses and IPv6 addresses based on the filters */
+ int is_ipv4 = tor_addr_is_v4(addr);
+ if ((is_ipv4 && ipv4_rules) || (!is_ipv4 && ipv6_rules)) {
+ addr_policy_append_reject_addr(dest, addr);
+ }
+ }
+}
+
+/** Add "reject addr:*" to <b>dest</b>, for each addr in addrs, creating the
+ * list as needed. */
+void
+addr_policy_append_reject_addr_list(smartlist_t **dest,
+ const smartlist_t *addrs)
+{
+ tor_assert(dest);
+ tor_assert(addrs);
+
+ SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, addr) {
+ addr_policy_append_reject_addr(dest, addr);
+ } SMARTLIST_FOREACH_END(addr);
+}
+
+/** Add "reject addr:*" to <b>dest</b>, for each addr in addrs, creating the
+ * list as needed. Filter using */
+static void
+addr_policy_append_reject_addr_list_filter(smartlist_t **dest,
+ const smartlist_t *addrs,
+ int ipv4_rules,
+ int ipv6_rules)
+{
+ tor_assert(dest);
+ tor_assert(addrs);
+
+ SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, addr) {
+ addr_policy_append_reject_addr_filter(dest, addr, ipv4_rules, ipv6_rules);
+ } SMARTLIST_FOREACH_END(addr);
}
/** Detect and excise "dead code" from the policy *<b>dest</b>. */
@@ -964,42 +1698,197 @@ exit_policy_remove_redundancies(smartlist_t *dest)
}
}
+/** Reject private helper for policies_parse_exit_policy_internal: rejects
+ * publicly routable addresses on this exit relay.
+ *
+ * Add reject entries to the linked list *<b>dest</b>:
+ * <ul>
+ * <li>if configured_addresses is non-NULL, add entries that reject each
+ * tor_addr_t in the list as a destination.
+ * <li>if reject_interface_addresses is true, add entries that reject each
+ * public IPv4 and IPv6 address of each interface on this machine.
+ * <li>if reject_configured_port_addresses is true, add entries that reject
+ * each IPv4 and IPv6 address configured for a port.
+ * </ul>
+ *
+ * IPv6 entries are only added if ipv6_exit is true. (All IPv6 addresses are
+ * already blocked by policies_parse_exit_policy_internal if ipv6_exit is
+ * false.)
+ *
+ * The list in <b>dest</b> is created as needed.
+ */
+void
+policies_parse_exit_policy_reject_private(
+ smartlist_t **dest,
+ int ipv6_exit,
+ const smartlist_t *configured_addresses,
+ int reject_interface_addresses,
+ int reject_configured_port_addresses)
+{
+ tor_assert(dest);
+
+ /* Reject configured addresses, if they are from public netblocks. */
+ if (configured_addresses) {
+ addr_policy_append_reject_addr_list_filter(dest, configured_addresses,
+ 1, ipv6_exit);
+ }
+
+ /* Reject configured port addresses, if they are from public netblocks. */
+ if (reject_configured_port_addresses) {
+ const smartlist_t *port_addrs = get_configured_ports();
+
+ SMARTLIST_FOREACH_BEGIN(port_addrs, port_cfg_t *, port) {
+
+ /* Only reject port IP addresses, not port unix sockets */
+ if (!port->is_unix_addr) {
+ addr_policy_append_reject_addr_filter(dest, &port->addr, 1, ipv6_exit);
+ }
+ } SMARTLIST_FOREACH_END(port);
+ }
+
+ /* Reject local addresses from public netblocks on any interface. */
+ if (reject_interface_addresses) {
+ smartlist_t *public_addresses = NULL;
+
+ /* Reject public IPv4 addresses on any interface */
+ public_addresses = get_interface_address6_list(LOG_INFO, AF_INET, 0);
+ addr_policy_append_reject_addr_list_filter(dest, public_addresses, 1, 0);
+ free_interface_address6_list(public_addresses);
+
+ /* Don't look for IPv6 addresses if we're configured as IPv4-only */
+ if (ipv6_exit) {
+ /* Reject public IPv6 addresses on any interface */
+ public_addresses = get_interface_address6_list(LOG_INFO, AF_INET6, 0);
+ addr_policy_append_reject_addr_list_filter(dest, public_addresses, 0, 1);
+ free_interface_address6_list(public_addresses);
+ }
+ }
+
+ /* If addresses were added multiple times, remove all but one of them. */
+ if (*dest) {
+ exit_policy_remove_redundancies(*dest);
+ }
+}
+
+/**
+ * Iterate through <b>policy</b> looking for redundant entries. Log a
+ * warning message with the first redundant entry, if any is found.
+ */
+static void
+policies_log_first_redundant_entry(const smartlist_t *policy)
+{
+ int found_final_effective_entry = 0;
+ int first_redundant_entry = 0;
+ tor_assert(policy);
+ SMARTLIST_FOREACH_BEGIN(policy, const addr_policy_t *, p) {
+ sa_family_t family;
+ int found_ipv4_wildcard = 0, found_ipv6_wildcard = 0;
+ const int i = p_sl_idx;
+
+ /* Look for accept/reject *[4|6|]:* entires */
+ if (p->prt_min <= 1 && p->prt_max == 65535 && p->maskbits == 0) {
+ family = tor_addr_family(&p->addr);
+ /* accept/reject *:* may have already been expanded into
+ * accept/reject *4:*,accept/reject *6:*
+ * But handle both forms.
+ */
+ if (family == AF_INET || family == AF_UNSPEC) {
+ found_ipv4_wildcard = 1;
+ }
+ if (family == AF_INET6 || family == AF_UNSPEC) {
+ found_ipv6_wildcard = 1;
+ }
+ }
+
+ /* We also find accept *4:*,reject *6:* ; and
+ * accept *4:*,<other policies>,accept *6:* ; and similar.
+ * That's ok, because they make any subsequent entries redundant. */
+ if (found_ipv4_wildcard && found_ipv6_wildcard) {
+ found_final_effective_entry = 1;
+ /* if we're not on the final entry in the list */
+ if (i < smartlist_len(policy) - 1) {
+ first_redundant_entry = i + 1;
+ }
+ break;
+ }
+ } SMARTLIST_FOREACH_END(p);
+
+ /* Work out if there are redundant trailing entries in the policy list */
+ if (found_final_effective_entry && first_redundant_entry > 0) {
+ const addr_policy_t *p;
+ /* Longest possible policy is
+ * "accept6 ffff:ffff:..255/128:10000-65535",
+ * which contains a max-length IPv6 address, plus 24 characters. */
+ char line[TOR_ADDR_BUF_LEN + 32];
+
+ tor_assert(first_redundant_entry < smartlist_len(policy));
+ p = smartlist_get(policy, first_redundant_entry);
+ /* since we've already parsed the policy into an addr_policy_t struct,
+ * we might not log exactly what the user typed in */
+ policy_write_item(line, TOR_ADDR_BUF_LEN + 32, p, 0);
+ log_warn(LD_DIR, "Exit policy '%s' and all following policies are "
+ "redundant, as it follows accept/reject *:* rules for both "
+ "IPv4 and IPv6. They will be removed from the exit policy. (Use "
+ "accept/reject *:* as the last entry in any exit policy.)",
+ line);
+ }
+}
+
#define DEFAULT_EXIT_POLICY \
"reject *:25,reject *:119,reject *:135-139,reject *:445," \
"reject *:563,reject *:1214,reject *:4661-4666," \
"reject *:6346-6429,reject *:6699,reject *:6881-6999,accept *:*"
-/** Parse the exit policy <b>cfg</b> into the linked list *<b>dest</b>. If
- * cfg doesn't end in an absolute accept or reject and if
+/** Parse the exit policy <b>cfg</b> into the linked list *<b>dest</b>.
+ *
+ * If <b>ipv6_exit</b> is false, prepend "reject *6:*" to the policy.
+ *
+ * If <b>rejectprivate</b> is true:
+ * - prepend "reject private:*" to the policy.
+ * - prepend entries that reject publicly routable addresses on this exit
+ * relay by calling policies_parse_exit_policy_reject_private
+ *
+ * If cfg doesn't end in an absolute accept or reject and if
* <b>add_default_policy</b> is true, add the default exit
- * policy afterwards. If <b>rejectprivate</b> is true, prepend
- * "reject private:*" to the policy. Return -1 if we can't parse cfg,
- * else return 0.
+ * policy afterwards.
+ *
+ * Return -1 if we can't parse cfg, else return 0.
*
* This function is used to parse the exit policy from our torrc. For
* the functions used to parse the exit policy from a router descriptor,
* see router_add_exit_policy.
*/
static int
-policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest,
+policies_parse_exit_policy_internal(config_line_t *cfg,
+ smartlist_t **dest,
int ipv6_exit,
int rejectprivate,
- uint32_t local_address,
+ const smartlist_t *configured_addresses,
+ int reject_interface_addresses,
+ int reject_configured_port_addresses,
int add_default_policy)
{
if (!ipv6_exit) {
append_exit_policy_string(dest, "reject *6:*");
}
if (rejectprivate) {
+ /* Reject IPv4 and IPv6 reserved private netblocks */
append_exit_policy_string(dest, "reject private:*");
- if (local_address) {
- char buf[POLICY_BUF_LEN];
- tor_snprintf(buf, sizeof(buf), "reject %s:*", fmt_addr32(local_address));
- append_exit_policy_string(dest, buf);
- }
+ /* Reject IPv4 and IPv6 publicly routable addresses on this exit relay */
+ policies_parse_exit_policy_reject_private(
+ dest, ipv6_exit,
+ configured_addresses,
+ reject_interface_addresses,
+ reject_configured_port_addresses);
}
if (parse_addr_policy(cfg, dest, -1))
return -1;
+
+ /* Before we add the default policy and final rejects, check to see if
+ * there are any lines after accept *:* or reject *:*. These lines have no
+ * effect, and are most likely an error. */
+ policies_log_first_redundant_entry(*dest);
+
if (add_default_policy) {
append_exit_policy_string(dest, DEFAULT_EXIT_POLICY);
} else {
@@ -1013,20 +1902,22 @@ policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest,
/** Parse exit policy in <b>cfg</b> into <b>dest</b> smartlist.
*
- * Add entry that rejects all IPv6 destinations unless
+ * Prepend an entry that rejects all IPv6 destinations unless
* <b>EXIT_POLICY_IPV6_ENABLED</b> bit is set in <b>options</b> bitmask.
*
- * If <b>EXIT_POLICY_REJECT_PRIVATE</b> bit is set in <b>options</b>,
- * do add entry that rejects all destinations in private subnetwork
- * Tor is running in.
+ * If <b>EXIT_POLICY_REJECT_PRIVATE</b> bit is set in <b>options</b>:
+ * - prepend an entry that rejects all destinations in all netblocks
+ * reserved for private use.
+ * - prepend entries that reject publicly routable addresses on this exit
+ * relay by calling policies_parse_exit_policy_internal
*
- * Respectively, if <b>EXIT_POLICY_ADD_DEFAULT</b> bit is set, add
+ * If <b>EXIT_POLICY_ADD_DEFAULT</b> bit is set in <b>options</b>, append
* default exit policy entries to <b>result</b> smartlist.
*/
int
policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
exit_policy_parser_cfg_t options,
- uint32_t local_address)
+ const smartlist_t *configured_addresses)
{
int ipv6_enabled = (options & EXIT_POLICY_IPV6_ENABLED) ? 1 : 0;
int reject_private = (options & EXIT_POLICY_REJECT_PRIVATE) ? 1 : 0;
@@ -1034,20 +1925,80 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
return policies_parse_exit_policy_internal(cfg,dest,ipv6_enabled,
reject_private,
- local_address,
+ configured_addresses,
+ reject_private,
+ reject_private,
add_default);
}
+/** Helper function that adds a copy of addr to a smartlist as long as it is
+ * non-NULL and not tor_addr_is_null().
+ *
+ * The caller is responsible for freeing all the tor_addr_t* in the smartlist.
+ */
+static void
+policies_copy_addr_to_smartlist(smartlist_t *addr_list, const tor_addr_t *addr)
+{
+ if (addr && !tor_addr_is_null(addr)) {
+ tor_addr_t *addr_copy = tor_malloc(sizeof(tor_addr_t));
+ tor_addr_copy(addr_copy, addr);
+ smartlist_add(addr_list, addr_copy);
+ }
+}
+
+/** Helper function that adds ipv4h_addr to a smartlist as a tor_addr_t *,
+ * as long as it is not tor_addr_is_null(), by converting it to a tor_addr_t
+ * and passing it to policies_add_addr_to_smartlist.
+ *
+ * The caller is responsible for freeing all the tor_addr_t* in the smartlist.
+ */
+static void
+policies_copy_ipv4h_to_smartlist(smartlist_t *addr_list, uint32_t ipv4h_addr)
+{
+ if (ipv4h_addr) {
+ tor_addr_t ipv4_tor_addr;
+ tor_addr_from_ipv4h(&ipv4_tor_addr, ipv4h_addr);
+ policies_copy_addr_to_smartlist(addr_list, &ipv4_tor_addr);
+ }
+}
+
+/** Helper function that adds copies of
+ * or_options->OutboundBindAddressIPv[4|6]_ to a smartlist as tor_addr_t *, as
+ * long as or_options is non-NULL, and the addresses are not
+ * tor_addr_is_null(), by passing them to policies_add_addr_to_smartlist.
+ *
+ * The caller is responsible for freeing all the tor_addr_t* in the smartlist.
+ */
+static void
+policies_copy_outbound_addresses_to_smartlist(smartlist_t *addr_list,
+ const or_options_t *or_options)
+{
+ if (or_options) {
+ policies_copy_addr_to_smartlist(addr_list,
+ &or_options->OutboundBindAddressIPv4_);
+ policies_copy_addr_to_smartlist(addr_list,
+ &or_options->OutboundBindAddressIPv6_);
+ }
+}
+
/** Parse <b>ExitPolicy</b> member of <b>or_options</b> into <b>result</b>
* smartlist.
- * If <b>or_options->IPv6Exit</b> is false, add an entry that
+ * If <b>or_options->IPv6Exit</b> is false, prepend an entry that
* rejects all IPv6 destinations.
*
- * If <b>or_options->ExitPolicyRejectPrivate</b> is true, add entry that
- * rejects all destinations in the private subnetwork of machine Tor
- * instance is running in.
+ * If <b>or_options->ExitPolicyRejectPrivate</b> is true:
+ * - prepend an entry that rejects all destinations in all netblocks reserved
+ * for private use.
+ * - if local_address is non-zero, treat it as a host-order IPv4 address, and
+ * add it to the list of configured addresses.
+ * - if ipv6_local_address is non-NULL, and not the null tor_addr_t, add it
+ * to the list of configured addresses.
+ * - if or_options->OutboundBindAddressIPv4_ is not the null tor_addr_t, add
+ * it to the list of configured addresses.
+ * - if or_options->OutboundBindAddressIPv6_ is not the null tor_addr_t, add
+ * it to the list of configured addresses.
*
- * If <b>or_options->BridgeRelay</b> is false, add entries of default
+ * If <b>or_options->BridgeRelay</b> is false, append entries of default
* Tor exit policy into <b>result</b> smartlist.
*
* If or_options->ExitRelay is false, then make our exit policy into
@@ -1056,16 +2007,23 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
int
policies_parse_exit_policy_from_options(const or_options_t *or_options,
uint32_t local_address,
+ const tor_addr_t *ipv6_local_address,
smartlist_t **result)
{
exit_policy_parser_cfg_t parser_cfg = 0;
+ smartlist_t *configured_addresses = NULL;
+ int rv = 0;
+ /* Short-circuit for non-exit relays */
if (or_options->ExitRelay == 0) {
append_exit_policy_string(result, "reject *4:*");
append_exit_policy_string(result, "reject *6:*");
return 0;
}
+ configured_addresses = smartlist_new();
+
+ /* Configure the parser */
if (or_options->IPv6Exit) {
parser_cfg |= EXIT_POLICY_IPV6_ENABLED;
}
@@ -1078,8 +2036,19 @@ policies_parse_exit_policy_from_options(const or_options_t *or_options,
parser_cfg |= EXIT_POLICY_ADD_DEFAULT;
}
- return policies_parse_exit_policy(or_options->ExitPolicy,result,
- parser_cfg,local_address);
+ /* Copy the configured addresses into the tor_addr_t* list */
+ policies_copy_ipv4h_to_smartlist(configured_addresses, local_address);
+ policies_copy_addr_to_smartlist(configured_addresses, ipv6_local_address);
+ policies_copy_outbound_addresses_to_smartlist(configured_addresses,
+ or_options);
+
+ rv = policies_parse_exit_policy(or_options->ExitPolicy, result, parser_cfg,
+ configured_addresses);
+
+ SMARTLIST_FOREACH(configured_addresses, tor_addr_t *, a, tor_free(a));
+ smartlist_free(configured_addresses);
+
+ return rv;
}
/** Add "reject *:*" to the end of the policy in *<b>dest</b>, allocating
@@ -1187,7 +2156,7 @@ policy_is_reject_star(const smartlist_t *policy, sa_family_t family)
/** Write a single address policy to the buf_len byte buffer at buf. Return
* the number of characters written, or -1 on failure. */
int
-policy_write_item(char *buf, size_t buflen, addr_policy_t *policy,
+policy_write_item(char *buf, size_t buflen, const addr_policy_t *policy,
int format_for_desc)
{
size_t written = 0;
@@ -1223,9 +2192,9 @@ policy_write_item(char *buf, size_t buflen, addr_policy_t *policy,
if (result < 0)
return -1;
written += strlen(buf);
- /* If the maskbits is 32 we don't need to give it. If the mask is 0,
- * we already wrote "*". */
- if (policy->maskbits < 32 && policy->maskbits > 0) {
+ /* If the maskbits is 32 (IPv4) or 128 (IPv6) we don't need to give it. If
+ the mask is 0, we already wrote "*". */
+ if (policy->maskbits < (is_ip6?128:32) && policy->maskbits > 0) {
if (tor_snprintf(buf+written, buflen-written, "/%d", policy->maskbits)<0)
return -1;
written += strlen(buf+written);
@@ -1705,7 +2674,7 @@ compare_tor_addr_to_short_policy(const tor_addr_t *addr, uint16_t port,
* allows exit enclaving. Trying it anyway would open up a cool attack
* where the node refuses due to exitpolicy, the client reacts in
* surprise by rewriting the node's exitpolicy to reject *:*, and then
- * a bad guy targets users by causing them to attempt such connections
+ * an adversary targets users by causing them to attempt such connections
* to 98% of the exits.
*
* Once microdescriptors can handle addresses in special cases (e.g. if
@@ -1766,6 +2735,53 @@ compare_tor_addr_to_node_policy(const tor_addr_t *addr, uint16_t port,
}
}
+/**
+ * Given <b>policy_list</b>, a list of addr_policy_t, produce a string
+ * representation of the list.
+ * If <b>include_ipv4</b> is true, include IPv4 entries.
+ * If <b>include_ipv6</b> is true, include IPv6 entries.
+ */
+char *
+policy_dump_to_string(const smartlist_t *policy_list,
+ int include_ipv4,
+ int include_ipv6)
+{
+ smartlist_t *policy_string_list;
+ char *policy_string = NULL;
+
+ policy_string_list = smartlist_new();
+
+ SMARTLIST_FOREACH_BEGIN(policy_list, addr_policy_t *, tmpe) {
+ char *pbuf;
+ int bytes_written_to_pbuf;
+ if ((tor_addr_family(&tmpe->addr) == AF_INET6) && (!include_ipv6)) {
+ continue; /* Don't include IPv6 parts of address policy */
+ }
+ if ((tor_addr_family(&tmpe->addr) == AF_INET) && (!include_ipv4)) {
+ continue; /* Don't include IPv4 parts of address policy */
+ }
+
+ pbuf = tor_malloc(POLICY_BUF_LEN);
+ bytes_written_to_pbuf = policy_write_item(pbuf,POLICY_BUF_LEN, tmpe, 1);
+
+ if (bytes_written_to_pbuf < 0) {
+ log_warn(LD_BUG, "policy_dump_to_string ran out of room!");
+ tor_free(pbuf);
+ goto done;
+ }
+
+ smartlist_add(policy_string_list,pbuf);
+ } SMARTLIST_FOREACH_END(tmpe);
+
+ policy_string = smartlist_join_strings(policy_string_list, "\n", 0, NULL);
+
+ done:
+ SMARTLIST_FOREACH(policy_string_list, char *, str, tor_free(str));
+ smartlist_free(policy_string_list);
+
+ return policy_string;
+}
+
/** Implementation for GETINFO control command: knows the answer for questions
* about "exit-policy/..." */
int
@@ -1777,6 +2793,57 @@ getinfo_helper_policies(control_connection_t *conn,
(void) errmsg;
if (!strcmp(question, "exit-policy/default")) {
*answer = tor_strdup(DEFAULT_EXIT_POLICY);
+ } else if (!strcmp(question, "exit-policy/reject-private/default")) {
+ smartlist_t *private_policy_strings;
+ const char **priv = private_nets;
+
+ private_policy_strings = smartlist_new();
+
+ while (*priv != NULL) {
+ /* IPv6 addresses are in "[]" and contain ":",
+ * IPv4 addresses are not in "[]" and contain "." */
+ smartlist_add_asprintf(private_policy_strings, "reject %s:*", *priv);
+ priv++;
+ }
+
+ *answer = smartlist_join_strings(private_policy_strings,
+ ",", 0, NULL);
+
+ SMARTLIST_FOREACH(private_policy_strings, char *, str, tor_free(str));
+ smartlist_free(private_policy_strings);
+ } else if (!strcmp(question, "exit-policy/reject-private/relay")) {
+ const or_options_t *options = get_options();
+ const routerinfo_t *me = router_get_my_routerinfo();
+
+ if (!me) {
+ *errmsg = "router_get_my_routerinfo returned NULL";
+ return -1;
+ }
+
+ if (!options->ExitPolicyRejectPrivate) {
+ *answer = tor_strdup("");
+ return 0;
+ }
+
+ smartlist_t *private_policy_list = smartlist_new();
+ smartlist_t *configured_addresses = smartlist_new();
+
+ /* Copy the configured addresses into the tor_addr_t* list */
+ policies_copy_ipv4h_to_smartlist(configured_addresses, me->addr);
+ policies_copy_addr_to_smartlist(configured_addresses, &me->ipv6_addr);
+ policies_copy_outbound_addresses_to_smartlist(configured_addresses,
+ options);
+
+ policies_parse_exit_policy_reject_private(
+ &private_policy_list,
+ options->IPv6Exit,
+ configured_addresses,
+ 1, 1);
+ *answer = policy_dump_to_string(private_policy_list, 1, 1);
+
+ addr_policy_list_free(private_policy_list);
+ SMARTLIST_FOREACH(configured_addresses, tor_addr_t *, a, tor_free(a));
+ smartlist_free(configured_addresses);
} else if (!strcmpstart(question, "exit-policy/")) {
const routerinfo_t *me = router_get_my_routerinfo();
diff --git a/src/or/policies.h b/src/or/policies.h
index 0225b57a2c..aaa6fa0a4e 100644
--- a/src/or/policies.h
+++ b/src/or/policies.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -22,13 +22,44 @@
#define EXIT_POLICY_REJECT_PRIVATE (1 << 1)
#define EXIT_POLICY_ADD_DEFAULT (1 << 2)
+typedef enum firewall_connection_t {
+ FIREWALL_OR_CONNECTION = 0,
+ FIREWALL_DIR_CONNECTION = 1
+} firewall_connection_t;
+
typedef int exit_policy_parser_cfg_t;
int firewall_is_fascist_or(void);
-int fascist_firewall_allows_address_or(const tor_addr_t *addr, uint16_t port);
-int fascist_firewall_allows_or(const routerinfo_t *ri);
-int fascist_firewall_allows_node(const node_t *node);
-int fascist_firewall_allows_address_dir(const tor_addr_t *addr, uint16_t port);
+int firewall_is_fascist_dir(void);
+int fascist_firewall_use_ipv6(const or_options_t *options);
+int fascist_firewall_prefer_ipv6_orport(const or_options_t *options);
+int fascist_firewall_prefer_ipv6_dirport(const or_options_t *options);
+
+int fascist_firewall_allows_address_addr(const tor_addr_t *addr,
+ uint16_t port,
+ firewall_connection_t fw_connection,
+ int pref_only, int pref_ipv6);
+
+int fascist_firewall_allows_rs(const routerstatus_t *rs,
+ firewall_connection_t fw_connection,
+ int pref_only);
+int fascist_firewall_allows_node(const node_t *node,
+ firewall_connection_t fw_connection,
+ int pref_only);
+int fascist_firewall_allows_dir_server(const dir_server_t *ds,
+ firewall_connection_t fw_connection,
+ int pref_only);
+
+int fascist_firewall_choose_address_rs(const routerstatus_t *rs,
+ firewall_connection_t fw_connection,
+ int pref_only, tor_addr_port_t* ap);
+int fascist_firewall_choose_address_node(const node_t *node,
+ firewall_connection_t fw_connection,
+ int pref_only, tor_addr_port_t* ap);
+int fascist_firewall_choose_address_dir_server(const dir_server_t *ds,
+ firewall_connection_t fw_connection,
+ int pref_only, tor_addr_port_t* ap);
+
int dir_policy_permits_address(const tor_addr_t *addr);
int socks_policy_permits_address(const tor_addr_t *addr);
int authdir_policy_permits_address(uint32_t addr, uint16_t port);
@@ -44,32 +75,38 @@ addr_policy_t *addr_policy_get_canonical_entry(addr_policy_t *ent);
int cmp_addr_policies(smartlist_t *a, smartlist_t *b);
MOCK_DECL(addr_policy_result_t, compare_tor_addr_to_addr_policy,
(const tor_addr_t *addr, uint16_t port, const smartlist_t *policy));
-
addr_policy_result_t compare_tor_addr_to_node_policy(const tor_addr_t *addr,
uint16_t port, const node_t *node);
-/*
-int policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
- int ipv6exit,
- int rejectprivate, uint32_t local_address,
- int add_default_policy);
-*/
-int policies_parse_exit_policy_from_options(const or_options_t *or_options,
- uint32_t local_address,
- smartlist_t **result);
+int policies_parse_exit_policy_from_options(
+ const or_options_t *or_options,
+ uint32_t local_address,
+ const tor_addr_t *ipv6_local_address,
+ smartlist_t **result);
int policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
exit_policy_parser_cfg_t options,
- uint32_t local_address);
+ const smartlist_t *configured_addresses);
+void policies_parse_exit_policy_reject_private(
+ smartlist_t **dest,
+ int ipv6_exit,
+ const smartlist_t *configured_addresses,
+ int reject_interface_addresses,
+ int reject_configured_port_addresses);
void policies_exit_policy_append_reject_star(smartlist_t **dest);
void addr_policy_append_reject_addr(smartlist_t **dest,
const tor_addr_t *addr);
+void addr_policy_append_reject_addr_list(smartlist_t **dest,
+ const smartlist_t *addrs);
void policies_set_node_exitpolicy_to_reject_all(node_t *exitrouter);
int exit_policy_is_general_exit(smartlist_t *policy);
int policy_is_reject_star(const smartlist_t *policy, sa_family_t family);
+char * policy_dump_to_string(const smartlist_t *policy_list,
+ int include_ipv4,
+ int include_ipv6);
int getinfo_helper_policies(control_connection_t *conn,
const char *question, char **answer,
const char **errmsg);
-int policy_write_item(char *buf, size_t buflen, addr_policy_t *item,
+int policy_write_item(char *buf, size_t buflen, const addr_policy_t *item,
int format_for_desc);
void addr_policy_list_free(smartlist_t *p);
@@ -86,5 +123,20 @@ addr_policy_result_t compare_tor_addr_to_short_policy(
const tor_addr_t *addr, uint16_t port,
const short_policy_t *policy);
+#ifdef POLICIES_PRIVATE
+STATIC void append_exit_policy_string(smartlist_t **policy, const char *more);
+STATIC int fascist_firewall_allows_address(const tor_addr_t *addr,
+ uint16_t port,
+ smartlist_t *firewall_policy,
+ int pref_only, int pref_ipv6);
+STATIC const tor_addr_port_t * fascist_firewall_choose_address(
+ const tor_addr_port_t *a,
+ const tor_addr_port_t *b,
+ int want_a,
+ firewall_connection_t fw_connection,
+ int pref_only, int pref_ipv6);
+
+#endif
+
#endif
diff --git a/src/or/reasons.c b/src/or/reasons.c
index 23ab6041a6..36921cafcd 100644
--- a/src/or/reasons.c
+++ b/src/or/reasons.c
@@ -1,5 +1,5 @@
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/reasons.h b/src/or/reasons.h
index 00a099061b..2e12c93728 100644
--- a/src/or/reasons.h
+++ b/src/or/reasons.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/relay.c b/src/or/relay.c
index eddad6a0cb..fb8c8e74d6 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -148,20 +148,15 @@ relay_digest_matches(crypto_digest_t *digest, cell_t *cell)
*
* If <b>encrypt_mode</b> is 1 then encrypt, else decrypt.
*
- * Return -1 if the crypto fails, else return 0.
+ * Returns 0.
*/
static int
relay_crypt_one_payload(crypto_cipher_t *cipher, uint8_t *in,
int encrypt_mode)
{
- int r;
(void)encrypt_mode;
- r = crypto_cipher_crypt_inplace(cipher, (char*) in, CELL_PAYLOAD_SIZE);
+ crypto_cipher_crypt_inplace(cipher, (char*) in, CELL_PAYLOAD_SIZE);
- if (r) {
- log_warn(LD_BUG,"Error during relay encryption");
- return -1;
- }
return 0;
}
@@ -833,7 +828,7 @@ connection_ap_process_end_not_open(
}
}
}
- /* check if he *ought* to have allowed it */
+ /* check if the exit *ought* to have allowed it */
adjust_exit_policy_from_exitpolicy_failure(circ,
conn,
@@ -1304,6 +1299,7 @@ connection_edge_process_relay_cell_not_open(
"Got 'connected' while not in state connect_wait. Dropping.");
return 0;
}
+ CONNECTION_AP_EXPECT_NONPENDING(entry_conn);
conn->base_.state = AP_CONN_STATE_OPEN;
log_info(LD_APP,"'connected' received for circid %u streamid %d "
"after %d seconds.",
@@ -2255,7 +2251,7 @@ circuit_consider_sending_sendme(circuit_t *circ, crypt_path_t *layer_hint)
static size_t total_cells_allocated = 0;
/** Release storage held by <b>cell</b>. */
-static INLINE void
+static inline void
packed_cell_free_unchecked(packed_cell_t *cell)
{
--total_cells_allocated;
@@ -2299,7 +2295,7 @@ dump_cell_pool_usage(int severity)
}
/** Allocate a new copy of packed <b>cell</b>. */
-static INLINE packed_cell_t *
+static inline packed_cell_t *
packed_cell_copy(const cell_t *cell, int wide_circ_ids)
{
packed_cell_t *c = packed_cell_new();
@@ -2378,7 +2374,7 @@ packed_cell_mem_cost(void)
return sizeof(packed_cell_t);
}
-/** DOCDOC */
+/* DOCDOC */
STATIC size_t
cell_queues_get_total_allocation(void)
{
diff --git a/src/or/relay.h b/src/or/relay.h
index a4f583d11e..e15551ca51 100644
--- a/src/or/relay.h
+++ b/src/or/relay.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/rendcache.c b/src/or/rendcache.c
index fe9a1344f6..f8206cd53b 100644
--- a/src/or/rendcache.c
+++ b/src/or/rendcache.c
@@ -1,25 +1,30 @@
-/* Copyright (c) 2015, The Tor Project, Inc. */
+/* Copyright (c) 2015-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file rendcache.c
- * \brief Hidden service desriptor cache.
+ * \brief Hidden service descriptor cache.
**/
+#define RENDCACHE_PRIVATE
#include "rendcache.h"
#include "config.h"
#include "rephist.h"
#include "routerlist.h"
#include "routerparse.h"
+#include "rendcommon.h"
/** Map from service id (as generated by rend_get_service_id) to
* rend_cache_entry_t. */
-static strmap_t *rend_cache = NULL;
+STATIC strmap_t *rend_cache = NULL;
+
+/** Map from service id to rend_cache_entry_t; only for hidden services. */
+static strmap_t *rend_cache_local_service = NULL;
/** Map from descriptor id to rend_cache_entry_t; only for hidden service
* directories. */
-static digestmap_t *rend_cache_v2_dir = NULL;
+STATIC digestmap_t *rend_cache_v2_dir = NULL;
/** (Client side only) Map from service id to rend_cache_failure_t. This
* cache is used to track intro point(IP) failures so we know when to keep
@@ -46,10 +51,10 @@ static digestmap_t *rend_cache_v2_dir = NULL;
* This scheme allows us to not realy on the descriptor's timestamp (which
* is rounded down to the hour) to know if we have a newer descriptor. We
* only rely on the usability of intro points from an internal state. */
-static strmap_t *rend_cache_failure = NULL;
+STATIC strmap_t *rend_cache_failure = NULL;
-/** DOCDOC */
-static size_t rend_cache_total_allocation = 0;
+/* DOCDOC */
+STATIC size_t rend_cache_total_allocation = 0;
/** Initializes the service descriptor cache.
*/
@@ -58,11 +63,12 @@ rend_cache_init(void)
{
rend_cache = strmap_new();
rend_cache_v2_dir = digestmap_new();
+ rend_cache_local_service = strmap_new();
rend_cache_failure = strmap_new();
}
/** Return the approximate number of bytes needed to hold <b>e</b>. */
-static size_t
+STATIC size_t
rend_cache_entry_allocation(const rend_cache_entry_t *e)
{
if (!e)
@@ -72,7 +78,7 @@ rend_cache_entry_allocation(const rend_cache_entry_t *e)
return sizeof(*e) + e->len + sizeof(*e->parsed);
}
-/** DOCDOC */
+/* DOCDOC */
size_t
rend_cache_get_total_allocation(void)
{
@@ -80,7 +86,7 @@ rend_cache_get_total_allocation(void)
}
/** Decrement the total bytes attributed to the rendezvous cache by n. */
-static void
+STATIC void
rend_cache_decrement_allocation(size_t n)
{
static int have_underflowed = 0;
@@ -97,7 +103,7 @@ rend_cache_decrement_allocation(size_t n)
}
/** Increase the total bytes attributed to the rendezvous cache by n. */
-static void
+STATIC void
rend_cache_increment_allocation(size_t n)
{
static int have_overflowed = 0;
@@ -113,7 +119,7 @@ rend_cache_increment_allocation(size_t n)
}
/** Helper: free a rend cache failure intro object. */
-static void
+STATIC void
rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t *entry)
{
if (entry == NULL) {
@@ -122,9 +128,15 @@ rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t *entry)
tor_free(entry);
}
+static void
+rend_cache_failure_intro_entry_free_(void *entry)
+{
+ rend_cache_failure_intro_entry_free(entry);
+}
+
/** Allocate a rend cache failure intro object and return it. <b>failure</b>
* is set into the object. This function can not fail. */
-static rend_cache_failure_intro_t *
+STATIC rend_cache_failure_intro_t *
rend_cache_failure_intro_entry_new(rend_intro_point_failure_t failure)
{
rend_cache_failure_intro_t *entry = tor_malloc(sizeof(*entry));
@@ -134,7 +146,7 @@ rend_cache_failure_intro_entry_new(rend_intro_point_failure_t failure)
}
/** Helper: free a rend cache failure object. */
-static void
+STATIC void
rend_cache_failure_entry_free(rend_cache_failure_t *entry)
{
if (entry == NULL) {
@@ -142,17 +154,15 @@ rend_cache_failure_entry_free(rend_cache_failure_t *entry)
}
/* Free and remove every intro failure object. */
- DIGESTMAP_FOREACH_MODIFY(entry->intro_failures, key,
- rend_cache_failure_intro_t *, e) {
- rend_cache_failure_intro_entry_free(e);
- MAP_DEL_CURRENT(key);
- } DIGESTMAP_FOREACH_END;
+ digestmap_free(entry->intro_failures,
+ rend_cache_failure_intro_entry_free_);
+
tor_free(entry);
}
/** Helper: deallocate a rend_cache_failure_t. (Used with strmap_free(),
* which requires a function pointer whose argument is void*). */
-static void
+STATIC void
rend_cache_failure_entry_free_(void *entry)
{
rend_cache_failure_entry_free(entry);
@@ -160,7 +170,7 @@ rend_cache_failure_entry_free_(void *entry)
/** Allocate a rend cache failure object and return it. This function can
* not fail. */
-static rend_cache_failure_t *
+STATIC rend_cache_failure_t *
rend_cache_failure_entry_new(void)
{
rend_cache_failure_t *entry = tor_malloc(sizeof(*entry));
@@ -170,7 +180,7 @@ rend_cache_failure_entry_new(void)
/** Remove failure cache entry for the service ID in the given descriptor
* <b>desc</b>. */
-static void
+STATIC void
rend_cache_failure_remove(rend_service_descriptor_t *desc)
{
char service_id[REND_SERVICE_ID_LEN_BASE32 + 1];
@@ -190,7 +200,7 @@ rend_cache_failure_remove(rend_service_descriptor_t *desc)
}
/** Helper: free storage held by a single service descriptor cache entry. */
-static void
+STATIC void
rend_cache_entry_free(rend_cache_entry_t *e)
{
if (!e)
@@ -218,9 +228,11 @@ rend_cache_free_all(void)
{
strmap_free(rend_cache, rend_cache_entry_free_);
digestmap_free(rend_cache_v2_dir, rend_cache_entry_free_);
+ strmap_free(rend_cache_local_service, rend_cache_entry_free_);
strmap_free(rend_cache_failure, rend_cache_failure_entry_free_);
rend_cache = NULL;
rend_cache_v2_dir = NULL;
+ rend_cache_local_service = NULL;
rend_cache_failure = NULL;
rend_cache_total_allocation = 0;
}
@@ -254,24 +266,33 @@ rend_cache_failure_clean(time_t now)
} STRMAP_FOREACH_END;
}
-/** Removes all old entries from the service descriptor cache.
+/** Removes all old entries from the client or service descriptor cache.
*/
void
-rend_cache_clean(time_t now)
+rend_cache_clean(time_t now, rend_cache_type_t cache_type)
{
strmap_iter_t *iter;
const char *key;
void *val;
rend_cache_entry_t *ent;
time_t cutoff = now - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW;
- for (iter = strmap_iter_init(rend_cache); !strmap_iter_done(iter); ) {
+ strmap_t *cache = NULL;
+
+ if (cache_type == REND_CACHE_TYPE_CLIENT) {
+ cache = rend_cache;
+ } else if (cache_type == REND_CACHE_TYPE_SERVICE) {
+ cache = rend_cache_local_service;
+ }
+ tor_assert(cache);
+
+ for (iter = strmap_iter_init(cache); !strmap_iter_done(iter); ) {
strmap_iter_get(iter, &key, &val);
ent = (rend_cache_entry_t*)val;
if (ent->parsed->timestamp < cutoff) {
- iter = strmap_iter_next_rmv(rend_cache, iter);
+ iter = strmap_iter_next_rmv(cache, iter);
rend_cache_entry_free(ent);
} else {
- iter = strmap_iter_next(rend_cache, iter);
+ iter = strmap_iter_next(cache, iter);
}
}
}
@@ -301,10 +322,10 @@ rend_cache_failure_purge(void)
}
/** Lookup the rend failure cache using a relay identity digest in
- * <b>identity</b> and service ID <b>service_id</b>. If found, the intro
- * failure is set in <b>intro_entry</b> else it stays untouched. Return 1
- * iff found else 0. */
-static int
+ * <b>identity</b> which has DIGEST_LEN bytes and service ID <b>service_id</b>
+ * which is a null-terminated string. If found, the intro failure is set in
+ * <b>intro_entry</b> else it stays untouched. Return 1 iff found else 0. */
+STATIC int
cache_failure_intro_lookup(const uint8_t *identity, const char *service_id,
rend_cache_failure_intro_t **intro_entry)
{
@@ -334,15 +355,26 @@ cache_failure_intro_lookup(const uint8_t *identity, const char *service_id,
return 0;
}
+/** Allocate a new cache failure intro object and copy the content from
+ * <b>entry</b> to this newly allocated object. Return it. */
+static rend_cache_failure_intro_t *
+cache_failure_intro_dup(const rend_cache_failure_intro_t *entry)
+{
+ rend_cache_failure_intro_t *ent_dup =
+ rend_cache_failure_intro_entry_new(entry->failure_type);
+ ent_dup->created_ts = entry->created_ts;
+ return ent_dup;
+}
+
/** Add an intro point failure to the failure cache using the relay
* <b>identity</b> and service ID <b>service_id</b>. Record the
* <b>failure</b> in that object. */
-static void
+STATIC void
cache_failure_intro_add(const uint8_t *identity, const char *service_id,
rend_intro_point_failure_t failure)
{
rend_cache_failure_t *fail_entry;
- rend_cache_failure_intro_t *entry;
+ rend_cache_failure_intro_t *entry, *old_entry;
/* Make sure we have a failure object for this service ID and if not,
* create it with this new intro failure entry. */
@@ -353,7 +385,10 @@ cache_failure_intro_add(const uint8_t *identity, const char *service_id,
strmap_set_lc(rend_cache_failure, service_id, fail_entry);
}
entry = rend_cache_failure_intro_entry_new(failure);
- digestmap_set(fail_entry->intro_failures, (char *) identity, entry);
+ old_entry = digestmap_set(fail_entry->intro_failures,
+ (char *) identity, entry);
+ /* This _should_ be NULL, but in case it isn't, free it. */
+ rend_cache_failure_intro_entry_free(old_entry);
}
/** Using a parsed descriptor <b>desc</b>, check if the introduction points
@@ -361,7 +396,7 @@ cache_failure_intro_add(const uint8_t *identity, const char *service_id,
* descriptor and kept into the failure cache. Then, each intro points that
* are NOT in the descriptor but in the failure cache for the given
* <b>service_id</b> are removed from the failure cache. */
-static void
+STATIC void
validate_intro_point_failure(const rend_service_descriptor_t *desc,
const char *service_id)
{
@@ -383,12 +418,16 @@ validate_intro_point_failure(const rend_service_descriptor_t *desc,
found = cache_failure_intro_lookup(identity, service_id, &entry);
if (found) {
+ /* Dup here since it will be freed at the end when removing the
+ * original entry in the cache. */
+ rend_cache_failure_intro_t *ent_dup = cache_failure_intro_dup(entry);
/* This intro point is in our cache, discard it from the descriptor
* because chances are that it's unusable. */
SMARTLIST_DEL_CURRENT(desc->intro_nodes, intro);
- rend_intro_point_free(intro);
/* Keep it for our new entry. */
- digestmap_set(new_entry->intro_failures, (char *) identity, entry);
+ digestmap_set(new_entry->intro_failures, (char *) identity, ent_dup);
+ /* Only free it when we're done looking at it. */
+ rend_intro_point_free(intro);
continue;
}
} SMARTLIST_FOREACH_END(intro);
@@ -444,8 +483,7 @@ rend_cache_clean_v2_descs_as_dir(time_t now, size_t force_remove)
digestmap_iter_get(iter, &key, &val);
ent = val;
if (ent->parsed->timestamp < cutoff ||
- ent->last_served < last_served_cutoff ||
- !hid_serv_responsible_for_desc_id(key)) {
+ ent->last_served < last_served_cutoff) {
char key_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
base32_encode(key_base32, sizeof(key_base32), key, DIGEST_LEN);
log_info(LD_REND, "Removing descriptor with ID '%s' from cache",
@@ -513,6 +551,42 @@ rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **e)
return ret;
}
+/*
+ * Lookup the v2 service descriptor with the service ID <b>query</b> in the
+ * local service descriptor cache. Return 0 if found and if <b>e</b> is
+ * non NULL, set it with the entry found. Else, a negative value is returned
+ * and <b>e</b> is untouched.
+ * -EINVAL means that <b>query</b> is not a valid service id.
+ * -ENOENT means that no entry in the cache was found. */
+int
+rend_cache_lookup_v2_desc_as_service(const char *query, rend_cache_entry_t **e)
+{
+ int ret = 0;
+ rend_cache_entry_t *entry = NULL;
+
+ tor_assert(rend_cache_local_service);
+ tor_assert(query);
+
+ if (!rend_valid_service_id(query)) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ /* Lookup descriptor and return. */
+ entry = strmap_get_lc(rend_cache_local_service, query);
+ if (!entry) {
+ ret = -ENOENT;
+ goto end;
+ }
+
+ if (e) {
+ *e = entry;
+ }
+
+ end:
+ return ret;
+}
+
/** Lookup the v2 service descriptor with base32-encoded <b>desc_id</b> and
* copy the pointer to it to *<b>desc</b>. Return 1 on success, 0 on
* well-formed-but-not-found, and -1 on failure.
@@ -548,9 +622,11 @@ rend_cache_lookup_v2_desc_as_dir(const char *desc_id, const char **desc)
* If we have a newer descriptor with the same ID, ignore this one.
* If we have an older descriptor with the same ID, replace it.
*
- * Return an appropriate rend_cache_store_status_t.
+ * Return 0 on success, or -1 if we couldn't parse any of them.
+ *
+ * We should only call this function for public (e.g. non bridge) relays.
*/
-rend_cache_store_status_t
+int
rend_cache_store_v2_desc_as_dir(const char *desc)
{
const or_options_t *options = get_options();
@@ -567,12 +643,6 @@ rend_cache_store_v2_desc_as_dir(const char *desc)
time_t now = time(NULL);
tor_assert(rend_cache_v2_dir);
tor_assert(desc);
- if (!hid_serv_acting_as_directory()) {
- /* Cannot store descs, because we are (currently) not acting as
- * hidden service directory. */
- log_info(LD_REND, "Cannot store descs: Not acting as hs dir");
- return RCS_NOTDIR;
- }
while (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content,
&intro_size, &encoded_size,
&next_desc, current_desc, 1) >= 0) {
@@ -582,14 +652,6 @@ rend_cache_store_v2_desc_as_dir(const char *desc)
/* For pretty log statements. */
base32_encode(desc_id_base32, sizeof(desc_id_base32),
desc_id, DIGEST_LEN);
- /* Is desc ID in the range that we are (directly or indirectly) responsible
- * for? */
- if (!hid_serv_responsible_for_desc_id(desc_id)) {
- log_info(LD_REND, "Service descriptor with desc ID %s is not in "
- "interval that we are responsible for.",
- safe_str_client(desc_id_base32));
- goto skip;
- }
/* Is descriptor too old? */
if (parsed->timestamp < now - REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) {
log_info(LD_REND, "Service descriptor with desc ID %s is too old.",
@@ -638,7 +700,6 @@ rend_cache_store_v2_desc_as_dir(const char *desc)
log_info(LD_REND, "Successfully stored service descriptor with desc ID "
"'%s' and len %d.",
safe_str(desc_id_base32), (int)encoded_size);
-
/* Statistics: Note down this potentially new HS. */
if (options->HiddenServiceStatistics) {
rep_hist_stored_maybe_new_hs(e->parsed->pk);
@@ -658,11 +719,85 @@ rend_cache_store_v2_desc_as_dir(const char *desc)
}
if (!number_parsed) {
log_info(LD_REND, "Could not parse any descriptor.");
- return RCS_BADDESC;
+ return -1;
}
log_info(LD_REND, "Parsed %d and added %d descriptor%s.",
number_parsed, number_stored, number_stored != 1 ? "s" : "");
- return RCS_OKAY;
+ return 0;
+}
+
+/** Parse the v2 service descriptor in <b>desc</b> and store it to the
+* local service rend cache. Don't attempt to decrypt the included list of
+* introduction points.
+*
+* If we have a newer descriptor with the same ID, ignore this one.
+* If we have an older descriptor with the same ID, replace it.
+*
+* Return 0 on success, or -1 if we couldn't understand the descriptor.
+*/
+int
+rend_cache_store_v2_desc_as_service(const char *desc)
+{
+ rend_service_descriptor_t *parsed = NULL;
+ char desc_id[DIGEST_LEN];
+ char *intro_content = NULL;
+ size_t intro_size;
+ size_t encoded_size;
+ const char *next_desc;
+ char service_id[REND_SERVICE_ID_LEN_BASE32+1];
+ rend_cache_entry_t *e;
+ int retval = -1;
+ tor_assert(rend_cache_local_service);
+ tor_assert(desc);
+
+ /* Parse the descriptor. */
+ if (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content,
+ &intro_size, &encoded_size,
+ &next_desc, desc, 0) < 0) {
+ log_warn(LD_REND, "Could not parse descriptor.");
+ goto err;
+ }
+ /* Compute service ID from public key. */
+ if (rend_get_service_id(parsed->pk, service_id)<0) {
+ log_warn(LD_REND, "Couldn't compute service ID.");
+ goto err;
+ }
+
+ /* Do we already have a newer descriptor? Allow new descriptors with a
+ rounded timestamp equal to or newer than the current descriptor */
+ e = (rend_cache_entry_t*) strmap_get_lc(rend_cache_local_service,
+ service_id);
+ if (e && e->parsed->timestamp > parsed->timestamp) {
+ log_info(LD_REND, "We already have a newer service descriptor for "
+ "service ID %s.", safe_str_client(service_id));
+ goto okay;
+ }
+ /* We don't care about the introduction points. */
+ tor_free(intro_content);
+ if (!e) {
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ strmap_set_lc(rend_cache_local_service, service_id, e);
+ } else {
+ rend_cache_decrement_allocation(rend_cache_entry_allocation(e));
+ rend_service_descriptor_free(e->parsed);
+ tor_free(e->desc);
+ }
+ e->parsed = parsed;
+ e->desc = tor_malloc_zero(encoded_size + 1);
+ strlcpy(e->desc, desc, encoded_size + 1);
+ e->len = encoded_size;
+ rend_cache_increment_allocation(rend_cache_entry_allocation(e));
+ log_debug(LD_REND,"Successfully stored rend desc '%s', len %d.",
+ safe_str_client(service_id), (int)encoded_size);
+ return 0;
+
+ okay:
+ retval = 0;
+
+ err:
+ rend_service_descriptor_free(parsed);
+ tor_free(intro_content);
+ return retval;
}
/** Parse the v2 service descriptor in <b>desc</b>, decrypt the included list
@@ -678,10 +813,10 @@ rend_cache_store_v2_desc_as_dir(const char *desc)
* If the descriptor's descriptor ID doesn't match <b>desc_id_base32</b>,
* reject it.
*
- * Return an appropriate rend_cache_store_status_t. If entry is not NULL,
- * set it with the cache entry pointer of the descriptor.
+ * Return 0 on success, or -1 if we rejected the descriptor.
+ * If entry is not NULL, set it with the cache entry pointer of the descriptor.
*/
-rend_cache_store_status_t
+int
rend_cache_store_v2_desc_as_client(const char *desc,
const char *desc_id_base32,
const rend_data_t *rend_query,
@@ -713,7 +848,7 @@ rend_cache_store_v2_desc_as_client(const char *desc,
char service_id[REND_SERVICE_ID_LEN_BASE32+1];
char want_desc_id[DIGEST_LEN];
rend_cache_entry_t *e;
- rend_cache_store_status_t retval = RCS_BADDESC;
+ int retval = -1;
tor_assert(rend_cache);
tor_assert(desc);
tor_assert(desc_id_base32);
@@ -860,13 +995,13 @@ rend_cache_store_v2_desc_as_client(const char *desc,
if (entry) {
*entry = e;
}
- return RCS_OKAY;
+ return 0;
okay:
if (entry) {
*entry = e;
}
- retval = RCS_OKAY;
+ retval = 0;
err:
rend_service_descriptor_free(parsed);
diff --git a/src/or/rendcache.h b/src/or/rendcache.h
index 0512058054..0e8b918753 100644
--- a/src/or/rendcache.h
+++ b/src/or/rendcache.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, The Tor Project, Inc. */
+/* Copyright (c) 2015-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -48,27 +48,29 @@ typedef struct rend_cache_failure_t {
digestmap_t *intro_failures;
} rend_cache_failure_t;
+typedef enum {
+ REND_CACHE_TYPE_CLIENT = 1,
+ REND_CACHE_TYPE_SERVICE = 2,
+} rend_cache_type_t;
+
void rend_cache_init(void);
-void rend_cache_clean(time_t now);
+void rend_cache_clean(time_t now, rend_cache_type_t cache_type);
void rend_cache_failure_clean(time_t now);
void rend_cache_clean_v2_descs_as_dir(time_t now, size_t min_to_remove);
void rend_cache_purge(void);
void rend_cache_free_all(void);
int rend_cache_lookup_entry(const char *query, int version,
rend_cache_entry_t **entry_out);
+int rend_cache_lookup_v2_desc_as_service(const char *query,
+ rend_cache_entry_t **entry_out);
int rend_cache_lookup_v2_desc_as_dir(const char *query, const char **desc);
-/** Return value from rend_cache_store_v2_desc_as_{dir,client}. */
-typedef enum {
- RCS_NOTDIR = -2, /**< We're not a directory */
- RCS_BADDESC = -1, /**< This descriptor is no good. */
- RCS_OKAY = 0 /**< All worked as expected */
-} rend_cache_store_status_t;
-
-rend_cache_store_status_t rend_cache_store_v2_desc_as_dir(const char *desc);
-rend_cache_store_status_t rend_cache_store_v2_desc_as_client(const char *desc,
- const char *desc_id_base32,
- const rend_data_t *rend_query,
- rend_cache_entry_t **entry);
+
+int rend_cache_store_v2_desc_as_dir(const char *desc);
+int rend_cache_store_v2_desc_as_service(const char *desc);
+int rend_cache_store_v2_desc_as_client(const char *desc,
+ const char *desc_id_base32,
+ const rend_data_t *rend_query,
+ rend_cache_entry_t **entry);
size_t rend_cache_get_total_allocation(void);
void rend_cache_intro_failure_note(rend_intro_point_failure_t failure,
@@ -76,5 +78,31 @@ void rend_cache_intro_failure_note(rend_intro_point_failure_t failure,
const char *service_id);
void rend_cache_failure_purge(void);
+#ifdef RENDCACHE_PRIVATE
+
+STATIC size_t rend_cache_entry_allocation(const rend_cache_entry_t *e);
+STATIC void rend_cache_entry_free(rend_cache_entry_t *e);
+STATIC void rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t
+ *entry);
+STATIC void rend_cache_failure_entry_free(rend_cache_failure_t *entry);
+STATIC int cache_failure_intro_lookup(const uint8_t *identity,
+ const char *service_id,
+ rend_cache_failure_intro_t
+ **intro_entry);
+STATIC void rend_cache_decrement_allocation(size_t n);
+STATIC void rend_cache_increment_allocation(size_t n);
+STATIC rend_cache_failure_intro_t *rend_cache_failure_intro_entry_new(
+ rend_intro_point_failure_t failure);
+STATIC rend_cache_failure_t *rend_cache_failure_entry_new(void);
+STATIC void rend_cache_failure_remove(rend_service_descriptor_t *desc);
+STATIC void cache_failure_intro_add(const uint8_t *identity,
+ const char *service_id,
+ rend_intro_point_failure_t failure);
+STATIC void validate_intro_point_failure(const rend_service_descriptor_t *desc,
+ const char *service_id);
+
+STATIC void rend_cache_failure_entry_free_(void *entry);
+#endif
+
#endif /* TOR_RENDCACHE_H */
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index 57c7b31604..c119d86adf 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -1,5 +1,5 @@
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -52,7 +52,7 @@ rend_client_introcirc_has_opened(origin_circuit_t *circ)
tor_assert(circ->cpath);
log_info(LD_REND,"introcirc is open");
- connection_ap_attach_pending();
+ connection_ap_attach_pending(1);
}
/** Send the establish-rendezvous cell along a rendezvous circuit. if
@@ -65,11 +65,7 @@ rend_client_send_establish_rendezvous(origin_circuit_t *circ)
tor_assert(circ->rend_data);
log_info(LD_REND, "Sending an ESTABLISH_RENDEZVOUS cell");
- if (crypto_rand(circ->rend_data->rend_cookie, REND_COOKIE_LEN) < 0) {
- log_warn(LD_BUG, "Internal error: Couldn't produce random cookie.");
- circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
- return -1;
- }
+ crypto_rand(circ->rend_data->rend_cookie, REND_COOKIE_LEN);
/* Set timestamp_dirty, because circuit_expire_building expects it,
* and the rend cookie also means we've used the circ. */
@@ -177,6 +173,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP,
AP_CONN_STATE_CIRCUIT_WAIT,
introcirc->rend_data->onion_address))) {
+ connection_ap_mark_as_non_pending_circuit(TO_ENTRY_CONN(conn));
conn->state = AP_CONN_STATE_RENDDESC_WAIT;
}
}
@@ -185,7 +182,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
goto cleanup;
}
- /* first 20 bytes of payload are the hash of Bob's pk */
+ /* first 20 bytes of payload are the hash of the service's pk */
intro_key = NULL;
SMARTLIST_FOREACH(entry->parsed->intro_nodes, rend_intro_point_t *,
intro, {
@@ -895,7 +892,6 @@ rend_client_fetch_v2_desc(rend_data_t *query, smartlist_t *hsdirs)
void
rend_client_refetch_v2_renddesc(rend_data_t *rend_query)
{
- int ret;
rend_cache_entry_t *e = NULL;
tor_assert(rend_query);
@@ -915,11 +911,10 @@ rend_client_refetch_v2_renddesc(rend_data_t *rend_query)
log_debug(LD_REND, "Fetching v2 rendezvous descriptor for service %s",
safe_str_client(rend_query->onion_address));
- ret = rend_client_fetch_v2_desc(rend_query, NULL);
- if (ret <= 0) {
- /* Close pending connections on error or if no hsdir can be found. */
- rend_client_desc_trynow(rend_query->onion_address);
- }
+ rend_client_fetch_v2_desc(rend_query, NULL);
+ /* We don't need to look the error code because either on failure or
+ * success, the necessary steps to continue the HS connection will be
+ * triggered once the descriptor arrives or if all fetch failed. */
return;
}
@@ -1059,9 +1054,11 @@ rend_client_report_intro_point_failure(extend_info_t *failed_intro,
rend_client_refetch_v2_renddesc(rend_query);
/* move all pending streams back to renddesc_wait */
+ /* NOTE: We can now do this faster, if we use pending_entry_connections */
while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP,
AP_CONN_STATE_CIRCUIT_WAIT,
rend_query->onion_address))) {
+ connection_ap_mark_as_non_pending_circuit(TO_ENTRY_CONN(conn));
conn->state = AP_CONN_STATE_RENDDESC_WAIT;
}
@@ -1097,9 +1094,9 @@ rend_client_rendezvous_acked(origin_circuit_t *circ, const uint8_t *request,
circ->base_.timestamp_dirty = time(NULL);
/* From a path bias point of view, this circuit is now successfully used.
- * Waiting any longer opens us up to attacks from Bob. He could induce
- * Alice to attempt to connect to his hidden service and never reply
- * to her rend requests */
+ * Waiting any longer opens us up to attacks from malicious hidden services.
+ * They could induce the client to attempt to connect to their hidden
+ * service and never reply to the client's rend requests */
pathbias_mark_use_success(circ);
/* XXXX This is a pretty brute-force approach. It'd be better to
@@ -1107,11 +1104,11 @@ rend_client_rendezvous_acked(origin_circuit_t *circ, const uint8_t *request,
* than trying to attach them all. See comments bug 743. */
/* If we already have the introduction circuit built, make sure we send
* the INTRODUCE cell _now_ */
- connection_ap_attach_pending();
+ connection_ap_attach_pending(1);
return 0;
}
-/** Bob sent us a rendezvous cell; join the circuits. */
+/** The service sent us a rendezvous cell; join the circuits. */
int
rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request,
size_t request_len)
@@ -1136,7 +1133,8 @@ rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request,
log_info(LD_REND,"Got RENDEZVOUS2 cell from hidden service.");
- /* first DH_KEY_LEN bytes are g^y from bob. Finish the dh handshake...*/
+ /* first DH_KEY_LEN bytes are g^y from the service. Finish the dh
+ * handshake...*/
tor_assert(circ->build_state);
tor_assert(circ->build_state->pending_final_cpath);
hop = circ->build_state->pending_final_cpath;
@@ -1165,7 +1163,7 @@ rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request,
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_REND_JOINED);
hop->state = CPATH_STATE_OPEN;
/* set the windows to default. these are the windows
- * that alice thinks bob has.
+ * that the client thinks the service has.
*/
hop->package_window = circuit_initial_package_window();
hop->deliver_window = CIRCWINDOW_START;
@@ -1226,12 +1224,7 @@ rend_client_desc_trynow(const char *query)
base_conn->timestamp_lastread = now;
base_conn->timestamp_lastwritten = now;
- if (connection_ap_handshake_attach_circuit(conn) < 0) {
- /* it will never work */
- log_warn(LD_REND,"Rendezvous attempt failed. Closing.");
- if (!base_conn->marked_for_close)
- connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
- }
+ connection_ap_mark_as_pending_circuit(conn);
} else { /* 404, or fetch didn't get that far */
log_notice(LD_REND,"Closing stream for '%s.onion': hidden service is "
"unavailable (try again later).",
@@ -1372,11 +1365,19 @@ rend_client_get_random_intro_impl(const rend_cache_entry_t *entry,
smartlist_del(usable_nodes, i);
goto again;
}
+#ifdef ENABLE_TOR2WEB_MODE
+ new_extend_info = extend_info_from_node(node, options->Tor2webMode);
+#else
new_extend_info = extend_info_from_node(node, 0);
+#endif
if (!new_extend_info) {
+ const char *alternate_reason = "";
+#ifdef ENABLE_TOR2WEB_MODE
+ alternate_reason = ", or we cannot connect directly to it";
+#endif
log_info(LD_REND, "We don't have a descriptor for the intro-point relay "
- "'%s'; trying another.",
- extend_info_describe(intro->extend_info));
+ "'%s'%s; trying another.",
+ extend_info_describe(intro->extend_info), alternate_reason);
smartlist_del(usable_nodes, i);
goto again;
} else {
diff --git a/src/or/rendclient.h b/src/or/rendclient.h
index 124433ef31..e90dac07ab 100644
--- a/src/or/rendclient.h
+++ b/src/or/rendclient.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c
index dfa6c1e8be..56c49fee47 100644
--- a/src/or/rendcommon.c
+++ b/src/or/rendcommon.c
@@ -1,5 +1,5 @@
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -11,13 +11,16 @@
#include "or.h"
#include "circuitbuild.h"
#include "config.h"
+#include "control.h"
#include "rendclient.h"
#include "rendcommon.h"
#include "rendmid.h"
#include "rendservice.h"
#include "rephist.h"
+#include "router.h"
#include "routerlist.h"
#include "routerparse.h"
+#include "networkstatus.h"
/** Return 0 if one and two are the same service ids, else -1 or 1 */
int
@@ -208,7 +211,7 @@ rend_encode_v2_intro_points(char **encoded, rend_service_descriptor_t *desc)
goto done;
}
/* Assemble everything for this introduction point. */
- address = tor_dup_addr(&info->addr);
+ address = tor_addr_to_str_dup(&info->addr);
res = tor_snprintf(unenc + unenc_written, unenc_len - unenc_written,
"introduction-point %s\n"
"ip-address %s\n"
@@ -268,11 +271,7 @@ rend_encrypt_v2_intro_points_basic(char **encrypted_out,
tor_assert(client_cookies && smartlist_len(client_cookies) > 0);
/* Generate session key. */
- if (crypto_rand(session_key, CIPHER_KEY_LEN) < 0) {
- log_warn(LD_REND, "Unable to generate random session key to encrypt "
- "introduction point string.");
- goto done;
- }
+ crypto_rand(session_key, CIPHER_KEY_LEN);
/* Determine length of encrypted introduction points including session
* keys. */
@@ -334,11 +333,7 @@ rend_encrypt_v2_intro_points_basic(char **encrypted_out,
REND_BASIC_AUTH_CLIENT_MULTIPLE;
i < REND_BASIC_AUTH_CLIENT_MULTIPLE - 1; i++) {
client_part = tor_malloc_zero(REND_BASIC_AUTH_CLIENT_ENTRY_LEN);
- if (crypto_rand(client_part, REND_BASIC_AUTH_CLIENT_ENTRY_LEN) < 0) {
- log_warn(LD_REND, "Unable to generate fake client entry.");
- tor_free(client_part);
- goto done;
- }
+ crypto_rand(client_part, REND_BASIC_AUTH_CLIENT_ENTRY_LEN);
smartlist_add(encrypted_session_keys, client_part);
}
/* Sort smartlist and put elements in result in order. */
@@ -461,6 +456,7 @@ rend_encode_v2_descriptors(smartlist_t *descs_out,
smartlist_t *client_cookies)
{
char service_id[DIGEST_LEN];
+ char service_id_base32[REND_SERVICE_ID_LEN_BASE32+1];
uint32_t time_period;
char *ipos_base64 = NULL, *ipos = NULL, *ipos_encrypted = NULL,
*descriptor_cookie = NULL;
@@ -655,6 +651,11 @@ rend_encode_v2_descriptors(smartlist_t *descs_out,
goto err;
}
smartlist_add(descs_out, enc);
+ /* Add the uploaded descriptor to the local service's descriptor cache */
+ rend_cache_store_v2_desc_as_service(enc->desc_str);
+ base32_encode(service_id_base32, sizeof(service_id_base32),
+ service_id, REND_SERVICE_ID_LEN);
+ control_event_hs_descriptor_created(service_id_base32, desc_id_base32, k);
}
log_info(LD_REND, "Successfully encoded a v2 descriptor and "
@@ -687,37 +688,6 @@ rend_get_service_id(crypto_pk_t *pk, char *out)
return 0;
}
-/** Determines whether <b>a</b> is in the interval of <b>b</b> (excluded) and
- * <b>c</b> (included) in a circular digest ring; returns 1 if this is the
- * case, and 0 otherwise.
- */
-int
-rend_id_is_in_interval(const char *a, const char *b, const char *c)
-{
- int a_b, b_c, c_a;
- tor_assert(a);
- tor_assert(b);
- tor_assert(c);
-
- /* There are five cases in which a is outside the interval ]b,c]: */
- a_b = tor_memcmp(a,b,DIGEST_LEN);
- if (a_b == 0)
- return 0; /* 1. a == b (b is excluded) */
- b_c = tor_memcmp(b,c,DIGEST_LEN);
- if (b_c == 0)
- return 0; /* 2. b == c (interval is empty) */
- else if (a_b <= 0 && b_c < 0)
- return 0; /* 3. a b c */
- c_a = tor_memcmp(c,a,DIGEST_LEN);
- if (c_a < 0 && a_b <= 0)
- return 0; /* 4. c a b */
- else if (b_c < 0 && c_a < 0)
- return 0; /* 5. b c a */
-
- /* In the other cases (a c b; b a c; c b a), a is inside the interval. */
- return 1;
-}
-
/** Return true iff <b>query</b> is a syntactically valid service ID (as
* generated by rend_get_service_id). */
int
@@ -952,6 +922,41 @@ rend_data_client_create(const char *onion_address, const char *desc_id,
return NULL;
}
+/** Determine the routers that are responsible for <b>id</b> (binary) and
+ * add pointers to those routers' routerstatus_t to <b>responsible_dirs</b>.
+ * Return -1 if we're returning an empty smartlist, else return 0.
+ */
+int
+hid_serv_get_responsible_directories(smartlist_t *responsible_dirs,
+ const char *id)
+{
+ int start, found, n_added = 0, i;
+ networkstatus_t *c = networkstatus_get_latest_consensus();
+ if (!c || !smartlist_len(c->routerstatus_list)) {
+ log_warn(LD_REND, "We don't have a consensus, so we can't perform v2 "
+ "rendezvous operations.");
+ return -1;
+ }
+ tor_assert(id);
+ start = networkstatus_vote_find_entry_idx(c, id, &found);
+ if (start == smartlist_len(c->routerstatus_list)) start = 0;
+ i = start;
+ do {
+ routerstatus_t *r = smartlist_get(c->routerstatus_list, i);
+ if (r->is_hs_dir) {
+ smartlist_add(responsible_dirs, r);
+ if (++n_added == REND_NUMBER_OF_CONSECUTIVE_REPLICAS)
+ return 0;
+ }
+ if (++i == smartlist_len(c->routerstatus_list))
+ i = 0;
+ } while (i != start);
+
+ /* Even though we don't have the desired number of hidden service
+ * directories, be happy if we got any. */
+ return smartlist_len(responsible_dirs) ? 0 : -1;
+}
+
/* Length of the 'extended' auth cookie used to encode auth type before
* base64 encoding. */
#define REND_DESC_COOKIE_LEN_EXT (REND_DESC_COOKIE_LEN + 1)
@@ -1062,3 +1067,4 @@ rend_auth_decode_cookie(const char *cookie_in, uint8_t *cookie_out,
return res;
}
+
diff --git a/src/or/rendcommon.h b/src/or/rendcommon.h
index f0a740b75f..88cf512f4a 100644
--- a/src/or/rendcommon.h
+++ b/src/or/rendcommon.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -19,7 +19,7 @@ typedef enum rend_intro_point_failure_t {
} rend_intro_point_failure_t;
/** Free all storage associated with <b>data</b> */
-static INLINE void
+static inline void
rend_data_free(rend_data_t *data)
{
if (!data) {
@@ -54,10 +54,11 @@ int rend_encode_v2_descriptors(smartlist_t *descs_out,
int rend_compute_v2_desc_id(char *desc_id_out, const char *service_id,
const char *descriptor_cookie,
time_t now, uint8_t replica);
-int rend_id_is_in_interval(const char *a, const char *b, const char *c);
void rend_get_descriptor_id_bytes(char *descriptor_id_out,
const char *service_id,
const char *secret_id_part);
+int hid_serv_get_responsible_directories(smartlist_t *responsible_dirs,
+ const char *id);
rend_data_t *rend_data_dup(const rend_data_t *data);
rend_data_t *rend_data_client_create(const char *onion_address,
diff --git a/src/or/rendmid.c b/src/or/rendmid.c
index 2451acb514..a33ad92966 100644
--- a/src/or/rendmid.c
+++ b/src/or/rendmid.c
@@ -1,5 +1,5 @@
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -80,7 +80,7 @@ rend_mid_establish_intro(or_circuit_t *circ, const uint8_t *request,
goto err;
}
- /* The request is valid. First, compute the hash of Bob's PK.*/
+ /* The request is valid. First, compute the hash of the service's PK.*/
if (crypto_pk_get_digest(pk, pk_digest)<0) {
log_warn(LD_BUG, "Internal error: couldn't hash public key.");
goto err;
@@ -178,7 +178,8 @@ rend_mid_introduce(or_circuit_t *circ, const uint8_t *request,
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
(char*)request, REND_SERVICE_ID_LEN);
- /* The first 20 bytes are all we look at: they have a hash of Bob's PK. */
+ /* The first 20 bytes are all we look at: they have a hash of the service's
+ * PK. */
intro_circ = circuit_get_intro_point((const uint8_t*)request);
if (!intro_circ) {
log_info(LD_REND,
@@ -202,7 +203,7 @@ rend_mid_introduce(or_circuit_t *circ, const uint8_t *request,
"Unable to send INTRODUCE2 cell to Tor client.");
goto err;
}
- /* And send an ack down Alice's circuit. Empty body means succeeded. */
+ /* And send an ack down the client's circuit. Empty body means succeeded. */
if (relay_send_command_from_edge(0,TO_CIRCUIT(circ),
RELAY_COMMAND_INTRODUCE_ACK,
NULL,0,NULL)) {
@@ -337,7 +338,7 @@ rend_mid_rendezvous(or_circuit_t *circ, const uint8_t *request,
circ->circuit_carries_hs_traffic_stats = 1;
}
- /* Send the RENDEZVOUS2 cell to Alice. */
+ /* Send the RENDEZVOUS2 cell to the client. */
if (relay_send_command_from_edge(0, TO_CIRCUIT(rend_circ),
RELAY_COMMAND_RENDEZVOUS2,
(char*)(request+REND_COOKIE_LEN),
diff --git a/src/or/rendmid.h b/src/or/rendmid.h
index 6bd691a740..10d1287085 100644
--- a/src/or/rendmid.h
+++ b/src/or/rendmid.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 030c836e78..21ac70a39b 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -1,5 +1,5 @@
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -40,7 +40,7 @@ static rend_intro_point_t *find_expiring_intro_point(
static extend_info_t *find_rp_for_intro(
const rend_intro_cell_t *intro,
- uint8_t *need_free_out, char **err_msg_out);
+ char **err_msg_out);
static int intro_point_accepted_intro_count(rend_intro_point_t *intro);
static int intro_point_should_expire_now(rend_intro_point_t *intro,
@@ -1452,13 +1452,6 @@ rend_service_receive_introduction(origin_circuit_t *circuit,
rend_intro_cell_t *parsed_req = NULL;
/* Rendezvous point */
extend_info_t *rp = NULL;
- /*
- * We need to look up and construct the extend_info_t for v0 and v1,
- * but all the info is in the cell and it's constructed by the parser
- * for v2 and v3, so freeing it would be a double-free. Use this to
- * keep track of whether we should free it.
- */
- uint8_t need_rp_free = 0;
/* XXX not handled yet */
char buf[RELAY_PAYLOAD_SIZE];
char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */
@@ -1598,7 +1591,7 @@ rend_service_receive_introduction(origin_circuit_t *circuit,
++(intro_point->accepted_introduce2_count);
/* Find the rendezvous point */
- rp = find_rp_for_intro(parsed_req, &need_rp_free, &err_msg);
+ rp = find_rp_for_intro(parsed_req, &err_msg);
if (!rp)
goto log_error;
@@ -1679,7 +1672,7 @@ rend_service_receive_introduction(origin_circuit_t *circuit,
/* help predict this next time */
rep_hist_note_used_internal(now, circ_needs_uptime, 1);
- /* Launch a circuit to alice's chosen rendezvous point.
+ /* Launch a circuit to the client's chosen rendezvous point.
*/
for (i=0;i<MAX_REND_FAILURES;i++) {
int flags = CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_IS_INTERNAL;
@@ -1757,27 +1750,25 @@ rend_service_receive_introduction(origin_circuit_t *circuit,
/* Free the parsed cell */
rend_service_free_intro(parsed_req);
- /* Free rp if we must */
- if (need_rp_free) extend_info_free(rp);
+ /* Free rp */
+ extend_info_free(rp);
return status;
}
/** Given a parsed and decrypted INTRODUCE2, find the rendezvous point or
- * return NULL and an error string if we can't.
- */
-
+ * return NULL and an error string if we can't. Return a newly allocated
+ * extend_info_t* for the rendezvous point. */
static extend_info_t *
find_rp_for_intro(const rend_intro_cell_t *intro,
- uint8_t *need_free_out, char **err_msg_out)
+ char **err_msg_out)
{
extend_info_t *rp = NULL;
char *err_msg = NULL;
const char *rp_nickname = NULL;
const node_t *node = NULL;
- uint8_t need_free = 0;
- if (!intro || !need_free_out) {
+ if (!intro) {
if (err_msg_out)
err_msg = tor_strdup("Bad parameters to find_rp_for_intro()");
@@ -1808,13 +1799,11 @@ find_rp_for_intro(const rend_intro_cell_t *intro,
}
goto err;
- } else {
- need_free = 1;
}
} else if (intro->version == 2) {
- rp = intro->u.v2.extend_info;
+ rp = extend_info_dup(intro->u.v2.extend_info);
} else if (intro->version == 3) {
- rp = intro->u.v3.extend_info;
+ rp = extend_info_dup(intro->u.v3.extend_info);
} else {
if (err_msg_out) {
tor_asprintf(&err_msg,
@@ -1825,6 +1814,18 @@ find_rp_for_intro(const rend_intro_cell_t *intro,
goto err;
}
+ /* Make sure the RP we are being asked to connect to is _not_ a private
+ * address unless it's allowed. Let's avoid to build a circuit to our
+ * second middle node and fail right after when extending to the RP. */
+ if (!extend_info_addr_is_allowed(&rp->addr)) {
+ if (err_msg_out) {
+ tor_asprintf(&err_msg,
+ "Relay IP in INTRODUCE2 cell is private address.");
+ }
+ extend_info_free(rp);
+ rp = NULL;
+ goto err;
+ }
goto done;
err:
@@ -1832,8 +1833,6 @@ find_rp_for_intro(const rend_intro_cell_t *intro,
else tor_free(err_msg);
done:
- if (rp && need_free_out) *need_free_out = need_free;
-
return rp;
}
@@ -2714,7 +2713,7 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
circuit->rend_data->rend_pk_digest);
if (!service) {
log_warn(LD_REND, "Unrecognized service ID %s on introduction circuit %u.",
- serviceid, (unsigned)circuit->base_.n_circ_id);
+ safe_str_client(serviceid), (unsigned)circuit->base_.n_circ_id);
reason = END_CIRC_REASON_NOSUCHSERVICE;
goto err;
}
@@ -2979,7 +2978,7 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
/* Append the cpath entry. */
hop->state = CPATH_STATE_OPEN;
/* set the windows to default. these are the windows
- * that bob thinks alice has.
+ * that the service thinks the client has.
*/
hop->package_window = circuit_initial_package_window();
hop->deliver_window = CIRCWINDOW_START;
@@ -3047,7 +3046,8 @@ find_expiring_intro_point(rend_service_t *service, origin_circuit_t *circ)
tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_INTRO);
- SMARTLIST_FOREACH(service->intro_nodes, rend_intro_point_t *, intro_point,
+ SMARTLIST_FOREACH(service->expiring_nodes, rend_intro_point_t *,
+ intro_point,
if (crypto_pk_eq_keys(intro_point->intro_key, circ->intro_key)) {
return intro_point;
});
@@ -3211,39 +3211,72 @@ upload_service_descriptor(rend_service_t *service)
rendpostperiod = get_options()->RendPostPeriod;
- /* Upload descriptor? */
- if (get_options()->PublishHidServDescriptors) {
- networkstatus_t *c = networkstatus_get_latest_consensus();
- if (c && smartlist_len(c->routerstatus_list) > 0) {
- int seconds_valid, i, j, num_descs;
- smartlist_t *descs = smartlist_new();
- smartlist_t *client_cookies = smartlist_new();
- /* Either upload a single descriptor (including replicas) or one
- * descriptor for each authorized client in case of authorization
- * type 'stealth'. */
- num_descs = service->auth_type == REND_STEALTH_AUTH ?
- smartlist_len(service->clients) : 1;
- for (j = 0; j < num_descs; j++) {
- crypto_pk_t *client_key = NULL;
- rend_authorized_client_t *client = NULL;
- smartlist_clear(client_cookies);
- switch (service->auth_type) {
- case REND_NO_AUTH:
- /* Do nothing here. */
- break;
- case REND_BASIC_AUTH:
- SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *,
- cl, smartlist_add(client_cookies, cl->descriptor_cookie));
- break;
- case REND_STEALTH_AUTH:
- client = smartlist_get(service->clients, j);
- client_key = client->client_key;
- smartlist_add(client_cookies, client->descriptor_cookie);
- break;
- }
- /* Encode the current descriptor. */
+ networkstatus_t *c = networkstatus_get_latest_consensus();
+ if (c && smartlist_len(c->routerstatus_list) > 0) {
+ int seconds_valid, i, j, num_descs;
+ smartlist_t *descs = smartlist_new();
+ smartlist_t *client_cookies = smartlist_new();
+ /* Either upload a single descriptor (including replicas) or one
+ * descriptor for each authorized client in case of authorization
+ * type 'stealth'. */
+ num_descs = service->auth_type == REND_STEALTH_AUTH ?
+ smartlist_len(service->clients) : 1;
+ for (j = 0; j < num_descs; j++) {
+ crypto_pk_t *client_key = NULL;
+ rend_authorized_client_t *client = NULL;
+ smartlist_clear(client_cookies);
+ switch (service->auth_type) {
+ case REND_NO_AUTH:
+ /* Do nothing here. */
+ break;
+ case REND_BASIC_AUTH:
+ SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *,
+ cl, smartlist_add(client_cookies, cl->descriptor_cookie));
+ break;
+ case REND_STEALTH_AUTH:
+ client = smartlist_get(service->clients, j);
+ client_key = client->client_key;
+ smartlist_add(client_cookies, client->descriptor_cookie);
+ break;
+ }
+ /* Encode the current descriptor. */
+ seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
+ now, 0,
+ service->auth_type,
+ client_key,
+ client_cookies);
+ if (seconds_valid < 0) {
+ log_warn(LD_BUG, "Internal error: couldn't encode service "
+ "descriptor; not uploading.");
+ smartlist_free(descs);
+ smartlist_free(client_cookies);
+ return;
+ }
+ rend_get_service_id(service->desc->pk, serviceid);
+ if (get_options()->PublishHidServDescriptors) {
+ /* Post the current descriptors to the hidden service directories. */
+ log_info(LD_REND, "Launching upload for hidden service %s",
+ serviceid);
+ directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
+ seconds_valid);
+ }
+ /* Free memory for descriptors. */
+ for (i = 0; i < smartlist_len(descs); i++)
+ rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
+ smartlist_clear(descs);
+ /* Update next upload time. */
+ if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS
+ > rendpostperiod)
+ service->next_upload_time = now + rendpostperiod;
+ else if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS)
+ service->next_upload_time = now + seconds_valid + 1;
+ else
+ service->next_upload_time = now + seconds_valid -
+ REND_TIME_PERIOD_OVERLAPPING_V2_DESCS + 1;
+ /* Post also the next descriptors, if necessary. */
+ if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) {
seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
- now, 0,
+ now, 1,
service->auth_type,
client_key,
client_cookies);
@@ -3254,51 +3287,23 @@ upload_service_descriptor(rend_service_t *service)
smartlist_free(client_cookies);
return;
}
- /* Post the current descriptors to the hidden service directories. */
- rend_get_service_id(service->desc->pk, serviceid);
- log_info(LD_REND, "Launching upload for hidden service %s",
- serviceid);
- directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
- seconds_valid);
+ if (get_options()->PublishHidServDescriptors) {
+ directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
+ seconds_valid);
+ }
/* Free memory for descriptors. */
for (i = 0; i < smartlist_len(descs); i++)
rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
smartlist_clear(descs);
- /* Update next upload time. */
- if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS
- > rendpostperiod)
- service->next_upload_time = now + rendpostperiod;
- else if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS)
- service->next_upload_time = now + seconds_valid + 1;
- else
- service->next_upload_time = now + seconds_valid -
- REND_TIME_PERIOD_OVERLAPPING_V2_DESCS + 1;
- /* Post also the next descriptors, if necessary. */
- if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) {
- seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
- now, 1,
- service->auth_type,
- client_key,
- client_cookies);
- if (seconds_valid < 0) {
- log_warn(LD_BUG, "Internal error: couldn't encode service "
- "descriptor; not uploading.");
- smartlist_free(descs);
- smartlist_free(client_cookies);
- return;
- }
- directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
- seconds_valid);
- /* Free memory for descriptors. */
- for (i = 0; i < smartlist_len(descs); i++)
- rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
- smartlist_clear(descs);
- }
}
- smartlist_free(descs);
- smartlist_free(client_cookies);
- uploaded = 1;
+ }
+ smartlist_free(descs);
+ smartlist_free(client_cookies);
+ uploaded = 1;
+ if (get_options()->PublishHidServDescriptors) {
log_info(LD_REND, "Successfully uploaded v2 rend descriptors!");
+ } else {
+ log_info(LD_REND, "Successfully stored created v2 rend descriptors!");
}
}
@@ -3643,9 +3648,6 @@ rend_consider_services_upload(time_t now)
MIN_REND_INITIAL_POST_DELAY_TESTING :
MIN_REND_INITIAL_POST_DELAY);
- if (!get_options()->PublishHidServDescriptors)
- return;
-
for (i=0; i < smartlist_len(rend_service_list); ++i) {
service = smartlist_get(rend_service_list, i);
if (!service->next_upload_time) { /* never been uploaded yet */
diff --git a/src/or/rendservice.h b/src/or/rendservice.h
index 493baa8f98..4966cb0302 100644
--- a/src/or/rendservice.h
+++ b/src/or/rendservice.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/rephist.c b/src/or/rephist.c
index fe0997c891..b94ad29650 100644
--- a/src/or/rephist.c
+++ b/src/or/rephist.c
@@ -1,5 +1,5 @@
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -148,7 +148,7 @@ get_link_history(const char *from_id, const char *to_id)
return NULL;
if (tor_digest_is_zero(to_id))
return NULL;
- lhist = (link_history_t*) digestmap_get(orhist->link_history_map, to_id);
+ lhist = digestmap_get(orhist->link_history_map, to_id);
if (!lhist) {
lhist = tor_malloc_zero(sizeof(link_history_t));
rephist_total_alloc += sizeof(link_history_t);
@@ -920,7 +920,7 @@ parse_possibly_bad_iso_time(const char *s, time_t *time_out)
* that's about as much before <b>now</b> as <b>t</b> was before
* <b>stored_at</b>.
*/
-static INLINE time_t
+static inline time_t
correct_time(time_t t, time_t now, time_t stored_at, time_t started_measuring)
{
if (t < started_measuring - 24*60*60*365)
@@ -1190,7 +1190,7 @@ commit_max(bw_array_t *b)
}
/** Shift the current observation time of <b>b</b> forward by one second. */
-static INLINE void
+static inline void
advance_obs(bw_array_t *b)
{
int nextidx;
@@ -1216,7 +1216,7 @@ advance_obs(bw_array_t *b)
/** Add <b>n</b> bytes to the number of bytes in <b>b</b> for second
* <b>when</b>. */
-static INLINE void
+static inline void
add_obs(bw_array_t *b, time_t when, uint64_t n)
{
if (when < b->cur_obs_time)
@@ -1250,6 +1250,18 @@ bw_array_new(void)
return b;
}
+/** Free storage held by bandwidth array <b>b</b>. */
+static void
+bw_array_free(bw_array_t *b)
+{
+ if (!b) {
+ return;
+ }
+
+ rephist_total_alloc -= sizeof(bw_array_t);
+ tor_free(b);
+}
+
/** Recent history of bandwidth observations for read operations. */
static bw_array_t *read_array = NULL;
/** Recent history of bandwidth observations for write operations. */
@@ -1266,10 +1278,11 @@ static bw_array_t *dir_write_array = NULL;
static void
bw_arrays_init(void)
{
- tor_free(read_array);
- tor_free(write_array);
- tor_free(dir_read_array);
- tor_free(dir_write_array);
+ bw_array_free(read_array);
+ bw_array_free(write_array);
+ bw_array_free(dir_read_array);
+ bw_array_free(dir_write_array);
+
read_array = bw_array_new();
write_array = bw_array_new();
dir_read_array = bw_array_new();
@@ -1780,6 +1793,7 @@ rep_hist_remove_predicted_ports(const smartlist_t *rmv_ports)
SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) {
if (bitarray_is_set(remove_ports, pp->port)) {
tor_free(pp);
+ rephist_total_alloc -= sizeof(*pp);
SMARTLIST_DEL_CURRENT(predicted_ports_list, pp);
}
} SMARTLIST_FOREACH_END(pp);
@@ -3026,21 +3040,21 @@ rep_hist_stored_maybe_new_hs(const crypto_pk_t *pubkey)
/* The number of cells that are supposed to be hidden from the adversary
* by adding noise from the Laplace distribution. This value, divided by
- * EPSILON, is Laplace parameter b. */
+ * EPSILON, is Laplace parameter b. It must be greather than 0. */
#define REND_CELLS_DELTA_F 2048
/* Security parameter for obfuscating number of cells with a value between
- * 0 and 1. Smaller values obfuscate observations more, but at the same
+ * ]0.0, 1.0]. Smaller values obfuscate observations more, but at the same
* time make statistics less usable. */
#define REND_CELLS_EPSILON 0.3
/* The number of cells that are supposed to be hidden from the adversary
* by rounding up to the next multiple of this number. */
#define REND_CELLS_BIN_SIZE 1024
-/* The number of service identities that are supposed to be hidden from
- * the adversary by adding noise from the Laplace distribution. This
- * value, divided by EPSILON, is Laplace parameter b. */
+/* The number of service identities that are supposed to be hidden from the
+ * adversary by adding noise from the Laplace distribution. This value,
+ * divided by EPSILON, is Laplace parameter b. It must be greater than 0. */
#define ONIONS_SEEN_DELTA_F 8
/* Security parameter for obfuscating number of service identities with a
- * value between 0 and 1. Smaller values obfuscate observations more, but
+ * value between ]0.0, 1.0]. Smaller values obfuscate observations more, but
* at the same time make statistics less usable. */
#define ONIONS_SEEN_EPSILON 0.3
/* The number of service identities that are supposed to be hidden from
@@ -3172,10 +3186,19 @@ rep_hist_free_all(void)
{
hs_stats_free(hs_stats);
digestmap_free(history_map, free_or_history);
- tor_free(read_array);
- tor_free(write_array);
- tor_free(dir_read_array);
- tor_free(dir_write_array);
+
+ bw_array_free(read_array);
+ read_array = NULL;
+
+ bw_array_free(write_array);
+ write_array = NULL;
+
+ bw_array_free(dir_read_array);
+ dir_read_array = NULL;
+
+ bw_array_free(dir_write_array);
+ dir_write_array = NULL;
+
tor_free(exit_bytes_read);
tor_free(exit_bytes_written);
tor_free(exit_streams);
@@ -3190,5 +3213,8 @@ rep_hist_free_all(void)
}
rep_hist_desc_stats_term();
total_descriptor_downloads = 0;
+
+ tor_assert_nonfatal(rephist_total_alloc == 0);
+ tor_assert_nonfatal_once(rephist_total_num == 0);
}
diff --git a/src/or/rephist.h b/src/or/rephist.h
index f94b4e8ff1..145da97d02 100644
--- a/src/or/rephist.h
+++ b/src/or/rephist.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/replaycache.c b/src/or/replaycache.c
index 569e0736cb..23a1737b18 100644
--- a/src/or/replaycache.c
+++ b/src/or/replaycache.c
@@ -1,4 +1,4 @@
- /* Copyright (c) 2012-2015, The Tor Project, Inc. */
+ /* Copyright (c) 2012-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/*
@@ -23,7 +23,7 @@ replaycache_free(replaycache_t *r)
return;
}
- if (r->digests_seen) digestmap_free(r->digests_seen, tor_free_);
+ if (r->digests_seen) digest256map_free(r->digests_seen, tor_free_);
tor_free(r);
}
@@ -54,7 +54,7 @@ replaycache_new(time_t horizon, time_t interval)
r->scrub_interval = interval;
r->scrubbed = 0;
r->horizon = horizon;
- r->digests_seen = digestmap_new();
+ r->digests_seen = digest256map_new();
err:
return r;
@@ -69,7 +69,7 @@ replaycache_add_and_test_internal(
time_t *elapsed)
{
int rv = 0;
- char digest[DIGEST_LEN];
+ uint8_t digest[DIGEST256_LEN];
time_t *access_time;
/* sanity check */
@@ -80,10 +80,10 @@ replaycache_add_and_test_internal(
}
/* compute digest */
- crypto_digest(digest, (const char *)data, len);
+ crypto_digest256((char *)digest, (const char *)data, len, DIGEST_SHA256);
/* check map */
- access_time = digestmap_get(r->digests_seen, digest);
+ access_time = digest256map_get(r->digests_seen, digest);
/* seen before? */
if (access_time != NULL) {
@@ -114,7 +114,7 @@ replaycache_add_and_test_internal(
/* No, so no hit and update the digest map with the current time */
access_time = tor_malloc(sizeof(*access_time));
*access_time = present;
- digestmap_set(r->digests_seen, digest, access_time);
+ digest256map_set(r->digests_seen, digest, access_time);
}
/* now scrub the cache if it's time */
@@ -130,8 +130,8 @@ replaycache_add_and_test_internal(
STATIC void
replaycache_scrub_if_needed_internal(time_t present, replaycache_t *r)
{
- digestmap_iter_t *itr = NULL;
- const char *digest;
+ digest256map_iter_t *itr = NULL;
+ const uint8_t *digest;
void *valp;
time_t *access_time;
@@ -149,19 +149,19 @@ replaycache_scrub_if_needed_internal(time_t present, replaycache_t *r)
if (r->horizon == 0) return;
/* okay, scrub time */
- itr = digestmap_iter_init(r->digests_seen);
- while (!digestmap_iter_done(itr)) {
- digestmap_iter_get(itr, &digest, &valp);
+ itr = digest256map_iter_init(r->digests_seen);
+ while (!digest256map_iter_done(itr)) {
+ digest256map_iter_get(itr, &digest, &valp);
access_time = (time_t *)valp;
/* aged out yet? */
if (*access_time < present - r->horizon) {
/* Advance the iterator and remove this one */
- itr = digestmap_iter_next_rmv(r->digests_seen, itr);
+ itr = digest256map_iter_next_rmv(r->digests_seen, itr);
/* Free the value removed */
tor_free(access_time);
} else {
/* Just advance the iterator */
- itr = digestmap_iter_next(r->digests_seen, itr);
+ itr = digest256map_iter_next(r->digests_seen, itr);
}
}
diff --git a/src/or/replaycache.h b/src/or/replaycache.h
index 9b9daf3831..64a6caf5f5 100644
--- a/src/or/replaycache.h
+++ b/src/or/replaycache.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2015, The Tor Project, Inc. */
+/* Copyright (c) 2012-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -26,7 +26,7 @@ struct replaycache_s {
/*
* Digest map: keys are digests, values are times the digest was last seen
*/
- digestmap_t *digests_seen;
+ digest256map_t *digests_seen;
};
#endif /* REPLAYCACHE_PRIVATE */
diff --git a/src/or/router.c b/src/or/router.c
index 03973ae90a..68bcf1326e 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#define ROUTER_PRIVATE
@@ -269,8 +269,8 @@ client_identity_key_is_set(void)
/** Return the key certificate for this v3 (voting) authority, or NULL
* if we have no such certificate. */
-authority_cert_t *
-get_my_v3_authority_cert(void)
+MOCK_IMPL(authority_cert_t *,
+get_my_v3_authority_cert, (void))
{
return authority_key_certificate;
}
@@ -1026,6 +1026,7 @@ init_keys(void)
ds = trusted_dir_server_new(options->Nickname, NULL,
router_get_advertised_dir_port(options, 0),
router_get_advertised_or_port(options),
+ NULL,
digest,
v3_digest,
type, 0.0);
@@ -1098,43 +1099,47 @@ check_whether_dirport_reachable(void)
can_reach_dir_port;
}
-/** Look at a variety of factors, and return 0 if we don't want to
- * advertise the fact that we have a DirPort open. Else return the
- * DirPort we want to advertise.
- *
- * Log a helpful message if we change our mind about whether to publish
- * a DirPort.
+/** The lower threshold of remaining bandwidth required to advertise (or
+ * automatically provide) directory services */
+/* XXX Should this be increased? */
+#define MIN_BW_TO_ADVERTISE_DIRSERVER 51200
+
+/** Return true iff we have enough configured bandwidth to cache directory
+ * information. */
+static int
+router_has_bandwidth_to_be_dirserver(const or_options_t *options)
+{
+ if (options->BandwidthRate < MIN_BW_TO_ADVERTISE_DIRSERVER) {
+ return 0;
+ }
+ if (options->RelayBandwidthRate > 0 &&
+ options->RelayBandwidthRate < MIN_BW_TO_ADVERTISE_DIRSERVER) {
+ return 0;
+ }
+ return 1;
+}
+
+/** Helper: Return 1 if we have sufficient resources for serving directory
+ * requests, return 0 otherwise.
+ * dir_port is either 0 or the configured DirPort number.
+ * If AccountingMax is set less than our advertised bandwidth, then don't
+ * serve requests. Likewise, if our advertised bandwidth is less than
+ * MIN_BW_TO_ADVERTISE_DIRSERVER, don't bother trying to serve requests.
*/
static int
-decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port)
+router_should_be_directory_server(const or_options_t *options, int dir_port)
{
static int advertising=1; /* start out assuming we will advertise */
int new_choice=1;
const char *reason = NULL;
- /* Section one: reasons to publish or not publish that aren't
- * worth mentioning to the user, either because they're obvious
- * or because they're normal behavior. */
-
- if (!dir_port) /* short circuit the rest of the function */
- return 0;
- if (authdir_mode(options)) /* always publish */
- return dir_port;
- if (net_is_disabled())
- return 0;
- if (!check_whether_dirport_reachable())
- return 0;
- if (!router_get_advertised_dir_port(options, dir_port))
- return 0;
-
- /* Section two: reasons to publish or not publish that the user
- * might find surprising. These are generally config options that
- * make us choose not to publish. */
-
- if (accounting_is_enabled(options)) {
+ if (accounting_is_enabled(options) &&
+ get_options()->AccountingRule != ACCT_IN) {
/* Don't spend bytes for directory traffic if we could end up hibernating,
* but allow DirPort otherwise. Some people set AccountingMax because
- * they're confused or to get statistics. */
+ * they're confused or to get statistics. Directory traffic has a much
+ * larger effect on output than input so there is no reason to turn it
+ * off if using AccountingRule in. */
int interval_length = accounting_get_interval_length();
uint32_t effective_bw = get_effective_bwrate(options);
uint64_t acc_bytes;
@@ -1157,10 +1162,7 @@ decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port)
new_choice = 0;
reason = "AccountingMax enabled";
}
-#define MIN_BW_TO_ADVERTISE_DIRPORT 51200
- } else if (options->BandwidthRate < MIN_BW_TO_ADVERTISE_DIRPORT ||
- (options->RelayBandwidthRate > 0 &&
- options->RelayBandwidthRate < MIN_BW_TO_ADVERTISE_DIRPORT)) {
+ } else if (! router_has_bandwidth_to_be_dirserver(options)) {
/* if we're advertising a small amount */
new_choice = 0;
reason = "BandwidthRate under 50KB";
@@ -1168,15 +1170,63 @@ decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port)
if (advertising != new_choice) {
if (new_choice == 1) {
- log_notice(LD_DIR, "Advertising DirPort as %d", dir_port);
+ if (dir_port > 0)
+ log_notice(LD_DIR, "Advertising DirPort as %d", dir_port);
+ else
+ log_notice(LD_DIR, "Advertising directory service support");
} else {
tor_assert(reason);
- log_notice(LD_DIR, "Not advertising DirPort (Reason: %s)", reason);
+ log_notice(LD_DIR, "Not advertising Dir%s (Reason: %s)",
+ dir_port ? "Port" : "ectory Service support", reason);
}
advertising = new_choice;
}
- return advertising ? dir_port : 0;
+ return advertising;
+}
+
+/** Return 1 if we are configured to accept either relay or directory requests
+ * from clients and we aren't at risk of exceeding our bandwidth limits, thus
+ * we should be a directory server. If not, return 0.
+ */
+int
+dir_server_mode(const or_options_t *options)
+{
+ if (!options->DirCache)
+ return 0;
+ return options->DirPort_set ||
+ (server_mode(options) && router_has_bandwidth_to_be_dirserver(options));
+}
+
+/** Look at a variety of factors, and return 0 if we don't want to
+ * advertise the fact that we have a DirPort open, else return the
+ * DirPort we want to advertise.
+ *
+ * Log a helpful message if we change our mind about whether to publish
+ * a DirPort.
+ */
+static int
+decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port)
+{
+ /* Part one: reasons to publish or not publish that aren't
+ * worth mentioning to the user, either because they're obvious
+ * or because they're normal behavior. */
+
+ if (!dir_port) /* short circuit the rest of the function */
+ return 0;
+ if (authdir_mode(options)) /* always publish */
+ return dir_port;
+ if (net_is_disabled())
+ return 0;
+ if (!check_whether_dirport_reachable())
+ return 0;
+ if (!router_get_advertised_dir_port(options, dir_port))
+ return 0;
+
+ /* Part two: reasons to publish or not publish that the user
+ * might find surprising. router_should_be_directory_server()
+ * considers config options that make us choose not to publish. */
+ return router_should_be_directory_server(options, dir_port) ? dir_port : 0;
}
/** Allocate and return a new extend_info_t that can be used to build
@@ -1243,14 +1293,15 @@ consider_testing_reachability(int test_or, int test_dir)
extend_info_free(ei);
}
+ /* XXX IPv6 self testing */
tor_addr_from_ipv4h(&addr, me->addr);
if (test_dir && !check_whether_dirport_reachable() &&
!connection_get_by_type_addr_port_purpose(
CONN_TYPE_DIR, &addr, me->dir_port,
DIR_PURPOSE_FETCH_SERVERDESC)) {
/* ask myself, via tor, for my server descriptor. */
- directory_initiate_command(&addr,
- me->or_port, me->dir_port,
+ directory_initiate_command(&addr, me->or_port,
+ &addr, me->dir_port,
me->cache_info.identity_digest,
DIR_PURPOSE_FETCH_SERVERDESC,
ROUTER_PURPOSE_GENERAL,
@@ -1267,7 +1318,8 @@ router_orport_found_reachable(void)
char *address = tor_dup_ip(me->addr);
log_notice(LD_OR,"Self-testing indicates your ORPort is reachable from "
"the outside. Excellent.%s",
- get_options()->PublishServerDescriptor_ != NO_DIRINFO ?
+ get_options()->PublishServerDescriptor_ != NO_DIRINFO
+ && check_whether_dirport_reachable() ?
" Publishing server descriptor." : "");
can_reach_or_port = 1;
mark_my_descriptor_dirty("ORPort found reachable");
@@ -1291,7 +1343,10 @@ router_dirport_found_reachable(void)
if (!can_reach_dir_port && me) {
char *address = tor_dup_ip(me->addr);
log_notice(LD_DIRSERV,"Self-testing indicates your DirPort is reachable "
- "from the outside. Excellent.");
+ "from the outside. Excellent.%s",
+ get_options()->PublishServerDescriptor_ != NO_DIRINFO
+ && check_whether_orport_reachable() ?
+ " Publishing server descriptor." : "");
can_reach_dir_port = 1;
if (decide_to_advertise_dirport(get_options(), me->dir_port)) {
mark_my_descriptor_dirty("DirPort found reachable");
@@ -1456,8 +1511,8 @@ static int server_is_advertised=0;
/** Return true iff we have published our descriptor lately.
*/
-int
-advertised_server_mode(void)
+MOCK_IMPL(int,
+advertised_server_mode,(void))
{
return server_is_advertised;
}
@@ -1494,7 +1549,8 @@ proxy_mode(const or_options_t *options)
* and
* - We have ORPort set
* and
- * - We believe we are reachable from the outside; or
+ * - We believe both our ORPort and DirPort (if present) are reachable from
+ * the outside; or
* - We are an authoritative directory server.
*/
static int
@@ -1513,7 +1569,7 @@ decide_if_publishable_server(void)
if (!router_get_advertised_or_port(options))
return 0;
- return check_whether_orport_reachable();
+ return check_whether_orport_reachable() && check_whether_dirport_reachable();
}
/** Initiate server descriptor upload as reasonable (if server is publishable,
@@ -1684,7 +1740,8 @@ router_upload_dir_desc_to_dirservers(int force)
int
router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port)
{
- if (!router_get_my_routerinfo()) /* make sure desc_routerinfo exists */
+ const routerinfo_t *me = router_get_my_routerinfo();
+ if (!me) /* make sure routerinfo exists */
return -1;
/* make sure it's resolved to something. this way we can't get a
@@ -1692,20 +1749,21 @@ router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port)
if (tor_addr_is_null(addr))
return -1;
- /* look at desc_routerinfo->exit_policy for both the v4 and the v6
- * policies. The exit_policy field in desc_routerinfo is a bit unusual,
- * in that it contains IPv6 and IPv6 entries. We don't want to look
- * at desc_routerinfio->ipv6_exit_policy, since that's a port summary. */
+ /* look at router_get_my_routerinfo()->exit_policy for both the v4 and the
+ * v6 policies. The exit_policy field in router_get_my_routerinfo() is a
+ * bit unusual, in that it contains IPv6 and IPv6 entries. We don't want to
+ * look at router_get_my_routerinfo()->ipv6_exit_policy, since that's a port
+ * summary. */
if ((tor_addr_family(addr) == AF_INET ||
tor_addr_family(addr) == AF_INET6)) {
return compare_tor_addr_to_addr_policy(addr, port,
- desc_routerinfo->exit_policy) != ADDR_POLICY_ACCEPTED;
+ me->exit_policy) != ADDR_POLICY_ACCEPTED;
#if 0
} else if (tor_addr_family(addr) == AF_INET6) {
return get_options()->IPv6Exit &&
desc_routerinfo->ipv6_exit_policy &&
compare_tor_addr_to_short_policy(addr, port,
- desc_routerinfo->ipv6_exit_policy) != ADDR_POLICY_ACCEPTED;
+ me->ipv6_exit_policy) != ADDR_POLICY_ACCEPTED;
#endif
} else {
return -1;
@@ -1714,13 +1772,13 @@ router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port)
/** Return true iff my exit policy is reject *:*. Return -1 if we don't
* have a descriptor */
-int
-router_my_exit_policy_is_reject_star(void)
+MOCK_IMPL(int,
+router_my_exit_policy_is_reject_star,(void))
{
- if (!router_get_my_routerinfo()) /* make sure desc_routerinfo exists */
+ if (!router_get_my_routerinfo()) /* make sure routerinfo exists */
return -1;
- return desc_routerinfo->policy_is_reject_star;
+ return router_get_my_routerinfo()->policy_is_reject_star;
}
/** Return true iff I'm a server and <b>digest</b> is equal to
@@ -1779,12 +1837,13 @@ const char *
router_get_my_descriptor(void)
{
const char *body;
- if (!router_get_my_routerinfo())
+ const routerinfo_t *me = router_get_my_routerinfo();
+ if (! me)
return NULL;
+ tor_assert(me->cache_info.saved_location == SAVED_NOWHERE);
+ body = signed_descriptor_get_body(&me->cache_info);
/* Make sure this is nul-terminated. */
- tor_assert(desc_routerinfo->cache_info.saved_location == SAVED_NOWHERE);
- body = signed_descriptor_get_body(&desc_routerinfo->cache_info);
- tor_assert(!body[desc_routerinfo->cache_info.signed_descriptor_len]);
+ tor_assert(!body[me->cache_info.signed_descriptor_len]);
log_debug(LD_GENERAL,"my desc is '%s'", body);
return body;
}
@@ -1819,8 +1878,8 @@ static int router_guess_address_from_dir_headers(uint32_t *guess);
* it's configured in torrc, or because we've learned it from
* dirserver headers. Place the answer in *<b>addr</b> and return
* 0 on success, else return -1 if we have no guess. */
-int
-router_pick_published_address(const or_options_t *options, uint32_t *addr)
+MOCK_IMPL(int,
+router_pick_published_address,(const or_options_t *options, uint32_t *addr))
{
*addr = get_last_resolved_addr();
if (!*addr &&
@@ -1865,6 +1924,8 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
ri->addr = addr;
ri->or_port = router_get_advertised_or_port(options);
ri->dir_port = router_get_advertised_dir_port(options, 0);
+ ri->supports_tunnelled_dir_requests = dir_server_mode(options) &&
+ router_should_be_directory_server(options, ri->dir_port);
ri->cache_info.published_on = time(NULL);
ri->onion_pkey = crypto_pk_dup_key(get_onion_key()); /* must invoke from
* main thread */
@@ -1880,7 +1941,11 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
! p->server_cfg.no_advertise &&
! p->server_cfg.bind_ipv4_only &&
tor_addr_family(&p->addr) == AF_INET6) {
- if (! tor_addr_is_internal(&p->addr, 0)) {
+ /* Like IPv4, if the relay is configured using the default
+ * authorities, disallow internal IPs. Otherwise, allow them. */
+ const int default_auth = (!options->DirAuthorities &&
+ !options->AlternateDirAuthority);
+ if (! tor_addr_is_internal(&p->addr, 0) || ! default_auth) {
ipv6_orport = p;
break;
} else {
@@ -1888,7 +1953,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
log_warn(LD_CONFIG,
"Unable to use configured IPv6 address \"%s\" in a "
"descriptor. Skipping it. "
- "Try specifying a globally reachable address explicitly. ",
+ "Try specifying a globally reachable address explicitly.",
tor_addr_to_str(addrbuf, &p->addr, sizeof(addrbuf), 1));
}
}
@@ -1922,7 +1987,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
/* DNS is screwed up; don't claim to be an exit. */
policies_exit_policy_append_reject_star(&ri->exit_policy);
} else {
- policies_parse_exit_policy_from_options(options,ri->addr,
+ policies_parse_exit_policy_from_options(options,ri->addr,&ri->ipv6_addr,
&ri->exit_policy);
}
ri->policy_is_reject_star =
@@ -2185,10 +2250,10 @@ check_descriptor_bandwidth_changed(time_t now)
{
static time_t last_changed = 0;
uint64_t prev, cur;
- if (!desc_routerinfo)
+ if (!router_get_my_routerinfo())
return;
- prev = desc_routerinfo->bandwidthcapacity;
+ prev = router_get_my_routerinfo()->bandwidthcapacity;
cur = we_are_hibernating() ? 0 : rep_hist_bandwidth_assess();
if ((prev != cur && (!prev || !cur)) ||
cur > prev*2 ||
@@ -2242,11 +2307,11 @@ check_descriptor_ipaddress_changed(time_t now)
(void) now;
- if (!desc_routerinfo)
+ if (router_get_my_routerinfo() == NULL)
return;
/* XXXX ipv6 */
- prev = desc_routerinfo->addr;
+ prev = router_get_my_routerinfo()->addr;
if (resolve_my_address(LOG_INFO, options, &cur, &method, &hostname) < 0) {
log_info(LD_CONFIG,"options->Address didn't resolve into an IP.");
return;
@@ -2318,7 +2383,7 @@ router_new_address_suggestion(const char *suggestion,
if (tor_addr_eq(&d_conn->base_.addr, &addr)) {
/* Don't believe anybody who says our IP is their IP. */
log_debug(LD_DIR, "A directory server told us our IP address is %s, "
- "but he's just reporting his own IP address. Ignoring.",
+ "but they are just reporting their own IP address. Ignoring.",
suggestion);
return;
}
@@ -2641,6 +2706,10 @@ router_dump_router_to_string(routerinfo_t *router,
tor_free(p6);
}
+ if (router->supports_tunnelled_dir_requests) {
+ smartlist_add(chunks, tor_strdup("tunnelled-dir-server\n"));
+ }
+
/* Sign the descriptor with Ed25519 */
if (emit_ed_sigs) {
smartlist_add(chunks, tor_strdup("router-sig-ed25519 "));
@@ -2728,44 +2797,13 @@ router_dump_exit_policy_to_string(const routerinfo_t *router,
int include_ipv4,
int include_ipv6)
{
- smartlist_t *exit_policy_strings;
- char *policy_string = NULL;
-
if ((!router->exit_policy) || (router->policy_is_reject_star)) {
return tor_strdup("reject *:*");
}
- exit_policy_strings = smartlist_new();
-
- SMARTLIST_FOREACH_BEGIN(router->exit_policy, addr_policy_t *, tmpe) {
- char *pbuf;
- int bytes_written_to_pbuf;
- if ((tor_addr_family(&tmpe->addr) == AF_INET6) && (!include_ipv6)) {
- continue; /* Don't include IPv6 parts of address policy */
- }
- if ((tor_addr_family(&tmpe->addr) == AF_INET) && (!include_ipv4)) {
- continue; /* Don't include IPv4 parts of address policy */
- }
-
- pbuf = tor_malloc(POLICY_BUF_LEN);
- bytes_written_to_pbuf = policy_write_item(pbuf,POLICY_BUF_LEN, tmpe, 1);
-
- if (bytes_written_to_pbuf < 0) {
- log_warn(LD_BUG, "router_dump_exit_policy_to_string ran out of room!");
- tor_free(pbuf);
- goto done;
- }
-
- smartlist_add(exit_policy_strings,pbuf);
- } SMARTLIST_FOREACH_END(tmpe);
-
- policy_string = smartlist_join_strings(exit_policy_strings, "\n", 0, NULL);
-
- done:
- SMARTLIST_FOREACH(exit_policy_strings, char *, str, tor_free(str));
- smartlist_free(exit_policy_strings);
-
- return policy_string;
+ return policy_dump_to_string(router->exit_policy,
+ include_ipv4,
+ include_ipv6);
}
/** Copy the primary (IPv4) OR port (IP address and TCP port) for
@@ -3380,28 +3418,16 @@ router_free_all(void)
/** Return a smartlist of tor_addr_port_t's with all the OR ports of
<b>ri</b>. Note that freeing of the items in the list as well as
- the smartlist itself is the callers responsibility.
-
- XXX duplicating code from node_get_all_orports(). */
+ the smartlist itself is the callers responsibility. */
smartlist_t *
router_get_all_orports(const routerinfo_t *ri)
{
- smartlist_t *sl = smartlist_new();
tor_assert(ri);
-
- if (ri->addr != 0) {
- tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t));
- tor_addr_from_ipv4h(&ap->addr, ri->addr);
- ap->port = ri->or_port;
- smartlist_add(sl, ap);
- }
- if (!tor_addr_is_null(&ri->ipv6_addr)) {
- tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t));
- tor_addr_copy(&ap->addr, &ri->ipv6_addr);
- ap->port = ri->or_port;
- smartlist_add(sl, ap);
- }
-
- return sl;
+ node_t fake_node;
+ memset(&fake_node, 0, sizeof(fake_node));
+ /* we don't modify ri, fake_node is passed as a const node_t *
+ */
+ fake_node.ri = (routerinfo_t *)ri;
+ return node_get_all_orports(&fake_node);
}
diff --git a/src/or/router.h b/src/or/router.h
index d8fcf0a9ad..5165462a13 100644
--- a/src/or/router.h
+++ b/src/or/router.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -22,7 +22,7 @@ int server_identity_key_is_set(void);
void set_client_identity_key(crypto_pk_t *k);
crypto_pk_t *get_tlsclient_identity_key(void);
int client_identity_key_is_set(void);
-authority_cert_t *get_my_v3_authority_cert(void);
+MOCK_DECL(authority_cert_t *, get_my_v3_authority_cert, (void));
crypto_pk_t *get_my_v3_authority_signing_key(void);
authority_cert_t *get_my_v3_legacy_cert(void);
crypto_pk_t *get_my_v3_legacy_signing_key(void);
@@ -41,6 +41,7 @@ int init_keys_client(void);
int check_whether_orport_reachable(void);
int check_whether_dirport_reachable(void);
+int dir_server_mode(const or_options_t *options);
void consider_testing_reachability(int test_or, int test_dir);
void router_orport_found_reachable(void);
void router_dirport_found_reachable(void);
@@ -67,7 +68,7 @@ uint16_t router_get_advertised_dir_port(const or_options_t *options,
MOCK_DECL(int, server_mode, (const or_options_t *options));
MOCK_DECL(int, public_server_mode, (const or_options_t *options));
-int advertised_server_mode(void);
+MOCK_DECL(int, advertised_server_mode, (void));
int proxy_mode(const or_options_t *options);
void consider_publishable_server(int force);
int should_refuse_unknown_exits(const or_options_t *options);
@@ -80,7 +81,7 @@ void check_descriptor_ipaddress_changed(time_t now);
void router_new_address_suggestion(const char *suggestion,
const dir_connection_t *d_conn);
int router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port);
-int router_my_exit_policy_is_reject_star(void);
+MOCK_DECL(int, router_my_exit_policy_is_reject_star,(void));
MOCK_DECL(const routerinfo_t *, router_get_my_routerinfo, (void));
extrainfo_t *router_get_my_extrainfo(void);
const char *router_get_my_descriptor(void);
@@ -89,7 +90,8 @@ int router_digest_is_me(const char *digest);
const uint8_t *router_get_my_id_digest(void);
int router_extrainfo_digest_is_me(const char *digest);
int router_is_me(const routerinfo_t *router);
-int router_pick_published_address(const or_options_t *options, uint32_t *addr);
+MOCK_DECL(int,router_pick_published_address,(const or_options_t *options,
+ uint32_t *addr));
int router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e);
int router_rebuild_descriptor(int force);
char *router_dump_router_to_string(routerinfo_t *router,
diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c
index 80b26e6bf8..fba3491f2b 100644
--- a/src/or/routerkeys.c
+++ b/src/or/routerkeys.c
@@ -1,6 +1,14 @@
-/* Copyright (c) 2014, The Tor Project, Inc. */
+/* Copyright (c) 2014-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+/**
+ * \file routerkeys.c
+ *
+ * \brief Functions and structures to handle generating and maintaining the
+ * set of keypairs necessary to be an OR. (Some of the code in router.c
+ * belongs here.)
+ */
+
#include "or.h"
#include "config.h"
#include "router.h"
@@ -200,8 +208,17 @@ write_secret_key(const ed25519_secret_key_t *key, int encrypted,
{
if (encrypted) {
int r = write_encrypted_secret_key(key, encrypted_fname);
- if (r != 0)
- return r; /* Either succeeded or failed unrecoverably */
+ if (r == 1) {
+ /* Success! */
+
+ /* Try to unlink the unencrypted key, if any existed before */
+ if (strcmp(fname, encrypted_fname))
+ unlink(fname);
+ return r;
+ } else if (r != 0) {
+ /* Unrecoverable failure! */
+ return r;
+ }
fprintf(stderr, "Not encrypting the secret key.\n");
}
@@ -237,8 +254,12 @@ write_secret_key(const ed25519_secret_key_t *key, int encrypted,
* If INIT_ED_KEY_MISSING_SECRET_OK is set in <b>flags</b>, and we find a
* public key file but no secret key file, return successfully anyway.
*
- * If INIT_ED_KEY_OMIT_SECRET is set in <b>flags</b>, do not even try to
- * load or return a secret key (but create and save one if needed).
+ * If INIT_ED_KEY_OMIT_SECRET is set in <b>flags</b>, do not try to load a
+ * secret key unless no public key is found. Do not return a secret key. (but
+ * create and save one if needed).
+ *
+ * If INIT_ED_KEY_NO_LOAD_SECRET is set in <b>flags</b>, don't try to load
+ * a secret key, no matter what.
*
* If INIT_ED_KEY_TRY_ENCRYPTED is set, we look for an encrypted secret key
* and consider encrypting any new secret key.
@@ -249,6 +270,9 @@ write_secret_key(const ed25519_secret_key_t *key, int encrypted,
*
* If INIT_ED_KEY_SUGGEST_KEYGEN is set, have log messages about failures
* refer to the --keygen option.
+ *
+ * If INIT_ED_KEY_EXPLICIT_FNAME is set, use the provided file name for the
+ * secret key file, encrypted or not.
*/
ed25519_keypair_t *
ed_key_init_from_file(const char *fname, uint32_t flags,
@@ -269,7 +293,9 @@ ed_key_init_from_file(const char *fname, uint32_t flags,
const int encrypt_key = !! (flags & INIT_ED_KEY_TRY_ENCRYPTED);
const int norepair = !! (flags & INIT_ED_KEY_NO_REPAIR);
const int split = !! (flags & INIT_ED_KEY_SPLIT);
- const int omit_secret = !! (flags & INIT_ED_KEY_OMIT_SECRET);
+ const int omit_secret = !! (flags & INIT_ED_KEY_OMIT_SECRET);
+ const int offline_secret = !! (flags & INIT_ED_KEY_OFFLINE_SECRET);
+ const int explicit_fname = !! (flags & INIT_ED_KEY_EXPLICIT_FNAME);
/* we don't support setting both of these flags at once. */
tor_assert((flags & (INIT_ED_KEY_NO_REPAIR|INIT_ED_KEY_NEEDCERT)) !=
@@ -282,19 +308,28 @@ ed_key_init_from_file(const char *fname, uint32_t flags,
char *got_tag = NULL;
ed25519_keypair_t *keypair = tor_malloc_zero(sizeof(ed25519_keypair_t));
- tor_asprintf(&secret_fname, "%s_secret_key", fname);
- tor_asprintf(&encrypted_secret_fname, "%s_secret_key_encrypted", fname);
+ if (explicit_fname) {
+ secret_fname = tor_strdup(fname);
+ encrypted_secret_fname = tor_strdup(fname);
+ } else {
+ tor_asprintf(&secret_fname, "%s_secret_key", fname);
+ tor_asprintf(&encrypted_secret_fname, "%s_secret_key_encrypted", fname);
+ }
tor_asprintf(&public_fname, "%s_public_key", fname);
tor_asprintf(&cert_fname, "%s_cert", fname);
/* Try to read the secret key. */
int have_secret = 0;
- if (try_to_load && (!omit_secret || file_status(public_fname)==FN_NOENT )) {
+ int load_secret = try_to_load &&
+ !offline_secret &&
+ (!omit_secret || file_status(public_fname)==FN_NOENT);
+ if (load_secret) {
int rv = ed25519_seckey_read_from_file(&keypair->seckey,
&got_tag, secret_fname);
if (rv == 0) {
have_secret = 1;
loaded_secret_fname = secret_fname;
+ tor_assert(got_tag);
} else {
if (errno != ENOENT && norepair) {
tor_log(severity, LD_OR, "Unable to read %s: %s", secret_fname,
@@ -312,6 +347,7 @@ ed_key_init_from_file(const char *fname, uint32_t flags,
if (r > 0) {
have_secret = 1;
have_encrypted_secret_file = 1;
+ tor_free(got_tag); /* convince coverity we aren't leaking */
got_tag = tor_strdup(tag);
loaded_secret_fname = encrypted_secret_fname;
} else if (errno != ENOENT && norepair) {
@@ -399,6 +435,10 @@ ed_key_init_from_file(const char *fname, uint32_t flags,
"but it was encrypted. Try 'tor --keygen' instead, so you "
"can enter the passphrase.",
secret_fname);
+ } else if (offline_secret) {
+ tor_log(severity, LD_OR, "We wanted to load a secret key from %s, "
+ "but you're keeping it offline. (OfflineMasterKey is set.)",
+ secret_fname);
} else {
tor_log(severity, LD_OR, "We needed to load a secret key from %s, "
"but couldn't find it. %s", secret_fname,
@@ -431,7 +471,7 @@ ed_key_init_from_file(const char *fname, uint32_t flags,
goto err;
}
- /* if it's absent, make a new keypair and save it. */
+ /* if it's absent, make a new keypair... */
if (!have_secret && !found_public) {
tor_free(keypair);
keypair = ed_key_new(signing_key, flags, now, lifetime,
@@ -440,8 +480,12 @@ ed_key_init_from_file(const char *fname, uint32_t flags,
tor_log(severity, LD_OR, "Couldn't create keypair");
goto err;
}
-
created_pk = created_sk = created_cert = 1;
+ }
+
+ /* Write it to disk if we're supposed to do with a new passphrase, or if
+ * we just created it. */
+ if (created_sk || (have_secret && get_options()->change_key_passphrase)) {
if (write_secret_key(&keypair->seckey,
encrypt_key,
secret_fname, tag, encrypted_secret_fname) < 0
@@ -482,10 +526,12 @@ ed_key_init_from_file(const char *fname, uint32_t flags,
tor_log(severity, LD_OR, "Cert was for wrong key");
bad_cert = 1;
} else if (signing_key &&
- tor_cert_checksig(cert, &signing_key->pubkey, now) < 0 &&
- (signing_key || cert->cert_expired)) {
+ tor_cert_checksig(cert, &signing_key->pubkey, now) < 0) {
tor_log(severity, LD_OR, "Can't check certificate");
bad_cert = 1;
+ } else if (cert->cert_expired) {
+ tor_log(severity, LD_OR, "Certificate is expired");
+ bad_cert = 1;
} else if (signing_key && cert->signing_key_included &&
! ed25519_pubkey_eq(&signing_key->pubkey, &cert->signing_key)) {
tor_log(severity, LD_OR, "Certificate signed by unexpectd key!");
@@ -635,11 +681,13 @@ load_ed_keys(const or_options_t *options, time_t now)
goto err; \
} while (0)
#define SET_KEY(key, newval) do { \
- ed25519_keypair_free(key); \
+ if ((key) != (newval)) \
+ ed25519_keypair_free(key); \
key = (newval); \
} while (0)
#define SET_CERT(cert, newval) do { \
- tor_cert_free(cert); \
+ if ((cert) != (newval)) \
+ tor_cert_free(cert); \
cert = (newval); \
} while (0)
#define EXPIRES_SOON(cert, interval) \
@@ -648,10 +696,7 @@ load_ed_keys(const or_options_t *options, time_t now)
/* XXXX support encrypted identity keys fully */
/* First try to get the signing key to see how it is. */
- if (master_signing_key) {
- check_signing_cert = signing_key_cert;
- use_signing = master_signing_key;
- } else {
+ {
char *fname =
options_get_datadir_fname2(options, "keys", "ed25519_signing");
sign = ed_key_init_from_file(
@@ -665,38 +710,61 @@ load_ed_keys(const or_options_t *options, time_t now)
use_signing = sign;
}
+ if (!use_signing && master_signing_key) {
+ check_signing_cert = signing_key_cert;
+ use_signing = master_signing_key;
+ }
+
+ const int offline_master =
+ options->OfflineMasterKey && options->command != CMD_KEYGEN;
const int need_new_signing_key =
NULL == use_signing ||
EXPIRES_SOON(check_signing_cert, 0) ||
- options->command == CMD_KEYGEN;
+ (options->command == CMD_KEYGEN && ! options->change_key_passphrase);
const int want_new_signing_key =
need_new_signing_key ||
EXPIRES_SOON(check_signing_cert, options->TestingSigningKeySlop);
+ /* We can only create a master key if we haven't been told that the
+ * master key will always be offline. Also, if we have a signing key,
+ * then we shouldn't make a new master ID key. */
+ const int can_make_master_id_key = !offline_master &&
+ NULL == use_signing;
+
if (need_new_signing_key) {
log_notice(LD_OR, "It looks like I need to generate and sign a new "
"medium-term signing key, because %s. To do that, I need to "
- "load (or create) the permanent master identity key.",
+ "load%s the permanent master identity key.",
(NULL == use_signing) ? "I don't have one" :
EXPIRES_SOON(check_signing_cert, 0) ? "the one I have is expired" :
- "you asked me to make one with --keygen");
- } else if (want_new_signing_key) {
+ "you asked me to make one with --keygen",
+ can_make_master_id_key ? " (or create)" : "");
+ } else if (want_new_signing_key && !offline_master) {
log_notice(LD_OR, "It looks like I should try to generate and sign a "
"new medium-term signing key, because the one I have is "
"going to expire soon. To do that, I'm going to have to try to "
"load the permanent master identity key.");
+ } else if (want_new_signing_key) {
+ log_notice(LD_OR, "It looks like I should try to generate and sign a "
+ "new medium-term signing key, because the one I have is "
+ "going to expire soon. But OfflineMasterKey is set, so I "
+ "won't try to load a permanent master identity key is set. "
+ "You will need to use 'tor --keygen' make a new signing key "
+ "and certificate.");
}
{
uint32_t flags =
(INIT_ED_KEY_SPLIT|
INIT_ED_KEY_EXTRA_STRONG|INIT_ED_KEY_NO_REPAIR);
- if (! use_signing)
+ if (can_make_master_id_key)
flags |= INIT_ED_KEY_CREATE;
if (! need_new_signing_key)
flags |= INIT_ED_KEY_MISSING_SECRET_OK;
- if (! want_new_signing_key)
+ if (! want_new_signing_key || offline_master)
flags |= INIT_ED_KEY_OMIT_SECRET;
+ if (offline_master)
+ flags |= INIT_ED_KEY_OFFLINE_SECRET;
if (options->command == CMD_KEYGEN)
flags |= INIT_ED_KEY_TRY_ENCRYPTED;
@@ -713,7 +781,12 @@ load_ed_keys(const or_options_t *options, time_t now)
goto err;
}
tor_free(fname);
- fname = options_get_datadir_fname2(options, "keys", "ed25519_master_id");
+ if (options->master_key_fname) {
+ fname = tor_strdup(options->master_key_fname);
+ flags |= INIT_ED_KEY_EXPLICIT_FNAME;
+ } else {
+ fname = options_get_datadir_fname2(options, "keys", "ed25519_master_id");
+ }
id = ed_key_init_from_file(
fname,
flags,
@@ -721,7 +794,10 @@ load_ed_keys(const or_options_t *options, time_t now)
tor_free(fname);
if (!id) {
if (need_new_signing_key) {
- FAIL("Missing identity key");
+ if (offline_master)
+ FAIL("Can't load master identity key; OfflineMasterKey is set.");
+ else
+ FAIL("Missing identity key");
} else {
log_warn(LD_OR, "Master public key was absent; inferring from "
"public key in signing certificate and saving to disk.");
@@ -772,6 +848,8 @@ load_ed_keys(const or_options_t *options, time_t now)
INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT);
char *fname =
options_get_datadir_fname2(options, "keys", "ed25519_signing");
+ ed25519_keypair_free(sign);
+ tor_cert_free(sign_cert);
sign = ed_key_init_from_file(fname,
flags, LOG_WARN,
sign_signing_key_with_id, now,
@@ -849,7 +927,7 @@ load_ed_keys(const or_options_t *options, time_t now)
return -1;
}
-/**DOCDOC*/
+/* DOCDOC */
int
generate_ed_link_cert(const or_options_t *options, time_t now)
{
@@ -861,7 +939,7 @@ generate_ed_link_cert(const or_options_t *options, time_t now)
return -1;
}
- const digests_t *digests = tor_x509_cert_get_cert_digests(link);
+ const common_digests_t *digests = tor_x509_cert_get_cert_digests(link);
if (link_cert_cert &&
! EXPIRES_SOON(link_cert_cert, options->TestingLinkKeySlop) &&
@@ -906,7 +984,7 @@ should_make_new_ed_keys(const or_options_t *options, const time_t now)
if (tor_tls_get_my_certs(1, &link, &id) < 0 || link == NULL)
return 1;
- const digests_t *digests = tor_x509_cert_get_cert_digests(link);
+ const common_digests_t *digests = tor_x509_cert_get_cert_digests(link);
if (!fast_memeq(digests->d[DIGEST_SHA256],
link_cert_cert->signed_key.pubkey,
diff --git a/src/or/routerkeys.h b/src/or/routerkeys.h
index b4e73aa33f..be9b19aea8 100644
--- a/src/or/routerkeys.h
+++ b/src/or/routerkeys.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014, The Tor Project, Inc. */
+/* Copyright (c) 2014-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_ROUTERKEYS_H
@@ -17,6 +17,8 @@
#define INIT_ED_KEY_TRY_ENCRYPTED (1u<<8)
#define INIT_ED_KEY_NO_REPAIR (1u<<9)
#define INIT_ED_KEY_SUGGEST_KEYGEN (1u<<10)
+#define INIT_ED_KEY_OFFLINE_SECRET (1u<<11)
+#define INIT_ED_KEY_EXPLICIT_FNAME (1u<<12)
struct tor_cert_st;
ed25519_keypair_t *ed_key_init_from_file(const char *fname, uint32_t flags,
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index 8bd80391bc..7f805f15b1 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -13,6 +13,7 @@
#define ROUTERLIST_PRIVATE
#include "or.h"
+#include "backtrace.h"
#include "crypto_ed25519.h"
#include "circuitstats.h"
#include "config.h"
@@ -67,8 +68,6 @@ typedef struct cert_list_t cert_list_t;
static int compute_weighted_bandwidths(const smartlist_t *sl,
bandwidth_weight_rule_t rule,
u64_dbl_t **bandwidths_out);
-static const routerstatus_t *router_pick_directory_server_impl(
- dirinfo_type_t auth, int flags, int *n_busy_out);
static const routerstatus_t *router_pick_trusteddirserver_impl(
const smartlist_t *sourcelist, dirinfo_type_t auth,
int flags, int *n_busy_out);
@@ -278,7 +277,7 @@ trusted_dirs_reload_certs(void)
/** Helper: return true iff we already have loaded the exact cert
* <b>cert</b>. */
-static INLINE int
+static inline int
already_have_cert(authority_cert_t *cert)
{
cert_list_t *cl = get_cert_list(cert->cache_info.identity_digest);
@@ -663,7 +662,7 @@ static const char *BAD_SIGNING_KEYS[] = {
NULL,
};
-/** DOCDOC */
+/* DOCDOC */
int
authority_cert_is_blacklisted(const authority_cert_t *cert)
{
@@ -897,8 +896,10 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
if (smartlist_len(fps) > 1) {
resource = smartlist_join_strings(fps, "", 0, NULL);
+ /* XXX - do we want certs from authorities or mirrors? - teor */
directory_get_from_dirserver(DIR_PURPOSE_FETCH_CERTIFICATE, 0,
- resource, PDS_RETRY_IF_NO_SERVERS);
+ resource, PDS_RETRY_IF_NO_SERVERS,
+ DL_WANT_ANY_DIRSERVER);
tor_free(resource);
}
/* else we didn't add any: they were all pending */
@@ -941,8 +942,10 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
if (smartlist_len(fp_pairs) > 1) {
resource = smartlist_join_strings(fp_pairs, "", 0, NULL);
+ /* XXX - do we want certs from authorities or mirrors? - teor */
directory_get_from_dirserver(DIR_PURPOSE_FETCH_CERTIFICATE, 0,
- resource, PDS_RETRY_IF_NO_SERVERS);
+ resource, PDS_RETRY_IF_NO_SERVERS,
+ DL_WANT_ANY_DIRSERVER);
tor_free(resource);
}
/* else they were all pending */
@@ -985,7 +988,7 @@ router_should_rebuild_store(desc_store_t *store)
/** Return the desc_store_t in <b>rl</b> that should be used to store
* <b>sd</b>. */
-static INLINE desc_store_t *
+static inline desc_store_t *
desc_get_store(routerlist_t *rl, const signed_descriptor_t *sd)
{
if (sd->is_extrainfo)
@@ -1295,8 +1298,8 @@ router_get_fallback_dir_servers(void)
/** Try to find a running dirserver that supports operations of <b>type</b>.
*
* If there are no running dirservers in our routerlist and the
- * <b>PDS_RETRY_IF_NO_SERVERS</b> flag is set, set all the authoritative ones
- * as running again, and pick one.
+ * <b>PDS_RETRY_IF_NO_SERVERS</b> flag is set, set all the fallback ones
+ * (including authorities) as running again, and pick one.
*
* If the <b>PDS_IGNORE_FASCISTFIREWALL</b> flag is set, then include
* dirservers that we can't reach.
@@ -1304,8 +1307,9 @@ router_get_fallback_dir_servers(void)
* If the <b>PDS_ALLOW_SELF</b> flag is not set, then don't include ourself
* (if we're a dirserver).
*
- * Don't pick an authority if any non-authority is viable; try to avoid using
- * servers that have returned 503 recently.
+ * Don't pick a fallback directory mirror if any non-fallback is viable;
+ * (the fallback directory mirrors include the authorities)
+ * try to avoid using servers that have returned 503 recently.
*/
const routerstatus_t *
router_pick_directory_server(dirinfo_type_t type, int flags)
@@ -1332,7 +1336,7 @@ router_pick_directory_server(dirinfo_type_t type, int flags)
log_info(LD_DIR,
"No reachable router entries for dirservers. "
"Trying them all again.");
- /* mark all authdirservers as up again */
+ /* mark all fallback directory mirrors as up again */
mark_all_dirservers_up(fallback_dir_servers);
/* try again */
choice = router_pick_directory_server_impl(type, flags, NULL);
@@ -1358,15 +1362,20 @@ router_get_trusteddirserver_by_digest(const char *digest)
}
/** Return the dir_server_t for the fallback dirserver whose identity
- * key hashes to <b>digest</b>, or NULL if no such authority is known.
+ * key hashes to <b>digest</b>, or NULL if no such fallback is in the list of
+ * fallback_dir_servers. (fallback_dir_servers is affected by the FallbackDir
+ * and UseDefaultFallbackDirs torrc options.)
*/
dir_server_t *
router_get_fallback_dirserver_by_digest(const char *digest)
{
- if (!trusted_dir_servers)
+ if (!fallback_dir_servers)
return NULL;
- SMARTLIST_FOREACH(trusted_dir_servers, dir_server_t *, ds,
+ if (!digest)
+ return NULL;
+
+ SMARTLIST_FOREACH(fallback_dir_servers, dir_server_t *, ds,
{
if (tor_memeq(ds->digest, digest, DIGEST_LEN))
return ds;
@@ -1375,6 +1384,17 @@ router_get_fallback_dirserver_by_digest(const char *digest)
return NULL;
}
+/** Return 1 if any fallback dirserver's identity key hashes to <b>digest</b>,
+ * or 0 if no such fallback is in the list of fallback_dir_servers.
+ * (fallback_dir_servers is affected by the FallbackDir and
+ * UseDefaultFallbackDirs torrc options.)
+ */
+int
+router_digest_is_fallback_dir(const char *digest)
+{
+ return (router_get_fallback_dirserver_by_digest(digest) != NULL);
+}
+
/** Return the dir_server_t for the directory authority whose
* v3 identity key hashes to <b>digest</b>, or NULL if no such authority
* is known.
@@ -1441,9 +1461,192 @@ router_pick_dirserver_generic(smartlist_t *sourcelist,
return router_pick_trusteddirserver_impl(sourcelist, type, flags, NULL);
}
+/* Check if we already have a directory fetch from ap, for serverdesc
+ * (including extrainfo) or microdesc documents.
+ * If so, return 1, if not, return 0.
+ * Also returns 0 if addr is NULL, tor_addr_is_null(addr), or dir_port is 0.
+ */
+STATIC int
+router_is_already_dir_fetching(const tor_addr_port_t *ap, int serverdesc,
+ int microdesc)
+{
+ if (!ap || tor_addr_is_null(&ap->addr) || !ap->port) {
+ return 0;
+ }
+
+ /* XX/teor - we're not checking tunnel connections here, see #17848
+ */
+ if (serverdesc && (
+ connection_get_by_type_addr_port_purpose(
+ CONN_TYPE_DIR, &ap->addr, ap->port, DIR_PURPOSE_FETCH_SERVERDESC)
+ || connection_get_by_type_addr_port_purpose(
+ CONN_TYPE_DIR, &ap->addr, ap->port, DIR_PURPOSE_FETCH_EXTRAINFO))) {
+ return 1;
+ }
+
+ if (microdesc && (
+ connection_get_by_type_addr_port_purpose(
+ CONN_TYPE_DIR, &ap->addr, ap->port, DIR_PURPOSE_FETCH_MICRODESC))) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Check if we already have a directory fetch from ds, for serverdesc
+ * (including extrainfo) or microdesc documents.
+ * If so, return 1, if not, return 0.
+ */
+static int
+router_is_already_dir_fetching_ds(const dir_server_t *ds,
+ int serverdesc,
+ int microdesc)
+{
+ tor_addr_port_t ipv4_dir_ap, ipv6_dir_ap;
+
+ /* Assume IPv6 DirPort is the same as IPv4 DirPort */
+ tor_addr_from_ipv4h(&ipv4_dir_ap.addr, ds->addr);
+ ipv4_dir_ap.port = ds->dir_port;
+ tor_addr_copy(&ipv6_dir_ap.addr, &ds->ipv6_addr);
+ ipv6_dir_ap.port = ds->dir_port;
+
+ return (router_is_already_dir_fetching(&ipv4_dir_ap, serverdesc, microdesc)
+ || router_is_already_dir_fetching(&ipv6_dir_ap, serverdesc, microdesc));
+}
+
+/* Check if we already have a directory fetch from rs, for serverdesc
+ * (including extrainfo) or microdesc documents.
+ * If so, return 1, if not, return 0.
+ */
+static int
+router_is_already_dir_fetching_rs(const routerstatus_t *rs,
+ int serverdesc,
+ int microdesc)
+{
+ tor_addr_port_t ipv4_dir_ap, ipv6_dir_ap;
+
+ /* Assume IPv6 DirPort is the same as IPv4 DirPort */
+ tor_addr_from_ipv4h(&ipv4_dir_ap.addr, rs->addr);
+ ipv4_dir_ap.port = rs->dir_port;
+ tor_addr_copy(&ipv6_dir_ap.addr, &rs->ipv6_addr);
+ ipv6_dir_ap.port = rs->dir_port;
+
+ return (router_is_already_dir_fetching(&ipv4_dir_ap, serverdesc, microdesc)
+ || router_is_already_dir_fetching(&ipv6_dir_ap, serverdesc, microdesc));
+}
+
+#ifndef LOG_FALSE_POSITIVES_DURING_BOOTSTRAP
+#define LOG_FALSE_POSITIVES_DURING_BOOTSTRAP 0
+#endif
+
+/* Log a message if rs is not found or not a preferred address */
+static void
+router_picked_poor_directory_log(const routerstatus_t *rs)
+{
+ const networkstatus_t *usable_consensus;
+ usable_consensus = networkstatus_get_reasonably_live_consensus(time(NULL),
+ usable_consensus_flavor());
+
+#if !LOG_FALSE_POSITIVES_DURING_BOOTSTRAP
+ /* Don't log early in the bootstrap process, it's normal to pick from a
+ * small pool of nodes. Of course, this won't help if we're trying to
+ * diagnose bootstrap issues. */
+ if (!smartlist_len(nodelist_get_list()) || !usable_consensus
+ || !router_have_minimum_dir_info()) {
+ return;
+ }
+#endif
+
+ /* We couldn't find a node, or the one we have doesn't fit our preferences.
+ * This might be a bug. */
+ if (!rs) {
+ static int logged_backtrace = 0;
+ log_info(LD_BUG, "Wanted to make an outgoing directory connection, but "
+ "all OR and Dir addresses for all relays were not reachable. "
+ "Check ReachableAddresses, ClientUseIPv4, and similar options.");
+ if (!logged_backtrace) {
+ log_backtrace(LOG_INFO, LD_BUG, "Node search initiated by");
+ logged_backtrace = 1;
+ }
+ } else if (!fascist_firewall_allows_rs(rs, FIREWALL_OR_CONNECTION, 1)
+ && !fascist_firewall_allows_rs(rs, FIREWALL_DIR_CONNECTION, 1)
+ ) {
+ log_info(LD_BUG, "Selected a directory %s with non-preferred OR and Dir "
+ "addresses for launching an outgoing connection: "
+ "IPv4 %s OR %d Dir %d IPv6 %s OR %d Dir %d",
+ routerstatus_describe(rs),
+ fmt_addr32(rs->addr), rs->or_port,
+ rs->dir_port, fmt_addr(&rs->ipv6_addr),
+ rs->ipv6_orport, rs->dir_port);
+ }
+}
+
+#undef LOG_FALSE_POSITIVES_DURING_BOOTSTRAP
+
/** How long do we avoid using a directory server after it's given us a 503? */
#define DIR_503_TIMEOUT (60*60)
+/* Common retry code for router_pick_directory_server_impl and
+ * router_pick_trusteddirserver_impl. Retry with the non-preferred IP version.
+ * Must be called before RETRY_WITHOUT_EXCLUDE().
+ *
+ * If we got no result, and we are applying IP preferences, and we are a
+ * client that could use an alternate IP version, try again with the
+ * opposite preferences. */
+#define RETRY_ALTERNATE_IP_VERSION(retry_label) \
+ STMT_BEGIN \
+ if (result == NULL && try_ip_pref && options->ClientUseIPv4 \
+ && fascist_firewall_use_ipv6(options) && !server_mode(options) \
+ && !n_busy) { \
+ n_excluded = 0; \
+ n_busy = 0; \
+ try_ip_pref = 0; \
+ goto retry_label; \
+ } \
+ STMT_END \
+
+/* Common retry code for router_pick_directory_server_impl and
+ * router_pick_trusteddirserver_impl. Retry without excluding nodes, but with
+ * the preferred IP version. Must be called after RETRY_ALTERNATE_IP_VERSION().
+ *
+ * If we got no result, and we are excluding nodes, and StrictNodes is
+ * not set, try again without excluding nodes. */
+#define RETRY_WITHOUT_EXCLUDE(retry_label) \
+ STMT_BEGIN \
+ if (result == NULL && try_excluding && !options->StrictNodes \
+ && n_excluded && !n_busy) { \
+ try_excluding = 0; \
+ n_excluded = 0; \
+ n_busy = 0; \
+ try_ip_pref = 1; \
+ goto retry_label; \
+ } \
+ STMT_END
+
+/* When iterating through the routerlist, can OR address/port preference
+ * and reachability checks be skipped?
+ */
+static int
+router_skip_or_reachability(const or_options_t *options, int try_ip_pref)
+{
+ /* Servers always have and prefer IPv4.
+ * And if clients are checking against the firewall for reachability only,
+ * but there's no firewall, don't bother checking */
+ return server_mode(options) || (!try_ip_pref && !firewall_is_fascist_or());
+}
+
+/* When iterating through the routerlist, can Dir address/port preference
+ * and reachability checks be skipped?
+ */
+static int
+router_skip_dir_reachability(const or_options_t *options, int try_ip_pref)
+{
+ /* Servers always have and prefer IPv4.
+ * And if clients are checking against the firewall for reachability only,
+ * but there's no firewall, don't bother checking */
+ return server_mode(options) || (!try_ip_pref && !firewall_is_fascist_dir());
+}
+
/** Pick a random running valid directory server/mirror from our
* routerlist. Arguments are as for router_pick_directory_server(), except:
*
@@ -1451,7 +1654,7 @@ router_pick_dirserver_generic(smartlist_t *sourcelist,
* directories that we excluded for no other reason than
* PDS_NO_EXISTING_SERVERDESC_FETCH or PDS_NO_EXISTING_MICRODESC_FETCH.
*/
-static const routerstatus_t *
+STATIC const routerstatus_t *
router_pick_directory_server_impl(dirinfo_type_t type, int flags,
int *n_busy_out)
{
@@ -1468,11 +1671,12 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags,
const int no_microdesc_fetching = (flags & PDS_NO_EXISTING_MICRODESC_FETCH);
const int for_guard = (flags & PDS_FOR_GUARD);
int try_excluding = 1, n_excluded = 0, n_busy = 0;
+ int try_ip_pref = 1;
if (!consensus)
return NULL;
- retry_without_exclude:
+ retry_search:
direct = smartlist_new();
tunnel = smartlist_new();
@@ -1481,17 +1685,20 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags,
overloaded_direct = smartlist_new();
overloaded_tunnel = smartlist_new();
+ const int skip_or_fw = router_skip_or_reachability(options, try_ip_pref);
+ const int skip_dir_fw = router_skip_dir_reachability(options, try_ip_pref);
+ const int must_have_or = directory_must_use_begindir(options);
+
/* Find all the running dirservers we know about. */
SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) {
int is_trusted, is_trusted_extrainfo;
int is_overloaded;
- tor_addr_t addr;
const routerstatus_t *status = node->rs;
const country_t country = node->country;
if (!status)
continue;
- if (!node->is_running || !status->dir_port || !node->is_valid)
+ if (!node->is_running || !node_is_dir(node) || !node->is_valid)
continue;
if (requireother && router_digest_is_me(node->identity))
continue;
@@ -1501,8 +1708,14 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags,
if ((type & EXTRAINFO_DIRINFO) &&
!router_supports_extrainfo(node->identity, is_trusted_extrainfo))
continue;
- if (for_guard && node->using_as_guard)
- continue; /* Don't make the same node a guard twice. */
+ /* Don't make the same node a guard twice */
+ if (for_guard && node->using_as_guard) {
+ continue;
+ }
+ /* Ensure that a directory guard is actually a guard node. */
+ if (for_guard && !node->is_possible_guard) {
+ continue;
+ }
if (try_excluding &&
routerset_contains_routerstatus(options->ExcludeNodes, status,
country)) {
@@ -1510,34 +1723,30 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags,
continue;
}
- /* XXXX IP6 proposal 118 */
- tor_addr_from_ipv4h(&addr, status->addr);
-
- if (no_serverdesc_fetching && (
- connection_get_by_type_addr_port_purpose(
- CONN_TYPE_DIR, &addr, status->dir_port, DIR_PURPOSE_FETCH_SERVERDESC)
- || connection_get_by_type_addr_port_purpose(
- CONN_TYPE_DIR, &addr, status->dir_port, DIR_PURPOSE_FETCH_EXTRAINFO)
- )) {
- ++n_busy;
- continue;
- }
-
- if (no_microdesc_fetching && connection_get_by_type_addr_port_purpose(
- CONN_TYPE_DIR, &addr, status->dir_port, DIR_PURPOSE_FETCH_MICRODESC)
- ) {
+ if (router_is_already_dir_fetching_rs(status,
+ no_serverdesc_fetching,
+ no_microdesc_fetching)) {
++n_busy;
continue;
}
is_overloaded = status->last_dir_503_at + DIR_503_TIMEOUT > now;
- if ((!fascistfirewall ||
- fascist_firewall_allows_address_or(&addr, status->or_port)))
+ /* Clients use IPv6 addresses if the server has one and the client
+ * prefers IPv6.
+ * Add the router if its preferred address and port are reachable.
+ * If we don't get any routers, we'll try again with the non-preferred
+ * address for each router (if any). (To ensure correct load-balancing
+ * we try routers that only have one address both times.)
+ */
+ if (!fascistfirewall || skip_or_fw ||
+ fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION,
+ try_ip_pref))
smartlist_add(is_trusted ? trusted_tunnel :
is_overloaded ? overloaded_tunnel : tunnel, (void*)node);
- else if (!fascistfirewall ||
- fascist_firewall_allows_address_dir(&addr, status->dir_port))
+ else if (!must_have_or && (skip_dir_fw ||
+ fascist_firewall_allows_node(node, FIREWALL_DIR_CONNECTION,
+ try_ip_pref)))
smartlist_add(is_trusted ? trusted_direct :
is_overloaded ? overloaded_direct : direct, (void*)node);
} SMARTLIST_FOREACH_END(node);
@@ -1568,19 +1777,15 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags,
smartlist_free(overloaded_direct);
smartlist_free(overloaded_tunnel);
- if (result == NULL && try_excluding && !options->StrictNodes && n_excluded
- && !n_busy) {
- /* If we got no result, and we are excluding nodes, and StrictNodes is
- * not set, try again without excluding nodes. */
- try_excluding = 0;
- n_excluded = 0;
- n_busy = 0;
- goto retry_without_exclude;
- }
+ RETRY_ALTERNATE_IP_VERSION(retry_search);
+
+ RETRY_WITHOUT_EXCLUDE(retry_search);
if (n_busy_out)
*n_busy_out = n_busy;
+ router_picked_poor_directory_log(result ? result->rs : NULL);
+
return result ? result->rs : NULL;
}
@@ -1631,30 +1836,36 @@ router_pick_trusteddirserver_impl(const smartlist_t *sourcelist,
smartlist_t *pick_from;
int n_busy = 0;
int try_excluding = 1, n_excluded = 0;
+ int try_ip_pref = 1;
if (!sourcelist)
return NULL;
- retry_without_exclude:
+ retry_search:
direct = smartlist_new();
tunnel = smartlist_new();
overloaded_direct = smartlist_new();
overloaded_tunnel = smartlist_new();
+ const int skip_or_fw = router_skip_or_reachability(options, try_ip_pref);
+ const int skip_dir_fw = router_skip_dir_reachability(options, try_ip_pref);
+ const int must_have_or = directory_must_use_begindir(options);
+
SMARTLIST_FOREACH_BEGIN(sourcelist, const dir_server_t *, d)
{
int is_overloaded =
d->fake_status.last_dir_503_at + DIR_503_TIMEOUT > now;
- tor_addr_t addr;
if (!d->is_running) continue;
if ((type & d->type) == 0)
continue;
+ int is_trusted_extrainfo = router_digest_is_trusted_dir_type(
+ d->digest, EXTRAINFO_DIRINFO);
if ((type & EXTRAINFO_DIRINFO) &&
- !router_supports_extrainfo(d->digest, 1))
+ !router_supports_extrainfo(d->digest, is_trusted_extrainfo))
continue;
if (requireother && me && router_digest_is_me(d->digest))
- continue;
+ continue;
if (try_excluding &&
routerset_contains_routerstatus(options->ExcludeNodes,
&d->fake_status, -1)) {
@@ -1662,34 +1873,26 @@ router_pick_trusteddirserver_impl(const smartlist_t *sourcelist,
continue;
}
- /* XXXX IP6 proposal 118 */
- tor_addr_from_ipv4h(&addr, d->addr);
-
- if (no_serverdesc_fetching) {
- if (connection_get_by_type_addr_port_purpose(
- CONN_TYPE_DIR, &addr, d->dir_port, DIR_PURPOSE_FETCH_SERVERDESC)
- || connection_get_by_type_addr_port_purpose(
- CONN_TYPE_DIR, &addr, d->dir_port, DIR_PURPOSE_FETCH_EXTRAINFO)) {
- //log_debug(LD_DIR, "We have an existing connection to fetch "
- // "descriptor from %s; delaying",d->description);
- ++n_busy;
- continue;
- }
- }
- if (no_microdesc_fetching) {
- if (connection_get_by_type_addr_port_purpose(
- CONN_TYPE_DIR, &addr, d->dir_port, DIR_PURPOSE_FETCH_MICRODESC)) {
- ++n_busy;
- continue;
- }
+ if (router_is_already_dir_fetching_ds(d, no_serverdesc_fetching,
+ no_microdesc_fetching)) {
+ ++n_busy;
+ continue;
}
- if (d->or_port &&
- (!fascistfirewall ||
- fascist_firewall_allows_address_or(&addr, d->or_port)))
+ /* Clients use IPv6 addresses if the server has one and the client
+ * prefers IPv6.
+ * Add the router if its preferred address and port are reachable.
+ * If we don't get any routers, we'll try again with the non-preferred
+ * address for each router (if any). (To ensure correct load-balancing
+ * we try routers that only have one address both times.)
+ */
+ if (!fascistfirewall || skip_or_fw ||
+ fascist_firewall_allows_dir_server(d, FIREWALL_OR_CONNECTION,
+ try_ip_pref))
smartlist_add(is_overloaded ? overloaded_tunnel : tunnel, (void*)d);
- else if (!fascistfirewall ||
- fascist_firewall_allows_address_dir(&addr, d->dir_port))
+ else if (!must_have_or && (skip_dir_fw ||
+ fascist_firewall_allows_dir_server(d, FIREWALL_DIR_CONNECTION,
+ try_ip_pref)))
smartlist_add(is_overloaded ? overloaded_direct : direct, (void*)d);
}
SMARTLIST_FOREACH_END(d);
@@ -1712,22 +1915,19 @@ router_pick_trusteddirserver_impl(const smartlist_t *sourcelist,
result = &selection->fake_status;
}
- if (n_busy_out)
- *n_busy_out = n_busy;
-
smartlist_free(direct);
smartlist_free(tunnel);
smartlist_free(overloaded_direct);
smartlist_free(overloaded_tunnel);
- if (result == NULL && try_excluding && !options->StrictNodes && n_excluded) {
- /* If we got no result, and we are excluding nodes, and StrictNodes is
- * not set, try again without excluding nodes. */
- try_excluding = 0;
- n_excluded = 0;
- goto retry_without_exclude;
- }
+ RETRY_ALTERNATE_IP_VERSION(retry_search);
+
+ RETRY_WITHOUT_EXCLUDE(retry_search);
+ router_picked_poor_directory_log(result);
+
+ if (n_busy_out)
+ *n_busy_out = n_busy;
return result;
}
@@ -1797,8 +1997,12 @@ routerlist_add_node_and_family(smartlist_t *sl, const routerinfo_t *router)
void
router_add_running_nodes_to_smartlist(smartlist_t *sl, int allow_invalid,
int need_uptime, int need_capacity,
- int need_guard, int need_desc)
-{ /* XXXX MOVE */
+ int need_guard, int need_desc,
+ int pref_addr)
+{
+ const int check_reach = !router_skip_or_reachability(get_options(),
+ pref_addr);
+ /* XXXX MOVE */
SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) {
if (!node->is_running ||
(!node->is_valid && !allow_invalid))
@@ -1809,6 +2013,11 @@ router_add_running_nodes_to_smartlist(smartlist_t *sl, int allow_invalid,
continue;
if (node_is_unreliable(node, need_uptime, need_capacity, need_guard))
continue;
+ /* Choose a node with an OR address that matches the firewall rules */
+ if (check_reach && !fascist_firewall_allows_node(node,
+ FIREWALL_OR_CONNECTION,
+ pref_addr))
+ continue;
smartlist_add(sl, (void *)node);
} SMARTLIST_FOREACH_END(node);
@@ -1891,7 +2100,7 @@ scale_array_elements_to_u64(u64_dbl_t *entries, int n_entries,
#if SIZEOF_VOID_P == 8
#define gt_i64_timei(a,b) ((a) > (b))
#else
-static INLINE int
+static inline int
gt_i64_timei(uint64_t a, uint64_t b)
{
int64_t diff = (int64_t) (b - a);
@@ -1969,7 +2178,7 @@ bridge_get_advertised_bandwidth_bounded(routerinfo_t *router)
/** Return bw*1000, unless bw*1000 would overflow, in which case return
* INT32_MAX. */
-static INLINE int32_t
+static inline int32_t
kb_to_bytes(uint32_t bw)
{
return (bw > (INT32_MAX/1000)) ? INT32_MAX : bw*1000;
@@ -2270,6 +2479,10 @@ node_sl_choose_by_bandwidth(const smartlist_t *sl,
* If <b>CRN_NEED_DESC</b> is set in flags, we only consider nodes that
* have a routerinfo or microdescriptor -- that is, enough info to be
* used to build a circuit.
+ * If <b>CRN_PREF_ADDR</b> is set in flags, we only consider nodes that
+ * have an address that is preferred by the ClientPreferIPv6ORPort setting
+ * (regardless of this flag, we exclude nodes that aren't allowed by the
+ * firewall, including ClientUseIPv4 0 and fascist_firewall_use_ipv6() == 0).
*/
const node_t *
router_choose_random_node(smartlist_t *excludedsmartlist,
@@ -2282,6 +2495,7 @@ router_choose_random_node(smartlist_t *excludedsmartlist,
const int allow_invalid = (flags & CRN_ALLOW_INVALID) != 0;
const int weight_for_exit = (flags & CRN_WEIGHT_AS_EXIT) != 0;
const int need_desc = (flags & CRN_NEED_DESC) != 0;
+ const int pref_addr = (flags & CRN_PREF_ADDR) != 0;
smartlist_t *sl=smartlist_new(),
*excludednodes=smartlist_new();
@@ -2307,7 +2521,7 @@ router_choose_random_node(smartlist_t *excludedsmartlist,
router_add_running_nodes_to_smartlist(sl, allow_invalid,
need_uptime, need_capacity,
- need_guard, need_desc);
+ need_guard, need_desc, pref_addr);
log_debug(LD_CIRC,
"We found %d running nodes.",
smartlist_len(sl));
@@ -2336,7 +2550,7 @@ router_choose_random_node(smartlist_t *excludedsmartlist,
choice = node_sl_choose_by_bandwidth(sl, rule);
smartlist_free(sl);
- if (!choice && (need_uptime || need_capacity || need_guard)) {
+ if (!choice && (need_uptime || need_capacity || need_guard || pref_addr)) {
/* try once more -- recurse but with fewer restrictions. */
log_info(LD_CIRC,
"We couldn't find any live%s%s%s routers; falling back "
@@ -2344,7 +2558,8 @@ router_choose_random_node(smartlist_t *excludedsmartlist,
need_capacity?", fast":"",
need_uptime?", stable":"",
need_guard?", guard":"");
- flags &= ~ (CRN_NEED_UPTIME|CRN_NEED_CAPACITY|CRN_NEED_GUARD);
+ flags &= ~ (CRN_NEED_UPTIME|CRN_NEED_CAPACITY|CRN_NEED_GUARD|
+ CRN_PREF_ADDR);
choice = router_choose_random_node(
excludedsmartlist, excludedset, flags);
}
@@ -2784,7 +2999,7 @@ dump_routerlist_mem_usage(int severity)
* in <b>sl</b> at position <b>idx</b>. Otherwise, search <b>sl</b> for
* <b>ri</b>. Return the index of <b>ri</b> in <b>sl</b>, or -1 if <b>ri</b>
* is not in <b>sl</b>. */
-static INLINE int
+static inline int
routerlist_find_elt_(smartlist_t *sl, void *ri, int idx)
{
if (idx < 0) {
@@ -3209,7 +3424,11 @@ routerlist_reparse_old(routerlist_t *rl, signed_descriptor_t *sd)
return ri;
}
-/** Free all memory held by the routerlist module. */
+/** Free all memory held by the routerlist module.
+ * Note: Calling routerlist_free_all() should always be paired with
+ * a call to nodelist_free_all(). These should only be called during
+ * cleanup.
+ */
void
routerlist_free_all(void)
{
@@ -4028,15 +4247,16 @@ router_exit_policy_rejects_all(const routerinfo_t *router)
}
/** Create an directory server at <b>address</b>:<b>port</b>, with OR identity
- * key <b>digest</b>. If <b>address</b> is NULL, add ourself. If
- * <b>is_authority</b>, this is a directory authority. Return the new
- * directory server entry on success or NULL on failure. */
+ * key <b>digest</b> which has DIGEST_LEN bytes. If <b>address</b> is NULL,
+ * add ourself. If <b>is_authority</b>, this is a directory authority. Return
+ * the new directory server entry on success or NULL on failure. */
static dir_server_t *
dir_server_new(int is_authority,
const char *nickname,
const tor_addr_t *addr,
const char *hostname,
uint16_t dir_port, uint16_t or_port,
+ const tor_addr_port_t *addrport_ipv6,
const char *digest, const char *v3_auth_digest,
dirinfo_type_t type,
double weight)
@@ -4045,16 +4265,18 @@ dir_server_new(int is_authority,
uint32_t a;
char *hostname_ = NULL;
+ tor_assert(digest);
+
if (weight < 0)
return NULL;
if (tor_addr_family(addr) == AF_INET)
a = tor_addr_to_ipv4h(addr);
else
- return NULL; /*XXXX Support IPv6 */
+ return NULL;
if (!hostname)
- hostname_ = tor_dup_addr(addr);
+ hostname_ = tor_addr_to_str_dup(addr);
else
hostname_ = tor_strdup(hostname);
@@ -4068,6 +4290,18 @@ dir_server_new(int is_authority,
ent->is_authority = is_authority;
ent->type = type;
ent->weight = weight;
+ if (addrport_ipv6) {
+ if (tor_addr_family(&addrport_ipv6->addr) != AF_INET6) {
+ log_warn(LD_BUG, "Hey, I got a non-ipv6 addr as addrport_ipv6.");
+ tor_addr_make_unspec(&ent->ipv6_addr);
+ } else {
+ tor_addr_copy(&ent->ipv6_addr, &addrport_ipv6->addr);
+ ent->ipv6_orport = addrport_ipv6->port;
+ }
+ } else {
+ tor_addr_make_unspec(&ent->ipv6_addr);
+ }
+
memcpy(ent->digest, digest, DIGEST_LEN);
if (v3_auth_digest && (type & V3_DIRINFO))
memcpy(ent->v3_identity_digest, v3_auth_digest, DIGEST_LEN);
@@ -4080,6 +4314,7 @@ dir_server_new(int is_authority,
hostname, (int)dir_port);
ent->fake_status.addr = ent->addr;
+ tor_addr_copy(&ent->fake_status.ipv6_addr, &ent->ipv6_addr);
memcpy(ent->fake_status.identity_digest, digest, DIGEST_LEN);
if (nickname)
strlcpy(ent->fake_status.nickname, nickname,
@@ -4088,6 +4323,7 @@ dir_server_new(int is_authority,
ent->fake_status.nickname[0] = '\0';
ent->fake_status.dir_port = ent->dir_port;
ent->fake_status.or_port = ent->or_port;
+ ent->fake_status.ipv6_orport = ent->ipv6_orport;
return ent;
}
@@ -4099,6 +4335,7 @@ dir_server_new(int is_authority,
dir_server_t *
trusted_dir_server_new(const char *nickname, const char *address,
uint16_t dir_port, uint16_t or_port,
+ const tor_addr_port_t *ipv6_addrport,
const char *digest, const char *v3_auth_digest,
dirinfo_type_t type, double weight)
{
@@ -4129,7 +4366,9 @@ trusted_dir_server_new(const char *nickname, const char *address,
tor_addr_from_ipv4h(&addr, a);
result = dir_server_new(1, nickname, &addr, hostname,
- dir_port, or_port, digest,
+ dir_port, or_port,
+ ipv6_addrport,
+ digest,
v3_auth_digest, type, weight);
tor_free(hostname);
return result;
@@ -4141,9 +4380,12 @@ trusted_dir_server_new(const char *nickname, const char *address,
dir_server_t *
fallback_dir_server_new(const tor_addr_t *addr,
uint16_t dir_port, uint16_t or_port,
+ const tor_addr_port_t *addrport_ipv6,
const char *id_digest, double weight)
{
- return dir_server_new(0, NULL, addr, NULL, dir_port, or_port, id_digest,
+ return dir_server_new(0, NULL, addr, NULL, dir_port, or_port,
+ addrport_ipv6,
+ id_digest,
NULL, ALL_DIRINFO, weight);
}
@@ -4212,7 +4454,7 @@ clear_dir_servers(void)
/** For every current directory connection whose purpose is <b>purpose</b>,
* and where the resource being downloaded begins with <b>prefix</b>, split
* rest of the resource into base16 fingerprints (or base64 fingerprints if
- * purpose==DIR_PURPPOSE_FETCH_MICRODESC), decode them, and set the
+ * purpose==DIR_PURPOSE_FETCH_MICRODESC), decode them, and set the
* corresponding elements of <b>result</b> to a nonzero value.
*/
static void
@@ -4368,14 +4610,14 @@ MOCK_IMPL(STATIC void, initiate_descriptor_downloads,
tor_free(cp);
if (source) {
- /* We know which authority we want. */
+ /* We know which authority or directory mirror we want. */
directory_initiate_command_routerstatus(source, purpose,
ROUTER_PURPOSE_GENERAL,
DIRIND_ONEHOP,
resource, NULL, 0, 0);
} else {
directory_get_from_dirserver(purpose, ROUTER_PURPOSE_GENERAL, resource,
- pds_flags);
+ pds_flags, DL_WANT_ANY_DIRSERVER);
}
tor_free(resource);
}
@@ -4386,13 +4628,20 @@ static int
max_dl_per_request(const or_options_t *options, int purpose)
{
/* Since squid does not like URLs >= 4096 bytes we limit it to 96.
- * 4096 - strlen(http://255.255.255.255/tor/server/d/.z) == 4058
- * 4058/41 (40 for the hash and 1 for the + that separates them) => 98
+ * 4096 - strlen(http://[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ * /tor/server/d/.z) == 4026
+ * 4026/41 (40 for the hash and 1 for the + that separates them) => 98
* So use 96 because it's a nice number.
+ *
+ * For microdescriptors, the calculation is
+ * 4096 - strlen(http://[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ * /tor/micro/d/.z) == 4027
+ * 4027/44 (43 for the hash and 1 for the - that separates them) => 91
+ * So use 90 because it's a nice number.
*/
int max = 96;
if (purpose == DIR_PURPOSE_FETCH_MICRODESC) {
- max = 92;
+ max = 90;
}
/* If we're going to tunnel our connections, we can ask for a lot more
* in a request. */
@@ -4657,9 +4906,14 @@ launch_dummy_descriptor_download_as_needed(time_t now,
last_descriptor_download_attempted + DUMMY_DOWNLOAD_INTERVAL < now &&
last_dummy_download + DUMMY_DOWNLOAD_INTERVAL < now) {
last_dummy_download = now;
+ /* XX/teor - do we want an authority here, because they are less likely
+ * to give us the wrong address? (See #17782)
+ * I'm leaving the previous behaviour intact, because I don't like
+ * the idea of some relays contacting an authority every 20 minutes. */
directory_get_from_dirserver(DIR_PURPOSE_FETCH_SERVERDESC,
ROUTER_PURPOSE_GENERAL, "authority.z",
- PDS_RETRY_IF_NO_SERVERS);
+ PDS_RETRY_IF_NO_SERVERS,
+ DL_WANT_ANY_DIRSERVER);
}
}
@@ -4845,7 +5099,9 @@ router_differences_are_cosmetic(const routerinfo_t *r1, const routerinfo_t *r2)
(r1->contact_info && r2->contact_info &&
strcasecmp(r1->contact_info, r2->contact_info)) ||
r1->is_hibernating != r2->is_hibernating ||
- cmp_addr_policies(r1->exit_policy, r2->exit_policy))
+ cmp_addr_policies(r1->exit_policy, r2->exit_policy) ||
+ (r1->supports_tunnelled_dir_requests !=
+ r2->supports_tunnelled_dir_requests))
return 0;
if ((r1->declared_family == NULL) != (r2->declared_family == NULL))
return 0;
@@ -4964,6 +5220,12 @@ routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri,
goto err;
}
+ if (!digest256_matches && !digest_matches) {
+ if (msg) *msg = "Neither digest256 or digest matched "
+ "digest from routerdesc";
+ goto err;
+ }
+
if (!digest256_matches) {
if (msg) *msg = "Extrainfo digest did not match digest256 from routerdesc";
goto err; /* Digest doesn't match declared value. */
@@ -5136,76 +5398,3 @@ refresh_all_country_info(void)
nodelist_refresh_countries();
}
-/** Determine the routers that are responsible for <b>id</b> (binary) and
- * add pointers to those routers' routerstatus_t to <b>responsible_dirs</b>.
- * Return -1 if we're returning an empty smartlist, else return 0.
- */
-int
-hid_serv_get_responsible_directories(smartlist_t *responsible_dirs,
- const char *id)
-{
- int start, found, n_added = 0, i;
- networkstatus_t *c = networkstatus_get_latest_consensus();
- if (!c || !smartlist_len(c->routerstatus_list)) {
- log_warn(LD_REND, "We don't have a consensus, so we can't perform v2 "
- "rendezvous operations.");
- return -1;
- }
- tor_assert(id);
- start = networkstatus_vote_find_entry_idx(c, id, &found);
- if (start == smartlist_len(c->routerstatus_list)) start = 0;
- i = start;
- do {
- routerstatus_t *r = smartlist_get(c->routerstatus_list, i);
- if (r->is_hs_dir) {
- smartlist_add(responsible_dirs, r);
- if (++n_added == REND_NUMBER_OF_CONSECUTIVE_REPLICAS)
- return 0;
- }
- if (++i == smartlist_len(c->routerstatus_list))
- i = 0;
- } while (i != start);
-
- /* Even though we don't have the desired number of hidden service
- * directories, be happy if we got any. */
- return smartlist_len(responsible_dirs) ? 0 : -1;
-}
-
-/** Return true if this node is currently acting as hidden service
- * directory, false otherwise. */
-int
-hid_serv_acting_as_directory(void)
-{
- const routerinfo_t *me = router_get_my_routerinfo();
- if (!me)
- return 0;
- return 1;
-}
-
-/** Return true if this node is responsible for storing the descriptor ID
- * in <b>query</b> and false otherwise. */
-int
-hid_serv_responsible_for_desc_id(const char *query)
-{
- const routerinfo_t *me;
- routerstatus_t *last_rs;
- const char *my_id, *last_id;
- int result;
- smartlist_t *responsible;
- if (!hid_serv_acting_as_directory())
- return 0;
- if (!(me = router_get_my_routerinfo()))
- return 0; /* This is redundant, but let's be paranoid. */
- my_id = me->cache_info.identity_digest;
- responsible = smartlist_new();
- if (hid_serv_get_responsible_directories(responsible, query) < 0) {
- smartlist_free(responsible);
- return 0;
- }
- last_rs = smartlist_get(responsible, smartlist_len(responsible)-1);
- last_id = last_rs->identity_digest;
- result = rend_id_is_in_interval(my_id, query, last_id);
- smartlist_free(responsible);
- return result;
-}
-
diff --git a/src/or/routerlist.h b/src/or/routerlist.h
index 200533fe91..bc48c2087c 100644
--- a/src/or/routerlist.h
+++ b/src/or/routerlist.h
@@ -1,6 +1,6 @@
/* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -50,6 +50,7 @@ const routerstatus_t *router_pick_directory_server(dirinfo_type_t type,
dir_server_t *router_get_trusteddirserver_by_digest(const char *d);
dir_server_t *router_get_fallback_dirserver_by_digest(
const char *digest);
+int router_digest_is_fallback_dir(const char *digest);
dir_server_t *trusteddirserver_get_by_v3_auth_digest(const char *d);
const routerstatus_t *router_pick_trusteddirserver(dirinfo_type_t type,
int flags);
@@ -60,7 +61,8 @@ void router_reset_status_download_failures(void);
int routers_have_same_or_addrs(const routerinfo_t *r1, const routerinfo_t *r2);
void router_add_running_nodes_to_smartlist(smartlist_t *sl, int allow_invalid,
int need_uptime, int need_capacity,
- int need_guard, int need_desc);
+ int need_guard, int need_desc,
+ int pref_addr);
const routerinfo_t *routerlist_find_my_routerinfo(void);
uint32_t router_get_advertised_bandwidth(const routerinfo_t *router);
@@ -109,7 +111,7 @@ static int WRA_NEVER_DOWNLOADABLE(was_router_added_t s);
* was added. It might still be necessary to check whether the descriptor
* generator should be notified.
*/
-static INLINE int
+static inline int
WRA_WAS_ADDED(was_router_added_t s) {
return s == ROUTER_ADDED_SUCCESSFULLY || s == ROUTER_ADDED_NOTIFY_GENERATOR;
}
@@ -120,7 +122,7 @@ WRA_WAS_ADDED(was_router_added_t s) {
* - it was outdated.
* - its certificates were expired.
*/
-static INLINE int WRA_WAS_OUTDATED(was_router_added_t s)
+static inline int WRA_WAS_OUTDATED(was_router_added_t s)
{
return (s == ROUTER_WAS_TOO_OLD ||
s == ROUTER_IS_ALREADY_KNOWN ||
@@ -130,13 +132,13 @@ static INLINE int WRA_WAS_OUTDATED(was_router_added_t s)
}
/** Return true iff the outcome code in <b>s</b> indicates that the descriptor
* was flat-out rejected. */
-static INLINE int WRA_WAS_REJECTED(was_router_added_t s)
+static inline int WRA_WAS_REJECTED(was_router_added_t s)
{
return (s == ROUTER_AUTHDIR_REJECTS);
}
/** Return true iff the outcome code in <b>s</b> indicates that the descriptor
* was flat-out rejected. */
-static INLINE int WRA_NEVER_DOWNLOADABLE(was_router_added_t s)
+static inline int WRA_NEVER_DOWNLOADABLE(was_router_added_t s)
{
return (s == ROUTER_AUTHDIR_REJECTS ||
s == ROUTER_BAD_EI ||
@@ -170,10 +172,12 @@ int router_exit_policy_rejects_all(const routerinfo_t *router);
dir_server_t *trusted_dir_server_new(const char *nickname, const char *address,
uint16_t dir_port, uint16_t or_port,
+ const tor_addr_port_t *addrport_ipv6,
const char *digest, const char *v3_auth_digest,
dirinfo_type_t type, double weight);
dir_server_t *fallback_dir_server_new(const tor_addr_t *addr,
uint16_t dir_port, uint16_t or_port,
+ const tor_addr_port_t *addrport_ipv6,
const char *id_digest, double weight);
void dir_server_add(dir_server_t *ent);
@@ -198,11 +202,6 @@ void routers_sort_by_identity(smartlist_t *routers);
void refresh_all_country_info(void);
-int hid_serv_get_responsible_directories(smartlist_t *responsible_dirs,
- const char *id);
-int hid_serv_acting_as_directory(void);
-int hid_serv_responsible_for_desc_id(const char *id);
-
void list_pending_microdesc_downloads(digest256map_t *result);
void launch_descriptor_downloads(int purpose,
smartlist_t *downloadable,
@@ -230,6 +229,9 @@ STATIC int choose_array_element_by_weight(const u64_dbl_t *entries,
int n_entries);
STATIC void scale_array_elements_to_u64(u64_dbl_t *entries, int n_entries,
uint64_t *total_out);
+STATIC const routerstatus_t *router_pick_directory_server_impl(
+ dirinfo_type_t auth, int flags,
+ int *n_busy_out);
MOCK_DECL(int, router_descriptor_is_older_than, (const routerinfo_t *router,
int seconds));
@@ -239,6 +241,8 @@ MOCK_DECL(STATIC was_router_added_t, extrainfo_insert,
MOCK_DECL(STATIC void, initiate_descriptor_downloads,
(const routerstatus_t *source, int purpose, smartlist_t *digests,
int lo, int hi, int pds_flags));
+STATIC int router_is_already_dir_fetching(const tor_addr_port_t *ap,
+ int serverdesc, int microdesc);
#endif
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index b1ac3dfee2..600d55294f 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -35,8 +35,9 @@
/****************************************************************************/
/** Enumeration of possible token types. The ones starting with K_ correspond
- * to directory 'keywords'. ERR_ is an error in the tokenizing process, EOF_
- * is an end-of-file marker, and NIL_ is used to encode not-a-token.
+ * to directory 'keywords'. A_ is for an annotation, R or C is related to
+ * hidden services, ERR_ is an error in the tokenizing process, EOF_ is an
+ * end-of-file marker, and NIL_ is used to encode not-a-token.
*/
typedef enum {
K_ACCEPT = 0,
@@ -125,6 +126,7 @@ typedef enum {
K_DIR_KEY_CERTIFICATION,
K_DIR_KEY_CROSSCERT,
K_DIR_ADDRESS,
+ K_DIR_TUNNELLED,
K_VOTE_STATUS,
K_VALID_AFTER,
@@ -318,6 +320,7 @@ static token_rule_t routerdesc_token_table[] = {
T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
T1( "bandwidth", K_BANDWIDTH, GE(3), NO_OBJ ),
A01("@purpose", A_PURPOSE, GE(1), NO_OBJ ),
+ T01("tunnelled-dir-server",K_DIR_TUNNELLED, NO_ARGS, NO_OBJ ),
END_OF_TABLE
};
@@ -535,7 +538,7 @@ static int router_get_hash_impl(const char *s, size_t s_len, char *digest,
char end_char,
digest_algorithm_t alg);
static int router_get_hashes_impl(const char *s, size_t s_len,
- digests_t *digests,
+ common_digests_t *digests,
const char *start_str, const char *end_str,
char end_char);
static void token_clear(directory_token_t *tok);
@@ -635,7 +638,7 @@ router_get_router_hash(const char *s, size_t s_len, char *digest)
/** Set <b>digests</b> to all the digests of the consensus document in
* <b>s</b> */
int
-router_get_networkstatus_v3_hashes(const char *s, digests_t *digests)
+router_get_networkstatus_v3_hashes(const char *s, common_digests_t *digests)
{
return router_get_hashes_impl(s,strlen(s),digests,
"network-status-version",
@@ -1609,6 +1612,12 @@ router_parse_entry_from_string(const char *s, const char *end,
router->wants_to_be_hs_dir = 1;
}
+ /* This router accepts tunnelled directory requests via begindir if it has
+ * an open dirport or it included "tunnelled-dir-server". */
+ if (find_opt_by_keyword(tokens, K_DIR_TUNNELLED) || router->dir_port > 0) {
+ router->supports_tunnelled_dir_requests = 1;
+ }
+
tok = find_by_keyword(tokens, K_ROUTER_SIGNATURE);
note_crypto_pk_op(VERIFY_RTR);
#ifdef COUNT_DISTINCT_DIGESTS
@@ -2061,7 +2070,7 @@ authority_cert_parse_from_string(const char *s, const char **end_of_string)
* object (starting with "r " at the start of a line). If none is found,
* return the start of the directory footer, or the next directory signature.
* If none is found, return the end of the string. */
-static INLINE const char *
+static inline const char *
find_start_of_next_routerstatus(const char *s)
{
const char *eos, *footer, *sig;
@@ -2294,6 +2303,8 @@ routerstatus_parse_entry_from_string(memarea_t *area,
rs->is_unnamed = 1;
} else if (!strcmp(tok->args[i], "HSDir")) {
rs->is_hs_dir = 1;
+ } else if (!strcmp(tok->args[i], "V2Dir")) {
+ rs->is_v2_dir = 1;
}
}
}
@@ -2836,7 +2847,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
smartlist_t *rs_tokens = NULL, *footer_tokens = NULL;
networkstatus_voter_info_t *voter = NULL;
networkstatus_t *ns = NULL;
- digests_t ns_digests;
+ common_digests_t ns_digests;
const char *cert, *end_of_header, *end_of_footer, *s_dup = s;
directory_token_t *tok;
int ok;
@@ -2862,7 +2873,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
(ns_type == NS_TYPE_CONSENSUS) ?
networkstatus_consensus_token_table :
networkstatus_token_table, 0)) {
- log_warn(LD_DIR, "Error tokenizing network-status vote header");
+ log_warn(LD_DIR, "Error tokenizing network-status header");
goto err;
}
@@ -3085,7 +3096,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
base16_decode(voter->identity_digest, sizeof(voter->identity_digest),
tok->args[1], HEX_DIGEST_LEN) < 0) {
log_warn(LD_DIR, "Error decoding identity digest %s in "
- "network-status vote.", escaped(tok->args[1]));
+ "network-status document.", escaped(tok->args[1]));
goto err;
}
if (ns->type != NS_TYPE_CONSENSUS &&
@@ -3144,7 +3155,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
voter = NULL;
}
if (smartlist_len(ns->voters) == 0) {
- log_warn(LD_DIR, "Missing dir-source elements in a vote networkstatus.");
+ log_warn(LD_DIR, "Missing dir-source elements in a networkstatus.");
goto err;
} else if (ns->type != NS_TYPE_CONSENSUS && smartlist_len(ns->voters) != 1) {
log_warn(LD_DIR, "Too many dir-source elements in a vote networkstatus.");
@@ -3205,8 +3216,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
}
if (fast_memcmp(rs1->identity_digest, rs2->identity_digest, DIGEST_LEN)
>= 0) {
- log_warn(LD_DIR, "Vote networkstatus entries not sorted by identity "
- "digest");
+ log_warn(LD_DIR, "Networkstatus entries not sorted by identity digest");
goto err;
}
}
@@ -3319,12 +3329,12 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
base16_decode(declared_identity, sizeof(declared_identity),
id_hexdigest, HEX_DIGEST_LEN) < 0) {
log_warn(LD_DIR, "Error decoding declared identity %s in "
- "network-status vote.", escaped(id_hexdigest));
+ "network-status document.", escaped(id_hexdigest));
goto err;
}
if (!(v = networkstatus_get_voter_by_id(ns, declared_identity))) {
- log_warn(LD_DIR, "ID on signature on network-status vote does not match "
- "any declared directory source.");
+ log_warn(LD_DIR, "ID on signature on network-status document does "
+ "not match any declared directory source.");
goto err;
}
sig = tor_malloc_zero(sizeof(document_signature_t));
@@ -3334,7 +3344,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
base16_decode(sig->signing_key_digest, sizeof(sig->signing_key_digest),
sk_hexdigest, HEX_DIGEST_LEN) < 0) {
log_warn(LD_DIR, "Error decoding declared signing key digest %s in "
- "network-status vote.", escaped(sk_hexdigest));
+ "network-status document.", escaped(sk_hexdigest));
tor_free(sig);
goto err;
}
@@ -3353,8 +3363,8 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
/* We already parsed a vote with this algorithm from this voter. Use the
first one. */
log_fn(LOG_PROTOCOL_WARN, LD_DIR, "We received a networkstatus "
- "that contains two votes from the same voter with the same "
- "algorithm. Ignoring the second vote.");
+ "that contains two signatures from the same voter with the same "
+ "algorithm. Ignoring the second signature.");
tor_free(sig);
continue;
}
@@ -3362,7 +3372,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
if (ns->type != NS_TYPE_CONSENSUS) {
if (check_signature_token(ns_digests.d[DIGEST_SHA1], DIGEST_LEN,
tok, ns->cert->signing_key, 0,
- "network-status vote")) {
+ "network-status document")) {
tor_free(sig);
goto err;
}
@@ -3381,7 +3391,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
} SMARTLIST_FOREACH_END(_tok);
if (! n_signatures) {
- log_warn(LD_DIR, "No signatures on networkstatus vote.");
+ log_warn(LD_DIR, "No signatures on networkstatus document.");
goto err;
} else if (ns->type == NS_TYPE_VOTE && n_signatures != 1) {
log_warn(LD_DIR, "Received more than one signature on a "
@@ -3432,15 +3442,16 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
return ns;
}
-/** Return the digests_t that holds the digests of the
+/** Return the common_digests_t that holds the digests of the
* <b>flavor_name</b>-flavored networkstatus according to the detached
- * signatures document <b>sigs</b>, allocating a new digests_t as neeeded. */
-static digests_t *
+ * signatures document <b>sigs</b>, allocating a new common_digests_t as
+ * neeeded. */
+static common_digests_t *
detached_get_digests(ns_detached_signatures_t *sigs, const char *flavor_name)
{
- digests_t *d = strmap_get(sigs->digests, flavor_name);
+ common_digests_t *d = strmap_get(sigs->digests, flavor_name);
if (!d) {
- d = tor_malloc_zero(sizeof(digests_t));
+ d = tor_malloc_zero(sizeof(common_digests_t));
strmap_set(sigs->digests, flavor_name, d);
}
return d;
@@ -3448,7 +3459,7 @@ detached_get_digests(ns_detached_signatures_t *sigs, const char *flavor_name)
/** Return the list of signatures of the <b>flavor_name</b>-flavored
* networkstatus according to the detached signatures document <b>sigs</b>,
- * allocating a new digests_t as neeeded. */
+ * allocating a new common_digests_t as neeeded. */
static smartlist_t *
detached_get_signatures(ns_detached_signatures_t *sigs,
const char *flavor_name)
@@ -3470,7 +3481,7 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
* networkstatus_parse_vote_from_string(). */
directory_token_t *tok;
memarea_t *area = NULL;
- digests_t *digests;
+ common_digests_t *digests;
smartlist_t *tokens = smartlist_new();
ns_detached_signatures_t *sigs =
@@ -3666,26 +3677,43 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
* assume_action is nonnegative, then insert its action (ADDR_POLICY_ACCEPT or
* ADDR_POLICY_REJECT) for items that specify no action.
*
+ * Returns NULL on policy errors.
+ *
+ * Set *<b>malformed_list</b> to true if the entire policy list should be
+ * discarded. Otherwise, set it to false, and only this item should be ignored
+ * on error - the rest of the policy list can continue to be processed and
+ * used.
+ *
* The addr_policy_t returned by this function can have its address set to
* AF_UNSPEC for '*'. Use policy_expand_unspec() to turn this into a pair
* of AF_INET and AF_INET6 items.
*/
MOCK_IMPL(addr_policy_t *,
-router_parse_addr_policy_item_from_string,(const char *s, int assume_action))
+router_parse_addr_policy_item_from_string,(const char *s, int assume_action,
+ int *malformed_list))
{
directory_token_t *tok = NULL;
const char *cp, *eos;
/* Longest possible policy is
- * "accept6 ffff:ffff:..255/ffff:...255:10000-65535",
- * which contains 2 max-length IPv6 addresses, plus 21 characters.
+ * "accept6 [ffff:ffff:..255]/128:10000-65535",
+ * which contains a max-length IPv6 address, plus 26 characters.
* But note that there can be an arbitrary amount of space between the
- * accept and the address:mask/port element. */
+ * accept and the address:mask/port element.
+ * We don't need to multiply TOR_ADDR_BUF_LEN by 2, as there is only one
+ * IPv6 address. But making the buffer shorter might cause valid long lines,
+ * which parsed in previous versions, to fail to parse in new versions.
+ * (These lines would have to have excessive amounts of whitespace.) */
char line[TOR_ADDR_BUF_LEN*2 + 32];
addr_policy_t *r;
memarea_t *area = NULL;
+ tor_assert(malformed_list);
+ *malformed_list = 0;
+
s = eat_whitespace(s);
- if ((*s == '*' || TOR_ISDIGIT(*s)) && assume_action >= 0) {
+ /* We can only do assume_action on []-quoted IPv6, as "a" (accept)
+ * and ":" (port separator) are ambiguous */
+ if ((*s == '*' || *s == '[' || TOR_ISDIGIT(*s)) && assume_action >= 0) {
if (tor_snprintf(line, sizeof(line), "%s %s",
assume_action == ADDR_POLICY_ACCEPT?"accept":"reject", s)<0) {
log_warn(LD_DIR, "Policy %s is too long.", escaped(s));
@@ -3710,9 +3738,34 @@ router_parse_addr_policy_item_from_string,(const char *s, int assume_action))
goto err;
}
+ /* Use the extended interpretation of accept/reject *,
+ * expanding it into an IPv4 wildcard and an IPv6 wildcard.
+ * Also permit *4 and *6 for IPv4 and IPv6 only wildcards. */
r = router_parse_addr_policy(tok, TAPMP_EXTENDED_STAR);
+ if (!r) {
+ goto err;
+ }
+
+ /* Ensure that accept6/reject6 fields are followed by IPv6 addresses.
+ * AF_UNSPEC addresses are only permitted on the accept/reject field type.
+ * Unlike descriptors, torrcs exit policy accept/reject can be followed by
+ * either an IPv4 or IPv6 address. */
+ if ((tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6) &&
+ tor_addr_family(&r->addr) != AF_INET6) {
+ /* This is a non-fatal error, just ignore this one entry. */
+ *malformed_list = 0;
+ log_warn(LD_DIR, "IPv4 address '%s' with accept6/reject6 field type in "
+ "exit policy. Ignoring, but continuing to parse rules. (Use "
+ "accept/reject with IPv4 addresses.)",
+ tok->n_args == 1 ? tok->args[0] : "");
+ addr_policy_free(r);
+ r = NULL;
+ goto done;
+ }
+
goto done;
err:
+ *malformed_list = 1;
r = NULL;
done:
token_clear(tok);
@@ -3729,19 +3782,27 @@ static int
router_add_exit_policy(routerinfo_t *router, directory_token_t *tok)
{
addr_policy_t *newe;
+ /* Use the standard interpretation of accept/reject *, an IPv4 wildcard. */
newe = router_parse_addr_policy(tok, 0);
if (!newe)
return -1;
if (! router->exit_policy)
router->exit_policy = smartlist_new();
+ /* Ensure that in descriptors, accept/reject fields are followed by
+ * IPv4 addresses, and accept6/reject6 fields are followed by
+ * IPv6 addresses. Unlike torrcs, descriptor exit policies do not permit
+ * accept/reject followed by IPv6. */
if (((tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6) &&
tor_addr_family(&newe->addr) == AF_INET)
||
((tok->tp == K_ACCEPT || tok->tp == K_REJECT) &&
tor_addr_family(&newe->addr) == AF_INET6)) {
+ /* There's nothing the user can do about other relays' descriptors,
+ * so we don't provide usage advice here. */
log_warn(LD_DIR, "Mismatch between field type and address type in exit "
- "policy");
+ "policy '%s'. Discarding entire router descriptor.",
+ tok->n_args == 1 ? tok->args[0] : "");
addr_policy_free(newe);
return -1;
}
@@ -3751,8 +3812,11 @@ router_add_exit_policy(routerinfo_t *router, directory_token_t *tok)
return 0;
}
-/** Given a K_ACCEPT or K_REJECT token and a router, create and return
- * a new exit_policy_t corresponding to the token. */
+/** Given a K_ACCEPT[6] or K_REJECT[6] token and a router, create and return
+ * a new exit_policy_t corresponding to the token. If TAPMP_EXTENDED_STAR
+ * is set in fmt_flags, K_ACCEPT6 and K_REJECT6 tokens followed by *
+ * expand to IPv6-only policies, otherwise they expand to IPv4 and IPv6
+ * policies */
static addr_policy_t *
router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags)
{
@@ -3776,6 +3840,13 @@ router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags)
else
newe.policy_type = ADDR_POLICY_ACCEPT;
+ /* accept6/reject6 * produces an IPv6 wildcard address only.
+ * (accept/reject * produces rules for IPv4 and IPv6 wildcard addresses.) */
+ if ((fmt_flags & TAPMP_EXTENDED_STAR)
+ && (tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6)) {
+ fmt_flags |= TAPMP_STAR_IPV6_ONLY;
+ }
+
if (tor_addr_parse_mask_ports(arg, fmt_flags, &newe.addr, &newe.maskbits,
&newe.prt_min, &newe.prt_max) < 0) {
log_warn(LD_DIR,"Couldn't parse line %s. Dropping", escaped(arg));
@@ -3785,9 +3856,12 @@ router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags)
return addr_policy_get_canonical_entry(&newe);
}
-/** Parse an exit policy line of the format "accept/reject private:...".
+/** Parse an exit policy line of the format "accept[6]/reject[6] private:...".
* This didn't exist until Tor 0.1.1.15, so nobody should generate it in
* router descriptors until earlier versions are obsolete.
+ *
+ * accept/reject and accept6/reject6 private all produce rules for both
+ * IPv4 and IPv6 addresses.
*/
static addr_policy_t *
router_parse_addr_policy_private(directory_token_t *tok)
@@ -3817,6 +3891,13 @@ router_parse_addr_policy_private(directory_token_t *tok)
result.prt_min = port_min;
result.prt_max = port_max;
+ if (tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6) {
+ log_warn(LD_GENERAL,
+ "'%s' expands into rules which apply to all private IPv4 and "
+ "IPv6 addresses. (Use accept/reject private:* for IPv4 and "
+ "IPv6.)", tok->n_args == 1 ? tok->args[0] : "");
+ }
+
return addr_policy_get_canonical_entry(&result);
}
@@ -3863,7 +3944,7 @@ token_clear(directory_token_t *tok)
* Return <b>tok</b> on success, or a new ERR_ token if the token didn't
* conform to the syntax we wanted.
**/
-static INLINE directory_token_t *
+static inline directory_token_t *
token_check_object(memarea_t *area, const char *kwd,
directory_token_t *tok, obj_syntax o_syn)
{
@@ -3928,7 +4009,7 @@ token_check_object(memarea_t *area, const char *kwd,
* number of parsed elements into the n_args field of <b>tok</b>. Allocate
* all storage in <b>area</b>. Return the number of arguments parsed, or
* return -1 if there was an insanely high number of arguments. */
-static INLINE int
+static inline int
get_token_arguments(memarea_t *area, directory_token_t *tok,
const char *s, const char *eol)
{
@@ -4363,7 +4444,7 @@ router_get_hash_impl(const char *s, size_t s_len, char *digest,
/** As router_get_hash_impl, but compute all hashes. */
static int
-router_get_hashes_impl(const char *s, size_t s_len, digests_t *digests,
+router_get_hashes_impl(const char *s, size_t s_len, common_digests_t *digests,
const char *start_str,
const char *end_str, char end_c)
{
@@ -4372,7 +4453,7 @@ router_get_hashes_impl(const char *s, size_t s_len, digests_t *digests,
&start,&end)<0)
return -1;
- if (crypto_digest_all(digests, start, end-start)) {
+ if (crypto_common_digests(digests, start, end-start)) {
log_warn(LD_BUG,"couldn't compute digests");
return -1;
}
diff --git a/src/or/routerparse.h b/src/or/routerparse.h
index 85e4b7d88e..c46eb1c0ae 100644
--- a/src/or/routerparse.h
+++ b/src/or/routerparse.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -14,7 +14,8 @@
int router_get_router_hash(const char *s, size_t s_len, char *digest);
int router_get_dir_hash(const char *s, char *digest);
-int router_get_networkstatus_v3_hashes(const char *s, digests_t *digests);
+int router_get_networkstatus_v3_hashes(const char *s,
+ common_digests_t *digests);
int router_get_extrainfo_hash(const char *s, size_t s_len, char *digest);
#define DIROBJ_MAX_SIG_LEN 256
char *router_get_dirobj_signature(const char *digest,
@@ -41,7 +42,7 @@ extrainfo_t *extrainfo_parse_entry_from_string(const char *s, const char *end,
int cache_copy, struct digest_ri_map_t *routermap,
int *can_dl_again_out);
MOCK_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string,
- (const char *s, int assume_action));
+ (const char *s, int assume_action, int *malformed_list));
version_status_t tor_version_is_obsolete(const char *myversion,
const char *versionlist);
int tor_version_as_new_as(const char *platform, const char *cutoff);
diff --git a/src/or/routerset.c b/src/or/routerset.c
index 99de11ed5e..f260914f4b 100644
--- a/src/or/routerset.c
+++ b/src/or/routerset.c
@@ -1,9 +1,16 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+/**
+ * \file routerset.c
+ *
+ * \brief Functions and structures to handle set-type selection of routers
+ * by name, ID, address, etc.
+ */
+
#define ROUTERSET_PRIVATE
#include "or.h"
@@ -85,10 +92,13 @@ routerset_parse(routerset_t *target, const char *s, const char *description)
int added_countries = 0;
char *countryname;
smartlist_t *list = smartlist_new();
+ int malformed_list;
smartlist_split_string(list, s, ",",
SPLIT_SKIP_SPACE | SPLIT_IGNORE_BLANK, 0);
SMARTLIST_FOREACH_BEGIN(list, char *, nick) {
addr_policy_t *p;
+ /* if it doesn't pass our validation, assume it's malformed */
+ malformed_list = 1;
if (is_legal_hexdigest(nick)) {
char d[DIGEST_LEN];
if (*nick == '$')
@@ -104,17 +114,25 @@ routerset_parse(routerset_t *target, const char *s, const char *description)
description);
smartlist_add(target->country_names, countryname);
added_countries = 1;
- } else if ((strchr(nick,'.') || strchr(nick, '*')) &&
- (p = router_parse_addr_policy_item_from_string(
- nick, ADDR_POLICY_REJECT))) {
+ } else if ((strchr(nick,'.') || strchr(nick, ':') || strchr(nick, '*'))
+ && (p = router_parse_addr_policy_item_from_string(
+ nick, ADDR_POLICY_REJECT,
+ &malformed_list))) {
+ /* IPv4 addresses contain '.', IPv6 addresses contain ':',
+ * and wildcard addresses contain '*'. */
log_debug(LD_CONFIG, "Adding address %s to %s", nick, description);
smartlist_add(target->policies, p);
- } else {
- log_warn(LD_CONFIG, "Entry '%s' in %s is malformed.", nick,
- description);
+ } else if (malformed_list) {
+ log_warn(LD_CONFIG, "Entry '%s' in %s is malformed. Discarding entire"
+ " list.", nick, description);
r = -1;
tor_free(nick);
SMARTLIST_DEL_CURRENT(list, nick);
+ } else {
+ log_notice(LD_CONFIG, "Entry '%s' in %s is ignored. Using the"
+ " remainder of the list.", nick, description);
+ tor_free(nick);
+ SMARTLIST_DEL_CURRENT(list, nick);
}
} SMARTLIST_FOREACH_END(nick);
policy_expand_unspec(&target->policies);
@@ -162,6 +180,17 @@ routerset_is_empty(const routerset_t *set)
return !set || smartlist_len(set->list) == 0;
}
+/** Return the number of entries in <b>set</b>. This does NOT return a
+ * negative value. */
+int
+routerset_len(const routerset_t *set)
+{
+ if (!set) {
+ return 0;
+ }
+ return smartlist_len(set->list);
+}
+
/** Helper. Return true iff <b>set</b> contains a router based on the other
* provided fields. Return higher values for more specific subentries: a
* single router is more specific than an address range of routers, which is
diff --git a/src/or/routerset.h b/src/or/routerset.h
index 8d41de8b6b..c2f7205c3e 100644
--- a/src/or/routerset.h
+++ b/src/or/routerset.h
@@ -1,6 +1,6 @@
/* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -38,6 +38,7 @@ void routerset_subtract_nodes(smartlist_t *out,
char *routerset_to_string(const routerset_t *routerset);
int routerset_equal(const routerset_t *old, const routerset_t *new);
void routerset_free(routerset_t *routerset);
+int routerset_len(const routerset_t *set);
#ifdef ROUTERSET_PRIVATE
STATIC char * routerset_get_countryname(const char *c);
diff --git a/src/or/scheduler.c b/src/or/scheduler.c
index 931bb6b744..8e4810b199 100644
--- a/src/or/scheduler.c
+++ b/src/or/scheduler.c
@@ -1,4 +1,4 @@
-/* * Copyright (c) 2013-2015, The Tor Project, Inc. */
+/* * Copyright (c) 2013-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/scheduler.h b/src/or/scheduler.h
index 27dd2d8388..94a44a0aa3 100644
--- a/src/or/scheduler.h
+++ b/src/or/scheduler.h
@@ -1,4 +1,4 @@
-/* * Copyright (c) 2013-2015, The Tor Project, Inc. */
+/* * Copyright (c) 2013-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/statefile.c b/src/or/statefile.c
index dd1894beb7..9594d9cec3 100644
--- a/src/or/statefile.c
+++ b/src/or/statefile.c
@@ -1,14 +1,22 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+/**
+ * \file statefile.c
+ *
+ * \brief Handles parsing and encoding the persistent 'state' file that carries
+ * miscellaneous persistent state between Tor invocations.
+ */
+
#define STATEFILE_PRIVATE
#include "or.h"
#include "circuitstats.h"
#include "config.h"
#include "confparse.h"
+#include "connection.h"
#include "entrynodes.h"
#include "hibernate.h"
#include "rephist.h"
@@ -372,6 +380,12 @@ or_state_load(void)
new_state = or_state_new();
} else if (contents) {
log_info(LD_GENERAL, "Loaded state from \"%s\"", fname);
+ /* Warn the user if their clock has been set backwards,
+ * they could be tricked into using old consensuses */
+ time_t apparent_skew = new_state->LastWritten - time(NULL);
+ if (apparent_skew > 0)
+ clock_skew_warning(NULL, (long)apparent_skew, 1, LD_GENERAL,
+ "local state file", fname);
} else {
log_info(LD_GENERAL, "Initialized state");
}
diff --git a/src/or/statefile.h b/src/or/statefile.h
index 8c790ea206..b13743481d 100644
--- a/src/or/statefile.h
+++ b/src/or/statefile.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_STATEFILE_H
diff --git a/src/or/status.c b/src/or/status.c
index 8f7be0aa3c..749cee4edf 100644
--- a/src/or/status.c
+++ b/src/or/status.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2015, The Tor Project, Inc. */
+/* Copyright (c) 2010-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -164,24 +164,38 @@ log_accounting(const time_t now, const or_options_t *options)
or_state_t *state = get_or_state();
char *acc_rcvd = bytes_to_usage(state->AccountingBytesReadInInterval);
char *acc_sent = bytes_to_usage(state->AccountingBytesWrittenInInterval);
+ char *acc_used = bytes_to_usage(get_accounting_bytes());
uint64_t acc_bytes = options->AccountingMax;
char *acc_max;
time_t interval_end = accounting_get_end_time();
char end_buf[ISO_TIME_LEN + 1];
char *remaining = NULL;
- if (options->AccountingRule == ACCT_SUM)
- acc_bytes *= 2;
acc_max = bytes_to_usage(acc_bytes);
format_local_iso_time(end_buf, interval_end);
remaining = secs_to_uptime(interval_end - now);
+ const char *acc_rule;
+ switch (options->AccountingRule) {
+ case ACCT_MAX: acc_rule = "max";
+ break;
+ case ACCT_SUM: acc_rule = "sum";
+ break;
+ case ACCT_OUT: acc_rule = "out";
+ break;
+ case ACCT_IN: acc_rule = "in";
+ break;
+ default: acc_rule = "max";
+ break;
+ }
+
log_notice(LD_HEARTBEAT, "Heartbeat: Accounting enabled. "
- "Sent: %s / %s, Received: %s / %s. The "
+ "Sent: %s, Received: %s, Used: %s / %s, Rule: %s. The "
"current accounting interval ends on %s, in %s.",
- acc_sent, acc_max, acc_rcvd, acc_max, end_buf, remaining);
+ acc_sent, acc_rcvd, acc_used, acc_max, acc_rule, end_buf, remaining);
tor_free(acc_rcvd);
tor_free(acc_sent);
+ tor_free(acc_used);
tor_free(acc_max);
tor_free(remaining);
}
diff --git a/src/or/status.h b/src/or/status.h
index 3dd8206e0f..b97e835037 100644
--- a/src/or/status.h
+++ b/src/or/status.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2015, The Tor Project, Inc. */
+/* Copyright (c) 2010-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_STATUS_H
diff --git a/src/or/tor_main.c b/src/or/tor_main.c
index af03b8c06a..ac32eef559 100644
--- a/src/or/tor_main.c
+++ b/src/or/tor_main.c
@@ -1,6 +1,6 @@
/* Copyright 2001-2004 Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/** String describing which Tor Git repository version the source was
@@ -27,6 +27,10 @@ int tor_main(int argc, char *argv[]);
int
main(int argc, char *argv[])
{
- return tor_main(argc, argv);
+ int r = tor_main(argc, argv);
+ if (r < 0 || r > 255)
+ return 1;
+ else
+ return r;
}
diff --git a/src/or/torcert.c b/src/or/torcert.c
index 596cd2be31..a6a33c675a 100644
--- a/src/or/torcert.c
+++ b/src/or/torcert.c
@@ -1,6 +1,13 @@
-/* Copyright (c) 2014, The Tor Project, Inc. */
+/* Copyright (c) 2014-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+/**
+ * \file torcert.c
+ *
+ * \brief Implementation for ed25519-signed certificates as used in the Tor
+ * protocol.
+ */
+
#include "crypto.h"
#include "torcert.h"
#include "ed25519_cert.h"
@@ -100,7 +107,7 @@ tor_cert_create(const ed25519_keypair_t *signing_key,
now, lifetime, flags);
}
-/** Release all storage held for <b>cert</>. */
+/** Release all storage held for <b>cert</b>. */
void
tor_cert_free(tor_cert_t *cert)
{
@@ -206,7 +213,11 @@ tor_cert_checksig(tor_cert_t *cert,
return -1;
} else {
cert->sig_ok = 1;
- memcpy(cert->signing_key.pubkey, checkable.pubkey->pubkey, 32);
+ /* Only copy the checkable public key when it is different from the signing
+ * key of the certificate to avoid undefined behavior. */
+ if (cert->signing_key.pubkey != checkable.pubkey->pubkey) {
+ memcpy(cert->signing_key.pubkey, checkable.pubkey->pubkey, 32);
+ }
cert->cert_valid = 1;
return 0;
}
diff --git a/src/or/torcert.h b/src/or/torcert.h
index b67dc525a2..9c819c0abb 100644
--- a/src/or/torcert.h
+++ b/src/or/torcert.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014, The Tor Project, Inc. */
+/* Copyright (c) 2014-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TORCERT_H_INCLUDED
diff --git a/src/or/transports.c b/src/or/transports.c
index ba2c784c2c..1b8b1e678c 100644
--- a/src/or/transports.c
+++ b/src/or/transports.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2015, The Tor Project, Inc. */
+/* Copyright (c) 2011-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -105,7 +105,7 @@
static process_environment_t *
create_managed_proxy_environment(const managed_proxy_t *mp);
-static INLINE int proxy_configuration_finished(const managed_proxy_t *mp);
+static inline int proxy_configuration_finished(const managed_proxy_t *mp);
static void handle_finished_proxy(managed_proxy_t *mp);
static void parse_method_error(const char *line, int is_server_method);
@@ -713,7 +713,7 @@ register_client_proxy(const managed_proxy_t *mp)
}
/** Register the transports of managed proxy <b>mp</b>. */
-static INLINE void
+static inline void
register_proxy(const managed_proxy_t *mp)
{
if (mp->is_server)
@@ -828,7 +828,7 @@ handle_finished_proxy(managed_proxy_t *mp)
/** Return true if the configuration of the managed proxy <b>mp</b> is
finished. */
-static INLINE int
+static inline int
proxy_configuration_finished(const managed_proxy_t *mp)
{
return (mp->conf_state == PT_PROTO_CONFIGURED ||
@@ -1100,7 +1100,7 @@ parse_smethod_line(const char *line, managed_proxy_t *mp)
smartlist_add(mp->transports, transport);
- /* For now, notify the user so that he knows where the server
+ /* For now, notify the user so that they know where the server
transport is listening. */
log_info(LD_CONFIG, "Server transport %s at %s:%d.",
method_name, address, (int)port);
diff --git a/src/or/transports.h b/src/or/transports.h
index 7c69941496..7de90dcbec 100644
--- a/src/or/transports.h
+++ b/src/or/transports.h
@@ -1,6 +1,6 @@
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2015, The Tor Project, Inc. */
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**