diff options
Diffstat (limited to 'src/or/directory.c')
-rw-r--r-- | src/or/directory.c | 593 |
1 files changed, 397 insertions, 196 deletions
diff --git a/src/or/directory.c b/src/or/directory.c index 29cf10cea..ee05ff897 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-2011, The Tor Project, Inc. */ + * Copyright (c) 2007-2012, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" @@ -15,7 +15,9 @@ #include "dirvote.h" #include "geoip.h" #include "main.h" +#include "microdesc.h" #include "networkstatus.h" +#include "nodelist.h" #include "policies.h" #include "rendclient.h" #include "rendcommon.h" @@ -78,6 +80,8 @@ static void dir_routerdesc_download_failed(smartlist_t *failed, int router_purpose, int was_extrainfo, int was_descriptor_digests); +static void dir_microdesc_download_failed(smartlist_t *failed, + int status_code); static void note_client_request(int purpose, int compressed, size_t bytes); static int client_likes_consensus(networkstatus_t *v, const char *want_url); @@ -126,8 +130,9 @@ purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose) { if (get_options()->AllDirActionsPrivate) return 1; - if (router_purpose == ROUTER_PURPOSE_BRIDGE && can_complete_circuit) - return 1; /* if no circuits yet, we may need this info to bootstrap. */ + if (router_purpose == ROUTER_PURPOSE_BRIDGE) + return 1; /* if no circuits yet, this might break bootstrapping, but it's + * needed to be safe. */ if (dir_purpose == DIR_PURPOSE_UPLOAD_DIR || dir_purpose == DIR_PURPOSE_UPLOAD_VOTE || dir_purpose == DIR_PURPOSE_UPLOAD_SIGNATURES || @@ -137,26 +142,28 @@ purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose) dir_purpose == DIR_PURPOSE_FETCH_CONSENSUS || dir_purpose == DIR_PURPOSE_FETCH_CERTIFICATE || dir_purpose == DIR_PURPOSE_FETCH_SERVERDESC || - dir_purpose == DIR_PURPOSE_FETCH_EXTRAINFO) + dir_purpose == DIR_PURPOSE_FETCH_EXTRAINFO || + dir_purpose == DIR_PURPOSE_FETCH_MICRODESC) return 0; return 1; } -/** Return a newly allocated string describing <b>auth</b>. */ -char * -authority_type_to_string(authority_type_t auth) +/** Return a newly allocated string describing <b>auth</b>. Only describes + * authority features. */ +static char * +authdir_type_to_string(dirinfo_type_t auth) { char *result; - smartlist_t *lst = smartlist_create(); - if (auth & V1_AUTHORITY) + smartlist_t *lst = smartlist_new(); + if (auth & V1_DIRINFO) smartlist_add(lst, (void*)"V1"); - if (auth & V2_AUTHORITY) + if (auth & V2_DIRINFO) smartlist_add(lst, (void*)"V2"); - if (auth & V3_AUTHORITY) + if (auth & V3_DIRINFO) smartlist_add(lst, (void*)"V3"); - if (auth & BRIDGE_AUTHORITY) + if (auth & BRIDGE_DIRINFO) smartlist_add(lst, (void*)"Bridge"); - if (auth & HIDSERV_AUTHORITY) + if (auth & HIDSERV_DIRINFO) smartlist_add(lst, (void*)"Hidden service"); if (smartlist_len(lst)) { result = smartlist_join_strings(lst, ", ", 0, NULL); @@ -201,6 +208,8 @@ dir_conn_purpose_to_string(int purpose) return "hidden-service v2 descriptor fetch"; case DIR_PURPOSE_UPLOAD_RENDDESC_V2: return "hidden-service v2 descriptor upload"; + case DIR_PURPOSE_FETCH_MICRODESC: + return "microdescriptor fetch"; } log_warn(LD_BUG, "Called with unknown purpose %d", purpose); @@ -213,17 +222,19 @@ dir_conn_purpose_to_string(int purpose) int router_supports_extrainfo(const char *identity_digest, int is_authority) { - routerinfo_t *ri = router_get_by_digest(identity_digest); + const node_t *node = node_get_by_id(identity_digest); - if (ri) { - if (ri->caches_extra_info) + if (node && node->ri) { + if (node->ri->caches_extra_info) return 1; - if (is_authority && ri->platform && - tor_version_as_new_as(ri->platform, "Tor 0.2.0.0-alpha-dev (r10070)")) + if (is_authority && node->ri->platform && + tor_version_as_new_as(node->ri->platform, + "Tor 0.2.0.0-alpha-dev (r10070)")) return 1; } if (is_authority) { - routerstatus_t *rs = router_get_consensus_status_by_id(identity_digest); + const routerstatus_t *rs = + router_get_consensus_status_by_id(identity_digest); if (rs && rs->version_supports_extrainfo_upload) return 1; } @@ -242,7 +253,7 @@ int directories_have_accepted_server_descriptor(void) { smartlist_t *servers = router_get_trusted_dir_servers(); - or_options_t *options = get_options(); + const or_options_t *options = get_options(); SMARTLIST_FOREACH(servers, trusted_dir_server_t *, d, { if ((d->type & options->_PublishServerDescriptor) && d->has_accepted_serverdesc) { @@ -271,11 +282,11 @@ directories_have_accepted_server_descriptor(void) */ void directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose, - authority_type_t type, + dirinfo_type_t type, const char *payload, size_t payload_len, size_t extrainfo_len) { - or_options_t *options = get_options(); + const or_options_t *options = get_options(); int post_via_tor; smartlist_t *dirservers = router_get_trusted_dir_servers(); int found = 0; @@ -296,8 +307,8 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose, if (exclude_self && router_digest_is_me(ds->digest)) continue; - if (options->ExcludeNodes && options->StrictNodes && - routerset_contains_routerstatus(options->ExcludeNodes, rs)) { + if (options->StrictNodes && + routerset_contains_routerstatus(options->ExcludeNodes, rs, -1)) { log_warn(LD_DIR, "Wanted to contact authority '%s' for %s, but " "it's in our ExcludedNodes list and StrictNodes is set. " "Skipping.", @@ -324,7 +335,7 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose, NULL, payload, upload_len, 0); } SMARTLIST_FOREACH_END(ds); if (!found) { - char *s = authority_type_to_string(type); + char *s = authdir_type_to_string(type); log_warn(LD_DIR, "Publishing server descriptor to directory authorities " "of type '%s', but no authorities of that type listed!", s); tor_free(s); @@ -341,39 +352,46 @@ void directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose, const char *resource, int pds_flags) { - routerstatus_t *rs = NULL; - or_options_t *options = get_options(); + const routerstatus_t *rs = NULL; + const or_options_t *options = get_options(); int prefer_authority = directory_fetches_from_authorities(options); + int require_authority = 0; int get_via_tor = purpose_needs_anonymity(dir_purpose, router_purpose); - authority_type_t type; + dirinfo_type_t type; time_t if_modified_since = 0; /* FFFF we could break this switch into its own function, and call * it elsewhere in directory.c. -RD */ switch (dir_purpose) { case DIR_PURPOSE_FETCH_EXTRAINFO: - type = EXTRAINFO_CACHE | - (router_purpose == ROUTER_PURPOSE_BRIDGE ? BRIDGE_AUTHORITY : - V3_AUTHORITY); + type = EXTRAINFO_DIRINFO | + (router_purpose == ROUTER_PURPOSE_BRIDGE ? BRIDGE_DIRINFO : + V3_DIRINFO); break; case DIR_PURPOSE_FETCH_V2_NETWORKSTATUS: - type = V2_AUTHORITY; + type = V2_DIRINFO; prefer_authority = 1; /* Only v2 authorities have these anyway. */ + require_authority = 1; /* Don't fallback to asking a non-authority */ break; case DIR_PURPOSE_FETCH_SERVERDESC: - type = (router_purpose == ROUTER_PURPOSE_BRIDGE ? BRIDGE_AUTHORITY : - V3_AUTHORITY); + type = (router_purpose == ROUTER_PURPOSE_BRIDGE ? BRIDGE_DIRINFO : + V3_DIRINFO); break; case DIR_PURPOSE_FETCH_RENDDESC: - type = HIDSERV_AUTHORITY; + type = HIDSERV_DIRINFO; break; case DIR_PURPOSE_FETCH_STATUS_VOTE: case DIR_PURPOSE_FETCH_DETACHED_SIGNATURES: - type = V3_AUTHORITY; + case DIR_PURPOSE_FETCH_CERTIFICATE: + type = V3_DIRINFO; break; case DIR_PURPOSE_FETCH_CONSENSUS: - case DIR_PURPOSE_FETCH_CERTIFICATE: - type = V3_AUTHORITY; + type = V3_DIRINFO; + if (resource && !strcmp(resource,"microdesc")) + type |= MICRODESC_DIRINFO; + break; + case DIR_PURPOSE_FETCH_MICRODESC: + type = MICRODESC_DIRINFO; break; default: log_warn(LD_BUG, "Unexpected purpose %d", (int)dir_purpose); @@ -381,25 +399,46 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose, } if (dir_purpose == DIR_PURPOSE_FETCH_CONSENSUS) { - networkstatus_t *v = networkstatus_get_latest_consensus(); - if (v) - if_modified_since = v->valid_after + 180; + int flav = FLAV_NS; + networkstatus_t *v; + if (resource) + flav = networkstatus_parse_flavor_name(resource); + + if (flav != -1) { + /* IF we have a parsed consensus of this type, we can do an + * if-modified-time based on it. */ + v = networkstatus_get_latest_consensus_by_flavor(flav); + if (v) + if_modified_since = v->valid_after + 180; + } else { + /* Otherwise it might be a consensus we don't parse, but which we + * do cache. Look at the cached copy, perhaps. */ + cached_dir_t *cd = dirserv_get_consensus(resource); + if (cd) + if_modified_since = cd->published + 180; + } } - if (!options->FetchServerDescriptors && type != HIDSERV_AUTHORITY) + if (!options->FetchServerDescriptors && type != HIDSERV_DIRINFO) return; if (!get_via_tor) { - if (options->UseBridges && type != BRIDGE_AUTHORITY) { - /* want to ask a running bridge for which we have a descriptor. */ - /* XXX023 we assume that all of our bridges can answer any - * possible directory question. This won't be true forever. -RD */ - /* It certainly is not true with conditional consensus downloading, - * so, for now, never assume the server supports that. */ - routerinfo_t *ri = choose_random_entry(NULL); - if (ri) { + 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(). + */ + /* XXX024 Not all bridges handle conditional consensus downloading, + * so, for now, never assume the server supports that. -PP */ + const node_t *node = choose_random_entry(NULL); + if (node && node->ri) { + /* every bridge has a routerinfo. */ tor_addr_t addr; - tor_addr_from_ipv4h(&addr, ri->addr); + routerinfo_t *ri = node->ri; + node_get_addr(node, &addr); directory_initiate_command(ri->address, &addr, ri->or_port, 0, 0, /* don't use conditional consensus url */ @@ -412,10 +451,11 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose, "nodes are available yet."); return; } else { - if (prefer_authority || type == BRIDGE_AUTHORITY) { + if (prefer_authority || type == BRIDGE_DIRINFO) { /* only ask authdirservers, and don't ask myself */ rs = router_pick_trusteddirserver(type, pds_flags); - if (rs == NULL && (pds_flags & PDS_NO_EXISTING_SERVERDESC_FETCH)) { + if (rs == NULL && (pds_flags & (PDS_NO_EXISTING_SERVERDESC_FETCH| + PDS_NO_EXISTING_MICRODESC_FETCH))) { /* We don't want to fetch from any authorities that we're currently * fetching server descriptors from, and we got no match. Did we * get no match because all the authorities have connections @@ -423,7 +463,8 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose, * return,) or because all the authorities are down or on fire or * unreachable or something (in which case we should go on with * our fallback code)? */ - pds_flags &= ~PDS_NO_EXISTING_SERVERDESC_FETCH; + pds_flags &= ~(PDS_NO_EXISTING_SERVERDESC_FETCH| + PDS_NO_EXISTING_MICRODESC_FETCH); rs = router_pick_trusteddirserver(type, pds_flags); if (rs) { log_debug(LD_DIR, "Deferring serverdesc fetch: all authorities " @@ -431,8 +472,13 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose, return; } } + if (rs == NULL && require_authority) { + log_info(LD_DIR, "No authorities were available for %s: will try " + "later.", dir_conn_purpose_to_string(dir_purpose)); + return; + } } - if (!rs && type != BRIDGE_AUTHORITY) { + if (!rs && type != BRIDGE_DIRINFO) { /* anybody with a non-zero dirport will do */ rs = router_pick_directory_server(type, pds_flags); if (!rs) { @@ -449,7 +495,7 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose, if (dir_purpose == DIR_PURPOSE_FETCH_RENDDESC) { /* only ask hidserv authorities, any of them will do */ pds_flags |= PDS_IGNORE_FASCISTFIREWALL|PDS_ALLOW_SELF; - rs = router_pick_trusteddirserver(HIDSERV_AUTHORITY, pds_flags); + rs = router_pick_trusteddirserver(HIDSERV_DIRINFO, pds_flags); } else { /* anybody with a non-zero dirport will do. Disregard firewalls. */ pds_flags |= PDS_IGNORE_FASCISTFIREWALL; @@ -495,7 +541,7 @@ directory_get_from_all_authorities(uint8_t dir_purpose, routerstatus_t *rs; if (router_digest_is_me(ds->digest)) continue; - if (!(ds->type & V3_AUTHORITY)) + if (!(ds->type & V3_DIRINFO)) continue; rs = &ds->fake_status; directory_initiate_command_routerstatus(rs, dir_purpose, router_purpose, @@ -506,7 +552,7 @@ directory_get_from_all_authorities(uint8_t dir_purpose, /** Same as directory_initiate_command_routerstatus(), but accepts * rendezvous data to fetch a hidden service descriptor. */ void -directory_initiate_command_routerstatus_rend(routerstatus_t *status, +directory_initiate_command_routerstatus_rend(const routerstatus_t *status, uint8_t dir_purpose, uint8_t router_purpose, int anonymized_connection, @@ -516,21 +562,22 @@ directory_initiate_command_routerstatus_rend(routerstatus_t *status, time_t if_modified_since, const rend_data_t *rend_query) { - or_options_t *options = get_options(); - routerinfo_t *router; + const or_options_t *options = get_options(); + const node_t *node; char address_buf[INET_NTOA_BUF_LEN+1]; struct in_addr in; const char *address; tor_addr_t addr; - router = router_get_by_digest(status->identity_digest); + node = node_get_by_id(status->identity_digest); - if (!router && anonymized_connection) { - log_info(LD_DIR, "Not sending anonymized request to directory %s; we " + if (!node && anonymized_connection) { + log_info(LD_DIR, "Not sending anonymized request to directory '%s'; we " "don't have its router descriptor.", routerstatus_describe(status)); return; - } else if (router) { - address = router->address; + } else if (node) { + node_get_address_string(node, address_buf, sizeof(address_buf)); + address = address_buf; } else { in.s_addr = htonl(status->addr); tor_inet_ntoa(&in, address_buf, sizeof(address_buf)); @@ -539,7 +586,7 @@ directory_initiate_command_routerstatus_rend(routerstatus_t *status, tor_addr_from_ipv4h(&addr, status->addr); if (options->ExcludeNodes && options->StrictNodes && - routerset_contains_routerstatus(options->ExcludeNodes, status)) { + routerset_contains_routerstatus(options->ExcludeNodes, status, -1)) { log_warn(LD_DIR, "Wanted to contact directory mirror %s for %s, but " "it's in our ExcludedNodes list and StrictNodes is set. " "Skipping. This choice might make your Tor not work.", @@ -574,7 +621,7 @@ directory_initiate_command_routerstatus_rend(routerstatus_t *status, * want to fetch. */ void -directory_initiate_command_routerstatus(routerstatus_t *status, +directory_initiate_command_routerstatus(const routerstatus_t *status, uint8_t dir_purpose, uint8_t router_purpose, int anonymized_connection, @@ -598,7 +645,7 @@ directory_conn_is_self_reachability_test(dir_connection_t *conn) { if (conn->requested_resource && !strcmpstart(conn->requested_resource,"authority")) { - routerinfo_t *me = router_get_my_routerinfo(); + const routerinfo_t *me = router_get_my_routerinfo(); if (me && router_digest_is_me(conn->identity_digest) && tor_addr_eq_ipv4h(&conn->_base.addr, me->addr) && /*XXXX prop 118*/ @@ -612,7 +659,7 @@ directory_conn_is_self_reachability_test(dir_connection_t *conn) * server due to a network error: Mark the router as down and try again if * possible. */ -void +static void connection_dir_request_failed(dir_connection_t *conn) { if (directory_conn_is_self_reachability_test(conn)) { @@ -626,15 +673,18 @@ connection_dir_request_failed(dir_connection_t *conn) connection_dir_download_v2_networkstatus_failed(conn, -1); } else if (conn->_base.purpose == DIR_PURPOSE_FETCH_SERVERDESC || conn->_base.purpose == DIR_PURPOSE_FETCH_EXTRAINFO) { - log_info(LD_DIR, "Giving up on directory server at '%s'; retrying", + log_info(LD_DIR, "Giving up on serverdesc/extrainfo fetch from " + "directory server at '%s'; retrying", conn->_base.address); if (conn->router_purpose == ROUTER_PURPOSE_BRIDGE) connection_dir_bridge_routerdesc_failed(conn); connection_dir_download_routerdesc_failed(conn); } else if (conn->_base.purpose == DIR_PURPOSE_FETCH_CONSENSUS) { - networkstatus_consensus_download_failed(0); + if (conn->requested_resource) + networkstatus_consensus_download_failed(0, conn->requested_resource); } else if (conn->_base.purpose == DIR_PURPOSE_FETCH_CERTIFICATE) { - log_info(LD_DIR, "Giving up on directory server at '%s'; retrying", + log_info(LD_DIR, "Giving up on certificate fetch from directory server " + "at '%s'; retrying", conn->_base.address); connection_dir_download_cert_failed(conn, 0); } else if (conn->_base.purpose == DIR_PURPOSE_FETCH_DETACHED_SIGNATURES) { @@ -643,6 +693,10 @@ connection_dir_request_failed(dir_connection_t *conn) } else if (conn->_base.purpose == DIR_PURPOSE_FETCH_STATUS_VOTE) { log_info(LD_DIR, "Giving up downloading votes from '%s'", conn->_base.address); + } else if (conn->_base.purpose == DIR_PURPOSE_FETCH_MICRODESC) { + log_info(LD_DIR, "Giving up on downloading microdescriptors from " + " directory server at '%s'; will retry", conn->_base.address); + connection_dir_download_routerdesc_failed(conn); } } @@ -672,7 +726,7 @@ connection_dir_download_v2_networkstatus_failed(dir_connection_t *conn, } else if (!strcmpstart(conn->requested_resource, "fp/")) { /* We were trying to download by fingerprint; mark them all as having * failed, and possibly retry them later.*/ - smartlist_t *failed = smartlist_create(); + smartlist_t *failed = smartlist_new(); dir_split_resource_into_fingerprints(conn->requested_resource+3, failed, NULL, 0); if (smartlist_len(failed)) { @@ -713,7 +767,8 @@ connection_dir_download_routerdesc_failed(dir_connection_t *conn) /* No need to relaunch descriptor downloads here: we already do it * every 10 or 60 seconds (FOO_DESCRIPTOR_RETRY_INTERVAL) in main.c. */ tor_assert(conn->_base.purpose == DIR_PURPOSE_FETCH_SERVERDESC || - conn->_base.purpose == DIR_PURPOSE_FETCH_EXTRAINFO); + conn->_base.purpose == DIR_PURPOSE_FETCH_EXTRAINFO || + conn->_base.purpose == DIR_PURPOSE_FETCH_MICRODESC); (void) conn; } @@ -732,7 +787,7 @@ connection_dir_bridge_routerdesc_failed(dir_connection_t *conn) if (!conn->requested_resource || strcmpstart(conn->requested_resource,"fp/")) return; - which = smartlist_create(); + which = smartlist_new(); dir_split_resource_into_fingerprints(conn->requested_resource + strlen("fp/"), which, NULL, 0); @@ -754,7 +809,7 @@ connection_dir_download_cert_failed(dir_connection_t *conn, int status) if (!conn->requested_resource) return; - failed = smartlist_create(); + failed = smartlist_new(); dir_split_resource_into_fingerprints(conn->requested_resource+3, failed, NULL, DSR_HEX); SMARTLIST_FOREACH(failed, char *, cp, @@ -776,7 +831,7 @@ connection_dir_download_cert_failed(dir_connection_t *conn, int status) * 3) Else yes. */ static int -directory_command_should_use_begindir(or_options_t *options, +directory_command_should_use_begindir(const or_options_t *options, const tor_addr_t *addr, int or_port, uint8_t router_purpose, int anonymized_connection) @@ -816,6 +871,20 @@ directory_initiate_command(const char *address, const tor_addr_t *_addr, if_modified_since, NULL); } +/** Return non-zero iff a directory connection with purpose + * <b>dir_purpose</b> reveals sensitive information about a Tor + * instance's client activities. (Such connections must be performed + * through normal three-hop Tor circuits.) */ +static int +is_sensitive_dir_purpose(uint8_t dir_purpose) +{ + return ((dir_purpose == DIR_PURPOSE_FETCH_RENDDESC) || + (dir_purpose == DIR_PURPOSE_HAS_FETCHED_RENDDESC) || + (dir_purpose == DIR_PURPOSE_UPLOAD_RENDDESC) || + (dir_purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2) || + (dir_purpose == DIR_PURPOSE_FETCH_RENDDESC_V2)); +} + /** Same as directory_initiate_command(), but accepts rendezvous data to * fetch a hidden service descriptor. */ static void @@ -831,7 +900,7 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr, const rend_data_t *rend_query) { dir_connection_t *conn; - or_options_t *options = get_options(); + const or_options_t *options = get_options(); int socket_error = 0; int use_begindir = supports_begindir && directory_command_should_use_begindir(options, _addr, @@ -850,6 +919,13 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr, log_debug(LD_DIR, "Initiating %s", dir_conn_purpose_to_string(dir_purpose)); +#ifndef NON_ANONYMOUS_MODE_ENABLED + tor_assert(!(is_sensitive_dir_purpose(dir_purpose) && + !anonymized_connection)); +#else + (void)is_sensitive_dir_purpose; +#endif + /* ensure that we don't make direct connections when a SOCKS server is * configured. */ if (!anonymized_connection && !use_begindir && !options->HTTPProxy && @@ -859,7 +935,7 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr, return; } - conn = dir_connection_new(AF_INET); + conn = dir_connection_new(tor_addr_family(&addr)); /* set up conn so it's got all the data we need to remember */ tor_addr_copy(&conn->_base.addr, &addr); @@ -911,7 +987,11 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr, error indicates broken link in windowsland. */ } } else { /* we want to connect via a tor connection */ - edge_connection_t *linked_conn; + entry_connection_t *linked_conn; + /* Anonymized tunneled connections can never share a circuit. + * One-hop directory connections can share circuits with each other + * but nothing else. */ + int iso_flags = anonymized_connection ? ISO_STREAM : ISO_SESSIONGRP; /* If it's an anonymized connection, remember the fact that we * wanted it for later: maybe we'll want it again soon. */ @@ -925,14 +1005,16 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr, * hook up both sides */ linked_conn = - connection_ap_make_link(conn->_base.address, conn->_base.port, - digest, use_begindir, conn->dirconn_direct); + connection_ap_make_link(TO_CONN(conn), + conn->_base.address, conn->_base.port, + digest, + SESSION_GROUP_DIRCONN, iso_flags, + use_begindir, conn->dirconn_direct); if (!linked_conn) { log_warn(LD_NET,"Making tunnel to dirserver failed."); connection_mark_for_close(TO_CONN(conn)); return; } - connection_link_connections(TO_CONN(conn), TO_CONN(linked_conn)); if (connection_add(TO_CONN(conn)) < 0) { log_warn(LD_NET,"Unable to add connection for link to dirserver."); @@ -945,8 +1027,13 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr, payload, payload_len, supports_conditional_consensus, if_modified_since); + connection_watch_events(TO_CONN(conn), READ_EVENT|WRITE_EVENT); - connection_start_reading(TO_CONN(linked_conn)); + IF_HAS_BUFFEREVENT(ENTRY_TO_CONN(linked_conn), { + connection_watch_events(ENTRY_TO_CONN(linked_conn), + READ_EVENT|WRITE_EVENT); + }) ELSE_IF_NO_BUFFEREVENT + connection_start_reading(ENTRY_TO_CONN(linked_conn)); } } @@ -984,22 +1071,32 @@ _compare_strs(const void **a, const void **b) * This url depends on whether or not the server we go to * is sufficiently new to support conditional consensus downloading, * i.e. GET .../consensus/<b>fpr</b>+<b>fpr</b>+<b>fpr</b> + * + * If 'resource' is provided, it is the name of a consensus flavor to request. */ static char * -directory_get_consensus_url(int supports_conditional_consensus) +directory_get_consensus_url(int supports_conditional_consensus, + const char *resource) { - char *url; - size_t len; + char *url = NULL; + const char *hyphen, *flavor; + if (resource==NULL || strcmp(resource, "ns")==0) { + flavor = ""; /* Request ns consensuses as "", so older servers will work*/ + hyphen = ""; + } else { + flavor = resource; + hyphen = "-"; + } if (supports_conditional_consensus) { char *authority_id_list; - smartlist_t *authority_digests = smartlist_create(); + smartlist_t *authority_digests = smartlist_new(); SMARTLIST_FOREACH(router_get_trusted_dir_servers(), trusted_dir_server_t *, ds, { char *hex; - if (!(ds->type & V3_AUTHORITY)) + if (!(ds->type & V3_DIRINFO)) continue; hex = tor_malloc(2*CONDITIONAL_CONSENSUS_FPR_LEN+1); @@ -1011,16 +1108,15 @@ directory_get_consensus_url(int supports_conditional_consensus) authority_id_list = smartlist_join_strings(authority_digests, "+", 0, NULL); - len = strlen(authority_id_list)+64; - url = tor_malloc(len); - tor_snprintf(url, len, "/tor/status-vote/current/consensus/%s.z", - authority_id_list); + tor_asprintf(&url, "/tor/status-vote/current/consensus%s%s/%s.z", + hyphen, flavor, authority_id_list); SMARTLIST_FOREACH(authority_digests, char *, cp, tor_free(cp)); smartlist_free(authority_digests); tor_free(authority_id_list); } else { - url = tor_strdup("/tor/status-vote/current/consensus.z"); + tor_asprintf(&url, "/tor/status-vote/current/consensus%s%s.z", + hyphen, flavor); } return url; } @@ -1036,13 +1132,11 @@ directory_send_command(dir_connection_t *conn, time_t if_modified_since) { char proxystring[256]; - char proxyauthstring[256]; char hoststring[128]; - char imsstring[RFC1123_TIME_LEN+32]; + smartlist_t *headers = smartlist_new(); char *url; char request[8192]; const char *httpcommand = NULL; - size_t len; tor_assert(conn); tor_assert(conn->_base.type == CONN_TYPE_DIR); @@ -1060,12 +1154,10 @@ directory_send_command(dir_connection_t *conn, } /* Format if-modified-since */ - if (!if_modified_since) { - imsstring[0] = '\0'; - } else { + if (if_modified_since) { char b[RFC1123_TIME_LEN+1]; format_rfc1123_time(b, if_modified_since); - tor_snprintf(imsstring, sizeof(imsstring), "\r\nIf-Modified-Since: %s", b); + smartlist_add_asprintf(headers, "If-Modified-Since: %s\r\n", b); } /* come up with some proxy lines, if we're using one. */ @@ -1080,31 +1172,27 @@ directory_send_command(dir_connection_t *conn, log_warn(LD_BUG, "Encoding http authenticator failed"); } if (base64_authenticator) { - tor_snprintf(proxyauthstring, sizeof(proxyauthstring), - "\r\nProxy-Authorization: Basic %s", + smartlist_add_asprintf(headers, + "Proxy-Authorization: Basic %s\r\n", base64_authenticator); tor_free(base64_authenticator); - } else { - proxyauthstring[0] = 0; } } else { proxystring[0] = 0; - proxyauthstring[0] = 0; } switch (purpose) { case DIR_PURPOSE_FETCH_V2_NETWORKSTATUS: tor_assert(resource); httpcommand = "GET"; - len = strlen(resource)+32; - url = tor_malloc(len); - tor_snprintf(url, len, "/tor/status/%s", resource); + tor_asprintf(&url, "/tor/status/%s", resource); break; case DIR_PURPOSE_FETCH_CONSENSUS: - tor_assert(!resource); + /* resource is optional. If present, it's a flavor name */ tor_assert(!payload); httpcommand = "GET"; - url = directory_get_consensus_url(supports_conditional_consensus); + url = directory_get_consensus_url(supports_conditional_consensus, + resource); log_info(LD_DIR, "Downloading consensus from %s using %s", hoststring, url); break; @@ -1112,17 +1200,13 @@ directory_send_command(dir_connection_t *conn, tor_assert(resource); tor_assert(!payload); httpcommand = "GET"; - len = strlen(resource)+32; - url = tor_malloc(len); - tor_snprintf(url, len, "/tor/keys/%s", resource); + tor_asprintf(&url, "/tor/keys/%s", resource); break; case DIR_PURPOSE_FETCH_STATUS_VOTE: tor_assert(resource); tor_assert(!payload); httpcommand = "GET"; - len = strlen(resource)+32; - url = tor_malloc(len); - tor_snprintf(url, len, "/tor/status-vote/next/%s.z", resource); + tor_asprintf(&url, "/tor/status-vote/next/%s.z", resource); break; case DIR_PURPOSE_FETCH_DETACHED_SIGNATURES: tor_assert(!resource); @@ -1133,23 +1217,29 @@ directory_send_command(dir_connection_t *conn, case DIR_PURPOSE_FETCH_SERVERDESC: tor_assert(resource); httpcommand = "GET"; - len = strlen(resource)+32; - url = tor_malloc(len); - tor_snprintf(url, len, "/tor/server/%s", resource); + tor_asprintf(&url, "/tor/server/%s", resource); break; case DIR_PURPOSE_FETCH_EXTRAINFO: tor_assert(resource); httpcommand = "GET"; - len = strlen(resource)+32; - url = tor_malloc(len); - tor_snprintf(url, len, "/tor/extra/%s", resource); + tor_asprintf(&url, "/tor/extra/%s", resource); break; - case DIR_PURPOSE_UPLOAD_DIR: + case DIR_PURPOSE_FETCH_MICRODESC: + tor_assert(resource); + httpcommand = "GET"; + tor_asprintf(&url, "/tor/micro/%s", resource); + break; + case DIR_PURPOSE_UPLOAD_DIR: { + const char *why = router_get_descriptor_gen_reason(); tor_assert(!resource); tor_assert(payload); httpcommand = "POST"; url = tor_strdup("/tor/"); + if (why) { + smartlist_add_asprintf(headers, "X-Desc-Gen-Reason: %s\r\n", why); + } break; + } case DIR_PURPOSE_UPLOAD_VOTE: tor_assert(!resource); tor_assert(payload); @@ -1167,9 +1257,7 @@ directory_send_command(dir_connection_t *conn, tor_assert(strlen(resource) <= REND_DESC_ID_V2_LEN_BASE32); tor_assert(!payload); httpcommand = "GET"; - len = strlen(resource) + 32; - url = tor_malloc(len); - tor_snprintf(url, len, "/tor/rendezvous2/%s", resource); + tor_asprintf(&url, "/tor/rendezvous2/%s", resource); break; case DIR_PURPOSE_UPLOAD_RENDDESC: tor_assert(!resource); @@ -1200,26 +1288,27 @@ directory_send_command(dir_connection_t *conn, connection_write_to_buf(url, strlen(url), TO_CONN(conn)); tor_free(url); - if (!strcmp(httpcommand, "GET") && !payload) { - tor_snprintf(request, sizeof(request), - " HTTP/1.0\r\nHost: %s%s%s\r\n\r\n", - hoststring, - imsstring, - proxyauthstring); - } else { - tor_snprintf(request, sizeof(request), - " HTTP/1.0\r\nContent-Length: %lu\r\nHost: %s%s%s\r\n\r\n", - payload ? (unsigned long)payload_len : 0, - hoststring, - imsstring, - proxyauthstring); + if (!strcmp(httpcommand, "POST") || payload) { + smartlist_add_asprintf(headers, "Content-Length: %lu\r\n", + payload ? (unsigned long)payload_len : 0); + } + + { + char *header = smartlist_join_strings(headers, "", 0, NULL); + tor_snprintf(request, sizeof(request), " HTTP/1.0\r\nHost: %s\r\n%s\r\n", + hoststring, header); + tor_free(header); } + connection_write_to_buf(request, strlen(request), TO_CONN(conn)); if (payload) { /* then send the payload afterwards too */ connection_write_to_buf(payload, payload_len, TO_CONN(conn)); } + + SMARTLIST_FOREACH(headers, char *, h, tor_free(h)); + smartlist_free(headers); } /** Parse an HTTP request string <b>headers</b> of the form @@ -1355,11 +1444,11 @@ parse_http_response(const char *headers, int *code, time_t *date, } *code = n2; - parsed_headers = smartlist_create(); + parsed_headers = smartlist_new(); smartlist_split_string(parsed_headers, headers, "\n", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); if (reason) { - smartlist_t *status_line_elements = smartlist_create(); + smartlist_t *status_line_elements = smartlist_new(); tor_assert(smartlist_len(parsed_headers)); smartlist_split_string(status_line_elements, smartlist_get(parsed_headers, 0), @@ -1419,6 +1508,9 @@ body_is_plausible(const char *body, size_t len, int purpose) return 1; /* empty bodies don't need decompression */ if (len < 32) return 0; + if (purpose == DIR_PURPOSE_FETCH_MICRODESC) { + return (!strcmpstart(body,"onion-key")); + } if (purpose != DIR_PURPOSE_FETCH_RENDDESC) { if (!strcmpstart(body,"router") || !strcmpstart(body,"signed-directory") || @@ -1495,11 +1587,12 @@ connection_dir_client_reached_eof(dir_connection_t *conn) int plausible; int skewed=0; int allow_partial = (conn->_base.purpose == DIR_PURPOSE_FETCH_SERVERDESC || - conn->_base.purpose == DIR_PURPOSE_FETCH_EXTRAINFO); + conn->_base.purpose == DIR_PURPOSE_FETCH_EXTRAINFO || + conn->_base.purpose == DIR_PURPOSE_FETCH_MICRODESC); int was_compressed=0; time_t now = time(NULL); - switch (fetch_from_buf_http(conn->_base.inbuf, + switch (connection_fetch_from_buf_http(TO_CONN(conn), &headers, MAX_HEADERS_SIZE, &body, &body_len, MAX_DIR_DL_SIZE, allow_partial)) { @@ -1526,9 +1619,11 @@ connection_dir_client_reached_eof(dir_connection_t *conn) if (!reason) reason = tor_strdup("[no reason given]"); log_debug(LD_DIR, - "Received response from directory server '%s:%d': %d %s", + "Received response from directory server '%s:%d': %d %s " + "(purpose: %d)", conn->_base.address, conn->_base.port, status_code, - escaped(reason)); + escaped(reason), + conn->_base.purpose); /* now check if it's got any hints for us about our IP address. */ if (conn->dirconn_direct) { @@ -1574,13 +1669,14 @@ connection_dir_client_reached_eof(dir_connection_t *conn) if (status_code == 503) { routerstatus_t *rs; trusted_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.", status_code, escaped(reason), conn->_base.address, conn->_base.port); - if ((rs = router_get_consensus_status_by_id(conn->identity_digest))) + if ((rs = router_get_mutable_consensus_status_by_id(id_digest))) rs->last_dir_503_at = now; - if ((ds = router_get_trusteddirserver_by_digest(conn->identity_digest))) + if ((ds = router_get_trusteddirserver_by_digest(id_digest))) ds->fake_status.last_dir_503_at = now; tor_free(body); tor_free(headers); tor_free(reason); @@ -1671,13 +1767,13 @@ connection_dir_client_reached_eof(dir_connection_t *conn) if (conn->requested_resource && !strcmpstart(conn->requested_resource,"fp/")) { source = NS_FROM_DIR_BY_FP; - which = smartlist_create(); + which = smartlist_new(); dir_split_resource_into_fingerprints(conn->requested_resource+3, which, NULL, 0); } else if (conn->requested_resource && !strcmpstart(conn->requested_resource, "all")) { source = NS_FROM_DIR_ALL; - which = smartlist_create(); + which = smartlist_new(); SMARTLIST_FOREACH(router_get_trusted_dir_servers(), trusted_dir_server_t *, ds, { @@ -1719,6 +1815,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn) if (conn->_base.purpose == DIR_PURPOSE_FETCH_CONSENSUS) { int r; + const char *flavname = conn->requested_resource; if (status_code != 200) { int severity = (status_code == 304) ? LOG_INFO : LOG_WARN; log(severity, LD_DIR, @@ -1727,22 +1824,24 @@ connection_dir_client_reached_eof(dir_connection_t *conn) status_code, escaped(reason), conn->_base.address, conn->_base.port); tor_free(body); tor_free(headers); tor_free(reason); - networkstatus_consensus_download_failed(status_code); + networkstatus_consensus_download_failed(status_code, flavname); return -1; } log_info(LD_DIR,"Received consensus directory (size %d) from server " "'%s:%d'", (int)body_len, conn->_base.address, conn->_base.port); - if ((r=networkstatus_set_current_consensus(body, "ns", 0))<0) { + if ((r=networkstatus_set_current_consensus(body, flavname, 0))<0) { log_fn(r<-1?LOG_WARN:LOG_INFO, LD_DIR, - "Unable to load consensus directory downloaded from " + "Unable to load %s consensus directory downloaded from " "server '%s:%d'. I'll try again soon.", - conn->_base.address, conn->_base.port); + flavname, conn->_base.address, conn->_base.port); tor_free(body); tor_free(headers); tor_free(reason); - networkstatus_consensus_download_failed(0); + networkstatus_consensus_download_failed(0, flavname); return -1; } /* launches router downloads as needed */ routers_update_all_from_networkstatus(now, 3); + update_microdescs_from_networkstatus(now); + update_microdesc_downloads(now); directory_info_has_arrived(now, 0); log_info(LD_DIR, "Successfully loaded consensus."); } @@ -1823,7 +1922,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn) if (conn->requested_resource && (!strcmpstart(conn->requested_resource,"d/") || !strcmpstart(conn->requested_resource,"fp/"))) { - which = smartlist_create(); + which = smartlist_new(); dir_split_resource_into_fingerprints(conn->requested_resource + (descriptor_digests ? 2 : 3), which, NULL, 0); @@ -1892,6 +1991,43 @@ connection_dir_client_reached_eof(dir_connection_t *conn) if (directory_conn_is_self_reachability_test(conn)) router_dirport_found_reachable(); } + if (conn->_base.purpose == DIR_PURPOSE_FETCH_MICRODESC) { + smartlist_t *which = NULL; + log_info(LD_DIR,"Received answer to microdescriptor request (status %d, " + "size %d) from server '%s:%d'", + status_code, (int)body_len, conn->_base.address, + conn->_base.port); + tor_assert(conn->requested_resource && + !strcmpstart(conn->requested_resource, "d/")); + which = smartlist_new(); + dir_split_resource_into_fingerprints(conn->requested_resource+2, + which, NULL, + DSR_DIGEST256|DSR_BASE64); + if (status_code != 200) { + log_info(LD_DIR, "Received status code %d (%s) from server " + "'%s:%d' while fetching \"/tor/micro/%s\". I'll try again " + "soon.", + status_code, escaped(reason), conn->_base.address, + (int)conn->_base.port, conn->requested_resource); + dir_microdesc_download_failed(which, status_code); + SMARTLIST_FOREACH(which, char *, cp, tor_free(cp)); + smartlist_free(which); + tor_free(body); tor_free(headers); tor_free(reason); + return 0; + } else { + smartlist_t *mds; + mds = microdescs_add_to_cache(get_microdesc_cache(), + body, body+body_len, SAVED_NOWHERE, 0, + now, which); + if (smartlist_len(which)) { + /* Mark remaining ones as failed. */ + dir_microdesc_download_failed(which, status_code); + } + SMARTLIST_FOREACH(which, char *, cp, tor_free(cp)); + smartlist_free(which); + smartlist_free(mds); + } + } if (conn->_base.purpose == DIR_PURPOSE_UPLOAD_DIR) { switch (status_code) { @@ -2166,7 +2302,7 @@ connection_dir_process_inbuf(dir_connection_t *conn) return 0; } - if (buf_datalen(conn->_base.inbuf) > MAX_DIRECTORY_OBJECT_SIZE) { + if (connection_get_inbuf_len(TO_CONN(conn)) > MAX_DIRECTORY_OBJECT_SIZE) { log_warn(LD_HTTP, "Too much data received from directory connection: " "denial of service attempt, or you need to upgrade?"); connection_mark_for_close(TO_CONN(conn)); @@ -2178,6 +2314,28 @@ connection_dir_process_inbuf(dir_connection_t *conn) return 0; } +/** Called when we're about to finally unlink and free a directory connection: + * perform necessary accounting and cleanup */ +void +connection_dir_about_to_close(dir_connection_t *dir_conn) +{ + connection_t *conn = TO_CONN(dir_conn); + + if (conn->state < DIR_CONN_STATE_CLIENT_FINISHED) { + /* It's a directory connection and connecting or fetching + * failed: forget about this router, and maybe try again. */ + connection_dir_request_failed(dir_conn); + } + /* If we were trying to fetch a v2 rend desc and did not succeed, + * retry as needed. (If a fetch is successful, the connection state + * is changed to DIR_PURPOSE_HAS_FETCHED_RENDDESC to mark that + * refetching is unnecessary.) */ + if (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC_V2 && + dir_conn->rend_data && + strlen(dir_conn->rend_data->onion_address) == REND_SERVICE_ID_LEN_BASE32) + rend_client_refetch_v2_renddesc(dir_conn->rend_data); +} + /** Create an http response for the client <b>conn</b> out of * <b>status</b> and <b>reason_phrase</b>. Write it to <b>conn</b>. */ @@ -2280,7 +2438,8 @@ write_http_response_header(dir_connection_t *conn, ssize_t length, cache_lifetime); } -#ifdef INSTRUMENT_DOWNLOADS +#if defined(INSTRUMENT_DOWNLOADS) || defined(RUNNING_DOXYGEN) +/* DOCDOC */ typedef struct request_t { uint64_t bytes; /**< How many bytes have we transferred? */ uint64_t count; /**< How many requests have we made? */ @@ -2317,11 +2476,9 @@ note_client_request(int purpose, int compressed, size_t bytes) case DIR_PURPOSE_UPLOAD_RENDDESC_V2: kind = "dl/ul-rend2"; break; } if (kind) { - key = tor_malloc(256); - tor_snprintf(key, 256, "%s%s", kind, compressed?".z":""); + tor_asprintf(&key, "%s%s", kind, compressed?".z":""); } else { - key = tor_malloc(256); - tor_snprintf(key, 256, "unknown purpose (%d)%s", + tor_asprintf(&key, "unknown purpose (%d)%s", purpose, compressed?".z":""); } note_request(key, bytes); @@ -2360,13 +2517,12 @@ char * directory_dump_request_log(void) { smartlist_t *lines; - char tmp[256]; char *result; strmap_iter_t *iter; ensure_request_map_initialized(); - lines = smartlist_create(); + lines = smartlist_new(); for (iter = strmap_iter_init(request_map); !strmap_iter_done(iter); @@ -2376,9 +2532,8 @@ directory_dump_request_log(void) request_t *r; strmap_iter_get(iter, &key, &val); r = val; - tor_snprintf(tmp, sizeof(tmp), "%s "U64_FORMAT" "U64_FORMAT"\n", + smartlist_add_asprintf(lines, "%s "U64_FORMAT" "U64_FORMAT"\n", key, U64_PRINTF_ARG(r->bytes), U64_PRINTF_ARG(r->count)); - smartlist_add(lines, tor_strdup(tmp)); } smartlist_sort_strings(lines); result = smartlist_join_strings(lines, "", 0, NULL); @@ -2426,7 +2581,7 @@ directory_dump_request_log(void) int client_likes_consensus(networkstatus_t *v, const char *want_url) { - smartlist_t *want_authorities = smartlist_create(); + smartlist_t *want_authorities = smartlist_new(); int need_at_least; int have = 0; @@ -2469,18 +2624,18 @@ client_likes_consensus(networkstatus_t *v, const char *want_url) * Always return 0. */ static int directory_handle_command_get(dir_connection_t *conn, const char *headers, - const char *body, size_t body_len) + const char *req_body, size_t req_body_len) { size_t dlen; char *url, *url_mem, *header; - or_options_t *options = get_options(); + const or_options_t *options = get_options(); time_t if_modified_since = 0; int compressed; size_t url_len; /* We ignore the body of a GET request. */ - (void)body; - (void)body_len; + (void)req_body; + (void)req_body_len; log_debug(LD_DIRSERV,"Received GET command."); @@ -2600,7 +2755,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, if (!strcmpstart(url,"/tor/status/") || !strcmpstart(url, "/tor/status-vote/current/consensus")) { /* v2 or v3 network status fetch. */ - smartlist_t *dir_fps = smartlist_create(); + 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; @@ -2642,7 +2797,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, want_fps = url+strlen(CONSENSUS_URL_PREFIX); } - /* XXXX MICRODESC NM NM should check document of correct flavor */ + /* XXXX023 MICRODESC NM NM should check document of correct flavor */ if (v && want_fps && !client_likes_consensus(v, want_fps)) { write_http_status_line(conn, 404, "Consensus not signed by sufficient " @@ -2699,8 +2854,10 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, { struct in_addr in; + tor_addr_t addr; if (tor_inet_aton((TO_CONN(conn))->address, &in)) { - geoip_note_client_seen(act, ntohl(in.s_addr), time(NULL)); + tor_addr_from_ipv4h(&addr, ntohl(in.s_addr)); + geoip_note_client_seen(act, &addr, time(NULL)); geoip_note_ns_response(act, GEOIP_SUCCESS); /* Note that a request for a network status has started, so that we * can measure the download time later on. */ @@ -2735,8 +2892,8 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, int current; ssize_t body_len = 0; ssize_t estimated_len = 0; - smartlist_t *items = smartlist_create(); - smartlist_t *dir_items = smartlist_create(); + smartlist_t *items = smartlist_new(); + smartlist_t *dir_items = smartlist_new(); int lifetime = 60; /* XXXX023 should actually use vote intervals. */ url += strlen("/tor/status-vote/"); current = !strcmpstart(url, "current/"); @@ -2764,7 +2921,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, smartlist_add(dir_items, (cached_dir_t*)d); } else { const cached_dir_t *d; - smartlist_t *fps = smartlist_create(); + smartlist_t *fps = smartlist_new(); int flags; if (!strcmpstart(url, "d/")) { url += 2; @@ -2828,7 +2985,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, } if (!strcmpstart(url, "/tor/micro/d/")) { - smartlist_t *fps = smartlist_create(); + smartlist_t *fps = smartlist_new(); dir_split_resource_into_fingerprints(url+strlen("/tor/micro/d/"), fps, NULL, @@ -2871,7 +3028,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, int cache_lifetime = 0; int is_extra = !strcmpstart(url,"/tor/extra/"); url += is_extra ? strlen("/tor/extra/") : strlen("/tor/server/"); - conn->fingerprint_stack = smartlist_create(); + conn->fingerprint_stack = smartlist_new(); res = dirserv_get_routerdesc_fingerprints(conn->fingerprint_stack, url, &msg, !connection_dir_is_encrypted(conn), @@ -2932,7 +3089,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, } if (!strcmpstart(url,"/tor/keys/")) { - smartlist_t *certs = smartlist_create(); + smartlist_t *certs = smartlist_new(); ssize_t len = -1; if (!strcmp(url, "/tor/keys/all")) { authority_cert_get_all(certs); @@ -2941,7 +3098,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, if (cert) smartlist_add(certs, cert); } else if (!strcmpstart(url, "/tor/keys/fp/")) { - smartlist_t *fps = smartlist_create(); + smartlist_t *fps = smartlist_new(); dir_split_resource_into_fingerprints(url+strlen("/tor/keys/fp/"), fps, NULL, DSR_HEX|DSR_SORT_UNIQ); @@ -2952,7 +3109,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, }); smartlist_free(fps); } else if (!strcmpstart(url, "/tor/keys/sk/")) { - smartlist_t *fps = smartlist_create(); + smartlist_t *fps = smartlist_new(); dir_split_resource_into_fingerprints(url+strlen("/tor/keys/sk/"), fps, NULL, DSR_HEX|DSR_SORT_UNIQ); @@ -2963,7 +3120,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, }); smartlist_free(fps); } else if (!strcmpstart(url, "/tor/keys/fp-sk/")) { - smartlist_t *fp_sks = smartlist_create(); + smartlist_t *fp_sks = smartlist_new(); dir_split_resource_into_fingerprint_pairs(url+strlen("/tor/keys/fp-sk/"), fp_sks); SMARTLIST_FOREACH(fp_sks, fp_pair_t *, pair, { @@ -3134,8 +3291,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, #if defined(EXPORTMALLINFO) && defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO) #define ADD_MALLINFO_LINE(x) do { \ - tor_snprintf(tmp, sizeof(tmp), "%s %d\n", #x, mi.x); \ - smartlist_add(lines, tor_strdup(tmp)); \ + smartlist_add_asprintf(lines, "%s %d\n", #x, mi.x); \ }while(0); if (!strcmp(url,"/tor/mallinfo.txt") && @@ -3144,11 +3300,10 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, size_t len; struct mallinfo mi; smartlist_t *lines; - char tmp[256]; memset(&mi, 0, sizeof(mi)); mi = mallinfo(); - lines = smartlist_create(); + lines = smartlist_new(); ADD_MALLINFO_LINE(arena) ADD_MALLINFO_LINE(ordblks) @@ -3191,7 +3346,7 @@ directory_handle_command_post(dir_connection_t *conn, const char *headers, const char *body, size_t body_len) { char *url = NULL; - or_options_t *options = get_options(); + const or_options_t *options = get_options(); log_debug(LD_DIRSERV,"Received POST command."); @@ -3335,7 +3490,7 @@ directory_handle_command(dir_connection_t *conn) tor_assert(conn); tor_assert(conn->_base.type == CONN_TYPE_DIR); - switch (fetch_from_buf_http(conn->_base.inbuf, + switch (connection_fetch_from_buf_http(TO_CONN(conn), &headers, MAX_HEADERS_SIZE, &body, &body_len, MAX_DIR_UL_SIZE, 0)) { case -1: /* overflow */ @@ -3388,14 +3543,27 @@ connection_dir_finished_flushing(dir_connection_t *conn) DIRREQ_DIRECT, DIRREQ_FLUSHING_DIR_CONN_FINISHED); switch (conn->_base.state) { + case DIR_CONN_STATE_CONNECTING: case DIR_CONN_STATE_CLIENT_SENDING: log_debug(LD_DIR,"client finished sending command."); conn->_base.state = DIR_CONN_STATE_CLIENT_READING; - connection_stop_writing(TO_CONN(conn)); return 0; case DIR_CONN_STATE_SERVER_WRITING: - log_debug(LD_DIRSERV,"Finished writing server response. Closing."); - connection_mark_for_close(TO_CONN(conn)); + if (conn->dir_spool_src != DIR_SPOOL_NONE) { +#ifdef USE_BUFFEREVENTS + /* This can happen with paired bufferevents, since a paired connection + * can flush immediately when you write to it, making the subsequent + * check in connection_handle_write_cb() decide that the connection + * is flushed. */ + log_debug(LD_DIRSERV, "Emptied a dirserv buffer, but still spooling."); +#else + log_warn(LD_BUG, "Emptied a dirserv buffer, but it's still spooling!"); + connection_mark_for_close(TO_CONN(conn)); +#endif + } else { + log_debug(LD_DIRSERV, "Finished writing server response. Closing."); + connection_mark_for_close(TO_CONN(conn)); + } return 0; default: log_warn(LD_BUG,"called in unexpected state %d.", @@ -3562,7 +3730,7 @@ download_status_reset(download_status_t *dls) const int *schedule; size_t schedule_len; - find_dl_schedule_and_len(dls, get_options()->DirPort, + find_dl_schedule_and_len(dls, get_options()->DirPort != NULL, &schedule, &schedule_len); dls->n_download_failures = 0; @@ -3621,6 +3789,39 @@ dir_routerdesc_download_failed(smartlist_t *failed, int status_code, * every 10 or 60 seconds (FOO_DESCRIPTOR_RETRY_INTERVAL) in main.c. */ } +/** Called when a connection to download microdescriptors has failed in whole + * or in part. <b>failed</b> is a list of every microdesc digest we didn't + * get. <b>status_code</b> is the http status code we received. Reschedule the + * microdesc downloads as appropriate. */ +static void +dir_microdesc_download_failed(smartlist_t *failed, + int status_code) +{ + networkstatus_t *consensus + = networkstatus_get_latest_consensus_by_flavor(FLAV_MICRODESC); + routerstatus_t *rs; + download_status_t *dls; + time_t now = time(NULL); + int server = directory_fetches_from_authorities(get_options()); + + if (! consensus) + return; + SMARTLIST_FOREACH_BEGIN(failed, const char *, d) { + rs = router_get_mutable_consensus_status_by_descriptor_digest(consensus,d); + if (!rs) + continue; + dls = &rs->dl_status; + if (dls->n_download_failures >= MAX_MICRODESC_DOWNLOAD_FAILURES) + continue; + { + char buf[BASE64_DIGEST256_LEN+1]; + digest256_to_base64(buf, d); + download_status_increment_failure(dls, status_code, buf, + server, now); + } + } SMARTLIST_FOREACH_END(d); +} + /** Helper. Compare two fp_pair_t objects, and return negative, 0, or * positive as appropriate. */ static int @@ -3642,8 +3843,8 @@ int dir_split_resource_into_fingerprint_pairs(const char *res, smartlist_t *pairs_out) { - smartlist_t *pairs_tmp = smartlist_create(); - smartlist_t *pairs_result = smartlist_create(); + smartlist_t *pairs_tmp = smartlist_new(); + smartlist_t *pairs_result = smartlist_new(); smartlist_split_string(pairs_tmp, res, "+", 0, 0); if (smartlist_len(pairs_tmp)) { @@ -3711,7 +3912,7 @@ dir_split_resource_into_fingerprints(const char *resource, HEX_DIGEST256_LEN : HEX_DIGEST_LEN; const int base64_digest_len = digests_are_256 ? BASE64_DIGEST256_LEN : BASE64_DIGEST_LEN; - smartlist_t *fp_tmp = smartlist_create(); + smartlist_t *fp_tmp = smartlist_new(); tor_assert(!(decode_hex && decode_base64)); tor_assert(fp_out); |