Loading doc/spec/dir-spec.txt +17 −0 Original line number Diff line number Diff line Loading @@ -1390,6 +1390,23 @@ $Id$ The most recent v3 consensus should be available at: http://<hostname>/tor/status-vote/current/consensus.z Starting with Tor version 0.2.1.1-alpha is also available at: http://<hostname>/tor/status-vote/current/consensus/<F1>+<F2>+<F3>.z Where F1, F2, etc. are authority identity fingerprints the client trusts. Servers will only return a consensus if more than half of the requested authorities have signed the document, otherwise a 404 error will be sent back. The fingerprints can be shortened to a length of any multiple of two, using only the leftmost part of the encoded fingerprint. Tor uses 3 bytes (6 hex characters) of the fingerprint. Clients SHOULD sort the fingerprints in ascending order. Server MUST accept any order. Clients SHOULD use this format when requesting consensus documents from directory authority servers and from caches running a version of Tor that is known to support this URL format. A concatenated set of all the current key certificates should be available at: http://<hostname>/tor/keys/all.z Loading src/or/circuitbuild.c +1 −0 Original line number Diff line number Diff line Loading @@ -2924,6 +2924,7 @@ launch_direct_bridge_descriptor_fetch(char *address, bridge_info_t *bridge) return; /* it's already on the way */ directory_initiate_command(address, bridge->addr, bridge->port, 0, 0, /* does not matter */ 1, bridge->identity, DIR_PURPOSE_FETCH_SERVERDESC, ROUTER_PURPOSE_BRIDGE, Loading src/or/directory.c +137 −4 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ const char directory_c_id[] = static void directory_send_command(dir_connection_t *conn, int purpose, int direct, const char *resource, const char *payload, size_t payload_len, int supports_conditional_consensus, time_t if_modified_since); static int directory_handle_command(dir_connection_t *conn); static int body_is_plausible(const char *body, size_t body_len, int purpose); Loading @@ -57,6 +58,7 @@ static void dir_routerdesc_download_failed(smartlist_t *failed, int was_extrainfo, int was_descriptor_digests); static void note_request(const char *key, size_t bytes); static int client_likes_consensus(networkstatus_t *v, const char *want_url); /********* START VARIABLES **********/ Loading Loading @@ -338,10 +340,13 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose, /* want to ask a running bridge for which we have a descriptor. */ /* XXX021 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) { directory_initiate_command(ri->address, ri->addr, ri->or_port, 0, 0, /* don't use conditional consensus url */ 1, ri->cache_info.identity_digest, dir_purpose, router_purpose, Loading Loading @@ -463,6 +468,7 @@ directory_initiate_command_routerstatus(routerstatus_t *status, } directory_initiate_command(address, status->addr, status->or_port, status->dir_port, status->version_supports_conditional_consensus, status->version_supports_begindir, status->identity_digest, dir_purpose, router_purpose, Loading Loading @@ -644,6 +650,7 @@ directory_command_should_use_begindir(or_options_t *options, uint32_t addr, void directory_initiate_command(const char *address, uint32_t addr, uint16_t or_port, uint16_t dir_port, int supports_conditional_consensus, int supports_begindir, const char *digest, uint8_t dir_purpose, uint8_t router_purpose, int anonymized_connection, const char *resource, Loading Loading @@ -706,7 +713,9 @@ directory_initiate_command(const char *address, uint32_t addr, case 0: /* queue the command on the outbuf */ directory_send_command(conn, dir_purpose, 1, resource, payload, payload_len, if_modified_since); payload, payload_len, supports_conditional_consensus, if_modified_since); connection_watch_events(TO_CONN(conn), EV_READ | EV_WRITE); /* writable indicates finish, readable indicates broken link, error indicates broken link in windowsland. */ Loading Loading @@ -743,7 +752,9 @@ directory_initiate_command(const char *address, uint32_t addr, conn->_base.state = DIR_CONN_STATE_CLIENT_SENDING; /* queue the command on the outbuf */ directory_send_command(conn, dir_purpose, 0, resource, payload, payload_len, if_modified_since); payload, payload_len, supports_conditional_consensus, if_modified_since); connection_watch_events(TO_CONN(conn), EV_READ | EV_WRITE); connection_start_reading(TO_CONN(linked_conn)); } Loading @@ -762,6 +773,63 @@ connection_dir_is_encrypted(dir_connection_t *conn) return TO_CONN(conn)->linked; } /** Helper for sorting * * sort strings alphabetically */ static int _compare_strs(const void **a, const void **b) { const char *s1 = *a, *s2 = *b; return strcmp(s1, s2); } /** Return the URL we should use for a consensus download. * * 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> */ #define CONDITIONAL_CONSENSUS_FPR_LEN 3 #if (CONDITIONAL_CONSENSUS_FPR_LEN > DIGEST_LEN) #error "conditional consensus fingerprint length is larger than digest length #endif static char * directory_get_consensus_url(int supports_conditional_consensus) { char *url; int len; if (supports_conditional_consensus) { char *authority_id_list; smartlist_t *authority_digets = smartlist_create(); SMARTLIST_FOREACH(router_get_trusted_dir_servers(), trusted_dir_server_t *, ds, { char *hex = tor_malloc(2*CONDITIONAL_CONSENSUS_FPR_LEN+1); base16_encode(hex, 2*CONDITIONAL_CONSENSUS_FPR_LEN+1, ds->digest, CONDITIONAL_CONSENSUS_FPR_LEN); smartlist_add(authority_digets, hex); }); smartlist_sort(authority_digets, _compare_strs); authority_id_list = smartlist_join_strings(authority_digets, "+", 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); SMARTLIST_FOREACH(authority_digets, char *, cp, tor_free(cp)); smartlist_free(authority_digets); tor_free(authority_id_list); } else { url = tor_strdup("/tor/status-vote/current/consensus.z"); } return url; } /** Queue an appropriate HTTP command on conn-\>outbuf. The other args * are as in directory_initiate_command. */ Loading @@ -769,6 +837,7 @@ static void directory_send_command(dir_connection_t *conn, int purpose, int direct, const char *resource, const char *payload, size_t payload_len, int supports_conditional_consensus, time_t if_modified_since) { char proxystring[256]; Loading Loading @@ -851,7 +920,10 @@ directory_send_command(dir_connection_t *conn, tor_assert(!resource); tor_assert(!payload); httpcommand = "GET"; url = tor_strdup("/tor/status-vote/current/consensus.z"); url = directory_get_consensus_url(supports_conditional_consensus); /* XXX021: downgrade/remove once done with conditional consensus fu */ log_notice(LD_DIR, "Downloading consensus from %s using %s", hoststring, url); break; case DIR_PURPOSE_FETCH_CERTIFICATE: tor_assert(resource); Loading Loading @@ -2164,6 +2236,58 @@ directory_dump_request_log(void) } #endif /** Decide whether a client would accept the consensus we have * * Clients can say they only want a consensus if it's signed by more * than half the authorities in a list. They pass this list in * the url as "...consensus/<b>fpr</b>+<b>fpr</b>+<b>fpr</b>". * * <b>fpr<b/> may be an abbreviated fingerprint, i.e. only a left substring * of the full authority identity digest. (Only strings of even length, * i.e. encodings of full bytes, are handled correctly. In the case * of an odd number of hex digits the last one is silently ignored.) * * Returns 1 if more than half of the requested authorities signed the * consensus, 0 otherwise. */ int client_likes_consensus(networkstatus_t *v, const char *want_url) { smartlist_t *want_authorities = smartlist_create(); int need_at_least; int have = 0; dir_split_resource_into_fingerprints(want_url, want_authorities, NULL, 0, 0); need_at_least = smartlist_len(want_authorities)/2+1; SMARTLIST_FOREACH(want_authorities, const char *, d, { char want_digest[DIGEST_LEN]; int want_len = strlen(d)/2; if (want_len > DIGEST_LEN) want_len = DIGEST_LEN; if (base16_decode(want_digest, DIGEST_LEN, d, want_len*2) < 0) { log_warn(LD_DIR,"Failed to decode requested authority digest %s.", d); continue; }; SMARTLIST_FOREACH(v->voters, networkstatus_voter_info_t *, vi, { if (vi->signature && !memcmp(vi->identity_digest, want_digest, want_len)) { have++; break; }; }); /* early exit, if we already have enough */ if (have >= need_at_least) break; }); SMARTLIST_FOREACH(want_authorities, char *, d, tor_free(d)); smartlist_free(want_authorities); return (have >= need_at_least); } /** Helper function: called when a dirserver gets a complete HTTP GET * request. Look for a request for a directory or for a rendezvous * service descriptor. On finding one, write a response into Loading Loading @@ -2291,7 +2415,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, } if (!strcmpstart(url,"/tor/status/") || !strcmp(url, "/tor/status-vote/current/consensus")) { || !strcmpstart(url, "/tor/status-vote/current/consensus")) { /* v2 or v3 network status fetch. */ smartlist_t *dir_fps = smartlist_create(); int is_v3 = !strcmpstart(url, "/tor/status-vote"); Loading @@ -2312,6 +2436,15 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, } else { networkstatus_t *v = networkstatus_get_latest_consensus(); time_t now = time(NULL); #define CONSENSUS_URL_PREFIX "/tor/status-vote/current/consensus/" if (!strcmpstart(url, CONSENSUS_URL_PREFIX) && !client_likes_consensus(v, url + strlen(CONSENSUS_URL_PREFIX))) { write_http_status_line(conn, 404, "Consensus not signed by sufficient " "number of requested authorities"); smartlist_free(dir_fps); goto done; } smartlist_add(dir_fps, tor_memdup("\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0", 20)); request_type = compressed?"v3.z":"v3"; Loading src/or/networkstatus.c +2 −0 Original line number Diff line number Diff line Loading @@ -1280,6 +1280,8 @@ routerstatus_has_changed(const routerstatus_t *a, const routerstatus_t *b) a->version_supports_begindir != b->version_supports_begindir || a->version_supports_extrainfo_upload != b->version_supports_extrainfo_upload || a->version_supports_conditional_consensus != b->version_supports_conditional_consensus || a->version_supports_v3_dir != b->version_supports_v3_dir; } Loading src/or/or.h +4 −0 Original line number Diff line number Diff line Loading @@ -1369,6 +1369,9 @@ typedef struct routerstatus_t { unsigned int version_known:1; /** True iff this router is a version that supports BEGIN_DIR cells. */ unsigned int version_supports_begindir:1; /** True iff this router is a version that supports conditional consensus * downloads (signed by list of authorities). */ unsigned int version_supports_conditional_consensus:1; /** True iff this router is a version that we can post extrainfo docs to. */ unsigned int version_supports_extrainfo_upload:1; /** True iff this router is a version that, if it caches directory info, Loading Loading @@ -3048,6 +3051,7 @@ int connection_dir_finished_connecting(dir_connection_t *conn); void connection_dir_request_failed(dir_connection_t *conn); void directory_initiate_command(const char *address, uint32_t addr, uint16_t or_port, uint16_t dir_port, int supports_conditional_consensus, int supports_begindir, const char *digest, uint8_t dir_purpose, uint8_t router_purpose, int anonymized_connection, Loading Loading
doc/spec/dir-spec.txt +17 −0 Original line number Diff line number Diff line Loading @@ -1390,6 +1390,23 @@ $Id$ The most recent v3 consensus should be available at: http://<hostname>/tor/status-vote/current/consensus.z Starting with Tor version 0.2.1.1-alpha is also available at: http://<hostname>/tor/status-vote/current/consensus/<F1>+<F2>+<F3>.z Where F1, F2, etc. are authority identity fingerprints the client trusts. Servers will only return a consensus if more than half of the requested authorities have signed the document, otherwise a 404 error will be sent back. The fingerprints can be shortened to a length of any multiple of two, using only the leftmost part of the encoded fingerprint. Tor uses 3 bytes (6 hex characters) of the fingerprint. Clients SHOULD sort the fingerprints in ascending order. Server MUST accept any order. Clients SHOULD use this format when requesting consensus documents from directory authority servers and from caches running a version of Tor that is known to support this URL format. A concatenated set of all the current key certificates should be available at: http://<hostname>/tor/keys/all.z Loading
src/or/circuitbuild.c +1 −0 Original line number Diff line number Diff line Loading @@ -2924,6 +2924,7 @@ launch_direct_bridge_descriptor_fetch(char *address, bridge_info_t *bridge) return; /* it's already on the way */ directory_initiate_command(address, bridge->addr, bridge->port, 0, 0, /* does not matter */ 1, bridge->identity, DIR_PURPOSE_FETCH_SERVERDESC, ROUTER_PURPOSE_BRIDGE, Loading
src/or/directory.c +137 −4 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ const char directory_c_id[] = static void directory_send_command(dir_connection_t *conn, int purpose, int direct, const char *resource, const char *payload, size_t payload_len, int supports_conditional_consensus, time_t if_modified_since); static int directory_handle_command(dir_connection_t *conn); static int body_is_plausible(const char *body, size_t body_len, int purpose); Loading @@ -57,6 +58,7 @@ static void dir_routerdesc_download_failed(smartlist_t *failed, int was_extrainfo, int was_descriptor_digests); static void note_request(const char *key, size_t bytes); static int client_likes_consensus(networkstatus_t *v, const char *want_url); /********* START VARIABLES **********/ Loading Loading @@ -338,10 +340,13 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose, /* want to ask a running bridge for which we have a descriptor. */ /* XXX021 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) { directory_initiate_command(ri->address, ri->addr, ri->or_port, 0, 0, /* don't use conditional consensus url */ 1, ri->cache_info.identity_digest, dir_purpose, router_purpose, Loading Loading @@ -463,6 +468,7 @@ directory_initiate_command_routerstatus(routerstatus_t *status, } directory_initiate_command(address, status->addr, status->or_port, status->dir_port, status->version_supports_conditional_consensus, status->version_supports_begindir, status->identity_digest, dir_purpose, router_purpose, Loading Loading @@ -644,6 +650,7 @@ directory_command_should_use_begindir(or_options_t *options, uint32_t addr, void directory_initiate_command(const char *address, uint32_t addr, uint16_t or_port, uint16_t dir_port, int supports_conditional_consensus, int supports_begindir, const char *digest, uint8_t dir_purpose, uint8_t router_purpose, int anonymized_connection, const char *resource, Loading Loading @@ -706,7 +713,9 @@ directory_initiate_command(const char *address, uint32_t addr, case 0: /* queue the command on the outbuf */ directory_send_command(conn, dir_purpose, 1, resource, payload, payload_len, if_modified_since); payload, payload_len, supports_conditional_consensus, if_modified_since); connection_watch_events(TO_CONN(conn), EV_READ | EV_WRITE); /* writable indicates finish, readable indicates broken link, error indicates broken link in windowsland. */ Loading Loading @@ -743,7 +752,9 @@ directory_initiate_command(const char *address, uint32_t addr, conn->_base.state = DIR_CONN_STATE_CLIENT_SENDING; /* queue the command on the outbuf */ directory_send_command(conn, dir_purpose, 0, resource, payload, payload_len, if_modified_since); payload, payload_len, supports_conditional_consensus, if_modified_since); connection_watch_events(TO_CONN(conn), EV_READ | EV_WRITE); connection_start_reading(TO_CONN(linked_conn)); } Loading @@ -762,6 +773,63 @@ connection_dir_is_encrypted(dir_connection_t *conn) return TO_CONN(conn)->linked; } /** Helper for sorting * * sort strings alphabetically */ static int _compare_strs(const void **a, const void **b) { const char *s1 = *a, *s2 = *b; return strcmp(s1, s2); } /** Return the URL we should use for a consensus download. * * 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> */ #define CONDITIONAL_CONSENSUS_FPR_LEN 3 #if (CONDITIONAL_CONSENSUS_FPR_LEN > DIGEST_LEN) #error "conditional consensus fingerprint length is larger than digest length #endif static char * directory_get_consensus_url(int supports_conditional_consensus) { char *url; int len; if (supports_conditional_consensus) { char *authority_id_list; smartlist_t *authority_digets = smartlist_create(); SMARTLIST_FOREACH(router_get_trusted_dir_servers(), trusted_dir_server_t *, ds, { char *hex = tor_malloc(2*CONDITIONAL_CONSENSUS_FPR_LEN+1); base16_encode(hex, 2*CONDITIONAL_CONSENSUS_FPR_LEN+1, ds->digest, CONDITIONAL_CONSENSUS_FPR_LEN); smartlist_add(authority_digets, hex); }); smartlist_sort(authority_digets, _compare_strs); authority_id_list = smartlist_join_strings(authority_digets, "+", 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); SMARTLIST_FOREACH(authority_digets, char *, cp, tor_free(cp)); smartlist_free(authority_digets); tor_free(authority_id_list); } else { url = tor_strdup("/tor/status-vote/current/consensus.z"); } return url; } /** Queue an appropriate HTTP command on conn-\>outbuf. The other args * are as in directory_initiate_command. */ Loading @@ -769,6 +837,7 @@ static void directory_send_command(dir_connection_t *conn, int purpose, int direct, const char *resource, const char *payload, size_t payload_len, int supports_conditional_consensus, time_t if_modified_since) { char proxystring[256]; Loading Loading @@ -851,7 +920,10 @@ directory_send_command(dir_connection_t *conn, tor_assert(!resource); tor_assert(!payload); httpcommand = "GET"; url = tor_strdup("/tor/status-vote/current/consensus.z"); url = directory_get_consensus_url(supports_conditional_consensus); /* XXX021: downgrade/remove once done with conditional consensus fu */ log_notice(LD_DIR, "Downloading consensus from %s using %s", hoststring, url); break; case DIR_PURPOSE_FETCH_CERTIFICATE: tor_assert(resource); Loading Loading @@ -2164,6 +2236,58 @@ directory_dump_request_log(void) } #endif /** Decide whether a client would accept the consensus we have * * Clients can say they only want a consensus if it's signed by more * than half the authorities in a list. They pass this list in * the url as "...consensus/<b>fpr</b>+<b>fpr</b>+<b>fpr</b>". * * <b>fpr<b/> may be an abbreviated fingerprint, i.e. only a left substring * of the full authority identity digest. (Only strings of even length, * i.e. encodings of full bytes, are handled correctly. In the case * of an odd number of hex digits the last one is silently ignored.) * * Returns 1 if more than half of the requested authorities signed the * consensus, 0 otherwise. */ int client_likes_consensus(networkstatus_t *v, const char *want_url) { smartlist_t *want_authorities = smartlist_create(); int need_at_least; int have = 0; dir_split_resource_into_fingerprints(want_url, want_authorities, NULL, 0, 0); need_at_least = smartlist_len(want_authorities)/2+1; SMARTLIST_FOREACH(want_authorities, const char *, d, { char want_digest[DIGEST_LEN]; int want_len = strlen(d)/2; if (want_len > DIGEST_LEN) want_len = DIGEST_LEN; if (base16_decode(want_digest, DIGEST_LEN, d, want_len*2) < 0) { log_warn(LD_DIR,"Failed to decode requested authority digest %s.", d); continue; }; SMARTLIST_FOREACH(v->voters, networkstatus_voter_info_t *, vi, { if (vi->signature && !memcmp(vi->identity_digest, want_digest, want_len)) { have++; break; }; }); /* early exit, if we already have enough */ if (have >= need_at_least) break; }); SMARTLIST_FOREACH(want_authorities, char *, d, tor_free(d)); smartlist_free(want_authorities); return (have >= need_at_least); } /** Helper function: called when a dirserver gets a complete HTTP GET * request. Look for a request for a directory or for a rendezvous * service descriptor. On finding one, write a response into Loading Loading @@ -2291,7 +2415,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, } if (!strcmpstart(url,"/tor/status/") || !strcmp(url, "/tor/status-vote/current/consensus")) { || !strcmpstart(url, "/tor/status-vote/current/consensus")) { /* v2 or v3 network status fetch. */ smartlist_t *dir_fps = smartlist_create(); int is_v3 = !strcmpstart(url, "/tor/status-vote"); Loading @@ -2312,6 +2436,15 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, } else { networkstatus_t *v = networkstatus_get_latest_consensus(); time_t now = time(NULL); #define CONSENSUS_URL_PREFIX "/tor/status-vote/current/consensus/" if (!strcmpstart(url, CONSENSUS_URL_PREFIX) && !client_likes_consensus(v, url + strlen(CONSENSUS_URL_PREFIX))) { write_http_status_line(conn, 404, "Consensus not signed by sufficient " "number of requested authorities"); smartlist_free(dir_fps); goto done; } smartlist_add(dir_fps, tor_memdup("\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0", 20)); request_type = compressed?"v3.z":"v3"; Loading
src/or/networkstatus.c +2 −0 Original line number Diff line number Diff line Loading @@ -1280,6 +1280,8 @@ routerstatus_has_changed(const routerstatus_t *a, const routerstatus_t *b) a->version_supports_begindir != b->version_supports_begindir || a->version_supports_extrainfo_upload != b->version_supports_extrainfo_upload || a->version_supports_conditional_consensus != b->version_supports_conditional_consensus || a->version_supports_v3_dir != b->version_supports_v3_dir; } Loading
src/or/or.h +4 −0 Original line number Diff line number Diff line Loading @@ -1369,6 +1369,9 @@ typedef struct routerstatus_t { unsigned int version_known:1; /** True iff this router is a version that supports BEGIN_DIR cells. */ unsigned int version_supports_begindir:1; /** True iff this router is a version that supports conditional consensus * downloads (signed by list of authorities). */ unsigned int version_supports_conditional_consensus:1; /** True iff this router is a version that we can post extrainfo docs to. */ unsigned int version_supports_extrainfo_upload:1; /** True iff this router is a version that, if it caches directory info, Loading Loading @@ -3048,6 +3051,7 @@ int connection_dir_finished_connecting(dir_connection_t *conn); void connection_dir_request_failed(dir_connection_t *conn); void directory_initiate_command(const char *address, uint32_t addr, uint16_t or_port, uint16_t dir_port, int supports_conditional_consensus, int supports_begindir, const char *digest, uint8_t dir_purpose, uint8_t router_purpose, int anonymized_connection, Loading