|
@@ -1414,6 +1414,10 @@ dirserv_get_runningrouters(const char **rr, int compress)
|
|
|
/** For authoritative directories: the current (v2) network status. */
|
|
|
static cached_dir_t *the_v2_networkstatus = NULL;
|
|
|
|
|
|
+/** For authoritative directories: out most recent vote for the (v3) network
|
|
|
+ * status */
|
|
|
+static cached_dir_t *the_v3_networkstatus_vote = NULL;
|
|
|
+
|
|
|
/** Return true iff our opinion of the routers has been stale for long
|
|
|
* enough that we should generate a new v2 network status doc. */
|
|
|
static int
|
|
@@ -1602,7 +1606,7 @@ routerstatus_format_entry(char *buf, size_t buf_len,
|
|
|
ipaddr,
|
|
|
(int)rs->or_port,
|
|
|
(int)rs->dir_port,
|
|
|
-
|
|
|
+ /* These must stay in alphabetical order. */
|
|
|
f_authority?" Authority":"",
|
|
|
rs->is_bad_exit?" BadExit":"",
|
|
|
rs->is_exit?" Exit":"",
|
|
@@ -1641,11 +1645,21 @@ routerstatus_format_entry(char *buf, size_t buf_len,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/** DOCDOC */
|
|
|
+static int
|
|
|
+_compare_routerinfo_by_id_digest(const void **a, const void **b)
|
|
|
+{
|
|
|
+ routerinfo_t *first = *(routerinfo_t **)a, *second = *(routerinfo_t **)b;
|
|
|
+ return memcmp(first->cache_info.identity_digest,
|
|
|
+ second->cache_info.identity_digest,
|
|
|
+ DIGEST_LEN);
|
|
|
+}
|
|
|
+
|
|
|
/** For v2 authoritative directories only: replace the contents of
|
|
|
* <b>the_v2_networkstatus</b> with a newly generated network status
|
|
|
- * object. */
|
|
|
+ * object. DOCDOC v2*/
|
|
|
static cached_dir_t *
|
|
|
-generate_v2_networkstatus(void)
|
|
|
+generate_networkstatus_opinion(int v2)
|
|
|
{
|
|
|
/** Longest status flag name that we generate. */
|
|
|
#define LONGEST_STATUS_FLAG_NAME_LEN 9
|
|
@@ -1671,7 +1685,7 @@ generate_v2_networkstatus(void)
|
|
|
char digest[DIGEST_LEN];
|
|
|
struct in_addr in;
|
|
|
uint32_t addr;
|
|
|
- crypto_pk_env_t *private_key = get_identity_key();
|
|
|
+ crypto_pk_env_t *private_key;
|
|
|
routerlist_t *rl = router_get_routerlist();
|
|
|
time_t now = time(NULL);
|
|
|
time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH;
|
|
@@ -1680,6 +1694,20 @@ generate_v2_networkstatus(void)
|
|
|
int listbadexits = options->AuthDirListBadExits;
|
|
|
int exits_can_be_guards;
|
|
|
const char *contact;
|
|
|
+ authority_cert_t *cert = NULL;
|
|
|
+ char *version_lines = NULL;
|
|
|
+ smartlist_t *routers = NULL;
|
|
|
+
|
|
|
+ if (v2) {
|
|
|
+ private_key = get_identity_key();
|
|
|
+ } else {
|
|
|
+ private_key = get_my_v3_authority_signing_key();
|
|
|
+ cert = get_my_v3_authority_cert();
|
|
|
+ if (!private_key || !cert) {
|
|
|
+ log_warn(LD_NET, "Didn't find key/certificate to generate v3 vote");
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
if (resolve_my_address(LOG_WARN, options, &addr, &hostname)<0) {
|
|
|
log_warn(LD_NET, "Couldn't resolve my hostname");
|
|
@@ -1699,29 +1727,47 @@ generate_v2_networkstatus(void)
|
|
|
goto done;
|
|
|
}
|
|
|
|
|
|
- if (crypto_pk_get_fingerprint(private_key, fingerprint, 0)<0) {
|
|
|
- log_err(LD_BUG, "Error computing fingerprint");
|
|
|
- goto done;
|
|
|
+ if (v2) {
|
|
|
+ if (crypto_pk_get_fingerprint(private_key, fingerprint, 0)<0) {
|
|
|
+ log_err(LD_BUG, "Error computing fingerprint");
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ base16_encode(fingerprint, sizeof(fingerprint),
|
|
|
+ cert->cache_info.identity_digest, DIGEST_LEN);
|
|
|
}
|
|
|
|
|
|
contact = get_options()->ContactInfo;
|
|
|
if (!contact)
|
|
|
contact = "(none)";
|
|
|
|
|
|
- len = 2048+strlen(client_versions)+strlen(server_versions);
|
|
|
+ if (versioning) {
|
|
|
+ size_t v_len = 32+strlen(client_versions)+strlen(server_versions);
|
|
|
+ version_lines = tor_malloc(v_len);
|
|
|
+ tor_snprintf(version_lines, v_len,
|
|
|
+ "client-versions %s\nserver-versions %s\n",
|
|
|
+ client_versions, server_versions);
|
|
|
+ } else {
|
|
|
+ version_lines = tor_strdup("");
|
|
|
+ }
|
|
|
+
|
|
|
+ len = 4096+strlen(client_versions)+strlen(server_versions);
|
|
|
len += identity_pkey_len*2;
|
|
|
len += (RS_ENTRY_LEN)*smartlist_len(rl->routers);
|
|
|
+ if (!v2) {
|
|
|
+ len += cert->cache_info.signed_descriptor_len;
|
|
|
+ }
|
|
|
|
|
|
status = tor_malloc(len);
|
|
|
- tor_snprintf(status, len,
|
|
|
+ if (v2) {
|
|
|
+ tor_snprintf(status, len,
|
|
|
"network-status-version 2\n"
|
|
|
"dir-source %s %s %d\n"
|
|
|
"fingerprint %s\n"
|
|
|
"contact %s\n"
|
|
|
"published %s\n"
|
|
|
"dir-options%s%s%s\n"
|
|
|
- "%s%s" /* client versions %s */
|
|
|
- "%s%s%s" /* \nserver versions %s \n */
|
|
|
+ "%s" /* client version line, server version line. */
|
|
|
"dir-signing-key\n%s\n",
|
|
|
hostname, ipaddr, (int)options->DirPort,
|
|
|
fingerprint,
|
|
@@ -1730,14 +1776,38 @@ generate_v2_networkstatus(void)
|
|
|
naming ? " Names" : "",
|
|
|
listbadexits ? " BadExits" : "",
|
|
|
versioning ? " Versions" : "",
|
|
|
- versioning ? "client-versions " : "",
|
|
|
- versioning ? client_versions : "",
|
|
|
- versioning ? "\nserver-versions " : "",
|
|
|
- versioning ? server_versions : "",
|
|
|
- versioning ? "\n" : "",
|
|
|
+ version_lines,
|
|
|
identity_pkey);
|
|
|
- outp = status + strlen(status);
|
|
|
- endp = status + len;
|
|
|
+ outp = status + strlen(status);
|
|
|
+ endp = status + len;
|
|
|
+ } else {
|
|
|
+ tor_snprintf(status, len,
|
|
|
+ "network-status-version 3\n"
|
|
|
+ "vote-status vote\n"
|
|
|
+ "published %s\n"
|
|
|
+ "valid-after %s\n"
|
|
|
+ "valid-until %s\n"
|
|
|
+ "%s" /* versions */
|
|
|
+ "known-flags Authority Exit Fast Guard Stable "
|
|
|
+ "Running Valid V2Dir%s%s\n"
|
|
|
+ "dir-source %s %s %s %s %d\n"
|
|
|
+ "contact %s\n",
|
|
|
+ published,
|
|
|
+ published, /* XXXX020 should be valid-after*/
|
|
|
+ published, /* XXXX020 should be valid-until*/
|
|
|
+ version_lines,
|
|
|
+ naming ? " Named" : "",
|
|
|
+ listbadexits ? " BadExit" : "",
|
|
|
+ options->Nickname, fingerprint, options->Address,
|
|
|
+ ipaddr, (int)options->DirPort,
|
|
|
+ contact);
|
|
|
+ outp = status + strlen(status);
|
|
|
+ endp = status + len;
|
|
|
+ tor_assert(outp + cert->cache_info.signed_descriptor_len < endp);
|
|
|
+ memcpy(outp, cert->cache_info.signed_descriptor_body,
|
|
|
+ cert->cache_info.signed_descriptor_len);
|
|
|
+ outp += cert->cache_info.signed_descriptor_len;
|
|
|
+ }
|
|
|
|
|
|
/* precompute this part, since we need it to decide what "stable"
|
|
|
* means. */
|
|
@@ -1751,7 +1821,11 @@ generate_v2_networkstatus(void)
|
|
|
* total_exit_bandwidth is close to total_bandwidth/3. */
|
|
|
exits_can_be_guards = total_exit_bandwidth >= (total_bandwidth / 3);
|
|
|
|
|
|
- SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, {
|
|
|
+ routers = smartlist_create();
|
|
|
+ smartlist_add_all(routers, rl->routers);
|
|
|
+ smartlist_sort(routers, _compare_routerinfo_by_id_digest);
|
|
|
+
|
|
|
+ SMARTLIST_FOREACH(routers, routerinfo_t *, ri, {
|
|
|
if (ri->cache_info.published_on >= cutoff) {
|
|
|
/* Already set by compute_performance_thresholds. */
|
|
|
int f_exit = ri->is_exit;
|
|
@@ -1808,15 +1882,35 @@ generate_v2_networkstatus(void)
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- if (tor_snprintf(outp, endp-outp, "directory-signature %s\n",
|
|
|
- get_options()->Nickname)<0) {
|
|
|
- log_warn(LD_BUG, "Unable to write signature line.");
|
|
|
- goto done;
|
|
|
- }
|
|
|
-
|
|
|
- if (router_get_networkstatus_v2_hash(status, digest)<0) {
|
|
|
- log_warn(LD_BUG, "Unable to hash network status");
|
|
|
- goto done;
|
|
|
+ if (v2) {
|
|
|
+ if (tor_snprintf(outp, endp-outp, "directory-signature %s\n",
|
|
|
+ get_options()->Nickname)<0) {
|
|
|
+ log_warn(LD_BUG, "Unable to write signature line.");
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ if (router_get_networkstatus_v2_hash(status, digest)<0) {
|
|
|
+ log_warn(LD_BUG, "Unable to hash network status");
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ outp += strlen(outp);
|
|
|
+ } else {
|
|
|
+ char hex_digest[HEX_DIGEST_LEN+1];
|
|
|
+ if (tor_snprintf(outp, endp-outp, "directory-signature %s ",
|
|
|
+ fingerprint)<0) {
|
|
|
+ log_warn(LD_BUG, "Unable to start signature line.");
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ if (router_get_networkstatus_v3_hash(status, digest)<0) {
|
|
|
+ log_warn(LD_BUG, "Unable to hash network status vote");
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ base16_encode(hex_digest, sizeof(hex_digest), digest, DIGEST_LEN);
|
|
|
+ outp += strlen(outp);
|
|
|
+ if (tor_snprintf(outp, endp-outp, "%s\n", hex_digest)<0) {
|
|
|
+ log_warn(LD_BUG, "Unable to end signature line.");
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ outp += strlen(outp);
|
|
|
}
|
|
|
|
|
|
note_crypto_pk_op(SIGN_DIR);
|
|
@@ -1825,20 +1919,28 @@ generate_v2_networkstatus(void)
|
|
|
goto done;
|
|
|
}
|
|
|
|
|
|
- if (the_v2_networkstatus)
|
|
|
- cached_dir_decref(the_v2_networkstatus);
|
|
|
- the_v2_networkstatus = new_cached_dir(status, now);
|
|
|
- status = NULL; /* So it doesn't get double-freed. */
|
|
|
- the_v2_networkstatus_is_dirty = 0;
|
|
|
- router_set_networkstatus(the_v2_networkstatus->dir,
|
|
|
- now, NS_GENERATED, NULL);
|
|
|
- r = the_v2_networkstatus;
|
|
|
+ {
|
|
|
+ cached_dir_t **ns_ptr =
|
|
|
+ v2 ? &the_v2_networkstatus : &the_v3_networkstatus_vote;
|
|
|
+ if (*ns_ptr)
|
|
|
+ cached_dir_decref(*ns_ptr);
|
|
|
+ *ns_ptr = new_cached_dir(status, now);
|
|
|
+ status = NULL; /* So it doesn't get double-freed. */
|
|
|
+ if (v2)
|
|
|
+ the_v2_networkstatus_is_dirty = 0;
|
|
|
+ router_set_networkstatus((*ns_ptr)->dir, now, NS_GENERATED, NULL);
|
|
|
+ r = *ns_ptr;
|
|
|
+ }
|
|
|
+
|
|
|
done:
|
|
|
tor_free(client_versions);
|
|
|
tor_free(server_versions);
|
|
|
+ tor_free(version_lines);
|
|
|
tor_free(status);
|
|
|
tor_free(hostname);
|
|
|
tor_free(identity_pkey);
|
|
|
+ if (routers)
|
|
|
+ smartlist_free(routers);
|
|
|
return r;
|
|
|
}
|
|
|
|
|
@@ -1855,7 +1957,7 @@ dirserv_get_networkstatus_v2_fingerprints(smartlist_t *result,
|
|
|
cached_v2_networkstatus = digestmap_new();
|
|
|
|
|
|
if (should_generate_v2_networkstatus())
|
|
|
- generate_v2_networkstatus();
|
|
|
+ generate_networkstatus_opinion(1);
|
|
|
|
|
|
if (!strcmp(key,"authority")) {
|
|
|
if (authdir_mode_v2(get_options())) {
|
|
@@ -1910,7 +2012,7 @@ dirserv_get_networkstatus_v2(smartlist_t *result,
|
|
|
SMARTLIST_FOREACH(fingerprints, const char *, fp,
|
|
|
{
|
|
|
if (router_digest_is_me(fp) && should_generate_v2_networkstatus())
|
|
|
- generate_v2_networkstatus();
|
|
|
+ generate_networkstatus_opinion(1);
|
|
|
cached = digestmap_get(cached_v2_networkstatus, fp);
|
|
|
if (cached) {
|
|
|
smartlist_add(result, cached);
|