aboutsummaryrefslogtreecommitdiff
path: root/src/or
diff options
context:
space:
mode:
Diffstat (limited to 'src/or')
-rw-r--r--src/or/Makefile.am11
-rw-r--r--src/or/buffers.c5
-rw-r--r--src/or/circuitbuild.c146
-rw-r--r--src/or/circuitbuild.h6
-rw-r--r--src/or/circuituse.c59
-rw-r--r--src/or/circuituse.h1
-rw-r--r--src/or/config.c1115
-rw-r--r--src/or/config.h18
-rw-r--r--src/or/connection.c212
-rw-r--r--src/or/connection.h3
-rw-r--r--src/or/connection_edge.c119
-rw-r--r--src/or/connection_edge.h3
-rw-r--r--src/or/connection_or.c30
-rw-r--r--src/or/control.c14
-rw-r--r--src/or/cpuworker.c1
-rw-r--r--src/or/directory.c16
-rw-r--r--src/or/directory.h2
-rw-r--r--src/or/dirserv.c10
-rw-r--r--src/or/dirvote.c32
-rw-r--r--src/or/dirvote.h3
-rw-r--r--src/or/dns.c7
-rw-r--r--src/or/hibernate.c11
-rw-r--r--src/or/main.c81
-rw-r--r--src/or/nodelist.c113
-rw-r--r--src/or/nodelist.h13
-rw-r--r--src/or/or.h123
-rw-r--r--src/or/relay.c1
-rw-r--r--src/or/rendclient.c19
-rw-r--r--src/or/rendcommon.c5
-rw-r--r--src/or/rendservice.c484
-rw-r--r--src/or/router.c146
-rw-r--r--src/or/router.h10
-rw-r--r--src/or/routerlist.c6
-rw-r--r--src/or/routerparse.c38
-rw-r--r--src/or/transports.c491
-rw-r--r--src/or/transports.h7
36 files changed, 2487 insertions, 874 deletions
diff --git a/src/or/Makefile.am b/src/or/Makefile.am
index 67adf504df..57f5dd65e4 100644
--- a/src/or/Makefile.am
+++ b/src/or/Makefile.am
@@ -74,7 +74,7 @@ AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
tor_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@
tor_LDADD = ./libtor.a ../common/libor.a ../common/libor-crypto.a \
../common/libor-event.a \
- @TOR_ZLIB_LIBS@ -lm @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
+ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
@TOR_LIB_WS32@ @TOR_LIB_GDI@
noinst_HEADERS = \
@@ -126,8 +126,9 @@ tor_main.o: micro-revision.i
micro-revision.i: FORCE
@rm -f micro-revision.tmp; \
- if test -d ../../.git && test -x "`which git 2>&1;true`"; then \
- HASH="`git rev-parse --short=16 HEAD`"; \
+ if test -d "$(top_srcdir)/.git" && \
+ test -x "`which git 2>&1;true`"; then \
+ HASH="`cd "$(top_srcdir)" && git rev-parse --short=16 HEAD`"; \
echo \"$$HASH\" > micro-revision.tmp; \
fi; \
if test ! -f micro-revision.tmp ; then \
@@ -141,10 +142,10 @@ micro-revision.i: FORCE
or_sha1.i: $(tor_SOURCES) $(libtor_a_SOURCES)
if test "@SHA1SUM@" != none; then \
- @SHA1SUM@ $(tor_SOURCES) $(libtor_a_SOURCES) | \
+ (cd "$(srcdir)" && @SHA1SUM@ $(tor_SOURCES) $(libtor_a_SOURCES)) | \
@SED@ -n 's/^\(.*\)$$/"\1\\n"/p' > or_sha1.i; \
elif test "@OPENSSL@" != none; then \
- @OPENSSL@ sha1 $(tor_SOURCES) $(libtor_a_SOURCES) | \
+ (cd "$(srcdir)" && @OPENSSL@ sha1 $(tor_SOURCES) $(libtor_a_SOURCES)) | \
@SED@ -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > or_sha1.i; \
else \
rm or_sha1.i; \
diff --git a/src/or/buffers.c b/src/or/buffers.c
index f4aac0f0e4..7c49423e16 100644
--- a/src/or/buffers.c
+++ b/src/or/buffers.c
@@ -401,9 +401,10 @@ buf_pullup(buf_t *buf, size_t bytes, int nulterminate)
if (buf->head->memlen >= capacity) {
/* We don't need to grow the first chunk, but we might need to repack it.*/
- if (CHUNK_REMAINING_CAPACITY(buf->head) < capacity-buf->datalen)
+ size_t needed = capacity - buf->head->datalen;
+ if (CHUNK_REMAINING_CAPACITY(buf->head) < needed)
chunk_repack(buf->head);
- tor_assert(CHUNK_REMAINING_CAPACITY(buf->head) >= capacity-buf->datalen);
+ tor_assert(CHUNK_REMAINING_CAPACITY(buf->head) >= needed);
} else {
chunk_t *newhead;
size_t newsize;
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index b04bd10120..a9e5d22943 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -2524,7 +2524,8 @@ onionskin_answer(or_circuit_t *circ, uint8_t cell_type, const char *payload,
append_cell_to_circuit_queue(TO_CIRCUIT(circ),
circ->p_conn, &cell, CELL_DIRECTION_IN, 0);
- log_debug(LD_CIRC,"Finished sending 'created' cell.");
+ log_debug(LD_CIRC,"Finished sending '%s' cell.",
+ circ->is_first_hop ? "created_fast" : "created");
if (!is_local_addr(&circ->p_conn->_base.addr) &&
!connection_or_nonopen_was_started_here(circ->p_conn)) {
@@ -3012,7 +3013,7 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit)
log_warn(LD_CIRC,"failed to choose an exit server");
return -1;
}
- exit = extend_info_from_node(node);
+ exit = extend_info_from_node(node, 0);
tor_assert(exit);
}
state->chosen_exit = exit;
@@ -3254,14 +3255,19 @@ onion_extend_cpath(origin_circuit_t *circ)
} else if (cur_len == 0) { /* picking first node */
const node_t *r = choose_good_entry_server(purpose, state);
if (r) {
- info = extend_info_from_node(r);
+ /* If we're extending to a bridge, use the preferred address
+ rather than the primary, for potentially extending to an IPv6
+ bridge. */
+ int use_pref_addr = (r->ri != NULL &&
+ r->ri->purpose == ROUTER_PURPOSE_BRIDGE);
+ info = extend_info_from_node(r, use_pref_addr);
tor_assert(info);
}
} else {
const node_t *r =
choose_good_middle_server(purpose, state, circ->cpath, cur_len);
if (r) {
- info = extend_info_from_node(r);
+ info = extend_info_from_node(r, 0);
tor_assert(info);
}
}
@@ -3320,28 +3326,36 @@ extend_info_alloc(const char *nickname, const char *digest,
return info;
}
-/** Allocate and return a new extend_info_t that can be used to build a
- * circuit to or through the router <b>r</b>. */
+/** Allocate and return a new extend_info_t that can be used to build
+ * a circuit to or through the router <b>r</b>. Use the primary
+ * address of the router unless <b>for_direct_connect</b> is true, in
+ * which case the preferred address is used instead. */
extend_info_t *
-extend_info_from_router(const routerinfo_t *r)
+extend_info_from_router(const routerinfo_t *r, int for_direct_connect)
{
- tor_addr_t addr;
+ tor_addr_port_t ap;
tor_assert(r);
- tor_addr_from_ipv4h(&addr, r->addr);
+
+ if (for_direct_connect)
+ router_get_pref_orport(r, &ap);
+ else
+ router_get_prim_orport(r, &ap);
return extend_info_alloc(r->nickname, r->cache_info.identity_digest,
- r->onion_pkey, &addr, r->or_port);
+ r->onion_pkey, &ap.addr, ap.port);
}
-/** Allocate and return a new extend_info that can be used to build a ircuit
- * to or through the node <b>node</b>. May return NULL if there is not
- * enough info about <b>node</b> to extend to it--for example, if there
- * is no routerinfo_t or microdesc_t.
+/** Allocate and return a new extend_info that can be used to build a
+ * ircuit to or through the node <b>node</b>. Use the primary address
+ * of the node unless <b>for_direct_connect</b> is true, in which case
+ * the preferred address is used instead. May return NULL if there is
+ * not enough info about <b>node</b> to extend to it--for example, if
+ * there is no routerinfo_t or microdesc_t.
**/
extend_info_t *
-extend_info_from_node(const node_t *node)
+extend_info_from_node(const node_t *node, int for_direct_connect)
{
if (node->ri) {
- return extend_info_from_router(node->ri);
+ return extend_info_from_router(node->ri, for_direct_connect);
} else if (node->rs && node->md) {
tor_addr_t addr;
tor_addr_from_ipv4h(&addr, node->rs->addr);
@@ -4841,10 +4855,11 @@ get_configured_bridge_by_addr_port_digest(const tor_addr_t *addr,
static bridge_info_t *
get_configured_bridge_by_routerinfo(const routerinfo_t *ri)
{
- tor_addr_t addr;
- tor_addr_from_ipv4h(&addr, ri->addr);
- return get_configured_bridge_by_addr_port_digest(&addr,
- ri->or_port, ri->cache_info.identity_digest);
+ tor_addr_port_t ap;
+
+ router_get_pref_orport(ri, &ap);
+ return get_configured_bridge_by_addr_port_digest(&ap.addr, ap.port,
+ ri->cache_info.identity_digest);
}
/** Return 1 if <b>ri</b> is one of our known bridges, else 0. */
@@ -4858,18 +4873,31 @@ routerinfo_is_a_configured_bridge(const routerinfo_t *ri)
int
node_is_a_configured_bridge(const node_t *node)
{
- tor_addr_t addr;
- uint16_t orport;
+ int retval = 0; /* Negative. */
+ smartlist_t *orports = NULL;
+
if (!node)
- return 0;
- if (node_get_addr(node, &addr) < 0)
- return 0;
- orport = node_get_orport(node);
- if (orport == 0)
- return 0;
+ goto out;
+
+ orports = node_get_all_orports(node);
+ if (orports == NULL)
+ goto out;
- return get_configured_bridge_by_addr_port_digest(
- &addr, orport, node->identity) != NULL;
+ SMARTLIST_FOREACH_BEGIN(orports, tor_addr_port_t *, orport) {
+ if (get_configured_bridge_by_addr_port_digest(&orport->addr, orport->port,
+ node->identity) != NULL) {
+ retval = 1;
+ goto out;
+ }
+ } SMARTLIST_FOREACH_END(orport);
+
+ out:
+ if (orports != NULL) {
+ SMARTLIST_FOREACH(orports, tor_addr_port_t *, p, tor_free(p));
+ smartlist_free(orports);
+ orports = NULL;
+ }
+ return retval;
}
/** We made a connection to a router at <b>addr</b>:<b>port</b>
@@ -5123,18 +5151,52 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node)
routerinfo_t *ri = node->ri;
tor_addr_from_ipv4h(&addr, ri->addr);
- if (!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) &&
- bridge->port == ri->or_port) {
+ if ((!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) &&
+ bridge->port == ri->or_port) ||
+ (!tor_addr_compare(&bridge->addr, &ri->ipv6_addr, CMP_EXACT) &&
+ bridge->port == ri->ipv6_orport)) {
/* they match, so no need to do anything */
} else {
- ri->addr = tor_addr_to_ipv4h(&bridge->addr);
- tor_free(ri->address);
- ri->address = tor_dup_ip(ri->addr);
- ri->or_port = bridge->port;
- log_info(LD_DIR,
- "Adjusted bridge routerinfo for '%s' to match configured "
- "address %s:%d.",
- ri->nickname, ri->address, ri->or_port);
+ if (tor_addr_family(&bridge->addr) == AF_INET) {
+ ri->addr = tor_addr_to_ipv4h(&bridge->addr);
+ tor_free(ri->address);
+ ri->address = tor_dup_ip(ri->addr);
+ ri->or_port = bridge->port;
+ log_info(LD_DIR,
+ "Adjusted bridge routerinfo for '%s' to match configured "
+ "address %s:%d.",
+ ri->nickname, ri->address, ri->or_port);
+ } else if (tor_addr_family(&bridge->addr) == AF_INET6) {
+ tor_addr_copy(&ri->ipv6_addr, &bridge->addr);
+ ri->ipv6_orport = bridge->port;
+ log_info(LD_DIR,
+ "Adjusted bridge routerinfo for '%s' to match configured "
+ "address %s:%d.",
+ ri->nickname, fmt_addr(&ri->ipv6_addr), ri->ipv6_orport);
+ } else {
+ log_err(LD_BUG, "Address family not supported: %d.",
+ tor_addr_family(&bridge->addr));
+ return;
+ }
+ }
+
+ /* Indicate that we prefer connecting to this bridge over the
+ protocol that the bridge address indicates. Last bridge
+ descriptor handled wins. */
+ ri->ipv6_preferred = tor_addr_family(&bridge->addr) == AF_INET6;
+
+ /* XXXipv6 we lack support for falling back to another address for
+ the same relay, warn the user */
+ if (!tor_addr_is_null(&ri->ipv6_addr))
+ {
+ tor_addr_port_t ap;
+ router_get_pref_orport(ri, &ap);
+ log_notice(LD_CONFIG,
+ "Bridge '%s' has both an IPv4 and an IPv6 address. "
+ "Will prefer using its %s address (%s:%d).",
+ ri->nickname,
+ ri->ipv6_preferred ? "IPv6" : "IPv4",
+ fmt_addr(&ap.addr), ap.port);
}
}
if (node->rs) {
@@ -5179,8 +5241,8 @@ learned_bridge_descriptor(routerinfo_t *ri, int from_cache)
rewrite_node_address_for_bridge(bridge, node);
add_an_entry_guard(node, 1, 1);
- log_notice(LD_DIR, "new bridge descriptor '%s' (%s)", ri->nickname,
- from_cache ? "cached" : "fresh");
+ log_notice(LD_DIR, "new bridge descriptor '%s' (%s): %s", ri->nickname,
+ from_cache ? "cached" : "fresh", router_describe(ri));
/* set entry->made_contact so if it goes down we don't drop it from
* our entry node list */
entry_guard_register_connect_status(ri->cache_info.identity_digest,
diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h
index 1052db6153..8df2748708 100644
--- a/src/or/circuitbuild.h
+++ b/src/or/circuitbuild.h
@@ -59,8 +59,10 @@ void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop);
extend_info_t *extend_info_alloc(const char *nickname, const char *digest,
crypto_pk_env_t *onion_key,
const tor_addr_t *addr, uint16_t port);
-extend_info_t *extend_info_from_router(const routerinfo_t *r);
-extend_info_t *extend_info_from_node(const node_t *node);
+extend_info_t *extend_info_from_router(const routerinfo_t *r,
+ int for_direct_connect);
+extend_info_t *extend_info_from_node(const node_t *node,
+ int for_direct_connect);
extend_info_t *extend_info_dup(extend_info_t *info);
void extend_info_free(extend_info_t *info);
const node_t *build_state_get_exit_node(cpath_build_state_t *state);
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index 23efe05348..ef4ac6faa3 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -1004,7 +1004,6 @@ circuit_testing_failed(origin_circuit_t *circ, int at_last_hop)
void
circuit_has_opened(origin_circuit_t *circ)
{
- int can_try_clearing_isolation = 0, tried_clearing_isolation = 0;
control_event_circuit_status(circ, CIRC_EVENT_BUILT, 0);
/* Remember that this circuit has finished building. Now if we start
@@ -1012,13 +1011,18 @@ circuit_has_opened(origin_circuit_t *circ)
* to consider its build time. */
circ->has_opened = 1;
- again:
-
switch (TO_CIRCUIT(circ)->purpose) {
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
rend_client_rendcirc_has_opened(circ);
- can_try_clearing_isolation = 1;
+ /* Start building an intro circ if we don't have one yet. */
connection_ap_attach_pending();
+ /* 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,
+ * thus circuit_try_attaching_streams would always clear the
+ * circuit's isolation state. circuit_try_attaching_streams is
+ * called later, when the rend circ enters _C_REND_JOINED
+ * state. */
break;
case CIRCUIT_PURPOSE_C_INTRODUCING:
rend_client_introcirc_has_opened(circ);
@@ -1026,8 +1030,7 @@ circuit_has_opened(origin_circuit_t *circ)
case CIRCUIT_PURPOSE_C_GENERAL:
/* Tell any AP connections that have been waiting for a new
* circuit that one is ready. */
- can_try_clearing_isolation = 1;
- connection_ap_attach_pending();
+ circuit_try_attaching_streams(circ);
break;
case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
/* at Bob, waiting for introductions */
@@ -1044,12 +1047,15 @@ circuit_has_opened(origin_circuit_t *circ)
* This won't happen in normal operation, but might happen if the
* controller did it. Just let it slide. */
}
+}
+/** If the stream-isolation state of <b>circ</b> can be cleared, clear
+ * it. Return non-zero iff <b>circ</b>'s isolation state was cleared. */
+static int
+circuit_try_clearing_isolation_state(origin_circuit_t *circ)
+{
if (/* The circuit may have become non-open if it was cannibalized.*/
circ->_base.state == CIRCUIT_STATE_OPEN &&
- /* Only if the purpose is clearable, and only if we haven't tried
- * to clear isolation yet, do we try. */
- can_try_clearing_isolation && !tried_clearing_isolation &&
/* If !isolation_values_set, there is nothing to clear. */
circ->isolation_values_set &&
/* It's not legal to clear a circuit's isolation info if it's ever had
@@ -1059,8 +1065,26 @@ circuit_has_opened(origin_circuit_t *circ)
* we didn't manage to attach any streams to it, then we can
* and should clear it and try again. */
circuit_clear_isolation(circ);
- tried_clearing_isolation = 1;
- goto again;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/** Called when a circuit becomes ready for streams to be attached to
+ * it. */
+void
+circuit_try_attaching_streams(origin_circuit_t *circ)
+{
+ /* Attach streams to this circuit if we can. */
+ connection_ap_attach_pending();
+
+ /* 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();
}
}
@@ -1439,7 +1463,10 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
int opt = conn->chosen_exit_optional;
r = node_get_by_nickname(conn->chosen_exit_name, 1);
if (r && node_has_descriptor(r)) {
- extend_info = extend_info_from_node(r);
+ /* We might want to connect to an IPv6 bridge for loading
+ descriptors so we use the preferred address rather than
+ the primary. */
+ extend_info = extend_info_from_node(r, conn->want_onehop ? 1 : 0);
} else {
log_debug(LD_DIR, "considering %d, %s",
want_onehop, conn->chosen_exit_name);
@@ -1489,6 +1516,12 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
else
new_circ_purpose = desired_circuit_purpose;
+ if (options->Tor2webMode &&
+ (new_circ_purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND ||
+ new_circ_purpose == CIRCUIT_PURPOSE_C_INTRODUCING)) {
+ want_onehop = 1;
+ }
+
{
int flags = CIRCLAUNCH_NEED_CAPACITY;
if (want_onehop) flags |= CIRCLAUNCH_ONEHOP_TUNNEL;
@@ -1684,7 +1717,7 @@ consider_recording_trackhost(const entry_connection_t *conn,
addressmap_register(conn->socks_request->address, new_address,
time(NULL) + options->TrackHostExitsExpire,
- ADDRMAPSRC_TRACKEXIT);
+ ADDRMAPSRC_TRACKEXIT, 0, 0);
}
/** Attempt to attach the connection <b>conn</b> to <b>circ</b>, and send a
diff --git a/src/or/circuituse.h b/src/or/circuituse.h
index 9867fd8205..bc11fe5d91 100644
--- a/src/or/circuituse.h
+++ b/src/or/circuituse.h
@@ -29,6 +29,7 @@ void reset_bandwidth_test(void);
int circuit_enough_testing_circs(void);
void circuit_has_opened(origin_circuit_t *circ);
+void circuit_try_attaching_streams(origin_circuit_t *circ);
void circuit_build_failed(origin_circuit_t *circ);
/** Flag to set when a circuit should have only a single hop. */
diff --git a/src/or/config.c b/src/or/config.c
index 06d7d5c022..be4113f41a 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -220,7 +220,7 @@ static config_var_t _option_vars[] = {
V(ConstrainedSockSize, MEMUNIT, "8192"),
V(ContactInfo, STRING, NULL),
V(ControlListenAddress, LINELIST, NULL),
- V(ControlPort, PORT, "0"),
+ V(ControlPort, LINELIST, NULL),
V(ControlPortFileGroupReadable,BOOL, "0"),
V(ControlPortWriteToFile, FILENAME, NULL),
V(ControlSocket, LINELIST, NULL),
@@ -231,12 +231,13 @@ static config_var_t _option_vars[] = {
V(CountPrivateBandwidth, BOOL, "0"),
V(DataDirectory, FILENAME, NULL),
OBSOLETE("DebugLogFile"),
- V(DirAllowPrivateAddresses, BOOL, NULL),
+ V(DisableNetwork, BOOL, "0"),
+ V(DirAllowPrivateAddresses, BOOL, "0"),
V(TestingAuthDirTimeToLearnReachability, INTERVAL, "30 minutes"),
V(DirListenAddress, LINELIST, NULL),
OBSOLETE("DirFetchPeriod"),
V(DirPolicy, LINELIST, NULL),
- V(DirPort, PORT, "0"),
+ V(DirPort, LINELIST, NULL),
V(DirPortFrontPage, FILENAME, NULL),
OBSOLETE("DirPostPeriod"),
OBSOLETE("DirRecordUsageByCountry"),
@@ -246,7 +247,9 @@ static config_var_t _option_vars[] = {
V(DirReqStatistics, BOOL, "1"),
VAR("DirServer", LINELIST, DirServers, NULL),
V(DisableAllSwap, BOOL, "0"),
+ V(DisableDebuggerAttachment, BOOL, "1"),
V(DisableIOCP, BOOL, "1"),
+ V(DynamicDHGroups, BOOL, "1"),
V(DNSPort, LINELIST, NULL),
V(DNSListenAddress, LINELIST, NULL),
V(DownloadExtraInfo, BOOL, "0"),
@@ -340,7 +343,7 @@ static config_var_t _option_vars[] = {
V(NumCPUs, UINT, "0"),
V(NumEntryGuards, UINT, "3"),
V(ORListenAddress, LINELIST, NULL),
- V(ORPort, PORT, "0"),
+ V(ORPort, LINELIST, NULL),
V(OutboundBindAddress, STRING, NULL),
OBSOLETE("PathlenCoinWeight"),
V(PerConnBWBurst, MEMUNIT, "0"),
@@ -394,6 +397,7 @@ static config_var_t _option_vars[] = {
V(TestSocks, BOOL, "0"),
OBSOLETE("TestVia"),
V(TokenBucketRefillInterval, MSEC_INTERVAL, "100 msec"),
+ V(Tor2webMode, BOOL, "0"),
V(TrackHostExits, CSV, NULL),
V(TrackHostExitsExpire, INTERVAL, "30 minutes"),
OBSOLETE("TrafficShaping"),
@@ -405,6 +409,7 @@ static config_var_t _option_vars[] = {
V(UseEntryGuards, BOOL, "1"),
V(UseMicrodescriptors, AUTOBOOL, "auto"),
V(User, STRING, NULL),
+ V(UserspaceIOCPBuffers, BOOL, "0"),
VAR("V1AuthoritativeDirectory",BOOL, V1AuthoritativeDir, "0"),
VAR("V2AuthoritativeDirectory",BOOL, V2AuthoritativeDir, "0"),
VAR("V3AuthoritativeDirectory",BOOL, V3AuthoritativeDir, "0"),
@@ -596,8 +601,11 @@ static int parse_dir_server_line(const char *line,
dirinfo_type_t required_type,
int validate_only);
static void port_cfg_free(port_cfg_t *port);
-static int parse_client_ports(const or_options_t *options, int validate_only,
+static int parse_ports(const or_options_t *options, int validate_only,
char **msg_out, int *n_ports_out);
+static int check_server_ports(const smartlist_t *ports,
+ const or_options_t *options);
+
static int validate_data_directory(or_options_t *options);
static int write_configuration_file(const char *fname,
const or_options_t *options);
@@ -610,9 +618,6 @@ static int or_state_validate(or_state_t *old_options, or_state_t *options,
static int or_state_load(void);
static int options_init_logs(or_options_t *options, int validate_only);
-static int is_listening_on_low_port(int port_option,
- const config_line_t *listen_options);
-
static uint64_t config_parse_memunit(const char *s, int *ok);
static int config_parse_msec_interval(const char *s, int *ok);
static int config_parse_interval(const char *s, int *ok);
@@ -659,16 +664,20 @@ static const config_format_t state_format = {
/** Command-line and config-file options. */
static or_options_t *global_options = NULL;
+/** DOCDOC */
+static or_options_t *global_default_options = NULL;
/** Name of most recently read torrc file. */
static char *torrc_fname = NULL;
+/** DOCDOC */
+static char *torrc_defaults_fname;
/** Persistent serialized state. */
static or_state_t *global_state = NULL;
/** Configuration Options set by command line. */
static config_line_t *global_cmdline_options = NULL;
/** Contents of most recently read DirPortFrontPage file. */
static char *global_dirfrontpagecontents = NULL;
-/** List of port_cfg_t for client-level (SOCKS, DNS, Trans, NATD) ports. */
-static smartlist_t *configured_client_ports = NULL;
+/** List of port_cfg_t for all configured ports. */
+static smartlist_t *configured_ports = NULL;
/** Return the contents of our frontpage string, or NULL if not configured. */
const char *
@@ -804,6 +813,8 @@ config_free_all(void)
{
or_options_free(global_options);
global_options = NULL;
+ or_options_free(global_default_options);
+ global_default_options = NULL;
config_free(&state_format, global_state);
global_state = NULL;
@@ -811,14 +822,15 @@ config_free_all(void)
config_free_lines(global_cmdline_options);
global_cmdline_options = NULL;
- if (configured_client_ports) {
- SMARTLIST_FOREACH(configured_client_ports,
+ if (configured_ports) {
+ SMARTLIST_FOREACH(configured_ports,
port_cfg_t *, p, tor_free(p));
- smartlist_free(configured_client_ports);
- configured_client_ports = NULL;
+ smartlist_free(configured_ports);
+ configured_ports = NULL;
}
tor_free(torrc_fname);
+ tor_free(torrc_defaults_fname);
tor_free(_version);
tor_free(global_dirfrontpagecontents);
}
@@ -1064,7 +1076,7 @@ options_act_reversible(const or_options_t *old_options, char **msg)
#endif
if (running_tor) {
- int n_client_ports=0;
+ int n_ports=0;
/* We need to set the connection limit before we can open the listeners. */
if (set_max_file_descriptors((unsigned)options->ConnLimit,
&options->_ConnLimit) < 0) {
@@ -1080,10 +1092,10 @@ options_act_reversible(const or_options_t *old_options, char **msg)
libevent_initialized = 1;
}
- /* Adjust the client port configuration so we can launch listeners. */
- if (parse_client_ports(options, 0, msg, &n_client_ports)) {
+ /* Adjust the port configuration so we can launch listeners. */
+ if (parse_ports(options, 0, msg, &n_ports)) {
if (!*msg)
- *msg = tor_strdup("Unexpected problem parsing client port config");
+ *msg = tor_strdup("Unexpected problem parsing port config");
goto rollback;
}
@@ -1091,13 +1103,22 @@ options_act_reversible(const or_options_t *old_options, char **msg)
consider_hibernation(time(NULL));
/* Launch the listeners. (We do this before we setuid, so we can bind to
- * ports under 1024.) We don't want to rebind if we're hibernating. */
+ * ports under 1024.) We don't want to rebind if we're hibernating. If
+ * networking is disabled, this will close all but the control listeners,
+ * but disable those. */
if (!we_are_hibernating()) {
if (retry_all_listeners(replaced_listeners, new_listeners) < 0) {
*msg = tor_strdup("Failed to bind one of the listener ports.");
goto rollback;
}
}
+ if (options->DisableNetwork) {
+ /* Aggressively close non-controller stuff, NOW */
+ log_notice(LD_NET, "DisableNetwork is set. Tor will not make or accept "
+ "non-control network connections. Shutting down all existing "
+ "connections.");
+ connection_mark_all_noncontrol_connections();
+ }
}
#if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H)
@@ -1266,6 +1287,24 @@ get_effective_bwburst(const or_options_t *options)
return (uint32_t)bw;
}
+/** Return True if any changes from <b>old_options</b> to
+ * <b>new_options</b> needs us to refresh our TLS context. */
+static int
+options_transition_requires_fresh_tls_context(const or_options_t *old_options,
+ const or_options_t *new_options)
+{
+ tor_assert(new_options);
+
+ if (!old_options)
+ return 0;
+
+ if ((old_options->DynamicDHGroups != new_options->DynamicDHGroups)) {
+ return 1;
+ }
+
+ return 0;
+}
+
/** Fetch the active option list, and take actions based on it. All of the
* things we do should survive being done repeatedly. If present,
* <b>old_options</b> contains the previous value of the options.
@@ -1285,6 +1324,14 @@ options_act(const or_options_t *old_options)
const int transition_affects_workers =
old_options && options_transition_affects_workers(old_options, options);
+ /* disable ptrace and later, other basic debugging techniques */
+ if (options->DisableDebuggerAttachment) {
+ tor_disable_debugger_attach();
+ } else {
+ log_notice(LD_CONFIG,"Debugger attachment enabled "
+ "for unprivileged users.");
+ }
+
if (running_tor && !have_lockfile()) {
if (try_locking(options, 1) < 0)
return -1;
@@ -1293,6 +1340,28 @@ options_act(const or_options_t *old_options)
if (consider_adding_dir_authorities(options, old_options) < 0)
return -1;
+#ifdef NON_ANONYMOUS_MODE_ENABLED
+ log(LOG_WARN, LD_GENERAL, "This copy of Tor was compiled to run in a "
+ "non-anonymous mode. It will provide NO ANONYMITY.");
+#endif
+
+#ifdef ENABLE_TOR2WEB_MODE
+ 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;
+ }
+#else
+ if (options->Tor2webMode) {
+ log_err(LD_CONFIG, "This copy of Tor was not compiled to run in "
+ "'tor2web mode'. It cannot be run with the Tor2webMode torrc "
+ "option enabled. To enable Tor2webMode recompile with the "
+ "--enable-tor2webmode option.");
+ return -1;
+ }
+#endif
+
if (options->Bridges) {
mark_bridge_list();
for (cl = options->Bridges; cl; cl = cl->next) {
@@ -1361,6 +1430,29 @@ options_act(const or_options_t *old_options)
finish_daemon(options->DataDirectory);
}
+ /* If needed, generate a new TLS DH prime according to the current torrc. */
+ if (server_mode(options)) {
+ if (!old_options) {
+ if (options->DynamicDHGroups) {
+ char *fname = get_datadir_fname2("keys", "dynamic_dh_params");
+ crypto_set_tls_dh_prime(fname);
+ tor_free(fname);
+ } else {
+ crypto_set_tls_dh_prime(NULL);
+ }
+ } else {
+ if (options->DynamicDHGroups && !old_options->DynamicDHGroups) {
+ char *fname = get_datadir_fname2("keys", "dynamic_dh_params");
+ crypto_set_tls_dh_prime(fname);
+ tor_free(fname);
+ } else if (!options->DynamicDHGroups && old_options->DynamicDHGroups) {
+ crypto_set_tls_dh_prime(NULL);
+ }
+ }
+ } else { /* clients don't need a dynamic DH prime. */
+ crypto_set_tls_dh_prime(NULL);
+ }
+
/* We want to reinit keys as needed before we do much of anything else:
keys are important, and other things can depend on them. */
if (transition_affects_workers ||
@@ -1370,6 +1462,13 @@ options_act(const or_options_t *old_options)
log_warn(LD_BUG,"Error initializing keys; exiting");
return -1;
}
+ } else if (old_options &&
+ options_transition_requires_fresh_tls_context(old_options,
+ options)) {
+ if (router_initialize_tls_context() < 0) {
+ log_warn(LD_BUG,"Error initializing TLS context.");
+ return -1;
+ }
}
/* Write our PID to the PID file. If we do not have write permissions we
@@ -1479,7 +1578,7 @@ options_act(const or_options_t *old_options)
int was_relay = 0;
if (options->BridgeRelay) {
time_t int_start = time(NULL);
- if (old_options->ORPort == options->ORPort) {
+ if (config_lines_eq(old_options->ORPort, options->ORPort)) {
int_start += RELAY_BRIDGE_STATS_DELAY;
was_relay = 1;
}
@@ -1718,7 +1817,11 @@ config_get_commandlines(int argc, char **argv, config_line_t **result)
int i = 1;
while (i < argc) {
+ unsigned command = CONFIG_LINE_NORMAL;
+ int want_arg = 1;
+
if (!strcmp(argv[i],"-f") ||
+ !strcmp(argv[i],"--defaults-torrc") ||
!strcmp(argv[i],"--hash-password")) {
i += 2; /* command-line option with argument. ignore them. */
continue;
@@ -1735,13 +1838,6 @@ config_get_commandlines(int argc, char **argv, config_line_t **result)
continue;
}
- if (i == argc-1) {
- log_warn(LD_CONFIG,"Command-line option '%s' with no value. Failing.",
- argv[i]);
- config_free_lines(front);
- return -1;
- }
-
*new = tor_malloc_zero(sizeof(config_line_t));
s = argv[i];
@@ -1750,15 +1846,33 @@ config_get_commandlines(int argc, char **argv, config_line_t **result)
s++;
if (*s == '-')
s++;
+ /* Figure out the command, if any. */
+ if (*s == '+') {
+ s++;
+ command = CONFIG_LINE_APPEND;
+ } else if (*s == '/') {
+ s++;
+ command = CONFIG_LINE_CLEAR;
+ /* A 'clear' command has no argument. */
+ want_arg = 0;
+ }
+
+ if (want_arg && i == argc-1) {
+ log_warn(LD_CONFIG,"Command-line option '%s' with no value. Failing.",
+ argv[i]);
+ config_free_lines(front);
+ return -1;
+ }
(*new)->key = tor_strdup(expand_abbrev(&options_format, s, 1, 1));
- (*new)->value = tor_strdup(argv[i+1]);
+ (*new)->value = want_arg ? tor_strdup(argv[i+1]) : tor_strdup("");
+ (*new)->command = command;
(*new)->next = NULL;
log(LOG_DEBUG, LD_CONFIG, "command line: parsed keyword '%s', value '%s'",
(*new)->key, (*new)->value);
new = &((*new)->next);
- i += 2;
+ i += want_arg ? 2 : 1;
}
*result = front;
return 0;
@@ -1773,7 +1887,7 @@ config_line_append(config_line_t **lst,
{
config_line_t *newline;
- newline = tor_malloc(sizeof(config_line_t));
+ newline = tor_malloc_zero(sizeof(config_line_t));
newline->key = tor_strdup(key);
newline->value = tor_strdup(val);
newline->next = NULL;
@@ -1786,9 +1900,12 @@ config_line_append(config_line_t **lst,
/** Helper: parse the config string and strdup into key/value
* strings. Set *result to the list, or NULL if parsing the string
* failed. Return 0 on success, -1 on failure. Warn and ignore any
- * misformatted lines. */
+ * misformatted lines.
+ *
+ * If <b>extended</b> is set, then treat keys beginning with / and with + as
+ * indicating "clear" and "append" respectively. */
int
-config_get_lines(const char *string, config_line_t **result)
+config_get_lines(const char *string, config_line_t **result, int extended)
{
config_line_t *list = NULL, **next;
char *k, *v;
@@ -1804,13 +1921,30 @@ config_get_lines(const char *string, config_line_t **result)
return -1;
}
if (k && v) {
+ unsigned command = CONFIG_LINE_NORMAL;
+ if (extended) {
+ if (k[0] == '+') {
+ char *k_new = tor_strdup(k+1);
+ tor_free(k);
+ k = k_new;
+ command = CONFIG_LINE_APPEND;
+ } else if (k[0] == '/') {
+ char *k_new = tor_strdup(k+1);
+ tor_free(k);
+ k = k_new;
+ tor_free(v);
+ v = tor_strdup("");
+ command = CONFIG_LINE_CLEAR;
+ }
+ }
/* This list can get long, so we keep a pointer to the end of it
* rather than using config_line_append over and over and getting
* n^2 performance. */
- *next = tor_malloc(sizeof(config_line_t));
+ *next = tor_malloc_zero(sizeof(config_line_t));
(*next)->key = k;
(*next)->value = v;
(*next)->next = NULL;
+ (*next)->command = command;
next = &((*next)->next);
} else {
tor_free(k);
@@ -2038,7 +2172,19 @@ config_assign_value(const config_format_t *fmt, or_options_t *options,
case CONFIG_TYPE_LINELIST:
case CONFIG_TYPE_LINELIST_S:
- config_line_append((config_line_t**)lvalue, c->key, c->value);
+ {
+ config_line_t *lastval = *(config_line_t**)lvalue;
+ if (lastval && lastval->fragile) {
+ if (c->command != CONFIG_LINE_APPEND) {
+ config_free_lines(lastval);
+ *(config_line_t**)lvalue = NULL;
+ } else {
+ lastval->fragile = 0;
+ }
+ }
+
+ config_line_append((config_line_t**)lvalue, c->key, c->value);
+ }
break;
case CONFIG_TYPE_OBSOLETE:
log_warn(LD_CONFIG, "Skipping obsolete configuration option '%s'", c->key);
@@ -2054,6 +2200,28 @@ config_assign_value(const config_format_t *fmt, or_options_t *options,
return 0;
}
+/** Mark every linelist in <b>options<b> "fragile", so that fresh assignments
+ * to it will replace old ones. */
+static void
+config_mark_lists_fragile(const config_format_t *fmt, or_options_t *options)
+{
+ int i;
+ tor_assert(fmt);
+ tor_assert(options);
+
+ for (i = 0; fmt->vars[i].name; ++i) {
+ const config_var_t *var = &fmt->vars[i];
+ config_line_t *list;
+ if (var->type != CONFIG_TYPE_LINELIST &&
+ var->type != CONFIG_TYPE_LINELIST_V)
+ continue;
+
+ list = *(config_line_t **)STRUCT_VAR_P(options, var->var_offset);
+ if (list)
+ list->fragile = 1;
+ }
+}
+
/** If <b>c</b> is a syntactically valid configuration line, update
* <b>options</b> with its value and return 0. Otherwise return -1 for bad
* key, -2 for bad value.
@@ -2096,8 +2264,9 @@ config_assign_line(const config_format_t *fmt, or_options_t *options,
if (!strlen(c->value)) {
/* reset or clear it, then return */
if (!clear_first) {
- if (var->type == CONFIG_TYPE_LINELIST ||
- var->type == CONFIG_TYPE_LINELIST_S) {
+ if ((var->type == CONFIG_TYPE_LINELIST ||
+ var->type == CONFIG_TYPE_LINELIST_S) &&
+ c->command != CONFIG_LINE_CLEAR) {
/* We got an empty linelist from the torrc or command line.
As a special case, call this an error. Warn and ignore. */
log_warn(LD_CONFIG,
@@ -2107,6 +2276,8 @@ config_assign_line(const config_format_t *fmt, or_options_t *options,
}
}
return 0;
+ } else if (c->command == CONFIG_LINE_CLEAR && !clear_first) {
+ option_reset(fmt, options, var, use_defaults);
}
if (options_seen && (var->type != CONFIG_TYPE_LINELIST &&
@@ -2201,7 +2372,7 @@ config_lines_dup(const config_line_t *inp)
config_line_t *result = NULL;
config_line_t **next_out = &result;
while (inp) {
- *next_out = tor_malloc(sizeof(config_line_t));
+ *next_out = tor_malloc_zero(sizeof(config_line_t));
(*next_out)->key = tor_strdup(inp->key);
(*next_out)->value = tor_strdup(inp->value);
inp = inp->next;
@@ -2438,6 +2609,12 @@ config_assign(const config_format_t *fmt, void *options, config_line_t *list,
list = list->next;
}
bitarray_free(options_seen);
+
+ /** Now we're done assigning a group of options to the configuration.
+ * Subsequent group assignments should _replace_ linelists, not extend
+ * them. */
+ config_mark_lists_fragile(fmt, options);
+
return 0;
}
@@ -2880,37 +3057,6 @@ options_init(or_options_t *options)
config_init(&options_format, options);
}
-/* Check if the port number given in <b>port_option</b> in combination with
- * the specified port in <b>listen_options</b> will result in Tor actually
- * opening a low port (meaning a port lower than 1024). Return 1 if
- * it is, or 0 if it isn't or the concept of a low port isn't applicable for
- * the platform we're on. */
-static int
-is_listening_on_low_port(int port_option,
- const config_line_t *listen_options)
-{
-#ifdef MS_WINDOWS
- (void) port_option;
- (void) listen_options;
- return 0; /* No port is too low for windows. */
-#else
- const config_line_t *l;
- uint16_t p;
- if (port_option == 0)
- return 0; /* We're not listening */
- if (listen_options == NULL)
- return (port_option < 1024);
-
- for (l = listen_options; l; l = l->next) {
- addr_port_lookup(LOG_WARN, l->value, NULL, NULL, &p);
- if (p<1024) {
- return 1;
- }
- }
- return 0;
-#endif
-}
-
/** Set all vars in the configuration object <b>options</b> to their default
* values. */
static void
@@ -2933,24 +3079,30 @@ config_init(const config_format_t *fmt, void *options)
* Else, if comment_defaults, write default values as comments.
*/
static char *
-config_dump(const config_format_t *fmt, const void *options, int minimal,
+config_dump(const config_format_t *fmt, const void *default_options,
+ const void *options, int minimal,
int comment_defaults)
{
smartlist_t *elements;
- or_options_t *defaults;
+ const or_options_t *defaults = default_options;
+ void *defaults_tmp = NULL;
config_line_t *line, *assigned;
char *result;
int i;
char *msg = NULL;
- defaults = config_alloc(fmt);
- config_init(fmt, defaults);
+ if (defaults == NULL) {
+ defaults = defaults_tmp = config_alloc(fmt);
+ config_init(fmt, defaults_tmp);
+ }
/* XXX use a 1 here so we don't add a new log line while dumping */
- if (fmt->validate_fn(NULL,defaults, 1, &msg) < 0) {
- log_err(LD_BUG, "Failed to validate default config.");
- tor_free(msg);
- tor_assert(0);
+ if (default_options == NULL) {
+ if (fmt->validate_fn(NULL, defaults_tmp, 1, &msg) < 0) {
+ log_err(LD_BUG, "Failed to validate default config.");
+ tor_free(msg);
+ tor_assert(0);
+ }
}
elements = smartlist_create();
@@ -2992,7 +3144,8 @@ config_dump(const config_format_t *fmt, const void *options, int minimal,
result = smartlist_join_strings(elements, "", 0, NULL);
SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
smartlist_free(elements);
- config_free(fmt, defaults);
+ if (defaults_tmp)
+ config_free(fmt, defaults_tmp);
return result;
}
@@ -3003,7 +3156,8 @@ config_dump(const config_format_t *fmt, const void *options, int minimal,
char *
options_dump(const or_options_t *options, int minimal)
{
- return config_dump(&options_format, options, minimal, 0);
+ return config_dump(&options_format, global_default_options,
+ options, minimal, 0);
}
/** Return 0 if every element of sl is a string holding a decimal
@@ -3128,7 +3282,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
int i;
config_line_t *cl;
const char *uname = get_uname();
- int n_client_ports=0;
+ int n_ports=0;
#define REJECT(arg) \
STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END
#define COMPLAIN(arg) STMT_BEGIN log(LOG_WARN, LD_CONFIG, arg); STMT_END
@@ -3146,13 +3300,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
"for details.", uname);
}
- if (options->ORPort == 0 && options->ORListenAddress != NULL)
- REJECT("ORPort must be defined if ORListenAddress is defined.");
-
- if (options->DirPort == 0 && options->DirListenAddress != NULL)
- REJECT("DirPort must be defined if DirListenAddress is defined.");
-
- if (parse_client_ports(options, 1, msg, &n_client_ports) < 0)
+ if (parse_ports(options, 1, msg, &n_ports) < 0)
return -1;
if (validate_data_directory(options)<0)
@@ -3199,7 +3347,9 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("Can't use a relative path to torrc when RunAsDaemon is set.");
#endif
- if (n_client_ports == 0 && options->ORPort == 0 && !options->RendConfigLines)
+ /* XXXX require that the only port not be DirPort? */
+ /* XXXX require that at least one port be listened-upon. */
+ if (n_ports == 0 && !options->RendConfigLines)
log(LOG_WARN, LD_CONFIG,
"SocksPort, TransPort, NATDPort, DNSPort, and ORPort are all "
"undefined, and there aren't any hidden services configured. "
@@ -3215,19 +3365,6 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("TokenBucketRefillInterval must be between 1 and 1000 inclusive.");
}
- if (options->AccountingMax &&
- (is_listening_on_low_port(options->ORPort, options->ORListenAddress) ||
- is_listening_on_low_port(options->DirPort, options->DirListenAddress)))
- {
- log(LOG_WARN, LD_CONFIG,
- "You have set AccountingMax to use hibernation. You have also "
- "chosen a low DirPort or OrPort. This combination can make Tor stop "
- "working when it tries to re-attach the port after a period of "
- "hibernation. Please choose a different port or turn off "
- "hibernation unless you know this combination will work on your "
- "platform.");
- }
-
if (options->ExcludeExitNodes || options->ExcludeNodes) {
options->_ExcludeExitNodesUnion = routerset_new();
routerset_union(options->_ExcludeExitNodesUnion,options->ExcludeExitNodes);
@@ -3429,6 +3566,10 @@ options_validate(or_options_t *old_options, or_options_t *options,
if (options->UseBridges && options->EntryNodes)
REJECT("You cannot set both UseBridges and EntryNodes.");
+ if (options->EntryNodes && !options->UseEntryGuards)
+ log_warn(LD_CONFIG, "EntryNodes is set, but UseEntryGuards is disabled. "
+ "EntryNodes will be ignored.");
+
options->_AllowInvalid = 0;
if (options->AllowInvalidNodes) {
SMARTLIST_FOREACH(options->AllowInvalidNodes, const char *, cp, {
@@ -3481,7 +3622,8 @@ options_validate(or_options_t *old_options, or_options_t *options,
if (options->BridgeRelay && options->DirPort) {
log_warn(LD_CONFIG, "Can't set a DirPort on a bridge relay; disabling "
"DirPort");
- options->DirPort = 0;
+ config_free_lines(options->DirPort);
+ options->DirPort = NULL;
}
if (options->MinUptimeHidServDirectoryV2 < 0) {
@@ -3502,6 +3644,24 @@ options_validate(or_options_t *old_options, or_options_t *options,
options->RendPostPeriod = MAX_DIR_PERIOD;
}
+ if (options->Tor2webMode && options->LearnCircuitBuildTimeout) {
+ /* LearnCircuitBuildTimeout and Tor2webMode are incompatible in
+ * two ways:
+ *
+ * - LearnCircuitBuildTimeout results in a low CBT, which
+ * Tor2webMode's use of one-hop rendezvous circuits lowers
+ * much further, producing *far* too many timeouts.
+ *
+ * - The adaptive CBT code does not update its timeout estimate
+ * using build times for single-hop circuits.
+ *
+ * If we fix both of these issues someday, we should test
+ * Tor2webMode with LearnCircuitBuildTimeout on again. */
+ log_notice(LD_CONFIG,"Tor2webMode is enabled; turning "
+ "LearnCircuitBuildTimeout off.");
+ options->LearnCircuitBuildTimeout = 0;
+ }
+
if (options->MaxCircuitDirtiness < MIN_MAX_CIRCUIT_DIRTINESS) {
log_warn(LD_CONFIG, "MaxCircuitDirtiness option is too short; "
"raising to %d seconds.", MIN_MAX_CIRCUIT_DIRTINESS);
@@ -3704,39 +3864,6 @@ options_validate(or_options_t *old_options, or_options_t *options,
}
}
- if (options->ControlListenAddress) {
- int all_are_local = 1;
- config_line_t *ln;
- for (ln = options->ControlListenAddress; ln; ln = ln->next) {
- if (strcmpstart(ln->value, "127."))
- all_are_local = 0;
- }
- if (!all_are_local) {
- if (!options->HashedControlPassword &&
- !options->HashedControlSessionPassword &&
- !options->CookieAuthentication) {
- log_warn(LD_CONFIG,
- "You have a ControlListenAddress set to accept "
- "unauthenticated connections from a non-local address. "
- "This means that programs not running on your computer "
- "can reconfigure your Tor, without even having to guess a "
- "password. That's so bad that I'm closing your ControlPort "
- "for you. If you need to control your Tor remotely, try "
- "enabling authentication and using a tool like stunnel or "
- "ssh to encrypt remote access.");
- options->ControlPort = 0;
- } else {
- log_warn(LD_CONFIG, "You have a ControlListenAddress set to accept "
- "connections from a non-local address. This means that "
- "programs not running on your computer can reconfigure your "
- "Tor. That's pretty bad, since the controller "
- "protocol isn't encrypted! Maybe you should just listen on "
- "127.0.0.1 and use a tool like stunnel or ssh to encrypt "
- "remote connections to your control port.");
- }
- }
- }
-
if (options->ControlPort && !options->HashedControlPassword &&
!options->HashedControlSessionPassword &&
!options->CookieAuthentication) {
@@ -3959,8 +4086,9 @@ options_validate(or_options_t *old_options, or_options_t *options,
}
});
- if (options->BridgeRelay == 1 && options->ORPort == 0)
- REJECT("BridgeRelay is 1, ORPort is 0. This is an invalid combination.");
+ if (options->BridgeRelay == 1 && ! options->ORPort)
+ REJECT("BridgeRelay is 1, ORPort is not set. This is an invalid "
+ "combination.");
return 0;
#undef REJECT
@@ -4051,7 +4179,7 @@ options_transition_affects_workers(const or_options_t *old_options,
{
if (!opt_streq(old_options->DataDirectory, new_options->DataDirectory) ||
old_options->NumCPUs != new_options->NumCPUs ||
- old_options->ORPort != new_options->ORPort ||
+ !config_lines_eq(old_options->ORPort, new_options->ORPort) ||
old_options->ServerDNSSearchDomains !=
new_options->ServerDNSSearchDomains ||
old_options->_SafeLogging != new_options->_SafeLogging ||
@@ -4081,9 +4209,10 @@ options_transition_affects_descriptor(const or_options_t *old_options,
!config_lines_eq(old_options->ExitPolicy,new_options->ExitPolicy) ||
old_options->ExitPolicyRejectPrivate !=
new_options->ExitPolicyRejectPrivate ||
- old_options->ORPort != new_options->ORPort ||
- old_options->DirPort != new_options->DirPort ||
+ !config_lines_eq(old_options->ORPort, new_options->ORPort) ||
+ !config_lines_eq(old_options->DirPort, new_options->DirPort) ||
old_options->ClientOnly != new_options->ClientOnly ||
+ old_options->DisableNetwork != new_options->DisableNetwork ||
old_options->_PublishServerDescriptor !=
new_options->_PublishServerDescriptor ||
get_effective_bwrate(old_options) != get_effective_bwrate(new_options) ||
@@ -4157,17 +4286,25 @@ get_windows_conf_root(void)
}
#endif
-/** Return the default location for our torrc file. */
+/** Return the default location for our torrc file.
+ * DOCDOC defaults_file */
static const char *
-get_default_conf_file(void)
+get_default_conf_file(int defaults_file)
{
#ifdef MS_WINDOWS
- static char path[MAX_PATH+1];
- strlcpy(path, get_windows_conf_root(), MAX_PATH);
- strlcat(path,"\\torrc",MAX_PATH);
- return path;
+ if (defaults_file) {
+ static char defaults_path[MAX_PATH+1];
+ tor_snprintf(defaults_path, MAX_PATH, "%s\\torrc-defaults",
+ get_windows_conf_root());
+ return defaults_path;
+ } else {
+ static char path[MAX_PATH+1];
+ tor_snprintf(path, MAX_PATH, "%s\\torrc",
+ get_windows_conf_root());
+ return path;
+ }
#else
- return (CONFDIR "/torrc");
+ return defaults_file ? CONFDIR "/torrc-defaults" : CONFDIR "/torrc";
#endif
}
@@ -4200,37 +4337,54 @@ check_nickname_list(const char *lst, const char *name, char **msg)
return r;
}
-/** Learn config file name from command line arguments, or use the default */
+/** Learn config file name from command line arguments, or use the default,
+ * DOCDOC defaults_file */
static char *
find_torrc_filename(int argc, char **argv,
+ int defaults_file,
int *using_default_torrc, int *ignore_missing_torrc)
{
char *fname=NULL;
int i;
+ const char *fname_opt = defaults_file ? "--defaults-torrc" : "-f";
+ const char *ignore_opt = defaults_file ? NULL : "--ignore-missing-torrc";
+
+ if (defaults_file)
+ *ignore_missing_torrc = 1;
for (i = 1; i < argc; ++i) {
- if (i < argc-1 && !strcmp(argv[i],"-f")) {
+ if (i < argc-1 && !strcmp(argv[i],fname_opt)) {
if (fname) {
- log(LOG_WARN, LD_CONFIG, "Duplicate -f options on command line.");
+ log(LOG_WARN, LD_CONFIG, "Duplicate %s options on command line.",
+ fname_opt);
tor_free(fname);
}
fname = expand_filename(argv[i+1]);
+
+ {
+ char *absfname;
+ absfname = make_path_absolute(fname);
+ tor_free(fname);
+ fname = absfname;
+ }
+
*using_default_torrc = 0;
++i;
- } else if (!strcmp(argv[i],"--ignore-missing-torrc")) {
+ } else if (ignore_opt && !strcmp(argv[i],ignore_opt)) {
*ignore_missing_torrc = 1;
}
}
if (*using_default_torrc) {
/* didn't find one, try CONFDIR */
- const char *dflt = get_default_conf_file();
+ const char *dflt = get_default_conf_file(defaults_file);
if (dflt && file_status(dflt) == FN_FILE) {
fname = tor_strdup(dflt);
} else {
#ifndef MS_WINDOWS
- char *fn;
- fn = expand_filename("~/.torrc");
+ char *fn = NULL;
+ if (!defaults_file)
+ fn = expand_filename("~/.torrc");
if (fn && file_status(fn) == FN_FILE) {
fname = fn;
} else {
@@ -4245,31 +4399,34 @@ find_torrc_filename(int argc, char **argv,
return fname;
}
-/** Load torrc from disk, setting torrc_fname if successful */
+/** Load torrc from disk, setting torrc_fname if successful.
+ * DOCDOC defaults_file */
static char *
-load_torrc_from_disk(int argc, char **argv)
+load_torrc_from_disk(int argc, char **argv, int defaults_file)
{
char *fname=NULL;
char *cf = NULL;
int using_default_torrc = 1;
int ignore_missing_torrc = 0;
+ char **fname_var = defaults_file ? &torrc_defaults_fname : &torrc_fname;
- fname = find_torrc_filename(argc, argv,
+ fname = find_torrc_filename(argc, argv, defaults_file,
&using_default_torrc, &ignore_missing_torrc);
tor_assert(fname);
log(LOG_DEBUG, LD_CONFIG, "Opening config file \"%s\"", fname);
- tor_free(torrc_fname);
- torrc_fname = fname;
+ tor_free(*fname_var);
+ *fname_var = fname;
/* Open config file */
if (file_status(fname) != FN_FILE ||
!(cf = read_file_to_str(fname,0,NULL))) {
- if (using_default_torrc == 1 || ignore_missing_torrc ) {
- log(LOG_NOTICE, LD_CONFIG, "Configuration file \"%s\" not present, "
- "using reasonable defaults.", fname);
+ if (using_default_torrc == 1 || ignore_missing_torrc) {
+ if (!defaults_file)
+ log(LOG_NOTICE, LD_CONFIG, "Configuration file \"%s\" not present, "
+ "using reasonable defaults.", fname);
tor_free(fname); /* sets fname to NULL */
- torrc_fname = NULL;
+ *fname_var = NULL;
cf = tor_strdup("");
} else {
log(LOG_WARN, LD_CONFIG,
@@ -4283,7 +4440,7 @@ load_torrc_from_disk(int argc, char **argv)
return cf;
err:
tor_free(fname);
- torrc_fname = NULL;
+ *fname_var = NULL;
return NULL;
}
@@ -4294,8 +4451,9 @@ load_torrc_from_disk(int argc, char **argv)
int
options_init_from_torrc(int argc, char **argv)
{
- char *cf=NULL;
- int i, retval, command;
+ char *cf=NULL, *cf_defaults=NULL;
+ int i, command;
+ int retval = -1;
static char **backup_argv;
static int backup_argc;
char *command_arg = NULL;
@@ -4354,24 +4512,24 @@ options_init_from_torrc(int argc, char **argv)
if (command == CMD_HASH_PASSWORD) {
cf = tor_strdup("");
} else {
- cf = load_torrc_from_disk(argc, argv);
+ cf_defaults = load_torrc_from_disk(argc, argv, 1);
+ cf = load_torrc_from_disk(argc, argv, 0);
if (!cf)
goto err;
}
- retval = options_init_from_string(cf, command, command_arg, &errmsg);
- tor_free(cf);
- if (retval < 0)
- goto err;
-
- return 0;
+ retval = options_init_from_string(cf_defaults, cf, command, command_arg,
+ &errmsg);
err:
+
+ tor_free(cf);
+ tor_free(cf_defaults);
if (errmsg) {
log(LOG_WARN,LD_CONFIG,"%s", errmsg);
tor_free(errmsg);
}
- return -1;
+ return retval < 0 ? -1 : 0;
}
/** Load the options from the configuration in <b>cf</b>, validate
@@ -4384,13 +4542,13 @@ options_init_from_torrc(int argc, char **argv)
* * -4 for error while setting the new options
*/
setopt_err_t
-options_init_from_string(const char *cf,
+options_init_from_string(const char *cf_defaults, const char *cf,
int command, const char *command_arg,
char **msg)
{
- or_options_t *oldoptions, *newoptions;
+ or_options_t *oldoptions, *newoptions, *newdefaultoptions=NULL;
config_line_t *cl;
- int retval;
+ int retval, i;
setopt_err_t err = SETOPT_ERR_MISC;
tor_assert(msg);
@@ -4403,17 +4561,24 @@ options_init_from_string(const char *cf,
newoptions->command = command;
newoptions->command_arg = command_arg;
- /* get config lines, assign them */
- retval = config_get_lines(cf, &cl);
- if (retval < 0) {
- err = SETOPT_ERR_PARSE;
- goto err;
- }
- retval = config_assign(&options_format, newoptions, cl, 0, 0, msg);
- config_free_lines(cl);
- if (retval < 0) {
- err = SETOPT_ERR_PARSE;
- goto err;
+ for (i = 0; i < 2; ++i) {
+ const char *body = i==0 ? cf_defaults : cf;
+ if (!body)
+ continue;
+ /* get config lines, assign them */
+ retval = config_get_lines(body, &cl, 1);
+ if (retval < 0) {
+ err = SETOPT_ERR_PARSE;
+ goto err;
+ }
+ retval = config_assign(&options_format, newoptions, cl, 0, 0, msg);
+ config_free_lines(cl);
+ if (retval < 0) {
+ err = SETOPT_ERR_PARSE;
+ goto err;
+ }
+ if (i==0)
+ newdefaultoptions = options_dup(&options_format, newoptions);
}
/* Go through command-line variables too */
@@ -4448,6 +4613,8 @@ options_init_from_string(const char *cf,
/* Clear newoptions and re-initialize them with new defaults. */
config_free(&options_format, newoptions);
+ config_free(&options_format, newdefaultoptions);
+ newdefaultoptions = NULL;
newoptions = tor_malloc_zero(sizeof(or_options_t));
newoptions->_magic = OR_OPTIONS_MAGIC;
options_init(newoptions);
@@ -4455,22 +4622,24 @@ options_init_from_string(const char *cf,
newoptions->command_arg = command_arg;
/* Assign all options a second time. */
- retval = config_get_lines(cf, &cl);
- if (retval < 0) {
- err = SETOPT_ERR_PARSE;
- goto err;
- }
- retval = config_assign(&options_format, newoptions, cl, 0, 0, msg);
- config_free_lines(cl);
- if (retval < 0) {
- err = SETOPT_ERR_PARSE;
- goto err;
- }
- retval = config_assign(&options_format, newoptions,
- global_cmdline_options, 0, 0, msg);
- if (retval < 0) {
- err = SETOPT_ERR_PARSE;
- goto err;
+ for (i = 0; i < 2; ++i) {
+ const char *body = i==0 ? cf_defaults : cf;
+ if (!body)
+ continue;
+ /* get config lines, assign them */
+ retval = config_get_lines(body, &cl, 1);
+ if (retval < 0) {
+ err = SETOPT_ERR_PARSE;
+ goto err;
+ }
+ retval = config_assign(&options_format, newoptions, cl, 0, 0, msg);
+ config_free_lines(cl);
+ if (retval < 0) {
+ err = SETOPT_ERR_PARSE;
+ goto err;
+ }
+ if (i==0)
+ newdefaultoptions = options_dup(&options_format, newoptions);
}
}
@@ -4489,11 +4658,14 @@ options_init_from_string(const char *cf,
err = SETOPT_ERR_SETTING;
goto err; /* frees and replaces old options */
}
+ config_free(&options_format, global_default_options);
+ global_default_options = newdefaultoptions;
return SETOPT_OK;
err:
config_free(&options_format, newoptions);
+ config_free(&options_format, newdefaultoptions);
if (*msg) {
char *old_msg = *msg;
tor_asprintf(msg, "Failed to parse/validate config: %s", old_msg);
@@ -4505,12 +4677,14 @@ options_init_from_string(const char *cf,
/** Return the location for our configuration file.
*/
const char *
-get_torrc_fname(void)
+get_torrc_fname(int defaults_fname)
{
- if (torrc_fname)
- return torrc_fname;
+ const char *fname = defaults_fname ? torrc_defaults_fname : torrc_fname;
+
+ if (fname)
+ return fname;
else
- return get_default_conf_file();
+ return get_default_conf_file(defaults_fname);
}
/** Adjust the address map based on the MapAddress elements in the
@@ -4526,24 +4700,60 @@ config_register_addressmaps(const or_options_t *options)
addressmap_clear_configured();
elts = smartlist_create();
for (opt = options->AddressMap; opt; opt = opt->next) {
+ int from_wildcard = 0, to_wildcard = 0;
smartlist_split_string(elts, opt->value, NULL,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2);
- if (smartlist_len(elts) >= 2) {
- from = smartlist_get(elts,0);
- to = smartlist_get(elts,1);
- if (address_is_invalid_destination(to, 1)) {
- log_warn(LD_CONFIG,
- "Skipping invalid argument '%s' to MapAddress", to);
- } else {
- addressmap_register(from, tor_strdup(to), 0, ADDRMAPSRC_TORRC);
- if (smartlist_len(elts)>2) {
- log_warn(LD_CONFIG,"Ignoring extra arguments to MapAddress.");
- }
- }
- } else {
+ if (smartlist_len(elts) < 2) {
log_warn(LD_CONFIG,"MapAddress '%s' has too few arguments. Ignoring.",
opt->value);
+ goto cleanup;
}
+
+ from = smartlist_get(elts,0);
+ to = smartlist_get(elts,1);
+
+ if (to[0] == '.' || from[0] == '.') {
+ log_warn(LD_CONFIG,"MapAddress '%s' is ambiguous - address starts with a"
+ "'.'. Ignoring.",opt->value);
+ goto cleanup;
+ }
+
+ if (!strcmp(to, "*") || !strcmp(from, "*")) {
+ log_warn(LD_CONFIG,"MapAddress '%s' is unsupported - can't remap from "
+ "or to *. Ignoring.",opt->value);
+ goto cleanup;
+ }
+ /* Detect asterisks in expressions of type: '*.example.com' */
+ if (!strncmp(from,"*.",2)) {
+ from += 2;
+ from_wildcard = 1;
+ }
+ if (!strncmp(to,"*.",2)) {
+ to += 2;
+ to_wildcard = 1;
+ }
+
+ if (to_wildcard && !from_wildcard) {
+ log_warn(LD_CONFIG,
+ "Skipping invalid argument '%s' to MapAddress: "
+ "can only use wildcard (i.e. '*.') if 'from' address "
+ "uses wildcard also", opt->value);
+ goto cleanup;
+ }
+
+ if (address_is_invalid_destination(to, 1)) {
+ log_warn(LD_CONFIG,
+ "Skipping invalid argument '%s' to MapAddress", opt->value);
+ goto cleanup;
+ }
+
+ addressmap_register(from, tor_strdup(to), 0, ADDRMAPSRC_TORRC,
+ from_wildcard, to_wildcard);
+
+ if (smartlist_len(elts) > 2)
+ log_warn(LD_CONFIG,"Ignoring extra arguments to MapAddress.");
+
+ cleanup:
SMARTLIST_FOREACH(elts, char*, cp, tor_free(cp));
smartlist_clear(elts);
}
@@ -5115,7 +5325,7 @@ parse_dir_server_line(const char *line, dirinfo_type_t required_type,
* clause once Tor 0.1.2.17 is obsolete. */
log_warn(LD_CONFIG, "Dangerous dirserver line. To correct, erase your "
"torrc file (%s), or reinstall Tor and use the default torrc.",
- get_torrc_fname());
+ get_torrc_fname(0));
goto err;
}
if (base16_decode(digest, DIGEST_LEN, fingerprint, HEX_DIGEST_LEN)<0) {
@@ -5179,12 +5389,53 @@ warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname)
} SMARTLIST_FOREACH_END(port);
}
-#define CL_PORT_NO_OPTIONS (1u<<0)
+/** DOCDOC */
+static void
+warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid)
+{
+ int warned = 0;
+ SMARTLIST_FOREACH_BEGIN(ports, port_cfg_t *, port) {
+ if (port->type != CONN_TYPE_CONTROL_LISTENER)
+ continue;
+ if (port->is_unix_addr)
+ continue;
+ if (!tor_addr_is_loopback(&port->addr)) {
+ if (forbid) {
+ if (!warned)
+ log_warn(LD_CONFIG,
+ "You have a ControlPort set to accept "
+ "unauthenticated connections from a non-local address. "
+ "This means that programs not running on your computer "
+ "can reconfigure your Tor, without even having to guess a "
+ "password. That's so bad that I'm closing your ControlPort "
+ "for you. If you need to control your Tor remotely, try "
+ "enabling authentication and using a tool like stunnel or "
+ "ssh to encrypt remote access.");
+ warned = 1;
+ port_cfg_free(port);
+ SMARTLIST_DEL_CURRENT(ports, port);
+ } else {
+ log_warn(LD_CONFIG, "You have a ControlPort set to accept "
+ "connections from a non-local address. This means that "
+ "programs not running on your computer can reconfigure your "
+ "Tor. That's pretty bad, since the controller "
+ "protocol isn't encrypted! Maybe you should just listen on "
+ "127.0.0.1 and use a tool like stunnel or ssh to encrypt "
+ "remote connections to your control port.");
+ return; /* No point in checking the rest */
+ }
+ }
+ } SMARTLIST_FOREACH_END(port);
+}
+
+#define CL_PORT_NO_OPTIONS (1u<<0)
#define CL_PORT_WARN_NONLOCAL (1u<<1)
#define CL_PORT_ALLOW_EXTRA_LISTENADDR (1u<<2)
+#define CL_PORT_SERVER_OPTIONS (1u<<3)
+#define CL_PORT_FORBID_NONLOCAL (1u<<4)
/**
- * Parse port configuration for a single client port type.
+ * Parse port configuration for a single port type.
*
* Read entries of the "FooPort" type from the list <b>ports</b>, and
* entries of the "FooListenAddress" type from the list
@@ -5204,17 +5455,22 @@ warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname)
* isolation options in the FooPort entries.
*
* If CL_PORT_WARN_NONLOCAL is set in <b>flags</b>, warn if any of the
- * ports are not on a local address.
+ * ports are not on a local address. If CL_PORT_FORBID_NONLOCAL is set,
+ * this is a contrl port with no password set: don't even allow it.
*
* Unless CL_PORT_ALLOW_EXTRA_LISTENADDR is set in <b>flags</b>, warn
* if FooListenAddress is set but FooPort is 0.
*
+ * If CL_PORT_SERVER_OPTIONS is set in <b>flags</b>, do not allow stream
+ * isolation options in the FooPort entries; instead allow the
+ * server-port option set.
+ *
* On success, if <b>out</b> is given, add a new port_cfg_t entry to
* <b>out</b> for every port that the client should listen on. Return 0
* on success, -1 on failure.
*/
static int
-parse_client_port_config(smartlist_t *out,
+parse_port_config(smartlist_t *out,
const config_line_t *ports,
const config_line_t *listenaddrs,
const char *portname,
@@ -5225,8 +5481,11 @@ parse_client_port_config(smartlist_t *out,
{
smartlist_t *elts;
int retval = -1;
- const unsigned allow_client_options = !(flags & CL_PORT_NO_OPTIONS);
+ const unsigned is_control = (listener_type == CONN_TYPE_CONTROL_LISTENER);
+ const unsigned allow_no_options = flags & CL_PORT_NO_OPTIONS;
+ const unsigned use_server_options = flags & CL_PORT_SERVER_OPTIONS;
const unsigned warn_nonlocal = flags & CL_PORT_WARN_NONLOCAL;
+ const unsigned forbid_nonlocal = flags & CL_PORT_FORBID_NONLOCAL;
const unsigned allow_spurious_listenaddr =
flags & CL_PORT_ALLOW_EXTRA_LISTENADDR;
@@ -5262,6 +5521,17 @@ parse_client_port_config(smartlist_t *out,
return -1;
}
+ if (use_server_options && out) {
+ /* Add a no_listen port. */
+ port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t));
+ cfg->type = listener_type;
+ cfg->port = mainport;
+ tor_addr_make_unspec(&cfg->addr); /* Server ports default to 0.0.0.0 */
+ cfg->no_listen = 1;
+ cfg->ipv4_only = 1;
+ smartlist_add(out, cfg);
+ }
+
for (; listenaddrs; listenaddrs = listenaddrs->next) {
tor_addr_t addr;
uint16_t port = 0;
@@ -5277,12 +5547,17 @@ parse_client_port_config(smartlist_t *out,
tor_addr_copy(&cfg->addr, &addr);
cfg->session_group = SESSION_GROUP_UNSET;
cfg->isolation_flags = ISO_DEFAULT;
+ cfg->no_advertise = 1;
smartlist_add(out, cfg);
}
}
- if (warn_nonlocal && out)
- warn_nonlocal_client_ports(out, portname);
+ if (warn_nonlocal && out) {
+ if (is_control)
+ warn_nonlocal_controller_ports(out, forbid_nonlocal);
+ else
+ warn_nonlocal_client_ports(out, portname);
+ }
return 0;
} /* end if (listenaddrs) */
@@ -5314,6 +5589,8 @@ parse_client_port_config(smartlist_t *out,
char *addrport;
uint16_t ptmp=0;
int ok;
+ int no_listen = 0, no_advertise = 0, all_addrs = 0,
+ ipv4_only = 0, ipv6_only = 0;
smartlist_split_string(elts, ports->value, NULL,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
@@ -5322,7 +5599,7 @@ parse_client_port_config(smartlist_t *out,
goto err;
}
- if (!allow_client_options && smartlist_len(elts) > 1) {
+ if (allow_no_options && smartlist_len(elts) > 1) {
log_warn(LD_CONFIG, "Too many options on %sPort line", portname);
goto err;
}
@@ -5361,56 +5638,107 @@ parse_client_port_config(smartlist_t *out,
}
/* Now parse the rest of the options, if any. */
- SMARTLIST_FOREACH_BEGIN(elts, char *, elt) {
- int no = 0, isoflag = 0;
- const char *elt_orig = elt;
- if (elt_sl_idx == 0)
- continue; /* Skip addr:port */
- if (!strcasecmpstart(elt, "SessionGroup=")) {
- int group = (int)tor_parse_long(elt+strlen("SessionGroup="),
- 10, 0, INT_MAX, &ok, NULL);
- if (!ok) {
- log_warn(LD_CONFIG, "Invalid %sPort option '%s'",
+ if (use_server_options) {
+ /* This is a server port; parse advertising options */
+ SMARTLIST_FOREACH_BEGIN(elts, char *, elt) {
+ if (elt_sl_idx == 0)
+ continue; /* Skip addr:port */
+
+ if (!strcasecmp(elt, "NoAdvertise")) {
+ no_advertise = 1;
+ } else if (!strcasecmp(elt, "NoListen")) {
+ no_listen = 1;
+#if 0
+ /* not implemented yet. */
+ } else if (!strcasecmp(elt, "AllAddrs")) {
+
+ all_addrs = 1;
+#endif
+ } else if (!strcasecmp(elt, "IPv4Only")) {
+ ipv4_only = 1;
+ } else if (!strcasecmp(elt, "IPv6Only")) {
+ ipv6_only = 1;
+ } else {
+ log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'",
portname, escaped(elt));
- goto err;
}
- if (sessiongroup >= 0) {
- log_warn(LD_CONFIG, "Multiple SessionGroup options on %sPort",
- portname);
- goto err;
- }
- sessiongroup = group;
- continue;
- }
+ } SMARTLIST_FOREACH_END(elt);
- if (!strcasecmpstart(elt, "No")) {
- no = 1;
- elt += 2;
+ if (no_advertise && no_listen) {
+ log_warn(LD_CONFIG, "Tried to set both NoListen and NoAdvertise "
+ "on %sPort line '%s'",
+ portname, escaped(ports->value));
+ goto err;
}
- if (!strcasecmpend(elt, "s"))
- elt[strlen(elt)-1] = '\0'; /* kill plurals. */
-
- if (!strcasecmp(elt, "IsolateDestPort")) {
- isoflag = ISO_DESTPORT;
- } else if (!strcasecmp(elt, "IsolateDestAddr")) {
- isoflag = ISO_DESTADDR;
- } else if (!strcasecmp(elt, "IsolateSOCKSAuth")) {
- isoflag = ISO_SOCKSAUTH;
- } else if (!strcasecmp(elt, "IsolateClientProtocol")) {
- isoflag = ISO_CLIENTPROTO;
- } else if (!strcasecmp(elt, "IsolateClientAddr")) {
- isoflag = ISO_CLIENTADDR;
- } else {
- log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'",
- portname, escaped(elt_orig));
+ if (ipv4_only && ipv6_only) {
+ log_warn(LD_CONFIG, "Tried to set both IPv4Only and IPv6Only "
+ "on %sPort line '%s'",
+ portname, escaped(ports->value));
+ goto err;
}
-
- if (no) {
- isolation &= ~isoflag;
- } else {
- isolation |= isoflag;
+ if (ipv4_only && tor_addr_family(&addr) == AF_INET6) {
+ log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv6",
+ portname);
+ goto err;
}
- } SMARTLIST_FOREACH_END(elt);
+ if (ipv6_only && tor_addr_family(&addr) == AF_INET) {
+ log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv4",
+ portname);
+ goto err;
+ }
+ } else {
+ /* This is a client port; parse isolation options */
+ SMARTLIST_FOREACH_BEGIN(elts, char *, elt) {
+ int no = 0, isoflag = 0;
+ const char *elt_orig = elt;
+ if (elt_sl_idx == 0)
+ continue; /* Skip addr:port */
+ if (!strcasecmpstart(elt, "SessionGroup=")) {
+ int group = (int)tor_parse_long(elt+strlen("SessionGroup="),
+ 10, 0, INT_MAX, &ok, NULL);
+ if (!ok) {
+ log_warn(LD_CONFIG, "Invalid %sPort option '%s'",
+ portname, escaped(elt));
+ goto err;
+ }
+ if (sessiongroup >= 0) {
+ log_warn(LD_CONFIG, "Multiple SessionGroup options on %sPort",
+ portname);
+ goto err;
+ }
+ sessiongroup = group;
+ continue;
+ }
+
+ if (!strcasecmpstart(elt, "No")) {
+ no = 1;
+ elt += 2;
+ }
+ if (!strcasecmpend(elt, "s"))
+ elt[strlen(elt)-1] = '\0'; /* kill plurals. */
+
+ if (!strcasecmp(elt, "IsolateDestPort")) {
+ isoflag = ISO_DESTPORT;
+ } else if (!strcasecmp(elt, "IsolateDestAddr")) {
+ isoflag = ISO_DESTADDR;
+ } else if (!strcasecmp(elt, "IsolateSOCKSAuth")) {
+ isoflag = ISO_SOCKSAUTH;
+ } else if (!strcasecmp(elt, "IsolateClientProtocol")) {
+ isoflag = ISO_CLIENTPROTO;
+ } else if (!strcasecmp(elt, "IsolateClientAddr")) {
+ isoflag = ISO_CLIENTADDR;
+ } else {
+ log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'",
+ portname, escaped(elt_orig));
+ }
+
+ if (no) {
+ isolation &= ~isoflag;
+ } else {
+ isolation |= isoflag;
+ }
+ } SMARTLIST_FOREACH_END(elt);
+ }
if (out && port) {
port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t));
@@ -5419,14 +5747,24 @@ parse_client_port_config(smartlist_t *out,
tor_addr_copy(&cfg->addr, &addr);
cfg->session_group = sessiongroup;
cfg->isolation_flags = isolation;
+ cfg->no_listen = no_listen;
+ cfg->no_listen = no_advertise;
+ cfg->all_addrs = all_addrs;
+ cfg->ipv4_only = ipv4_only;
+ cfg->ipv6_only = ipv6_only;
+
smartlist_add(out, cfg);
}
SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp));
smartlist_clear(elts);
}
- if (warn_nonlocal && out)
- warn_nonlocal_client_ports(out, portname);
+ if (warn_nonlocal && out) {
+ if (is_control)
+ warn_nonlocal_controller_ports(out, forbid_nonlocal);
+ else
+ warn_nonlocal_client_ports(out, portname);
+ }
retval = 0;
err:
@@ -5435,6 +5773,27 @@ parse_client_port_config(smartlist_t *out,
return retval;
}
+/** DOCDOC */
+static int
+parse_socket_config(smartlist_t *out, const config_line_t *cfg,
+ int listener_type)
+{
+
+ if (!out)
+ return 0;
+
+ for ( ; cfg; cfg = cfg->next) {
+ size_t len = strlen(cfg->value);
+ port_cfg_t *port = tor_malloc_zero(sizeof(port_cfg_t) + len + 1);
+ port->is_unix_addr = 1;
+ memcpy(port->unix_addr, cfg->value, len+1);
+ port->type = listener_type;
+ smartlist_add(out, port);
+ }
+
+ return 0;
+}
+
/** Parse all client port types (Socks, DNS, Trans, NATD) from
* <b>options</b>. On success, set *<b>n_ports_out</b> to the number of
* ports that are listed and return 0. On failure, set *<b>msg</b> to a
@@ -5444,8 +5803,8 @@ parse_client_port_config(smartlist_t *out,
* new list of ports parsed from <b>options</b>.
**/
static int
-parse_client_ports(const or_options_t *options, int validate_only,
- char **msg, int *n_ports_out)
+parse_ports(const or_options_t *options, int validate_only,
+ char **msg, int *n_ports_out)
{
smartlist_t *ports;
int retval = -1;
@@ -5454,7 +5813,7 @@ parse_client_ports(const or_options_t *options, int validate_only,
*n_ports_out = 0;
- if (parse_client_port_config(ports,
+ if (parse_port_config(ports,
options->SocksPort, options->SocksListenAddress,
"Socks", CONN_TYPE_AP_LISTENER,
"127.0.0.1", 9050,
@@ -5462,7 +5821,7 @@ parse_client_ports(const or_options_t *options, int validate_only,
*msg = tor_strdup("Invalid SocksPort/SocksListenAddress configuration");
goto err;
}
- if (parse_client_port_config(ports,
+ if (parse_port_config(ports,
options->DNSPort, options->DNSListenAddress,
"DNS", CONN_TYPE_AP_DNS_LISTENER,
"127.0.0.1", 0,
@@ -5470,7 +5829,7 @@ parse_client_ports(const or_options_t *options, int validate_only,
*msg = tor_strdup("Invalid DNSPort/DNSListenAddress configuration");
goto err;
}
- if (parse_client_port_config(ports,
+ if (parse_port_config(ports,
options->TransPort, options->TransListenAddress,
"Trans", CONN_TYPE_AP_TRANS_LISTENER,
"127.0.0.1", 0,
@@ -5478,7 +5837,7 @@ parse_client_ports(const or_options_t *options, int validate_only,
*msg = tor_strdup("Invalid TransPort/TransListenAddress configuration");
goto err;
}
- if (parse_client_port_config(ports,
+ if (parse_port_config(ports,
options->NATDPort, options->NATDListenAddress,
"NATD", CONN_TYPE_AP_NATD_LISTENER,
"127.0.0.1", 0,
@@ -5486,16 +5845,63 @@ parse_client_ports(const or_options_t *options, int validate_only,
*msg = tor_strdup("Invalid NatdPort/NatdListenAddress configuration");
goto err;
}
+ {
+ unsigned control_port_flags = CL_PORT_NO_OPTIONS | CL_PORT_WARN_NONLOCAL;
+ const int any_passwords = (options->HashedControlPassword ||
+ options->HashedControlSessionPassword ||
+ options->CookieAuthentication);
+ if (! any_passwords)
+ control_port_flags |= CL_PORT_FORBID_NONLOCAL;
+
+ if (parse_port_config(ports,
+ options->ControlPort, options->ControlListenAddress,
+ "Control", CONN_TYPE_CONTROL_LISTENER,
+ "127.0.0.1", 0,
+ control_port_flags) < 0) {
+ *msg = tor_strdup("Invalid ControlPort/ControlListenAddress "
+ "configuration");
+ goto err;
+ }
+ if (parse_socket_config(ports,
+ options->ControlSocket,
+ CONN_TYPE_CONTROL_LISTENER) < 0) {
+ *msg = tor_strdup("Invalid ControlSocket configuration");
+ goto err;
+ }
+ }
+ if (! options->ClientOnly) {
+ if (parse_port_config(ports,
+ options->ORPort, options->ORListenAddress,
+ "OR", CONN_TYPE_OR_LISTENER,
+ "0.0.0.0", 0,
+ CL_PORT_SERVER_OPTIONS) < 0) {
+ *msg = tor_strdup("Invalid ORPort/ORListenAddress configuration");
+ goto err;
+ }
+ if (parse_port_config(ports,
+ options->DirPort, options->DirListenAddress,
+ "Dir", CONN_TYPE_DIR_LISTENER,
+ "0.0.0.0", 0,
+ CL_PORT_SERVER_OPTIONS) < 0) {
+ *msg = tor_strdup("Invalid DirPort/DirListenAddress configuration");
+ goto err;
+ }
+ }
+
+ if (check_server_ports(ports, options) < 0) {
+ *msg = tor_strdup("Misconfigured server ports");
+ goto err;
+ }
*n_ports_out = smartlist_len(ports);
if (!validate_only) {
- if (configured_client_ports) {
- SMARTLIST_FOREACH(configured_client_ports,
+ if (configured_ports) {
+ SMARTLIST_FOREACH(configured_ports,
port_cfg_t *, p, port_cfg_free(p));
- smartlist_free(configured_client_ports);
+ smartlist_free(configured_ports);
}
- configured_client_ports = ports;
+ configured_ports = ports;
ports = NULL; /* prevent free below. */
}
@@ -5508,14 +5914,107 @@ parse_client_ports(const or_options_t *options, int validate_only,
return retval;
}
+/** DOCDOC */
+static int
+check_server_ports(const smartlist_t *ports,
+ const or_options_t *options)
+{
+ int n_orport_advertised = 0;
+ int n_orport_advertised_ipv4 = 0;
+ int n_orport_listeners = 0;
+ int n_dirport_advertised = 0;
+ int n_dirport_listeners = 0;
+ int n_low_port = 0;
+ int r = 0;
+
+ SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) {
+ if (port->type == CONN_TYPE_DIR_LISTENER) {
+ if (! port->no_advertise)
+ ++n_dirport_advertised;
+ if (! port->no_listen)
+ ++n_dirport_listeners;
+ } else if (port->type == CONN_TYPE_OR_LISTENER) {
+ if (! port->no_advertise) {
+ ++n_orport_advertised;
+ if (tor_addr_family(&port->addr) == AF_INET ||
+ (tor_addr_family(&port->addr) == AF_UNSPEC && !port->ipv6_only))
+ ++n_orport_advertised_ipv4;
+ }
+ if (! port->no_listen)
+ ++n_orport_listeners;
+ } else {
+ continue;
+ }
+#ifndef MS_WINDOWS
+ if (!port->no_advertise && port->port < 1024)
+ ++n_low_port;
+#endif
+ } SMARTLIST_FOREACH_END(port);
+
+ if (n_orport_advertised && !n_orport_listeners) {
+ log_warn(LD_CONFIG, "We are advertising an ORPort, but not actually "
+ "listening on one.");
+ r = -1;
+ }
+ if (n_dirport_advertised && !n_dirport_listeners) {
+ log_warn(LD_CONFIG, "We are advertising a DirPort, but not actually "
+ "listening on one.");
+ r = -1;
+ }
+ if (n_dirport_advertised > 1) {
+ log_warn(LD_CONFIG, "Can't advertise more than one DirPort.");
+ r = -1;
+ }
+ if (n_orport_advertised && !n_orport_advertised_ipv4 &&
+ !options->BridgeRelay) {
+ log_warn(LD_CONFIG, "Configured non-bridge only to listen on an IPv6 "
+ "address.");
+ r = -1;
+ }
+
+ if (n_low_port && options->AccountingMax) {
+ log(LOG_WARN, LD_CONFIG,
+ "You have set AccountingMax to use hibernation. You have also "
+ "chosen a low DirPort or OrPort. This combination can make Tor stop "
+ "working when it tries to re-attach the port after a period of "
+ "hibernation. Please choose a different port or turn off "
+ "hibernation unless you know this combination will work on your "
+ "platform.");
+ }
+
+ return r;
+}
+
/** Return a list of port_cfg_t for client ports parsed from the
* options. */
const smartlist_t *
-get_configured_client_ports(void)
+get_configured_ports(void)
{
- if (!configured_client_ports)
- configured_client_ports = smartlist_create();
- return configured_client_ports;
+ if (!configured_ports)
+ configured_ports = smartlist_create();
+ return configured_ports;
+}
+
+/** Return the first advertised port of type <b>listener_type</b> in
+ <b>address_family</b>. */
+int
+get_first_advertised_port_by_type_af(int listener_type, int address_family)
+{
+ if (!configured_ports)
+ return 0;
+ SMARTLIST_FOREACH_BEGIN(configured_ports, const port_cfg_t *, cfg) {
+ if (cfg->type == listener_type &&
+ !cfg->no_advertise &&
+ (tor_addr_family(&cfg->addr) == address_family ||
+ tor_addr_family(&cfg->addr) == AF_UNSPEC)) {
+ if (tor_addr_family(&cfg->addr) != AF_UNSPEC ||
+ (address_family == AF_INET && !cfg->ipv6_only) ||
+ (address_family == AF_INET6 && !cfg->ipv4_only)) {
+ return cfg->port;
+ }
+ }
+ } SMARTLIST_FOREACH_END(cfg);
+ return 0;
}
/** Adjust the value of options->DataDirectory, or fill it in if it's
@@ -5674,7 +6173,7 @@ options_save_current(void)
* If we try falling back to datadirectory or something, we have a better
* chance of saving the configuration, but a better chance of doing
* something the user never expected. */
- return write_configuration_file(get_torrc_fname(), get_options());
+ return write_configuration_file(get_torrc_fname(0), get_options());
}
/** Mapping from a unit name to a multiplier for converting that unit into a
@@ -5893,7 +6392,7 @@ init_libevent(const or_options_t *options)
suppress_libevent_log_msg(NULL);
tor_check_libevent_version(tor_libevent_get_method(),
- get_options()->ORPort != 0,
+ get_options()->ORPort != NULL,
&badness);
if (badness) {
const char *v = tor_libevent_get_version_str();
@@ -6140,7 +6639,7 @@ or_state_load(void)
if (contents) {
config_line_t *lines=NULL;
int assign_retval;
- if (config_get_lines(contents, &lines)<0)
+ if (config_get_lines(contents, &lines, 0)<0)
goto done;
assign_retval = config_assign(&state_format, new_state,
lines, 0, 0, &errmsg);
@@ -6244,7 +6743,7 @@ or_state_save(time_t now)
tor_free(global_state->TorVersion);
tor_asprintf(&global_state->TorVersion, "Tor %s", get_version());
- state = config_dump(&state_format, global_state, 1, 0);
+ state = config_dump(&state_format, NULL, global_state, 1, 0);
format_local_iso_time(tbuf, now);
tor_asprintf(&contents,
"# Tor state file last generated on %s local time\n"
@@ -6341,21 +6840,29 @@ get_transport_bindaddr(const char *line, const char *transport)
return NULL;
}
-/** Return a static string containing the address:port a proxy
- * transport should bind on. */
-const char *
+/** Return a string containing the address:port that a proxy transport
+ * should bind on. The string is stored on the heap and must be freed
+ * by the caller of this function. */
+char *
get_bindaddr_for_transport(const char *transport)
{
- static const char default_addrport[] = "127.0.0.1:0";
- const char *bindaddr = NULL;
+ char *default_addrport = NULL;
+ const char *stored_bindaddr = NULL;
config_line_t *line = get_transport_in_state_by_name(transport);
- if (!line)
- return default_addrport;
-
- bindaddr = get_transport_bindaddr(line->value, transport);
-
- return bindaddr ? bindaddr : default_addrport;
+ if (!line) /* Found no references in state for this transport. */
+ goto no_bindaddr_found;
+
+ stored_bindaddr = get_transport_bindaddr(line->value, transport);
+ if (stored_bindaddr) /* found stored bindaddr in state file. */
+ return tor_strdup(stored_bindaddr);
+
+ no_bindaddr_found:
+ /** If we didn't find references for this pluggable transport in the
+ state file, we should instruct the pluggable transport proxy to
+ listen on INADDR_ANY on a random ephemeral port. */
+ tor_asprintf(&default_addrport, "%s:%s", fmt_addr32(INADDR_ANY), "0");
+ return default_addrport;
}
/** Save <b>transport</b> listening on <b>addr</b>:<b>port</b> to
diff --git a/src/or/config.h b/src/or/config.h
index 76f6841d70..88258c133e 100644
--- a/src/or/config.h
+++ b/src/or/config.h
@@ -23,24 +23,24 @@ const char *escaped_safe_str_client(const char *address);
const char *escaped_safe_str(const char *address);
const char *get_version(void);
-int config_get_lines(const char *string, config_line_t **result);
+int config_get_lines(const char *string, config_line_t **result, int extended);
void config_free_lines(config_line_t *front);
setopt_err_t options_trial_assign(config_line_t *list, int use_defaults,
int clear_first, char **msg);
int resolve_my_address(int warn_severity, const or_options_t *options,
uint32_t *addr, char **hostname_out);
-int is_local_addr(const tor_addr_t *addr) ATTR_PURE;
+int is_local_addr(const tor_addr_t *addr);
void options_init(or_options_t *options);
char *options_dump(const or_options_t *options, int minimal);
int options_init_from_torrc(int argc, char **argv);
-setopt_err_t options_init_from_string(const char *cf,
+setopt_err_t options_init_from_string(const char *cf_defaults, const char *cf,
int command, const char *command_arg, char **msg);
int option_is_recognized(const char *key);
const char *option_get_canonical_name(const char *key);
config_line_t *option_get_assignment(const or_options_t *options,
const char *key);
int options_save_current(void);
-const char *get_torrc_fname(void);
+const char *get_torrc_fname(int defaults_fname);
char *options_get_datadir_fname2_suffix(const or_options_t *options,
const char *sub1, const char *sub2,
const char *suffix);
@@ -64,14 +64,20 @@ or_state_t *get_or_state(void);
int did_last_state_file_write_fail(void);
int or_state_save(time_t now);
-const smartlist_t *get_configured_client_ports(void);
+const smartlist_t *get_configured_ports(void);
+int get_first_advertised_port_by_type_af(int listener_type,
+ int address_family);
+#define get_primary_or_port() \
+ (get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER, AF_INET))
+#define get_primary_dir_port() \
+ (get_first_advertised_port_by_type_af(CONN_TYPE_DIR_LISTENER, AF_INET))
int options_need_geoip_info(const or_options_t *options,
const char **reason_out);
void save_transport_to_state(const char *transport_name,
const tor_addr_t *addr, uint16_t port);
-const char *get_bindaddr_for_transport(const char *transport);
+char *get_bindaddr_for_transport(const char *transport);
int getinfo_helper_config(control_connection_t *conn,
const char *question, char **answer,
diff --git a/src/or/connection.c b/src/or/connection.c
index a52bf48078..a6d5d8f1e4 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -880,7 +880,8 @@ connection_create_listener(const struct sockaddr *listensockaddr,
return NULL;
}
- if (listensockaddr->sa_family == AF_INET) {
+ if (listensockaddr->sa_family == AF_INET ||
+ listensockaddr->sa_family == AF_INET6) {
int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER);
if (is_tcp)
start_reading = 1;
@@ -890,7 +891,7 @@ connection_create_listener(const struct sockaddr *listensockaddr,
log_notice(LD_NET, "Opening %s on %s:%d",
conn_type_to_string(type), fmt_addr(&addr), usePort);
- s = tor_open_socket(PF_INET,
+ s = tor_open_socket(tor_addr_family(&addr),
is_tcp ? SOCK_STREAM : SOCK_DGRAM,
is_tcp ? IPPROTO_TCP: IPPROTO_UDP);
if (!SOCKET_OK(s)) {
@@ -1050,7 +1051,12 @@ connection_create_listener(const struct sockaddr *listensockaddr,
}
/** Do basic sanity checking on a newly received socket. Return 0
- * if it looks ok, else return -1. */
+ * if it looks ok, else return -1.
+ *
+ * Notably, some TCP stacks can erroneously have accept() return successfully
+ * with socklen 0, when the client sends an RST before the accept call (as
+ * nmap does). We want to detect that, and not go on with the connection.
+ */
static int
check_sockaddr(struct sockaddr *sa, int len, int level)
{
@@ -1116,14 +1122,14 @@ connection_handle_listener_read(connection_t *conn, int new_type)
tor_socket_t news; /* the new socket */
connection_t *newconn;
/* information about the remote peer when connecting to other routers */
- char addrbuf[256];
- struct sockaddr *remote = (struct sockaddr*)addrbuf;
+ struct sockaddr_storage addrbuf;
+ struct sockaddr *remote = (struct sockaddr*)&addrbuf;
/* length of the remote address. Must be whatever accept() needs. */
socklen_t remotelen = (socklen_t)sizeof(addrbuf);
const or_options_t *options = get_options();
tor_assert((size_t)remotelen >= sizeof(struct sockaddr_in));
- memset(addrbuf, 0, sizeof(addrbuf));
+ memset(&addrbuf, 0, sizeof(addrbuf));
news = tor_accept_socket(conn->s,remote,&remotelen);
if (!SOCKET_OK(news)) { /* accept() error */
@@ -1160,21 +1166,9 @@ connection_handle_listener_read(connection_t *conn, int new_type)
uint16_t port;
if (check_sockaddr(remote, remotelen, LOG_INFO)<0) {
log_info(LD_NET,
- "accept() returned a strange address; trying getsockname().");
- remotelen=sizeof(addrbuf);
- memset(addrbuf, 0, sizeof(addrbuf));
- if (getsockname(news, remote, &remotelen)<0) {
- int e = tor_socket_errno(news);
- log_warn(LD_NET, "getsockname() for new connection failed: %s",
- tor_socket_strerror(e));
- } else {
- if (check_sockaddr((struct sockaddr*)addrbuf, remotelen,
- LOG_WARN) < 0) {
- log_warn(LD_NET,"Something's wrong with this conn. Closing it.");
- tor_close_socket(news);
- return 0;
- }
- }
+ "accept() returned a strange address; closing connection.");
+ tor_close_socket(news);
+ return 0;
}
if (check_sockaddr_family_match(remote->sa_family, conn) < 0) {
@@ -1302,7 +1296,7 @@ connection_connect(connection_t *conn, const char *address,
{
tor_socket_t s;
int inprogress = 0;
- char addrbuf[256];
+ struct sockaddr_storage addrbuf;
struct sockaddr *dest_addr;
int dest_addr_len;
const or_options_t *options = get_options();
@@ -1318,6 +1312,24 @@ connection_connect(connection_t *conn, const char *address,
else
protocol_family = PF_INET;
+ if (get_options()->DisableNetwork) {
+ /* We should never even try to connect anyplace if DisableNetwork is set.
+ * Warn if we do, and refuse to make the connection. */
+ static ratelim_t disablenet_violated = RATELIM_INIT(30*60);
+ char *m;
+#ifdef MS_WINDOWS
+ *socket_error = WSAENETUNREACH;
+#else
+ *socket_error = ENETUNREACH;
+#endif
+ if ((m = rate_limit_log(&disablenet_violated, approx_time()))) {
+ log_warn(LD_BUG, "Tried to open a socket with DisableNetwork set.%s", m);
+ tor_free(m);
+ }
+ tor_fragile_assert();
+ return -1;
+ }
+
s = tor_open_socket(protocol_family,SOCK_STREAM,IPPROTO_TCP);
if (s < 0) {
*socket_error = tor_socket_errno(-1);
@@ -1352,8 +1364,8 @@ connection_connect(connection_t *conn, const char *address,
if (options->ConstrainedSockets)
set_constrained_socket_buffers(s, (int)options->ConstrainedSockSize);
- memset(addrbuf,0,sizeof(addrbuf));
- dest_addr = (struct sockaddr*) addrbuf;
+ memset(&addrbuf,0,sizeof(addrbuf));
+ dest_addr = (struct sockaddr*) &addrbuf;
dest_addr_len = tor_addr_to_sockaddr(addr, port, dest_addr, sizeof(addrbuf));
tor_assert(dest_addr_len > 0);
@@ -1796,6 +1808,9 @@ retry_listener_ports(smartlist_t *old_conns,
(conn->socket_family == AF_UNIX && ! wanted->is_unix_addr))
continue;
+ if (wanted->no_listen)
+ continue; /* We don't want to open a listener for this one */
+
if (wanted->is_unix_addr) {
if (conn->socket_family == AF_UNIX &&
!strcmp(wanted->unix_addr, conn->address)) {
@@ -1834,6 +1849,8 @@ retry_listener_ports(smartlist_t *old_conns,
connection_t *conn;
int real_port = port->port == CFG_AUTO_PORT ? 0 : port->port;
tor_assert(real_port <= UINT16_MAX);
+ if (port->no_listen)
+ continue;
if (port->is_unix_addr) {
listensockaddr = (struct sockaddr *)
@@ -1870,82 +1887,6 @@ retry_listener_ports(smartlist_t *old_conns,
return r;
}
-/**
- * Launch any configured listener connections of type <b>type</b>. (A
- * listener is configured if <b>port_option</b> is non-zero. If any
- * ListenAddress configuration options are given in <b>cfg</b>, create a
- * connection binding to each one. Otherwise, create a single
- * connection binding to the address <b>default_addr</b>.)
- *
- * We assume that we're starting with a list of existing listener connection_t
- * pointers in <b>old_conns</b>: we do not launch listeners that are already
- * in that list. Instead, we just remove them from the list.
- *
- * All new connections we launch are added to <b>new_conns</b>.
- */
-static int
-retry_listeners(smartlist_t *old_conns,
- int type, const config_line_t *cfg,
- int port_option, const char *default_addr,
- smartlist_t *new_conns,
- int is_sockaddr_un)
-{
- smartlist_t *ports = smartlist_create();
- tor_addr_t dflt_addr;
- int retval = 0;
-
- if (default_addr) {
- tor_addr_parse(&dflt_addr, default_addr);
- } else {
- tor_addr_make_unspec(&dflt_addr);
- }
-
- if (port_option) {
- if (!cfg) {
- port_cfg_t *port = tor_malloc_zero(sizeof(port_cfg_t));
- tor_addr_copy(&port->addr, &dflt_addr);
- port->port = port_option;
- port->type = type;
- smartlist_add(ports, port);
- } else {
- const config_line_t *c;
- for (c = cfg; c; c = c->next) {
- port_cfg_t *port;
- tor_addr_t addr;
- uint16_t portval = 0;
- if (is_sockaddr_un) {
- size_t len = strlen(c->value);
- port = tor_malloc_zero(sizeof(port_cfg_t) + len + 1);
- port->is_unix_addr = 1;
- memcpy(port->unix_addr, c->value, len+1);
- } else {
- if (tor_addr_port_lookup(c->value, &addr, &portval) < 0) {
- log_warn(LD_CONFIG, "Can't parse/resolve %s %s",
- c->key, c->value);
- retval = -1;
- continue;
- }
- port = tor_malloc_zero(sizeof(port_cfg_t));
- tor_addr_copy(&port->addr, &addr);
- }
- port->type = type;
- port->port = portval ? portval : port_option;
- smartlist_add(ports, port);
- }
- }
- }
-
- if (retval == -1)
- goto cleanup;
-
- retval = retry_listener_ports(old_conns, ports, new_conns);
-
- cleanup:
- SMARTLIST_FOREACH(ports, port_cfg_t *, p, tor_free(p));
- smartlist_free(ports);
- return retval;
-}
-
/** Launch listeners for each port you should have open. Only launch
* listeners who are not already open, and only close listeners we no longer
* want.
@@ -1968,35 +1909,10 @@ retry_all_listeners(smartlist_t *replaced_conns,
smartlist_add(listeners, conn);
} SMARTLIST_FOREACH_END(conn);
- if (! options->ClientOnly) {
- if (retry_listeners(listeners,
- CONN_TYPE_OR_LISTENER, options->ORListenAddress,
- options->ORPort, "0.0.0.0",
- new_conns, 0) < 0)
- retval = -1;
- if (retry_listeners(listeners,
- CONN_TYPE_DIR_LISTENER, options->DirListenAddress,
- options->DirPort, "0.0.0.0",
- new_conns, 0) < 0)
- retval = -1;
- }
-
if (retry_listener_ports(listeners,
- get_configured_client_ports(),
+ get_configured_ports(),
new_conns) < 0)
retval = -1;
- if (retry_listeners(listeners,
- CONN_TYPE_CONTROL_LISTENER,
- options->ControlListenAddress,
- options->ControlPort, "127.0.0.1",
- new_conns, 0) < 0)
- retval = -1;
- if (retry_listeners(listeners,
- CONN_TYPE_CONTROL_LISTENER,
- options->ControlSocket,
- options->ControlSocket ? 1 : 0, NULL,
- new_conns, 1) < 0)
- retval = -1;
/* Any members that were still in 'listeners' don't correspond to
* any configured port. Kill 'em. */
@@ -2013,6 +1929,7 @@ retry_all_listeners(smartlist_t *replaced_conns,
smartlist_free(listeners);
+ /* XXXprop186 should take all advertised ports into account */
if (old_or_port != router_get_advertised_or_port(options) ||
old_dir_port != router_get_advertised_dir_port(options, 0)) {
/* Our chosen ORPort or DirPort is not what it used to be: the
@@ -2025,6 +1942,43 @@ retry_all_listeners(smartlist_t *replaced_conns,
return retval;
}
+/** Mark every listener of type other than CONTROL_LISTENER to be closed. */
+void
+connection_mark_all_noncontrol_listeners(void)
+{
+ SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) {
+ if (conn->marked_for_close)
+ continue;
+ if (conn->type == CONN_TYPE_CONTROL_LISTENER)
+ continue;
+ if (connection_is_listener(conn))
+ connection_mark_for_close(conn);
+ } SMARTLIST_FOREACH_END(conn);
+}
+
+/** Mark every external conection not used for controllers for close. */
+void
+connection_mark_all_noncontrol_connections(void)
+{
+ SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) {
+ if (conn->marked_for_close)
+ continue;
+ switch (conn->type) {
+ case CONN_TYPE_CPUWORKER:
+ case CONN_TYPE_CONTROL_LISTENER:
+ case CONN_TYPE_CONTROL:
+ break;
+ case CONN_TYPE_AP:
+ connection_mark_unattached_ap(TO_ENTRY_CONN(conn),
+ END_STREAM_REASON_HIBERNATING);
+ break;
+ default:
+ connection_mark_for_close(conn);
+ break;
+ }
+ } SMARTLIST_FOREACH_END(conn);
+}
+
/** Return 1 if we should apply rate limiting to <b>conn</b>, and 0
* otherwise.
* Right now this just checks if it's an internal IP address or an
@@ -2999,6 +2953,9 @@ connection_handle_event_cb(struct bufferevent *bufev, short event, void *arg)
{
connection_t *conn = arg;
(void) bufev;
+ if (conn->marked_for_close)
+ return;
+
if (event & BEV_EVENT_CONNECTED) {
tor_assert(connection_state_is_connecting(conn));
if (connection_finished_connecting(conn)<0)
@@ -3176,8 +3133,7 @@ connection_handle_write_impl(connection_t *conn, int force)
/* Sometimes, "writable" means "connected". */
if (connection_state_is_connecting(conn)) {
if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0) {
- log_warn(LD_BUG,
- "getsockopt() syscall failed?! Please report to tor-ops.");
+ log_warn(LD_BUG, "getsockopt() syscall failed");
if (CONN_IS_EDGE(conn))
connection_edge_end_errno(TO_EDGE_CONN(conn));
connection_mark_for_close(conn);
diff --git a/src/or/connection.h b/src/or/connection.h
index 9f11489727..c4b8bf8abe 100644
--- a/src/or/connection.h
+++ b/src/or/connection.h
@@ -66,6 +66,9 @@ int get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type,
int retry_all_listeners(smartlist_t *replaced_conns,
smartlist_t *new_conns);
+void connection_mark_all_noncontrol_listeners(void);
+void connection_mark_all_noncontrol_connections(void);
+
ssize_t connection_bucket_write_limit(connection_t *conn, time_t now);
int global_write_bucket_low(connection_t *conn, size_t attempt, int priority);
void connection_bucket_init(void);
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index efaad79b6a..aba9ba2727 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -803,11 +803,18 @@ connection_ap_detach_retriable(entry_connection_t *conn,
* the configuration file, "1" for mappings set from the control
* interface, and other values for DNS and TrackHostExit mappings that can
* expire.)
+ *
+ * A mapping may be 'wildcarded'. If "src_wildcard" is true, then
+ * any address that ends with a . followed by the key for this entry will
+ * get remapped by it. If "dst_wildcard" is also true, then only the
+ * matching suffix of such addresses will get replaced by new_address.
*/
typedef struct {
char *new_address;
time_t expires;
addressmap_entry_source_t source:3;
+ unsigned src_wildcard:1;
+ unsigned dst_wildcard:1;
short num_resolve_failures;
} addressmap_entry_t;
@@ -1054,6 +1061,37 @@ addressmap_free_all(void)
virtaddress_reversemap = NULL;
}
+/** Try to find a match for AddressMap expressions that use
+ * wildcard notation such as '*.c.d *.e.f' (so 'a.c.d' will map to 'a.e.f') or
+ * '*.c.d a.b.c' (so 'a.c.d' will map to a.b.c).
+ * Return the matching entry in AddressMap or NULL if no match is found.
+ * For expressions such as '*.c.d *.e.f', truncate <b>address</b> 'a.c.d'
+ * to 'a' before we return the matching AddressMap entry.
+ *
+ * This function does not handle the case where a pattern of the form "*.c.d"
+ * matches the address c.d -- that's done by the main addressmap_rewrite
+ * function.
+ */
+static addressmap_entry_t *
+addressmap_match_superdomains(char *address)
+{
+ addressmap_entry_t *val;
+ char *cp;
+
+ cp = address;
+ while ((cp = strchr(cp, '.'))) {
+ /* cp now points to a suffix of address that begins with a . */
+ val = strmap_get_lc(addressmap, cp+1);
+ if (val && val->src_wildcard) {
+ if (val->dst_wildcard)
+ *cp = '\0';
+ return val;
+ }
+ ++cp;
+ }
+ return NULL;
+}
+
/** Look at address, and rewrite it until it doesn't want any
* more rewrites; but don't get into an infinite loop.
* Don't write more than maxlen chars into address. Return true if the
@@ -1066,25 +1104,49 @@ addressmap_rewrite(char *address, size_t maxlen, time_t *expires_out)
{
addressmap_entry_t *ent;
int rewrites;
- char *cp;
time_t expires = TIME_MAX;
for (rewrites = 0; rewrites < 16; rewrites++) {
+ int exact_match = 0;
+ char *addr_orig = tor_strdup(escaped_safe_str_client(address));
+
ent = strmap_get(addressmap, address);
if (!ent || !ent->new_address) {
+ ent = addressmap_match_superdomains(address);
+ } else {
+ if (ent->src_wildcard && !ent->dst_wildcard &&
+ !strcasecmp(address, ent->new_address)) {
+ /* This is a rule like *.example.com example.com, and we just got
+ * "example.com" */
+ tor_free(addr_orig);
+ if (expires_out)
+ *expires_out = expires;
+ return rewrites > 0;
+ }
+
+ exact_match = 1;
+ }
+
+ if (!ent || !ent->new_address) {
+ tor_free(addr_orig);
if (expires_out)
*expires_out = expires;
return (rewrites > 0); /* done, no rewrite needed */
}
- cp = tor_strdup(escaped_safe_str_client(ent->new_address));
+ if (ent->dst_wildcard && !exact_match) {
+ strlcat(address, ".", maxlen);
+ strlcat(address, ent->new_address, maxlen);
+ } else {
+ strlcpy(address, ent->new_address, maxlen);
+ }
+
log_info(LD_APP, "Addressmap: rewriting %s to %s",
- escaped_safe_str_client(address), cp);
+ addr_orig, escaped_safe_str_client(address));
if (ent->expires > 1 && ent->expires < expires)
expires = ent->expires;
- tor_free(cp);
- strlcpy(address, ent->new_address, maxlen);
+ tor_free(addr_orig);
}
log_warn(LD_CONFIG,
"Loop detected: we've rewritten %s 16 times! Using it as-is.",
@@ -1148,17 +1210,34 @@ addressmap_have_mapping(const char *address, int update_expiry)
* <b>new_address</b> should be a newly dup'ed string, which we'll use or
* free as appropriate. We will leave address alone.
*
- * If <b>new_address</b> is NULL, or equal to <b>address</b>, remove
- * any mappings that exist from <b>address</b>.
- */
+ * If <b>wildcard_addr</b> is true, then the mapping will match any address
+ * equal to <b>address</b>, or any address ending with a period followed by
+ * <b>address</b>. If <b>wildcard_addr</b> and <b>wildcard_new_addr</b> are
+ * both true, the mapping will rewrite addresses that end with
+ * ".<b>address</b>" into ones that end with ".<b>new_address</b>."
+ *
+ * If <b>new_address</b> is NULL, or <b>new_address</b> is equal to
+ * <b>address</b> and <b>wildcard_addr</b> is equal to
+ * <b>wildcard_new_addr</b>, remove any mappings that exist from
+ * <b>address</b>.
+ *
+ *
+ * It is an error to set <b>wildcard_new_addr</b> if <b>wildcard_addr</b> is
+ * not set. */
void
addressmap_register(const char *address, char *new_address, time_t expires,
- addressmap_entry_source_t source)
+ addressmap_entry_source_t source,
+ const int wildcard_addr,
+ const int wildcard_new_addr)
{
addressmap_entry_t *ent;
+ if (wildcard_new_addr)
+ tor_assert(wildcard_addr);
+
ent = strmap_get(addressmap, address);
- if (!new_address || !strcasecmp(address,new_address)) {
+ if (!new_address || (!strcasecmp(address,new_address) &&
+ wildcard_addr == wildcard_new_addr)) {
/* Remove the mapping, if any. */
tor_free(new_address);
if (ent) {
@@ -1193,6 +1272,8 @@ addressmap_register(const char *address, char *new_address, time_t expires,
ent->expires = expires==2 ? 1 : expires;
ent->num_resolve_failures = 0;
ent->source = source;
+ ent->src_wildcard = wildcard_addr ? 1 : 0;
+ ent->dst_wildcard = wildcard_new_addr ? 1 : 0;
log_info(LD_CONFIG, "Addressmap: (re)mapped '%s' to '%s'",
safe_str_client(address),
@@ -1277,7 +1358,7 @@ client_dns_set_addressmap_impl(const char *address, const char *name,
"%s", name);
}
addressmap_register(extendedaddress, tor_strdup(extendedval),
- time(NULL) + ttl, ADDRMAPSRC_DNS);
+ time(NULL) + ttl, ADDRMAPSRC_DNS, 0, 0);
}
/** Record the fact that <b>address</b> resolved to <b>val</b>.
@@ -1529,7 +1610,7 @@ addressmap_register_virtual_address(int type, char *new_address)
log_info(LD_APP, "Registering map from %s to %s", *addrp, new_address);
if (vent_needs_to_be_added)
strmap_set(virtaddress_reversemap, new_address, vent);
- addressmap_register(*addrp, new_address, 2, ADDRMAPSRC_AUTOMAP);
+ addressmap_register(*addrp, new_address, 2, ADDRMAPSRC_AUTOMAP, 0, 0);
#if 0
{
@@ -1892,6 +1973,14 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
return -1;
}
+ if (options->Tor2webMode) {
+ log_warn(LD_APP, "Refusing to connect to non-hidden-service hostname %s "
+ "because tor2web mode is enabled.",
+ safe_str_client(socks->address));
+ connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY);
+ return -1;
+ }
+
if (socks->command == SOCKS_COMMAND_RESOLVE) {
uint32_t answer;
struct in_addr in;
@@ -2453,7 +2542,9 @@ connection_ap_handshake_send_begin(entry_connection_t *ap_conn)
begin_type = ap_conn->use_begindir ?
RELAY_COMMAND_BEGIN_DIR : RELAY_COMMAND_BEGIN;
if (begin_type == RELAY_COMMAND_BEGIN) {
+#ifndef NON_ANONYMOUS_MODE_ENABLED
tor_assert(circ->build_state->onehop_tunnel == 0);
+#endif
}
if (connection_edge_send_command(edge_conn, begin_type,
@@ -2593,7 +2684,7 @@ connection_ap_make_link(connection_t *partner,
want_onehop ? "direct" : "anonymized",
safe_str_client(address), port);
- conn = entry_connection_new(CONN_TYPE_AP, AF_INET);
+ conn = entry_connection_new(CONN_TYPE_AP, tor_addr_family(&partner->addr));
base_conn = ENTRY_TO_CONN(conn);
base_conn->linked = 1; /* so that we can add it safely below. */
@@ -3199,7 +3290,7 @@ connection_exit_connect_dir(edge_connection_t *exitconn)
exitconn->_base.state = EXIT_CONN_STATE_OPEN;
- dirconn = dir_connection_new(AF_INET);
+ dirconn = dir_connection_new(tor_addr_family(&exitconn->_base.addr));
tor_addr_copy(&dirconn->_base.addr, &exitconn->_base.addr);
dirconn->_base.port = 0;
diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h
index 830667e601..47c9c45b1a 100644
--- a/src/or/connection_edge.h
+++ b/src/or/connection_edge.h
@@ -78,7 +78,8 @@ int addressmap_rewrite(char *address, size_t maxlen, time_t *expires_out);
int addressmap_have_mapping(const char *address, int update_timeout);
void addressmap_register(const char *address, char *new_address,
- time_t expires, addressmap_entry_source_t source);
+ time_t expires, addressmap_entry_source_t source,
+ int address_wildcard, int new_address_wildcard);
int parse_virtual_addr_network(const char *val, int validate_only,
char **msg);
int client_dns_incr_failures(const char *address);
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index cce99e4d65..7609138e68 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -626,7 +626,9 @@ connection_or_update_token_buckets(smartlist_t *conns,
/** If we don't necessarily know the router we're connecting to, but we
* have an addr/port/id_digest, then fill in as much as we can. Start
- * by checking to see if this describes a router we know. */
+ * by checking to see if this describes a router we know.
+ * <b>started_here</b> is 1 if we are the initiator of <b>conn</b> and
+ * 0 if it's an incoming connection. */
void
connection_or_init_conn_from_address(or_connection_t *conn,
const tor_addr_t *addr, uint16_t port,
@@ -641,10 +643,11 @@ connection_or_init_conn_from_address(or_connection_t *conn,
tor_addr_copy(&conn->_base.addr, addr);
tor_addr_copy(&conn->real_addr, addr);
if (r) {
- tor_addr_t node_addr;
- node_get_addr(r, &node_addr);
- /* XXXX proposal 118 will make this more complex. */
- if (tor_addr_eq(&conn->_base.addr, &node_addr))
+ tor_addr_port_t node_ap;
+ node_get_pref_orport(r, &node_ap);
+ /* XXXX proposal 186 is making this more complex. For now, a conn
+ is canonical when it uses the _preferred_ address. */
+ if (tor_addr_eq(&conn->_base.addr, &node_ap.addr))
conn->is_canonical = 1;
if (!started_here) {
/* Override the addr/port, so our log messages will make sense.
@@ -657,12 +660,12 @@ connection_or_init_conn_from_address(or_connection_t *conn,
* right IP address and port 56244, that wouldn't be as helpful. now we
* log the "right" port too, so we know if it's moria1 or moria2.
*/
- tor_addr_copy(&conn->_base.addr, &node_addr);
- conn->_base.port = node_get_orport(r);
+ tor_addr_copy(&conn->_base.addr, &node_ap.addr);
+ conn->_base.port = node_ap.port;
}
conn->nickname = tor_strdup(node_get_nickname(r));
tor_free(conn->_base.address);
- conn->_base.address = tor_dup_addr(&node_addr);
+ conn->_base.address = tor_dup_addr(&node_ap.addr);
} else {
const char *n;
/* If we're an authoritative directory server, we may know a
@@ -1029,7 +1032,7 @@ connection_or_connect(const tor_addr_t *_addr, uint16_t port,
return NULL;
}
- conn = or_connection_new(AF_INET);
+ conn = or_connection_new(tor_addr_family(&addr));
/* set up conn so it's got all the data we need to remember */
connection_or_init_conn_from_address(conn, &addr, port, id_digest, 1);
@@ -1096,12 +1099,13 @@ connection_tls_start_handshake(or_connection_t *conn, int receiving)
conn->_base.state = OR_CONN_STATE_TLS_HANDSHAKING;
tor_assert(!conn->tls);
conn->tls = tor_tls_new(conn->_base.s, receiving);
- tor_tls_set_logged_address(conn->tls, // XXX client and relay?
- escaped_safe_str(conn->_base.address));
if (!conn->tls) {
log_warn(LD_BUG,"tor_tls_new failed. Closing.");
return -1;
}
+ tor_tls_set_logged_address(conn->tls, // XXX client and relay?
+ escaped_safe_str(conn->_base.address));
+
#ifdef USE_BUFFEREVENTS
if (connection_type_uses_bufferevent(TO_CONN(conn))) {
const int filtering = get_options()->_UseFilteringSSLBufferevents;
@@ -1485,7 +1489,9 @@ connection_or_client_learned_peer_id(or_connection_t *conn,
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED,
END_OR_CONN_REASON_OR_IDENTITY);
if (!authdir_mode_tests_reachability(options))
- control_event_bootstrap_problem("foo", END_OR_CONN_REASON_OR_IDENTITY);
+ control_event_bootstrap_problem(
+ "Unexpected identity in router certificate",
+ END_OR_CONN_REASON_OR_IDENTITY);
as_expected = 0;
}
if (authdir_mode_tests_reachability(options)) {
diff --git a/src/or/control.c b/src/or/control.c
index 42eaed2765..c9d3765ad3 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -737,7 +737,7 @@ control_setconf_helper(control_connection_t *conn, uint32_t len, char *body,
SMARTLIST_FOREACH(entries, char *, cp, tor_free(cp));
smartlist_free(entries);
- if (config_get_lines(config, &lines) < 0) {
+ if (config_get_lines(config, &lines, 0) < 0) {
log_warn(LD_CONTROL,"Controller gave us config lines we can't parse.");
connection_write_str_to_buf("551 Couldn't parse configuration\r\n",
conn);
@@ -883,7 +883,7 @@ handle_control_loadconf(control_connection_t *conn, uint32_t len,
const char *msg = NULL;
(void) len;
- retval = options_init_from_string(body, CMD_RUN_TOR, NULL, &errstring);
+ retval = options_init_from_string(NULL, body, CMD_RUN_TOR, NULL, &errstring);
if (retval != SETOPT_OK)
log_warn(LD_CONTROL,
@@ -1331,7 +1331,8 @@ handle_control_mapaddress(control_connection_t *conn, uint32_t len,
smartlist_add(reply, ans);
}
} else {
- addressmap_register(from, tor_strdup(to), 1, ADDRMAPSRC_CONTROLLER);
+ addressmap_register(from, tor_strdup(to), 1,
+ ADDRMAPSRC_CONTROLLER, 0, 0);
tor_snprintf(ans, anslen, "250-%s", line);
smartlist_add(reply, ans);
}
@@ -1378,7 +1379,9 @@ getinfo_helper_misc(control_connection_t *conn, const char *question,
if (!strcmp(question, "version")) {
*answer = tor_strdup(get_version());
} else if (!strcmp(question, "config-file")) {
- *answer = tor_strdup(get_torrc_fname());
+ *answer = tor_strdup(get_torrc_fname(0));
+ } else if (!strcmp(question, "config-defaults-file")) {
+ *answer = tor_strdup(get_torrc_fname(1));
} else if (!strcmp(question, "config-text")) {
*answer = options_dump(get_options(), 1);
} else if (!strcmp(question, "info/names")) {
@@ -2106,6 +2109,7 @@ typedef struct getinfo_item_t {
static const getinfo_item_t getinfo_items[] = {
ITEM("version", misc, "The current version of Tor."),
ITEM("config-file", misc, "Current location of the \"torrc\" file."),
+ ITEM("config-defaults-file", misc, "Current location of the defaults file."),
ITEM("config-text", misc,
"Return the string that would be written by a saveconf command."),
ITEM("accounting/bytes", accounting,
@@ -2481,7 +2485,7 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
/* now circ refers to something that is ready to be extended */
SMARTLIST_FOREACH(nodes, const node_t *, node,
{
- extend_info_t *info = extend_info_from_node(node);
+ extend_info_t *info = extend_info_from_node(node, 0);
tor_assert(info); /* True, since node_has_descriptor(node) == true */
circuit_append_new_exit(circ, info);
extend_info_free(info);
diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c
index 914003790a..e4460c5981 100644
--- a/src/or/cpuworker.c
+++ b/src/or/cpuworker.c
@@ -347,6 +347,7 @@ spawn_cpuworker(void)
/* set up conn so it's got all the data we need to remember */
conn->s = fd;
conn->address = tor_strdup("localhost");
+ tor_addr_make_unspec(&conn->addr);
if (connection_add(conn) < 0) { /* no space, forget it */
log_warn(LD_NET,"connection_add for cpuworker failed. Giving up.");
diff --git a/src/or/directory.c b/src/or/directory.c
index 776b7a25f9..12636bac33 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -410,7 +410,7 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose,
} else {
/* Otherwise it might be a consensus we don't parse, but which we
* do cache. Look at the cached copy, perhaps. */
- cached_dir_t *cd = dirserv_get_consensus(resource ? resource : "ns");
+ cached_dir_t *cd = dirserv_get_consensus(resource);
if (cd)
if_modified_since = cd->published + 180;
}
@@ -907,8 +907,12 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr,
log_debug(LD_DIR, "Initiating %s", dir_conn_purpose_to_string(dir_purpose));
+#ifndef NON_ANONYMOUS_MODE_ENABLED
tor_assert(!(is_sensitive_dir_purpose(dir_purpose) &&
!anonymized_connection));
+#else
+ (void)is_sensitive_dir_purpose;
+#endif
/* ensure that we don't make direct connections when a SOCKS server is
* configured. */
@@ -919,7 +923,7 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr,
return;
}
- conn = dir_connection_new(AF_INET);
+ conn = dir_connection_new(tor_addr_family(&addr));
/* set up conn so it's got all the data we need to remember */
tor_addr_copy(&conn->_base.addr, &addr);
@@ -1619,9 +1623,11 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
if (!reason) reason = tor_strdup("[no reason given]");
log_debug(LD_DIR,
- "Received response from directory server '%s:%d': %d %s",
+ "Received response from directory server '%s:%d': %d %s "
+ "(purpose: %d)",
conn->_base.address, conn->_base.port, status_code,
- escaped(reason));
+ escaped(reason),
+ conn->_base.purpose);
/* now check if it's got any hints for us about our IP address. */
if (conn->dirconn_direct) {
@@ -3729,7 +3735,7 @@ download_status_reset(download_status_t *dls)
const int *schedule;
size_t schedule_len;
- find_dl_schedule_and_len(dls, get_options()->DirPort,
+ find_dl_schedule_and_len(dls, get_options()->DirPort != NULL,
&schedule, &schedule_len);
dls->n_download_failures = 0;
diff --git a/src/or/directory.h b/src/or/directory.h
index 8c63bb5dfd..5050f700d2 100644
--- a/src/or/directory.h
+++ b/src/or/directory.h
@@ -80,7 +80,7 @@ time_t download_status_increment_failure(download_status_t *dls,
* the optional status code <b>sc</b>. */
#define download_status_failed(dls, sc) \
download_status_increment_failure((dls), (sc), NULL, \
- get_options()->DirPort, time(NULL))
+ get_options()->DirPort!=NULL, time(NULL))
void download_status_reset(download_status_t *dls);
static int download_status_is_ready(download_status_t *dls, time_t now,
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index be62459b16..f4bbca8500 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -232,7 +232,7 @@ dirserv_load_fingerprint_file(void)
}
tor_free(fname);
- result = config_get_lines(cf, &front);
+ result = config_get_lines(cf, &front, 0);
tor_free(cf);
if (result < 0) {
log_warn(LD_CONFIG, "Error reading from fingerprint file");
@@ -1212,7 +1212,7 @@ directory_fetches_from_authorities(const or_options_t *options)
return 1; /* we don't know our IP address; ask an authority. */
refuseunknown = ! router_my_exit_policy_is_reject_star() &&
should_refuse_unknown_exits(options);
- if (options->DirPort == 0 && !refuseunknown)
+ if (options->DirPort == NULL && !refuseunknown)
return 0;
if (!server_mode(options) || !advertised_server_mode())
return 0;
@@ -1248,7 +1248,7 @@ directory_fetches_dir_info_later(const or_options_t *options)
int
directory_caches_v2_dir_info(const or_options_t *options)
{
- return options->DirPort != 0;
+ return options->DirPort != NULL;
}
/** Return 1 if we want to keep descriptors, networkstatuses, etc around
@@ -1273,7 +1273,7 @@ directory_caches_dir_info(const or_options_t *options)
int
directory_permits_begindir_requests(const or_options_t *options)
{
- return options->BridgeRelay != 0 || options->DirPort != 0;
+ return options->BridgeRelay != 0 || options->DirPort != NULL;
}
/** Return 1 if we want to allow controllers to ask us directory
@@ -1282,7 +1282,7 @@ directory_permits_begindir_requests(const or_options_t *options)
int
directory_permits_controller_requests(const or_options_t *options)
{
- return options->DirPort != 0;
+ return options->DirPort != NULL;
}
/** Return 1 if we have no need to fetch new descriptors. This generally
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index bf34c62af3..01e2358c44 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -50,7 +50,7 @@ static int dirvote_publish_consensus(void);
static char *make_consensus_method_list(int low, int high, const char *sep);
/** The highest consensus method that we currently support. */
-#define MAX_SUPPORTED_CONSENSUS_METHOD 11
+#define MAX_SUPPORTED_CONSENSUS_METHOD 12
/** Lowest consensus method that contains a 'directory-footer' marker */
#define MIN_METHOD_FOR_FOOTER 9
@@ -64,6 +64,10 @@ static char *make_consensus_method_list(int low, int high, const char *sep);
/** Lowest consensus method that generates microdescriptors */
#define MIN_METHOD_FOR_MICRODESC 8
+/** Lowest consensus method that ensures a majority of authorities voted
+ * for a param. */
+#define MIN_METHOD_FOR_MAJORITY_PARAMS 12
+
/* =====
* Voting
* =====*/
@@ -608,11 +612,16 @@ compute_consensus_versions_list(smartlist_t *lst, int n_versioning)
return result;
}
+/** Minimum number of directory authorities voting for a parameter to
+ * include it in the consensus, if consensus method 12 or later is to be
+ * used. See proposal 178 for details. */
+#define MIN_VOTES_FOR_PARAM 3
+
/** Helper: given a list of valid networkstatus_t, return a new string
* containing the contents of the consensus network parameter set.
*/
/* private */ char *
-dirvote_compute_params(smartlist_t *votes)
+dirvote_compute_params(smartlist_t *votes, int method, int total_authorities)
{
int i;
int32_t *vals;
@@ -669,11 +678,17 @@ dirvote_compute_params(smartlist_t *votes)
next_param = smartlist_get(param_list, param_sl_idx+1);
if (!next_param || strncmp(next_param, param, cur_param_len)) {
/* We've reached the end of a series. */
- int32_t median = median_int32(vals, i);
- char *out_string = tor_malloc(64+cur_param_len);
- memcpy(out_string, param, cur_param_len);
- tor_snprintf(out_string+cur_param_len,64, "%ld", (long)median);
- smartlist_add(output, out_string);
+ /* Make sure enough authorities voted on this param, unless the
+ * the consensus method we use is too old for that. */
+ if (method < MIN_METHOD_FOR_MAJORITY_PARAMS ||
+ i > total_authorities/2 ||
+ i >= MIN_VOTES_FOR_PARAM) {
+ int32_t median = median_int32(vals, i);
+ char *out_string = tor_malloc(64+cur_param_len);
+ memcpy(out_string, param, cur_param_len);
+ tor_snprintf(out_string+cur_param_len,64, "%ld", (long)median);
+ smartlist_add(output, out_string);
+ }
i = 0;
if (next_param) {
@@ -1496,7 +1511,8 @@ networkstatus_compute_consensus(smartlist_t *votes,
}
if (consensus_method >= MIN_METHOD_FOR_PARAMS) {
- params = dirvote_compute_params(votes);
+ params = dirvote_compute_params(votes, consensus_method,
+ total_authorities);
if (params) {
smartlist_add(chunks, tor_strdup("params "));
smartlist_add(chunks, params);
diff --git a/src/or/dirvote.h b/src/or/dirvote.h
index d19635173f..1f4dc362b5 100644
--- a/src/or/dirvote.h
+++ b/src/or/dirvote.h
@@ -84,7 +84,8 @@ document_signature_t *voter_get_sig_by_algorithm(
#ifdef DIRVOTE_PRIVATE
char *format_networkstatus_vote(crypto_pk_env_t *private_key,
networkstatus_t *v3_ns);
-char *dirvote_compute_params(smartlist_t *votes);
+char *dirvote_compute_params(smartlist_t *votes, int method,
+ int total_authorities);
#endif
#endif
diff --git a/src/or/dns.c b/src/or/dns.c
index 8ed9536903..beb110acb2 100644
--- a/src/or/dns.c
+++ b/src/or/dns.c
@@ -1395,6 +1395,10 @@ launch_resolve(edge_connection_t *exitconn)
int r;
int options = get_options()->ServerDNSSearchDomains ? 0
: DNS_QUERY_NO_SEARCH;
+
+ if (get_options()->DisableNetwork)
+ return -1;
+
/* What? Nameservers not configured? Sounds like a bug. */
if (!nameservers_configured) {
log_warn(LD_EXIT, "(Harmless.) Nameservers not configured, but resolve "
@@ -1601,6 +1605,9 @@ launch_test_addresses(int fd, short event, void *args)
(void)event;
(void)args;
+ if (options->DisableNetwork)
+ return;
+
log_info(LD_EXIT, "Launching checks to see whether our nameservers like to "
"hijack *everything*.");
/* This situation is worse than the failure-hijacking situation. When this
diff --git a/src/or/hibernate.c b/src/or/hibernate.c
index 6fd2b4f197..ce64581d1c 100644
--- a/src/or/hibernate.c
+++ b/src/or/hibernate.c
@@ -735,7 +735,6 @@ hibernate_soft_limit_reached(void)
static void
hibernate_begin(hibernate_state_t new_state, time_t now)
{
- connection_t *conn;
const or_options_t *options = get_options();
if (new_state == HIBERNATE_STATE_EXITING &&
@@ -756,15 +755,7 @@ hibernate_begin(hibernate_state_t new_state, time_t now)
}
/* close listeners. leave control listener(s). */
- while ((conn = connection_get_by_type(CONN_TYPE_OR_LISTENER)) ||
- (conn = connection_get_by_type(CONN_TYPE_AP_LISTENER)) ||
- (conn = connection_get_by_type(CONN_TYPE_AP_TRANS_LISTENER)) ||
- (conn = connection_get_by_type(CONN_TYPE_AP_DNS_LISTENER)) ||
- (conn = connection_get_by_type(CONN_TYPE_AP_NATD_LISTENER)) ||
- (conn = connection_get_by_type(CONN_TYPE_DIR_LISTENER))) {
- log_info(LD_NET,"Closing listener type %d", conn->type);
- connection_mark_for_close(conn);
- }
+ connection_mark_all_noncontrol_listeners();
/* XXX kill intro point circs */
/* XXX upload rendezvous service descriptors with no intro points */
diff --git a/src/or/main.c b/src/or/main.c
index 7008d388a1..1ba33957a6 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -196,6 +196,26 @@ free_old_inbuf(connection_t *conn)
}
#endif
+#if defined(MS_WINDOWS) && defined(USE_BUFFEREVENTS)
+/** Remove the kernel-space send and receive buffers for <b>s</b>. For use
+ * with IOCP only. */
+static int
+set_buffer_lengths_to_zero(tor_socket_t s)
+{
+ int zero = 0;
+ int r = 0;
+ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&zero, sizeof(zero))) {
+ log_warn(LD_NET, "Unable to clear SO_SNDBUF");
+ r = -1;
+ }
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&zero, sizeof(zero))) {
+ log_warn(LD_NET, "Unable to clear SO_RCVBUF");
+ r = -1;
+ }
+ return r;
+}
+#endif
+
/** Add <b>conn</b> to the array of connections that we can poll on. The
* connection's socket must be set; the connection starts out
* non-reading and non-writing.
@@ -216,6 +236,14 @@ connection_add_impl(connection_t *conn, int is_connecting)
#ifdef USE_BUFFEREVENTS
if (connection_type_uses_bufferevent(conn)) {
if (SOCKET_OK(conn->s) && !conn->linked) {
+
+#ifdef MS_WINDOWS
+ if (tor_libevent_using_iocp_bufferevents() &&
+ get_options()->UserspaceIOCPBuffers) {
+ set_buffer_lengths_to_zero(conn->s);
+ }
+#endif
+
conn->bufev = bufferevent_socket_new(
tor_libevent_get_base(),
conn->s,
@@ -906,7 +934,7 @@ directory_info_has_arrived(time_t now, int from_cache)
update_extrainfo_downloads(now);
}
- if (server_mode(options) && !we_are_hibernating() && !from_cache &&
+ if (server_mode(options) && !net_is_disabled() && !from_cache &&
(can_complete_circuit || !any_predicted_circuits(now)))
consider_testing_reachability(1, 1);
}
@@ -1133,11 +1161,11 @@ run_scheduled_events(time_t now)
if (router_rebuild_descriptor(1)<0) {
log_info(LD_CONFIG, "Couldn't rebuild router descriptor");
}
- if (advertised_server_mode())
+ if (advertised_server_mode() & !options->DisableNetwork)
router_upload_dir_desc_to_dirservers(0);
}
- if (time_to_try_getting_descriptors < now) {
+ if (!options->DisableNetwork && time_to_try_getting_descriptors < now) {
update_all_descriptor_downloads(now);
update_extrainfo_downloads(now);
if (router_have_minimum_dir_info())
@@ -1161,10 +1189,7 @@ run_scheduled_events(time_t now)
last_rotated_x509_certificate = now;
if (last_rotated_x509_certificate+MAX_SSL_KEY_LIFETIME_INTERNAL < now) {
log_info(LD_GENERAL,"Rotating tls context.");
- if (tor_tls_context_init(public_server_mode(options),
- get_tlsclient_identity_key(),
- is_server ? get_server_identity_key() : NULL,
- MAX_SSL_KEY_LIFETIME_ADVERTISED) < 0) {
+ if (router_initialize_tls_context() < 0) {
log_warn(LD_BUG, "Error reinitializing TLS context");
/* XXX is it a bug here, that we just keep going? -RD */
}
@@ -1191,7 +1216,7 @@ run_scheduled_events(time_t now)
if (time_to_launch_reachability_tests < now &&
(authdir_mode_tests_reachability(options)) &&
- !we_are_hibernating()) {
+ !net_is_disabled()) {
time_to_launch_reachability_tests = now + REACHABILITY_TEST_INTERVAL;
/* try to determine reachability of the other Tor relays */
dirserv_test_reachability(now);
@@ -1327,7 +1352,7 @@ run_scheduled_events(time_t now)
/* 2b. Once per minute, regenerate and upload the descriptor if the old
* one is inaccurate. */
- if (time_to_check_descriptor < now) {
+ if (time_to_check_descriptor < now && !options->DisableNetwork) {
static int dirport_reachability_count = 0;
time_to_check_descriptor = now + CHECK_DESCRIPTOR_INTERVAL;
check_descriptor_bandwidth_changed(now);
@@ -1402,7 +1427,7 @@ run_scheduled_events(time_t now)
connection_expire_held_open();
/** 3d. And every 60 seconds, we relaunch listeners if any died. */
- if (!we_are_hibernating() && time_to_check_listeners < now) {
+ if (!net_is_disabled() && time_to_check_listeners < now) {
retry_all_listeners(NULL, NULL);
time_to_check_listeners = now+60;
}
@@ -1413,7 +1438,7 @@ run_scheduled_events(time_t now)
* and we make a new circ if there are no clean circuits.
*/
have_dir_info = router_have_minimum_dir_info();
- if (have_dir_info && !we_are_hibernating())
+ if (have_dir_info && !net_is_disabled())
circuit_build_needed_circs(now);
/* every 10 seconds, but not at the same second as other such events */
@@ -1444,7 +1469,7 @@ run_scheduled_events(time_t now)
circuit_close_all_marked();
/** 7. And upload service descriptors if necessary. */
- if (can_complete_circuit && !we_are_hibernating()) {
+ if (can_complete_circuit && !net_is_disabled()) {
rend_consider_services_upload(now);
rend_consider_descriptor_republication();
}
@@ -1461,7 +1486,8 @@ run_scheduled_events(time_t now)
/** 9. and if we're a server, check whether our DNS is telling stories to
* us. */
- if (public_server_mode(options) && time_to_check_for_correct_dns < now) {
+ if (!net_is_disabled() &&
+ public_server_mode(options) && time_to_check_for_correct_dns < now) {
if (!time_to_check_for_correct_dns) {
time_to_check_for_correct_dns = now + 60 + crypto_rand_int(120);
} else {
@@ -1480,19 +1506,21 @@ run_scheduled_events(time_t now)
}
/** 11. check the port forwarding app */
- if (time_to_check_port_forwarding < now &&
+ if (!net_is_disabled() &&
+ time_to_check_port_forwarding < now &&
options->PortForwarding &&
is_server) {
#define PORT_FORWARDING_CHECK_INTERVAL 5
+ /* XXXXX this should take a list of ports, not just two! */
tor_check_port_forwarding(options->PortForwardingHelper,
- options->DirPort,
- options->ORPort,
+ get_primary_dir_port(),
+ get_primary_or_port(),
now);
time_to_check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL;
}
/** 11b. check pending unconfigured managed proxies */
- if (pt_proxies_configuration_pending())
+ if (!net_is_disabled() && pt_proxies_configuration_pending())
pt_configure_remaining_proxies();
/** 11c. validate pluggable transports configuration if we need to */
@@ -1564,7 +1592,7 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg)
control_event_stream_bandwidth_used();
if (server_mode(options) &&
- !we_are_hibernating() &&
+ !net_is_disabled() &&
seconds_elapsed > 0 &&
can_complete_circuit &&
stats_n_seconds_working / TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT !=
@@ -1765,7 +1793,8 @@ do_hup(void)
/* retry appropriate downloads */
router_reset_status_download_failures();
router_reset_descriptor_download_failures();
- update_networkstatus_downloads(time(NULL));
+ if (!options->DisableNetwork)
+ update_networkstatus_downloads(time(NULL));
/* We'll retry routerstatus downloads in about 10 seconds; no need to
* force a retry there. */
@@ -2239,15 +2268,14 @@ tor_init(int argc, char *argv[])
{
const char *version = get_version();
- log_notice(LD_GENERAL, "Tor v%s%s running on %s.", version,
#ifdef USE_BUFFEREVENTS
- " (with bufferevents)",
+ log_notice(LD_GENERAL, "Tor v%s (with bufferevents) running on %s.",
+ version, get_uname());
#else
- "",
+ log_notice(LD_GENERAL, "Tor v%s running on %s.", version, get_uname());
#endif
- get_uname());
- log_notice(LD_GENERAL, "WARNING: Tor can't help you if you use it wrong. "
+ log_notice(LD_GENERAL, "Tor can't help you if you use it wrong! "
"Learn how to be safe at "
"https://www.torproject.org/download/download#warning");
@@ -2256,6 +2284,11 @@ tor_init(int argc, char *argv[])
"Expect more bugs than usual.");
}
+#ifdef NON_ANONYMOUS_MODE_ENABLED
+ log(LOG_WARN, LD_GENERAL, "This copy of Tor was compiled to run in a "
+ "non-anonymous mode. It will provide NO ANONYMITY.");
+#endif
+
if (network_init()<0) {
log_err(LD_BUG,"Error initializing network; exiting.");
return -1;
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
index b93b919c13..eafc9b8b74 100644
--- a/src/or/nodelist.c
+++ b/src/or/nodelist.c
@@ -646,24 +646,70 @@ node_exit_policy_rejects_all(const node_t *node)
return 1;
}
-/** Copy the address for <b>node</b> into *<b>addr_out</b>. */
-int
-node_get_addr(const node_t *node, tor_addr_t *addr_out)
+/** Return list of tor_addr_port_t with all OR ports (in the sense IP
+ * addr + TCP port) for <b>node</b>. Caller must free all elements
+ * using tor_free() and free the list using smartlist_free().
+ *
+ * XXX this is potentially a memory fragmentation hog -- if on
+ * critical path consider the option of having the caller allocate the
+ * memory
+ */
+smartlist_t *
+node_get_all_orports(const node_t *node)
+{
+ smartlist_t *sl = smartlist_create();
+
+ if (node->ri != NULL) {
+ if (node->ri->addr != 0) {
+ tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t));
+ tor_addr_from_ipv4h(&ap->addr, node->ri->addr);
+ ap->port = node->ri->or_port;
+ smartlist_add(sl, ap);
+ }
+ if (!tor_addr_is_null(&node->ri->ipv6_addr)) {
+ tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t));
+ tor_addr_copy(&ap->addr, &node->ri->ipv6_addr);
+ ap->port = node->ri->or_port;
+ smartlist_add(sl, ap);
+ }
+ } else if (node->rs != NULL) {
+ tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t));
+ tor_addr_from_ipv4h(&ap->addr, node->rs->addr);
+ ap->port = node->rs->or_port;
+ smartlist_add(sl, ap);
+ }
+
+ return sl;
+}
+
+/** Copy the primary (IPv4) OR port (IP address and TCP port) for
+ * <b>node</b> into *<b>ap_out</b>. */
+void
+node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out)
{
if (node->ri) {
- tor_addr_from_ipv4h(addr_out, node->ri->addr);
- return 0;
- } else if (node->rs) {
- tor_addr_from_ipv4h(addr_out, node->rs->addr);
- return 0;
+ router_get_prim_orport(node->ri, ap_out);
}
- return -1;
+ else if (node->rs) {
+ tor_addr_from_ipv4h(&ap_out->addr, node->rs->addr);
+ ap_out->port = node->rs->or_port;
+ }
+}
+
+/** Wrapper around node_get_prim_orport for backward
+ compatibility. */
+void
+node_get_addr(const node_t *node, tor_addr_t *addr_out)
+{
+ tor_addr_port_t ap;
+ node_get_prim_orport(node, &ap);
+ tor_addr_copy(addr_out, &ap.addr);
}
/** Return the host-order IPv4 address for <b>node</b>, or 0 if it doesn't
* seem to have one. */
uint32_t
-node_get_addr_ipv4h(const node_t *node)
+node_get_prim_addr_ipv4h(const node_t *node)
{
if (node->ri) {
return node->ri->addr;
@@ -673,9 +719,38 @@ node_get_addr_ipv4h(const node_t *node)
return 0;
}
-/** Copy a string representation of the IP address for <b>node</b> into the
- * <b>len</b>-byte buffer at <b>buf</b>.
- */
+/** Copy the preferred OR port (IP address and TCP port) for
+ * <b>node</b> into <b>ap_out</b>. */
+void
+node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out)
+{
+ if (node->ri) {
+ router_get_pref_orport(node->ri, ap_out);
+ } else if (node->rs) {
+ /* No IPv6 in routerstatus_t yet. XXXprop186 ok for private
+ bridges but needs fixing */
+ tor_addr_from_ipv4h(&ap_out->addr, node->rs->addr);
+ ap_out->port = node->rs->or_port;
+ }
+}
+
+/** Copy the preferred IPv6 OR port (address and TCP port) for
+ * <b>node</b> into *<b>ap_out</b>. */
+void
+node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out)
+{
+ if (node->ri) {
+ router_get_pref_ipv6_orport(node->ri, ap_out);
+ } else if (node->rs) {
+ /* No IPv6 in routerstatus_t yet. XXXprop186 ok for private
+ bridges but needs fixing */
+ tor_addr_make_unspec(&ap_out->addr);
+ ap_out->port = 0;
+ }
+}
+
+/** Copy a string representation of an IP address for <b>node</b> into
+ * the <b>len</b>-byte buffer at <b>buf</b>. */
void
node_get_address_string(const node_t *node, char *buf, size_t len)
{
@@ -701,18 +776,6 @@ node_get_declared_uptime(const node_t *node)
return -1;
}
-/** Return <b>node</b>'s declared or_port */
-uint16_t
-node_get_orport(const node_t *node)
-{
- if (node->ri)
- return node->ri->or_port;
- else if (node->rs)
- return node->rs->or_port;
- else
- return 0;
-}
-
/** Return <b>node</b>'s platform string, or NULL if we don't know it. */
const char *
node_get_platform(const node_t *node)
diff --git a/src/or/nodelist.h b/src/or/nodelist.h
index bd2e63953c..1b7549dade 100644
--- a/src/or/nodelist.h
+++ b/src/or/nodelist.h
@@ -37,10 +37,13 @@ int node_get_purpose(const node_t *node);
(node_get_purpose((node)) == ROUTER_PURPOSE_BRIDGE)
int node_is_me(const node_t *node);
int node_exit_policy_rejects_all(const node_t *node);
-int node_get_addr(const node_t *node, tor_addr_t *addr_out);
-uint32_t node_get_addr_ipv4h(const node_t *node);
+smartlist_t *node_get_all_orports(const node_t *node);
+void node_get_prim_orport(const node_t *node, tor_addr_port_t *addr_port_out);
+void node_get_pref_orport(const node_t *node, tor_addr_port_t *addr_port_out);
+void node_get_pref_ipv6_orport(const node_t *node,
+ tor_addr_port_t *addr_port_out);
+uint32_t node_get_prim_addr_ipv4h(const node_t *node);
int node_allows_single_hop_exits(const node_t *node);
-uint16_t node_get_orport(const node_t *node);
const char *node_get_nickname(const node_t *node);
const char *node_get_platform(const node_t *node);
void node_get_address_string(const node_t *node, char *cp, size_t len);
@@ -50,6 +53,10 @@ const smartlist_t *node_get_declared_family(const node_t *node);
smartlist_t *nodelist_get_list(void);
+/* Temporary during transition to multiple addresses. */
+void node_get_addr(const node_t *node, tor_addr_t *addr_out);
+#define node_get_addr_ipv4h(n) node_get_prim_addr_ipv4h((n))
+
/* XXXX These need to move out of routerlist.c */
void nodelist_refresh_countries(void);
void node_set_country(node_t *node);
diff --git a/src/or/or.h b/src/or/or.h
index 67ba62bdd6..63ff5c4b31 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -130,6 +130,10 @@
#define cell_t tor_cell_t
#endif
+#ifdef ENABLE_TOR2WEB_MODE
+#define NON_ANONYMOUS_MODE_ENABLED 1
+#endif
+
/** Length of longest allowable configured nickname. */
#define MAX_NICKNAME_LEN 19
/** Length of a router identity encoded as a hexadecimal digest, plus
@@ -789,10 +793,10 @@ typedef struct rend_data_t {
char rend_cookie[REND_COOKIE_LEN];
} rend_data_t;
-/** Time interval for tracking possible replays of INTRODUCE2 cells.
- * Incoming cells with timestamps half of this interval in the past or
- * future are dropped immediately. */
-#define REND_REPLAY_TIME_INTERVAL (60 * 60)
+/** Time interval for tracking replays of DH public keys received in
+ * INTRODUCE2 cells. Used only to avoid launching multiple
+ * simultaneous attempts to connect to the same rendezvous point. */
+#define REND_REPLAY_TIME_INTERVAL (5 * 60)
/** Used to indicate which way a cell is going on a circuit. */
typedef enum {
@@ -1720,6 +1724,13 @@ typedef struct {
uint16_t or_port; /**< Port for TLS connections. */
uint16_t dir_port; /**< Port for HTTP directory connections. */
+ /* DOCDOC */
+ /* XXXXX187 Actually these should probably be part of a list of addresses,
+ * not just a special case. Use abstractions to access these; don't do it
+ * directly. */
+ tor_addr_t ipv6_addr;
+ uint16_t ipv6_orport;
+
crypto_pk_env_t *onion_pkey; /**< Public RSA key for onions. */
crypto_pk_env_t *identity_pkey; /**< Public RSA key for signing. */
@@ -1751,6 +1762,8 @@ typedef struct {
/** True if, after we have added this router, we should re-launch
* tests for it. */
unsigned int needs_retest_if_added:1;
+ /** True if ipv6_addr:ipv6_orport is preferred. */
+ unsigned int ipv6_preferred:1;
/** Tor can use this router for general positions in circuits; we got it
* from a directory server as usual, or we're an authority and a server
@@ -2831,16 +2844,37 @@ typedef struct port_cfg_t {
int session_group; /**< A session group, or -1 if this port is not in a
* session group. */
+ /* Server port types (or, dir) only: */
+ unsigned int no_advertise : 1;
+ unsigned int no_listen : 1;
+ unsigned int all_addrs : 1;
+ unsigned int ipv4_only : 1;
+ unsigned int ipv6_only : 1;
+
/* Unix sockets only: */
/** Path for an AF_UNIX address */
char unix_addr[FLEXIBLE_ARRAY_MEMBER];
} port_cfg_t;
+/** Ordinary configuration line. */
+#define CONFIG_LINE_NORMAL 0
+/** Appends to previous configuration for the same option, even if we
+ * would ordinary replace it. */
+#define CONFIG_LINE_APPEND 1
+/* Removes all previous configuration for an option. */
+#define CONFIG_LINE_CLEAR 2
+
/** A linked list of lines in a config file. */
typedef struct config_line_t {
char *key;
char *value;
struct config_line_t *next;
+ /** What special treatment (if any) does this line require? */
+ unsigned int command:2;
+ /** If true, subsequent assignments to this linelist should replace
+ * it, not extend it. Set only on the first item in a linelist in an
+ * or_options_t. */
+ unsigned int fragile:1;
} config_line_t;
typedef struct routerset_t routerset_t;
@@ -2873,6 +2907,8 @@ typedef struct {
char *Address; /**< OR only: configured address for this onion router. */
char *PidFile; /**< Where to store PID of Tor process. */
+ int DynamicDHGroups; /**< Dynamic generation of prime moduli for use in DH.*/
+
routerset_t *ExitNodes; /**< Structure containing nicknames, digests,
* country codes and IP address patterns of ORs to
* consider as exits. */
@@ -2930,17 +2966,18 @@ typedef struct {
int DirAllowPrivateAddresses;
char *User; /**< Name of user to run Tor as. */
char *Group; /**< Name of group to run Tor as. */
- int ORPort; /**< Port to listen on for OR connections. */
+ config_line_t *ORPort; /**< Ports to listen on for OR connections. */
config_line_t *SocksPort; /**< Ports to listen on for SOCKS connections. */
/** Ports to listen on for transparent pf/netfilter connections. */
config_line_t *TransPort;
config_line_t *NATDPort; /**< Ports to listen on for transparent natd
* connections. */
- int ControlPort; /**< Port to listen on for control connections. */
+ config_line_t *ControlPort; /**< Port to listen on for control
+ * connections. */
config_line_t *ControlSocket; /**< List of Unix Domain Sockets to listen on
* for control connections. */
int ControlSocketsGroupWritable; /**< Boolean: Are control sockets g+rw? */
- int DirPort; /**< Port to listen on for directory connections. */
+ config_line_t *DirPort; /**< Port to listen on for directory connections. */
config_line_t *DNSPort; /**< Port to listen on for DNS requests. */
int AssumeReachable; /**< Whether to publish our descriptor regardless. */
int AuthoritativeDir; /**< Boolean: is this an authoritative directory? */
@@ -3008,6 +3045,11 @@ typedef struct {
int AllDirActionsPrivate; /**< Should every directory action be sent
* through a Tor circuit? */
+ /** Run in 'tor2web mode'? (I.e. only make client connections to hidden
+ * services, and use a single hop for all hidden-service-related
+ * circuits.) */
+ int Tor2webMode;
+
int ConnLimit; /**< Demanded minimum number of simultaneous connections. */
int _ConnLimit; /**< Maximum allowed number of simultaneous connections. */
int RunAsDaemon; /**< If true, run in the background. (Unix only) */
@@ -3248,6 +3290,8 @@ typedef struct {
disclaimer. This allows a server administrator to show
that they're running Tor and anyone visiting their server
will know this without any specialized knowledge. */
+ int DisableDebuggerAttachment; /**< Currently Linux only specific attempt to
+ disable ptrace; needs BSD testing. */
/** Boolean: if set, we start even if our resolv.conf file is missing
* or broken. */
int ServerDNSAllowBrokenConfig;
@@ -3432,6 +3476,15 @@ typedef struct {
* never use it. If -1, we do what the consensus says. */
int OptimisticData;
+ /** If 1, and we are using IOCP, we set the kernel socket SNDBUF and RCVBUF
+ * to 0 to try to save kernel memory and avoid the dread "Out of buffers"
+ * issue. */
+ int UserspaceIOCPBuffers;
+
+ /** If 1, we accept and launch no external network connections, except on
+ * control ports. */
+ int DisableNetwork;
+
} or_options_t;
/** Persistent state for an onion router, as saved to disk. */
@@ -4017,6 +4070,26 @@ typedef struct rend_encoded_v2_service_descriptor_t {
* introduction point. See also rend_intro_point_t.unreachable_count. */
#define MAX_INTRO_POINT_REACHABILITY_FAILURES 5
+/** The maximum number of distinct INTRODUCE2 cells which a hidden
+ * service's introduction point will receive before it begins to
+ * expire.
+ *
+ * XXX023 Is this number at all sane? */
+#define INTRO_POINT_LIFETIME_INTRODUCTIONS 16384
+
+/** The minimum number of seconds that an introduction point will last
+ * before expiring due to old age. (If it receives
+ * INTRO_POINT_LIFETIME_INTRODUCTIONS INTRODUCE2 cells, it may expire
+ * sooner.)
+ *
+ * XXX023 Should this be configurable? */
+#define INTRO_POINT_LIFETIME_MIN_SECONDS 18*60*60
+/** The maximum number of seconds that an introduction point will last
+ * before expiring due to old age.
+ *
+ * XXX023 Should this be configurable? */
+#define INTRO_POINT_LIFETIME_MAX_SECONDS 24*60*60
+
/** Introduction point information. Used both in rend_service_t (on
* the service side) and in rend_service_descriptor_t (on both the
* client and service side). */
@@ -4036,6 +4109,42 @@ typedef struct rend_intro_point_t {
* circuit to this intro point for some reason other than our
* circuit-build timeout. See also MAX_INTRO_POINT_REACHABILITY_FAILURES. */
unsigned int unreachable_count : 3;
+
+ /** (Service side only) Flag indicating that this intro point was
+ * included in the last HS descriptor we generated. */
+ unsigned int listed_in_last_desc : 1;
+
+ /** (Service side only) Flag indicating that
+ * rend_service_note_removing_intro_point has been called for this
+ * intro point. */
+ unsigned int rend_service_note_removing_intro_point_called : 1;
+
+ /** (Service side only) A digestmap recording the INTRODUCE2 cells
+ * this intro point's circuit has received. Each key is the digest
+ * of the RSA-encrypted part of a received INTRODUCE2 cell; each
+ * value is a pointer to the time_t at which the cell was received.
+ * This digestmap is used to prevent replay attacks. */
+ digestmap_t *accepted_intro_rsa_parts;
+
+ /** (Service side only) The time at which this intro point was first
+ * published, or -1 if this intro point has not yet been
+ * published. */
+ time_t time_published;
+
+ /** (Service side only) The time at which this intro point should
+ * (start to) expire, or -1 if we haven't decided when this intro
+ * point should expire. */
+ time_t time_to_expire;
+
+ /** (Service side only) The time at which we decided that this intro
+ * point should start expiring, or -1 if this intro point is not yet
+ * expiring.
+ *
+ * This field also serves as a flag to indicate that we have decided
+ * to expire this intro point, in case intro_point_should_expire_now
+ * flaps (perhaps due to a clock jump; perhaps due to other
+ * weirdness, or even a (present or future) bug). */
+ time_t time_expiring;
} rend_intro_point_t;
/** Information used to connect to a hidden service. Used on both the
diff --git a/src/or/relay.c b/src/or/relay.c
index ac3114bda5..6cf4b73a5f 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -924,6 +924,7 @@ connection_edge_process_relay_cell_not_open(
}
circuit_log_path(LOG_INFO,LD_APP,TO_ORIGIN_CIRCUIT(circ));
/* don't send a socks reply to transparent conns */
+ tor_assert(entry_conn->socks_request != NULL);
if (!entry_conn->socks_request->has_finished)
connection_ap_handshake_socks_reply(entry_conn, NULL, 0, 0);
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index 6a45207e29..8aae8c5cb5 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -139,8 +139,10 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
tor_assert(rendcirc->rend_data);
tor_assert(!rend_cmp_service_ids(introcirc->rend_data->onion_address,
rendcirc->rend_data->onion_address));
+#ifndef NON_ANONYMOUS_MODE_ENABLED
tor_assert(!(introcirc->build_state->onehop_tunnel));
tor_assert(!(rendcirc->build_state->onehop_tunnel));
+#endif
if (rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1,
&entry) < 1) {
@@ -331,7 +333,9 @@ rend_client_introduction_acked(origin_circuit_t *circ,
}
tor_assert(circ->build_state->chosen_exit);
+#ifndef NON_ANONYMOUS_MODE_ENABLED
tor_assert(!(circ->build_state->onehop_tunnel));
+#endif
tor_assert(circ->rend_data);
if (request_len == 0) {
@@ -343,7 +347,9 @@ rend_client_introduction_acked(origin_circuit_t *circ,
rendcirc = circuit_get_by_rend_query_and_purpose(
circ->rend_data->onion_address, CIRCUIT_PURPOSE_C_REND_READY);
if (rendcirc) { /* remember the ack */
+#ifndef NON_ANONYMOUS_MODE_ENABLED
tor_assert(!(rendcirc->build_state->onehop_tunnel));
+#endif
rendcirc->_base.purpose = CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED;
/* Set timestamp_dirty, because circuit_expire_building expects
* it to specify when a circuit entered the
@@ -529,6 +535,7 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query)
char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
time_t now = time(NULL);
char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64];
+ int tor2web_mode = get_options()->Tor2webMode;
tor_assert(desc_id);
tor_assert(rend_query);
/* Determine responsible dirs. Even if we can't get all we want,
@@ -587,7 +594,8 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query)
directory_initiate_command_routerstatus_rend(hs_dir,
DIR_PURPOSE_FETCH_RENDDESC_V2,
ROUTER_PURPOSE_GENERAL,
- 1, desc_id_base32, NULL, 0, 0,
+ !tor2web_mode, desc_id_base32,
+ NULL, 0, 0,
rend_query);
log_info(LD_REND, "Sending fetch request for v2 descriptor for "
"service '%s' with descriptor ID '%s', auth type %d, "
@@ -884,10 +892,9 @@ rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request,
onion_append_to_cpath(&circ->cpath, hop);
circ->build_state->pending_final_cpath = NULL; /* prevent double-free */
- /* XXXX023 This is a pretty brute-force approach. It'd be better to
- * attach only the connections that are waiting on this circuit, rather
- * than trying to attach them all. See comments bug 743. */
- connection_ap_attach_pending();
+
+ circuit_try_attaching_streams(circ);
+
memset(keys, 0, sizeof(keys));
return 0;
err:
@@ -1059,7 +1066,7 @@ rend_client_get_random_intro_impl(const rend_cache_entry_t *entry,
smartlist_del(usable_nodes, i);
goto again;
}
- new_extend_info = extend_info_from_node(node);
+ new_extend_info = extend_info_from_node(node, 0);
if (!new_extend_info) {
log_info(LD_REND, "We don't have a descriptor for the intro-point relay "
"'%s'; trying another.",
diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c
index 94bb002210..d09e6566c2 100644
--- a/src/or/rendcommon.c
+++ b/src/or/rendcommon.c
@@ -440,6 +440,11 @@ rend_intro_point_free(rend_intro_point_t *intro)
extend_info_free(intro->extend_info);
crypto_free_pk_env(intro->intro_key);
+
+ if (intro->accepted_intro_rsa_parts != NULL) {
+ digestmap_free(intro->accepted_intro_rsa_parts, _tor_free);
+ }
+
tor_free(intro);
}
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index e0c1a8c87a..76caeffd0f 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -26,6 +26,11 @@
static origin_circuit_t *find_intro_circuit(rend_intro_point_t *intro,
const char *pk_digest);
+static rend_intro_point_t *find_intro_point(origin_circuit_t *circ);
+
+static int intro_point_accepted_intro_count(rend_intro_point_t *intro);
+static int intro_point_should_expire_now(rend_intro_point_t *intro,
+ time_t now);
/** Represents the mapping from a virtual port of a rendezvous service to
* a real port on some IP.
@@ -36,8 +41,10 @@ typedef struct rend_service_port_config_t {
tor_addr_t real_addr;
} rend_service_port_config_t;
-/** Try to maintain this many intro points per service if possible. */
-#define NUM_INTRO_POINTS 3
+/** Try to maintain this many intro points per service by default. */
+#define NUM_INTRO_POINTS_DEFAULT 3
+/** Maintain no more than this many intro points per hidden service. */
+#define NUM_INTRO_POINTS_MAX 10
/** If we can't build our intro circuits, don't retry for this long. */
#define INTRO_CIRC_RETRY_PERIOD (60*5)
@@ -51,6 +58,10 @@ typedef struct rend_service_port_config_t {
* rendezvous point before giving up? */
#define MAX_REND_TIMEOUT 30
+/** How many seconds should we wait for new HS descriptors to reach
+ * our clients before we close an expiring intro point? */
+#define INTRO_POINT_EXPIRATION_GRACE_PERIOD 5*60
+
/** Represents a single hidden service running at this OP. */
typedef struct rend_service_t {
/* Fields specified in config file */
@@ -72,17 +83,24 @@ typedef struct rend_service_t {
* introduction points. */
int n_intro_circuits_launched; /**< Count of intro circuits we have
* established in this period. */
+ unsigned int n_intro_points_wanted; /**< Number of intro points this
+ * service wants to have open. */
rend_service_descriptor_t *desc; /**< Current hidden service descriptor. */
time_t desc_is_dirty; /**< Time at which changes to the hidden service
* descriptor content occurred, or 0 if it's
* up-to-date. */
time_t next_upload_time; /**< Scheduled next hidden service descriptor
* upload time. */
- /** Map from digests of Diffie-Hellman values INTRODUCE2 to time_t of when
- * they were received; used to prevent replays. */
- digestmap_t *accepted_intros;
- /** Time at which we last removed expired values from accepted_intros. */
- time_t last_cleaned_accepted_intros;
+ /** Map from digests of Diffie-Hellman values INTRODUCE2 to time_t
+ * of when they were received. Clients may send INTRODUCE1 cells
+ * for the same rendezvous point through two or more different
+ * introduction points; when they do, this digestmap keeps us from
+ * launching multiple simultaneous attempts to connect to the same
+ * rend point. */
+ digestmap_t *accepted_intro_dh_parts;
+ /** Time at which we last removed expired values from
+ * accepted_intro_dh_parts. */
+ time_t last_cleaned_accepted_intro_dh_parts;
} rend_service_t;
/** A list of rend_service_t's for services run on this OP.
@@ -98,6 +116,17 @@ num_rend_services(void)
return smartlist_len(rend_service_list);
}
+/** Return a string identifying <b>service</b>, suitable for use in a
+ * log message. The result does not need to be freed, but may be
+ * overwritten by the next call to this function. */
+static const char *
+rend_service_describe_for_log(rend_service_t *service)
+{
+ /* XXX024 Use this function throughout rendservice.c. */
+ /* XXX024 Return a more useful description? */
+ return safe_str_client(service->service_id);
+}
+
/** Helper: free storage held by a single service authorized client entry. */
static void
rend_authorized_client_free(rend_authorized_client_t *client)
@@ -142,7 +171,7 @@ rend_service_free(rend_service_t *service)
rend_authorized_client_free(c););
smartlist_free(service->clients);
}
- digestmap_free(service->accepted_intros, _tor_free);
+ digestmap_free(service->accepted_intro_dh_parts, _tor_free);
tor_free(service);
}
@@ -319,6 +348,7 @@ rend_config_services(const or_options_t *options, int validate_only)
service->directory = tor_strdup(line->value);
service->ports = smartlist_create();
service->intro_period_started = time(NULL);
+ service->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT;
continue;
}
if (!service) {
@@ -542,16 +572,38 @@ rend_service_update_descriptor(rend_service_t *service)
for (i = 0; i < smartlist_len(service->intro_nodes); ++i) {
rend_intro_point_t *intro_svc = smartlist_get(service->intro_nodes, i);
rend_intro_point_t *intro_desc;
+
+ /* This intro point won't be listed in the descriptor... */
+ intro_svc->listed_in_last_desc = 0;
+
+ if (intro_svc->time_expiring != -1) {
+ /* This intro point is expiring. Don't list it. */
+ continue;
+ }
+
circ = find_intro_circuit(intro_svc, service->pk_digest);
- if (!circ || circ->_base.purpose != CIRCUIT_PURPOSE_S_INTRO)
+ if (!circ || circ->_base.purpose != CIRCUIT_PURPOSE_S_INTRO) {
+ /* This intro point's circuit isn't finished yet. Don't list it. */
continue;
+ }
- /* We have an entirely established intro circuit. */
+ /* ...unless this intro point is listed in the descriptor. */
+ intro_svc->listed_in_last_desc = 1;
+
+ /* We have an entirely established intro circuit. Publish it in
+ * our descriptor. */
intro_desc = tor_malloc_zero(sizeof(rend_intro_point_t));
intro_desc->extend_info = extend_info_dup(intro_svc->extend_info);
if (intro_svc->intro_key)
intro_desc->intro_key = crypto_pk_dup_key(intro_svc->intro_key);
smartlist_add(d->intro_nodes, intro_desc);
+
+ if (intro_svc->time_published == -1) {
+ /* We are publishing this intro point in a descriptor for the
+ * first time -- note the current time in the service's copy of
+ * the intro point. */
+ intro_svc->time_published = time(NULL);
+ }
}
}
@@ -857,15 +909,16 @@ rend_check_authorization(rend_service_t *service,
/** Remove elements from <b>service</b>'s replay cache that are old enough to
* be noticed by timestamp checking. */
static void
-clean_accepted_intros(rend_service_t *service, time_t now)
+clean_accepted_intro_dh_parts(rend_service_t *service, time_t now)
{
const time_t cutoff = now - REND_REPLAY_TIME_INTERVAL;
- service->last_cleaned_accepted_intros = now;
- if (!service->accepted_intros)
+ service->last_cleaned_accepted_intro_dh_parts = now;
+ if (!service->accepted_intro_dh_parts)
return;
- DIGESTMAP_FOREACH_MODIFY(service->accepted_intros, digest, time_t *, t) {
+ DIGESTMAP_FOREACH_MODIFY(service->accepted_intro_dh_parts, digest,
+ time_t *, t) {
if (*t < cutoff) {
tor_free(t);
MAP_DEL_CURRENT(digest);
@@ -873,6 +926,104 @@ clean_accepted_intros(rend_service_t *service, time_t now)
} DIGESTMAP_FOREACH_END;
}
+/** Called when <b>intro</b> will soon be removed from
+ * <b>service</b>'s list of intro points. */
+static void
+rend_service_note_removing_intro_point(rend_service_t *service,
+ rend_intro_point_t *intro)
+{
+ time_t now = time(NULL);
+
+ /* Don't process an intro point twice here. */
+ if (intro->rend_service_note_removing_intro_point_called) {
+ return;
+ } else {
+ intro->rend_service_note_removing_intro_point_called = 1;
+ }
+
+ /* Update service->n_intro_points_wanted based on how long intro
+ * lasted and how many introductions it handled. */
+ if (intro->time_published == -1) {
+ /* This intro point was never used. Don't change
+ * n_intro_points_wanted. */
+ } else {
+ /* We want to increase the number of introduction points service
+ * operates if intro was heavily used, or decrease the number of
+ * intro points if intro was lightly used.
+ *
+ * We consider an intro point's target 'usage' to be
+ * INTRO_POINT_LIFETIME_INTRODUCTIONS introductions in
+ * INTRO_POINT_LIFETIME_MIN_SECONDS seconds. To calculate intro's
+ * fraction of target usage, we divide the fraction of
+ * _LIFETIME_INTRODUCTIONS introductions that it has handled by
+ * the fraction of _LIFETIME_MIN_SECONDS for which it existed.
+ *
+ * Then we take the reciprocal of that fraction of desired usage,
+ * then multiply by a fudge factor of 1.5, to decide how many new
+ * introduction points should ideally replace intro (which is now
+ * closed or soon to be closed). In theory, assuming that
+ * introduction load is distributed equally across all intro
+ * points and ignoring the fact that different intro points are
+ * established and closed at different times, that number of intro
+ * points should bring all of our intro points exactly to our
+ * target usage.
+ *
+ * Then we clamp that number to a number of intro points we might
+ * be willing to replace this intro point with and turn it into an
+ * integer. then we clamp it again to the number of new intro
+ * points we could establish now, then we adjust
+ * service->n_intro_points_wanted and let rend_services_introduce
+ * create the new intro points we want (if any).
+ */
+ double fractional_n_intro_points_wanted_to_replace_this_one =
+ ((((double)now - intro->time_published) /
+ INTRO_POINT_LIFETIME_MIN_SECONDS) *
+ ((intro_point_accepted_intro_count(intro)) /
+ INTRO_POINT_LIFETIME_INTRODUCTIONS)) * 1.5;
+ unsigned int n_intro_points_wanted_to_replace_this_one;
+ unsigned int n_intro_points_wanted_now;
+ unsigned int n_intro_points_really_wanted_now;
+ int n_intro_points_really_replacing_this_one;
+
+ if (fractional_n_intro_points_wanted_to_replace_this_one >
+ NUM_INTRO_POINTS_MAX) {
+ n_intro_points_wanted_to_replace_this_one = NUM_INTRO_POINTS_MAX;
+ } else if (fractional_n_intro_points_wanted_to_replace_this_one < 0) {
+ n_intro_points_wanted_to_replace_this_one = 0;
+ } else {
+ n_intro_points_wanted_to_replace_this_one = (unsigned)
+ fractional_n_intro_points_wanted_to_replace_this_one;
+ }
+
+ n_intro_points_wanted_now =
+ service->n_intro_points_wanted +
+ n_intro_points_wanted_to_replace_this_one - 1;
+
+ if (n_intro_points_wanted_now < NUM_INTRO_POINTS_DEFAULT) {
+ /* XXXX This should be NUM_INTRO_POINTS_MIN instead. Perhaps
+ * another use of NUM_INTRO_POINTS_DEFAULT should be, too. */
+ n_intro_points_really_wanted_now = NUM_INTRO_POINTS_DEFAULT;
+ } else if (n_intro_points_wanted_now > NUM_INTRO_POINTS_MAX) {
+ n_intro_points_really_wanted_now = NUM_INTRO_POINTS_MAX;
+ } else {
+ n_intro_points_really_wanted_now = n_intro_points_wanted_now;
+ }
+
+ n_intro_points_really_replacing_this_one =
+ n_intro_points_really_wanted_now - service->n_intro_points_wanted + 1;
+
+ log_info(LD_REND, "Replacing closing intro point for service %s "
+ "with %d new intro points (wanted %g replacements); "
+ "service will now try to have %u intro points",
+ rend_service_describe_for_log(service),
+ n_intro_points_really_replacing_this_one,
+ fractional_n_intro_points_wanted_to_replace_this_one,
+ n_intro_points_really_wanted_now);
+
+ service->n_intro_points_wanted = n_intro_points_really_wanted_now;
+ }
+}
+
/******
* Handle cells
******/
@@ -890,6 +1041,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
char buf[RELAY_PAYLOAD_SIZE];
char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */
rend_service_t *service;
+ rend_intro_point_t *intro_point;
int r, i, v3_shift = 0;
size_t len, keylen;
crypto_dh_env_t *dh = NULL;
@@ -910,7 +1062,9 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
time_t *access_time;
const or_options_t *options = get_options();
+#ifndef NON_ANONYMOUS_MODE_ENABLED
tor_assert(!(circuit->build_state->onehop_tunnel));
+#endif
tor_assert(circuit->rend_data);
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
@@ -937,7 +1091,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
service = rend_service_get_by_pk_digest(
circuit->rend_data->rend_pk_digest);
if (!service) {
- log_warn(LD_REND, "Got an INTRODUCE2 cell for an unrecognized service %s.",
+ log_warn(LD_BUG, "Internal error: Got an INTRODUCE2 cell on an intro "
+ "circ for an unrecognized service %s.",
escaped(serviceid));
return -1;
}
@@ -962,17 +1117,26 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
return -1;
}
- if (!service->accepted_intros)
- service->accepted_intros = digestmap_new();
+ intro_point = find_intro_point(circuit);
+ if (intro_point == NULL) {
+ log_warn(LD_BUG, "Internal error: Got an INTRODUCE2 cell on an intro circ "
+ "(for service %s) with no corresponding rend_intro_point_t.",
+ escaped(serviceid));
+ return -1;
+ }
+
+ if (!service->accepted_intro_dh_parts)
+ service->accepted_intro_dh_parts = digestmap_new();
+
+ if (!intro_point->accepted_intro_rsa_parts)
+ intro_point->accepted_intro_rsa_parts = digestmap_new();
{
char pkpart_digest[DIGEST_LEN];
- /* Check for replay of PK-encrypted portion. It is slightly naughty to
- use the same digestmap to check for this and for g^x replays, but
- collisions are tremendously unlikely.
- */
+ /* Check for replay of PK-encrypted portion. */
crypto_digest(pkpart_digest, (char*)request+DIGEST_LEN, keylen);
- access_time = digestmap_get(service->accepted_intros, pkpart_digest);
+ access_time = digestmap_get(intro_point->accepted_intro_rsa_parts,
+ pkpart_digest);
if (access_time != NULL) {
log_warn(LD_REND, "Possible replay detected! We received an "
"INTRODUCE2 cell with same PK-encrypted part %d seconds ago. "
@@ -981,7 +1145,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
}
access_time = tor_malloc(sizeof(time_t));
*access_time = now;
- digestmap_set(service->accepted_intros, pkpart_digest, access_time);
+ digestmap_set(intro_point->accepted_intro_rsa_parts,
+ pkpart_digest, access_time);
}
/* Next N bytes is encrypted with service key */
@@ -997,7 +1162,6 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
len = r;
if (*buf == 3) {
/* Version 3 INTRODUCE2 cell. */
- time_t ts = 0;
v3_shift = 1;
auth_type = buf[1];
switch (auth_type) {
@@ -1019,17 +1183,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
log_info(LD_REND, "Unknown authorization type '%d'", auth_type);
}
- /* Check timestamp. */
- ts = ntohl(get_uint32(buf+1+v3_shift));
+ /* Skip the timestamp field. We no longer use it. */
v3_shift += 4;
- if ((now - ts) < -1 * REND_REPLAY_TIME_INTERVAL / 2 ||
- (now - ts) > REND_REPLAY_TIME_INTERVAL / 2) {
- /* This is far more likely to mean that a client's clock is
- * skewed than that a replay attack is in progress. */
- log_info(LD_REND, "INTRODUCE2 cell is too %s. Discarding.",
- (now - ts) < 0 ? "old" : "new");
- return -1;
- }
}
if (*buf == 2 || *buf == 3) {
/* Version 2 INTRODUCE2 cell. */
@@ -1099,7 +1254,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
goto err;
}
- extend_info = extend_info_from_node(node);
+ extend_info = extend_info_from_node(node, 0);
}
if (len != REND_COOKIE_LEN+DH_KEY_LEN) {
@@ -1128,7 +1283,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
/* Check whether there is a past request with the same Diffie-Hellman,
* part 1. */
- access_time = digestmap_get(service->accepted_intros, diffie_hellman_hash);
+ access_time = digestmap_get(service->accepted_intro_dh_parts,
+ diffie_hellman_hash);
if (access_time != NULL) {
/* A Tor client will send a new INTRODUCE1 cell with the same rend
* cookie and DH public key as its previous one if its intro circ
@@ -1150,9 +1306,11 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
* one hour. */
access_time = tor_malloc(sizeof(time_t));
*access_time = now;
- digestmap_set(service->accepted_intros, diffie_hellman_hash, access_time);
- if (service->last_cleaned_accepted_intros + REND_REPLAY_TIME_INTERVAL < now)
- clean_accepted_intros(service, now);
+ digestmap_set(service->accepted_intro_dh_parts,
+ diffie_hellman_hash, access_time);
+ if (service->last_cleaned_accepted_intro_dh_parts + REND_REPLAY_TIME_INTERVAL
+ < now)
+ clean_accepted_intro_dh_parts(service, now);
/* If the service performs client authorization, check included auth data. */
if (service->clients) {
@@ -1394,7 +1552,9 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
crypto_pk_env_t *intro_key;
tor_assert(circuit->_base.purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO);
+#ifndef NON_ANONYMOUS_MODE_ENABLED
tor_assert(!(circuit->build_state->onehop_tunnel));
+#endif
tor_assert(circuit->cpath);
tor_assert(circuit->rend_data);
@@ -1412,7 +1572,8 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
/* If we already have enough introduction circuits for this service,
* redefine this one as a general circuit or close it, depending. */
- if (count_established_intro_points(serviceid) > NUM_INTRO_POINTS) {
+ if (count_established_intro_points(serviceid) >
+ (int)service->n_intro_points_wanted) { /* XXX023 remove cast */
const or_options_t *options = get_options();
if (options->ExcludeNodes) {
/* XXXX in some future version, we can test whether the transition is
@@ -1550,7 +1711,9 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
tor_assert(circuit->_base.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
tor_assert(circuit->cpath);
tor_assert(circuit->build_state);
+#ifndef NON_ANONYMOUS_MODE_ENABLED
tor_assert(!(circuit->build_state->onehop_tunnel));
+#endif
tor_assert(circuit->rend_data);
hop = circuit->build_state->pending_final_cpath;
tor_assert(hop);
@@ -1652,6 +1815,35 @@ find_intro_circuit(rend_intro_point_t *intro, const char *pk_digest)
return NULL;
}
+/** Return a pointer to the rend_intro_point_t corresponding to the
+ * service-side introduction circuit <b>circ</b>. */
+static rend_intro_point_t *
+find_intro_point(origin_circuit_t *circ)
+{
+ const char *serviceid;
+ rend_service_t *service = NULL;
+
+ tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
+ TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_INTRO);
+ tor_assert(circ->rend_data);
+ serviceid = circ->rend_data->onion_address;
+
+ SMARTLIST_FOREACH(rend_service_list, rend_service_t *, s,
+ if (tor_memeq(s->service_id, serviceid, REND_SERVICE_ID_LEN_BASE32)) {
+ service = s;
+ break;
+ });
+
+ if (service == NULL) return NULL;
+
+ SMARTLIST_FOREACH(service->intro_nodes, rend_intro_point_t *, intro_point,
+ if (crypto_pk_cmp_keys(intro_point->intro_key, circ->intro_key) == 0) {
+ return intro_point;
+ });
+
+ return NULL;
+}
+
/** Determine the responsible hidden service directories for the
* rend_encoded_v2_service_descriptor_t's in <b>descs</b> and upload them;
* <b>service_id</b> and <b>seconds_valid</b> are only passed for logging
@@ -1855,6 +2047,64 @@ upload_service_descriptor(rend_service_t *service)
service->desc_is_dirty = 0;
}
+/** Return the number of INTRODUCE2 cells an intro point has
+ * received. */
+static int
+intro_point_accepted_intro_count(rend_intro_point_t *intro)
+{
+ if (intro->accepted_intro_rsa_parts == NULL) {
+ return 0;
+ } else {
+ return digestmap_size(intro->accepted_intro_rsa_parts);
+ }
+}
+
+/** Return non-zero iff <b>intro</b> should 'expire' now (i.e. we
+ * should stop publishing it in new descriptors and eventually close
+ * it). */
+static int
+intro_point_should_expire_now(rend_intro_point_t *intro,
+ time_t now)
+{
+ tor_assert(intro != NULL);
+
+ if (intro->time_published == -1) {
+ /* Don't expire an intro point if we haven't even published it yet. */
+ return 0;
+ }
+
+ if (intro->time_expiring != -1) {
+ /* We've already started expiring this intro point. *Don't* let
+ * this function's result 'flap'. */
+ return 1;
+ }
+
+ if (intro_point_accepted_intro_count(intro) >=
+ INTRO_POINT_LIFETIME_INTRODUCTIONS) {
+ /* This intro point has been used too many times. Expire it now. */
+ return 1;
+ }
+
+ if (intro->time_to_expire == -1) {
+ /* This intro point has been published, but we haven't picked an
+ * expiration time for it. Pick one now. */
+ int intro_point_lifetime_seconds =
+ INTRO_POINT_LIFETIME_MIN_SECONDS +
+ crypto_rand_int(INTRO_POINT_LIFETIME_MAX_SECONDS -
+ INTRO_POINT_LIFETIME_MIN_SECONDS);
+
+ /* Start the expiration timer now, rather than when the intro
+ * point was first published. There shouldn't be much of a time
+ * difference. */
+ intro->time_to_expire = now + intro_point_lifetime_seconds;
+
+ return 0;
+ }
+
+ /* This intro point has a time to expire set already. Use it. */
+ return (now >= intro->time_to_expire);
+}
+
/** For every service, check how many intro points it currently has, and:
* - Pick new intro points as necessary.
* - Launch circuits to any new intro points.
@@ -1866,7 +2116,9 @@ rend_services_introduce(void)
const node_t *node;
rend_service_t *service;
rend_intro_point_t *intro;
- int changed, prev_intro_nodes;
+ int intro_point_set_changed, prev_intro_nodes;
+ unsigned int n_intro_points_unexpired;
+ unsigned int n_intro_points_to_open;
smartlist_t *intro_nodes;
time_t now;
const or_options_t *options = get_options();
@@ -1879,7 +2131,16 @@ rend_services_introduce(void)
service = smartlist_get(rend_service_list, i);
tor_assert(service);
- changed = 0;
+
+ /* intro_point_set_changed becomes non-zero iff the set of intro
+ * points to be published in service's descriptor has changed. */
+ intro_point_set_changed = 0;
+
+ /* n_intro_points_unexpired collects the number of non-expiring
+ * intro points we have, so that we know how many new intro
+ * circuits we need to launch for this service. */
+ n_intro_points_unexpired = 0;
+
if (now > service->intro_period_started+INTRO_CIRC_RETRY_PERIOD) {
/* One period has elapsed; we can try building circuits again. */
service->intro_period_started = now;
@@ -1893,59 +2154,119 @@ rend_services_introduce(void)
/* Find out which introduction points we have in progress for this
service. */
- for (j=0; j < smartlist_len(service->intro_nodes); ++j) {
- intro = smartlist_get(service->intro_nodes, j);
+ SMARTLIST_FOREACH_BEGIN(service->intro_nodes, rend_intro_point_t *,
+ intro) {
+ origin_circuit_t *intro_circ =
+ find_intro_circuit(intro, service->pk_digest);
+
+ if (intro->time_expiring + INTRO_POINT_EXPIRATION_GRACE_PERIOD > now) {
+ /* This intro point has completely expired. Remove it, and
+ * mark the circuit for close if it's still alive. */
+ if (intro_circ != NULL) {
+ circuit_mark_for_close(TO_CIRCUIT(intro_circ),
+ END_CIRC_REASON_FINISHED);
+ }
+ rend_intro_point_free(intro);
+ intro = NULL; /* SMARTLIST_DEL_CURRENT takes a name, not a value. */
+ SMARTLIST_DEL_CURRENT(service->intro_nodes, intro);
+ /* We don't need to set intro_point_set_changed here, because
+ * this intro point wouldn't have been published in a current
+ * descriptor anyway. */
+ continue;
+ }
+
node = node_get_by_id(intro->extend_info->identity_digest);
- if (!node || !find_intro_circuit(intro, service->pk_digest)) {
- log_info(LD_REND,"Giving up on %s as intro point for %s.",
+ if (!node || !intro_circ) {
+ int removing_this_intro_point_changes_the_intro_point_set = 1;
+ log_info(LD_REND, "Giving up on %s as intro point for %s"
+ " (circuit disappeared).",
safe_str_client(extend_info_describe(intro->extend_info)),
safe_str_client(service->service_id));
- if (service->desc) {
- SMARTLIST_FOREACH(service->desc->intro_nodes, rend_intro_point_t *,
- dintro, {
- if (tor_memeq(dintro->extend_info->identity_digest,
- intro->extend_info->identity_digest, DIGEST_LEN)) {
- log_info(LD_REND, "The intro point we are giving up on was "
- "included in the last published descriptor. "
- "Marking current descriptor as dirty.");
- service->desc_is_dirty = now;
- }
- });
+ rend_service_note_removing_intro_point(service, intro);
+ if (intro->time_expiring != -1) {
+ log_info(LD_REND, "We were already expiring the intro point; "
+ "no need to mark the HS descriptor as dirty over this.");
+ removing_this_intro_point_changes_the_intro_point_set = 0;
+ } else if (intro->listed_in_last_desc) {
+ log_info(LD_REND, "The intro point we are giving up on was "
+ "included in the last published descriptor. "
+ "Marking current descriptor as dirty.");
+ service->desc_is_dirty = now;
}
rend_intro_point_free(intro);
- smartlist_del(service->intro_nodes,j--);
- changed = 1;
+ intro = NULL; /* SMARTLIST_DEL_CURRENT takes a name, not a value. */
+ SMARTLIST_DEL_CURRENT(service->intro_nodes, intro);
+ if (removing_this_intro_point_changes_the_intro_point_set)
+ intro_point_set_changed = 1;
}
+
+ if (intro != NULL && intro_point_should_expire_now(intro, now)) {
+ log_info(LD_REND, "Expiring %s as intro point for %s.",
+ safe_str_client(extend_info_describe(intro->extend_info)),
+ safe_str_client(service->service_id));
+
+ rend_service_note_removing_intro_point(service, intro);
+
+ /* The polite (and generally Right) way to expire an intro
+ * point is to establish a new one to replace it, publish a
+ * new descriptor that doesn't list any expiring intro points,
+ * and *then*, once our upload attempts for the new descriptor
+ * have ended (whether in success or failure), close the
+ * expiring intro points.
+ *
+ * Unfortunately, we can't find out when the new descriptor
+ * has actually been uploaded, so we'll have to settle for a
+ * five-minute timer. Start it. XXX023 This sucks. */
+ intro->time_expiring = now;
+
+ intro_point_set_changed = 1;
+ }
+
+ if (intro != NULL && intro->time_expiring == -1)
+ ++n_intro_points_unexpired;
+
if (node)
smartlist_add(intro_nodes, (void*)node);
- }
-
- /* We have enough intro points, and the intro points we thought we had were
- * all connected.
- */
- if (!changed && smartlist_len(service->intro_nodes) >= NUM_INTRO_POINTS) {
- /* We have all our intro points! Start a fresh period and reset the
- * circuit count. */
+ } SMARTLIST_FOREACH_END(intro);
+
+ if (!intro_point_set_changed &&
+ (n_intro_points_unexpired >= service->n_intro_points_wanted)) {
+ /* We have enough intro circuits in progress, and none of our
+ * intro circuits have died since the last call to
+ * rend_services_introduce! Start a fresh period and reset the
+ * circuit count.
+ *
+ * XXXX WTF? */
service->intro_period_started = now;
service->n_intro_circuits_launched = 0;
continue;
}
- /* Remember how many introduction circuits we started with. */
+ /* Remember how many introduction circuits we started with.
+ *
+ * prev_intro_nodes serves a different purpose than
+ * n_intro_points_unexpired -- this variable tells us where our
+ * previously-created intro points end and our new ones begin in
+ * the intro-point list, so we don't have to launch the circuits
+ * at the same time as we create the intro points they correspond
+ * to. XXXX This is daft. */
prev_intro_nodes = smartlist_len(service->intro_nodes);
+
/* We have enough directory information to start establishing our
- * intro points. We want to end up with three intro points, but if
- * we're just starting, we launch five and pick the first three that
- * complete.
+ * intro points. We want to end up with n_intro_points_wanted
+ * intro points, but if we're just starting, we launch two extra
+ * circuits and use the first n_intro_points_wanted that complete.
*
* The ones after the first three will be converted to 'general'
* internal circuits in rend_service_intro_has_opened(), and then
* we'll drop them from the list of intro points next time we
* go through the above "find out which introduction points we have
* in progress" loop. */
-#define NUM_INTRO_POINTS_INIT (NUM_INTRO_POINTS + 2)
- for (j=prev_intro_nodes; j < (prev_intro_nodes == 0 ?
- NUM_INTRO_POINTS_INIT : NUM_INTRO_POINTS); ++j) {
+ n_intro_points_to_open = (service->n_intro_points_wanted +
+ (prev_intro_nodes == 0 ? 2 : 0));
+ for (j = (int)n_intro_points_unexpired;
+ j < (int)n_intro_points_to_open;
+ ++j) { /* XXXX remove casts */
router_crn_flags_t flags = CRN_NEED_UPTIME|CRN_NEED_DESC;
if (get_options()->_AllowInvalid & ALLOW_INVALID_INTRODUCTION)
flags |= CRN_ALLOW_INVALID;
@@ -1953,16 +2274,21 @@ rend_services_introduce(void)
options->ExcludeNodes, flags);
if (!node) {
log_warn(LD_REND,
- "Could only establish %d introduction points for %s.",
- smartlist_len(service->intro_nodes), service->service_id);
+ "Could only establish %d introduction points for %s; "
+ "wanted %u.",
+ smartlist_len(service->intro_nodes), service->service_id,
+ n_intro_points_to_open);
break;
}
- changed = 1;
+ intro_point_set_changed = 1;
smartlist_add(intro_nodes, (void*)node);
intro = tor_malloc_zero(sizeof(rend_intro_point_t));
- intro->extend_info = extend_info_from_node(node);
+ intro->extend_info = extend_info_from_node(node, 0);
intro->intro_key = crypto_new_pk_env();
tor_assert(!crypto_pk_generate_key(intro->intro_key));
+ intro->time_published = -1;
+ intro->time_to_expire = -1;
+ intro->time_expiring = -1;
smartlist_add(service->intro_nodes, intro);
log_info(LD_REND, "Picked router %s as an intro point for %s.",
safe_str_client(node_describe(node)),
@@ -1970,7 +2296,7 @@ rend_services_introduce(void)
}
/* If there's no need to launch new circuits, stop here. */
- if (!changed)
+ if (!intro_point_set_changed)
continue;
/* Establish new introduction points. */
diff --git a/src/or/router.c b/src/or/router.c
index b6b96a5fff..a08a2009a7 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -484,6 +484,16 @@ v3_authority_check_key_expiry(void)
last_warned = now;
}
+int
+router_initialize_tls_context(void)
+{
+ return tor_tls_context_init(public_server_mode(get_options()),
+ get_tlsclient_identity_key(),
+ server_mode(get_options()) ?
+ get_server_identity_key() : NULL,
+ MAX_SSL_KEY_LIFETIME_ADVERTISED);
+}
+
/** Initialize all OR private keys, and the TLS context, as necessary.
* On OPs, this only initializes the tls context. Return 0 on success,
* or -1 if Tor should die.
@@ -530,10 +540,7 @@ init_keys(void)
}
set_client_identity_key(prkey);
/* Create a TLS context. */
- if (tor_tls_context_init(0,
- get_tlsclient_identity_key(),
- NULL,
- MAX_SSL_KEY_LIFETIME_ADVERTISED) < 0) {
+ if (router_initialize_tls_context() < 0) {
log_err(LD_GENERAL,"Error creating TLS context for Tor client.");
return -1;
}
@@ -626,13 +633,11 @@ init_keys(void)
tor_free(keydir);
/* 3. Initialize link key and TLS context. */
- if (tor_tls_context_init(public_server_mode(options),
- get_tlsclient_identity_key(),
- get_server_identity_key(),
- MAX_SSL_KEY_LIFETIME_ADVERTISED) < 0) {
+ if (router_initialize_tls_context() < 0) {
log_err(LD_GENERAL,"Error initializing TLS context");
return -1;
}
+
/* 4. Build our router descriptor. */
/* Must be called after keys are initialized. */
mydesc = router_get_my_descriptor();
@@ -780,7 +785,7 @@ check_whether_dirport_reachable(void)
const or_options_t *options = get_options();
return !options->DirPort ||
options->AssumeReachable ||
- we_are_hibernating() ||
+ net_is_disabled() ||
can_reach_dir_port;
}
@@ -806,7 +811,7 @@ decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port)
return 0;
if (authdir_mode(options)) /* always publish */
return dir_port;
- if (we_are_hibernating())
+ if (net_is_disabled())
return 0;
if (!check_whether_dirport_reachable())
return 0;
@@ -888,7 +893,8 @@ consider_testing_reachability(int test_or, int test_dir)
log_info(LD_CIRC, "Testing %s of my ORPort: %s:%d.",
!orport_reachable ? "reachability" : "bandwidth",
me->address, me->or_port);
- ei = extend_info_from_router(me);
+ /* XXX IPv6 self testing IPv6 orports will need pref_addr */
+ ei = extend_info_from_router(me, 0);
circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei,
CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL);
extend_info_free(ei);
@@ -974,6 +980,14 @@ router_perform_bandwidth_test(int num_circs, time_t now)
}
}
+/** Return true iff our network is in some sense disabled: either we're
+ * hibernating, entering hibernation, or */
+int
+net_is_disabled(void)
+{
+ return get_options()->DisableNetwork || we_are_hibernating();
+}
+
/** Return true iff we believe ourselves to be an authoritative
* directory server.
*/
@@ -1070,7 +1084,7 @@ int
server_mode(const or_options_t *options)
{
if (options->ClientOnly) return 0;
- return (options->ORPort != 0 || options->ORListenAddress);
+ return (options->ORPort || options->ORListenAddress);
}
/** Return true iff we are trying to be a non-bridge server.
@@ -1121,7 +1135,14 @@ int
proxy_mode(const or_options_t *options)
{
(void)options;
- return smartlist_len(get_configured_client_ports()) > 0;
+ SMARTLIST_FOREACH_BEGIN(get_configured_ports(), const port_cfg_t *, p) {
+ if (p->type == CONN_TYPE_AP_LISTENER ||
+ p->type == CONN_TYPE_AP_TRANS_LISTENER ||
+ p->type == CONN_TYPE_AP_DNS_LISTENER ||
+ p->type == CONN_TYPE_AP_NATD_LISTENER)
+ return 1;
+ } SMARTLIST_FOREACH_END(p);
+ return 0;
}
/** Decide if we're a publishable server. We are a publishable server if:
@@ -1180,17 +1201,21 @@ consider_publishable_server(int force)
/** Return the port that we should advertise as our ORPort; this is either
* the one configured in the ORPort option, or the one we actually bound to
- * if ORPort is "auto". */
+ * if ORPort is "auto".
+ */
uint16_t
router_get_advertised_or_port(const or_options_t *options)
{
- if (options->ORPort == CFG_AUTO_PORT) {
+ int port = get_primary_or_port();
+ (void)options;
+
+ if (port == CFG_AUTO_PORT) {
connection_t *c = connection_get_by_type(CONN_TYPE_OR_LISTENER);
if (c)
return c->port;
return 0;
}
- return options->ORPort;
+ return port;
}
/** Return the port that we should advertise as our DirPort;
@@ -1201,15 +1226,18 @@ router_get_advertised_or_port(const or_options_t *options)
uint16_t
router_get_advertised_dir_port(const or_options_t *options, uint16_t dirport)
{
- if (!options->DirPort)
+ int dirport_configured = get_primary_dir_port();
+ (void)options;
+
+ if (!dirport_configured)
return dirport;
- if (options->DirPort == CFG_AUTO_PORT) {
+ if (dirport_configured == CFG_AUTO_PORT) {
connection_t *c = connection_get_by_type(CONN_TYPE_DIR_LISTENER);
if (c)
return c->port;
return 0;
}
- return options->DirPort;
+ return dirport_configured;
}
/*
@@ -1466,6 +1494,24 @@ router_rebuild_descriptor(int force)
ri->cache_info.published_on = time(NULL);
ri->onion_pkey = crypto_pk_dup_key(get_onion_key()); /* must invoke from
* main thread */
+ if (options->BridgeRelay) {
+ /* For now, only bridges advertise an ipv6 or-address. And only one. */
+ const port_cfg_t *ipv6_orport = NULL;
+ SMARTLIST_FOREACH_BEGIN(get_configured_ports(), const port_cfg_t *, p) {
+ if (p->type == CONN_TYPE_OR_LISTENER &&
+ ! p->no_advertise &&
+ ! p->ipv4_only &&
+ tor_addr_family(&p->addr) == AF_INET6 &&
+ ! tor_addr_is_internal(&p->addr, 1)) {
+ ipv6_orport = p;
+ break;
+ }
+ } SMARTLIST_FOREACH_END(p);
+ if (ipv6_orport) {
+ tor_addr_copy(&ri->ipv6_addr, &ipv6_orport->addr);
+ ri->ipv6_orport = ipv6_orport->port;
+ }
+ }
ri->identity_pkey = crypto_pk_dup_key(get_server_identity_key());
if (crypto_pk_get_digest(ri->identity_pkey,
ri->cache_info.identity_digest)<0) {
@@ -1875,6 +1921,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
int result=0;
addr_policy_t *tmpe;
char *family_line;
+ char *extra_or_address = NULL;
const or_options_t *options = get_options();
/* Make sure the identity key matches the one in the routerinfo. */
@@ -1927,9 +1974,22 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
router->cache_info.extra_info_digest, DIGEST_LEN);
}
+ if (router->ipv6_orport &&
+ tor_addr_family(&router->ipv6_addr) == AF_INET6) {
+ char addr[TOR_ADDR_BUF_LEN];
+ const char *a;
+ a = tor_addr_to_str(addr, &router->ipv6_addr, sizeof(addr), 1);
+ if (a) {
+ tor_asprintf(&extra_or_address,
+ "or-address %s:%d\n", a, router->ipv6_orport);
+ log_notice(LD_OR, "My line is <%s>", extra_or_address);
+ }
+ }
+
/* Generate the easy portion of the router descriptor. */
result = tor_snprintf(s, maxlen,
"router %s %s %d 0 %d\n"
+ "%s"
"platform %s\n"
"opt protocols Link 1 2 Circuit 1\n"
"published %s\n"
@@ -1944,6 +2004,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
router->address,
router->or_port,
decide_to_advertise_dirport(options, router->dir_port),
+ extra_or_address ? extra_or_address : "",
router->platform,
published,
fingerprint,
@@ -1964,6 +2025,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
tor_free(family_line);
tor_free(onion_pkey);
tor_free(identity_pkey);
+ tor_free(extra_or_address);
if (result < 0) {
log_warn(LD_BUG,"descriptor snprintf #1 ran out of room!");
@@ -2059,6 +2121,52 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
return (int)written+1;
}
+/** Copy the primary (IPv4) OR port (IP address and TCP port) for
+ * <b>router</b> into *<b>ap_out</b>. */
+void
+router_get_prim_orport(const routerinfo_t *router, tor_addr_port_t *ap_out)
+{
+ tor_assert(ap_out != NULL);
+ tor_addr_from_ipv4h(&ap_out->addr, router->addr);
+ ap_out->port = router->or_port;
+}
+
+/** Return 1 if we prefer the IPv6 address and OR TCP port of
+ * <b>router</b>, else 0.
+ *
+ * We prefer the IPv6 address if the router has one and
+ * i) the routerinfo_t says so
+ * or
+ * ii) the router has no IPv4 address. */
+int
+router_ipv6_preferred(const routerinfo_t *router)
+{
+ return (!tor_addr_is_null(&router->ipv6_addr)
+ && (router->ipv6_preferred || router->addr == 0));
+}
+
+/** Copy the preferred OR port (IP address and TCP port) for
+ * <b>router</b> into *<b>addr_out</b>. */
+void
+router_get_pref_orport(const routerinfo_t *router, tor_addr_port_t *ap_out)
+{
+ if (router_ipv6_preferred(router))
+ router_get_pref_ipv6_orport(router, ap_out);
+ else
+ router_get_prim_orport(router, ap_out);
+}
+
+/** Copy the preferred IPv6 OR port (IP address and TCP port) for
+ * <b>router</b> into *<b>ap_out</b>. */
+void
+router_get_pref_ipv6_orport(const routerinfo_t *router,
+ tor_addr_port_t *ap_out)
+{
+ tor_assert(ap_out != NULL);
+ tor_addr_copy(&ap_out->addr, &router->ipv6_addr);
+ ap_out->port = router->ipv6_orport;
+}
+
/** Load the contents of <b>filename</b>, find the last line starting with
* <b>end_line</b>, ensure that its timestamp is not more than 25 hours in
* the past or more than 1 hour in the future with respect to <b>now</b>,
diff --git a/src/or/router.h b/src/or/router.h
index f9d156cb09..d426b25da6 100644
--- a/src/or/router.h
+++ b/src/or/router.h
@@ -30,6 +30,7 @@ crypto_pk_env_t *init_key_from_file(const char *fname, int generate,
int severity);
void v3_authority_check_key_expiry(void);
+int router_initialize_tls_context(void);
int init_keys(void);
int check_whether_orport_reachable(void);
@@ -39,6 +40,8 @@ void router_orport_found_reachable(void);
void router_dirport_found_reachable(void);
void router_perform_bandwidth_test(int num_circs, time_t now);
+int net_is_disabled(void);
+
int authdir_mode(const or_options_t *options);
int authdir_mode_v1(const or_options_t *options);
int authdir_mode_v2(const or_options_t *options);
@@ -82,6 +85,13 @@ int router_pick_published_address(const or_options_t *options, uint32_t *addr);
int router_rebuild_descriptor(int force);
int router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
crypto_pk_env_t *ident_key);
+void router_get_prim_orport(const routerinfo_t *router,
+ tor_addr_port_t *addr_port_out);
+void router_get_pref_orport(const routerinfo_t *router,
+ tor_addr_port_t *addr_port_out);
+void router_get_pref_ipv6_orport(const routerinfo_t *router,
+ tor_addr_port_t *addr_port_out);
+int router_ipv6_preferred(const routerinfo_t *router);
int extrainfo_dump_to_string(char **s, extrainfo_t *extrainfo,
crypto_pk_env_t *ident_key);
int is_legal_nickname(const char *s);
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index d97b978f43..689df99c57 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -3244,7 +3244,7 @@ router_set_status(const char *digest, int up)
log_debug(LD_DIR,"Marking router %s as %s.",
node_describe(node), up ? "up" : "down");
#endif
- if (!up && node_is_me(node) && !we_are_hibernating())
+ if (!up && node_is_me(node) && !net_is_disabled())
log_warn(LD_NET, "We just marked ourself as down. Are your external "
"addresses reachable?");
node->is_running = up;
@@ -4009,6 +4009,8 @@ signed_desc_digest_is_recognized(signed_descriptor_t *desc)
void
update_all_descriptor_downloads(time_t now)
{
+ if (get_options()->DisableNetwork)
+ return;
update_router_descriptor_downloads(now);
update_microdesc_downloads(now);
launch_dummy_descriptor_download_as_needed(now, get_options());
@@ -4021,6 +4023,8 @@ routerlist_retry_directory_downloads(time_t now)
{
router_reset_status_download_failures();
router_reset_descriptor_download_failures();
+ if (get_options()->DisableNetwork)
+ return;
update_networkstatus_downloads(now);
update_all_descriptor_downloads(now);
}
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 4ea7b964cf..678b11971b 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -64,6 +64,7 @@ typedef enum {
K_DIR_OPTIONS,
K_CLIENT_VERSIONS,
K_SERVER_VERSIONS,
+ K_OR_ADDRESS,
K_P,
K_R,
K_S,
@@ -286,6 +287,7 @@ static token_rule_t routerdesc_token_table[] = {
T01("family", K_FAMILY, ARGS, NO_OBJ ),
T01("caches-extra-info", K_CACHES_EXTRA_INFO, NO_ARGS, NO_OBJ ),
+ T0N("or-address", K_OR_ADDRESS, GE(1), NO_OBJ ),
T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
T1( "bandwidth", K_BANDWIDTH, GE(3), NO_OBJ ),
@@ -541,6 +543,7 @@ static int router_get_hashes_impl(const char *s, size_t s_len,
const char *start_str, const char *end_str,
char end_char);
static void token_clear(directory_token_t *tok);
+static smartlist_t *find_all_by_keyword(smartlist_t *s, directory_keyword k);
static smartlist_t *find_all_exitpolicy(smartlist_t *s);
static directory_token_t *_find_by_keyword(smartlist_t *s,
directory_keyword keyword,
@@ -1506,6 +1509,27 @@ router_parse_entry_from_string(const char *s, const char *end,
"older Tors.");
goto err;
}
+ {
+ smartlist_t *or_addresses = find_all_by_keyword(tokens, K_OR_ADDRESS);
+ if (or_addresses) {
+ SMARTLIST_FOREACH_BEGIN(or_addresses, directory_token_t *, t) {
+ tor_addr_t a;
+ maskbits_t bits;
+ uint16_t port_min, port_max;
+ /* XXXX Prop186 the full spec allows much more than this. */
+ if (tor_addr_parse_mask_ports(t->args[0], &a, &bits, &port_min,
+ &port_max) == AF_INET6 &&
+ bits == 128 &&
+ port_min == port_max) {
+ /* Okay, this is one we can understand. */
+ tor_addr_copy(&router->ipv6_addr, &a);
+ router->ipv6_orport = port_min;
+ break;
+ }
+ } SMARTLIST_FOREACH_END(t);
+ smartlist_free(or_addresses);
+ }
+ }
exit_policy_tokens = find_all_exitpolicy(tokens);
if (!smartlist_len(exit_policy_tokens)) {
log_warn(LD_DIR, "No exit policy tokens in descriptor.");
@@ -4134,6 +4158,20 @@ _find_by_keyword(smartlist_t *s, directory_keyword keyword,
return tok;
}
+/** DOCDOC */
+static smartlist_t *
+find_all_by_keyword(smartlist_t *s, directory_keyword k)
+{
+ smartlist_t *out = NULL;
+ SMARTLIST_FOREACH(s, directory_token_t *, t,
+ if (t->tp == k) {
+ if (!out)
+ out = smartlist_create();
+ smartlist_add(out, t);
+ });
+ return out;
+}
+
/** Return a newly allocated smartlist of all accept or reject tokens in
* <b>s</b>.
*/
diff --git a/src/or/transports.c b/src/or/transports.c
index 6e8200f407..abf9d884f7 100644
--- a/src/or/transports.c
+++ b/src/or/transports.c
@@ -4,6 +4,83 @@
/**
* \file transports.c
* \brief Pluggable Transports related code.
+ *
+ * \details
+ * Each managed proxy is represented by a <b>managed_proxy_t</b>.
+ * Each managed proxy can support multiple transports.
+ * Each managed proxy gets configured through a multistep process.
+ *
+ * ::managed_proxy_list contains all the managed proxies this tor
+ * instance is supporting.
+ * In the ::managed_proxy_list there are ::unconfigured_proxies_n
+ * managed proxies that are still unconfigured.
+ *
+ * In every run_scheduled_event() tick, we attempt to launch and then
+ * configure the unconfiged managed proxies, using the configuration
+ * protocol defined in the 180_pluggable_transport.txt proposal. A
+ * managed proxy might need several ticks to get fully configured.
+ *
+ * When a managed proxy is fully configured, we register all its
+ * transports to the circuitbuild.c subsystem. At that point the
+ * transports are owned by the circuitbuild.c subsystem.
+ *
+ * When a managed proxy fails to follow the 180 configuration
+ * protocol, it gets marked as broken and gets destroyed.
+ *
+ * <b>In a little more detail:</b>
+ *
+ * While we are serially parsing torrc, we store all the transports
+ * that a proxy should spawn in its <em>transports_to_launch</em>
+ * element.
+ *
+ * When we finish reading the torrc, we spawn the managed proxy and
+ * expect {S,C}METHOD lines from its output. We add transports
+ * described by METHOD lines to its <em>transports</em> element, as
+ * transport_t structs.
+ *
+ * When the managed proxy stops spitting METHOD lines (signified by a
+ * '{S,C}METHODS DONE' message) we register all the transports
+ * collected to the circuitbuild.c subsystem. At this point, the
+ * pointers to transport_t can be transformed into dangling pointers
+ * at any point by the circuitbuild.c subsystem, and so we replace all
+ * transport_t pointers with strings describing the transport names.
+ * We can still go from a transport name to a transport_t using the
+ * fact that each transport name uniquely identifies a transport_t.
+ *
+ * <b>In even more detail, this is what happens when a SIGHUP
+ * occurs:</b>
+ *
+ * We immediately destroy all unconfigured proxies (We shouldn't have
+ * unconfigured proxies in the first place, except when SIGHUP rings
+ * immediately after tor is launched.).
+ *
+ * We mark all managed proxies and transports to signify that they
+ * must be removed if they don't contribute by the new torrc
+ * (we mark using the <b>marked_for_removal</b> element).
+ * We also mark all managed proxies to signify that they might need to
+ * be restarted so that they end up supporting all the transports the
+ * new torrc wants them to support (using the <b>got_hup</b> element).
+ * We also clear their <b>transports_to_launch</b> list so that we can
+ * put there the transports we need to launch according to the new
+ * torrc.
+ *
+ * We then start parsing torrc again.
+ *
+ * Everytime we encounter a transport line using a known pre-SIGHUP
+ * managed proxy, we cleanse that proxy from the removal mark.
+ * We also mark it as unconfigured so that on the next scheduled
+ * events tick, we investigate whether we need to restart the proxy
+ * so that it also spawns the new transports.
+ * If the post-SIGHUP <b>transports_to_launch</b> list is identical to
+ * the pre-SIGHUP one, it means that no changes were introduced to
+ * this proxy during the SIGHUP and no restart has to take place.
+ *
+ * During the post-SIGHUP torrc parsing, we unmark all transports
+ * spawned by managed proxies that we find in our torrc.
+ * We do that so that if we don't need to restart a managed proxy, we
+ * can continue using its old transports normally.
+ * If we end up restarting the proxy, we destroy and unregister all
+ * old transports from the circuitbuild.c subsystem.
**/
#define PT_PRIVATE
@@ -12,12 +89,20 @@
#include "circuitbuild.h"
#include "transports.h"
#include "util.h"
+#include "router.h"
-static void set_managed_proxy_environment(char ***envp,
+#ifdef MS_WINDOWS
+static void set_managed_proxy_environment(LPVOID *envp,
const managed_proxy_t *mp);
+#else
+static int set_managed_proxy_environment(char ***envp,
+ const managed_proxy_t *mp);
+#endif
+
static INLINE int proxy_configuration_finished(const managed_proxy_t *mp);
-static void managed_proxy_destroy(managed_proxy_t *mp);
+static void managed_proxy_destroy(managed_proxy_t *mp,
+ int also_terminate_process);
static void handle_finished_proxy(managed_proxy_t *mp);
static void configure_proxy(managed_proxy_t *mp);
@@ -56,84 +141,6 @@ static smartlist_t *managed_proxy_list = NULL;
/** Number of still unconfigured proxies. */
static int unconfigured_proxies_n = 0;
-/** "The main idea is:"
-
- Each managed proxy is represented by a 'managed_proxy_t'.
- Each managed proxy can support multiple transports.
- Each managed proxy gets configured through a multistep process.
-
- 'managed_proxy_list' contains all the managed proxies this tor
- instance is supporting.
- In the 'managed_proxy_list' there are 'unconfigured_proxies_n'
- managed proxies that are still unconfigured.
-
- In every run_scheduled_event() tick, we attempt to launch and then
- configure the unconfiged managed proxies, using the configuration
- protocol defined in the 180_pluggable_transport.txt proposal. A
- managed proxy might need several ticks to get fully configured.
-
- When a managed proxy is fully configured, we register all its
- transports to the circuitbuild.c subsystem. At that point the
- transports are owned by the circuitbuild.c subsystem.
-
- When a managed proxy fails to follow the 180 configuration
- protocol, it gets marked as broken and gets destroyed.
-
- "In a little more technical detail:"
-
- While we are serially parsing torrc, we store all the transports
- that a proxy should spawn in its 'transports_to_launch' element.
-
- When we finish reading the torrc, we spawn the managed proxy and
- expect {S,C}METHOD lines from its output. We add transports
- described by METHOD lines to its 'transports' element, as
- 'transport_t' structs.
-
- When the managed proxy stops spitting METHOD lines (signified by a
- '{S,C}METHODS DONE' message) we register all the transports
- collected to the circuitbuild.c subsystem. At this point, the
- 'transport_t's can be transformed into dangling pointers at any
- point by the circuitbuild.c subsystem, and so we replace all
- 'transport_t's with strings describing the transport names. We
- can still go from a transport name to a 'transport_t' using the
- fact that transport names uniquely identify 'transport_t's.
-
- "In even more technical detail I shall describe what happens when
- the SIGHUP bell tolls:"
-
- We immediately destroy all unconfigured proxies (We shouldn't have
- unconfigured proxies in the first place, except when SIGHUP rings
- immediately after tor is launched.).
-
- We mark all managed proxies and transports to signify that they
- must be removed if they don't contribute by the new torrc
- (marked_for_removal).
- We also mark all managed proxies to signify that they might need
- to be restarted so that they end up supporting all the transports
- the new torrc wants them to support (got_hup).
- We also clear their 'transports_to_launch' list so that we can put
- there the transports we need to launch according to the new torrc.
-
- We then start parsing torrc again.
-
- Everytime we encounter a transport line using a known pre-SIGHUP
- managed proxy, we cleanse that proxy from the removal mark.
-
- We also mark it as unconfigured so that on the next scheduled
- events tick, we investigate whether we need to restart the proxy
- so that it also spawns the new transports.
- If the post-SIGHUP 'transports_to_launch' list is identical to the
- pre-SIGHUP one, it means that no changes were introduced to this
- proxy during the SIGHUP and no restart has to take place.
-
- During the post-SIGHUP torrc parsing, we unmark all transports
- spawned by managed proxies that we find in our torrc.
- We do that so that if we don't need to restart a managed proxy, we
- can continue using its old transports normally.
- If we end up restarting the proxy, we destroy and unregister all
- old transports from the circuitbuild.c subsystem.
-*/
-
/** Return true if there are still unconfigured managed proxies. */
int
pt_proxies_configuration_pending(void)
@@ -229,12 +236,10 @@ proxy_prepare_for_restart(managed_proxy_t *mp)
transport_t *t_tmp = NULL;
tor_assert(mp->conf_state == PT_PROTO_COMPLETED);
- tor_assert(mp->pid);
- /* kill the old obfsproxy process */
- tor_terminate_process(mp->pid);
- mp->pid = 0;
- fclose(mp->_stdout);
+ /* destroy the process handle and terminate the process. */
+ tor_process_handle_destroy(mp->process_handle, 1);
+ mp->process_handle = NULL;
/* destroy all its old transports. we no longer use them. */
SMARTLIST_FOREACH_BEGIN(mp->transports, const char *, t_name) {
@@ -256,52 +261,52 @@ proxy_prepare_for_restart(managed_proxy_t *mp)
static int
launch_managed_proxy(managed_proxy_t *mp)
{
- (void) mp;
- (void) set_managed_proxy_environment;
- return -1;
-#if 0
- /* XXXX023 we must reenable this code for managed proxies to work.
- * "All it needs" is revision to work with the new tor_spawn_background
- * API. */
- char **envp=NULL;
- int pid;
- process_handle_t proc;
- FILE *stdout_read = NULL;
- int stdout_pipe=-1, stderr_pipe=-1;
+ int retval;
+
+#ifdef MS_WINDOWS
+
+ LPVOID envp=NULL;
- /* prepare the environment variables for the managed proxy */
set_managed_proxy_environment(&envp, mp);
+ tor_assert(envp);
- pid = tor_spawn_background(mp->argv[0], (const char **)mp->argv,
- (const char **)envp, &proc);
- if (pid < 0) {
- log_warn(LD_GENERAL, "Managed proxy at '%s' failed at launch.",
- mp->argv[0]);
+ /* Passing NULL as lpApplicationName makes Windows search for the .exe */
+ retval = tor_spawn_background(NULL, (const char **)mp->argv, envp,
+ &mp->process_handle);
+
+ tor_free(envp);
+
+#else
+
+ char **envp=NULL;
+
+ /* prepare the environment variables for the managed proxy */
+ if (set_managed_proxy_environment(&envp, mp) < 0) {
+ log_warn(LD_GENERAL, "Could not setup the environment of "
+ "the managed proxy at '%s'.", mp->argv[0]);
+ free_execve_args(envp);
return -1;
}
+ retval = tor_spawn_background(mp->argv[0], (const char **)mp->argv,
+ (const char **)envp, &mp->process_handle);
+
/* free the memory allocated by set_managed_proxy_environment(). */
free_execve_args(envp);
- /* Set stdout/stderr pipes to be non-blocking */
-#ifdef _WIN32
- {
- u_long nonblocking = 1;
- ioctlsocket(stdout_pipe, FIONBIO, &nonblocking);
- }
-#else
- fcntl(stdout_pipe, F_SETFL, O_NONBLOCK);
#endif
- /* Open the buffered IO streams */
- stdout_read = fdopen(stdout_pipe, "r");
+ if (retval == PROCESS_STATUS_ERROR) {
+ log_warn(LD_GENERAL, "Managed proxy at '%s' failed at launch.",
+ mp->argv[0]);
+ return -1;
+ }
- log_info(LD_CONFIG, "Managed proxy has spawned at PID %d.", pid);
+ log_info(LD_CONFIG, "Managed proxy at '%s' has spawned with PID '%d'.",
+ mp->argv[0], tor_process_get_pid(mp->process_handle));
mp->conf_state = PT_PROTO_LAUNCHED;
- mp->_stdout = stdout_read;
- mp->pid = pid;
-#endif
+
return 0;
}
@@ -314,7 +319,8 @@ pt_configure_remaining_proxies(void)
log_debug(LD_CONFIG, "Configuring remaining managed proxies (%d)!",
unconfigured_proxies_n);
SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) {
- tor_assert(mp->conf_state != PT_PROTO_BROKEN);
+ tor_assert(mp->conf_state != PT_PROTO_BROKEN ||
+ mp->conf_state != PT_PROTO_FAILED_LAUNCH);
if (mp->got_hup) {
mp->got_hup = 0;
@@ -343,6 +349,65 @@ pt_configure_remaining_proxies(void)
} SMARTLIST_FOREACH_END(mp);
}
+#ifdef MS_WINDOWS
+
+/** Attempt to continue configuring managed proxy <b>mp</b>. */
+static void
+configure_proxy(managed_proxy_t *mp)
+{
+ int pos;
+ char stdout_buf[200];
+ smartlist_t *lines = NULL;
+
+ /* if we haven't launched the proxy yet, do it now */
+ if (mp->conf_state == PT_PROTO_INFANT) {
+ if (launch_managed_proxy(mp) < 0) { /* launch fail */
+ mp->conf_state = PT_PROTO_FAILED_LAUNCH;
+ handle_finished_proxy(mp);
+ }
+ return;
+ }
+
+ tor_assert(mp->conf_state != PT_PROTO_INFANT);
+ tor_assert(mp->process_handle);
+
+ pos = tor_read_all_handle(tor_process_get_stdout_pipe(mp->process_handle),
+ stdout_buf, sizeof(stdout_buf) - 1, NULL);
+ if (pos < 0) {
+ log_notice(LD_GENERAL, "Failed to read data from managed proxy");
+ mp->conf_state = PT_PROTO_BROKEN;
+ goto done;
+ }
+
+ if (pos == 0) /* proxy has nothing interesting to say. */
+ return;
+
+ /* End with a null even if there isn't a \r\n at the end */
+ /* TODO: What if this is a partial line? */
+ stdout_buf[pos] = '\0';
+
+ /* Split up the buffer */
+ lines = smartlist_create();
+ tor_split_lines(lines, stdout_buf, pos);
+
+ /* Handle lines. */
+ SMARTLIST_FOREACH_BEGIN(lines, const char *, line) {
+ handle_proxy_line(line, mp);
+ if (proxy_configuration_finished(mp))
+ goto done;
+ } SMARTLIST_FOREACH_END(line);
+
+ done:
+ /* if the proxy finished configuring, exit the loop. */
+ if (proxy_configuration_finished(mp))
+ handle_finished_proxy(mp);
+
+ if (lines)
+ smartlist_free(lines);
+}
+
+#else /* MS_WINDOWS */
+
/** Attempt to continue configuring managed proxy <b>mp</b>. */
static void
configure_proxy(managed_proxy_t *mp)
@@ -352,15 +417,19 @@ configure_proxy(managed_proxy_t *mp)
/* if we haven't launched the proxy yet, do it now */
if (mp->conf_state == PT_PROTO_INFANT) {
- launch_managed_proxy(mp);
+ if (launch_managed_proxy(mp) < 0) { /* launch fail */
+ mp->conf_state = PT_PROTO_FAILED_LAUNCH;
+ handle_finished_proxy(mp);
+ }
return;
}
tor_assert(mp->conf_state != PT_PROTO_INFANT);
+ tor_assert(mp->process_handle);
while (1) {
- r = get_string_from_pipe(mp->_stdout, stdout_buf,
- sizeof(stdout_buf) - 1);
+ r = get_string_from_pipe(tor_process_get_stdout_pipe(mp->process_handle),
+ stdout_buf, sizeof(stdout_buf) - 1);
if (r == IO_STREAM_OKAY) { /* got a line; handle it! */
handle_proxy_line((const char *)stdout_buf, mp);
@@ -382,6 +451,8 @@ configure_proxy(managed_proxy_t *mp)
}
}
+#endif /* MS_WINDOWS */
+
/** Register server managed proxy <b>mp</b> transports to state */
static void
register_server_proxy(managed_proxy_t *mp)
@@ -394,6 +465,10 @@ register_server_proxy(managed_proxy_t *mp)
tor_assert(mp->conf_state != PT_PROTO_COMPLETED);
SMARTLIST_FOREACH_BEGIN(mp->transports, transport_t *, t) {
save_transport_to_state(t->name, &t->addr, t->port);
+ /* LOG_WARN so that the bridge operator can easily find the
+ transport's port in the log file and send it to the users. */
+ log_warn(LD_GENERAL, "Registered server transport '%s' at '%s:%d'",
+ t->name, fmt_addr(&t->addr), (int)t->port);
smartlist_add(sm_tmp, tor_strdup(t->name));
} SMARTLIST_FOREACH_END(t);
@@ -453,7 +528,8 @@ register_proxy(managed_proxy_t *mp)
/** Free memory allocated by managed proxy <b>mp</b>. */
static void
-managed_proxy_destroy(managed_proxy_t *mp)
+managed_proxy_destroy(managed_proxy_t *mp,
+ int also_terminate_process)
{
if (mp->conf_state != PT_PROTO_COMPLETED)
SMARTLIST_FOREACH(mp->transports, transport_t *, t, transport_free(t));
@@ -470,15 +546,11 @@ managed_proxy_destroy(managed_proxy_t *mp)
/* remove it from the list of managed proxies */
smartlist_remove(managed_proxy_list, mp);
- /* close its stdout stream */
- if (mp->_stdout)
- fclose(mp->_stdout);
-
/* free the argv */
free_execve_args(mp->argv);
- if (mp->pid)
- tor_terminate_process(mp->pid);
+ tor_process_handle_destroy(mp->process_handle, also_terminate_process);
+ mp->process_handle = NULL;
tor_free(mp);
}
@@ -489,7 +561,10 @@ handle_finished_proxy(managed_proxy_t *mp)
{
switch (mp->conf_state) {
case PT_PROTO_BROKEN: /* if broken: */
- managed_proxy_destroy(mp); /* annihilate it. */
+ managed_proxy_destroy(mp, 1); /* annihilate it. */
+ break;
+ case PT_PROTO_FAILED_LAUNCH: /* if it failed before launching: */
+ managed_proxy_destroy(mp, 0); /* destroy it but don't terminate */
break;
case PT_PROTO_CONFIGURED: /* if configured correctly: */
register_proxy(mp); /* register its transports */
@@ -515,7 +590,8 @@ static INLINE int
proxy_configuration_finished(const managed_proxy_t *mp)
{
return (mp->conf_state == PT_PROTO_CONFIGURED ||
- mp->conf_state == PT_PROTO_BROKEN);
+ mp->conf_state == PT_PROTO_BROKEN ||
+ mp->conf_state == PT_PROTO_FAILED_LAUNCH);
}
/** This function is called when a proxy sends an {S,C}METHODS DONE message. */
@@ -537,7 +613,7 @@ handle_methods_done(const managed_proxy_t *mp)
void
handle_proxy_line(const char *line, managed_proxy_t *mp)
{
- log_debug(LD_GENERAL, "Got a line from managed proxy: %s\n", line);
+ log_debug(LD_GENERAL, "Got a line from managed proxy: %s", line);
if (strlen(line) < SMALLEST_MANAGED_LINE_SIZE) {
log_warn(LD_GENERAL, "Managed proxy configuration line is too small. "
@@ -614,14 +690,16 @@ handle_proxy_line(const char *line, managed_proxy_t *mp)
return;
} else if (!strcmpstart(line, SPAWN_ERROR_MESSAGE)) {
log_warn(LD_GENERAL, "Could not launch managed proxy executable!");
- goto err;
+ mp->conf_state = PT_PROTO_FAILED_LAUNCH;
+ return;
}
log_warn(LD_CONFIG, "Unknown line received by managed proxy. (%s)", line);
err:
mp->conf_state = PT_PROTO_BROKEN;
- return;
+ log_warn(LD_CONFIG, "Managed proxy at '%s' failed the configuration protocol"
+ " and will be destroyed.", mp->argv ? mp->argv[0] : "");
}
/** Parses an ENV-ERROR <b>line</b> and warns the user accordingly. */
@@ -841,13 +919,18 @@ static char *
get_bindaddr_for_proxy(const managed_proxy_t *mp)
{
char *bindaddr = NULL;
+ char *bindaddr_tmp = NULL;
smartlist_t *string_tmp = smartlist_create();
tor_assert(mp->is_server);
SMARTLIST_FOREACH_BEGIN(mp->transports_to_launch, char *, t) {
- tor_asprintf(&bindaddr, "%s-%s", t, get_bindaddr_for_transport(t));
+ bindaddr_tmp = get_bindaddr_for_transport(t);
+
+ tor_asprintf(&bindaddr, "%s-%s", t, bindaddr_tmp);
smartlist_add(string_tmp, bindaddr);
+
+ tor_free(bindaddr_tmp);
} SMARTLIST_FOREACH_END(t);
bindaddr = smartlist_join_strings(string_tmp, ",", 0, NULL);
@@ -858,8 +941,114 @@ get_bindaddr_for_proxy(const managed_proxy_t *mp)
return bindaddr;
}
-/** Prepare the <b>envp</b> of managed proxy <b>mp</b> */
+#ifdef MS_WINDOWS
+
+/** Prepare the environment <b>envp</b> of managed proxy <b>mp</b>.
+ * <b>envp</b> is allocated on the heap and should be freed by the
+ * caller after its use. */
static void
+set_managed_proxy_environment(LPVOID *envp, const managed_proxy_t *mp)
+{
+ const or_options_t *options = get_options();
+
+ char *tmp=NULL;
+ char *state_tmp=NULL;
+ char *state_env=NULL;
+ char *transports_to_launch=NULL;
+ char *transports_env=NULL;
+ char *bindaddr_tmp=NULL;
+ char *bindaddr_env=NULL;
+ char *orport_env=NULL;
+
+ char version_env[31]; /* XXX temp */
+ char extended_env[43]; /* XXX temp */
+
+ int env_size = 0;
+
+ /* A smartlist carrying all the env. variables that the managed
+ proxy should inherit. */
+ smartlist_t *envs = smartlist_create();
+
+ /* Copy the whole environment of the Tor process.
+ It should also copy PATH and HOME of the Tor process.*/
+ char **environ_tmp = environ;
+ while (*environ_tmp)
+ smartlist_add(envs, *environ_tmp++);
+
+ /* Create the TOR_PT_* environment variables. */
+ state_tmp = get_datadir_fname("pt_state/"); /* XXX temp */
+ tor_asprintf(&state_env, "TOR_PT_STATE_LOCATION=%s", state_tmp);
+
+ strcpy(version_env, "TOR_PT_MANAGED_TRANSPORT_VER=1");
+
+ transports_to_launch =
+ smartlist_join_strings(mp->transports_to_launch, ",", 0, NULL);
+
+ tor_asprintf(&transports_env,
+ mp->is_server ?
+ "TOR_PT_SERVER_TRANSPORTS=%s" : "TOR_PT_CLIENT_TRANSPORTS=%s",
+ transports_to_launch);
+
+ smartlist_add(envs, state_env);
+ smartlist_add(envs, version_env);
+ smartlist_add(envs, transports_env);
+
+ if (mp->is_server) {
+ tor_asprintf(&orport_env, "TOR_PT_ORPORT=127.0.0.1:%s",
+ options->ORPort->value);
+
+ bindaddr_tmp = get_bindaddr_for_proxy(mp);
+ tor_asprintf(&bindaddr_env, "TOR_PT_SERVER_BINDADDR=%s", bindaddr_tmp);
+
+ strcpy(extended_env, "TOR_PT_EXTENDED_SERVER_PORT=127.0.0.1:4200");
+
+ smartlist_add(envs, orport_env);
+ smartlist_add(envs, extended_env);
+ smartlist_add(envs, bindaddr_env);
+ }
+
+ /* It seems like some versions of Windows need a sorted lpEnvironment
+ block. */
+ smartlist_sort_strings(envs);
+
+ /* An environment block consists of a null-terminated block of
+ null-terminated strings: */
+
+ /* Calculate the block's size. */
+ SMARTLIST_FOREACH(envs, const char *, s,
+ env_size += strlen(s) + 1);
+ env_size += 1; /* space for last NUL */
+
+ *envp = tor_malloc(env_size);
+ tmp = *envp;
+
+ /* Create the block. */
+ SMARTLIST_FOREACH_BEGIN(envs, const char *, s) {
+ memcpy(tmp, s, strlen(s)); /* copy the env. variable string */
+ tmp += strlen(s);
+ memset(tmp, '\0', 1); /* append NUL at the end of the string */
+ tmp += 1;
+ } SMARTLIST_FOREACH_END(s);
+ memset(tmp, '\0', 1); /* last NUL */
+
+ /* Finally, free the whole mess. */
+ tor_free(state_tmp);
+ tor_free(state_env);
+ tor_free(transports_to_launch);
+ tor_free(transports_env);
+ tor_free(bindaddr_tmp);
+ tor_free(bindaddr_env);
+ tor_free(orport_env);
+
+ smartlist_free(envs);
+}
+
+#else /* MS_WINDOWS */
+
+/** Prepare the environment <b>envp</b> of managed proxy <b>mp</b>.
+ * <b>envp</b> is allocated on the heap and should be freed by the
+ * caller after its use. */
+static int
set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp)
{
const or_options_t *options = get_options();
@@ -867,7 +1056,10 @@ set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp)
char *state_loc=NULL;
char *transports_to_launch=NULL;
char *bindaddr=NULL;
+ char *home_env=NULL;
+ char *path_env=NULL;
+ int r = -1;
int n_envs = mp->is_server ? ENVIRON_SIZE_SERVER : ENVIRON_SIZE_CLIENT;
/* allocate enough space for our env. vars and a NULL pointer */
@@ -878,15 +1070,21 @@ set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp)
transports_to_launch =
smartlist_join_strings(mp->transports_to_launch, ",", 0, NULL);
- tor_asprintf(tmp++, "HOME=%s", getenv("HOME"));
- tor_asprintf(tmp++, "PATH=%s", getenv("PATH"));
+ home_env = getenv("HOME");
+ path_env = getenv("PATH");
+ if (!home_env || !path_env)
+ goto done;
+
+ tor_asprintf(tmp++, "HOME=%s", home_env);
+ tor_asprintf(tmp++, "PATH=%s", path_env);
tor_asprintf(tmp++, "TOR_PT_STATE_LOCATION=%s", state_loc);
tor_asprintf(tmp++, "TOR_PT_MANAGED_TRANSPORT_VER=1"); /* temp */
if (mp->is_server) {
bindaddr = get_bindaddr_for_proxy(mp);
/* XXX temp */
- tor_asprintf(tmp++, "TOR_PT_ORPORT=127.0.0.1:%d", options->ORPort);
+ tor_asprintf(tmp++, "TOR_PT_ORPORT=127.0.0.1:%d",
+ router_get_advertised_or_port(options));
tor_asprintf(tmp++, "TOR_PT_SERVER_BINDADDR=%s", bindaddr);
tor_asprintf(tmp++, "TOR_PT_SERVER_TRANSPORTS=%s", transports_to_launch);
/* XXX temp*/
@@ -896,11 +1094,18 @@ set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp)
}
*tmp = NULL;
+ r = 0;
+
+ done:
tor_free(state_loc);
tor_free(transports_to_launch);
tor_free(bindaddr);
+
+ return r;
}
+#endif /* MS_WINDOWS */
+
/** Create and return a new managed proxy for <b>transport</b> using
* <b>proxy_argv</b>. If <b>is_server</b> is true, it's a server
* managed proxy. */
@@ -995,9 +1200,9 @@ pt_prepare_proxy_list_for_config_read(void)
SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) {
/* Destroy unconfigured proxies. */
if (mp->conf_state != PT_PROTO_COMPLETED) {
- managed_proxy_destroy(mp);
- unconfigured_proxies_n--;
- continue;
+ managed_proxy_destroy(mp, 1);
+ unconfigured_proxies_n--;
+ continue;
}
tor_assert(mp->conf_state == PT_PROTO_COMPLETED);
@@ -1024,7 +1229,7 @@ sweep_proxy_list(void)
SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) {
if (mp->marked_for_removal) {
SMARTLIST_DEL_CURRENT(managed_proxy_list, mp);
- managed_proxy_destroy(mp);
+ managed_proxy_destroy(mp, 1);
}
} SMARTLIST_FOREACH_END(mp);
}
@@ -1039,7 +1244,7 @@ pt_free_all(void)
free them. Otherwise, it hasn't registered its transports yet
and we should free them here. */
SMARTLIST_FOREACH(managed_proxy_list, managed_proxy_t *, mp,
- managed_proxy_destroy(mp));
+ managed_proxy_destroy(mp, 1));
smartlist_free(managed_proxy_list);
managed_proxy_list=NULL;
diff --git a/src/or/transports.h b/src/or/transports.h
index 4a93387596..314af2b3a0 100644
--- a/src/or/transports.h
+++ b/src/or/transports.h
@@ -36,7 +36,8 @@ enum pt_proto_state {
PT_PROTO_ACCEPTING_METHODS, /* accepting methods */
PT_PROTO_CONFIGURED, /* configured successfully */
PT_PROTO_COMPLETED, /* configure and registered its transports */
- PT_PROTO_BROKEN
+ PT_PROTO_BROKEN, /* broke during the protocol */
+ PT_PROTO_FAILED_LAUNCH /* failed while launching */
};
/** Structure containing information of a managed proxy. */
@@ -47,8 +48,8 @@ typedef struct {
int is_server; /* is it a server proxy? */
- FILE *_stdout; /* a stream to its stdout
- (closed in managed_proxy_destroy()) */
+ /* A pointer to the process handle of this managed proxy. */
+ process_handle_t *process_handle;
int pid; /* The Process ID this managed proxy is using. */