summaryrefslogtreecommitdiff
path: root/src/or
diff options
context:
space:
mode:
Diffstat (limited to 'src/or')
-rw-r--r--src/or/Makefile.nmake80
-rw-r--r--src/or/addressmap.c1078
-rw-r--r--src/or/addressmap.h60
-rw-r--r--src/or/buffers.c26
-rw-r--r--src/or/buffers.h2
-rw-r--r--src/or/channel.c183
-rw-r--r--src/or/channel.h8
-rw-r--r--src/or/channeltls.c6
-rw-r--r--src/or/channeltls.h2
-rw-r--r--src/or/circuitbuild.c1796
-rw-r--r--src/or/circuitbuild.h28
-rw-r--r--src/or/circuitlist.c78
-rw-r--r--src/or/circuitlist.h2
-rw-r--r--src/or/circuitmux.c2
-rw-r--r--src/or/circuitmux.h2
-rw-r--r--src/or/circuitmux_ewma.c2
-rw-r--r--src/or/circuitmux_ewma.h2
-rw-r--r--src/or/circuitstats.c22
-rw-r--r--src/or/circuitstats.h2
-rw-r--r--src/or/circuituse.c315
-rw-r--r--src/or/circuituse.h2
-rw-r--r--src/or/command.c85
-rw-r--r--src/or/command.h2
-rw-r--r--src/or/config.c722
-rw-r--r--src/or/config.h8
-rw-r--r--src/or/confparse.c2
-rw-r--r--src/or/confparse.h4
-rw-r--r--src/or/connection.c154
-rw-r--r--src/or/connection.h50
-rw-r--r--src/or/connection_edge.c1424
-rw-r--r--src/or/connection_edge.h72
-rw-r--r--src/or/connection_or.c53
-rw-r--r--src/or/connection_or.h2
-rw-r--r--src/or/control.c25
-rw-r--r--src/or/control.h2
-rw-r--r--src/or/cpuworker.c387
-rw-r--r--src/or/cpuworker.h10
-rw-r--r--src/or/directory.c137
-rw-r--r--src/or/directory.h2
-rw-r--r--src/or/dirserv.c151
-rw-r--r--src/or/dirserv.h5
-rw-r--r--src/or/dirvote.c154
-rw-r--r--src/or/dirvote.h16
-rw-r--r--src/or/dns.c892
-rw-r--r--src/or/dns.h3
-rw-r--r--src/or/dnsserv.c12
-rw-r--r--src/or/dnsserv.h2
-rw-r--r--src/or/entrynodes.c320
-rw-r--r--src/or/entrynodes.h36
-rw-r--r--src/or/eventdns_tor.h2
-rw-r--r--src/or/geoip.c208
-rw-r--r--src/or/geoip.h9
-rw-r--r--src/or/hibernate.c16
-rw-r--r--src/or/hibernate.h2
-rw-r--r--src/or/include.am19
-rw-r--r--src/or/main.c69
-rw-r--r--src/or/main.h2
-rw-r--r--src/or/microdesc.c22
-rw-r--r--src/or/microdesc.h2
-rw-r--r--src/or/networkstatus.c76
-rw-r--r--src/or/networkstatus.h3
-rw-r--r--src/or/nodelist.c192
-rw-r--r--src/or/nodelist.h3
-rw-r--r--src/or/ntmain.c2
-rw-r--r--src/or/ntmain.h2
-rw-r--r--src/or/onion.c1199
-rw-r--r--src/or/onion.h120
-rw-r--r--src/or/onion_fast.c123
-rw-r--r--src/or/onion_fast.h38
-rw-r--r--src/or/onion_ntor.c295
-rw-r--r--src/or/onion_ntor.h63
-rw-r--r--src/or/onion_tap.c218
-rw-r--r--src/or/onion_tap.h37
-rw-r--r--src/or/or.h387
-rw-r--r--src/or/policies.c195
-rw-r--r--src/or/policies.h8
-rw-r--r--src/or/reasons.c9
-rw-r--r--src/or/reasons.h2
-rw-r--r--src/or/relay.c322
-rw-r--r--src/or/relay.h11
-rw-r--r--src/or/rendclient.c81
-rw-r--r--src/or/rendclient.h2
-rw-r--r--src/or/rendcommon.c4
-rw-r--r--src/or/rendcommon.h2
-rw-r--r--src/or/rendmid.c6
-rw-r--r--src/or/rendmid.h2
-rw-r--r--src/or/rendservice.c109
-rw-r--r--src/or/rendservice.h2
-rw-r--r--src/or/rephist.c32
-rw-r--r--src/or/rephist.h4
-rw-r--r--src/or/replaycache.c2
-rw-r--r--src/or/replaycache.h2
-rw-r--r--src/or/router.c387
-rw-r--r--src/or/router.h10
-rw-r--r--src/or/routerlist.c457
-rw-r--r--src/or/routerlist.h34
-rw-r--r--src/or/routerparse.c311
-rw-r--r--src/or/routerparse.h4
-rw-r--r--src/or/routerset.c44
-rw-r--r--src/or/routerset.h3
-rw-r--r--src/or/statefile.c16
-rw-r--r--src/or/statefile.h2
-rw-r--r--src/or/status.c8
-rw-r--r--src/or/status.h2
-rw-r--r--src/or/tor_main.c2
-rw-r--r--src/or/transports.c6
-rw-r--r--src/or/transports.h2
107 files changed, 9879 insertions, 3743 deletions
diff --git a/src/or/Makefile.nmake b/src/or/Makefile.nmake
index 677618e74f..d67123a9a6 100644
--- a/src/or/Makefile.nmake
+++ b/src/or/Makefile.nmake
@@ -1,28 +1,74 @@
all: tor.exe
-CFLAGS = /I ..\win32 /I ..\..\..\build-alpha\include /I ..\common
+CFLAGS = /I ..\win32 /I ..\..\..\build-alpha\include /I ..\common \
+ /I ..\ext
-LIBS = ..\..\..\build-alpha\lib\libevent.a \
- ..\..\..\build-alpha\lib\libcrypto.a \
- ..\..\..\build-alpha\lib\libssl.a \
- ..\..\..\build-alpha\lib\libz.a \
- ws2_32.lib advapi32.lib shell32.lib
+LIBS = ..\..\..\build-alpha\lib\libevent.lib \
+ ..\..\..\build-alpha\lib\libcrypto.lib \
+ ..\..\..\build-alpha\lib\libssl.lib \
+ ..\..\..\build-alpha\lib\libz.lib \
+ ws2_32.lib advapi32.lib shell32.lib \
+ crypt32.lib gdi32.lib user32.lib
-LIBTOR_OBJECTS = buffers.obj channel.obj channeltls.obj circuitbuild.obj \
- circuitlist.obj circuitmux.obj circuitmux_ewma.obj circuituse.obj \
- command.obj config.obj connection.obj connection_edge.obj \
- connection_or.obj control.obj cpuworker.obj directory.obj \
- dirserv.obj dirvote.obj dns.obj dnsserv.obj geoip.obj hibernate.obj \
- main.obj microdesc.obj networkstatus.obj nodelist.obj onion.obj \
- policies.obj reasons.obj relay.obj rendclient.obj rendcommon.obj \
- rendmid.obj rendservice.obj rephist.obj router.obj routerlist.obj \
- routerparse.obj status.obj config_codedigest.obj ntmain.obj
+LIBTOR_OBJECTS = \
+ addressmap.obj \
+ buffers.obj \
+ channel.obj \
+ channeltls.obj \
+ circuitbuild.obj \
+ circuitlist.obj \
+ circuitmux.obj \
+ circuitmux_ewma.obj \
+ circuitstats.obj \
+ circuituse.obj \
+ command.obj \
+ config.obj \
+ config_codedigest.obj \
+ confparse.obj \
+ connection.obj \
+ connection_edge.obj \
+ connection_or.obj \
+ control.obj \
+ cpuworker.obj \
+ directory.obj \
+ dirserv.obj \
+ dirvote.obj \
+ dns.obj \
+ dnsserv.obj \
+ entrynodes.obj \
+ geoip.obj \
+ hibernate.obj \
+ main.obj \
+ microdesc.obj \
+ networkstatus.obj \
+ nodelist.obj \
+ ntmain.obj \
+ onion.obj \
+ onion_fast.obj \
+ onion_ntor.obj \
+ onion_tap.obj \
+ policies.obj \
+ reasons.obj \
+ relay.obj \
+ rendclient.obj \
+ rendcommon.obj \
+ rendmid.obj \
+ rendservice.obj \
+ rephist.obj \
+ replaycache.obj \
+ router.obj \
+ routerlist.obj \
+ routerparse.obj \
+ routerset.obj \
+ statefile.obj \
+ status.obj \
+ transports.obj
libtor.lib: $(LIBTOR_OBJECTS)
- lib $(LIBTOR_OBJECTS) /out:libtor.lib
+ lib $(LIBTOR_OBJECTS) /out:$@
tor.exe: libtor.lib tor_main.obj
- $(CC) $(CFLAGS) $(LIBS) libtor.lib ..\common\*.lib tor_main.obj
+ $(CC) $(CFLAGS) $(LIBS) libtor.lib ..\common\*.lib tor_main.obj /Fe$@
clean:
del $(LIBTOR_OBJECTS) *.lib tor.exe
diff --git a/src/or/addressmap.c b/src/or/addressmap.c
new file mode 100644
index 0000000000..826eb301db
--- /dev/null
+++ b/src/or/addressmap.c
@@ -0,0 +1,1078 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define ADDRESSMAP_PRIVATE
+
+#include "or.h"
+#include "addressmap.h"
+#include "circuituse.h"
+#include "config.h"
+#include "connection_edge.h"
+#include "control.h"
+#include "dns.h"
+#include "routerset.h"
+#include "nodelist.h"
+
+/** A client-side struct to remember requests to rewrite addresses
+ * to new addresses. These structs are stored in the hash table
+ * "addressmap" below.
+ *
+ * There are 5 ways to set an address mapping:
+ * - A MapAddress command from the controller [permanent]
+ * - An AddressMap directive in the torrc [permanent]
+ * - When a TrackHostExits torrc directive is triggered [temporary]
+ * - When a DNS resolve succeeds [temporary]
+ * - When a DNS resolve fails [temporary]
+ *
+ * When an addressmap request is made but one is already registered,
+ * the new one is replaced only if the currently registered one has
+ * no "new_address" (that is, it's in the process of DNS resolve),
+ * or if the new one is permanent (expires==0 or 1).
+ *
+ * (We overload the 'expires' field, using "0" for mappings set via
+ * 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;
+ ENUM_BF(addressmap_entry_source_t) source:3;
+ unsigned src_wildcard:1;
+ unsigned dst_wildcard:1;
+ short num_resolve_failures;
+} addressmap_entry_t;
+
+/** Entry for mapping addresses to which virtual address we mapped them to. */
+typedef struct {
+ char *ipv4_address;
+ char *ipv6_address;
+ char *hostname_address;
+} virtaddress_entry_t;
+
+/** A hash table to store client-side address rewrite instructions. */
+static strmap_t *addressmap=NULL;
+
+/**
+ * Table mapping addresses to which virtual address, if any, we
+ * assigned them to.
+ *
+ * We maintain the following invariant: if [A,B] is in
+ * virtaddress_reversemap, then B must be a virtual address, and [A,B]
+ * must be in addressmap. We do not require that the converse hold:
+ * if it fails, then we could end up mapping two virtual addresses to
+ * the same address, which is no disaster.
+ **/
+static strmap_t *virtaddress_reversemap=NULL;
+
+/** Initialize addressmap. */
+void
+addressmap_init(void)
+{
+ addressmap = strmap_new();
+ virtaddress_reversemap = strmap_new();
+}
+
+/** Free the memory associated with the addressmap entry <b>_ent</b>. */
+static void
+addressmap_ent_free(void *_ent)
+{
+ addressmap_entry_t *ent;
+ if (!_ent)
+ return;
+
+ ent = _ent;
+ tor_free(ent->new_address);
+ tor_free(ent);
+}
+
+/** Free storage held by a virtaddress_entry_t* entry in <b>ent</b>. */
+static void
+addressmap_virtaddress_ent_free(void *_ent)
+{
+ virtaddress_entry_t *ent;
+ if (!_ent)
+ return;
+
+ ent = _ent;
+ tor_free(ent->ipv4_address);
+ tor_free(ent->hostname_address);
+ tor_free(ent);
+}
+
+/** Free storage held by a virtaddress_entry_t* entry in <b>ent</b>. */
+static void
+addressmap_virtaddress_remove(const char *address, addressmap_entry_t *ent)
+{
+ if (ent && ent->new_address &&
+ address_is_in_virtual_range(ent->new_address)) {
+ virtaddress_entry_t *ve =
+ strmap_get(virtaddress_reversemap, ent->new_address);
+ /*log_fn(LOG_NOTICE,"remove reverse mapping for %s",ent->new_address);*/
+ if (ve) {
+ if (!strcmp(address, ve->ipv4_address))
+ tor_free(ve->ipv4_address);
+ if (!strcmp(address, ve->hostname_address))
+ tor_free(ve->hostname_address);
+ if (!ve->ipv4_address && !ve->hostname_address) {
+ tor_free(ve);
+ strmap_remove(virtaddress_reversemap, ent->new_address);
+ }
+ }
+ }
+}
+
+/** Remove <b>ent</b> (which must be mapped to by <b>address</b>) from the
+ * client address maps. */
+static void
+addressmap_ent_remove(const char *address, addressmap_entry_t *ent)
+{
+ addressmap_virtaddress_remove(address, ent);
+ addressmap_ent_free(ent);
+}
+
+/** Unregister all TrackHostExits mappings from any address to
+ * *.exitname.exit. */
+void
+clear_trackexithost_mappings(const char *exitname)
+{
+ char *suffix = NULL;
+ if (!addressmap || !exitname)
+ return;
+ tor_asprintf(&suffix, ".%s.exit", exitname);
+ tor_strlower(suffix);
+
+ STRMAP_FOREACH_MODIFY(addressmap, address, addressmap_entry_t *, ent) {
+ if (ent->source == ADDRMAPSRC_TRACKEXIT &&
+ !strcmpend(ent->new_address, suffix)) {
+ addressmap_ent_remove(address, ent);
+ MAP_DEL_CURRENT(address);
+ }
+ } STRMAP_FOREACH_END;
+
+ tor_free(suffix);
+}
+
+/** Remove all TRACKEXIT mappings from the addressmap for which the target
+ * host is unknown or no longer allowed, or for which the source address
+ * is no longer in trackexithosts. */
+void
+addressmap_clear_excluded_trackexithosts(const or_options_t *options)
+{
+ const routerset_t *allow_nodes = options->ExitNodes;
+ const routerset_t *exclude_nodes = options->ExcludeExitNodesUnion_;
+
+ if (!addressmap)
+ return;
+ if (routerset_is_empty(allow_nodes))
+ allow_nodes = NULL;
+ if (allow_nodes == NULL && routerset_is_empty(exclude_nodes))
+ return;
+
+ STRMAP_FOREACH_MODIFY(addressmap, address, addressmap_entry_t *, ent) {
+ size_t len;
+ const char *target = ent->new_address, *dot;
+ char *nodename;
+ const node_t *node;
+
+ if (!target) {
+ /* DNS resolving in progress */
+ continue;
+ } else if (strcmpend(target, ".exit")) {
+ /* Not a .exit mapping */
+ continue;
+ } else if (ent->source != ADDRMAPSRC_TRACKEXIT) {
+ /* Not a trackexit mapping. */
+ continue;
+ }
+ len = strlen(target);
+ if (len < 6)
+ continue; /* malformed. */
+ dot = target + len - 6; /* dot now points to just before .exit */
+ while (dot > target && *dot != '.')
+ dot--;
+ if (*dot == '.') dot++;
+ nodename = tor_strndup(dot, len-5-(dot-target));;
+ node = node_get_by_nickname(nodename, 0);
+ tor_free(nodename);
+ if (!node ||
+ (allow_nodes && !routerset_contains_node(allow_nodes, node)) ||
+ routerset_contains_node(exclude_nodes, node) ||
+ !hostname_in_track_host_exits(options, address)) {
+ /* We don't know this one, or we want to be rid of it. */
+ addressmap_ent_remove(address, ent);
+ MAP_DEL_CURRENT(address);
+ }
+ } STRMAP_FOREACH_END;
+}
+
+/** Return true iff <b>address</b> is one that we are configured to
+ * automap on resolve according to <b>options</b>. */
+int
+addressmap_address_should_automap(const char *address,
+ const or_options_t *options)
+{
+ const smartlist_t *suffix_list = options->AutomapHostsSuffixes;
+
+ if (!suffix_list)
+ return 0;
+
+ SMARTLIST_FOREACH_BEGIN(suffix_list, const char *, suffix) {
+ if (!strcasecmpend(address, suffix))
+ return 1;
+ } SMARTLIST_FOREACH_END(suffix);
+ return 0;
+}
+
+/** Remove all AUTOMAP mappings from the addressmap for which the
+ * source address no longer matches AutomapHostsSuffixes, which is
+ * no longer allowed by AutomapHostsOnResolve, or for which the
+ * target address is no longer in the virtual network. */
+void
+addressmap_clear_invalid_automaps(const or_options_t *options)
+{
+ int clear_all = !options->AutomapHostsOnResolve;
+ const smartlist_t *suffixes = options->AutomapHostsSuffixes;
+
+ if (!addressmap)
+ return;
+
+ if (!suffixes)
+ clear_all = 1; /* This should be impossible, but let's be sure. */
+
+ STRMAP_FOREACH_MODIFY(addressmap, src_address, addressmap_entry_t *, ent) {
+ int remove = clear_all;
+ if (ent->source != ADDRMAPSRC_AUTOMAP)
+ continue; /* not an automap mapping. */
+
+ if (!remove) {
+ remove = ! addressmap_address_should_automap(src_address, options);
+ }
+
+ if (!remove && ! address_is_in_virtual_range(ent->new_address))
+ remove = 1;
+
+ if (remove) {
+ addressmap_ent_remove(src_address, ent);
+ MAP_DEL_CURRENT(src_address);
+ }
+ } STRMAP_FOREACH_END;
+}
+
+/** Remove all entries from the addressmap that were set via the
+ * configuration file or the command line. */
+void
+addressmap_clear_configured(void)
+{
+ addressmap_get_mappings(NULL, 0, 0, 0);
+}
+
+/** Remove all entries from the addressmap that are set to expire, ever. */
+void
+addressmap_clear_transient(void)
+{
+ addressmap_get_mappings(NULL, 2, TIME_MAX, 0);
+}
+
+/** Clean out entries from the addressmap cache that were
+ * added long enough ago that they are no longer valid.
+ */
+void
+addressmap_clean(time_t now)
+{
+ addressmap_get_mappings(NULL, 2, now, 0);
+}
+
+/** Free all the elements in the addressmap, and free the addressmap
+ * itself. */
+void
+addressmap_free_all(void)
+{
+ strmap_free(addressmap, addressmap_ent_free);
+ addressmap = NULL;
+
+ strmap_free(virtaddress_reversemap, addressmap_virtaddress_ent_free);
+ 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
+ * address changed; false otherwise. Set *<b>expires_out</b> to the
+ * expiry time of the result, or to <b>time_max</b> if the result does
+ * not expire.
+ *
+ * If <b>exit_source_out</b> is non-null, we set it as follows. If we the
+ * address starts out as a non-exit address, and we remap it to an .exit
+ * address at any point, then set *<b>exit_source_out</b> to the
+ * address_entry_source_t of the first such rule. Set *<b>exit_source_out</b>
+ * to ADDRMAPSRC_NONE if there is no such rewrite, or if the original address
+ * was a .exit.
+ */
+int
+addressmap_rewrite(char *address, size_t maxlen,
+ unsigned flags,
+ time_t *expires_out,
+ addressmap_entry_source_t *exit_source_out)
+{
+ addressmap_entry_t *ent;
+ int rewrites;
+ time_t expires = TIME_MAX;
+ addressmap_entry_source_t exit_source = ADDRMAPSRC_NONE;
+ char *addr_orig = tor_strdup(address);
+ char *log_addr_orig = NULL;
+
+ for (rewrites = 0; rewrites < 16; rewrites++) {
+ int exact_match = 0;
+ log_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" */
+ goto done;
+ }
+
+ exact_match = 1;
+ }
+
+ if (!ent || !ent->new_address) {
+ goto done;
+ }
+
+ if (ent && ent->source == ADDRMAPSRC_DNS) {
+ sa_family_t f;
+ tor_addr_t tmp;
+ f = tor_addr_parse(&tmp, ent->new_address);
+ if (f == AF_INET && !(flags & AMR_FLAG_USE_IPV4_DNS))
+ goto done;
+ else if (f == AF_INET6 && !(flags & AMR_FLAG_USE_IPV6_DNS))
+ goto done;
+ }
+
+ if (ent->dst_wildcard && !exact_match) {
+ strlcat(address, ".", maxlen);
+ strlcat(address, ent->new_address, maxlen);
+ } else {
+ strlcpy(address, ent->new_address, maxlen);
+ }
+
+ if (!strcmpend(address, ".exit") &&
+ strcmpend(addr_orig, ".exit") &&
+ exit_source == ADDRMAPSRC_NONE) {
+ exit_source = ent->source;
+ }
+
+ log_info(LD_APP, "Addressmap: rewriting %s to %s",
+ log_addr_orig, escaped_safe_str_client(address));
+ if (ent->expires > 1 && ent->expires < expires)
+ expires = ent->expires;
+
+ tor_free(log_addr_orig);
+ }
+ log_warn(LD_CONFIG,
+ "Loop detected: we've rewritten %s 16 times! Using it as-is.",
+ escaped_safe_str_client(address));
+ /* it's fine to rewrite a rewrite, but don't loop forever */
+
+ done:
+ tor_free(addr_orig);
+ tor_free(log_addr_orig);
+ if (exit_source_out)
+ *exit_source_out = exit_source;
+ if (expires_out)
+ *expires_out = TIME_MAX;
+ return (rewrites > 0);
+}
+
+/** If we have a cached reverse DNS entry for the address stored in the
+ * <b>maxlen</b>-byte buffer <b>address</b> (typically, a dotted quad) then
+ * rewrite to the cached value and return 1. Otherwise return 0. Set
+ * *<b>expires_out</b> to the expiry time of the result, or to <b>time_max</b>
+ * if the result does not expire. */
+int
+addressmap_rewrite_reverse(char *address, size_t maxlen, unsigned flags,
+ time_t *expires_out)
+{
+ char *s, *cp;
+ addressmap_entry_t *ent;
+ int r = 0;
+ {
+ sa_family_t f;
+ tor_addr_t tmp;
+ f = tor_addr_parse(&tmp, address);
+ if (f == AF_INET && !(flags & AMR_FLAG_USE_IPV4_DNS))
+ return 0;
+ else if (f == AF_INET6 && !(flags & AMR_FLAG_USE_IPV6_DNS))
+ return 0;
+ }
+
+ tor_asprintf(&s, "REVERSE[%s]", address);
+ ent = strmap_get(addressmap, s);
+ if (ent) {
+ cp = tor_strdup(escaped_safe_str_client(ent->new_address));
+ log_info(LD_APP, "Rewrote reverse lookup %s -> %s",
+ escaped_safe_str_client(s), cp);
+ tor_free(cp);
+ strlcpy(address, ent->new_address, maxlen);
+ r = 1;
+ }
+
+ if (expires_out)
+ *expires_out = (ent && ent->expires > 1) ? ent->expires : TIME_MAX;
+
+ tor_free(s);
+ return r;
+}
+
+/** Return 1 if <b>address</b> is already registered, else return 0. If address
+ * is already registered, and <b>update_expires</b> is non-zero, then update
+ * the expiry time on the mapping with update_expires if it is a
+ * mapping created by TrackHostExits. */
+int
+addressmap_have_mapping(const char *address, int update_expiry)
+{
+ addressmap_entry_t *ent;
+ if (!(ent=strmap_get_lc(addressmap, address)))
+ return 0;
+ if (update_expiry && ent->source==ADDRMAPSRC_TRACKEXIT)
+ ent->expires=time(NULL) + update_expiry;
+ return 1;
+}
+
+/** Register a request to map <b>address</b> to <b>new_address</b>,
+ * which will expire on <b>expires</b> (or 0 if never expires from
+ * config file, 1 if never expires from controller, 2 if never expires
+ * (virtual address mapping) from the controller.)
+ *
+ * <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>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,
+ 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) &&
+ wildcard_addr == wildcard_new_addr)) {
+ /* Remove the mapping, if any. */
+ tor_free(new_address);
+ if (ent) {
+ addressmap_ent_remove(address,ent);
+ strmap_remove(addressmap, address);
+ }
+ return;
+ }
+ if (!ent) { /* make a new one and register it */
+ ent = tor_malloc_zero(sizeof(addressmap_entry_t));
+ strmap_set(addressmap, address, ent);
+ } else if (ent->new_address) { /* we need to clean up the old mapping. */
+ if (expires > 1) {
+ log_info(LD_APP,"Temporary addressmap ('%s' to '%s') not performed, "
+ "since it's already mapped to '%s'",
+ safe_str_client(address),
+ safe_str_client(new_address),
+ safe_str_client(ent->new_address));
+ tor_free(new_address);
+ return;
+ }
+ if (address_is_in_virtual_range(ent->new_address) &&
+ expires != 2) {
+ /* XXX This isn't the perfect test; we want to avoid removing
+ * mappings set from the control interface _as virtual mapping */
+ addressmap_virtaddress_remove(address, ent);
+ }
+ tor_free(ent->new_address);
+ } /* else { we have an in-progress resolve with no mapping. } */
+
+ ent->new_address = new_address;
+ 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),
+ safe_str_client(ent->new_address));
+ control_event_address_mapped(address, ent->new_address, expires, NULL);
+}
+
+/** An attempt to resolve <b>address</b> failed at some OR.
+ * Increment the number of resolve failures we have on record
+ * for it, and then return that number.
+ */
+int
+client_dns_incr_failures(const char *address)
+{
+ addressmap_entry_t *ent = strmap_get(addressmap, address);
+ if (!ent) {
+ ent = tor_malloc_zero(sizeof(addressmap_entry_t));
+ ent->expires = time(NULL) + MAX_DNS_ENTRY_AGE;
+ strmap_set(addressmap,address,ent);
+ }
+ if (ent->num_resolve_failures < SHORT_MAX)
+ ++ent->num_resolve_failures; /* don't overflow */
+ log_info(LD_APP, "Address %s now has %d resolve failures.",
+ safe_str_client(address),
+ ent->num_resolve_failures);
+ return ent->num_resolve_failures;
+}
+
+/** If <b>address</b> is in the client DNS addressmap, reset
+ * the number of resolve failures we have on record for it.
+ * This is used when we fail a stream because it won't resolve:
+ * otherwise future attempts on that address will only try once.
+ */
+void
+client_dns_clear_failures(const char *address)
+{
+ addressmap_entry_t *ent = strmap_get(addressmap, address);
+ if (ent)
+ ent->num_resolve_failures = 0;
+}
+
+/** Record the fact that <b>address</b> resolved to <b>name</b>.
+ * We can now use this in subsequent streams via addressmap_rewrite()
+ * so we can more correctly choose an exit that will allow <b>address</b>.
+ *
+ * If <b>exitname</b> is defined, then append the addresses with
+ * ".exitname.exit" before registering the mapping.
+ *
+ * If <b>ttl</b> is nonnegative, the mapping will be valid for
+ * <b>ttl</b>seconds; otherwise, we use the default.
+ */
+static void
+client_dns_set_addressmap_impl(entry_connection_t *for_conn,
+ const char *address, const char *name,
+ const char *exitname,
+ int ttl)
+{
+ char *extendedaddress=NULL, *extendedval=NULL;
+ (void)for_conn;
+
+ tor_assert(address);
+ tor_assert(name);
+
+ if (ttl<0)
+ ttl = DEFAULT_DNS_TTL;
+ else
+ ttl = dns_clip_ttl(ttl);
+
+ if (exitname) {
+ /* XXXX fails to ever get attempts to get an exit address of
+ * google.com.digest[=~]nickname.exit; we need a syntax for this that
+ * won't make strict RFC952-compliant applications (like us) barf. */
+ tor_asprintf(&extendedaddress,
+ "%s.%s.exit", address, exitname);
+ tor_asprintf(&extendedval,
+ "%s.%s.exit", name, exitname);
+ } else {
+ tor_asprintf(&extendedaddress,
+ "%s", address);
+ tor_asprintf(&extendedval,
+ "%s", name);
+ }
+ addressmap_register(extendedaddress, extendedval,
+ time(NULL) + ttl, ADDRMAPSRC_DNS, 0, 0);
+ tor_free(extendedaddress);
+}
+
+/** Record the fact that <b>address</b> resolved to <b>val</b>.
+ * We can now use this in subsequent streams via addressmap_rewrite()
+ * so we can more correctly choose an exit that will allow <b>address</b>.
+ *
+ * If <b>exitname</b> is defined, then append the addresses with
+ * ".exitname.exit" before registering the mapping.
+ *
+ * If <b>ttl</b> is nonnegative, the mapping will be valid for
+ * <b>ttl</b>seconds; otherwise, we use the default.
+ */
+void
+client_dns_set_addressmap(entry_connection_t *for_conn,
+ const char *address,
+ const tor_addr_t *val,
+ const char *exitname,
+ int ttl)
+{
+ tor_addr_t addr_tmp;
+ char valbuf[TOR_ADDR_BUF_LEN];
+
+ tor_assert(address);
+ tor_assert(val);
+
+ if (tor_addr_parse(&addr_tmp, address) >= 0)
+ return; /* If address was an IP address already, don't add a mapping. */
+
+ if (tor_addr_family(val) == AF_INET) {
+ if (! for_conn->cache_ipv4_answers)
+ return;
+ } else if (tor_addr_family(val) == AF_INET6) {
+ if (! for_conn->cache_ipv6_answers)
+ return;
+ }
+
+ if (! tor_addr_to_str(valbuf, val, sizeof(valbuf), 1))
+ return;
+
+ client_dns_set_addressmap_impl(for_conn, address, valbuf, exitname, ttl);
+}
+
+/** Add a cache entry noting that <b>address</b> (ordinarily a dotted quad)
+ * resolved via a RESOLVE_PTR request to the hostname <b>v</b>.
+ *
+ * If <b>exitname</b> is defined, then append the addresses with
+ * ".exitname.exit" before registering the mapping.
+ *
+ * If <b>ttl</b> is nonnegative, the mapping will be valid for
+ * <b>ttl</b>seconds; otherwise, we use the default.
+ */
+void
+client_dns_set_reverse_addressmap(entry_connection_t *for_conn,
+ const char *address, const char *v,
+ const char *exitname,
+ int ttl)
+{
+ char *s = NULL;
+ {
+ tor_addr_t tmp_addr;
+ sa_family_t f = tor_addr_parse(&tmp_addr, address);
+ if ((f == AF_INET && ! for_conn->cache_ipv4_answers) ||
+ (f == AF_INET6 && ! for_conn->cache_ipv6_answers))
+ return;
+ }
+ tor_asprintf(&s, "REVERSE[%s]", address);
+ client_dns_set_addressmap_impl(for_conn, s, v, exitname, ttl);
+ tor_free(s);
+}
+
+/* By default, we hand out 127.192.0.1 through 127.254.254.254.
+ * These addresses should map to localhost, so even if the
+ * application accidentally tried to connect to them directly (not
+ * via Tor), it wouldn't get too far astray.
+ *
+ * These options are configured by parse_virtual_addr_network().
+ */
+
+static virtual_addr_conf_t virtaddr_conf_ipv4;
+static virtual_addr_conf_t virtaddr_conf_ipv6;
+
+/** Read a netmask of the form 127.192.0.0/10 from "val", and check whether
+ * it's a valid set of virtual addresses to hand out in response to MAPADDRESS
+ * requests. Return 0 on success; set *msg (if provided) to a newly allocated
+ * string and return -1 on failure. If validate_only is false, sets the
+ * actual virtual address range to the parsed value. */
+int
+parse_virtual_addr_network(const char *val, sa_family_t family,
+ int validate_only,
+ char **msg)
+{
+ const int ipv6 = (family == AF_INET6);
+ tor_addr_t addr;
+ maskbits_t bits;
+ const int max_bits = ipv6 ? 40 : 16;
+ virtual_addr_conf_t *conf = ipv6 ? &virtaddr_conf_ipv6 : &virtaddr_conf_ipv4;
+
+ if (tor_addr_parse_mask_ports(val, 0, &addr, &bits, NULL, NULL) < 0) {
+ if (msg)
+ tor_asprintf(msg, "Error parsing VirtualAddressNetwork%s %s",
+ ipv6?"IPv6":"", val);
+ return -1;
+ }
+ if (tor_addr_family(&addr) != family) {
+ if (msg)
+ tor_asprintf(msg, "Incorrect address type for VirtualAddressNetwork%s",
+ ipv6?"IPv6":"");
+ return -1;
+ }
+#if 0
+ if (port_min != 1 || port_max != 65535) {
+ if (msg)
+ tor_asprintf(msg, "Can't specify ports on VirtualAddressNetwork%s",
+ ipv6?"IPv6":"");
+ return -1;
+ }
+#endif
+
+ if (bits > max_bits) {
+ if (msg)
+ tor_asprintf(msg, "VirtualAddressNetwork%s expects a /%d "
+ "network or larger",ipv6?"IPv6":"", max_bits);
+ return -1;
+ }
+
+ if (validate_only)
+ return 0;
+
+ tor_addr_copy(&conf->addr, &addr);
+ conf->bits = bits;
+
+ return 0;
+}
+
+/**
+ * Return true iff <b>addr</b> is likely to have been returned by
+ * client_dns_get_unused_address.
+ **/
+int
+address_is_in_virtual_range(const char *address)
+{
+ tor_addr_t addr;
+ tor_assert(address);
+ if (!strcasecmpend(address, ".virtual")) {
+ return 1;
+ } else if (tor_addr_parse(&addr, address) >= 0) {
+ const virtual_addr_conf_t *conf = (tor_addr_family(&addr) == AF_INET6) ?
+ &virtaddr_conf_ipv6 : &virtaddr_conf_ipv4;
+ if (tor_addr_compare_masked(&addr, &conf->addr, conf->bits, CMP_EXACT)==0)
+ return 1;
+ }
+ return 0;
+}
+
+/** Return a random address conforming to the virtual address configuration
+ * in <b>conf</b>.
+ */
+/* private */ void
+get_random_virtual_addr(const virtual_addr_conf_t *conf, tor_addr_t *addr_out)
+{
+ uint8_t tmp[4];
+ const uint8_t *addr_bytes;
+ uint8_t bytes[16];
+ const int ipv6 = tor_addr_family(&conf->addr) == AF_INET6;
+ const int total_bytes = ipv6 ? 16 : 4;
+
+ tor_assert(conf->bits <= total_bytes * 8);
+
+ /* Set addr_bytes to the bytes of the virtual network, in host order */
+ if (ipv6) {
+ addr_bytes = tor_addr_to_in6_addr8(&conf->addr);
+ } else {
+ set_uint32(tmp, tor_addr_to_ipv4n(&conf->addr));
+ addr_bytes = tmp;
+ }
+
+ /* Get an appropriate number of random bytes. */
+ crypto_rand((char*)bytes, total_bytes);
+
+ /* Now replace the first "conf->bits" bits of 'bytes' with addr_bytes*/
+ if (conf->bits >= 8)
+ memcpy(bytes, addr_bytes, conf->bits / 8);
+ if (conf->bits & 7) {
+ uint8_t mask = 0xff >> (conf->bits & 7);
+ bytes[conf->bits/8] &= mask;
+ bytes[conf->bits/8] |= addr_bytes[conf->bits/8] & ~mask;
+ }
+
+ if (ipv6)
+ tor_addr_from_ipv6_bytes(addr_out, (char*) bytes);
+ else
+ tor_addr_from_ipv4n(addr_out, get_uint32(bytes));
+
+ tor_assert(tor_addr_compare_masked(addr_out, &conf->addr,
+ conf->bits, CMP_EXACT)==0);
+}
+
+/** Return a newly allocated string holding an address of <b>type</b>
+ * (one of RESOLVED_TYPE_{IPV4|HOSTNAME}) that has not yet been mapped,
+ * and that is very unlikely to be the address of any real host.
+ *
+ * May return NULL if we have run out of virtual addresses.
+ */
+static char *
+addressmap_get_virtual_address(int type)
+{
+ char buf[64];
+ tor_assert(addressmap);
+
+ if (type == RESOLVED_TYPE_HOSTNAME) {
+ char rand[10];
+ do {
+ crypto_rand(rand, sizeof(rand));
+ base32_encode(buf,sizeof(buf),rand,sizeof(rand));
+ strlcat(buf, ".virtual", sizeof(buf));
+ } while (strmap_get(addressmap, buf));
+ return tor_strdup(buf);
+ } else if (type == RESOLVED_TYPE_IPV4 || type == RESOLVED_TYPE_IPV6) {
+ const int ipv6 = (type == RESOLVED_TYPE_IPV6);
+ const virtual_addr_conf_t *conf = ipv6 ?
+ &virtaddr_conf_ipv6 : &virtaddr_conf_ipv4;
+
+ /* Don't try more than 1000 times. This gives us P < 1e-9 for
+ * failing to get a good address so long as the address space is
+ * less than ~97.95% full. That's always going to be true under
+ * sensible circumstances for an IPv6 /10, and it's going to be
+ * true for an IPv4 /10 as long as we've handed out less than
+ * 4.08 million addresses. */
+ uint32_t attempts = 1000;
+
+ tor_addr_t addr;
+
+ while (attempts--) {
+ get_random_virtual_addr(conf, &addr);
+
+ if (!ipv6) {
+ /* Don't hand out any .0 or .255 address. */
+ const uint32_t a = tor_addr_to_ipv4h(&addr);
+ if ((a & 0xff) == 0 || (a & 0xff) == 0xff)
+ continue;
+ }
+
+ tor_addr_to_str(buf, &addr, sizeof(buf), 1);
+ if (!strmap_get(addressmap, buf)) {
+ /* XXXX This code is to make sure I didn't add an undecorated version
+ * by mistake. I hope it's needless. */
+ char tmp[TOR_ADDR_BUF_LEN];
+ tor_addr_to_str(buf, &addr, sizeof(tmp), 0);
+ if (strmap_get(addressmap, tmp)) {
+ log_warn(LD_BUG, "%s wasn't in the addressmap, but %s was.",
+ buf, tmp);
+ continue;
+ }
+
+ return tor_strdup(buf);
+ }
+ }
+ log_warn(LD_CONFIG, "Ran out of virtual addresses!");
+ return NULL;
+ } else {
+ log_warn(LD_BUG, "Called with unsupported address type (%d)", type);
+ return NULL;
+ }
+}
+
+/** A controller has requested that we map some address of type
+ * <b>type</b> to the address <b>new_address</b>. Choose an address
+ * that is unlikely to be used, and map it, and return it in a newly
+ * allocated string. If another address of the same type is already
+ * mapped to <b>new_address</b>, try to return a copy of that address.
+ *
+ * The string in <b>new_address</b> may be freed or inserted into a map
+ * as appropriate. May return NULL if are out of virtual addresses.
+ **/
+const char *
+addressmap_register_virtual_address(int type, char *new_address)
+{
+ char **addrp;
+ virtaddress_entry_t *vent;
+ int vent_needs_to_be_added = 0;
+
+ tor_assert(new_address);
+ tor_assert(addressmap);
+ tor_assert(virtaddress_reversemap);
+
+ vent = strmap_get(virtaddress_reversemap, new_address);
+ if (!vent) {
+ vent = tor_malloc_zero(sizeof(virtaddress_entry_t));
+ vent_needs_to_be_added = 1;
+ }
+
+ if (type == RESOLVED_TYPE_IPV4)
+ addrp = &vent->ipv4_address;
+ else if (type == RESOLVED_TYPE_IPV6)
+ addrp = &vent->ipv6_address;
+ else
+ addrp = &vent->hostname_address;
+
+ if (*addrp) {
+ addressmap_entry_t *ent = strmap_get(addressmap, *addrp);
+ if (ent && ent->new_address &&
+ !strcasecmp(new_address, ent->new_address)) {
+ tor_free(new_address);
+ tor_assert(!vent_needs_to_be_added);
+ return tor_strdup(*addrp);
+ } else {
+ log_warn(LD_BUG,
+ "Internal confusion: I thought that '%s' was mapped to by "
+ "'%s', but '%s' really maps to '%s'. This is a harmless bug.",
+ safe_str_client(new_address),
+ safe_str_client(*addrp),
+ safe_str_client(*addrp),
+ ent?safe_str_client(ent->new_address):"(nothing)");
+ }
+ }
+
+ tor_free(*addrp);
+ *addrp = addressmap_get_virtual_address(type);
+ if (!*addrp) {
+ tor_free(vent);
+ tor_free(new_address);
+ return NULL;
+ }
+ 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, 0, 0);
+
+#if 0
+ {
+ /* Try to catch possible bugs */
+ addressmap_entry_t *ent;
+ ent = strmap_get(addressmap, *addrp);
+ tor_assert(ent);
+ tor_assert(!strcasecmp(ent->new_address,new_address));
+ vent = strmap_get(virtaddress_reversemap, new_address);
+ tor_assert(vent);
+ tor_assert(!strcasecmp(*addrp,
+ (type == RESOLVED_TYPE_IPV4) ?
+ vent->ipv4_address : vent->hostname_address));
+ log_info(LD_APP, "Map from %s to %s okay.",
+ safe_str_client(*addrp),
+ safe_str_client(new_address));
+ }
+#endif
+
+ return *addrp;
+}
+
+/** Return 1 if <b>address</b> has funny characters in it like colons. Return
+ * 0 if it's fine, or if we're configured to allow it anyway. <b>client</b>
+ * should be true if we're using this address as a client; false if we're
+ * using it as a server.
+ */
+int
+address_is_invalid_destination(const char *address, int client)
+{
+ if (client) {
+ if (get_options()->AllowNonRFC953Hostnames)
+ return 0;
+ } else {
+ if (get_options()->ServerDNSAllowNonRFC953Hostnames)
+ return 0;
+ }
+
+ /* It might be an IPv6 address! */
+ {
+ tor_addr_t a;
+ if (tor_addr_parse(&a, address) >= 0)
+ return 0;
+ }
+
+ while (*address) {
+ if (TOR_ISALNUM(*address) ||
+ *address == '-' ||
+ *address == '.' ||
+ *address == '_') /* Underscore is not allowed, but Windows does it
+ * sometimes, just to thumb its nose at the IETF. */
+ ++address;
+ else
+ return 1;
+ }
+ return 0;
+}
+
+/** Iterate over all address mappings which have expiry times between
+ * min_expires and max_expires, inclusive. If sl is provided, add an
+ * "old-addr new-addr expiry" string to sl for each mapping, omitting
+ * the expiry time if want_expiry is false. If sl is NULL, remove the
+ * mappings.
+ */
+void
+addressmap_get_mappings(smartlist_t *sl, time_t min_expires,
+ time_t max_expires, int want_expiry)
+{
+ strmap_iter_t *iter;
+ const char *key;
+ void *val_;
+ addressmap_entry_t *val;
+
+ if (!addressmap)
+ addressmap_init();
+
+ for (iter = strmap_iter_init(addressmap); !strmap_iter_done(iter); ) {
+ strmap_iter_get(iter, &key, &val_);
+ val = val_;
+ if (val->expires >= min_expires && val->expires <= max_expires) {
+ if (!sl) {
+ iter = strmap_iter_next_rmv(addressmap,iter);
+ addressmap_ent_remove(key, val);
+ continue;
+ } else if (val->new_address) {
+ const char *src_wc = val->src_wildcard ? "*." : "";
+ const char *dst_wc = val->dst_wildcard ? "*." : "";
+ if (want_expiry) {
+ if (val->expires < 3 || val->expires == TIME_MAX)
+ smartlist_add_asprintf(sl, "%s%s %s%s NEVER",
+ src_wc, key, dst_wc, val->new_address);
+ else {
+ char time[ISO_TIME_LEN+1];
+ format_iso_time(time, val->expires);
+ smartlist_add_asprintf(sl, "%s%s %s%s \"%s\"",
+ src_wc, key, dst_wc, val->new_address,
+ time);
+ }
+ } else {
+ smartlist_add_asprintf(sl, "%s%s %s%s",
+ src_wc, key, dst_wc, val->new_address);
+ }
+ }
+ }
+ iter = strmap_iter_next(addressmap,iter);
+ }
+}
+
diff --git a/src/or/addressmap.h b/src/or/addressmap.h
new file mode 100644
index 0000000000..40210ee990
--- /dev/null
+++ b/src/or/addressmap.h
@@ -0,0 +1,60 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_ADDRESSMAP_H
+#define TOR_ADDRESSMAP_H
+
+void addressmap_init(void);
+void addressmap_clear_excluded_trackexithosts(const or_options_t *options);
+void addressmap_clear_invalid_automaps(const or_options_t *options);
+void addressmap_clean(time_t now);
+void addressmap_clear_configured(void);
+void addressmap_clear_transient(void);
+void addressmap_free_all(void);
+#define AMR_FLAG_USE_IPV4_DNS (1u<<0)
+#define AMR_FLAG_USE_IPV6_DNS (1u<<1)
+int addressmap_rewrite(char *address, size_t maxlen, unsigned flags,
+ time_t *expires_out,
+ addressmap_entry_source_t *exit_source_out);
+int addressmap_rewrite_reverse(char *address, size_t maxlen, unsigned flags,
+ 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,
+ const int address_wildcard,
+ const int new_address_wildcard);
+int parse_virtual_addr_network(const char *val,
+ sa_family_t family, int validate_only,
+ char **msg);
+int client_dns_incr_failures(const char *address);
+void client_dns_clear_failures(const char *address);
+void client_dns_set_addressmap(entry_connection_t *for_conn,
+ const char *address, const tor_addr_t *val,
+ const char *exitname, int ttl);
+const char *addressmap_register_virtual_address(int type, char *new_address);
+void addressmap_get_mappings(smartlist_t *sl, time_t min_expires,
+ time_t max_expires, int want_expiry);
+int address_is_in_virtual_range(const char *addr);
+void clear_trackexithost_mappings(const char *exitname);
+void client_dns_set_reverse_addressmap(entry_connection_t *for_conn,
+ const char *address, const char *v,
+ const char *exitname, int ttl);
+int addressmap_address_should_automap(const char *address,
+ const or_options_t *options);
+
+#ifdef ADDRESSMAP_PRIVATE
+typedef struct virtual_addr_conf_t {
+ tor_addr_t addr;
+ maskbits_t bits;
+} virtual_addr_conf_t;
+
+void get_random_virtual_addr(const virtual_addr_conf_t *conf,
+ tor_addr_t *addr_out);
+#endif
+
+#endif
+
diff --git a/src/or/buffers.c b/src/or/buffers.c
index 0e9bb99545..33ea978daa 100644
--- a/src/or/buffers.c
+++ b/src/or/buffers.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -12,6 +12,7 @@
**/
#define BUFFERS_PRIVATE
#include "or.h"
+#include "addressmap.h"
#include "buffers.h"
#include "config.h"
#include "connection_edge.h"
@@ -334,11 +335,11 @@ buf_dump_freelist_sizes(int severity)
{
#ifdef ENABLE_BUF_FREELISTS
int i;
- log(severity, LD_MM, "====== Buffer freelists:");
+ tor_log(severity, LD_MM, "====== Buffer freelists:");
for (i = 0; freelists[i].alloc_size; ++i) {
uint64_t total = ((uint64_t)freelists[i].cur_length) *
freelists[i].alloc_size;
- log(severity, LD_MM,
+ tor_log(severity, LD_MM,
U64_FORMAT" bytes in %d %d-byte chunks ["U64_FORMAT
" misses; "U64_FORMAT" frees; "U64_FORMAT" hits]",
U64_PRINTF_ARG(total),
@@ -347,7 +348,7 @@ buf_dump_freelist_sizes(int severity)
U64_PRINTF_ARG(freelists[i].n_free),
U64_PRINTF_ARG(freelists[i].n_hit));
}
- log(severity, LD_MM, U64_FORMAT" allocations in non-freelist sizes",
+ tor_log(severity, LD_MM, U64_FORMAT" allocations in non-freelist sizes",
U64_PRINTF_ARG(n_freelist_miss));
#else
(void)severity;
@@ -1516,22 +1517,19 @@ log_unsafe_socks_warning(int socks_protocol, const char *address,
static ratelim_t socks_ratelim = RATELIM_INIT(SOCKS_WARN_INTERVAL);
const or_options_t *options = get_options();
- char *m = NULL;
if (! options->WarnUnsafeSocks)
return;
- if (safe_socks || (m = rate_limit_log(&socks_ratelim, approx_time()))) {
- log_warn(LD_APP,
+ if (safe_socks) {
+ log_fn_ratelim(&socks_ratelim, LOG_WARN, LD_APP,
"Your application (using socks%d to port %d) is giving "
"Tor only an IP address. Applications that do DNS resolves "
"themselves may leak information. Consider using Socks4A "
"(e.g. via privoxy or socat) instead. For more information, "
"please see https://wiki.torproject.org/TheOnionRouter/"
- "TorFAQ#SOCKSAndDNS.%s%s",
+ "TorFAQ#SOCKSAndDNS.%s",
socks_protocol,
(int)port,
- safe_socks ? " Rejecting." : "",
- m ? m : "");
- tor_free(m);
+ safe_socks ? " Rejecting." : "");
}
control_event_client_status(LOG_WARN,
"DANGEROUS_SOCKS PROTOCOL=SOCKS%d ADDRESS=%s:%d",
@@ -1556,14 +1554,14 @@ socks_request_free(socks_request_t *req)
if (!req)
return;
if (req->username) {
- memset(req->username, 0x10, req->usernamelen);
+ memwipe(req->username, 0x10, req->usernamelen);
tor_free(req->username);
}
if (req->password) {
- memset(req->password, 0x04, req->passwordlen);
+ memwipe(req->password, 0x04, req->passwordlen);
tor_free(req->password);
}
- memset(req, 0xCC, sizeof(socks_request_t));
+ memwipe(req, 0xCC, sizeof(socks_request_t));
tor_free(req);
}
diff --git a/src/or/buffers.h b/src/or/buffers.h
index f31d2910f7..c947f0ba98 100644
--- a/src/or/buffers.h
+++ b/src/or/buffers.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/channel.c b/src/or/channel.c
index 9b353a102c..f8afc405e6 100644
--- a/src/or/channel.c
+++ b/src/or/channel.c
@@ -1,4 +1,4 @@
-/* * Copyright (c) 2012, The Tor Project, Inc. */
+/* * Copyright (c) 2012-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -33,7 +33,7 @@
typedef struct cell_queue_entry_s cell_queue_entry_t;
struct cell_queue_entry_s {
- SIMPLEQ_ENTRY(cell_queue_entry_s) next;
+ TOR_SIMPLEQ_ENTRY(cell_queue_entry_s) next;
enum {
CELL_QUEUE_FIXED,
CELL_QUEUE_VAR,
@@ -89,7 +89,7 @@ HT_HEAD(channel_idmap, channel_idmap_entry_s) channel_identity_map =
typedef struct channel_idmap_entry_s {
HT_ENTRY(channel_idmap_entry_s) node;
uint8_t digest[DIGEST_LEN];
- LIST_HEAD(channel_list_s, channel_s) channel_list;
+ TOR_LIST_HEAD(channel_list_s, channel_s) channel_list;
} channel_idmap_entry_t;
static INLINE unsigned
@@ -554,10 +554,10 @@ channel_add_to_digest_map(channel_t *chan)
if (! ent) {
ent = tor_malloc(sizeof(channel_idmap_entry_t));
memcpy(ent->digest, chan->identity_digest, DIGEST_LEN);
- LIST_INIT(&ent->channel_list);
+ TOR_LIST_INIT(&ent->channel_list);
HT_INSERT(channel_idmap, &channel_identity_map, ent);
}
- LIST_INSERT_HEAD(&ent->channel_list, chan, next_with_same_id);
+ TOR_LIST_INSERT_HEAD(&ent->channel_list, chan, next_with_same_id);
log_debug(LD_CHANNEL,
"Added channel %p (global ID " U64_FORMAT ") "
@@ -612,7 +612,7 @@ channel_remove_from_digest_map(channel_t *chan)
#endif
/* Pull it out of its list, wherever that list is */
- LIST_REMOVE(chan, next_with_same_id);
+ TOR_LIST_REMOVE(chan, next_with_same_id);
memcpy(search.digest, chan->identity_digest, DIGEST_LEN);
ent = HT_FIND(channel_idmap, &channel_identity_map, &search);
@@ -621,7 +621,7 @@ channel_remove_from_digest_map(channel_t *chan)
if (ent) {
/* Okay, it's here */
- if (LIST_EMPTY(&ent->channel_list)) {
+ if (TOR_LIST_EMPTY(&ent->channel_list)) {
HT_REMOVE(channel_idmap, &channel_identity_map, ent);
tor_free(ent);
}
@@ -691,7 +691,7 @@ channel_find_by_remote_digest(const char *identity_digest)
memcpy(search.digest, identity_digest, DIGEST_LEN);
ent = HT_FIND(channel_idmap, &channel_identity_map, &search);
if (ent) {
- rv = LIST_FIRST(&ent->channel_list);
+ rv = TOR_LIST_FIRST(&ent->channel_list);
}
return rv;
@@ -709,7 +709,7 @@ channel_next_with_digest(channel_t *chan)
{
tor_assert(chan);
- return LIST_NEXT(chan, next_with_same_id);
+ return TOR_LIST_NEXT(chan, next_with_same_id);
}
/**
@@ -735,8 +735,8 @@ channel_init(channel_t *chan)
chan->next_circ_id = crypto_rand_int(1 << 15);
/* Initialize queues. */
- SIMPLEQ_INIT(&chan->incoming_queue);
- SIMPLEQ_INIT(&chan->outgoing_queue);
+ TOR_SIMPLEQ_INIT(&chan->incoming_queue);
+ TOR_SIMPLEQ_INIT(&chan->outgoing_queue);
/* Initialize list entries. */
memset(&chan->next_with_same_id, 0, sizeof(chan->next_with_same_id));
@@ -879,16 +879,16 @@ channel_force_free(channel_t *chan)
}
/* We might still have a cell queue; kill it */
- SIMPLEQ_FOREACH_SAFE(cell, &chan->incoming_queue, next, cell_tmp) {
+ TOR_SIMPLEQ_FOREACH_SAFE(cell, &chan->incoming_queue, next, cell_tmp) {
cell_queue_entry_free(cell, 0);
}
- SIMPLEQ_INIT(&chan->incoming_queue);
+ TOR_SIMPLEQ_INIT(&chan->incoming_queue);
/* Outgoing cell queue is similar, but we can have to free packed cells */
- SIMPLEQ_FOREACH_SAFE(cell, &chan->outgoing_queue, next, cell_tmp) {
+ TOR_SIMPLEQ_FOREACH_SAFE(cell, &chan->outgoing_queue, next, cell_tmp) {
cell_queue_entry_free(cell, 0);
}
- SIMPLEQ_INIT(&chan->outgoing_queue);
+ TOR_SIMPLEQ_INIT(&chan->outgoing_queue);
tor_free(chan);
}
@@ -1051,12 +1051,25 @@ channel_set_cell_handlers(channel_t *chan,
chan->var_cell_handler = var_cell_handler;
/* Re-run the queue if we have one and there's any reason to */
- if (! SIMPLEQ_EMPTY(&chan->incoming_queue) &&
+ if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue) &&
try_again &&
(chan->cell_handler ||
chan->var_cell_handler)) channel_process_cells(chan);
}
+/*
+ * On closing channels
+ *
+ * There are three functions that close channels, for use in
+ * different circumstances:
+ *
+ * - Use channel_mark_for_close() for most cases
+ * - Use channel_close_from_lower_layer() if you are connection_or.c
+ * and the other end closes the underlying connection.
+ * - Use channel_close_for_error() if you are connection_or.c and
+ * some sort of error has occurred.
+ */
+
/**
* Mark a channel for closure
*
@@ -1673,7 +1686,7 @@ channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q)
}
/* Can we send it right out? If so, try */
- if (SIMPLEQ_EMPTY(&chan->outgoing_queue) &&
+ if (TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue) &&
chan->state == CHANNEL_STATE_OPEN) {
/* Pick the right write function for this cell type and save the result */
switch (q->type) {
@@ -1715,7 +1728,7 @@ channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q)
* used the stack.
*/
tmp = cell_queue_entry_dup(q);
- SIMPLEQ_INSERT_TAIL(&chan->outgoing_queue, tmp, next);
+ TOR_SIMPLEQ_INSERT_TAIL(&chan->outgoing_queue, tmp, next);
/* Try to process the queue? */
if (chan->state == CHANNEL_STATE_OPEN) channel_flush_cells(chan);
}
@@ -1901,15 +1914,15 @@ channel_change_state(channel_t *chan, channel_state_t to_state)
channel_do_open_actions(chan);
/* Check for queued cells to process */
- if (! SIMPLEQ_EMPTY(&chan->incoming_queue))
+ if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue))
channel_process_cells(chan);
- if (! SIMPLEQ_EMPTY(&chan->outgoing_queue))
+ if (! TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue))
channel_flush_cells(chan);
} else if (to_state == CHANNEL_STATE_CLOSED ||
to_state == CHANNEL_STATE_ERROR) {
/* Assert that all queues are empty */
- tor_assert(SIMPLEQ_EMPTY(&chan->incoming_queue));
- tor_assert(SIMPLEQ_EMPTY(&chan->outgoing_queue));
+ tor_assert(TOR_SIMPLEQ_EMPTY(&chan->incoming_queue));
+ tor_assert(TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue));
}
}
@@ -2083,7 +2096,7 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan,
/* If we aren't in CHANNEL_STATE_OPEN, nothing goes through */
if (chan->state == CHANNEL_STATE_OPEN) {
while ((unlimited || num_cells > flushed) &&
- NULL != (q = SIMPLEQ_FIRST(&chan->outgoing_queue))) {
+ NULL != (q = TOR_SIMPLEQ_FIRST(&chan->outgoing_queue))) {
if (1) {
/*
@@ -2172,7 +2185,7 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan,
}
/* if q got NULLed out, we used it and should remove the queue entry */
- if (!q) SIMPLEQ_REMOVE_HEAD(&chan->outgoing_queue, next);
+ if (!q) TOR_SIMPLEQ_REMOVE_HEAD(&chan->outgoing_queue, next);
/* No cell removed from list, so we can't go on any further */
else break;
}
@@ -2180,7 +2193,7 @@ channel_flush_some_cells_from_outgoing_queue(channel_t *chan,
}
/* Did we drain the queue? */
- if (SIMPLEQ_EMPTY(&chan->outgoing_queue)) {
+ if (TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue)) {
channel_timestamp_drained(chan);
}
@@ -2214,7 +2227,7 @@ channel_more_to_flush(channel_t *chan)
tor_assert(chan);
/* Check if we have any queued */
- if (! SIMPLEQ_EMPTY(&chan->incoming_queue))
+ if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue))
return 1;
/* Check if any circuits would like to queue some */
@@ -2422,13 +2435,13 @@ channel_process_cells(channel_t *chan)
if (!(chan->cell_handler ||
chan->var_cell_handler)) return;
/* Nothing we can do if we have no cells */
- if (SIMPLEQ_EMPTY(&chan->incoming_queue)) return;
+ if (TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) return;
/*
* Process cells until we're done or find one we have no current handler
* for.
*/
- while (NULL != (q = SIMPLEQ_FIRST(&chan->incoming_queue))) {
+ while (NULL != (q = TOR_SIMPLEQ_FIRST(&chan->incoming_queue))) {
tor_assert(q);
tor_assert(q->type == CELL_QUEUE_FIXED ||
q->type == CELL_QUEUE_VAR);
@@ -2436,7 +2449,7 @@ channel_process_cells(channel_t *chan)
if (q->type == CELL_QUEUE_FIXED &&
chan->cell_handler) {
/* Handle a fixed-length cell */
- SIMPLEQ_REMOVE_HEAD(&chan->incoming_queue, next);
+ TOR_SIMPLEQ_REMOVE_HEAD(&chan->incoming_queue, next);
tor_assert(q->u.fixed.cell);
log_debug(LD_CHANNEL,
"Processing incoming cell_t %p for channel %p (global ID "
@@ -2448,7 +2461,7 @@ channel_process_cells(channel_t *chan)
} else if (q->type == CELL_QUEUE_VAR &&
chan->var_cell_handler) {
/* Handle a variable-length cell */
- SIMPLEQ_REMOVE_HEAD(&chan->incoming_queue, next);
+ TOR_SIMPLEQ_REMOVE_HEAD(&chan->incoming_queue, next);
tor_assert(q->u.var.var_cell);
log_debug(LD_CHANNEL,
"Processing incoming var_cell_t %p for channel %p (global ID "
@@ -2483,7 +2496,7 @@ channel_queue_cell(channel_t *chan, cell_t *cell)
/* Do we need to queue it, or can we just call the handler right away? */
if (!(chan->cell_handler)) need_to_queue = 1;
- if (! SIMPLEQ_EMPTY(&chan->incoming_queue))
+ if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue))
need_to_queue = 1;
/* Timestamp for receiving */
@@ -2509,7 +2522,7 @@ channel_queue_cell(channel_t *chan, cell_t *cell)
"(global ID " U64_FORMAT ")",
cell, chan,
U64_PRINTF_ARG(chan->global_identifier));
- SIMPLEQ_INSERT_TAIL(&chan->incoming_queue, q, next);
+ TOR_SIMPLEQ_INSERT_TAIL(&chan->incoming_queue, q, next);
if (chan->cell_handler ||
chan->var_cell_handler) {
channel_process_cells(chan);
@@ -2536,7 +2549,7 @@ channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell)
/* Do we need to queue it, or can we just call the handler right away? */
if (!(chan->var_cell_handler)) need_to_queue = 1;
- if (! SIMPLEQ_EMPTY(&chan->incoming_queue))
+ if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue))
need_to_queue = 1;
/* Timestamp for receiving */
@@ -2562,7 +2575,7 @@ channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell)
"(global ID " U64_FORMAT ")",
var_cell, chan,
U64_PRINTF_ARG(chan->global_identifier));
- SIMPLEQ_INSERT_TAIL(&chan->incoming_queue, q, next);
+ TOR_SIMPLEQ_INSERT_TAIL(&chan->incoming_queue, q, next);
if (chan->cell_handler ||
chan->var_cell_handler) {
channel_process_cells(chan);
@@ -2585,17 +2598,29 @@ channel_send_destroy(circid_t circ_id, channel_t *chan, int reason)
tor_assert(chan);
- memset(&cell, 0, sizeof(cell_t));
- cell.circ_id = circ_id;
- cell.command = CELL_DESTROY;
- cell.payload[0] = (uint8_t) reason;
- log_debug(LD_OR,
- "Sending destroy (circID %d) on channel %p "
- "(global ID " U64_FORMAT ")",
- circ_id, chan,
- U64_PRINTF_ARG(chan->global_identifier));
+ /* Check to make sure we can send on this channel first */
+ if (!(chan->state == CHANNEL_STATE_CLOSING ||
+ chan->state == CHANNEL_STATE_CLOSED ||
+ chan->state == CHANNEL_STATE_ERROR)) {
+ memset(&cell, 0, sizeof(cell_t));
+ cell.circ_id = circ_id;
+ cell.command = CELL_DESTROY;
+ cell.payload[0] = (uint8_t) reason;
+ log_debug(LD_OR,
+ "Sending destroy (circID %d) on channel %p "
+ "(global ID " U64_FORMAT ")",
+ circ_id, chan,
+ U64_PRINTF_ARG(chan->global_identifier));
- channel_write_cell(chan, &cell);
+ channel_write_cell(chan, &cell);
+ } else {
+ log_warn(LD_BUG,
+ "Someone called channel_send_destroy() for circID %d "
+ "on a channel " U64_FORMAT " at %p in state %s (%d)",
+ circ_id, U64_PRINTF_ARG(chan->global_identifier),
+ chan, channel_state_to_string(chan->state),
+ chan->state);
+ }
return 0;
}
@@ -2611,10 +2636,10 @@ void
channel_dumpstats(int severity)
{
if (all_channels && smartlist_len(all_channels) > 0) {
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
"Dumping statistics about %d channels:",
smartlist_len(all_channels));
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
"%d are active, and %d are done and waiting for cleanup",
(active_channels != NULL) ?
smartlist_len(active_channels) : 0,
@@ -2624,10 +2649,10 @@ channel_dumpstats(int severity)
SMARTLIST_FOREACH(all_channels, channel_t *, chan,
channel_dump_statistics(chan, severity));
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
"Done spamming about channels now");
} else {
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
"No channels to dump");
}
}
@@ -2643,10 +2668,10 @@ void
channel_listener_dumpstats(int severity)
{
if (all_listeners && smartlist_len(all_listeners) > 0) {
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
"Dumping statistics about %d channel listeners:",
smartlist_len(all_listeners));
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
"%d are active and %d are done and waiting for cleanup",
(active_listeners != NULL) ?
smartlist_len(active_listeners) : 0,
@@ -2656,10 +2681,10 @@ channel_listener_dumpstats(int severity)
SMARTLIST_FOREACH(all_listeners, channel_listener_t *, chan_l,
channel_listener_dump_statistics(chan_l, severity));
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
"Done spamming about channel listeners now");
} else {
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
"No channel listeners to dump");
}
}
@@ -3090,7 +3115,7 @@ chan_cell_queue_len(const chan_cell_queue_t *queue)
{
int r = 0;
cell_queue_entry_t *cell;
- SIMPLEQ_FOREACH(cell, queue, next)
+ TOR_SIMPLEQ_FOREACH(cell, queue, next)
++r;
return r;
}
@@ -3114,13 +3139,13 @@ channel_dump_statistics(channel_t *chan, int severity)
age = (double)(now - chan->timestamp_created);
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
"Channel " U64_FORMAT " (at %p) with transport %s is in state "
"%s (%d)",
U64_PRINTF_ARG(chan->global_identifier), chan,
channel_describe_transport(chan),
channel_state_to_string(chan->state), chan->state);
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " was created at " U64_FORMAT
" (" U64_FORMAT " seconds ago) "
"and last active at " U64_FORMAT " (" U64_FORMAT " seconds ago)",
@@ -3133,14 +3158,14 @@ channel_dump_statistics(channel_t *chan, int severity)
/* Handle digest and nickname */
if (!tor_digest_is_zero(chan->identity_digest)) {
if (chan->nickname) {
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " says it is connected "
"to an OR with digest %s and nickname %s",
U64_PRINTF_ARG(chan->global_identifier),
hex_str(chan->identity_digest, DIGEST_LEN),
chan->nickname);
} else {
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " says it is connected "
"to an OR with digest %s and no known nickname",
U64_PRINTF_ARG(chan->global_identifier),
@@ -3148,13 +3173,13 @@ channel_dump_statistics(channel_t *chan, int severity)
}
} else {
if (chan->nickname) {
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " does not know the digest"
" of the OR it is connected to, but reports its nickname is %s",
U64_PRINTF_ARG(chan->global_identifier),
chan->nickname);
} else {
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " does not know the digest"
" or the nickname of the OR it is connected to",
U64_PRINTF_ARG(chan->global_identifier));
@@ -3166,7 +3191,7 @@ channel_dump_statistics(channel_t *chan, int severity)
if (have_remote_addr) {
char *actual = tor_strdup(channel_get_actual_remote_descr(chan));
remote_addr_str = tor_dup_addr(&remote_addr);
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " says its remote address"
" is %s, and gives a canonical description of \"%s\" and an "
"actual description of \"%s\"",
@@ -3178,7 +3203,7 @@ channel_dump_statistics(channel_t *chan, int severity)
tor_free(actual);
} else {
char *actual = tor_strdup(channel_get_actual_remote_descr(chan));
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " does not know its remote "
"address, but gives a canonical description of \"%s\" and an "
"actual description of \"%s\"",
@@ -3189,7 +3214,7 @@ channel_dump_statistics(channel_t *chan, int severity)
}
/* Handle marks */
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " has these marks: %s %s %s "
"%s %s %s",
U64_PRINTF_ARG(chan->global_identifier),
@@ -3208,7 +3233,7 @@ channel_dump_statistics(channel_t *chan, int severity)
"incoming" : "outgoing");
/* Describe queues */
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " has %d queued incoming cells"
" and %d queued outgoing cells",
U64_PRINTF_ARG(chan->global_identifier),
@@ -3216,7 +3241,7 @@ channel_dump_statistics(channel_t *chan, int severity)
chan_cell_queue_len(&chan->outgoing_queue));
/* Describe circuits */
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " has %d active circuits out of"
" %d in total",
U64_PRINTF_ARG(chan->global_identifier),
@@ -3226,25 +3251,25 @@ channel_dump_statistics(channel_t *chan, int severity)
circuitmux_num_circuits(chan->cmux) : 0);
/* Describe timestamps */
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " was last used by a "
"client at " U64_FORMAT " (" U64_FORMAT " seconds ago)",
U64_PRINTF_ARG(chan->global_identifier),
U64_PRINTF_ARG(chan->timestamp_client),
U64_PRINTF_ARG(now - chan->timestamp_client));
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " was last drained at "
U64_FORMAT " (" U64_FORMAT " seconds ago)",
U64_PRINTF_ARG(chan->global_identifier),
U64_PRINTF_ARG(chan->timestamp_drained),
U64_PRINTF_ARG(now - chan->timestamp_drained));
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " last received a cell "
"at " U64_FORMAT " (" U64_FORMAT " seconds ago)",
U64_PRINTF_ARG(chan->global_identifier),
U64_PRINTF_ARG(chan->timestamp_recv),
U64_PRINTF_ARG(now - chan->timestamp_recv));
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " last trasmitted a cell "
"at " U64_FORMAT " (" U64_FORMAT " seconds ago)",
U64_PRINTF_ARG(chan->global_identifier),
@@ -3252,7 +3277,7 @@ channel_dump_statistics(channel_t *chan, int severity)
U64_PRINTF_ARG(now - chan->timestamp_xmit));
/* Describe counters and rates */
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " has received "
U64_FORMAT " cells and transmitted " U64_FORMAT,
U64_PRINTF_ARG(chan->global_identifier),
@@ -3263,13 +3288,13 @@ channel_dump_statistics(channel_t *chan, int severity)
if (chan->n_cells_recved > 0) {
avg = (double)(chan->n_cells_recved) / age;
if (avg >= 1.0) {
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " has averaged %f "
"cells received per second",
U64_PRINTF_ARG(chan->global_identifier), avg);
} else if (avg >= 0.0) {
interval = 1.0 / avg;
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " has averaged %f "
"seconds between received cells",
U64_PRINTF_ARG(chan->global_identifier), interval);
@@ -3278,13 +3303,13 @@ channel_dump_statistics(channel_t *chan, int severity)
if (chan->n_cells_xmitted > 0) {
avg = (double)(chan->n_cells_xmitted) / age;
if (avg >= 1.0) {
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " has averaged %f "
"cells transmitted per second",
U64_PRINTF_ARG(chan->global_identifier), avg);
} else if (avg >= 0.0) {
interval = 1.0 / avg;
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " has averaged %f "
"seconds between transmitted cells",
U64_PRINTF_ARG(chan->global_identifier), interval);
@@ -3312,13 +3337,13 @@ channel_listener_dump_statistics(channel_listener_t *chan_l, int severity)
age = (double)(now - chan_l->timestamp_created);
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
"Channel listener " U64_FORMAT " (at %p) with transport %s is in "
"state %s (%d)",
U64_PRINTF_ARG(chan_l->global_identifier), chan_l,
channel_listener_describe_transport(chan_l),
channel_listener_state_to_string(chan_l->state), chan_l->state);
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel listener " U64_FORMAT " was created at " U64_FORMAT
" (" U64_FORMAT " seconds ago) "
"and last active at " U64_FORMAT " (" U64_FORMAT " seconds ago)",
@@ -3328,7 +3353,7 @@ channel_listener_dump_statistics(channel_listener_t *chan_l, int severity)
U64_PRINTF_ARG(chan_l->timestamp_active),
U64_PRINTF_ARG(now - chan_l->timestamp_active));
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel listener " U64_FORMAT " last accepted an incoming "
"channel at " U64_FORMAT " (" U64_FORMAT " seconds ago) "
"and has accepted " U64_FORMAT " channels in total",
@@ -3346,13 +3371,13 @@ channel_listener_dump_statistics(channel_listener_t *chan_l, int severity)
chan_l->n_accepted > 0) {
avg = (double)(chan_l->n_accepted) / age;
if (avg >= 1.0) {
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel listener " U64_FORMAT " has averaged %f incoming "
"channels per second",
U64_PRINTF_ARG(chan_l->global_identifier), avg);
} else if (avg >= 0.0) {
interval = 1.0 / avg;
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" * Channel listener " U64_FORMAT " has averaged %f seconds "
"between incoming channels",
U64_PRINTF_ARG(chan_l->global_identifier), interval);
@@ -3479,7 +3504,7 @@ channel_has_queued_writes(channel_t *chan)
tor_assert(chan);
tor_assert(chan->has_queued_writes);
- if (! SIMPLEQ_EMPTY(&chan->outgoing_queue)) {
+ if (! TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue)) {
has_writes = 1;
} else {
/* Check with the lower layer */
diff --git a/src/or/channel.h b/src/or/channel.h
index a21271ca1e..0933ec8d39 100644
--- a/src/or/channel.h
+++ b/src/or/channel.h
@@ -1,4 +1,4 @@
-/* * Copyright (c) 2012, The Tor Project, Inc. */
+/* * Copyright (c) 2012-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -19,7 +19,7 @@ typedef void (*channel_cell_handler_fn_ptr)(channel_t *, cell_t *);
typedef void (*channel_var_cell_handler_fn_ptr)(channel_t *, var_cell_t *);
struct cell_queue_entry_s;
-SIMPLEQ_HEAD(chan_cell_queue, cell_queue_entry_s) incoming_queue;
+TOR_SIMPLEQ_HEAD(chan_cell_queue, cell_queue_entry_s) incoming_queue;
typedef struct chan_cell_queue chan_cell_queue_t;
/*
@@ -125,7 +125,7 @@ struct channel_s {
* Linked list of channels with the same identity digest, for the
* digest->channel map
*/
- LIST_ENTRY(channel_s) next_with_same_id;
+ TOR_LIST_ENTRY(channel_s) next_with_same_id;
/* List of incoming cells to handle */
chan_cell_queue_t incoming_queue;
@@ -142,7 +142,7 @@ struct channel_s {
* When we send CREATE cells along this connection, which half of the
* space should we use?
*/
- circ_id_type_t circ_id_type:2;
+ ENUM_BF(circ_id_type_t) circ_id_type:2;
/** DOCDOC*/
unsigned wide_circ_ids:1;
/*
diff --git a/src/or/channeltls.c b/src/or/channeltls.c
index a699d3c7d1..1035a14127 100644
--- a/src/or/channeltls.c
+++ b/src/or/channeltls.c
@@ -1,4 +1,4 @@
-/* * Copyright (c) 2012, The Tor Project, Inc. */
+/* * Copyright (c) 2012-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -915,6 +915,8 @@ channel_tls_handle_cell(cell_t *cell, or_connection_t *conn)
case CELL_RELAY:
case CELL_RELAY_EARLY:
case CELL_DESTROY:
+ case CELL_CREATE2:
+ case CELL_CREATED2:
/*
* These are all transport independent and we pass them up through the
* channel_t mechanism. They are ultimately handled in command.c.
@@ -1533,7 +1535,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
safe_str(chan->conn->base_.address), \
chan->conn->base_.port, (s)); \
connection_or_close_for_error(chan->conn, 0); \
- return; \
+ goto err; \
} while (0)
if (chan->conn->base_.state != OR_CONN_STATE_OR_HANDSHAKING_V3)
diff --git a/src/or/channeltls.h b/src/or/channeltls.h
index b7e0475de6..b4a7e2beac 100644
--- a/src/or/channeltls.h
+++ b/src/or/channeltls.h
@@ -1,4 +1,4 @@
-/* * Copyright (c) 2012, The Tor Project, Inc. */
+/* * Copyright (c) 2012-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index 7607348b81..40751e02b1 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -25,9 +25,12 @@
#include "directory.h"
#include "entrynodes.h"
#include "main.h"
+#include "microdesc.h"
#include "networkstatus.h"
#include "nodelist.h"
#include "onion.h"
+#include "onion_tap.h"
+#include "onion_fast.h"
#include "policies.h"
#include "transports.h"
#include "relay.h"
@@ -37,6 +40,7 @@
#include "routerparse.h"
#include "routerset.h"
#include "crypto.h"
+#include "connection_edge.h"
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
@@ -53,14 +57,21 @@ static channel_t * channel_connect_for_circuit(const tor_addr_t *addr,
uint16_t port,
const char *id_digest);
static int circuit_deliver_create_cell(circuit_t *circ,
- uint8_t cell_type, const char *payload);
+ const create_cell_t *create_cell,
+ int relayed);
static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit);
static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath);
static int onion_extend_cpath(origin_circuit_t *circ);
static int count_acceptable_nodes(smartlist_t *routers);
static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
-static int entry_guard_inc_first_hop_count(entry_guard_t *guard);
-static void pathbias_count_success(origin_circuit_t *circ);
+static int entry_guard_inc_circ_attempt_count(entry_guard_t *guard);
+static void pathbias_count_build_success(origin_circuit_t *circ);
+static void pathbias_count_successful_close(origin_circuit_t *circ);
+static void pathbias_count_collapse(origin_circuit_t *circ);
+static void pathbias_count_use_failed(origin_circuit_t *circ);
+static void pathbias_measure_use_rate(entry_guard_t *guard);
+static void pathbias_measure_close_rate(entry_guard_t *guard);
+static void pathbias_scale_use_rates(entry_guard_t *guard);
/** This function tries to get a channel to the specified endpoint,
* and then calls command_setup_channel() to give it the right
@@ -470,14 +481,13 @@ circuit_n_chan_done(channel_t *chan, int status)
* died? */
}
} else {
- /* pull the create cell out of circ->onionskin, and send it */
- tor_assert(circ->n_chan_onionskin);
- if (circuit_deliver_create_cell(circ,CELL_CREATE,
- circ->n_chan_onionskin)<0) {
+ /* pull the create cell out of circ->n_chan_create_cell, and send it */
+ tor_assert(circ->n_chan_create_cell);
+ if (circuit_deliver_create_cell(circ, circ->n_chan_create_cell, 1)<0) {
circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT);
continue;
}
- tor_free(circ->n_chan_onionskin);
+ tor_free(circ->n_chan_create_cell);
circuit_set_state(circ, CIRCUIT_STATE_OPEN);
}
}
@@ -488,22 +498,25 @@ circuit_n_chan_done(channel_t *chan, int status)
/** Find a new circid that isn't currently in use on the circ->n_chan
* for the outgoing
- * circuit <b>circ</b>, and deliver a cell of type <b>cell_type</b>
- * (either CELL_CREATE or CELL_CREATE_FAST) with payload <b>payload</b>
- * to this circuit.
- * Return -1 if we failed to find a suitable circid, else return 0.
+ * circuit <b>circ</b>, and deliver the cell <b>create_cell</b> to this
+ * circuit. If <b>relayed</b> is true, this is a create cell somebody
+ * gave us via an EXTEND cell, so we shouldn't worry if we don't understand
+ * it. Return -1 if we failed to find a suitable circid, else return 0.
*/
static int
-circuit_deliver_create_cell(circuit_t *circ, uint8_t cell_type,
- const char *payload)
+circuit_deliver_create_cell(circuit_t *circ, const create_cell_t *create_cell,
+ int relayed)
{
cell_t cell;
circid_t id;
+ int r;
tor_assert(circ);
tor_assert(circ->n_chan);
- tor_assert(payload);
- tor_assert(cell_type == CELL_CREATE || cell_type == CELL_CREATE_FAST);
+ tor_assert(create_cell);
+ tor_assert(create_cell->cell_type == CELL_CREATE ||
+ create_cell->cell_type == CELL_CREATE_FAST ||
+ create_cell->cell_type == CELL_CREATE2);
id = get_unique_circ_id_by_chan(circ->n_chan);
if (!id) {
@@ -514,14 +527,30 @@ circuit_deliver_create_cell(circuit_t *circ, uint8_t cell_type,
circuit_set_n_circid_chan(circ, id, circ->n_chan);
memset(&cell, 0, sizeof(cell_t));
- cell.command = cell_type;
+ r = relayed ? create_cell_format_relayed(&cell, create_cell)
+ : create_cell_format(&cell, create_cell);
+ if (r < 0) {
+ log_warn(LD_CIRC,"Couldn't format create cell");
+ return -1;
+ }
cell.circ_id = circ->n_circ_id;
- memcpy(cell.payload, payload, ONIONSKIN_CHALLENGE_LEN);
append_cell_to_circuit_queue(circ, circ->n_chan, &cell,
CELL_DIRECTION_OUT, 0);
if (CIRCUIT_IS_ORIGIN(circ)) {
+ /* Update began timestamp for circuits starting their first hop */
+ if (TO_ORIGIN_CIRCUIT(circ)->cpath->state == CPATH_STATE_CLOSED) {
+ if (circ->n_chan->state != CHANNEL_STATE_OPEN) {
+ log_warn(LD_CIRC,
+ "Got first hop for a circuit without an opened channel. "
+ "State: %s.", channel_state_to_string(circ->n_chan->state));
+ tor_fragile_assert();
+ }
+
+ tor_gettimeofday(&circ->timestamp_began);
+ }
+
/* mark it so it gets better rate limiting treatment. */
channel_timestamp_client(circ->n_chan);
}
@@ -595,6 +624,73 @@ circuit_timeout_want_to_count_circ(origin_circuit_t *circ)
&& circ->build_state->desired_path_len == DEFAULT_ROUTE_LEN;
}
+#ifdef CURVE25519_ENABLED
+/** Return true if the ntor handshake is enabled in the configuration, or if
+ * it's been set to "auto" in the configuration and it's enabled in the
+ * consensus. */
+static int
+circuits_can_use_ntor(void)
+{
+ const or_options_t *options = get_options();
+ if (options->UseNTorHandshake != -1)
+ return options->UseNTorHandshake;
+ return networkstatus_get_param(NULL, "UseNTorHandshake", 0, 0, 1);
+}
+#endif
+
+/** Decide whether to use a TAP or ntor handshake for connecting to <b>ei</b>
+ * directly, and set *<b>cell_type_out</b> and *<b>handshake_type_out</b>
+ * accordingly. */
+static void
+circuit_pick_create_handshake(uint8_t *cell_type_out,
+ uint16_t *handshake_type_out,
+ const extend_info_t *ei)
+{
+#ifdef CURVE25519_ENABLED
+ if (!tor_mem_is_zero((const char*)ei->curve25519_onion_key.public_key,
+ CURVE25519_PUBKEY_LEN) &&
+ circuits_can_use_ntor()) {
+ *cell_type_out = CELL_CREATE2;
+ *handshake_type_out = ONION_HANDSHAKE_TYPE_NTOR;
+ return;
+ }
+#else
+ (void) ei;
+#endif
+
+ *cell_type_out = CELL_CREATE;
+ *handshake_type_out = ONION_HANDSHAKE_TYPE_TAP;
+}
+
+/** Decide whether to use a TAP or ntor handshake for connecting to <b>ei</b>
+ * directly, and set *<b>handshake_type_out</b> accordingly. Decide whether,
+ * in extending through <b>node</b> to do so, we should use an EXTEND2 or an
+ * EXTEND cell to do so, and set *<b>cell_type_out</b> and
+ * *<b>create_cell_type_out</b> accordingly. */
+static void
+circuit_pick_extend_handshake(uint8_t *cell_type_out,
+ uint8_t *create_cell_type_out,
+ uint16_t *handshake_type_out,
+ const node_t *node_prev,
+ const extend_info_t *ei)
+{
+ uint8_t t;
+ circuit_pick_create_handshake(&t, handshake_type_out, ei);
+ /* XXXX024 The check for whether the node has a curve25519 key is a bad
+ * proxy for whether it can do extend2 cells; once a version that
+ * handles extend2 cells is out, remove it. */
+ if (node_prev &&
+ *handshake_type_out != ONION_HANDSHAKE_TYPE_TAP &&
+ (node_has_curve25519_onion_key(node_prev) ||
+ (node_prev->rs && node_prev->rs->version_supports_extend2_cells))) {
+ *cell_type_out = RELAY_COMMAND_EXTEND2;
+ *create_cell_type_out = CELL_CREATE2;
+ } else {
+ *cell_type_out = RELAY_COMMAND_EXTEND;
+ *create_cell_type_out = CELL_CREATE;
+ }
+}
+
/** This is the backbone function for building circuits.
*
* If circ's first hop is closed, then we need to build a create
@@ -610,16 +706,16 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
{
crypt_path_t *hop;
const node_t *node;
- char payload[2+4+DIGEST_LEN+ONIONSKIN_CHALLENGE_LEN];
- char *onionskin;
- size_t payload_len;
tor_assert(circ);
if (circ->cpath->state == CPATH_STATE_CLOSED) {
+ /* This is the first hop. */
+ create_cell_t cc;
int fast;
- uint8_t cell_type;
+ int len;
log_debug(LD_CIRC,"First skin; sending create cell.");
+ memset(&cc, 0, sizeof(cc));
if (circ->build_state->onehop_tunnel)
control_event_bootstrap(BOOTSTRAP_STATUS_ONEHOP_CREATE, 0);
else
@@ -629,30 +725,31 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
fast = should_use_create_fast_for_circuit(circ);
if (!fast) {
/* We are an OR and we know the right onion key: we should
- * send an old slow create cell.
+ * send a create cell.
*/
- cell_type = CELL_CREATE;
- if (onion_skin_create(circ->cpath->extend_info->onion_key,
- &(circ->cpath->dh_handshake_state),
- payload) < 0) {
- log_warn(LD_CIRC,"onion_skin_create (first hop) failed.");
- return - END_CIRC_REASON_INTERNAL;
- }
+ circuit_pick_create_handshake(&cc.cell_type, &cc.handshake_type,
+ circ->cpath->extend_info);
note_request("cell: create", 1);
} else {
/* We are not an OR, and we're building the first hop of a circuit to a
* new OR: we can be speedy and use CREATE_FAST to save an RSA operation
* and a DH operation. */
- cell_type = CELL_CREATE_FAST;
- memset(payload, 0, sizeof(payload));
- crypto_rand((char*) circ->cpath->fast_handshake_state,
- sizeof(circ->cpath->fast_handshake_state));
- memcpy(payload, circ->cpath->fast_handshake_state,
- sizeof(circ->cpath->fast_handshake_state));
+ cc.cell_type = CELL_CREATE_FAST;
+ cc.handshake_type = ONION_HANDSHAKE_TYPE_FAST;
note_request("cell: create fast", 1);
}
- if (circuit_deliver_create_cell(TO_CIRCUIT(circ), cell_type, payload) < 0)
+ len = onion_skin_create(cc.handshake_type,
+ circ->cpath->extend_info,
+ &circ->cpath->handshake_state,
+ cc.onionskin);
+ if (len < 0) {
+ log_warn(LD_CIRC,"onion_skin_create (first hop) failed.");
+ return - END_CIRC_REASON_INTERNAL;
+ }
+ cc.handshake_len = len;
+
+ if (circuit_deliver_create_cell(TO_CIRCUIT(circ), &cc, 0) < 0)
return - END_CIRC_REASON_RESOURCELIMIT;
circ->cpath->state = CPATH_STATE_AWAITING_KEYS;
@@ -661,10 +758,13 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
fast ? "CREATE_FAST" : "CREATE",
node ? node_describe(node) : "<unnamed>");
} else {
+ extend_cell_t ec;
+ int len;
tor_assert(circ->cpath->state == CPATH_STATE_OPEN);
tor_assert(circ->base_.state == CIRCUIT_STATE_BUILDING);
log_debug(LD_CIRC,"starting to send subsequent skin.");
hop = onion_next_hop_in_cpath(circ->cpath);
+ memset(&ec, 0, sizeof(ec));
if (!hop) {
/* done building the circuit. whew. */
circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OPEN);
@@ -672,7 +772,7 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
struct timeval end;
long timediff;
tor_gettimeofday(&end);
- timediff = tv_mdiff(&circ->base_.timestamp_created, &end);
+ timediff = tv_mdiff(&circ->base_.timestamp_began, &end);
/*
* If the circuit build time is much greater than we would have cut
@@ -719,13 +819,14 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
}
}
- pathbias_count_success(circ);
+ pathbias_count_build_success(circ);
circuit_rep_hist_note_result(circ);
circuit_has_opened(circ); /* do other actions as necessary */
/* We're done with measurement circuits here. Just close them */
- if (circ->base_.purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT)
+ if (circ->base_.purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_FINISHED);
+ }
return 0;
}
@@ -734,29 +835,50 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
return - END_CIRC_REASON_INTERNAL;
}
- set_uint32(payload, tor_addr_to_ipv4n(&hop->extend_info->addr));
- set_uint16(payload+4, htons(hop->extend_info->port));
+ {
+ const node_t *prev_node;
+ prev_node = node_get_by_id(hop->prev->extend_info->identity_digest);
+ circuit_pick_extend_handshake(&ec.cell_type,
+ &ec.create_cell.cell_type,
+ &ec.create_cell.handshake_type,
+ prev_node,
+ hop->extend_info);
+ }
- onionskin = payload+2+4;
- memcpy(payload+2+4+ONIONSKIN_CHALLENGE_LEN,
- hop->extend_info->identity_digest, DIGEST_LEN);
- payload_len = 2+4+ONIONSKIN_CHALLENGE_LEN+DIGEST_LEN;
+ tor_addr_copy(&ec.orport_ipv4.addr, &hop->extend_info->addr);
+ ec.orport_ipv4.port = hop->extend_info->port;
+ tor_addr_make_unspec(&ec.orport_ipv6.addr);
+ memcpy(ec.node_id, hop->extend_info->identity_digest, DIGEST_LEN);
- if (onion_skin_create(hop->extend_info->onion_key,
- &(hop->dh_handshake_state), onionskin) < 0) {
+ len = onion_skin_create(ec.create_cell.handshake_type,
+ hop->extend_info,
+ &hop->handshake_state,
+ ec.create_cell.onionskin);
+ if (len < 0) {
log_warn(LD_CIRC,"onion_skin_create failed.");
return - END_CIRC_REASON_INTERNAL;
}
+ ec.create_cell.handshake_len = len;
log_info(LD_CIRC,"Sending extend relay cell.");
note_request("cell: extend", 1);
- /* send it to hop->prev, because it will transfer
- * it to a create cell and then send to hop */
- if (relay_send_command_from_edge(0, TO_CIRCUIT(circ),
- RELAY_COMMAND_EXTEND,
- payload, payload_len, hop->prev) < 0)
- return 0; /* circuit is closed */
+ {
+ uint8_t command = 0;
+ uint16_t payload_len=0;
+ uint8_t payload[RELAY_PAYLOAD_SIZE];
+ if (extend_cell_format(&command, &payload_len, payload, &ec)<0) {
+ log_warn(LD_CIRC,"Couldn't format extend cell");
+ return -END_CIRC_REASON_INTERNAL;
+ }
+ /* send it to hop->prev, because it will transfer
+ * it to a create cell and then send to hop */
+ if (relay_send_command_from_edge(0, TO_CIRCUIT(circ),
+ command,
+ (char*)payload, payload_len,
+ hop->prev) < 0)
+ return 0; /* circuit is closed */
+ }
hop->state = CPATH_STATE_AWAITING_KEYS;
}
return 0;
@@ -795,11 +917,7 @@ circuit_extend(cell_t *cell, circuit_t *circ)
{
channel_t *n_chan;
relay_header_t rh;
- char *onionskin;
- char *id_digest=NULL;
- uint32_t n_addr32;
- uint16_t n_port;
- tor_addr_t n_addr;
+ extend_cell_t ec;
const char *msg = NULL;
int should_launch = 0;
@@ -822,27 +940,21 @@ circuit_extend(cell_t *cell, circuit_t *circ)
relay_header_unpack(&rh, cell->payload);
- if (rh.length < 4+2+ONIONSKIN_CHALLENGE_LEN+DIGEST_LEN) {
+ if (extend_cell_parse(&ec, rh.command,
+ cell->payload+RELAY_HEADER_SIZE,
+ rh.length) < 0) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
- "Wrong length %d on extend cell. Closing circuit.",
- rh.length);
+ "Can't parse extend cell. Closing circuit.");
return -1;
}
- n_addr32 = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE));
- n_port = ntohs(get_uint16(cell->payload+RELAY_HEADER_SIZE+4));
- onionskin = (char*) cell->payload+RELAY_HEADER_SIZE+4+2;
- id_digest = (char*) cell->payload+RELAY_HEADER_SIZE+4+2+
- ONIONSKIN_CHALLENGE_LEN;
- tor_addr_from_ipv4h(&n_addr, n_addr32);
-
- if (!n_port || !n_addr32) {
+ if (!ec.orport_ipv4.port || tor_addr_is_null(&ec.orport_ipv4.addr)) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Client asked me to extend to zero destination port or addr.");
return -1;
}
- if (tor_addr_is_internal(&n_addr, 0) &&
+ if (tor_addr_is_internal(&ec.orport_ipv4.addr, 0) &&
!get_options()->ExtendAllowPrivateAddresses) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Client asked me to extend to a private address");
@@ -855,7 +967,7 @@ circuit_extend(cell_t *cell, circuit_t *circ)
* fingerprints -- a) because it opens the user up to a mitm attack,
* and b) because it lets an attacker force the relay to hold open a
* new TLS connection for each extend request. */
- if (tor_digest_is_zero(id_digest)) {
+ if (tor_digest_is_zero((const char*)ec.node_id)) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Client asked me to extend without specifying an id_digest.");
return -1;
@@ -864,7 +976,7 @@ circuit_extend(cell_t *cell, circuit_t *circ)
/* Next, check if we're being asked to connect to the hop that the
* extend cell came from. There isn't any reason for that, and it can
* assist circular-path attacks. */
- if (tor_memeq(id_digest,
+ if (tor_memeq(ec.node_id,
TO_OR_CIRCUIT(circ)->p_chan->identity_digest,
DIGEST_LEN)) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
@@ -872,27 +984,33 @@ circuit_extend(cell_t *cell, circuit_t *circ)
return -1;
}
- n_chan = channel_get_for_extend(id_digest,
- &n_addr,
+ n_chan = channel_get_for_extend((const char*)ec.node_id,
+ &ec.orport_ipv4.addr,
&msg,
&should_launch);
if (!n_chan) {
log_debug(LD_CIRC|LD_OR,"Next router (%s): %s",
- fmt_addrport(&n_addr, n_port), msg?msg:"????");
+ fmt_addrport(&ec.orport_ipv4.addr,ec.orport_ipv4.port),
+ msg?msg:"????");
circ->n_hop = extend_info_new(NULL /*nickname*/,
- id_digest,
- NULL /*onion_key*/,
- &n_addr, n_port);
+ (const char*)ec.node_id,
+ NULL /*onion_key*/,
+ NULL /*curve25519_key*/,
+ &ec.orport_ipv4.addr,
+ ec.orport_ipv4.port);
+
+ circ->n_chan_create_cell = tor_memdup(&ec.create_cell,
+ sizeof(ec.create_cell));
- circ->n_chan_onionskin = tor_malloc(ONIONSKIN_CHALLENGE_LEN);
- memcpy(circ->n_chan_onionskin, onionskin, ONIONSKIN_CHALLENGE_LEN);
circuit_set_state(circ, CIRCUIT_STATE_CHAN_WAIT);
if (should_launch) {
/* we should try to open a connection */
- n_chan = channel_connect_for_circuit(&n_addr, n_port, id_digest);
+ n_chan = channel_connect_for_circuit(&ec.orport_ipv4.addr,
+ ec.orport_ipv4.port,
+ (const char*)ec.node_id);
if (!n_chan) {
log_info(LD_CIRC,"Launching n_chan failed. Closing circuit.");
circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED);
@@ -913,8 +1031,9 @@ circuit_extend(cell_t *cell, circuit_t *circ)
"n_chan is %s",
channel_get_canonical_remote_descr(n_chan));
- if (circuit_deliver_create_cell(circ, CELL_CREATE, onionskin) < 0)
+ if (circuit_deliver_create_cell(circ, &ec.create_cell, 1) < 0)
return -1;
+
return 0;
}
@@ -968,12 +1087,12 @@ circuit_init_cpath_crypto(crypt_path_t *cpath, const char *key_data,
return 0;
}
-/** The minimum number of first hop completions before we start
+/** The minimum number of circuit attempts before we start
* thinking about warning about path bias and dropping guards */
static int
pathbias_get_min_circs(const or_options_t *options)
{
-#define DFLT_PATH_BIAS_MIN_CIRC 20
+#define DFLT_PATH_BIAS_MIN_CIRC 150
if (options->PathBiasCircThreshold >= 5)
return options->PathBiasCircThreshold;
else
@@ -982,10 +1101,11 @@ pathbias_get_min_circs(const or_options_t *options)
5, INT32_MAX);
}
+/** The circuit success rate below which we issue a notice */
static double
pathbias_get_notice_rate(const or_options_t *options)
{
-#define DFLT_PATH_BIAS_NOTICE_PCT 40
+#define DFLT_PATH_BIAS_NOTICE_PCT 70
if (options->PathBiasNoticeRate >= 0.0)
return options->PathBiasNoticeRate;
else
@@ -994,23 +1114,61 @@ pathbias_get_notice_rate(const or_options_t *options)
}
/* XXXX024 I'd like to have this be static again, but entrynodes.c needs it. */
+/** The circuit success rate below which we issue a warn */
+static double
+pathbias_get_warn_rate(const or_options_t *options)
+{
+#define DFLT_PATH_BIAS_WARN_PCT 50
+ if (options->PathBiasWarnRate >= 0.0)
+ return options->PathBiasWarnRate;
+ else
+ return networkstatus_get_param(NULL, "pb_warnpct",
+ DFLT_PATH_BIAS_WARN_PCT, 0, 100)/100.0;
+}
+
+/* XXXX024 I'd like to have this be static again, but entrynodes.c needs it. */
+/**
+ * The extreme rate is the rate at which we would drop the guard,
+ * if pb_dropguard is also set. Otherwise we just warn.
+ */
double
-pathbias_get_disable_rate(const or_options_t *options)
+pathbias_get_extreme_rate(const or_options_t *options)
+{
+#define DFLT_PATH_BIAS_EXTREME_PCT 30
+ if (options->PathBiasExtremeRate >= 0.0)
+ return options->PathBiasExtremeRate;
+ else
+ return networkstatus_get_param(NULL, "pb_extremepct",
+ DFLT_PATH_BIAS_EXTREME_PCT, 0, 100)/100.0;
+}
+
+/* XXXX024 I'd like to have this be static again, but entrynodes.c needs it. */
+/**
+ * If 1, we actually disable use of guards that fall below
+ * the extreme_pct.
+ */
+int
+pathbias_get_dropguards(const or_options_t *options)
{
-// XXX: This needs tuning based on use + experimentation before we set it
-#define DFLT_PATH_BIAS_DISABLE_PCT 0
- if (options->PathBiasDisableRate >= 0.0)
- return options->PathBiasDisableRate;
+#define DFLT_PATH_BIAS_DROP_GUARDS 0
+ if (options->PathBiasDropGuards >= 0)
+ return options->PathBiasDropGuards;
else
- return networkstatus_get_param(NULL, "pb_disablepct",
- DFLT_PATH_BIAS_DISABLE_PCT, 0, 100)/100.0;
+ return networkstatus_get_param(NULL, "pb_dropguards",
+ DFLT_PATH_BIAS_DROP_GUARDS, 0, 1);
}
+/**
+ * This is the number of circuits at which we scale our
+ * counts by mult_factor/scale_factor. Note, this count is
+ * not exact, as we only perform the scaling in the event
+ * of no integer truncation.
+ */
static int
pathbias_get_scale_threshold(const or_options_t *options)
{
-#define DFLT_PATH_BIAS_SCALE_THRESHOLD 200
- if (options->PathBiasScaleThreshold >= 2)
+#define DFLT_PATH_BIAS_SCALE_THRESHOLD 300
+ if (options->PathBiasScaleThreshold >= 10)
return options->PathBiasScaleThreshold;
else
return networkstatus_get_param(NULL, "pb_scalecircs",
@@ -1018,51 +1176,198 @@ pathbias_get_scale_threshold(const or_options_t *options)
INT32_MAX);
}
+/**
+ * Compute the path bias scaling ratio from the consensus
+ * parameters pb_multfactor/pb_scalefactor.
+ *
+ * Returns a value in (0, 1.0] which we multiply our pathbias
+ * counts with to scale them down.
+ */
+static double
+pathbias_get_scale_ratio(const or_options_t *options)
+{
+ /*
+ * The scale factor is the denominator for our scaling
+ * of circuit counts for our path bias window.
+ *
+ * Note that our use of doubles for the path bias state
+ * file means that powers of 2 work best here.
+ */
+ int denominator = networkstatus_get_param(NULL, "pb_scalefactor",
+ 2, 2, INT32_MAX);
+ (void) options;
+ /**
+ * The mult factor is the numerator for our scaling
+ * of circuit counts for our path bias window. It
+ * allows us to scale by fractions.
+ */
+ return networkstatus_get_param(NULL, "pb_multfactor",
+ 1, 1, denominator)/((double)denominator);
+}
+
+/** The minimum number of circuit usage attempts before we start
+ * thinking about warning about path use bias and dropping guards */
+static int
+pathbias_get_min_use(const or_options_t *options)
+{
+#define DFLT_PATH_BIAS_MIN_USE 20
+ if (options->PathBiasUseThreshold >= 3)
+ return options->PathBiasUseThreshold;
+ else
+ return networkstatus_get_param(NULL, "pb_minuse",
+ DFLT_PATH_BIAS_MIN_USE,
+ 3, INT32_MAX);
+}
+
+/** The circuit use success rate below which we issue a notice */
+static double
+pathbias_get_notice_use_rate(const or_options_t *options)
+{
+#define DFLT_PATH_BIAS_NOTICE_USE_PCT 80
+ if (options->PathBiasNoticeUseRate >= 0.0)
+ return options->PathBiasNoticeUseRate;
+ else
+ return networkstatus_get_param(NULL, "pb_noticeusepct",
+ DFLT_PATH_BIAS_NOTICE_USE_PCT,
+ 0, 100)/100.0;
+}
+
+/**
+ * The extreme use rate is the rate at which we would drop the guard,
+ * if pb_dropguard is also set. Otherwise we just warn.
+ */
+double
+pathbias_get_extreme_use_rate(const or_options_t *options)
+{
+#define DFLT_PATH_BIAS_EXTREME_USE_PCT 60
+ if (options->PathBiasExtremeUseRate >= 0.0)
+ return options->PathBiasExtremeUseRate;
+ else
+ return networkstatus_get_param(NULL, "pb_extremeusepct",
+ DFLT_PATH_BIAS_EXTREME_USE_PCT,
+ 0, 100)/100.0;
+}
+
+/**
+ * This is the number of circuits at which we scale our
+ * use counts by mult_factor/scale_factor. Note, this count is
+ * not exact, as we only perform the scaling in the event
+ * of no integer truncation.
+ */
static int
-pathbias_get_scale_factor(const or_options_t *options)
+pathbias_get_scale_use_threshold(const or_options_t *options)
{
-#define DFLT_PATH_BIAS_SCALE_FACTOR 2
- if (options->PathBiasScaleFactor >= 1)
- return options->PathBiasScaleFactor;
+#define DFLT_PATH_BIAS_SCALE_USE_THRESHOLD 100
+ if (options->PathBiasScaleUseThreshold >= 10)
+ return options->PathBiasScaleUseThreshold;
else
- return networkstatus_get_param(NULL, "pb_scalefactor",
- DFLT_PATH_BIAS_SCALE_FACTOR, 1, INT32_MAX);
+ return networkstatus_get_param(NULL, "pb_scaleuse",
+ DFLT_PATH_BIAS_SCALE_USE_THRESHOLD,
+ 10, INT32_MAX);
}
+/**
+ * Convert a Guard's path state to string.
+ */
static const char *
pathbias_state_to_string(path_state_t state)
{
switch (state) {
case PATH_STATE_NEW_CIRC:
return "new";
- case PATH_STATE_DID_FIRST_HOP:
- return "first hop";
- case PATH_STATE_SUCCEEDED:
- return "succeeded";
+ case PATH_STATE_BUILD_ATTEMPTED:
+ return "build attempted";
+ case PATH_STATE_BUILD_SUCCEEDED:
+ return "build succeeded";
+ case PATH_STATE_USE_ATTEMPTED:
+ return "use attempted";
+ case PATH_STATE_USE_SUCCEEDED:
+ return "use succeeded";
+ case PATH_STATE_USE_FAILED:
+ return "use failed";
+ case PATH_STATE_ALREADY_COUNTED:
+ return "already counted";
}
return "unknown";
}
/**
- * Check our circuit state to see if this is a successful first hop.
- * If so, record it in the current guard's path bias first_hop count.
+ * This function decides if a circuit has progressed far enough to count
+ * as a circuit "attempt". As long as end-to-end tagging is possible,
+ * we assume the adversary will use it over hop-to-hop failure. Therefore,
+ * we only need to account bias for the last hop. This should make us
+ * much more resilient to ambient circuit failure, and also make that
+ * failure easier to measure (we only need to measure Exit failure rates).
+ */
+static int
+pathbias_is_new_circ_attempt(origin_circuit_t *circ)
+{
+#define N2N_TAGGING_IS_POSSIBLE
+#ifdef N2N_TAGGING_IS_POSSIBLE
+ /* cpath is a circular list. We want circs with more than one hop,
+ * and the second hop must be waiting for keys still (it's just
+ * about to get them). */
+ return circ->cpath &&
+ circ->cpath->next != circ->cpath &&
+ circ->cpath->next->state == CPATH_STATE_AWAITING_KEYS;
+#else
+ /* If tagging attacks are no longer possible, we probably want to
+ * count bias from the first hop. However, one could argue that
+ * timing-based tagging is still more useful than per-hop failure.
+ * In which case, we'd never want to use this.
+ */
+ return circ->cpath &&
+ circ->cpath->state == CPATH_STATE_AWAITING_KEYS;
+#endif
+}
+
+/**
+ * Decide if the path bias code should count a circuit.
*
- * Also check for several potential error cases for bug #6475.
+ * @returns 1 if we should count it, 0 otherwise.
*/
static int
-pathbias_count_first_hop(origin_circuit_t *circ)
+pathbias_should_count(origin_circuit_t *circ)
{
-#define FIRST_HOP_NOTICE_INTERVAL (600)
- static ratelim_t first_hop_notice_limit =
- RATELIM_INIT(FIRST_HOP_NOTICE_INTERVAL);
+#define PATHBIAS_COUNT_INTERVAL (600)
+ static ratelim_t count_limit =
+ RATELIM_INIT(PATHBIAS_COUNT_INTERVAL);
char *rate_msg = NULL;
/* We can't do path bias accounting without entry guards.
- * Testing and controller circuits also have no guards. */
+ * Testing and controller circuits also have no guards.
+ *
+ * We also don't count server-side rends, because their
+ * endpoint could be chosen maliciously.
+ * Similarly, we can't count client-side intro attempts,
+ * because clients can be manipulated into connecting to
+ * malicious intro points. */
if (get_options()->UseEntryGuards == 0 ||
circ->base_.purpose == CIRCUIT_PURPOSE_TESTING ||
- circ->base_.purpose == CIRCUIT_PURPOSE_CONTROLLER) {
+ circ->base_.purpose == CIRCUIT_PURPOSE_CONTROLLER ||
+ circ->base_.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND ||
+ circ->base_.purpose == CIRCUIT_PURPOSE_S_REND_JOINED ||
+ (circ->base_.purpose >= CIRCUIT_PURPOSE_C_INTRODUCING &&
+ circ->base_.purpose <= CIRCUIT_PURPOSE_C_INTRODUCE_ACKED)) {
+
+ /* Check to see if the shouldcount result has changed due to a
+ * unexpected purpose change that would affect our results.
+ *
+ * The reason we check the path state too here is because for the
+ * cannibalized versions of these purposes, we count them as successful
+ * before their purpose change.
+ */
+ if (circ->pathbias_shouldcount == PATHBIAS_SHOULDCOUNT_COUNTED
+ && circ->path_state != PATH_STATE_ALREADY_COUNTED) {
+ log_info(LD_BUG,
+ "Circuit %d is now being ignored despite being counted "
+ "in the past. Purpose is %s, path state is %s",
+ circ->global_identifier,
+ circuit_purpose_to_string(circ->base_.purpose),
+ pathbias_state_to_string(circ->path_state));
+ }
+ circ->pathbias_shouldcount = PATHBIAS_SHOULDCOUNT_IGNORED;
return 0;
}
@@ -1072,8 +1377,7 @@ pathbias_count_first_hop(origin_circuit_t *circ)
/* Check for inconsistency */
if (circ->build_state->desired_path_len != 1 ||
!circ->build_state->onehop_tunnel) {
- if ((rate_msg = rate_limit_log(&first_hop_notice_limit,
- approx_time()))) {
+ if ((rate_msg = rate_limit_log(&count_limit, approx_time()))) {
log_notice(LD_BUG,
"One-hop circuit has length %d. Path state is %s. "
"Circuit is a %s currently %s.%s",
@@ -1086,13 +1390,58 @@ pathbias_count_first_hop(origin_circuit_t *circ)
}
tor_fragile_assert();
}
+
+ /* Check to see if the shouldcount result has changed due to a
+ * unexpected change that would affect our results */
+ if (circ->pathbias_shouldcount == PATHBIAS_SHOULDCOUNT_COUNTED) {
+ log_info(LD_BUG,
+ "One-hop circuit %d is now being ignored despite being counted "
+ "in the past. Purpose is %s, path state is %s",
+ circ->global_identifier,
+ circuit_purpose_to_string(circ->base_.purpose),
+ pathbias_state_to_string(circ->path_state));
+ }
+ circ->pathbias_shouldcount = PATHBIAS_SHOULDCOUNT_IGNORED;
return 0;
}
- if (circ->cpath->state == CPATH_STATE_AWAITING_KEYS) {
+ /* Check to see if the shouldcount result has changed due to a
+ * unexpected purpose change that would affect our results */
+ if (circ->pathbias_shouldcount == PATHBIAS_SHOULDCOUNT_IGNORED) {
+ log_info(LD_BUG,
+ "Circuit %d is now being counted despite being ignored "
+ "in the past. Purpose is %s, path state is %s",
+ circ->global_identifier,
+ circuit_purpose_to_string(circ->base_.purpose),
+ pathbias_state_to_string(circ->path_state));
+ }
+ circ->pathbias_shouldcount = PATHBIAS_SHOULDCOUNT_COUNTED;
+
+ return 1;
+}
+
+/**
+ * Check our circuit state to see if this is a successful circuit attempt.
+ * If so, record it in the current guard's path bias circ_attempt count.
+ *
+ * Also check for several potential error cases for bug #6475.
+ */
+static int
+pathbias_count_build_attempt(origin_circuit_t *circ)
+{
+#define CIRC_ATTEMPT_NOTICE_INTERVAL (600)
+ static ratelim_t circ_attempt_notice_limit =
+ RATELIM_INIT(CIRC_ATTEMPT_NOTICE_INTERVAL);
+ char *rate_msg = NULL;
+
+ if (!pathbias_should_count(circ)) {
+ return 0;
+ }
+
+ if (pathbias_is_new_circ_attempt(circ)) {
/* Help track down the real cause of bug #6475: */
- if (circ->has_opened && circ->path_state != PATH_STATE_DID_FIRST_HOP) {
- if ((rate_msg = rate_limit_log(&first_hop_notice_limit,
+ if (circ->has_opened && circ->path_state != PATH_STATE_BUILD_ATTEMPTED) {
+ if ((rate_msg = rate_limit_log(&circ_attempt_notice_limit,
approx_time()))) {
log_info(LD_BUG,
"Opened circuit is in strange path state %s. "
@@ -1105,22 +1454,28 @@ pathbias_count_first_hop(origin_circuit_t *circ)
}
}
- /* Don't count cannibalized circs for path bias */
+ /* Don't re-count cannibalized circs.. */
if (!circ->has_opened) {
- entry_guard_t *guard;
+ entry_guard_t *guard = NULL;
+
+ if (circ->cpath && circ->cpath->extend_info) {
+ guard = entry_guard_get_by_id_digest(
+ circ->cpath->extend_info->identity_digest);
+ } else if (circ->base_.n_chan) {
+ guard =
+ entry_guard_get_by_id_digest(circ->base_.n_chan->identity_digest);
+ }
- guard =
- entry_guard_get_by_id_digest(circ->base_.n_chan->identity_digest);
if (guard) {
if (circ->path_state == PATH_STATE_NEW_CIRC) {
- circ->path_state = PATH_STATE_DID_FIRST_HOP;
+ circ->path_state = PATH_STATE_BUILD_ATTEMPTED;
- if (entry_guard_inc_first_hop_count(guard) < 0) {
+ if (entry_guard_inc_circ_attempt_count(guard) < 0) {
/* Bogus guard; we already warned. */
return -END_CIRC_REASON_TORPROTOCOL;
}
} else {
- if ((rate_msg = rate_limit_log(&first_hop_notice_limit,
+ if ((rate_msg = rate_limit_log(&circ_attempt_notice_limit,
approx_time()))) {
log_info(LD_BUG,
"Unopened circuit has strange path state %s. "
@@ -1133,9 +1488,9 @@ pathbias_count_first_hop(origin_circuit_t *circ)
}
}
} else {
- if ((rate_msg = rate_limit_log(&first_hop_notice_limit,
+ if ((rate_msg = rate_limit_log(&circ_attempt_notice_limit,
approx_time()))) {
- log_info(LD_BUG,
+ log_info(LD_CIRC,
"Unopened circuit has no known guard. "
"Circuit is a %s currently %s.%s",
circuit_purpose_to_string(circ->base_.purpose),
@@ -1145,22 +1500,6 @@ pathbias_count_first_hop(origin_circuit_t *circ)
}
}
}
- } else {
- /* Help track down the real cause of bug #6475: */
- if (circ->path_state == PATH_STATE_NEW_CIRC) {
- if ((rate_msg = rate_limit_log(&first_hop_notice_limit,
- approx_time()))) {
- log_info(LD_BUG,
- "A %s circuit is in cpath state %d (opened: %d). "
- "Circuit is a %s currently %s.%s",
- pathbias_state_to_string(circ->path_state),
- circ->cpath->state, circ->has_opened,
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state),
- rate_msg);
- tor_free(rate_msg);
- }
- }
}
return 0;
@@ -1174,7 +1513,7 @@ pathbias_count_first_hop(origin_circuit_t *circ)
* Also check for several potential error cases for bug #6475.
*/
static void
-pathbias_count_success(origin_circuit_t *circ)
+pathbias_count_build_success(origin_circuit_t *circ)
{
#define SUCCESS_NOTICE_INTERVAL (600)
static ratelim_t success_notice_limit =
@@ -1182,49 +1521,26 @@ pathbias_count_success(origin_circuit_t *circ)
char *rate_msg = NULL;
entry_guard_t *guard = NULL;
- /* We can't do path bias accounting without entry guards.
- * Testing and controller circuits also have no guards. */
- if (get_options()->UseEntryGuards == 0 ||
- circ->base_.purpose == CIRCUIT_PURPOSE_TESTING ||
- circ->base_.purpose == CIRCUIT_PURPOSE_CONTROLLER) {
+ if (!pathbias_should_count(circ)) {
return;
}
- /* Ignore one hop circuits */
- if (circ->build_state->onehop_tunnel ||
- circ->build_state->desired_path_len == 1) {
- /* Check for consistency */
- if (circ->build_state->desired_path_len != 1 ||
- !circ->build_state->onehop_tunnel) {
- if ((rate_msg = rate_limit_log(&success_notice_limit,
- approx_time()))) {
- log_notice(LD_BUG,
- "One-hop circuit has length %d. Path state is %s. "
- "Circuit is a %s currently %s.%s",
- circ->build_state->desired_path_len,
- pathbias_state_to_string(circ->path_state),
- circuit_purpose_to_string(circ->base_.purpose),
- circuit_state_to_string(circ->base_.state),
- rate_msg);
- tor_free(rate_msg);
- }
- tor_fragile_assert();
- }
- return;
- }
-
- /* Don't count cannibalized/reused circs for path bias */
+ /* Don't count cannibalized/reused circs for path bias
+ * "build" success, since they get counted under "use" success. */
if (!circ->has_opened) {
- guard =
- entry_guard_get_by_id_digest(circ->base_.n_chan->identity_digest);
+ if (circ->cpath && circ->cpath->extend_info) {
+ guard = entry_guard_get_by_id_digest(
+ circ->cpath->extend_info->identity_digest);
+ }
if (guard) {
- if (circ->path_state == PATH_STATE_DID_FIRST_HOP) {
- circ->path_state = PATH_STATE_SUCCEEDED;
- guard->circuit_successes++;
+ if (circ->path_state == PATH_STATE_BUILD_ATTEMPTED) {
+ circ->path_state = PATH_STATE_BUILD_SUCCEEDED;
+ guard->circ_successes++;
+ entry_guards_changed();
- log_info(LD_PROTOCOL, "Got success count %u/%u for guard %s=%s",
- guard->circuit_successes, guard->first_hops,
+ log_info(LD_CIRC, "Got success count %f/%f for guard %s=%s",
+ guard->circ_successes, guard->circ_attempts,
guard->nickname, hex_str(guard->identity, DIGEST_LEN));
} else {
if ((rate_msg = rate_limit_log(&success_notice_limit,
@@ -1240,10 +1556,10 @@ pathbias_count_success(origin_circuit_t *circ)
}
}
- if (guard->first_hops < guard->circuit_successes) {
- log_notice(LD_BUG, "Unexpectedly high circuit_successes (%u/%u) "
+ if (guard->circ_attempts < guard->circ_successes) {
+ log_notice(LD_BUG, "Unexpectedly high successes counts (%f/%f) "
"for guard %s=%s",
- guard->circuit_successes, guard->first_hops,
+ guard->circ_successes, guard->circ_attempts,
guard->nickname, hex_str(guard->identity, DIGEST_LEN));
}
/* In rare cases, CIRCUIT_PURPOSE_TESTING can get converted to
@@ -1252,7 +1568,7 @@ pathbias_count_success(origin_circuit_t *circ)
} else if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
if ((rate_msg = rate_limit_log(&success_notice_limit,
approx_time()))) {
- log_info(LD_BUG,
+ log_info(LD_CIRC,
"Completed circuit has no known guard. "
"Circuit is a %s currently %s.%s",
circuit_purpose_to_string(circ->base_.purpose),
@@ -1262,7 +1578,7 @@ pathbias_count_success(origin_circuit_t *circ)
}
}
} else {
- if (circ->path_state != PATH_STATE_SUCCEEDED) {
+ if (circ->path_state < PATH_STATE_BUILD_SUCCEEDED) {
if ((rate_msg = rate_limit_log(&success_notice_limit,
approx_time()))) {
log_info(LD_BUG,
@@ -1278,73 +1594,975 @@ pathbias_count_success(origin_circuit_t *circ)
}
}
-/** Increment the number of times we successfully extended a circuit to
- * 'guard', first checking if the failure rate is high enough that we should
- * eliminate the guard. Return -1 if the guard looks no good; return 0 if the
- * guard looks fine. */
+/**
+ * Record an attempt to use a circuit. Changes the circuit's
+ * path state and update its guard's usage counter.
+ *
+ * Used for path bias usage accounting.
+ */
+void
+pathbias_count_use_attempt(origin_circuit_t *circ)
+{
+ entry_guard_t *guard;
+
+ if (!pathbias_should_count(circ)) {
+ return;
+ }
+
+ if (circ->path_state < PATH_STATE_BUILD_SUCCEEDED) {
+ log_notice(LD_BUG,
+ "Used circuit is in strange path state %s. "
+ "Circuit is a %s currently %s.",
+ pathbias_state_to_string(circ->path_state),
+ circuit_purpose_to_string(circ->base_.purpose),
+ circuit_state_to_string(circ->base_.state));
+ } else if (circ->path_state < PATH_STATE_USE_ATTEMPTED) {
+ guard = entry_guard_get_by_id_digest(
+ circ->cpath->extend_info->identity_digest);
+ if (guard) {
+ pathbias_measure_use_rate(guard);
+ pathbias_scale_use_rates(guard);
+ guard->use_attempts++;
+ entry_guards_changed();
+
+ log_debug(LD_CIRC,
+ "Marked circuit %d (%f/%f) as used for guard %s=%s.",
+ circ->global_identifier,
+ guard->use_successes, guard->use_attempts,
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN));
+ }
+
+ circ->path_state = PATH_STATE_USE_ATTEMPTED;
+ } else {
+ /* Harmless but educational log message */
+ log_info(LD_CIRC,
+ "Used circuit %d is already in path state %s. "
+ "Circuit is a %s currently %s.",
+ circ->global_identifier,
+ pathbias_state_to_string(circ->path_state),
+ circuit_purpose_to_string(circ->base_.purpose),
+ circuit_state_to_string(circ->base_.state));
+ }
+
+ return;
+}
+
+/**
+ * Check the circuit's path state is appropriate and mark it as
+ * successfully used. Used for path bias usage accounting.
+ *
+ * We don't actually increment the guard's counters until
+ * pathbias_check_close(), because the circuit can still transition
+ * back to PATH_STATE_USE_ATTEMPTED if a stream fails later (this
+ * is done so we can probe the circuit for liveness at close).
+ */
+void
+pathbias_mark_use_success(origin_circuit_t *circ)
+{
+ if (!pathbias_should_count(circ)) {
+ return;
+ }
+
+ if (circ->path_state < PATH_STATE_USE_ATTEMPTED) {
+ log_notice(LD_BUG,
+ "Used circuit %d is in strange path state %s. "
+ "Circuit is a %s currently %s.",
+ circ->global_identifier,
+ pathbias_state_to_string(circ->path_state),
+ circuit_purpose_to_string(circ->base_.purpose),
+ circuit_state_to_string(circ->base_.state));
+
+ pathbias_count_use_attempt(circ);
+ }
+
+ /* We don't do any accounting at the guard until actual circuit close */
+ circ->path_state = PATH_STATE_USE_SUCCEEDED;
+
+ return;
+}
+
+/**
+ * If a stream ever detatches from a circuit in a retriable way,
+ * we need to mark this circuit as still needing either another
+ * successful stream, or in need of a probe.
+ *
+ * An adversary could let the first stream request succeed (ie the
+ * resolve), but then tag and timeout the remainder (via cell
+ * dropping), forcing them on new circuits.
+ *
+ * Rolling back the state will cause us to probe such circuits, which
+ * should lead to probe failures in the event of such tagging due to
+ * either unrecognized cells coming in while we wait for the probe,
+ * or the cipher state getting out of sync in the case of dropped cells.
+ */
+void
+pathbias_mark_use_rollback(origin_circuit_t *circ)
+{
+ if (circ->path_state == PATH_STATE_USE_SUCCEEDED) {
+ log_info(LD_CIRC,
+ "Rolling back pathbias use state to 'attempted' for detached "
+ "circuit %d", circ->global_identifier);
+ circ->path_state = PATH_STATE_USE_ATTEMPTED;
+ }
+}
+
+/**
+ * Actually count a circuit success towards a guard's usage counters
+ * if the path state is appropriate.
+ */
+static void
+pathbias_count_use_success(origin_circuit_t *circ)
+{
+ entry_guard_t *guard;
+
+ if (!pathbias_should_count(circ)) {
+ return;
+ }
+
+ if (circ->path_state != PATH_STATE_USE_SUCCEEDED) {
+ log_notice(LD_BUG,
+ "Successfully used circuit %d is in strange path state %s. "
+ "Circuit is a %s currently %s.",
+ circ->global_identifier,
+ pathbias_state_to_string(circ->path_state),
+ circuit_purpose_to_string(circ->base_.purpose),
+ circuit_state_to_string(circ->base_.state));
+ } else {
+ guard = entry_guard_get_by_id_digest(
+ circ->cpath->extend_info->identity_digest);
+ if (guard) {
+ guard->use_successes++;
+ entry_guards_changed();
+
+ log_debug(LD_CIRC,
+ "Marked circuit %d (%f/%f) as used successfully for guard "
+ "%s=%s.",
+ circ->global_identifier, guard->use_successes,
+ guard->use_attempts, guard->nickname,
+ hex_str(guard->identity, DIGEST_LEN));
+ }
+ }
+
+ return;
+}
+
+/**
+ * Send a probe down a circuit that the client attempted to use,
+ * but for which the stream timed out/failed. The probe is a
+ * RELAY_BEGIN cell with a 0.a.b.c destination address, which
+ * the exit will reject and reply back, echoing that address.
+ *
+ * The reason for such probes is because it is possible to bias
+ * a user's paths simply by causing timeouts, and these timeouts
+ * are not possible to differentiate from unresponsive servers.
+ *
+ * The probe is sent at the end of the circuit lifetime for two
+ * reasons: to prevent cryptographic taggers from being able to
+ * drop cells to cause timeouts, and to prevent easy recognition
+ * of probes before any real client traffic happens.
+ *
+ * Returns -1 if we couldn't probe, 0 otherwise.
+ */
static int
-entry_guard_inc_first_hop_count(entry_guard_t *guard)
+pathbias_send_usable_probe(circuit_t *circ)
+{
+ /* Based on connection_ap_handshake_send_begin() */
+ char payload[CELL_PAYLOAD_SIZE];
+ int payload_len;
+ origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+ crypt_path_t *cpath_layer = NULL;
+ char *probe_nonce = NULL;
+
+ tor_assert(ocirc);
+
+ cpath_layer = ocirc->cpath->prev;
+
+ if (cpath_layer->state != CPATH_STATE_OPEN) {
+ /* This can happen for cannibalized circuits. Their
+ * last hop isn't yet open */
+ log_info(LD_CIRC,
+ "Got pathbias probe request for unopened circuit %d. "
+ "Opened %d, len %d", ocirc->global_identifier,
+ ocirc->has_opened, ocirc->build_state->desired_path_len);
+ return -1;
+ }
+
+ /* We already went down this road. */
+ if (circ->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING &&
+ ocirc->pathbias_probe_id) {
+ log_info(LD_CIRC,
+ "Got pathbias probe request for circuit %d with "
+ "outstanding probe", ocirc->global_identifier);
+ return -1;
+ }
+
+ /* Can't probe if the channel isn't open */
+ if (circ->n_chan == NULL ||
+ (circ->n_chan->state != CHANNEL_STATE_OPEN
+ && circ->n_chan->state != CHANNEL_STATE_MAINT)) {
+ log_info(LD_CIRC,
+ "Skipping pathbias probe for circuit %d: Channel is not open.",
+ ocirc->global_identifier);
+ return -1;
+ }
+
+ circuit_change_purpose(circ, CIRCUIT_PURPOSE_PATH_BIAS_TESTING);
+
+ /* Update timestamp for when circuit_expire_building() should kill us */
+ tor_gettimeofday(&circ->timestamp_began);
+
+ /* Generate a random address for the nonce */
+ crypto_rand((char*)&ocirc->pathbias_probe_nonce,
+ sizeof(ocirc->pathbias_probe_nonce));
+ ocirc->pathbias_probe_nonce &= 0x00ffffff;
+ probe_nonce = tor_dup_ip(ocirc->pathbias_probe_nonce);
+
+ tor_snprintf(payload,RELAY_PAYLOAD_SIZE, "%s:25", probe_nonce);
+ payload_len = (int)strlen(payload)+1;
+
+ // XXX: need this? Can we assume ipv4 will always be supported?
+ // If not, how do we tell?
+ //if (payload_len <= RELAY_PAYLOAD_SIZE - 4 && edge_conn->begincell_flags) {
+ // set_uint32(payload + payload_len, htonl(edge_conn->begincell_flags));
+ // payload_len += 4;
+ //}
+
+ /* Generate+Store stream id, make sure it's non-zero */
+ ocirc->pathbias_probe_id = get_unique_stream_id_by_circ(ocirc);
+
+ if (ocirc->pathbias_probe_id==0) {
+ log_warn(LD_CIRC,
+ "Ran out of stream IDs on circuit %u during "
+ "pathbias probe attempt.", ocirc->global_identifier);
+ tor_free(probe_nonce);
+ return -1;
+ }
+
+ log_info(LD_CIRC,
+ "Sending pathbias testing cell to %s:25 on stream %d for circ %d.",
+ probe_nonce, ocirc->pathbias_probe_id, ocirc->global_identifier);
+ tor_free(probe_nonce);
+
+ /* Send a test relay cell */
+ if (relay_send_command_from_edge(ocirc->pathbias_probe_id, circ,
+ RELAY_COMMAND_BEGIN, payload,
+ payload_len, cpath_layer) < 0) {
+ log_notice(LD_CIRC,
+ "Failed to send pathbias probe cell on circuit %d.",
+ ocirc->global_identifier);
+ return -1;
+ }
+
+ /* Mark it freshly dirty so it doesn't get expired in the meantime */
+ circ->timestamp_dirty = time(NULL);
+
+ return 0;
+}
+
+/**
+ * Check the response to a pathbias probe, to ensure the
+ * cell is recognized and the nonce and other probe
+ * characteristics are as expected.
+ *
+ * If the response is valid, return 0. Otherwise return < 0.
+ */
+int
+pathbias_check_probe_response(circuit_t *circ, const cell_t *cell)
+{
+ /* Based on connection_edge_process_relay_cell() */
+ relay_header_t rh;
+ int reason;
+ uint32_t ipv4_host;
+ origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+
+ tor_assert(cell);
+ tor_assert(ocirc);
+ tor_assert(circ->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING);
+
+ relay_header_unpack(&rh, cell->payload);
+
+ reason = rh.length > 0 ?
+ get_uint8(cell->payload+RELAY_HEADER_SIZE) : END_STREAM_REASON_MISC;
+
+ if (rh.command == RELAY_COMMAND_END &&
+ reason == END_STREAM_REASON_EXITPOLICY &&
+ ocirc->pathbias_probe_id == rh.stream_id) {
+
+ /* Check length+extract host: It is in network order after the reason code.
+ * See connection_edge_end(). */
+ if (rh.length < 9) { /* reason+ipv4+dns_ttl */
+ log_notice(LD_PROTOCOL,
+ "Short path bias probe response length field (%d).", rh.length);
+ return - END_CIRC_REASON_TORPROTOCOL;
+ }
+
+ ipv4_host = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+1));
+
+ /* Check nonce */
+ if (ipv4_host == ocirc->pathbias_probe_nonce) {
+ pathbias_mark_use_success(ocirc);
+ circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED);
+ log_info(LD_CIRC,
+ "Got valid path bias probe back for circ %d, stream %d.",
+ ocirc->global_identifier, ocirc->pathbias_probe_id);
+ return 0;
+ } else {
+ log_notice(LD_CIRC,
+ "Got strange probe value 0x%x vs 0x%x back for circ %d, "
+ "stream %d.", ipv4_host, ocirc->pathbias_probe_nonce,
+ ocirc->global_identifier, ocirc->pathbias_probe_id);
+ return -1;
+ }
+ }
+ log_info(LD_CIRC,
+ "Got another cell back back on pathbias probe circuit %d: "
+ "Command: %d, Reason: %d, Stream-id: %d",
+ ocirc->global_identifier, rh.command, reason, rh.stream_id);
+ return -1;
+}
+
+/**
+ * Check if a circuit was used and/or closed successfully.
+ *
+ * If we attempted to use the circuit to carry a stream but failed
+ * for whatever reason, or if the circuit mysteriously died before
+ * we could attach any streams, record these two cases.
+ *
+ * If we *have* successfully used the circuit, or it appears to
+ * have been closed by us locally, count it as a success.
+ *
+ * Returns 0 if we're done making decisions with the circ,
+ * or -1 if we want to probe it first.
+ */
+int
+pathbias_check_close(origin_circuit_t *ocirc, int reason)
+{
+ circuit_t *circ = &ocirc->base_;
+
+ if (!pathbias_should_count(ocirc)) {
+ return 0;
+ }
+
+ switch (ocirc->path_state) {
+ /* If the circuit was closed after building, but before use, we need
+ * to ensure we were the ones who tried to close it (and not a remote
+ * actor). */
+ case PATH_STATE_BUILD_SUCCEEDED:
+ if (reason & END_CIRC_REASON_FLAG_REMOTE) {
+ /* Remote circ close reasons on an unused circuit all could be bias */
+ log_info(LD_CIRC,
+ "Circuit %d remote-closed without successful use for reason %d. "
+ "Circuit purpose %d currently %d,%s. Len %d.",
+ ocirc->global_identifier,
+ reason, circ->purpose, ocirc->has_opened,
+ circuit_state_to_string(circ->state),
+ ocirc->build_state->desired_path_len);
+ pathbias_count_collapse(ocirc);
+ } else if ((reason & ~END_CIRC_REASON_FLAG_REMOTE)
+ == END_CIRC_REASON_CHANNEL_CLOSED &&
+ circ->n_chan &&
+ circ->n_chan->reason_for_closing
+ != CHANNEL_CLOSE_REQUESTED) {
+ /* If we didn't close the channel ourselves, it could be bias */
+ /* XXX: Only count bias if the network is live?
+ * What about clock jumps/suspends? */
+ log_info(LD_CIRC,
+ "Circuit %d's channel closed without successful use for reason "
+ "%d, channel reason %d. Circuit purpose %d currently %d,%s. Len "
+ "%d.", ocirc->global_identifier,
+ reason, circ->n_chan->reason_for_closing,
+ circ->purpose, ocirc->has_opened,
+ circuit_state_to_string(circ->state),
+ ocirc->build_state->desired_path_len);
+ pathbias_count_collapse(ocirc);
+ } else {
+ pathbias_count_successful_close(ocirc);
+ }
+ break;
+
+ /* If we tried to use a circuit but failed, we should probe it to ensure
+ * it has not been tampered with. */
+ case PATH_STATE_USE_ATTEMPTED:
+ /* XXX: Only probe and/or count failure if the network is live?
+ * What about clock jumps/suspends? */
+ if (pathbias_send_usable_probe(circ) == 0)
+ return -1;
+ else
+ pathbias_count_use_failed(ocirc);
+
+ /* Any circuit where there were attempted streams but no successful
+ * streams could be bias */
+ log_info(LD_CIRC,
+ "Circuit %d closed without successful use for reason %d. "
+ "Circuit purpose %d currently %d,%s. Len %d.",
+ ocirc->global_identifier,
+ reason, circ->purpose, ocirc->has_opened,
+ circuit_state_to_string(circ->state),
+ ocirc->build_state->desired_path_len);
+ break;
+
+ case PATH_STATE_USE_SUCCEEDED:
+ pathbias_count_successful_close(ocirc);
+ pathbias_count_use_success(ocirc);
+ break;
+
+ case PATH_STATE_USE_FAILED:
+ pathbias_count_use_failed(ocirc);
+ break;
+
+ default:
+ // Other states are uninteresting. No stats to count.
+ break;
+ }
+
+ ocirc->path_state = PATH_STATE_ALREADY_COUNTED;
+
+ return 0;
+}
+
+/**
+ * Count a successfully closed circuit.
+ */
+static void
+pathbias_count_successful_close(origin_circuit_t *circ)
+{
+ entry_guard_t *guard = NULL;
+ if (!pathbias_should_count(circ)) {
+ return;
+ }
+
+ if (circ->cpath && circ->cpath->extend_info) {
+ guard = entry_guard_get_by_id_digest(
+ circ->cpath->extend_info->identity_digest);
+ }
+
+ if (guard) {
+ /* In the long run: circuit_success ~= successful_circuit_close +
+ * circ_failure + stream_failure */
+ guard->successful_circuits_closed++;
+ entry_guards_changed();
+ } else if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
+ /* In rare cases, CIRCUIT_PURPOSE_TESTING can get converted to
+ * CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT and have no guards here.
+ * No need to log that case. */
+ log_info(LD_CIRC,
+ "Successfully closed circuit has no known guard. "
+ "Circuit is a %s currently %s",
+ circuit_purpose_to_string(circ->base_.purpose),
+ circuit_state_to_string(circ->base_.state));
+ }
+}
+
+/**
+ * Count a circuit that fails after it is built, but before it can
+ * carry any traffic.
+ *
+ * This is needed because there are ways to destroy a
+ * circuit after it has successfully completed. Right now, this is
+ * used for purely informational/debugging purposes.
+ */
+static void
+pathbias_count_collapse(origin_circuit_t *circ)
+{
+ entry_guard_t *guard = NULL;
+
+ if (!pathbias_should_count(circ)) {
+ return;
+ }
+
+ if (circ->cpath && circ->cpath->extend_info) {
+ guard = entry_guard_get_by_id_digest(
+ circ->cpath->extend_info->identity_digest);
+ }
+
+ if (guard) {
+ guard->collapsed_circuits++;
+ entry_guards_changed();
+ } else if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
+ /* In rare cases, CIRCUIT_PURPOSE_TESTING can get converted to
+ * CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT and have no guards here.
+ * No need to log that case. */
+ log_info(LD_CIRC,
+ "Destroyed circuit has no known guard. "
+ "Circuit is a %s currently %s",
+ circuit_purpose_to_string(circ->base_.purpose),
+ circuit_state_to_string(circ->base_.state));
+ }
+}
+
+/**
+ * Count a known failed circuit (because we could not probe it).
+ *
+ * This counter is informational.
+ */
+static void
+pathbias_count_use_failed(origin_circuit_t *circ)
+{
+ entry_guard_t *guard = NULL;
+ if (!pathbias_should_count(circ)) {
+ return;
+ }
+
+ if (circ->cpath && circ->cpath->extend_info) {
+ guard = entry_guard_get_by_id_digest(
+ circ->cpath->extend_info->identity_digest);
+ }
+
+ if (guard) {
+ guard->unusable_circuits++;
+ entry_guards_changed();
+ } else if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
+ /* In rare cases, CIRCUIT_PURPOSE_TESTING can get converted to
+ * CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT and have no guards here.
+ * No need to log that case. */
+ /* XXX note cut-and-paste code in this function compared to nearby
+ * functions. Would be nice to refactor. -RD */
+ log_info(LD_CIRC,
+ "Stream-failing circuit has no known guard. "
+ "Circuit is a %s currently %s",
+ circuit_purpose_to_string(circ->base_.purpose),
+ circuit_state_to_string(circ->base_.state));
+ }
+}
+
+/**
+ * Count timeouts for path bias log messages.
+ *
+ * These counts are purely informational.
+ */
+void
+pathbias_count_timeout(origin_circuit_t *circ)
+{
+ entry_guard_t *guard = NULL;
+
+ if (!pathbias_should_count(circ)) {
+ return;
+ }
+
+ /* For hidden service circs, they can actually be used
+ * successfully and then time out later (because
+ * the other side declines to use them). */
+ if (circ->path_state == PATH_STATE_USE_SUCCEEDED) {
+ return;
+ }
+
+ if (circ->cpath && circ->cpath->extend_info) {
+ guard = entry_guard_get_by_id_digest(
+ circ->cpath->extend_info->identity_digest);
+ }
+
+ if (guard) {
+ guard->timeouts++;
+ entry_guards_changed();
+ }
+}
+
+/**
+ * Helper function to count all of the currently opened circuits
+ * for a guard that are in a given path state range. The state
+ * range is inclusive on both ends.
+ */
+static int
+pathbias_count_circs_in_states(entry_guard_t *guard,
+ path_state_t from,
+ path_state_t to)
+{
+ circuit_t *circ;
+ int open_circuits = 0;
+
+ /* Count currently open circuits. Give them the benefit of the doubt. */
+ for (circ = global_circuitlist; circ; circ = circ->next) {
+ origin_circuit_t *ocirc = NULL;
+ if (!CIRCUIT_IS_ORIGIN(circ) || /* didn't originate here */
+ circ->marked_for_close) /* already counted */
+ continue;
+
+ ocirc = TO_ORIGIN_CIRCUIT(circ);
+
+ if (!ocirc->cpath || !ocirc->cpath->extend_info)
+ continue;
+
+ if (ocirc->path_state >= from &&
+ ocirc->path_state <= to &&
+ pathbias_should_count(ocirc) &&
+ fast_memeq(guard->identity,
+ ocirc->cpath->extend_info->identity_digest,
+ DIGEST_LEN)) {
+ log_debug(LD_CIRC, "Found opened circuit %d in path_state %s",
+ ocirc->global_identifier,
+ pathbias_state_to_string(ocirc->path_state));
+ open_circuits++;
+ }
+ }
+
+ return open_circuits;
+}
+
+/**
+ * Return the number of circuits counted as successfully closed for
+ * this guard.
+ *
+ * Also add in the currently open circuits to give them the benefit
+ * of the doubt.
+ */
+double
+pathbias_get_close_success_count(entry_guard_t *guard)
+{
+ return guard->successful_circuits_closed +
+ pathbias_count_circs_in_states(guard,
+ PATH_STATE_BUILD_SUCCEEDED,
+ PATH_STATE_USE_SUCCEEDED);
+}
+
+/**
+ * Return the number of circuits counted as successfully used
+ * this guard.
+ *
+ * Also add in the currently open circuits that we are attempting
+ * to use to give them the benefit of the doubt.
+ */
+double
+pathbias_get_use_success_count(entry_guard_t *guard)
+{
+ return guard->use_successes +
+ pathbias_count_circs_in_states(guard,
+ PATH_STATE_USE_ATTEMPTED,
+ PATH_STATE_USE_SUCCEEDED);
+}
+
+/**
+ * Check the path bias use rate against our consensus parameter limits.
+ *
+ * Emits a log message if the use success rates are too low.
+ *
+ * If pathbias_get_dropguards() is set, we also disable the use of
+ * very failure prone guards.
+ */
+static void
+pathbias_measure_use_rate(entry_guard_t *guard)
{
const or_options_t *options = get_options();
- entry_guards_changed();
+ if (guard->use_attempts > pathbias_get_min_use(options)) {
+ /* Note: We rely on the < comparison here to allow us to set a 0
+ * rate and disable the feature entirely. If refactoring, don't
+ * change to <= */
+ if (pathbias_get_use_success_count(guard)/guard->use_attempts
+ < pathbias_get_extreme_use_rate(options)) {
+ /* Dropping is currently disabled by default. */
+ if (pathbias_get_dropguards(options)) {
+ if (!guard->path_bias_disabled) {
+ log_warn(LD_CIRC,
+ "Your Guard %s=%s is failing to carry an extremely large "
+ "amount of stream on its circuits. "
+ "To avoid potential route manipulation attacks, Tor has "
+ "disabled use of this guard. "
+ "Use counts are %ld/%ld. Success counts are %ld/%ld. "
+ "%ld circuits completed, %ld were unusable, %ld collapsed, "
+ "and %ld timed out. "
+ "For reference, your timeout cutoff is %ld seconds.",
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN),
+ tor_lround(pathbias_get_use_success_count(guard)),
+ tor_lround(guard->use_attempts),
+ tor_lround(pathbias_get_close_success_count(guard)),
+ tor_lround(guard->circ_attempts),
+ tor_lround(guard->circ_successes),
+ tor_lround(guard->unusable_circuits),
+ tor_lround(guard->collapsed_circuits),
+ tor_lround(guard->timeouts),
+ tor_lround(circ_times.close_ms/1000));
+ guard->path_bias_disabled = 1;
+ guard->bad_since = approx_time();
+ entry_guards_changed();
+ return;
+ }
+ } else if (!guard->path_bias_use_extreme) {
+ guard->path_bias_use_extreme = 1;
+ log_warn(LD_CIRC,
+ "Your Guard %s=%s is failing to carry an extremely large "
+ "amount of streams on its circuits. "
+ "This could indicate a route manipulation attack, network "
+ "overload, bad local network connectivity, or a bug. "
+ "Use counts are %ld/%ld. Success counts are %ld/%ld. "
+ "%ld circuits completed, %ld were unusable, %ld collapsed, "
+ "and %ld timed out. "
+ "For reference, your timeout cutoff is %ld seconds.",
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN),
+ tor_lround(pathbias_get_use_success_count(guard)),
+ tor_lround(guard->use_attempts),
+ tor_lround(pathbias_get_close_success_count(guard)),
+ tor_lround(guard->circ_attempts),
+ tor_lround(guard->circ_successes),
+ tor_lround(guard->unusable_circuits),
+ tor_lround(guard->collapsed_circuits),
+ tor_lround(guard->timeouts),
+ tor_lround(circ_times.close_ms/1000));
+ }
+ } else if (pathbias_get_use_success_count(guard)/guard->use_attempts
+ < pathbias_get_notice_use_rate(options)) {
+ if (!guard->path_bias_use_noticed) {
+ guard->path_bias_use_noticed = 1;
+ log_notice(LD_CIRC,
+ "Your Guard %s=%s is failing to carry more streams on its "
+ "circuits than usual. "
+ "Most likely this means the Tor network is overloaded "
+ "or your network connection is poor. "
+ "Use counts are %ld/%ld. Success counts are %ld/%ld. "
+ "%ld circuits completed, %ld were unusable, %ld collapsed, "
+ "and %ld timed out. "
+ "For reference, your timeout cutoff is %ld seconds.",
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN),
+ tor_lround(pathbias_get_use_success_count(guard)),
+ tor_lround(guard->use_attempts),
+ tor_lround(pathbias_get_close_success_count(guard)),
+ tor_lround(guard->circ_attempts),
+ tor_lround(guard->circ_successes),
+ tor_lround(guard->unusable_circuits),
+ tor_lround(guard->collapsed_circuits),
+ tor_lround(guard->timeouts),
+ tor_lround(circ_times.close_ms/1000));
+ }
+ }
+ }
+}
- if (guard->first_hops > (unsigned)pathbias_get_min_circs(options)) {
+/**
+ * Check the path bias circuit close status rates against our consensus
+ * parameter limits.
+ *
+ * Emits a log message if the use success rates are too low.
+ *
+ * If pathbias_get_dropguards() is set, we also disable the use of
+ * very failure prone guards.
+ *
+ * XXX: This function shares similar log messages and checks to
+ * pathbias_measure_use_rate(). It may be possible to combine them
+ * eventually, especially if we can ever remove the need for 3
+ * levels of closure warns (if the overall circuit failure rate
+ * goes down with ntor). One way to do so would be to multiply
+ * the build rate with the use rate to get an idea of the total
+ * fraction of the total network paths the user is able to use.
+ * See ticket #8159.
+ */
+static void
+pathbias_measure_close_rate(entry_guard_t *guard)
+{
+ const or_options_t *options = get_options();
+
+ if (guard->circ_attempts > pathbias_get_min_circs(options)) {
/* Note: We rely on the < comparison here to allow us to set a 0
* rate and disable the feature entirely. If refactoring, don't
* change to <= */
- if (guard->circuit_successes/((double)guard->first_hops)
- < pathbias_get_disable_rate(options)) {
-
- /* This message is currently disabled by default. */
- log_warn(LD_PROTOCOL,
- "Extremely low circuit success rate %u/%u for guard %s=%s. "
- "This indicates either an overloaded guard, an attack, or "
- "a bug.",
- guard->circuit_successes, guard->first_hops, guard->nickname,
- hex_str(guard->identity, DIGEST_LEN));
-
- guard->path_bias_disabled = 1;
- guard->bad_since = approx_time();
- return -1;
- } else if (guard->circuit_successes/((double)guard->first_hops)
- < pathbias_get_notice_rate(options)
- && !guard->path_bias_notice) {
- guard->path_bias_notice = 1;
- log_notice(LD_PROTOCOL,
- "Low circuit success rate %u/%u for guard %s=%s.",
- guard->circuit_successes, guard->first_hops, guard->nickname,
- hex_str(guard->identity, DIGEST_LEN));
+ if (pathbias_get_close_success_count(guard)/guard->circ_attempts
+ < pathbias_get_extreme_rate(options)) {
+ /* Dropping is currently disabled by default. */
+ if (pathbias_get_dropguards(options)) {
+ if (!guard->path_bias_disabled) {
+ log_warn(LD_CIRC,
+ "Your Guard %s=%s is failing an extremely large "
+ "amount of circuits. "
+ "To avoid potential route manipulation attacks, Tor has "
+ "disabled use of this guard. "
+ "Success counts are %ld/%ld. Use counts are %ld/%ld. "
+ "%ld circuits completed, %ld were unusable, %ld collapsed, "
+ "and %ld timed out. "
+ "For reference, your timeout cutoff is %ld seconds.",
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN),
+ tor_lround(pathbias_get_close_success_count(guard)),
+ tor_lround(guard->circ_attempts),
+ tor_lround(pathbias_get_use_success_count(guard)),
+ tor_lround(guard->use_attempts),
+ tor_lround(guard->circ_successes),
+ tor_lround(guard->unusable_circuits),
+ tor_lround(guard->collapsed_circuits),
+ tor_lround(guard->timeouts),
+ tor_lround(circ_times.close_ms/1000));
+ guard->path_bias_disabled = 1;
+ guard->bad_since = approx_time();
+ entry_guards_changed();
+ return;
+ }
+ } else if (!guard->path_bias_extreme) {
+ guard->path_bias_extreme = 1;
+ log_warn(LD_CIRC,
+ "Your Guard %s=%s is failing an extremely large "
+ "amount of circuits. "
+ "This could indicate a route manipulation attack, "
+ "extreme network overload, or a bug. "
+ "Success counts are %ld/%ld. Use counts are %ld/%ld. "
+ "%ld circuits completed, %ld were unusable, %ld collapsed, "
+ "and %ld timed out. "
+ "For reference, your timeout cutoff is %ld seconds.",
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN),
+ tor_lround(pathbias_get_close_success_count(guard)),
+ tor_lround(guard->circ_attempts),
+ tor_lround(pathbias_get_use_success_count(guard)),
+ tor_lround(guard->use_attempts),
+ tor_lround(guard->circ_successes),
+ tor_lround(guard->unusable_circuits),
+ tor_lround(guard->collapsed_circuits),
+ tor_lround(guard->timeouts),
+ tor_lround(circ_times.close_ms/1000));
+ }
+ } else if (pathbias_get_close_success_count(guard)/guard->circ_attempts
+ < pathbias_get_warn_rate(options)) {
+ if (!guard->path_bias_warned) {
+ guard->path_bias_warned = 1;
+ log_warn(LD_CIRC,
+ "Your Guard %s=%s is failing a very large "
+ "amount of circuits. "
+ "Most likely this means the Tor network is "
+ "overloaded, but it could also mean an attack against "
+ "you or potentially the guard itself. "
+ "Success counts are %ld/%ld. Use counts are %ld/%ld. "
+ "%ld circuits completed, %ld were unusable, %ld collapsed, "
+ "and %ld timed out. "
+ "For reference, your timeout cutoff is %ld seconds.",
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN),
+ tor_lround(pathbias_get_close_success_count(guard)),
+ tor_lround(guard->circ_attempts),
+ tor_lround(pathbias_get_use_success_count(guard)),
+ tor_lround(guard->use_attempts),
+ tor_lround(guard->circ_successes),
+ tor_lround(guard->unusable_circuits),
+ tor_lround(guard->collapsed_circuits),
+ tor_lround(guard->timeouts),
+ tor_lround(circ_times.close_ms/1000));
+ }
+ } else if (pathbias_get_close_success_count(guard)/guard->circ_attempts
+ < pathbias_get_notice_rate(options)) {
+ if (!guard->path_bias_noticed) {
+ guard->path_bias_noticed = 1;
+ log_notice(LD_CIRC,
+ "Your Guard %s=%s is failing more circuits than "
+ "usual. "
+ "Most likely this means the Tor network is overloaded. "
+ "Success counts are %ld/%ld. Use counts are %ld/%ld. "
+ "%ld circuits completed, %ld were unusable, %ld collapsed, "
+ "and %ld timed out. "
+ "For reference, your timeout cutoff is %ld seconds.",
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN),
+ tor_lround(pathbias_get_close_success_count(guard)),
+ tor_lround(guard->circ_attempts),
+ tor_lround(pathbias_get_use_success_count(guard)),
+ tor_lround(guard->use_attempts),
+ tor_lround(guard->circ_successes),
+ tor_lround(guard->unusable_circuits),
+ tor_lround(guard->collapsed_circuits),
+ tor_lround(guard->timeouts),
+ tor_lround(circ_times.close_ms/1000));
+ }
}
}
+}
+
+/**
+ * This function scales the path bias use rates if we have
+ * more data than the scaling threshold. This allows us to
+ * be more sensitive to recent measurements.
+ *
+ * XXX: The attempt count transfer stuff here might be done
+ * better by keeping separate pending counters that get
+ * transfered at circuit close. See ticket #8160.
+ */
+static void
+pathbias_scale_close_rates(entry_guard_t *guard)
+{
+ const or_options_t *options = get_options();
/* If we get a ton of circuits, just scale everything down */
- if (guard->first_hops > (unsigned)pathbias_get_scale_threshold(options)) {
- const int scale_factor = pathbias_get_scale_factor(options);
- /* For now, only scale if there will be no rounding error...
- * XXX024: We want to switch to a real moving average for 0.2.4. */
- if ((guard->first_hops % scale_factor) == 0 &&
- (guard->circuit_successes % scale_factor) == 0) {
- log_info(LD_PROTOCOL,
- "Scaling pathbias counts to (%u/%u)/%d for guard %s=%s",
- guard->circuit_successes, guard->first_hops,
- scale_factor, guard->nickname, hex_str(guard->identity,
- DIGEST_LEN));
- guard->first_hops /= scale_factor;
- guard->circuit_successes /= scale_factor;
- }
+ if (guard->circ_attempts > pathbias_get_scale_threshold(options)) {
+ double scale_ratio = pathbias_get_scale_ratio(options);
+ int opened_attempts = pathbias_count_circs_in_states(guard,
+ PATH_STATE_BUILD_ATTEMPTED, PATH_STATE_BUILD_ATTEMPTED);
+ int opened_built = pathbias_count_circs_in_states(guard,
+ PATH_STATE_BUILD_SUCCEEDED,
+ PATH_STATE_USE_FAILED);
+ guard->circ_attempts -= opened_attempts;
+ guard->circ_successes -= opened_built;
+
+ guard->circ_attempts *= scale_ratio;
+ guard->circ_successes *= scale_ratio;
+ guard->timeouts *= scale_ratio;
+ guard->successful_circuits_closed *= scale_ratio;
+ guard->collapsed_circuits *= scale_ratio;
+ guard->unusable_circuits *= scale_ratio;
+
+ guard->circ_attempts += opened_attempts;
+ guard->circ_successes += opened_built;
+
+ entry_guards_changed();
+
+ log_info(LD_CIRC,
+ "Scaled pathbias counts to (%f,%f)/%f (%d/%d open) for guard "
+ "%s=%s",
+ guard->circ_successes, guard->successful_circuits_closed,
+ guard->circ_attempts, opened_built, opened_attempts,
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN));
+ }
+}
+
+/**
+ * This function scales the path bias circuit close rates if we have
+ * more data than the scaling threshold. This allows us to be more
+ * sensitive to recent measurements.
+ *
+ * XXX: The attempt count transfer stuff here might be done
+ * better by keeping separate pending counters that get
+ * transfered at circuit close. See ticket #8160.
+ */
+void
+pathbias_scale_use_rates(entry_guard_t *guard)
+{
+ const or_options_t *options = get_options();
+
+ /* If we get a ton of circuits, just scale everything down */
+ if (guard->use_attempts > pathbias_get_scale_use_threshold(options)) {
+ double scale_ratio = pathbias_get_scale_ratio(options);
+ int opened_attempts = pathbias_count_circs_in_states(guard,
+ PATH_STATE_USE_ATTEMPTED, PATH_STATE_USE_SUCCEEDED);
+ guard->use_attempts -= opened_attempts;
+
+ guard->use_attempts *= scale_ratio;
+ guard->use_successes *= scale_ratio;
+
+ guard->use_attempts += opened_attempts;
+
+ log_info(LD_CIRC,
+ "Scaled pathbias use counts to %f/%f (%d open) for guard %s=%s",
+ guard->use_successes, guard->use_attempts, opened_attempts,
+ guard->nickname, hex_str(guard->identity, DIGEST_LEN));
+ entry_guards_changed();
}
- guard->first_hops++;
- log_info(LD_PROTOCOL, "Got success count %u/%u for guard %s=%s",
- guard->circuit_successes, guard->first_hops, guard->nickname,
+}
+
+/** Increment the number of times we successfully extended a circuit to
+ * <b>guard</b>, first checking if the failure rate is high enough that
+ * we should eliminate the guard. Return -1 if the guard looks no good;
+ * return 0 if the guard looks fine.
+ */
+static int
+entry_guard_inc_circ_attempt_count(entry_guard_t *guard)
+{
+ entry_guards_changed();
+
+ pathbias_measure_close_rate(guard);
+
+ if (guard->path_bias_disabled)
+ return -1;
+
+ pathbias_scale_close_rates(guard);
+ guard->circ_attempts++;
+
+ log_info(LD_CIRC, "Got success count %f/%f for guard %s=%s",
+ guard->circ_successes, guard->circ_attempts, guard->nickname,
hex_str(guard->identity, DIGEST_LEN));
return 0;
}
-/** A created or extended cell came back to us on the circuit, and it included
- * <b>reply</b> as its body. (If <b>reply_type</b> is CELL_CREATED, the body
- * contains (the second DH key, plus KH). If <b>reply_type</b> is
- * CELL_CREATED_FAST, the body contains a secret y and a hash H(x|y).)
+/** A "created" cell <b>reply</b> came back to us on circuit <b>circ</b>.
+ * (The body of <b>reply</b> varies depending on what sort of handshake
+ * this is.)
*
* Calculate the appropriate keys and digests, make sure KH is
* correct, and initialize this hop of the cpath.
@@ -1352,14 +2570,14 @@ entry_guard_inc_first_hop_count(entry_guard_t *guard)
* Return - reason if we want to mark circ for close, else return 0.
*/
int
-circuit_finish_handshake(origin_circuit_t *circ, uint8_t reply_type,
- const uint8_t *reply)
+circuit_finish_handshake(origin_circuit_t *circ,
+ const created_cell_t *reply)
{
char keys[CPATH_KEY_MATERIAL_LEN];
crypt_path_t *hop;
int rv;
- if ((rv = pathbias_count_first_hop(circ)) < 0)
+ if ((rv = pathbias_count_build_attempt(circ)) < 0)
return rv;
if (circ->cpath->state == CPATH_STATE_AWAITING_KEYS) {
@@ -1373,39 +2591,25 @@ circuit_finish_handshake(origin_circuit_t *circ, uint8_t reply_type,
}
tor_assert(hop->state == CPATH_STATE_AWAITING_KEYS);
- if (reply_type == CELL_CREATED && hop->dh_handshake_state) {
- if (onion_skin_client_handshake(hop->dh_handshake_state, (char*)reply,keys,
- DIGEST_LEN*2+CIPHER_KEY_LEN*2) < 0) {
+ {
+ if (onion_skin_client_handshake(hop->handshake_state.tag,
+ &hop->handshake_state,
+ reply->reply, reply->handshake_len,
+ (uint8_t*)keys, sizeof(keys),
+ (uint8_t*)hop->rend_circ_nonce) < 0) {
log_warn(LD_CIRC,"onion_skin_client_handshake failed.");
return -END_CIRC_REASON_TORPROTOCOL;
}
- /* Remember hash of g^xy */
- memcpy(hop->handshake_digest, reply+DH_KEY_LEN, DIGEST_LEN);
- } else if (reply_type == CELL_CREATED_FAST && !hop->dh_handshake_state) {
- if (fast_client_handshake(hop->fast_handshake_state, reply,
- (uint8_t*)keys,
- DIGEST_LEN*2+CIPHER_KEY_LEN*2) < 0) {
- log_warn(LD_CIRC,"fast_client_handshake failed.");
- return -END_CIRC_REASON_TORPROTOCOL;
- }
- memcpy(hop->handshake_digest, reply+DIGEST_LEN, DIGEST_LEN);
- } else {
- log_warn(LD_PROTOCOL,"CREATED cell type did not match CREATE cell type.");
- return -END_CIRC_REASON_TORPROTOCOL;
}
- crypto_dh_free(hop->dh_handshake_state); /* don't need it anymore */
- hop->dh_handshake_state = NULL;
-
- memset(hop->fast_handshake_state, 0, sizeof(hop->fast_handshake_state));
+ onion_handshake_state_release(&hop->handshake_state);
if (circuit_init_cpath_crypto(hop, keys, 0)<0) {
return -END_CIRC_REASON_TORPROTOCOL;
}
hop->state = CPATH_STATE_OPEN;
- log_info(LD_CIRC,"Finished building %scircuit hop:",
- (reply_type == CELL_CREATED_FAST) ? "fast " : "");
+ log_info(LD_CIRC,"Finished building circuit hop:");
circuit_log_path(LOG_INFO,LD_CIRC,circ);
control_event_circuit_status(circ, CIRC_EVENT_EXTENDED, 0);
@@ -1414,9 +2618,9 @@ circuit_finish_handshake(origin_circuit_t *circ, uint8_t reply_type,
/** We received a relay truncated cell on circ.
*
- * Since we don't ask for truncates currently, getting a truncated
+ * Since we don't send truncates currently, getting a truncated
* means that a connection broke or an extend failed. For now,
- * just give up: for circ to close, and return 0.
+ * just give up: force circ to close, and return 0.
*/
int
circuit_truncated(origin_circuit_t *circ, crypt_path_t *layer, int reason)
@@ -1427,7 +2631,7 @@ circuit_truncated(origin_circuit_t *circ, crypt_path_t *layer, int reason)
tor_assert(circ);
tor_assert(layer);
- /* XXX Since we don't ask for truncates currently, getting a truncated
+ /* XXX Since we don't send truncates currently, getting a truncated
* means that a connection broke or an extend failed. For now,
* just give up.
*/
@@ -1465,24 +2669,26 @@ circuit_truncated(origin_circuit_t *circ, crypt_path_t *layer, int reason)
* cell back.
*/
int
-onionskin_answer(or_circuit_t *circ, uint8_t cell_type, const char *payload,
- const char *keys)
+onionskin_answer(or_circuit_t *circ,
+ const created_cell_t *created_cell,
+ const char *keys,
+ const uint8_t *rend_circ_nonce)
{
cell_t cell;
crypt_path_t *tmp_cpath;
+ if (created_cell_format(&cell, created_cell) < 0) {
+ log_warn(LD_BUG,"couldn't format created cell (type=%d, len=%d)",
+ (int)created_cell->cell_type, (int)created_cell->handshake_len);
+ return -1;
+ }
+ cell.circ_id = circ->p_circ_id;
+
tmp_cpath = tor_malloc_zero(sizeof(crypt_path_t));
tmp_cpath->magic = CRYPT_PATH_MAGIC;
- memset(&cell, 0, sizeof(cell_t));
- cell.command = cell_type;
- cell.circ_id = circ->p_circ_id;
-
circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OPEN);
- memcpy(cell.payload, payload,
- cell_type == CELL_CREATED ? ONIONSKIN_REPLY_LEN : DIGEST_LEN*2);
-
log_debug(LD_CIRC,"init digest forward 0x%.8x, backward 0x%.8x.",
(unsigned int)get_uint32(keys),
(unsigned int)get_uint32(keys+20));
@@ -1498,12 +2704,9 @@ onionskin_answer(or_circuit_t *circ, uint8_t cell_type, const char *payload,
tmp_cpath->magic = 0;
tor_free(tmp_cpath);
- if (cell_type == CELL_CREATED)
- memcpy(circ->handshake_digest, cell.payload+DH_KEY_LEN, DIGEST_LEN);
- else
- memcpy(circ->handshake_digest, cell.payload+DIGEST_LEN, DIGEST_LEN);
+ memcpy(circ->rend_circ_nonce, rend_circ_nonce, DIGEST_LEN);
- circ->is_first_hop = (cell_type == CELL_CREATED_FAST);
+ circ->is_first_hop = (created_cell->cell_type == CELL_CREATED_FAST);
append_cell_to_circuit_queue(TO_CIRCUIT(circ),
circ->p_chan, &cell, CELL_DIRECTION_IN, 0);
@@ -1521,15 +2724,18 @@ onionskin_answer(or_circuit_t *circ, uint8_t cell_type, const char *payload,
return 0;
}
-/** Choose a length for a circuit of purpose <b>purpose</b>.
- * Default length is 3 + the number of endpoints that would give something
- * away. If the routerlist <b>routers</b> doesn't have enough routers
+/** Choose a length for a circuit of purpose <b>purpose</b>: three + the
+ * number of endpoints that would give something away about our destination.
+ *
+ * If the routerlist <b>nodes</b> doesn't have enough routers
* to handle the desired path length, return as large a path length as
* is feasible, except if it's less than 2, in which case return -1.
+ * XXX ^^ I think this behavior is a hold-over from back when we had only a
+ * few relays in the network, and certainly back before guards existed.
+ * We should very likely get rid of it. -RD
*/
static int
-new_route_len(uint8_t purpose, extend_info_t *exit,
- smartlist_t *nodes)
+new_route_len(uint8_t purpose, extend_info_t *exit, smartlist_t *nodes)
{
int num_acceptable_routers;
int routelen;
@@ -1594,7 +2800,7 @@ circuit_all_predicted_ports_handled(time_t now, int *need_uptime,
enough = (smartlist_len(sl) == 0);
for (i = 0; i < smartlist_len(sl); ++i) {
port = smartlist_get(sl, i);
- if (smartlist_string_num_isin(LongLivedServices, *port))
+ if (smartlist_contains_int_as_string(LongLivedServices, *port))
*need_uptime = 1;
tor_free(port);
}
@@ -2042,6 +3248,9 @@ circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *exit)
{
int err_reason = 0;
warn_if_last_router_excluded(circ, exit);
+
+ tor_gettimeofday(&circ->base_.timestamp_began);
+
circuit_append_new_exit(circ, exit);
circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_BUILDING);
if ((err_reason = circuit_send_next_onion_skin(circ))<0) {
@@ -2050,6 +3259,9 @@ circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *exit)
circuit_mark_for_close(TO_CIRCUIT(circ), -err_reason);
return -1;
}
+
+ // XXX: Should cannibalized circuits be dirty or not? Not easy to say..
+
return 0;
}
@@ -2303,8 +3515,9 @@ onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice)
/** Allocate a new extend_info object based on the various arguments. */
extend_info_t *
extend_info_new(const char *nickname, const char *digest,
- crypto_pk_t *onion_key,
- const tor_addr_t *addr, uint16_t port)
+ crypto_pk_t *onion_key,
+ const curve25519_public_key_t *curve25519_key,
+ const tor_addr_t *addr, uint16_t port)
{
extend_info_t *info = tor_malloc_zero(sizeof(extend_info_t));
memcpy(info->identity_digest, digest, DIGEST_LEN);
@@ -2312,6 +3525,13 @@ extend_info_new(const char *nickname, const char *digest,
strlcpy(info->nickname, nickname, sizeof(info->nickname));
if (onion_key)
info->onion_key = crypto_pk_dup_key(onion_key);
+#ifdef CURVE25519_ENABLED
+ if (curve25519_key)
+ memcpy(&info->curve25519_onion_key, curve25519_key,
+ sizeof(curve25519_public_key_t));
+#else
+ (void)curve25519_key;
+#endif
tor_addr_copy(&info->addr, addr);
info->port = port;
return info;
@@ -2346,12 +3566,14 @@ extend_info_from_node(const node_t *node, int for_direct_connect)
return extend_info_new(node->ri->nickname,
node->identity,
node->ri->onion_pkey,
+ node->ri->onion_curve25519_pkey,
&ap.addr,
ap.port);
else if (node->rs && node->md)
return extend_info_new(node->rs->nickname,
node->identity,
node->md->onion_pkey,
+ node->md->onion_curve25519_pkey,
&ap.addr,
ap.port);
else
diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h
index 78575afcf2..3ca8d1531d 100644
--- a/src/or/circuitbuild.h
+++ b/src/or/circuitbuild.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -30,12 +30,15 @@ void circuit_note_clock_jumped(int seconds_elapsed);
int circuit_extend(cell_t *cell, circuit_t *circ);
int circuit_init_cpath_crypto(crypt_path_t *cpath, const char *key_data,
int reverse);
-int circuit_finish_handshake(origin_circuit_t *circ, uint8_t cell_type,
- const uint8_t *reply);
+struct created_cell_t;
+int circuit_finish_handshake(origin_circuit_t *circ,
+ const struct created_cell_t *created_cell);
int circuit_truncated(origin_circuit_t *circ, crypt_path_t *layer,
int reason);
-int onionskin_answer(or_circuit_t *circ, uint8_t cell_type,
- const char *payload, const char *keys);
+int onionskin_answer(or_circuit_t *circ,
+ const struct created_cell_t *created_cell,
+ const char *keys,
+ const uint8_t *rend_circ_nonce);
int circuit_all_predicted_ports_handled(time_t now, int *need_uptime,
int *need_capacity);
@@ -43,8 +46,9 @@ int circuit_append_new_exit(origin_circuit_t *circ, extend_info_t *info);
int circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *info);
void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop);
extend_info_t *extend_info_new(const char *nickname, const char *digest,
- crypto_pk_t *onion_key,
- const tor_addr_t *addr, uint16_t port);
+ crypto_pk_t *onion_key,
+ const curve25519_public_key_t *curve25519_key,
+ const tor_addr_t *addr, uint16_t port);
extend_info_t *extend_info_from_node(const node_t *r, int for_direct_connect);
extend_info_t *extend_info_dup(extend_info_t *info);
void extend_info_free(extend_info_t *info);
@@ -53,7 +57,15 @@ const char *build_state_get_exit_nickname(cpath_build_state_t *state);
const node_t *choose_good_entry_server(uint8_t purpose,
cpath_build_state_t *state);
-double pathbias_get_disable_rate(const or_options_t *options);
+double pathbias_get_extreme_rate(const or_options_t *options);
+double pathbias_get_extreme_use_rate(const or_options_t *options);
+int pathbias_get_dropguards(const or_options_t *options);
+void pathbias_count_timeout(origin_circuit_t *circ);
+int pathbias_check_close(origin_circuit_t *circ, int reason);
+int pathbias_check_probe_response(circuit_t *circ, const cell_t *cell);
+void pathbias_count_use_attempt(origin_circuit_t *circ);
+void pathbias_mark_use_success(origin_circuit_t *circ);
+void pathbias_mark_use_rollback(origin_circuit_t *circ);
#endif
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index 3db235ae08..ef32680736 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -1,7 +1,7 @@
/* Copyright 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -23,6 +23,7 @@
#include "networkstatus.h"
#include "nodelist.h"
#include "onion.h"
+#include "onion_fast.h"
#include "relay.h"
#include "rendclient.h"
#include "rendcommon.h"
@@ -251,7 +252,7 @@ circuit_set_state(circuit_t *circ, uint8_t state)
smartlist_add(circuits_pending_chans, circ);
}
if (state == CIRCUIT_STATE_OPEN)
- tor_assert(!circ->n_chan_onionskin);
+ tor_assert(!circ->n_chan_create_cell);
circ->state = state;
}
@@ -413,6 +414,8 @@ circuit_purpose_to_controller_string(uint8_t purpose)
return "MEASURE_TIMEOUT";
case CIRCUIT_PURPOSE_CONTROLLER:
return "CONTROLLER";
+ case CIRCUIT_PURPOSE_PATH_BIAS_TESTING:
+ return "PATH_BIAS_TESTING";
default:
tor_snprintf(buf, sizeof(buf), "UNKNOWN_%d", (int)purpose);
@@ -440,6 +443,7 @@ circuit_purpose_to_controller_hs_state_string(uint8_t purpose)
case CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT:
case CIRCUIT_PURPOSE_TESTING:
case CIRCUIT_PURPOSE_CONTROLLER:
+ case CIRCUIT_PURPOSE_PATH_BIAS_TESTING:
return NULL;
case CIRCUIT_PURPOSE_INTRO_POINT:
@@ -555,6 +559,11 @@ init_circuit_base(circuit_t *circ)
{
tor_gettimeofday(&circ->timestamp_created);
+ // Gets reset when we send CREATE_FAST.
+ // circuit_expire_building() expects these to be equal
+ // until the orconn is built.
+ circ->timestamp_began = circ->timestamp_created;
+
circ->package_window = circuit_initial_package_window();
circ->deliver_window = CIRCWINDOW_START;
@@ -637,11 +646,11 @@ circuit_free(circuit_t *circ)
tor_free(ocirc->dest_address);
if (ocirc->socks_username) {
- memset(ocirc->socks_username, 0x12, ocirc->socks_username_len);
+ memwipe(ocirc->socks_username, 0x12, ocirc->socks_username_len);
tor_free(ocirc->socks_username);
}
if (ocirc->socks_password) {
- memset(ocirc->socks_password, 0x06, ocirc->socks_password_len);
+ memwipe(ocirc->socks_password, 0x06, ocirc->socks_password_len);
tor_free(ocirc->socks_password);
}
} else {
@@ -673,7 +682,7 @@ circuit_free(circuit_t *circ)
}
extend_info_free(circ->n_hop);
- tor_free(circ->n_chan_onionskin);
+ tor_free(circ->n_chan_create_cell);
/* Remove from map. */
circuit_set_n_circid_chan(circ, 0, NULL);
@@ -682,7 +691,7 @@ circuit_free(circuit_t *circ)
* "active" checks will be violated. */
cell_queue_clear(&circ->n_chan_cells);
- memset(mem, 0xAA, memlen); /* poison memory */
+ memwipe(mem, 0xAA, memlen); /* poison memory */
tor_free(mem);
}
@@ -743,10 +752,11 @@ circuit_free_cpath_node(crypt_path_t *victim)
crypto_cipher_free(victim->b_crypto);
crypto_digest_free(victim->f_digest);
crypto_digest_free(victim->b_digest);
- crypto_dh_free(victim->dh_handshake_state);
+ onion_handshake_state_release(&victim->handshake_state);
+ crypto_dh_free(victim->rend_dh_handshake_state);
extend_info_free(victim->extend_info);
- memset(victim, 0xBB, sizeof(crypt_path_t)); /* poison memory */
+ memwipe(victim, 0xBB, sizeof(crypt_path_t)); /* poison memory */
tor_free(victim);
}
@@ -773,11 +783,11 @@ circuit_dump_conn_details(int severity,
int this_circid,
int other_circid)
{
- log(severity, LD_CIRC, "Conn %d has %s circuit: circID %d (other side %d), "
- "state %d (%s), born %ld:",
+ tor_log(severity, LD_CIRC, "Conn %d has %s circuit: circID %d "
+ "(other side %d), state %d (%s), born %ld:",
conn_array_index, type, this_circid, other_circid, circ->state,
circuit_state_to_string(circ->state),
- (long)circ->timestamp_created.tv_sec);
+ (long)circ->timestamp_began.tv_sec);
if (CIRCUIT_IS_ORIGIN(circ)) { /* circ starts at this node */
circuit_log_path(severity, LD_CIRC, TO_ORIGIN_CIRCUIT(circ));
}
@@ -836,11 +846,11 @@ circuit_dump_chan_details(int severity,
int this_circid,
int other_circid)
{
- log(severity, LD_CIRC, "Conn %p has %s circuit: circID %d (other side %d), "
- "state %d (%s), born %ld:",
+ tor_log(severity, LD_CIRC, "Conn %p has %s circuit: circID %d "
+ "(other side %d), state %d (%s), born %ld:",
chan, type, this_circid, other_circid, circ->state,
circuit_state_to_string(circ->state),
- (long)circ->timestamp_created.tv_sec);
+ (long)circ->timestamp_began.tv_sec);
if (CIRCUIT_IS_ORIGIN(circ)) { /* circ starts at this node */
circuit_log_path(severity, LD_CIRC, TO_ORIGIN_CIRCUIT(circ));
}
@@ -1033,8 +1043,13 @@ circuit_unlink_all_from_channel(channel_t *chan, int reason)
for (circ = global_circuitlist; circ; circ = circ->next) {
int mark = 0;
if (circ->n_chan == chan) {
- circuit_set_n_circid_chan(circ, 0, NULL);
- mark = 1;
+ circuit_set_n_circid_chan(circ, 0, NULL);
+ mark = 1;
+
+ /* If we didn't request this closure, pass the remote
+ * bit to mark_for_close. */
+ if (chan->reason_for_closing != CHANNEL_CLOSE_REQUESTED)
+ reason |= END_CIRC_REASON_FLAG_REMOTE;
}
if (! CIRCUIT_IS_ORIGIN(circ)) {
or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
@@ -1328,7 +1343,7 @@ circuit_mark_for_close_(circuit_t *circ, int reason, int line,
tor_assert(file);
if (circ->marked_for_close) {
- log(LOG_WARN,LD_BUG,
+ log_warn(LD_BUG,
"Duplicate call to circuit_mark_for_close at %s:%d"
" (first at %s:%d)", file, line,
circ->marked_for_close_file, circ->marked_for_close);
@@ -1342,7 +1357,13 @@ circuit_mark_for_close_(circuit_t *circ, int reason, int line,
}
reason = END_CIRC_REASON_NONE;
}
+
if (CIRCUIT_IS_ORIGIN(circ)) {
+ if (pathbias_check_close(TO_ORIGIN_CIRCUIT(circ), reason) == -1) {
+ /* Don't close it yet, we need to test it first */
+ return;
+ }
+
/* We don't send reasons when closing circuits at the origin. */
reason = END_CIRC_REASON_NONE;
}
@@ -1411,7 +1432,12 @@ circuit_mark_for_close_(circuit_t *circ, int reason, int line,
}
if (circ->n_chan) {
circuit_clear_cell_queue(circ, circ->n_chan);
- channel_send_destroy(circ->n_circ_id, circ->n_chan, reason);
+ /* Only send destroy if the channel isn't closing anyway */
+ if (!(circ->n_chan->state == CHANNEL_STATE_CLOSING ||
+ circ->n_chan->state == CHANNEL_STATE_CLOSED ||
+ circ->n_chan->state == CHANNEL_STATE_ERROR)) {
+ channel_send_destroy(circ->n_circ_id, circ->n_chan, reason);
+ }
circuitmux_detach_circuit(circ->n_chan->cmux, circ);
}
@@ -1439,7 +1465,12 @@ circuit_mark_for_close_(circuit_t *circ, int reason, int line,
if (or_circ->p_chan) {
circuit_clear_cell_queue(circ, or_circ->p_chan);
- channel_send_destroy(or_circ->p_circ_id, or_circ->p_chan, reason);
+ /* Only send destroy if the channel isn't closing anyway */
+ if (!(or_circ->p_chan->state == CHANNEL_STATE_CLOSING ||
+ or_circ->p_chan->state == CHANNEL_STATE_CLOSED ||
+ or_circ->p_chan->state == CHANNEL_STATE_ERROR)) {
+ channel_send_destroy(or_circ->p_circ_id, or_circ->p_chan, reason);
+ }
circuitmux_detach_circuit(or_circ->p_chan->cmux, circ);
}
} else {
@@ -1482,7 +1513,8 @@ assert_cpath_layer_ok(const crypt_path_t *cp)
tor_assert(cp->b_crypto);
/* fall through */
case CPATH_STATE_CLOSED:
- tor_assert(!cp->dh_handshake_state);
+ /*XXXX Assert that there's no handshake_state either. */
+ tor_assert(!cp->rend_dh_handshake_state);
break;
case CPATH_STATE_AWAITING_KEYS:
/* tor_assert(cp->dh_handshake_state); */
@@ -1569,7 +1601,7 @@ assert_circuit_ok(const circuit_t *c)
tor_assert(c->deliver_window >= 0);
tor_assert(c->package_window >= 0);
if (c->state == CIRCUIT_STATE_OPEN) {
- tor_assert(!c->n_chan_onionskin);
+ tor_assert(!c->n_chan_create_cell);
if (or_circ) {
tor_assert(or_circ->n_crypto);
tor_assert(or_circ->p_crypto);
@@ -1579,10 +1611,10 @@ assert_circuit_ok(const circuit_t *c)
}
if (c->state == CIRCUIT_STATE_CHAN_WAIT && !c->marked_for_close) {
tor_assert(circuits_pending_chans &&
- smartlist_isin(circuits_pending_chans, c));
+ smartlist_contains(circuits_pending_chans, c));
} else {
tor_assert(!circuits_pending_chans ||
- !smartlist_isin(circuits_pending_chans, c));
+ !smartlist_contains(circuits_pending_chans, c));
}
if (origin_circ && origin_circ->cpath) {
assert_cpath_ok(origin_circ->cpath);
diff --git a/src/or/circuitlist.h b/src/or/circuitlist.h
index a885af2fa0..e81c0785fe 100644
--- a/src/or/circuitlist.h
+++ b/src/or/circuitlist.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c
index f3b6b7cd7b..dcc1901819 100644
--- a/src/or/circuitmux.c
+++ b/src/or/circuitmux.c
@@ -1,4 +1,4 @@
-/* * Copyright (c) 2012, The Tor Project, Inc. */
+/* * Copyright (c) 2012-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/circuitmux.h b/src/or/circuitmux.h
index ebab1ba7d3..25644ffab7 100644
--- a/src/or/circuitmux.h
+++ b/src/or/circuitmux.h
@@ -1,4 +1,4 @@
-/* * Copyright (c) 2012, The Tor Project, Inc. */
+/* * Copyright (c) 2012-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/circuitmux_ewma.c b/src/or/circuitmux_ewma.c
index e1964d2383..3f37d7b9a0 100644
--- a/src/or/circuitmux_ewma.c
+++ b/src/or/circuitmux_ewma.c
@@ -1,4 +1,4 @@
-/* * Copyright (c) 2012, The Tor Project, Inc. */
+/* * Copyright (c) 2012-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/circuitmux_ewma.h b/src/or/circuitmux_ewma.h
index 5621acc936..a512745c77 100644
--- a/src/or/circuitmux_ewma.h
+++ b/src/or/circuitmux_ewma.h
@@ -1,4 +1,4 @@
-/* * Copyright (c) 2012, The Tor Project, Inc. */
+/* * Copyright (c) 2012-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/circuitstats.c b/src/or/circuitstats.c
index 6d529d1e43..73e34d9ed7 100644
--- a/src/or/circuitstats.c
+++ b/src/or/circuitstats.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#define CIRCUITSTATS_PRIVATE
@@ -183,16 +183,6 @@ circuit_build_times_quantile_cutoff(void)
return num/100.0;
}
-/* DOCDOC circuit_build_times_get_bw_scale */
-int
-circuit_build_times_get_bw_scale(networkstatus_t *ns)
-{
- return networkstatus_get_param(ns, "bwweightscale",
- BW_WEIGHT_SCALE,
- BW_MIN_WEIGHT_SCALE,
- BW_MAX_WEIGHT_SCALE);
-}
-
/**
* Retrieve and bounds-check the cbtclosequantile consensus paramter.
*
@@ -1469,11 +1459,6 @@ circuit_build_times_set_timeout_worker(circuit_build_times_t *cbt)
max_time = circuit_build_times_max(cbt);
- /* Sometimes really fast guard nodes give us such a steep curve
- * that this ends up being not that much greater than timeout_ms.
- * Make it be at least 1 min to handle this case. */
- cbt->close_ms = MAX(cbt->close_ms, circuit_build_times_initial_timeout());
-
if (cbt->timeout_ms > max_time) {
log_info(LD_CIRC,
"Circuit build timeout of %dms is beyond the maximum build "
@@ -1490,6 +1475,11 @@ circuit_build_times_set_timeout_worker(circuit_build_times_t *cbt)
cbt->close_ms = 2*max_time;
}
+ /* Sometimes really fast guard nodes give us such a steep curve
+ * that this ends up being not that much greater than timeout_ms.
+ * Make it be at least 1 min to handle this case. */
+ cbt->close_ms = MAX(cbt->close_ms, circuit_build_times_initial_timeout());
+
cbt->have_computed_timeout = 1;
return 1;
}
diff --git a/src/or/circuitstats.h b/src/or/circuitstats.h
index efe2799b0f..87dce99f4f 100644
--- a/src/or/circuitstats.h
+++ b/src/or/circuitstats.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index ded78550f2..c0612039be 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -10,6 +10,7 @@
**/
#include "or.h"
+#include "addressmap.h"
#include "channel.h"
#include "circuitbuild.h"
#include "circuitlist.h"
@@ -175,6 +176,13 @@ circuit_is_better(const origin_circuit_t *oa, const origin_circuit_t *ob,
const uint8_t purpose = ENTRY_TO_CONN(conn)->purpose;
int a_bits, b_bits;
+ /* If one of the circuits was allowed to live due to relaxing its timeout,
+ * it is definitely worse (it's probably a much slower path). */
+ if (oa->relaxed_timeout && !ob->relaxed_timeout)
+ return 0; /* ob is better. It's not relaxed. */
+ if (!oa->relaxed_timeout && ob->relaxed_timeout)
+ return 1; /* oa is better. It's not relaxed. */
+
switch (purpose) {
case CIRCUIT_PURPOSE_C_GENERAL:
/* if it's used but less dirty it's best;
@@ -186,7 +194,7 @@ circuit_is_better(const origin_circuit_t *oa, const origin_circuit_t *ob,
return 1;
} else {
if (a->timestamp_dirty ||
- timercmp(&a->timestamp_created, &b->timestamp_created, >))
+ timercmp(&a->timestamp_began, &b->timestamp_began, >))
return 1;
if (ob->build_state->is_internal)
/* XXX023 what the heck is this internal thing doing here. I
@@ -272,17 +280,19 @@ circuit_get_best(const entry_connection_t *conn,
if (!CIRCUIT_IS_ORIGIN(circ))
continue;
origin_circ = TO_ORIGIN_CIRCUIT(circ);
- if (!circuit_is_acceptable(origin_circ,conn,must_be_open,purpose,
- need_uptime,need_internal,now.tv_sec))
- continue;
+ /* Log an info message if we're going to launch a new intro circ in
+ * parallel */
if (purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT &&
- !must_be_open && circ->state != CIRCUIT_STATE_OPEN &&
- tv_mdiff(&now, &circ->timestamp_created) > circ_times.timeout_ms) {
- intro_going_on_but_too_old = 1;
- continue;
+ !must_be_open && origin_circ->hs_circ_has_timed_out) {
+ intro_going_on_but_too_old = 1;
+ continue;
}
+ if (!circuit_is_acceptable(origin_circ,conn,must_be_open,purpose,
+ need_uptime,need_internal,now.tv_sec))
+ continue;
+
/* now this is an acceptable circ to hand back. but that doesn't
* mean it's the *best* circ to hand back. try to decide.
*/
@@ -359,13 +369,38 @@ circuit_expire_building(void)
* circuit_build_times_get_initial_timeout() if we haven't computed
* custom timeouts yet */
struct timeval general_cutoff, begindir_cutoff, fourhop_cutoff,
- cannibalize_cutoff, close_cutoff, extremely_old_cutoff,
- hs_extremely_old_cutoff;
+ close_cutoff, extremely_old_cutoff, hs_extremely_old_cutoff,
+ cannibalized_cutoff, c_intro_cutoff, s_intro_cutoff, stream_cutoff;
const or_options_t *options = get_options();
struct timeval now;
cpath_build_state_t *build_state;
+ int any_opened_circs = 0;
tor_gettimeofday(&now);
+
+ /* Check to see if we have any opened circuits. If we don't,
+ * we want to be more lenient with timeouts, in case the
+ * user has relocated and/or changed network connections.
+ * See bug #3443. */
+ while (next_circ) {
+ if (!CIRCUIT_IS_ORIGIN(next_circ) || /* didn't originate here */
+ next_circ->marked_for_close) { /* don't mess with marked circs */
+ next_circ = next_circ->next;
+ continue;
+ }
+
+ if (TO_ORIGIN_CIRCUIT(next_circ)->has_opened &&
+ next_circ->state == CIRCUIT_STATE_OPEN &&
+ TO_ORIGIN_CIRCUIT(next_circ)->build_state &&
+ TO_ORIGIN_CIRCUIT(next_circ)->build_state->desired_path_len
+ == DEFAULT_ROUTE_LEN) {
+ any_opened_circs = 1;
+ break;
+ }
+ next_circ = next_circ->next;
+ }
+ next_circ = global_circuitlist;
+
#define SET_CUTOFF(target, msec) do { \
long ms = tor_lround(msec); \
struct timeval diff; \
@@ -374,10 +409,60 @@ circuit_expire_building(void)
timersub(&now, &diff, &target); \
} while (0)
+ /**
+ * Because circuit build timeout is calculated only based on 3 hop
+ * general purpose circuit construction, we need to scale the timeout
+ * to make it properly apply to longer circuits, and circuits of
+ * certain usage types. The following diagram illustrates how we
+ * derive the scaling below. In short, we calculate the number
+ * of times our telescoping-based circuit construction causes cells
+ * to traverse each link for the circuit purpose types in question,
+ * and then assume each link is equivalent.
+ *
+ * OP --a--> A --b--> B --c--> C
+ * OP --a--> A --b--> B --c--> C --d--> D
+ *
+ * Let h = a = b = c = d
+ *
+ * Three hops (general_cutoff)
+ * RTTs = 3a + 2b + c
+ * RTTs = 6h
+ * Cannibalized:
+ * RTTs = a+b+c+d
+ * RTTs = 4h
+ * Four hops:
+ * RTTs = 4a + 3b + 2c + d
+ * RTTs = 10h
+ * Client INTRODUCE1+ACK: // XXX: correct?
+ * RTTs = 5a + 4b + 3c + 2d
+ * RTTs = 14h
+ * Server intro:
+ * RTTs = 4a + 3b + 2c
+ * RTTs = 9h
+ */
SET_CUTOFF(general_cutoff, circ_times.timeout_ms);
SET_CUTOFF(begindir_cutoff, circ_times.timeout_ms);
- SET_CUTOFF(fourhop_cutoff, circ_times.timeout_ms * (4/3.0));
- SET_CUTOFF(cannibalize_cutoff, circ_times.timeout_ms / 2.0);
+
+ /* > 3hop circs seem to have a 1.0 second delay on their cannibalized
+ * 4th hop. */
+ SET_CUTOFF(fourhop_cutoff, circ_times.timeout_ms * (10/6.0) + 1000);
+
+ /* CIRCUIT_PURPOSE_C_ESTABLISH_REND behaves more like a RELAY cell.
+ * Use the stream cutoff (more or less). */
+ SET_CUTOFF(stream_cutoff, MAX(options->CircuitStreamTimeout,15)*1000 + 1000);
+
+ /* Be lenient with cannibalized circs. They already survived the official
+ * CBT, and they're usually not performance-critical. */
+ SET_CUTOFF(cannibalized_cutoff,
+ MAX(circ_times.close_ms*(4/6.0),
+ options->CircuitStreamTimeout * 1000) + 1000);
+
+ /* Intro circs have an extra round trip (and are also 4 hops long) */
+ SET_CUTOFF(c_intro_cutoff, circ_times.timeout_ms * (14/6.0) + 1000);
+
+ /* Server intro circs have an extra round trip */
+ SET_CUTOFF(s_intro_cutoff, circ_times.timeout_ms * (9/6.0) + 1000);
+
SET_CUTOFF(close_cutoff, circ_times.close_ms);
SET_CUTOFF(extremely_old_cutoff, circ_times.close_ms*2 + 1000);
@@ -390,28 +475,81 @@ circuit_expire_building(void)
victim = next_circ;
next_circ = next_circ->next;
if (!CIRCUIT_IS_ORIGIN(victim) || /* didn't originate here */
- victim->marked_for_close) /* don't mess with marked circs */
+ victim->marked_for_close) /* don't mess with marked circs */
+ continue;
+
+ /* If we haven't yet started the first hop, it means we don't have
+ * any orconns available, and thus have not started counting time yet
+ * for this circuit. See circuit_deliver_create_cell() and uses of
+ * timestamp_began.
+ *
+ * Continue to wait in this case. The ORConn should timeout
+ * independently and kill us then.
+ */
+ if (TO_ORIGIN_CIRCUIT(victim)->cpath->state == CPATH_STATE_CLOSED) {
continue;
+ }
build_state = TO_ORIGIN_CIRCUIT(victim)->build_state;
if (build_state && build_state->onehop_tunnel)
cutoff = begindir_cutoff;
- else if (build_state && build_state->desired_path_len == 4
- && !TO_ORIGIN_CIRCUIT(victim)->has_opened)
- cutoff = fourhop_cutoff;
- else if (TO_ORIGIN_CIRCUIT(victim)->has_opened)
- cutoff = cannibalize_cutoff;
else if (victim->purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT)
cutoff = close_cutoff;
+ else if (victim->purpose == CIRCUIT_PURPOSE_C_INTRODUCING ||
+ victim->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT)
+ cutoff = c_intro_cutoff;
+ else if (victim->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO)
+ cutoff = s_intro_cutoff;
+ else if (victim->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND)
+ cutoff = stream_cutoff;
+ else if (victim->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING)
+ cutoff = close_cutoff;
+ else if (TO_ORIGIN_CIRCUIT(victim)->has_opened &&
+ victim->state != CIRCUIT_STATE_OPEN)
+ cutoff = cannibalized_cutoff;
+ else if (build_state && build_state->desired_path_len >= 4)
+ cutoff = fourhop_cutoff;
else
cutoff = general_cutoff;
if (TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out)
cutoff = hs_extremely_old_cutoff;
- if (timercmp(&victim->timestamp_created, &cutoff, >))
+ if (timercmp(&victim->timestamp_began, &cutoff, >))
continue; /* it's still young, leave it alone */
+ if (!any_opened_circs) {
+ /* It's still young enough that we wouldn't close it, right? */
+ if (timercmp(&victim->timestamp_began, &close_cutoff, >)) {
+ if (!TO_ORIGIN_CIRCUIT(victim)->relaxed_timeout) {
+ int first_hop_succeeded = TO_ORIGIN_CIRCUIT(victim)->cpath->state
+ == CPATH_STATE_OPEN;
+ log_info(LD_CIRC,
+ "No circuits are opened. Relaxing timeout for "
+ "a circuit with channel state %s. %d guards are live.",
+ channel_state_to_string(victim->n_chan->state),
+ num_live_entry_guards(0));
+
+ /* We count the timeout here for CBT, because technically this
+ * was a timeout, and the timeout value needs to reset if we
+ * see enough of them. Note this means we also need to avoid
+ * double-counting below, too. */
+ circuit_build_times_count_timeout(&circ_times, first_hop_succeeded);
+ TO_ORIGIN_CIRCUIT(victim)->relaxed_timeout = 1;
+ }
+ continue;
+ } else {
+ static ratelim_t relax_timeout_limit = RATELIM_INIT(3600);
+ log_fn_ratelim(&relax_timeout_limit, LOG_NOTICE, LD_CIRC,
+ "No circuits are opened. Relaxed timeout for "
+ "a circuit with channel state %s to %ldms. "
+ "However, it appears the circuit has timed out anyway. "
+ "%d guards are live.",
+ channel_state_to_string(victim->n_chan->state),
+ (long)circ_times.close_ms, num_live_entry_guards(0));
+ }
+ }
+
#if 0
/* some debug logs, to help track bugs */
if (victim->purpose >= CIRCUIT_PURPOSE_C_INTRODUCING &&
@@ -439,8 +577,6 @@ circuit_expire_building(void)
default: /* most open circuits can be left alone. */
continue; /* yes, continue inside a switch refers to the nearest
* enclosing loop. C is smart. */
- case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
- case CIRCUIT_PURPOSE_C_INTRODUCING:
case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
break; /* too old, need to die */
case CIRCUIT_PURPOSE_C_REND_READY:
@@ -452,6 +588,19 @@ circuit_expire_building(void)
victim->timestamp_dirty > cutoff.tv_sec)
continue;
break;
+ case CIRCUIT_PURPOSE_PATH_BIAS_TESTING:
+ /* Open path bias testing circuits are given a long
+ * time to complete the test, but not forever */
+ TO_ORIGIN_CIRCUIT(victim)->path_state = PATH_STATE_USE_FAILED;
+ break;
+ case CIRCUIT_PURPOSE_C_INTRODUCING:
+ /* We keep old introducing circuits around for
+ * a while in parallel, and they can end up "opened".
+ * We decide below if we're going to mark them timed
+ * out and eventually close them.
+ */
+ break;
+ case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED:
case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT:
/* rend and intro circs become dirty each time they
@@ -488,9 +637,12 @@ circuit_expire_building(void)
/* Record this failure to check for too many timeouts
* in a row. This function does not record a time value yet
* (we do that later); it only counts the fact that we did
- * have a timeout. */
- circuit_build_times_count_timeout(&circ_times,
- first_hop_succeeded);
+ * have a timeout. We also want to avoid double-counting
+ * already "relaxed" circuits, which are counted above. */
+ if (!TO_ORIGIN_CIRCUIT(victim)->relaxed_timeout) {
+ circuit_build_times_count_timeout(&circ_times,
+ first_hop_succeeded);
+ }
continue;
}
@@ -499,16 +651,16 @@ circuit_expire_building(void)
* it off at, we probably had a suspend event along this codepath,
* and we should discard the value.
*/
- if (timercmp(&victim->timestamp_created, &extremely_old_cutoff, <)) {
+ if (timercmp(&victim->timestamp_began, &extremely_old_cutoff, <)) {
log_notice(LD_CIRC,
"Extremely large value for circuit build timeout: %lds. "
"Assuming clock jump. Purpose %d (%s)",
- (long)(now.tv_sec - victim->timestamp_created.tv_sec),
+ (long)(now.tv_sec - victim->timestamp_began.tv_sec),
victim->purpose,
circuit_purpose_to_string(victim->purpose));
} else if (circuit_build_times_count_close(&circ_times,
first_hop_succeeded,
- victim->timestamp_created.tv_sec)) {
+ victim->timestamp_began.tv_sec)) {
circuit_build_times_set_timeout(&circ_times);
}
}
@@ -537,6 +689,9 @@ circuit_expire_building(void)
if (TO_ORIGIN_CIRCUIT(victim)->build_state->pending_final_cpath ==
NULL)
break;
+ /* fallthrough! */
+ case CIRCUIT_PURPOSE_C_INTRODUCING:
+ /* connection_ap_handshake_attach_circuit() will relaunch for us */
case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT:
case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED:
/* If we have reached this line, we want to spare the circ for now. */
@@ -569,21 +724,31 @@ circuit_expire_building(void)
}
if (victim->n_chan)
- log_info(LD_CIRC,"Abandoning circ %s:%d (state %d:%s, purpose %d)",
+ log_info(LD_CIRC,
+ "Abandoning circ %u %s:%d (state %d,%d:%s, purpose %d, "
+ "len %d)", TO_ORIGIN_CIRCUIT(victim)->global_identifier,
channel_get_canonical_remote_descr(victim->n_chan),
victim->n_circ_id,
+ TO_ORIGIN_CIRCUIT(victim)->has_opened,
victim->state, circuit_state_to_string(victim->state),
- victim->purpose);
+ victim->purpose,
+ TO_ORIGIN_CIRCUIT(victim)->build_state->desired_path_len);
else
- log_info(LD_CIRC,"Abandoning circ %d (state %d:%s, purpose %d)",
- victim->n_circ_id, victim->state,
- circuit_state_to_string(victim->state), victim->purpose);
+ log_info(LD_CIRC,
+ "Abandoning circ %u %d (state %d,%d:%s, purpose %d, len %d)",
+ TO_ORIGIN_CIRCUIT(victim)->global_identifier,
+ victim->n_circ_id, TO_ORIGIN_CIRCUIT(victim)->has_opened,
+ victim->state,
+ circuit_state_to_string(victim->state), victim->purpose,
+ TO_ORIGIN_CIRCUIT(victim)->build_state->desired_path_len);
circuit_log_path(LOG_INFO,LD_CIRC,TO_ORIGIN_CIRCUIT(victim));
if (victim->purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT)
circuit_mark_for_close(victim, END_CIRC_REASON_MEASUREMENT_EXPIRED);
else
circuit_mark_for_close(victim, END_CIRC_REASON_TIMEOUT);
+
+ pathbias_count_timeout(TO_ORIGIN_CIRCUIT(victim));
}
}
@@ -623,7 +788,8 @@ circuit_stream_is_being_handled(entry_connection_t *conn,
const node_t *exitnode;
int num=0;
time_t now = time(NULL);
- int need_uptime = smartlist_string_num_isin(get_options()->LongLivedPorts,
+ int need_uptime = smartlist_contains_int_as_string(
+ get_options()->LongLivedPorts,
conn ? conn->socks_request->port : port);
for (circ=global_circuitlist;circ;circ = circ->next) {
@@ -790,7 +956,7 @@ circuit_build_needed_circs(time_t now)
circ = circuit_get_youngest_clean_open(CIRCUIT_PURPOSE_C_GENERAL);
if (get_options()->RunTesting &&
circ &&
- circ->timestamp_created.tv_sec + TESTING_CIRCUIT_INTERVAL < now) {
+ circ->timestamp_began.tv_sec + TESTING_CIRCUIT_INTERVAL < now) {
log_fn(LOG_INFO,"Creating a new testing circuit.");
circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, 0);
}
@@ -908,9 +1074,12 @@ circuit_expire_old_circuits_clientside(void)
"purpose %d)",
circ->n_circ_id, (long)(now.tv_sec - circ->timestamp_dirty),
circ->purpose);
- circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED);
+ /* Don't do this magic for testing circuits. Their death is governed
+ * by circuit_expire_building */
+ if (circ->purpose != CIRCUIT_PURPOSE_PATH_BIAS_TESTING)
+ circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED);
} else if (!circ->timestamp_dirty && circ->state == CIRCUIT_STATE_OPEN) {
- if (timercmp(&circ->timestamp_created, &cutoff, <)) {
+ if (timercmp(&circ->timestamp_began, &cutoff, <)) {
if (circ->purpose == CIRCUIT_PURPOSE_C_GENERAL ||
circ->purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT ||
circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
@@ -920,7 +1089,7 @@ circuit_expire_old_circuits_clientside(void)
circ->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND) {
log_debug(LD_CIRC,
"Closing circuit that has been unused for %ld msec.",
- tv_mdiff(&circ->timestamp_created, &now));
+ tv_mdiff(&circ->timestamp_began, &now));
circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED);
} else if (!TO_ORIGIN_CIRCUIT(circ)->is_ancient) {
/* Server-side rend joined circuits can end up really old, because
@@ -934,7 +1103,7 @@ circuit_expire_old_circuits_clientside(void)
"Ancient non-dirty circuit %d is still around after "
"%ld milliseconds. Purpose: %d (%s)",
TO_ORIGIN_CIRCUIT(circ)->global_identifier,
- tv_mdiff(&circ->timestamp_created, &now),
+ tv_mdiff(&circ->timestamp_began, &now),
circ->purpose,
circuit_purpose_to_string(circ->purpose));
TO_ORIGIN_CIRCUIT(circ)->is_ancient = 1;
@@ -1315,20 +1484,45 @@ circuit_launch_by_extend_info(uint8_t purpose,
circ = circuit_find_to_cannibalize(purpose, extend_info, flags);
if (circ) {
uint8_t old_purpose = circ->base_.purpose;
- struct timeval old_timestamp_created;
+ struct timeval old_timestamp_began;
log_info(LD_CIRC,"Cannibalizing circ '%s' for purpose %d (%s)",
build_state_get_exit_nickname(circ->build_state), purpose,
circuit_purpose_to_string(purpose));
+ if ((purpose == CIRCUIT_PURPOSE_S_CONNECT_REND ||
+ purpose == CIRCUIT_PURPOSE_C_INTRODUCING) &&
+ circ->path_state == PATH_STATE_BUILD_SUCCEEDED) {
+ /* Path bias: Cannibalized rends pre-emptively count as a
+ * successfully built but unused closed circuit. We don't
+ * wait until the extend (or the close) because the rend
+ * point could be malicious.
+ *
+ * Same deal goes for client side introductions. Clients
+ * can be manipulated to connect repeatedly to them
+ * (especially web clients).
+ *
+ * If we decide to probe the initial portion of these circs,
+ * (up to the adversary's final hop), we need to remove this,
+ * or somehow mark the circuit with a special path state.
+ */
+
+ /* This must be called before the purpose change */
+ pathbias_check_close(circ, END_CIRC_REASON_FINISHED);
+ }
+
circuit_change_purpose(TO_CIRCUIT(circ), purpose);
- /* reset the birth date of this circ, else expire_building
+ /* Reset the start date of this circ, else expire_building
* will see it and think it's been trying to build since it
- * began. */
- tor_gettimeofday(&circ->base_.timestamp_created);
+ * began.
+ *
+ * Technically, the code should reset this when the
+ * create cell is finally sent, but we're close enough
+ * here. */
+ tor_gettimeofday(&circ->base_.timestamp_began);
control_event_circuit_cannibalized(circ, old_purpose,
- &old_timestamp_created);
+ &old_timestamp_began);
switch (purpose) {
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
@@ -1417,7 +1611,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
want_onehop = conn->want_onehop;
need_uptime = !conn->want_onehop && !conn->use_begindir &&
- smartlist_string_num_isin(options->LongLivedPorts,
+ smartlist_contains_int_as_string(options->LongLivedPorts,
conn->socks_request->port);
if (desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL)
@@ -1576,8 +1770,8 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
return -1;
}
extend_info = extend_info_new(conn->chosen_exit_name+1,
- digest, NULL, &addr,
- conn->socks_request->port);
+ digest, NULL, NULL, &addr,
+ conn->socks_request->port);
} else {
/* We will need an onion key for the router, and we
* don't have one. Refuse or relax requirements. */
@@ -1832,6 +2026,8 @@ connection_ap_handshake_attach_chosen_circuit(entry_connection_t *conn,
if (!circ->base_.timestamp_dirty)
circ->base_.timestamp_dirty = time(NULL);
+ pathbias_count_use_attempt(circ);
+
link_apconn_to_circ(conn, circ, cpath);
tor_assert(conn->socks_request);
if (conn->socks_request->command == SOCKS_COMMAND_CONNECT) {
@@ -1958,6 +2154,11 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
* feasibility, at this point.
*/
rendcirc->base_.timestamp_dirty = time(NULL);
+
+ /* We've also attempted to use them. If they fail, we need to
+ * probe them for path bias */
+ pathbias_count_use_attempt(rendcirc);
+
link_apconn_to_circ(conn, rendcirc, NULL);
if (connection_ap_handshake_send_begin(conn) < 0)
return 0; /* already marked, let them fade away */
@@ -1980,28 +2181,12 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
if (retval > 0) {
/* one has already sent the intro. keep waiting. */
- circuit_t *c = NULL;
tor_assert(introcirc);
log_info(LD_REND, "Intro circ %d present and awaiting ack (rend %d). "
"Stalling. (stream %d sec old)",
introcirc->base_.n_circ_id,
rendcirc ? rendcirc->base_.n_circ_id : 0,
conn_age);
- /* abort parallel intro circs, if any */
- for (c = global_circuitlist; c; c = c->next) {
- if (c->purpose == CIRCUIT_PURPOSE_C_INTRODUCING &&
- !c->marked_for_close && CIRCUIT_IS_ORIGIN(c)) {
- origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(c);
- if (oc->rend_data &&
- !rend_cmp_service_ids(
- ENTRY_TO_EDGE_CONN(conn)->rend_data->onion_address,
- oc->rend_data->onion_address)) {
- log_info(LD_REND|LD_CIRC, "Closing introduction circuit that we "
- "built in parallel.");
- circuit_mark_for_close(c, END_CIRC_REASON_TIMEOUT);
- }
- }
- }
return 0;
}
@@ -2025,6 +2210,10 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
case 0: /* success */
rendcirc->base_.timestamp_dirty = time(NULL);
introcirc->base_.timestamp_dirty = time(NULL);
+
+ pathbias_count_use_attempt(introcirc);
+ pathbias_count_use_attempt(rendcirc);
+
assert_circuit_ok(TO_CIRCUIT(rendcirc));
assert_circuit_ok(TO_CIRCUIT(introcirc));
return 0;
diff --git a/src/or/circuituse.h b/src/or/circuituse.h
index e8760c22d3..d4d68aad92 100644
--- a/src/or/circuituse.h
+++ b/src/or/circuituse.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/command.c b/src/or/command.c
index 4007cd6001..dfe4f65916 100644
--- a/src/or/command.c
+++ b/src/or/command.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -133,11 +133,13 @@ command_process_cell(channel_t *chan, cell_t *cell)
switch (cell->command) {
case CELL_CREATE:
case CELL_CREATE_FAST:
+ case CELL_CREATE2:
++stats_n_create_cells_processed;
PROCESS_CELL(create, cell, chan);
break;
case CELL_CREATED:
case CELL_CREATED_FAST:
+ case CELL_CREATED2:
++stats_n_created_cells_processed;
PROCESS_CELL(created, cell, chan);
break;
@@ -187,6 +189,7 @@ command_process_create_cell(cell_t *cell, channel_t *chan)
or_circuit_t *circ;
const or_options_t *options = get_options();
int id_is_high;
+ create_cell_t *create_cell;
tor_assert(cell);
tor_assert(chan);
@@ -218,6 +221,14 @@ command_process_create_cell(cell_t *cell, channel_t *chan)
return;
}
+ if (cell->circ_id == 0) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Received a create cell (type %d) from %s with zero circID; "
+ " ignoring.", (int)cell->command,
+ channel_get_actual_remote_descr(chan));
+ return;
+ }
+
/* If the high bit of the circuit ID is not as expected, close the
* circ. */
if (chan->wide_circ_ids)
@@ -255,12 +266,18 @@ command_process_create_cell(cell_t *cell, channel_t *chan)
circ = or_circuit_new(cell->circ_id, chan);
circ->base_.purpose = CIRCUIT_PURPOSE_OR;
circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_ONIONSKIN_PENDING);
- if (cell->command == CELL_CREATE) {
- char *onionskin = tor_malloc(ONIONSKIN_CHALLENGE_LEN);
- memcpy(onionskin, cell->payload, ONIONSKIN_CHALLENGE_LEN);
+ create_cell = tor_malloc_zero(sizeof(create_cell_t));
+ if (create_cell_parse(create_cell, cell) < 0) {
+ tor_free(create_cell);
+ log_fn(LOG_PROTOCOL_WARN, LD_OR,
+ "Bogus/unrecognized create cell; closing.");
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
+ return;
+ }
+ if (create_cell->handshake_type != ONION_HANDSHAKE_TYPE_FAST) {
/* hand it off to the cpuworkers, and then return. */
- if (assign_onionskin_to_cpuworker(NULL, circ, onionskin) < 0) {
+ if (assign_onionskin_to_cpuworker(NULL, circ, create_cell) < 0) {
log_debug(LD_GENERAL,"Failed to hand off onionskin. Closing.");
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT);
return;
@@ -269,26 +286,40 @@ command_process_create_cell(cell_t *cell, channel_t *chan)
} else {
/* This is a CREATE_FAST cell; we can handle it immediately without using
* a CPU worker. */
- char keys[CPATH_KEY_MATERIAL_LEN];
- char reply[DIGEST_LEN*2];
-
- tor_assert(cell->command == CELL_CREATE_FAST);
+ uint8_t keys[CPATH_KEY_MATERIAL_LEN];
+ uint8_t rend_circ_nonce[DIGEST_LEN];
+ int len;
+ created_cell_t created_cell;
/* Make sure we never try to use the OR connection on which we
* received this cell to satisfy an EXTEND request, */
channel_mark_client(chan);
- if (fast_server_handshake(cell->payload, (uint8_t*)reply,
- (uint8_t*)keys, sizeof(keys))<0) {
+ memset(&created_cell, 0, sizeof(created_cell));
+ len = onion_skin_server_handshake(ONION_HANDSHAKE_TYPE_FAST,
+ create_cell->onionskin,
+ create_cell->handshake_len,
+ NULL,
+ created_cell.reply,
+ keys, CPATH_KEY_MATERIAL_LEN,
+ rend_circ_nonce);
+ tor_free(create_cell);
+ if (len < 0) {
log_warn(LD_OR,"Failed to generate key material. Closing.");
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
+ tor_free(create_cell);
return;
}
- if (onionskin_answer(circ, CELL_CREATED_FAST, reply, keys)<0) {
+ created_cell.cell_type = CELL_CREATED_FAST;
+ created_cell.handshake_len = len;
+
+ if (onionskin_answer(circ, &created_cell,
+ (const char *)keys, rend_circ_nonce)<0) {
log_warn(LD_OR,"Failed to reply to CREATE_FAST cell. Closing.");
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
return;
}
+ memwipe(keys, 0, sizeof(keys));
}
}
@@ -304,6 +335,7 @@ static void
command_process_created_cell(cell_t *cell, channel_t *chan)
{
circuit_t *circ;
+ extended_cell_t extended_cell;
circ = circuit_get_by_circid_channel(cell->circ_id, chan);
@@ -321,12 +353,18 @@ command_process_created_cell(cell_t *cell, channel_t *chan)
return;
}
+ if (created_cell_parse(&extended_cell.created_cell, cell) < 0) {
+ log_fn(LOG_PROTOCOL_WARN, LD_OR, "Unparseable created cell.");
+ circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL);
+ return;
+ }
+
if (CIRCUIT_IS_ORIGIN(circ)) { /* we're the OP. Handshake this. */
origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ);
int err_reason = 0;
log_debug(LD_OR,"at OP. Finishing handshake.");
- if ((err_reason = circuit_finish_handshake(origin_circ, cell->command,
- cell->payload)) < 0) {
+ if ((err_reason = circuit_finish_handshake(origin_circ,
+ &extended_cell.created_cell)) < 0) {
log_warn(LD_OR,"circuit_finish_handshake failed.");
circuit_mark_for_close(circ, -err_reason);
return;
@@ -339,11 +377,24 @@ command_process_created_cell(cell_t *cell, channel_t *chan)
return;
}
} else { /* pack it into an extended relay cell, and send it. */
+ uint8_t command=0;
+ uint16_t len=0;
+ uint8_t payload[RELAY_PAYLOAD_SIZE];
log_debug(LD_OR,
"Converting created cell to extended relay cell, sending.");
- relay_send_command_from_edge(0, circ, RELAY_COMMAND_EXTENDED,
- (char*)cell->payload, ONIONSKIN_REPLY_LEN,
- NULL);
+ memset(payload, 0, sizeof(payload));
+ if (extended_cell.created_cell.cell_type == CELL_CREATED2)
+ extended_cell.cell_type = RELAY_COMMAND_EXTENDED2;
+ else
+ extended_cell.cell_type = RELAY_COMMAND_EXTENDED;
+ if (extended_cell_format(&command, &len, payload, &extended_cell) < 0) {
+ log_fn(LOG_PROTOCOL_WARN, LD_OR, "Can't format extended cell.");
+ circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL);
+ return;
+ }
+
+ relay_send_command_from_edge(0, circ, command,
+ (const char*)payload, len, NULL);
}
}
diff --git a/src/or/command.h b/src/or/command.h
index 375d704561..913f46a5cd 100644
--- a/src/or/command.h
+++ b/src/or/command.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/config.c b/src/or/config.c
index bf32fae0e2..f88842624c 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -12,6 +12,7 @@
#define CONFIG_PRIVATE
#include "or.h"
+#include "addressmap.h"
#include "channel.h"
#include "circuitbuild.h"
#include "circuitlist.h"
@@ -80,6 +81,7 @@ static config_abbrev_t option_abbrevs_[] = {
{ "BandwidthRateBytes", "BandwidthRate", 0, 0},
{ "BandwidthBurstBytes", "BandwidthBurst", 0, 0},
{ "DirFetchPostPeriod", "StatusFetchPeriod", 0, 0},
+ { "DirServer", "DirAuthority", 0, 0}, /* XXXX024 later, make this warn? */
{ "MaxConn", "ConnLimit", 0, 1},
{ "ORBindAddress", "ORListenAddress", 0, 0},
{ "DirBindAddress", "DirListenAddress", 0, 0},
@@ -96,6 +98,7 @@ static config_abbrev_t option_abbrevs_[] = {
{ "HashedControlPassword", "__HashedControlSessionPassword", 1, 0},
{ "StrictEntryNodes", "StrictNodes", 0, 1},
{ "StrictExitNodes", "StrictNodes", 0, 1},
+ { "VirtualAddrNetwork", "VirtualAddrNetworkIPv4", 0, 0},
{ "_UseFilteringSSLBufferevents", "UseFilteringSSLBufferevents", 0, 1},
{ NULL, NULL, 0, 0},
};
@@ -205,7 +208,8 @@ static config_var_t option_vars_[] = {
OBSOLETE("DirRecordUsageRetainIPs"),
OBSOLETE("DirRecordUsageSaveInterval"),
V(DirReqStatistics, BOOL, "1"),
- VAR("DirServer", LINELIST, DirServers, NULL),
+ VAR("DirAuthority", LINELIST, DirAuthorities, NULL),
+ V(DirAuthorityFallbackRate, DOUBLE, "1.0"),
V(DisableAllSwap, BOOL, "0"),
V(DisableDebuggerAttachment, BOOL, "1"),
V(DisableIOCP, BOOL, "1"),
@@ -226,13 +230,9 @@ static config_var_t option_vars_[] = {
V(ExitPortStatistics, BOOL, "0"),
V(ExtendAllowPrivateAddresses, BOOL, "0"),
V(ExtraInfoStatistics, BOOL, "1"),
+ V(FallbackDir, LINELIST, NULL),
-#if defined (WINCE)
- V(FallbackNetworkstatusFile, FILENAME, "fallback-consensus"),
-#else
- V(FallbackNetworkstatusFile, FILENAME,
- SHARE_DATADIR PATH_SEPARATOR "tor" PATH_SEPARATOR "fallback-consensus"),
-#endif
+ OBSOLETE("FallbackNetworkstatusFile"),
V(FascistFirewall, BOOL, "0"),
V(FirewallPorts, CSV, ""),
V(FastFirstHopPK, BOOL, "1"),
@@ -242,6 +242,7 @@ static config_var_t option_vars_[] = {
V(FetchHidServDescriptors, BOOL, "1"),
V(FetchUselessDescriptors, BOOL, "0"),
V(FetchV2Networkstatus, BOOL, "0"),
+ V(GeoIPExcludeUnknown, AUTOBOOL, "auto"),
#ifdef _WIN32
V(GeoIPFile, FILENAME, "<default>"),
V(GeoIPv6File, FILENAME, "<default>"),
@@ -275,7 +276,9 @@ static config_var_t option_vars_[] = {
V(HTTPProxyAuthenticator, STRING, NULL),
V(HTTPSProxy, STRING, NULL),
V(HTTPSProxyAuthenticator, STRING, NULL),
+ V(IPv6Exit, BOOL, "0"),
VAR("ServerTransportPlugin", LINELIST, ServerTransportPlugin, NULL),
+ V(ServerTransportListenAddr, LINELIST, NULL),
V(Socks4Proxy, STRING, NULL),
V(Socks5Proxy, STRING, NULL),
V(Socks5ProxyUsername, STRING, NULL),
@@ -294,7 +297,8 @@ static config_var_t option_vars_[] = {
V(MaxAdvertisedBandwidth, MEMUNIT, "1 GB"),
V(MaxCircuitDirtiness, INTERVAL, "10 minutes"),
V(MaxClientCircuitsPending, UINT, "32"),
- V(MaxOnionsPending, UINT, "100"),
+ OBSOLETE("MaxOnionsPending"),
+ V(MaxOnionQueueDelay, MSEC_INTERVAL, "1750 msec"),
OBSOLETE("MonthlyAccountingStart"),
V(MyFamily, STRING, NULL),
V(NewCircuitPeriod, INTERVAL, "30 seconds"),
@@ -306,17 +310,29 @@ static config_var_t option_vars_[] = {
OBSOLETE("NoPublish"),
VAR("NodeFamily", LINELIST, NodeFamilies, NULL),
V(NumCPUs, UINT, "0"),
+ V(NumDirectoryGuards, UINT, "3"),
V(NumEntryGuards, UINT, "3"),
V(ORListenAddress, LINELIST, NULL),
VPORT(ORPort, LINELIST, NULL),
V(OutboundBindAddress, LINELIST, NULL),
+ OBSOLETE("PathBiasDisableRate"),
V(PathBiasCircThreshold, INT, "-1"),
V(PathBiasNoticeRate, DOUBLE, "-1"),
- V(PathBiasDisableRate, DOUBLE, "-1"),
+ V(PathBiasWarnRate, DOUBLE, "-1"),
+ V(PathBiasExtremeRate, DOUBLE, "-1"),
V(PathBiasScaleThreshold, INT, "-1"),
- V(PathBiasScaleFactor, INT, "-1"),
+ OBSOLETE("PathBiasScaleFactor"),
+ OBSOLETE("PathBiasMultFactor"),
+ V(PathBiasDropGuards, AUTOBOOL, "0"),
+ OBSOLETE("PathBiasUseCloseCounts"),
+
+ V(PathBiasUseThreshold, INT, "-1"),
+ V(PathBiasNoticeUseRate, DOUBLE, "-1"),
+ V(PathBiasExtremeUseRate, DOUBLE, "-1"),
+ V(PathBiasScaleUseThreshold, INT, "-1"),
+ V(PathsNeededToBuildCircuits, DOUBLE, "-1"),
OBSOLETE("PathlenCoinWeight"),
V(PerConnBWBurst, MEMUNIT, "0"),
V(PerConnBWRate, MEMUNIT, "0"),
@@ -370,6 +386,7 @@ static config_var_t option_vars_[] = {
OBSOLETE("TestVia"),
V(TokenBucketRefillInterval, MSEC_INTERVAL, "100 msec"),
V(Tor2webMode, BOOL, "0"),
+ V(TLSECGroup, STRING, NULL),
V(TrackHostExits, CSV, NULL),
V(TrackHostExitsExpire, INTERVAL, "30 minutes"),
OBSOLETE("TrafficShaping"),
@@ -379,7 +396,9 @@ static config_var_t option_vars_[] = {
V(UpdateBridgesFromAuthority, BOOL, "0"),
V(UseBridges, BOOL, "0"),
V(UseEntryGuards, BOOL, "1"),
+ V(UseEntryGuardsAsDirGuards, BOOL, "1"),
V(UseMicrodescriptors, AUTOBOOL, "auto"),
+ V(UseNTorHandshake, AUTOBOOL, "auto"),
V(User, STRING, NULL),
V(UserspaceIOCPBuffers, BOOL, "0"),
VAR("V1AuthoritativeDirectory",BOOL, V1AuthoritativeDir, "0"),
@@ -395,7 +414,8 @@ static config_var_t option_vars_[] = {
V(V3AuthUseLegacyKey, BOOL, "0"),
V(V3BandwidthsFile, FILENAME, NULL),
VAR("VersioningAuthoritativeDirectory",BOOL,VersioningAuthoritativeDir, "0"),
- V(VirtualAddrNetwork, STRING, "127.192.0.0/10"),
+ V(VirtualAddrNetworkIPv4, STRING, "127.192.0.0/10"),
+ V(VirtualAddrNetworkIPv6, STRING, "[FE80::]/10"),
V(WarnPlaintextPorts, CSV, "23,109,110,143"),
V(UseFilteringSSLBufferevents, BOOL, "0"),
VAR("__ReloadTorrcOnSIGHUP", BOOL, ReloadTorrcOnSIGHUP, "1"),
@@ -465,9 +485,13 @@ static int parse_bridge_line(const char *line, int validate_only);
static int parse_client_transport_line(const char *line, int validate_only);
static int parse_server_transport_line(const char *line, int validate_only);
-static int parse_dir_server_line(const char *line,
+static char *get_bindaddr_from_transport_listen_line(const char *line,
+ const char *transport);
+static int parse_dir_authority_line(const char *line,
dirinfo_type_t required_type,
int validate_only);
+static int parse_dir_fallback_line(const char *line,
+ int validate_only);
static void port_cfg_free(port_cfg_t *port);
static int parse_ports(or_options_t *options, int validate_only,
char **msg_out, int *n_ports_out);
@@ -582,9 +606,12 @@ set_options(or_options_t *new_val, char **msg)
var_name, 1);
if (line) {
- for (; line; line = line->next) {
+ config_line_t *next;
+ for (; line; line = next) {
+ next = line->next;
smartlist_add(elements, line->key);
smartlist_add(elements, line->value);
+ tor_free(line);
}
} else {
smartlist_add(elements, (char*)options_format.vars[i].name);
@@ -673,7 +700,7 @@ config_free_all(void)
if (configured_ports) {
SMARTLIST_FOREACH(configured_ports,
- port_cfg_t *, p, tor_free(p));
+ port_cfg_t *, p, port_cfg_free(p));
smartlist_free(configured_ports);
configured_ports = NULL;
}
@@ -750,7 +777,7 @@ static void
add_default_trusted_dir_authorities(dirinfo_type_t type)
{
int i;
- const char *dirservers[] = {
+ const char *authorities[] = {
"moria1 orport=9101 no-v2 "
"v3ident=D586D18309DED4CD6D57C18FDB97EFA96D330566 "
"128.31.0.39:9131 9695 DFC3 5FFE B861 329B 9F1A B04C 4639 7020 CE31",
@@ -779,10 +806,27 @@ add_default_trusted_dir_authorities(dirinfo_type_t type)
"154.35.32.5:80 CF6D 0AAF B385 BE71 B8E1 11FC 5CFF 4B47 9237 33BC",
NULL
};
- for (i=0; dirservers[i]; i++) {
- if (parse_dir_server_line(dirservers[i], type, 0)<0) {
- log_err(LD_BUG, "Couldn't parse internal dirserver line %s",
- dirservers[i]);
+ for (i=0; authorities[i]; i++) {
+ if (parse_dir_authority_line(authorities[i], type, 0)<0) {
+ log_err(LD_BUG, "Couldn't parse internal DirAuthority line %s",
+ authorities[i]);
+ }
+ }
+}
+
+/** Add the default fallback directory servers into the fallback directory
+ * server list. */
+static void
+add_default_fallback_dir_servers(void)
+{
+ int i;
+ const char *fallback[] = {
+ NULL
+ };
+ for (i=0; fallback[i]; i++) {
+ if (parse_dir_fallback_line(fallback[i], 0)<0) {
+ log_err(LD_BUG, "Couldn't parse internal FallbackDir line %s",
+ fallback[i]);
}
}
}
@@ -792,28 +836,29 @@ add_default_trusted_dir_authorities(dirinfo_type_t type)
* user if we changed any dangerous ones.
*/
static int
-validate_dir_authorities(or_options_t *options, or_options_t *old_options)
+validate_dir_servers(or_options_t *options, or_options_t *old_options)
{
config_line_t *cl;
- if (options->DirServers &&
+ if (options->DirAuthorities &&
(options->AlternateDirAuthority || options->AlternateBridgeAuthority ||
options->AlternateHSAuthority)) {
log_warn(LD_CONFIG,
- "You cannot set both DirServers and Alternate*Authority.");
+ "You cannot set both DirAuthority and Alternate*Authority.");
return -1;
}
/* do we want to complain to the user about being partitionable? */
- if ((options->DirServers &&
+ if ((options->DirAuthorities &&
(!old_options ||
- !config_lines_eq(options->DirServers, old_options->DirServers))) ||
+ !config_lines_eq(options->DirAuthorities,
+ old_options->DirAuthorities))) ||
(options->AlternateDirAuthority &&
(!old_options ||
!config_lines_eq(options->AlternateDirAuthority,
old_options->AlternateDirAuthority)))) {
log_warn(LD_CONFIG,
- "You have used DirServer or AlternateDirAuthority to "
+ "You have used DirAuthority or AlternateDirAuthority to "
"specify alternate directory authorities in "
"your configuration. This is potentially dangerous: it can "
"make you look different from all other Tor users, and hurt "
@@ -824,17 +869,20 @@ validate_dir_authorities(or_options_t *options, or_options_t *old_options)
/* Now go through the four ways you can configure an alternate
* set of directory authorities, and make sure none are broken. */
- for (cl = options->DirServers; cl; cl = cl->next)
- if (parse_dir_server_line(cl->value, NO_DIRINFO, 1)<0)
+ for (cl = options->DirAuthorities; cl; cl = cl->next)
+ if (parse_dir_authority_line(cl->value, NO_DIRINFO, 1)<0)
return -1;
for (cl = options->AlternateBridgeAuthority; cl; cl = cl->next)
- if (parse_dir_server_line(cl->value, NO_DIRINFO, 1)<0)
+ if (parse_dir_authority_line(cl->value, NO_DIRINFO, 1)<0)
return -1;
for (cl = options->AlternateDirAuthority; cl; cl = cl->next)
- if (parse_dir_server_line(cl->value, NO_DIRINFO, 1)<0)
+ if (parse_dir_authority_line(cl->value, NO_DIRINFO, 1)<0)
return -1;
for (cl = options->AlternateHSAuthority; cl; cl = cl->next)
- if (parse_dir_server_line(cl->value, NO_DIRINFO, 1)<0)
+ if (parse_dir_authority_line(cl->value, NO_DIRINFO, 1)<0)
+ return -1;
+ for (cl = options->FallbackDir; cl; cl = cl->next)
+ if (parse_dir_fallback_line(cl->value, 1)<0)
return -1;
return 0;
}
@@ -843,13 +891,15 @@ validate_dir_authorities(or_options_t *options, or_options_t *old_options)
* as appropriate.
*/
static int
-consider_adding_dir_authorities(const or_options_t *options,
- const or_options_t *old_options)
+consider_adding_dir_servers(const or_options_t *options,
+ const or_options_t *old_options)
{
config_line_t *cl;
int need_to_update =
- !smartlist_len(router_get_trusted_dir_servers()) || !old_options ||
- !config_lines_eq(options->DirServers, old_options->DirServers) ||
+ !smartlist_len(router_get_trusted_dir_servers()) ||
+ !smartlist_len(router_get_fallback_dir_servers()) || !old_options ||
+ !config_lines_eq(options->DirAuthorities, old_options->DirAuthorities) ||
+ !config_lines_eq(options->FallbackDir, old_options->FallbackDir) ||
!config_lines_eq(options->AlternateBridgeAuthority,
old_options->AlternateBridgeAuthority) ||
!config_lines_eq(options->AlternateDirAuthority,
@@ -861,9 +911,9 @@ consider_adding_dir_authorities(const or_options_t *options,
return 0; /* all done */
/* Start from a clean slate. */
- clear_trusted_dir_servers();
+ clear_dir_servers();
- if (!options->DirServers) {
+ if (!options->DirAuthorities) {
/* then we may want some of the defaults */
dirinfo_type_t type = NO_DIRINFO;
if (!options->AlternateBridgeAuthority)
@@ -875,18 +925,23 @@ consider_adding_dir_authorities(const or_options_t *options,
type |= HIDSERV_DIRINFO;
add_default_trusted_dir_authorities(type);
}
+ if (!options->FallbackDir)
+ add_default_fallback_dir_servers();
- for (cl = options->DirServers; cl; cl = cl->next)
- if (parse_dir_server_line(cl->value, NO_DIRINFO, 0)<0)
+ for (cl = options->DirAuthorities; cl; cl = cl->next)
+ if (parse_dir_authority_line(cl->value, NO_DIRINFO, 0)<0)
return -1;
for (cl = options->AlternateBridgeAuthority; cl; cl = cl->next)
- if (parse_dir_server_line(cl->value, NO_DIRINFO, 0)<0)
+ if (parse_dir_authority_line(cl->value, NO_DIRINFO, 0)<0)
return -1;
for (cl = options->AlternateDirAuthority; cl; cl = cl->next)
- if (parse_dir_server_line(cl->value, NO_DIRINFO, 0)<0)
+ if (parse_dir_authority_line(cl->value, NO_DIRINFO, 0)<0)
return -1;
for (cl = options->AlternateHSAuthority; cl; cl = cl->next)
- if (parse_dir_server_line(cl->value, NO_DIRINFO, 0)<0)
+ if (parse_dir_authority_line(cl->value, NO_DIRINFO, 0)<0)
+ return -1;
+ for (cl = options->FallbackDir; cl; cl = cl->next)
+ if (parse_dir_fallback_line(cl->value, 0)<0)
return -1;
return 0;
}
@@ -1041,7 +1096,7 @@ options_act_reversible(const or_options_t *old_options, char **msg)
mark_logs_temp(); /* Close current logs once new logs are open. */
logs_marked = 1;
- if (options_init_logs(options, 0)<0) { /* Configure the log(s) */
+ if (options_init_logs(options, 0)<0) { /* Configure the tor_log(s) */
*msg = tor_strdup("Failed to init Log options. See logs for details.");
goto rollback;
}
@@ -1158,6 +1213,9 @@ options_transition_requires_fresh_tls_context(const or_options_t *old_options,
return 1;
}
+ if (!opt_streq(old_options->TLSECGroup, new_options->TLSECGroup))
+ return 1;
+
return 0;
}
@@ -1210,11 +1268,11 @@ options_act(const or_options_t *old_options)
return -1;
}
- if (consider_adding_dir_authorities(options, old_options) < 0)
+ if (consider_adding_dir_servers(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 "
+ log_warn(LD_GENERAL, "This copy of Tor was compiled to run in a "
"non-anonymous mode. It will provide NO ANONYMITY.");
#endif
@@ -1346,7 +1404,8 @@ options_act(const or_options_t *old_options)
/* Register addressmap directives */
config_register_addressmaps(options);
- parse_virtual_addr_network(options->VirtualAddrNetwork, 0, NULL);
+ parse_virtual_addr_network(options->VirtualAddrNetworkIPv4, AF_INET,0,NULL);
+ parse_virtual_addr_network(options->VirtualAddrNetworkIPv6, AF_INET6,0,NULL);
/* Update address policies. */
if (policies_parse_from_options(options) < 0) {
@@ -1459,8 +1518,10 @@ options_act(const or_options_t *old_options)
if (!smartlist_strings_eq(old_options->AutomapHostsSuffixes,
options->AutomapHostsSuffixes))
revise_automap_entries = 1;
- else if (!opt_streq(old_options->VirtualAddrNetwork,
- options->VirtualAddrNetwork))
+ else if (!opt_streq(old_options->VirtualAddrNetworkIPv4,
+ options->VirtualAddrNetworkIPv4) ||
+ !opt_streq(old_options->VirtualAddrNetworkIPv6,
+ options->VirtualAddrNetworkIPv6))
revise_automap_entries = 1;
}
@@ -1517,6 +1578,18 @@ options_act(const or_options_t *old_options)
config_maybe_load_geoip_files_(options, old_options);
+ if (geoip_is_loaded(AF_INET) && options->GeoIPExcludeUnknown) {
+ /* ExcludeUnknown is true or "auto" */
+ const int is_auto = options->GeoIPExcludeUnknown == -1;
+ int changed;
+
+ changed = routerset_add_unknown_ccs(&options->ExcludeNodes, is_auto);
+ changed += routerset_add_unknown_ccs(&options->ExcludeExitNodes, is_auto);
+
+ if (changed)
+ routerset_add_unknown_ccs(&options->ExcludeExitNodesUnion_, is_auto);
+ }
+
if (options->CellStatistics || options->DirReqStatistics ||
options->EntryStatistics || options->ExitPortStatistics ||
options->ConnDirectionStatistics ||
@@ -1713,7 +1786,7 @@ config_get_commandlines(int argc, char **argv, config_line_t **result)
(*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'",
+ log_debug(LD_CONFIG, "command line: parsed keyword '%s', value '%s'",
(*new)->key, (*new)->value);
new = &((*new)->next);
@@ -1796,7 +1869,7 @@ print_usage(void)
printf(
"Copyright (c) 2001-2004, Roger Dingledine\n"
"Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson\n"
-"Copyright (c) 2007-2012, The Tor Project, Inc.\n\n"
+"Copyright (c) 2007-2013, The Tor Project, Inc.\n\n"
"tor -f <torrc> [args]\n"
"See man page for options, or https://www.torproject.org/ for "
"documentation.\n");
@@ -1820,21 +1893,41 @@ list_torrc_options(void)
/** Last value actually set by resolve_my_address. */
static uint32_t last_resolved_addr = 0;
+
+/** Accessor for last_resolved_addr from outside this file. */
+uint32_t
+get_last_resolved_addr(void)
+{
+ return last_resolved_addr;
+}
+
/**
- * Based on <b>options-\>Address</b>, guess our public IP address and put it
- * (in host order) into *<b>addr_out</b>. If <b>hostname_out</b> is provided,
- * set *<b>hostname_out</b> to a new string holding the hostname we used to
- * get the address. Return 0 if all is well, or -1 if we can't find a suitable
+ * Use <b>options-\>Address</b> to guess our public IP address.
+ *
+ * Return 0 if all is well, or -1 if we can't find a suitable
* public IP address.
+ *
+ * If we are returning 0:
+ * - Put our public IP address (in host order) into *<b>addr_out</b>.
+ * - If <b>method_out</b> is non-NULL, set *<b>method_out</b> to a static
+ * string describing how we arrived at our answer.
+ * - If <b>hostname_out</b> is non-NULL, and we resolved a hostname to
+ * get our address, set *<b>hostname_out</b> to a newly allocated string
+ * holding that hostname. (If we didn't get our address by resolving a
+ * hostname, set *<b>hostname_out</b> to NULL.)
+ *
* XXXX ipv6
*/
int
resolve_my_address(int warn_severity, const or_options_t *options,
- uint32_t *addr_out, char **hostname_out)
+ uint32_t *addr_out,
+ const char **method_out, char **hostname_out)
{
struct in_addr in;
uint32_t addr; /* host order */
char hostname[256];
+ const char *method_used;
+ const char *hostname_used;
int explicit_ip=1;
int explicit_hostname=1;
int from_interface=0;
@@ -1845,6 +1938,10 @@ resolve_my_address(int warn_severity, const or_options_t *options,
tor_assert(addr_out);
+ /*
+ * Step one: Fill in 'hostname' to be our best guess.
+ */
+
if (address && *address) {
strlcpy(hostname, address, sizeof(hostname));
} else { /* then we need to guess our address */
@@ -1855,10 +1952,14 @@ resolve_my_address(int warn_severity, const or_options_t *options,
log_fn(warn_severity, LD_NET,"Error obtaining local hostname");
return -1;
}
- log_debug(LD_CONFIG,"Guessed local host name as '%s'",hostname);
+ log_debug(LD_CONFIG, "Guessed local host name as '%s'", hostname);
}
- /* now we know hostname. resolve it and keep only the IP address */
+ /*
+ * Step two: Now that we know 'hostname', parse it or resolve it. If
+ * it doesn't parse or resolve, look at the interface address. Set 'addr'
+ * to be our (host-order) 32-bit answer.
+ */
if (tor_inet_aton(hostname, &in) == 0) {
/* then we have to resolve it */
@@ -1915,21 +2016,26 @@ resolve_my_address(int warn_severity, const or_options_t *options,
* illformed */
}
+ /*
+ * Step three: Check whether 'addr' is an internal IP address, and error
+ * out if it is and we don't want that.
+ */
+
addr_string = tor_dup_ip(addr);
if (is_internal_IP(addr, 0)) {
/* make sure we're ok with publishing an internal IP */
- if (!options->DirServers && !options->AlternateDirAuthority) {
- /* if they are using the default dirservers, disallow internal IPs
+ if (!options->DirAuthorities && !options->AlternateDirAuthority) {
+ /* if they are using the default authorities, disallow internal IPs
* always. */
log_fn(warn_severity, LD_CONFIG,
"Address '%s' resolves to private IP address '%s'. "
- "Tor servers that use the default DirServers must have public "
- "IP addresses.", hostname, addr_string);
+ "Tor servers that use the default DirAuthorities must have "
+ "public IP addresses.", hostname, addr_string);
tor_free(addr_string);
return -1;
}
if (!explicit_ip) {
- /* even if they've set their own dirservers, require an explicit IP if
+ /* even if they've set their own authorities, require an explicit IP if
* they're using an internal address. */
log_fn(warn_severity, LD_CONFIG, "Address '%s' resolves to private "
"IP address '%s'. Please set the Address config option to be "
@@ -1939,37 +2045,65 @@ resolve_my_address(int warn_severity, const or_options_t *options,
}
}
- log_debug(LD_CONFIG, "Resolved Address to '%s'.", fmt_addr32(addr));
+ /*
+ * Step four: We have a winner! 'addr' is our answer for sure, and
+ * 'addr_string' is its string form. Fill out the various fields to
+ * say how we decided it.
+ */
+
+ log_debug(LD_CONFIG, "Resolved Address to '%s'.", addr_string);
+
+ if (explicit_ip) {
+ method_used = "CONFIGURED";
+ hostname_used = NULL;
+ } else if (explicit_hostname) {
+ method_used = "RESOLVED";
+ hostname_used = hostname;
+ } else if (from_interface) {
+ method_used = "INTERFACE";
+ hostname_used = NULL;
+ } else {
+ method_used = "GETHOSTNAME";
+ hostname_used = hostname;
+ }
+
*addr_out = addr;
+ if (method_out)
+ *method_out = method_used;
+ if (hostname_out)
+ *hostname_out = hostname_used ? tor_strdup(hostname_used) : NULL;
+
+ /*
+ * Step five: Check if the answer has changed since last time (or if
+ * there was no last time), and if so call various functions to keep
+ * us up-to-date.
+ */
+
if (last_resolved_addr && last_resolved_addr != *addr_out) {
/* Leave this as a notice, regardless of the requested severity,
* at least until dynamic IP address support becomes bulletproof. */
log_notice(LD_NET,
- "Your IP address seems to have changed to %s. Updating.",
- addr_string);
+ "Your IP address seems to have changed to %s "
+ "(METHOD=%s%s%s). Updating.",
+ addr_string, method_used,
+ hostname_used ? " HOSTNAME=" : "",
+ hostname_used ? hostname_used : "");
ip_address_changed(0);
}
+
if (last_resolved_addr != *addr_out) {
- const char *method;
- const char *h = hostname;
- if (explicit_ip) {
- method = "CONFIGURED";
- h = NULL;
- } else if (explicit_hostname) {
- method = "RESOLVED";
- } else if (from_interface) {
- method = "INTERFACE";
- h = NULL;
- } else {
- method = "GETHOSTNAME";
- }
control_event_server_status(LOG_NOTICE,
- "EXTERNAL_ADDRESS ADDRESS=%s METHOD=%s %s%s",
- addr_string, method, h?"HOSTNAME=":"", h);
+ "EXTERNAL_ADDRESS ADDRESS=%s METHOD=%s%s%s",
+ addr_string, method_used,
+ hostname_used ? " HOSTNAME=" : "",
+ hostname_used ? hostname_used : "");
}
last_resolved_addr = *addr_out;
- if (hostname_out)
- *hostname_out = tor_strdup(hostname);
+
+ /*
+ * And finally, clean up and return success.
+ */
+
tor_free(addr_string);
return 0;
}
@@ -2161,7 +2295,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
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
+#define COMPLAIN(arg) STMT_BEGIN log_warn(LD_CONFIG, arg); STMT_END
tor_assert(msg);
*msg = NULL;
@@ -2170,7 +2304,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
(!strcmpstart(uname, "Windows 95") ||
!strcmpstart(uname, "Windows 98") ||
!strcmpstart(uname, "Windows Me"))) {
- log(LOG_WARN, LD_CONFIG, "Tor is running as a server, but you are "
+ log_warn(LD_CONFIG, "Tor is running as a server, but you are "
"running %s; this probably won't work. See "
"https://wiki.torproject.org/TheOnionRouter/TorFAQ#ServerOS "
"for details.", uname);
@@ -2199,7 +2333,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
}
if (server_mode(options) && !options->ContactInfo)
- log(LOG_NOTICE, LD_CONFIG, "Your ContactInfo config option is not set. "
+ log_notice(LD_CONFIG, "Your ContactInfo config option is not set. "
"Please consider setting it, so we can contact you if your server is "
"misconfigured or something else goes wrong.");
@@ -2211,13 +2345,13 @@ options_validate(or_options_t *old_options, or_options_t *options,
config_line_append(&options->Logs, "Log", "warn stdout");
}
- if (options_init_logs(options, 1)<0) /* Validate the log(s) */
+ if (options_init_logs(options, 1)<0) /* Validate the tor_log(s) */
REJECT("Failed to validate Log options. See logs for details.");
if (authdir_mode(options)) {
/* confirm that our address isn't broken, so we can complain now */
uint32_t tmp;
- if (resolve_my_address(LOG_WARN, options, &tmp, NULL) < 0)
+ if (resolve_my_address(LOG_WARN, options, &tmp, NULL, NULL) < 0)
REJECT("Failed to resolve/guess local address. See logs for details.");
}
@@ -2229,7 +2363,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
/* 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,
+ log_warn(LD_CONFIG,
"SocksPort, TransPort, NATDPort, DNSPort, and ORPort are all "
"undefined, and there aren't any hidden services configured. "
"Tor will still run, but probably won't do anything.");
@@ -2263,6 +2397,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
}
}
+ if (options->TLSECGroup && (strcasecmp(options->TLSECGroup, "P256") &&
+ strcasecmp(options->TLSECGroup, "P224"))) {
+ COMPLAIN("Unrecognized TLSECGroup: Falling back to the default.");
+ tor_free(options->TLSECGroup);
+ }
+
if (options->ExcludeNodes && options->StrictNodes) {
COMPLAIN("You have asked to exclude certain relays from all positions "
"in your circuits. Expect hidden services and other Tor "
@@ -2330,6 +2470,18 @@ options_validate(or_options_t *old_options, or_options_t *options,
return -1;
}
+ if (options->PathsNeededToBuildCircuits >= 0.0) {
+ if (options->PathsNeededToBuildCircuits < 0.25) {
+ log_warn(LD_CONFIG, "PathsNeededToBuildCircuits is too low. Increasing "
+ "to 0.25");
+ options->PathsNeededToBuildCircuits = 0.25;
+ } else if (options->PathsNeededToBuildCircuits < 0.95) {
+ log_warn(LD_CONFIG, "PathsNeededToBuildCircuits is too high. Decreasing "
+ "to 0.95");
+ options->PathsNeededToBuildCircuits = 0.95;
+ }
+ }
+
if (options->MaxClientCircuitsPending <= 0 ||
options->MaxClientCircuitsPending > MAX_MAX_CLIENT_CIRCUITS_PENDING) {
tor_asprintf(msg,
@@ -2371,7 +2523,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
});
new_line->value = smartlist_join_strings(instead,",",0,NULL);
/* These have been deprecated since 0.1.1.5-alpha-cvs */
- log(LOG_NOTICE, LD_CONFIG,
+ log_notice(LD_CONFIG,
"Converting FascistFirewall and FirewallPorts "
"config options to new format: \"ReachableAddresses %s\"",
new_line->value);
@@ -2386,7 +2538,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
new_line->key = tor_strdup("ReachableDirAddresses");
new_line->value = tor_strdup("*:80");
options->ReachableDirAddresses = new_line;
- log(LOG_NOTICE, LD_CONFIG, "Converting FascistFirewall config option "
+ log_notice(LD_CONFIG, "Converting FascistFirewall config option "
"to new format: \"ReachableDirAddresses *:80\"");
}
if (!options->ReachableORAddresses) {
@@ -2394,7 +2546,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
new_line->key = tor_strdup("ReachableORAddresses");
new_line->value = tor_strdup("*:443");
options->ReachableORAddresses = new_line;
- log(LOG_NOTICE, LD_CONFIG, "Converting FascistFirewall config option "
+ log_notice(LD_CONFIG, "Converting FascistFirewall config option "
"to new format: \"ReachableORAddresses *:443\"");
}
}
@@ -2574,6 +2726,37 @@ options_validate(or_options_t *old_options, or_options_t *options,
RECOMMENDED_MIN_CIRCUIT_BUILD_TIMEOUT );
}
+ if (options->PathBiasNoticeRate > 1.0) {
+ tor_asprintf(msg,
+ "PathBiasNoticeRate is too high. "
+ "It must be between 0 and 1.0");
+ return -1;
+ }
+ if (options->PathBiasWarnRate > 1.0) {
+ tor_asprintf(msg,
+ "PathBiasWarnRate is too high. "
+ "It must be between 0 and 1.0");
+ return -1;
+ }
+ if (options->PathBiasExtremeRate > 1.0) {
+ tor_asprintf(msg,
+ "PathBiasExtremeRate is too high. "
+ "It must be between 0 and 1.0");
+ return -1;
+ }
+ if (options->PathBiasNoticeUseRate > 1.0) {
+ tor_asprintf(msg,
+ "PathBiasNoticeUseRate is too high. "
+ "It must be between 0 and 1.0");
+ return -1;
+ }
+ if (options->PathBiasExtremeUseRate > 1.0) {
+ tor_asprintf(msg,
+ "PathBiasExtremeUseRate is too high. "
+ "It must be between 0 and 1.0");
+ return -1;
+ }
+
if (options->MaxCircuitDirtiness < MIN_MAX_CIRCUIT_DIRTINESS) {
log_warn(LD_CONFIG, "MaxCircuitDirtiness option is too short; "
"raising to %d seconds.", MIN_MAX_CIRCUIT_DIRTINESS);
@@ -2837,8 +3020,9 @@ options_validate(or_options_t *old_options, or_options_t *options,
if (validate_addr_policies(options, msg) < 0)
return -1;
- if (validate_dir_authorities(options, old_options) < 0)
- REJECT("Directory authority line did not parse. See logs for details.");
+ if (validate_dir_servers(options, old_options) < 0)
+ REJECT("Directory authority/fallback line did not parse. See logs "
+ "for details.");
if (options->UseBridges && !options->Bridges)
REJECT("If you set UseBridges, you must specify at least one bridge.");
@@ -2867,6 +3051,22 @@ options_validate(or_options_t *old_options, or_options_t *options,
escaped(options->ServerTransportPlugin->value));
}
+ for (cl = options->ServerTransportListenAddr; cl; cl = cl->next) {
+ /** If get_bindaddr_from_transport_listen_line() fails with
+ 'transport' being NULL, it means that something went wrong
+ while parsing the ServerTransportListenAddr line. */
+ char *bindaddr = get_bindaddr_from_transport_listen_line(cl->value, NULL);
+ if (!bindaddr)
+ REJECT("ServerTransportListenAddr did not parse. See logs for details.");
+ tor_free(bindaddr);
+ }
+
+ if (options->ServerTransportListenAddr && !options->ServerTransportPlugin) {
+ log_notice(LD_GENERAL, "You need at least a single managed-proxy to "
+ "specify a transport listen address. The "
+ "ServerTransportListenAddr line will be ignored.");
+ }
+
if (options->ConstrainedSockets) {
/* If the user wants to constrain socket buffer use, make sure the desired
* limit is between MIN|MAX_TCPSOCK_BUFFER in k increments. */
@@ -2918,7 +3118,11 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("Failed to configure client authorization for hidden services. "
"See logs for details.");
- if (parse_virtual_addr_network(options->VirtualAddrNetwork, 1, NULL)<0)
+ if (parse_virtual_addr_network(options->VirtualAddrNetworkIPv4,
+ AF_INET, 1, msg)<0)
+ return -1;
+ if (parse_virtual_addr_network(options->VirtualAddrNetworkIPv6,
+ AF_INET6, 1, msg)<0)
return -1;
if (options->PreferTunneledDirConns && !options->TunnelDirConns)
@@ -2940,7 +3144,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
}
if (options->TestingTorNetwork &&
- !(options->DirServers ||
+ !(options->DirAuthorities ||
(options->AlternateDirAuthority &&
options->AlternateBridgeAuthority))) {
REJECT("TestingTorNetwork may only be configured in combination with "
@@ -2948,7 +3152,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
"and AlternateBridgeAuthority configured.");
}
- if (options->AllowSingleHopExits && !options->DirServers) {
+ if (options->AllowSingleHopExits && !options->DirAuthorities) {
COMPLAIN("You have set AllowSingleHopExits; now your relay will allow "
"others to make one-hop exits. However, since by default most "
"clients avoid relays that set this option, most clients will "
@@ -3168,6 +3372,7 @@ 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->IPv6Exit != new_options->IPv6Exit ||
!config_lines_eq(old_options->ORPort_lines,
new_options->ORPort_lines) ||
!config_lines_eq(old_options->DirPort_lines,
@@ -3326,7 +3531,7 @@ find_torrc_filename(int argc, char **argv,
for (i = 1; i < argc; ++i) {
if (i < argc-1 && !strcmp(argv[i],fname_opt)) {
if (fname) {
- log(LOG_WARN, LD_CONFIG, "Duplicate %s options on command line.",
+ log_warn(LD_CONFIG, "Duplicate %s options on command line.",
fname_opt);
tor_free(fname);
}
@@ -3389,7 +3594,7 @@ load_torrc_from_disk(int argc, char **argv, int defaults_file)
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);
+ log_debug(LD_CONFIG, "Opening config file \"%s\"", fname);
tor_free(*fname_var);
*fname_var = fname;
@@ -3399,18 +3604,18 @@ load_torrc_from_disk(int argc, char **argv, int defaults_file)
!(cf = read_file_to_str(fname,0,NULL))) {
if (using_default_torrc == 1 || ignore_missing_torrc) {
if (!defaults_file)
- log(LOG_NOTICE, LD_CONFIG, "Configuration file \"%s\" not present, "
+ log_notice(LD_CONFIG, "Configuration file \"%s\" not present, "
"using reasonable defaults.", fname);
tor_free(fname); /* sets fname to NULL */
*fname_var = NULL;
cf = tor_strdup("");
} else {
- log(LOG_WARN, LD_CONFIG,
+ log_warn(LD_CONFIG,
"Unable to open configuration file \"%s\".", fname);
goto err;
}
} else {
- log(LOG_NOTICE, LD_CONFIG, "Read configuration file \"%s\".", fname);
+ log_notice(LD_CONFIG, "Read configuration file \"%s\".", fname);
}
return cf;
@@ -3502,7 +3707,7 @@ options_init_from_torrc(int argc, char **argv)
tor_free(cf);
tor_free(cf_defaults);
if (errmsg) {
- log(LOG_WARN,LD_CONFIG,"%s", errmsg);
+ log_warn(LD_CONFIG,"%s", errmsg);
tor_free(errmsg);
}
return retval < 0 ? -1 : 0;
@@ -4105,6 +4310,80 @@ parse_client_transport_line(const char *line, int validate_only)
return r;
}
+/** Given a ServerTransportListenAddr <b>line</b>, return its
+ * <address:port> string. Return NULL if the line was not
+ * well-formed.
+ *
+ * If <b>transport</b> is set, return NULL if the line is not
+ * referring to <b>transport</b>.
+ *
+ * The returned string is allocated on the heap and it's the
+ * responsibility of the caller to free it. */
+static char *
+get_bindaddr_from_transport_listen_line(const char *line,const char *transport)
+{
+ smartlist_t *items = NULL;
+ const char *parsed_transport = NULL;
+ char *addrport = NULL;
+ tor_addr_t addr;
+ uint16_t port = 0;
+
+ items = smartlist_new();
+ smartlist_split_string(items, line, NULL,
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+
+ if (smartlist_len(items) < 2) {
+ log_warn(LD_CONFIG,"Too few arguments on ServerTransportListenAddr line.");
+ goto err;
+ }
+
+ parsed_transport = smartlist_get(items, 0);
+ addrport = tor_strdup(smartlist_get(items, 1));
+
+ /* If 'transport' is given, check if it matches the one on the line */
+ if (transport && strcmp(transport, parsed_transport))
+ goto err;
+
+ /* Validate addrport */
+ if (tor_addr_port_parse(LOG_WARN, addrport, &addr, &port)<0) {
+ log_warn(LD_CONFIG, "Error parsing ServerTransportListenAddr "
+ "address '%s'", addrport);
+ goto err;
+ }
+
+ goto done;
+
+ err:
+ tor_free(addrport);
+ addrport = NULL;
+
+ done:
+ SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+ smartlist_free(items);
+
+ return addrport;
+}
+
+/** Given the name of a pluggable transport in <b>transport</b>, check
+ * the configuration file to see if the user has explicitly asked for
+ * it to listen on a specific port. Return a <address:port> string if
+ * so, otherwise NULL. */
+char *
+get_transport_bindaddr_from_config(const char *transport)
+{
+ config_line_t *cl;
+ const or_options_t *options = get_options();
+
+ for (cl = options->ServerTransportListenAddr; cl; cl = cl->next) {
+ char *bindaddr =
+ get_bindaddr_from_transport_listen_line(cl->value, transport);
+ if (bindaddr)
+ return bindaddr;
+ }
+
+ return NULL;
+}
+
/** Read the contents of a ServerTransportPlugin line from
* <b>line</b>. Return 0 if the line is well-formed, and -1 if it
* isn't.
@@ -4225,15 +4504,15 @@ parse_server_transport_line(const char *line, int validate_only)
return r;
}
-/** Read the contents of a DirServer line from <b>line</b>. If
+/** Read the contents of a DirAuthority line from <b>line</b>. If
* <b>validate_only</b> is 0, and the line is well-formed, and it
* shares any bits with <b>required_type</b> or <b>required_type</b>
* is 0, then add the dirserver described in the line (minus whatever
* bits it's missing) as a valid authority. Return 0 on success,
* or -1 if the line isn't well-formed or if we can't add it. */
static int
-parse_dir_server_line(const char *line, dirinfo_type_t required_type,
- int validate_only)
+parse_dir_authority_line(const char *line, dirinfo_type_t required_type,
+ int validate_only)
{
smartlist_t *items = NULL;
int r;
@@ -4243,6 +4522,7 @@ parse_dir_server_line(const char *line, dirinfo_type_t required_type,
char v3_digest[DIGEST_LEN];
dirinfo_type_t type = V2_DIRINFO;
int is_not_hidserv_authority = 0, is_not_v2_authority = 0;
+ double weight = 1.0;
items = smartlist_new();
smartlist_split_string(items, line, NULL,
@@ -4278,6 +4558,14 @@ parse_dir_server_line(const char *line, dirinfo_type_t required_type,
if (!ok)
log_warn(LD_CONFIG, "Invalid orport '%s' on DirServer line.",
portstring);
+ } else if (!strcmpstart(flag, "weight=")) {
+ int ok;
+ const char *wstring = flag + strlen("weight=");
+ weight = tor_parse_double(wstring, 0, UINT64_MAX, &ok, NULL);
+ if (!ok) {
+ log_warn(LD_CONFIG, "Invalid weight '%s' on DirAuthority line.",flag);
+ weight=1.0;
+ }
} else if (!strcasecmpstart(flag, "v3ident=")) {
char *idstr = flag + strlen("v3ident=");
if (strlen(idstr) != HEX_DIGEST_LEN ||
@@ -4334,14 +4622,16 @@ parse_dir_server_line(const char *line, dirinfo_type_t required_type,
}
if (!validate_only && (!required_type || required_type & type)) {
+ dir_server_t *ds;
if (required_type)
type &= required_type; /* pare down what we think of them as an
* authority for. */
log_debug(LD_DIR, "Trusted %d dirserver at %s:%d (%s)", (int)type,
address, (int)dir_port, (char*)smartlist_get(items,0));
- if (!add_trusted_dir_server(nickname, address, dir_port, or_port,
- digest, v3_digest, type))
+ if (!(ds = trusted_dir_server_new(nickname, address, dir_port, or_port,
+ digest, v3_digest, type, weight)))
goto err;
+ dir_server_add(ds);
}
r = 0;
@@ -4360,6 +4650,110 @@ parse_dir_server_line(const char *line, dirinfo_type_t required_type,
return r;
}
+/** Read the contents of a FallbackDir line from <b>line</b>. If
+ * <b>validate_only</b> is 0, and the line is well-formed, then add the
+ * dirserver described in the line as a fallback directory. Return 0 on
+ * success, or -1 if the line isn't well-formed or if we can't add it. */
+static int
+parse_dir_fallback_line(const char *line,
+ int validate_only)
+{
+ int r = -1;
+ smartlist_t *items = smartlist_new(), *positional = smartlist_new();
+ int orport = -1;
+ uint16_t dirport;
+ tor_addr_t addr;
+ int ok;
+ char id[DIGEST_LEN];
+ char *address=NULL;
+ double weight=1.0;
+
+ memset(id, 0, sizeof(id));
+ smartlist_split_string(items, line, NULL,
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+ SMARTLIST_FOREACH_BEGIN(items, const char *, cp) {
+ const char *eq = strchr(cp, '=');
+ ok = 1;
+ if (! eq) {
+ smartlist_add(positional, (char*)cp);
+ continue;
+ }
+ if (!strcmpstart(cp, "orport=")) {
+ orport = (int)tor_parse_long(cp+strlen("orport="), 10,
+ 1, 65535, &ok, NULL);
+ } else if (!strcmpstart(cp, "id=")) {
+ ok = !base16_decode(id, DIGEST_LEN,
+ cp+strlen("id="), strlen(cp)-strlen("id="));
+ } else if (!strcmpstart(cp, "weight=")) {
+ int ok;
+ const char *wstring = cp + strlen("weight=");
+ weight = tor_parse_double(wstring, 0, UINT64_MAX, &ok, NULL);
+ if (!ok) {
+ log_warn(LD_CONFIG, "Invalid weight '%s' on FallbackDir line.", cp);
+ weight=1.0;
+ }
+ }
+
+ if (!ok) {
+ log_warn(LD_CONFIG, "Bad FallbackDir option %s", escaped(cp));
+ goto end;
+ }
+ } SMARTLIST_FOREACH_END(cp);
+
+ if (smartlist_len(positional) != 1) {
+ log_warn(LD_CONFIG, "Couldn't parse FallbackDir line %s", escaped(line));
+ goto end;
+ }
+
+ if (tor_digest_is_zero(id)) {
+ log_warn(LD_CONFIG, "Missing identity on FallbackDir line");
+ goto end;
+ }
+
+ if (orport <= 0) {
+ log_warn(LD_CONFIG, "Missing orport on FallbackDir line");
+ goto end;
+ }
+
+ if (tor_addr_port_split(LOG_INFO, smartlist_get(positional, 0),
+ &address, &dirport) < 0 ||
+ tor_addr_parse(&addr, address)<0) {
+ log_warn(LD_CONFIG, "Couldn't parse address:port %s on FallbackDir line",
+ (const char*)smartlist_get(positional, 0));
+ goto end;
+ }
+
+ if (!validate_only) {
+ dir_server_t *ds;
+ ds = fallback_dir_server_new(&addr, dirport, orport, id, weight);
+ if (!ds) {
+ log_warn(LD_CONFIG, "Couldn't create FallbackDir %s", escaped(line));
+ goto end;
+ }
+ dir_server_add(ds);
+ }
+
+ r = 0;
+
+ end:
+ SMARTLIST_FOREACH(items, char *, cp, tor_free(cp));
+ smartlist_free(items);
+ smartlist_free(positional);
+ tor_free(address);
+ return r;
+}
+
+/** Allocate and return a new port_cfg_t with reasonable defaults. */
+static port_cfg_t *
+port_cfg_new(void)
+{
+ port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t));
+ cfg->ipv4_traffic = 1;
+ cfg->cache_ipv4_answers = 1;
+ cfg->prefer_ipv6_virtaddr = 1;
+ return cfg;
+}
+
/** Free all storage held in <b>port</b> */
static void
port_cfg_free(port_cfg_t *port)
@@ -4367,12 +4761,15 @@ port_cfg_free(port_cfg_t *port)
tor_free(port);
}
-/** Warn for every port in <b>ports</b> that is on a publicly routable
- * address. */
+/** Warn for every port in <b>ports</b> of type <b>listener_type</b> that is
+ * on a publicly routable address. */
static void
-warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname)
+warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname,
+ int listener_type)
{
SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) {
+ if (port->type != listener_type)
+ continue;
if (port->is_unix_addr) {
/* Unix sockets aren't accessible over a network. */
} else if (!tor_addr_is_internal(&port->addr, 1)) {
@@ -4438,6 +4835,7 @@ warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid)
#define CL_PORT_ALLOW_EXTRA_LISTENADDR (1u<<2)
#define CL_PORT_SERVER_OPTIONS (1u<<3)
#define CL_PORT_FORBID_NONLOCAL (1u<<4)
+#define CL_PORT_TAKES_HOSTNAMES (1u<<5)
/**
* Parse port configuration for a single port type.
@@ -4470,6 +4868,9 @@ warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid)
* isolation options in the FooPort entries; instead allow the
* server-port option set.
*
+ * If CL_PORT_TAKES_HOSTNAMES is set in <b>flags</b>, allow the options
+ * {No,}IPv{4,6}Traffic.
+ *
* 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.
@@ -4493,6 +4894,7 @@ parse_port_config(smartlist_t *out,
const unsigned forbid_nonlocal = flags & CL_PORT_FORBID_NONLOCAL;
const unsigned allow_spurious_listenaddr =
flags & CL_PORT_ALLOW_EXTRA_LISTENADDR;
+ const unsigned takes_hostnames = flags & CL_PORT_TAKES_HOSTNAMES;
int got_zero_port=0, got_nonzero_port=0;
/* FooListenAddress is deprecated; let's make it work like it used to work,
@@ -4529,12 +4931,14 @@ parse_port_config(smartlist_t *out,
if (use_server_options && out) {
/* Add a no_listen port. */
- port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t));
+ port_cfg_t *cfg = port_cfg_new();
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;
+ cfg->bind_ipv4_only = 1;
+ cfg->ipv4_traffic = 1;
+ cfg->prefer_ipv6_virtaddr = 1;
smartlist_add(out, cfg);
}
@@ -4547,7 +4951,7 @@ parse_port_config(smartlist_t *out,
return -1;
}
if (out) {
- port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t));
+ port_cfg_t *cfg = port_cfg_new();
cfg->type = listener_type;
cfg->port = port ? port : mainport;
tor_addr_copy(&cfg->addr, &addr);
@@ -4562,7 +4966,7 @@ parse_port_config(smartlist_t *out,
if (is_control)
warn_nonlocal_controller_ports(out, forbid_nonlocal);
else
- warn_nonlocal_client_ports(out, portname);
+ warn_nonlocal_client_ports(out, portname, listener_type);
}
return 0;
} /* end if (listenaddrs) */
@@ -4571,7 +4975,7 @@ parse_port_config(smartlist_t *out,
* one. */
if (! ports) {
if (defaultport && out) {
- port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t));
+ port_cfg_t *cfg = port_cfg_new();
cfg->type = listener_type;
cfg->port = defaultport;
tor_addr_parse(&cfg->addr, defaultaddr);
@@ -4596,7 +5000,11 @@ parse_port_config(smartlist_t *out,
uint16_t ptmp=0;
int ok;
int no_listen = 0, no_advertise = 0, all_addrs = 0,
- ipv4_only = 0, ipv6_only = 0;
+ bind_ipv4_only = 0, bind_ipv6_only = 0,
+ ipv4_traffic = 1, ipv6_traffic = 0, prefer_ipv6 = 0,
+ cache_ipv4 = 1, use_cached_ipv4 = 0,
+ cache_ipv6 = 0, use_cached_ipv6 = 0,
+ prefer_ipv6_automap = 1;
smartlist_split_string(elts, ports->value, NULL,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
@@ -4661,9 +5069,9 @@ parse_port_config(smartlist_t *out,
all_addrs = 1;
#endif
} else if (!strcasecmp(elt, "IPv4Only")) {
- ipv4_only = 1;
+ bind_ipv4_only = 1;
} else if (!strcasecmp(elt, "IPv6Only")) {
- ipv6_only = 1;
+ bind_ipv6_only = 1;
} else {
log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'",
portname, escaped(elt));
@@ -4676,18 +5084,18 @@ parse_port_config(smartlist_t *out,
portname, escaped(ports->value));
goto err;
}
- if (ipv4_only && ipv6_only) {
+ if (bind_ipv4_only && bind_ipv6_only) {
log_warn(LD_CONFIG, "Tried to set both IPv4Only and IPv6Only "
"on %sPort line '%s'",
portname, escaped(ports->value));
goto err;
}
- if (ipv4_only && tor_addr_family(&addr) == AF_INET6) {
+ if (bind_ipv4_only && tor_addr_family(&addr) == AF_INET6) {
log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv6",
portname);
goto err;
}
- if (ipv6_only && tor_addr_family(&addr) == AF_INET) {
+ if (bind_ipv6_only && tor_addr_family(&addr) == AF_INET) {
log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv4",
portname);
goto err;
@@ -4720,6 +5128,42 @@ parse_port_config(smartlist_t *out,
no = 1;
elt += 2;
}
+
+ if (takes_hostnames) {
+ if (!strcasecmp(elt, "IPv4Traffic")) {
+ ipv4_traffic = ! no;
+ continue;
+ } else if (!strcasecmp(elt, "IPv6Traffic")) {
+ ipv6_traffic = ! no;
+ continue;
+ } else if (!strcasecmp(elt, "PreferIPv6")) {
+ prefer_ipv6 = ! no;
+ continue;
+ }
+ }
+ if (!strcasecmp(elt, "CacheIPv4DNS")) {
+ cache_ipv4 = ! no;
+ continue;
+ } else if (!strcasecmp(elt, "CacheIPv6DNS")) {
+ cache_ipv6 = ! no;
+ continue;
+ } else if (!strcasecmp(elt, "CacheDNS")) {
+ cache_ipv4 = cache_ipv6 = ! no;
+ continue;
+ } else if (!strcasecmp(elt, "UseIPv4Cache")) {
+ use_cached_ipv4 = ! no;
+ continue;
+ } else if (!strcasecmp(elt, "UseIPv6Cache")) {
+ use_cached_ipv6 = ! no;
+ continue;
+ } else if (!strcasecmp(elt, "UseDNSCache")) {
+ use_cached_ipv4 = use_cached_ipv6 = ! no;
+ continue;
+ } else if (!strcasecmp(elt, "PreferIPv6Automap")) {
+ prefer_ipv6_automap = ! no;
+ continue;
+ }
+
if (!strcasecmpend(elt, "s"))
elt[strlen(elt)-1] = '\0'; /* kill plurals. */
@@ -4751,8 +5195,14 @@ parse_port_config(smartlist_t *out,
else
got_zero_port = 1;
+ if (ipv4_traffic == 0 && ipv6_traffic == 0) {
+ log_warn(LD_CONFIG, "You have a %sPort entry with both IPv4 and "
+ "IPv6 disabled; that won't work.", portname);
+ goto err;
+ }
+
if (out && port) {
- port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t));
+ port_cfg_t *cfg = port_cfg_new();
tor_addr_copy(&cfg->addr, &addr);
cfg->port = port;
cfg->type = listener_type;
@@ -4761,8 +5211,16 @@ parse_port_config(smartlist_t *out,
cfg->no_advertise = no_advertise;
cfg->no_listen = no_listen;
cfg->all_addrs = all_addrs;
- cfg->ipv4_only = ipv4_only;
- cfg->ipv6_only = ipv6_only;
+ cfg->bind_ipv4_only = bind_ipv4_only;
+ cfg->bind_ipv6_only = bind_ipv6_only;
+ cfg->ipv4_traffic = ipv4_traffic;
+ cfg->ipv6_traffic = ipv6_traffic;
+ cfg->prefer_ipv6 = prefer_ipv6;
+ cfg->cache_ipv4_answers = cache_ipv4;
+ cfg->cache_ipv6_answers = cache_ipv6;
+ cfg->use_cached_ipv4_answers = use_cached_ipv4;
+ cfg->use_cached_ipv6_answers = use_cached_ipv6;
+ cfg->prefer_ipv6_virtaddr = prefer_ipv6_automap;
smartlist_add(out, cfg);
}
@@ -4774,7 +5232,7 @@ parse_port_config(smartlist_t *out,
if (is_control)
warn_nonlocal_controller_ports(out, forbid_nonlocal);
else
- warn_nonlocal_client_ports(out, portname);
+ warn_nonlocal_client_ports(out, portname, listener_type);
}
if (got_zero_port && got_nonzero_port) {
@@ -4855,7 +5313,8 @@ parse_ports(or_options_t *options, int validate_only,
options->SocksPort_lines, options->SocksListenAddress,
"Socks", CONN_TYPE_AP_LISTENER,
"127.0.0.1", 9050,
- CL_PORT_WARN_NONLOCAL|CL_PORT_ALLOW_EXTRA_LISTENADDR) < 0) {
+ CL_PORT_WARN_NONLOCAL|CL_PORT_ALLOW_EXTRA_LISTENADDR|
+ CL_PORT_TAKES_HOSTNAMES) < 0) {
*msg = tor_strdup("Invalid SocksPort/SocksListenAddress configuration");
goto err;
}
@@ -4863,7 +5322,7 @@ parse_ports(or_options_t *options, int validate_only,
options->DNSPort_lines, options->DNSListenAddress,
"DNS", CONN_TYPE_AP_DNS_LISTENER,
"127.0.0.1", 0,
- CL_PORT_WARN_NONLOCAL) < 0) {
+ CL_PORT_WARN_NONLOCAL|CL_PORT_TAKES_HOSTNAMES) < 0) {
*msg = tor_strdup("Invalid DNSPort/DNSListenAddress configuration");
goto err;
}
@@ -4995,7 +5454,8 @@ check_server_ports(const smartlist_t *ports,
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))
+ (tor_addr_family(&port->addr) == AF_UNSPEC &&
+ !port->bind_ipv6_only))
++n_orport_advertised_ipv4;
}
if (! port->no_listen)
@@ -5031,7 +5491,7 @@ check_server_ports(const smartlist_t *ports,
}
if (n_low_port && options->AccountingMax) {
- log(LOG_WARN, LD_CONFIG,
+ 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 "
@@ -5125,8 +5585,8 @@ get_first_advertised_port_by_type_af(int listener_type, int address_family)
(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)) {
+ (address_family == AF_INET && !cfg->bind_ipv6_only) ||
+ (address_family == AF_INET6 && !cfg->bind_ipv4_only)) {
return cfg->port;
}
}
diff --git a/src/or/config.h b/src/or/config.h
index f3b28adb78..ef4acac514 100644
--- a/src/or/config.h
+++ b/src/or/config.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -26,8 +26,10 @@ const char *get_short_version(void);
setopt_err_t options_trial_assign(config_line_t *list, int use_defaults,
int clear_first, char **msg);
+uint32_t get_last_resolved_addr(void);
int resolve_my_address(int warn_severity, const or_options_t *options,
- uint32_t *addr, char **hostname_out);
+ uint32_t *addr_out,
+ const char **method_out, char **hostname_out);
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);
@@ -82,6 +84,8 @@ const char *tor_get_digests(void);
uint32_t get_effective_bwrate(const or_options_t *options);
uint32_t get_effective_bwburst(const or_options_t *options);
+char *get_transport_bindaddr_from_config(const char *transport);
+
#ifdef CONFIG_PRIVATE
/* Used only by config.c and test.c */
or_options_t *options_new(void);
diff --git a/src/or/confparse.c b/src/or/confparse.c
index 67cf43fe8c..717d4ac2aa 100644
--- a/src/or/confparse.c
+++ b/src/or/confparse.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#include "or.h"
diff --git a/src/or/confparse.h b/src/or/confparse.h
index f33208eb54..1b987f3bf9 100644
--- a/src/or/confparse.h
+++ b/src/or/confparse.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_CONFPARSE_H
@@ -23,7 +23,7 @@ typedef enum config_type_t {
CONFIG_TYPE_BOOL, /**< A boolean value, expressed as 0 or 1. */
CONFIG_TYPE_AUTOBOOL, /**< A boolean+auto value, expressed 0 for false,
* 1 for true, and -1 for auto */
- CONFIG_TYPE_ISOTIME, /**< An ISO-formatted time relative to GMT. */
+ CONFIG_TYPE_ISOTIME, /**< An ISO-formatted time relative to UTC. */
CONFIG_TYPE_CSV, /**< A list of strings, separated by commas and
* optional whitespace. */
CONFIG_TYPE_LINELIST, /**< Uninterpreted config lines */
diff --git a/src/or/connection.c b/src/or/connection.c
index cd81970b5f..c659e65fe5 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -281,6 +281,13 @@ entry_connection_new(int type, int socket_family)
tor_assert(type == CONN_TYPE_AP);
connection_init(time(NULL), ENTRY_TO_CONN(entry_conn), type, socket_family);
entry_conn->socks_request = socks_request_new();
+ /* If this is coming from a listener, we'll set it up based on the listener
+ * in a little while. Otherwise, we're doing this as a linked connection
+ * of some kind, and we should set it up here based on the socket family */
+ if (socket_family == AF_INET)
+ entry_conn->ipv4_traffic_ok = 1;
+ else if (socket_family == AF_INET6)
+ entry_conn->ipv6_traffic_ok = 1;
return entry_conn;
}
@@ -574,7 +581,7 @@ connection_free_(connection_t *conn)
}
#endif
- memset(mem, 0xCC, memlen); /* poison memory */
+ memwipe(mem, 0xCC, memlen); /* poison memory */
tor_free(mem);
}
@@ -688,8 +695,43 @@ connection_mark_for_close_(connection_t *conn, int line, const char *file)
tor_assert(line < 1<<16); /* marked_for_close can only fit a uint16_t. */
tor_assert(file);
+ if (conn->type == CONN_TYPE_OR) {
+ /*
+ * An or_connection should have been closed through one of the channel-
+ * aware functions in connection_or.c. We'll assume this is an error
+ * close and do that, and log a bug warning.
+ */
+ log_warn(LD_CHANNEL | LD_BUG,
+ "Something tried to close an or_connection_t without going "
+ "through channels at %s:%d",
+ file, line);
+ connection_or_close_for_error(TO_OR_CONN(conn), 0);
+ } else {
+ /* Pass it down to the real function */
+ connection_mark_for_close_internal_(conn, line, file);
+ }
+}
+
+/** Mark <b>conn</b> to be closed next time we loop through
+ * conn_close_if_marked() in main.c; the _internal version bypasses the
+ * CONN_TYPE_OR checks; this should be called when you either are sure that
+ * if this is an or_connection_t the controlling channel has been notified
+ * (e.g. with connection_or_notify_error()), or you actually are the
+ * connection_or_close_for_error() or connection_or_close_normally function.
+ * For all other cases, use connection_mark_and_flush() instead, which
+ * checks for or_connection_t properly, instead. See below.
+ */
+void
+connection_mark_for_close_internal_(connection_t *conn,
+ int line, const char *file)
+{
+ assert_connection_ok(conn,0);
+ tor_assert(line);
+ tor_assert(line < 1<<16); /* marked_for_close can only fit a uint16_t. */
+ tor_assert(file);
+
if (conn->marked_for_close) {
- log(LOG_WARN,LD_BUG,"Duplicate call to connection_mark_for_close at %s:%d"
+ log_warn(LD_BUG,"Duplicate call to connection_mark_for_close at %s:%d"
" (first at %s:%d)", file, line, conn->marked_for_close_file,
conn->marked_for_close);
tor_fragile_assert();
@@ -702,7 +744,8 @@ connection_mark_for_close_(connection_t *conn, int line, const char *file)
* this so we can find things that call this wrongly when the asserts hit.
*/
log_debug(LD_CHANNEL,
- "Calling connection_mark_for_close on an OR conn at %s:%d",
+ "Calling connection_mark_for_close_internal_() on an OR conn "
+ "at %s:%d",
file, line);
}
@@ -1015,6 +1058,7 @@ connection_listener_new(const struct sockaddr *listensockaddr,
if (bind(s, listensockaddr, (socklen_t)sizeof(struct sockaddr_un)) == -1) {
log_warn(LD_NET,"Bind to %s failed: %s.", address,
tor_socket_strerror(tor_socket_errno(s)));
+ tor_close_socket(s);
goto err;
}
#ifdef HAVE_PWD_H
@@ -1023,9 +1067,12 @@ connection_listener_new(const struct sockaddr *listensockaddr,
if (pw == NULL) {
log_warn(LD_NET,"Unable to chown() %s socket: user %s not found.",
address, options->User);
+ tor_close_socket(s);
+ goto err;
} else if (chown(address, pw->pw_uid, pw->pw_gid) < 0) {
log_warn(LD_NET,"Unable to chown() %s socket: %s.",
address, strerror(errno));
+ tor_close_socket(s);
goto err;
}
}
@@ -1079,6 +1126,19 @@ connection_listener_new(const struct sockaddr *listensockaddr,
lis_conn->session_group = global_next_session_group--;
}
}
+ if (type == CONN_TYPE_AP_LISTENER) {
+ lis_conn->socks_ipv4_traffic = port_cfg->ipv4_traffic;
+ lis_conn->socks_ipv6_traffic = port_cfg->ipv6_traffic;
+ lis_conn->socks_prefer_ipv6 = port_cfg->prefer_ipv6;
+ } else {
+ lis_conn->socks_ipv4_traffic = 1;
+ lis_conn->socks_ipv6_traffic = 1;
+ }
+ lis_conn->cache_ipv4_answers = port_cfg->cache_ipv4_answers;
+ lis_conn->cache_ipv6_answers = port_cfg->cache_ipv6_answers;
+ lis_conn->use_cached_ipv4_answers = port_cfg->use_cached_ipv4_answers;
+ lis_conn->use_cached_ipv6_answers = port_cfg->use_cached_ipv6_answers;
+ lis_conn->prefer_ipv6_virtaddr = port_cfg->prefer_ipv6_virtaddr;
if (connection_add(conn) < 0) { /* no space, forget it */
log_warn(LD_NET,"connection_add for listener failed. Giving up.");
@@ -1312,6 +1372,18 @@ connection_init_accepted_conn(connection_t *conn,
TO_ENTRY_CONN(conn)->session_group = listener->session_group;
TO_ENTRY_CONN(conn)->nym_epoch = get_signewnym_epoch();
TO_ENTRY_CONN(conn)->socks_request->listener_type = listener->base_.type;
+ TO_ENTRY_CONN(conn)->ipv4_traffic_ok = listener->socks_ipv4_traffic;
+ TO_ENTRY_CONN(conn)->ipv6_traffic_ok = listener->socks_ipv6_traffic;
+ TO_ENTRY_CONN(conn)->prefer_ipv6_traffic = listener->socks_prefer_ipv6;
+ TO_ENTRY_CONN(conn)->cache_ipv4_answers = listener->cache_ipv4_answers;
+ TO_ENTRY_CONN(conn)->cache_ipv6_answers = listener->cache_ipv6_answers;
+ TO_ENTRY_CONN(conn)->use_cached_ipv4_answers =
+ listener->use_cached_ipv4_answers;
+ TO_ENTRY_CONN(conn)->use_cached_ipv6_answers =
+ listener->use_cached_ipv6_answers;
+ TO_ENTRY_CONN(conn)->prefer_ipv6_virtaddr =
+ listener->prefer_ipv6_virtaddr;
+
switch (TO_CONN(listener)->type) {
case CONN_TYPE_AP_LISTENER:
conn->state = AP_CONN_STATE_SOCKS_WAIT;
@@ -1373,16 +1445,9 @@ connection_connect(connection_t *conn, const char *address,
/* 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 _WIN32
- *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);
- }
+ *socket_error = SOCK_ERRNO(ENETUNREACH);
+ log_fn_ratelim(&disablenet_violated, LOG_WARN, LD_BUG,
+ "Tried to open a socket with DisableNetwork set.");
tor_fragile_assert();
return -1;
}
@@ -1460,7 +1525,7 @@ connection_connect(connection_t *conn, const char *address,
/* it succeeded. we're connected. */
log_fn(inprogress?LOG_DEBUG:LOG_INFO, LD_NET,
- "Connection to %s:%u %s (sock %d).",
+ "Connection to %s:%u %s (sock "TOR_SOCKET_T_FORMAT").",
escaped_safe_str_client(address),
port, inprogress?"in progress":"established", s);
conn->s = s;
@@ -1643,6 +1708,7 @@ connection_read_https_proxy_response(connection_t *conn)
tor_free(headers);
return -1;
}
+ tor_free(headers);
if (!reason) reason = tor_strdup("[no reason given]");
if (status_code == 200) {
@@ -2466,7 +2532,7 @@ connection_bucket_refill_helper(int *bucket, int rate, int burst,
*bucket = burst;
}
}
- log(LOG_DEBUG, LD_NET,"%s now %d.", name, *bucket);
+ log_debug(LD_NET,"%s now %d.", name, *bucket);
}
}
@@ -2727,7 +2793,11 @@ connection_handle_read_impl(connection_t *conn)
}
}
connection_close_immediate(conn); /* Don't flush; connection is dead. */
- connection_mark_for_close(conn);
+ /*
+ * This can bypass normal channel checking since we did
+ * connection_or_notify_error() above.
+ */
+ connection_mark_for_close_internal(conn);
return -1;
}
n_read += buf_datalen(conn->inbuf) - before;
@@ -3208,6 +3278,7 @@ connection_handle_write_impl(connection_t *conn, int force)
ssize_t max_to_write;
time_t now = approx_time();
size_t n_read = 0, n_written = 0;
+ int dont_stop_writing = 0;
tor_assert(!connection_is_listener(conn));
@@ -3243,7 +3314,11 @@ connection_handle_write_impl(connection_t *conn, int force)
tor_socket_strerror(e));
connection_close_immediate(conn);
- connection_mark_for_close(conn);
+ /*
+ * This can bypass normal channel checking since we did
+ * connection_or_notify_error() above.
+ */
+ connection_mark_for_close_internal(conn);
return -1;
} else {
return 0; /* no change, see if next time is better */
@@ -3260,6 +3335,7 @@ connection_handle_write_impl(connection_t *conn, int force)
if (connection_speaks_cells(conn) &&
conn->state > OR_CONN_STATE_PROXY_HANDSHAKING) {
or_connection_t *or_conn = TO_OR_CONN(conn);
+ size_t initial_size;
if (conn->state == OR_CONN_STATE_TLS_HANDSHAKING ||
conn->state == OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING) {
connection_stop_writing(conn);
@@ -3270,7 +3346,11 @@ connection_handle_write_impl(connection_t *conn, int force)
"TLS error in connection_tls_"
"continue_handshake()");
connection_close_immediate(conn);
- connection_mark_for_close(conn);
+ /*
+ * This can bypass normal channel checking since we did
+ * connection_or_notify_error() above.
+ */
+ connection_mark_for_close_internal(conn);
return -1;
}
return 0;
@@ -3279,6 +3359,7 @@ connection_handle_write_impl(connection_t *conn, int force)
}
/* else open, or closing */
+ initial_size = buf_datalen(conn->outbuf);
result = flush_buf_tls(or_conn->tls, conn->outbuf,
max_to_write, &conn->outbuf_flushlen);
@@ -3300,12 +3381,17 @@ connection_handle_write_impl(connection_t *conn, int force)
"TLS error in during flush" :
"TLS closed during flush");
connection_close_immediate(conn);
- connection_mark_for_close(conn);
+ /*
+ * This can bypass normal channel checking since we did
+ * connection_or_notify_error() above.
+ */
+ connection_mark_for_close_internal(conn);
return -1;
case TOR_TLS_WANTWRITE:
log_debug(LD_NET,"wanted write.");
/* we're already writing */
- return 0;
+ dont_stop_writing = 1;
+ break;
case TOR_TLS_WANTREAD:
/* Make sure to avoid a loop if the receive buckets are empty. */
log_debug(LD_NET,"wanted read.");
@@ -3327,6 +3413,12 @@ connection_handle_write_impl(connection_t *conn, int force)
tor_tls_get_n_raw_bytes(or_conn->tls, &n_read, &n_written);
log_debug(LD_GENERAL, "After TLS write of %d: %ld read, %ld written",
result, (long)n_read, (long)n_written);
+ /* So we notice bytes were written even on error */
+ /* XXXX024 This cast is safe since we can never write INT_MAX bytes in a
+ * single set of TLS operations. But it looks kinda ugly. If we refactor
+ * the *_buf_tls functions, we should make them return ssize_t or size_t
+ * or something. */
+ result = (int)(initial_size-buf_datalen(conn->outbuf));
} else {
CONN_LOG_PROTECT(conn,
result = flush_buf(conn->s, conn->outbuf,
@@ -3365,11 +3457,16 @@ connection_handle_write_impl(connection_t *conn, int force)
"connection_flushed_some()");
}
- connection_mark_for_close(conn);
+ /*
+ * This can bypass normal channel checking since we did
+ * connection_or_notify_error() above.
+ */
+ connection_mark_for_close_internal(conn);
}
}
- if (!connection_wants_to_flush(conn)) { /* it's done flushing */
+ if (!connection_wants_to_flush(conn) &&
+ !dont_stop_writing) { /* it's done flushing */
if (connection_finished_flushing(conn) < 0) {
/* already marked */
return -1;
@@ -3813,7 +3910,7 @@ client_check_address_changed(tor_socket_t sock)
} else {
/* The interface changed. We're a client, so we need to regenerate our
* keys. First, reset the state. */
- log(LOG_NOTICE, LD_NET, "Our IP address has changed. Rotating keys...");
+ log_notice(LD_NET, "Our IP address has changed. Rotating keys...");
tor_addr_copy(*last_interface_ip_ptr, &iface_addr);
SMARTLIST_FOREACH(outgoing_addrs, tor_addr_t*, a_ptr, tor_free(a_ptr));
smartlist_clear(outgoing_addrs);
@@ -3897,8 +3994,9 @@ connection_flushed_some(connection_t *conn)
return r;
}
-/** We just finished flushing bytes from conn-\>outbuf, and there
- * are no more bytes remaining.
+/** We just finished flushing bytes to the appropriately low network layer,
+ * and there are no more bytes remaining in conn-\>outbuf, conn-\>bev, or
+ * conn-\>tls to be flushed.
*
* This function just passes conn to the connection-specific
* connection_*_finished_flushing() function.
@@ -4025,14 +4123,14 @@ connection_dump_buffer_mem_stats(int severity)
total_alloc += alloc_by_type[i];
}
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
"In buffers for %d connections: "U64_FORMAT" used/"U64_FORMAT" allocated",
smartlist_len(conns),
U64_PRINTF_ARG(total_used), U64_PRINTF_ARG(total_alloc));
for (i=CONN_TYPE_MIN_; i <= CONN_TYPE_MAX_; ++i) {
if (!n_conns_by_type[i])
continue;
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
" For %d %s connections: "U64_FORMAT" used/"U64_FORMAT" allocated",
n_conns_by_type[i], conn_type_to_string(i),
U64_PRINTF_ARG(used_by_type[i]), U64_PRINTF_ARG(alloc_by_type[i]));
diff --git a/src/or/connection.h b/src/or/connection.h
index 6ed9e4a41f..c78fe6e652 100644
--- a/src/or/connection.h
+++ b/src/or/connection.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -31,21 +31,53 @@ void connection_free(connection_t *conn);
void connection_free_all(void);
void connection_about_to_close_connection(connection_t *conn);
void connection_close_immediate(connection_t *conn);
-void connection_mark_for_close_(connection_t *conn,int line, const char *file);
+void connection_mark_for_close_(connection_t *conn,
+ int line, const char *file);
+void connection_mark_for_close_internal_(connection_t *conn,
+ int line, const char *file);
#define connection_mark_for_close(c) \
connection_mark_for_close_((c), __LINE__, SHORT_FILE__)
+#define connection_mark_for_close_internal(c) \
+ connection_mark_for_close_internal_((c), __LINE__, SHORT_FILE__)
/**
* Mark 'c' for close, but try to hold it open until all the data is written.
+ * Use the _internal versions of connection_mark_for_close; this should be
+ * called when you either are sure that if this is an or_connection_t the
+ * controlling channel has been notified (e.g. with
+ * connection_or_notify_error()), or you actually are the
+ * connection_or_close_for_error() or connection_or_close_normally function.
+ * For all other cases, use connection_mark_and_flush() instead, which
+ * checks for or_connection_t properly, instead. See below.
*/
-#define connection_mark_and_flush_(c,line,file) \
- do { \
- connection_t *tmp_conn_ = (c); \
- connection_mark_for_close_(tmp_conn_, (line), (file)); \
- tmp_conn_->hold_open_until_flushed = 1; \
- IF_HAS_BUFFEREVENT(tmp_conn_, \
- connection_start_writing(tmp_conn_)); \
+#define connection_mark_and_flush_internal_(c,line,file) \
+ do { \
+ connection_t *tmp_conn_ = (c); \
+ connection_mark_for_close_internal_(tmp_conn_, (line), (file)); \
+ tmp_conn_->hold_open_until_flushed = 1; \
+ IF_HAS_BUFFEREVENT(tmp_conn_, \
+ connection_start_writing(tmp_conn_)); \
+ } while (0)
+
+#define connection_mark_and_flush_internal(c) \
+ connection_mark_and_flush_internal_((c), __LINE__, SHORT_FILE__)
+
+/**
+ * Mark 'c' for close, but try to hold it open until all the data is written.
+ */
+#define connection_mark_and_flush_(c,line,file) \
+ do { \
+ connection_t *tmp_conn_ = (c); \
+ if (tmp_conn_->type == CONN_TYPE_OR) { \
+ log_warn(LD_CHANNEL | LD_BUG, \
+ "Something tried to close (and flush) an or_connection_t" \
+ " without going through channels at %s:%d", \
+ file, line); \
+ connection_or_close_for_error(TO_OR_CONN(tmp_conn_), 1); \
+ } else { \
+ connection_mark_and_flush_internal_(c, line, file); \
+ } \
} while (0)
#define connection_mark_and_flush(c) \
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 4d528a810e..b4fa3e6fe2 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -1,15 +1,17 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file connection_edge.c
* \brief Handle edge streams.
**/
+#define CONNECTION_EDGE_PRIVATE
#include "or.h"
+#include "addressmap.h"
#include "buffers.h"
#include "channel.h"
#include "circuitlist.h"
@@ -35,6 +37,7 @@
#include "router.h"
#include "routerlist.h"
#include "routerset.h"
+#include "circuitbuild.h"
#ifdef HAVE_LINUX_TYPES_H
#include <linux/types.h>
@@ -56,9 +59,7 @@
static int connection_ap_handshake_process_socks(entry_connection_t *conn);
static int connection_ap_process_natd(entry_connection_t *conn);
static int connection_exit_connect_dir(edge_connection_t *exitconn);
-static int address_is_in_virtual_range(const char *addr);
static int consider_plaintext_ports(entry_connection_t *conn, uint16_t port);
-static void clear_trackexithost_mappings(const char *exitname);
static int connection_ap_supports_optimistic_data(const entry_connection_t *);
/** An AP stream has failed/finished. If it hasn't already sent back
@@ -124,7 +125,8 @@ connection_edge_reached_eof(edge_connection_t *conn)
/* it still has stuff to process. don't let it die yet. */
return 0;
}
- log_info(LD_EDGE,"conn (fd %d) reached eof. Closing.", conn->base_.s);
+ log_info(LD_EDGE,"conn (fd "TOR_SOCKET_T_FORMAT") reached eof. Closing.",
+ conn->base_.s);
if (!conn->base_.marked_for_close) {
/* only mark it if not already marked. it's possible to
* get the 'end' right around when the client hangs up on us. */
@@ -313,11 +315,13 @@ connection_edge_end(edge_connection_t *conn, uint8_t reason)
}
if (circ && !circ->marked_for_close) {
- log_debug(LD_EDGE,"Sending end on conn (fd %d).",conn->base_.s);
+ log_debug(LD_EDGE,"Sending end on conn (fd "TOR_SOCKET_T_FORMAT").",
+ conn->base_.s);
connection_edge_send_command(conn, RELAY_COMMAND_END,
payload, payload_len);
} else {
- log_debug(LD_EDGE,"No circ to send end on conn (fd %d).",
+ log_debug(LD_EDGE,"No circ to send end on conn "
+ "(fd "TOR_SOCKET_T_FORMAT").",
conn->base_.s);
}
@@ -392,6 +396,48 @@ connection_edge_finished_flushing(edge_connection_t *conn)
return 0;
}
+/** Longest size for the relay payload of a RELAY_CONNECTED cell that we're
+ * able to generate. */
+/* 4 zero bytes; 1 type byte; 16 byte IPv6 address; 4 byte TTL. */
+#define MAX_CONNECTED_CELL_PAYLOAD_LEN 25
+
+/** Set the buffer at <b>payload_out</b> -- which must have at least
+ * MAX_CONNECTED_CELL_PAYLOAD_LEN bytes available -- to the body of a
+ * RELAY_CONNECTED cell indicating that we have connected to <b>addr</b>, and
+ * that the name resolution that led us to <b>addr</b> will be valid for
+ * <b>ttl</b> seconds. Return -1 on error, or the number of bytes used on
+ * success. */
+/* private */int
+connected_cell_format_payload(uint8_t *payload_out,
+ const tor_addr_t *addr,
+ uint32_t ttl)
+{
+ const sa_family_t family = tor_addr_family(addr);
+ int connected_payload_len;
+
+ /* should be needless */
+ memset(payload_out, 0, MAX_CONNECTED_CELL_PAYLOAD_LEN);
+
+ if (family == AF_INET) {
+ set_uint32(payload_out, tor_addr_to_ipv4n(addr));
+ connected_payload_len = 4;
+ } else if (family == AF_INET6) {
+ set_uint32(payload_out, 0);
+ set_uint8(payload_out + 4, 6);
+ memcpy(payload_out + 5, tor_addr_to_in6_addr8(addr), 16);
+ connected_payload_len = 21;
+ } else {
+ return -1;
+ }
+
+ set_uint32(payload_out + connected_payload_len, htonl(dns_clip_ttl(ttl)));
+ connected_payload_len += 4;
+
+ tor_assert(connected_payload_len <= MAX_CONNECTED_CELL_PAYLOAD_LEN);
+
+ return connected_payload_len;
+}
+
/** Connected handler for exit connections: start writing pending
* data, deliver 'CONNECTED' relay cells as appropriate, and check
* any pending data that may have been received. */
@@ -423,22 +469,16 @@ connection_edge_finished_connecting(edge_connection_t *edge_conn)
RELAY_COMMAND_CONNECTED, NULL, 0) < 0)
return 0; /* circuit is closed, don't continue */
} else {
- char connected_payload[20];
- int connected_payload_len;
- if (tor_addr_family(&conn->addr) == AF_INET) {
- set_uint32(connected_payload, tor_addr_to_ipv4n(&conn->addr));
- set_uint32(connected_payload+4,
- htonl(dns_clip_ttl(edge_conn->address_ttl)));
- connected_payload_len = 8;
- } else {
- memcpy(connected_payload, tor_addr_to_in6_addr8(&conn->addr), 16);
- set_uint32(connected_payload+16,
- htonl(dns_clip_ttl(edge_conn->address_ttl)));
- connected_payload_len = 20;
- }
+ uint8_t connected_payload[MAX_CONNECTED_CELL_PAYLOAD_LEN];
+ int connected_payload_len =
+ connected_cell_format_payload(connected_payload, &conn->addr,
+ edge_conn->address_ttl);
+ if (connected_payload_len < 0)
+ return -1;
+
if (connection_edge_send_command(edge_conn,
- RELAY_COMMAND_CONNECTED,
- connected_payload, connected_payload_len) < 0)
+ RELAY_COMMAND_CONNECTED,
+ (char*)connected_payload, connected_payload_len) < 0)
return 0; /* circuit is closed, don't continue */
}
tor_assert(edge_conn->package_window > 0);
@@ -602,6 +642,10 @@ connection_ap_expire_beginning(void)
" '%s.onion'.",
seconds_idle,
safe_str_client(entry_conn->socks_request->address));
+ /* Roll back path bias use state so that we probe the circuit
+ * if nothing else succeeds on it */
+ pathbias_mark_use_rollback(TO_ORIGIN_CIRCUIT(circ));
+
connection_edge_end(conn, END_STREAM_REASON_TIMEOUT);
connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TIMEOUT);
}
@@ -754,7 +798,7 @@ circuit_discard_optional_exit_enclaves(extend_info_t *info)
/** The AP connection <b>conn</b> has just failed while attaching or
* sending a BEGIN or resolving on <b>circ</b>, but another circuit
* might work. Detach the circuit, and either reattach it, launch a
- * new circuit, tell the controller, or give up as a appropriate.
+ * new circuit, tell the controller, or give up as appropriate.
*
* Returns -1 on err, 1 on success, 0 on not-yet-sure.
*/
@@ -766,6 +810,10 @@ connection_ap_detach_retriable(entry_connection_t *conn,
control_event_stream_status(conn, STREAM_EVENT_FAILED_RETRIABLE, reason);
ENTRY_TO_CONN(conn)->timestamp_lastread = time(NULL);
+ /* Roll back path bias use state so that we probe the circuit
+ * if nothing else succeeds on it */
+ pathbias_mark_use_rollback(circ);
+
if (conn->pending_optimistic_data) {
generic_buffer_set_to_copy(&conn->sending_optimistic_data,
conn->pending_optimistic_data);
@@ -784,957 +832,16 @@ connection_ap_detach_retriable(entry_connection_t *conn,
}
}
-/** A client-side struct to remember requests to rewrite addresses
- * to new addresses. These structs are stored in the hash table
- * "addressmap" below.
- *
- * There are 5 ways to set an address mapping:
- * - A MapAddress command from the controller [permanent]
- * - An AddressMap directive in the torrc [permanent]
- * - When a TrackHostExits torrc directive is triggered [temporary]
- * - When a DNS resolve succeeds [temporary]
- * - When a DNS resolve fails [temporary]
- *
- * When an addressmap request is made but one is already registered,
- * the new one is replaced only if the currently registered one has
- * no "new_address" (that is, it's in the process of DNS resolve),
- * or if the new one is permanent (expires==0 or 1).
- *
- * (We overload the 'expires' field, using "0" for mappings set via
- * 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;
-
-/** Entry for mapping addresses to which virtual address we mapped them to. */
-typedef struct {
- char *ipv4_address;
- char *hostname_address;
-} virtaddress_entry_t;
-
-/** A hash table to store client-side address rewrite instructions. */
-static strmap_t *addressmap=NULL;
-/**
- * Table mapping addresses to which virtual address, if any, we
- * assigned them to.
- *
- * We maintain the following invariant: if [A,B] is in
- * virtaddress_reversemap, then B must be a virtual address, and [A,B]
- * must be in addressmap. We do not require that the converse hold:
- * if it fails, then we could end up mapping two virtual addresses to
- * the same address, which is no disaster.
- **/
-static strmap_t *virtaddress_reversemap=NULL;
-
-/** Initialize addressmap. */
-void
-addressmap_init(void)
-{
- addressmap = strmap_new();
- virtaddress_reversemap = strmap_new();
-}
-
-/** Free the memory associated with the addressmap entry <b>_ent</b>. */
-static void
-addressmap_ent_free(void *_ent)
-{
- addressmap_entry_t *ent;
- if (!_ent)
- return;
-
- ent = _ent;
- tor_free(ent->new_address);
- tor_free(ent);
-}
-
-/** Free storage held by a virtaddress_entry_t* entry in <b>ent</b>. */
-static void
-addressmap_virtaddress_ent_free(void *_ent)
-{
- virtaddress_entry_t *ent;
- if (!_ent)
- return;
-
- ent = _ent;
- tor_free(ent->ipv4_address);
- tor_free(ent->hostname_address);
- tor_free(ent);
-}
-
-/** Free storage held by a virtaddress_entry_t* entry in <b>ent</b>. */
-static void
-addressmap_virtaddress_remove(const char *address, addressmap_entry_t *ent)
-{
- if (ent && ent->new_address &&
- address_is_in_virtual_range(ent->new_address)) {
- virtaddress_entry_t *ve =
- strmap_get(virtaddress_reversemap, ent->new_address);
- /*log_fn(LOG_NOTICE,"remove reverse mapping for %s",ent->new_address);*/
- if (ve) {
- if (!strcmp(address, ve->ipv4_address))
- tor_free(ve->ipv4_address);
- if (!strcmp(address, ve->hostname_address))
- tor_free(ve->hostname_address);
- if (!ve->ipv4_address && !ve->hostname_address) {
- tor_free(ve);
- strmap_remove(virtaddress_reversemap, ent->new_address);
- }
- }
- }
-}
-
-/** Remove <b>ent</b> (which must be mapped to by <b>address</b>) from the
- * client address maps. */
-static void
-addressmap_ent_remove(const char *address, addressmap_entry_t *ent)
-{
- addressmap_virtaddress_remove(address, ent);
- addressmap_ent_free(ent);
-}
-
-/** Unregister all TrackHostExits mappings from any address to
- * *.exitname.exit. */
-static void
-clear_trackexithost_mappings(const char *exitname)
-{
- char *suffix = NULL;
- if (!addressmap || !exitname)
- return;
- tor_asprintf(&suffix, ".%s.exit", exitname);
- tor_strlower(suffix);
-
- STRMAP_FOREACH_MODIFY(addressmap, address, addressmap_entry_t *, ent) {
- if (ent->source == ADDRMAPSRC_TRACKEXIT &&
- !strcmpend(ent->new_address, suffix)) {
- addressmap_ent_remove(address, ent);
- MAP_DEL_CURRENT(address);
- }
- } STRMAP_FOREACH_END;
-
- tor_free(suffix);
-}
-
-/** Remove all TRACKEXIT mappings from the addressmap for which the target
- * host is unknown or no longer allowed, or for which the source address
- * is no longer in trackexithosts. */
-void
-addressmap_clear_excluded_trackexithosts(const or_options_t *options)
-{
- const routerset_t *allow_nodes = options->ExitNodes;
- const routerset_t *exclude_nodes = options->ExcludeExitNodesUnion_;
-
- if (!addressmap)
- return;
- if (routerset_is_empty(allow_nodes))
- allow_nodes = NULL;
- if (allow_nodes == NULL && routerset_is_empty(exclude_nodes))
- return;
-
- STRMAP_FOREACH_MODIFY(addressmap, address, addressmap_entry_t *, ent) {
- size_t len;
- const char *target = ent->new_address, *dot;
- char *nodename;
- const node_t *node;
-
- if (!target) {
- /* DNS resolving in progress */
- continue;
- } else if (strcmpend(target, ".exit")) {
- /* Not a .exit mapping */
- continue;
- } else if (ent->source != ADDRMAPSRC_TRACKEXIT) {
- /* Not a trackexit mapping. */
- continue;
- }
- len = strlen(target);
- if (len < 6)
- continue; /* malformed. */
- dot = target + len - 6; /* dot now points to just before .exit */
- while (dot > target && *dot != '.')
- dot--;
- if (*dot == '.') dot++;
- nodename = tor_strndup(dot, len-5-(dot-target));;
- node = node_get_by_nickname(nodename, 0);
- tor_free(nodename);
- if (!node ||
- (allow_nodes && !routerset_contains_node(allow_nodes, node)) ||
- routerset_contains_node(exclude_nodes, node) ||
- !hostname_in_track_host_exits(options, address)) {
- /* We don't know this one, or we want to be rid of it. */
- addressmap_ent_remove(address, ent);
- MAP_DEL_CURRENT(address);
- }
- } STRMAP_FOREACH_END;
-}
-
-/** Remove all AUTOMAP mappings from the addressmap for which the
- * source address no longer matches AutomapHostsSuffixes, which is
- * no longer allowed by AutomapHostsOnResolve, or for which the
- * target address is no longer in the virtual network. */
-void
-addressmap_clear_invalid_automaps(const or_options_t *options)
-{
- int clear_all = !options->AutomapHostsOnResolve;
- const smartlist_t *suffixes = options->AutomapHostsSuffixes;
-
- if (!addressmap)
- return;
-
- if (!suffixes)
- clear_all = 1; /* This should be impossible, but let's be sure. */
-
- STRMAP_FOREACH_MODIFY(addressmap, src_address, addressmap_entry_t *, ent) {
- int remove = clear_all;
- if (ent->source != ADDRMAPSRC_AUTOMAP)
- continue; /* not an automap mapping. */
-
- if (!remove) {
- int suffix_found = 0;
- SMARTLIST_FOREACH(suffixes, const char *, suffix, {
- if (!strcasecmpend(src_address, suffix)) {
- suffix_found = 1;
- break;
- }
- });
- if (!suffix_found)
- remove = 1;
- }
-
- if (!remove && ! address_is_in_virtual_range(ent->new_address))
- remove = 1;
-
- if (remove) {
- addressmap_ent_remove(src_address, ent);
- MAP_DEL_CURRENT(src_address);
- }
- } STRMAP_FOREACH_END;
-}
-
-/** Remove all entries from the addressmap that were set via the
- * configuration file or the command line. */
-void
-addressmap_clear_configured(void)
-{
- addressmap_get_mappings(NULL, 0, 0, 0);
-}
-
-/** Remove all entries from the addressmap that are set to expire, ever. */
-void
-addressmap_clear_transient(void)
-{
- addressmap_get_mappings(NULL, 2, TIME_MAX, 0);
-}
-
-/** Clean out entries from the addressmap cache that were
- * added long enough ago that they are no longer valid.
- */
-void
-addressmap_clean(time_t now)
-{
- addressmap_get_mappings(NULL, 2, now, 0);
-}
-
-/** Free all the elements in the addressmap, and free the addressmap
- * itself. */
-void
-addressmap_free_all(void)
-{
- strmap_free(addressmap, addressmap_ent_free);
- addressmap = NULL;
-
- strmap_free(virtaddress_reversemap, addressmap_virtaddress_ent_free);
- 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
- * address changed; false otherwise. Set *<b>expires_out</b> to the
- * expiry time of the result, or to <b>time_max</b> if the result does
- * not expire.
- *
- * If <b>exit_source_out</b> is non-null, we set it as follows. If we the
- * address starts out as a non-exit address, and we remap it to an .exit
- * address at any point, then set *<b>exit_source_out</b> to the
- * address_entry_source_t of the first such rule. Set *<b>exit_source_out</b>
- * to ADDRMAPSRC_NONE if there is no such rewrite, or if the original address
- * was a .exit.
- */
-int
-addressmap_rewrite(char *address, size_t maxlen, time_t *expires_out,
- addressmap_entry_source_t *exit_source_out)
-{
- addressmap_entry_t *ent;
- int rewrites;
- time_t expires = TIME_MAX;
- addressmap_entry_source_t exit_source = ADDRMAPSRC_NONE;
- char *addr_orig = tor_strdup(address);
- char *log_addr_orig = NULL;
-
- for (rewrites = 0; rewrites < 16; rewrites++) {
- int exact_match = 0;
- log_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" */
- goto done;
- }
-
- exact_match = 1;
- }
-
- if (!ent || !ent->new_address) {
- goto done;
- }
-
- if (ent->dst_wildcard && !exact_match) {
- strlcat(address, ".", maxlen);
- strlcat(address, ent->new_address, maxlen);
- } else {
- strlcpy(address, ent->new_address, maxlen);
- }
-
- if (!strcmpend(address, ".exit") &&
- strcmpend(addr_orig, ".exit") &&
- exit_source == ADDRMAPSRC_NONE) {
- exit_source = ent->source;
- }
-
- log_info(LD_APP, "Addressmap: rewriting %s to %s",
- log_addr_orig, escaped_safe_str_client(address));
- if (ent->expires > 1 && ent->expires < expires)
- expires = ent->expires;
-
- tor_free(log_addr_orig);
- }
- log_warn(LD_CONFIG,
- "Loop detected: we've rewritten %s 16 times! Using it as-is.",
- escaped_safe_str_client(address));
- /* it's fine to rewrite a rewrite, but don't loop forever */
-
- done:
- tor_free(addr_orig);
- tor_free(log_addr_orig);
- if (exit_source_out)
- *exit_source_out = exit_source;
- if (expires_out)
- *expires_out = TIME_MAX;
- return (rewrites > 0);
-}
-
-/** If we have a cached reverse DNS entry for the address stored in the
- * <b>maxlen</b>-byte buffer <b>address</b> (typically, a dotted quad) then
- * rewrite to the cached value and return 1. Otherwise return 0. Set
- * *<b>expires_out</b> to the expiry time of the result, or to <b>time_max</b>
- * if the result does not expire. */
-static int
-addressmap_rewrite_reverse(char *address, size_t maxlen, time_t *expires_out)
-{
- char *s, *cp;
- addressmap_entry_t *ent;
- int r = 0;
- tor_asprintf(&s, "REVERSE[%s]", address);
- ent = strmap_get(addressmap, s);
- if (ent) {
- cp = tor_strdup(escaped_safe_str_client(ent->new_address));
- log_info(LD_APP, "Rewrote reverse lookup %s -> %s",
- escaped_safe_str_client(s), cp);
- tor_free(cp);
- strlcpy(address, ent->new_address, maxlen);
- r = 1;
- }
-
- if (expires_out)
- *expires_out = (ent && ent->expires > 1) ? ent->expires : TIME_MAX;
-
- tor_free(s);
- return r;
-}
-
-/** Return 1 if <b>address</b> is already registered, else return 0. If address
- * is already registered, and <b>update_expires</b> is non-zero, then update
- * the expiry time on the mapping with update_expires if it is a
- * mapping created by TrackHostExits. */
-int
-addressmap_have_mapping(const char *address, int update_expiry)
-{
- addressmap_entry_t *ent;
- if (!(ent=strmap_get_lc(addressmap, address)))
- return 0;
- if (update_expiry && ent->source==ADDRMAPSRC_TRACKEXIT)
- ent->expires=time(NULL) + update_expiry;
- return 1;
-}
-
-/** Register a request to map <b>address</b> to <b>new_address</b>,
- * which will expire on <b>expires</b> (or 0 if never expires from
- * config file, 1 if never expires from controller, 2 if never expires
- * (virtual address mapping) from the controller.)
- *
- * <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>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,
- 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) &&
- wildcard_addr == wildcard_new_addr)) {
- /* Remove the mapping, if any. */
- tor_free(new_address);
- if (ent) {
- addressmap_ent_remove(address,ent);
- strmap_remove(addressmap, address);
- }
- return;
- }
- if (!ent) { /* make a new one and register it */
- ent = tor_malloc_zero(sizeof(addressmap_entry_t));
- strmap_set(addressmap, address, ent);
- } else if (ent->new_address) { /* we need to clean up the old mapping. */
- if (expires > 1) {
- log_info(LD_APP,"Temporary addressmap ('%s' to '%s') not performed, "
- "since it's already mapped to '%s'",
- safe_str_client(address),
- safe_str_client(new_address),
- safe_str_client(ent->new_address));
- tor_free(new_address);
- return;
- }
- if (address_is_in_virtual_range(ent->new_address) &&
- expires != 2) {
- /* XXX This isn't the perfect test; we want to avoid removing
- * mappings set from the control interface _as virtual mapping */
- addressmap_virtaddress_remove(address, ent);
- }
- tor_free(ent->new_address);
- } /* else { we have an in-progress resolve with no mapping. } */
-
- ent->new_address = new_address;
- 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),
- safe_str_client(ent->new_address));
- control_event_address_mapped(address, ent->new_address, expires, NULL);
-}
-
-/** An attempt to resolve <b>address</b> failed at some OR.
- * Increment the number of resolve failures we have on record
- * for it, and then return that number.
- */
-int
-client_dns_incr_failures(const char *address)
-{
- addressmap_entry_t *ent = strmap_get(addressmap, address);
- if (!ent) {
- ent = tor_malloc_zero(sizeof(addressmap_entry_t));
- ent->expires = time(NULL) + MAX_DNS_ENTRY_AGE;
- strmap_set(addressmap,address,ent);
- }
- if (ent->num_resolve_failures < SHORT_MAX)
- ++ent->num_resolve_failures; /* don't overflow */
- log_info(LD_APP, "Address %s now has %d resolve failures.",
- safe_str_client(address),
- ent->num_resolve_failures);
- return ent->num_resolve_failures;
-}
-
-/** If <b>address</b> is in the client DNS addressmap, reset
- * the number of resolve failures we have on record for it.
- * This is used when we fail a stream because it won't resolve:
- * otherwise future attempts on that address will only try once.
- */
-void
-client_dns_clear_failures(const char *address)
-{
- addressmap_entry_t *ent = strmap_get(addressmap, address);
- if (ent)
- ent->num_resolve_failures = 0;
-}
-
-/** Record the fact that <b>address</b> resolved to <b>name</b>.
- * We can now use this in subsequent streams via addressmap_rewrite()
- * so we can more correctly choose an exit that will allow <b>address</b>.
- *
- * If <b>exitname</b> is defined, then append the addresses with
- * ".exitname.exit" before registering the mapping.
- *
- * If <b>ttl</b> is nonnegative, the mapping will be valid for
- * <b>ttl</b>seconds; otherwise, we use the default.
- */
-static void
-client_dns_set_addressmap_impl(const char *address, const char *name,
- const char *exitname,
- int ttl)
-{
- /* <address>.<hex or nickname>.exit\0 or just <address>\0 */
- char extendedaddress[MAX_SOCKS_ADDR_LEN+MAX_VERBOSE_NICKNAME_LEN+10];
- /* 123.123.123.123.<hex or nickname>.exit\0 or just 123.123.123.123\0 */
- char extendedval[INET_NTOA_BUF_LEN+MAX_VERBOSE_NICKNAME_LEN+10];
-
- tor_assert(address);
- tor_assert(name);
-
- if (ttl<0)
- ttl = DEFAULT_DNS_TTL;
- else
- ttl = dns_clip_ttl(ttl);
-
- if (exitname) {
- /* XXXX fails to ever get attempts to get an exit address of
- * google.com.digest[=~]nickname.exit; we need a syntax for this that
- * won't make strict RFC952-compliant applications (like us) barf. */
- tor_snprintf(extendedaddress, sizeof(extendedaddress),
- "%s.%s.exit", address, exitname);
- tor_snprintf(extendedval, sizeof(extendedval),
- "%s.%s.exit", name, exitname);
- } else {
- tor_snprintf(extendedaddress, sizeof(extendedaddress),
- "%s", address);
- tor_snprintf(extendedval, sizeof(extendedval),
- "%s", name);
- }
- addressmap_register(extendedaddress, tor_strdup(extendedval),
- time(NULL) + ttl, ADDRMAPSRC_DNS, 0, 0);
-}
-
-/** Record the fact that <b>address</b> resolved to <b>val</b>.
- * We can now use this in subsequent streams via addressmap_rewrite()
- * so we can more correctly choose an exit that will allow <b>address</b>.
- *
- * If <b>exitname</b> is defined, then append the addresses with
- * ".exitname.exit" before registering the mapping.
- *
- * If <b>ttl</b> is nonnegative, the mapping will be valid for
- * <b>ttl</b>seconds; otherwise, we use the default.
- */
-void
-client_dns_set_addressmap(const char *address, uint32_t val,
- const char *exitname,
- int ttl)
-{
- struct in_addr in;
- char valbuf[INET_NTOA_BUF_LEN];
-
- tor_assert(address);
-
- if (tor_inet_aton(address, &in))
- return; /* If address was an IP address already, don't add a mapping. */
- in.s_addr = htonl(val);
- tor_inet_ntoa(&in,valbuf,sizeof(valbuf));
-
- client_dns_set_addressmap_impl(address, valbuf, exitname, ttl);
-}
-
-/** Add a cache entry noting that <b>address</b> (ordinarily a dotted quad)
- * resolved via a RESOLVE_PTR request to the hostname <b>v</b>.
- *
- * If <b>exitname</b> is defined, then append the addresses with
- * ".exitname.exit" before registering the mapping.
- *
- * If <b>ttl</b> is nonnegative, the mapping will be valid for
- * <b>ttl</b>seconds; otherwise, we use the default.
- */
-static void
-client_dns_set_reverse_addressmap(const char *address, const char *v,
- const char *exitname,
- int ttl)
-{
- char *s = NULL;
- tor_asprintf(&s, "REVERSE[%s]", address);
- client_dns_set_addressmap_impl(s, v, exitname, ttl);
- tor_free(s);
-}
-
-/* By default, we hand out 127.192.0.1 through 127.254.254.254.
- * These addresses should map to localhost, so even if the
- * application accidentally tried to connect to them directly (not
- * via Tor), it wouldn't get too far astray.
- *
- * These options are configured by parse_virtual_addr_network().
- */
-/** Which network should we use for virtual IPv4 addresses? Only the first
- * bits of this value are fixed. */
-static uint32_t virtual_addr_network = 0x7fc00000u;
-/** How many bits of <b>virtual_addr_network</b> are fixed? */
-static maskbits_t virtual_addr_netmask_bits = 10;
-/** What's the next virtual address we will hand out? */
-static uint32_t next_virtual_addr = 0x7fc00000u;
-
-/** Read a netmask of the form 127.192.0.0/10 from "val", and check whether
- * it's a valid set of virtual addresses to hand out in response to MAPADDRESS
- * requests. Return 0 on success; set *msg (if provided) to a newly allocated
- * string and return -1 on failure. If validate_only is false, sets the
- * actual virtual address range to the parsed value. */
-int
-parse_virtual_addr_network(const char *val, int validate_only,
- char **msg)
-{
- uint32_t addr;
- uint16_t port_min, port_max;
- maskbits_t bits;
-
- if (parse_addr_and_port_range(val, &addr, &bits, &port_min, &port_max)) {
- if (msg) *msg = tor_strdup("Error parsing VirtualAddressNetwork");
- return -1;
- }
-
- if (port_min != 1 || port_max != 65535) {
- if (msg) *msg = tor_strdup("Can't specify ports on VirtualAddressNetwork");
- return -1;
- }
-
- if (bits > 16) {
- if (msg) *msg = tor_strdup("VirtualAddressNetwork expects a /16 "
- "network or larger");
- return -1;
- }
-
- if (validate_only)
- return 0;
-
- virtual_addr_network = (uint32_t)( addr & (0xfffffffful << (32-bits)) );
- virtual_addr_netmask_bits = bits;
-
- if (addr_mask_cmp_bits(next_virtual_addr, addr, bits))
- next_virtual_addr = addr;
-
- return 0;
-}
-
-/**
- * Return true iff <b>addr</b> is likely to have been returned by
- * client_dns_get_unused_address.
- **/
-static int
-address_is_in_virtual_range(const char *address)
-{
- struct in_addr in;
- tor_assert(address);
- if (!strcasecmpend(address, ".virtual")) {
- return 1;
- } else if (tor_inet_aton(address, &in)) {
- uint32_t addr = ntohl(in.s_addr);
- if (!addr_mask_cmp_bits(addr, virtual_addr_network,
- virtual_addr_netmask_bits))
- return 1;
- }
- return 0;
-}
-
-/** Increment the value of next_virtual_addr; reset it to the start of the
- * virtual address range if it wraps around.
- */
-static INLINE void
-increment_virtual_addr(void)
-{
- ++next_virtual_addr;
- if (addr_mask_cmp_bits(next_virtual_addr, virtual_addr_network,
- virtual_addr_netmask_bits))
- next_virtual_addr = virtual_addr_network;
-}
-
-/** Return a newly allocated string holding an address of <b>type</b>
- * (one of RESOLVED_TYPE_{IPV4|HOSTNAME}) that has not yet been mapped,
- * and that is very unlikely to be the address of any real host.
- *
- * May return NULL if we have run out of virtual addresses.
- */
-static char *
-addressmap_get_virtual_address(int type)
-{
- char buf[64];
- tor_assert(addressmap);
-
- if (type == RESOLVED_TYPE_HOSTNAME) {
- char rand[10];
- do {
- crypto_rand(rand, sizeof(rand));
- base32_encode(buf,sizeof(buf),rand,sizeof(rand));
- strlcat(buf, ".virtual", sizeof(buf));
- } while (strmap_get(addressmap, buf));
- return tor_strdup(buf);
- } else if (type == RESOLVED_TYPE_IPV4) {
- // This is an imperfect estimate of how many addresses are available, but
- // that's ok.
- struct in_addr in;
- uint32_t available = 1u << (32-virtual_addr_netmask_bits);
- while (available) {
- /* Don't hand out any .0 or .255 address. */
- while ((next_virtual_addr & 0xff) == 0 ||
- (next_virtual_addr & 0xff) == 0xff) {
- increment_virtual_addr();
- if (! --available) {
- log_warn(LD_CONFIG, "Ran out of virtual addresses!");
- return NULL;
- }
- }
- in.s_addr = htonl(next_virtual_addr);
- tor_inet_ntoa(&in, buf, sizeof(buf));
- if (!strmap_get(addressmap, buf)) {
- increment_virtual_addr();
- break;
- }
-
- increment_virtual_addr();
- --available;
- // log_info(LD_CONFIG, "%d addrs available", (int)available);
- if (! available) {
- log_warn(LD_CONFIG, "Ran out of virtual addresses!");
- return NULL;
- }
- }
- return tor_strdup(buf);
- } else {
- log_warn(LD_BUG, "Called with unsupported address type (%d)", type);
- return NULL;
- }
-}
-
-/** A controller has requested that we map some address of type
- * <b>type</b> to the address <b>new_address</b>. Choose an address
- * that is unlikely to be used, and map it, and return it in a newly
- * allocated string. If another address of the same type is already
- * mapped to <b>new_address</b>, try to return a copy of that address.
- *
- * The string in <b>new_address</b> may be freed or inserted into a map
- * as appropriate. May return NULL if are out of virtual addresses.
- **/
-const char *
-addressmap_register_virtual_address(int type, char *new_address)
-{
- char **addrp;
- virtaddress_entry_t *vent;
- int vent_needs_to_be_added = 0;
-
- tor_assert(new_address);
- tor_assert(addressmap);
- tor_assert(virtaddress_reversemap);
-
- vent = strmap_get(virtaddress_reversemap, new_address);
- if (!vent) {
- vent = tor_malloc_zero(sizeof(virtaddress_entry_t));
- vent_needs_to_be_added = 1;
- }
-
- addrp = (type == RESOLVED_TYPE_IPV4) ?
- &vent->ipv4_address : &vent->hostname_address;
- if (*addrp) {
- addressmap_entry_t *ent = strmap_get(addressmap, *addrp);
- if (ent && ent->new_address &&
- !strcasecmp(new_address, ent->new_address)) {
- tor_free(new_address);
- tor_assert(!vent_needs_to_be_added);
- return tor_strdup(*addrp);
- } else
- log_warn(LD_BUG,
- "Internal confusion: I thought that '%s' was mapped to by "
- "'%s', but '%s' really maps to '%s'. This is a harmless bug.",
- safe_str_client(new_address),
- safe_str_client(*addrp),
- safe_str_client(*addrp),
- ent?safe_str_client(ent->new_address):"(nothing)");
- }
-
- tor_free(*addrp);
- *addrp = addressmap_get_virtual_address(type);
- if (!*addrp) {
- tor_free(vent);
- tor_free(new_address);
- return NULL;
- }
- 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, 0, 0);
-
-#if 0
- {
- /* Try to catch possible bugs */
- addressmap_entry_t *ent;
- ent = strmap_get(addressmap, *addrp);
- tor_assert(ent);
- tor_assert(!strcasecmp(ent->new_address,new_address));
- vent = strmap_get(virtaddress_reversemap, new_address);
- tor_assert(vent);
- tor_assert(!strcasecmp(*addrp,
- (type == RESOLVED_TYPE_IPV4) ?
- vent->ipv4_address : vent->hostname_address));
- log_info(LD_APP, "Map from %s to %s okay.",
- safe_str_client(*addrp),
- safe_str_client(new_address));
- }
-#endif
-
- return *addrp;
-}
-
-/** Return 1 if <b>address</b> has funny characters in it like colons. Return
- * 0 if it's fine, or if we're configured to allow it anyway. <b>client</b>
- * should be true if we're using this address as a client; false if we're
- * using it as a server.
- */
-int
-address_is_invalid_destination(const char *address, int client)
-{
- if (client) {
- if (get_options()->AllowNonRFC953Hostnames)
- return 0;
- } else {
- if (get_options()->ServerDNSAllowNonRFC953Hostnames)
- return 0;
- }
-
- while (*address) {
- if (TOR_ISALNUM(*address) ||
- *address == '-' ||
- *address == '.' ||
- *address == '_') /* Underscore is not allowed, but Windows does it
- * sometimes, just to thumb its nose at the IETF. */
- ++address;
- else
- return 1;
- }
- return 0;
-}
-
-/** Iterate over all address mappings which have expiry times between
- * min_expires and max_expires, inclusive. If sl is provided, add an
- * "old-addr new-addr expiry" string to sl for each mapping, omitting
- * the expiry time if want_expiry is false. If sl is NULL, remove the
- * mappings.
- */
-void
-addressmap_get_mappings(smartlist_t *sl, time_t min_expires,
- time_t max_expires, int want_expiry)
-{
- strmap_iter_t *iter;
- const char *key;
- void *val_;
- addressmap_entry_t *val;
-
- if (!addressmap)
- addressmap_init();
-
- for (iter = strmap_iter_init(addressmap); !strmap_iter_done(iter); ) {
- strmap_iter_get(iter, &key, &val_);
- val = val_;
- if (val->expires >= min_expires && val->expires <= max_expires) {
- if (!sl) {
- iter = strmap_iter_next_rmv(addressmap,iter);
- addressmap_ent_remove(key, val);
- continue;
- } else if (val->new_address) {
- const char *src_wc = val->src_wildcard ? "*." : "";
- const char *dst_wc = val->dst_wildcard ? "*." : "";
- if (want_expiry) {
- if (val->expires < 3 || val->expires == TIME_MAX)
- smartlist_add_asprintf(sl, "%s%s %s%s NEVER",
- src_wc, key, dst_wc, val->new_address);
- else {
- char time[ISO_TIME_LEN+1];
- format_iso_time(time, val->expires);
- smartlist_add_asprintf(sl, "%s%s %s%s \"%s\"",
- src_wc, key, dst_wc, val->new_address,
- time);
- }
- } else {
- smartlist_add_asprintf(sl, "%s%s %s%s",
- src_wc, key, dst_wc, val->new_address);
- }
- }
- }
- iter = strmap_iter_next(addressmap,iter);
- }
-}
-
/** Check if <b>conn</b> is using a dangerous port. Then warn and/or
* reject depending on our config options. */
static int
consider_plaintext_ports(entry_connection_t *conn, uint16_t port)
{
const or_options_t *options = get_options();
- int reject = smartlist_string_num_isin(options->RejectPlaintextPorts, port);
+ int reject = smartlist_contains_int_as_string(
+ options->RejectPlaintextPorts, port);
- if (smartlist_string_num_isin(options->WarnPlaintextPorts, port)) {
+ if (smartlist_contains_int_as_string(options->WarnPlaintextPorts, port)) {
log_warn(LD_APP, "Application request to port %d: this port is "
"commonly used for unencrypted protocols. Please make sure "
"you don't send anything you would mind the rest of the "
@@ -1800,7 +907,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
socks_request_t *socks = conn->socks_request;
hostname_type_t addresstype;
const or_options_t *options = get_options();
- struct in_addr addr_tmp;
+ tor_addr_t addr_tmp;
/* We set this to true if this is an address we should automatically
* remap to a local address in VirtualAddrNetwork */
int automap = 0;
@@ -1830,17 +937,20 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
conn->original_dest_address = tor_strdup(conn->socks_request->address);
if (socks->command == SOCKS_COMMAND_RESOLVE &&
- !tor_inet_aton(socks->address, &addr_tmp) &&
- options->AutomapHostsOnResolve && options->AutomapHostsSuffixes) {
- SMARTLIST_FOREACH(options->AutomapHostsSuffixes, const char *, cp,
- if (!strcasecmpend(socks->address, cp)) {
- automap = 1;
- break;
- });
+ tor_addr_parse(&addr_tmp, socks->address)<0 &&
+ options->AutomapHostsOnResolve) {
+ automap = addressmap_address_should_automap(socks->address, options);
if (automap) {
const char *new_addr;
+ int addr_type = RESOLVED_TYPE_IPV4;
+ if (conn->socks_request->socks_version != 4) {
+ if (!conn->ipv4_traffic_ok ||
+ (conn->ipv6_traffic_ok && conn->prefer_ipv6_traffic) ||
+ conn->prefer_ipv6_virtaddr)
+ addr_type = RESOLVED_TYPE_IPV6;
+ }
new_addr = addressmap_register_virtual_address(
- RESOLVED_TYPE_IPV4, tor_strdup(socks->address));
+ addr_type, tor_strdup(socks->address));
if (! new_addr) {
log_warn(LD_APP, "Unable to automap address %s",
escaped_safe_str(socks->address));
@@ -1855,8 +965,14 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
}
if (socks->command == SOCKS_COMMAND_RESOLVE_PTR) {
+ unsigned rewrite_flags = 0;
+ if (conn->use_cached_ipv4_answers)
+ rewrite_flags |= AMR_FLAG_USE_IPV4_DNS;
+ if (conn->use_cached_ipv6_answers)
+ rewrite_flags |= AMR_FLAG_USE_IPV6_DNS;
+
if (addressmap_rewrite_reverse(socks->address, sizeof(socks->address),
- &map_expires)) {
+ rewrite_flags, &map_expires)) {
char *result = tor_strdup(socks->address);
/* remember _what_ is supposed to have been resolved. */
tor_snprintf(socks->address, sizeof(socks->address), "REVERSE[%s]",
@@ -1887,8 +1003,13 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
}
} else if (!automap) {
/* For address map controls, remap the address. */
+ unsigned rewrite_flags = 0;
+ if (conn->use_cached_ipv4_answers)
+ rewrite_flags |= AMR_FLAG_USE_IPV4_DNS;
+ if (conn->use_cached_ipv6_answers)
+ rewrite_flags |= AMR_FLAG_USE_IPV6_DNS;
if (addressmap_rewrite(socks->address, sizeof(socks->address),
- &map_expires, &exit_source)) {
+ rewrite_flags, &map_expires, &exit_source)) {
control_event_stream_status(conn, STREAM_EVENT_REMAP,
REMAP_STREAM_SOURCE_CACHE);
}
@@ -2089,6 +1210,37 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
}
}
+ {
+ tor_addr_t addr;
+ /* XXX Duplicate call to tor_addr_parse. */
+ if (tor_addr_parse(&addr, socks->address) >= 0) {
+ sa_family_t family = tor_addr_family(&addr);
+ if ((family == AF_INET && ! conn->ipv4_traffic_ok) ||
+ (family == AF_INET6 && ! conn->ipv4_traffic_ok)) {
+ log_warn(LD_NET, "Rejecting SOCKS request for an IP address "
+ "family that this listener does not support.");
+ connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY);
+ return -1;
+ } else if (family == AF_INET6 && socks->socks_version == 4) {
+ log_warn(LD_NET, "Rejecting SOCKS4 request for an IPv6 address.");
+ connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY);
+ return -1;
+ } else if (socks->socks_version == 4 && !conn->ipv4_traffic_ok) {
+ log_warn(LD_NET, "Rejecting SOCKS4 request on a listener with "
+ "no IPv4 traffic supported.");
+ connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY);
+ return -1;
+ } else if (family == AF_INET6) {
+ conn->ipv4_traffic_ok = 0;
+ } else if (family == AF_INET) {
+ conn->ipv6_traffic_ok = 0;
+ }
+ }
+ }
+
+ if (socks->socks_version == 4)
+ conn->ipv6_traffic_ok = 0;
+
if (!conn->use_begindir && !conn->chosen_exit_name && !circ) {
/* see if we can find a suitable enclave exit */
const node_t *r =
@@ -2258,7 +1410,7 @@ connection_ap_get_original_destination(entry_connection_t *conn,
}
tor_addr_from_sockaddr(&addr, (struct sockaddr*)&orig_dst, &req->port);
- tor_addr_to_str(req->address, &addr, sizeof(req->address), 0);
+ tor_addr_to_str(req->address, &addr, sizeof(req->address), 1);
return 0;
#elif defined(TRANS_PF)
@@ -2319,7 +1471,7 @@ connection_ap_get_original_destination(entry_connection_t *conn,
return -1;
}
- tor_addr_to_str(req->address, &addr, sizeof(req->address), 0);
+ tor_addr_to_str(req->address, &addr, sizeof(req->address), 1);
req->port = ntohs(pnl.rdport);
return 0;
@@ -2519,7 +1671,7 @@ connection_ap_process_natd(entry_connection_t *conn)
/** Iterate over the two bytes of stream_id until we get one that is not
* already in use; return it. Return 0 if can't get a unique stream_id.
*/
-static streamid_t
+streamid_t
get_unique_stream_id_by_circ(origin_circuit_t *circ)
{
edge_connection_t *tmpconn;
@@ -2557,6 +1709,65 @@ connection_ap_supports_optimistic_data(const entry_connection_t *conn)
return conn->may_use_optimistic_data;
}
+/** Return a bitmask of BEGIN_FLAG_* flags that we should transmit in the
+ * RELAY_BEGIN cell for <b>ap_conn</b>. */
+static uint32_t
+connection_ap_get_begincell_flags(entry_connection_t *ap_conn)
+{
+ edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(ap_conn);
+ const node_t *exitnode = NULL;
+ const crypt_path_t *cpath_layer = edge_conn->cpath_layer;
+ uint32_t flags = 0;
+
+ /* No flags for begindir */
+ if (ap_conn->use_begindir)
+ return 0;
+
+ /* No flags for hidden services. */
+ if (edge_conn->on_circuit->purpose != CIRCUIT_PURPOSE_C_GENERAL)
+ return 0;
+
+ /* If only IPv4 is supported, no flags */
+ if (ap_conn->ipv4_traffic_ok && !ap_conn->ipv6_traffic_ok)
+ return 0;
+
+ if (! cpath_layer ||
+ ! cpath_layer->extend_info)
+ return 0;
+
+ if (!ap_conn->ipv4_traffic_ok)
+ flags |= BEGIN_FLAG_IPV4_NOT_OK;
+
+ exitnode = node_get_by_id(cpath_layer->extend_info->identity_digest);
+
+ if (ap_conn->ipv6_traffic_ok && exitnode) {
+ tor_addr_t a;
+ tor_addr_make_null(&a, AF_INET6);
+ if (compare_tor_addr_to_node_policy(&a, ap_conn->socks_request->port,
+ exitnode)
+ != ADDR_POLICY_REJECTED) {
+ /* Only say "IPv6 OK" if the exit node supports IPv6. Otherwise there's
+ * no point. */
+ flags |= BEGIN_FLAG_IPV6_OK;
+ }
+ }
+
+ if (flags == BEGIN_FLAG_IPV6_OK) {
+ /* When IPv4 and IPv6 are both allowed, consider whether to say we
+ * prefer IPv6. Otherwise there's no point in declaring a preference */
+ if (ap_conn->prefer_ipv6_traffic)
+ flags |= BEGIN_FLAG_IPV6_PREFERRED;
+ }
+
+ if (flags == BEGIN_FLAG_IPV4_NOT_OK) {
+ log_warn(LD_BUG, "Hey; I'm about to ask a node for a connection that I "
+ "am telling it to fulfil with neither IPv4 nor IPv6. That's "
+ "probably not going to work.");
+ }
+
+ return flags;
+}
+
/** Write a relay begin cell, using destaddr and destport from ap_conn's
* socks_request field, and send it down circ.
*
@@ -2592,11 +1803,18 @@ connection_ap_handshake_send_begin(entry_connection_t *ap_conn)
return -1;
}
+ /* Set up begin cell flags. */
+ edge_conn->begincell_flags = connection_ap_get_begincell_flags(ap_conn);
+
tor_snprintf(payload,RELAY_PAYLOAD_SIZE, "%s:%d",
(circ->base_.purpose == CIRCUIT_PURPOSE_C_GENERAL) ?
ap_conn->socks_request->address : "",
ap_conn->socks_request->port);
payload_len = (int)strlen(payload)+1;
+ if (payload_len <= RELAY_PAYLOAD_SIZE - 4 && edge_conn->begincell_flags) {
+ set_uint32(payload + payload_len, htonl(edge_conn->begincell_flags));
+ payload_len += 4;
+ }
log_info(LD_APP,
"Sending relay cell %d to begin stream %d.",
@@ -2619,7 +1837,8 @@ connection_ap_handshake_send_begin(entry_connection_t *ap_conn)
edge_conn->package_window = STREAMWINDOW_START;
edge_conn->deliver_window = STREAMWINDOW_START;
base_conn->state = AP_CONN_STATE_CONNECT_WAIT;
- log_info(LD_APP,"Address/port sent, ap socket %d, n_circ_id %d",
+ log_info(LD_APP,"Address/port sent, ap socket "TOR_SOCKET_T_FORMAT
+ ", n_circ_id %d",
base_conn->s, circ->base_.n_circ_id);
control_event_stream_status(ap_conn, STREAM_EVENT_SENT_CONNECT, 0);
@@ -2688,7 +1907,7 @@ connection_ap_handshake_send_resolve(entry_connection_t *ap_conn)
/* We're doing a reverse lookup. The input could be an IP address, or
* could be an .in-addr.arpa or .ip6.arpa address */
- r = tor_addr_parse_PTR_name(&addr, a, AF_INET, 1);
+ r = tor_addr_parse_PTR_name(&addr, a, AF_UNSPEC, 1);
if (r <= 0) {
log_warn(LD_APP, "Rejecting ill-formed reverse lookup of %s",
safe_str_client(a));
@@ -2720,7 +1939,8 @@ connection_ap_handshake_send_resolve(entry_connection_t *ap_conn)
tor_free(base_conn->address); /* Maybe already set by dnsserv. */
base_conn->address = tor_strdup("(Tor_internal)");
base_conn->state = AP_CONN_STATE_RESOLVE_WAIT;
- log_info(LD_APP,"Address sent for resolve, ap socket %d, n_circ_id %d",
+ log_info(LD_APP,"Address sent for resolve, ap socket "TOR_SOCKET_T_FORMAT
+ ", n_circ_id %d",
base_conn->s, circ->base_.n_circ_id);
control_event_stream_status(ap_conn, STREAM_EVENT_NEW, 0);
control_event_stream_status(ap_conn, STREAM_EVENT_SENT_RESOLVE, 0);
@@ -2858,13 +2078,25 @@ connection_ap_handshake_socks_resolved(entry_connection_t *conn,
if (ttl >= 0) {
if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4) {
- uint32_t a = ntohl(get_uint32(answer));
- if (a)
- client_dns_set_addressmap(conn->socks_request->address, a,
+ tor_addr_t a;
+ tor_addr_from_ipv4n(&a, get_uint32(answer));
+ if (! tor_addr_is_null(&a)) {
+ client_dns_set_addressmap(conn,
+ conn->socks_request->address, &a,
+ conn->chosen_exit_name, ttl);
+ }
+ } else if (answer_type == RESOLVED_TYPE_IPV6 && answer_len == 16) {
+ tor_addr_t a;
+ tor_addr_from_ipv6_bytes(&a, (char*)answer);
+ if (! tor_addr_is_null(&a)) {
+ client_dns_set_addressmap(conn,
+ conn->socks_request->address, &a,
conn->chosen_exit_name, ttl);
+ }
} else if (answer_type == RESOLVED_TYPE_HOSTNAME && answer_len < 256) {
char *cp = tor_strndup((char*)answer, answer_len);
- client_dns_set_reverse_addressmap(conn->socks_request->address,
+ client_dns_set_reverse_addressmap(conn,
+ conn->socks_request->address,
cp,
conn->chosen_exit_name, ttl);
tor_free(cp);
@@ -2964,6 +2196,31 @@ connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply,
status==SOCKS5_SUCCEEDED ? STREAM_EVENT_SUCCEEDED : STREAM_EVENT_FAILED,
endreason);
+ /* Flag this stream's circuit as having completed a stream successfully
+ * (for path bias) */
+ if (status == SOCKS5_SUCCEEDED ||
+ endreason == END_STREAM_REASON_RESOLVEFAILED ||
+ endreason == END_STREAM_REASON_CONNECTREFUSED ||
+ endreason == END_STREAM_REASON_CONNRESET ||
+ endreason == END_STREAM_REASON_NOROUTE ||
+ endreason == END_STREAM_REASON_RESOURCELIMIT) {
+ if (!conn->edge_.on_circuit ||
+ !CIRCUIT_IS_ORIGIN(conn->edge_.on_circuit)) {
+ // DNS remaps can trigger this. So can failed hidden service
+ // lookups.
+ log_info(LD_BUG,
+ "No origin circuit for successful SOCKS stream "U64_FORMAT
+ ". Reason: %d",
+ U64_PRINTF_ARG(ENTRY_TO_CONN(conn)->global_identifier),
+ endreason);
+ } else {
+ // XXX: Hrmm. It looks like optimistic data can't go through this
+ // codepath, but someone should probably test it and make sure.
+ // We don't want to mark optimistically opened streams as successful.
+ pathbias_mark_use_success(TO_ORIGIN_CIRCUIT(conn->edge_.on_circuit));
+ }
+ }
+
if (conn->socks_request->has_finished) {
log_warn(LD_BUG, "(Harmless.) duplicate calls to "
"connection_ap_handshake_socks_reply.");
@@ -2994,6 +2251,70 @@ connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply,
return;
}
+/** Read a RELAY_BEGIN or RELAY_BEGINDIR cell from <b>cell</b>, decode it, and
+ * place the result in <b>bcell</b>. On success return 0; on failure return
+ * <0 and set *<b>end_reason_out</b> to the end reason we should send back to
+ * the client.
+ *
+ * Return -1 in the case where want to send a RELAY_END cell, and < -1 when
+ * we don't.
+ **/
+/* static */ int
+begin_cell_parse(const cell_t *cell, begin_cell_t *bcell,
+ uint8_t *end_reason_out)
+{
+ relay_header_t rh;
+ const uint8_t *body, *nul;
+
+ memset(bcell, 0, sizeof(*bcell));
+ *end_reason_out = END_STREAM_REASON_MISC;
+
+ relay_header_unpack(&rh, cell->payload);
+ if (rh.length > RELAY_PAYLOAD_SIZE) {
+ return -2; /*XXXX why not TORPROTOCOL? */
+ }
+
+ bcell->stream_id = rh.stream_id;
+
+ if (rh.command == RELAY_COMMAND_BEGIN_DIR) {
+ bcell->is_begindir = 1;
+ return 0;
+ } else if (rh.command != RELAY_COMMAND_BEGIN) {
+ log_warn(LD_BUG, "Got an unexpected command %d", (int)rh.command);
+ *end_reason_out = END_STREAM_REASON_INTERNAL;
+ return -1;
+ }
+
+ body = cell->payload + RELAY_HEADER_SIZE;
+ nul = memchr(body, 0, rh.length);
+ if (! nul) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Relay begin cell has no \\0. Closing.");
+ *end_reason_out = END_STREAM_REASON_TORPROTOCOL;
+ return -1;
+ }
+
+ if (tor_addr_port_split(LOG_PROTOCOL_WARN,
+ (char*)(body),
+ &bcell->address,&bcell->port)<0) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Unable to parse addr:port in relay begin cell. Closing.");
+ *end_reason_out = END_STREAM_REASON_TORPROTOCOL;
+ return -1;
+ }
+ if (bcell->port == 0) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Missing port in relay begin cell. Closing.");
+ tor_free(bcell->address);
+ *end_reason_out = END_STREAM_REASON_TORPROTOCOL;
+ return -1;
+ }
+ if (body + rh.length >= nul + 4)
+ bcell->flags = ntohl(get_uint32(nul+1));
+
+ return 0;
+}
+
/** A relay 'begin' or 'begin_dir' cell has arrived, and either we are
* an exit hop for the circuit, or we are the origin and it is a
* rendezvous begin.
@@ -3017,10 +2338,13 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
{
edge_connection_t *n_stream;
relay_header_t rh;
- char *address=NULL;
- uint16_t port;
+ char *address = NULL;
+ uint16_t port = 0;
or_circuit_t *or_circ = NULL;
const or_options_t *options = get_options();
+ begin_cell_t bcell;
+ int r;
+ uint8_t end_reason=0;
assert_circuit_ok(circ);
if (!CIRCUIT_IS_ORIGIN(circ))
@@ -3044,31 +2368,20 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
return 0;
}
- if (rh.command == RELAY_COMMAND_BEGIN) {
- if (!memchr(cell->payload+RELAY_HEADER_SIZE, 0, rh.length)) {
- log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
- "Relay begin cell has no \\0. Closing.");
- relay_send_end_cell_from_edge(rh.stream_id, circ,
- END_STREAM_REASON_TORPROTOCOL, NULL);
- return 0;
- }
- if (tor_addr_port_split(LOG_PROTOCOL_WARN,
- (char*)(cell->payload+RELAY_HEADER_SIZE),
- &address,&port)<0) {
- log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
- "Unable to parse addr:port in relay begin cell. Closing.");
- relay_send_end_cell_from_edge(rh.stream_id, circ,
- END_STREAM_REASON_TORPROTOCOL, NULL);
- return 0;
- }
- if (port==0) {
- log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
- "Missing port in relay begin cell. Closing.");
- relay_send_end_cell_from_edge(rh.stream_id, circ,
- END_STREAM_REASON_TORPROTOCOL, NULL);
- tor_free(address);
- return 0;
- }
+ r = begin_cell_parse(cell, &bcell, &end_reason);
+ if (r < -1) {
+ return -1;
+ } else if (r == -1) {
+ tor_free(bcell.address);
+ relay_send_end_cell_from_edge(rh.stream_id, circ, end_reason, NULL);
+ return 0;
+ }
+
+ if (! bcell.is_begindir) {
+ /* Steal reference */
+ address = bcell.address;
+ port = bcell.port;
+
if (or_circ && or_circ->p_chan) {
if (!options->AllowSingleHopExits &&
(or_circ->is_first_hop ||
@@ -3118,7 +2431,21 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
return 0;
}
+ if (! options->IPv6Exit) {
+ /* I don't care if you prefer IPv6; I can't give you any. */
+ bcell.flags &= ~BEGIN_FLAG_IPV6_PREFERRED;
+ /* If you don't want IPv4, I can't help. */
+ if (bcell.flags & BEGIN_FLAG_IPV4_NOT_OK) {
+ tor_free(address);
+ relay_send_end_cell_from_edge(rh.stream_id, circ,
+ END_STREAM_REASON_EXITPOLICY, NULL);
+ return 0;
+ }
+ }
+
log_debug(LD_EXIT,"Creating new exit connection.");
+ /* The 'AF_INET' here is temporary; we might need to change it later in
+ * connection_exit_connect(). */
n_stream = edge_connection_new(CONN_TYPE_EXIT, AF_INET);
/* Remember the tunneled request ID in the new edge connection, so that
@@ -3126,7 +2453,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
n_stream->dirreq_id = circ->dirreq_id;
n_stream->base_.purpose = EXIT_PURPOSE_CONNECT;
-
+ n_stream->begincell_flags = bcell.flags;
n_stream->stream_id = rh.stream_id;
n_stream->base_.port = port;
/* leave n_stream->s at -1, because it's not yet valid */
@@ -3162,6 +2489,10 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
assert_circuit_ok(circ);
connection_exit_connect(n_stream);
+
+ /* For path bias: This circuit was used successfully */
+ pathbias_mark_use_success(origin_circ);
+
tor_free(address);
return 0;
}
@@ -3275,8 +2606,11 @@ connection_exit_connect(edge_connection_t *edge_conn)
connection_t *conn = TO_CONN(edge_conn);
int socket_error = 0;
- if (!connection_edge_is_rendezvous_stream(edge_conn) &&
- router_compare_to_my_exit_policy(edge_conn)) {
+ if ( (!connection_edge_is_rendezvous_stream(edge_conn) &&
+ router_compare_to_my_exit_policy(&edge_conn->base_.addr,
+ edge_conn->base_.port)) ||
+ (tor_addr_family(&conn->addr) == AF_INET6 &&
+ ! get_options()->IPv6Exit)) {
log_info(LD_EXIT,"%s:%d failed exit policy. Closing.",
escaped_safe_str_client(conn->address), conn->port);
connection_edge_end(edge_conn, END_STREAM_REASON_EXITPOLICY);
@@ -3288,6 +2622,9 @@ connection_exit_connect(edge_connection_t *edge_conn)
addr = &conn->addr;
port = conn->port;
+ if (tor_addr_family(addr) == AF_INET6)
+ conn->socket_family = AF_INET6;
+
log_debug(LD_EXIT,"about to try connecting");
switch (connection_connect(conn, conn->address, addr, port, &socket_error)) {
case -1: {
@@ -3324,21 +2661,21 @@ connection_exit_connect(edge_connection_t *edge_conn)
RELAY_COMMAND_CONNECTED,
NULL, 0);
} else { /* normal stream */
- char connected_payload[20];
- int connected_payload_len;
- if (tor_addr_family(&conn->addr) == AF_INET) {
- set_uint32(connected_payload, tor_addr_to_ipv4n(&conn->addr));
- connected_payload_len = 4;
- } else {
- memcpy(connected_payload, tor_addr_to_in6_addr8(&conn->addr), 16);
- connected_payload_len = 16;
+ uint8_t connected_payload[MAX_CONNECTED_CELL_PAYLOAD_LEN];
+ int connected_payload_len =
+ connected_cell_format_payload(connected_payload, &conn->addr,
+ edge_conn->address_ttl);
+ if (connected_payload_len < 0) {
+ connection_edge_end(edge_conn, END_STREAM_REASON_INTERNAL);
+ circuit_detach_stream(circuit_get_by_edge_conn(edge_conn), edge_conn);
+ connection_free(conn);
+ return;
}
- set_uint32(connected_payload+connected_payload_len,
- htonl(dns_clip_ttl(edge_conn->address_ttl)));
- connected_payload_len += 4;
+
connection_edge_send_command(edge_conn,
RELAY_COMMAND_CONNECTED,
- connected_payload, connected_payload_len);
+ (char*)connected_payload,
+ connected_payload_len);
}
}
@@ -3453,11 +2790,15 @@ connection_ap_can_use_exit(const entry_connection_t *conn, const node_t *exit)
}
if (conn->socks_request->command == SOCKS_COMMAND_CONNECT) {
- struct in_addr in;
tor_addr_t addr, *addrp = NULL;
addr_policy_result_t r;
- if (tor_inet_aton(conn->socks_request->address, &in)) {
- tor_addr_from_in(&addr, &in);
+ if (0 == tor_addr_parse(&addr, conn->socks_request->address)) {
+ addrp = &addr;
+ } else if (!conn->ipv4_traffic_ok && conn->ipv6_traffic_ok) {
+ tor_addr_make_null(&addr, AF_INET6);
+ addrp = &addr;
+ } else if (conn->ipv4_traffic_ok && !conn->ipv6_traffic_ok) {
+ tor_addr_make_null(&addr, AF_INET);
addrp = &addr;
}
r = compare_tor_addr_to_node_policy(addrp, conn->socks_request->port,exit);
@@ -3483,6 +2824,9 @@ connection_ap_can_use_exit(const entry_connection_t *conn, const node_t *exit)
/** If address is of the form "y.onion" with a well-formed handle y:
* Put a NUL after y, lower-case it, and return ONION_HOSTNAME.
*
+ * If address is of the form "x.y.onion" with a well-formed handle x:
+ * Drop "x.", put a NUL after y, lower-case it, and return ONION_HOSTNAME.
+ *
* If address is of the form "y.onion" with a badly-formed handle y:
* Return BAD_HOSTNAME and log a message.
*
@@ -3496,6 +2840,7 @@ hostname_type_t
parse_extended_hostname(char *address)
{
char *s;
+ char *q;
char query[REND_SERVICE_ID_LEN_BASE32+1];
s = strrchr(address,'.');
@@ -3510,9 +2855,18 @@ parse_extended_hostname(char *address)
/* so it is .onion */
*s = 0; /* NUL-terminate it */
- if (strlcpy(query, address, REND_SERVICE_ID_LEN_BASE32+1) >=
+ /* locate a 'sub-domain' component, in order to remove it */
+ q = strrchr(address, '.');
+ if (q == address) {
+ goto failed; /* reject sub-domain, as DNS does */
+ }
+ q = (NULL == q) ? address : q + 1;
+ if (strlcpy(query, q, REND_SERVICE_ID_LEN_BASE32+1) >=
REND_SERVICE_ID_LEN_BASE32+1)
goto failed;
+ if (q != address) {
+ memmove(address, q, strlen(q) + 1 /* also get \0 */);
+ }
if (rend_valid_service_id(query)) {
return ONION_HOSTNAME; /* success */
}
@@ -3716,11 +3070,11 @@ circuit_clear_isolation(origin_circuit_t *circ)
circ->session_group = -1;
circ->nym_epoch = 0;
if (circ->socks_username) {
- memset(circ->socks_username, 0x11, circ->socks_username_len);
+ memwipe(circ->socks_username, 0x11, circ->socks_username_len);
tor_free(circ->socks_username);
}
if (circ->socks_password) {
- memset(circ->socks_password, 0x05, circ->socks_password_len);
+ memwipe(circ->socks_password, 0x05, circ->socks_password_len);
tor_free(circ->socks_password);
}
circ->socks_username_len = circ->socks_password_len = 0;
diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h
index 42fb73c036..ea284cbcfd 100644
--- a/src/or/connection_edge.h
+++ b/src/or/connection_edge.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -67,30 +67,6 @@ int connection_ap_process_transparent(entry_connection_t *conn);
int address_is_invalid_destination(const char *address, int client);
-void addressmap_init(void);
-void addressmap_clear_excluded_trackexithosts(const or_options_t *options);
-void addressmap_clear_invalid_automaps(const or_options_t *options);
-void addressmap_clean(time_t now);
-void addressmap_clear_configured(void);
-void addressmap_clear_transient(void);
-void addressmap_free_all(void);
-int addressmap_rewrite(char *address, size_t maxlen, time_t *expires_out,
- addressmap_entry_source_t *exit_source_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,
- const int address_wildcard,
- const 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);
-void client_dns_clear_failures(const char *address);
-void client_dns_set_addressmap(const char *address, uint32_t val,
- const char *exitname, int ttl);
-const char *addressmap_register_virtual_address(int type, char *new_address);
-void addressmap_get_mappings(smartlist_t *sl, time_t min_expires,
- time_t max_expires, int want_expiry);
int connection_ap_rewrite_and_attach_if_allowed(entry_connection_t *conn,
origin_circuit_t *circ,
crypt_path_t *cpath);
@@ -114,6 +90,52 @@ int connection_edge_update_circuit_isolation(const entry_connection_t *conn,
origin_circuit_t *circ,
int dry_run);
void circuit_clear_isolation(origin_circuit_t *circ);
+streamid_t get_unique_stream_id_by_circ(origin_circuit_t *circ);
+
+/** @name Begin-cell flags
+ *
+ * These flags are used in RELAY_BEGIN cells to change the default behavior
+ * of the cell.
+ *
+ * @{
+ **/
+/** When this flag is set, the client is willing to get connected to IPv6
+ * addresses */
+#define BEGIN_FLAG_IPV6_OK (1u<<0)
+/** When this flag is set, the client DOES NOT support connecting to IPv4
+ * addresses. (The sense of this flag is inverted from IPV6_OK, so that the
+ * old default behavior of Tor is equivalent to having all flags set to 0.)
+ **/
+#define BEGIN_FLAG_IPV4_NOT_OK (1u<<1)
+/** When this flag is set, if we find both an IPv4 and an IPv6 address,
+ * we use the IPv6 address. Otherwise we use the IPv4 address. */
+#define BEGIN_FLAG_IPV6_PREFERRED (1u<<2)
+/**@}*/
+
+#ifdef CONNECTION_EDGE_PRIVATE
+
+/** A parsed BEGIN or BEGIN_DIR cell */
+typedef struct begin_cell_t {
+ /** The address the client has asked us to connect to, or NULL if this is
+ * a BEGIN_DIR cell*/
+ char *address;
+ /** The flags specified in the BEGIN cell's body. One or more of
+ * BEGIN_FLAG_*. */
+ uint32_t flags;
+ /** The client's requested port. */
+ uint16_t port;
+ /** The client's requested Stream ID */
+ uint16_t stream_id;
+ /** True iff this is a BEGIN_DIR cell. */
+ unsigned is_begindir : 1;
+} begin_cell_t;
+
+int begin_cell_parse(const cell_t *cell, begin_cell_t *bcell,
+ uint8_t *end_reason_out);
+int connected_cell_format_payload(uint8_t *payload_out,
+ const tor_addr_t *addr,
+ uint32_t ttl);
+#endif
#endif
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index 4da43670ce..c4415c5f88 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -295,13 +295,13 @@ connection_or_report_broken_states(int severity, int domain)
smartlist_sort(items, broken_state_count_compare);
- log(severity, domain, "%d connections have failed%s", total,
+ tor_log(severity, domain, "%d connections have failed%s", total,
smartlist_len(items) > MAX_REASONS_TO_REPORT ? ". Top reasons:" : ":");
SMARTLIST_FOREACH_BEGIN(items, const broken_state_count_t *, c) {
if (c_sl_idx > MAX_REASONS_TO_REPORT)
break;
- log(severity, domain,
+ tor_log(severity, domain,
" %d connections died in state %s", (int)c->count, c->state);
} SMARTLIST_FOREACH_END(c);
@@ -873,7 +873,7 @@ connection_or_group_set_badness(or_connection_t *head, int force)
< now) {
log_info(LD_OR,
"Marking OR conn to %s:%d as too old for new circuits "
- "(fd %d, %d secs old).",
+ "(fd "TOR_SOCKET_T_FORMAT", %d secs old).",
or_conn->base_.address, or_conn->base_.port, or_conn->base_.s,
(int)(now - or_conn->base_.timestamp_created));
connection_or_mark_bad_for_new_circs(or_conn);
@@ -904,8 +904,8 @@ connection_or_group_set_badness(or_connection_t *head, int force)
* and this one is open but not canonical. Mark it bad. */
log_info(LD_OR,
"Marking OR conn to %s:%d as unsuitable for new circuits: "
- "(fd %d, %d secs old). It is not canonical, and we have "
- "another connection to that OR that is.",
+ "(fd "TOR_SOCKET_T_FORMAT", %d secs old). It is not "
+ "canonical, and we have another connection to that OR that is.",
or_conn->base_.address, or_conn->base_.port, or_conn->base_.s,
(int)(now - or_conn->base_.timestamp_created));
connection_or_mark_bad_for_new_circs(or_conn);
@@ -952,8 +952,9 @@ connection_or_group_set_badness(or_connection_t *head, int force)
if (best->is_canonical) {
log_info(LD_OR,
"Marking OR conn to %s:%d as unsuitable for new circuits: "
- "(fd %d, %d secs old). We have a better canonical one "
- "(fd %d; %d secs old).",
+ "(fd "TOR_SOCKET_T_FORMAT", %d secs old). "
+ "We have a better canonical one "
+ "(fd "TOR_SOCKET_T_FORMAT"; %d secs old).",
or_conn->base_.address, or_conn->base_.port, or_conn->base_.s,
(int)(now - or_conn->base_.timestamp_created),
best->base_.s, (int)(now - best->base_.timestamp_created));
@@ -962,8 +963,9 @@ connection_or_group_set_badness(or_connection_t *head, int force)
&best->real_addr, CMP_EXACT)) {
log_info(LD_OR,
"Marking OR conn to %s:%d as unsuitable for new circuits: "
- "(fd %d, %d secs old). We have a better one with the "
- "same address (fd %d; %d secs old).",
+ "(fd "TOR_SOCKET_T_FORMAT", %d secs old). We have a better "
+ "one with the "
+ "same address (fd "TOR_SOCKET_T_FORMAT"; %d secs old).",
or_conn->base_.address, or_conn->base_.port, or_conn->base_.s,
(int)(now - or_conn->base_.timestamp_created),
best->base_.s, (int)(now - best->base_.timestamp_created));
@@ -1167,8 +1169,8 @@ connection_or_close_normally(or_connection_t *orconn, int flush)
channel_t *chan = NULL;
tor_assert(orconn);
- if (flush) connection_mark_and_flush(TO_CONN(orconn));
- else connection_mark_for_close(TO_CONN(orconn));
+ if (flush) connection_mark_and_flush_internal(TO_CONN(orconn));
+ else connection_mark_for_close_internal(TO_CONN(orconn));
if (orconn->chan) {
chan = TLS_CHAN_TO_BASE(orconn->chan);
/* Don't transition if we're already in closing, closed or error */
@@ -1190,8 +1192,8 @@ connection_or_close_for_error(or_connection_t *orconn, int flush)
channel_t *chan = NULL;
tor_assert(orconn);
- if (flush) connection_mark_and_flush(TO_CONN(orconn));
- else connection_mark_for_close(TO_CONN(orconn));
+ if (flush) connection_mark_and_flush_internal(TO_CONN(orconn));
+ else connection_mark_for_close_internal(TO_CONN(orconn));
if (orconn->chan) {
chan = TLS_CHAN_TO_BASE(orconn->chan);
/* Don't transition if we're already in closing, closed or error */
@@ -1265,7 +1267,8 @@ connection_tls_start_handshake(or_connection_t *conn, int receiving)
}
#endif
connection_start_reading(TO_CONN(conn));
- log_debug(LD_HANDSHAKE,"starting TLS handshake on fd %d", conn->base_.s);
+ log_debug(LD_HANDSHAKE,"starting TLS handshake on fd "TOR_SOCKET_T_FORMAT,
+ conn->base_.s);
note_crypto_pk_op(receiving ? TLS_HANDSHAKE_S : TLS_HANDSHAKE_C);
IF_HAS_BUFFEREVENT(TO_CONN(conn), {
@@ -1338,7 +1341,8 @@ connection_tls_continue_handshake(or_connection_t *conn)
if (conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING) {
if (tor_tls_received_v3_certificate(conn->tls)) {
log_info(LD_OR, "Client got a v3 cert! Moving on to v3 "
- "handshake.");
+ "handshake with ciphersuite %s",
+ tor_tls_get_ciphersuite_name(conn->tls));
return connection_or_launch_v3_or_handshake(conn);
} else {
log_debug(LD_OR, "Done with initial SSL handshake (client-side)."
@@ -1663,10 +1667,12 @@ connection_tls_finish_handshake(or_connection_t *conn)
char digest_rcvd[DIGEST_LEN];
int started_here = connection_or_nonopen_was_started_here(conn);
- log_debug(LD_HANDSHAKE,"%s tls handshake on %p with %s done. verifying.",
+ log_debug(LD_HANDSHAKE,"%s tls handshake on %p with %s done, using "
+ "ciphersuite %s. verifying.",
started_here?"outgoing":"incoming",
conn,
- safe_str_client(conn->base_.address));
+ safe_str_client(conn->base_.address),
+ tor_tls_get_ciphersuite_name(conn->tls));
directory_set_dirty();
@@ -1744,7 +1750,7 @@ or_handshake_state_free(or_handshake_state_t *state)
crypto_digest_free(state->digest_received);
tor_cert_free(state->auth_cert);
tor_cert_free(state->id_cert);
- memset(state, 0xBE, sizeof(or_handshake_state_t));
+ memwipe(state, 0xBE, sizeof(or_handshake_state_t));
tor_free(state);
}
@@ -1787,7 +1793,7 @@ or_handshake_state_record_cell(or_connection_t *conn,
this very often at all. */
cell_pack(&packed, cell, conn->wide_circ_ids);
crypto_digest_add_bytes(d, packed.body, cell_network_size);
- memset(&packed, 0, sizeof(packed));
+ memwipe(&packed, 0, sizeof(packed));
}
/** Remember that a variable-length <b>cell</b> has been transmitted (if
@@ -1824,7 +1830,7 @@ or_handshake_state_record_var_cell(or_connection_t *conn,
crypto_digest_add_bytes(d, buf, n);
crypto_digest_add_bytes(d, (const char *)cell->payload, cell->payload_len);
- memset(buf, 0, sizeof(buf));
+ memwipe(buf, 0, sizeof(buf));
}
/** Set <b>conn</b>'s state to OR_CONN_STATE_OPEN, and tell other subsystems
@@ -1929,7 +1935,8 @@ connection_or_process_cells_from_inbuf(or_connection_t *conn)
while (1) {
log_debug(LD_OR,
- "%d: starting, inbuf_datalen %d (%d pending in tls object).",
+ TOR_SOCKET_T_FORMAT": starting, inbuf_datalen %d "
+ "(%d pending in tls object).",
conn->base_.s,(int)connection_get_inbuf_len(TO_CONN(conn)),
tor_tls_get_pending_bytes(conn->tls));
if (connection_fetch_var_cell_from_buf(conn, &var_cell)) {
@@ -2158,7 +2165,7 @@ connection_or_send_auth_challenge_cell(or_connection_t *conn)
connection_or_write_var_cell_to_buf(cell, conn);
var_cell_free(cell);
- memset(challenge, 0, sizeof(challenge));
+ memwipe(challenge, 0, sizeof(challenge));
return 0;
}
diff --git a/src/or/connection_or.h b/src/or/connection_or.h
index c0f8ec1ee4..85e68f1a33 100644
--- a/src/or/connection_or.h
+++ b/src/or/connection_or.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/control.c b/src/or/control.c
index ad2f2788f4..03e5d79c8e 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -1,5 +1,5 @@
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -11,6 +11,7 @@
#define CONTROL_PRIVATE
#include "or.h"
+#include "addressmap.h"
#include "buffers.h"
#include "channel.h"
#include "channeltls.h"
@@ -1220,7 +1221,8 @@ handle_control_authenticate(control_connection_t *conn, uint32_t len,
connection_mark_for_close(TO_CONN(conn));
return 0;
ok:
- log_info(LD_CONTROL, "Authenticated control connection (%d)", conn->base_.s);
+ log_info(LD_CONTROL, "Authenticated control connection ("TOR_SOCKET_T_FORMAT
+ ")", conn->base_.s);
send_control_done(conn);
conn->base_.state = CONTROL_CONN_STATE_OPEN;
tor_free(password);
@@ -1371,10 +1373,13 @@ handle_control_mapaddress(control_connection_t *conn, uint32_t len,
"512-syntax error: invalid address '%s'", to);
log_warn(LD_CONTROL,
"Skipping invalid argument '%s' in MapAddress msg", to);
- } else if (!strcmp(from, ".") || !strcmp(from, "0.0.0.0")) {
+ } else if (!strcmp(from, ".") || !strcmp(from, "0.0.0.0") ||
+ !strcmp(from, "::")) {
+ const char type =
+ !strcmp(from,".") ? RESOLVED_TYPE_HOSTNAME :
+ (!strcmp(from, "0.0.0.0") ? RESOLVED_TYPE_IPV4 : RESOLVED_TYPE_IPV6);
const char *address = addressmap_register_virtual_address(
- !strcmp(from,".") ? RESOLVED_TYPE_HOSTNAME : RESOLVED_TYPE_IPV4,
- tor_strdup(to));
+ type, tor_strdup(to));
if (!address) {
smartlist_add_asprintf(reply,
"451-resource exhausted: skipping '%s'", line);
@@ -2942,7 +2947,7 @@ handle_control_resolve(control_connection_t *conn, uint32_t len,
send_control_done(conn);
SMARTLIST_FOREACH(failed, const char *, arg, {
control_event_address_mapped(arg, arg, time(NULL),
- "Unable to launch resolve request");
+ "internal");
});
SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
@@ -3138,6 +3143,8 @@ handle_control_authchallenge(control_connection_t *conn, uint32_t len,
"SERVERNONCE=%s\r\n",
server_hash_encoded,
server_nonce_encoded);
+
+ tor_free(client_nonce);
return 0;
}
@@ -3577,9 +3584,9 @@ control_event_circuit_status_minor(origin_circuit_t *circ,
/* event_tail can currently be up to 130 chars long */
const char *hs_state_str =
circuit_purpose_to_controller_hs_state_string(purpose);
- const struct timeval *old_timestamp_created = tv;
+ const struct timeval *old_timestamp_began = tv;
char tbuf[ISO_TIME_USEC_LEN+1];
- format_iso_time_nospace_usec(tbuf, old_timestamp_created);
+ format_iso_time_nospace_usec(tbuf, old_timestamp_began);
tor_snprintf(event_tail, sizeof(event_tail),
" OLD_PURPOSE=%s%s%s OLD_TIME_CREATED=%s",
@@ -4658,7 +4665,7 @@ control_event_bootstrap(bootstrap_status_t status, int progress)
if (status > bootstrap_percent ||
(progress && progress > bootstrap_percent)) {
bootstrap_status_to_string(status, &tag, &summary);
- log(status ? LOG_NOTICE : LOG_INFO, LD_CONTROL,
+ tor_log(status ? LOG_NOTICE : LOG_INFO, LD_CONTROL,
"Bootstrapped %d%%: %s.", progress ? progress : status, summary);
tor_snprintf(buf, sizeof(buf),
"BOOTSTRAP PROGRESS=%d TAG=%s SUMMARY=\"%s\"",
diff --git a/src/or/control.h b/src/or/control.h
index eea3af724c..51ae230b09 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c
index a25a8a780a..af5f91a623 100644
--- a/src/or/cpuworker.c
+++ b/src/or/cpuworker.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2003-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -32,9 +32,6 @@
/** The tag specifies which circuit this onionskin was from. */
#define TAG_LEN 12
-/** How many bytes are sent from the cpuworker back to tor? */
-#define LEN_ONION_RESPONSE \
- (1+TAG_LEN+ONIONSKIN_REPLY_LEN+CPATH_KEY_MATERIAL_LEN)
/** How many cpuworkers we have running right now. */
static int num_cpuworkers=0;
@@ -70,7 +67,7 @@ connection_cpu_finished_flushing(connection_t *conn)
/** Pack global_id and circ_id; set *tag to the result. (See note on
* cpuworker_main for wire format.) */
static void
-tag_pack(char *tag, uint64_t chan_id, circid_t circ_id)
+tag_pack(uint8_t *tag, uint64_t chan_id, circid_t circ_id)
{
/*XXXX RETHINK THIS WHOLE MESS !!!! !NM NM NM NM*/
/*XXXX DOUBLEPLUSTHIS!!!! AS AS AS AS*/
@@ -81,12 +78,69 @@ tag_pack(char *tag, uint64_t chan_id, circid_t circ_id)
/** Unpack <b>tag</b> into addr, port, and circ_id.
*/
static void
-tag_unpack(const char *tag, uint64_t *chan_id, circid_t *circ_id)
+tag_unpack(const uint8_t *tag, uint64_t *chan_id, circid_t *circ_id)
{
*chan_id = get_uint64(tag);
*circ_id = get_uint32(tag+8);
}
+/** Magic numbers to make sure our cpuworker_requests don't grow any
+ * mis-framing bugs. */
+#define CPUWORKER_REQUEST_MAGIC 0xda4afeed
+#define CPUWORKER_REPLY_MAGIC 0x5eedf00d
+
+/** A request sent to a cpuworker. */
+typedef struct cpuworker_request_t {
+ /** Magic number; must be CPUWORKER_REQUEST_MAGIC. */
+ uint32_t magic;
+ /** Opaque tag to identify the job */
+ uint8_t tag[TAG_LEN];
+ /** Task code. Must be one of CPUWORKER_TASK_* */
+ uint8_t task;
+
+ /** Flag: Are we timing this request? */
+ unsigned timed : 1;
+ /** If we're timing this request, when was it sent to the cpuworker? */
+ struct timeval started_at;
+
+ /** A create cell for the cpuworker to process. */
+ create_cell_t create_cell;
+
+ /* Turn the above into a tagged union if needed. */
+} cpuworker_request_t;
+
+/** A reply sent by a cpuworker. */
+typedef struct cpuworker_reply_t {
+ /** Magic number; must be CPUWORKER_REPLY_MAGIC. */
+ uint32_t magic;
+ /** Opaque tag to identify the job; matches the request's tag.*/
+ uint8_t tag[TAG_LEN];
+ /** True iff we got a successful request. */
+ uint8_t success;
+
+ /** Are we timing this request? */
+ unsigned int timed : 1;
+ /** What handshake type was the request? (Used for timing) */
+ uint16_t handshake_type;
+ /** When did we send the request to the cpuworker? */
+ struct timeval started_at;
+ /** Once the cpuworker received the request, how many microseconds did it
+ * take? (This shouldn't overflow; 4 billion micoseconds is over an hour,
+ * and we'll never have an onion handshake that takes so long.) */
+ uint32_t n_usec;
+
+ /** Output of processing a create cell
+ *
+ * @{
+ */
+ /** The created cell to send back. */
+ created_cell_t created_cell;
+ /** The keys to use on this circuit. */
+ uint8_t keys[CPATH_KEY_MATERIAL_LEN];
+ /** Input to use for authenticating introduce1 cells. */
+ uint8_t rend_auth_material[DIGEST_LEN];
+} cpuworker_reply_t;
+
/** Called when the onion key has changed and we need to spawn new
* cpuworkers. Close all currently idle cpuworkers, and mark the last
* rotation time as now.
@@ -125,6 +179,112 @@ connection_cpu_reached_eof(connection_t *conn)
return 0;
}
+/** Indexed by handshake type: how many onionskins have we processed and
+ * counted of that type? */
+static uint64_t onionskins_n_processed[MAX_ONION_HANDSHAKE_TYPE+1];
+/** Indexed by handshake type, corresponding to the onionskins counted in
+ * onionskins_n_processed: how many microseconds have we spent in cpuworkers
+ * processing that kind of onionskin? */
+static uint64_t onionskins_usec_internal[MAX_ONION_HANDSHAKE_TYPE+1];
+/** Indexed by handshake type, corresponding to onionskins counted in
+ * onionskins_n_processed: how many microseconds have we spent waiting for
+ * cpuworkers to give us answers for that kind of onionskin?
+ */
+static uint64_t onionskins_usec_roundtrip[MAX_ONION_HANDSHAKE_TYPE+1];
+
+/** If any onionskin takes longer than this, we clip them to this
+ * time. (microseconds) */
+#define MAX_BELIEVABLE_ONIONSKIN_DELAY (2*1000*1000)
+
+static tor_weak_rng_t request_sample_rng = TOR_WEAK_RNG_INIT;
+
+/** Return true iff we'd like to measure a handshake of type
+ * <b>onionskin_type</b>. Call only from the main thread. */
+static int
+should_time_request(uint16_t onionskin_type)
+{
+ /* If we've never heard of this type, we shouldn't even be here. */
+ if (onionskin_type > MAX_ONION_HANDSHAKE_TYPE)
+ return 0;
+ /* Measure the first N handshakes of each type, to ensure we have a
+ * sample */
+ if (onionskins_n_processed[onionskin_type] < 4096)
+ return 1;
+ /** Otherwise, measure with P=1/128. We avoid doing this for every
+ * handshake, since the measurement itself can take a little time. */
+ return tor_weak_random_one_in_n(&request_sample_rng, 128);
+}
+
+/** Return an estimate of how many microseconds we will need for a single
+ * cpuworker to to process <b>n_requests</b> onionskins of type
+ * <b>onionskin_type</b>. */
+uint64_t
+estimated_usec_for_onionskins(uint32_t n_requests, uint16_t onionskin_type)
+{
+ if (onionskin_type > MAX_ONION_HANDSHAKE_TYPE) /* should be impossible */
+ return 1000 * (uint64_t)n_requests;
+ if (PREDICT_UNLIKELY(onionskins_n_processed[onionskin_type] < 100)) {
+ /* Until we have 100 data points, just asssume everything takes 1 msec. */
+ return 1000 * (uint64_t)n_requests;
+ } else {
+ /* This can't overflow: we'll never have more than 500000 onionskins
+ * measured in onionskin_usec_internal, and they won't take anything near
+ * 1 sec each, and we won't have anything like 1 million queued
+ * onionskins. But that's 5e5 * 1e6 * 1e6, which is still less than
+ * UINT64_MAX. */
+ return (onionskins_usec_internal[onionskin_type] * n_requests) /
+ onionskins_n_processed[onionskin_type];
+ }
+}
+
+/** Compute the absolute and relative overhead of using the cpuworker
+ * framework for onionskins of type <b>onionskin_type</b>.*/
+static int
+get_overhead_for_onionskins(uint32_t *usec_out, double *frac_out,
+ uint16_t onionskin_type)
+{
+ uint64_t overhead;
+
+ *usec_out = 0;
+ *frac_out = 0.0;
+
+ if (onionskin_type > MAX_ONION_HANDSHAKE_TYPE) /* should be impossible */
+ return -1;
+ if (onionskins_n_processed[onionskin_type] == 0 ||
+ onionskins_usec_internal[onionskin_type] == 0 ||
+ onionskins_usec_roundtrip[onionskin_type] == 0)
+ return -1;
+
+ overhead = onionskins_usec_roundtrip[onionskin_type] -
+ onionskins_usec_internal[onionskin_type];
+
+ *usec_out = (uint32_t)(overhead / onionskins_n_processed[onionskin_type]);
+ *frac_out = U64_TO_DBL(overhead) / onionskins_usec_internal[onionskin_type];
+
+ return 0;
+}
+
+/** If we've measured overhead for onionskins of type <b>onionskin_type</b>,
+ * log it. */
+void
+cpuworker_log_onionskin_overhead(int severity, int onionskin_type,
+ const char *onionskin_type_name)
+{
+ uint32_t overhead;
+ double relative_overhead;
+ int r;
+
+ r = get_overhead_for_onionskins(&overhead, &relative_overhead,
+ onionskin_type);
+ if (!overhead || r<0)
+ return;
+
+ log_fn(severity, LD_OR,
+ "%s onionskins have averaged %u usec overhead (%.2f%%) in "
+ "cpuworker code ",
+ onionskin_type_name, (unsigned)overhead, relative_overhead*100);
+}
+
/** Called when we get data from a cpuworker. If the answer is not complete,
* wait for a complete answer. If the answer is complete,
* process it as appropriate.
@@ -132,8 +292,6 @@ connection_cpu_reached_eof(connection_t *conn)
int
connection_cpu_process_inbuf(connection_t *conn)
{
- char success;
- char buf[LEN_ONION_RESPONSE];
uint64_t chan_id;
circid_t circ_id;
channel_t *p_chan = NULL;
@@ -146,15 +304,40 @@ connection_cpu_process_inbuf(connection_t *conn)
return 0;
if (conn->state == CPUWORKER_STATE_BUSY_ONION) {
- if (connection_get_inbuf_len(conn) < LEN_ONION_RESPONSE)
+ cpuworker_reply_t rpl;
+ if (connection_get_inbuf_len(conn) < sizeof(cpuworker_reply_t))
return 0; /* not yet */
- tor_assert(connection_get_inbuf_len(conn) == LEN_ONION_RESPONSE);
-
- connection_fetch_from_buf(&success,1,conn);
- connection_fetch_from_buf(buf,LEN_ONION_RESPONSE-1,conn);
-
+ tor_assert(connection_get_inbuf_len(conn) == sizeof(cpuworker_reply_t));
+
+ connection_fetch_from_buf((void*)&rpl,sizeof(cpuworker_reply_t),conn);
+
+ tor_assert(rpl.magic == CPUWORKER_REPLY_MAGIC);
+
+ if (rpl.timed && rpl.success &&
+ rpl.handshake_type <= MAX_ONION_HANDSHAKE_TYPE) {
+ /* Time how long this request took. The handshake_type check should be
+ needless, but let's leave it in to be safe. */
+ struct timeval tv_end, tv_diff;
+ int64_t usec_roundtrip;
+ tor_gettimeofday(&tv_end);
+ timersub(&tv_end, &rpl.started_at, &tv_diff);
+ usec_roundtrip = ((int64_t)tv_diff.tv_sec)*1000000 + tv_diff.tv_usec;
+ if (usec_roundtrip >= 0 &&
+ usec_roundtrip < MAX_BELIEVABLE_ONIONSKIN_DELAY) {
+ ++onionskins_n_processed[rpl.handshake_type];
+ onionskins_usec_internal[rpl.handshake_type] += rpl.n_usec;
+ onionskins_usec_roundtrip[rpl.handshake_type] += usec_roundtrip;
+ if (onionskins_n_processed[rpl.handshake_type] >= 500000) {
+ /* Scale down every 500000 handshakes. On a busy server, that's
+ * less impressive than it sounds. */
+ onionskins_n_processed[rpl.handshake_type] /= 2;
+ onionskins_usec_internal[rpl.handshake_type] /= 2;
+ onionskins_usec_roundtrip[rpl.handshake_type] /= 2;
+ }
+ }
+ }
/* parse out the circ it was talking about */
- tag_unpack(buf, &chan_id, &circ_id);
+ tag_unpack(rpl.tag, &chan_id, &circ_id);
circ = NULL;
log_debug(LD_OR,
"Unpacking cpuworker reply, chan_id is " U64_FORMAT
@@ -165,7 +348,7 @@ connection_cpu_process_inbuf(connection_t *conn)
if (p_chan)
circ = circuit_get_by_circid_channel(circ_id, p_chan);
- if (success == 0) {
+ if (rpl.success == 0) {
log_debug(LD_OR,
"decoding onionskin failed. "
"(Old key or bad software.) Closing.");
@@ -183,8 +366,10 @@ connection_cpu_process_inbuf(connection_t *conn)
goto done_processing;
}
tor_assert(! CIRCUIT_IS_ORIGIN(circ));
- if (onionskin_answer(TO_OR_CIRCUIT(circ), CELL_CREATED, buf+TAG_LEN,
- buf+TAG_LEN+ONIONSKIN_REPLY_LEN) < 0) {
+ if (onionskin_answer(TO_OR_CIRCUIT(circ),
+ &rpl.created_cell,
+ (const char*)rpl.keys,
+ rpl.rend_auth_material) < 0) {
log_warn(LD_OR,"onionskin_answer failed. Closing.");
circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL);
goto done_processing;
@@ -211,32 +396,21 @@ connection_cpu_process_inbuf(connection_t *conn)
* Read and writes from fdarray[1]. Reads requests, writes answers.
*
* Request format:
- * Task type [1 byte, always CPUWORKER_TASK_ONION]
- * Opaque tag TAG_LEN
- * Onionskin challenge ONIONSKIN_CHALLENGE_LEN
+ * cpuworker_request_t.
* Response format:
- * Success/failure [1 byte, boolean.]
- * Opaque tag TAG_LEN
- * Onionskin challenge ONIONSKIN_REPLY_LEN
- * Negotiated keys KEY_LEN*2+DIGEST_LEN*2
- *
- * (Note: this _should_ be by addr/port, since we're concerned with specific
- * connections, not with routers (where we'd use identity).)
+ * cpuworker_reply_t
*/
static void
cpuworker_main(void *data)
{
- char question[ONIONSKIN_CHALLENGE_LEN];
- uint8_t question_type;
+ /* For talking to the parent thread/process */
tor_socket_t *fdarray = data;
tor_socket_t fd;
/* variables for onion processing */
- char keys[CPATH_KEY_MATERIAL_LEN];
- char reply_to_proxy[ONIONSKIN_REPLY_LEN];
- char buf[LEN_ONION_RESPONSE];
- char tag[TAG_LEN];
- crypto_pk_t *onion_key = NULL, *last_onion_key = NULL;
+ server_onion_keys_t onion_keys;
+ cpuworker_request_t req;
+ cpuworker_reply_t rpl;
fd = fdarray[1]; /* this side is ours */
#ifndef TOR_IS_MULTITHREADED
@@ -247,68 +421,85 @@ cpuworker_main(void *data)
#endif
tor_free(data);
- dup_onion_keys(&onion_key, &last_onion_key);
+ setup_server_onion_keys(&onion_keys);
for (;;) {
- ssize_t r;
-
- if ((r = recv(fd, (void *)&question_type, 1, 0)) != 1) {
-// log_fn(LOG_ERR,"read type failed. Exiting.");
- if (r == 0) {
- log_info(LD_OR,
- "CPU worker exiting because Tor process closed connection "
- "(either rotated keys or died).");
- } else {
- log_info(LD_OR,
- "CPU worker exiting because of error on connection to Tor "
- "process.");
- log_info(LD_OR,"(Error on %d was %s)",
- fd, tor_socket_strerror(tor_socket_errno(fd)));
- }
+ if (read_all(fd, (void *)&req, sizeof(req), 1) != sizeof(req)) {
+ log_info(LD_OR, "read request failed. Exiting.");
goto end;
}
- tor_assert(question_type == CPUWORKER_TASK_ONION);
-
- if (read_all(fd, tag, TAG_LEN, 1) != TAG_LEN) {
- log_err(LD_BUG,"read tag failed. Exiting.");
- goto end;
- }
-
- if (read_all(fd, question, ONIONSKIN_CHALLENGE_LEN, 1) !=
- ONIONSKIN_CHALLENGE_LEN) {
- log_err(LD_BUG,"read question failed. Exiting.");
- goto end;
- }
-
- if (question_type == CPUWORKER_TASK_ONION) {
- if (onion_skin_server_handshake(question, onion_key, last_onion_key,
- reply_to_proxy, keys, CPATH_KEY_MATERIAL_LEN) < 0) {
+ tor_assert(req.magic == CPUWORKER_REQUEST_MAGIC);
+
+ memset(&rpl, 0, sizeof(rpl));
+
+ if (req.task == CPUWORKER_TASK_ONION) {
+ const create_cell_t *cc = &req.create_cell;
+ created_cell_t *cell_out = &rpl.created_cell;
+ struct timeval tv_start, tv_end;
+ int n;
+ rpl.timed = req.timed;
+ rpl.started_at = req.started_at;
+ rpl.handshake_type = cc->handshake_type;
+ if (req.timed)
+ tor_gettimeofday(&tv_start);
+ n = onion_skin_server_handshake(cc->handshake_type,
+ cc->onionskin, cc->handshake_len,
+ &onion_keys,
+ cell_out->reply,
+ rpl.keys, CPATH_KEY_MATERIAL_LEN,
+ rpl.rend_auth_material);
+ if (n < 0) {
/* failure */
log_debug(LD_OR,"onion_skin_server_handshake failed.");
- *buf = 0; /* indicate failure in first byte */
- memcpy(buf+1,tag,TAG_LEN);
- /* send all zeros as answer */
- memset(buf+1+TAG_LEN, 0, LEN_ONION_RESPONSE-(1+TAG_LEN));
+ memset(&rpl, 0, sizeof(rpl));
+ memcpy(rpl.tag, req.tag, TAG_LEN);
+ rpl.success = 0;
} else {
/* success */
log_debug(LD_OR,"onion_skin_server_handshake succeeded.");
- buf[0] = 1; /* 1 means success */
- memcpy(buf+1,tag,TAG_LEN);
- memcpy(buf+1+TAG_LEN,reply_to_proxy,ONIONSKIN_REPLY_LEN);
- memcpy(buf+1+TAG_LEN+ONIONSKIN_REPLY_LEN,keys,CPATH_KEY_MATERIAL_LEN);
+ memcpy(rpl.tag, req.tag, TAG_LEN);
+ cell_out->handshake_len = n;
+ switch (cc->cell_type) {
+ case CELL_CREATE:
+ cell_out->cell_type = CELL_CREATED; break;
+ case CELL_CREATE2:
+ cell_out->cell_type = CELL_CREATED2; break;
+ case CELL_CREATE_FAST:
+ cell_out->cell_type = CELL_CREATED_FAST; break;
+ default:
+ tor_assert(0);
+ goto end;
+ }
+ rpl.success = 1;
}
- if (write_all(fd, buf, LEN_ONION_RESPONSE, 1) != LEN_ONION_RESPONSE) {
+ rpl.magic = CPUWORKER_REPLY_MAGIC;
+ if (req.timed) {
+ struct timeval tv_diff;
+ int64_t usec;
+ tor_gettimeofday(&tv_end);
+ timersub(&tv_end, &tv_start, &tv_diff);
+ usec = ((int64_t)tv_diff.tv_sec)*1000000 + tv_diff.tv_usec;
+ if (usec < 0 || usec > MAX_BELIEVABLE_ONIONSKIN_DELAY)
+ rpl.n_usec = MAX_BELIEVABLE_ONIONSKIN_DELAY;
+ else
+ rpl.n_usec = (uint32_t) usec;
+ }
+ if (write_all(fd, (void*)&rpl, sizeof(rpl), 1) != sizeof(rpl)) {
log_err(LD_BUG,"writing response buf failed. Exiting.");
goto end;
}
log_debug(LD_OR,"finished writing response.");
+ } else if (req.task == CPUWORKER_TASK_SHUTDOWN) {
+ log_info(LD_OR,"Clean shutdown: exiting");
+ goto end;
}
+ memwipe(&req, 0, sizeof(req));
+ memwipe(&rpl, 0, sizeof(req));
}
end:
- if (onion_key)
- crypto_pk_free(onion_key);
- if (last_onion_key)
- crypto_pk_free(last_onion_key);
+ memwipe(&req, 0, sizeof(req));
+ memwipe(&rpl, 0, sizeof(req));
+ release_server_onion_keys(&onion_keys);
tor_close_socket(fd);
crypto_thread_cleanup();
spawn_exit();
@@ -371,6 +562,7 @@ static void
spawn_enough_cpuworkers(void)
{
int num_cpuworkers_needed = get_num_cpus(get_options());
+ int reseed = 0;
if (num_cpuworkers_needed < MIN_CPUWORKERS)
num_cpuworkers_needed = MIN_CPUWORKERS;
@@ -383,7 +575,11 @@ spawn_enough_cpuworkers(void)
return;
}
num_cpuworkers++;
+ reseed++;
}
+
+ if (reseed)
+ crypto_seed_weak_rng(&request_sample_rng);
}
/** Take a pending task from the queue and assign it to 'cpuworker'. */
@@ -391,7 +587,7 @@ static void
process_pending_task(connection_t *cpuworker)
{
or_circuit_t *circ;
- char *onionskin = NULL;
+ create_cell_t *onionskin = NULL;
tor_assert(cpuworker);
@@ -444,12 +640,13 @@ cull_wedged_cpuworkers(void)
*/
int
assign_onionskin_to_cpuworker(connection_t *cpuworker,
- or_circuit_t *circ, char *onionskin)
+ or_circuit_t *circ,
+ create_cell_t *onionskin)
{
- char qbuf[1];
- char tag[TAG_LEN];
+ cpuworker_request_t req;
time_t now = approx_time();
static time_t last_culled_cpuworkers = 0;
+ int should_time;
/* Checking for wedged cpuworkers requires a linear search over all
* connections, so let's do it only once a minute.
@@ -483,21 +680,31 @@ assign_onionskin_to_cpuworker(connection_t *cpuworker,
tor_free(onionskin);
return -1;
}
- tag_pack(tag, circ->p_chan->global_identifier,
+
+ should_time = should_time_request(onionskin->handshake_type);
+ memset(&req, 0, sizeof(req));
+ req.magic = CPUWORKER_REQUEST_MAGIC;
+ tag_pack(req.tag, circ->p_chan->global_identifier,
circ->p_circ_id);
+ req.timed = should_time;
cpuworker->state = CPUWORKER_STATE_BUSY_ONION;
/* touch the lastwritten timestamp, since that's how we check to
* see how long it's been since we asked the question, and sometimes
* we check before the first call to connection_handle_write(). */
- cpuworker->timestamp_lastwritten = time(NULL);
+ cpuworker->timestamp_lastwritten = now;
num_cpuworkers_busy++;
- qbuf[0] = CPUWORKER_TASK_ONION;
- connection_write_to_buf(qbuf, 1, cpuworker);
- connection_write_to_buf(tag, sizeof(tag), cpuworker);
- connection_write_to_buf(onionskin, ONIONSKIN_CHALLENGE_LEN, cpuworker);
+ req.task = CPUWORKER_TASK_ONION;
+ memcpy(&req.create_cell, onionskin, sizeof(create_cell_t));
+
tor_free(onionskin);
+
+ if (should_time)
+ tor_gettimeofday(&req.started_at);
+
+ connection_write_to_buf((void*)&req, sizeof(req), cpuworker);
+ memwipe(&req, 0, sizeof(req));
}
return 0;
}
diff --git a/src/or/cpuworker.h b/src/or/cpuworker.h
index 73c7eefd4c..317cef43ba 100644
--- a/src/or/cpuworker.h
+++ b/src/or/cpuworker.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -17,9 +17,15 @@ void cpuworkers_rotate(void);
int connection_cpu_finished_flushing(connection_t *conn);
int connection_cpu_reached_eof(connection_t *conn);
int connection_cpu_process_inbuf(connection_t *conn);
+struct create_cell_t;
int assign_onionskin_to_cpuworker(connection_t *cpuworker,
or_circuit_t *circ,
- char *onionskin);
+ struct create_cell_t *onionskin);
+
+uint64_t estimated_usec_for_onionskins(uint32_t n_requests,
+ uint16_t onionskin_type);
+void cpuworker_log_onionskin_overhead(int severity, int onionskin_type,
+ const char *onionskin_type_name);
#endif
diff --git a/src/or/directory.c b/src/or/directory.c
index 1d511b5749..6b61fc6a99 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#include "or.h"
@@ -244,9 +244,9 @@ router_supports_extrainfo(const char *identity_digest, int is_authority)
int
directories_have_accepted_server_descriptor(void)
{
- smartlist_t *servers = router_get_trusted_dir_servers();
+ const smartlist_t *servers = router_get_trusted_dir_servers();
const or_options_t *options = get_options();
- SMARTLIST_FOREACH(servers, trusted_dir_server_t *, d, {
+ SMARTLIST_FOREACH(servers, dir_server_t *, d, {
if ((d->type & options->PublishServerDescriptor_) &&
d->has_accepted_serverdesc) {
return 1;
@@ -280,7 +280,7 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
{
const or_options_t *options = get_options();
int post_via_tor;
- smartlist_t *dirservers = router_get_trusted_dir_servers();
+ const smartlist_t *dirservers = router_get_trusted_dir_servers();
int found = 0;
const int exclude_self = (dir_purpose == DIR_PURPOSE_UPLOAD_VOTE ||
dir_purpose == DIR_PURPOSE_UPLOAD_SIGNATURES);
@@ -288,7 +288,7 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
/* This tries dirservers which we believe to be down, but ultimately, that's
* harmless, and we may as well err on the side of getting things uploaded.
*/
- SMARTLIST_FOREACH_BEGIN(dirservers, trusted_dir_server_t *, ds) {
+ SMARTLIST_FOREACH_BEGIN(dirservers, dir_server_t *, ds) {
routerstatus_t *rs = &(ds->fake_status);
size_t upload_len = payload_len;
tor_addr_t ds_addr;
@@ -334,6 +334,60 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
}
}
+/** Return true iff, according to the values in <b>options</b>, we should be
+ * using directory guards for direct downloads of directory information. */
+static int
+should_use_directory_guards(const or_options_t *options)
+{
+ /* Public (non-bridge) servers never use directory guards. */
+ if (public_server_mode(options))
+ return 0;
+ /* If guards are disabled, or directory guards are disabled, we can't
+ * use directory guards.
+ */
+ if (!options->UseEntryGuards || !options->UseEntryGuardsAsDirGuards)
+ return 0;
+ /* If we're configured to fetch directory info aggressively or of a
+ * nonstandard type, don't use directory guards. */
+ if (options->DownloadExtraInfo || options->FetchDirInfoEarly ||
+ options->FetchDirInfoExtraEarly || options->FetchUselessDescriptors ||
+ options->FetchV2Networkstatus)
+ return 0;
+ if (! options->PreferTunneledDirConns)
+ return 0;
+ return 1;
+}
+
+/** Pick an unconsetrained directory server from among our guards, the latest
+ * networkstatus, or the fallback dirservers, for use in downloading
+ * information of type <b>type</b>, and return its routerstatus. */
+static const routerstatus_t *
+directory_pick_generic_dirserver(dirinfo_type_t type, int pds_flags,
+ uint8_t dir_purpose)
+{
+ const routerstatus_t *rs = NULL;
+ const or_options_t *options = get_options();
+
+ if (options->UseBridges)
+ log_warn(LD_BUG, "Called when we have UseBridges set.");
+
+ if (should_use_directory_guards(options)) {
+ const node_t *node = choose_random_dirguard(type);
+ if (node)
+ rs = node->rs;
+ } else {
+ /* anybody with a non-zero dirport will do */
+ rs = router_pick_directory_server(type, pds_flags);
+ }
+ if (!rs) {
+ log_info(LD_DIR, "No router found for %s; falling back to "
+ "dirserver list.", dir_conn_purpose_to_string(dir_purpose));
+ rs = router_pick_fallback_dirserver(type, pds_flags);
+ }
+
+ return rs;
+}
+
/** Start a connection to a random running directory server, using
* connection purpose <b>dir_purpose</b>, intending to fetch descriptors
* of purpose <b>router_purpose</b>, and requesting <b>resource</b>.
@@ -418,12 +472,13 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose,
if (options->UseBridges && type != BRIDGE_DIRINFO) {
/* We want to ask a running bridge for which we have a descriptor.
*
- * Be careful here: we should only ask questions that we know our
- * bridges can answer. So far we're solving that by backing off to
- * the behavior supported by our oldest bridge; see for example
- * any_bridges_dont_support_microdescriptors().
+ * When we ask choose_random_entry() for a bridge, we specify what
+ * sort of dir fetch we'll be doing, so it won't return a bridge
+ * that can't answer our question.
*/
- const node_t *node = choose_random_entry(NULL);
+ /* XXX024 Not all bridges handle conditional consensus downloading,
+ * so, for now, never assume the server supports that. -PP */
+ const node_t *node = choose_random_dirguard(type);
if (node && node->ri) {
/* every bridge has a routerinfo. */
tor_addr_t addr;
@@ -469,14 +524,13 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose,
}
}
if (!rs && type != BRIDGE_DIRINFO) {
- /* anybody with a non-zero dirport will do */
- rs = router_pick_directory_server(type, pds_flags);
+ /* */
+ rs = directory_pick_generic_dirserver(type, pds_flags,
+ dir_purpose);
if (!rs) {
- log_info(LD_DIR, "No router found for %s; falling back to "
- "dirserver list.", dir_conn_purpose_to_string(dir_purpose));
- rs = router_pick_trusteddirserver(type, pds_flags);
- if (!rs)
- get_via_tor = 1; /* last resort: try routing it via Tor */
+ /*XXXX024 I'm pretty sure this can never do any good, since
+ * rs isn't set. */
+ get_via_tor = 1; /* last resort: try routing it via Tor */
}
}
}
@@ -528,7 +582,7 @@ directory_get_from_all_authorities(uint8_t dir_purpose,
dir_purpose == DIR_PURPOSE_FETCH_DETACHED_SIGNATURES);
SMARTLIST_FOREACH_BEGIN(router_get_trusted_dir_servers(),
- trusted_dir_server_t *, ds) {
+ dir_server_t *, ds) {
routerstatus_t *rs;
if (router_digest_is_me(ds->digest))
continue;
@@ -716,8 +770,8 @@ connection_dir_download_v2_networkstatus_failed(dir_connection_t *conn,
/* We're a non-authoritative directory cache; try again. Ignore status
* code, since we don't want to keep trying forever in a tight loop
* if all the authorities are shutting us out. */
- smartlist_t *trusted_dirs = router_get_trusted_dir_servers();
- SMARTLIST_FOREACH(trusted_dirs, trusted_dir_server_t *, ds,
+ const smartlist_t *trusted_dirs = router_get_trusted_dir_servers();
+ SMARTLIST_FOREACH(trusted_dirs, dir_server_t *, ds,
download_status_failed(&ds->v2_ns_dl_status, 0));
directory_get_from_dirserver(conn->base_.purpose, conn->router_purpose,
"all.z", 0 /* don't retry_if_no_servers */);
@@ -1088,7 +1142,7 @@ directory_get_consensus_url(const char *resource)
smartlist_t *authority_digests = smartlist_new();
SMARTLIST_FOREACH_BEGIN(router_get_trusted_dir_servers(),
- trusted_dir_server_t *, ds) {
+ dir_server_t *, ds) {
char *hex;
if (!(ds->type & V3_DIRINFO))
continue;
@@ -1657,7 +1711,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
if (status_code == 503) {
routerstatus_t *rs;
- trusted_dir_server_t *ds;
+ dir_server_t *ds;
const char *id_digest = conn->identity_digest;
log_info(LD_DIR,"Received http status code %d (%s) from server "
"'%s:%d'. I'll try again soon.",
@@ -1665,7 +1719,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
conn->base_.port);
if ((rs = router_get_mutable_consensus_status_by_id(id_digest)))
rs->last_dir_503_at = now;
- if ((ds = router_get_trusteddirserver_by_digest(id_digest)))
+ if ((ds = router_get_fallback_dirserver_by_digest(id_digest)))
ds->fake_status.last_dir_503_at = now;
tor_free(body); tor_free(headers); tor_free(reason);
@@ -1764,7 +1818,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
source = NS_FROM_DIR_ALL;
which = smartlist_new();
SMARTLIST_FOREACH(router_get_trusted_dir_servers(),
- trusted_dir_server_t *, ds,
+ dir_server_t *, ds,
{
char *hex = tor_malloc(HEX_DIGEST_LEN+1);
base16_encode(hex, HEX_DIGEST_LEN+1, ds->digest, DIGEST_LEN);
@@ -1807,7 +1861,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
const char *flavname = conn->requested_resource;
if (status_code != 200) {
int severity = (status_code == 304) ? LOG_INFO : LOG_WARN;
- log(severity, LD_DIR,
+ tor_log(severity, LD_DIR,
"Received http status code %d (%s) from server "
"'%s:%d' while fetching consensus directory.",
status_code, escaped(reason), conn->base_.address,
@@ -2021,7 +2075,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
if (conn->base_.purpose == DIR_PURPOSE_UPLOAD_DIR) {
switch (status_code) {
case 200: {
- trusted_dir_server_t *ds =
+ dir_server_t *ds =
router_get_trusteddirserver_by_digest(conn->identity_digest);
char *rejected_hdr = http_get_header(headers,
"X-Descriptor-Not-New: ");
@@ -2747,8 +2801,6 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
/* v2 or v3 network status fetch. */
smartlist_t *dir_fps = smartlist_new();
int is_v3 = !strcmpstart(url, "/tor/status-vote");
- geoip_client_action_t act =
- is_v3 ? GEOIP_CLIENT_NETWORKSTATUS : GEOIP_CLIENT_NETWORKSTATUS_V2;
const char *request_type = NULL;
const char *key = url + strlen("/tor/status/");
long lifetime = NETWORKSTATUS_CACHE_LIFETIME;
@@ -2798,7 +2850,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
write_http_status_line(conn, 404, "Consensus not signed by sufficient "
"number of requested authorities");
smartlist_free(dir_fps);
- geoip_note_ns_response(act, GEOIP_REJECT_NOT_ENOUGH_SIGS);
+ geoip_note_ns_response(GEOIP_REJECT_NOT_ENOUGH_SIGS);
tor_free(flavor);
goto done;
}
@@ -2817,7 +2869,8 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
if (!smartlist_len(dir_fps)) { /* we failed to create/cache cp */
write_http_status_line(conn, 503, "Network status object unavailable");
smartlist_free(dir_fps);
- geoip_note_ns_response(act, GEOIP_REJECT_UNAVAILABLE);
+ if (is_v3)
+ geoip_note_ns_response(GEOIP_REJECT_UNAVAILABLE);
goto done;
}
@@ -2825,13 +2878,15 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
write_http_status_line(conn, 404, "Not found");
SMARTLIST_FOREACH(dir_fps, char *, cp, tor_free(cp));
smartlist_free(dir_fps);
- geoip_note_ns_response(act, GEOIP_REJECT_NOT_FOUND);
+ if (is_v3)
+ geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND);
goto done;
} else if (!smartlist_len(dir_fps)) {
write_http_status_line(conn, 304, "Not modified");
SMARTLIST_FOREACH(dir_fps, char *, cp, tor_free(cp));
smartlist_free(dir_fps);
- geoip_note_ns_response(act, GEOIP_REJECT_NOT_MODIFIED);
+ if (is_v3)
+ geoip_note_ns_response(GEOIP_REJECT_NOT_MODIFIED);
goto done;
}
@@ -2843,24 +2898,24 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
write_http_status_line(conn, 503, "Directory busy, try again later");
SMARTLIST_FOREACH(dir_fps, char *, fp, tor_free(fp));
smartlist_free(dir_fps);
- geoip_note_ns_response(act, GEOIP_REJECT_BUSY);
+ if (is_v3)
+ geoip_note_ns_response(GEOIP_REJECT_BUSY);
goto done;
}
- {
+ if (is_v3) {
struct in_addr in;
tor_addr_t addr;
if (tor_inet_aton((TO_CONN(conn))->address, &in)) {
tor_addr_from_ipv4h(&addr, ntohl(in.s_addr));
- geoip_note_client_seen(act, &addr, time(NULL));
- geoip_note_ns_response(act, GEOIP_SUCCESS);
+ geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, time(NULL));
+ geoip_note_ns_response(GEOIP_SUCCESS);
/* Note that a request for a network status has started, so that we
* can measure the download time later on. */
if (conn->dirreq_id)
- geoip_start_dirreq(conn->dirreq_id, dlen, act,
- DIRREQ_TUNNELED);
+ geoip_start_dirreq(conn->dirreq_id, dlen, DIRREQ_TUNNELED);
else
- geoip_start_dirreq(TO_CONN(conn)->global_identifier, dlen, act,
+ geoip_start_dirreq(TO_CONN(conn)->global_identifier, dlen,
DIRREQ_DIRECT);
}
}
@@ -3597,13 +3652,13 @@ dir_networkstatus_download_failed(smartlist_t *failed, int status_code)
return;
SMARTLIST_FOREACH_BEGIN(failed, const char *, fp) {
char digest[DIGEST_LEN];
- trusted_dir_server_t *dir;
+ dir_server_t *dir;
if (base16_decode(digest, DIGEST_LEN, fp, strlen(fp))<0) {
log_warn(LD_BUG, "Called with bad fingerprint in list: %s",
escaped(fp));
continue;
}
- dir = router_get_trusteddirserver_by_digest(digest);
+ dir = router_get_fallback_dirserver_by_digest(digest);
if (dir)
download_status_failed(&dir->v2_ns_dl_status, status_code);
diff --git a/src/or/directory.h b/src/or/directory.h
index 9ff78d12c4..41f18a1725 100644
--- a/src/or/directory.h
+++ b/src/or/directory.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index e7aa582cfc..0819d4bd24 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#define DIRSERV_PRIVATE
@@ -66,16 +66,6 @@ static cached_dir_t *the_directory = NULL;
/** For authoritative directories: the current (v1) network status. */
static cached_dir_t the_runningrouters;
-/** Array of start and end of consensus methods used for supported
- microdescriptor formats. */
-static const struct consensus_method_range_t {
- int low;
- int high;
-} microdesc_consensus_methods[] = {
- {MIN_METHOD_FOR_MICRODESC, MIN_METHOD_FOR_A_LINES - 1},
- {MIN_METHOD_FOR_A_LINES, MAX_SUPPORTED_CONSENSUS_METHOD},
- {-1, -1}};
-
static void directory_remove_invalid(void);
static cached_dir_t *dirserv_regenerate_directory(void);
static char *format_versions_list(config_line_t *ln);
@@ -1145,6 +1135,8 @@ int
dirserv_dump_directory_to_string(char **dir_out,
crypto_pk_t *private_key)
{
+ /* XXXX 024 Get rid of this function if we can confirm that nobody's
+ * fetching these any longer */
char *cp;
char *identity_pkey; /* Identity key, DER64-encoded. */
char *recommended_versions;
@@ -1445,21 +1437,12 @@ free_cached_dir_(void *_d)
* If <b>is_running_routers</b>, this is really a v1 running_routers
* document rather than a v1 directory.
*/
-void
-dirserv_set_cached_directory(const char *directory, time_t published,
- int is_running_routers)
+static void
+dirserv_set_cached_directory(const char *directory, time_t published)
{
- time_t now = time(NULL);
- if (is_running_routers) {
- if (published >= now - MAX_V1_RR_AGE)
- set_cached_dir(&cached_runningrouters, tor_strdup(directory), published);
- } else {
- if (published >= now - MAX_V1_DIRECTORY_AGE) {
- cached_dir_decref(cached_directory);
- cached_directory = new_cached_dir(tor_strdup(directory), published);
- }
- }
+ cached_dir_decref(cached_directory);
+ cached_directory = new_cached_dir(tor_strdup(directory), published);
}
/** If <b>networkstatus</b> is non-NULL, we've just received a v2
@@ -1476,7 +1459,6 @@ dirserv_set_cached_networkstatus_v2(const char *networkstatus,
time_t published)
{
cached_dir_t *d, *old_d;
- smartlist_t *trusted_dirs;
if (!cached_v2_networkstatus)
cached_v2_networkstatus = digestmap_new();
@@ -1499,9 +1481,9 @@ dirserv_set_cached_networkstatus_v2(const char *networkstatus,
}
/* Now purge old entries. */
- trusted_dirs = router_get_trusted_dir_servers();
+
if (digestmap_size(cached_v2_networkstatus) >
- smartlist_len(trusted_dirs) + MAX_UNTRUSTED_NETWORKSTATUSES) {
+ get_n_authorities(V2_DIRINFO) + MAX_UNTRUSTED_NETWORKSTATUSES) {
/* We need to remove the oldest untrusted networkstatus. */
const char *oldest = NULL;
time_t oldest_published = TIME_MAX;
@@ -1641,6 +1623,8 @@ dirserv_get_directory(void)
static cached_dir_t *
dirserv_regenerate_directory(void)
{
+ /* XXXX 024 Get rid of this function if we can confirm that nobody's
+ * fetching these any longer */
char *new_directory=NULL;
if (dirserv_dump_directory_to_string(&new_directory,
@@ -1660,7 +1644,7 @@ dirserv_regenerate_directory(void)
/* Save the directory to disk so we re-load it quickly on startup.
*/
- dirserv_set_cached_directory(the_directory->dir, time(NULL), 0);
+ dirserv_set_cached_directory(the_directory->dir, time(NULL));
return the_directory;
}
@@ -1887,6 +1871,22 @@ dirserv_thinks_router_is_hs_dir(const routerinfo_t *router,
node->is_running);
}
+/** Don't consider routers with less bandwidth than this when computing
+ * thresholds. */
+#define ABSOLUTE_MIN_BW_VALUE_TO_CONSIDER 4096
+
+/** Helper for dirserv_compute_performance_thresholds(): Decide whether to
+ * include a router in our calculations, and return true iff we should. */
+static int
+router_counts_toward_thresholds(const node_t *node, time_t now,
+ const digestmap_t *omit_as_sybil)
+{
+ return node->ri && router_is_active(node->ri, node, now) &&
+ !digestmap_get(omit_as_sybil, node->ri->cache_info.identity_digest) &&
+ (router_get_advertised_bandwidth(node->ri) >=
+ ABSOLUTE_MIN_BW_VALUE_TO_CONSIDER);
+}
+
/** Look through the routerlist, the Mean Time Between Failure history, and
* the Weighted Fractional Uptime history, and use them to set thresholds for
* the Stable, Fast, and Guard flags. Update the fields stable_uptime,
@@ -1896,7 +1896,8 @@ dirserv_thinks_router_is_hs_dir(const routerinfo_t *router,
*
* Also, set the is_exit flag of each router appropriately. */
static void
-dirserv_compute_performance_thresholds(routerlist_t *rl)
+dirserv_compute_performance_thresholds(routerlist_t *rl,
+ digestmap_t *omit_as_sybil)
{
int n_active, n_active_nonexit, n_familiar;
uint32_t *uptimes, *bandwidths, *bandwidths_excluding_exits;
@@ -1937,8 +1938,8 @@ dirserv_compute_performance_thresholds(routerlist_t *rl)
/* Now, fill in the arrays. */
SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) {
- routerinfo_t *ri = node->ri;
- if (ri && router_is_active(ri, node, now)) {
+ if (router_counts_toward_thresholds(node, now, omit_as_sybil)) {
+ routerinfo_t *ri = node->ri;
const char *id = ri->cache_info.identity_digest;
uint32_t bw;
node->is_exit = (!router_exit_policy_rejects_all(ri) &&
@@ -1978,9 +1979,12 @@ dirserv_compute_performance_thresholds(routerlist_t *rl)
{
/* We can vote on a parameter for the minimum and maximum. */
+#define ABSOLUTE_MIN_VALUE_FOR_FAST_FLAG 4096
int32_t min_fast, max_fast;
min_fast = networkstatus_get_param(NULL, "FastFlagMinThreshold",
- 0, 0, INT32_MAX);
+ ABSOLUTE_MIN_VALUE_FOR_FAST_FLAG,
+ ABSOLUTE_MIN_VALUE_FOR_FAST_FLAG,
+ INT32_MAX);
max_fast = networkstatus_get_param(NULL, "FastFlagMaxThreshold",
INT32_MAX, min_fast, INT32_MAX);
if (fast_bandwidth < (uint32_t)min_fast)
@@ -1999,8 +2003,8 @@ dirserv_compute_performance_thresholds(routerlist_t *rl)
n_familiar = 0;
SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) {
- routerinfo_t *ri = node->ri;
- if (ri && router_is_active(ri, node, now)) {
+ if (router_counts_toward_thresholds(node, now, omit_as_sybil)) {
+ routerinfo_t *ri = node->ri;
const char *id = ri->cache_info.identity_digest;
long tk = rep_hist_get_weighted_time_known(id, now);
if (tk < guard_tk)
@@ -2020,7 +2024,7 @@ dirserv_compute_performance_thresholds(routerlist_t *rl)
median_uint32(bandwidths_excluding_exits, n_active_nonexit);
}
- log(LOG_INFO, LD_DIRSERV,
+ log_info(LD_DIRSERV,
"Cutoffs: For Stable, %lu sec uptime, %lu sec MTBF. "
"For Fast: %lu bytes/sec. "
"For Guard: WFU %.03f%%, time-known %lu sec, "
@@ -2042,6 +2046,30 @@ dirserv_compute_performance_thresholds(routerlist_t *rl)
tor_free(wfus);
}
+/** Give a statement of our current performance thresholds for inclusion
+ * in a vote document. */
+char *
+dirserv_get_flag_thresholds_line(void)
+{
+ char *result=NULL;
+ tor_asprintf(&result,
+ "stable-uptime=%lu stable-mtbf=%lu "
+ "fast-speed=%lu "
+ "guard-wfu=%.03f%% guard-tk=%lu "
+ "guard-bw-inc-exits=%lu guard-bw-exc-exits=%lu "
+ "enough-mtbf=%d",
+ (unsigned long)stable_uptime,
+ (unsigned long)stable_mtbf,
+ (unsigned long)fast_bandwidth,
+ guard_wfu*100,
+ (unsigned long)guard_tk,
+ (unsigned long)guard_bandwidth_including_exits,
+ (unsigned long)guard_bandwidth_excluding_exits,
+ enough_mtbf_info ? 1 : 0);
+
+ return result;
+}
+
/** Given a platform string as in a routerinfo_t (possibly null), return a
* newly allocated version string for a networkstatus document, or NULL if the
* platform doesn't give a Tor version. */
@@ -2184,7 +2212,7 @@ routerstatus_format_entry(char *buf, size_t buf_len,
"(wanted descriptor %s).",
id, dd);
return -1;
- };
+ }
/* This assert can fire for the control port, because
* it can request NS documents before all descriptors
@@ -2208,7 +2236,7 @@ routerstatus_format_entry(char *buf, size_t buf_len,
tor_assert(tor_memeq(desc->cache_info.signed_descriptor_digest,
rs->descriptor_digest,
DIGEST_LEN));
- };
+ }
}
if (format == NS_CONTROL_PORT && rs->has_bandwidth) {
@@ -2237,7 +2265,7 @@ routerstatus_format_entry(char *buf, size_t buf_len,
}
if (desc) {
- summary = policy_summarize(desc->exit_policy);
+ summary = policy_summarize(desc->exit_policy, AF_INET);
r = tor_snprintf(cp, buf_len - (cp-buf), "p %s\n", summary);
if (r<0) {
log_warn(LD_BUG, "Not enough space in buffer.");
@@ -2273,7 +2301,7 @@ compare_routerinfo_by_ip_and_bw_(const void **a, const void **b)
else if (first->addr > second->addr)
return 1;
- /* Potentially, this next bit could cause k n lg n memcmp calls. But in
+ /* Potentially, this next bit could cause k n lg n memeq calls. But in
* reality, we will almost never get here, since addresses will usually be
* different. */
@@ -2722,11 +2750,11 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
tor_assert(private_key);
tor_assert(cert);
- if (resolve_my_address(LOG_WARN, options, &addr, &hostname)<0) {
+ if (resolve_my_address(LOG_WARN, options, &addr, NULL, &hostname)<0) {
log_warn(LD_NET, "Couldn't resolve my hostname");
return NULL;
}
- if (!strchr(hostname, '.')) {
+ if (!hostname || !strchr(hostname, '.')) {
tor_free(hostname);
hostname = tor_dup_ip(addr);
}
@@ -2754,22 +2782,25 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
dirserv_set_router_is_running(ri, now);
});
- dirserv_compute_performance_thresholds(rl);
-
routers = smartlist_new();
smartlist_add_all(routers, rl->routers);
routers_sort_by_identity(routers);
omit_as_sybil = get_possible_sybil_list(routers);
+ DIGESTMAP_FOREACH(omit_as_sybil, sybil_id, void *, ignore) {
+ (void) ignore;
+ rep_hist_make_router_pessimal(sybil_id, now);
+ } DIGESTMAP_FOREACH_END;
+
+ dirserv_compute_performance_thresholds(rl, omit_as_sybil);
+
routerstatuses = smartlist_new();
microdescriptors = smartlist_new();
SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) {
- const struct consensus_method_range_t *cmr = NULL;
if (ri->cache_info.published_on >= cutoff) {
routerstatus_t *rs;
vote_routerstatus_t *vrs;
- microdesc_t *md;
node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest);
if (!node)
continue;
@@ -2787,23 +2818,8 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
rs->is_flagged_running = 0;
vrs->version = version_from_platform(ri->platform);
- for (cmr = microdesc_consensus_methods;
- cmr->low != -1 && cmr->high != -1;
- cmr++) {
- md = dirvote_create_microdescriptor(ri, cmr->low);
- if (md) {
- char buf[128];
- vote_microdesc_hash_t *h;
- dirvote_format_microdesc_vote_line(buf, sizeof(buf), md,
- cmr->low, cmr->high);
- h = tor_malloc_zero(sizeof(vote_microdesc_hash_t));
- h->microdesc_hash_line = tor_strdup(buf);
- h->next = vrs->microdesc;
- vrs->microdesc = h;
- md->last_listed = now;
- smartlist_add(microdescriptors, md);
- }
- }
+ vrs->microdesc = dirvote_format_all_microdesc_vote_lines(ri, now,
+ microdescriptors);
smartlist_add(routerstatuses, vrs);
}
@@ -2944,10 +2960,12 @@ generate_v2_networkstatus_opinion(void)
private_key = get_server_identity_key();
- if (resolve_my_address(LOG_WARN, options, &addr, &hostname)<0) {
+ if (resolve_my_address(LOG_WARN, options, &addr, NULL, &hostname)<0) {
log_warn(LD_NET, "Couldn't resolve my hostname");
goto done;
}
+ if (!hostname)
+ hostname = tor_dup_ip(addr);
format_iso_time(published, now);
@@ -3011,14 +3029,13 @@ generate_v2_networkstatus_opinion(void)
dirserv_set_router_is_running(ri, now);
});
- dirserv_compute_performance_thresholds(rl);
-
routers = smartlist_new();
smartlist_add_all(routers, rl->routers);
routers_sort_by_identity(routers);
-
omit_as_sybil = get_possible_sybil_list(routers);
+ dirserv_compute_performance_thresholds(rl, omit_as_sybil);
+
SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) {
if (ri->cache_info.published_on >= cutoff) {
routerstatus_t rs;
@@ -3130,7 +3147,7 @@ dirserv_get_networkstatus_v2_fingerprints(smartlist_t *result,
}
} else {
SMARTLIST_FOREACH(router_get_trusted_dir_servers(),
- trusted_dir_server_t *, ds,
+ dir_server_t *, ds,
if (ds->type & V2_DIRINFO)
smartlist_add(result, tor_memdup(ds->digest, DIGEST_LEN)));
}
diff --git a/src/or/dirserv.h b/src/or/dirserv.h
index ca401407d5..add09f44a3 100644
--- a/src/or/dirserv.h
+++ b/src/or/dirserv.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -70,6 +70,7 @@ int list_server_status_v1(smartlist_t *routers, char **router_status_out,
int for_controller);
int dirserv_dump_directory_to_string(char **dir_out,
crypto_pk_t *private_key);
+char *dirserv_get_flag_thresholds_line(void);
int directory_fetches_from_authorities(const or_options_t *options);
int directory_fetches_dir_info_early(const or_options_t *options);
@@ -87,8 +88,6 @@ void directory_set_dirty(void);
cached_dir_t *dirserv_get_directory(void);
cached_dir_t *dirserv_get_runningrouters(void);
cached_dir_t *dirserv_get_consensus(const char *flavor_name);
-void dirserv_set_cached_directory(const char *directory, time_t when,
- int is_running_routers);
void dirserv_set_cached_networkstatus_v2(const char *directory,
const char *identity,
time_t published);
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index 70209435cb..358708b6c5 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#define DIRVOTE_PRIVATE
@@ -134,6 +134,8 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
char fu[ISO_TIME_LEN+1];
char vu[ISO_TIME_LEN+1];
char *flags = smartlist_join_strings(v3_ns->known_flags, " ", 0, NULL);
+ /* XXXX Abstraction violation: should be pulling a field out of v3_ns.*/
+ char *flag_thresholds = dirserv_get_flag_thresholds_line();
char *params;
authority_cert_t *cert = v3_ns->cert;
char *methods =
@@ -160,6 +162,7 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
"voting-delay %d %d\n"
"%s" /* versions */
"known-flags %s\n"
+ "flag-thresholds %s\n"
"params %s\n"
"dir-source %s %s %s %s %d %d\n"
"contact %s\n",
@@ -169,6 +172,7 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
v3_ns->vote_seconds, v3_ns->dist_seconds,
version_lines,
flags,
+ flag_thresholds,
params,
voter->nickname, fingerprint, voter->address,
fmt_addr32(addr), voter->dir_port, voter->or_port,
@@ -181,6 +185,7 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
tor_free(params);
tor_free(flags);
+ tor_free(flag_thresholds);
tor_free(methods);
outp = status + strlen(status);
endp = status + len;
@@ -2116,7 +2121,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
digest, digest_len,
signing_key)) {
log_warn(LD_BUG, "Couldn't sign consensus networkstatus.");
- return NULL; /* This leaks, but it should never happen. */
+ goto done;
}
smartlist_add(chunks, tor_strdup(sigbuf));
@@ -2139,7 +2144,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
digest, digest_len,
legacy_signing_key)) {
log_warn(LD_BUG, "Couldn't sign consensus networkstatus.");
- return NULL; /* This leaks, but it should never happen. */
+ goto done;
}
smartlist_add(chunks, tor_strdup(sigbuf));
}
@@ -2147,13 +2152,6 @@ networkstatus_compute_consensus(smartlist_t *votes,
result = smartlist_join_strings(chunks, "", 0, NULL);
- tor_free(client_versions);
- tor_free(server_versions);
- SMARTLIST_FOREACH(flags, char *, cp, tor_free(cp));
- smartlist_free(flags);
- SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
- smartlist_free(chunks);
-
{
networkstatus_t *c;
if (!(c = networkstatus_parse_vote_from_string(result, NULL,
@@ -2161,7 +2159,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
log_err(LD_BUG, "Generated a networkstatus consensus we couldn't "
"parse.");
tor_free(result);
- return NULL;
+ goto done;
}
// Verify balancing parameters
if (consensus_method >= MIN_METHOD_FOR_BW_WEIGHTS && added_weights) {
@@ -2170,6 +2168,15 @@ networkstatus_compute_consensus(smartlist_t *votes,
networkstatus_vote_free(c);
}
+ done:
+
+ tor_free(client_versions);
+ tor_free(server_versions);
+ SMARTLIST_FOREACH(flags, char *, cp, tor_free(cp));
+ smartlist_free(flags);
+ SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
+ smartlist_free(chunks);
+
return result;
}
@@ -2223,7 +2230,7 @@ networkstatus_add_detached_signatures(networkstatus_t *target,
{
digests_t *digests = strmap_get(sigs->digests, flavor);
int n_matches = 0;
- digest_algorithm_t alg;
+ int alg;
if (!digests) {
*msg_out = "No digests for given consensus flavor";
return -1;
@@ -2288,7 +2295,7 @@ networkstatus_add_detached_signatures(networkstatus_t *target,
if (sig->good_signature || !old_sig || old_sig->bad_signature) {
log_info(LD_DIR, "Adding signature from %s with %s", voter_identity,
algorithm);
- log(severity, LD_DIR, "Added a signature for %s from %s.",
+ tor_log(severity, LD_DIR, "Added a signature for %s from %s.",
target_voter->nickname, source);
++r;
if (old_sig) {
@@ -2787,7 +2794,7 @@ dirvote_fetch_missing_votes(void)
char *resource;
SMARTLIST_FOREACH_BEGIN(router_get_trusted_dir_servers(),
- trusted_dir_server_t *, ds) {
+ dir_server_t *, ds) {
if (!(ds->type & V3_DIRINFO))
continue;
if (!dirvote_get_vote(ds->v3_identity_digest,
@@ -2905,7 +2912,7 @@ list_v3_auth_ids(void)
smartlist_t *known_v3_keys = smartlist_new();
char *keys;
SMARTLIST_FOREACH(router_get_trusted_dir_servers(),
- trusted_dir_server_t *, ds,
+ dir_server_t *, ds,
if ((ds->type & V3_DIRINFO) &&
!tor_digest_is_zero(ds->v3_identity_digest))
smartlist_add(known_v3_keys,
@@ -2926,7 +2933,7 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out)
{
networkstatus_t *vote;
networkstatus_voter_info_t *vi;
- trusted_dir_server_t *ds;
+ dir_server_t *ds;
pending_vote_t *pending_vote = NULL;
const char *end_of_vote = NULL;
int any_failed = 0;
@@ -3110,7 +3117,7 @@ dirvote_compute_consensuses(void)
}
tor_assert(pending_vote_list);
SMARTLIST_FOREACH(pending_vote_list, pending_vote_t *, v, {
- if (smartlist_string_isin(v->vote->known_flags, "Running"))
+ if (smartlist_contains_string(v->vote->known_flags, "Running"))
n_vote_running++;
});
if (!n_vote_running) {
@@ -3471,7 +3478,7 @@ dirvote_free_all(void)
const char *
dirvote_get_pending_consensus(consensus_flavor_t flav)
{
- tor_assert(((int)flav) >= 0 && flav < N_CONSENSUS_FLAVORS);
+ tor_assert(((int)flav) >= 0 && (int)flav < N_CONSENSUS_FLAVORS);
return pending_consensuses[flav].body;
}
@@ -3534,12 +3541,8 @@ dirvote_get_vote(const char *fp, int flags)
return NULL;
}
-/** Construct and return a new microdescriptor from a routerinfo <b>ri</b>.
- *
- * XXX Right now, there is only one way to generate microdescriptors from
- * router descriptors. This may change in future consensus methods. If so,
- * we'll need an internal way to remember which method we used, and ask for a
- * particular method.
+/** Construct and return a new microdescriptor from a routerinfo <b>ri</b>
+ * according to <b>consensus_method</b>.
**/
microdesc_t *
dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
@@ -3552,12 +3555,21 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
if (crypto_pk_write_public_key_to_string(ri->onion_pkey, &key, &keylen)<0)
goto done;
- summary = policy_summarize(ri->exit_policy);
+ summary = policy_summarize(ri->exit_policy, AF_INET);
if (ri->declared_family)
family = smartlist_join_strings(ri->declared_family, " ", 0, NULL);
smartlist_add_asprintf(chunks, "onion-key\n%s", key);
+ if (consensus_method >= MIN_METHOD_FOR_NTOR_KEY &&
+ ri->onion_curve25519_pkey) {
+ char kbuf[128];
+ base64_encode(kbuf, sizeof(kbuf),
+ (const char*)ri->onion_curve25519_pkey->public_key,
+ CURVE25519_PUBKEY_LEN);
+ smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf);
+ }
+
if (consensus_method >= MIN_METHOD_FOR_A_LINES &&
!tor_addr_is_null(&ri->ipv6_addr) && ri->ipv6_orport)
smartlist_add_asprintf(chunks, "a %s\n",
@@ -3569,6 +3581,17 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
if (summary && strcmp(summary, "reject 1-65535"))
smartlist_add_asprintf(chunks, "p %s\n", summary);
+ if (consensus_method >= MIN_METHOD_FOR_P6_LINES &&
+ ri->ipv6_exit_policy) {
+ /* XXXX024 This doesn't match proposal 208, which says these should
+ * be taken unchanged from the routerinfo. That's bogosity, IMO:
+ * the proposal should have said to do this instead.*/
+ char *p6 = write_short_policy(ri->ipv6_exit_policy);
+ if (p6 && strcmp(p6, "reject 1-65535"))
+ smartlist_add_asprintf(chunks, "p6 %s\n", p6);
+ tor_free(p6);
+ }
+
output = smartlist_join_strings(chunks, "", 0, NULL);
{
@@ -3628,6 +3651,87 @@ dirvote_format_microdesc_vote_line(char *out_buf, size_t out_buf_len,
return ret;
}
+/** Array of start and end of consensus methods used for supported
+ microdescriptor formats. */
+static const struct consensus_method_range_t {
+ int low;
+ int high;
+} microdesc_consensus_methods[] = {
+ {MIN_METHOD_FOR_MICRODESC, MIN_METHOD_FOR_A_LINES - 1},
+ {MIN_METHOD_FOR_A_LINES, MIN_METHOD_FOR_P6_LINES - 1},
+ {MIN_METHOD_FOR_P6_LINES, MIN_METHOD_FOR_NTOR_KEY - 1},
+ {MIN_METHOD_FOR_NTOR_KEY, MAX_SUPPORTED_CONSENSUS_METHOD},
+ {-1, -1}
+};
+
+/** Helper type used when generating the microdescriptor lines in a directory
+ * vote. */
+typedef struct microdesc_vote_line_t {
+ int low;
+ int high;
+ microdesc_t *md;
+ struct microdesc_vote_line_t *next;
+} microdesc_vote_line_t;
+
+/** Generate and return a linked list of all the lines that should appear to
+ * describe a router's microdescriptor versions in a directory vote.
+ * Add the generated microdescriptors to <b>microdescriptors_out</b>. */
+vote_microdesc_hash_t *
+dirvote_format_all_microdesc_vote_lines(const routerinfo_t *ri, time_t now,
+ smartlist_t *microdescriptors_out)
+{
+ const struct consensus_method_range_t *cmr;
+ microdesc_vote_line_t *entries = NULL, *ep;
+ vote_microdesc_hash_t *result = NULL;
+
+ /* Generate the microdescriptors. */
+ for (cmr = microdesc_consensus_methods;
+ cmr->low != -1 && cmr->high != -1;
+ cmr++) {
+ microdesc_t *md = dirvote_create_microdescriptor(ri, cmr->low);
+ if (md) {
+ microdesc_vote_line_t *e =
+ tor_malloc_zero(sizeof(microdesc_vote_line_t));
+ e->md = md;
+ e->low = cmr->low;
+ e->high = cmr->high;
+ e->next = entries;
+ entries = e;
+ }
+ }
+
+ /* Compress adjacent identical ones */
+ for (ep = entries; ep; ep = ep->next) {
+ while (ep->next &&
+ fast_memeq(ep->md->digest, ep->next->md->digest, DIGEST256_LEN) &&
+ ep->low == ep->next->high + 1) {
+ microdesc_vote_line_t *next = ep->next;
+ ep->low = next->low;
+ microdesc_free(next->md);
+ ep->next = next->next;
+ tor_free(next);
+ }
+ }
+
+ /* Format them into vote_microdesc_hash_t, and add to microdescriptors_out.*/
+ while ((ep = entries)) {
+ char buf[128];
+ vote_microdesc_hash_t *h;
+ dirvote_format_microdesc_vote_line(buf, sizeof(buf), ep->md,
+ ep->low, ep->high);
+ h = tor_malloc_zero(sizeof(vote_microdesc_hash_t));
+ h->microdesc_hash_line = tor_strdup(buf);
+ h->next = result;
+ result = h;
+ ep->md->last_listed = now;
+ smartlist_add(microdescriptors_out, ep->md);
+ entries = ep->next;
+ tor_free(ep);
+ }
+
+ return result;
+}
+
/** If <b>vrs</b> has a hash made for the consensus method <b>method</b> with
* the digest algorithm <b>alg</b>, decode it and copy it into
* <b>digest256_out</b> and return 0. Otherwise return -1. */
diff --git a/src/or/dirvote.h b/src/or/dirvote.h
index 04cf2f9710..366b7cf037 100644
--- a/src/or/dirvote.h
+++ b/src/or/dirvote.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -20,7 +20,7 @@
#define MIN_VOTE_INTERVAL 300
/** The highest consensus method that we currently support. */
-#define MAX_SUPPORTED_CONSENSUS_METHOD 14
+#define MAX_SUPPORTED_CONSENSUS_METHOD 16
/** Lowest consensus method that contains a 'directory-footer' marker */
#define MIN_METHOD_FOR_FOOTER 9
@@ -45,6 +45,13 @@
/** Lowest consensus method that contains "a" lines. */
#define MIN_METHOD_FOR_A_LINES 14
+/** Lowest consensus method where microdescs may include a "p6" line. */
+#define MIN_METHOD_FOR_P6_LINES 15
+
+/** Lowest consensus method where microdescs may include an onion-key-ntor
+ * line */
+#define MIN_METHOD_FOR_NTOR_KEY 16
+
void dirvote_free_all(void);
/* vote manipulation */
@@ -102,6 +109,11 @@ ssize_t dirvote_format_microdesc_vote_line(char *out, size_t out_len,
const microdesc_t *md,
int consensus_method_low,
int consensus_method_high);
+vote_microdesc_hash_t *dirvote_format_all_microdesc_vote_lines(
+ const routerinfo_t *ri,
+ time_t now,
+ smartlist_t *microdescriptors_out);
+
int vote_routerstatus_find_microdesc_hash(char *digest256_out,
const vote_routerstatus_t *vrs,
int method,
diff --git a/src/or/dns.c b/src/or/dns.c
index 5e1d0b48db..edcf92e5b3 100644
--- a/src/or/dns.c
+++ b/src/or/dns.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2003-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -61,6 +61,9 @@ struct evdns_request;
#define evdns_base_resolve_ipv4(base, addr, options, cb, ptr) \
((evdns_resolve_ipv4((addr), (options), (cb), (ptr))!=0) \
? NULL : ((void*)1))
+#define evdns_base_resolve_ipv6(base, addr, options, cb, ptr) \
+ ((evdns_resolve_ipv6((addr), (options), (cb), (ptr))!=0) \
+ ? NULL : ((void*)1))
#define evdns_base_resolve_reverse(base, addr, options, cb, ptr) \
((evdns_resolve_reverse((addr), (options), (cb), (ptr))!=0) \
? NULL : ((void*)1))
@@ -84,12 +87,6 @@ struct evdns_request;
* that the resolver is wedged? */
#define RESOLVE_MAX_TIMEOUT 300
-/** Possible outcomes from hostname lookup: permanent failure,
- * transient (retryable) failure, and success. */
-#define DNS_RESOLVE_FAILED_TRANSIENT 1
-#define DNS_RESOLVE_FAILED_PERMANENT 2
-#define DNS_RESOLVE_SUCCEEDED 3
-
/** Our evdns_base; this structure handles all our name lookups. */
static struct evdns_base *the_evdns_base = NULL;
@@ -117,7 +114,7 @@ typedef struct pending_connection_t {
/* Possible states for a cached resolve_t */
/** We are waiting for the resolver system to tell us an answer here.
* When we get one, or when we time out, the state of this cached_resolve_t
- * will become "DONE" and we'll possibly add a CACHED_VALID or a CACHED_FAILED
+ * will become "DONE" and we'll possibly add a CACHED
* entry. This cached_resolve_t will be in the hash table so that we will
* know not to launch more requests for this addr, but rather to add more
* connections to the pending list for the addr. */
@@ -128,10 +125,18 @@ typedef struct pending_connection_t {
#define CACHE_STATE_DONE 1
/** We are caching an answer for this address. This should have no pending
* connections, and should appear in the hash table. */
-#define CACHE_STATE_CACHED_VALID 2
-/** We are caching a failure for this address. This should have no pending
- * connections, and should appear in the hash table */
-#define CACHE_STATE_CACHED_FAILED 3
+#define CACHE_STATE_CACHED 2
+
+/** @name status values for a single DNS request.
+ *
+ * @{ */
+/** The DNS request is in progress. */
+#define RES_STATUS_INFLIGHT 1
+/** The DNS request finished and gave an answer */
+#define RES_STATUS_DONE_OK 2
+/** The DNS request finished and gave an error */
+#define RES_STATUS_DONE_ERR 3
+/**@}*/
/** A DNS request: possibly completed, possibly pending; cached_resolve
* structs are stored at the OR side in a hash table, and as a linked
@@ -139,19 +144,39 @@ typedef struct pending_connection_t {
*/
typedef struct cached_resolve_t {
HT_ENTRY(cached_resolve_t) node;
- uint32_t magic;
+ uint32_t magic; /**< Must be CACHED_RESOLVE_MAGIC */
char address[MAX_ADDRESSLEN]; /**< The hostname to be resolved. */
+
+ union {
+ uint32_t addr_ipv4; /**< IPv4 addr for <b>address</b>, if successful.
+ * (In host order.) */
+ int err_ipv4; /**< One of DNS_ERR_*, if IPv4 lookup failed. */
+ } result_ipv4; /**< Outcome of IPv4 lookup */
+ union {
+ struct in6_addr addr_ipv6; /**< IPv6 addr for <b>address</b>, if
+ * successful */
+ int err_ipv6; /**< One of DNS_ERR_*, if IPv6 lookup failed. */
+ } result_ipv6; /**< Outcome of IPv6 lookup, if any */
union {
- struct {
- struct in6_addr addr6; /**< IPv6 addr for <b>address</b>. */
- uint32_t addr; /**< IPv4 addr for <b>address</b>. */
- } a;
- char *hostname; /**< Hostname for <b>address</b> (if a reverse lookup) */
- } result;
- uint8_t state; /**< Is this cached entry pending/done/valid/failed? */
- uint8_t is_reverse; /**< Is this a reverse (addr-to-hostname) lookup? */
+ char *hostname; /** A hostname, if PTR lookup happened successfully*/
+ int err_hostname; /** One of DNS_ERR_*, if PTR lookup failed. */
+ } result_ptr;
+ /** @name Status fields
+ *
+ * These take one of the RES_STATUS_* values, depending on the state
+ * of the corresponding lookup.
+ *
+ * @{ */
+ unsigned int res_status_ipv4 : 2;
+ unsigned int res_status_ipv6 : 2;
+ unsigned int res_status_hostname : 2;
+ /**@}*/
+ uint8_t state; /**< Is this cached entry pending/done/informative? */
+
time_t expire; /**< Remove items from cache after this time. */
- uint32_t ttl; /**< What TTL did the nameserver tell us? */
+ uint32_t ttl_ipv4; /**< What TTL did the nameserver tell us? */
+ uint32_t ttl_ipv6; /**< What TTL did the nameserver tell us? */
+ uint32_t ttl_hostname; /**< What TTL did the nameserver tell us? */
/** Connections that want to know when we get an answer for this resolve. */
pending_connection_t *pending_connections;
/** Position of this element in the heap*/
@@ -159,17 +184,28 @@ typedef struct cached_resolve_t {
} cached_resolve_t;
static void purge_expired_resolves(time_t now);
-static void dns_found_answer(const char *address, uint8_t is_reverse,
- uint32_t addr, const char *hostname, char outcome,
+static void dns_found_answer(const char *address, uint8_t query_type,
+ int dns_answer,
+ const tor_addr_t *addr,
+ const char *hostname,
uint32_t ttl);
-static void send_resolved_cell(edge_connection_t *conn, uint8_t answer_type);
-static int launch_resolve(edge_connection_t *exitconn);
+static void send_resolved_cell(edge_connection_t *conn, uint8_t answer_type,
+ const cached_resolve_t *resolve);
+static int launch_resolve(cached_resolve_t *resolve);
static void add_wildcarded_test_address(const char *address);
static int configure_nameservers(int force);
static int answer_is_wildcarded(const char *ip);
static int dns_resolve_impl(edge_connection_t *exitconn, int is_resolve,
or_circuit_t *oncirc, char **resolved_to_hostname,
- int *made_connection_pending_out);
+ int *made_connection_pending_out,
+ cached_resolve_t **resolve_out);
+static int set_exitconn_info_from_resolve(edge_connection_t *exitconn,
+ const cached_resolve_t *resolve,
+ char **hostname_out);
+static int evdns_err_is_transient(int err);
+static void inform_pending_connections(cached_resolve_t *resolve);
+static void make_pending_resolve_cached(cached_resolve_t *cached);
+
#ifdef DEBUG_DNS_CACHE
static void assert_cache_ok_(void);
#define assert_cache_ok() assert_cache_ok_()
@@ -181,6 +217,13 @@ static void assert_resolve_ok(cached_resolve_t *resolve);
/** Hash table of cached_resolve objects. */
static HT_HEAD(cache_map, cached_resolve_t) cache_root;
+/** Global: how many IPv6 requests have we made in all? */
+static uint64_t n_ipv6_requests_made = 0;
+/** Global: how many IPv6 requests have timed out? */
+static uint64_t n_ipv6_timeouts = 0;
+/** Global: Do we think that IPv6 DNS is broken? */
+static int dns_is_broken_for_ipv6 = 0;
+
/** Function to compare hashed resolves on their addresses; used to
* implement hash tables. */
static INLINE int
@@ -219,7 +262,7 @@ evdns_log_cb(int warn, const char *msg)
int severity = warn ? LOG_WARN : LOG_INFO;
if (!strcmpstart(msg, "Resolve requested for") &&
get_options()->SafeLogging) {
- log(LOG_INFO, LD_EXIT, "eventdns: Resolve requested.");
+ log_info(LD_EXIT, "eventdns: Resolve requested.");
return;
} else if (!strcmpstart(msg, "Search: ")) {
return;
@@ -248,7 +291,7 @@ evdns_log_cb(int warn, const char *msg)
control_event_server_status(LOG_WARN, "NAMESERVER_ALL_DOWN");
all_down = 1;
}
- log(severity, LD_EXIT, "eventdns: %s", msg);
+ tor_log(severity, LD_EXIT, "eventdns: %s", msg);
}
/** Helper: passed to eventdns.c as a callback so it can generate random
@@ -345,8 +388,8 @@ free_cached_resolve_(cached_resolve_t *r)
r->pending_connections = victim->next;
tor_free(victim);
}
- if (r->is_reverse)
- tor_free(r->result.hostname);
+ if (r->res_status_hostname == RES_STATUS_DONE_OK)
+ tor_free(r->result_ptr.hostname);
r->magic = 0xFF00FF00;
tor_free(r);
}
@@ -370,6 +413,65 @@ compare_cached_resolves_by_expiry_(const void *_a, const void *_b)
* will expire. */
static smartlist_t *cached_resolve_pqueue = NULL;
+static void
+cached_resolve_add_answer(cached_resolve_t *resolve,
+ int query_type,
+ int dns_result,
+ const tor_addr_t *answer_addr,
+ const char *answer_hostname,
+ uint32_t ttl)
+{
+ if (query_type == DNS_PTR) {
+ if (resolve->res_status_hostname != RES_STATUS_INFLIGHT)
+ return;
+
+ if (dns_result == DNS_ERR_NONE && answer_hostname) {
+ resolve->result_ptr.hostname = tor_strdup(answer_hostname);
+ resolve->res_status_hostname = RES_STATUS_DONE_OK;
+ } else {
+ resolve->result_ptr.err_hostname = dns_result;
+ resolve->res_status_hostname = RES_STATUS_DONE_ERR;
+ }
+ resolve->ttl_hostname = ttl;
+ } else if (query_type == DNS_IPv4_A) {
+ if (resolve->res_status_ipv4 != RES_STATUS_INFLIGHT)
+ return;
+
+ if (dns_result == DNS_ERR_NONE && answer_addr) {
+ tor_assert(tor_addr_family(answer_addr) == AF_INET);
+ resolve->result_ipv4.addr_ipv4 = tor_addr_to_ipv4h(answer_addr);
+ resolve->res_status_ipv4 = RES_STATUS_DONE_OK;
+ } else {
+ resolve->result_ipv4.err_ipv4 = dns_result;
+ resolve->res_status_ipv4 = RES_STATUS_DONE_ERR;
+ }
+
+ } else if (query_type == DNS_IPv6_AAAA) {
+ if (resolve->res_status_ipv6 != RES_STATUS_INFLIGHT)
+ return;
+
+ if (dns_result == DNS_ERR_NONE && answer_addr) {
+ tor_assert(tor_addr_family(answer_addr) == AF_INET6);
+ memcpy(&resolve->result_ipv6.addr_ipv6,
+ tor_addr_to_in6(answer_addr),
+ sizeof(struct in6_addr));
+ resolve->res_status_ipv6 = RES_STATUS_DONE_OK;
+ } else {
+ resolve->result_ipv6.err_ipv6 = dns_result;
+ resolve->res_status_ipv6 = RES_STATUS_DONE_ERR;
+ }
+ }
+}
+
+/** Return true iff there are no in-flight requests for <b>resolve</b>. */
+static int
+cached_resolve_have_all_answers(const cached_resolve_t *resolve)
+{
+ return (resolve->res_status_ipv4 != RES_STATUS_INFLIGHT &&
+ resolve->res_status_ipv6 != RES_STATUS_INFLIGHT &&
+ resolve->res_status_hostname != RES_STATUS_INFLIGHT);
+}
+
/** Set an expiry time for a cached_resolve_t, and add it to the expiry
* priority queue */
static void
@@ -435,8 +537,7 @@ purge_expired_resolves(time_t now)
"Expiring a dns resolve %s that's still pending. Forgot to "
"cull it? DNS resolve didn't tell us about the timeout?",
escaped_safe_str(resolve->address));
- } else if (resolve->state == CACHE_STATE_CACHED_VALID ||
- resolve->state == CACHE_STATE_CACHED_FAILED) {
+ } else if (resolve->state == CACHE_STATE_CACHED) {
log_debug(LD_EXIT,
"Forgetting old cached resolve (address %s, expires %lu)",
escaped_safe_str(resolve->address),
@@ -465,8 +566,7 @@ purge_expired_resolves(time_t now)
}
}
- if (resolve->state == CACHE_STATE_CACHED_VALID ||
- resolve->state == CACHE_STATE_CACHED_FAILED ||
+ if (resolve->state == CACHE_STATE_CACHED ||
resolve->state == CACHE_STATE_PENDING) {
removed = HT_REMOVE(cache_map, &cache_root, resolve);
if (removed != resolve) {
@@ -481,8 +581,8 @@ purge_expired_resolves(time_t now)
cached_resolve_t *tmp = HT_FIND(cache_map, &cache_root, resolve);
tor_assert(tmp != resolve);
}
- if (resolve->is_reverse)
- tor_free(resolve->result.hostname);
+ if (resolve->res_status_hostname == RES_STATUS_DONE_OK)
+ tor_free(resolve->result_ptr.hostname);
resolve->magic = 0xF0BBF0BB;
tor_free(resolve);
}
@@ -490,19 +590,24 @@ purge_expired_resolves(time_t now)
assert_cache_ok();
}
+/* argument for send_resolved_cell only, meaning "let the answer type be ipv4
+ * or ipv6 depending on the connection's address". */
+#define RESOLVED_TYPE_AUTO 0xff
+
/** Send a response to the RESOLVE request of a connection.
* <b>answer_type</b> must be one of
- * RESOLVED_TYPE_(IPV4|ERROR|ERROR_TRANSIENT).
+ * RESOLVED_TYPE_(AUTO|ERROR|ERROR_TRANSIENT|).
*
* If <b>circ</b> is provided, and we have a cached answer, send the
* answer back along circ; otherwise, send the answer back along
* <b>conn</b>'s attached circuit.
*/
static void
-send_resolved_cell(edge_connection_t *conn, uint8_t answer_type)
+send_resolved_cell(edge_connection_t *conn, uint8_t answer_type,
+ const cached_resolve_t *resolved)
{
- char buf[RELAY_PAYLOAD_SIZE];
- size_t buflen;
+ char buf[RELAY_PAYLOAD_SIZE], *cp = buf;
+ size_t buflen = 0;
uint32_t ttl;
buf[0] = answer_type;
@@ -510,19 +615,36 @@ send_resolved_cell(edge_connection_t *conn, uint8_t answer_type)
switch (answer_type)
{
- case RESOLVED_TYPE_IPV4:
- buf[1] = 4;
- set_uint32(buf+2, tor_addr_to_ipv4n(&conn->base_.addr));
- set_uint32(buf+6, htonl(ttl));
- buflen = 10;
- break;
- /*XXXX IP6 need ipv6 implementation */
+ case RESOLVED_TYPE_AUTO:
+ if (resolved && resolved->res_status_ipv4 == RES_STATUS_DONE_OK) {
+ cp[0] = RESOLVED_TYPE_IPV4;
+ cp[1] = 4;
+ set_uint32(cp+2, htonl(resolved->result_ipv4.addr_ipv4));
+ set_uint32(cp+6, htonl(ttl));
+ cp += 10;
+ }
+ if (resolved && resolved->res_status_ipv6 == RES_STATUS_DONE_OK) {
+ const uint8_t *bytes = resolved->result_ipv6.addr_ipv6.s6_addr;
+ cp[0] = RESOLVED_TYPE_IPV6;
+ cp[1] = 16;
+ memcpy(cp+2, bytes, 16);
+ set_uint32(cp+18, htonl(ttl));
+ cp += 22;
+ }
+ if (cp != buf) {
+ buflen = cp - buf;
+ break;
+ } else {
+ answer_type = RESOLVED_TYPE_ERROR;
+ /* fall through. */
+ }
case RESOLVED_TYPE_ERROR_TRANSIENT:
case RESOLVED_TYPE_ERROR:
{
const char *errmsg = "Error resolving hostname";
size_t msglen = strlen(errmsg);
+ buf[0] = answer_type;
buf[1] = msglen;
strlcpy(buf+2, errmsg, sizeof(buf)-2);
set_uint32(buf+2+msglen, htonl(ttl));
@@ -600,10 +722,11 @@ dns_resolve(edge_connection_t *exitconn)
int is_resolve, r;
int made_connection_pending = 0;
char *hostname = NULL;
+ cached_resolve_t *resolve = NULL;
is_resolve = exitconn->base_.purpose == EXIT_PURPOSE_RESOLVE;
r = dns_resolve_impl(exitconn, is_resolve, oncirc, &hostname,
- &made_connection_pending);
+ &made_connection_pending, &resolve);
switch (r) {
case 1:
@@ -614,7 +737,7 @@ dns_resolve(edge_connection_t *exitconn)
if (hostname)
send_resolved_hostname_cell(exitconn, hostname);
else
- send_resolved_cell(exitconn, RESOLVED_TYPE_IPV4);
+ send_resolved_cell(exitconn, RESOLVED_TYPE_AUTO, resolve);
exitconn->on_circuit = NULL;
} else {
/* Add to the n_streams list; the calling function will send back a
@@ -636,7 +759,8 @@ dns_resolve(edge_connection_t *exitconn)
* and stop everybody waiting for the same connection. */
if (is_resolve) {
send_resolved_cell(exitconn,
- (r == -1) ? RESOLVED_TYPE_ERROR : RESOLVED_TYPE_ERROR_TRANSIENT);
+ (r == -1) ? RESOLVED_TYPE_ERROR : RESOLVED_TYPE_ERROR_TRANSIENT,
+ NULL);
}
exitconn->on_circuit = NULL;
@@ -670,19 +794,21 @@ dns_resolve(edge_connection_t *exitconn)
* Set *<b>made_connection_pending_out</b> to true if we have placed
* <b>exitconn</b> on the list of pending connections for some resolve; set it
* to false otherwise.
+ *
+ * Set *<b>resolve_out</b> to a cached resolve, if we found one.
*/
static int
dns_resolve_impl(edge_connection_t *exitconn, int is_resolve,
or_circuit_t *oncirc, char **hostname_out,
- int *made_connection_pending_out)
+ int *made_connection_pending_out,
+ cached_resolve_t **resolve_out)
{
cached_resolve_t *resolve;
cached_resolve_t search;
pending_connection_t *pending_connection;
- const routerinfo_t *me;
+ int is_reverse = 0;
tor_addr_t addr;
time_t now = time(NULL);
- uint8_t is_reverse = 0;
int r;
assert_connection_ok(TO_CONN(exitconn), 0);
tor_assert(!SOCKET_OK(exitconn->base_.s));
@@ -693,23 +819,23 @@ dns_resolve_impl(edge_connection_t *exitconn, int is_resolve,
/* first check if exitconn->base_.address is an IP. If so, we already
* know the answer. */
if (tor_addr_parse(&addr, exitconn->base_.address) >= 0) {
- if (tor_addr_family(&addr) == AF_INET) {
+ if (tor_addr_family(&addr) == AF_INET ||
+ tor_addr_family(&addr) == AF_INET6) {
tor_addr_copy(&exitconn->base_.addr, &addr);
exitconn->address_ttl = DEFAULT_DNS_TTL;
return 1;
} else {
- /* XXXX IPv6 */
+ /* XXXX unspec? Bogus? */
return -1;
}
}
/* If we're a non-exit, don't even do DNS lookups. */
- if (!(me = router_get_my_routerinfo()) ||
- policy_is_reject_star(me->exit_policy)) {
+ if (router_my_exit_policy_is_reject_star())
return -1;
- }
+
if (address_is_invalid_destination(exitconn->base_.address, 0)) {
- log(LOG_PROTOCOL_WARN, LD_EXIT,
+ tor_log(LOG_PROTOCOL_WARN, LD_EXIT,
"Rejecting invalid destination address %s",
escaped_safe_str(exitconn->base_.address));
return -1;
@@ -749,6 +875,7 @@ dns_resolve_impl(edge_connection_t *exitconn, int is_resolve,
//log_notice(LD_EXIT, "Looks like an address %s",
//exitconn->base_.address);
}
+ exitconn->is_reverse_dns_lookup = is_reverse;
/* now check the hash table to see if 'address' is already there. */
strlcpy(search.address, exitconn->base_.address, sizeof(search.address));
@@ -763,27 +890,19 @@ dns_resolve_impl(edge_connection_t *exitconn, int is_resolve,
pending_connection->next = resolve->pending_connections;
resolve->pending_connections = pending_connection;
*made_connection_pending_out = 1;
- log_debug(LD_EXIT,"Connection (fd %d) waiting for pending DNS "
- "resolve of %s", exitconn->base_.s,
+ log_debug(LD_EXIT,"Connection (fd "TOR_SOCKET_T_FORMAT") waiting "
+ "for pending DNS resolve of %s", exitconn->base_.s,
escaped_safe_str(exitconn->base_.address));
return 0;
- case CACHE_STATE_CACHED_VALID:
- log_debug(LD_EXIT,"Connection (fd %d) found cached answer for %s",
+ case CACHE_STATE_CACHED:
+ log_debug(LD_EXIT,"Connection (fd "TOR_SOCKET_T_FORMAT") found "
+ "cached answer for %s",
exitconn->base_.s,
escaped_safe_str(resolve->address));
- exitconn->address_ttl = resolve->ttl;
- if (resolve->is_reverse) {
- tor_assert(is_resolve);
- *hostname_out = tor_strdup(resolve->result.hostname);
- } else {
- tor_addr_from_ipv4h(&exitconn->base_.addr, resolve->result.a.addr);
- }
- return 1;
- case CACHE_STATE_CACHED_FAILED:
- log_debug(LD_EXIT,"Connection (fd %d) found cached error for %s",
- exitconn->base_.s,
- escaped_safe_str(exitconn->base_.address));
- return -1;
+
+ *resolve_out = resolve;
+
+ return set_exitconn_info_from_resolve(exitconn, resolve, hostname_out);
case CACHE_STATE_DONE:
log_err(LD_BUG, "Found a 'DONE' dns resolve still in the cache.");
tor_fragile_assert();
@@ -796,7 +915,6 @@ dns_resolve_impl(edge_connection_t *exitconn, int is_resolve,
resolve->magic = CACHED_RESOLVE_MAGIC;
resolve->state = CACHE_STATE_PENDING;
resolve->minheap_idx = -1;
- resolve->is_reverse = is_reverse;
strlcpy(resolve->address, exitconn->base_.address, sizeof(resolve->address));
/* add this connection to the pending list */
@@ -813,7 +931,112 @@ dns_resolve_impl(edge_connection_t *exitconn, int is_resolve,
escaped_safe_str(exitconn->base_.address));
assert_cache_ok();
- return launch_resolve(exitconn);
+ return launch_resolve(resolve);
+}
+
+/** Given an exit connection <b>exitconn</b>, and a cached_resolve_t
+ * <b>resolve</b> whose DNS lookups have all succeeded or failed, update the
+ * appropriate fields (address_ttl and addr) of <b>exitconn</b>.
+ *
+ * If this is a reverse lookup, set *<b>hostname_out</b> to a newly allocated
+ * copy of the name resulting hostname.
+ *
+ * Return -2 on a transient error, -1 on a permenent error, and 1 on
+ * a successful lookup.
+ */
+static int
+set_exitconn_info_from_resolve(edge_connection_t *exitconn,
+ const cached_resolve_t *resolve,
+ char **hostname_out)
+{
+ int ipv4_ok, ipv6_ok, answer_with_ipv4, r;
+ uint32_t begincell_flags;
+ const int is_resolve = exitconn->base_.purpose == EXIT_PURPOSE_RESOLVE;
+ tor_assert(exitconn);
+ tor_assert(resolve);
+
+ if (exitconn->is_reverse_dns_lookup) {
+ exitconn->address_ttl = resolve->ttl_hostname;
+ if (resolve->res_status_hostname == RES_STATUS_DONE_OK) {
+ *hostname_out = tor_strdup(resolve->result_ptr.hostname);
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+
+ /* If we're here then the connection wants one or either of ipv4, ipv6, and
+ * we can give it one or both. */
+ if (is_resolve) {
+ begincell_flags = BEGIN_FLAG_IPV6_OK;
+ } else {
+ begincell_flags = exitconn->begincell_flags;
+ }
+
+ ipv4_ok = (resolve->res_status_ipv4 == RES_STATUS_DONE_OK) &&
+ ! (begincell_flags & BEGIN_FLAG_IPV4_NOT_OK);
+ ipv6_ok = (resolve->res_status_ipv6 == RES_STATUS_DONE_OK) &&
+ (begincell_flags & BEGIN_FLAG_IPV6_OK) &&
+ get_options()->IPv6Exit;
+
+ /* Now decide which one to actually give. */
+ if (ipv4_ok && ipv6_ok && is_resolve) {
+ answer_with_ipv4 = 1;
+ } else if (ipv4_ok && ipv6_ok) {
+ /* If we have both, see if our exit policy has an opinion. */
+ const uint16_t port = exitconn->base_.port;
+ int ipv4_allowed, ipv6_allowed;
+ tor_addr_t a4, a6;
+ tor_addr_from_ipv4h(&a4, resolve->result_ipv4.addr_ipv4);
+ tor_addr_from_in6(&a6, &resolve->result_ipv6.addr_ipv6);
+ ipv4_allowed = !router_compare_to_my_exit_policy(&a4, port);
+ ipv6_allowed = !router_compare_to_my_exit_policy(&a6, port);
+ if (ipv4_allowed && !ipv6_allowed) {
+ answer_with_ipv4 = 1;
+ } else if (ipv6_allowed && !ipv4_allowed) {
+ answer_with_ipv4 = 0;
+ } else {
+ /* Our exit policy would permit both. Answer with whichever the user
+ * prefers */
+ answer_with_ipv4 = !(begincell_flags &
+ BEGIN_FLAG_IPV6_PREFERRED);
+ }
+ } else {
+ /* Otherwise if one is okay, send it back. */
+ if (ipv4_ok) {
+ answer_with_ipv4 = 1;
+ } else if (ipv6_ok) {
+ answer_with_ipv4 = 0;
+ } else {
+ /* Neither one was okay. Choose based on user preference. */
+ answer_with_ipv4 = !(begincell_flags &
+ BEGIN_FLAG_IPV6_PREFERRED);
+ }
+ }
+
+ /* Finally, we write the answer back. */
+ r = 1;
+ if (answer_with_ipv4) {
+ if (resolve->res_status_ipv4 == RES_STATUS_DONE_OK) {
+ tor_addr_from_ipv4h(&exitconn->base_.addr,
+ resolve->result_ipv4.addr_ipv4);
+ } else {
+ r = evdns_err_is_transient(resolve->result_ipv4.err_ipv4) ? -2 : -1;
+ }
+
+ exitconn->address_ttl = resolve->ttl_ipv4;
+ } else {
+ if (resolve->res_status_ipv6 == RES_STATUS_DONE_OK) {
+ tor_addr_from_in6(&exitconn->base_.addr,
+ &resolve->result_ipv6.addr_ipv6);
+ } else {
+ r = evdns_err_is_transient(resolve->result_ipv6.err_ipv6) ? -2 : -1;
+ }
+
+ exitconn->address_ttl = resolve->ttl_ipv6;
+ }
+
+ return r;
}
/** Log an error and abort if conn is waiting for a DNS resolve.
@@ -891,8 +1114,8 @@ connection_dns_remove(edge_connection_t *conn)
if (pend->conn == conn) {
resolve->pending_connections = pend->next;
tor_free(pend);
- log_debug(LD_EXIT, "First connection (fd %d) no longer waiting "
- "for resolve of %s",
+ log_debug(LD_EXIT, "First connection (fd "TOR_SOCKET_T_FORMAT") no "
+ "longer waiting for resolve of %s",
conn->base_.s,
escaped_safe_str(conn->base_.address));
return;
@@ -903,7 +1126,8 @@ connection_dns_remove(edge_connection_t *conn)
pend->next = victim->next;
tor_free(victim);
log_debug(LD_EXIT,
- "Connection (fd %d) no longer waiting for resolve of %s",
+ "Connection (fd "TOR_SOCKET_T_FORMAT") no longer waiting "
+ "for resolve of %s",
conn->base_.s, escaped_safe_str(conn->base_.address));
return; /* more are pending */
}
@@ -987,47 +1211,6 @@ dns_cancel_pending_resolve(const char *address)
resolve->state = CACHE_STATE_DONE;
}
-/** Helper: adds an entry to the DNS cache mapping <b>address</b> to the ipv4
- * address <b>addr</b> (if is_reverse is 0) or the hostname <b>hostname</b> (if
- * is_reverse is 1). <b>ttl</b> is a cache ttl; <b>outcome</b> is one of
- * DNS_RESOLVE_{FAILED_TRANSIENT|FAILED_PERMANENT|SUCCEEDED}.
- **/
-static void
-add_answer_to_cache(const char *address, uint8_t is_reverse, uint32_t addr,
- const char *hostname, char outcome, uint32_t ttl)
-{
- cached_resolve_t *resolve;
- if (outcome == DNS_RESOLVE_FAILED_TRANSIENT)
- return;
-
- //log_notice(LD_EXIT, "Adding to cache: %s -> %s (%lx, %s), %d",
- // address, is_reverse?"(reverse)":"", (unsigned long)addr,
- // hostname?hostname:"NULL",(int)outcome);
-
- resolve = tor_malloc_zero(sizeof(cached_resolve_t));
- resolve->magic = CACHED_RESOLVE_MAGIC;
- resolve->state = (outcome == DNS_RESOLVE_SUCCEEDED) ?
- CACHE_STATE_CACHED_VALID : CACHE_STATE_CACHED_FAILED;
- strlcpy(resolve->address, address, sizeof(resolve->address));
- resolve->is_reverse = is_reverse;
- if (is_reverse) {
- if (outcome == DNS_RESOLVE_SUCCEEDED) {
- tor_assert(hostname);
- resolve->result.hostname = tor_strdup(hostname);
- } else {
- tor_assert(! hostname);
- resolve->result.hostname = NULL;
- }
- } else {
- tor_assert(!hostname);
- resolve->result.a.addr = addr;
- }
- resolve->ttl = ttl;
- assert_resolve_ok(resolve);
- HT_INSERT(cache_map, &cache_root, resolve);
- set_expiry(resolve, time(NULL) + dns_get_expiry_ttl(ttl));
-}
-
/** Return true iff <b>address</b> is one of the addresses we use to verify
* that well-known sites aren't being hijacked by our DNS servers. */
static INLINE int
@@ -1035,25 +1218,26 @@ is_test_address(const char *address)
{
const or_options_t *options = get_options();
return options->ServerDNSTestAddresses &&
- smartlist_string_isin_case(options->ServerDNSTestAddresses, address);
+ smartlist_contains_string_case(options->ServerDNSTestAddresses, address);
}
-/** Called on the OR side when a DNS worker or the eventdns library tells us
- * the outcome of a DNS resolve: tell all pending connections about the result
- * of the lookup, and cache the value. (<b>address</b> is a NUL-terminated
- * string containing the address to look up; <b>addr</b> is an IPv4 address in
- * host order; <b>outcome</b> is one of
- * DNS_RESOLVE_{FAILED_TRANSIENT|FAILED_PERMANENT|SUCCEEDED}.
+/** Called on the OR side when the eventdns library tells us the outcome of a
+ * single DNS resolve: remember the answer, and tell all pending connections
+ * about the result of the lookup if the lookup is now done. (<b>address</b>
+ * is a NUL-terminated string containing the address to look up;
+ * <b>query_type</b> is one of DNS_{IPv4_A,IPv6_AAAA,PTR}; <b>dns_answer</b>
+ * is DNS_OK or one of DNS_ERR_*, <b>addr</b> is an IPv4 or IPv6 address if we
+ * got one; <b>hostname</b> is a hostname fora PTR request if we got one, and
+ * <b>ttl</b> is the time-to-live of this answer, in seconds.)
*/
static void
-dns_found_answer(const char *address, uint8_t is_reverse, uint32_t addr,
- const char *hostname, char outcome, uint32_t ttl)
+dns_found_answer(const char *address, uint8_t query_type,
+ int dns_answer,
+ const tor_addr_t *addr,
+ const char *hostname, uint32_t ttl)
{
- pending_connection_t *pend;
cached_resolve_t search;
- cached_resolve_t *resolve, *removed;
- edge_connection_t *pendconn;
- circuit_t *circ;
+ cached_resolve_t *resolve;
assert_cache_ok();
@@ -1063,9 +1247,8 @@ dns_found_answer(const char *address, uint8_t is_reverse, uint32_t addr,
if (!resolve) {
int is_test_addr = is_test_address(address);
if (!is_test_addr)
- log_info(LD_EXIT,"Resolved unasked address %s; caching anyway.",
+ log_info(LD_EXIT,"Resolved unasked address %s; ignoring.",
escaped_safe_str(address));
- add_answer_to_cache(address, is_reverse, addr, hostname, outcome, ttl);
return;
}
assert_resolve_ok(resolve);
@@ -1081,17 +1264,34 @@ dns_found_answer(const char *address, uint8_t is_reverse, uint32_t addr,
tor_assert(resolve->pending_connections == NULL);
return;
}
- /* Removed this assertion: in fact, we'll sometimes get a double answer
- * to the same question. This can happen when we ask one worker to resolve
- * X.Y.Z., then we cancel the request, and then we ask another worker to
- * resolve X.Y.Z. */
- /* tor_assert(resolve->state == CACHE_STATE_PENDING); */
+
+ cached_resolve_add_answer(resolve, query_type, dns_answer,
+ addr, hostname, ttl);
+
+ if (cached_resolve_have_all_answers(resolve)) {
+ inform_pending_connections(resolve);
+
+ make_pending_resolve_cached(resolve);
+ }
+}
+
+/** Given a pending cached_resolve_t that we just finished resolving,
+ * inform every connection that was waiting for the outcome of that
+ * resolution. */
+static void
+inform_pending_connections(cached_resolve_t *resolve)
+{
+ pending_connection_t *pend;
+ edge_connection_t *pendconn;
+ int r;
while (resolve->pending_connections) {
+ char *hostname = NULL;
pend = resolve->pending_connections;
pendconn = pend->conn; /* don't pass complex things to the
connection_mark_for_close macro */
assert_connection_ok(TO_CONN(pendconn),time(NULL));
+
if (pendconn->base_.marked_for_close) {
/* prevent double-remove. */
pendconn->base_.state = EXIT_CONN_STATE_RESOLVEFAILED;
@@ -1099,10 +1299,12 @@ dns_found_answer(const char *address, uint8_t is_reverse, uint32_t addr,
tor_free(pend);
continue;
}
- tor_addr_from_ipv4h(&pendconn->base_.addr, addr);
- pendconn->address_ttl = ttl;
- if (outcome != DNS_RESOLVE_SUCCEEDED) {
+ r = set_exitconn_info_from_resolve(pendconn,
+ resolve,
+ &hostname);
+
+ if (r < 0) {
/* prevent double-remove. */
pendconn->base_.state = EXIT_CONN_STATE_RESOLVEFAILED;
if (pendconn->base_.purpose == EXIT_PURPOSE_CONNECT) {
@@ -1110,15 +1312,16 @@ dns_found_answer(const char *address, uint8_t is_reverse, uint32_t addr,
/* This detach must happen after we send the end cell. */
circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn);
} else {
- send_resolved_cell(pendconn, outcome == DNS_RESOLVE_FAILED_PERMANENT ?
- RESOLVED_TYPE_ERROR : RESOLVED_TYPE_ERROR_TRANSIENT);
+ send_resolved_cell(pendconn, r == -1 ?
+ RESOLVED_TYPE_ERROR : RESOLVED_TYPE_ERROR_TRANSIENT,
+ NULL);
/* This detach must happen after we send the resolved cell. */
circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn);
}
connection_free(TO_CONN(pendconn));
} else {
+ circuit_t *circ;
if (pendconn->base_.purpose == EXIT_PURPOSE_CONNECT) {
- tor_assert(!is_reverse);
/* prevent double-remove. */
pend->conn->base_.state = EXIT_CONN_STATE_CONNECTING;
@@ -1137,10 +1340,10 @@ dns_found_answer(const char *address, uint8_t is_reverse, uint32_t addr,
/* prevent double-remove. This isn't really an accurate state,
* but it does the right thing. */
pendconn->base_.state = EXIT_CONN_STATE_RESOLVEFAILED;
- if (is_reverse)
+ if (pendconn->is_reverse_dns_lookup)
send_resolved_hostname_cell(pendconn, hostname);
else
- send_resolved_cell(pendconn, RESOLVED_TYPE_IPV4);
+ send_resolved_cell(pendconn, RESOLVED_TYPE_AUTO, resolve);
circ = circuit_get_by_edge_conn(pendconn);
tor_assert(circ);
circuit_detach_stream(circ, pendconn);
@@ -1150,9 +1353,21 @@ dns_found_answer(const char *address, uint8_t is_reverse, uint32_t addr,
resolve->pending_connections = pend->next;
tor_free(pend);
}
+}
+
+/** Remove a pending cached_resolve_t from the hashtable, and add a
+ * corresponding cached cached_resolve_t.
+ *
+ * This function is only necessary because of the perversity of our
+ * cache timeout code; see inline comment for ideas on eliminating it.
+ **/
+static void
+make_pending_resolve_cached(cached_resolve_t *resolve)
+{
+ cached_resolve_t *removed;
resolve->state = CACHE_STATE_DONE;
- removed = HT_REMOVE(cache_map, &cache_root, &search);
+ removed = HT_REMOVE(cache_map, &cache_root, resolve);
if (removed != resolve) {
log_err(LD_BUG, "The pending resolve we found wasn't removable from"
" the cache. Tried to purge %s (%p); instead got %s (%p).",
@@ -1161,8 +1376,42 @@ dns_found_answer(const char *address, uint8_t is_reverse, uint32_t addr,
}
assert_resolve_ok(resolve);
assert_cache_ok();
+ /* The resolve will eventually just hit the time-out in the expiry queue and
+ * expire. See fd0bafb0dedc7e2 for a brief explanation of how this got that
+ * way. XXXXX we could do better!*/
+
+ {
+ cached_resolve_t *new_resolve = tor_memdup(resolve,
+ sizeof(cached_resolve_t));
+ uint32_t ttl = UINT32_MAX;
+ new_resolve->expire = 0; /* So that set_expiry won't croak. */
+ if (resolve->res_status_hostname == RES_STATUS_DONE_OK)
+ new_resolve->result_ptr.hostname =
+ tor_strdup(resolve->result_ptr.hostname);
+
+ new_resolve->state = CACHE_STATE_CACHED;
+
+ assert_resolve_ok(new_resolve);
+ HT_INSERT(cache_map, &cache_root, new_resolve);
+
+ if ((resolve->res_status_ipv4 == RES_STATUS_DONE_OK ||
+ resolve->res_status_ipv4 == RES_STATUS_DONE_ERR) &&
+ resolve->ttl_ipv4 < ttl)
+ ttl = resolve->ttl_ipv4;
+
+ if ((resolve->res_status_ipv6 == RES_STATUS_DONE_OK ||
+ resolve->res_status_ipv6 == RES_STATUS_DONE_ERR) &&
+ resolve->ttl_ipv6 < ttl)
+ ttl = resolve->ttl_ipv6;
+
+ if ((resolve->res_status_hostname == RES_STATUS_DONE_OK ||
+ resolve->res_status_hostname == RES_STATUS_DONE_ERR) &&
+ resolve->ttl_hostname < ttl)
+ ttl = resolve->ttl_hostname;
+
+ set_expiry(new_resolve, time(NULL) + dns_get_expiry_ttl(ttl));
+ }
- add_answer_to_cache(address, is_reverse, addr, hostname, outcome, ttl);
assert_cache_ok();
}
@@ -1325,23 +1574,40 @@ static void
evdns_callback(int result, char type, int count, int ttl, void *addresses,
void *arg)
{
- char *string_address = arg;
- uint8_t is_reverse = 0;
- int status = DNS_RESOLVE_FAILED_PERMANENT;
- uint32_t addr = 0;
+ char *arg_ = arg;
+ uint8_t orig_query_type = arg_[0];
+ char *string_address = arg_ + 1;
+ tor_addr_t addr;
const char *hostname = NULL;
int was_wildcarded = 0;
+ tor_addr_make_unspec(&addr);
+
+ /* Keep track of whether IPv6 is working */
+ if (type == DNS_IPv6_AAAA) {
+ if (result == DNS_ERR_TIMEOUT) {
+ ++n_ipv6_timeouts;
+ }
+
+ if (n_ipv6_timeouts > 10 &&
+ n_ipv6_timeouts > n_ipv6_requests_made / 2) {
+ if (! dns_is_broken_for_ipv6) {
+ log_notice(LD_EXIT, "More than half of our IPv6 requests seem to "
+ "have timed out. I'm going to assume I can't get AAAA "
+ "responses.");
+ dns_is_broken_for_ipv6 = 1;
+ }
+ }
+ }
+
if (result == DNS_ERR_NONE) {
if (type == DNS_IPv4_A && count) {
char answer_buf[INET_NTOA_BUF_LEN+1];
- struct in_addr in;
char *escaped_address;
uint32_t *addrs = addresses;
- in.s_addr = addrs[0];
- addr = ntohl(addrs[0]);
- status = DNS_RESOLVE_SUCCEEDED;
- tor_inet_ntoa(&in, answer_buf, sizeof(answer_buf));
+ tor_addr_from_ipv4n(&addr, addrs[0]);
+
+ tor_addr_to_str(answer_buf, &addr, sizeof(answer_buf), 0);
escaped_address = esc_for_log(string_address);
if (answer_is_wildcarded(answer_buf)) {
@@ -1350,8 +1616,30 @@ evdns_callback(int result, char type, int count, int ttl, void *addresses,
safe_str(escaped_address),
escaped_safe_str(answer_buf));
was_wildcarded = 1;
- addr = 0;
- status = DNS_RESOLVE_FAILED_PERMANENT;
+ tor_addr_make_unspec(&addr);
+ result = DNS_ERR_NOTEXIST;
+ } else {
+ log_debug(LD_EXIT, "eventdns said that %s resolves to %s",
+ safe_str(escaped_address),
+ escaped_safe_str(answer_buf));
+ }
+ tor_free(escaped_address);
+ } else if (type == DNS_IPv6_AAAA && count) {
+ char answer_buf[TOR_ADDR_BUF_LEN];
+ char *escaped_address;
+ struct in6_addr *addrs = addresses;
+ tor_addr_from_in6(&addr, &addrs[0]);
+ tor_inet_ntop(AF_INET6, &addrs[0], answer_buf, sizeof(answer_buf));
+ escaped_address = esc_for_log(string_address);
+
+ if (answer_is_wildcarded(answer_buf)) {
+ log_debug(LD_EXIT, "eventdns said that %s resolves to ISP-hijacked "
+ "address %s; treating as a failure.",
+ safe_str(escaped_address),
+ escaped_safe_str(answer_buf));
+ was_wildcarded = 1;
+ tor_addr_make_unspec(&addr);
+ result = DNS_ERR_NOTEXIST;
} else {
log_debug(LD_EXIT, "eventdns said that %s resolves to %s",
safe_str(escaped_address),
@@ -1360,9 +1648,7 @@ evdns_callback(int result, char type, int count, int ttl, void *addresses,
tor_free(escaped_address);
} else if (type == DNS_PTR && count) {
char *escaped_address;
- is_reverse = 1;
hostname = ((char**)addresses)[0];
- status = DNS_RESOLVE_SUCCEEDED;
escaped_address = esc_for_log(string_address);
log_debug(LD_EXIT, "eventdns said that %s resolves to %s",
safe_str(escaped_address),
@@ -1375,9 +1661,6 @@ evdns_callback(int result, char type, int count, int ttl, void *addresses,
log_warn(LD_BUG, "eventdns returned no addresses or error for %s!",
escaped_safe_str(string_address));
}
- } else {
- if (evdns_err_is_transient(result))
- status = DNS_RESOLVE_FAILED_TRANSIENT;
}
if (was_wildcarded) {
if (is_test_address(string_address)) {
@@ -1386,23 +1669,78 @@ evdns_callback(int result, char type, int count, int ttl, void *addresses,
add_wildcarded_test_address(string_address);
}
}
+
+ if (orig_query_type && type && orig_query_type != type) {
+ log_warn(LD_BUG, "Weird; orig_query_type == %d but type == %d",
+ (int)orig_query_type, (int)type);
+ }
if (result != DNS_ERR_SHUTDOWN)
- dns_found_answer(string_address, is_reverse, addr, hostname, status, ttl);
- tor_free(string_address);
+ dns_found_answer(string_address, orig_query_type,
+ result, &addr, hostname, ttl);
+
+ tor_free(arg_);
+}
+
+/** Start a single DNS resolve for <b>address</b> (if <b>query_type</b> is
+ * DNS_IPv4_A or DNS_IPv6_AAAA) <b>ptr_address</b> (if <b>query_type</b> is
+ * DNS_PTR). Return 0 if we launched the request, -1 otherwise. */
+static int
+launch_one_resolve(const char *address, uint8_t query_type,
+ const tor_addr_t *ptr_address)
+{
+ const int options = get_options()->ServerDNSSearchDomains ? 0
+ : DNS_QUERY_NO_SEARCH;
+ const size_t addr_len = strlen(address);
+ struct evdns_request *req = 0;
+ char *addr = tor_malloc(addr_len + 2);
+ addr[0] = (char) query_type;
+ memcpy(addr+1, address, addr_len + 1);
+
+ switch (query_type) {
+ case DNS_IPv4_A:
+ req = evdns_base_resolve_ipv4(the_evdns_base,
+ address, options, evdns_callback, addr);
+ break;
+ case DNS_IPv6_AAAA:
+ req = evdns_base_resolve_ipv6(the_evdns_base,
+ address, options, evdns_callback, addr);
+ ++n_ipv6_requests_made;
+ break;
+ case DNS_PTR:
+ if (tor_addr_family(ptr_address) == AF_INET)
+ req = evdns_base_resolve_reverse(the_evdns_base,
+ tor_addr_to_in(ptr_address),
+ DNS_QUERY_NO_SEARCH,
+ evdns_callback, addr);
+ else if (tor_addr_family(ptr_address) == AF_INET6)
+ req = evdns_base_resolve_reverse_ipv6(the_evdns_base,
+ tor_addr_to_in6(ptr_address),
+ DNS_QUERY_NO_SEARCH,
+ evdns_callback, addr);
+ else
+ log_warn(LD_BUG, "Called with PTR query and unexpected address family");
+ break;
+ default:
+ log_warn(LD_BUG, "Called with unexpectd query type %d", (int)query_type);
+ break;
+ }
+
+ if (req) {
+ return 0;
+ } else {
+ tor_free(addr);
+ return -1;
+ }
}
/** For eventdns: start resolving as necessary to find the target for
* <b>exitconn</b>. Returns -1 on error, -2 on transient error,
* 0 on "resolve launched." */
static int
-launch_resolve(edge_connection_t *exitconn)
+launch_resolve(cached_resolve_t *resolve)
{
- char *addr;
- struct evdns_request *req = NULL;
tor_addr_t a;
int r;
- int options = get_options()->ServerDNSSearchDomains ? 0
- : DNS_QUERY_NO_SEARCH;
if (get_options()->DisableNetwork)
return -1;
@@ -1416,40 +1754,45 @@ launch_resolve(edge_connection_t *exitconn)
}
}
- addr = tor_strdup(exitconn->base_.address);
-
r = tor_addr_parse_PTR_name(
- &a, exitconn->base_.address, AF_UNSPEC, 0);
+ &a, resolve->address, AF_UNSPEC, 0);
tor_assert(the_evdns_base);
if (r == 0) {
log_info(LD_EXIT, "Launching eventdns request for %s",
- escaped_safe_str(exitconn->base_.address));
- req = evdns_base_resolve_ipv4(the_evdns_base,
- exitconn->base_.address, options,
- evdns_callback, addr);
+ escaped_safe_str(resolve->address));
+ resolve->res_status_ipv4 = RES_STATUS_INFLIGHT;
+ if (get_options()->IPv6Exit)
+ resolve->res_status_ipv6 = RES_STATUS_INFLIGHT;
+
+ if (launch_one_resolve(resolve->address, DNS_IPv4_A, NULL) < 0) {
+ resolve->res_status_ipv4 = 0;
+ r = -1;
+ }
+
+ if (r==0 && get_options()->IPv6Exit) {
+ /* We ask for an IPv6 address for *everything*. */
+ if (launch_one_resolve(resolve->address, DNS_IPv6_AAAA, NULL) < 0) {
+ resolve->res_status_ipv6 = 0;
+ r = -1;
+ }
+ }
} else if (r == 1) {
+ r = 0;
log_info(LD_EXIT, "Launching eventdns reverse request for %s",
- escaped_safe_str(exitconn->base_.address));
- if (tor_addr_family(&a) == AF_INET)
- req = evdns_base_resolve_reverse(the_evdns_base,
- tor_addr_to_in(&a), DNS_QUERY_NO_SEARCH,
- evdns_callback, addr);
- else
- req = evdns_base_resolve_reverse_ipv6(the_evdns_base,
- tor_addr_to_in6(&a), DNS_QUERY_NO_SEARCH,
- evdns_callback, addr);
+ escaped_safe_str(resolve->address));
+ resolve->res_status_hostname = RES_STATUS_INFLIGHT;
+ if (launch_one_resolve(resolve->address, DNS_PTR, &a) < 0) {
+ resolve->res_status_hostname = 0;
+ r = -1;
+ }
} else if (r == -1) {
log_warn(LD_BUG, "Somehow a malformed in-addr.arpa address reached here.");
}
- r = 0;
- if (!req) {
+ if (r < 0) {
log_fn(LOG_PROTOCOL_WARN, LD_EXIT, "eventdns rejected address %s.",
- escaped_safe_str(addr));
- r = -1;
- tor_free(addr); /* There is no evdns request in progress; stop
- * addr from getting leaked. */
+ escaped_safe_str(resolve->address));
}
return r;
}
@@ -1482,8 +1825,8 @@ static int dns_wildcarded_test_address_notice_given = 0;
/** True iff all addresses seem to be getting wildcarded. */
static int dns_is_completely_invalid = 0;
-/** Called when we see <b>id</b> (a dotted quad) in response to a request for
- * a hopefully bogus address. */
+/** Called when we see <b>id</b> (a dotted quad or IPv6 address) in response
+ * to a request for a hopefully bogus address. */
static void
wildcard_increment_answer(const char *id)
{
@@ -1500,8 +1843,8 @@ wildcard_increment_answer(const char *id)
if (*ip > 5 && n_wildcard_requests > 10) {
if (!dns_wildcard_list) dns_wildcard_list = smartlist_new();
- if (!smartlist_string_isin(dns_wildcard_list, id)) {
- log(dns_wildcard_notice_given ? LOG_INFO : LOG_NOTICE, LD_EXIT,
+ if (!smartlist_contains_string(dns_wildcard_list, id)) {
+ tor_log(dns_wildcard_notice_given ? LOG_INFO : LOG_NOTICE, LD_EXIT,
"Your DNS provider has given \"%s\" as an answer for %d different "
"invalid addresses. Apparently they are hijacking DNS failures. "
"I'll try to correct for this by treating future occurrences of "
@@ -1523,7 +1866,8 @@ add_wildcarded_test_address(const char *address)
if (!dns_wildcarded_test_address_list)
dns_wildcarded_test_address_list = smartlist_new();
- if (smartlist_string_isin_case(dns_wildcarded_test_address_list, address))
+ if (smartlist_contains_string_case(dns_wildcarded_test_address_list,
+ address))
return;
n_test_addrs = get_options()->ServerDNSTestAddresses ?
@@ -1532,7 +1876,7 @@ add_wildcarded_test_address(const char *address)
smartlist_add(dns_wildcarded_test_address_list, tor_strdup(address));
n = smartlist_len(dns_wildcarded_test_address_list);
if (n > n_test_addrs/2) {
- log(dns_wildcarded_test_address_notice_given ? LOG_INFO : LOG_NOTICE,
+ tor_log(dns_wildcarded_test_address_notice_given ? LOG_INFO : LOG_NOTICE,
LD_EXIT, "Your DNS provider tried to redirect \"%s\" to a junk "
"address. It has done this with %d test addresses so far. I'm "
"going to stop being an exit node for now, since our DNS seems so "
@@ -1555,18 +1899,28 @@ evdns_wildcard_check_callback(int result, char type, int count, int ttl,
{
(void)ttl;
++n_wildcard_requests;
- if (result == DNS_ERR_NONE && type == DNS_IPv4_A && count) {
- uint32_t *addrs = addresses;
- int i;
+ if (result == DNS_ERR_NONE && count) {
char *string_address = arg;
- for (i = 0; i < count; ++i) {
- char answer_buf[INET_NTOA_BUF_LEN+1];
- struct in_addr in;
- in.s_addr = addrs[i];
- tor_inet_ntoa(&in, answer_buf, sizeof(answer_buf));
- wildcard_increment_answer(answer_buf);
+ int i;
+ if (type == DNS_IPv4_A) {
+ const uint32_t *addrs = addresses;
+ for (i = 0; i < count; ++i) {
+ char answer_buf[INET_NTOA_BUF_LEN+1];
+ struct in_addr in;
+ in.s_addr = addrs[i];
+ tor_inet_ntoa(&in, answer_buf, sizeof(answer_buf));
+ wildcard_increment_answer(answer_buf);
+ }
+ } else if (type == DNS_IPv6_AAAA) {
+ const struct in6_addr *addrs = addresses;
+ for (i = 0; i < count; ++i) {
+ char answer_buf[TOR_ADDR_BUF_LEN+1];
+ tor_inet_ntop(AF_INET6, &addrs[i], answer_buf, sizeof(answer_buf));
+ wildcard_increment_answer(answer_buf);
+ }
}
- log(dns_wildcard_one_notice_given ? LOG_INFO : LOG_NOTICE, LD_EXIT,
+
+ tor_log(dns_wildcard_one_notice_given ? LOG_INFO : LOG_NOTICE, LD_EXIT,
"Your DNS provider gave an answer for \"%s\", which "
"is not supposed to exist. Apparently they are hijacking "
"DNS failures. Trying to correct for this. We've noticed %d "
@@ -1582,7 +1936,8 @@ evdns_wildcard_check_callback(int result, char type, int count, int ttl,
* <b>min_len</b> and <b>max_len</b> random (plausible) characters followed by
* <b>suffix</b> */
static void
-launch_wildcard_check(int min_len, int max_len, const char *suffix)
+launch_wildcard_check(int min_len, int max_len, int is_ipv6,
+ const char *suffix)
{
char *addr;
struct evdns_request *req;
@@ -1592,7 +1947,15 @@ launch_wildcard_check(int min_len, int max_len, const char *suffix)
"domains with request for bogus hostname \"%s\"", addr);
tor_assert(the_evdns_base);
- req = evdns_base_resolve_ipv4(
+ if (is_ipv6)
+ req = evdns_base_resolve_ipv6(
+ the_evdns_base,
+ /* This "addr" tells us which address to resolve */
+ addr,
+ DNS_QUERY_NO_SEARCH, evdns_wildcard_check_callback,
+ /* This "addr" is an argument to the callback*/ addr);
+ else
+ req = evdns_base_resolve_ipv4(
the_evdns_base,
/* This "addr" tells us which address to resolve */
addr,
@@ -1607,10 +1970,9 @@ launch_wildcard_check(int min_len, int max_len, const char *suffix)
/** Launch attempts to resolve a bunch of known-good addresses (configured in
* ServerDNSTestAddresses). [Callback for a libevent timer] */
static void
-launch_test_addresses(int fd, short event, void *args)
+launch_test_addresses(evutil_socket_t fd, short event, void *args)
{
const or_options_t *options = get_options();
- struct evdns_request *req;
(void)fd;
(void)event;
(void)args;
@@ -1623,21 +1985,22 @@ launch_test_addresses(int fd, short event, void *args)
/* This situation is worse than the failure-hijacking situation. When this
* happens, we're no good for DNS requests at all, and we shouldn't really
* be an exit server.*/
- if (!options->ServerDNSTestAddresses)
- return;
- tor_assert(the_evdns_base);
- SMARTLIST_FOREACH_BEGIN(options->ServerDNSTestAddresses,
- const char *, address) {
- char *a = tor_strdup(address);
- req = evdns_base_resolve_ipv4(the_evdns_base,
- address, DNS_QUERY_NO_SEARCH, evdns_callback, a);
+ if (options->ServerDNSTestAddresses) {
- if (!req) {
- log_info(LD_EXIT, "eventdns rejected test address %s",
- escaped_safe_str(address));
- tor_free(a);
- }
- } SMARTLIST_FOREACH_END(address);
+ tor_assert(the_evdns_base);
+ SMARTLIST_FOREACH_BEGIN(options->ServerDNSTestAddresses,
+ const char *, address) {
+ if (launch_one_resolve(address, DNS_IPv4_A, NULL) < 0) {
+ log_info(LD_EXIT, "eventdns rejected test address %s",
+ escaped_safe_str(address));
+ }
+
+ if (launch_one_resolve(address, DNS_IPv6_AAAA, NULL) < 0) {
+ log_info(LD_EXIT, "eventdns rejected test address %s",
+ escaped_safe_str(address));
+ }
+ } SMARTLIST_FOREACH_END(address);
+ }
}
#define N_WILDCARD_CHECKS 2
@@ -1649,27 +2012,29 @@ launch_test_addresses(int fd, short event, void *args)
static void
dns_launch_wildcard_checks(void)
{
- int i;
+ int i, ipv6;
log_info(LD_EXIT, "Launching checks to see whether our nameservers like "
"to hijack DNS failures.");
- for (i = 0; i < N_WILDCARD_CHECKS; ++i) {
- /* RFC2606 reserves these. Sadly, some DNS hijackers, in a silly attempt
- * to 'comply' with rfc2606, refrain from giving A records for these.
- * This is the standards-compliance equivalent of making sure that your
- * crackhouse's elevator inspection certificate is up to date.
- */
- launch_wildcard_check(2, 16, ".invalid");
- launch_wildcard_check(2, 16, ".test");
-
- /* These will break specs if there are ever any number of
- * 8+-character top-level domains. */
- launch_wildcard_check(8, 16, "");
-
- /* Try some random .com/org/net domains. This will work fine so long as
- * not too many resolve to the same place. */
- launch_wildcard_check(8, 16, ".com");
- launch_wildcard_check(8, 16, ".org");
- launch_wildcard_check(8, 16, ".net");
+ for (ipv6 = 0; ipv6 <= 1; ++ipv6) {
+ for (i = 0; i < N_WILDCARD_CHECKS; ++i) {
+ /* RFC2606 reserves these. Sadly, some DNS hijackers, in a silly
+ * attempt to 'comply' with rfc2606, refrain from giving A records for
+ * these. This is the standards-compliance equivalent of making sure
+ * that your crackhouse's elevator inspection certificate is up to date.
+ */
+ launch_wildcard_check(2, 16, ipv6, ".invalid");
+ launch_wildcard_check(2, 16, ipv6, ".test");
+
+ /* These will break specs if there are ever any number of
+ * 8+-character top-level domains. */
+ launch_wildcard_check(8, 16, ipv6, "");
+
+ /* Try some random .com/org/net domains. This will work fine so long as
+ * not too many resolve to the same place. */
+ launch_wildcard_check(8, 16, ipv6, ".com");
+ launch_wildcard_check(8, 16, ipv6, ".org");
+ launch_wildcard_check(8, 16, ipv6, ".net");
+ }
}
}
@@ -1703,6 +2068,13 @@ dns_seems_to_be_broken(void)
return dns_is_completely_invalid;
}
+/** Return true iff we think that IPv6 hostname lookup is broken */
+int
+dns_seems_to_be_broken_for_ipv6(void)
+{
+ return dns_is_broken_for_ipv6;
+}
+
/** Forget what we've previously learned about our DNS servers' correctness. */
void
dns_reset_correctness_checks(void)
@@ -1712,6 +2084,8 @@ dns_reset_correctness_checks(void)
n_wildcard_requests = 0;
+ n_ipv6_requests_made = n_ipv6_timeouts = 0;
+
if (dns_wildcard_list) {
SMARTLIST_FOREACH(dns_wildcard_list, char *, cp, tor_free(cp));
smartlist_clear(dns_wildcard_list);
@@ -1722,7 +2096,8 @@ dns_reset_correctness_checks(void)
smartlist_clear(dns_wildcarded_test_address_list);
}
dns_wildcard_one_notice_given = dns_wildcard_notice_given =
- dns_wildcarded_test_address_notice_given = dns_is_completely_invalid = 0;
+ dns_wildcarded_test_address_notice_given = dns_is_completely_invalid =
+ dns_is_broken_for_ipv6 = 0;
}
/** Return true iff we have noticed that the dotted-quad <b>ip</b> has been
@@ -1730,7 +2105,7 @@ dns_reset_correctness_checks(void)
static int
answer_is_wildcarded(const char *ip)
{
- return dns_wildcard_list && smartlist_string_isin(dns_wildcard_list, ip);
+ return dns_wildcard_list && smartlist_contains_string(dns_wildcard_list, ip);
}
/** Exit with an assertion if <b>resolve</b> is corrupt. */
@@ -1746,11 +2121,14 @@ assert_resolve_ok(cached_resolve_t *resolve)
}
if (resolve->state == CACHE_STATE_PENDING ||
resolve->state == CACHE_STATE_DONE) {
+#if 0
tor_assert(!resolve->ttl);
if (resolve->is_reverse)
- tor_assert(!resolve->result.hostname);
+ tor_assert(!resolve->hostname);
else
- tor_assert(!resolve->result.a.addr);
+ tor_assert(!resolve->result_ipv4.addr_ipv4);
+#endif
+ /*XXXXX ADD MORE */
}
}
@@ -1773,8 +2151,8 @@ dump_dns_mem_usage(int severity)
/* Print out the count and estimated size of our &cache_root. It undercounts
hostnames in cached reverse resolves.
*/
- log(severity, LD_MM, "Our DNS cache has %d entries.", hash_count);
- log(severity, LD_MM, "Our DNS cache size is approximately %u bytes.",
+ tor_log(severity, LD_MM, "Our DNS cache has %d entries.", hash_count);
+ tor_log(severity, LD_MM, "Our DNS cache size is approximately %u bytes.",
(unsigned)hash_mem);
}
diff --git a/src/or/dns.h b/src/or/dns.h
index 441a6c3506..022cd4ac63 100644
--- a/src/or/dns.h
+++ b/src/or/dns.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -24,6 +24,7 @@ void dns_cancel_pending_resolve(const char *question);
int dns_resolve(edge_connection_t *exitconn);
void dns_launch_correctness_checks(void);
int dns_seems_to_be_broken(void);
+int dns_seems_to_be_broken_for_ipv6(void);
void dns_reset_correctness_checks(void);
void dump_dns_mem_usage(int severity);
diff --git a/src/or/dnsserv.c b/src/or/dnsserv.c
index 5875d96b8b..7032b58145 100644
--- a/src/or/dnsserv.c
+++ b/src/or/dnsserv.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2007-2012, The Tor Project, Inc. */
+/* Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -89,6 +89,7 @@ evdns_server_callback(struct evdns_server_request *req, void *data_)
continue;
switch (req->questions[i]->type) {
case EVDNS_TYPE_A:
+ case EVDNS_TYPE_AAAA:
case EVDNS_TYPE_PTR:
q = req->questions[i];
default:
@@ -101,7 +102,7 @@ evdns_server_callback(struct evdns_server_request *req, void *data_)
evdns_server_request_respond(req, DNS_ERR_NOTIMPL);
return;
}
- if (q->type != EVDNS_TYPE_A) {
+ if (q->type != EVDNS_TYPE_A && q->type != EVDNS_TYPE_AAAA) {
tor_assert(q->type == EVDNS_TYPE_PTR);
}
@@ -125,7 +126,7 @@ evdns_server_callback(struct evdns_server_request *req, void *data_)
TO_CONN(conn)->port = port;
TO_CONN(conn)->address = tor_dup_addr(&tor_addr);
- if (q->type == EVDNS_TYPE_A)
+ if (q->type == EVDNS_TYPE_A || q->type == EVDNS_TYPE_AAAA)
entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE;
else
entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR;
@@ -289,8 +290,9 @@ dnsserv_resolved(entry_connection_t *conn,
* or more of the questions in the request); then, call
* evdns_server_request_respond. */
if (answer_type == RESOLVED_TYPE_IPV6) {
- log_info(LD_APP, "Got an IPv6 answer; that's not implemented.");
- err = DNS_ERR_NOTIMPL;
+ evdns_server_request_add_aaaa_reply(req,
+ name,
+ 1, answer, ttl);
} else if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4 &&
conn->socks_request->command == SOCKS_COMMAND_RESOLVE) {
evdns_server_request_add_a_reply(req,
diff --git a/src/or/dnsserv.h b/src/or/dnsserv.h
index 39bd1d33b2..6bdb98de70 100644
--- a/src/or/dnsserv.h
+++ b/src/or/dnsserv.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c
index edb26dc088..51c3a56742 100644
--- a/src/or/entrynodes.c
+++ b/src/or/entrynodes.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -23,6 +23,7 @@
#include "directory.h"
#include "entrynodes.h"
#include "main.h"
+#include "microdesc.h"
#include "nodelist.h"
#include "policies.h"
#include "router.h"
@@ -61,6 +62,9 @@ static smartlist_t *entry_guards = NULL;
static int entry_guards_dirty = 0;
static void bridge_free(bridge_info_t *bridge);
+static const node_t *choose_random_entry_impl(cpath_build_state_t *state,
+ int for_directory,
+ dirinfo_type_t dirtype);
/** Return the list of entry guards, creating it if necessary. */
const smartlist_t *
@@ -125,6 +129,16 @@ entry_guard_set_status(entry_guard_t *e, const node_t *node,
control_event_guard(e->nickname, e->identity, "GOOD");
changed = 1;
}
+
+ if (node) {
+ int is_dir = node_is_dir(node) && node->rs &&
+ node->rs->version_supports_microdesc_cache;
+ if (e->is_dir_cache != is_dir) {
+ e->is_dir_cache = is_dir;
+ changed = 1;
+ }
+ }
+
return changed;
}
@@ -160,10 +174,13 @@ entry_is_time_to_retry(entry_guard_t *e, time_t now)
* is true).
*
* If the answer is no, set *<b>msg</b> to an explanation of why.
+ *
+ * If need_descriptor is true, only return the node if we currently have
+ * a descriptor (routerinfo or microdesc) for it.
*/
static INLINE const node_t *
entry_is_live(entry_guard_t *e, int need_uptime, int need_capacity,
- int assume_reachable, const char **msg)
+ int assume_reachable, int need_descriptor, const char **msg)
{
const node_t *node;
const or_options_t *options = get_options();
@@ -184,7 +201,11 @@ entry_is_live(entry_guard_t *e, int need_uptime, int need_capacity,
return NULL;
}
node = node_get_by_id(e->identity);
- if (!node || !node_has_descriptor(node)) {
+ if (!node) {
+ *msg = "no node info";
+ return NULL;
+ }
+ if (need_descriptor && !node_has_descriptor(node)) {
*msg = "no descriptor";
return NULL;
}
@@ -219,18 +240,19 @@ entry_is_live(entry_guard_t *e, int need_uptime, int need_capacity,
}
/** Return the number of entry guards that we think are usable. */
-static int
-num_live_entry_guards(void)
+int
+num_live_entry_guards(int for_directory)
{
int n = 0;
const char *msg;
if (! entry_guards)
return 0;
- SMARTLIST_FOREACH(entry_guards, entry_guard_t *, entry,
- {
- if (entry_is_live(entry, 0, 1, 0, &msg))
+ SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) {
+ if (for_directory && !entry->is_dir_cache)
+ continue;
+ if (entry_is_live(entry, 0, 1, 0, !for_directory, &msg))
++n;
- });
+ } SMARTLIST_FOREACH_END(entry);
return n;
}
@@ -257,7 +279,7 @@ log_entry_guards(int severity)
SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e)
{
const char *msg = NULL;
- if (entry_is_live(e, 0, 1, 0, &msg))
+ if (entry_is_live(e, 0, 1, 0, 0, &msg))
smartlist_add_asprintf(elements, "%s [%s] (up %s)",
e->nickname,
hex_str(e->identity, DIGEST_LEN),
@@ -316,7 +338,8 @@ control_event_guard_deferred(void)
* already in our entry_guards list, put it at the *beginning*.
* Else, put the one we pick at the end of the list. */
static const node_t *
-add_an_entry_guard(const node_t *chosen, int reset_status, int prepend)
+add_an_entry_guard(const node_t *chosen, int reset_status, int prepend,
+ int for_directory)
{
const node_t *node;
entry_guard_t *entry;
@@ -329,18 +352,32 @@ add_an_entry_guard(const node_t *chosen, int reset_status, int prepend)
entry->bad_since = 0;
entry->can_retry = 1;
}
+ entry->is_dir_cache = node->rs &&
+ node->rs->version_supports_microdesc_cache;
return NULL;
}
- } else {
+ } else if (!for_directory) {
node = choose_good_entry_server(CIRCUIT_PURPOSE_C_GENERAL, NULL);
if (!node)
return NULL;
+ } else {
+ const routerstatus_t *rs;
+ rs = router_pick_directory_server(MICRODESC_DIRINFO|V3_DIRINFO,
+ PDS_PREFER_TUNNELED_DIR_CONNS_);
+ if (!rs)
+ return NULL;
+ node = node_get_by_id(rs->identity_digest);
+ if (!node)
+ return NULL;
}
entry = tor_malloc_zero(sizeof(entry_guard_t));
log_info(LD_CIRC, "Chose %s as new entry guard.",
node_describe(node));
strlcpy(entry->nickname, node_get_nickname(node), sizeof(entry->nickname));
memcpy(entry->identity, node->identity, DIGEST_LEN);
+ entry->is_dir_cache = node_is_dir(node) &&
+ node->rs && node->rs->version_supports_microdesc_cache;
+
/* Choose expiry time smudged over the past month. The goal here
* is to a) spread out when Tor clients rotate their guards, so they
* don't all select them on the same day, and b) avoid leaving a
@@ -361,14 +398,16 @@ add_an_entry_guard(const node_t *chosen, int reset_status, int prepend)
/** If the use of entry guards is configured, choose more entry guards
* until we have enough in the list. */
static void
-pick_entry_guards(const or_options_t *options)
+pick_entry_guards(const or_options_t *options, int for_directory)
{
int changed = 0;
+ const int num_needed = for_directory ? options->NumDirectoryGuards :
+ options->NumEntryGuards;
tor_assert(entry_guards);
- while (num_live_entry_guards() < options->NumEntryGuards) {
- if (!add_an_entry_guard(NULL, 0, 0))
+ while (num_live_entry_guards(for_directory) < num_needed) {
+ if (!add_an_entry_guard(NULL, 0, 0, for_directory))
break;
changed = 1;
}
@@ -531,7 +570,7 @@ entry_guards_compute_status(const or_options_t *options, time_t now)
SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) {
const char *reason = digestmap_get(reasons, entry->identity);
const char *live_msg = "";
- const node_t *r = entry_is_live(entry, 0, 1, 0, &live_msg);
+ const node_t *r = entry_is_live(entry, 0, 1, 0, 0, &live_msg);
log_info(LD_CIRC, "Summary: Entry %s [%s] is %s, %s%s%s, and %s%s.",
entry->nickname,
hex_str(entry->identity, DIGEST_LEN),
@@ -543,7 +582,7 @@ entry_guards_compute_status(const or_options_t *options, time_t now)
r ? "" : live_msg);
} SMARTLIST_FOREACH_END(entry);
log_info(LD_CIRC, " (%d/%d entry guards are usable/new)",
- num_live_entry_guards(), smartlist_len(entry_guards));
+ num_live_entry_guards(0), smartlist_len(entry_guards));
log_entry_guards(LOG_INFO);
entry_guards_changed();
}
@@ -610,7 +649,7 @@ entry_guard_register_connect_status(const char *digest, int succeeded,
"Connection to never-contacted entry guard '%s' (%s) failed. "
"Removing from the list. %d/%d entry guards usable/new.",
entry->nickname, buf,
- num_live_entry_guards()-1, smartlist_len(entry_guards)-1);
+ num_live_entry_guards(0)-1, smartlist_len(entry_guards)-1);
control_event_guard(entry->nickname, entry->identity, "DROPPED");
entry_guard_free(entry);
smartlist_del_keeporder(entry_guards, idx);
@@ -649,7 +688,7 @@ entry_guard_register_connect_status(const char *digest, int succeeded,
break;
if (e->made_contact) {
const char *msg;
- const node_t *r = entry_is_live(e, 0, 1, 1, &msg);
+ const node_t *r = entry_is_live(e, 0, 1, 1, 0, &msg);
if (r && e->unreachable_since) {
refuse_conn = 1;
e->can_retry = 1;
@@ -661,7 +700,7 @@ entry_guard_register_connect_status(const char *digest, int succeeded,
"Connected to new entry guard '%s' (%s). Marking earlier "
"entry guards up. %d/%d entry guards usable/new.",
entry->nickname, buf,
- num_live_entry_guards(), smartlist_len(entry_guards));
+ num_live_entry_guards(0), smartlist_len(entry_guards));
log_entry_guards(LOG_INFO);
changed = 1;
}
@@ -724,7 +763,7 @@ entry_guards_set_from_config(const or_options_t *options)
smartlist_add(entry_fps, (void*)node->identity));
SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e, {
- if (smartlist_digest_isin(entry_fps, e->identity))
+ if (smartlist_contains_digest(entry_fps, e->identity))
smartlist_add(old_entry_guards_on_list, e);
else
smartlist_add(old_entry_guards_not_on_list, e);
@@ -759,7 +798,7 @@ entry_guards_set_from_config(const or_options_t *options)
/* Next, the rest of EntryNodes */
SMARTLIST_FOREACH_BEGIN(entry_nodes, const node_t *, node) {
- add_an_entry_guard(node, 0, 0);
+ add_an_entry_guard(node, 0, 0, 0);
if (smartlist_len(entry_guards) > options->NumEntryGuards * 10)
break;
} SMARTLIST_FOREACH_END(node);
@@ -791,14 +830,64 @@ entry_list_is_constrained(const or_options_t *options)
return 0;
}
+/** Return true iff this node can answer directory questions about
+ * microdescriptors. */
+static int
+node_understands_microdescriptors(const node_t *node)
+{
+ tor_assert(node);
+ if (node->rs && node->rs->version_supports_microdesc_cache)
+ return 1;
+ if (node->ri && tor_version_supports_microdescriptors(node->ri->platform))
+ return 1;
+ return 0;
+}
+
+/** Return true iff <b>node</b> is able to answer directory questions
+ * of type <b>dirinfo</b>. */
+static int
+node_can_handle_dirinfo(const node_t *node, dirinfo_type_t dirinfo)
+{
+ /* Checking dirinfo for any type other than microdescriptors isn't required
+ yet, since we only choose directory guards that can support microdescs,
+ routerinfos, and networkstatuses, AND we don't use directory guards if
+ we're configured to do direct downloads of anything else. The only case
+ where we might have a guard that doesn't know about a type of directory
+ information is when we're retrieving directory information from a
+ bridge. */
+
+ if ((dirinfo & MICRODESC_DIRINFO) &&
+ !node_understands_microdescriptors(node))
+ return 0;
+ return 1;
+}
+
/** Pick a live (up and listed) entry guard from entry_guards. If
* <b>state</b> is non-NULL, this is for a specific circuit --
* make sure not to pick this circuit's exit or any node in the
* exit's family. If <b>state</b> is NULL, we're looking for a random
- * guard (likely a bridge). */
+ * guard (likely a bridge). If <b>dirinfo</b> is not NO_DIRINFO, then
+ * only select from nodes that know how to answer directory questions
+ * of that type. */
const node_t *
choose_random_entry(cpath_build_state_t *state)
{
+ return choose_random_entry_impl(state, 0, 0);
+}
+
+/** Pick a live (up and listed) directory guard from entry_guards for
+ * downloading information of type <b>type</b>. */
+const node_t *
+choose_random_dirguard(dirinfo_type_t type)
+{
+ return choose_random_entry_impl(NULL, 1, type);
+}
+
+/** Helper for choose_random{entry,dirguard}. */
+static const node_t *
+choose_random_entry_impl(cpath_build_state_t *state, int for_directory,
+ dirinfo_type_t dirinfo_type)
+{
const or_options_t *options = get_options();
smartlist_t *live_entry_guards = smartlist_new();
smartlist_t *exit_family = smartlist_new();
@@ -808,6 +897,9 @@ choose_random_entry(cpath_build_state_t *state)
int need_uptime = state ? state->need_uptime : 0;
int need_capacity = state ? state->need_capacity : 0;
int preferred_min, consider_exit_family = 0;
+ int need_descriptor = !for_directory;
+ const int num_needed = for_directory ? options->NumDirectoryGuards :
+ options->NumEntryGuards;
if (chosen_exit) {
nodelist_add_node_and_family(exit_family, chosen_exit);
@@ -821,20 +913,28 @@ choose_random_entry(cpath_build_state_t *state)
entry_guards_set_from_config(options);
if (!entry_list_is_constrained(options) &&
- smartlist_len(entry_guards) < options->NumEntryGuards)
- pick_entry_guards(options);
+ smartlist_len(entry_guards) < num_needed)
+ pick_entry_guards(options, for_directory);
retry:
smartlist_clear(live_entry_guards);
SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) {
const char *msg;
- node = entry_is_live(entry, need_uptime, need_capacity, 0, &msg);
+ node = entry_is_live(entry, need_uptime, need_capacity, 0,
+ need_descriptor, &msg);
if (!node)
continue; /* down, no point */
+ if (for_directory) {
+ if (!entry->is_dir_cache)
+ continue; /* We need a directory and didn't get one. */
+ }
if (node == chosen_exit)
continue; /* don't pick the same node for entry and exit */
- if (consider_exit_family && smartlist_isin(exit_family, node))
+ if (consider_exit_family && smartlist_contains(exit_family, node))
continue; /* avoid relays that are family members of our exit */
+ if (dirinfo_type != NO_DIRINFO &&
+ !node_can_handle_dirinfo(node, dirinfo_type))
+ continue; /* this node won't be able to answer our dir questions */
#if 0 /* since EntryNodes is always strict now, this clause is moot */
if (options->EntryNodes &&
!routerset_contains_node(options->EntryNodes, node)) {
@@ -859,7 +959,7 @@ choose_random_entry(cpath_build_state_t *state)
* guard list without needing to. */
goto choose_and_finish;
}
- if (smartlist_len(live_entry_guards) >= options->NumEntryGuards)
+ if (smartlist_len(live_entry_guards) >= num_needed)
goto choose_and_finish; /* we have enough */
} SMARTLIST_FOREACH_END(entry);
@@ -881,7 +981,7 @@ choose_random_entry(cpath_build_state_t *state)
/* XXX if guard doesn't imply fast and stable, then we need
* to tell add_an_entry_guard below what we want, or it might
* be a long time til we get it. -RD */
- node = add_an_entry_guard(NULL, 0, 0);
+ node = add_an_entry_guard(NULL, 0, 0, for_directory);
if (node) {
entry_guards_changed();
/* XXX we start over here in case the new node we added shares
@@ -972,6 +1072,17 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg)
"Bad hex digest for EntryGuard");
}
}
+ if (smartlist_len(args) >= 3) {
+ const char *is_cache = smartlist_get(args, 2);
+ if (!strcasecmp(is_cache, "DirCache")) {
+ node->is_dir_cache = 1;
+ } else if (!strcasecmp(is_cache, "NoDirCache")) {
+ node->is_dir_cache = 0;
+ } else {
+ log_warn(LD_CONFIG, "Bogus third argument to EntryGuard line: %s",
+ escaped(is_cache));
+ }
+ }
SMARTLIST_FOREACH(args, char*, cp, tor_free(cp));
smartlist_free(args);
if (*msg)
@@ -1019,9 +1130,44 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg)
continue;
}
digestmap_set(added_by, d, tor_strdup(line->value+HEX_DIGEST_LEN+1));
+ } else if (!strcasecmp(line->key, "EntryGuardPathUseBias")) {
+ const or_options_t *options = get_options();
+ double use_cnt, success_cnt;
+
+ if (!node) {
+ *msg = tor_strdup("Unable to parse entry nodes: "
+ "EntryGuardPathUseBias without EntryGuard");
+ break;
+ }
+
+ if (tor_sscanf(line->value, "%lf %lf",
+ &use_cnt, &success_cnt) != 2) {
+ log_info(LD_GENERAL, "Malformed path use bias line for node %s",
+ node->nickname);
+ continue;
+ }
+
+ node->use_attempts = use_cnt;
+ node->use_successes = success_cnt;
+
+ log_info(LD_GENERAL, "Read %f/%f path use bias for node %s",
+ node->use_successes, node->use_attempts, node->nickname);
+
+ /* Note: We rely on the < comparison here to allow us to set a 0
+ * rate and disable the feature entirely. If refactoring, don't
+ * change to <= */
+ if (pathbias_get_use_success_count(node)/node->use_attempts
+ < pathbias_get_extreme_use_rate(options) &&
+ pathbias_get_dropguards(options)) {
+ node->path_bias_disabled = 1;
+ log_info(LD_GENERAL,
+ "Path use bias is too high (%f/%f); disabling node %s",
+ node->circ_successes, node->circ_attempts, node->nickname);
+ }
} else if (!strcasecmp(line->key, "EntryGuardPathBias")) {
const or_options_t *options = get_options();
- unsigned hop_cnt, success_cnt;
+ double hop_cnt, success_cnt, timeouts, collapsed, successful_closed,
+ unusable;
if (!node) {
*msg = tor_strdup("Unable to parse entry nodes: "
@@ -1029,25 +1175,48 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg)
break;
}
- if (tor_sscanf(line->value, "%u %u", &success_cnt, &hop_cnt) != 2) {
- log_warn(LD_GENERAL, "Unable to parse guard path bias info: "
- "Misformated EntryGuardPathBias %s", escaped(line->value));
- continue;
+ /* First try 3 params, then 2. */
+ /* In the long run: circuit_success ~= successful_circuit_close +
+ * collapsed_circuits +
+ * unusable_circuits */
+ if (tor_sscanf(line->value, "%lf %lf %lf %lf %lf %lf",
+ &hop_cnt, &success_cnt, &successful_closed,
+ &collapsed, &unusable, &timeouts) != 6) {
+ int old_success, old_hops;
+ if (tor_sscanf(line->value, "%u %u", &old_success, &old_hops) != 2) {
+ continue;
+ }
+ log_info(LD_GENERAL, "Reading old-style EntryGuardPathBias %s",
+ escaped(line->value));
+
+ success_cnt = old_success;
+ successful_closed = old_success;
+ hop_cnt = old_hops;
+ timeouts = 0;
+ collapsed = 0;
+ unusable = 0;
}
- node->first_hops = hop_cnt;
- node->circuit_successes = success_cnt;
- log_info(LD_GENERAL, "Read %u/%u path bias for node %s",
- node->circuit_successes, node->first_hops, node->nickname);
+ node->circ_attempts = hop_cnt;
+ node->circ_successes = success_cnt;
+
+ node->successful_circuits_closed = successful_closed;
+ node->timeouts = timeouts;
+ node->collapsed_circuits = collapsed;
+ node->unusable_circuits = unusable;
+
+ log_info(LD_GENERAL, "Read %f/%f path bias for node %s",
+ node->circ_successes, node->circ_attempts, node->nickname);
/* Note: We rely on the < comparison here to allow us to set a 0
* rate and disable the feature entirely. If refactoring, don't
* change to <= */
- if (node->circuit_successes/((double)node->first_hops)
- < pathbias_get_disable_rate(options)) {
+ if (pathbias_get_close_success_count(node)/node->circ_attempts
+ < pathbias_get_extreme_rate(options) &&
+ pathbias_get_dropguards(options)) {
node->path_bias_disabled = 1;
log_info(LD_GENERAL,
- "Path bias is too high (%u/%u); disabling node %s",
- node->circuit_successes, node->first_hops, node->nickname);
+ "Path bias is too high (%f/%f); disabling node %s",
+ node->circ_successes, node->circ_attempts, node->nickname);
}
} else {
@@ -1138,7 +1307,8 @@ entry_guards_update_state(or_state_t *state)
*next = line = tor_malloc_zero(sizeof(config_line_t));
line->key = tor_strdup("EntryGuard");
base16_encode(dbuf, sizeof(dbuf), e->identity, DIGEST_LEN);
- tor_asprintf(&line->value, "%s %s", e->nickname, dbuf);
+ tor_asprintf(&line->value, "%s %s %sDirCache", e->nickname, dbuf,
+ e->is_dir_cache ? "" : "No");
next = &(line->next);
if (e->unreachable_since) {
*next = line = tor_malloc_zero(sizeof(config_line_t));
@@ -1170,11 +1340,26 @@ entry_guards_update_state(or_state_t *state)
d, e->chosen_by_version, t);
next = &(line->next);
}
- if (e->first_hops) {
+ if (e->circ_attempts > 0) {
*next = line = tor_malloc_zero(sizeof(config_line_t));
line->key = tor_strdup("EntryGuardPathBias");
- tor_asprintf(&line->value, "%u %u",
- e->circuit_successes, e->first_hops);
+ /* In the long run: circuit_success ~= successful_circuit_close +
+ * collapsed_circuits +
+ * unusable_circuits */
+ tor_asprintf(&line->value, "%f %f %f %f %f %f",
+ e->circ_attempts, e->circ_successes,
+ pathbias_get_close_success_count(e),
+ e->collapsed_circuits,
+ e->unusable_circuits, e->timeouts);
+ next = &(line->next);
+ }
+ if (e->use_attempts > 0) {
+ *next = line = tor_malloc_zero(sizeof(config_line_t));
+ line->key = tor_strdup("EntryGuardPathUseBias");
+
+ tor_asprintf(&line->value, "%f %f",
+ e->use_attempts,
+ pathbias_get_use_success_count(e));
next = &(line->next);
}
@@ -1392,9 +1577,17 @@ learned_router_identity(const tor_addr_t *addr, uint16_t port,
bridge_info_t *bridge =
get_configured_bridge_by_addr_port_digest(addr, port, digest);
if (bridge && tor_digest_is_zero(bridge->identity)) {
+ char *transport_info = NULL;
+ const char *transport_name =
+ find_transport_name_by_bridge_addrport(addr, port);
+ if (transport_name)
+ tor_asprintf(&transport_info, " (with transport '%s')", transport_name);
+
memcpy(bridge->identity, digest, DIGEST_LEN);
- log_notice(LD_DIR, "Learned fingerprint %s for bridge %s",
- hex_str(digest, DIGEST_LEN), fmt_addrport(addr, port));
+ log_notice(LD_DIR, "Learned fingerprint %s for bridge %s%s.",
+ hex_str(digest, DIGEST_LEN), fmt_addrport(addr, port),
+ transport_info ? transport_info : "");
+ tor_free(transport_info);
}
}
@@ -1502,7 +1695,7 @@ routerset_contains_bridge(const routerset_t *routerset,
return 0;
extinfo = extend_info_new(
- NULL, bridge->identity, NULL, &bridge->addr, bridge->port);
+ NULL, bridge->identity, NULL, NULL, &bridge->addr, bridge->port);
result = routerset_contains_extendinfo(routerset, extinfo);
extend_info_free(extinfo);
return result;
@@ -1520,7 +1713,9 @@ find_bridge_by_digest(const char *digest)
return NULL;
}
-/* DOCDOC find_transport_name_by_bridge_addrport */
+/** Given the <b>addr</b> and <b>port</b> of a bridge, if that bridge
+ * supports a pluggable transport, return its name. Otherwise, return
+ * NULL. */
const char *
find_transport_name_by_bridge_addrport(const tor_addr_t *addr, uint16_t port)
{
@@ -1795,7 +1990,7 @@ learned_bridge_descriptor(routerinfo_t *ri, int from_cache)
node = node_get_mutable_by_id(ri->cache_info.identity_digest);
tor_assert(node);
rewrite_node_address_for_bridge(bridge, node);
- add_an_entry_guard(node, 1, 1);
+ add_an_entry_guard(node, 1, 1, 0);
log_notice(LD_DIR, "new bridge descriptor '%s' (%s): %s", ri->nickname,
from_cache ? "cached" : "fresh", router_describe(ri));
@@ -1819,7 +2014,7 @@ int
any_bridge_descriptors_known(void)
{
tor_assert(get_options()->UseBridges);
- return choose_random_entry(NULL)!=NULL ? 1 : 0;
+ return choose_random_entry(NULL) != NULL;
}
/** Return 1 if there are any directory conns fetching bridge descriptors
@@ -1901,29 +2096,24 @@ entries_retry_all(const or_options_t *options)
entries_retry_helper(options, 1);
}
-/** Return true if we've ever had a bridge running a Tor version that can't
- * provide microdescriptors to us. In that case fall back to asking for
- * full descriptors. Eventually all bridges will support microdescriptors
- * and we can take this check out; see bug 4013. */
+/** Return true if at least one of our bridges runs a Tor version that can
+ * provide microdescriptors to us. If not, we'll fall back to asking for
+ * full descriptors. */
int
-any_bridges_dont_support_microdescriptors(void)
+any_bridge_supports_microdescriptors(void)
{
const node_t *node;
- static int ever_answered_yes = 0;
if (!get_options()->UseBridges || !entry_guards)
return 0;
- if (ever_answered_yes)
- return 1; /* if we ever answer 'yes', always answer 'yes' */
SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) {
node = node_get_by_id(e->identity);
- if (node && node->ri &&
+ if (node && node->is_running &&
node_is_bridge(node) && node_is_a_configured_bridge(node) &&
- !tor_version_supports_microdescriptors(node->ri->platform)) {
+ node_understands_microdescriptors(node)) {
/* This is one of our current bridges, and we know enough about
- * it to know that it won't be able to answer our microdescriptor
+ * it to know that it will be able to answer our microdescriptor
* questions. */
- ever_answered_yes = 1;
- return 1;
+ return 1;
}
} SMARTLIST_FOREACH_END(e);
return 0;
diff --git a/src/or/entrynodes.h b/src/or/entrynodes.h
index 1a12cf4bc3..52b8dc00e4 100644
--- a/src/or/entrynodes.h
+++ b/src/or/entrynodes.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -31,10 +31,19 @@ typedef struct entry_guard_t {
* router, 1 if we have. */
unsigned int can_retry : 1; /**< Should we retry connecting to this entry,
* in spite of having it marked as unreachable?*/
- unsigned int path_bias_notice : 1; /**< Did we alert the user about path bias
+ unsigned int path_bias_noticed : 1; /**< Did we alert the user about path
+ * bias for this node already? */
+ unsigned int path_bias_warned : 1; /**< Did we alert the user about path bias
* for this node already? */
+ unsigned int path_bias_extreme : 1; /**< Did we alert the user about path
+ * bias for this node already? */
unsigned int path_bias_disabled : 1; /**< Have we disabled this node because
* of path bias issues? */
+ unsigned int path_bias_use_noticed : 1; /**< Did we alert the user about path
+ * use bias for this node already? */
+ unsigned int path_bias_use_extreme : 1; /**< Did we alert the user about path
+ * use bias for this node already? */
+ unsigned int is_dir_cache : 1; /**< Is this node a directory cache? */
time_t bad_since; /**< 0 if this guard is currently usable, or the time at
* which it was observed to become (according to the
* directory or the user configuration) unusable. */
@@ -44,14 +53,27 @@ typedef struct entry_guard_t {
time_t last_attempted; /**< 0 if we can connect to this guard, or the time
* at which we last failed to connect to it. */
- unsigned first_hops; /**< Number of first hops this guard has completed */
- unsigned circuit_successes; /**< Number of successfully built circuits using
+ double circ_attempts; /**< Number of circuits this guard has "attempted" */
+ double circ_successes; /**< Number of successfully built circuits using
+ * this guard as first hop. */
+ double successful_circuits_closed; /**< Number of circuits that carried
+ * streams successfully. */
+ double collapsed_circuits; /**< Number of fully built circuits that were
+ * remotely closed before any streams were
+ * attempted. */
+ double unusable_circuits; /**< Number of circuits for which streams were
+ * attempted, but none succeeded. */
+ double timeouts; /**< Number of 'right-censored' circuit timeouts for this
+ * guard. */
+ double use_attempts; /**< Number of circuits we tried to use with streams */
+ double use_successes; /**< Number of successfully used circuits using
* this guard as first hop. */
} entry_guard_t;
entry_guard_t *entry_guard_get_by_id_digest(const char *digest);
void entry_guards_changed(void);
const smartlist_t *get_entry_guards(void);
+int num_live_entry_guards(int for_directory);
#endif
@@ -61,6 +83,7 @@ int entry_guard_register_connect_status(const char *digest, int succeeded,
void entry_nodes_should_be_added(void);
int entry_list_is_constrained(const or_options_t *options);
const node_t *choose_random_entry(cpath_build_state_t *state);
+const node_t *choose_random_dirguard(dirinfo_type_t t);
int entry_guards_parse_state(or_state_t *state, int set, char **msg);
void entry_guards_update_state(or_state_t *state);
int getinfo_helper_entry_guards(control_connection_t *conn,
@@ -85,7 +108,7 @@ int any_pending_bridge_descriptor_fetches(void);
int entries_known_but_down(const or_options_t *options);
void entries_retry_all(const or_options_t *options);
-int any_bridges_dont_support_microdescriptors(void);
+int any_bridge_supports_microdescriptors(void);
void entry_guards_free_all(void);
@@ -97,5 +120,8 @@ int find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port,
int validate_pluggable_transports_config(void);
+double pathbias_get_close_success_count(entry_guard_t *guard);
+double pathbias_get_use_success_count(entry_guard_t *guard);
+
#endif
diff --git a/src/or/eventdns_tor.h b/src/or/eventdns_tor.h
index 0775643b5c..69662281bc 100644
--- a/src/or/eventdns_tor.h
+++ b/src/or/eventdns_tor.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2007-2012, The Tor Project, Inc. */
+/* Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_EVENTDNS_TOR_H
diff --git a/src/or/geoip.c b/src/or/geoip.c
index 2fd77d8b97..e2e98e8ec4 100644
--- a/src/or/geoip.c
+++ b/src/or/geoip.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2007-2012, The Tor Project, Inc. */
+/* Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -38,7 +38,6 @@ typedef struct geoip_ipv6_entry_t {
/** A per-country record for GeoIP request history. */
typedef struct geoip_country_t {
char countrycode[3];
- uint32_t n_v2_ns_requests;
uint32_t n_v3_ns_requests;
} geoip_country_t;
@@ -224,7 +223,8 @@ static int
geoip_ipv6_compare_entries_(const void **_a, const void **_b)
{
const geoip_ipv6_entry_t *a = *_a, *b = *_b;
- return memcmp(a->ip_low.s6_addr, b->ip_low.s6_addr, sizeof(struct in6_addr));
+ return fast_memcmp(a->ip_low.s6_addr, b->ip_low.s6_addr,
+ sizeof(struct in6_addr));
}
/** bsearch helper: return -1, 1, or 0 based on comparison of an IPv6
@@ -235,10 +235,10 @@ geoip_ipv6_compare_key_to_entry_(const void *_key, const void **_member)
const struct in6_addr *addr = (struct in6_addr *)_key;
const geoip_ipv6_entry_t *entry = *_member;
- if (memcmp(addr->s6_addr, entry->ip_low.s6_addr,
+ if (fast_memcmp(addr->s6_addr, entry->ip_low.s6_addr,
sizeof(struct in6_addr)) < 0)
return -1;
- else if (memcmp(addr->s6_addr, entry->ip_high.s6_addr,
+ else if (fast_memcmp(addr->s6_addr, entry->ip_high.s6_addr,
sizeof(struct in6_addr)) > 0)
return 1;
else
@@ -514,67 +514,6 @@ client_history_clear(void)
}
}
-/** How often do we update our estimate which share of v2 and v3 directory
- * requests is sent to us? We could as well trigger updates of shares from
- * network status updates, but that means adding a lot of calls into code
- * that is independent from geoip stats (and keeping them up-to-date). We
- * are perfectly fine with an approximation of 15-minute granularity. */
-#define REQUEST_SHARE_INTERVAL (15 * 60)
-
-/** When did we last determine which share of v2 and v3 directory requests
- * is sent to us? */
-static time_t last_time_determined_shares = 0;
-
-/** Sum of products of v2 shares times the number of seconds for which we
- * consider these shares as valid. */
-static double v2_share_times_seconds;
-
-/** Sum of products of v3 shares times the number of seconds for which we
- * consider these shares as valid. */
-static double v3_share_times_seconds;
-
-/** Number of seconds we are determining v2 and v3 shares. */
-static int share_seconds;
-
-/** Try to determine which fraction of v2 and v3 directory requests aimed at
- * caches will be sent to us at time <b>now</b> and store that value in
- * order to take a mean value later on. */
-static void
-geoip_determine_shares(time_t now)
-{
- double v2_share = 0.0, v3_share = 0.0;
- if (router_get_my_share_of_directory_requests(&v2_share, &v3_share) < 0)
- return;
- if (last_time_determined_shares) {
- v2_share_times_seconds += v2_share *
- ((double) (now - last_time_determined_shares));
- v3_share_times_seconds += v3_share *
- ((double) (now - last_time_determined_shares));
- share_seconds += (int)(now - last_time_determined_shares);
- }
- last_time_determined_shares = now;
-}
-
-/** Calculate which fraction of v2 and v3 directory requests aimed at caches
- * have been sent to us since the last call of this function up to time
- * <b>now</b>. Set *<b>v2_share_out</b> and *<b>v3_share_out</b> to the
- * fractions of v2 and v3 protocol shares we expect to have seen. Reset
- * counters afterwards. Return 0 on success, -1 on failure (e.g. when zero
- * seconds have passed since the last call).*/
-static int
-geoip_get_mean_shares(time_t now, double *v2_share_out,
- double *v3_share_out)
-{
- geoip_determine_shares(now);
- if (!share_seconds)
- return -1;
- *v2_share_out = v2_share_times_seconds / ((double) share_seconds);
- *v3_share_out = v3_share_times_seconds / ((double) share_seconds);
- v2_share_times_seconds = v3_share_times_seconds = 0.0;
- share_seconds = 0;
- return 0;
-}
-
/** Note that we've seen a client connect from the IP <b>addr</b>
* at time <b>now</b>. Ignored by all but bridges and directories if
* configured accordingly. */
@@ -609,22 +548,14 @@ geoip_note_client_seen(geoip_client_action_t action,
else
ent->last_seen_in_minutes = 0;
- if (action == GEOIP_CLIENT_NETWORKSTATUS ||
- action == GEOIP_CLIENT_NETWORKSTATUS_V2) {
+ if (action == GEOIP_CLIENT_NETWORKSTATUS) {
int country_idx = geoip_get_country_by_addr(addr);
if (country_idx < 0)
country_idx = 0; /** unresolved requests are stored at index 0. */
if (country_idx >= 0 && country_idx < smartlist_len(geoip_countries)) {
geoip_country_t *country = smartlist_get(geoip_countries, country_idx);
- if (action == GEOIP_CLIENT_NETWORKSTATUS)
- ++country->n_v3_ns_requests;
- else
- ++country->n_v2_ns_requests;
+ ++country->n_v3_ns_requests;
}
-
- /* Periodically determine share of requests that we should see */
- if (last_time_determined_shares + REQUEST_SHARE_INTERVAL < now)
- geoip_determine_shares(now);
}
}
@@ -651,36 +582,24 @@ geoip_remove_old_clients(time_t cutoff)
&cutoff);
}
-/** How many responses are we giving to clients requesting v2 network
- * statuses? */
-static uint32_t ns_v2_responses[GEOIP_NS_RESPONSE_NUM];
-
/** How many responses are we giving to clients requesting v3 network
* statuses? */
static uint32_t ns_v3_responses[GEOIP_NS_RESPONSE_NUM];
-/** Note that we've rejected a client's request for a v2 or v3 network
- * status, encoded in <b>action</b> for reason <b>reason</b> at time
- * <b>now</b>. */
+/** Note that we've rejected a client's request for a v3 network status
+ * for reason <b>reason</b> at time <b>now</b>. */
void
-geoip_note_ns_response(geoip_client_action_t action,
- geoip_ns_response_t response)
+geoip_note_ns_response(geoip_ns_response_t response)
{
static int arrays_initialized = 0;
if (!get_options()->DirReqStatistics)
return;
if (!arrays_initialized) {
- memset(ns_v2_responses, 0, sizeof(ns_v2_responses));
memset(ns_v3_responses, 0, sizeof(ns_v3_responses));
arrays_initialized = 1;
}
- tor_assert(action == GEOIP_CLIENT_NETWORKSTATUS ||
- action == GEOIP_CLIENT_NETWORKSTATUS_V2);
tor_assert(response < GEOIP_NS_RESPONSE_NUM);
- if (action == GEOIP_CLIENT_NETWORKSTATUS)
- ns_v3_responses[response]++;
- else
- ns_v2_responses[response]++;
+ ns_v3_responses[response]++;
}
/** Do not mention any country from which fewer than this number of IPs have
@@ -735,7 +654,6 @@ typedef struct dirreq_map_entry_t {
unsigned int state:3; /**< State of this directory request. */
unsigned int type:1; /**< Is this a direct or a tunneled request? */
unsigned int completed:1; /**< Is this request complete? */
- unsigned int action:2; /**< Is this a v2 or v3 request? */
/** When did we receive the request and started sending the response? */
struct timeval request_time;
size_t response_size; /**< What is the size of the response in bytes? */
@@ -804,12 +722,11 @@ dirreq_map_get_(dirreq_type_t type, uint64_t dirreq_id)
}
/** Note that an either direct or tunneled (see <b>type</b>) directory
- * request for a network status with unique ID <b>dirreq_id</b> of size
- * <b>response_size</b> and action <b>action</b> (either v2 or v3) has
- * started. */
+ * request for a v3 network status with unique ID <b>dirreq_id</b> of size
+ * <b>response_size</b> has started. */
void
geoip_start_dirreq(uint64_t dirreq_id, size_t response_size,
- geoip_client_action_t action, dirreq_type_t type)
+ dirreq_type_t type)
{
dirreq_map_entry_t *ent;
if (!get_options()->DirReqStatistics)
@@ -818,7 +735,6 @@ geoip_start_dirreq(uint64_t dirreq_id, size_t response_size,
ent->dirreq_id = dirreq_id;
tor_gettimeofday(&ent->request_time);
ent->response_size = response_size;
- ent->action = action;
ent->type = type;
dirreq_map_put_(ent, type, dirreq_id);
}
@@ -859,8 +775,7 @@ geoip_change_dirreq_state(uint64_t dirreq_id, dirreq_type_t type,
* times by deciles and quartiles. Return NULL if we have not observed
* requests for long enough. */
static char *
-geoip_get_dirreq_history(geoip_client_action_t action,
- dirreq_type_t type)
+geoip_get_dirreq_history(dirreq_type_t type)
{
char *result = NULL;
smartlist_t *dirreq_completed = NULL;
@@ -870,13 +785,10 @@ geoip_get_dirreq_history(geoip_client_action_t action,
struct timeval now;
tor_gettimeofday(&now);
- if (action != GEOIP_CLIENT_NETWORKSTATUS &&
- action != GEOIP_CLIENT_NETWORKSTATUS_V2)
- return NULL;
dirreq_completed = smartlist_new();
for (ptr = HT_START(dirreqmap, &dirreq_map); ptr; ptr = next) {
ent = *ptr;
- if (ent->action != action || ent->type != type) {
+ if (ent->type != type) {
next = HT_NEXT(dirreqmap, &dirreq_map, ptr);
continue;
} else {
@@ -1062,18 +974,15 @@ geoip_get_client_history(geoip_client_action_t action,
}
/** Return a newly allocated string holding the per-country request history
- * for <b>action</b> in a format suitable for an extra-info document, or NULL
- * on failure. */
+ * for v3 network statuses in a format suitable for an extra-info document,
+ * or NULL on failure. */
char *
-geoip_get_request_history(geoip_client_action_t action)
+geoip_get_request_history(void)
{
smartlist_t *entries, *strings;
char *result;
unsigned granularity = IP_GRANULARITY;
- if (action != GEOIP_CLIENT_NETWORKSTATUS &&
- action != GEOIP_CLIENT_NETWORKSTATUS_V2)
- return NULL;
if (!geoip_countries)
return NULL;
@@ -1081,8 +990,7 @@ geoip_get_request_history(geoip_client_action_t action)
SMARTLIST_FOREACH_BEGIN(geoip_countries, geoip_country_t *, c) {
uint32_t tot = 0;
c_hist_t *ent;
- tot = (action == GEOIP_CLIENT_NETWORKSTATUS) ?
- c->n_v3_ns_requests : c->n_v2_ns_requests;
+ tot = c->n_v3_ns_requests;
if (!tot)
continue;
ent = tor_malloc_zero(sizeof(c_hist_t));
@@ -1120,14 +1028,13 @@ void
geoip_reset_dirreq_stats(time_t now)
{
SMARTLIST_FOREACH(geoip_countries, geoip_country_t *, c, {
- c->n_v2_ns_requests = c->n_v3_ns_requests = 0;
+ c->n_v3_ns_requests = 0;
});
{
clientmap_entry_t **ent, **next, *this;
for (ent = HT_START(clientmap, &client_history); ent != NULL;
ent = next) {
- if ((*ent)->action == GEOIP_CLIENT_NETWORKSTATUS ||
- (*ent)->action == GEOIP_CLIENT_NETWORKSTATUS_V2) {
+ if ((*ent)->action == GEOIP_CLIENT_NETWORKSTATUS) {
this = *ent;
next = HT_NEXT_RMV(clientmap, &client_history, ent);
tor_free(this);
@@ -1136,10 +1043,6 @@ geoip_reset_dirreq_stats(time_t now)
}
}
}
- v2_share_times_seconds = v3_share_times_seconds = 0.0;
- last_time_determined_shares = 0;
- share_seconds = 0;
- memset(ns_v2_responses, 0, sizeof(ns_v2_responses));
memset(ns_v3_responses, 0, sizeof(ns_v3_responses));
{
dirreq_map_entry_t **ent, **next, *this;
@@ -1167,12 +1070,9 @@ char *
geoip_format_dirreq_stats(time_t now)
{
char t[ISO_TIME_LEN+1];
- double v2_share = 0.0, v3_share = 0.0;
int i;
- char *v3_ips_string, *v2_ips_string, *v3_reqs_string, *v2_reqs_string,
- *v2_share_string = NULL, *v3_share_string = NULL,
- *v3_direct_dl_string, *v2_direct_dl_string,
- *v3_tunneled_dl_string, *v2_tunneled_dl_string;
+ char *v3_ips_string, *v3_reqs_string, *v3_direct_dl_string,
+ *v3_tunneled_dl_string;
char *result;
if (!start_of_dirreq_stats_interval)
@@ -1181,90 +1081,45 @@ geoip_format_dirreq_stats(time_t now)
tor_assert(now >= start_of_dirreq_stats_interval);
format_iso_time(t, now);
- geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS_V2, &v2_ips_string,
- NULL);
geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS, &v3_ips_string, NULL);
- v2_reqs_string = geoip_get_request_history(
- GEOIP_CLIENT_NETWORKSTATUS_V2);
- v3_reqs_string = geoip_get_request_history(GEOIP_CLIENT_NETWORKSTATUS);
+ v3_reqs_string = geoip_get_request_history();
#define RESPONSE_GRANULARITY 8
for (i = 0; i < GEOIP_NS_RESPONSE_NUM; i++) {
- ns_v2_responses[i] = round_uint32_to_next_multiple_of(
- ns_v2_responses[i], RESPONSE_GRANULARITY);
ns_v3_responses[i] = round_uint32_to_next_multiple_of(
ns_v3_responses[i], RESPONSE_GRANULARITY);
}
#undef RESPONSE_GRANULARITY
- if (!geoip_get_mean_shares(now, &v2_share, &v3_share)) {
- tor_asprintf(&v2_share_string, "dirreq-v2-share %0.2f%%\n",
- v2_share*100);
- tor_asprintf(&v3_share_string, "dirreq-v3-share %0.2f%%\n",
- v3_share*100);
- }
-
- v2_direct_dl_string = geoip_get_dirreq_history(
- GEOIP_CLIENT_NETWORKSTATUS_V2, DIRREQ_DIRECT);
- v3_direct_dl_string = geoip_get_dirreq_history(
- GEOIP_CLIENT_NETWORKSTATUS, DIRREQ_DIRECT);
-
- v2_tunneled_dl_string = geoip_get_dirreq_history(
- GEOIP_CLIENT_NETWORKSTATUS_V2, DIRREQ_TUNNELED);
- v3_tunneled_dl_string = geoip_get_dirreq_history(
- GEOIP_CLIENT_NETWORKSTATUS, DIRREQ_TUNNELED);
+ v3_direct_dl_string = geoip_get_dirreq_history(DIRREQ_DIRECT);
+ v3_tunneled_dl_string = geoip_get_dirreq_history(DIRREQ_TUNNELED);
/* Put everything together into a single string. */
tor_asprintf(&result, "dirreq-stats-end %s (%d s)\n"
"dirreq-v3-ips %s\n"
- "dirreq-v2-ips %s\n"
"dirreq-v3-reqs %s\n"
- "dirreq-v2-reqs %s\n"
"dirreq-v3-resp ok=%u,not-enough-sigs=%u,unavailable=%u,"
"not-found=%u,not-modified=%u,busy=%u\n"
- "dirreq-v2-resp ok=%u,unavailable=%u,"
- "not-found=%u,not-modified=%u,busy=%u\n"
- "%s"
- "%s"
"dirreq-v3-direct-dl %s\n"
- "dirreq-v2-direct-dl %s\n"
- "dirreq-v3-tunneled-dl %s\n"
- "dirreq-v2-tunneled-dl %s\n",
+ "dirreq-v3-tunneled-dl %s\n",
t,
(unsigned) (now - start_of_dirreq_stats_interval),
v3_ips_string ? v3_ips_string : "",
- v2_ips_string ? v2_ips_string : "",
v3_reqs_string ? v3_reqs_string : "",
- v2_reqs_string ? v2_reqs_string : "",
ns_v3_responses[GEOIP_SUCCESS],
ns_v3_responses[GEOIP_REJECT_NOT_ENOUGH_SIGS],
ns_v3_responses[GEOIP_REJECT_UNAVAILABLE],
ns_v3_responses[GEOIP_REJECT_NOT_FOUND],
ns_v3_responses[GEOIP_REJECT_NOT_MODIFIED],
ns_v3_responses[GEOIP_REJECT_BUSY],
- ns_v2_responses[GEOIP_SUCCESS],
- ns_v2_responses[GEOIP_REJECT_UNAVAILABLE],
- ns_v2_responses[GEOIP_REJECT_NOT_FOUND],
- ns_v2_responses[GEOIP_REJECT_NOT_MODIFIED],
- ns_v2_responses[GEOIP_REJECT_BUSY],
- v2_share_string ? v2_share_string : "",
- v3_share_string ? v3_share_string : "",
v3_direct_dl_string ? v3_direct_dl_string : "",
- v2_direct_dl_string ? v2_direct_dl_string : "",
- v3_tunneled_dl_string ? v3_tunneled_dl_string : "",
- v2_tunneled_dl_string ? v2_tunneled_dl_string : "");
+ v3_tunneled_dl_string ? v3_tunneled_dl_string : "");
/* Free partial strings. */
tor_free(v3_ips_string);
- tor_free(v2_ips_string);
tor_free(v3_reqs_string);
- tor_free(v2_reqs_string);
- tor_free(v2_share_string);
- tor_free(v3_share_string);
tor_free(v3_direct_dl_string);
- tor_free(v2_direct_dl_string);
tor_free(v3_tunneled_dl_string);
- tor_free(v2_tunneled_dl_string);
return result;
}
@@ -1495,8 +1350,11 @@ load_bridge_stats(time_t now)
fname = get_datadir_fname2("stats", "bridge-stats");
contents = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL);
- if (contents && validate_bridge_stats(contents, now))
+ if (contents && validate_bridge_stats(contents, now)) {
bridge_stats_extrainfo = contents;
+ } else {
+ tor_free(contents);
+ }
tor_free(fname);
}
diff --git a/src/or/geoip.h b/src/or/geoip.h
index 2272486477..ebefee5f4e 100644
--- a/src/or/geoip.h
+++ b/src/or/geoip.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -30,18 +30,17 @@ void geoip_note_client_seen(geoip_client_action_t action,
const tor_addr_t *addr, time_t now);
void geoip_remove_old_clients(time_t cutoff);
-void geoip_note_ns_response(geoip_client_action_t action,
- geoip_ns_response_t response);
+void geoip_note_ns_response(geoip_ns_response_t response);
int geoip_get_client_history(geoip_client_action_t action,
char **country_str, char **ipver_str);
-char *geoip_get_request_history(geoip_client_action_t action);
+char *geoip_get_request_history(void);
int getinfo_helper_geoip(control_connection_t *control_conn,
const char *question, char **answer,
const char **errmsg);
void geoip_free_all(void);
void geoip_start_dirreq(uint64_t dirreq_id, size_t response_size,
- geoip_client_action_t action, dirreq_type_t type);
+ dirreq_type_t type);
void geoip_change_dirreq_state(uint64_t dirreq_id, dirreq_type_t type,
dirreq_state_t new_state);
diff --git a/src/or/hibernate.c b/src/or/hibernate.c
index b33e5e216c..36af4d8f83 100644
--- a/src/or/hibernate.c
+++ b/src/or/hibernate.c
@@ -1,5 +1,5 @@
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -23,6 +23,8 @@ hibernating, phase 2:
#define HIBERNATE_PRIVATE
#include "or.h"
+#include "channel.h"
+#include "channeltls.h"
#include "config.h"
#include "connection.h"
#include "connection_edge.h"
@@ -846,7 +848,13 @@ hibernate_go_dormant(time_t now)
if (conn->type == CONN_TYPE_AP) /* send socks failure if needed */
connection_mark_unattached_ap(TO_ENTRY_CONN(conn),
END_STREAM_REASON_HIBERNATING);
- else
+ else if (conn->type == CONN_TYPE_OR) {
+ if (TO_OR_CONN(conn)->chan) {
+ channel_mark_for_close(TLS_CHAN_TO_BASE(TO_OR_CONN(conn)->chan));
+ } else {
+ connection_mark_for_close(conn);
+ }
+ } else
connection_mark_for_close(conn);
}
@@ -882,12 +890,12 @@ hibernate_end_time_elapsed(time_t now)
/* We weren't sleeping before; we should sleep now. */
log_notice(LD_ACCT,
"Accounting period ended. Commencing hibernation until "
- "%s GMT", buf);
+ "%s UTC", buf);
hibernate_go_dormant(now);
} else {
log_notice(LD_ACCT,
"Accounting period ended. This period, we will hibernate"
- " until %s GMT",buf);
+ " until %s UTC",buf);
}
}
}
diff --git a/src/or/hibernate.h b/src/or/hibernate.h
index 5f99cde015..d2d6989e10 100644
--- a/src/or/hibernate.h
+++ b/src/or/hibernate.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/include.am b/src/or/include.am
index 01f4784d08..241015488a 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -15,7 +15,14 @@ else
evdns_source=src/ext/eventdns.c
endif
+if CURVE25519_ENABLED
+onion_ntor_source=src/or/onion_ntor.c
+else
+onion_ntor_source=
+endif
+
src_or_libtor_a_SOURCES = \
+ src/or/addressmap.c \
src/or/buffers.c \
src/or/channel.c \
src/or/channeltls.c \
@@ -46,6 +53,8 @@ src_or_libtor_a_SOURCES = \
src/or/networkstatus.c \
src/or/nodelist.c \
src/or/onion.c \
+ src/or/onion_fast.c \
+ src/or/onion_tap.c \
src/or/transports.c \
src/or/policies.c \
src/or/reasons.c \
@@ -64,6 +73,7 @@ src_or_libtor_a_SOURCES = \
src/or/status.c \
$(evdns_source) \
$(tor_platform_source) \
+ $(onion_ntor_source) \
src/or/config_codedigest.c
#libtor_a_LIBADD = ../common/libor.a ../common/libor-crypto.a \
@@ -85,12 +95,14 @@ AM_CPPFLAGS += -DSHARE_DATADIR="\"$(datadir)\"" \
src_or_tor_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@
-src_or_tor_LDADD = src/or/libtor.a src/common/libor.a src/common/libor-crypto.a \
+src_or_tor_LDADD = src/or/libtor.a src/common/libor.a \
+ src/common/libor-crypto.a $(LIBDONNA) \
src/common/libor-event.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
- @TOR_LIB_WS32@ @TOR_LIB_GDI@
+ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
ORHEADERS = \
+ src/or/addressmap.h \
src/or/buffers.h \
src/or/channel.h \
src/or/channeltls.h \
@@ -123,6 +135,9 @@ ORHEADERS = \
src/or/nodelist.h \
src/or/ntmain.h \
src/or/onion.h \
+ src/or/onion_fast.h \
+ src/or/onion_ntor.h \
+ src/or/onion_tap.h \
src/or/or.h \
src/or/transports.h \
src/or/policies.h \
diff --git a/src/or/main.c b/src/or/main.c
index 0ba28db05e..b5d1e2da34 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -12,6 +12,7 @@
#define MAIN_PRIVATE
#include "or.h"
+#include "addressmap.h"
#include "buffers.h"
#include "channel.h"
#include "channeltls.h"
@@ -421,7 +422,7 @@ connection_unlink(connection_t *conn)
void
add_connection_to_closeable_list(connection_t *conn)
{
- tor_assert(!smartlist_isin(closeable_connection_lst, conn));
+ tor_assert(!smartlist_contains(closeable_connection_lst, conn));
tor_assert(conn->marked_for_close);
assert_connection_ok(conn, time(NULL));
smartlist_add(closeable_connection_lst, conn);
@@ -431,14 +432,14 @@ add_connection_to_closeable_list(connection_t *conn)
int
connection_is_on_closeable_list(connection_t *conn)
{
- return smartlist_isin(closeable_connection_lst, conn);
+ return smartlist_contains(closeable_connection_lst, conn);
}
/** Return true iff conn is in the current poll array. */
int
connection_in_array(connection_t *conn)
{
- return smartlist_isin(connection_array, conn);
+ return smartlist_contains(connection_array, conn);
}
/** Set <b>*array</b> to an array of all connections, and <b>*n</b>
@@ -665,7 +666,7 @@ connection_start_reading_from_linked_conn(connection_t *conn)
tor_event_base_loopexit(tor_libevent_get_base(), &tv);
}
} else {
- tor_assert(smartlist_isin(active_linked_connection_lst, conn));
+ tor_assert(smartlist_contains(active_linked_connection_lst, conn));
}
}
@@ -685,7 +686,7 @@ connection_stop_reading_from_linked_conn(connection_t *conn)
* so let's leave it alone for now. */
smartlist_remove(active_linked_connection_lst, conn);
} else {
- tor_assert(!smartlist_isin(active_linked_connection_lst, conn));
+ tor_assert(!smartlist_contains(active_linked_connection_lst, conn));
}
}
@@ -812,7 +813,8 @@ conn_close_if_marked(int i)
}
#endif
- log_debug(LD_NET,"Cleaning up connection (fd %d).",conn->s);
+ log_debug(LD_NET,"Cleaning up connection (fd "TOR_SOCKET_T_FORMAT").",
+ conn->s);
/* If the connection we are about to close was trying to connect to
a proxy server and failed, the client won't be able to use that
@@ -971,7 +973,7 @@ directory_info_has_arrived(time_t now, int from_cache)
if (!router_have_minimum_dir_info()) {
int quiet = from_cache ||
directory_too_idle_to_fetch_descriptors(options, now);
- log(quiet ? LOG_INFO : LOG_NOTICE, LD_DIR,
+ tor_log(quiet ? LOG_INFO : LOG_NOTICE, LD_DIR,
"I learned some more directory information, but not enough to "
"build a circuit: %s", get_dir_info_status_string());
update_all_descriptor_downloads(now);
@@ -1155,7 +1157,6 @@ run_scheduled_events(time_t now)
static time_t time_to_check_v3_certificate = 0;
static time_t time_to_check_listeners = 0;
static time_t time_to_check_descriptor = 0;
- static time_t time_to_check_ipaddress = 0;
static time_t time_to_shrink_memory = 0;
static time_t time_to_try_getting_descriptors = 0;
static time_t time_to_reset_descriptor_failures = 0;
@@ -1199,7 +1200,7 @@ run_scheduled_events(time_t now)
* eventually. */
if (signewnym_is_pending &&
time_of_last_signewnym + MAX_SIGNEWNYM_RATE <= now) {
- log(LOG_INFO, LD_CONTROL, "Honoring delayed NEWNYM request");
+ log_info(LD_CONTROL, "Honoring delayed NEWNYM request");
signewnym_impl(now);
}
@@ -1218,7 +1219,7 @@ 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() & !options->DisableNetwork)
+ if (advertised_server_mode() && !options->DisableNetwork)
router_upload_dir_desc_to_dirservers(0);
}
@@ -1401,11 +1402,10 @@ run_scheduled_events(time_t now)
/** 2. Periodically, we consider force-uploading our descriptor
* (if we've passed our internal checks). */
-/** How often do we check whether part of our router info has changed in a way
- * that would require an upload? */
+/** How often do we check whether part of our router info has changed in a
+ * way that would require an upload? That includes checking whether our IP
+ * address has changed. */
#define CHECK_DESCRIPTOR_INTERVAL (60)
-/** How often do we (as a router) check whether our IP address has changed? */
-#define CHECK_IPADDRESS_INTERVAL (15*60)
/* 2b. Once per minute, regenerate and upload the descriptor if the old
* one is inaccurate. */
@@ -1413,10 +1413,7 @@ run_scheduled_events(time_t now)
static int dirport_reachability_count = 0;
time_to_check_descriptor = now + CHECK_DESCRIPTOR_INTERVAL;
check_descriptor_bandwidth_changed(now);
- if (time_to_check_ipaddress < now) {
- time_to_check_ipaddress = now + CHECK_IPADDRESS_INTERVAL;
- check_descriptor_ipaddress_changed(now);
- }
+ check_descriptor_ipaddress_changed(now);
mark_my_descriptor_dirty_if_too_old(now);
consider_publishable_server(0);
/* also, check religiously for reachability, if it's within the first
@@ -2081,7 +2078,7 @@ process_signal(uintptr_t sig)
time_t now = time(NULL);
if (time_of_last_signewnym + MAX_SIGNEWNYM_RATE > now) {
signewnym_is_pending = 1;
- log(LOG_NOTICE, LD_CONTROL,
+ log_notice(LD_CONTROL,
"Rate limiting NEWNYM request: delaying by %d second(s)",
(int)(MAX_SIGNEWNYM_RATE+time_of_last_signewnym-now));
} else {
@@ -2113,7 +2110,7 @@ static void
dumpmemusage(int severity)
{
connection_dump_buffer_mem_stats(severity);
- log(severity, LD_GENERAL, "In rephist: "U64_FORMAT" used by %d Tors.",
+ tor_log(severity, LD_GENERAL, "In rephist: "U64_FORMAT" used by %d Tors.",
U64_PRINTF_ARG(rephist_total_alloc), rephist_total_num);
dump_routerlist_mem_usage(severity);
dump_cell_pool_usage(severity);
@@ -2131,27 +2128,27 @@ dumpstats(int severity)
time_t elapsed;
size_t rbuf_cap, wbuf_cap, rbuf_len, wbuf_len;
- log(severity, LD_GENERAL, "Dumping stats:");
+ tor_log(severity, LD_GENERAL, "Dumping stats:");
SMARTLIST_FOREACH_BEGIN(connection_array, connection_t *, conn) {
int i = conn_sl_idx;
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
"Conn %d (socket %d) type %d (%s), state %d (%s), created %d secs ago",
i, (int)conn->s, conn->type, conn_type_to_string(conn->type),
conn->state, conn_state_to_string(conn->type, conn->state),
(int)(now - conn->timestamp_created));
if (!connection_is_listener(conn)) {
- log(severity,LD_GENERAL,
+ tor_log(severity,LD_GENERAL,
"Conn %d is to %s:%d.", i,
safe_str_client(conn->address),
conn->port);
- log(severity,LD_GENERAL,
+ tor_log(severity,LD_GENERAL,
"Conn %d: %d bytes waiting on inbuf (len %d, last read %d secs ago)",
i,
(int)connection_get_inbuf_len(conn),
(int)buf_allocation(conn->inbuf),
(int)(now - conn->timestamp_lastread));
- log(severity,LD_GENERAL,
+ tor_log(severity,LD_GENERAL,
"Conn %d: %d bytes waiting on outbuf "
"(len %d, last written %d secs ago)",i,
(int)connection_get_outbuf_len(conn),
@@ -2162,7 +2159,7 @@ dumpstats(int severity)
if (or_conn->tls) {
tor_tls_get_buffer_sizes(or_conn->tls, &rbuf_cap, &rbuf_len,
&wbuf_cap, &wbuf_len);
- log(severity, LD_GENERAL,
+ tor_log(severity, LD_GENERAL,
"Conn %d: %d/%d bytes used on OpenSSL read buffer; "
"%d/%d bytes used on write buffer.",
i, (int)rbuf_len, (int)rbuf_cap, (int)wbuf_len, (int)wbuf_cap);
@@ -2176,7 +2173,7 @@ dumpstats(int severity)
channel_dumpstats(severity);
channel_listener_dumpstats(severity);
- log(severity, LD_NET,
+ tor_log(severity, LD_NET,
"Cells processed: "U64_FORMAT" padding\n"
" "U64_FORMAT" create\n"
" "U64_FORMAT" created\n"
@@ -2192,33 +2189,36 @@ dumpstats(int severity)
U64_PRINTF_ARG(stats_n_relay_cells_delivered),
U64_PRINTF_ARG(stats_n_destroy_cells_processed));
if (stats_n_data_cells_packaged)
- log(severity,LD_NET,"Average packaged cell fullness: %2.3f%%",
+ tor_log(severity,LD_NET,"Average packaged cell fullness: %2.3f%%",
100*(U64_TO_DBL(stats_n_data_bytes_packaged) /
U64_TO_DBL(stats_n_data_cells_packaged*RELAY_PAYLOAD_SIZE)) );
if (stats_n_data_cells_received)
- log(severity,LD_NET,"Average delivered cell fullness: %2.3f%%",
+ tor_log(severity,LD_NET,"Average delivered cell fullness: %2.3f%%",
100*(U64_TO_DBL(stats_n_data_bytes_received) /
U64_TO_DBL(stats_n_data_cells_received*RELAY_PAYLOAD_SIZE)) );
+ cpuworker_log_onionskin_overhead(severity, ONION_HANDSHAKE_TYPE_TAP, "TAP");
+ cpuworker_log_onionskin_overhead(severity, ONION_HANDSHAKE_TYPE_NTOR,"ntor");
+
if (now - time_of_process_start >= 0)
elapsed = now - time_of_process_start;
else
elapsed = 0;
if (elapsed) {
- log(severity, LD_NET,
+ tor_log(severity, LD_NET,
"Average bandwidth: "U64_FORMAT"/%d = %d bytes/sec reading",
U64_PRINTF_ARG(stats_n_bytes_read),
(int)elapsed,
(int) (stats_n_bytes_read/elapsed));
- log(severity, LD_NET,
+ tor_log(severity, LD_NET,
"Average bandwidth: "U64_FORMAT"/%d = %d bytes/sec writing",
U64_PRINTF_ARG(stats_n_bytes_written),
(int)elapsed,
(int) (stats_n_bytes_written/elapsed));
}
- log(severity, LD_NET, "--------------- Dumping memory information:");
+ tor_log(severity, LD_NET, "--------------- Dumping memory information:");
dumpmemusage(severity);
rep_hist_dump_stats(now,severity);
@@ -2359,7 +2359,7 @@ tor_init(int argc, char *argv[])
}
#ifdef NON_ANONYMOUS_MODE_ENABLED
- log(LOG_WARN, LD_GENERAL, "This copy of Tor was compiled to run in a "
+ log_warn(LD_GENERAL, "This copy of Tor was compiled to run in a "
"non-anonymous mode. It will provide NO ANONYMITY.");
#endif
@@ -2386,6 +2386,7 @@ tor_init(int argc, char *argv[])
log_err(LD_BUG, "Unable to initialize OpenSSL. Exiting.");
return -1;
}
+ stream_choice_seed_weak_rng();
return 0;
}
diff --git a/src/or/main.h b/src/or/main.h
index da2bcfd493..338449b6a6 100644
--- a/src/or/main.h
+++ b/src/or/main.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/microdesc.c b/src/or/microdesc.c
index 42a35f0676..ac48930faf 100644
--- a/src/or/microdesc.c
+++ b/src/or/microdesc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2012, The Tor Project, Inc. */
+/* Copyright (c) 2009-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#include "or.h"
@@ -75,7 +75,7 @@ dump_microdescriptor(FILE *f, microdesc_t *md, size_t *annotation_len_out)
{
ssize_t r = 0;
size_t written;
- /* XXXX drops unkown annotations. */
+ /* XXXX drops unknown annotations. */
if (md->last_listed) {
char buf[ISO_TIME_LEN+1];
char annotation[ISO_TIME_LEN+32];
@@ -132,7 +132,7 @@ get_microdesc_cache(void)
*/
/** Decode the microdescriptors from the string starting at <b>s</b> and
- * ending at <b>eos</b>, and store them in <b>cache</b>. If <b>no-save</b>,
+ * ending at <b>eos</b>, and store them in <b>cache</b>. If <b>no_save</b>,
* mark them as non-writable to disk. If <b>where</b> is SAVED_IN_CACHE,
* leave their bodies as pointers to the mmap'd cache. If where is
* <b>SAVED_NOWHERE</b>, do not allow annotations. If listed_at is positive,
@@ -160,7 +160,7 @@ microdescs_add_to_cache(microdesc_cache_t *cache,
md->last_listed = listed_at);
}
if (requested_digests256) {
- digestmap_t *requested; /* XXXX actuqlly we should just use a
+ digestmap_t *requested; /* XXXX actually we should just use a
digest256map */
requested = digestmap_new();
SMARTLIST_FOREACH(requested_digests256, const char *, cp,
@@ -169,7 +169,7 @@ microdescs_add_to_cache(microdesc_cache_t *cache,
if (digestmap_get(requested, md->digest)) {
digestmap_set(requested, md->digest, (void*)2);
} else {
- log_fn(LOG_PROTOCOL_WARN, LD_DIR, "Received non-requested microcdesc");
+ log_fn(LOG_PROTOCOL_WARN, LD_DIR, "Received non-requested microdesc");
microdesc_free(md);
SMARTLIST_DEL_CURRENT(descriptors, md);
}
@@ -188,7 +188,7 @@ microdescs_add_to_cache(microdesc_cache_t *cache,
return added;
}
-/** As microdescs_add_to_cache, but takes a list of micrdescriptors instead of
+/** As microdescs_add_to_cache, but takes a list of microdescriptors instead of
* a string to decode. Frees any members of <b>descriptors</b> that it does
* not add. */
smartlist_t *
@@ -232,7 +232,7 @@ microdescs_add_list_to_cache(microdesc_cache_t *cache,
size_t annotation_len;
size = dump_microdescriptor(f, md, &annotation_len);
if (size < 0) {
- /* we already warned in dump_microdescriptor; */
+ /* we already warned in dump_microdescriptor */
abort_writing_to_file(open_file);
smartlist_clear(added);
return added;
@@ -479,7 +479,7 @@ microdesc_cache_rebuild(microdesc_cache_t *cache, int force)
if (PREDICT_UNLIKELY(
md->bodylen < 9 || fast_memneq(md->body, "onion-key", 9) != 0)) {
/* XXXX once bug 2022 is solved, we can kill this block and turn it
- * into just the tor_assert(!memcmp) */
+ * into just the tor_assert(fast_memeq) */
off_t avail = cache->cache_content->size - md->off;
char *bad_str;
tor_assert(avail >= 0);
@@ -575,6 +575,7 @@ microdesc_free(microdesc_t *md)
if (md->onion_pkey)
crypto_pk_free(md->onion_pkey);
+ tor_free(md->onion_curve25519_pkey);
if (md->body && md->saved_location != SAVED_IN_CACHE)
tor_free(md->body);
@@ -583,6 +584,7 @@ microdesc_free(microdesc_t *md)
smartlist_free(md->family);
}
short_policy_free(md->exit_policy);
+ short_policy_free(md->ipv6_exit_policy);
tor_free(md);
}
@@ -728,9 +730,9 @@ we_use_microdescriptors_for_circuits(const or_options_t *options)
int ret = options->UseMicrodescriptors;
if (ret == -1) {
/* UseMicrodescriptors is "auto"; we need to decide: */
- /* If we are configured to use bridges and one of our bridges doesn't
+ /* If we are configured to use bridges and none of our bridges
* know what a microdescriptor is, the answer is no. */
- if (options->UseBridges && any_bridges_dont_support_microdescriptors())
+ if (options->UseBridges && !any_bridge_supports_microdescriptors())
return 0;
/* Otherwise, we decide that we'll use microdescriptors iff we are
* not a server, and we're not autofetching everything. */
diff --git a/src/or/microdesc.h b/src/or/microdesc.h
index 4b18caaae0..4e58aa33f0 100644
--- a/src/or/microdesc.h
+++ b/src/or/microdesc.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index 89afb5a5c1..71ac054f88 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -219,8 +219,6 @@ router_reload_consensus_networkstatus(void)
{
char *filename;
char *s;
- struct stat st;
- const or_options_t *options = get_options();
const unsigned int flags = NSSET_FROM_CACHE | NSSET_DONT_DOWNLOAD_CERTS;
int flav;
@@ -263,25 +261,6 @@ router_reload_consensus_networkstatus(void)
tor_free(filename);
}
- if (!current_consensus ||
- (stat(options->FallbackNetworkstatusFile, &st)==0 &&
- st.st_mtime > current_consensus->valid_after)) {
- s = read_file_to_str(options->FallbackNetworkstatusFile,
- RFTS_IGNORE_MISSING, NULL);
- if (s) {
- if (networkstatus_set_current_consensus(s, "ns",
- flags|NSSET_ACCEPT_OBSOLETE)) {
- log_info(LD_FS, "Couldn't load consensus networkstatus from \"%s\"",
- options->FallbackNetworkstatusFile);
- } else {
- log_notice(LD_FS,
- "Loaded fallback consensus networkstatus from \"%s\"",
- options->FallbackNetworkstatusFile);
- }
- tor_free(s);
- }
- }
-
if (!current_consensus) {
if (!named_server_map)
named_server_map = strmap_new();
@@ -417,7 +396,7 @@ networkstatus_vote_free(networkstatus_t *ns)
digestmap_free(ns->desc_digest_map, NULL);
- memset(ns, 11, sizeof(*ns));
+ memwipe(ns, 11, sizeof(*ns));
tor_free(ns);
}
@@ -565,7 +544,7 @@ networkstatus_check_consensus_signature(networkstatus_t *consensus,
/* Now see whether we're missing any voters entirely. */
SMARTLIST_FOREACH(router_get_trusted_dir_servers(),
- trusted_dir_server_t *, ds,
+ dir_server_t *, ds,
{
if ((ds->type & V3_DIRINFO) &&
!networkstatus_get_voter_by_id(consensus, ds->v3_identity_digest))
@@ -582,7 +561,7 @@ networkstatus_check_consensus_signature(networkstatus_t *consensus,
if (warn >= 0) {
SMARTLIST_FOREACH(unrecognized, networkstatus_voter_info_t *, voter,
{
- log(severity, LD_DIR, "Consensus includes unrecognized authority "
+ tor_log(severity, LD_DIR, "Consensus includes unrecognized authority "
"'%s' at %s:%d (contact %s; identity %s)",
voter->nickname, voter->address, (int)voter->dir_port,
voter->contact?voter->contact:"n/a",
@@ -590,16 +569,16 @@ networkstatus_check_consensus_signature(networkstatus_t *consensus,
});
SMARTLIST_FOREACH(need_certs_from, networkstatus_voter_info_t *, voter,
{
- log(severity, LD_DIR, "Looks like we need to download a new "
+ tor_log(severity, LD_DIR, "Looks like we need to download a new "
"certificate from authority '%s' at %s:%d (contact %s; "
"identity %s)",
voter->nickname, voter->address, (int)voter->dir_port,
voter->contact?voter->contact:"n/a",
hex_str(voter->identity_digest, DIGEST_LEN));
});
- SMARTLIST_FOREACH(missing_authorities, trusted_dir_server_t *, ds,
+ SMARTLIST_FOREACH(missing_authorities, dir_server_t *, ds,
{
- log(severity, LD_DIR, "Consensus does not include configured "
+ tor_log(severity, LD_DIR, "Consensus does not include configured "
"authority '%s' at %s:%d (identity %s)",
ds->nickname, ds->address, (int)ds->dir_port,
hex_str(ds->v3_identity_digest, DIGEST_LEN));
@@ -635,7 +614,7 @@ networkstatus_check_consensus_signature(networkstatus_t *consensus,
"because we were missing the keys.", n_missing_key);
}
joined = smartlist_join_strings(sl, " ", 0, NULL);
- log(severity, LD_DIR, "%s", joined);
+ tor_log(severity, LD_DIR, "%s", joined);
tor_free(joined);
SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
smartlist_free(sl);
@@ -739,7 +718,7 @@ router_set_networkstatus_v2(const char *s, time_t arrived_at,
int i, found;
time_t now;
int skewed = 0;
- trusted_dir_server_t *trusted_dir = NULL;
+ dir_server_t *trusted_dir = NULL;
const char *source_desc = NULL;
char fp[HEX_DIGEST_LEN+1];
char published[ISO_TIME_LEN+1];
@@ -775,7 +754,7 @@ router_set_networkstatus_v2(const char *s, time_t arrived_at,
long delta = now - ns->published_on;
format_time_interval(dbuf, sizeof(dbuf), delta);
log_warn(LD_GENERAL, "Network status from %s was published %s in the "
- "future (%s GMT). Check your time and date settings! "
+ "future (%s UTC). Check your time and date settings! "
"Not caching.",
source_desc, dbuf, published);
control_event_general_status(LOG_WARN,
@@ -795,7 +774,7 @@ router_set_networkstatus_v2(const char *s, time_t arrived_at,
}
if (requested_fingerprints) {
- if (smartlist_string_isin(requested_fingerprints, fp)) {
+ if (smartlist_contains_string(requested_fingerprints, fp)) {
smartlist_string_remove(requested_fingerprints, fp);
} else {
if (source != NS_FROM_DIR_ALL) {
@@ -1144,7 +1123,7 @@ update_v2_networkstatus_cache_downloads(time_t now)
if (authority) {
/* An authority launches a separate connection for everybody. */
- SMARTLIST_FOREACH_BEGIN(trusted_dir_servers, trusted_dir_server_t *, ds)
+ SMARTLIST_FOREACH_BEGIN(trusted_dir_servers, dir_server_t *, ds)
{
char resource[HEX_DIGEST_LEN+6]; /* fp/hexdigit.z\0 */
tor_addr_t addr;
@@ -1674,9 +1653,6 @@ networkstatus_set_current_consensus(const char *consensus,
if (from_cache && !accept_obsolete &&
c->valid_until < now-OLD_ROUTER_DESC_MAX_AGE) {
- /* XXXX If we try to make fallbackconsensus work again, we should
- * consider taking this out. Until then, believing obsolete consensuses
- * is causing more harm than good. See also bug 887. */
log_info(LD_DIR, "Loaded an expired consensus. Discarding.");
goto done;
}
@@ -1874,7 +1850,7 @@ networkstatus_set_current_consensus(const char *consensus,
format_iso_time(tbuf, c->valid_after);
format_time_interval(dbuf, sizeof(dbuf), delta);
log_warn(LD_GENERAL, "Our clock is %s behind the time published in the "
- "consensus network status document (%s GMT). Tor needs an "
+ "consensus network status document (%s UTC). Tor needs an "
"accurate clock to work correctly. Please check your time and "
"date settings!", dbuf, tbuf);
control_event_general_status(LOG_WARN,
@@ -2042,7 +2018,7 @@ void
routers_update_status_from_consensus_networkstatus(smartlist_t *routers,
int reset_failures)
{
- trusted_dir_server_t *ds;
+ dir_server_t *ds;
const or_options_t *options = get_options();
int authdir = authdir_mode_v2(options) || authdir_mode_v3(options);
networkstatus_t *ns = current_consensus;
@@ -2062,7 +2038,7 @@ routers_update_status_from_consensus_networkstatus(smartlist_t *routers,
/* We have a routerstatus for this router. */
const char *digest = router->cache_info.identity_digest;
- ds = router_get_trusteddirserver_by_digest(digest);
+ ds = router_get_fallback_dirserver_by_digest(digest);
/* Is it the same descriptor, or only the same identity? */
if (tor_memeq(router->cache_info.signed_descriptor_digest,
@@ -2263,6 +2239,21 @@ networkstatus_get_param(const networkstatus_t *ns, const char *param_name,
default_val, min_val, max_val);
}
+/**
+ * Retrieve the consensus parameter that governs the
+ * fixed-point precision of our network balancing 'bandwidth-weights'
+ * (which are themselves integer consensus values). We divide them
+ * by this value and ensure they never exceed this value.
+ */
+int
+networkstatus_get_weight_scale_param(networkstatus_t *ns)
+{
+ return networkstatus_get_param(ns, "bwweightscale",
+ BW_WEIGHT_SCALE,
+ BW_MIN_WEIGHT_SCALE,
+ BW_MAX_WEIGHT_SCALE);
+}
+
/** Return the value of a integer bw weight parameter from the networkstatus
* <b>ns</b> whose name is <b>weight_name</b>. If <b>ns</b> is NULL, try
* loading the latest consensus ourselves. Return <b>default_val</b> if no
@@ -2279,7 +2270,7 @@ networkstatus_get_bw_weight(networkstatus_t *ns, const char *weight_name,
if (!ns || !ns->weight_params)
return default_val;
- max = circuit_build_times_get_bw_scale(ns);
+ max = networkstatus_get_weight_scale_param(ns);
param = get_net_param_from_list(ns->weight_params, weight_name,
default_val, -1,
BW_MAX_WEIGHT_SCALE);
@@ -2374,8 +2365,11 @@ getinfo_helper_networkstatus(control_connection_t *conn,
return 0;
} else if (!strcmpstart(question, "ns/id/")) {
char d[DIGEST_LEN];
+ const char *q = question + 6;
+ if (*q == '$')
+ ++q;
- if (base16_decode(d, DIGEST_LEN, question+6, strlen(question+6))) {
+ if (base16_decode(d, DIGEST_LEN, q, strlen(q))) {
*errmsg = "Data not decodeable as hex";
return -1;
}
diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h
index fe16f33918..b437c5ec2f 100644
--- a/src/or/networkstatus.h
+++ b/src/or/networkstatus.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -112,6 +112,7 @@ int networkstatus_parse_flavor_name(const char *flavname);
void document_signature_free(document_signature_t *sig);
document_signature_t *document_signature_dup(const document_signature_t *sig);
void networkstatus_free_all(void);
+int networkstatus_get_weight_scale_param(networkstatus_t *ns);
#endif
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
index 5a726377ec..ee1bc392e3 100644
--- a/src/or/nodelist.c
+++ b/src/or/nodelist.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#include "or.h"
@@ -624,7 +624,7 @@ node_is_dir(const node_t *node)
}
/** Return true iff <b>node</b> has either kind of usable descriptor -- that
- * is, a routerdecriptor or a microdescriptor. */
+ * is, a routerdescriptor or a microdescriptor. */
int
node_has_descriptor(const node_t *node)
{
@@ -916,6 +916,18 @@ node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out)
}
}
+/** Return true iff <b>node</b> has a curve25519 onion key. */
+int
+node_has_curve25519_onion_key(const node_t *node)
+{
+ if (node->ri)
+ return node->ri->onion_curve25519_pkey != NULL;
+ else if (node->md)
+ return node->md->onion_curve25519_pkey != NULL;
+ else
+ return 0;
+}
+
/** Refresh the country code of <b>ri</b>. This function MUST be called on
* each router when the GeoIP database is reloaded, and on all new routers. */
void
@@ -1139,7 +1151,7 @@ node_is_unreliable(const node_t *node, int need_uptime,
}
/** Return 1 if all running sufficiently-stable routers we can use will reject
- * addr:port, return 0 if any might accept it. */
+ * addr:port. Return 0 if any might accept it. */
int
router_exit_policy_all_nodes_reject(const tor_addr_t *addr, uint16_t port,
int need_uptime)
@@ -1167,8 +1179,13 @@ router_set_status(const char *digest, int up)
node_t *node;
tor_assert(digest);
+ SMARTLIST_FOREACH(router_get_fallback_dir_servers(),
+ dir_server_t *, d,
+ if (tor_memeq(d->digest, digest, DIGEST_LEN))
+ d->is_running = up);
+
SMARTLIST_FOREACH(router_get_trusted_dir_servers(),
- trusted_dir_server_t *, d,
+ dir_server_t *, d,
if (tor_memeq(d->digest, digest, DIGEST_LEN))
d->is_running = up);
@@ -1196,7 +1213,7 @@ static int have_min_dir_info = 0;
static int need_to_update_have_min_dir_info = 1;
/** String describing what we're missing before we have enough directory
* info. */
-static char dir_info_status[128] = "";
+static char dir_info_status[256] = "";
/** Return true iff we have enough networkstatus and router information to
* start building circuits. Right now, this means "more than half the
@@ -1236,10 +1253,12 @@ get_dir_info_status_string(void)
* descriptors for. Store the former in *<b>num_usable</b> and the latter in
* *<b>num_present</b>. If <b>in_set</b> is non-NULL, only consider those
* routers in <b>in_set</b>. If <b>exit_only</b> is true, only consider nodes
- * with the Exit flag.
+ * with the Exit flag. If *descs_out is present, add a node_t for each
+ * usable descriptor to it.
*/
static void
count_usable_descriptors(int *num_present, int *num_usable,
+ smartlist_t *descs_out,
const networkstatus_t *consensus,
const or_options_t *options, time_t now,
routerset_t *in_set, int exit_only)
@@ -1249,6 +1268,10 @@ count_usable_descriptors(int *num_present, int *num_usable,
SMARTLIST_FOREACH_BEGIN(consensus->routerstatus_list, routerstatus_t *, rs)
{
+ const node_t *node = node_get_by_id(rs->identity_digest);
+ if (!node)
+ continue; /* This would be a bug: every entry in the consensus is
+ * supposed to have a node. */
if (exit_only && ! rs->is_exit)
continue;
if (in_set && ! routerset_contains_routerstatus(in_set, rs, -1))
@@ -1265,12 +1288,81 @@ count_usable_descriptors(int *num_present, int *num_usable,
/* we have the descriptor listed in the consensus. */
++*num_present;
}
+ if (descs_out)
+ smartlist_add(descs_out, (node_t*)node);
}
}
SMARTLIST_FOREACH_END(rs);
- log_debug(LD_DIR, "%d usable, %d present (%s).", *num_usable, *num_present,
- md ? "microdescs" : "descs");
+ log_debug(LD_DIR, "%d usable, %d present (%s%s).",
+ *num_usable, *num_present,
+ md ? "microdesc" : "desc", exit_only ? " exits" : "s");
+}
+
+/** Return an extimate of which fraction of usable paths through the Tor
+ * network we have available for use. */
+static double
+compute_frac_paths_available(const networkstatus_t *consensus,
+ const or_options_t *options, time_t now,
+ int *num_present_out, int *num_usable_out,
+ char **status_out)
+{
+ smartlist_t *guards = smartlist_new();
+ smartlist_t *mid = smartlist_new();
+ smartlist_t *exits = smartlist_new();
+ smartlist_t *myexits= smartlist_new();
+ double f_guard, f_mid, f_exit, f_myexit;
+ int np, nu; /* Ignored */
+ const int authdir = authdir_mode_v2(options) || authdir_mode_v3(options);
+
+ count_usable_descriptors(num_present_out, num_usable_out,
+ mid, consensus, options, now, NULL, 0);
+ if (options->EntryNodes) {
+ count_usable_descriptors(&np, &nu, guards, consensus, options, now,
+ options->EntryNodes, 0);
+ } else {
+ SMARTLIST_FOREACH(mid, const node_t *, node, {
+ if (authdir) {
+ if (node->rs && node->rs->is_possible_guard)
+ smartlist_add(guards, (node_t*)node);
+ } else {
+ if (node->is_possible_guard)
+ smartlist_add(guards, (node_t*)node);
+ }
+ });
+ }
+
+ count_usable_descriptors(&np, &nu, exits, consensus, options, now,
+ NULL, 1);
+ count_usable_descriptors(&np, &nu, myexits, consensus, options, now,
+ options->ExitNodes, 1);
+
+ f_guard = frac_nodes_with_descriptors(guards, WEIGHT_FOR_GUARD);
+ f_mid = frac_nodes_with_descriptors(mid, WEIGHT_FOR_MID);
+ f_exit = frac_nodes_with_descriptors(exits, WEIGHT_FOR_EXIT);
+ f_myexit= frac_nodes_with_descriptors(myexits,WEIGHT_FOR_EXIT);
+
+ smartlist_free(guards);
+ smartlist_free(mid);
+ smartlist_free(exits);
+ smartlist_free(myexits);
+
+ /* This is a tricky point here: we don't want to make it easy for a
+ * directory to trickle exits to us until it learns which exits we have
+ * configured, so require that we have a threshold both of total exits
+ * and usable exits. */
+ if (f_myexit < f_exit)
+ f_exit = f_myexit;
+
+ tor_asprintf(status_out,
+ "%d%% of guards bw, "
+ "%d%% of midpoint bw, and "
+ "%d%% of exit bw",
+ (int)(f_guard*100),
+ (int)(f_mid*100),
+ (int)(f_exit*100));
+
+ return f_guard * f_mid * f_exit;
}
/** We just fetched a new set of descriptors. Compute how far through
@@ -1288,7 +1380,7 @@ count_loading_descriptors_progress(void)
if (!consensus)
return 0; /* can't count descriptors if we have no list of them */
- count_usable_descriptors(&num_present, &num_usable,
+ count_usable_descriptors(&num_present, &num_usable, NULL,
consensus, get_options(), now, NULL, 0);
if (num_usable == 0)
@@ -1301,14 +1393,28 @@ count_loading_descriptors_progress(void)
BOOTSTRAP_STATUS_LOADING_DESCRIPTORS));
}
+/** Return the fraction of paths needed before we're willing to build
+ * circuits, as configured in <b>options</b>, or in the consensus <b>ns</b>. */
+static double
+get_frac_paths_needed_for_circs(const or_options_t *options,
+ const networkstatus_t *ns)
+{
+#define DFLT_PCT_USABLE_NEEDED 60
+ if (options->PathsNeededToBuildCircuits >= 1.0) {
+ return options->PathsNeededToBuildCircuits;
+ } else {
+ return networkstatus_get_param(ns, "min_paths_for_circs_pct",
+ DFLT_PCT_USABLE_NEEDED,
+ 25, 95)/100.0;
+ }
+}
+
/** Change the value of have_min_dir_info, setting it true iff we have enough
* network and router information to build circuits. Clear the value of
* need_to_update_have_min_dir_info. */
static void
update_router_have_minimum_dir_info(void)
{
- int num_present = 0, num_usable=0;
- int num_exit_present = 0, num_exit_usable = 0;
time_t now = time(NULL);
int res;
const or_options_t *options = get_options();
@@ -1337,66 +1443,40 @@ update_router_have_minimum_dir_info(void)
using_md = consensus->flavor == FLAV_MICRODESC;
- count_usable_descriptors(&num_present, &num_usable, consensus, options, now,
- NULL, 0);
- count_usable_descriptors(&num_exit_present, &num_exit_usable,
- consensus, options, now, options->ExitNodes, 1);
-
-/* What fraction of desired server descriptors do we need before we will
- * build circuits? */
-#define FRAC_USABLE_NEEDED .75
-/* What fraction of desired _exit_ server descriptors do we need before we
- * will build circuits? */
-#define FRAC_EXIT_USABLE_NEEDED .5
-
- if (num_present < num_usable * FRAC_USABLE_NEEDED) {
- tor_snprintf(dir_info_status, sizeof(dir_info_status),
- "We have only %d/%d usable %sdescriptors.",
- num_present, num_usable, using_md ? "micro" : "");
- res = 0;
- control_event_bootstrap(BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS, 0);
- goto done;
- } else if (num_present < 2) {
- tor_snprintf(dir_info_status, sizeof(dir_info_status),
- "Only %d %sdescriptor%s here and believed reachable!",
- num_present, using_md ? "micro" : "", num_present ? "" : "s");
- res = 0;
- goto done;
- } else if (num_exit_present < num_exit_usable * FRAC_EXIT_USABLE_NEEDED) {
- tor_snprintf(dir_info_status, sizeof(dir_info_status),
- "We have only %d/%d usable exit node descriptors.",
- num_exit_present, num_exit_usable);
- res = 0;
- control_event_bootstrap(BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS, 0);
- goto done;
- }
-
- /* Check for entry nodes. */
- if (options->EntryNodes) {
- count_usable_descriptors(&num_present, &num_usable, consensus, options,
- now, options->EntryNodes, 0);
+ {
+ char *status = NULL;
+ int num_present=0, num_usable=0;
+ double paths = compute_frac_paths_available(consensus, options, now,
+ &num_present, &num_usable,
+ &status);
- if (!num_usable || !num_present) {
+ if (paths < get_frac_paths_needed_for_circs(options,consensus)) {
tor_snprintf(dir_info_status, sizeof(dir_info_status),
- "We have only %d/%d usable entry node %sdescriptors.",
- num_present, num_usable, using_md?"micro":"");
+ "We need more %sdescriptors: we have %d/%d, and "
+ "can only build %d%% of likely paths. (We have %s.)",
+ using_md?"micro":"", num_present, num_usable,
+ (int)(paths*100), status);
+ /* log_notice(LD_NET, "%s", dir_info_status); */
+ tor_free(status);
res = 0;
+ control_event_bootstrap(BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS, 0);
goto done;
}
- }
- res = 1;
+ tor_free(status);
+ res = 1;
+ }
done:
if (res && !have_min_dir_info) {
- log(LOG_NOTICE, LD_DIR,
+ log_notice(LD_DIR,
"We now have enough directory information to build circuits.");
control_event_client_status(LOG_NOTICE, "ENOUGH_DIR_INFO");
control_event_bootstrap(BOOTSTRAP_STATUS_CONN_OR, 0);
}
if (!res && have_min_dir_info) {
int quiet = directory_too_idle_to_fetch_descriptors(options, now);
- log(quiet ? LOG_INFO : LOG_NOTICE, LD_DIR,
+ tor_log(quiet ? LOG_INFO : LOG_NOTICE, LD_DIR,
"Our directory information is no longer up-to-date "
"enough to build circuits: %s", dir_info_status);
diff --git a/src/or/nodelist.h b/src/or/nodelist.h
index 13a3847414..65cf0d0284 100644
--- a/src/or/nodelist.h
+++ b/src/or/nodelist.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -54,6 +54,7 @@ int node_ipv6_preferred(const node_t *node);
int node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out);
void node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out);
void node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out);
+int node_has_curve25519_onion_key(const node_t *node);
smartlist_t *nodelist_get_list(void);
diff --git a/src/or/ntmain.c b/src/or/ntmain.c
index d001f7be13..8b67b86822 100644
--- a/src/or/ntmain.c
+++ b/src/or/ntmain.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#define MAIN_PRIVATE
diff --git a/src/or/ntmain.h b/src/or/ntmain.h
index 95a835a42e..d3027936cd 100644
--- a/src/or/ntmain.h
+++ b/src/or/ntmain.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/onion.c b/src/or/onion.c
index 17d8e777ad..d4a65022fc 100644
--- a/src/or/onion.c
+++ b/src/or/onion.c
@@ -1,47 +1,99 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file onion.c
- * \brief Functions to queue create cells, and handle onionskin
- * parsing and creation.
+ * \brief Functions to queue create cells, wrap the various onionskin types,
+ * and parse and create the CREATE cell and its allies.
**/
#include "or.h"
#include "circuitlist.h"
#include "config.h"
+#include "cpuworker.h"
#include "onion.h"
+#include "onion_fast.h"
+#include "onion_ntor.h"
+#include "onion_tap.h"
+#include "relay.h"
#include "rephist.h"
+#include "router.h"
+#include "tor_queue.h"
/** Type for a linked list of circuits that are waiting for a free CPU worker
* to process a waiting onion handshake. */
typedef struct onion_queue_t {
+ TOR_TAILQ_ENTRY(onion_queue_t) next;
or_circuit_t *circ;
- char *onionskin;
+ create_cell_t *onionskin;
time_t when_added;
- struct onion_queue_t *next;
} onion_queue_t;
/** 5 seconds on the onion queue til we just send back a destroy */
#define ONIONQUEUE_WAIT_CUTOFF 5
-/** First and last elements in the linked list of circuits waiting for CPU
- * workers, or NULL if the list is empty.
- * @{ */
-static onion_queue_t *ol_list=NULL;
-static onion_queue_t *ol_tail=NULL;
-/**@}*/
-/** Length of ol_list */
-static int ol_length=0;
+/** Queue of circuits waiting for CPU workers, or NULL if the list is empty.*/
+TOR_TAILQ_HEAD(onion_queue_head_t, onion_queue_t) ol_list =
+ TOR_TAILQ_HEAD_INITIALIZER(ol_list);
+
+/** Number of entries of each type currently in ol_list. */
+static int ol_entries[MAX_ONION_HANDSHAKE_TYPE+1];
+
+static void onion_queue_entry_remove(onion_queue_t *victim);
+
+/* XXXX024 Check lengths vs MAX_ONIONSKIN_{CHALLENGE,REPLY}_LEN.
+ *
+ * (By which I think I meant, "make sure that no
+ * X_ONIONSKIN_CHALLENGE/REPLY_LEN is greater than
+ * MAX_ONIONSKIN_CHALLENGE/REPLY_LEN." Also, make sure that we can pass
+ * over-large values via EXTEND2/EXTENDED2, for future-compatibility.*/
+
+/** Return true iff we have room to queue another oninoskin of type
+ * <b>type</b>. */
+static int
+have_room_for_onionskin(uint16_t type)
+{
+ const or_options_t *options = get_options();
+ int num_cpus;
+ uint64_t tap_usec, ntor_usec;
+ /* If we've got fewer than 50 entries, we always have room for one more. */
+ if (ol_entries[ONION_HANDSHAKE_TYPE_TAP] +
+ ol_entries[ONION_HANDSHAKE_TYPE_NTOR] < 50)
+ return 1;
+ num_cpus = get_num_cpus(options);
+ /* Compute how many microseconds we'd expect to need to clear all
+ * onionskins in the current queue. */
+ tap_usec = estimated_usec_for_onionskins(
+ ol_entries[ONION_HANDSHAKE_TYPE_TAP],
+ ONION_HANDSHAKE_TYPE_TAP) / num_cpus;
+ ntor_usec = estimated_usec_for_onionskins(
+ ol_entries[ONION_HANDSHAKE_TYPE_NTOR],
+ ONION_HANDSHAKE_TYPE_NTOR) / num_cpus;
+ /* See whether that exceeds MaxOnionQueueDelay. If so, we can't queue
+ * this. */
+ if ((tap_usec + ntor_usec) / 1000 > (uint64_t)options->MaxOnionQueueDelay)
+ return 0;
+#ifdef CURVE25519_ENABLED
+ /* If we support the ntor handshake, then don't let TAP handshakes use
+ * more than 2/3 of the space on the queue. */
+ if (type == ONION_HANDSHAKE_TYPE_TAP &&
+ tap_usec / 1000 > (uint64_t)options->MaxOnionQueueDelay * 2 / 3)
+ return 0;
+#else
+ (void) type;
+#endif
+
+ return 1;
+}
/** Add <b>circ</b> to the end of ol_list and return 0, except
* if ol_list is too long, in which case do nothing and return -1.
*/
int
-onion_pending_add(or_circuit_t *circ, char *onionskin)
+onion_pending_add(or_circuit_t *circ, create_cell_t *onionskin)
{
onion_queue_t *tmp;
time_t now = time(NULL);
@@ -51,19 +103,7 @@ onion_pending_add(or_circuit_t *circ, char *onionskin)
tmp->onionskin = onionskin;
tmp->when_added = now;
- if (!ol_tail) {
- tor_assert(!ol_list);
- tor_assert(!ol_length);
- ol_list = tmp;
- ol_tail = tmp;
- ol_length++;
- return 0;
- }
-
- tor_assert(ol_list);
- tor_assert(!ol_tail->next);
-
- if (ol_length >= get_options()->MaxOnionsPending) {
+ if (!have_room_for_onionskin(onionskin->handshake_type)) {
#define WARN_TOO_MANY_CIRC_CREATIONS_INTERVAL (60)
static ratelim_t last_warned =
RATELIM_INIT(WARN_TOO_MANY_CIRC_CREATIONS_INTERVAL);
@@ -80,13 +120,19 @@ onion_pending_add(or_circuit_t *circ, char *onionskin)
return -1;
}
- ol_length++;
- ol_tail->next = tmp;
- ol_tail = tmp;
- while ((int)(now - ol_list->when_added) >= ONIONQUEUE_WAIT_CUTOFF) {
- /* cull elderly requests. */
- circ = ol_list->circ;
- onion_pending_remove(ol_list->circ);
+ ++ol_entries[onionskin->handshake_type];
+ circ->onionqueue_entry = tmp;
+ TOR_TAILQ_INSERT_TAIL(&ol_list, tmp, next);
+
+ /* cull elderly requests. */
+ while (1) {
+ onion_queue_t *head = TOR_TAILQ_FIRST(&ol_list);
+ if (now - head->when_added < (time_t)ONIONQUEUE_WAIT_CUTOFF)
+ break;
+
+ circ = head->circ;
+ circ->onionqueue_entry = NULL;
+ onion_queue_entry_remove(head);
log_info(LD_CIRC,
"Circuit create request is too old; canceling due to overload.");
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT);
@@ -98,20 +144,24 @@ onion_pending_add(or_circuit_t *circ, char *onionskin)
* NULL if the list is empty.
*/
or_circuit_t *
-onion_next_task(char **onionskin_out)
+onion_next_task(create_cell_t **onionskin_out)
{
or_circuit_t *circ;
+ onion_queue_t *head = TOR_TAILQ_FIRST(&ol_list);
- if (!ol_list)
+ if (!head)
return NULL; /* no onions pending, we're done */
- tor_assert(ol_list->circ);
- tor_assert(ol_list->circ->p_chan); /* make sure it's still valid */
- tor_assert(ol_length > 0);
- circ = ol_list->circ;
- *onionskin_out = ol_list->onionskin;
- ol_list->onionskin = NULL; /* prevent free. */
- onion_pending_remove(ol_list->circ);
+ tor_assert(head->circ);
+ tor_assert(head->circ->p_chan); /* make sure it's still valid */
+ circ = head->circ;
+ if (head->onionskin &&
+ head->onionskin->handshake_type <= MAX_ONION_HANDSHAKE_TYPE)
+ --ol_entries[head->onionskin->handshake_type];
+ *onionskin_out = head->onionskin;
+ head->onionskin = NULL; /* prevent free. */
+ circ->onionqueue_entry = NULL;
+ onion_queue_entry_remove(head);
return circ;
}
@@ -121,328 +171,869 @@ onion_next_task(char **onionskin_out)
void
onion_pending_remove(or_circuit_t *circ)
{
- onion_queue_t *tmpo, *victim;
-
- if (!ol_list)
- return; /* nothing here. */
-
- /* first check to see if it's the first entry */
- tmpo = ol_list;
- if (tmpo->circ == circ) {
- /* it's the first one. remove it from the list. */
- ol_list = tmpo->next;
- if (!ol_list)
- ol_tail = NULL;
- ol_length--;
- victim = tmpo;
- } else { /* we need to hunt through the rest of the list */
- for ( ;tmpo->next && tmpo->next->circ != circ; tmpo=tmpo->next) ;
- if (!tmpo->next) {
- log_debug(LD_GENERAL,
- "circ (p_circ_id %d) not in list, probably at cpuworker.",
- circ->p_circ_id);
- return;
- }
- /* now we know tmpo->next->circ == circ */
- victim = tmpo->next;
- tmpo->next = victim->next;
- if (ol_tail == victim)
- ol_tail = tmpo;
- ol_length--;
- }
+ onion_queue_t *victim;
- /* now victim points to the element that needs to be removed */
+ if (!circ)
+ return;
+
+ victim = circ->onionqueue_entry;
+ if (victim)
+ onion_queue_entry_remove(victim);
+}
+
+/** Remove a queue entry <b>victim</b> from the queue, unlinking it from
+ * its circuit and freeing it and any structures it owns.*/
+static void
+onion_queue_entry_remove(onion_queue_t *victim)
+{
+ TOR_TAILQ_REMOVE(&ol_list, victim, next);
+
+ if (victim->circ)
+ victim->circ->onionqueue_entry = NULL;
+
+ if (victim->onionskin &&
+ victim->onionskin->handshake_type <= MAX_ONION_HANDSHAKE_TYPE)
+ --ol_entries[victim->onionskin->handshake_type];
tor_free(victim->onionskin);
tor_free(victim);
}
-/*----------------------------------------------------------------------*/
+/** Remove all circuits from the pending list. Called from tor_free_all. */
+void
+clear_pending_onions(void)
+{
+ onion_queue_t *victim;
+ while ((victim = TOR_TAILQ_FIRST(&ol_list))) {
+ onion_queue_entry_remove(victim);
+ }
+ memset(ol_entries, 0, sizeof(ol_entries));
+}
-/** Given a router's 128 byte public key,
- * stores the following in onion_skin_out:
- * - [42 bytes] OAEP padding
- * - [16 bytes] Symmetric key for encrypting blob past RSA
- * - [70 bytes] g^x part 1 (inside the RSA)
- * - [58 bytes] g^x part 2 (symmetrically encrypted)
- *
- * Stores the DH private key into handshake_state_out for later completion
- * of the handshake.
- *
- * The meeting point/cookies and auth are zeroed out for now.
+/* ============================================================ */
+
+/** Fill in a server_onion_keys_t object at <b>keys</b> with all of the keys
+ * and other info we might need to do onion handshakes. (We make a copy of
+ * our keys for each cpuworker to avoid race conditions with the main thread,
+ * and to avoid locking) */
+void
+setup_server_onion_keys(server_onion_keys_t *keys)
+{
+ memset(keys, 0, sizeof(server_onion_keys_t));
+ memcpy(keys->my_identity, router_get_my_id_digest(), DIGEST_LEN);
+ dup_onion_keys(&keys->onion_key, &keys->last_onion_key);
+#ifdef CURVE25519_ENABLED
+ keys->curve25519_key_map = construct_ntor_key_map();
+ keys->junk_keypair = tor_malloc_zero(sizeof(curve25519_keypair_t));
+ curve25519_keypair_generate(keys->junk_keypair, 0);
+#endif
+}
+
+/** Release all storage held in <b>keys</b>, but do not free <b>keys</b>
+ * itself (as it's likely to be stack-allocated.) */
+void
+release_server_onion_keys(server_onion_keys_t *keys)
+{
+ if (! keys)
+ return;
+
+ crypto_pk_free(keys->onion_key);
+ crypto_pk_free(keys->last_onion_key);
+#ifdef CURVE25519_ENABLED
+ ntor_key_map_free(keys->curve25519_key_map);
+ tor_free(keys->junk_keypair);
+#endif
+ memset(keys, 0, sizeof(server_onion_keys_t));
+}
+
+/** Release whatever storage is held in <b>state</b>, depending on its
+ * type, and clear its pointer. */
+void
+onion_handshake_state_release(onion_handshake_state_t *state)
+{
+ switch (state->tag) {
+ case ONION_HANDSHAKE_TYPE_TAP:
+ crypto_dh_free(state->u.tap);
+ state->u.tap = NULL;
+ break;
+ case ONION_HANDSHAKE_TYPE_FAST:
+ fast_handshake_state_free(state->u.fast);
+ state->u.fast = NULL;
+ break;
+#ifdef CURVE25519_ENABLED
+ case ONION_HANDSHAKE_TYPE_NTOR:
+ ntor_handshake_state_free(state->u.ntor);
+ state->u.ntor = NULL;
+ break;
+#endif
+ default:
+ log_warn(LD_BUG, "called with unknown handshake state type %d",
+ (int)state->tag);
+ tor_fragile_assert();
+ }
+}
+
+/** Perform the first step of a circuit-creation handshake of type <b>type</b>
+ * (one of ONION_HANDSHAKE_TYPE_*): generate the initial "onion skin" in
+ * <b>onion_skin_out</b>, and store any state information in <b>state_out</b>.
+ * Return -1 on failure, and the length of the onionskin on acceptance.
*/
int
-onion_skin_create(crypto_pk_t *dest_router_key,
- crypto_dh_t **handshake_state_out,
- char *onion_skin_out) /* ONIONSKIN_CHALLENGE_LEN bytes */
+onion_skin_create(int type,
+ const extend_info_t *node,
+ onion_handshake_state_t *state_out,
+ uint8_t *onion_skin_out)
{
- char challenge[DH_KEY_LEN];
- crypto_dh_t *dh = NULL;
- int dhbytes, pkbytes;
-
- tor_assert(dest_router_key);
- tor_assert(handshake_state_out);
- tor_assert(onion_skin_out);
- *handshake_state_out = NULL;
- memset(onion_skin_out, 0, ONIONSKIN_CHALLENGE_LEN);
+ int r = -1;
- if (!(dh = crypto_dh_new(DH_TYPE_CIRCUIT)))
- goto err;
+ switch (type) {
+ case ONION_HANDSHAKE_TYPE_TAP:
+ if (!node->onion_key)
+ return -1;
- dhbytes = crypto_dh_get_bytes(dh);
- pkbytes = (int) crypto_pk_keysize(dest_router_key);
- tor_assert(dhbytes == 128);
- tor_assert(pkbytes == 128);
+ if (onion_skin_TAP_create(node->onion_key,
+ &state_out->u.tap,
+ (char*)onion_skin_out) < 0)
+ return -1;
- if (crypto_dh_get_public(dh, challenge, dhbytes))
- goto err;
+ r = TAP_ONIONSKIN_CHALLENGE_LEN;
+ break;
+ case ONION_HANDSHAKE_TYPE_FAST:
+ if (fast_onionskin_create(&state_out->u.fast, onion_skin_out) < 0)
+ return -1;
- note_crypto_pk_op(ENC_ONIONSKIN);
+ r = CREATE_FAST_LEN;
+ break;
+ case ONION_HANDSHAKE_TYPE_NTOR:
+#ifdef CURVE25519_ENABLED
+ if (tor_mem_is_zero((const char*)node->curve25519_onion_key.public_key,
+ CURVE25519_PUBKEY_LEN))
+ return -1;
+ if (onion_skin_ntor_create((const uint8_t*)node->identity_digest,
+ &node->curve25519_onion_key,
+ &state_out->u.ntor,
+ onion_skin_out) < 0)
+ return -1;
- /* set meeting point, meeting cookie, etc here. Leave zero for now. */
- if (crypto_pk_public_hybrid_encrypt(dest_router_key, onion_skin_out,
- ONIONSKIN_CHALLENGE_LEN,
- challenge, DH_KEY_LEN,
- PK_PKCS1_OAEP_PADDING, 1)<0)
- goto err;
+ r = NTOR_ONIONSKIN_LEN;
+#else
+ return -1;
+#endif
+ break;
+ default:
+ log_warn(LD_BUG, "called with unknown handshake state type %d", type);
+ tor_fragile_assert();
+ r = -1;
+ }
- memset(challenge, 0, sizeof(challenge));
- *handshake_state_out = dh;
+ if (r > 0)
+ state_out->tag = (uint16_t) type;
- return 0;
- err:
- memset(challenge, 0, sizeof(challenge));
- if (dh) crypto_dh_free(dh);
- return -1;
+ return r;
}
-/** Given an encrypted DH public key as generated by onion_skin_create,
- * and the private key for this onion router, generate the reply (128-byte
- * DH plus the first 20 bytes of shared key material), and store the
- * next key_out_len bytes of key material in key_out.
+/** Perform the second (server-side) step of a circuit-creation handshake of
+ * type <b>type</b>, responding to the client request in <b>onion_skin</b>
+ * using the keys in <b>keys</b>. On success, write our response into
+ * <b>reply_out</b>, generate <b>keys_out_len</b> bytes worth of key material
+ * in <b>keys_out_len</b>, a hidden service nonce to <b>rend_nonce_out</b>,
+ * and return the length of the reply. On failure, return -1.
*/
int
-onion_skin_server_handshake(const char *onion_skin, /*ONIONSKIN_CHALLENGE_LEN*/
- crypto_pk_t *private_key,
- crypto_pk_t *prev_private_key,
- char *handshake_reply_out, /*ONIONSKIN_REPLY_LEN*/
- char *key_out,
- size_t key_out_len)
+onion_skin_server_handshake(int type,
+ const uint8_t *onion_skin, size_t onionskin_len,
+ const server_onion_keys_t *keys,
+ uint8_t *reply_out,
+ uint8_t *keys_out, size_t keys_out_len,
+ uint8_t *rend_nonce_out)
{
- char challenge[ONIONSKIN_CHALLENGE_LEN];
- crypto_dh_t *dh = NULL;
- ssize_t len;
- char *key_material=NULL;
- size_t key_material_len=0;
- int i;
- crypto_pk_t *k;
-
- len = -1;
- for (i=0;i<2;++i) {
- k = i==0?private_key:prev_private_key;
- if (!k)
- break;
- note_crypto_pk_op(DEC_ONIONSKIN);
- len = crypto_pk_private_hybrid_decrypt(k, challenge,
- ONIONSKIN_CHALLENGE_LEN,
- onion_skin, ONIONSKIN_CHALLENGE_LEN,
- PK_PKCS1_OAEP_PADDING,0);
- if (len>0)
- break;
+ int r = -1;
+
+ switch (type) {
+ case ONION_HANDSHAKE_TYPE_TAP:
+ if (onionskin_len != TAP_ONIONSKIN_CHALLENGE_LEN)
+ return -1;
+ if (onion_skin_TAP_server_handshake((const char*)onion_skin,
+ keys->onion_key, keys->last_onion_key,
+ (char*)reply_out,
+ (char*)keys_out, keys_out_len)<0)
+ return -1;
+ r = TAP_ONIONSKIN_REPLY_LEN;
+ memcpy(rend_nonce_out, reply_out+DH_KEY_LEN, DIGEST_LEN);
+ break;
+ case ONION_HANDSHAKE_TYPE_FAST:
+ if (onionskin_len != CREATE_FAST_LEN)
+ return -1;
+ if (fast_server_handshake(onion_skin, reply_out, keys_out, keys_out_len)<0)
+ return -1;
+ r = CREATED_FAST_LEN;
+ memcpy(rend_nonce_out, reply_out+DIGEST_LEN, DIGEST_LEN);
+ break;
+ case ONION_HANDSHAKE_TYPE_NTOR:
+#ifdef CURVE25519_ENABLED
+ if (onionskin_len < NTOR_ONIONSKIN_LEN)
+ return -1;
+ {
+ size_t keys_tmp_len = keys_out_len + DIGEST_LEN;
+ uint8_t *keys_tmp = tor_malloc(keys_out_len + DIGEST_LEN);
+
+ if (onion_skin_ntor_server_handshake(
+ onion_skin, keys->curve25519_key_map,
+ keys->junk_keypair,
+ keys->my_identity,
+ reply_out, keys_tmp, keys_tmp_len)<0) {
+ tor_free(keys_tmp);
+ return -1;
+ }
+ memcpy(keys_out, keys_tmp, keys_out_len);
+ memcpy(rend_nonce_out, keys_tmp+keys_out_len, DIGEST_LEN);
+ memwipe(keys_tmp, 0, keys_tmp_len);
+ tor_free(keys_tmp);
+ r = NTOR_REPLY_LEN;
+ }
+#else
+ return -1;
+#endif
+ break;
+ default:
+ log_warn(LD_BUG, "called with unknown handshake state type %d", type);
+ tor_fragile_assert();
+ return -1;
}
- if (len<0) {
- log_info(LD_PROTOCOL,
- "Couldn't decrypt onionskin: client may be using old onion key");
- goto err;
- } else if (len != DH_KEY_LEN) {
- log_warn(LD_PROTOCOL, "Unexpected onionskin length after decryption: %ld",
- (long)len);
- goto err;
+
+ return r;
+}
+
+/** Perform the final (client-side) step of a circuit-creation handshake of
+ * type <b>type</b>, using our state in <b>handshake_state</b> and the
+ * server's response in <b>reply</b>. On success, generate <b>keys_out_len</b>
+ * bytes worth of key material in <b>keys_out_len</b>, set
+ * <b>rend_authenticator_out</b> to the "KH" field that can be used to
+ * establish introduction points at this hop, and return 0. On failure,
+ * return -1. */
+int
+onion_skin_client_handshake(int type,
+ const onion_handshake_state_t *handshake_state,
+ const uint8_t *reply, size_t reply_len,
+ uint8_t *keys_out, size_t keys_out_len,
+ uint8_t *rend_authenticator_out)
+{
+ if (handshake_state->tag != type)
+ return -1;
+
+ switch (type) {
+ case ONION_HANDSHAKE_TYPE_TAP:
+ if (reply_len != TAP_ONIONSKIN_REPLY_LEN)
+ return -1;
+ if (onion_skin_TAP_client_handshake(handshake_state->u.tap,
+ (const char*)reply,
+ (char *)keys_out, keys_out_len) < 0)
+ return -1;
+
+ memcpy(rend_authenticator_out, reply+DH_KEY_LEN, DIGEST_LEN);
+
+ return 0;
+ case ONION_HANDSHAKE_TYPE_FAST:
+ if (reply_len != CREATED_FAST_LEN)
+ return -1;
+ if (fast_client_handshake(handshake_state->u.fast, reply,
+ keys_out, keys_out_len) < 0)
+ return -1;
+
+ memcpy(rend_authenticator_out, reply+DIGEST_LEN, DIGEST_LEN);
+ return 0;
+#ifdef CURVE25519_ENABLED
+ case ONION_HANDSHAKE_TYPE_NTOR:
+ if (reply_len < NTOR_REPLY_LEN)
+ return -1;
+ {
+ size_t keys_tmp_len = keys_out_len + DIGEST_LEN;
+ uint8_t *keys_tmp = tor_malloc(keys_tmp_len);
+ if (onion_skin_ntor_client_handshake(handshake_state->u.ntor,
+ reply,
+ keys_tmp, keys_tmp_len) < 0) {
+ tor_free(keys_tmp);
+ return -1;
+ }
+ memcpy(keys_out, keys_tmp, keys_out_len);
+ memcpy(rend_authenticator_out, keys_tmp + keys_out_len, DIGEST_LEN);
+ memwipe(keys_tmp, 0, keys_tmp_len);
+ tor_free(keys_tmp);
+ }
+ return 0;
+#endif
+ default:
+ log_warn(LD_BUG, "called with unknown handshake state type %d", type);
+ tor_fragile_assert();
+ return -1;
}
+}
- dh = crypto_dh_new(DH_TYPE_CIRCUIT);
- if (!dh) {
- log_warn(LD_BUG, "Couldn't allocate DH key");
- goto err;
+/** Helper: return 0 if <b>cell</b> appears valid, -1 otherwise. If
+ * <b>unknown_ok</b> is true, allow cells with handshake types we don't
+ * recognize. */
+static int
+check_create_cell(const create_cell_t *cell, int unknown_ok)
+{
+ switch (cell->cell_type) {
+ case CELL_CREATE:
+ if (cell->handshake_type != ONION_HANDSHAKE_TYPE_TAP &&
+ cell->handshake_type != ONION_HANDSHAKE_TYPE_NTOR)
+ return -1;
+ break;
+ case CELL_CREATE_FAST:
+ if (cell->handshake_type != ONION_HANDSHAKE_TYPE_FAST)
+ return -1;
+ break;
+ case CELL_CREATE2:
+ break;
+ default:
+ return -1;
}
- if (crypto_dh_get_public(dh, handshake_reply_out, DH_KEY_LEN)) {
- log_info(LD_GENERAL, "crypto_dh_get_public failed.");
- goto err;
+
+ switch (cell->handshake_type) {
+ case ONION_HANDSHAKE_TYPE_TAP:
+ if (cell->handshake_len != TAP_ONIONSKIN_CHALLENGE_LEN)
+ return -1;
+ break;
+ case ONION_HANDSHAKE_TYPE_FAST:
+ if (cell->handshake_len != CREATE_FAST_LEN)
+ return -1;
+ break;
+#ifdef CURVE25519_ENABLED
+ case ONION_HANDSHAKE_TYPE_NTOR:
+ if (cell->handshake_len != NTOR_ONIONSKIN_LEN)
+ return -1;
+ break;
+#endif
+ default:
+ if (! unknown_ok)
+ return -1;
}
- key_material_len = DIGEST_LEN+key_out_len;
- key_material = tor_malloc(key_material_len);
- len = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, dh, challenge,
- DH_KEY_LEN, key_material,
- key_material_len);
- if (len < 0) {
- log_info(LD_GENERAL, "crypto_dh_compute_secret failed.");
- goto err;
+ return 0;
+}
+
+/** Helper: parse the CREATE2 payload at <b>p</b>, which could be up to
+ * <b>p_len</b> bytes long, and use it to fill the fields of
+ * <b>cell_out</b>. Return 0 on success and -1 on failure.
+ *
+ * Note that part of the body of an EXTEND2 cell is a CREATE2 payload, so
+ * this function is also used for parsing those.
+ */
+static int
+parse_create2_payload(create_cell_t *cell_out, const uint8_t *p, size_t p_len)
+{
+ if (p_len < 4)
+ return -1;
+ cell_out->cell_type = CELL_CREATE2;
+ cell_out->handshake_type = ntohs(get_uint16(p));
+ cell_out->handshake_len = ntohs(get_uint16(p+2));
+ if (cell_out->handshake_len > CELL_PAYLOAD_SIZE - 4 ||
+ cell_out->handshake_len > p_len - 4)
+ return -1;
+ if (cell_out->handshake_type == ONION_HANDSHAKE_TYPE_FAST)
+ return -1;
+ memcpy(cell_out->onionskin, p+4, cell_out->handshake_len);
+ return 0;
+}
+
+/** Magic string which, in a CREATE or EXTEND cell, indicates that a seeming
+ * TAP payload is really an ntor payload. We'd do away with this if every
+ * relay supported EXTEND2, but we want to be able to extend from A to B with
+ * ntor even when A doesn't understand EXTEND2 and so can't generate a
+ * CREATE2 cell.
+ **/
+#define NTOR_CREATE_MAGIC "ntorNTORntorNTOR"
+
+/** Parse a CREATE, CREATE_FAST, or CREATE2 cell from <b>cell_in</b> into
+ * <b>cell_out</b>. Return 0 on success, -1 on failure. (We reject some
+ * syntactically valid CREATE2 cells that we can't generate or react to.) */
+int
+create_cell_parse(create_cell_t *cell_out, const cell_t *cell_in)
+{
+ memset(cell_out, 0, sizeof(*cell_out));
+
+ switch (cell_in->command) {
+ case CELL_CREATE:
+ cell_out->cell_type = CELL_CREATE;
+ if (tor_memeq(cell_in->payload, NTOR_CREATE_MAGIC, 16)) {
+ cell_out->handshake_type = ONION_HANDSHAKE_TYPE_NTOR;
+ cell_out->handshake_len = NTOR_ONIONSKIN_LEN;
+ memcpy(cell_out->onionskin, cell_in->payload+16, NTOR_ONIONSKIN_LEN);
+ } else {
+ cell_out->handshake_type = ONION_HANDSHAKE_TYPE_TAP;
+ cell_out->handshake_len = TAP_ONIONSKIN_CHALLENGE_LEN;
+ memcpy(cell_out->onionskin, cell_in->payload,
+ TAP_ONIONSKIN_CHALLENGE_LEN);
+ }
+ break;
+ case CELL_CREATE_FAST:
+ cell_out->cell_type = CELL_CREATE_FAST;
+ cell_out->handshake_type = ONION_HANDSHAKE_TYPE_FAST;
+ cell_out->handshake_len = CREATE_FAST_LEN;
+ memcpy(cell_out->onionskin, cell_in->payload, CREATE_FAST_LEN);
+ break;
+ case CELL_CREATE2:
+ if (parse_create2_payload(cell_out, cell_in->payload,
+ CELL_PAYLOAD_SIZE) < 0)
+ return -1;
+ break;
+ default:
+ return -1;
}
- /* send back H(K|0) as proof that we learned K. */
- memcpy(handshake_reply_out+DH_KEY_LEN, key_material, DIGEST_LEN);
+ return check_create_cell(cell_out, 0);
+}
- /* use the rest of the key material for our shared keys, digests, etc */
- memcpy(key_out, key_material+DIGEST_LEN, key_out_len);
+/** Helper: return 0 if <b>cell</b> appears valid, -1 otherwise. */
+static int
+check_created_cell(const created_cell_t *cell)
+{
+ switch (cell->cell_type) {
+ case CELL_CREATED:
+ if (cell->handshake_len != TAP_ONIONSKIN_REPLY_LEN &&
+ cell->handshake_len != NTOR_REPLY_LEN)
+ return -1;
+ break;
+ case CELL_CREATED_FAST:
+ if (cell->handshake_len != CREATED_FAST_LEN)
+ return -1;
+ break;
+ case CELL_CREATED2:
+ if (cell->handshake_len > RELAY_PAYLOAD_SIZE-2)
+ return -1;
+ break;
+ }
- memset(challenge, 0, sizeof(challenge));
- memset(key_material, 0, key_material_len);
- tor_free(key_material);
- crypto_dh_free(dh);
return 0;
- err:
- memset(challenge, 0, sizeof(challenge));
- if (key_material) {
- memset(key_material, 0, key_material_len);
- tor_free(key_material);
+}
+
+/** Parse a CREATED, CREATED_FAST, or CREATED2 cell from <b>cell_in</b> into
+ * <b>cell_out</b>. Return 0 on success, -1 on failure. */
+int
+created_cell_parse(created_cell_t *cell_out, const cell_t *cell_in)
+{
+ memset(cell_out, 0, sizeof(*cell_out));
+
+ switch (cell_in->command) {
+ case CELL_CREATED:
+ cell_out->cell_type = CELL_CREATED;
+ cell_out->handshake_len = TAP_ONIONSKIN_REPLY_LEN;
+ memcpy(cell_out->reply, cell_in->payload, TAP_ONIONSKIN_REPLY_LEN);
+ break;
+ case CELL_CREATED_FAST:
+ cell_out->cell_type = CELL_CREATED_FAST;
+ cell_out->handshake_len = CREATED_FAST_LEN;
+ memcpy(cell_out->reply, cell_in->payload, CREATED_FAST_LEN);
+ break;
+ case CELL_CREATED2:
+ {
+ const uint8_t *p = cell_in->payload;
+ cell_out->cell_type = CELL_CREATED2;
+ cell_out->handshake_len = ntohs(get_uint16(p));
+ if (cell_out->handshake_len > CELL_PAYLOAD_SIZE - 2)
+ return -1;
+ memcpy(cell_out->reply, p+2, cell_out->handshake_len);
+ break;
+ }
}
- if (dh) crypto_dh_free(dh);
- return -1;
+ return check_created_cell(cell_out);
}
-/** Finish the client side of the DH handshake.
- * Given the 128 byte DH reply + 20 byte hash as generated by
- * onion_skin_server_handshake and the handshake state generated by
- * onion_skin_create, verify H(K) with the first 20 bytes of shared
- * key material, then generate key_out_len more bytes of shared key
- * material and store them in key_out.
- *
- * After the invocation, call crypto_dh_free on handshake_state.
+/** Helper: return 0 if <b>cell</b> appears valid, -1 otherwise. */
+static int
+check_extend_cell(const extend_cell_t *cell)
+{
+ if (tor_digest_is_zero((const char*)cell->node_id))
+ return -1;
+ /* We don't currently allow EXTEND2 cells without an IPv4 address */
+ if (tor_addr_family(&cell->orport_ipv4.addr) == AF_UNSPEC)
+ return -1;
+ if (cell->create_cell.cell_type == CELL_CREATE) {
+ if (cell->cell_type != RELAY_COMMAND_EXTEND)
+ return -1;
+ } else if (cell->create_cell.cell_type == CELL_CREATE2) {
+ if (cell->cell_type != RELAY_COMMAND_EXTEND2 &&
+ cell->cell_type != RELAY_COMMAND_EXTEND)
+ return -1;
+ } else {
+ /* In particular, no CREATE_FAST cells are allowed */
+ return -1;
+ }
+ if (cell->create_cell.handshake_type == ONION_HANDSHAKE_TYPE_FAST)
+ return -1;
+
+ return check_create_cell(&cell->create_cell, 1);
+}
+
+/** Protocol constants for specifier types in EXTEND2
+ * @{
*/
+#define SPECTYPE_IPV4 0
+#define SPECTYPE_IPV6 1
+#define SPECTYPE_LEGACY_ID 2
+/** @} */
+
+/** Parse an EXTEND or EXTEND2 cell (according to <b>command</b>) from the
+ * <b>payload_length</b> bytes of <b>payload</b> into <b>cell_out</b>. Return
+ * 0 on success, -1 on failure. */
int
-onion_skin_client_handshake(crypto_dh_t *handshake_state,
- const char *handshake_reply, /* ONIONSKIN_REPLY_LEN bytes */
- char *key_out,
- size_t key_out_len)
+extend_cell_parse(extend_cell_t *cell_out, const uint8_t command,
+ const uint8_t *payload, size_t payload_length)
{
- ssize_t len;
- char *key_material=NULL;
- size_t key_material_len;
- tor_assert(crypto_dh_get_bytes(handshake_state) == DH_KEY_LEN);
-
- key_material_len = DIGEST_LEN + key_out_len;
- key_material = tor_malloc(key_material_len);
- len = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, handshake_state,
- handshake_reply, DH_KEY_LEN, key_material,
- key_material_len);
- if (len < 0)
- goto err;
-
- if (tor_memneq(key_material, handshake_reply+DH_KEY_LEN, DIGEST_LEN)) {
- /* H(K) does *not* match. Something fishy. */
- log_warn(LD_PROTOCOL,"Digest DOES NOT MATCH on onion handshake. "
- "Bug or attack.");
- goto err;
+ const uint8_t *eop;
+
+ memset(cell_out, 0, sizeof(*cell_out));
+ if (payload_length > RELAY_PAYLOAD_SIZE)
+ return -1;
+ eop = payload + payload_length;
+
+ switch (command) {
+ case RELAY_COMMAND_EXTEND:
+ {
+ if (payload_length != 6 + TAP_ONIONSKIN_CHALLENGE_LEN + DIGEST_LEN)
+ return -1;
+
+ cell_out->cell_type = RELAY_COMMAND_EXTEND;
+ tor_addr_from_ipv4n(&cell_out->orport_ipv4.addr, get_uint32(payload));
+ cell_out->orport_ipv4.port = ntohs(get_uint16(payload+4));
+ tor_addr_make_unspec(&cell_out->orport_ipv6.addr);
+ if (tor_memeq(payload + 6, NTOR_CREATE_MAGIC, 16)) {
+ cell_out->create_cell.cell_type = CELL_CREATE2;
+ cell_out->create_cell.handshake_type = ONION_HANDSHAKE_TYPE_NTOR;
+ cell_out->create_cell.handshake_len = NTOR_ONIONSKIN_LEN;
+ memcpy(cell_out->create_cell.onionskin, payload + 22,
+ NTOR_ONIONSKIN_LEN);
+ } else {
+ cell_out->create_cell.cell_type = CELL_CREATE;
+ cell_out->create_cell.handshake_type = ONION_HANDSHAKE_TYPE_TAP;
+ cell_out->create_cell.handshake_len = TAP_ONIONSKIN_CHALLENGE_LEN;
+ memcpy(cell_out->create_cell.onionskin, payload + 6,
+ TAP_ONIONSKIN_CHALLENGE_LEN);
+ }
+ memcpy(cell_out->node_id, payload + 6 + TAP_ONIONSKIN_CHALLENGE_LEN,
+ DIGEST_LEN);
+ break;
+ }
+ case RELAY_COMMAND_EXTEND2:
+ {
+ uint8_t n_specs = *payload, spectype, speclen;
+ int i;
+ int found_ipv4 = 0, found_ipv6 = 0, found_id = 0;
+ tor_addr_make_unspec(&cell_out->orport_ipv4.addr);
+ tor_addr_make_unspec(&cell_out->orport_ipv6.addr);
+
+ cell_out->cell_type = RELAY_COMMAND_EXTEND2;
+ ++payload;
+ /* Parse the specifiers. We'll only take the first IPv4 and first IPv6
+ * addres, and the node ID, and ignore everything else */
+ for (i = 0; i < n_specs; ++i) {
+ if (eop - payload < 2)
+ return -1;
+ spectype = payload[0];
+ speclen = payload[1];
+ payload += 2;
+ if (eop - payload < speclen)
+ return -1;
+ switch (spectype) {
+ case SPECTYPE_IPV4:
+ if (speclen != 6)
+ return -1;
+ if (!found_ipv4) {
+ tor_addr_from_ipv4n(&cell_out->orport_ipv4.addr,
+ get_uint32(payload));
+ cell_out->orport_ipv4.port = ntohs(get_uint16(payload+4));
+ found_ipv4 = 1;
+ }
+ break;
+ case SPECTYPE_IPV6:
+ if (speclen != 18)
+ return -1;
+ if (!found_ipv6) {
+ tor_addr_from_ipv6_bytes(&cell_out->orport_ipv6.addr,
+ (const char*)payload);
+ cell_out->orport_ipv6.port = ntohs(get_uint16(payload+16));
+ found_ipv6 = 1;
+ }
+ break;
+ case SPECTYPE_LEGACY_ID:
+ if (speclen != 20)
+ return -1;
+ if (found_id)
+ return -1;
+ memcpy(cell_out->node_id, payload, 20);
+ found_id = 1;
+ break;
+ }
+ payload += speclen;
+ }
+ if (!found_id || !found_ipv4)
+ return -1;
+ if (parse_create2_payload(&cell_out->create_cell,payload,eop-payload)<0)
+ return -1;
+ break;
+ }
+ default:
+ return -1;
}
- /* use the rest of the key material for our shared keys, digests, etc */
- memcpy(key_out, key_material+DIGEST_LEN, key_out_len);
+ return check_extend_cell(cell_out);
+}
- memset(key_material, 0, key_material_len);
- tor_free(key_material);
- return 0;
- err:
- memset(key_material, 0, key_material_len);
- tor_free(key_material);
- return -1;
+/** Helper: return 0 if <b>cell</b> appears valid, -1 otherwise. */
+static int
+check_extended_cell(const extended_cell_t *cell)
+{
+ if (cell->created_cell.cell_type == CELL_CREATED) {
+ if (cell->cell_type != RELAY_COMMAND_EXTENDED)
+ return -1;
+ } else if (cell->created_cell.cell_type == CELL_CREATED2) {
+ if (cell->cell_type != RELAY_COMMAND_EXTENDED2)
+ return -1;
+ } else {
+ return -1;
+ }
+
+ return check_created_cell(&cell->created_cell);
}
-/** Implement the server side of the CREATE_FAST abbreviated handshake. The
- * client has provided DIGEST_LEN key bytes in <b>key_in</b> ("x"). We
- * generate a reply of DIGEST_LEN*2 bytes in <b>key_out</b>, consisting of a
- * new random "y", followed by H(x|y) to check for correctness. We set
- * <b>key_out_len</b> bytes of key material in <b>key_out</b>.
- * Return 0 on success, &lt;0 on failure.
- **/
+/** Parse an EXTENDED or EXTENDED2 cell (according to <b>command</b>) from the
+ * <b>payload_length</b> bytes of <b>payload</b> into <b>cell_out</b>. Return
+ * 0 on success, -1 on failure. */
int
-fast_server_handshake(const uint8_t *key_in, /* DIGEST_LEN bytes */
- uint8_t *handshake_reply_out, /* DIGEST_LEN*2 bytes */
- uint8_t *key_out,
- size_t key_out_len)
+extended_cell_parse(extended_cell_t *cell_out,
+ const uint8_t command, const uint8_t *payload,
+ size_t payload_len)
{
- char tmp[DIGEST_LEN+DIGEST_LEN];
- char *out = NULL;
- size_t out_len;
- int r = -1;
+ memset(cell_out, 0, sizeof(*cell_out));
+ if (payload_len > RELAY_PAYLOAD_SIZE)
+ return -1;
+
+ switch (command) {
+ case RELAY_COMMAND_EXTENDED:
+ if (payload_len != TAP_ONIONSKIN_REPLY_LEN)
+ return -1;
+ cell_out->cell_type = RELAY_COMMAND_EXTENDED;
+ cell_out->created_cell.cell_type = CELL_CREATED;
+ cell_out->created_cell.handshake_len = TAP_ONIONSKIN_REPLY_LEN;
+ memcpy(cell_out->created_cell.reply, payload, TAP_ONIONSKIN_REPLY_LEN);
+ break;
+ case RELAY_COMMAND_EXTENDED2:
+ {
+ cell_out->cell_type = RELAY_COMMAND_EXTENDED2;
+ cell_out->created_cell.cell_type = CELL_CREATED2;
+ cell_out->created_cell.handshake_len = ntohs(get_uint16(payload));
+ if (cell_out->created_cell.handshake_len > RELAY_PAYLOAD_SIZE - 2 ||
+ cell_out->created_cell.handshake_len > payload_len - 2)
+ return -1;
+ memcpy(cell_out->created_cell.reply, payload+2,
+ cell_out->created_cell.handshake_len);
+ }
+ break;
+ default:
+ return -1;
+ }
- if (crypto_rand((char*)handshake_reply_out, DIGEST_LEN)<0)
+ return check_extended_cell(cell_out);
+}
+
+/** Fill <b>cell_out</b> with a correctly formatted version of the
+ * CREATE{,_FAST,2} cell in <b>cell_in</b>. Return 0 on success, -1 on
+ * failure. This is a cell we didn't originate if <b>relayed</b> is true. */
+static int
+create_cell_format_impl(cell_t *cell_out, const create_cell_t *cell_in,
+ int relayed)
+{
+ uint8_t *p;
+ size_t space;
+ if (check_create_cell(cell_in, relayed) < 0)
return -1;
- memcpy(tmp, key_in, DIGEST_LEN);
- memcpy(tmp+DIGEST_LEN, handshake_reply_out, DIGEST_LEN);
- out_len = key_out_len+DIGEST_LEN;
- out = tor_malloc(out_len);
- if (crypto_expand_key_material(tmp, sizeof(tmp), out, out_len)) {
- goto done;
+ memset(cell_out->payload, 0, sizeof(cell_out->payload));
+ cell_out->command = cell_in->cell_type;
+
+ p = cell_out->payload;
+ space = sizeof(cell_out->payload);
+
+ switch (cell_in->cell_type) {
+ case CELL_CREATE:
+ if (cell_in->handshake_type == ONION_HANDSHAKE_TYPE_NTOR) {
+ memcpy(p, NTOR_CREATE_MAGIC, 16);
+ p += 16;
+ space -= 16;
+ }
+ /* Fall through */
+ case CELL_CREATE_FAST:
+ tor_assert(cell_in->handshake_len <= space);
+ memcpy(p, cell_in->onionskin, cell_in->handshake_len);
+ break;
+ case CELL_CREATE2:
+ tor_assert(cell_in->handshake_len <= sizeof(cell_out->payload)-4);
+ set_uint16(cell_out->payload, htons(cell_in->handshake_type));
+ set_uint16(cell_out->payload+2, htons(cell_in->handshake_len));
+ memcpy(cell_out->payload + 4, cell_in->onionskin, cell_in->handshake_len);
+ break;
+ default:
+ return -1;
}
- memcpy(handshake_reply_out+DIGEST_LEN, out, DIGEST_LEN);
- memcpy(key_out, out+DIGEST_LEN, key_out_len);
- r = 0;
- done:
- memset(tmp, 0, sizeof(tmp));
- memset(out, 0, out_len);
- tor_free(out);
- return r;
+
+ return 0;
}
-/** Implement the second half of the client side of the CREATE_FAST handshake.
- * We sent the server <b>handshake_state</b> ("x") already, and the server
- * told us <b>handshake_reply_out</b> (y|H(x|y)). Make sure that the hash is
- * correct, and generate key material in <b>key_out</b>. Return 0 on success,
- * true on failure.
- *
- * NOTE: The "CREATE_FAST" handshake path is distinguishable from regular
- * "onionskin" handshakes, and is not secure if an adversary can see or modify
- * the messages. Therefore, it should only be used by clients, and only as
- * the first hop of a circuit (since the first hop is already authenticated
- * and protected by TLS).
- */
int
-fast_client_handshake(const uint8_t *handshake_state,/*DIGEST_LEN bytes*/
- const uint8_t *handshake_reply_out,/*DIGEST_LEN*2 bytes*/
- uint8_t *key_out,
- size_t key_out_len)
+create_cell_format(cell_t *cell_out, const create_cell_t *cell_in)
{
- char tmp[DIGEST_LEN+DIGEST_LEN];
- char *out;
- size_t out_len;
- int r = -1;
+ return create_cell_format_impl(cell_out, cell_in, 0);
+}
+
+int
+create_cell_format_relayed(cell_t *cell_out, const create_cell_t *cell_in)
+{
+ return create_cell_format_impl(cell_out, cell_in, 1);
+}
- memcpy(tmp, handshake_state, DIGEST_LEN);
- memcpy(tmp+DIGEST_LEN, handshake_reply_out, DIGEST_LEN);
- out_len = key_out_len+DIGEST_LEN;
- out = tor_malloc(out_len);
- if (crypto_expand_key_material(tmp, sizeof(tmp), out, out_len)) {
- goto done;
+/** Fill <b>cell_out</b> with a correctly formatted version of the
+ * CREATED{,_FAST,2} cell in <b>cell_in</b>. Return 0 on success, -1 on
+ * failure. */
+int
+created_cell_format(cell_t *cell_out, const created_cell_t *cell_in)
+{
+ if (check_created_cell(cell_in) < 0)
+ return -1;
+
+ memset(cell_out->payload, 0, sizeof(cell_out->payload));
+ cell_out->command = cell_in->cell_type;
+
+ switch (cell_in->cell_type) {
+ case CELL_CREATED:
+ case CELL_CREATED_FAST:
+ tor_assert(cell_in->handshake_len <= sizeof(cell_out->payload));
+ memcpy(cell_out->payload, cell_in->reply, cell_in->handshake_len);
+ break;
+ case CELL_CREATED2:
+ tor_assert(cell_in->handshake_len <= sizeof(cell_out->payload)-2);
+ set_uint16(cell_out->payload, htons(cell_in->handshake_len));
+ memcpy(cell_out->payload + 2, cell_in->reply, cell_in->handshake_len);
+ break;
+ default:
+ return -1;
}
- if (tor_memneq(out, handshake_reply_out+DIGEST_LEN, DIGEST_LEN)) {
- /* H(K) does *not* match. Something fishy. */
- log_warn(LD_PROTOCOL,"Digest DOES NOT MATCH on fast handshake. "
- "Bug or attack.");
- goto done;
+ return 0;
+}
+
+/** Format the EXTEND{,2} cell in <b>cell_in</b>, storing its relay payload in
+ * <b>payload_out</b>, the number of bytes used in *<b>len_out</b>, and the
+ * relay command in *<b>command_out</b>. The <b>payload_out</b> must have
+ * RELAY_PAYLOAD_SIZE bytes available. Return 0 on success, -1 on failure. */
+int
+extend_cell_format(uint8_t *command_out, uint16_t *len_out,
+ uint8_t *payload_out, const extend_cell_t *cell_in)
+{
+ uint8_t *p, *eop;
+ if (check_extend_cell(cell_in) < 0)
+ return -1;
+
+ p = payload_out;
+ eop = payload_out + RELAY_PAYLOAD_SIZE;
+
+ memset(p, 0, RELAY_PAYLOAD_SIZE);
+
+ switch (cell_in->cell_type) {
+ case RELAY_COMMAND_EXTEND:
+ {
+ *command_out = RELAY_COMMAND_EXTEND;
+ *len_out = 6 + TAP_ONIONSKIN_CHALLENGE_LEN + DIGEST_LEN;
+ set_uint32(p, tor_addr_to_ipv4n(&cell_in->orport_ipv4.addr));
+ set_uint16(p+4, ntohs(cell_in->orport_ipv4.port));
+ if (cell_in->create_cell.handshake_type == ONION_HANDSHAKE_TYPE_NTOR) {
+ memcpy(p+6, NTOR_CREATE_MAGIC, 16);
+ memcpy(p+22, cell_in->create_cell.onionskin, NTOR_ONIONSKIN_LEN);
+ } else {
+ memcpy(p+6, cell_in->create_cell.onionskin,
+ TAP_ONIONSKIN_CHALLENGE_LEN);
+ }
+ memcpy(p+6+TAP_ONIONSKIN_CHALLENGE_LEN, cell_in->node_id, DIGEST_LEN);
+ }
+ break;
+ case RELAY_COMMAND_EXTEND2:
+ {
+ uint8_t n = 2;
+ *command_out = RELAY_COMMAND_EXTEND2;
+
+ *p++ = n; /* 2 identifiers */
+ *p++ = SPECTYPE_IPV4; /* First is IPV4. */
+ *p++ = 6; /* It's 6 bytes long. */
+ set_uint32(p, tor_addr_to_ipv4n(&cell_in->orport_ipv4.addr));
+ set_uint16(p+4, htons(cell_in->orport_ipv4.port));
+ p += 6;
+ *p++ = SPECTYPE_LEGACY_ID; /* Next is an identity digest. */
+ *p++ = 20; /* It's 20 bytes long */
+ memcpy(p, cell_in->node_id, DIGEST_LEN);
+ p += 20;
+
+ /* Now we can send the handshake */
+ set_uint16(p, htons(cell_in->create_cell.handshake_type));
+ set_uint16(p+2, htons(cell_in->create_cell.handshake_len));
+ p += 4;
+
+ if (cell_in->create_cell.handshake_len > eop - p)
+ return -1;
+
+ memcpy(p, cell_in->create_cell.onionskin,
+ cell_in->create_cell.handshake_len);
+
+ p += cell_in->create_cell.handshake_len;
+ *len_out = p - payload_out;
+ }
+ break;
+ default:
+ return -1;
}
- memcpy(key_out, out+DIGEST_LEN, key_out_len);
- r = 0;
- done:
- memset(tmp, 0, sizeof(tmp));
- memset(out, 0, out_len);
- tor_free(out);
- return r;
+
+ return 0;
}
-/** Remove all circuits from the pending list. Called from tor_free_all. */
-void
-clear_pending_onions(void)
+/** Format the EXTENDED{,2} cell in <b>cell_in</b>, storing its relay payload
+ * in <b>payload_out</b>, the number of bytes used in *<b>len_out</b>, and the
+ * relay command in *<b>command_out</b>. The <b>payload_out</b> must have
+ * RELAY_PAYLOAD_SIZE bytes available. Return 0 on success, -1 on failure. */
+int
+extended_cell_format(uint8_t *command_out, uint16_t *len_out,
+ uint8_t *payload_out, const extended_cell_t *cell_in)
{
- while (ol_list) {
- onion_queue_t *victim = ol_list;
- ol_list = victim->next;
- tor_free(victim->onionskin);
- tor_free(victim);
+ uint8_t *p;
+ if (check_extended_cell(cell_in) < 0)
+ return -1;
+
+ p = payload_out;
+ memset(p, 0, RELAY_PAYLOAD_SIZE);
+
+ switch (cell_in->cell_type) {
+ case RELAY_COMMAND_EXTENDED:
+ {
+ *command_out = RELAY_COMMAND_EXTENDED;
+ *len_out = TAP_ONIONSKIN_REPLY_LEN;
+ memcpy(payload_out, cell_in->created_cell.reply,
+ TAP_ONIONSKIN_REPLY_LEN);
+ }
+ break;
+ case RELAY_COMMAND_EXTENDED2:
+ {
+ *command_out = RELAY_COMMAND_EXTENDED2;
+ *len_out = 2 + cell_in->created_cell.handshake_len;
+ set_uint16(payload_out, htons(cell_in->created_cell.handshake_len));
+ if (2+cell_in->created_cell.handshake_len > RELAY_PAYLOAD_SIZE)
+ return -1;
+ memcpy(payload_out+2, cell_in->created_cell.reply,
+ cell_in->created_cell.handshake_len);
+ }
+ break;
+ default:
+ return -1;
}
- ol_list = ol_tail = NULL;
- ol_length = 0;
+
+ return 0;
}
diff --git a/src/or/onion.h b/src/or/onion.h
index e7626f9709..db4c999c9e 100644
--- a/src/or/onion.h
+++ b/src/or/onion.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -12,37 +12,107 @@
#ifndef TOR_ONION_H
#define TOR_ONION_H
-int onion_pending_add(or_circuit_t *circ, char *onionskin);
-or_circuit_t *onion_next_task(char **onionskin_out);
+struct create_cell_t;
+int onion_pending_add(or_circuit_t *circ, struct create_cell_t *onionskin);
+or_circuit_t *onion_next_task(struct create_cell_t **onionskin_out);
void onion_pending_remove(or_circuit_t *circ);
+void clear_pending_onions(void);
+
+typedef struct server_onion_keys_t {
+ uint8_t my_identity[DIGEST_LEN];
+ crypto_pk_t *onion_key;
+ crypto_pk_t *last_onion_key;
+#ifdef CURVE25519_ENABLED
+ di_digest256_map_t *curve25519_key_map;
+ curve25519_keypair_t *junk_keypair;
+#endif
+} server_onion_keys_t;
-int onion_skin_create(crypto_pk_t *router_key,
- crypto_dh_t **handshake_state_out,
- char *onion_skin_out);
+#define MAX_ONIONSKIN_CHALLENGE_LEN 255
+#define MAX_ONIONSKIN_REPLY_LEN 255
-int onion_skin_server_handshake(const char *onion_skin,
- crypto_pk_t *private_key,
- crypto_pk_t *prev_private_key,
- char *handshake_reply_out,
- char *key_out,
- size_t key_out_len);
+void setup_server_onion_keys(server_onion_keys_t *keys);
+void release_server_onion_keys(server_onion_keys_t *keys);
-int onion_skin_client_handshake(crypto_dh_t *handshake_state,
- const char *handshake_reply,
- char *key_out,
- size_t key_out_len);
+void onion_handshake_state_release(onion_handshake_state_t *state);
-int fast_server_handshake(const uint8_t *key_in,
- uint8_t *handshake_reply_out,
- uint8_t *key_out,
- size_t key_out_len);
+int onion_skin_create(int type,
+ const extend_info_t *node,
+ onion_handshake_state_t *state_out,
+ uint8_t *onion_skin_out);
+int onion_skin_server_handshake(int type,
+ const uint8_t *onion_skin, size_t onionskin_len,
+ const server_onion_keys_t *keys,
+ uint8_t *reply_out,
+ uint8_t *keys_out, size_t key_out_len,
+ uint8_t *rend_nonce_out);
+int onion_skin_client_handshake(int type,
+ const onion_handshake_state_t *handshake_state,
+ const uint8_t *reply, size_t reply_len,
+ uint8_t *keys_out, size_t key_out_len,
+ uint8_t *rend_authenticator_out);
-int fast_client_handshake(const uint8_t *handshake_state,
- const uint8_t *handshake_reply_out,
- uint8_t *key_out,
- size_t key_out_len);
+/** A parsed CREATE, CREATE_FAST, or CREATE2 cell. */
+typedef struct create_cell_t {
+ /** The cell command. One of CREATE{,_FAST,2} */
+ uint8_t cell_type;
+ /** One of the ONION_HANDSHAKE_TYPE_* values */
+ uint16_t handshake_type;
+ /** The number of bytes used in <b>onionskin</b>. */
+ uint16_t handshake_len;
+ /** The client-side message for the circuit creation handshake. */
+ uint8_t onionskin[CELL_PAYLOAD_SIZE - 4];
+} create_cell_t;
-void clear_pending_onions(void);
+/** A parsed CREATED, CREATED_FAST, or CREATED2 cell. */
+typedef struct created_cell_t {
+ /** The cell command. One of CREATED{,_FAST,2} */
+ uint8_t cell_type;
+ /** The number of bytes used in <b>reply</b>. */
+ uint16_t handshake_len;
+ /** The server-side message for the circuit creation handshake. */
+ uint8_t reply[CELL_PAYLOAD_SIZE - 2];
+} created_cell_t;
+
+/** A parsed RELAY_EXTEND or RELAY_EXTEND2 cell */
+typedef struct extend_cell_t {
+ /** One of RELAY_EXTEND or RELAY_EXTEND2 */
+ uint8_t cell_type;
+ /** An IPv4 address and port for the node we're connecting to. */
+ tor_addr_port_t orport_ipv4;
+ /** An IPv6 address and port for the node we're connecting to. Not currently
+ * used. */
+ tor_addr_port_t orport_ipv6;
+ /** Identity fingerprint of the node we're conecting to.*/
+ uint8_t node_id[DIGEST_LEN];
+ /** The "create cell" embedded in this extend cell. Note that unlike the
+ * create cells we generate ourself, this once can have a handshake type we
+ * don't recognize. */
+ create_cell_t create_cell;
+} extend_cell_t;
+
+/** A parsed RELAY_EXTEND or RELAY_EXTEND2 cell */
+typedef struct extended_cell_t {
+ /** One of RELAY_EXTENDED or RELAY_EXTENDED2. */
+ uint8_t cell_type;
+ /** The "created cell" embedded in this extended cell. */
+ created_cell_t created_cell;
+} extended_cell_t;
+
+int create_cell_parse(create_cell_t *cell_out, const cell_t *cell_in);
+int created_cell_parse(created_cell_t *cell_out, const cell_t *cell_in);
+int extend_cell_parse(extend_cell_t *cell_out, const uint8_t command,
+ const uint8_t *payload_in, size_t payload_len);
+int extended_cell_parse(extended_cell_t *cell_out, const uint8_t command,
+ const uint8_t *payload_in, size_t payload_len);
+
+int create_cell_format(cell_t *cell_out, const create_cell_t *cell_in);
+int create_cell_format_relayed(cell_t *cell_out, const create_cell_t *cell_in);
+int created_cell_format(cell_t *cell_out, const created_cell_t *cell_in);
+int extend_cell_format(uint8_t *command_out, uint16_t *len_out,
+ uint8_t *payload_out, const extend_cell_t *cell_in);
+int extended_cell_format(uint8_t *command_out, uint16_t *len_out,
+ uint8_t *payload_out, const extended_cell_t *cell_in);
#endif
diff --git a/src/or/onion_fast.c b/src/or/onion_fast.c
new file mode 100644
index 0000000000..aa034a8bd6
--- /dev/null
+++ b/src/or/onion_fast.c
@@ -0,0 +1,123 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file onion_fast.c
+ * \brief Functions implement the CREATE_FAST circuit handshake.
+ **/
+
+#include "or.h"
+#include "onion_fast.h"
+
+/** Release all state held in <b>victim</b>. */
+void
+fast_handshake_state_free(fast_handshake_state_t *victim)
+{
+ if (! victim)
+ return;
+ memwipe(victim, 0, sizeof(fast_handshake_state_t));
+ tor_free(victim);
+}
+
+/** Create the state needed to perform a CREATE_FAST hasnshake. Return 0
+ * on success, -1 on failure. */
+int
+fast_onionskin_create(fast_handshake_state_t **handshake_state_out,
+ uint8_t *handshake_out)
+{
+ fast_handshake_state_t *s;
+ *handshake_state_out = s = tor_malloc(sizeof(fast_handshake_state_t));
+ if (crypto_rand((char*)s->state, sizeof(s->state)) < 0) {
+ tor_free(s);
+ return -1;
+ }
+ memcpy(handshake_out, s->state, DIGEST_LEN);
+ return 0;
+}
+
+/** Implement the server side of the CREATE_FAST abbreviated handshake. The
+ * client has provided DIGEST_LEN key bytes in <b>key_in</b> ("x"). We
+ * generate a reply of DIGEST_LEN*2 bytes in <b>key_out</b>, consisting of a
+ * new random "y", followed by H(x|y) to check for correctness. We set
+ * <b>key_out_len</b> bytes of key material in <b>key_out</b>.
+ * Return 0 on success, &lt;0 on failure.
+ **/
+int
+fast_server_handshake(const uint8_t *key_in, /* DIGEST_LEN bytes */
+ uint8_t *handshake_reply_out, /* DIGEST_LEN*2 bytes */
+ uint8_t *key_out,
+ size_t key_out_len)
+{
+ uint8_t tmp[DIGEST_LEN+DIGEST_LEN];
+ uint8_t *out = NULL;
+ size_t out_len;
+ int r = -1;
+
+ if (crypto_rand((char*)handshake_reply_out, DIGEST_LEN)<0)
+ return -1;
+
+ memcpy(tmp, key_in, DIGEST_LEN);
+ memcpy(tmp+DIGEST_LEN, handshake_reply_out, DIGEST_LEN);
+ out_len = key_out_len+DIGEST_LEN;
+ out = tor_malloc(out_len);
+ if (crypto_expand_key_material_TAP(tmp, sizeof(tmp), out, out_len)) {
+ goto done;
+ }
+ memcpy(handshake_reply_out+DIGEST_LEN, out, DIGEST_LEN);
+ memcpy(key_out, out+DIGEST_LEN, key_out_len);
+ r = 0;
+ done:
+ memwipe(tmp, 0, sizeof(tmp));
+ memwipe(out, 0, out_len);
+ tor_free(out);
+ return r;
+}
+
+/** Implement the second half of the client side of the CREATE_FAST handshake.
+ * We sent the server <b>handshake_state</b> ("x") already, and the server
+ * told us <b>handshake_reply_out</b> (y|H(x|y)). Make sure that the hash is
+ * correct, and generate key material in <b>key_out</b>. Return 0 on success,
+ * true on failure.
+ *
+ * NOTE: The "CREATE_FAST" handshake path is distinguishable from regular
+ * "onionskin" handshakes, and is not secure if an adversary can see or modify
+ * the messages. Therefore, it should only be used by clients, and only as
+ * the first hop of a circuit (since the first hop is already authenticated
+ * and protected by TLS).
+ */
+int
+fast_client_handshake(const fast_handshake_state_t *handshake_state,
+ const uint8_t *handshake_reply_out,/*DIGEST_LEN*2 bytes*/
+ uint8_t *key_out,
+ size_t key_out_len)
+{
+ uint8_t tmp[DIGEST_LEN+DIGEST_LEN];
+ uint8_t *out;
+ size_t out_len;
+ int r = -1;
+
+ memcpy(tmp, handshake_state->state, DIGEST_LEN);
+ memcpy(tmp+DIGEST_LEN, handshake_reply_out, DIGEST_LEN);
+ out_len = key_out_len+DIGEST_LEN;
+ out = tor_malloc(out_len);
+ if (crypto_expand_key_material_TAP(tmp, sizeof(tmp), out, out_len)) {
+ goto done;
+ }
+ if (tor_memneq(out, handshake_reply_out+DIGEST_LEN, DIGEST_LEN)) {
+ /* H(K) does *not* match. Something fishy. */
+ log_warn(LD_PROTOCOL,"Digest DOES NOT MATCH on fast handshake. "
+ "Bug or attack.");
+ goto done;
+ }
+ memcpy(key_out, out+DIGEST_LEN, key_out_len);
+ r = 0;
+ done:
+ memwipe(tmp, 0, sizeof(tmp));
+ memwipe(out, 0, out_len);
+ tor_free(out);
+ return r;
+}
+
diff --git a/src/or/onion_fast.h b/src/or/onion_fast.h
new file mode 100644
index 0000000000..8c078378d2
--- /dev/null
+++ b/src/or/onion_fast.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file onion_fast.h
+ * \brief Header file for onion_fast.c.
+ **/
+
+#ifndef TOR_ONION_FAST_H
+#define TOR_ONION_FAST_H
+
+#define CREATE_FAST_LEN DIGEST_LEN
+#define CREATED_FAST_LEN (DIGEST_LEN*2)
+
+typedef struct fast_handshake_state_t {
+ uint8_t state[DIGEST_LEN];
+} fast_handshake_state_t;
+
+void fast_handshake_state_free(fast_handshake_state_t *victim);
+
+int fast_onionskin_create(fast_handshake_state_t **handshake_state_out,
+ uint8_t *handshake_out);
+
+int fast_server_handshake(const uint8_t *message_in,
+ uint8_t *handshake_reply_out,
+ uint8_t *key_out,
+ size_t key_out_len);
+
+int fast_client_handshake(const fast_handshake_state_t *handshake_state,
+ const uint8_t *handshake_reply_out,
+ uint8_t *key_out,
+ size_t key_out_len);
+
+#endif
+
diff --git a/src/or/onion_ntor.c b/src/or/onion_ntor.c
new file mode 100644
index 0000000000..9cf7d5dd6e
--- /dev/null
+++ b/src/or/onion_ntor.c
@@ -0,0 +1,295 @@
+/* Copyright (c) 2012-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+
+#include "crypto.h"
+#define ONION_NTOR_PRIVATE
+#include "onion_ntor.h"
+#include "torlog.h"
+#include "util.h"
+
+/** Free storage held in an ntor handshake state. */
+void
+ntor_handshake_state_free(ntor_handshake_state_t *state)
+{
+ if (!state)
+ return;
+ memwipe(state, 0, sizeof(*state));
+ tor_free(state);
+}
+
+/** Convenience function to represent HMAC_SHA256 as our instantiation of
+ * ntor's "tweaked hash'. Hash the <b>inp_len</b> bytes at <b>inp</b> into
+ * a DIGEST256_LEN-byte digest at <b>out</b>, with the hash changing
+ * depending on the value of <b>tweak</b>. */
+static void
+h_tweak(uint8_t *out,
+ const uint8_t *inp, size_t inp_len,
+ const char *tweak)
+{
+ size_t tweak_len = strlen(tweak);
+ crypto_hmac_sha256((char*)out, tweak, tweak_len, (const char*)inp, inp_len);
+}
+
+/** Wrapper around a set of tweak-values for use with the ntor handshake. */
+typedef struct tweakset_t {
+ const char *t_mac;
+ const char *t_key;
+ const char *t_verify;
+ const char *m_expand;
+} tweakset_t;
+
+/** The tweaks to be used with our handshake. */
+const tweakset_t proto1_tweaks = {
+#define PROTOID "ntor-curve25519-sha256-1"
+#define PROTOID_LEN 24
+ PROTOID ":mac",
+ PROTOID ":key_extract",
+ PROTOID ":verify",
+ PROTOID ":key_expand"
+};
+
+/** Convenience macro: copy <b>len</b> bytes from <b>inp</b> to <b>ptr</b>,
+ * and advance <b>ptr</b> by the number of bytes copied. */
+#define APPEND(ptr, inp, len) \
+ STMT_BEGIN { \
+ memcpy(ptr, (inp), (len)); \
+ ptr += len; \
+ } STMT_END
+
+/**
+ * Compute the first client-side step of the ntor handshake for communicating
+ * with a server whose DIGEST_LEN-byte server identity is <b>router_id</b>,
+ * and whose onion key is <b>router_key</b>. Store the NTOR_ONIONSKIN_LEN-byte
+ * message in <b>onion_skin_out</b>, and store the handshake state in
+ * *<b>handshake_state_out</b>. Return 0 on success, -1 on failure.
+ */
+int
+onion_skin_ntor_create(const uint8_t *router_id,
+ const curve25519_public_key_t *router_key,
+ ntor_handshake_state_t **handshake_state_out,
+ uint8_t *onion_skin_out)
+{
+ ntor_handshake_state_t *state;
+ uint8_t *op;
+
+ state = tor_malloc_zero(sizeof(ntor_handshake_state_t));
+
+ memcpy(state->router_id, router_id, DIGEST_LEN);
+ memcpy(&state->pubkey_B, router_key, sizeof(curve25519_public_key_t));
+ if (curve25519_secret_key_generate(&state->seckey_x, 0) < 0) {
+ tor_free(state);
+ return -1;
+ }
+ curve25519_public_key_generate(&state->pubkey_X, &state->seckey_x);
+
+ op = onion_skin_out;
+ APPEND(op, router_id, DIGEST_LEN);
+ APPEND(op, router_key->public_key, CURVE25519_PUBKEY_LEN);
+ APPEND(op, state->pubkey_X.public_key, CURVE25519_PUBKEY_LEN);
+ tor_assert(op == onion_skin_out + NTOR_ONIONSKIN_LEN);
+
+ *handshake_state_out = state;
+
+ return 0;
+}
+
+#define SERVER_STR "Server"
+#define SERVER_STR_LEN 6
+
+#define SECRET_INPUT_LEN (CURVE25519_PUBKEY_LEN * 3 + \
+ CURVE25519_OUTPUT_LEN * 2 + \
+ DIGEST_LEN + PROTOID_LEN)
+#define AUTH_INPUT_LEN (DIGEST256_LEN + DIGEST_LEN + \
+ CURVE25519_PUBKEY_LEN*3 + \
+ PROTOID_LEN + SERVER_STR_LEN)
+
+/**
+ * Perform the server side of an ntor handshake. Given an
+ * NTOR_ONIONSKIN_LEN-byte message in <b>onion_skin</b>, our own identity
+ * fingerprint as <b>my_node_id</b>, and an associative array mapping public
+ * onion keys to curve25519_keypair_t in <b>private_keys</b>, attempt to
+ * perform the handshake. Use <b>junk_keys</b> if present if the handshake
+ * indicates an unrecognized public key. Write an NTOR_REPLY_LEN-byte
+ * message to send back to the client into <b>handshake_reply_out</b>, and
+ * generate <b>key_out_len</b> bytes of key material in <b>key_out</b>. Return
+ * 0 on success, -1 on failure.
+ */
+int
+onion_skin_ntor_server_handshake(const uint8_t *onion_skin,
+ const di_digest256_map_t *private_keys,
+ const curve25519_keypair_t *junk_keys,
+ const uint8_t *my_node_id,
+ uint8_t *handshake_reply_out,
+ uint8_t *key_out,
+ size_t key_out_len)
+{
+ const tweakset_t *T = &proto1_tweaks;
+ /* Sensitive stack-allocated material. Kept in an anonymous struct to make
+ * it easy to wipe. */
+ struct {
+ uint8_t secret_input[SECRET_INPUT_LEN];
+ uint8_t auth_input[AUTH_INPUT_LEN];
+ curve25519_public_key_t pubkey_X;
+ curve25519_secret_key_t seckey_y;
+ curve25519_public_key_t pubkey_Y;
+ uint8_t verify[DIGEST256_LEN];
+ } s;
+ uint8_t *si = s.secret_input, *ai = s.auth_input;
+ const curve25519_keypair_t *keypair_bB;
+ int bad;
+
+ /* Decode the onion skin */
+ /* XXXX Does this possible early-return business threaten our security? */
+ if (tor_memneq(onion_skin, my_node_id, DIGEST_LEN))
+ return -1;
+ /* Note that on key-not-found, we go through with this operation anyway,
+ * using "junk_keys". This will result in failed authentication, but won't
+ * leak whether we recognized the key. */
+ keypair_bB = dimap_search(private_keys, onion_skin + DIGEST_LEN,
+ (void*)junk_keys);
+ if (!keypair_bB)
+ return -1;
+
+ memcpy(s.pubkey_X.public_key, onion_skin+DIGEST_LEN+DIGEST256_LEN,
+ CURVE25519_PUBKEY_LEN);
+
+ /* Make y, Y */
+ curve25519_secret_key_generate(&s.seckey_y, 0);
+ curve25519_public_key_generate(&s.pubkey_Y, &s.seckey_y);
+
+ /* NOTE: If we ever use a group other than curve25519, or a different
+ * representation for its points, we may need to perform different or
+ * additional checks on X here and on Y in the client handshake, or lose our
+ * security properties. What checks we need would depend on the properties
+ * of the group and its representation.
+ *
+ * In short: if you use anything other than curve25519, this aspect of the
+ * code will need to be reconsidered carefully. */
+
+ /* build secret_input */
+ curve25519_handshake(si, &s.seckey_y, &s.pubkey_X);
+ bad = safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN);
+ si += CURVE25519_OUTPUT_LEN;
+ curve25519_handshake(si, &keypair_bB->seckey, &s.pubkey_X);
+ bad |= safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN);
+ si += CURVE25519_OUTPUT_LEN;
+
+ APPEND(si, my_node_id, DIGEST_LEN);
+ APPEND(si, keypair_bB->pubkey.public_key, CURVE25519_PUBKEY_LEN);
+ APPEND(si, s.pubkey_X.public_key, CURVE25519_PUBKEY_LEN);
+ APPEND(si, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN);
+ APPEND(si, PROTOID, PROTOID_LEN);
+ tor_assert(si == s.secret_input + sizeof(s.secret_input));
+
+ /* Compute hashes of secret_input */
+ h_tweak(s.verify, s.secret_input, sizeof(s.secret_input), T->t_verify);
+
+ /* Compute auth_input */
+ APPEND(ai, s.verify, DIGEST256_LEN);
+ APPEND(ai, my_node_id, DIGEST_LEN);
+ APPEND(ai, keypair_bB->pubkey.public_key, CURVE25519_PUBKEY_LEN);
+ APPEND(ai, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN);
+ APPEND(ai, s.pubkey_X.public_key, CURVE25519_PUBKEY_LEN);
+ APPEND(ai, PROTOID, PROTOID_LEN);
+ APPEND(ai, SERVER_STR, SERVER_STR_LEN);
+ tor_assert(ai == s.auth_input + sizeof(s.auth_input));
+
+ /* Build the reply */
+ memcpy(handshake_reply_out, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN);
+ h_tweak(handshake_reply_out+CURVE25519_PUBKEY_LEN,
+ s.auth_input, sizeof(s.auth_input),
+ T->t_mac);
+
+ /* Generate the key material */
+ crypto_expand_key_material_rfc5869_sha256(
+ s.secret_input, sizeof(s.secret_input),
+ (const uint8_t*)T->t_key, strlen(T->t_key),
+ (const uint8_t*)T->m_expand, strlen(T->m_expand),
+ key_out, key_out_len);
+
+ /* Wipe all of our local state */
+ memwipe(&s, 0, sizeof(s));
+
+ return bad ? -1 : 0;
+}
+
+/**
+ * Perform the final client side of the ntor handshake, using the state in
+ * <b>handshake_state</b> and the server's NTOR_REPLY_LEN-byte reply in
+ * <b>handshake_reply</b>. Generate <b>key_out_len</b> bytes of key material
+ * in <b>key_out</b>. Return 0 on success, -1 on failure.
+ */
+int
+onion_skin_ntor_client_handshake(
+ const ntor_handshake_state_t *handshake_state,
+ const uint8_t *handshake_reply,
+ uint8_t *key_out,
+ size_t key_out_len)
+{
+ const tweakset_t *T = &proto1_tweaks;
+ /* Sensitive stack-allocated material. Kept in an anonymous struct to make
+ * it easy to wipe. */
+ struct {
+ curve25519_public_key_t pubkey_Y;
+ uint8_t secret_input[SECRET_INPUT_LEN];
+ uint8_t verify[DIGEST256_LEN];
+ uint8_t auth_input[AUTH_INPUT_LEN];
+ uint8_t auth[DIGEST256_LEN];
+ } s;
+ uint8_t *ai = s.auth_input, *si = s.secret_input;
+ const uint8_t *auth_candidate;
+ int bad;
+
+ /* Decode input */
+ memcpy(s.pubkey_Y.public_key, handshake_reply, CURVE25519_PUBKEY_LEN);
+ auth_candidate = handshake_reply + CURVE25519_PUBKEY_LEN;
+
+ /* See note in server_handshake above about checking points. The
+ * circumstances under which we'd need to check Y for membership are
+ * different than those under which we'd be checking X. */
+
+ /* Compute secret_input */
+ curve25519_handshake(si, &handshake_state->seckey_x, &s.pubkey_Y);
+ bad = safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN);
+ si += CURVE25519_OUTPUT_LEN;
+ curve25519_handshake(si, &handshake_state->seckey_x,
+ &handshake_state->pubkey_B);
+ bad |= safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN);
+ si += CURVE25519_OUTPUT_LEN;
+ APPEND(si, handshake_state->router_id, DIGEST_LEN);
+ APPEND(si, handshake_state->pubkey_B.public_key, CURVE25519_PUBKEY_LEN);
+ APPEND(si, handshake_state->pubkey_X.public_key, CURVE25519_PUBKEY_LEN);
+ APPEND(si, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN);
+ APPEND(si, PROTOID, PROTOID_LEN);
+ tor_assert(si == s.secret_input + sizeof(s.secret_input));
+
+ /* Compute verify from secret_input */
+ h_tweak(s.verify, s.secret_input, sizeof(s.secret_input), T->t_verify);
+
+ /* Compute auth_input */
+ APPEND(ai, s.verify, DIGEST256_LEN);
+ APPEND(ai, handshake_state->router_id, DIGEST_LEN);
+ APPEND(ai, handshake_state->pubkey_B.public_key, CURVE25519_PUBKEY_LEN);
+ APPEND(ai, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN);
+ APPEND(ai, handshake_state->pubkey_X.public_key, CURVE25519_PUBKEY_LEN);
+ APPEND(ai, PROTOID, PROTOID_LEN);
+ APPEND(ai, SERVER_STR, SERVER_STR_LEN);
+ tor_assert(ai == s.auth_input + sizeof(s.auth_input));
+
+ /* Compute auth */
+ h_tweak(s.auth, s.auth_input, sizeof(s.auth_input), T->t_mac);
+
+ bad |= tor_memneq(s.auth, auth_candidate, DIGEST256_LEN);
+
+ crypto_expand_key_material_rfc5869_sha256(
+ s.secret_input, sizeof(s.secret_input),
+ (const uint8_t*)T->t_key, strlen(T->t_key),
+ (const uint8_t*)T->m_expand, strlen(T->m_expand),
+ key_out, key_out_len);
+
+ memwipe(&s, 0, sizeof(s));
+ return bad ? -1 : 0;
+}
+
diff --git a/src/or/onion_ntor.h b/src/or/onion_ntor.h
new file mode 100644
index 0000000000..c942e6e0f0
--- /dev/null
+++ b/src/or/onion_ntor.h
@@ -0,0 +1,63 @@
+/* Copyright (c) 2012-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_ONION_NTOR_H
+#define TOR_ONION_NTOR_H
+
+#include "torint.h"
+#include "crypto_curve25519.h"
+#include "di_ops.h"
+
+/** State to be maintained by a client between sending an ntor onionskin
+ * and receiving a reply. */
+typedef struct ntor_handshake_state_t ntor_handshake_state_t;
+
+/** Length of an ntor onionskin, as sent from the client to server. */
+#define NTOR_ONIONSKIN_LEN 84
+/** Length of an ntor reply, as sent from server to client. */
+#define NTOR_REPLY_LEN 64
+
+#ifdef CURVE25519_ENABLED
+void ntor_handshake_state_free(ntor_handshake_state_t *state);
+
+int onion_skin_ntor_create(const uint8_t *router_id,
+ const curve25519_public_key_t *router_key,
+ ntor_handshake_state_t **handshake_state_out,
+ uint8_t *onion_skin_out);
+
+int onion_skin_ntor_server_handshake(const uint8_t *onion_skin,
+ const di_digest256_map_t *private_keys,
+ const curve25519_keypair_t *junk_keypair,
+ const uint8_t *my_node_id,
+ uint8_t *handshake_reply_out,
+ uint8_t *key_out,
+ size_t key_out_len);
+
+int onion_skin_ntor_client_handshake(
+ const ntor_handshake_state_t *handshake_state,
+ const uint8_t *handshake_reply,
+ uint8_t *key_out,
+ size_t key_out_len);
+
+#ifdef ONION_NTOR_PRIVATE
+
+/** Storage held by a client while waiting for an ntor reply from a server. */
+struct ntor_handshake_state_t {
+ /** Identity digest of the router we're talking to. */
+ uint8_t router_id[DIGEST_LEN];
+ /** Onion key of the router we're talking to. */
+ curve25519_public_key_t pubkey_B;
+
+ /**
+ * Short-lived keypair for use with this handshake.
+ * @{ */
+ curve25519_secret_key_t seckey_x;
+ curve25519_public_key_t pubkey_X;
+ /** @} */
+};
+#endif
+
+#endif
+
+#endif
+
diff --git a/src/or/onion_tap.c b/src/or/onion_tap.c
new file mode 100644
index 0000000000..3782e75abf
--- /dev/null
+++ b/src/or/onion_tap.c
@@ -0,0 +1,218 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file onion_tap.c
+ * \brief Functions to implement the original Tor circuit extension handshake
+ * (a.k.a TAP).
+ *
+ * We didn't call it "TAP" ourselves -- Ian Goldberg named it in "On the
+ * Security of the Tor Authentication Protocol". (Spoiler: it's secure, but
+ * its security is kind of fragile and implementation dependent. Never modify
+ * this implementation without reading and understanding that paper at least.)
+ **/
+
+#include "or.h"
+#include "config.h"
+#include "onion_tap.h"
+#include "rephist.h"
+
+/*----------------------------------------------------------------------*/
+
+/** Given a router's 128 byte public key,
+ * stores the following in onion_skin_out:
+ * - [42 bytes] OAEP padding
+ * - [16 bytes] Symmetric key for encrypting blob past RSA
+ * - [70 bytes] g^x part 1 (inside the RSA)
+ * - [58 bytes] g^x part 2 (symmetrically encrypted)
+ *
+ * Stores the DH private key into handshake_state_out for later completion
+ * of the handshake.
+ *
+ * The meeting point/cookies and auth are zeroed out for now.
+ */
+int
+onion_skin_TAP_create(crypto_pk_t *dest_router_key,
+ crypto_dh_t **handshake_state_out,
+ char *onion_skin_out) /* TAP_ONIONSKIN_CHALLENGE_LEN bytes */
+{
+ char challenge[DH_KEY_LEN];
+ crypto_dh_t *dh = NULL;
+ int dhbytes, pkbytes;
+
+ tor_assert(dest_router_key);
+ tor_assert(handshake_state_out);
+ tor_assert(onion_skin_out);
+ *handshake_state_out = NULL;
+ memset(onion_skin_out, 0, TAP_ONIONSKIN_CHALLENGE_LEN);
+
+ if (!(dh = crypto_dh_new(DH_TYPE_CIRCUIT)))
+ goto err;
+
+ dhbytes = crypto_dh_get_bytes(dh);
+ pkbytes = (int) crypto_pk_keysize(dest_router_key);
+ tor_assert(dhbytes == 128);
+ tor_assert(pkbytes == 128);
+
+ if (crypto_dh_get_public(dh, challenge, dhbytes))
+ goto err;
+
+ note_crypto_pk_op(ENC_ONIONSKIN);
+
+ /* set meeting point, meeting cookie, etc here. Leave zero for now. */
+ if (crypto_pk_public_hybrid_encrypt(dest_router_key, onion_skin_out,
+ TAP_ONIONSKIN_CHALLENGE_LEN,
+ challenge, DH_KEY_LEN,
+ PK_PKCS1_OAEP_PADDING, 1)<0)
+ goto err;
+
+ memwipe(challenge, 0, sizeof(challenge));
+ *handshake_state_out = dh;
+
+ return 0;
+ err:
+ memwipe(challenge, 0, sizeof(challenge));
+ if (dh) crypto_dh_free(dh);
+ return -1;
+}
+
+/** Given an encrypted DH public key as generated by onion_skin_create,
+ * and the private key for this onion router, generate the reply (128-byte
+ * DH plus the first 20 bytes of shared key material), and store the
+ * next key_out_len bytes of key material in key_out.
+ */
+int
+onion_skin_TAP_server_handshake(
+ /*TAP_ONIONSKIN_CHALLENGE_LEN*/
+ const char *onion_skin,
+ crypto_pk_t *private_key,
+ crypto_pk_t *prev_private_key,
+ /*TAP_ONIONSKIN_REPLY_LEN*/
+ char *handshake_reply_out,
+ char *key_out,
+ size_t key_out_len)
+{
+ char challenge[TAP_ONIONSKIN_CHALLENGE_LEN];
+ crypto_dh_t *dh = NULL;
+ ssize_t len;
+ char *key_material=NULL;
+ size_t key_material_len=0;
+ int i;
+ crypto_pk_t *k;
+
+ len = -1;
+ for (i=0;i<2;++i) {
+ k = i==0?private_key:prev_private_key;
+ if (!k)
+ break;
+ note_crypto_pk_op(DEC_ONIONSKIN);
+ len = crypto_pk_private_hybrid_decrypt(k, challenge,
+ TAP_ONIONSKIN_CHALLENGE_LEN,
+ onion_skin,
+ TAP_ONIONSKIN_CHALLENGE_LEN,
+ PK_PKCS1_OAEP_PADDING,0);
+ if (len>0)
+ break;
+ }
+ if (len<0) {
+ log_info(LD_PROTOCOL,
+ "Couldn't decrypt onionskin: client may be using old onion key");
+ goto err;
+ } else if (len != DH_KEY_LEN) {
+ log_warn(LD_PROTOCOL, "Unexpected onionskin length after decryption: %ld",
+ (long)len);
+ goto err;
+ }
+
+ dh = crypto_dh_new(DH_TYPE_CIRCUIT);
+ if (!dh) {
+ log_warn(LD_BUG, "Couldn't allocate DH key");
+ goto err;
+ }
+ if (crypto_dh_get_public(dh, handshake_reply_out, DH_KEY_LEN)) {
+ log_info(LD_GENERAL, "crypto_dh_get_public failed.");
+ goto err;
+ }
+
+ key_material_len = DIGEST_LEN+key_out_len;
+ key_material = tor_malloc(key_material_len);
+ len = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, dh, challenge,
+ DH_KEY_LEN, key_material,
+ key_material_len);
+ if (len < 0) {
+ log_info(LD_GENERAL, "crypto_dh_compute_secret failed.");
+ goto err;
+ }
+
+ /* send back H(K|0) as proof that we learned K. */
+ memcpy(handshake_reply_out+DH_KEY_LEN, key_material, DIGEST_LEN);
+
+ /* use the rest of the key material for our shared keys, digests, etc */
+ memcpy(key_out, key_material+DIGEST_LEN, key_out_len);
+
+ memwipe(challenge, 0, sizeof(challenge));
+ memwipe(key_material, 0, key_material_len);
+ tor_free(key_material);
+ crypto_dh_free(dh);
+ return 0;
+ err:
+ memwipe(challenge, 0, sizeof(challenge));
+ if (key_material) {
+ memwipe(key_material, 0, key_material_len);
+ tor_free(key_material);
+ }
+ if (dh) crypto_dh_free(dh);
+
+ return -1;
+}
+
+/** Finish the client side of the DH handshake.
+ * Given the 128 byte DH reply + 20 byte hash as generated by
+ * onion_skin_server_handshake and the handshake state generated by
+ * onion_skin_create, verify H(K) with the first 20 bytes of shared
+ * key material, then generate key_out_len more bytes of shared key
+ * material and store them in key_out.
+ *
+ * After the invocation, call crypto_dh_free on handshake_state.
+ */
+int
+onion_skin_TAP_client_handshake(crypto_dh_t *handshake_state,
+ const char *handshake_reply, /* TAP_ONIONSKIN_REPLY_LEN bytes */
+ char *key_out,
+ size_t key_out_len)
+{
+ ssize_t len;
+ char *key_material=NULL;
+ size_t key_material_len;
+ tor_assert(crypto_dh_get_bytes(handshake_state) == DH_KEY_LEN);
+
+ key_material_len = DIGEST_LEN + key_out_len;
+ key_material = tor_malloc(key_material_len);
+ len = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, handshake_state,
+ handshake_reply, DH_KEY_LEN, key_material,
+ key_material_len);
+ if (len < 0)
+ goto err;
+
+ if (tor_memneq(key_material, handshake_reply+DH_KEY_LEN, DIGEST_LEN)) {
+ /* H(K) does *not* match. Something fishy. */
+ log_warn(LD_PROTOCOL,"Digest DOES NOT MATCH on onion handshake. "
+ "Bug or attack.");
+ goto err;
+ }
+
+ /* use the rest of the key material for our shared keys, digests, etc */
+ memcpy(key_out, key_material+DIGEST_LEN, key_out_len);
+
+ memwipe(key_material, 0, key_material_len);
+ tor_free(key_material);
+ return 0;
+ err:
+ memwipe(key_material, 0, key_material_len);
+ tor_free(key_material);
+ return -1;
+}
+
diff --git a/src/or/onion_tap.h b/src/or/onion_tap.h
new file mode 100644
index 0000000000..b978b66737
--- /dev/null
+++ b/src/or/onion_tap.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file onion_tap.h
+ * \brief Header file for onion_tap.c.
+ **/
+
+#ifndef TOR_ONION_TAP_H
+#define TOR_ONION_TAP_H
+
+#define TAP_ONIONSKIN_CHALLENGE_LEN (PKCS1_OAEP_PADDING_OVERHEAD+\
+ CIPHER_KEY_LEN+\
+ DH_KEY_LEN)
+#define TAP_ONIONSKIN_REPLY_LEN (DH_KEY_LEN+DIGEST_LEN)
+
+int onion_skin_TAP_create(crypto_pk_t *router_key,
+ crypto_dh_t **handshake_state_out,
+ char *onion_skin_out);
+
+int onion_skin_TAP_server_handshake(const char *onion_skin,
+ crypto_pk_t *private_key,
+ crypto_pk_t *prev_private_key,
+ char *handshake_reply_out,
+ char *key_out,
+ size_t key_out_len);
+
+int onion_skin_TAP_client_handshake(crypto_dh_t *handshake_state,
+ const char *handshake_reply,
+ char *key_out,
+ size_t key_out_len);
+
+#endif
+
diff --git a/src/or/or.h b/src/or/or.h
index 356953b436..d9da49d32d 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -81,7 +81,6 @@
#include <process.h>
#include <direct.h>
#include <windows.h>
-#define snprintf _snprintf
#endif
#ifdef USE_BUFFEREVENTS
@@ -99,6 +98,7 @@
#include "compat_libevent.h"
#include "ht.h"
#include "replaycache.h"
+#include "crypto_curve25519.h"
/* These signals are defined to help handle_control_signal work.
*/
@@ -279,6 +279,7 @@ typedef enum {
#define CPUWORKER_STATE_MAX_ 2
#define CPUWORKER_TASK_ONION CPUWORKER_STATE_BUSY_ONION
+#define CPUWORKER_TASK_SHUTDOWN 255
#define OR_CONN_STATE_MIN_ 1
/** State for a connection to an OR: waiting for connect() to finish. */
@@ -520,7 +521,9 @@ typedef enum {
#define CIRCUIT_PURPOSE_TESTING 18
/** A controller made this circuit and Tor should not use it. */
#define CIRCUIT_PURPOSE_CONTROLLER 19
-#define CIRCUIT_PURPOSE_MAX_ 19
+/** This circuit is used for path bias probing only */
+#define CIRCUIT_PURPOSE_PATH_BIAS_TESTING 20
+#define CIRCUIT_PURPOSE_MAX_ 20
/** A catch-all for unrecognized purposes. Currently we don't expect
* to make or see any circuits with this purpose. */
#define CIRCUIT_PURPOSE_UNKNOWN 255
@@ -560,6 +563,8 @@ typedef enum {
#define RELAY_COMMAND_RESOLVE 11
#define RELAY_COMMAND_RESOLVED 12
#define RELAY_COMMAND_BEGIN_DIR 13
+#define RELAY_COMMAND_EXTEND2 14
+#define RELAY_COMMAND_EXTENDED2 15
#define RELAY_COMMAND_ESTABLISH_INTRO 32
#define RELAY_COMMAND_ESTABLISH_RENDEZVOUS 33
@@ -826,6 +831,8 @@ typedef enum {
#define CELL_VERSIONS 7
#define CELL_NETINFO 8
#define CELL_RELAY_EARLY 9
+#define CELL_CREATE2 10
+#define CELL_CREATED2 11
#define CELL_VPADDING 128
#define CELL_CERTS 129
@@ -1243,6 +1250,36 @@ typedef struct listener_connection_t {
uint8_t isolation_flags;
/**@}*/
+ /** For a SOCKS listeners, these fields describe whether we should
+ * allow IPv4 and IPv6 addresses from our exit nodes, respectively.
+ *
+ * @{
+ */
+ unsigned int socks_ipv4_traffic : 1;
+ unsigned int socks_ipv6_traffic : 1;
+ /** @} */
+ /** For a socks listener: should we tell the exit that we prefer IPv6
+ * addresses? */
+ unsigned int socks_prefer_ipv6 : 1;
+
+ /** For a socks listener: should we cache IPv4/IPv6 DNS information that
+ * exit nodes tell us?
+ *
+ * @{ */
+ unsigned int cache_ipv4_answers : 1;
+ unsigned int cache_ipv6_answers : 1;
+ /** @} */
+ /** For a socks listeners: if we find an answer in our client-side DNS cache,
+ * should we use it?
+ *
+ * @{ */
+ unsigned int use_cached_ipv4_answers : 1;
+ unsigned int use_cached_ipv6_answers : 1;
+ /** @} */
+ /** For socks listeners: When we can automap an address to IPv4 or IPv6,
+ * do we prefer IPv6? */
+ unsigned int prefer_ipv6_virtaddr : 1;
+
} listener_connection_t;
/** Minimum length of the random part of an AUTH_CHALLENGE cell. */
@@ -1387,6 +1424,7 @@ typedef struct or_connection_t {
or_handshake_state_t *handshake_state; /**< If we are setting this connection
* up, state information to do so. */
+
time_t timestamp_lastempty; /**< When was the outbuf last completely empty?*/
time_t timestamp_last_added_nonpadding; /** When did we last add a
* non-padding cell to the outbuf? */
@@ -1433,6 +1471,8 @@ typedef struct edge_connection_t {
uint32_t address_ttl; /**< TTL for address-to-addr mapping on exit
* connection. Exit connections only. */
+ uint32_t begincell_flags; /** Flags sent or received in the BEGIN cell
+ * for this connection */
streamid_t stream_id; /**< The stream ID used for this edge connection on its
* circuit */
@@ -1448,6 +1488,8 @@ typedef struct edge_connection_t {
/** True iff this connection is for a DNS request only. */
unsigned int is_dns_request:1;
+ /** True iff this connection is for a PTR DNS request. (exit only) */
+ unsigned int is_reverse_dns_lookup:1;
unsigned int edge_has_sent_end:1; /**< For debugging; only used on edge
* connections. Set once we've set the stream end,
@@ -1539,8 +1581,42 @@ typedef struct entry_connection_t {
*/
unsigned int may_use_optimistic_data : 1;
+ /** Should we permit IPv4 and IPv6 traffic to use this connection?
+ *
+ * @{ */
+ unsigned int ipv4_traffic_ok : 1;
+ unsigned int ipv6_traffic_ok : 1;
+ /** @} */
+ /** Should we say we prefer IPv6 traffic? */
+ unsigned int prefer_ipv6_traffic : 1;
+
+ /** For a socks listener: should we cache IPv4/IPv6 DNS information that
+ * exit nodes tell us?
+ *
+ * @{ */
+ unsigned int cache_ipv4_answers : 1;
+ unsigned int cache_ipv6_answers : 1;
+ /** @} */
+ /** For a socks listeners: if we find an answer in our client-side DNS cache,
+ * should we use it?
+ *
+ * @{ */
+ unsigned int use_cached_ipv4_answers : 1;
+ unsigned int use_cached_ipv6_answers : 1;
+ /** @} */
+ /** For socks listeners: When we can automap an address to IPv4 or IPv6,
+ * do we prefer IPv6? */
+ unsigned int prefer_ipv6_virtaddr : 1;
+
} entry_connection_t;
+typedef enum {
+ DIR_SPOOL_NONE=0, DIR_SPOOL_SERVER_BY_DIGEST, DIR_SPOOL_SERVER_BY_FP,
+ DIR_SPOOL_EXTRA_BY_DIGEST, DIR_SPOOL_EXTRA_BY_FP,
+ DIR_SPOOL_CACHED_DIR, DIR_SPOOL_NETWORKSTATUS,
+ DIR_SPOOL_MICRODESC, /* NOTE: if we add another entry, add another bit. */
+} dir_spool_source_t;
+
/** Subtype of connection_t for an "directory connection" -- that is, an HTTP
* connection to retrieve or serve directory material. */
typedef struct dir_connection_t {
@@ -1559,12 +1635,8 @@ typedef struct dir_connection_t {
* "spooling" of directory material to the outbuf. Otherwise, we'd have
* to append everything to the outbuf in one enormous chunk. */
/** What exactly are we spooling right now? */
- enum {
- DIR_SPOOL_NONE=0, DIR_SPOOL_SERVER_BY_DIGEST, DIR_SPOOL_SERVER_BY_FP,
- DIR_SPOOL_EXTRA_BY_DIGEST, DIR_SPOOL_EXTRA_BY_FP,
- DIR_SPOOL_CACHED_DIR, DIR_SPOOL_NETWORKSTATUS,
- DIR_SPOOL_MICRODESC, /* NOTE: if we add another entry, add another bit. */
- } dir_spool_src : 3;
+ ENUM_BF(dir_spool_source_t) dir_spool_src : 3;
+
/** If we're fetching descriptors, what router purpose shall we assign
* to them? */
uint8_t router_purpose;
@@ -1740,7 +1812,8 @@ typedef enum {
/** A reference-counted address policy rule. */
typedef struct addr_policy_t {
int refcnt; /**< Reference count */
- addr_policy_action_t policy_type:2;/**< What to do when the policy matches.*/
+ /** What to do when the policy matches.*/
+ ENUM_BF(addr_policy_action_t) policy_type:2;
unsigned int is_private:1; /**< True iff this is the pseudo-address,
* "private". */
unsigned int is_canonical:1; /**< True iff this policy is the canonical
@@ -1749,7 +1822,15 @@ typedef struct addr_policy_t {
maskbits_t maskbits; /**< Accept/reject all addresses <b>a</b> such that the
* first <b>maskbits</b> bits of <b>a</b> match
* <b>addr</b>. */
- tor_addr_t addr; /**< Base address to accept or reject. */
+ /** Base address to accept or reject.
+ *
+ * Note that wildcards are treated
+ * differntly depending on address family. An AF_UNSPEC address means
+ * "All addresses, IPv4 or IPv6." An AF_INET address with maskbits==0 means
+ * "All IPv4 addresses" and an AF_INET6 address with maskbits == 0 means
+ * "All IPv6 addresses".
+ **/
+ tor_addr_t addr;
uint16_t prt_min; /**< Lowest port number to accept/reject. */
uint16_t prt_max; /**< Highest port number to accept/reject. */
} addr_policy_t;
@@ -1800,7 +1881,7 @@ typedef struct download_status_t {
* again? */
uint8_t n_download_failures; /**< Number of failures trying to download the
* most recent descriptor. */
- download_schedule_t schedule : 8;
+ ENUM_BF(download_schedule_t) schedule : 8;
} download_status_t;
/** If n_download_failures is this high, the download can never happen. */
@@ -1879,6 +1960,8 @@ typedef struct {
crypto_pk_t *onion_pkey; /**< Public RSA key for onions. */
crypto_pk_t *identity_pkey; /**< Public RSA key for signing. */
+ /** Public curve25519 key for onions */
+ curve25519_public_key_t *onion_curve25519_pkey;
char *platform; /**< What software/operating system is this OR using? */
@@ -1889,7 +1972,10 @@ typedef struct {
/** How many bytes/s is this router known to handle? */
uint32_t bandwidthcapacity;
smartlist_t *exit_policy; /**< What streams will this OR permit
- * to exit? NULL for 'reject *:*'. */
+ * to exit on IPv4? NULL for 'reject *:*'. */
+ /** What streams will this OR permit to exit on IPv6?
+ * NULL for 'reject *:*' */
+ struct short_policy_t *ipv6_exit_policy;
long uptime; /**< How many seconds the router claims to have been up */
smartlist_t *declared_family; /**< Nicknames of router which this router
* claims are its family. */
@@ -1999,6 +2085,9 @@ typedef struct routerstatus_t {
/** True iff this router is a version that allows DATA cells to arrive on
* a stream before it has sent a CONNECTED cell. */
unsigned int version_supports_optimistic_data:1;
+ /** True iff this router has a version that allows it to accept EXTEND2
+ * cells */
+ unsigned int version_supports_extend2_cells:1;
unsigned int has_bandwidth:1; /**< The vote/consensus had bw info */
unsigned int has_exitsummary:1; /**< The vote/consensus had exit summaries */
@@ -2061,7 +2150,7 @@ typedef struct microdesc_t {
*/
time_t last_listed;
/** Where is this microdescriptor currently stored? */
- saved_location_t saved_location : 3;
+ ENUM_BF(saved_location_t) saved_location : 3;
/** If true, do not attempt to cache this microdescriptor on disk. */
unsigned int no_save : 1;
/** If true, this microdesc has an entry in the microdesc_map */
@@ -2089,14 +2178,19 @@ typedef struct microdesc_t {
/** As routerinfo_t.onion_pkey */
crypto_pk_t *onion_pkey;
+ /** As routerinfo_t.onion_curve25519_pkey */
+ curve25519_public_key_t *onion_curve25519_pkey;
/** As routerinfo_t.ipv6_add */
tor_addr_t ipv6_addr;
/** As routerinfo_t.ipv6_orport */
uint16_t ipv6_orport;
/** As routerinfo_t.family */
smartlist_t *family;
- /** Exit policy summary */
+ /** IPv4 exit policy summary */
short_policy_t *exit_policy;
+ /** IPv6 exit policy summary */
+ short_policy_t *ipv6_exit_policy;
+
} microdesc_t;
/** A node_t represents a Tor router.
@@ -2306,8 +2400,8 @@ typedef enum {
/** A common structure to hold a v3 network status vote, or a v3 network
* status consensus. */
typedef struct networkstatus_t {
- networkstatus_type_t type : 8; /**< Vote, consensus, or opinion? */
- consensus_flavor_t flavor : 8; /**< If a consensus, what kind? */
+ ENUM_BF(networkstatus_type_t) type : 8; /**< Vote, consensus, or opinion? */
+ ENUM_BF(consensus_flavor_t) flavor : 8; /**< If a consensus, what kind? */
time_t published; /**< Vote only: Time when vote was written. */
time_t valid_after; /**< Time after which this vote or consensus applies. */
time_t fresh_until; /**< Time before which this is the most recent vote or
@@ -2445,6 +2539,9 @@ typedef struct extend_info_t {
uint16_t port; /**< OR port. */
tor_addr_t addr; /**< IP address. */
crypto_pk_t *onion_key; /**< Current onionskin key. */
+#ifdef CURVE25519_ENABLED
+ curve25519_public_key_t curve25519_onion_key;
+#endif
} extend_info_t;
/** Certificate for v3 directory protocol: binds long-term authority identity
@@ -2497,8 +2594,25 @@ typedef enum {
MICRODESC_DIRINFO=1 << 6,
} dirinfo_type_t;
+#define ALL_DIRINFO ((dirinfo_type_t)((1<<7)-1))
+
#define CRYPT_PATH_MAGIC 0x70127012u
+struct fast_handshake_state_t;
+struct ntor_handshake_state_t;
+#define ONION_HANDSHAKE_TYPE_TAP 0x0000
+#define ONION_HANDSHAKE_TYPE_FAST 0x0001
+#define ONION_HANDSHAKE_TYPE_NTOR 0x0002
+#define MAX_ONION_HANDSHAKE_TYPE 0x0002
+typedef struct {
+ uint16_t tag;
+ union {
+ struct fast_handshake_state_t *fast;
+ crypto_dh_t *tap;
+ struct ntor_handshake_state_t *ntor;
+ } u;
+} onion_handshake_state_t;
+
/** Holds accounting information for a single step in the layered encryption
* performed by a circuit. Used only at the client edge of a circuit. */
typedef struct crypt_path_t {
@@ -2517,17 +2631,15 @@ typedef struct crypt_path_t {
/** Digest state for cells heading away from the OR at this step. */
crypto_digest_t *b_digest;
- /** Current state of Diffie-Hellman key negotiation with the OR at this
+ /** Current state of the handshake as performed with the OR at this
* step. */
- crypto_dh_t *dh_handshake_state;
- /** Current state of 'fast' (non-PK) key negotiation with the OR at this
- * step. Used to save CPU when TLS is already providing all the
- * authentication, secrecy, and integrity we need, and we're already
- * distinguishable from an OR.
- */
- uint8_t fast_handshake_state[DIGEST_LEN];
+ onion_handshake_state_t handshake_state;
+ /** Diffie-hellman handshake state for performing an introduction
+ * operations */
+ crypto_dh_t *rend_dh_handshake_state;
+
/** Negotiated key material shared with the OR at this step. */
- char handshake_digest[DIGEST_LEN];/* KH in tor-spec.txt */
+ char rend_circ_nonce[DIGEST_LEN];/* KH in tor-spec.txt */
/** Information to extend to the OR at this step. */
extend_info_t *extend_info;
@@ -2568,10 +2680,6 @@ typedef struct {
#define CPATH_KEY_MATERIAL_LEN (20*2+16*2)
#define DH_KEY_LEN DH_BYTES
-#define ONIONSKIN_CHALLENGE_LEN (PKCS1_OAEP_PADDING_OVERHEAD+\
- CIPHER_KEY_LEN+\
- DH_KEY_LEN)
-#define ONIONSKIN_REPLY_LEN (DH_KEY_LEN+DIGEST_LEN)
/** Information used to build a circuit. */
typedef struct {
@@ -2603,6 +2711,8 @@ typedef struct {
#define ORIGIN_CIRCUIT_MAGIC 0x35315243u
#define OR_CIRCUIT_MAGIC 0x98ABC04Fu
+struct create_cell_t;
+
/**
* A circuit is a path over the onion routing
* network. Applications can connect to one end of the circuit, and can
@@ -2677,15 +2787,24 @@ typedef struct circuit_t {
* more. */
int deliver_window;
- /** For storage while n_chan is pending
- * (state CIRCUIT_STATE_CHAN_WAIT). When defined, it is always
- * length ONIONSKIN_CHALLENGE_LEN. */
- char *n_chan_onionskin;
+ /** For storage while n_chan is pending (state CIRCUIT_STATE_CHAN_WAIT). */
+ struct create_cell_t *n_chan_create_cell;
- /** When was this circuit created? We keep this timestamp with a higher
- * resolution than most so that the circuit-build-time tracking code can
- * get millisecond resolution. */
+ /** When did circuit construction actually begin (ie send the
+ * CREATE cell or begin cannibalization).
+ *
+ * Note: This timer will get reset if we decide to cannibalize
+ * a circuit. It may also get reset during certain phases of hidden
+ * service circuit use.
+ *
+ * We keep this timestamp with a higher resolution than most so that the
+ * circuit-build-time tracking code can get millisecond resolution.
+ */
+ struct timeval timestamp_began;
+
+ /** This timestamp marks when the init_circuit_base constructor ran. */
struct timeval timestamp_created;
+
/** When the circuit was first used, or 0 if the circuit is clean.
*
* XXXX023 Note that some code will artifically adjust this value backward
@@ -2727,19 +2846,58 @@ typedef struct circuit_t {
/**
* Describes the circuit building process in simplified terms based
- * on the path bias accounting state for a circuit. Created to prevent
- * overcounting due to unknown cases of circuit reuse. See Bug #6475.
+ * on the path bias accounting state for a circuit.
+ *
+ * NOTE: These state values are enumerated in the order for which we
+ * expect circuits to transition through them. If you add states,
+ * you need to preserve this overall ordering. The various pathbias
+ * state transition and accounting functions (pathbias_mark_* and
+ * pathbias_count_*) contain ordinal comparisons to enforce proper
+ * state transitions for corrections.
+ *
+ * This state machine and the associated logic was created to prevent
+ * miscounting due to unknown cases of circuit reuse. See also tickets
+ * #6475 and #7802.
*/
typedef enum {
/** This circuit is "new". It has not yet completed a first hop
* or been counted by the path bias code. */
PATH_STATE_NEW_CIRC = 0,
- /** This circuit has completed a first hop, and has been counted by
+ /** This circuit has completed one/two hops, and has been counted by
* the path bias logic. */
- PATH_STATE_DID_FIRST_HOP = 1,
- /** This circuit has been completely built, and has been counted as
- * successful by the path bias logic. */
- PATH_STATE_SUCCEEDED = 2,
+ PATH_STATE_BUILD_ATTEMPTED = 1,
+ /** This circuit has been completely built */
+ PATH_STATE_BUILD_SUCCEEDED = 2,
+ /** Did we try to attach any SOCKS streams or hidserv introductions to
+ * this circuit?
+ *
+ * Note: If we ever implement end-to-end stream timing through test
+ * stream probes (#5707), we must *not* set this for those probes
+ * (or any other automatic streams) because the adversary could
+ * just tag at a later point.
+ */
+ PATH_STATE_USE_ATTEMPTED = 3,
+ /** Did any SOCKS streams or hidserv introductions actually succeed on
+ * this circuit?
+ *
+ * If any streams detatch/fail from this circuit, the code transitions
+ * the circuit back to PATH_STATE_USE_ATTEMPTED to ensure we probe. See
+ * pathbias_mark_use_rollback() for that.
+ */
+ PATH_STATE_USE_SUCCEEDED = 4,
+
+ /**
+ * This is a special state to indicate that we got a corrupted
+ * relay cell on a circuit and we don't intend to probe it.
+ */
+ PATH_STATE_USE_FAILED = 5,
+
+ /**
+ * This is a special state to indicate that we already counted
+ * the circuit. Used to guard against potential state machine
+ * violations.
+ */
+ PATH_STATE_ALREADY_COUNTED = 6,
} path_state_t;
/** An origin_circuit_t holds data necessary to build and use a circuit.
@@ -2775,9 +2933,32 @@ typedef struct origin_circuit_t {
* cannibalized circuits. */
unsigned int has_opened : 1;
- /** Kludge to help us prevent the warn in bug #6475 and eventually
- * debug why we are not seeing first hops in some cases. */
- path_state_t path_state : 2;
+ /**
+ * Path bias state machine. Used to ensure integrity of our
+ * circuit building and usage accounting. See path_state_t
+ * for more details.
+ */
+ ENUM_BF(path_state_t) path_state : 3;
+
+ /**
+ * Tristate variable to guard against pathbias miscounting
+ * due to circuit purpose transitions changing the decision
+ * of pathbias_should_count(). This variable is informational
+ * only. The current results of pathbias_should_count() are
+ * the official decision for pathbias accounting.
+ */
+ uint8_t pathbias_shouldcount;
+#define PATHBIAS_SHOULDCOUNT_UNDECIDED 0
+#define PATHBIAS_SHOULDCOUNT_IGNORED 1
+#define PATHBIAS_SHOULDCOUNT_COUNTED 2
+
+ /** For path probing. Store the temporary probe stream ID
+ * for response comparison */
+ streamid_t pathbias_probe_id;
+
+ /** For path probing. Store the temporary probe address nonce
+ * (in host byte order) for response comparison. */
+ uint32_t pathbias_probe_nonce;
/** Set iff this is a hidden-service circuit which has timed out
* according to our current circuit-build timeout, but which has
@@ -2795,6 +2976,10 @@ typedef struct origin_circuit_t {
* service-side introduction circuits never have this flag set.) */
unsigned int hs_circ_has_timed_out : 1;
+ /** Set iff this circuit has been given a relaxed timeout because
+ * no circuits have opened. Used to prevent spamming logs. */
+ unsigned int relaxed_timeout : 1;
+
/** Set iff this is a service-side rendezvous circuit for which a
* new connection attempt has been launched. We consider launching
* a new service-side rend circ to a client when the previous one
@@ -2872,9 +3057,10 @@ typedef struct origin_circuit_t {
* ISO_STREAM. */
uint64_t associated_isolated_stream_global_id;
/**@}*/
-
} origin_circuit_t;
+struct onion_queue_t;
+
/** An or_circuit_t holds information needed to implement a circuit at an
* OR. */
typedef struct or_circuit_t {
@@ -2888,6 +3074,9 @@ typedef struct or_circuit_t {
* cells to p_chan. NULL if we have no cells pending, or if we're not
* linked to an OR connection. */
struct circuit_t *prev_active_on_p_chan;
+ /** Pointer to an entry on the onion queue, if this circuit is waiting for a
+ * chance to give an onionskin to a cpuworker. Used only in onion.c */
+ struct onion_queue_t *onionqueue_entry;
/** The circuit_id used in the previous (backward) hop of this circuit. */
circid_t p_circ_id;
@@ -2939,7 +3128,8 @@ typedef struct or_circuit_t {
char rend_token[REND_TOKEN_LEN];
/* ???? move to a subtype or adjunct structure? Wastes 20 bytes -NM */
- char handshake_digest[DIGEST_LEN]; /**< Stores KH for the handshake. */
+ /** Stores KH for the handshake. */
+ char rend_circ_nonce[DIGEST_LEN];/* KH in tor-spec.txt */
/** How many more relay_early cells can we send on this circuit, according
* to the specification? */
@@ -3045,8 +3235,31 @@ typedef struct port_cfg_t {
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;
+ unsigned int bind_ipv4_only : 1;
+ unsigned int bind_ipv6_only : 1;
+
+ /* Client port types only: */
+ unsigned int ipv4_traffic : 1;
+ unsigned int ipv6_traffic : 1;
+ unsigned int prefer_ipv6 : 1;
+
+ /** For a socks listener: should we cache IPv4/IPv6 DNS information that
+ * exit nodes tell us?
+ *
+ * @{ */
+ unsigned int cache_ipv4_answers : 1;
+ unsigned int cache_ipv6_answers : 1;
+ /** @} */
+ /** For a socks listeners: if we find an answer in our client-side DNS cache,
+ * should we use it?
+ *
+ * @{ */
+ unsigned int use_cached_ipv4_answers : 1;
+ unsigned int use_cached_ipv6_answers : 1;
+ /** @} */
+ /** For socks listeners: When we can automap an address to IPv4 or IPv6,
+ * do we prefer IPv6? */
+ unsigned int prefer_ipv6_virtaddr : 1;
/* Unix sockets only: */
/** Path for an AF_UNIX address */
@@ -3237,6 +3450,9 @@ typedef struct {
config_line_t *ServerTransportPlugin; /**< List of client
transport plugins. */
+ /** List of TCP/IP addresses that transports should listen at. */
+ config_line_t *ServerTransportListenAddr;
+
int BridgeRelay; /**< Boolean: are we acting as a bridge relay? We make
* this explicit so we can change how we behave in the
* future. */
@@ -3344,9 +3560,7 @@ typedef struct {
* and try a new circuit if the stream has been
* waiting for this many seconds. If zero, use
* our default internal timeout schedule. */
- int MaxOnionsPending; /**< How many circuit CREATE requests do we allow
- * to wait simultaneously before we start dropping
- * them? */
+ int MaxOnionQueueDelay; /**<DOCDOC*/
int NewCircuitPeriod; /**< How long do we use a circuit before building
* a new one? */
int MaxCircuitDirtiness; /**< Never use circs that were first used more than
@@ -3398,7 +3612,14 @@ typedef struct {
/** List of configuration lines for replacement directory authorities.
* If you just want to replace one class of authority at a time,
* use the "Alternate*Authority" options below instead. */
- config_line_t *DirServers;
+ config_line_t *DirAuthorities;
+
+ /** List of fallback directory servers */
+ config_line_t *FallbackDir;
+
+ /** Weight to apply to all directory authority rates if considering them
+ * along with fallbackdirs */
+ double DirAuthorityFallbackRate;
/** If set, use these main (currently v3) directory authorities and
* not the default ones. */
@@ -3508,6 +3729,9 @@ typedef struct {
int UseEntryGuards; /**< Boolean: Do we try to enter from a smallish number
* of fixed nodes? */
int NumEntryGuards; /**< How many entry guards do we try to establish? */
+ int UseEntryGuardsAsDirGuards; /** Boolean: Do we try to get directory info
+ * from a smallish number of fixed nodes? */
+ int NumDirectoryGuards; /**< How many dir guards do we try to establish? */
int RephistTrackTime; /**< How many seconds do we keep rephist info? */
int FastFirstHopPK; /**< If Tor believes it is safe, should we save a third
* of our PK time by sending CREATE_FAST cells? */
@@ -3518,8 +3742,10 @@ typedef struct {
/** Should we fetch our dir info at the start of the consensus period? */
int FetchDirInfoExtraEarly;
- char *VirtualAddrNetwork; /**< Address and mask to hand out for virtual
- * MAPADDRESS requests. */
+ char *VirtualAddrNetworkIPv4; /**< Address and mask to hand out for virtual
+ * MAPADDRESS requests for IPv4 addresses */
+ char *VirtualAddrNetworkIPv6; /**< Address and mask to hand out for virtual
+ * MAPADDRESS requests for IPv6 addresses */
int ServerDNSSearchDomains; /**< Boolean: If set, we don't force exit
* addresses to be FQDNs, but rather search for them in
* the local domains. */
@@ -3667,10 +3893,6 @@ typedef struct {
* of certain configuration options. */
int TestingTorNetwork;
- /** File to check for a consensus networkstatus, if we don't have one
- * cached. */
- char *FallbackNetworkstatusFile;
-
/** If true, and we have GeoIP data, and we're a bridge, keep a per-country
* count of how many client addresses have contacted us so that we can help
* the bridge authority guess which countries have blocked access to us. */
@@ -3680,6 +3902,11 @@ typedef struct {
char *GeoIPFile;
char *GeoIPv6File;
+ /** Autobool: if auto, then any attempt to Exclude{Exit,}Nodes a particular
+ * country code will exclude all nodes in ?? and A1. If true, all nodes in
+ * ?? and A1 are excluded. Has no effect if we don't know any GeoIP data. */
+ int GeoIPExcludeUnknown;
+
/** If true, SIGHUP should reload the torrc. Sometimes controllers want
* to make this false. */
int ReloadTorrcOnSIGHUP;
@@ -3743,11 +3970,31 @@ typedef struct {
*/
int PathBiasCircThreshold;
double PathBiasNoticeRate;
- double PathBiasDisableRate;
+ double PathBiasWarnRate;
+ double PathBiasExtremeRate;
+ int PathBiasDropGuards;
int PathBiasScaleThreshold;
- int PathBiasScaleFactor;
/** @} */
+ /**
+ * Parameters for path-bias use detection
+ * @{
+ */
+ int PathBiasUseThreshold;
+ double PathBiasNoticeUseRate;
+ double PathBiasExtremeUseRate;
+ int PathBiasScaleUseThreshold;
+ /** @} */
+
+ int IPv6Exit; /**< Do we support exiting to IPv6 addresses? */
+
+ char *TLSECGroup; /**< One of "P256", "P224", or nil for auto */
+
+ /** Autobool: should we use the ntor handshake if we can? */
+ int UseNTorHandshake;
+
+ /** Fraction: */
+ double PathsNeededToBuildCircuits;
} or_options_t;
/** Persistent state for an onion router, as saved to disk. */
@@ -4358,12 +4605,12 @@ typedef struct rend_encoded_v2_service_descriptor_t {
* sooner.)
*
* XXX023 Should this be configurable? */
-#define INTRO_POINT_LIFETIME_MIN_SECONDS 18*60*60
+#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
+#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
@@ -4458,19 +4705,23 @@ typedef struct rend_cache_entry_t {
/********************************* routerlist.c ***************************/
-/** Represents information about a single trusted directory server. */
-typedef struct trusted_dir_server_t {
+/** Represents information about a single trusted or fallback directory
+ * server. */
+typedef struct dir_server_t {
char *description;
char *nickname;
char *address; /**< Hostname. */
uint32_t addr; /**< IPv4 address. */
uint16_t dir_port; /**< Directory port. */
uint16_t or_port; /**< OR port: Used for tunneling connections. */
+ double weight; /** Weight used when selecting this node at random */
char digest[DIGEST_LEN]; /**< Digest of identity key. */
char v3_identity_digest[DIGEST_LEN]; /**< Digest of v3 (authority only,
* high-security) identity key. */
unsigned int is_running:1; /**< True iff we think this server is running. */
+ unsigned int is_authority:1; /**< True iff this is a directory authority
+ * of some kind. */
/** True iff this server has accepted the most recent server descriptor
* we tried to upload to it. */
@@ -4489,7 +4740,7 @@ typedef struct trusted_dir_server_t {
* as a routerstatus_t. Not updated by the
* router-status management code!
**/
-} trusted_dir_server_t;
+} dir_server_t;
#define ROUTER_REQUIRED_MIN_BANDWIDTH (20*1024)
diff --git a/src/or/policies.c b/src/or/policies.c
index 09ba10bbe7..9696b8123b 100644
--- a/src/or/policies.c
+++ b/src/or/policies.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -59,8 +59,10 @@ typedef struct policy_summary_item_t {
static const char *private_nets[] = {
"0.0.0.0/8", "169.254.0.0/16",
"127.0.0.0/8", "192.168.0.0/16", "10.0.0.0/8", "172.16.0.0/12",
- // "fc00::/7", "fe80::/10", "fec0::/10", "::/127",
- NULL };
+ "[::]/8",
+ "[fc00::]/7", "[fe80::]/10", "[fec0::]/10", "[ff00::]/8", "[::]/127",
+ NULL
+};
/** Replace all "private" entries in *<b>policy</b> with their expanded
* equivalents. */
@@ -87,7 +89,8 @@ policy_expand_private(smartlist_t **policy)
memcpy(&newpolicy, p, sizeof(addr_policy_t));
newpolicy.is_private = 0;
newpolicy.is_canonical = 0;
- if (tor_addr_parse_mask_ports(private_nets[i], &newpolicy.addr,
+ if (tor_addr_parse_mask_ports(private_nets[i], 0,
+ &newpolicy.addr,
&newpolicy.maskbits, &port_min, &port_max)<0) {
tor_assert(0);
}
@@ -100,6 +103,49 @@ policy_expand_private(smartlist_t **policy)
*policy = tmp;
}
+/** Expand each of the AF_UNSPEC elements in *<b>policy</b> (which indicate
+ * protocol-neutral wildcards) into a pair of wildcard elements: one IPv4-
+ * specific and one IPv6-specific. */
+void
+policy_expand_unspec(smartlist_t **policy)
+{
+ smartlist_t *tmp;
+ if (!*policy)
+ return;
+
+ tmp = smartlist_new();
+ SMARTLIST_FOREACH_BEGIN(*policy, addr_policy_t *, p) {
+ sa_family_t family = tor_addr_family(&p->addr);
+ if (family == AF_INET6 || family == AF_INET || p->is_private) {
+ smartlist_add(tmp, p);
+ } else if (family == AF_UNSPEC) {
+ addr_policy_t newpolicy_ipv4;
+ addr_policy_t newpolicy_ipv6;
+ memcpy(&newpolicy_ipv4, p, sizeof(addr_policy_t));
+ memcpy(&newpolicy_ipv6, p, sizeof(addr_policy_t));
+ newpolicy_ipv4.is_canonical = 0;
+ newpolicy_ipv6.is_canonical = 0;
+ if (p->maskbits != 0) {
+ log_warn(LD_BUG, "AF_UNSPEC policy with maskbits==%d", p->maskbits);
+ newpolicy_ipv4.maskbits = 0;
+ newpolicy_ipv6.maskbits = 0;
+ }
+ tor_addr_from_ipv4h(&newpolicy_ipv4.addr, 0);
+ tor_addr_from_ipv6_bytes(&newpolicy_ipv6.addr,
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
+ smartlist_add(tmp, addr_policy_get_canonical_entry(&newpolicy_ipv4));
+ smartlist_add(tmp, addr_policy_get_canonical_entry(&newpolicy_ipv6));
+ addr_policy_free(p);
+ } else {
+ log_warn(LD_BUG, "Funny-looking address policy with family %d", family);
+ smartlist_add(tmp, p);
+ }
+ } SMARTLIST_FOREACH_END(p);
+
+ smartlist_free(*policy);
+ *policy = tmp;
+}
+
/**
* Given a linked list of config lines containing "allow" and "deny"
* tokens, parse them and append the result to <b>dest</b>. Return -1
@@ -144,6 +190,7 @@ parse_addr_policy(config_line_t *cfg, smartlist_t **dest,
addr_policy_list_free(result);
} else {
policy_expand_private(&result);
+ policy_expand_unspec(&result);
if (*dest) {
smartlist_add_all(*dest, result);
@@ -327,7 +374,7 @@ addr_is_in_cc_list(uint32_t addr, const smartlist_t *cc_list)
tor_addr_from_ipv4h(&tar, addr);
country = geoip_get_country_by_addr(&tar);
name = geoip_get_country_name(country);
- return smartlist_string_isin_case(cc_list, name);
+ return smartlist_contains_string_case(cc_list, name);
}
/** Return 1 if <b>addr</b>:<b>port</b> is permitted to publish to our
@@ -390,6 +437,7 @@ validate_addr_policies(const or_options_t *options, char **msg)
*msg = NULL;
if (policies_parse_exit_policy(options->ExitPolicy, &addr_policy,
+ options->IPv6Exit,
options->ExitPolicyRejectPrivate, NULL,
!options->BridgeRelay))
REJECT("Error in ExitPolicy entry.");
@@ -734,6 +782,10 @@ compare_tor_addr_to_addr_policy(const tor_addr_t *addr, uint16_t port,
static int
addr_policy_covers(addr_policy_t *a, addr_policy_t *b)
{
+ if (tor_addr_family(&a->addr) != tor_addr_family(&b->addr)) {
+ /* You can't cover a different family. */
+ return 0;
+ }
/* We can ignore accept/reject, since "accept *:80, reject *:80" reduces
* to "accept *:80". */
if (a->maskbits > b->maskbits) {
@@ -789,20 +841,32 @@ append_exit_policy_string(smartlist_t **policy, const char *more)
static void
exit_policy_remove_redundancies(smartlist_t *dest)
{
- addr_policy_t *ap, *tmp, *victim;
+ addr_policy_t *ap, *tmp;
int i, j;
- /* Step one: find a *:* entry and cut off everything after it. */
- for (i = 0; i < smartlist_len(dest); ++i) {
- ap = smartlist_get(dest, i);
- if (ap->maskbits == 0 && ap->prt_min <= 1 && ap->prt_max >= 65535) {
- /* This is a catch-all line -- later lines are unreachable. */
- while (i+1 < smartlist_len(dest)) {
- victim = smartlist_get(dest, i+1);
- smartlist_del(dest, i+1);
- addr_policy_free(victim);
+ /* Step one: kill every ipv4 thing after *4:*, every IPv6 thing after *6:*
+ */
+ {
+ int kill_v4=0, kill_v6=0;
+ for (i = 0; i < smartlist_len(dest); ++i) {
+ sa_family_t family;
+ ap = smartlist_get(dest, i);
+ family = tor_addr_family(&ap->addr);
+ if ((family == AF_INET && kill_v4) ||
+ (family == AF_INET6 && kill_v6)) {
+ smartlist_del_keeporder(dest, i--);
+ addr_policy_free(ap);
+ continue;
+ }
+
+ if (ap->maskbits == 0 && ap->prt_min <= 1 && ap->prt_max >= 65535) {
+ /* This is a catch-all line -- later lines are unreachable. */
+ if (family == AF_INET) {
+ kill_v4 = 1;
+ } else if (family == AF_INET6) {
+ kill_v6 = 1;
+ }
}
- break;
}
}
@@ -817,7 +881,7 @@ exit_policy_remove_redundancies(smartlist_t *dest)
char p1[POLICY_BUF_LEN], p2[POLICY_BUF_LEN];
policy_write_item(p1, sizeof(p1), tmp, 0);
policy_write_item(p2, sizeof(p2), ap, 0);
- log(LOG_DEBUG, LD_CONFIG, "Removing exit policy %s (%d). It is made "
+ log_debug(LD_CONFIG, "Removing exit policy %s (%d). It is made "
"redundant by %s (%d).", p1, j, p2, i);
smartlist_del_keeporder(dest, j--);
addr_policy_free(tmp);
@@ -846,7 +910,7 @@ exit_policy_remove_redundancies(smartlist_t *dest)
char p1[POLICY_BUF_LEN], p2[POLICY_BUF_LEN];
policy_write_item(p1, sizeof(p1), ap, 0);
policy_write_item(p2, sizeof(p2), tmp, 0);
- log(LOG_DEBUG, LD_CONFIG, "Removing exit policy %s. It is already "
+ log_debug(LD_CONFIG, "Removing exit policy %s. It is already "
"covered by %s.", p1, p2);
smartlist_del_keeporder(dest, i--);
addr_policy_free(ap);
@@ -868,12 +932,20 @@ exit_policy_remove_redundancies(smartlist_t *dest)
* policy afterwards. If <b>rejectprivate</b> is true, prepend
* "reject private:*" to the policy. Return -1 if we can't parse cfg,
* else return 0.
+ *
+ * This function is used to parse the exit policy from our torrc. For
+ * the functions used to parse the exit policy from a router descriptor,
+ * see router_add_exit_policy.
*/
int
policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
+ int ipv6_exit,
int rejectprivate, const char *local_address,
int add_default_policy)
{
+ if (!ipv6_exit) {
+ append_exit_policy_string(dest, "reject *6:*");
+ }
if (rejectprivate) {
append_exit_policy_string(dest, "reject private:*");
if (local_address) {
@@ -884,10 +956,12 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
}
if (parse_addr_policy(cfg, dest, -1))
return -1;
- if (add_default_policy)
+ if (add_default_policy) {
append_exit_policy_string(dest, DEFAULT_EXIT_POLICY);
- else
- append_exit_policy_string(dest, "reject *:*");
+ } else {
+ append_exit_policy_string(dest, "reject *4:*");
+ append_exit_policy_string(dest, "reject *6:*");
+ }
exit_policy_remove_redundancies(*dest);
return 0;
@@ -898,7 +972,8 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
void
policies_exit_policy_append_reject_star(smartlist_t **dest)
{
- append_exit_policy_string(dest, "reject *:*");
+ append_exit_policy_string(dest, "reject *4:*");
+ append_exit_policy_string(dest, "reject *6:*");
}
/** Replace the exit policy of <b>node</b> with reject *:* */
@@ -974,18 +1049,23 @@ exit_policy_is_general_exit(smartlist_t *policy)
/** Return false if <b>policy</b> might permit access to some addr:port;
* otherwise if we are certain it rejects everything, return true. */
int
-policy_is_reject_star(const smartlist_t *policy)
+policy_is_reject_star(const smartlist_t *policy, sa_family_t family)
{
if (!policy) /*XXXX disallow NULL policies? */
return 1;
- SMARTLIST_FOREACH(policy, addr_policy_t *, p, {
- if (p->policy_type == ADDR_POLICY_ACCEPT)
+ SMARTLIST_FOREACH_BEGIN(policy, addr_policy_t *, p) {
+ if (p->policy_type == ADDR_POLICY_ACCEPT &&
+ (tor_addr_family(&p->addr) == family ||
+ tor_addr_family(&p->addr) == AF_UNSPEC)) {
return 0;
- else if (p->policy_type == ADDR_POLICY_REJECT &&
- p->prt_min <= 1 && p->prt_max == 65535 &&
- p->maskbits == 0)
+ } else if (p->policy_type == ADDR_POLICY_REJECT &&
+ p->prt_min <= 1 && p->prt_max == 65535 &&
+ p->maskbits == 0 &&
+ (tor_addr_family(&p->addr) == family ||
+ tor_addr_family(&p->addr) == AF_UNSPEC)) {
return 1;
- });
+ }
+ } SMARTLIST_FOREACH_END(p);
return 1;
}
@@ -1000,17 +1080,26 @@ policy_write_item(char *buf, size_t buflen, addr_policy_t *policy,
const char *addrpart;
int result;
const int is_accept = policy->policy_type == ADDR_POLICY_ACCEPT;
- const int is_ip6 = tor_addr_family(&policy->addr) == AF_INET6;
+ const sa_family_t family = tor_addr_family(&policy->addr);
+ const int is_ip6 = (family == AF_INET6);
tor_addr_to_str(addrbuf, &policy->addr, sizeof(addrbuf), 1);
/* write accept/reject 1.2.3.4 */
- if (policy->is_private)
+ if (policy->is_private) {
addrpart = "private";
- else if (policy->maskbits == 0)
- addrpart = "*";
- else
+ } else if (policy->maskbits == 0) {
+ if (format_for_desc)
+ addrpart = "*";
+ else if (family == AF_INET6)
+ addrpart = "*6";
+ else if (family == AF_INET)
+ addrpart = "*4";
+ else
+ addrpart = "*";
+ } else {
addrpart = addrbuf;
+ }
result = tor_snprintf(buf, buflen, "%s%s %s",
is_accept ? "accept" : "reject",
@@ -1192,8 +1281,8 @@ policy_summary_add_item(smartlist_t *summary, addr_policy_t *p)
for (i = 0; private_nets[i]; ++i) {
tor_addr_t addr;
maskbits_t maskbits;
- if (tor_addr_parse_mask_ports(private_nets[i], &addr,
- &maskbits, NULL, NULL)<0) {
+ if (tor_addr_parse_mask_ports(private_nets[i], 0, &addr,
+ &maskbits, NULL, NULL)<0) {
tor_assert(0);
}
if (tor_addr_compare(&p->addr, &addr, CMP_EXACT) == 0 &&
@@ -1219,7 +1308,7 @@ policy_summary_add_item(smartlist_t *summary, addr_policy_t *p)
* is an exception to the shorter-representation-wins rule).
*/
char *
-policy_summarize(smartlist_t *policy)
+policy_summarize(smartlist_t *policy, sa_family_t family)
{
smartlist_t *summary = policy_summary_create();
smartlist_t *accepts, *rejects;
@@ -1231,9 +1320,16 @@ policy_summarize(smartlist_t *policy)
tor_assert(policy);
/* Create the summary list */
- SMARTLIST_FOREACH(policy, addr_policy_t *, p, {
+ SMARTLIST_FOREACH_BEGIN(policy, addr_policy_t *, p) {
+ sa_family_t f = tor_addr_family(&p->addr);
+ if (f != AF_INET && f != AF_INET6) {
+ log_warn(LD_BUG, "Weird family when summarizing address policy");
+ }
+ if (f != family)
+ continue;
+ /* XXXX-ipv6 More family work is needed */
policy_summary_add_item(summary, p);
- });
+ } SMARTLIST_FOREACH_END(p);
/* Now create two lists of strings, one for accepted and one
* for rejected ports. We take care to merge ranges so that
@@ -1520,7 +1616,7 @@ short_policy_is_reject_star(const short_policy_t *policy)
policy->entries[0].max_port == 65535);
}
-/** Decides whether addr:port is probably or definitely accepted or rejected by
+/** Decide whether addr:port is probably or definitely accepted or rejected by
* <b>node</b>. See compare_tor_addr_to_addr_policy for details on addr/port
* interpretation. */
addr_policy_result_t
@@ -1530,16 +1626,29 @@ compare_tor_addr_to_node_policy(const tor_addr_t *addr, uint16_t port,
if (node->rejects_all)
return ADDR_POLICY_REJECTED;
- if (node->ri)
+ if (addr && tor_addr_family(addr) == AF_INET6) {
+ const short_policy_t *p = NULL;
+ if (node->ri)
+ p = node->ri->ipv6_exit_policy;
+ else if (node->md)
+ p = node->md->ipv6_exit_policy;
+ if (p)
+ return compare_tor_addr_to_short_policy(addr, port, p);
+ else
+ return ADDR_POLICY_REJECTED;
+ }
+
+ if (node->ri) {
return compare_tor_addr_to_addr_policy(addr, port, node->ri->exit_policy);
- else if (node->md) {
+ } else if (node->md) {
if (node->md->exit_policy == NULL)
return ADDR_POLICY_REJECTED;
else
return compare_tor_addr_to_short_policy(addr, port,
node->md->exit_policy);
- } else
+ } else {
return ADDR_POLICY_PROBABLY_REJECTED;
+ }
}
/** Implementation for GETINFO control command: knows the answer for questions
diff --git a/src/or/policies.h b/src/or/policies.h
index 431e69eb0d..da375c4425 100644
--- a/src/or/policies.h
+++ b/src/or/policies.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -31,6 +31,7 @@ int authdir_policy_badexit_address(uint32_t addr, uint16_t port);
int validate_addr_policies(const or_options_t *options, char **msg);
void policy_expand_private(smartlist_t **policy);
+void policy_expand_unspec(smartlist_t **policy);
int policies_parse_from_options(const or_options_t *options);
addr_policy_t *addr_policy_get_canonical_entry(addr_policy_t *ent);
@@ -42,12 +43,13 @@ addr_policy_result_t compare_tor_addr_to_node_policy(const tor_addr_t *addr,
uint16_t port, const node_t *node);
int policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
+ int ipv6exit,
int rejectprivate, const char *local_address,
int add_default_policy);
void policies_exit_policy_append_reject_star(smartlist_t **dest);
void policies_set_node_exitpolicy_to_reject_all(node_t *exitrouter);
int exit_policy_is_general_exit(smartlist_t *policy);
-int policy_is_reject_star(const smartlist_t *policy);
+int policy_is_reject_star(const smartlist_t *policy, sa_family_t family);
int getinfo_helper_policies(control_connection_t *conn,
const char *question, char **answer,
const char **errmsg);
@@ -58,7 +60,7 @@ void addr_policy_list_free(smartlist_t *p);
void addr_policy_free(addr_policy_t *p);
void policies_free_all(void);
-char *policy_summarize(smartlist_t *policy);
+char *policy_summarize(smartlist_t *policy, sa_family_t family);
short_policy_t *parse_short_policy(const char *summary);
char *write_short_policy(const short_policy_t *policy);
diff --git a/src/or/reasons.c b/src/or/reasons.c
index 874a86774b..637f8cdc7d 100644
--- a/src/or/reasons.c
+++ b/src/or/reasons.c
@@ -1,5 +1,5 @@
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -105,7 +105,12 @@ stream_end_reason_to_socks5_response(int reason)
case END_STREAM_REASON_DESTROY:
return SOCKS5_GENERAL_ERROR;
case END_STREAM_REASON_DONE:
- return SOCKS5_SUCCEEDED;
+ /* Note that 'DONE' usually indicates a successful close from the other
+ * side of the stream... but if we receive it before a connected cell --
+ * that is, before we have sent a SOCKS reply -- that means that the
+ * other side of the circuit closed the connection before telling us it
+ * was complete. */
+ return SOCKS5_CONNECTION_REFUSED;
case END_STREAM_REASON_TIMEOUT:
return SOCKS5_TTL_EXPIRED;
case END_STREAM_REASON_NOROUTE:
diff --git a/src/or/reasons.h b/src/or/reasons.h
index 0dbf5aa693..fe7e67722a 100644
--- a/src/or/reasons.h
+++ b/src/or/reasons.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/relay.c b/src/or/relay.c
index b1e4bfb8f3..9ff9e1e1f4 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -12,6 +12,7 @@
#define RELAY_PRIVATE
#include "or.h"
+#include "addressmap.h"
#include "buffers.h"
#include "channel.h"
#include "circuitbuild.h"
@@ -26,6 +27,7 @@
#include "mempool.h"
#include "networkstatus.h"
#include "nodelist.h"
+#include "onion.h"
#include "policies.h"
#include "reasons.h"
#include "relay.h"
@@ -68,6 +70,9 @@ uint64_t stats_n_relay_cells_relayed = 0;
*/
uint64_t stats_n_relay_cells_delivered = 0;
+/** Used to tell which stream to read from first on a circuit. */
+static tor_weak_rng_t stream_choice_rng = TOR_WEAK_RNG_INIT;
+
/** Update digest from the payload of cell. Assign integrity part to
* cell.
*/
@@ -184,7 +189,17 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ,
}
if (recognized) {
- edge_connection_t *conn = relay_lookup_conn(circ, cell, cell_direction,
+ edge_connection_t *conn = NULL;
+
+ if (circ->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING) {
+ pathbias_check_probe_response(circ, cell);
+
+ /* We need to drop this cell no matter what to avoid code that expects
+ * a certain purpose (such as the hidserv code). */
+ return 0;
+ }
+
+ conn = relay_lookup_conn(circ, cell, cell_direction,
layer_hint);
if (cell_direction == CELL_DIRECTION_OUT) {
++stats_n_relay_cells_delivered;
@@ -220,7 +235,15 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ,
} else {
log_fn(LOG_PROTOCOL_WARN, LD_OR,
"Dropping unrecognized inbound cell on origin circuit.");
- return 0;
+ /* If we see unrecognized cells on path bias testing circs,
+ * it's bad mojo. Those circuits need to die.
+ * XXX: Shouldn't they always die? */
+ if (circ->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING) {
+ TO_ORIGIN_CIRCUIT(circ)->path_state = PATH_STATE_USE_FAILED;
+ return -END_CIRC_REASON_TORPROTOCOL;
+ } else {
+ return 0;
+ }
}
if (!chan) {
@@ -570,6 +593,7 @@ relay_send_command_from_edge(streamid_t stream_id, circuit_t *circ,
origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ);
if (origin_circ->remaining_relay_early_cells > 0 &&
(relay_command == RELAY_COMMAND_EXTEND ||
+ relay_command == RELAY_COMMAND_EXTEND2 ||
cpath_layer != origin_circ->cpath)) {
/* If we've got any relay_early cells left and (we're sending
* an extend cell or we're not talking to the first hop), use
@@ -583,7 +607,8 @@ relay_send_command_from_edge(streamid_t stream_id, circuit_t *circ,
* task 878. */
origin_circ->relay_early_commands[
origin_circ->relay_early_cells_sent++] = relay_command;
- } else if (relay_command == RELAY_COMMAND_EXTEND) {
+ } else if (relay_command == RELAY_COMMAND_EXTEND ||
+ relay_command == RELAY_COMMAND_EXTEND2) {
/* If no RELAY_EARLY cells can be sent over this circuit, log which
* commands have been sent as RELAY_EARLY cells before; helps debug
* task 878. */
@@ -688,11 +713,37 @@ connection_ap_process_end_not_open(
struct in_addr in;
node_t *exitrouter;
int reason = *(cell->payload+RELAY_HEADER_SIZE);
- int control_reason = reason | END_STREAM_REASON_FLAG_REMOTE;
+ int control_reason;
edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn);
(void) layer_hint; /* unused */
- if (rh->length > 0 && edge_reason_is_retriable(reason) &&
+ if (rh->length > 0) {
+ if (reason == END_STREAM_REASON_TORPROTOCOL ||
+ reason == END_STREAM_REASON_INTERNAL ||
+ reason == END_STREAM_REASON_DESTROY) {
+ /* All three of these reasons could mean a failed tag
+ * hit the exit and it complained. Do not probe.
+ * Fail the circuit. */
+ circ->path_state = PATH_STATE_USE_FAILED;
+ return -END_CIRC_REASON_TORPROTOCOL;
+ } else {
+ /* Path bias: If we get a valid reason code from the exit,
+ * it wasn't due to tagging.
+ *
+ * We rely on recognized+digest being strong enough to make
+ * tags unlikely to allow us to get tagged, yet 'recognized'
+ * reason codes here. */
+ pathbias_mark_use_success(circ);
+ }
+ }
+
+ if (rh->length == 0) {
+ reason = END_STREAM_REASON_MISC;
+ }
+
+ control_reason = reason | END_STREAM_REASON_FLAG_REMOTE;
+
+ if (edge_reason_is_retriable(reason) &&
/* avoid retry if rend */
!connection_edge_is_rendezvous_stream(edge_conn)) {
const char *chosen_exit_digest =
@@ -704,28 +755,57 @@ connection_ap_process_end_not_open(
switch (reason) {
case END_STREAM_REASON_EXITPOLICY:
if (rh->length >= 5) {
- uint32_t addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+1));
- int ttl;
- if (!addr) {
+ tor_addr_t addr;
+
+ int ttl = -1;
+ tor_addr_make_unspec(&addr);
+ if (rh->length == 5 || rh->length == 9) {
+ tor_addr_from_ipv4n(&addr,
+ get_uint32(cell->payload+RELAY_HEADER_SIZE+1));
+ if (rh->length == 9)
+ ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+5));
+ } else if (rh->length == 17 || rh->length == 21) {
+ tor_addr_from_ipv6_bytes(&addr,
+ (char*)(cell->payload+RELAY_HEADER_SIZE+1));
+ if (rh->length == 21)
+ ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+17));
+ }
+ if (tor_addr_is_null(&addr)) {
log_info(LD_APP,"Address '%s' resolved to 0.0.0.0. Closing,",
safe_str(conn->socks_request->address));
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
return 0;
}
- if (rh->length >= 9)
- ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+5));
- else
- ttl = -1;
+ if ((tor_addr_family(&addr) == AF_INET && !conn->ipv4_traffic_ok) ||
+ (tor_addr_family(&addr) == AF_INET6 && !conn->ipv6_traffic_ok)) {
+ log_fn(LOG_PROTOCOL_WARN, LD_APP,
+ "Got an EXITPOLICY failure on a connection with a "
+ "mismatched family. Closing.");
+ connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
+ return 0;
+ }
if (get_options()->ClientDNSRejectInternalAddresses &&
- is_internal_IP(addr, 0)) {
+ tor_addr_is_internal(&addr, 0)) {
log_info(LD_APP,"Address '%s' resolved to internal. Closing,",
safe_str(conn->socks_request->address));
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
return 0;
}
- client_dns_set_addressmap(conn->socks_request->address, addr,
+
+ client_dns_set_addressmap(conn,
+ conn->socks_request->address, &addr,
conn->chosen_exit_name, ttl);
+
+ {
+ char new_addr[TOR_ADDR_BUF_LEN];
+ tor_addr_to_str(new_addr, &addr, sizeof(new_addr), 1);
+ if (strcmp(conn->socks_request->address, new_addr)) {
+ strlcpy(conn->socks_request->address, new_addr,
+ sizeof(conn->socks_request->address));
+ control_event_stream_status(conn, STREAM_EVENT_REMAP, 0);
+ }
+ }
}
/* check if he *ought* to have allowed it */
if (exitrouter &&
@@ -738,12 +818,7 @@ connection_ap_process_end_not_open(
node_describe(exitrouter));
policies_set_node_exitpolicy_to_reject_all(exitrouter);
}
- /* rewrite it to an IP if we learned one. */
- if (addressmap_rewrite(conn->socks_request->address,
- sizeof(conn->socks_request->address),
- NULL, NULL)) {
- control_event_stream_status(conn, STREAM_EVENT_REMAP, 0);
- }
+
if (conn->chosen_exit_optional ||
conn->chosen_exit_retries) {
/* stop wanting a specific exit */
@@ -827,20 +902,60 @@ connection_ap_process_end_not_open(
}
/** Helper: change the socks_request-&gt;address field on conn to the
- * dotted-quad representation of <b>new_addr</b> (given in host order),
+ * dotted-quad representation of <b>new_addr</b>,
* and send an appropriate REMAP event. */
static void
-remap_event_helper(entry_connection_t *conn, uint32_t new_addr)
+remap_event_helper(entry_connection_t *conn, const tor_addr_t *new_addr)
{
- struct in_addr in;
-
- in.s_addr = htonl(new_addr);
- tor_inet_ntoa(&in, conn->socks_request->address,
- sizeof(conn->socks_request->address));
+ tor_addr_to_str(conn->socks_request->address, new_addr,
+ sizeof(conn->socks_request->address),
+ 1);
control_event_stream_status(conn, STREAM_EVENT_REMAP,
REMAP_STREAM_SOURCE_EXIT);
}
+/** Extract the contents of a connected cell in <b>cell</b>, whose relay
+ * header has already been parsed into <b>rh</b>. On success, set
+ * <b>addr_out</b> to the address we're connected to, and <b>ttl_out</b> to
+ * the ttl of that address, in seconds, and return 0. On failure, return
+ * -1. */
+int
+connected_cell_parse(const relay_header_t *rh, const cell_t *cell,
+ tor_addr_t *addr_out, int *ttl_out)
+{
+ uint32_t bytes;
+ const uint8_t *payload = cell->payload + RELAY_HEADER_SIZE;
+
+ tor_addr_make_unspec(addr_out);
+ *ttl_out = -1;
+ if (rh->length == 0)
+ return 0;
+ if (rh->length < 4)
+ return -1;
+ bytes = ntohl(get_uint32(payload));
+
+ /* If bytes is 0, this is maybe a v6 address. Otherwise it's a v4 address */
+ if (bytes != 0) {
+ /* v4 address */
+ tor_addr_from_ipv4h(addr_out, bytes);
+ if (rh->length >= 8) {
+ bytes = ntohl(get_uint32(payload + 4));
+ if (bytes <= INT32_MAX)
+ *ttl_out = bytes;
+ }
+ } else {
+ if (rh->length < 25) /* 4 bytes of 0s, 1 addr, 16 ipv4, 4 ttl. */
+ return -1;
+ if (get_uint8(payload + 4) != 6)
+ return -1;
+ tor_addr_from_ipv6_bytes(addr_out, (char*)(payload + 5));
+ bytes = ntohl(get_uint32(payload + 21));
+ if (bytes <= INT32_MAX)
+ *ttl_out = (int) bytes;
+ }
+ return 0;
+}
+
/** An incoming relay cell has arrived from circuit <b>circ</b> to
* stream <b>conn</b>.
*
@@ -871,6 +986,8 @@ connection_edge_process_relay_cell_not_open(
if (conn->base_.type == CONN_TYPE_AP &&
rh->command == RELAY_COMMAND_CONNECTED) {
+ tor_addr_t addr;
+ int ttl;
entry_connection_t *entry_conn = EDGE_TO_ENTRY_CONN(conn);
tor_assert(CIRCUIT_IS_ORIGIN(circ));
if (conn->base_.state != AP_CONN_STATE_CONNECT_WAIT) {
@@ -881,26 +998,41 @@ connection_edge_process_relay_cell_not_open(
conn->base_.state = AP_CONN_STATE_OPEN;
log_info(LD_APP,"'connected' received after %d seconds.",
(int)(time(NULL) - conn->base_.timestamp_lastread));
- if (rh->length >= 4) {
- uint32_t addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE));
- int ttl;
- if (!addr || (get_options()->ClientDNSRejectInternalAddresses &&
- is_internal_IP(addr, 0))) {
+ if (connected_cell_parse(rh, cell, &addr, &ttl) < 0) {
+ log_fn(LOG_PROTOCOL_WARN, LD_APP,
+ "Got a badly formatted connected cell. Closing.");
+ connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL);
+ connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TORPROTOCOL);
+ }
+ if (tor_addr_family(&addr) != AF_UNSPEC) {
+ const sa_family_t family = tor_addr_family(&addr);
+ if (tor_addr_is_null(&addr) ||
+ (get_options()->ClientDNSRejectInternalAddresses &&
+ tor_addr_is_internal(&addr, 0))) {
log_info(LD_APP, "...but it claims the IP address was %s. Closing.",
- fmt_addr32(addr));
+ fmt_addr(&addr));
+ connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL);
+ connection_mark_unattached_ap(entry_conn,
+ END_STREAM_REASON_TORPROTOCOL);
+ return 0;
+ }
+
+ if ((family == AF_INET && ! entry_conn->ipv4_traffic_ok) ||
+ (family == AF_INET6 && ! entry_conn->ipv6_traffic_ok)) {
+ log_fn(LOG_PROTOCOL_WARN, LD_APP,
+ "Got a connected cell to %s with unsupported address family."
+ " Closing.", fmt_addr(&addr));
connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL);
connection_mark_unattached_ap(entry_conn,
END_STREAM_REASON_TORPROTOCOL);
return 0;
}
- if (rh->length >= 8)
- ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+4));
- else
- ttl = -1;
- client_dns_set_addressmap(entry_conn->socks_request->address, addr,
+
+ client_dns_set_addressmap(entry_conn,
+ entry_conn->socks_request->address, &addr,
entry_conn->chosen_exit_name, ttl);
- remap_event_helper(entry_conn, addr);
+ remap_event_helper(entry_conn, &addr);
}
circuit_log_path(LOG_INFO,LD_APP,TO_ORIGIN_CIRCUIT(circ));
/* don't send a socks reply to transparent conns */
@@ -990,8 +1122,15 @@ connection_edge_process_relay_cell_not_open(
ttl,
-1);
if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4) {
- uint32_t addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+2));
- remap_event_helper(entry_conn, addr);
+ tor_addr_t addr;
+ tor_addr_from_ipv4n(&addr,
+ get_uint32(cell->payload+RELAY_HEADER_SIZE+2));
+ remap_event_helper(entry_conn, &addr);
+ } else if (answer_type == RESOLVED_TYPE_IPV6 && answer_len == 16) {
+ tor_addr_t addr;
+ tor_addr_from_ipv6_bytes(&addr,
+ (char*)(cell->payload+RELAY_HEADER_SIZE+2));
+ remap_event_helper(entry_conn, &addr);
}
connection_mark_unattached_ap(entry_conn,
END_STREAM_REASON_DONE |
@@ -1046,6 +1185,23 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
return - END_CIRC_REASON_TORPROTOCOL;
}
+ if (rh.stream_id == 0) {
+ switch (rh.command) {
+ case RELAY_COMMAND_BEGIN:
+ case RELAY_COMMAND_CONNECTED:
+ case RELAY_COMMAND_DATA:
+ case RELAY_COMMAND_END:
+ case RELAY_COMMAND_RESOLVE:
+ case RELAY_COMMAND_RESOLVED:
+ case RELAY_COMMAND_BEGIN_DIR:
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Relay command %d with zero "
+ "stream_id. Dropping.", (int)rh.command);
+ return 0;
+ default:
+ ;
+ }
+ }
+
/* either conn is NULL, in which case we've got a control cell, or else
* conn points to the recognized stream. */
@@ -1151,7 +1307,8 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
return 0;
}
/* XXX add to this log_fn the exit node's nickname? */
- log_info(domain,"%d: end cell (%s) for stream %d. Removing stream.",
+ log_info(domain,TOR_SOCKET_T_FORMAT": end cell (%s) for stream %d. "
+ "Removing stream.",
conn->base_.s,
stream_end_reason_to_string(reason),
conn->stream_id);
@@ -1172,7 +1329,8 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
connection_mark_and_flush(TO_CONN(conn));
}
return 0;
- case RELAY_COMMAND_EXTEND: {
+ case RELAY_COMMAND_EXTEND:
+ case RELAY_COMMAND_EXTEND2: {
static uint64_t total_n_extend=0, total_nonearly=0;
total_n_extend++;
if (rh.stream_id) {
@@ -1207,17 +1365,27 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
return circuit_extend(cell, circ);
}
case RELAY_COMMAND_EXTENDED:
+ case RELAY_COMMAND_EXTENDED2:
if (!layer_hint) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"'extended' unsupported at non-origin. Dropping.");
return 0;
}
log_debug(domain,"Got an extended cell! Yay.");
- if ((reason = circuit_finish_handshake(TO_ORIGIN_CIRCUIT(circ),
- CELL_CREATED,
- cell->payload+RELAY_HEADER_SIZE)) < 0) {
- log_warn(domain,"circuit_finish_handshake failed.");
- return reason;
+ {
+ extended_cell_t extended_cell;
+ if (extended_cell_parse(&extended_cell, rh.command,
+ (const uint8_t*)cell->payload+RELAY_HEADER_SIZE,
+ rh.length)<0) {
+ log_warn(LD_PROTOCOL,
+ "Can't parse EXTENDED cell; killing circuit.");
+ return -END_CIRC_REASON_TORPROTOCOL;
+ }
+ if ((reason = circuit_finish_handshake(TO_ORIGIN_CIRCUIT(circ),
+ &extended_cell.created_cell)) < 0) {
+ log_warn(domain,"circuit_finish_handshake failed.");
+ return reason;
+ }
}
if ((reason=circuit_send_next_onion_skin(TO_ORIGIN_CIRCUIT(circ)))<0) {
log_info(domain,"circuit_send_next_onion_skin() failed.");
@@ -1284,7 +1452,8 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
/*XXXX024: Downgrade this back to LOG_PROTOCOL_WARN after a while*/
log_fn(LOG_WARN, LD_PROTOCOL,
"Bug/attack: unexpected sendme cell from client. "
- "Closing circ.");
+ "Closing circ (window %d).",
+ circ->package_window);
return -END_CIRC_REASON_TORPROTOCOL;
}
circ->package_window += CIRCWINDOW_INCREMENT;
@@ -1394,11 +1563,12 @@ connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial,
circuit_t *circ;
const unsigned domain = conn->base_.type == CONN_TYPE_AP ? LD_APP : LD_EXIT;
int sending_from_optimistic = 0;
+ entry_connection_t *entry_conn =
+ conn->base_.type == CONN_TYPE_AP ? EDGE_TO_ENTRY_CONN(conn) : NULL;
const int sending_optimistically =
+ entry_conn &&
conn->base_.type == CONN_TYPE_AP &&
conn->base_.state != AP_CONN_STATE_OPEN;
- entry_connection_t *entry_conn =
- conn->base_.type == CONN_TYPE_AP ? EDGE_TO_ENTRY_CONN(conn) : NULL;
crypt_path_t *cpath_layer = conn->cpath_layer;
tor_assert(conn);
@@ -1473,7 +1643,8 @@ connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial,
connection_fetch_from_buf(payload, length, TO_CONN(conn));
}
- log_debug(domain,"(%d) Packaging %d bytes (%d waiting).", conn->base_.s,
+ log_debug(domain,TOR_SOCKET_T_FORMAT": Packaging %d bytes (%d waiting).",
+ conn->base_.s,
(int)length, (int)connection_get_inbuf_len(TO_CONN(conn)));
if (sending_optimistically && !sending_from_optimistic) {
@@ -1573,6 +1744,12 @@ circuit_resume_edge_reading(circuit_t *circ, crypt_path_t *layer_hint)
circ, layer_hint);
}
+void
+stream_choice_seed_weak_rng(void)
+{
+ crypto_seed_weak_rng(&stream_choice_rng);
+}
+
/** A helper function for circuit_resume_edge_reading() above.
* The arguments are the same, except that <b>conn</b> is the head
* of a linked list of edge streams that should each be considered.
@@ -1588,11 +1765,18 @@ circuit_resume_edge_reading_helper(edge_connection_t *first_conn,
int cells_on_queue;
int cells_per_conn;
edge_connection_t *chosen_stream = NULL;
+ int max_to_package;
+
+ if (first_conn == NULL) {
+ /* Don't bother to try to do the rest of this if there are no connections
+ * to resume. */
+ return 0;
+ }
/* How many cells do we have space for? It will be the minimum of
* the number needed to exhaust the package window, and the minimum
* needed to fill the cell queue. */
- int max_to_package = circ->package_window;
+ max_to_package = circ->package_window;
if (CIRCUIT_IS_ORIGIN(circ)) {
cells_on_queue = circ->n_chan_cells.n;
} else {
@@ -1617,10 +1801,19 @@ circuit_resume_edge_reading_helper(edge_connection_t *first_conn,
int num_streams = 0;
for (conn = first_conn; conn; conn = conn->next_stream) {
num_streams++;
- if ((tor_weak_random() % num_streams)==0)
+ if (tor_weak_random_one_in_n(&stream_choice_rng, num_streams)) {
chosen_stream = conn;
+ }
/* Invariant: chosen_stream has been chosen uniformly at random from
- * among the first num_streams streams on first_conn. */
+ * among the first num_streams streams on first_conn.
+ *
+ * (Note that we iterate over every stream on the circuit, so that after
+ * we've considered the first stream, we've chosen it with P=1; and
+ * after we consider the second stream, we've switched to it with P=1/2
+ * and stayed with the first stream with P=1/2; and after we've
+ * considered the third stream, we've switched to it with P=1/3 and
+ * remained with one of the first two streams with P=(2/3), giving each
+ * one P=(1/2)(2/3) )=(1/3).) */
}
}
@@ -1863,8 +2056,9 @@ dump_cell_pool_usage(int severity)
n_cells += TO_OR_CIRCUIT(c)->p_chan_cells.n;
++n_circs;
}
- log(severity, LD_MM, "%d cells allocated on %d circuits. %d cells leaked.",
- n_cells, n_circs, total_cells_allocated - n_cells);
+ tor_log(severity, LD_MM,
+ "%d cells allocated on %d circuits. %d cells leaked.",
+ n_cells, n_circs, total_cells_allocated - n_cells);
mp_pool_log_status(cell_pool, severity);
}
@@ -1977,7 +2171,8 @@ cell_queue_pop(cell_queue_t *queue)
* circuit mux.
*/
void
-update_circuit_on_cmux(circuit_t *circ, cell_direction_t direction)
+update_circuit_on_cmux_(circuit_t *circ, cell_direction_t direction,
+ const char *file, int lineno)
{
channel_t *chan = NULL;
or_circuit_t *or_circ = NULL;
@@ -2000,7 +2195,11 @@ update_circuit_on_cmux(circuit_t *circ, cell_direction_t direction)
cmux = chan->cmux;
/* Cmux sanity check */
- tor_assert(circuitmux_is_circuit_attached(cmux, circ));
+ if (! circuitmux_is_circuit_attached(cmux, circ)) {
+ log_warn(LD_BUG, "called on non-attachd circuit from %s:%d",
+ file, lineno);
+ return;
+ }
tor_assert(circuitmux_attached_circuit_direction(cmux, circ) == direction);
assert_cmux_ok_paranoid(chan);
@@ -2335,7 +2534,8 @@ circuit_clear_cell_queue(circuit_t *circ, channel_t *chan)
cell_queue_clear(queue);
/* Update the cell counter in the cmux */
- update_circuit_on_cmux(circ, direction);
+ if (chan->cmux && circuitmux_is_circuit_attached(chan->cmux, circ))
+ update_circuit_on_cmux(circ, direction);
}
/** Fail with an assert if the circuit mux on chan is corrupt
diff --git a/src/or/relay.h b/src/or/relay.h
index 96c2a9dbb2..7e59838f95 100644
--- a/src/or/relay.h
+++ b/src/or/relay.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -55,7 +55,10 @@ void append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan,
void channel_unlink_all_circuits(channel_t *chan);
int channel_flush_from_first_active_circuit(channel_t *chan, int max);
void assert_circuit_mux_okay(channel_t *chan);
-void update_circuit_on_cmux(circuit_t *circ, cell_direction_t direction);
+void update_circuit_on_cmux_(circuit_t *circ, cell_direction_t direction,
+ const char *file, int lineno);
+#define update_circuit_on_cmux(circ, direction) \
+ update_circuit_on_cmux_((circ), (direction), SHORT_FILE__, __LINE__)
int append_address_to_payload(uint8_t *payload_out, const tor_addr_t *addr);
const uint8_t *decode_address_from_payload(tor_addr_t *addr_out,
@@ -63,9 +66,13 @@ const uint8_t *decode_address_from_payload(tor_addr_t *addr_out,
int payload_len);
void circuit_clear_cell_queue(circuit_t *circ, channel_t *chan);
+void stream_choice_seed_weak_rng(void);
+
#ifdef RELAY_PRIVATE
int relay_crypt(circuit_t *circ, cell_t *cell, cell_direction_t cell_direction,
crypt_path_t **layer_hint, char *recognized);
+int connected_cell_parse(const relay_header_t *rh, const cell_t *cell,
+ tor_addr_t *addr_out, int *ttl_out);
#endif
#endif
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index 915a41a0c3..61e3b917e3 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -1,5 +1,5 @@
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -66,6 +66,14 @@ rend_client_send_establish_rendezvous(origin_circuit_t *circ)
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
return -1;
}
+
+ /* Set timestamp_dirty, because circuit_expire_building expects it,
+ * and the rend cookie also means we've used the circ. */
+ circ->base_.timestamp_dirty = time(NULL);
+
+ /* We've attempted to use this circuit. Probe it if we fail */
+ pathbias_count_use_attempt(circ);
+
if (relay_send_command_from_edge(0, TO_CIRCUIT(circ),
RELAY_COMMAND_ESTABLISH_RENDEZVOUS,
circ->rend_data->rend_cookie,
@@ -100,6 +108,7 @@ rend_client_reextend_intro_circuit(origin_circuit_t *circ)
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
return -1;
}
+ // XXX: should we not re-extend if hs_circ_has_timed_out?
if (circ->remaining_relay_early_cells) {
log_info(LD_REND,
"Re-extending circ %d, this time to %s.",
@@ -206,12 +215,12 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
cpath = rendcirc->build_state->pending_final_cpath =
tor_malloc_zero(sizeof(crypt_path_t));
cpath->magic = CRYPT_PATH_MAGIC;
- if (!(cpath->dh_handshake_state = crypto_dh_new(DH_TYPE_REND))) {
+ if (!(cpath->rend_dh_handshake_state = crypto_dh_new(DH_TYPE_REND))) {
log_warn(LD_BUG, "Internal error: couldn't allocate DH.");
status = -2;
goto perm_err;
}
- if (crypto_dh_generate_public(cpath->dh_handshake_state)<0) {
+ if (crypto_dh_generate_public(cpath->rend_dh_handshake_state)<0) {
log_warn(LD_BUG, "Internal error: couldn't generate g^x.");
status = -2;
goto perm_err;
@@ -261,7 +270,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
dh_offset = MAX_NICKNAME_LEN+1+REND_COOKIE_LEN;
}
- if (crypto_dh_get_public(cpath->dh_handshake_state, tmp+dh_offset,
+ if (crypto_dh_get_public(cpath->rend_dh_handshake_state, tmp+dh_offset,
DH_KEY_LEN)<0) {
log_warn(LD_BUG, "Internal error: couldn't extract g^x.");
status = -2;
@@ -310,6 +319,8 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
* state. */
introcirc->base_.timestamp_dirty = time(NULL);
+ pathbias_count_use_attempt(introcirc);
+
goto cleanup;
perm_err:
@@ -317,8 +328,8 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
circuit_mark_for_close(TO_CIRCUIT(introcirc), END_CIRC_REASON_INTERNAL);
circuit_mark_for_close(TO_CIRCUIT(rendcirc), END_CIRC_REASON_INTERNAL);
cleanup:
- memset(payload, 0, sizeof(payload));
- memset(tmp, 0, sizeof(tmp));
+ memwipe(payload, 0, sizeof(payload));
+ memwipe(tmp, 0, sizeof(tmp));
return status;
}
@@ -338,6 +349,32 @@ rend_client_rendcirc_has_opened(origin_circuit_t *circ)
}
}
+/**
+ * Called to close other intro circuits we launched in parallel
+ * due to timeout.
+ */
+static void
+rend_client_close_other_intros(const char *onion_address)
+{
+ circuit_t *c;
+ /* abort parallel intro circs, if any */
+ for (c = circuit_get_global_list_(); c; c = c->next) {
+ if ((c->purpose == CIRCUIT_PURPOSE_C_INTRODUCING ||
+ c->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) &&
+ !c->marked_for_close && CIRCUIT_IS_ORIGIN(c)) {
+ origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(c);
+ if (oc->rend_data &&
+ !rend_cmp_service_ids(onion_address,
+ oc->rend_data->onion_address)) {
+ log_info(LD_REND|LD_CIRC, "Closing introduction circuit %d that we "
+ "built in parallel (Purpose %d).", oc->global_identifier,
+ c->purpose);
+ circuit_mark_for_close(c, END_CIRC_REASON_TIMEOUT);
+ }
+ }
+ }
+}
+
/** Called when get an ACK or a NAK for a REND_INTRODUCE1 cell.
*/
int
@@ -361,6 +398,10 @@ rend_client_introduction_acked(origin_circuit_t *circ,
#endif
tor_assert(circ->rend_data);
+ /* For path bias: This circuit was used successfully. Valid
+ * nacks and acks count. */
+ pathbias_mark_use_success(circ);
+
if (request_len == 0) {
/* It's an ACK; the introduction point relayed our introduction request. */
/* Locate the rend circ which is waiting to hear about this ack,
@@ -385,6 +426,9 @@ rend_client_introduction_acked(origin_circuit_t *circ,
circuit_change_purpose(TO_CIRCUIT(circ),
CIRCUIT_PURPOSE_C_INTRODUCE_ACKED);
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_FINISHED);
+
+ /* close any other intros launched in parallel */
+ rend_client_close_other_intros(circ->rend_data->onion_address);
} else {
/* It's a NAK; the introduction point didn't relay our request. */
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_INTRODUCING);
@@ -696,7 +740,7 @@ rend_client_refetch_v2_renddesc(const rend_data_t *rend_query)
rend_client_desc_trynow(rend_query->onion_address);
done:
- memset(descriptor_id, 0, sizeof(descriptor_id));
+ memwipe(descriptor_id, 0, sizeof(descriptor_id));
return;
}
@@ -858,6 +902,13 @@ rend_client_rendezvous_acked(origin_circuit_t *circ, const uint8_t *request,
/* Set timestamp_dirty, because circuit_expire_building expects it
* to specify when a circuit entered the _C_REND_READY state. */
circ->base_.timestamp_dirty = time(NULL);
+
+ /* From a path bias point of view, this circuit is now successfully used.
+ * Waiting any longer opens us up to attacks from Bob. He could induce
+ * Alice to attempt to connect to his hidden service and never reply
+ * to her rend requests */
+ pathbias_mark_use_success(circ);
+
/* XXXX 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. */
@@ -896,9 +947,9 @@ rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request,
tor_assert(circ->build_state);
tor_assert(circ->build_state->pending_final_cpath);
hop = circ->build_state->pending_final_cpath;
- tor_assert(hop->dh_handshake_state);
+ tor_assert(hop->rend_dh_handshake_state);
if (crypto_dh_compute_secret(LOG_PROTOCOL_WARN,
- hop->dh_handshake_state, (char*)request,
+ hop->rend_dh_handshake_state, (char*)request,
DH_KEY_LEN,
keys, DIGEST_LEN+CPATH_KEY_MATERIAL_LEN)<0) {
log_warn(LD_GENERAL, "Couldn't complete DH handshake.");
@@ -914,8 +965,8 @@ rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request,
goto err;
}
- crypto_dh_free(hop->dh_handshake_state);
- hop->dh_handshake_state = NULL;
+ crypto_dh_free(hop->rend_dh_handshake_state);
+ hop->rend_dh_handshake_state = NULL;
/* All is well. Extend the circuit. */
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_REND_JOINED);
@@ -936,10 +987,10 @@ rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request,
circuit_try_attaching_streams(circ);
- memset(keys, 0, sizeof(keys));
+ memwipe(keys, 0, sizeof(keys));
return 0;
err:
- memset(keys, 0, sizeof(keys));
+ memwipe(keys, 0, sizeof(keys));
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
return -1;
}
@@ -1281,8 +1332,8 @@ rend_parse_service_authorization(const or_options_t *options,
} else {
strmap_free(parsed, rend_service_authorization_strmap_item_free);
}
- memset(descriptor_cookie_tmp, 0, sizeof(descriptor_cookie_tmp));
- memset(descriptor_cookie_base64ext, 0, sizeof(descriptor_cookie_base64ext));
+ memwipe(descriptor_cookie_tmp, 0, sizeof(descriptor_cookie_tmp));
+ memwipe(descriptor_cookie_base64ext, 0, sizeof(descriptor_cookie_base64ext));
return res;
}
diff --git a/src/or/rendclient.h b/src/or/rendclient.h
index b71fe48be3..1f731d0ae5 100644
--- a/src/or/rendclient.h
+++ b/src/or/rendclient.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c
index 9e306bdf73..79c1a724e4 100644
--- a/src/or/rendcommon.c
+++ b/src/or/rendcommon.c
@@ -1,5 +1,5 @@
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -954,7 +954,7 @@ rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **e)
return 1;
}
-/** <b>query</b> is a base-32'ed service id. If it's malformed, return -1.
+/** <b>query</b> is a base32'ed service id. If it's malformed, return -1.
* Else look it up.
* - If it is found, point *desc to it, and write its length into
* *desc_len, and return 1.
diff --git a/src/or/rendcommon.h b/src/or/rendcommon.h
index fe574b152f..189891b747 100644
--- a/src/or/rendcommon.h
+++ b/src/or/rendcommon.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/rendmid.c b/src/or/rendmid.c
index dc2dc1d9e7..1046ce3814 100644
--- a/src/or/rendmid.c
+++ b/src/or/rendmid.c
@@ -1,5 +1,5 @@
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -56,8 +56,8 @@ rend_mid_establish_intro(or_circuit_t *circ, const uint8_t *request,
goto err;
}
- /* Next 20 bytes: Hash of handshake_digest | "INTRODUCE" */
- memcpy(buf, circ->handshake_digest, DIGEST_LEN);
+ /* Next 20 bytes: Hash of rend_circ_nonce | "INTRODUCE" */
+ memcpy(buf, circ->rend_circ_nonce, DIGEST_LEN);
memcpy(buf+DIGEST_LEN, "INTRODUCE", 9);
if (crypto_digest(expected_digest, buf, DIGEST_LEN+9) < 0) {
log_warn(LD_BUG, "Internal error computing digest.");
diff --git a/src/or/rendmid.h b/src/or/rendmid.h
index 74ba16b316..310276ac96 100644
--- a/src/or/rendmid.h
+++ b/src/or/rendmid.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index fe0333ef40..10d232c039 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -1,5 +1,5 @@
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -88,7 +88,7 @@ typedef struct rend_service_port_config_t {
/** 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
+#define INTRO_POINT_EXPIRATION_GRACE_PERIOD (5*60)
/** Represents a single hidden service running at this OP. */
typedef struct rend_service_t {
@@ -161,7 +161,7 @@ rend_authorized_client_free(rend_authorized_client_t *client)
crypto_pk_free(client->client_key);
tor_strclear(client->client_name);
tor_free(client->client_name);
- memset(client->descriptor_cookie, 0, sizeof(client->descriptor_cookie));
+ memwipe(client->descriptor_cookie, 0, sizeof(client->descriptor_cookie));
tor_free(client);
}
@@ -699,10 +699,10 @@ rend_service_load_keys(rend_service_t *s)
tor_snprintf(buf, sizeof(buf),"%s.onion\n", s->service_id);
if (write_str_to_file(fname,buf,0)<0) {
log_warn(LD_CONFIG, "Could not write onion address to hostname file.");
- memset(buf, 0, sizeof(buf));
+ memwipe(buf, 0, sizeof(buf));
return -1;
}
- memset(buf, 0, sizeof(buf));
+ memwipe(buf, 0, sizeof(buf));
/* If client authorization is configured, load or generate keys. */
if (s->auth_type != REND_NO_AUTH) {
@@ -830,13 +830,13 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname)
* len is string length, not buffer length, but last byte is NUL
* anyway.
*/
- memset(client_key_out, 0, len);
+ memwipe(client_key_out, 0, len);
tor_free(client_key_out);
goto err;
}
written = tor_snprintf(buf + written, sizeof(buf) - written,
"client-key\n%s", client_key_out);
- memset(client_key_out, 0, len);
+ memwipe(client_key_out, 0, len);
tor_free(client_key_out);
if (written < 0) {
log_warn(LD_BUG, "Could not write client entry.");
@@ -897,13 +897,13 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname)
}
strmap_free(parsed_clients, rend_authorized_client_strmap_item_free);
- memset(cfname, 0, sizeof(cfname));
+ memwipe(cfname, 0, sizeof(cfname));
/* Clear stack buffers that held key-derived material. */
- memset(buf, 0, sizeof(buf));
- memset(desc_cook_out, 0, sizeof(desc_cook_out));
- memset(service_id, 0, sizeof(service_id));
- memset(extended_desc_cookie, 0, sizeof(extended_desc_cookie));
+ memwipe(buf, 0, sizeof(buf));
+ memwipe(desc_cook_out, 0, sizeof(desc_cook_out));
+ memwipe(service_id, 0, sizeof(service_id));
+ memwipe(extended_desc_cookie, 0, sizeof(extended_desc_cookie));
return r;
}
@@ -931,7 +931,7 @@ rend_service_requires_uptime(rend_service_t *service)
for (i=0; i < smartlist_len(service->ports); ++i) {
p = smartlist_get(service->ports, i);
- if (smartlist_string_num_isin(get_options()->LongLivedPorts,
+ if (smartlist_contains_int_as_string(get_options()->LongLivedPorts,
p->virtual_port))
return 1;
}
@@ -1378,11 +1378,11 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
cpath->magic = CRYPT_PATH_MAGIC;
launched->build_state->expiry_time = now + MAX_REND_TIMEOUT;
- cpath->dh_handshake_state = dh;
+ cpath->rend_dh_handshake_state = dh;
dh = NULL;
if (circuit_init_cpath_crypto(cpath,keys+DIGEST_LEN,1)<0)
goto err;
- memcpy(cpath->handshake_digest, keys, DIGEST_LEN);
+ memcpy(cpath->rend_circ_nonce, keys, DIGEST_LEN);
goto done;
@@ -1406,13 +1406,13 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
tor_free(err_msg);
done:
- memset(keys, 0, sizeof(keys));
- memset(buf, 0, sizeof(buf));
- memset(serviceid, 0, sizeof(serviceid));
- memset(hexcookie, 0, sizeof(hexcookie));
- memset(intro_key_digest, 0, sizeof(intro_key_digest));
- memset(auth_data, 0, sizeof(auth_data));
- memset(diffie_hellman_hash, 0, sizeof(diffie_hellman_hash));
+ memwipe(keys, 0, sizeof(keys));
+ memwipe(buf, 0, sizeof(buf));
+ memwipe(serviceid, 0, sizeof(serviceid));
+ memwipe(hexcookie, 0, sizeof(hexcookie));
+ memwipe(intro_key_digest, 0, sizeof(intro_key_digest));
+ memwipe(auth_data, 0, sizeof(auth_data));
+ memwipe(diffie_hellman_hash, 0, sizeof(diffie_hellman_hash));
/* Free the parsed cell */
if (parsed_req) {
@@ -1540,7 +1540,7 @@ rend_service_free_intro(rend_intro_cell_t *request)
/* Have plaintext? */
if (request->plaintext) {
/* Zero it out just to be safe */
- memset(request->plaintext, 0, request->plaintext_len);
+ memwipe(request->plaintext, 0, request->plaintext_len);
tor_free(request->plaintext);
request->plaintext_len = 0;
}
@@ -1561,7 +1561,7 @@ rend_service_free_intro(rend_intro_cell_t *request)
break;
case 3:
if (request->u.v3.auth_data) {
- memset(request->u.v3.auth_data, 0, request->u.v3.auth_len);
+ memwipe(request->u.v3.auth_data, 0, request->u.v3.auth_len);
tor_free(request->u.v3.auth_data);
}
@@ -1577,7 +1577,7 @@ rend_service_free_intro(rend_intro_cell_t *request)
}
/* Zero it out to make sure sensitive stuff doesn't hang around in memory */
- memset(request, 0, sizeof(*request));
+ memwipe(request, 0, sizeof(*request));
tor_free(request);
}
@@ -2075,9 +2075,9 @@ rend_service_decrypt_intro(
else tor_free(err_msg);
/* clean up potentially sensitive material */
- memset(buf, 0, sizeof(buf));
- memset(key_digest, 0, sizeof(key_digest));
- memset(service_id, 0, sizeof(service_id));
+ memwipe(buf, 0, sizeof(buf));
+ memwipe(key_digest, 0, sizeof(key_digest));
+ memwipe(service_id, 0, sizeof(service_id));
return status;
}
@@ -2483,7 +2483,7 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
len = r;
set_uint16(buf, htons((uint16_t)len));
len += 2;
- memcpy(auth, circuit->cpath->prev->handshake_digest, DIGEST_LEN);
+ memcpy(auth, circuit->cpath->prev->rend_circ_nonce, DIGEST_LEN);
memcpy(auth+DIGEST_LEN, "INTRODUCE", 9);
if (crypto_digest(buf+len, auth, DIGEST_LEN+9))
goto err;
@@ -2508,14 +2508,17 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
goto err;
}
+ /* We've attempted to use this circuit */
+ pathbias_count_use_attempt(circuit);
+
goto done;
err:
circuit_mark_for_close(TO_CIRCUIT(circuit), reason);
done:
- memset(buf, 0, sizeof(buf));
- memset(auth, 0, sizeof(auth));
- memset(serviceid, 0, sizeof(serviceid));
+ memwipe(buf, 0, sizeof(buf));
+ memwipe(auth, 0, sizeof(auth));
+ memwipe(serviceid, 0, sizeof(serviceid));
return;
}
@@ -2555,6 +2558,10 @@ rend_service_intro_established(origin_circuit_t *circuit,
"Received INTRO_ESTABLISHED cell on circuit %d for service %s",
circuit->base_.n_circ_id, serviceid);
+ /* Getting a valid INTRODUCE_ESTABLISHED means we've successfully
+ * used the circ */
+ pathbias_mark_use_success(circuit);
+
return 0;
err:
circuit_mark_for_close(TO_CIRCUIT(circuit), END_CIRC_REASON_TORPROTOCOL);
@@ -2581,6 +2588,14 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
tor_assert(!(circuit->build_state->onehop_tunnel));
#endif
tor_assert(circuit->rend_data);
+
+ /* Declare the circuit dirty to avoid reuse, and for path-bias */
+ if (!circuit->base_.timestamp_dirty)
+ circuit->base_.timestamp_dirty = time(NULL);
+
+ /* This may be redundant */
+ pathbias_count_use_attempt(circuit);
+
hop = circuit->build_state->service_pending_final_cpath_ref->cpath;
base16_encode(hexcookie,9,circuit->rend_data->rend_cookie,4);
@@ -2624,13 +2639,13 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
/* All we need to do is send a RELAY_RENDEZVOUS1 cell... */
memcpy(buf, circuit->rend_data->rend_cookie, REND_COOKIE_LEN);
- if (crypto_dh_get_public(hop->dh_handshake_state,
+ if (crypto_dh_get_public(hop->rend_dh_handshake_state,
buf+REND_COOKIE_LEN, DH_KEY_LEN)<0) {
log_warn(LD_GENERAL,"Couldn't get DH public key.");
reason = END_CIRC_REASON_INTERNAL;
goto err;
}
- memcpy(buf+REND_COOKIE_LEN+DH_KEY_LEN, hop->handshake_digest,
+ memcpy(buf+REND_COOKIE_LEN+DH_KEY_LEN, hop->rend_circ_nonce,
DIGEST_LEN);
/* Send the cell */
@@ -2643,8 +2658,8 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
goto err;
}
- crypto_dh_free(hop->dh_handshake_state);
- hop->dh_handshake_state = NULL;
+ crypto_dh_free(hop->rend_dh_handshake_state);
+ hop->rend_dh_handshake_state = NULL;
/* Append the cpath entry. */
hop->state = CPATH_STATE_OPEN;
@@ -2665,9 +2680,9 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
err:
circuit_mark_for_close(TO_CIRCUIT(circuit), reason);
done:
- memset(buf, 0, sizeof(buf));
- memset(serviceid, 0, sizeof(serviceid));
- memset(hexcookie, 0, sizeof(hexcookie));
+ memwipe(buf, 0, sizeof(buf));
+ memwipe(serviceid, 0, sizeof(serviceid));
+ memwipe(hexcookie, 0, sizeof(hexcookie));
return;
}
@@ -2766,7 +2781,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
char *hs_dir_ip;
const node_t *node;
hs_dir = smartlist_get(responsible_dirs, j);
- if (smartlist_digest_isin(renddesc->successful_uploads,
+ if (smartlist_contains_digest(renddesc->successful_uploads,
hs_dir->identity_digest))
/* Don't upload descriptor if we succeeded in doing so last time. */
continue;
@@ -2801,7 +2816,8 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
hs_dir->or_port);
tor_free(hs_dir_ip);
/* Remember successful upload to this router for next time. */
- if (!smartlist_digest_isin(successful_uploads, hs_dir->identity_digest))
+ if (!smartlist_contains_digest(successful_uploads,
+ hs_dir->identity_digest))
smartlist_add(successful_uploads, hs_dir->identity_digest);
}
smartlist_clear(responsible_dirs);
@@ -2819,7 +2835,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
if (!renddesc->successful_uploads)
renddesc->successful_uploads = smartlist_new();
SMARTLIST_FOREACH(successful_uploads, const char *, c, {
- if (!smartlist_digest_isin(renddesc->successful_uploads, c)) {
+ if (!smartlist_contains_digest(renddesc->successful_uploads, c)) {
char *hsdir_id = tor_memdup(c, DIGEST_LEN);
smartlist_add(renddesc->successful_uploads, hsdir_id);
}
@@ -3052,7 +3068,8 @@ rend_services_introduce(void)
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) {
+ if (intro_circ != NULL &&
+ intro_circ->base_.purpose != CIRCUIT_PURPOSE_PATH_BIAS_TESTING) {
circuit_mark_for_close(TO_CIRCUIT(intro_circ),
END_CIRC_REASON_FINISHED);
}
@@ -3287,7 +3304,7 @@ rend_service_dump_stats(int severity)
for (i=0; i < smartlist_len(rend_service_list); ++i) {
service = smartlist_get(rend_service_list, i);
- log(severity, LD_GENERAL, "Service configured in \"%s\":",
+ tor_log(severity, LD_GENERAL, "Service configured in \"%s\":",
service->directory);
for (j=0; j < smartlist_len(service->intro_nodes); ++j) {
intro = smartlist_get(service->intro_nodes, j);
@@ -3295,11 +3312,11 @@ rend_service_dump_stats(int severity)
circ = find_intro_circuit(intro, service->pk_digest);
if (!circ) {
- log(severity, LD_GENERAL, " Intro point %d at %s: no circuit",
+ tor_log(severity, LD_GENERAL, " Intro point %d at %s: no circuit",
j, safe_name);
continue;
}
- log(severity, LD_GENERAL, " Intro point %d at %s: circuit is %s",
+ tor_log(severity, LD_GENERAL, " Intro point %d at %s: circuit is %s",
j, safe_name, circuit_state_to_string(circ->base_.state));
}
}
diff --git a/src/or/rendservice.h b/src/or/rendservice.h
index 1671602348..ff31ba6edb 100644
--- a/src/or/rendservice.h
+++ b/src/or/rendservice.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/rephist.c b/src/or/rephist.c
index e61e599d7e..55f321d5ff 100644
--- a/src/or/rephist.c
+++ b/src/or/rephist.c
@@ -1,5 +1,5 @@
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -310,9 +310,10 @@ rep_hist_note_router_reachable(const char *id, const tor_addr_t *at_addr,
tor_assert(hist);
tor_assert((!at_addr && !at_port) || (at_addr && at_port));
- addr_changed = at_addr &&
+ addr_changed = at_addr && !tor_addr_is_null(&hist->last_reached_addr) &&
tor_addr_compare(at_addr, &hist->last_reached_addr, CMP_EXACT) != 0;
- port_changed = at_port && at_port != hist->last_reached_port;
+ port_changed = at_port && hist->last_reached_port &&
+ at_port != hist->last_reached_port;
if (!started_tracking_stability)
started_tracking_stability = time(NULL);
@@ -422,6 +423,21 @@ rep_hist_note_router_unreachable(const char *id, time_t when)
}
}
+/** Mark a router with ID <b>id</b> as non-Running, and retroactively declare
+ * that it has never been running: give it no stability and no WFU. */
+void
+rep_hist_make_router_pessimal(const char *id, time_t when)
+{
+ or_history_t *hist = get_or_history(id);
+ tor_assert(hist);
+
+ rep_hist_note_router_unreachable(id, when);
+ mark_or_down(hist, when, 1);
+
+ hist->weighted_run_length = 0;
+ hist->weighted_uptime = 0;
+}
+
/** Helper: Discount all old MTBF data, if it is time to do so. Return
* the time at which we should next discount MTBF data. */
time_t
@@ -648,7 +664,7 @@ rep_hist_dump_stats(time_t now, int severity)
rep_history_clean(now - get_options()->RephistTrackTime);
- log(severity, LD_HIST, "--------------- Dumping history information:");
+ tor_log(severity, LD_HIST, "--------------- Dumping history information:");
for (orhist_it = digestmap_iter_init(history_map);
!digestmap_iter_done(orhist_it);
@@ -673,7 +689,7 @@ rep_hist_dump_stats(time_t now, int severity)
} else {
uptime=1.0;
}
- log(severity, LD_HIST,
+ tor_log(severity, LD_HIST,
"OR %s [%s]: %ld/%ld good connections; uptime %ld/%ld sec (%.2f%%); "
"wmtbf %lu:%02lu:%02lu",
name1, hexdigest1,
@@ -707,7 +723,7 @@ rep_hist_dump_stats(time_t now, int severity)
else
len += ret;
}
- log(severity, LD_HIST, "%s", buffer);
+ tor_log(severity, LD_HIST, "%s", buffer);
}
}
}
@@ -1534,7 +1550,7 @@ rep_hist_get_bandwidth_lines(void)
/* [dirreq-](read|write)-history yyyy-mm-dd HH:MM:SS (n s) n,n,n... */
/* The n,n,n part above. Largest representation of a uint64_t is 20 chars
* long, plus the comma. */
-#define MAX_HIST_VALUE_LEN 21*NUM_TOTALS
+#define MAX_HIST_VALUE_LEN (21*NUM_TOTALS)
len = (67+MAX_HIST_VALUE_LEN)*4;
buf = tor_malloc_zero(len);
cp = buf;
@@ -2042,7 +2058,7 @@ note_crypto_pk_op(pk_op_t operation)
void
dump_pk_ops(int severity)
{
- log(severity, LD_HIST,
+ tor_log(severity, LD_HIST,
"PK operations: %lu directory objects signed, "
"%lu directory objects verified, "
"%lu routerdescs signed, "
diff --git a/src/or/rephist.h b/src/or/rephist.h
index 28dec8f902..811cd8d450 100644
--- a/src/or/rephist.h
+++ b/src/or/rephist.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -24,6 +24,8 @@ void rep_hist_dump_stats(time_t now, int severity);
void rep_hist_note_bytes_read(size_t num_bytes, time_t when);
void rep_hist_note_bytes_written(size_t num_bytes, time_t when);
+void rep_hist_make_router_pessimal(const char *id, time_t when);
+
void rep_hist_note_dir_bytes_read(size_t num_bytes, time_t when);
void rep_hist_note_dir_bytes_written(size_t num_bytes, time_t when);
diff --git a/src/or/replaycache.c b/src/or/replaycache.c
index 868b21c9b0..59b98489b7 100644
--- a/src/or/replaycache.c
+++ b/src/or/replaycache.c
@@ -1,4 +1,4 @@
- /* Copyright (c) 2012, The Tor Project, Inc. */
+ /* Copyright (c) 2012-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/*
diff --git a/src/or/replaycache.h b/src/or/replaycache.h
index 757102b960..de20cab627 100644
--- a/src/or/replaycache.h
+++ b/src/or/replaycache.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Tor Project, Inc. */
+/* Copyright (c) 2012-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/or/router.c b/src/or/router.c
index 562704f142..1f372711cc 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#define ROUTER_PRIVATE
@@ -13,6 +13,7 @@
#include "config.h"
#include "connection.h"
#include "control.h"
+#include "crypto_curve25519.h"
#include "directory.h"
#include "dirserv.h"
#include "dns.h"
@@ -54,6 +55,13 @@ static crypto_pk_t *onionkey=NULL;
/** Previous private onionskin decryption key: used to decode CREATE cells
* generated by clients that have an older version of our descriptor. */
static crypto_pk_t *lastonionkey=NULL;
+#ifdef CURVE25519_ENABLED
+/** Current private ntor secret key: used to perform the ntor handshake. */
+static curve25519_keypair_t curve25519_onion_key;
+/** Previous private ntor secret key: used to perform the ntor handshake
+ * with clients that have an older version of our descriptor. */
+static curve25519_keypair_t last_curve25519_onion_key;
+#endif
/** Private server "identity key": used to sign directory info and TLS
* certificates. Never changes. */
static crypto_pk_t *server_identitykey=NULL;
@@ -126,6 +134,55 @@ dup_onion_keys(crypto_pk_t **key, crypto_pk_t **last)
tor_mutex_release(key_lock);
}
+#ifdef CURVE25519_ENABLED
+/** Return the current secret onion key for the ntor handshake. Must only
+ * be called from the main thread. */
+static const curve25519_keypair_t *
+get_current_curve25519_keypair(void)
+{
+ return &curve25519_onion_key;
+}
+/** Return a map from KEYID (the key itself) to keypairs for use in the ntor
+ * handshake. Must only be called from the main thread. */
+di_digest256_map_t *
+construct_ntor_key_map(void)
+{
+ di_digest256_map_t *m = NULL;
+
+ dimap_add_entry(&m,
+ curve25519_onion_key.pubkey.public_key,
+ tor_memdup(&curve25519_onion_key,
+ sizeof(curve25519_keypair_t)));
+ if (!tor_mem_is_zero((const char*)
+ last_curve25519_onion_key.pubkey.public_key,
+ CURVE25519_PUBKEY_LEN)) {
+ dimap_add_entry(&m,
+ last_curve25519_onion_key.pubkey.public_key,
+ tor_memdup(&last_curve25519_onion_key,
+ sizeof(curve25519_keypair_t)));
+ }
+
+ return m;
+}
+/** Helper used to deallocate a di_digest256_map_t returned by
+ * construct_ntor_key_map. */
+static void
+ntor_key_map_free_helper(void *arg)
+{
+ curve25519_keypair_t *k = arg;
+ memwipe(k, 0, sizeof(*k));
+ tor_free(k);
+}
+/** Release all storage from a keymap returned by construct_ntor_key_map. */
+void
+ntor_key_map_free(di_digest256_map_t *map)
+{
+ if (!map)
+ return;
+ dimap_free(map, ntor_key_map_free_helper);
+}
+#endif
+
/** Return the time when the onion key was last set. This is either the time
* when the process launched, or the time of the most recent key rotation since
* the process launched.
@@ -253,11 +310,18 @@ void
rotate_onion_key(void)
{
char *fname, *fname_prev;
- crypto_pk_t *prkey;
+ crypto_pk_t *prkey = NULL;
or_state_t *state = get_or_state();
+#ifdef CURVE25519_ENABLED
+ curve25519_keypair_t new_curve25519_keypair;
+#endif
time_t now;
fname = get_datadir_fname2("keys", "secret_onion_key");
fname_prev = get_datadir_fname2("keys", "secret_onion_key.old");
+ if (file_status(fname) == FN_FILE) {
+ if (replace_file(fname, fname_prev))
+ goto error;
+ }
if (!(prkey = crypto_pk_new())) {
log_err(LD_GENERAL,"Error constructing rotated onion key");
goto error;
@@ -266,19 +330,38 @@ rotate_onion_key(void)
log_err(LD_BUG,"Error generating onion key");
goto error;
}
+ if (crypto_pk_write_private_key_to_filename(prkey, fname)) {
+ log_err(LD_FS,"Couldn't write generated onion key to \"%s\".", fname);
+ goto error;
+ }
+#ifdef CURVE25519_ENABLED
+ tor_free(fname);
+ tor_free(fname_prev);
+ fname = get_datadir_fname2("keys", "secret_onion_key_ntor");
+ fname_prev = get_datadir_fname2("keys", "secret_onion_key_ntor.old");
+ if (curve25519_keypair_generate(&new_curve25519_keypair, 1) < 0)
+ goto error;
if (file_status(fname) == FN_FILE) {
if (replace_file(fname, fname_prev))
goto error;
}
- if (crypto_pk_write_private_key_to_filename(prkey, fname)) {
- log_err(LD_FS,"Couldn't write generated onion key to \"%s\".", fname);
+ if (curve25519_keypair_write_to_file(&new_curve25519_keypair, fname,
+ "onion") < 0) {
+ log_err(LD_FS,"Couldn't write curve25519 onion key to \"%s\".",fname);
goto error;
}
+#endif
log_info(LD_GENERAL, "Rotating onion key");
tor_mutex_acquire(key_lock);
crypto_pk_free(lastonionkey);
lastonionkey = onionkey;
onionkey = prkey;
+#ifdef CURVE25519_ENABLED
+ memcpy(&last_curve25519_onion_key, &curve25519_onion_key,
+ sizeof(curve25519_keypair_t));
+ memcpy(&curve25519_onion_key, &new_curve25519_keypair,
+ sizeof(curve25519_keypair_t));
+#endif
now = time(NULL);
state->LastRotatedOnionKey = onionkey_set_at = now;
tor_mutex_release(key_lock);
@@ -290,6 +373,9 @@ rotate_onion_key(void)
if (prkey)
crypto_pk_free(prkey);
done:
+#ifdef CURVE25519_ENABLED
+ memwipe(&new_curve25519_keypair, 0, sizeof(new_curve25519_keypair));
+#endif
tor_free(fname);
tor_free(fname_prev);
}
@@ -305,14 +391,14 @@ init_key_from_file(const char *fname, int generate, int severity)
crypto_pk_t *prkey = NULL;
if (!(prkey = crypto_pk_new())) {
- log(severity, LD_GENERAL,"Error constructing key");
+ tor_log(severity, LD_GENERAL,"Error constructing key");
goto error;
}
switch (file_status(fname)) {
case FN_DIR:
case FN_ERROR:
- log(severity, LD_FS,"Can't read key from \"%s\"", fname);
+ tor_log(severity, LD_FS,"Can't read key from \"%s\"", fname);
goto error;
case FN_NOENT:
if (generate) {
@@ -320,8 +406,8 @@ init_key_from_file(const char *fname, int generate, int severity)
if (try_locking(get_options(), 0)<0) {
/* Make sure that --list-fingerprint only creates new keys
* if there is no possibility for a deadlock. */
- log(severity, LD_FS, "Another Tor process has locked \"%s\". Not "
- "writing any new keys.", fname);
+ tor_log(severity, LD_FS, "Another Tor process has locked \"%s\". "
+ "Not writing any new keys.", fname);
/*XXXX The 'other process' might make a key in a second or two;
* maybe we should wait for it. */
goto error;
@@ -330,16 +416,16 @@ init_key_from_file(const char *fname, int generate, int severity)
log_info(LD_GENERAL, "No key found in \"%s\"; generating fresh key.",
fname);
if (crypto_pk_generate_key(prkey)) {
- log(severity, LD_GENERAL,"Error generating onion key");
+ tor_log(severity, LD_GENERAL,"Error generating onion key");
goto error;
}
if (crypto_pk_check_key(prkey) <= 0) {
- log(severity, LD_GENERAL,"Generated key seems invalid");
+ tor_log(severity, LD_GENERAL,"Generated key seems invalid");
goto error;
}
log_info(LD_GENERAL, "Generated key seems valid");
if (crypto_pk_write_private_key_to_filename(prkey, fname)) {
- log(severity, LD_FS,
+ tor_log(severity, LD_FS,
"Couldn't write generated key to \"%s\".", fname);
goto error;
}
@@ -349,7 +435,7 @@ init_key_from_file(const char *fname, int generate, int severity)
return prkey;
case FN_FILE:
if (crypto_pk_read_private_key_from_filename(prkey, fname)) {
- log(severity, LD_GENERAL,"Error loading private key.");
+ tor_log(severity, LD_GENERAL,"Error loading private key.");
goto error;
}
return prkey;
@@ -363,6 +449,77 @@ init_key_from_file(const char *fname, int generate, int severity)
return NULL;
}
+#ifdef CURVE25519_ENABLED
+/** Load a curve25519 keypair from the file <b>fname</b>, writing it into
+ * <b>keys_out</b>. If the file isn't found and <b>generate</b> is true,
+ * create a new keypair and write it into the file. If there are errors, log
+ * them at level <b>severity</b>. Generate files using <b>tag</b> in their
+ * ASCII wrapper. */
+static int
+init_curve25519_keypair_from_file(curve25519_keypair_t *keys_out,
+ const char *fname,
+ int generate,
+ int severity,
+ const char *tag)
+{
+ switch (file_status(fname)) {
+ case FN_DIR:
+ case FN_ERROR:
+ tor_log(severity, LD_FS,"Can't read key from \"%s\"", fname);
+ goto error;
+ case FN_NOENT:
+ if (generate) {
+ if (!have_lockfile()) {
+ if (try_locking(get_options(), 0)<0) {
+ /* Make sure that --list-fingerprint only creates new keys
+ * if there is no possibility for a deadlock. */
+ tor_log(severity, LD_FS, "Another Tor process has locked \"%s\". "
+ "Not writing any new keys.", fname);
+ /*XXXX The 'other process' might make a key in a second or two;
+ * maybe we should wait for it. */
+ goto error;
+ }
+ }
+ log_info(LD_GENERAL, "No key found in \"%s\"; generating fresh key.",
+ fname);
+ if (curve25519_keypair_generate(keys_out, 1) < 0)
+ goto error;
+ if (curve25519_keypair_write_to_file(keys_out, fname, tag)<0) {
+ tor_log(severity, LD_FS,
+ "Couldn't write generated key to \"%s\".", fname);
+ memset(keys_out, 0, sizeof(*keys_out));
+ goto error;
+ }
+ } else {
+ log_info(LD_GENERAL, "No key found in \"%s\"", fname);
+ }
+ return 0;
+ case FN_FILE:
+ {
+ char *tag_in=NULL;
+ if (curve25519_keypair_read_from_file(keys_out, &tag_in, fname) < 0) {
+ tor_log(severity, LD_GENERAL,"Error loading private key.");
+ tor_free(tag_in);
+ goto error;
+ }
+ if (!tag_in || strcmp(tag_in, tag)) {
+ tor_log(severity, LD_GENERAL,"Unexpected tag %s on private key.",
+ escaped(tag_in));
+ tor_free(tag_in);
+ goto error;
+ }
+ tor_free(tag_in);
+ return 0;
+ }
+ default:
+ tor_assert(0);
+ }
+
+ error:
+ return -1;
+}
+#endif
+
/** Try to load the vote-signing private key and certificate for being a v3
* directory authority, and make sure they match. If <b>legacy</b>, load a
* legacy key/cert set for emergency key migration; otherwise load the regular
@@ -474,14 +631,14 @@ v3_authority_check_key_expiry(void)
return;
if (time_left <= 0) {
- log(badness, LD_DIR, "Your v3 authority certificate has expired."
- " Generate a new one NOW.");
+ tor_log(badness, LD_DIR, "Your v3 authority certificate has expired."
+ " Generate a new one NOW.");
} else if (time_left <= 24*60*60) {
- log(badness, LD_DIR, "Your v3 authority certificate expires in %d hours;"
- " Generate a new one NOW.", time_left/(60*60));
+ tor_log(badness, LD_DIR, "Your v3 authority certificate expires in %d "
+ "hours; Generate a new one NOW.", time_left/(60*60));
} else {
- log(badness, LD_DIR, "Your v3 authority certificate expires in %d days;"
- " Generate a new one soon.", time_left/(24*60*60));
+ tor_log(badness, LD_DIR, "Your v3 authority certificate expires in %d "
+ "days; Generate a new one soon.", time_left/(24*60*60));
}
last_warned = now;
}
@@ -491,7 +648,18 @@ v3_authority_check_key_expiry(void)
int
router_initialize_tls_context(void)
{
- return tor_tls_context_init(public_server_mode(get_options()),
+ unsigned int flags = 0;
+ const or_options_t *options = get_options();
+ if (public_server_mode(options))
+ flags |= TOR_TLS_CTX_IS_PUBLIC_SERVER;
+ if (options->TLSECGroup) {
+ if (!strcasecmp(options->TLSECGroup, "P256"))
+ flags |= TOR_TLS_CTX_USE_ECDHE_P256;
+ else if (!strcasecmp(options->TLSECGroup, "P224"))
+ flags |= TOR_TLS_CTX_USE_ECDHE_P224;
+ }
+
+ return tor_tls_context_init(flags,
get_tlsclient_identity_key(),
server_mode(get_options()) ?
get_server_identity_key() : NULL,
@@ -517,7 +685,7 @@ init_keys(void)
const or_options_t *options = get_options();
dirinfo_type_t type;
time_t now = time(NULL);
- trusted_dir_server_t *ds;
+ dir_server_t *ds;
int v3_digest_set = 0;
authority_cert_t *cert = NULL;
@@ -630,12 +798,35 @@ init_keys(void)
keydir = get_datadir_fname2("keys", "secret_onion_key.old");
if (!lastonionkey && file_status(keydir) == FN_FILE) {
- prkey = init_key_from_file(keydir, 1, LOG_ERR);
+ prkey = init_key_from_file(keydir, 1, LOG_ERR); /* XXXX Why 1? */
if (prkey)
lastonionkey = prkey;
}
tor_free(keydir);
+#ifdef CURVE25519_ENABLED
+ {
+ /* 2b. Load curve25519 onion keys. */
+ int r;
+ keydir = get_datadir_fname2("keys", "secret_onion_key_ntor");
+ r = init_curve25519_keypair_from_file(&curve25519_onion_key,
+ keydir, 1, LOG_ERR, "onion");
+ tor_free(keydir);
+ if (r<0)
+ return -1;
+
+ keydir = get_datadir_fname2("keys", "secret_onion_key_ntor.old");
+ if (tor_mem_is_zero((const char *)
+ last_curve25519_onion_key.pubkey.public_key,
+ CURVE25519_PUBKEY_LEN) &&
+ file_status(keydir) == FN_FILE) {
+ init_curve25519_keypair_from_file(&last_curve25519_onion_key,
+ keydir, 0, LOG_ERR, "onion");
+ }
+ tor_free(keydir);
+ }
+#endif
+
/* 3. Initialize link key and TLS context. */
if (router_initialize_tls_context() < 0) {
log_err(LD_GENERAL,"Error initializing TLS context");
@@ -711,7 +902,7 @@ init_keys(void)
tor_free(cp);
tor_free(keydir);
- log(LOG_NOTICE, LD_GENERAL,
+ log_notice(LD_GENERAL,
"Your Tor server's identity key fingerprint is '%s %s'",
options->Nickname, fingerprint);
if (!authdir_mode(options))
@@ -732,17 +923,18 @@ init_keys(void)
ds = router_get_trusteddirserver_by_digest(digest);
if (!ds) {
- ds = add_trusted_dir_server(options->Nickname, NULL,
+ ds = trusted_dir_server_new(options->Nickname, NULL,
router_get_advertised_dir_port(options, 0),
router_get_advertised_or_port(options),
digest,
v3_digest,
- type);
+ type, 0.0);
if (!ds) {
log_err(LD_GENERAL,"We want to be a directory authority, but we "
"couldn't add ourselves to the authority list. Failing.");
return -1;
}
+ dir_server_add(ds);
}
if (ds->type != type) {
log_warn(LD_DIR, "Configured authority type does not match authority "
@@ -870,10 +1062,10 @@ decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port)
if (advertising != new_choice) {
if (new_choice == 1) {
- log(LOG_NOTICE, LD_DIR, "Advertising DirPort as %d", dir_port);
+ log_notice(LD_DIR, "Advertising DirPort as %d", dir_port);
} else {
tor_assert(reason);
- log(LOG_NOTICE, LD_DIR, "Not advertising DirPort (Reason: %s)", reason);
+ log_notice(LD_DIR, "Not advertising DirPort (Reason: %s)", reason);
}
advertising = new_choice;
}
@@ -893,7 +1085,8 @@ extend_info_from_router(const routerinfo_t *r)
router_get_prim_orport(r, &ap);
return extend_info_new(r->nickname, r->cache_info.identity_digest,
- r->onion_pkey, &ap.addr, ap.port);
+ r->onion_pkey, r->onion_curve25519_pkey,
+ &ap.addr, ap.port);
}
/** Some time has passed, or we just got new directory information.
@@ -924,14 +1117,11 @@ consider_testing_reachability(int test_or, int test_dir)
if (test_or || test_dir) {
#define SELF_EXCLUDED_WARN_INTERVAL 3600
static ratelim_t warning_limit=RATELIM_INIT(SELF_EXCLUDED_WARN_INTERVAL);
- char *msg;
- if ((msg = rate_limit_log(&warning_limit, approx_time()))) {
- log_warn(LD_CIRC, "Can't peform self-tests for this relay: we have "
+ log_fn_ratelim(&warning_limit, LOG_WARN, LD_CIRC,
+ "Can't peform self-tests for this relay: we have "
"listed ourself in ExcludeNodes, and StrictNodes is set. "
"We cannot learn whether we are usable, and will not "
- "be able to advertise ourself.%s", msg);
- tor_free(msg);
- }
+ "be able to advertise ourself.");
}
return;
}
@@ -1371,22 +1561,34 @@ router_upload_dir_desc_to_dirservers(int force)
* conn. Return 0 if we accept; non-0 if we reject.
*/
int
-router_compare_to_my_exit_policy(edge_connection_t *conn)
+router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port)
{
if (!router_get_my_routerinfo()) /* make sure desc_routerinfo exists */
return -1;
/* make sure it's resolved to something. this way we can't get a
'maybe' below. */
- if (tor_addr_is_null(&conn->base_.addr))
+ if (tor_addr_is_null(addr))
return -1;
- /* XXXX IPv6 */
- if (tor_addr_family(&conn->base_.addr) != AF_INET)
+ /* look at desc_routerinfo->exit_policy for both the v4 and the v6
+ * policies. The exit_policy field in desc_routerinfo is a bit unusual,
+ * in that it contains IPv6 and IPv6 entries. We don't want to look
+ * at desc_routerinfio->ipv6_exit_policy, since that's a port summary. */
+ if ((tor_addr_family(addr) == AF_INET ||
+ tor_addr_family(addr) == AF_INET6)) {
+ return compare_tor_addr_to_addr_policy(addr, port,
+ desc_routerinfo->exit_policy) != ADDR_POLICY_ACCEPTED;
+#if 0
+ } else if (tor_addr_family(addr) == AF_INET6) {
+ return get_options()->IPv6Exit &&
+ desc_routerinfo->ipv6_exit_policy &&
+ compare_tor_addr_to_short_policy(addr, port,
+ desc_routerinfo->ipv6_exit_policy) != ADDR_POLICY_ACCEPTED;
+#endif
+ } else {
return -1;
-
- return compare_tor_addr_to_addr_policy(&conn->base_.addr, conn->base_.port,
- desc_routerinfo->exit_policy) != ADDR_POLICY_ACCEPTED;
+ }
}
/** Return true iff my exit policy is reject *:*. Return -1 if we don't
@@ -1409,6 +1611,13 @@ router_digest_is_me(const char *digest)
tor_memeq(server_identitykey_digest, digest, DIGEST_LEN));
}
+/** Return my identity digest. */
+const uint8_t *
+router_get_my_id_digest(void)
+{
+ return (const uint8_t *)server_identitykey_digest;
+}
+
/** Return true iff I'm a server and <b>digest</b> is equal to
* my identity digest. */
int
@@ -1504,7 +1713,9 @@ static int router_guess_address_from_dir_headers(uint32_t *guess);
int
router_pick_published_address(const or_options_t *options, uint32_t *addr)
{
- if (resolve_my_address(LOG_INFO, options, addr, NULL) < 0) {
+ *addr = get_last_resolved_addr();
+ if (!*addr &&
+ resolve_my_address(LOG_INFO, options, addr, NULL, NULL) < 0) {
log_info(LD_CONFIG, "Could not determine our address locally. "
"Checking if directory headers provide any hints.");
if (router_guess_address_from_dir_headers(addr) < 0) {
@@ -1555,6 +1766,11 @@ 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 */
+#ifdef CURVE25519_ENABLED
+ ri->onion_curve25519_pkey =
+ tor_memdup(&get_current_curve25519_keypair()->pubkey,
+ sizeof(curve25519_public_key_t));
+#endif
/* For now, at most one IPv6 or-address is being advertised. */
{
@@ -1562,7 +1778,7 @@ router_rebuild_descriptor(int force)
SMARTLIST_FOREACH_BEGIN(get_configured_ports(), const port_cfg_t *, p) {
if (p->type == CONN_TYPE_OR_LISTENER &&
! p->no_advertise &&
- ! p->ipv4_only &&
+ ! p->bind_ipv4_only &&
tor_addr_family(&p->addr) == AF_INET6) {
if (! tor_addr_is_internal(&p->addr, 0)) {
ipv6_orport = p;
@@ -1605,11 +1821,20 @@ router_rebuild_descriptor(int force)
policies_exit_policy_append_reject_star(&ri->exit_policy);
} else {
policies_parse_exit_policy(options->ExitPolicy, &ri->exit_policy,
+ options->IPv6Exit,
options->ExitPolicyRejectPrivate,
ri->address, !options->BridgeRelay);
}
ri->policy_is_reject_star =
- policy_is_reject_star(ri->exit_policy);
+ policy_is_reject_star(ri->exit_policy, AF_INET) &&
+ policy_is_reject_star(ri->exit_policy, AF_INET6);
+
+ if (options->IPv6Exit) {
+ char *p_tmp = policy_summarize(ri->exit_policy, AF_INET6);
+ if (p_tmp)
+ ri->ipv6_exit_policy = parse_short_policy(p_tmp);
+ tor_free(p_tmp);
+ }
#if 0
/* XXXX NM NM I belive this is safe to remove */
@@ -1633,7 +1858,7 @@ router_rebuild_descriptor(int force)
member = node_get_by_nickname(name, 1);
if (!member) {
int is_legal = is_legal_nickname_or_hexdigest(name);
- if (!smartlist_string_isin(warned_nonexistent_family, name) &&
+ if (!smartlist_contains_string(warned_nonexistent_family, name) &&
!is_legal_hexdigest(name)) {
if (is_legal)
log_warn(LD_CONFIG,
@@ -1659,7 +1884,7 @@ router_rebuild_descriptor(int force)
base16_encode(fp+1,HEX_DIGEST_LEN+1,
member->identity, DIGEST_LEN);
smartlist_add(ri->declared_family, fp);
- if (smartlist_string_isin(warned_nonexistent_family, name))
+ if (smartlist_contains_string(warned_nonexistent_family, name))
smartlist_string_remove(warned_nonexistent_family, name);
}
skip:
@@ -1720,9 +1945,13 @@ router_rebuild_descriptor(int force)
anyway, since they don't have a DirPort, and always connect to the
bridge authority anonymously. But just in case they somehow think of
sending them on an unencrypted connection, don't allow them to try. */
- ri->cache_info.send_unencrypted = ei->cache_info.send_unencrypted = 0;
+ ri->cache_info.send_unencrypted = 0;
+ if (ei)
+ ei->cache_info.send_unencrypted = 0;
} else {
- ri->cache_info.send_unencrypted = ei->cache_info.send_unencrypted = 1;
+ ri->cache_info.send_unencrypted = 1;
+ if (ei)
+ ei->cache_info.send_unencrypted = 1;
}
router_get_router_hash(ri->cache_info.signed_descriptor_body,
@@ -1867,6 +2096,9 @@ check_descriptor_ipaddress_changed(time_t now)
{
uint32_t prev, cur;
const or_options_t *options = get_options();
+ const char *method = NULL;
+ char *hostname = NULL;
+
(void) now;
if (!desc_routerinfo)
@@ -1874,18 +2106,29 @@ check_descriptor_ipaddress_changed(time_t now)
/* XXXX ipv6 */
prev = desc_routerinfo->addr;
- if (resolve_my_address(LOG_INFO, options, &cur, NULL) < 0) {
+ if (resolve_my_address(LOG_INFO, options, &cur, &method, &hostname) < 0) {
log_info(LD_CONFIG,"options->Address didn't resolve into an IP.");
return;
}
if (prev != cur) {
+ char *source;
tor_addr_t tmp_prev, tmp_cur;
+
tor_addr_from_ipv4h(&tmp_prev, prev);
tor_addr_from_ipv4h(&tmp_cur, cur);
- log_addr_has_changed(LOG_NOTICE, &tmp_prev, &tmp_cur, "resolve");
+
+ tor_asprintf(&source, "METHOD=%s%s%s", method,
+ hostname ? " HOSTNAME=" : "",
+ hostname ? hostname : "");
+
+ log_addr_has_changed(LOG_NOTICE, &tmp_prev, &tmp_cur, source);
+ tor_free(source);
+
ip_address_changed(0);
}
+
+ tor_free(hostname);
}
/** The most recently guessed value of our IP address, based on directory
@@ -1919,7 +2162,9 @@ router_new_address_suggestion(const char *suggestion,
}
/* XXXX ipv6 */
- if (resolve_my_address(LOG_INFO, options, &cur, NULL) >= 0) {
+ cur = get_last_resolved_addr();
+ if (cur ||
+ resolve_my_address(LOG_INFO, options, &cur, NULL, NULL) >= 0) {
/* We're all set -- we already know our address. Great. */
tor_addr_from_ipv4h(&last_guessed_ip, cur); /* store it in case we
need it later */
@@ -2002,7 +2247,6 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
size_t onion_pkeylen, identity_pkeylen;
size_t written;
int result=0;
- addr_policy_t *tmpe;
char *family_line;
char *extra_or_address = NULL;
const or_options_t *options = get_options();
@@ -2127,15 +2371,32 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
written += result;
}
+#ifdef CURVE25519_ENABLED
+ if (router->onion_curve25519_pkey) {
+ char kbuf[128];
+ base64_encode(kbuf, sizeof(kbuf),
+ (const char *)router->onion_curve25519_pkey->public_key,
+ CURVE25519_PUBKEY_LEN);
+ result = tor_snprintf(s+written,maxlen-written, "ntor-onion-key %s",
+ kbuf);
+ if (result<0) {
+ log_warn(LD_BUG,"descriptor snprintf ran out of room!");
+ return -1;
+ }
+ written += result;
+ }
+#endif
+
/* Write the exit policy to the end of 's'. */
if (!router->exit_policy || !smartlist_len(router->exit_policy)) {
strlcat(s+written, "reject *:*\n", maxlen-written);
written += strlen("reject *:*\n");
- tmpe = NULL;
} else if (router->exit_policy) {
int i;
for (i = 0; i < smartlist_len(router->exit_policy); ++i) {
- tmpe = smartlist_get(router->exit_policy, i);
+ addr_policy_t *tmpe = smartlist_get(router->exit_policy, i);
+ if (tor_addr_family(&tmpe->addr) == AF_INET6)
+ continue; /* Don't include IPv6 parts of address policy */
result = policy_write_item(s+written, maxlen-written, tmpe, 1);
if (result < 0) {
log_warn(LD_BUG,"descriptor policy_write_item ran out of room!");
@@ -2151,6 +2412,21 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
}
}
+ if (router->ipv6_exit_policy) {
+ char *p6 = write_short_policy(router->ipv6_exit_policy);
+ if (p6 && strcmp(p6, "reject 1-65535")) {
+ result = tor_snprintf(s+written, maxlen-written,
+ "ipv6-policy %s\n", p6);
+ if (result<0) {
+ log_warn(LD_BUG,"Descriptor printf of policy ran out of room");
+ tor_free(p6);
+ return -1;
+ }
+ written += result;
+ }
+ tor_free(p6);
+ }
+
if (written + DIROBJ_MAX_SIG_LEN > maxlen) {
/* Not enough room for signature. */
log_warn(LD_BUG,"not enough room left in descriptor for signature!");
@@ -2760,6 +3036,11 @@ router_free_all(void)
crypto_pk_free(legacy_signing_key);
authority_cert_free(legacy_key_certificate);
+#ifdef CURVE25519_ENABLED
+ memwipe(&curve25519_onion_key, 0, sizeof(curve25519_onion_key));
+ memwipe(&last_curve25519_onion_key, 0, sizeof(last_curve25519_onion_key));
+#endif
+
if (warned_nonexistent_family) {
SMARTLIST_FOREACH(warned_nonexistent_family, char *, cp, tor_free(cp));
smartlist_free(warned_nonexistent_family);
diff --git a/src/or/router.h b/src/or/router.h
index 7ab057706d..fd2076af01 100644
--- a/src/or/router.h
+++ b/src/or/router.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -30,6 +30,11 @@ crypto_pk_t *init_key_from_file(const char *fname, int generate,
int severity);
void v3_authority_check_key_expiry(void);
+#ifdef CURVE25519_ENABLED
+di_digest256_map_t *construct_ntor_key_map(void);
+void ntor_key_map_free(di_digest256_map_t *map);
+#endif
+
int router_initialize_tls_context(void);
int init_keys(void);
@@ -72,13 +77,14 @@ void check_descriptor_bandwidth_changed(time_t now);
void check_descriptor_ipaddress_changed(time_t now);
void router_new_address_suggestion(const char *suggestion,
const dir_connection_t *d_conn);
-int router_compare_to_my_exit_policy(edge_connection_t *conn);
+int router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port);
int router_my_exit_policy_is_reject_star(void);
const routerinfo_t *router_get_my_routerinfo(void);
extrainfo_t *router_get_my_extrainfo(void);
const char *router_get_my_descriptor(void);
const char *router_get_descriptor_gen_reason(void);
int router_digest_is_me(const char *digest);
+const uint8_t *router_get_my_id_digest(void);
int router_extrainfo_digest_is_me(const char *digest);
int router_is_me(const routerinfo_t *router);
int router_fingerprint_is_me(const char *fp);
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index 1cefef9891..2f08167f18 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -42,14 +42,21 @@
/****************************************************************************/
/* static function prototypes */
+static int compute_weighted_bandwidths(const smartlist_t *sl,
+ bandwidth_weight_rule_t rule,
+ u64_dbl_t **bandwidths_out);
static const routerstatus_t *router_pick_directory_server_impl(
dirinfo_type_t auth, int flags);
static const routerstatus_t *router_pick_trusteddirserver_impl(
- dirinfo_type_t auth, int flags, int *n_busy_out);
-static void mark_all_trusteddirservers_up(void);
+ const smartlist_t *sourcelist, dirinfo_type_t auth,
+ int flags, int *n_busy_out);
+static const routerstatus_t *router_pick_dirserver_generic(
+ smartlist_t *sourcelist,
+ dirinfo_type_t type, int flags);
+static void mark_all_dirservers_up(smartlist_t *server_list);
static int router_nickname_matches(const routerinfo_t *router,
const char *nickname);
-static void trusted_dir_server_free(trusted_dir_server_t *ds);
+static void dir_server_free(dir_server_t *ds);
static int signed_desc_digest_is_recognized(signed_descriptor_t *desc);
static const char *signed_descriptor_get_body_impl(
const signed_descriptor_t *desc,
@@ -72,9 +79,12 @@ DECLARE_TYPED_DIGESTMAP_FNS(eimap_, digest_ei_map_t, extrainfo_t)
/****************************************************************************/
-/** Global list of a trusted_dir_server_t object for each trusted directory
- * server. */
+/** Global list of a dir_server_t object for each directory
+ * authority. */
static smartlist_t *trusted_dir_servers = NULL;
+/** Global list of dir_server_t objects for all directory authorities
+ * and all fallback directory servers. */
+static smartlist_t *fallback_dir_servers = NULL;
/** List of for a given authority, and download status for latest certificate.
*/
@@ -119,7 +129,7 @@ get_n_authorities(dirinfo_type_t type)
int n = 0;
if (!trusted_dir_servers)
return 0;
- SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, ds,
+ SMARTLIST_FOREACH(trusted_dir_servers, dir_server_t *, ds,
if (ds->type & type)
++n);
return n;
@@ -190,7 +200,7 @@ int
trusted_dirs_load_certs_from_string(const char *contents, int from_store,
int flush)
{
- trusted_dir_server_t *ds;
+ dir_server_t *ds;
const char *s, *eos;
int failure_code = 0;
@@ -530,11 +540,11 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
} SMARTLIST_FOREACH_END(sig);
} SMARTLIST_FOREACH_END(voter);
}
- SMARTLIST_FOREACH_BEGIN(trusted_dir_servers, trusted_dir_server_t *, ds) {
+ SMARTLIST_FOREACH_BEGIN(trusted_dir_servers, dir_server_t *, ds) {
int found = 0;
if (!(ds->type & V3_DIRINFO))
continue;
- if (smartlist_digest_isin(missing_digests, ds->v3_identity_digest))
+ if (smartlist_contains_digest(missing_digests, ds->v3_identity_digest))
continue;
cl = get_cert_list(ds->v3_identity_digest);
SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert, {
@@ -914,11 +924,11 @@ router_reload_router_list(void)
return 0;
}
-/** Return a smartlist containing a list of trusted_dir_server_t * for all
+/** Return a smartlist containing a list of dir_server_t * for all
* known trusted dirservers. Callers must not modify the list or its
* contents.
*/
-smartlist_t *
+const smartlist_t *
router_get_trusted_dir_servers(void)
{
if (!trusted_dir_servers)
@@ -927,6 +937,15 @@ router_get_trusted_dir_servers(void)
return trusted_dir_servers;
}
+const smartlist_t *
+router_get_fallback_dir_servers(void)
+{
+ if (!fallback_dir_servers)
+ fallback_dir_servers = smartlist_new();
+
+ return fallback_dir_servers;
+}
+
/** Try to find a running dirserver that supports operations of <b>type</b>.
*
* If there are no running dirservers in our routerlist and the
@@ -960,7 +979,7 @@ router_pick_directory_server(dirinfo_type_t type, int flags)
"No reachable router entries for dirservers. "
"Trying them all again.");
/* mark all authdirservers as up again */
- mark_all_trusteddirservers_up();
+ mark_all_dirservers_up(fallback_dir_servers);
/* try again */
choice = router_pick_directory_server_impl(type, flags);
return choice;
@@ -1007,16 +1026,34 @@ router_get_my_share_of_directory_requests(double *v2_share_out,
return 0;
}
-/** Return the trusted_dir_server_t for the directory authority whose identity
+/** Return the dir_server_t for the directory authority whose identity
* key hashes to <b>digest</b>, or NULL if no such authority is known.
*/
-trusted_dir_server_t *
+dir_server_t *
router_get_trusteddirserver_by_digest(const char *digest)
{
if (!trusted_dir_servers)
return NULL;
- SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, ds,
+ SMARTLIST_FOREACH(trusted_dir_servers, dir_server_t *, ds,
+ {
+ if (tor_memeq(ds->digest, digest, DIGEST_LEN))
+ return ds;
+ });
+
+ return NULL;
+}
+
+/** Return the dir_server_t for the fallback dirserver whose identity
+ * key hashes to <b>digest</b>, or NULL if no such authority is known.
+ */
+dir_server_t *
+router_get_fallback_dirserver_by_digest(const char *digest)
+{
+ if (!trusted_dir_servers)
+ return NULL;
+
+ SMARTLIST_FOREACH(trusted_dir_servers, dir_server_t *, ds,
{
if (tor_memeq(ds->digest, digest, DIGEST_LEN))
return ds;
@@ -1025,17 +1062,17 @@ router_get_trusteddirserver_by_digest(const char *digest)
return NULL;
}
-/** Return the trusted_dir_server_t for the directory authority whose
+/** Return the dir_server_t for the directory authority whose
* v3 identity key hashes to <b>digest</b>, or NULL if no such authority
* is known.
*/
-trusted_dir_server_t *
+dir_server_t *
trusteddirserver_get_by_v3_auth_digest(const char *digest)
{
if (!trusted_dir_servers)
return NULL;
- SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, ds,
+ SMARTLIST_FOREACH(trusted_dir_servers, dir_server_t *, ds,
{
if (tor_memeq(ds->v3_identity_digest, digest, DIGEST_LEN) &&
(ds->type & V3_DIRINFO))
@@ -1045,18 +1082,37 @@ trusteddirserver_get_by_v3_auth_digest(const char *digest)
return NULL;
}
-/** Try to find a running trusted dirserver. Flags are as for
+/** Try to find a running directory authority. Flags are as for
* router_pick_directory_server.
*/
const routerstatus_t *
router_pick_trusteddirserver(dirinfo_type_t type, int flags)
{
+ return router_pick_dirserver_generic(trusted_dir_servers, type, flags);
+}
+
+/** Try to find a running fallback directory Flags are as for
+ * router_pick_directory_server.
+ */
+const routerstatus_t *
+router_pick_fallback_dirserver(dirinfo_type_t type, int flags)
+{
+ return router_pick_dirserver_generic(fallback_dir_servers, type, flags);
+}
+
+/** Try to find a running fallback directory Flags are as for
+ * router_pick_directory_server.
+ */
+static const routerstatus_t *
+router_pick_dirserver_generic(smartlist_t *sourcelist,
+ dirinfo_type_t type, int flags)
+{
const routerstatus_t *choice;
int busy = 0;
if (get_options()->PreferTunneledDirConns)
flags |= PDS_PREFER_TUNNELED_DIR_CONNS_;
- choice = router_pick_trusteddirserver_impl(type, flags, &busy);
+ choice = router_pick_trusteddirserver_impl(sourcelist, type, flags, &busy);
if (choice || !(flags & PDS_RETRY_IF_NO_SERVERS))
return choice;
if (busy) {
@@ -1069,9 +1125,9 @@ router_pick_trusteddirserver(dirinfo_type_t type, int flags)
}
log_info(LD_DIR,
- "No trusted dirservers are reachable. Trying them all again.");
- mark_all_trusteddirservers_up();
- return router_pick_trusteddirserver_impl(type, flags, NULL);
+ "No dirservers are reachable. Trying them all again.");
+ mark_all_dirservers_up(sourcelist);
+ return router_pick_trusteddirserver_impl(sourcelist, type, flags, NULL);
}
/** How long do we avoid using a directory server after it's given us a 503? */
@@ -1196,28 +1252,56 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags)
return result ? result->rs : NULL;
}
-/** Choose randomly from among the trusted dirservers that are up. Flags
- * are as for router_pick_directory_server_impl().
+/** Pick a random element from a list of dir_server_t, weighting by their
+ * <b>weight</b> field. */
+static const dir_server_t *
+dirserver_choose_by_weight(const smartlist_t *servers, double authority_weight)
+{
+ int n = smartlist_len(servers);
+ int i;
+ u64_dbl_t *weights;
+ const dir_server_t *ds;
+
+ weights = tor_malloc(sizeof(u64_dbl_t) * n);
+ for (i = 0; i < n; ++i) {
+ ds = smartlist_get(servers, i);
+ weights[i].dbl = ds->weight;
+ if (ds->is_authority)
+ weights[i].dbl *= authority_weight;
+ }
+
+ scale_array_elements_to_u64(weights, n, NULL);
+ i = choose_array_element_by_weight(weights, n);
+ tor_free(weights);
+ return (i < 0) ? NULL : smartlist_get(servers, i);
+}
+
+/** Choose randomly from among the dir_server_ts in sourcelist that
+ * are up. Flags are as for router_pick_directory_server_impl().
*/
static const routerstatus_t *
-router_pick_trusteddirserver_impl(dirinfo_type_t type, int flags,
+router_pick_trusteddirserver_impl(const smartlist_t *sourcelist,
+ dirinfo_type_t type, int flags,
int *n_busy_out)
{
const or_options_t *options = get_options();
smartlist_t *direct, *tunnel;
smartlist_t *overloaded_direct, *overloaded_tunnel;
const routerinfo_t *me = router_get_my_routerinfo();
- const routerstatus_t *result;
+ const routerstatus_t *result = NULL;
time_t now = time(NULL);
const int requireother = ! (flags & PDS_ALLOW_SELF);
const int fascistfirewall = ! (flags & PDS_IGNORE_FASCISTFIREWALL);
const int prefer_tunnel = (flags & PDS_PREFER_TUNNELED_DIR_CONNS_);
const int no_serverdesc_fetching =(flags & PDS_NO_EXISTING_SERVERDESC_FETCH);
const int no_microdesc_fetching =(flags & PDS_NO_EXISTING_MICRODESC_FETCH);
+ const double auth_weight = (sourcelist == fallback_dir_servers) ?
+ options->DirAuthorityFallbackRate : 1.0;
+ smartlist_t *pick_from;
int n_busy = 0;
int try_excluding = 1, n_excluded = 0;
- if (!trusted_dir_servers)
+ if (!sourcelist)
return NULL;
retry_without_exclude:
@@ -1227,7 +1311,7 @@ router_pick_trusteddirserver_impl(dirinfo_type_t type, int flags,
overloaded_direct = smartlist_new();
overloaded_tunnel = smartlist_new();
- SMARTLIST_FOREACH_BEGIN(trusted_dir_servers, trusted_dir_server_t *, d)
+ SMARTLIST_FOREACH_BEGIN(sourcelist, const dir_server_t *, d)
{
int is_overloaded =
d->fake_status.last_dir_503_at + DIR_503_TIMEOUT > now;
@@ -1273,23 +1357,29 @@ router_pick_trusteddirserver_impl(dirinfo_type_t type, int flags,
d->or_port &&
(!fascistfirewall ||
fascist_firewall_allows_address_or(&addr, d->or_port)))
- smartlist_add(is_overloaded ? overloaded_tunnel : tunnel,
- &d->fake_status);
+ smartlist_add(is_overloaded ? overloaded_tunnel : tunnel, (void*)d);
else if (!fascistfirewall ||
fascist_firewall_allows_address_dir(&addr, d->dir_port))
- smartlist_add(is_overloaded ? overloaded_direct : direct,
- &d->fake_status);
+ smartlist_add(is_overloaded ? overloaded_direct : direct, (void*)d);
}
SMARTLIST_FOREACH_END(d);
if (smartlist_len(tunnel)) {
- result = smartlist_choose(tunnel);
+ pick_from = tunnel;
} else if (smartlist_len(overloaded_tunnel)) {
- result = smartlist_choose(overloaded_tunnel);
+ pick_from = overloaded_tunnel;
} else if (smartlist_len(direct)) {
- result = smartlist_choose(direct);
+ pick_from = direct;
} else {
- result = smartlist_choose(overloaded_direct);
+ pick_from = overloaded_direct;
+ }
+
+ {
+ const dir_server_t *selection =
+ dirserver_choose_by_weight(pick_from, auth_weight);
+
+ if (selection)
+ result = &selection->fake_status;
}
if (n_busy_out)
@@ -1311,19 +1401,19 @@ router_pick_trusteddirserver_impl(dirinfo_type_t type, int flags,
return result;
}
-/** Go through and mark the authoritative dirservers as up. */
+/** Mark as running every dir_server_t in <b>server_list</b>. */
static void
-mark_all_trusteddirservers_up(void)
+mark_all_dirservers_up(smartlist_t *server_list)
{
- SMARTLIST_FOREACH(nodelist_get_list(), node_t *, node, {
- if (router_digest_is_trusted_dir(node->identity))
- node->is_running = 1;
- });
- if (trusted_dir_servers) {
- SMARTLIST_FOREACH_BEGIN(trusted_dir_servers, trusted_dir_server_t *, dir) {
+ if (server_list) {
+ SMARTLIST_FOREACH_BEGIN(server_list, dir_server_t *, dir) {
routerstatus_t *rs;
+ node_t *node;
dir->is_running = 1;
download_status_reset(&dir->v2_ns_dl_status);
+ node = node_get_mutable_by_id(dir->digest);
+ if (node)
+ node->is_running = 1;
rs = router_get_mutable_consensus_status_by_id(dir->digest);
if (rs) {
rs->last_dir_503_at = 0;
@@ -1348,7 +1438,7 @@ routers_have_same_or_addrs(const routerinfo_t *r1, const routerinfo_t *r2)
void
router_reset_status_download_failures(void)
{
- mark_all_trusteddirservers_up();
+ mark_all_dirservers_up(fallback_dir_servers);
}
/** Given a <b>router</b>, add every node_t in its family (including the
@@ -1594,9 +1684,35 @@ kb_to_bytes(uint32_t bw)
* guards proportionally less.
*/
static const node_t *
-smartlist_choose_node_by_bandwidth_weights(smartlist_t *sl,
+smartlist_choose_node_by_bandwidth_weights(const smartlist_t *sl,
bandwidth_weight_rule_t rule)
{
+ u64_dbl_t *bandwidths=NULL;
+
+ if (compute_weighted_bandwidths(sl, rule, &bandwidths) < 0)
+ return NULL;
+
+ scale_array_elements_to_u64(bandwidths, smartlist_len(sl),
+ &sl_last_total_weighted_bw);
+
+ {
+ int idx = choose_array_element_by_weight(bandwidths,
+ smartlist_len(sl));
+ tor_free(bandwidths);
+ return idx < 0 ? NULL : smartlist_get(sl, idx);
+ }
+}
+
+/** Given a list of routers and a weighting rule as in
+ * smartlist_choose_node_by_bandwidth_weights, compute weighted bandwidth
+ * values for each node and store them in a freshly allocated
+ * *<b>bandwidths_out</b> of the same length as <b>sl</b>, and holding results
+ * as doubles. Return 0 on success, -1 on failure. */
+static int
+compute_weighted_bandwidths(const smartlist_t *sl,
+ bandwidth_weight_rule_t rule,
+ u64_dbl_t **bandwidths_out)
+{
int64_t weight_scale;
double Wg = -1, Wm = -1, We = -1, Wd = -1;
double Wgb = -1, Wmb = -1, Web = -1, Wdb = -1;
@@ -1615,10 +1731,10 @@ smartlist_choose_node_by_bandwidth_weights(smartlist_t *sl,
"Empty routerlist passed in to consensus weight node "
"selection for rule %s",
bandwidth_weight_rule_to_string(rule));
- return NULL;
+ return -1;
}
- weight_scale = circuit_build_times_get_bw_scale(NULL);
+ weight_scale = networkstatus_get_weight_scale_param(NULL);
if (rule == WEIGHT_FOR_GUARD) {
Wg = networkstatus_get_bw_weight(NULL, "Wgg", -1);
@@ -1669,7 +1785,7 @@ smartlist_choose_node_by_bandwidth_weights(smartlist_t *sl,
log_debug(LD_CIRC,
"Got negative bandwidth weights. Defaulting to old selection"
" algorithm.");
- return NULL; // Use old algorithm.
+ return -1; // Use old algorithm.
}
Wg /= weight_scale;
@@ -1699,7 +1815,7 @@ smartlist_choose_node_by_bandwidth_weights(smartlist_t *sl,
log_warn(LD_BUG,
"Consensus is not listing bandwidths. Defaulting back to "
"old router selection algorithm.");
- return NULL;
+ return -1;
}
this_bw = kb_to_bytes(node->rs->bandwidth);
} else if (node->ri) {
@@ -1732,20 +1848,53 @@ smartlist_choose_node_by_bandwidth_weights(smartlist_t *sl,
sl_last_weighted_bw_of_me = (uint64_t) bandwidths[node_sl_idx].dbl;
} SMARTLIST_FOREACH_END(node);
- log_debug(LD_CIRC, "Choosing node for rule %s based on weights "
+ log_debug(LD_CIRC, "Generated weighted bandwidths for rule %s based "
+ "on weights "
"Wg=%f Wm=%f We=%f Wd=%f with total bw "U64_FORMAT,
bandwidth_weight_rule_to_string(rule),
Wg, Wm, We, Wd, U64_PRINTF_ARG(weighted_bw));
- scale_array_elements_to_u64(bandwidths, smartlist_len(sl),
- &sl_last_total_weighted_bw);
+ *bandwidths_out = bandwidths;
- {
- int idx = choose_array_element_by_weight(bandwidths,
- smartlist_len(sl));
- tor_free(bandwidths);
- return idx < 0 ? NULL : smartlist_get(sl, idx);
+ return 0;
+}
+
+/** For all nodes in <b>sl</b>, return the fraction of those nodes, weighted
+ * by their weighted bandwidths with rule <b>rule</b>, for which we have
+ * descriptors. */
+double
+frac_nodes_with_descriptors(const smartlist_t *sl,
+ bandwidth_weight_rule_t rule)
+{
+ u64_dbl_t *bandwidths = NULL;
+ double total, present;
+
+ if (smartlist_len(sl) == 0)
+ return 0.0;
+
+ if (compute_weighted_bandwidths(sl, rule, &bandwidths) < 0) {
+ int n_with_descs = 0;
+ SMARTLIST_FOREACH(sl, const node_t *, node, {
+ if (node_has_descriptor(node))
+ n_with_descs++;
+ });
+ return ((double)n_with_descs) / (double)smartlist_len(sl);
}
+
+ total = present = 0.0;
+ SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) {
+ const double bw = bandwidths[node_sl_idx].dbl;
+ total += bw;
+ if (node_has_descriptor(node))
+ present += bw;
+ } SMARTLIST_FOREACH_END(node);
+
+ tor_free(bandwidths);
+
+ if (total < 1.0)
+ return 0;
+
+ return present / total;
}
/** Helper function:
@@ -1762,7 +1911,7 @@ smartlist_choose_node_by_bandwidth_weights(smartlist_t *sl,
* guards proportionally less.
*/
static const node_t *
-smartlist_choose_node_by_bandwidth(smartlist_t *sl,
+smartlist_choose_node_by_bandwidth(const smartlist_t *sl,
bandwidth_weight_rule_t rule)
{
unsigned int i;
@@ -1968,7 +2117,7 @@ smartlist_choose_node_by_bandwidth(smartlist_t *sl,
/** Choose a random element of status list <b>sl</b>, weighted by
* the advertised bandwidth of each node */
const node_t *
-node_sl_choose_by_bandwidth(smartlist_t *sl,
+node_sl_choose_by_bandwidth(const smartlist_t *sl,
bandwidth_weight_rule_t rule)
{ /*XXXX MOVE */
const node_t *ret;
@@ -2185,7 +2334,7 @@ router_digest_is_trusted_dir_type(const char *digest, dirinfo_type_t type)
return 0;
if (authdir_mode(get_options()) && router_digest_is_me(digest))
return 1;
- SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, ent,
+ SMARTLIST_FOREACH(trusted_dir_servers, dir_server_t *, ent,
if (tor_memeq(digest, ent->digest, DIGEST_LEN)) {
return (!type) || ((type & ent->type) != 0);
});
@@ -2199,7 +2348,7 @@ router_addr_is_trusted_dir(uint32_t addr)
{
if (!trusted_dir_servers)
return 0;
- SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, ent,
+ SMARTLIST_FOREACH(trusted_dir_servers, dir_server_t *, ent,
if (ent->addr == addr)
return 1;
);
@@ -2395,6 +2544,7 @@ routerinfo_free(routerinfo_t *router)
tor_free(router->contact_info);
if (router->onion_pkey)
crypto_pk_free(router->onion_pkey);
+ tor_free(router->onion_curve25519_pkey);
if (router->identity_pkey)
crypto_pk_free(router->identity_pkey);
if (router->declared_family) {
@@ -2402,6 +2552,7 @@ routerinfo_free(routerinfo_t *router)
smartlist_free(router->declared_family);
}
addr_policy_list_free(router->exit_policy);
+ short_policy_free(router->ipv6_exit_policy);
memset(router, 77, sizeof(routerinfo_t));
@@ -2496,7 +2647,7 @@ dump_routerlist_mem_usage(int severity)
SMARTLIST_FOREACH(routerlist->old_routers, signed_descriptor_t *, sd,
olddescs += sd->signed_descriptor_len);
- log(severity, LD_DIR,
+ tor_log(severity, LD_DIR,
"In %d live descriptors: "U64_FORMAT" bytes. "
"In %d old descriptors: "U64_FORMAT" bytes.",
smartlist_len(routerlist->routers), U64_PRINTF_ARG(livedescs),
@@ -2909,12 +3060,10 @@ routerlist_free_all(void)
smartlist_free(warned_nicknames);
warned_nicknames = NULL;
}
- if (trusted_dir_servers) {
- SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, ds,
- trusted_dir_server_free(ds));
- smartlist_free(trusted_dir_servers);
- trusted_dir_servers = NULL;
- }
+ clear_dir_servers();
+ smartlist_free(trusted_dir_servers);
+ smartlist_free(fallback_dir_servers);
+ trusted_dir_servers = fallback_dir_servers = NULL;
if (trusted_dir_certs) {
DIGESTMAP_FOREACH(trusted_dir_certs, key, cert_list_t *, cl) {
SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
@@ -3240,7 +3389,7 @@ routerlist_remove_old_cached_routers_with_id(time_t now,
signed_descriptor_t *r_next;
lifespans[i-lo].idx = i;
if (r->last_listed_as_valid_until >= now ||
- (retain && digestset_isin(retain, r->signed_descriptor_digest))) {
+ (retain && digestset_contains(retain, r->signed_descriptor_digest))) {
must_keep[i-lo] = 1;
}
if (i < hi) {
@@ -3374,7 +3523,7 @@ routerlist_remove_old_routers(void)
router = smartlist_get(routerlist->routers, i);
if (router->cache_info.published_on <= cutoff &&
router->cache_info.last_listed_as_valid_until < now &&
- !digestset_isin(retain,
+ !digestset_contains(retain,
router->cache_info.signed_descriptor_digest)) {
/* Too old: remove it. (If we're a cache, just move it into
* old_routers.) */
@@ -3395,7 +3544,7 @@ routerlist_remove_old_routers(void)
sd = smartlist_get(routerlist->old_routers, i);
if (sd->published_on <= cutoff &&
sd->last_listed_as_valid_until < now &&
- !digestset_isin(retain, sd->signed_descriptor_digest)) {
+ !digestset_contains(retain, sd->signed_descriptor_digest)) {
/* Too old. Remove it. */
routerlist_remove_old(routerlist, sd, i--);
}
@@ -3574,7 +3723,7 @@ router_load_routers_from_string(const char *s, const char *eos,
ri->cache_info.signed_descriptor_digest :
ri->cache_info.identity_digest,
DIGEST_LEN);
- if (smartlist_string_isin(requested_fingerprints, fp)) {
+ if (smartlist_contains_string(requested_fingerprints, fp)) {
smartlist_string_remove(requested_fingerprints, fp);
} else {
char *requested =
@@ -3721,47 +3870,47 @@ router_exit_policy_rejects_all(const routerinfo_t *router)
return router->policy_is_reject_star;
}
-/** Add to the list of authoritative directory servers one at
- * <b>address</b>:<b>port</b>, with identity key <b>digest</b>. If
- * <b>address</b> is NULL, add ourself. Return the new trusted directory
- * server entry on success or NULL if we couldn't add it. */
-trusted_dir_server_t *
-add_trusted_dir_server(const char *nickname, const char *address,
- uint16_t dir_port, uint16_t or_port,
- const char *digest, const char *v3_auth_digest,
- dirinfo_type_t type)
+/** Create an directory server at <b>address</b>:<b>port</b>, with OR identity
+ * key <b>digest</b>. If <b>address</b> is NULL, add ourself. If
+ * <b>is_authority</b>, this is a directory authority. Return the new
+ * directory server entry on success or NULL on failure. */
+static dir_server_t *
+dir_server_new(int is_authority,
+ const char *nickname,
+ const tor_addr_t *addr,
+ const char *hostname,
+ uint16_t dir_port, uint16_t or_port,
+ const char *digest, const char *v3_auth_digest,
+ dirinfo_type_t type,
+ double weight)
{
- trusted_dir_server_t *ent;
+ dir_server_t *ent;
uint32_t a;
- char *hostname = NULL;
- if (!trusted_dir_servers)
- trusted_dir_servers = smartlist_new();
+ char *hostname_ = NULL;
- if (!address) { /* The address is us; we should guess. */
- if (resolve_my_address(LOG_WARN, get_options(), &a, &hostname) < 0) {
- log_warn(LD_CONFIG,
- "Couldn't find a suitable address when adding ourself as a "
- "trusted directory server.");
- return NULL;
- }
- } else {
- if (tor_lookup_hostname(address, &a)) {
- log_warn(LD_CONFIG,
- "Unable to lookup address for directory server at '%s'",
- address);
- return NULL;
- }
- hostname = tor_strdup(address);
- }
+ if (weight < 0)
+ return NULL;
+
+ if (tor_addr_family(addr) == AF_INET)
+ a = tor_addr_to_ipv4h(addr);
+ else
+ return NULL; /*XXXX Support IPv6 */
+
+ if (!hostname)
+ hostname_ = tor_dup_addr(addr);
+ else
+ hostname_ = tor_strdup(hostname);
- ent = tor_malloc_zero(sizeof(trusted_dir_server_t));
+ ent = tor_malloc_zero(sizeof(dir_server_t));
ent->nickname = nickname ? tor_strdup(nickname) : NULL;
- ent->address = hostname;
+ ent->address = hostname_;
ent->addr = a;
ent->dir_port = dir_port;
ent->or_port = or_port;
ent->is_running = 1;
+ ent->is_authority = is_authority;
ent->type = type;
+ ent->weight = weight;
memcpy(ent->digest, digest, DIGEST_LEN);
if (v3_auth_digest && (type & V3_DIRINFO))
memcpy(ent->v3_identity_digest, v3_auth_digest, DIGEST_LEN);
@@ -3783,11 +3932,80 @@ add_trusted_dir_server(const char *nickname, const char *address,
ent->fake_status.dir_port = ent->dir_port;
ent->fake_status.or_port = ent->or_port;
- smartlist_add(trusted_dir_servers, ent);
- router_dir_info_changed();
return ent;
}
+/** Create an authoritative directory server at
+ * <b>address</b>:<b>port</b>, with identity key <b>digest</b>. If
+ * <b>address</b> is NULL, add ourself. Return the new trusted directory
+ * server entry on success or NULL if we couldn't add it. */
+dir_server_t *
+trusted_dir_server_new(const char *nickname, const char *address,
+ uint16_t dir_port, uint16_t or_port,
+ const char *digest, const char *v3_auth_digest,
+ dirinfo_type_t type, double weight)
+{
+ uint32_t a;
+ tor_addr_t addr;
+ char *hostname=NULL;
+ dir_server_t *result;
+
+ if (!address) { /* The address is us; we should guess. */
+ if (resolve_my_address(LOG_WARN, get_options(),
+ &a, NULL, &hostname) < 0) {
+ log_warn(LD_CONFIG,
+ "Couldn't find a suitable address when adding ourself as a "
+ "trusted directory server.");
+ return NULL;
+ }
+ if (!hostname)
+ hostname = tor_dup_ip(a);
+ } else {
+ if (tor_lookup_hostname(address, &a)) {
+ log_warn(LD_CONFIG,
+ "Unable to lookup address for directory server at '%s'",
+ address);
+ return NULL;
+ }
+ hostname = tor_strdup(address);
+ }
+ tor_addr_from_ipv4h(&addr, a);
+
+ result = dir_server_new(1, nickname, &addr, hostname,
+ dir_port, or_port, digest,
+ v3_auth_digest, type, weight);
+ tor_free(hostname);
+ return result;
+}
+
+/** Return a new dir_server_t for a fallback directory server at
+ * <b>addr</b>:<b>or_port</b>/<b>dir_port</b>, with identity key digest
+ * <b>id_digest</b> */
+dir_server_t *
+fallback_dir_server_new(const tor_addr_t *addr,
+ uint16_t dir_port, uint16_t or_port,
+ const char *id_digest, double weight)
+{
+ return dir_server_new(0, NULL, addr, NULL, dir_port, or_port, id_digest,
+ NULL, ALL_DIRINFO, weight);
+}
+
+/** Add a directory server to the global list(s). */
+void
+dir_server_add(dir_server_t *ent)
+{
+ if (!trusted_dir_servers)
+ trusted_dir_servers = smartlist_new();
+ if (!fallback_dir_servers)
+ fallback_dir_servers = smartlist_new();
+
+ if (ent->is_authority)
+ smartlist_add(trusted_dir_servers, ent);
+
+ smartlist_add(fallback_dir_servers, ent);
+ router_dir_info_changed();
+}
+
/** Free storage held in <b>cert</b>. */
void
authority_cert_free(authority_cert_t *cert)
@@ -3804,7 +4022,7 @@ authority_cert_free(authority_cert_t *cert)
/** Free storage held in <b>ds</b>. */
static void
-trusted_dir_server_free(trusted_dir_server_t *ds)
+dir_server_free(dir_server_t *ds)
{
if (!ds)
return;
@@ -3815,13 +4033,18 @@ trusted_dir_server_free(trusted_dir_server_t *ds)
tor_free(ds);
}
-/** Remove all members from the list of trusted dir servers. */
+/** Remove all members from the list of dir servers. */
void
-clear_trusted_dir_servers(void)
+clear_dir_servers(void)
{
+ if (fallback_dir_servers) {
+ SMARTLIST_FOREACH(fallback_dir_servers, dir_server_t *, ent,
+ dir_server_free(ent));
+ smartlist_clear(fallback_dir_servers);
+ } else {
+ fallback_dir_servers = smartlist_new();
+ }
if (trusted_dir_servers) {
- SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, ent,
- trusted_dir_server_free(ent));
smartlist_clear(trusted_dir_servers);
} else {
trusted_dir_servers = smartlist_new();
@@ -4127,7 +4350,7 @@ update_router_descriptor_cache_downloads_v2(time_t now)
*/
n_download = 0;
SMARTLIST_FOREACH_BEGIN(networkstatus_v2_list, networkstatus_v2_t *, ns) {
- trusted_dir_server_t *ds;
+ dir_server_t *ds;
smartlist_t *dl;
dl = downloadable[ns_sl_idx] = smartlist_new();
download_from[ns_sl_idx] = smartlist_new();
@@ -4202,7 +4425,7 @@ update_router_descriptor_cache_downloads_v2(time_t now)
/* Now, we can actually launch our requests. */
for (i=0; i<n; ++i) {
networkstatus_v2_t *ns = smartlist_get(networkstatus_v2_list, i);
- trusted_dir_server_t *ds =
+ dir_server_t *ds =
router_get_trusteddirserver_by_digest(ns->identity_digest);
smartlist_t *dl = download_from[i];
int pds_flags = PDS_RETRY_IF_NO_SERVERS;
@@ -4255,7 +4478,7 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote,
if (is_vote) {
/* where's it from, so we know whom to ask for descriptors */
- trusted_dir_server_t *ds;
+ dir_server_t *ds;
networkstatus_voter_info_t *voter = smartlist_get(consensus->voters, 0);
tor_assert(voter);
ds = trusteddirserver_get_by_v3_auth_digest(voter->identity_digest);
@@ -4279,7 +4502,7 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote,
sd->signed_descriptor_digest, DIGEST_LEN)) {
/* We have a descriptor with this digest, but either there is no
* entry in routerlist with the same ID (!ri), or there is one,
- * but the identity digest differs (memcmp).
+ * but the identity digest differs (memneq).
*/
smartlist_add(no_longer_old, sd);
++n_in_oldrouters; /* We have it in old_routers. */
diff --git a/src/or/routerlist.h b/src/or/routerlist.h
index c8381996d2..1849fff31c 100644
--- a/src/or/routerlist.h
+++ b/src/or/routerlist.h
@@ -1,6 +1,6 @@
/* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -25,14 +25,19 @@ void authority_cert_dl_failed(const char *id_digest, int status);
void authority_certs_fetch_missing(networkstatus_t *status, time_t now);
int router_reload_router_list(void);
int authority_cert_dl_looks_uncertain(const char *id_digest);
-smartlist_t *router_get_trusted_dir_servers(void);
+const smartlist_t *router_get_trusted_dir_servers(void);
+const smartlist_t *router_get_fallback_dir_servers(void);
const routerstatus_t *router_pick_directory_server(dirinfo_type_t type,
int flags);
-trusted_dir_server_t *router_get_trusteddirserver_by_digest(const char *d);
-trusted_dir_server_t *trusteddirserver_get_by_v3_auth_digest(const char *d);
+dir_server_t *router_get_trusteddirserver_by_digest(const char *d);
+dir_server_t *router_get_fallback_dirserver_by_digest(
+ const char *digest);
+dir_server_t *trusteddirserver_get_by_v3_auth_digest(const char *d);
const routerstatus_t *router_pick_trusteddirserver(dirinfo_type_t type,
int flags);
+const routerstatus_t *router_pick_fallback_dirserver(dirinfo_type_t type,
+ int flags);
int router_get_my_share_of_directory_requests(double *v2_share_out,
double *v3_share_out);
void router_reset_status_download_failures(void);
@@ -42,8 +47,10 @@ const routerinfo_t *routerlist_find_my_routerinfo(void);
uint32_t router_get_advertised_bandwidth(const routerinfo_t *router);
uint32_t router_get_advertised_bandwidth_capped(const routerinfo_t *router);
-const node_t *node_sl_choose_by_bandwidth(smartlist_t *sl,
+const node_t *node_sl_choose_by_bandwidth(const smartlist_t *sl,
bandwidth_weight_rule_t rule);
+double frac_nodes_with_descriptors(const smartlist_t *sl,
+ bandwidth_weight_rule_t rule);
const node_t *router_choose_random_node(smartlist_t *excludedsmartlist,
struct routerset_t *excludedset,
@@ -127,13 +134,18 @@ void router_load_extrainfo_from_string(const char *s, const char *eos,
void routerlist_retry_directory_downloads(time_t now);
int router_exit_policy_rejects_all(const routerinfo_t *router);
-trusted_dir_server_t *add_trusted_dir_server(const char *nickname,
- const char *address,
- uint16_t dir_port, uint16_t or_port,
- const char *digest, const char *v3_auth_digest,
- dirinfo_type_t type);
+
+dir_server_t *trusted_dir_server_new(const char *nickname, const char *address,
+ uint16_t dir_port, uint16_t or_port,
+ const char *digest, const char *v3_auth_digest,
+ dirinfo_type_t type, double weight);
+dir_server_t *fallback_dir_server_new(const tor_addr_t *addr,
+ uint16_t dir_port, uint16_t or_port,
+ const char *id_digest, double weight);
+void dir_server_add(dir_server_t *ent);
+
void authority_cert_free(authority_cert_t *cert);
-void clear_trusted_dir_servers(void);
+void clear_dir_servers(void);
int any_trusted_dir_is_v1_authority(void);
void update_consensus_router_descriptor_downloads(time_t now, int is_vote,
networkstatus_t *consensus);
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 8d6cd1c7fa..2a3de12c35 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -43,6 +43,7 @@ typedef enum {
K_SIGNED_DIRECTORY,
K_SIGNING_KEY,
K_ONION_KEY,
+ K_ONION_KEY_NTOR,
K_ROUTER_SIGNATURE,
K_PUBLISHED,
K_RUNNING_ROUTERS,
@@ -66,6 +67,7 @@ typedef enum {
K_SERVER_VERSIONS,
K_OR_ADDRESS,
K_P,
+ K_P6,
K_R,
K_A,
K_S,
@@ -77,6 +79,7 @@ typedef enum {
K_CACHES_EXTRA_INFO,
K_HIDDEN_SERVICE_DIR,
K_ALLOW_SINGLE_HOP_EXITS,
+ K_IPV6_POLICY,
K_DIRREQ_END,
K_DIRREQ_V2_IPS,
@@ -271,8 +274,10 @@ static token_rule_t routerdesc_token_table[] = {
T0N("reject6", K_REJECT6, ARGS, NO_OBJ ),
T0N("accept6", K_ACCEPT6, ARGS, NO_OBJ ),
T1_START( "router", K_ROUTER, GE(5), NO_OBJ ),
+ T01("ipv6-policy", K_IPV6_POLICY, CONCAT_ARGS, NO_OBJ),
T1( "signing-key", K_SIGNING_KEY, NO_ARGS, NEED_KEY_1024 ),
T1( "onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY_1024 ),
+ T01("ntor-onion-key", K_ONION_KEY_NTOR, GE(1), NO_OBJ ),
T1_END( "router-signature", K_ROUTER_SIGNATURE, NO_ARGS, NEED_OBJ ),
T1( "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ ),
T01("uptime", K_UPTIME, GE(1), NO_OBJ ),
@@ -373,25 +378,6 @@ static token_rule_t dir_footer_token_table[] = {
END_OF_TABLE
};
-/** List of tokens recognized in v1 directory headers/footers. */
-static token_rule_t dir_token_table[] = {
- /* don't enforce counts; this is obsolete. */
- T( "network-status", K_NETWORK_STATUS, NO_ARGS, NO_OBJ ),
- T( "directory-signature", K_DIRECTORY_SIGNATURE, ARGS, NEED_OBJ ),
- T( "recommended-software",K_RECOMMENDED_SOFTWARE,CONCAT_ARGS, NO_OBJ ),
- T( "signed-directory", K_SIGNED_DIRECTORY, NO_ARGS, NO_OBJ ),
-
- T( "running-routers", K_RUNNING_ROUTERS, ARGS, NO_OBJ ),
- T( "router-status", K_ROUTER_STATUS, ARGS, NO_OBJ ),
- T( "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ ),
- T( "opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
- T( "contact", K_CONTACT, CONCAT_ARGS, NO_OBJ ),
- T( "dir-signing-key", K_DIR_SIGNING_KEY, ARGS, OBJ_OK ),
- T( "fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ),
-
- END_OF_TABLE
-};
-
/** List of tokens common to V3 authority certificates and V3 consensuses. */
#define CERTIFICATE_MEMBERS \
T1("dir-key-certificate-version", K_DIR_KEY_CERTIFICATE_VERSION, \
@@ -524,9 +510,11 @@ static token_rule_t networkstatus_detached_signature_token_table[] = {
/** List of tokens recognized in microdescriptors */
static token_rule_t microdesc_token_table[] = {
T1_START("onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY_1024),
+ T01("ntor-onion-key", K_ONION_KEY_NTOR, GE(1), NO_OBJ ),
T0N("a", K_A, GE(1), NO_OBJ ),
T01("family", K_FAMILY, ARGS, NO_OBJ ),
T01("p", K_P, CONCAT_ARGS, NO_OBJ ),
+ T01("p6", K_P6, CONCAT_ARGS, NO_OBJ ),
A01("@last-listed", A_LAST_LISTED, CONCAT_ARGS, NO_OBJ ),
END_OF_TABLE
};
@@ -535,7 +523,8 @@ static token_rule_t microdesc_token_table[] = {
/* static function prototypes */
static int router_add_exit_policy(routerinfo_t *router,directory_token_t *tok);
-static addr_policy_t *router_parse_addr_policy(directory_token_t *tok);
+static addr_policy_t *router_parse_addr_policy(directory_token_t *tok,
+ unsigned fmt_flags);
static addr_policy_t *router_parse_addr_policy_private(directory_token_t *tok);
static int router_get_hash_impl(const char *s, size_t s_len, char *digest,
@@ -576,7 +565,6 @@ static int check_signature_token(const char *digest,
crypto_pk_t *pkey,
int flags,
const char *doctype);
-static crypto_pk_t *find_dir_signing_key(const char *str, const char *eos);
#undef DEBUG_AREA_ALLOC
@@ -819,218 +807,6 @@ tor_version_is_obsolete(const char *myversion, const char *versionlist)
return ret;
}
-/** Read a signed directory from <b>str</b>. If it's well-formed, return 0.
- * Otherwise, return -1. If we're a directory cache, cache it.
- */
-int
-router_parse_directory(const char *str)
-{
- directory_token_t *tok;
- char digest[DIGEST_LEN];
- time_t published_on;
- int r;
- const char *end, *cp, *str_dup = str;
- smartlist_t *tokens = NULL;
- crypto_pk_t *declared_key = NULL;
- memarea_t *area = memarea_new();
-
- /* XXXX This could be simplified a lot, but it will all go away
- * once pre-0.1.1.8 is obsolete, and for now it's better not to
- * touch it. */
-
- if (router_get_dir_hash(str, digest)) {
- log_warn(LD_DIR, "Unable to compute digest of directory");
- goto err;
- }
- log_debug(LD_DIR,"Received directory hashes to %s",hex_str(digest,4));
-
- /* Check signature first, before we try to tokenize. */
- cp = str;
- while (cp && (end = strstr(cp+1, "\ndirectory-signature")))
- cp = end;
- if (cp == str || !cp) {
- log_warn(LD_DIR, "No signature found on directory."); goto err;
- }
- ++cp;
- tokens = smartlist_new();
- if (tokenize_string(area,cp,strchr(cp,'\0'),tokens,dir_token_table,0)) {
- log_warn(LD_DIR, "Error tokenizing directory signature"); goto err;
- }
- if (smartlist_len(tokens) != 1) {
- log_warn(LD_DIR, "Unexpected number of tokens in signature"); goto err;
- }
- tok=smartlist_get(tokens,0);
- if (tok->tp != K_DIRECTORY_SIGNATURE) {
- log_warn(LD_DIR,"Expected a single directory signature"); goto err;
- }
- declared_key = find_dir_signing_key(str, str+strlen(str));
- note_crypto_pk_op(VERIFY_DIR);
- if (check_signature_token(digest, DIGEST_LEN, tok, declared_key,
- CST_CHECK_AUTHORITY, "directory")<0)
- goto err;
-
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
- smartlist_clear(tokens);
- memarea_clear(area);
-
- /* Now try to parse the first part of the directory. */
- if ((end = strstr(str,"\nrouter "))) {
- ++end;
- } else if ((end = strstr(str, "\ndirectory-signature"))) {
- ++end;
- } else {
- end = str + strlen(str);
- }
-
- if (tokenize_string(area,str,end,tokens,dir_token_table,0)) {
- log_warn(LD_DIR, "Error tokenizing directory"); goto err;
- }
-
- tok = find_by_keyword(tokens, K_PUBLISHED);
- tor_assert(tok->n_args == 1);
-
- if (parse_iso_time(tok->args[0], &published_on) < 0) {
- goto err;
- }
-
- /* Now that we know the signature is okay, and we have a
- * publication time, cache the directory. */
- if (directory_caches_v1_dir_info(get_options()) &&
- !authdir_mode_v1(get_options()))
- dirserv_set_cached_directory(str, published_on, 0);
-
- r = 0;
- goto done;
- err:
- dump_desc(str_dup, "v1 directory");
- r = -1;
- done:
- if (declared_key) crypto_pk_free(declared_key);
- if (tokens) {
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
- smartlist_free(tokens);
- }
- if (area) {
- DUMP_AREA(area, "v1 directory");
- memarea_drop_all(area);
- }
- return r;
-}
-
-/** Read a signed router status statement from <b>str</b>. If it's
- * well-formed, return 0. Otherwise, return -1. If we're a directory cache,
- * cache it.*/
-int
-router_parse_runningrouters(const char *str)
-{
- char digest[DIGEST_LEN];
- directory_token_t *tok;
- time_t published_on;
- int r = -1;
- crypto_pk_t *declared_key = NULL;
- smartlist_t *tokens = NULL;
- const char *eos = str + strlen(str), *str_dup = str;
- memarea_t *area = NULL;
-
- if (router_get_runningrouters_hash(str, digest)) {
- log_warn(LD_DIR, "Unable to compute digest of running-routers");
- goto err;
- }
- area = memarea_new();
- tokens = smartlist_new();
- if (tokenize_string(area,str,eos,tokens,dir_token_table,0)) {
- log_warn(LD_DIR, "Error tokenizing running-routers"); goto err;
- }
- tok = smartlist_get(tokens,0);
- if (tok->tp != K_NETWORK_STATUS) {
- log_warn(LD_DIR, "Network-status starts with wrong token");
- goto err;
- }
-
- tok = find_by_keyword(tokens, K_PUBLISHED);
- tor_assert(tok->n_args == 1);
- if (parse_iso_time(tok->args[0], &published_on) < 0) {
- goto err;
- }
- if (!(tok = find_opt_by_keyword(tokens, K_DIRECTORY_SIGNATURE))) {
- log_warn(LD_DIR, "Missing signature on running-routers");
- goto err;
- }
- declared_key = find_dir_signing_key(str, eos);
- note_crypto_pk_op(VERIFY_DIR);
- if (check_signature_token(digest, DIGEST_LEN, tok, declared_key,
- CST_CHECK_AUTHORITY, "running-routers")
- < 0)
- goto err;
-
- /* Now that we know the signature is okay, and we have a
- * publication time, cache the list. */
- if (get_options()->DirPort_set && !authdir_mode_v1(get_options()))
- dirserv_set_cached_directory(str, published_on, 1);
-
- r = 0;
- err:
- dump_desc(str_dup, "v1 running-routers");
- if (declared_key) crypto_pk_free(declared_key);
- if (tokens) {
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
- smartlist_free(tokens);
- }
- if (area) {
- DUMP_AREA(area, "v1 running-routers");
- memarea_drop_all(area);
- }
- return r;
-}
-
-/** Given a directory or running-routers string in <b>str</b>, try to
- * find the its dir-signing-key token (if any). If this token is
- * present, extract and return the key. Return NULL on failure. */
-static crypto_pk_t *
-find_dir_signing_key(const char *str, const char *eos)
-{
- const char *cp;
- directory_token_t *tok;
- crypto_pk_t *key = NULL;
- memarea_t *area = NULL;
- tor_assert(str);
- tor_assert(eos);
-
- /* Is there a dir-signing-key in the directory? */
- cp = tor_memstr(str, eos-str, "\nopt dir-signing-key");
- if (!cp)
- cp = tor_memstr(str, eos-str, "\ndir-signing-key");
- if (!cp)
- return NULL;
- ++cp; /* Now cp points to the start of the token. */
-
- area = memarea_new();
- tok = get_next_token(area, &cp, eos, dir_token_table);
- if (!tok) {
- log_warn(LD_DIR, "Unparseable dir-signing-key token");
- goto done;
- }
- if (tok->tp != K_DIR_SIGNING_KEY) {
- log_warn(LD_DIR, "Dir-signing-key token did not parse as expected");
- goto done;
- }
-
- if (tok->key) {
- key = tok->key;
- tok->key = NULL; /* steal reference. */
- } else {
- log_warn(LD_DIR, "Dir-signing-key token contained no key");
- }
-
- done:
- if (tok) token_clear(tok);
- if (area) {
- DUMP_AREA(area, "dir-signing-key token");
- memarea_drop_all(area);
- }
- return key;
-}
-
/** Return true iff <b>key</b> is allowed to sign directories.
*/
static int
@@ -1253,7 +1029,7 @@ dump_distinct_digest_count(int severity)
#ifdef COUNT_DISTINCT_DIGESTS
if (!verified_digests)
verified_digests = digestmap_new();
- log(severity, LD_GENERAL, "%d *distinct* router digests verified",
+ tor_log(severity, LD_GENERAL, "%d *distinct* router digests verified",
digestmap_size(verified_digests));
#else
(void)severity; /* suppress "unused parameter" warning */
@@ -1280,7 +1056,8 @@ find_single_ipv6_orport(const smartlist_t *list,
uint16_t port_min, port_max;
tor_assert(t->n_args >= 1);
/* XXXX Prop186 the full spec allows much more than this. */
- if (tor_addr_parse_mask_ports(t->args[0], &a, &bits, &port_min,
+ if (tor_addr_parse_mask_ports(t->args[0], 0,
+ &a, &bits, &port_min,
&port_max) == AF_INET6 &&
bits == 128 &&
port_min == port_max) {
@@ -1510,6 +1287,17 @@ router_parse_entry_from_string(const char *s, const char *end,
router->onion_pkey = tok->key;
tok->key = NULL; /* Prevent free */
+ if ((tok = find_opt_by_keyword(tokens, K_ONION_KEY_NTOR))) {
+ curve25519_public_key_t k;
+ tor_assert(tok->n_args >= 1);
+ if (curve25519_public_from_base64(&k, tok->args[0]) < 0) {
+ log_warn(LD_DIR, "Bogus ntor-onion-key in routerinfo");
+ goto err;
+ }
+ router->onion_curve25519_pkey =
+ tor_memdup(&k, sizeof(curve25519_public_key_t));
+ }
+
tok = find_by_keyword(tokens, K_SIGNING_KEY);
router->identity_pkey = tok->key;
tok->key = NULL; /* Prevent free */
@@ -1568,7 +1356,18 @@ router_parse_entry_from_string(const char *s, const char *end,
goto err;
});
policy_expand_private(&router->exit_policy);
- if (policy_is_reject_star(router->exit_policy))
+
+ if ((tok = find_opt_by_keyword(tokens, K_IPV6_POLICY)) && tok->n_args) {
+ router->ipv6_exit_policy = parse_short_policy(tok->args[0]);
+ if (! router->ipv6_exit_policy) {
+ log_warn(LD_DIR , "Error in ipv6-policy %s", escaped(tok->args[0]));
+ goto err;
+ }
+ }
+
+ if (policy_is_reject_star(router->exit_policy, AF_INET) &&
+ (!router->ipv6_exit_policy ||
+ short_policy_is_reject_star(router->ipv6_exit_policy)))
router->policy_is_reject_star = 1;
if ((tok = find_opt_by_keyword(tokens, K_FAMILY)) && tok->n_args) {
@@ -2153,6 +1952,8 @@ routerstatus_parse_entry_from_string(memarea_t *area,
tor_version_supports_microdescriptors(tok->args[0]);
rs->version_supports_optimistic_data =
tor_version_as_new_as(tok->args[0], "0.2.3.1-alpha");
+ rs->version_supports_extend2_cells =
+ tor_version_as_new_as(tok->args[0], "0.2.4.7-alpha");
}
if (vote_rs) {
vote_rs->version = tor_strdup(tok->args[0]);
@@ -2454,7 +2255,7 @@ networkstatus_verify_bw_weights(networkstatus_t *ns)
const char *casename = NULL;
int valid = 1;
- weight_scale = circuit_build_times_get_bw_scale(ns);
+ weight_scale = networkstatus_get_weight_scale_param(ns);
Wgg = networkstatus_get_bw_weight(ns, "Wgg", -1);
Wgm = networkstatus_get_bw_weight(ns, "Wgm", -1);
Wgd = networkstatus_get_bw_weight(ns, "Wgd", -1);
@@ -3632,6 +3433,10 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
/** Parse the addr policy in the string <b>s</b> and return it. If
* assume_action is nonnegative, then insert its action (ADDR_POLICY_ACCEPT or
* ADDR_POLICY_REJECT) for items that specify no action.
+ *
+ * The addr_policy_t returned by this function can have its address set to
+ * AF_UNSPEC for '*'. Use policy_expand_unspec() to turn this into a pair
+ * of AF_INET and AF_INET6 items.
*/
addr_policy_t *
router_parse_addr_policy_item_from_string(const char *s, int assume_action)
@@ -3671,7 +3476,7 @@ router_parse_addr_policy_item_from_string(const char *s, int assume_action)
goto err;
}
- r = router_parse_addr_policy(tok);
+ r = router_parse_addr_policy(tok, TAPMP_EXTENDED_STAR);
goto done;
err:
r = NULL;
@@ -3690,7 +3495,7 @@ static int
router_add_exit_policy(routerinfo_t *router, directory_token_t *tok)
{
addr_policy_t *newe;
- newe = router_parse_addr_policy(tok);
+ newe = router_parse_addr_policy(tok, 0);
if (!newe)
return -1;
if (! router->exit_policy)
@@ -3715,7 +3520,7 @@ router_add_exit_policy(routerinfo_t *router, directory_token_t *tok)
/** Given a K_ACCEPT or K_REJECT token and a router, create and return
* a new exit_policy_t corresponding to the token. */
static addr_policy_t *
-router_parse_addr_policy(directory_token_t *tok)
+router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags)
{
addr_policy_t newe;
char *arg;
@@ -3737,7 +3542,7 @@ router_parse_addr_policy(directory_token_t *tok)
else
newe.policy_type = ADDR_POLICY_ACCEPT;
- if (tor_addr_parse_mask_ports(arg, &newe.addr, &newe.maskbits,
+ if (tor_addr_parse_mask_ports(arg, fmt_flags, &newe.addr, &newe.maskbits,
&newe.prt_min, &newe.prt_max) < 0) {
log_warn(LD_DIR,"Couldn't parse line %s. Dropping", escaped(arg));
return NULL;
@@ -4039,7 +3844,7 @@ get_next_token(memarea_t *area,
if ((size_t)(eol-next) != 9+obname_len+5 ||
strcmp_len(next+9, tok->object_type, obname_len) ||
strcmp_len(eol-5, "-----", 5)) {
- snprintf(ebuf, sizeof(ebuf), "Malformed object: mismatched end tag %s",
+ tor_snprintf(ebuf, sizeof(ebuf), "Malformed object: mismatched end tag %s",
tok->object_type);
ebuf[sizeof(ebuf)-1] = '\0';
RET_ERR(ebuf);
@@ -4284,8 +4089,8 @@ router_get_hash_impl_helper(const char *s, size_t s_len,
/** Compute the digest of the substring of <b>s</b> taken from the first
* occurrence of <b>start_str</b> through the first instance of c after the
- * first subsequent occurrence of <b>end_str</b>; store the 20-byte result in
- * <b>digest</b>; return 0 on success.
+ * first subsequent occurrence of <b>end_str</b>; store the 20-byte or 32-byte
+ * result in <b>digest</b>; return 0 on success.
*
* If no such substring exists, return -1.
*/
@@ -4454,6 +4259,17 @@ microdescs_parse_from_string(const char *s, const char *eos,
md->onion_pkey = tok->key;
tok->key = NULL;
+ if ((tok = find_opt_by_keyword(tokens, K_ONION_KEY_NTOR))) {
+ curve25519_public_key_t k;
+ tor_assert(tok->n_args >= 1);
+ if (curve25519_public_from_base64(&k, tok->args[0]) < 0) {
+ log_warn(LD_DIR, "Bogus ntor-onion-key in microdesc");
+ goto next;
+ }
+ md->onion_curve25519_pkey =
+ tor_memdup(&k, sizeof(curve25519_public_key_t));
+ }
+
{
smartlist_t *a_lines = find_all_by_keyword(tokens, K_A);
if (a_lines) {
@@ -4478,6 +4294,9 @@ microdescs_parse_from_string(const char *s, const char *eos,
if ((tok = find_opt_by_keyword(tokens, K_P))) {
md->exit_policy = parse_short_policy(tok->args[0]);
}
+ if ((tok = find_opt_by_keyword(tokens, K_P6))) {
+ md->ipv6_exit_policy = parse_short_policy(tok->args[0]);
+ }
crypto_digest256(md->digest, md->body, md->bodylen, DIGEST_SHA256);
@@ -4637,7 +4456,7 @@ tor_version_parse(const char *s, tor_version_t *out)
if (close_paren-cp > HEX_DIGEST_LEN)
return -1;
hexlen = (int)(close_paren-cp);
- memset(digest, 0, sizeof(digest));
+ memwipe(digest, 0, sizeof(digest));
if ( hexlen == 0 || (hexlen % 2) == 1)
return -1;
if (base16_decode(digest, hexlen/2, cp, hexlen))
diff --git a/src/or/routerparse.h b/src/or/routerparse.h
index 6cf94c1c2f..4cc9bfd3ae 100644
--- a/src/or/routerparse.h
+++ b/src/or/routerparse.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -31,8 +31,6 @@ int router_parse_list_from_string(const char **s, const char *eos,
int is_extrainfo,
int allow_annotations,
const char *prepend_annotations);
-int router_parse_runningrouters(const char *str);
-int router_parse_directory(const char *str);
routerinfo_t *router_parse_entry_from_string(const char *s, const char *end,
int cache_copy,
diff --git a/src/or/routerset.c b/src/or/routerset.c
index 8a5ff218b2..1eca5b6f6f 100644
--- a/src/or/routerset.c
+++ b/src/or/routerset.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#include "or.h"
@@ -95,7 +95,7 @@ routerset_refresh_countries(routerset_t *target)
tor_assert(cc < target->n_countries);
bitarray_set(target->countries, cc);
} else {
- log(LOG_WARN, LD_CONFIG, "Country code '%s' is not recognized.",
+ log_warn(LD_CONFIG, "Country code '%s' is not recognized.",
country);
}
} SMARTLIST_FOREACH_END(country);
@@ -148,6 +148,7 @@ routerset_parse(routerset_t *target, const char *s, const char *description)
SMARTLIST_DEL_CURRENT(list, nick);
}
} SMARTLIST_FOREACH_END(nick);
+ policy_expand_unspec(&target->policies);
smartlist_add_all(target->list, list);
smartlist_free(list);
if (added_countries)
@@ -225,6 +226,45 @@ routerset_contains(const routerset_t *set, const tor_addr_t *addr,
return 0;
}
+/** If *<b>setp</b> includes at least one country code, or if
+ * <b>only_some_cc_set</b> is 0, add the ?? and A1 country codes to
+ * *<b>setp</b>, creating it as needed. Return true iff *<b>setp</b> changed.
+ */
+int
+routerset_add_unknown_ccs(routerset_t **setp, int only_if_some_cc_set)
+{
+ routerset_t *set;
+ int add_unknown, add_a1;
+ if (only_if_some_cc_set) {
+ if (!*setp || smartlist_len((*setp)->country_names) == 0)
+ return 0;
+ }
+ if (!*setp)
+ *setp = routerset_new();
+
+ set = *setp;
+
+ add_unknown = ! smartlist_contains_string_case(set->country_names, "??") &&
+ geoip_get_country("??") >= 0;
+ add_a1 = ! smartlist_contains_string_case(set->country_names, "a1") &&
+ geoip_get_country("A1") >= 0;
+
+ if (add_unknown) {
+ smartlist_add(set->country_names, tor_strdup("??"));
+ smartlist_add(set->list, tor_strdup("{??}"));
+ }
+ if (add_a1) {
+ smartlist_add(set->country_names, tor_strdup("a1"));
+ smartlist_add(set->list, tor_strdup("{a1}"));
+ }
+
+ if (add_unknown || add_a1) {
+ routerset_refresh_countries(set);
+ return 1;
+ }
+ return 0;
+}
+
/** Return true iff we can tell that <b>ei</b> is a member of <b>set</b>. */
int
routerset_contains_extendinfo(const routerset_t *set, const extend_info_t *ei)
diff --git a/src/or/routerset.h b/src/or/routerset.h
index ad0832e4df..bfa0c59ac1 100644
--- a/src/or/routerset.h
+++ b/src/or/routerset.h
@@ -1,6 +1,6 @@
/* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -31,6 +31,7 @@ int routerset_contains_node(const routerset_t *set, const node_t *node);
void routerset_get_all_nodes(smartlist_t *out, const routerset_t *routerset,
const routerset_t *excludeset,
int running_only);
+int routerset_add_unknown_ccs(routerset_t **setp, int only_if_some_cc_set);
#if 0
void routersets_get_node_disjunction(smartlist_t *target,
const smartlist_t *source,
diff --git a/src/or/statefile.c b/src/or/statefile.c
index beb9cf81ba..bcb7b07417 100644
--- a/src/or/statefile.c
+++ b/src/or/statefile.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#include "or.h"
@@ -52,6 +52,7 @@ static config_var_t state_vars_[] = {
VAR("EntryGuardUnlistedSince", LINELIST_S, EntryGuards, NULL),
VAR("EntryGuardAddedBy", LINELIST_S, EntryGuards, NULL),
VAR("EntryGuardPathBias", LINELIST_S, EntryGuards, NULL),
+ VAR("EntryGuardPathUseBias", LINELIST_S, EntryGuards, NULL),
V(EntryGuards, LINELIST_V, NULL),
VAR("TransportProxy", LINELIST_S, TransportProxies, NULL),
@@ -416,7 +417,7 @@ or_state_save(time_t now)
format_local_iso_time(tbuf, now);
tor_asprintf(&contents,
"# Tor state file last generated on %s local time\n"
- "# Other times below are in GMT\n"
+ "# Other times below are in UTC\n"
"# You *do not* need to edit this file.\n\n%s",
tbuf, state);
tor_free(state);
@@ -517,8 +518,17 @@ get_stored_bindaddr_for_server_transport(const char *transport)
{
char *default_addrport = NULL;
const char *stored_bindaddr = NULL;
+ config_line_t *line = NULL;
+
+ {
+ /* See if the user explicitly asked for a specific listening
+ address for this transport. */
+ char *conf_bindaddr = get_transport_bindaddr_from_config(transport);
+ if (conf_bindaddr)
+ return conf_bindaddr;
+ }
- config_line_t *line = get_transport_in_state_by_name(transport);
+ line = get_transport_in_state_by_name(transport);
if (!line) /* Found no references in state for this transport. */
goto no_bindaddr_found;
diff --git a/src/or/statefile.h b/src/or/statefile.h
index 4770d500d1..dcdee6c604 100644
--- a/src/or/statefile.h
+++ b/src/or/statefile.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_STATEFILE_H
diff --git a/src/or/status.c b/src/or/status.c
index fc01d0a242..126167dcb9 100644
--- a/src/or/status.c
+++ b/src/or/status.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, The Tor Project, Inc. */
+/* Copyright (c) 2010-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -10,6 +10,7 @@
#include "config.h"
#include "status.h"
#include "nodelist.h"
+#include "relay.h"
#include "router.h"
#include "circuitlist.h"
#include "main.h"
@@ -106,6 +107,11 @@ log_heartbeat(time_t now)
"circuits open. I've sent %s and received %s.",
uptime, count_circuits(),bw_sent,bw_rcvd);
+ if (stats_n_data_cells_packaged)
+ log_notice(LD_HEARTBEAT, "Average packaged cell fullness: %2.3f%%",
+ 100*(U64_TO_DBL(stats_n_data_bytes_packaged) /
+ U64_TO_DBL(stats_n_data_cells_packaged*RELAY_PAYLOAD_SIZE)) );
+
tor_free(uptime);
tor_free(bw_sent);
tor_free(bw_rcvd);
diff --git a/src/or/status.h b/src/or/status.h
index de49d751c3..7c3b969c86 100644
--- a/src/or/status.h
+++ b/src/or/status.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, The Tor Project, Inc. */
+/* Copyright (c) 2010-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_STATUS_H
diff --git a/src/or/tor_main.c b/src/or/tor_main.c
index 2f4922317d..806b5ebb38 100644
--- a/src/or/tor_main.c
+++ b/src/or/tor_main.c
@@ -1,6 +1,6 @@
/* Copyright 2001-2004 Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/** String describing which Tor subversion repository version the source was
diff --git a/src/or/transports.c b/src/or/transports.c
index 0319071097..647b293168 100644
--- a/src/or/transports.c
+++ b/src/or/transports.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Tor Project, Inc. */
+/* Copyright (c) 2011-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -428,7 +428,7 @@ static void
add_transport_to_proxy(const char *transport, managed_proxy_t *mp)
{
tor_assert(mp->transports_to_launch);
- if (!smartlist_string_isin(mp->transports_to_launch, transport))
+ if (!smartlist_contains_string(mp->transports_to_launch, transport))
smartlist_add(mp->transports_to_launch, tor_strdup(transport));
}
@@ -453,7 +453,7 @@ proxy_needs_restart(const managed_proxy_t *mp)
goto needs_restart;
SMARTLIST_FOREACH_BEGIN(mp->transports, const transport_t *, t) {
- if (!smartlist_string_isin(mp->transports_to_launch, t->name))
+ if (!smartlist_contains_string(mp->transports_to_launch, t->name))
goto needs_restart;
} SMARTLIST_FOREACH_END(t);
diff --git a/src/or/transports.h b/src/or/transports.h
index 86a2530fcb..6ee82f4556 100644
--- a/src/or/transports.h
+++ b/src/or/transports.h
@@ -1,6 +1,6 @@
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2012, The Tor Project, Inc. */
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**