diff options
Diffstat (limited to 'src/or')
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*)ô->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 = ô->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, <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, <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->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 */ /** |