aboutsummaryrefslogtreecommitdiff
path: root/src/or/dirserv.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/dirserv.c')
-rw-r--r--src/or/dirserv.c669
1 files changed, 489 insertions, 180 deletions
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index 851fc5cbdd..8b215011f3 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -41,7 +41,7 @@ static time_t the_v2_networkstatus_is_dirty = 1;
static cached_dir_t *the_directory = NULL;
/** For authoritative directories: the current (v1) network status. */
-static cached_dir_t the_runningrouters = { NULL, NULL, 0, 0, 0, -1 };
+static cached_dir_t the_runningrouters;
static void directory_remove_invalid(void);
static cached_dir_t *dirserv_regenerate_directory(void);
@@ -63,13 +63,16 @@ static signed_descriptor_t *get_signed_descriptor_by_fp(const char *fp,
time_t publish_cutoff);
static int dirserv_add_extrainfo(extrainfo_t *ei, const char **msg);
+/************** Measured Bandwidth parsing code ******/
+#define MAX_MEASUREMENT_AGE (3*24*60*60) /* 3 days */
+
/************** Fingerprint handling code ************/
#define FP_NAMED 1 /**< Listed in fingerprint file. */
#define FP_INVALID 2 /**< Believed invalid. */
#define FP_REJECT 4 /**< We will not publish this router. */
#define FP_BADDIR 8 /**< We'll tell clients to avoid using this as a dir. */
-#define FP_BADEXIT 16 /**< We'll tell clients not to use this as an exit. */
+#define FP_BADEXIT 16 /**< We'll tell clients not to use this as an exit. */
#define FP_UNNAMED 32 /**< Another router has this name in fingerprint file. */
/** Encapsulate a nickname and an FP_* status; target of status_by_digest
@@ -99,7 +102,7 @@ authdir_config_new(void)
return list;
}
-/** Add the fingerprint <b>fp</b> for the nickname <b>nickname</b> to
+/** Add the fingerprint <b>fp</b> for <b>nickname</b> to
* the smartlist of fingerprint_entry_t's <b>list</b>. Return 0 if it's
* new, or 1 if we replaced the old value.
*/
@@ -181,8 +184,7 @@ dirserv_add_own_fingerprint(const char *nickname, crypto_pk_env_t *pk)
* file. The file format is line-based, with each non-blank holding one
* nickname, some space, and a fingerprint for that nickname. On success,
* replace the current fingerprint list with the new list and return 0. On
- * failure, leave the current fingerprint list untouched, and
- * return -1. */
+ * failure, leave the current fingerprint list untouched, and return -1. */
int
dirserv_load_fingerprint_file(void)
{
@@ -368,10 +370,10 @@ dirserv_get_status_impl(const char *id_digest, const char *nickname,
strmap_size(fingerprint_list->fp_by_name),
digestmap_size(fingerprint_list->status_by_digest));
- /* 0.1.1.17-rc was the first version that claimed to be stable, doesn't
- * crash and drop circuits all the time, and is even vaguely compatible with
- * the current network */
- if (platform && !tor_version_as_new_as(platform,"0.1.1.17-rc")) {
+ /* Tor 0.1.2.x is pretty old, but there are a lot of them running still,
+ * and there aren't any critical relay-side vulnerabilities. Once more
+ * of them die off, we should raise this minimum to 0.2.0.x. */
+ if (platform && !tor_version_as_new_as(platform,"0.1.2.14")) {
if (msg)
*msg = "Tor version is far too old to work.";
return FP_REJECT;
@@ -520,7 +522,7 @@ authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg,
/* Okay. Now check whether the fingerprint is recognized. */
uint32_t status = dirserv_router_get_status(ri, msg);
time_t now;
- int severity = complain ? LOG_NOTICE : LOG_INFO;
+ int severity = (complain && ri->contact_info) ? LOG_NOTICE : LOG_INFO;
tor_assert(msg);
if (status & FP_REJECT)
return -1; /* msg is already set. */
@@ -835,46 +837,6 @@ directory_remove_invalid(void)
routerlist_assert_ok(rl);
}
-/** Write a list of unregistered descriptors into a newly allocated
- * string and return it. Used by dirserv operators to keep track of
- * fast nodes that haven't registered.
- */
-int
-getinfo_helper_dirserv_unregistered(control_connection_t *control_conn,
- const char *question, char **answer_out)
-{
- smartlist_t *answerlist;
- char buf[1024];
- char *answer;
- int min_bw = atoi(question);
- routerlist_t *rl = router_get_routerlist();
-
- (void) control_conn;
-
- if (strcmpstart(question, "unregistered-servers-"))
- return 0;
- question += strlen("unregistered-servers-");
-
- answerlist = smartlist_create();
- SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ent, {
- uint32_t r = dirserv_router_get_status(ent, NULL);
- if (router_get_advertised_bandwidth(ent) >= (size_t)min_bw &&
- !(r & FP_NAMED)) {
- /* then log this one */
- tor_snprintf(buf, sizeof(buf),
- "%s: BW %d on '%s'.",
- ent->nickname, router_get_advertised_bandwidth(ent),
- ent->platform ? ent->platform : "");
- smartlist_add(answerlist, tor_strdup(buf));
- }
- });
- answer = smartlist_join_strings(answerlist, "\r\n", 0, NULL);
- SMARTLIST_FOREACH(answerlist, char *, cp, tor_free(cp));
- smartlist_free(answerlist);
- *answer_out = answer;
- return 0;
-}
-
/** Mark the directory as <b>dirty</b> -- when we're next asked for a
* directory, we will rebuild it instead of reusing the most recently
* generated one.
@@ -933,6 +895,13 @@ list_single_server_status(routerinfo_t *desc, int is_live)
return tor_strdup(buf);
}
+static INLINE int
+running_long_enough_to_decide_unreachable(void)
+{
+ return time_of_process_start
+ + get_options()->TestingAuthDirTimeToLearnReachability < approx_time();
+}
+
/** Each server needs to have passed a reachability test no more
* than this number of seconds ago, or he is listed as down in
* the directory. */
@@ -944,6 +913,10 @@ list_single_server_status(routerinfo_t *desc, int is_live)
void
dirserv_set_router_is_running(routerinfo_t *router, time_t now)
{
+ /*XXXX022 This function is a mess. Separate out the part that calculates
+ whether it's reachable and the part that tells rephist that the router was
+ unreachable.
+ */
int answer;
if (router_is_me(router) && !we_are_hibernating())
@@ -952,7 +925,7 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now)
answer = get_options()->AssumeReachable ||
now < router->last_reachable + REACHABLE_TIMEOUT;
- if (!answer) {
+ if (!answer && running_long_enough_to_decide_unreachable()) {
/* not considered reachable. tell rephist. */
rep_hist_note_router_unreachable(router->cache_info.identity_digest, now);
}
@@ -965,7 +938,6 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now)
* *<b>router_status_out</b>. Return 0 on success, -1 on failure.
*
* If for_controller is true, include the routers with very old descriptors.
- * If for_controller is &gt;1, use the verbose nickname format.
*/
int
list_server_status_v1(smartlist_t *routers, char **router_status_out,
@@ -985,23 +957,22 @@ list_server_status_v1(smartlist_t *routers, char **router_status_out,
rs_entries = smartlist_create();
- SMARTLIST_FOREACH(routers, routerinfo_t *, ri,
- {
+ SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) {
if (authdir) {
/* Update router status in routerinfo_t. */
dirserv_set_router_is_running(ri, now);
}
- if (for_controller == 1 || ri->cache_info.published_on >= cutoff)
- smartlist_add(rs_entries, list_single_server_status(ri, ri->is_running));
- else if (for_controller > 2) {
+ if (for_controller) {
char name_buf[MAX_VERBOSE_NICKNAME_LEN+2];
char *cp = name_buf;
if (!ri->is_running)
*cp++ = '!';
router_get_verbose_nickname(cp, ri);
smartlist_add(rs_entries, tor_strdup(name_buf));
+ } else if (ri->cache_info.published_on >= cutoff) {
+ smartlist_add(rs_entries, list_single_server_status(ri, ri->is_running));
}
- });
+ } SMARTLIST_FOREACH_END(ri);
*router_status_out = smartlist_join_strings(rs_entries, " ", 0, NULL);
@@ -1119,7 +1090,8 @@ dirserv_dump_directory_to_string(char **dir_out,
return -1;
}
note_crypto_pk_op(SIGN_DIR);
- if (router_append_dirobj_signature(buf,buf_len,digest,private_key)<0) {
+ if (router_append_dirobj_signature(buf,buf_len,digest,DIGEST_LEN,
+ private_key)<0) {
tor_free(buf);
return -1;
}
@@ -1150,12 +1122,12 @@ directory_fetches_from_authorities(or_options_t *options)
return 0;
if (server_mode(options) && router_pick_published_address(options, &addr)<0)
return 1; /* we don't know our IP address; ask an authority. */
- if (options->DirPort == 0)
+ if (options->DirPort == 0 && !options->RefuseUnknownExits)
return 0;
if (!server_mode(options) || !advertised_server_mode())
return 0;
me = router_get_my_routerinfo();
- if (!me || !me->dir_port)
+ if (!me || (!me->dir_port && !options->RefuseUnknownExits))
return 0; /* if dirport not advertised, return 0 too */
return 1;
}
@@ -1195,7 +1167,11 @@ directory_caches_v2_dir_info(or_options_t *options)
int
directory_caches_dir_info(or_options_t *options)
{
- return options->BridgeRelay != 0 || options->DirPort != 0;
+ if (options->BridgeRelay || options->DirPort)
+ return 1;
+ if (!server_mode(options) || !advertised_server_mode())
+ return 0;
+ return options->RefuseUnknownExits;
}
/** Return 1 if we want to allow remote people to ask us directory
@@ -1238,14 +1214,14 @@ directory_too_idle_to_fetch_descriptors(or_options_t *options, time_t now)
static cached_dir_t *cached_directory = NULL;
/** The v1 runningrouters document we'll serve (as a cache or as an authority)
* if requested. */
-static cached_dir_t cached_runningrouters = { NULL, NULL, 0, 0, 0, -1 };
+static cached_dir_t cached_runningrouters;
/** Used for other dirservers' v2 network statuses. Map from hexdigest to
* cached_dir_t. */
static digestmap_t *cached_v2_networkstatus = NULL;
-/** The v3 consensus network status that we're currently serving. */
-static cached_dir_t *cached_v3_networkstatus = NULL;
+/** Map from flavor name to the v3 consensuses that we're currently serving. */
+static strmap_t *cached_consensuses = NULL;
/** Possibly replace the contents of <b>d</b> with the value of
* <b>directory</b> published on <b>when</b>, unless <b>when</b> is older than
@@ -1319,7 +1295,11 @@ clear_cached_dir(cached_dir_t *d)
static void
_free_cached_dir(void *_d)
{
- cached_dir_t *d = (cached_dir_t *)_d;
+ cached_dir_t *d;
+ if (!_d)
+ return;
+
+ d = (cached_dir_t *)_d;
cached_dir_decref(d);
}
@@ -1413,17 +1393,26 @@ dirserv_set_cached_networkstatus_v2(const char *networkstatus,
}
}
-/** Replace the v3 consensus networkstatus that we're serving with
- * <b>networkstatus</b>, published at <b>published</b>. No validation is
- * performed. */
+/** Replace the v3 consensus networkstatus of type <b>flavor_name</b> that
+ * we're serving with <b>networkstatus</b>, published at <b>published</b>. No
+ * validation is performed. */
void
-dirserv_set_cached_networkstatus_v3(const char *networkstatus,
- time_t published)
+dirserv_set_cached_consensus_networkstatus(const char *networkstatus,
+ const char *flavor_name,
+ const digests_t *digests,
+ time_t published)
{
- if (cached_v3_networkstatus)
- cached_dir_decref(cached_v3_networkstatus);
- cached_v3_networkstatus = new_cached_dir(
- tor_strdup(networkstatus), published);
+ cached_dir_t *new_networkstatus;
+ cached_dir_t *old_networkstatus;
+ if (!cached_consensuses)
+ cached_consensuses = strmap_new();
+
+ new_networkstatus = new_cached_dir(tor_strdup(networkstatus), published);
+ memcpy(&new_networkstatus->digests, digests, sizeof(digests_t));
+ old_networkstatus = strmap_set(cached_consensuses, flavor_name,
+ new_networkstatus);
+ if (old_networkstatus)
+ cached_dir_decref(old_networkstatus);
}
/** Remove any v2 networkstatus from the directory cache that was published
@@ -1577,7 +1566,8 @@ generate_runningrouters(void)
goto err;
}
note_crypto_pk_op(SIGN_DIR);
- if (router_append_dirobj_signature(s, len, digest, private_key)<0)
+ if (router_append_dirobj_signature(s, len, digest, DIGEST_LEN,
+ private_key)<0)
goto err;
set_cached_dir(&the_runningrouters, s, time(NULL));
@@ -1605,9 +1595,9 @@ dirserv_get_runningrouters(void)
/** Return the latest downloaded consensus networkstatus in encoded, signed,
* optionally compressed format, suitable for sending to clients. */
cached_dir_t *
-dirserv_get_consensus(void)
+dirserv_get_consensus(const char *flavor_name)
{
- return cached_v3_networkstatus;
+ return strmap_get(cached_consensuses, flavor_name);
}
/** For authoritative directories: the current (v2) network status. */
@@ -1790,7 +1780,8 @@ dirserv_compute_performance_thresholds(routerlist_t *rl)
if (router_is_active(ri, now)) {
const char *id = ri->cache_info.identity_digest;
uint32_t bw;
- ri->is_exit = exit_policy_is_general_exit(ri->exit_policy);
+ ri->is_exit = (!router_exit_policy_rejects_all(ri) &&
+ exit_policy_is_general_exit(ri->exit_policy));
uptimes[n_active] = (uint32_t)real_uptime(ri, now);
mtbfs[n_active] = rep_hist_get_stability(id, now);
tks [n_active] = rep_hist_get_weighted_time_known(id, now);
@@ -1897,16 +1888,20 @@ version_from_platform(const char *platform)
* which has at least <b>buf_len</b> free characters. Do NUL-termination.
* Use the same format as in network-status documents. If <b>version</b> is
* non-NULL, add a "v" line for the platform. Return 0 on success, -1 on
- * failure. If <b>first_line_only</b> is true, don't include any flags
- * or version line.
+ * failure.
+ *
+ * The format argument has three possible values:
+ * NS_V2 - Output an entry suitable for a V2 NS opinion document
+ * NS_V3_CONSENSUS - Output the first portion of a V3 NS consensus entry
+ * NS_V3_CONSENSUS_MICRODESC - Output the first portion of a V3 microdesc
+ * consensus entry.
+ * NS_V3_VOTE - Output a complete V3 NS vote
+ * NS_CONTROL_PORT - Output a NS document for the control port
*/
int
routerstatus_format_entry(char *buf, size_t buf_len,
routerstatus_t *rs, const char *version,
- int first_line_only, int v2_format)
-/* XXX: first_line_only and v2_format should probably be be both
- * replaced by a single purpose parameter.
- */
+ routerstatus_format_type_t format)
{
int r;
struct in_addr in;
@@ -1925,10 +1920,11 @@ routerstatus_format_entry(char *buf, size_t buf_len,
tor_inet_ntoa(&in, ipaddr, sizeof(ipaddr));
r = tor_snprintf(buf, buf_len,
- "r %s %s %s %s %s %d %d\n",
+ "r %s %s %s%s%s %s %d %d\n",
rs->nickname,
identity64,
- digest64,
+ (format==NS_V3_CONSENSUS_MICRODESC)?"":digest64,
+ (format==NS_V3_CONSENSUS_MICRODESC)?"":" ",
published,
ipaddr,
(int)rs->or_port,
@@ -1937,7 +1933,12 @@ routerstatus_format_entry(char *buf, size_t buf_len,
log_warn(LD_BUG, "Not enough space in buffer.");
return -1;
}
- if (first_line_only)
+
+ /* TODO: Maybe we want to pass in what we need to build the rest of
+ * this here, instead of in the caller. Then we could use the
+ * networkstatus_type_t values, with an additional control port value
+ * added -MP */
+ if (format == NS_V3_CONSENSUS || format == NS_V3_CONSENSUS_MICRODESC)
return 0;
cp = buf + strlen(buf);
@@ -1974,62 +1975,87 @@ routerstatus_format_entry(char *buf, size_t buf_len,
cp += strlen(cp);
}
- if (!v2_format) {
+ if (format != NS_V2) {
routerinfo_t* desc = router_get_by_digest(rs->identity_digest);
+ uint32_t bw;
+
+ if (format != NS_CONTROL_PORT) {
+ /* Blow up more or less nicely if we didn't get anything or not the
+ * thing we expected.
+ */
+ if (!desc) {
+ char id[HEX_DIGEST_LEN+1];
+ char dd[HEX_DIGEST_LEN+1];
+
+ base16_encode(id, sizeof(id), rs->identity_digest, DIGEST_LEN);
+ base16_encode(dd, sizeof(dd), rs->descriptor_digest, DIGEST_LEN);
+ log_warn(LD_BUG, "Cannot get any descriptor for %s "
+ "(wanted descriptor %s).",
+ id, dd);
+ return -1;
+ };
+
+ /* This assert can fire for the control port, because
+ * it can request NS documents before all descriptors
+ * have been fetched. */
+ if (memcmp(desc->cache_info.signed_descriptor_digest,
+ rs->descriptor_digest,
+ DIGEST_LEN)) {
+ char rl_d[HEX_DIGEST_LEN+1];
+ char rs_d[HEX_DIGEST_LEN+1];
+ char id[HEX_DIGEST_LEN+1];
+
+ base16_encode(rl_d, sizeof(rl_d),
+ desc->cache_info.signed_descriptor_digest, DIGEST_LEN);
+ base16_encode(rs_d, sizeof(rs_d), rs->descriptor_digest, DIGEST_LEN);
+ base16_encode(id, sizeof(id), rs->identity_digest, DIGEST_LEN);
+ log_err(LD_BUG, "descriptor digest in routerlist does not match "
+ "the one in routerstatus: %s vs %s "
+ "(router %s)\n",
+ rl_d, rs_d, id);
+
+ tor_assert(!memcmp(desc->cache_info.signed_descriptor_digest,
+ rs->descriptor_digest,
+ DIGEST_LEN));
+ };
+ }
- /* Blow up more or less nicely if we didn't get anything or not the
- * thing we expected.
- */
- if (!desc) {
- char id[HEX_DIGEST_LEN+1];
- char dd[HEX_DIGEST_LEN+1];
-
- base16_encode(id, sizeof(id), rs->identity_digest, DIGEST_LEN);
- base16_encode(dd, sizeof(dd), rs->descriptor_digest, DIGEST_LEN);
- log_warn(LD_BUG, "Cannot get any descriptor for %s "
- "(wanted descriptor %s).",
- id, dd);
- return -1;
- };
- if (memcmp(desc->cache_info.signed_descriptor_digest,
- rs->descriptor_digest,
- DIGEST_LEN)) {
- char rl_d[HEX_DIGEST_LEN+1];
- char rs_d[HEX_DIGEST_LEN+1];
- char id[HEX_DIGEST_LEN+1];
-
- base16_encode(rl_d, sizeof(rl_d),
- desc->cache_info.signed_descriptor_digest, DIGEST_LEN);
- base16_encode(rs_d, sizeof(rs_d), rs->descriptor_digest, DIGEST_LEN);
- base16_encode(id, sizeof(id), rs->identity_digest, DIGEST_LEN);
- log_err(LD_BUG, "descriptor digest in routerlist does not match "
- "the one in routerstatus: %s vs %s "
- "(router %s)\n",
- rl_d, rs_d, id);
-
- tor_assert(!memcmp(desc->cache_info.signed_descriptor_digest,
- rs->descriptor_digest,
- DIGEST_LEN));
- };
-
+ if (format == NS_CONTROL_PORT && rs->has_bandwidth) {
+ bw = rs->bandwidth;
+ } else {
+ tor_assert(desc);
+ bw = router_get_advertised_bandwidth_capped(desc) / 1000;
+ }
r = tor_snprintf(cp, buf_len - (cp-buf),
- "w Bandwidth=%d\n",
- router_get_advertised_bandwidth_capped(desc) / 1024);
+ "w Bandwidth=%d\n", bw);
+
if (r<0) {
log_warn(LD_BUG, "Not enough space in buffer.");
return -1;
}
cp += strlen(cp);
+ if (format == NS_V3_VOTE && rs->has_measured_bw) {
+ *--cp = '\0'; /* Kill "\n" */
+ r = tor_snprintf(cp, buf_len - (cp-buf),
+ " Measured=%d\n", rs->measured_bw);
+ if (r<0) {
+ log_warn(LD_BUG, "Not enough space in buffer for weight line.");
+ return -1;
+ }
+ cp += strlen(cp);
+ }
- summary = policy_summarize(desc->exit_policy);
- r = tor_snprintf(cp, buf_len - (cp-buf), "p %s\n", summary);
- if (r<0) {
- log_warn(LD_BUG, "Not enough space in buffer.");
+ if (desc) {
+ summary = policy_summarize(desc->exit_policy);
+ r = tor_snprintf(cp, buf_len - (cp-buf), "p %s\n", summary);
+ if (r<0) {
+ log_warn(LD_BUG, "Not enough space in buffer.");
+ tor_free(summary);
+ return -1;
+ }
+ cp += strlen(cp);
tor_free(summary);
- return -1;
}
- cp += strlen(cp);
- tor_free(summary);
}
return 0;
@@ -2232,6 +2258,177 @@ router_clear_status_flags(routerinfo_t *router)
router->is_bad_exit = router->is_bad_directory = 0;
}
+/**
+ * Helper function to parse out a line in the measured bandwidth file
+ * into a measured_bw_line_t output structure. Returns -1 on failure
+ * or 0 on success.
+ */
+int
+measured_bw_line_parse(measured_bw_line_t *out, const char *orig_line)
+{
+ char *line = tor_strdup(orig_line);
+ char *cp = line;
+ int got_bw = 0;
+ int got_node_id = 0;
+ char *strtok_state; /* lame sauce d'jour */
+ cp = tor_strtok_r(cp, " \t", &strtok_state);
+
+ if (!cp) {
+ log_warn(LD_DIRSERV, "Invalid line in bandwidth file: %s",
+ escaped(orig_line));
+ tor_free(line);
+ return -1;
+ }
+
+ if (orig_line[strlen(orig_line)-1] != '\n') {
+ log_warn(LD_DIRSERV, "Incomplete line in bandwidth file: %s",
+ escaped(orig_line));
+ tor_free(line);
+ return -1;
+ }
+
+ do {
+ if (strcmpstart(cp, "bw=") == 0) {
+ int parse_ok = 0;
+ char *endptr;
+ if (got_bw) {
+ log_warn(LD_DIRSERV, "Double bw= in bandwidth file line: %s",
+ escaped(orig_line));
+ tor_free(line);
+ return -1;
+ }
+ cp+=strlen("bw=");
+
+ out->bw = tor_parse_long(cp, 0, 0, LONG_MAX, &parse_ok, &endptr);
+ if (!parse_ok || (*endptr && !TOR_ISSPACE(*endptr))) {
+ log_warn(LD_DIRSERV, "Invalid bandwidth in bandwidth file line: %s",
+ escaped(orig_line));
+ tor_free(line);
+ return -1;
+ }
+ got_bw=1;
+ } else if (strcmpstart(cp, "node_id=$") == 0) {
+ if (got_node_id) {
+ log_warn(LD_DIRSERV, "Double node_id= in bandwidth file line: %s",
+ escaped(orig_line));
+ tor_free(line);
+ return -1;
+ }
+ cp+=strlen("node_id=$");
+
+ if (strlen(cp) != HEX_DIGEST_LEN ||
+ base16_decode(out->node_id, DIGEST_LEN, cp, HEX_DIGEST_LEN)) {
+ log_warn(LD_DIRSERV, "Invalid node_id in bandwidth file line: %s",
+ escaped(orig_line));
+ tor_free(line);
+ return -1;
+ }
+ strncpy(out->node_hex, cp, sizeof(out->node_hex));
+ got_node_id=1;
+ }
+ } while ((cp = tor_strtok_r(NULL, " \t", &strtok_state)));
+
+ if (got_bw && got_node_id) {
+ tor_free(line);
+ return 0;
+ } else {
+ log_warn(LD_DIRSERV, "Incomplete line in bandwidth file: %s",
+ escaped(orig_line));
+ tor_free(line);
+ return -1;
+ }
+}
+
+/**
+ * Helper function to apply a parsed measurement line to a list
+ * of bandwidth statuses. Returns true if a line is found,
+ * false otherwise.
+ */
+int
+measured_bw_line_apply(measured_bw_line_t *parsed_line,
+ smartlist_t *routerstatuses)
+{
+ routerstatus_t *rs = NULL;
+ if (!routerstatuses)
+ return 0;
+
+ rs = smartlist_bsearch(routerstatuses, parsed_line->node_id,
+ compare_digest_to_routerstatus_entry);
+
+ if (rs) {
+ rs->has_measured_bw = 1;
+ rs->measured_bw = (uint32_t)parsed_line->bw;
+ } else {
+ log_info(LD_DIRSERV, "Node ID %s not found in routerstatus list",
+ parsed_line->node_hex);
+ }
+
+ return rs != NULL;
+}
+
+/**
+ * Read the measured bandwidth file and apply it to the list of
+ * routerstatuses. Returns -1 on error, 0 otherwise.
+ */
+int
+dirserv_read_measured_bandwidths(const char *from_file,
+ smartlist_t *routerstatuses)
+{
+ char line[256];
+ FILE *fp = fopen(from_file, "r");
+ int applied_lines = 0;
+ time_t file_time;
+ int ok;
+ if (fp == NULL) {
+ log_warn(LD_CONFIG, "Can't open bandwidth file at configured location: %s",
+ from_file);
+ return -1;
+ }
+
+ if (!fgets(line, sizeof(line), fp)
+ || !strlen(line) || line[strlen(line)-1] != '\n') {
+ log_warn(LD_DIRSERV, "Long or truncated time in bandwidth file: %s",
+ escaped(line));
+ fclose(fp);
+ return -1;
+ }
+
+ line[strlen(line)-1] = '\0';
+ file_time = tor_parse_ulong(line, 10, 0, ULONG_MAX, &ok, NULL);
+ if (!ok) {
+ log_warn(LD_DIRSERV, "Non-integer time in bandwidth file: %s",
+ escaped(line));
+ fclose(fp);
+ return -1;
+ }
+
+ if ((time(NULL) - file_time) > MAX_MEASUREMENT_AGE) {
+ log_warn(LD_DIRSERV, "Bandwidth measurement file stale. Age: %u",
+ (unsigned)(time(NULL) - file_time));
+ fclose(fp);
+ return -1;
+ }
+
+ if (routerstatuses)
+ smartlist_sort(routerstatuses, compare_routerstatus_entries);
+
+ while (!feof(fp)) {
+ measured_bw_line_t parsed_line;
+ if (fgets(line, sizeof(line), fp) && strlen(line)) {
+ if (measured_bw_line_parse(&parsed_line, line) != -1) {
+ if (measured_bw_line_apply(&parsed_line, routerstatuses) > 0)
+ applied_lines++;
+ }
+ }
+ }
+
+ fclose(fp);
+ log_info(LD_DIRSERV,
+ "Bandwidth measurement file successfully read. "
+ "Applied %d measurements.", applied_lines);
+ return 0;
+}
+
/** Return a new networkstatus_t* containing our current opinion. (For v3
* authorities) */
networkstatus_t *
@@ -2256,15 +2453,12 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_env_t *private_key,
networkstatus_voter_info_t *voter = NULL;
vote_timing_t timing;
digestmap_t *omit_as_sybil = NULL;
- int vote_on_reachability = 1;
+ const int vote_on_reachability = running_long_enough_to_decide_unreachable();
+ smartlist_t *microdescriptors = NULL;
tor_assert(private_key);
tor_assert(cert);
- if (now - time_of_process_start <
- options->TestingAuthDirTimeToLearnReachability)
- vote_on_reachability = 0;
-
if (resolve_my_address(LOG_WARN, options, &addr, &hostname)<0) {
log_warn(LD_NET, "Couldn't resolve my hostname");
return NULL;
@@ -2309,11 +2503,13 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_env_t *private_key,
omit_as_sybil = get_possible_sybil_list(routers);
routerstatuses = smartlist_create();
+ microdescriptors = smartlist_create();
- SMARTLIST_FOREACH(routers, routerinfo_t *, ri, {
+ SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) {
if (ri->cache_info.published_on >= cutoff) {
routerstatus_t *rs;
vote_routerstatus_t *vrs;
+ microdesc_t *md;
vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
rs = &vrs->status;
@@ -2328,12 +2524,39 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_env_t *private_key,
rs->is_running = 0;
vrs->version = version_from_platform(ri->platform);
+ md = dirvote_create_microdescriptor(ri);
+ if (md) {
+ char buf[128];
+ vote_microdesc_hash_t *h;
+ dirvote_format_microdesc_vote_line(buf, sizeof(buf), md);
+ h = tor_malloc(sizeof(vote_microdesc_hash_t));
+ h->microdesc_hash_line = tor_strdup(buf);
+ h->next = NULL;
+ vrs->microdesc = h;
+ md->last_listed = now;
+ smartlist_add(microdescriptors, md);
+ }
+
smartlist_add(routerstatuses, vrs);
}
- });
+ } SMARTLIST_FOREACH_END(ri);
+
+ {
+ smartlist_t *added =
+ microdescs_add_list_to_cache(get_microdesc_cache(),
+ microdescriptors, SAVED_NOWHERE, 0);
+ smartlist_free(added);
+ smartlist_free(microdescriptors);
+ }
+
smartlist_free(routers);
digestmap_free(omit_as_sybil, NULL);
+ if (options->V3BandwidthsFile) {
+ dirserv_read_measured_bandwidths(options->V3BandwidthsFile,
+ routerstatuses);
+ }
+
v3_out = tor_malloc_zero(sizeof(networkstatus_t));
v3_out->type = NS_TYPE_VOTE;
@@ -2383,15 +2606,22 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_env_t *private_key,
}
smartlist_sort_strings(v3_out->known_flags);
+ if (options->ConsensusParams) {
+ v3_out->net_params = smartlist_create();
+ smartlist_split_string(v3_out->net_params,
+ options->ConsensusParams, NULL, 0, 0);
+ smartlist_sort_strings(v3_out->net_params);
+ }
+
voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
voter->nickname = tor_strdup(options->Nickname);
memcpy(voter->identity_digest, identity_digest, DIGEST_LEN);
+ voter->sigs = smartlist_create();
voter->address = hostname;
voter->addr = addr;
voter->dir_port = options->DirPort;
voter->or_port = options->ORPort;
voter->contact = tor_strdup(contact);
- memcpy(voter->signing_key_digest, signing_key_digest, DIGEST_LEN);
if (options->V3AuthUseLegacyKey) {
authority_cert_t *c = get_my_v3_legacy_cert();
if (c) {
@@ -2537,7 +2767,7 @@ generate_v2_networkstatus_opinion(void)
if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest))
clear_status_flags_on_sybil(&rs);
- if (routerstatus_format_entry(outp, endp-outp, &rs, version, 0, 1)) {
+ if (routerstatus_format_entry(outp, endp-outp, &rs, version, NS_V2)) {
log_warn(LD_BUG, "Unable to print router status.");
tor_free(version);
goto done;
@@ -2559,7 +2789,8 @@ generate_v2_networkstatus_opinion(void)
outp += strlen(outp);
note_crypto_pk_op(SIGN_DIR);
- if (router_append_dirobj_signature(outp,endp-outp,digest,private_key)<0) {
+ if (router_append_dirobj_signature(outp,endp-outp,digest,DIGEST_LEN,
+ private_key)<0) {
log_warn(LD_BUG, "Unable to sign router status.");
goto done;
}
@@ -2591,10 +2822,8 @@ generate_v2_networkstatus_opinion(void)
tor_free(status);
tor_free(hostname);
tor_free(identity_pkey);
- if (routers)
- smartlist_free(routers);
- if (omit_as_sybil)
- digestmap_free(omit_as_sybil, NULL);
+ smartlist_free(routers);
+ digestmap_free(omit_as_sybil, NULL);
return r;
}
@@ -2642,7 +2871,8 @@ dirserv_get_networkstatus_v2_fingerprints(smartlist_t *result,
log_info(LD_DIRSERV,
"Client requested 'all' network status objects; we have none.");
} else if (!strcmpstart(key, "fp/")) {
- dir_split_resource_into_fingerprints(key+3, result, NULL, 1, 1);
+ dir_split_resource_into_fingerprints(key+3, result, NULL,
+ DSR_HEX|DSR_SORT_UNIQ);
}
}
@@ -2707,10 +2937,12 @@ dirserv_get_routerdesc_fingerprints(smartlist_t *fps_out, const char *key,
} else if (!strcmpstart(key, "d/")) {
by_id = 0;
key += strlen("d/");
- dir_split_resource_into_fingerprints(key, fps_out, NULL, 1, 1);
+ dir_split_resource_into_fingerprints(key, fps_out, NULL,
+ DSR_HEX|DSR_SORT_UNIQ);
} else if (!strcmpstart(key, "fp/")) {
key += strlen("fp/");
- dir_split_resource_into_fingerprints(key, fps_out, NULL, 1, 1);
+ dir_split_resource_into_fingerprints(key, fps_out, NULL,
+ DSR_HEX|DSR_SORT_UNIQ);
} else {
*msg = "Key not recognized";
return -1;
@@ -2775,7 +3007,8 @@ dirserv_get_routerdescs(smartlist_t *descs_out, const char *key,
} else if (!strcmpstart(key, "/tor/server/d/")) {
smartlist_t *digests = smartlist_create();
key += strlen("/tor/server/d/");
- dir_split_resource_into_fingerprints(key, digests, NULL, 1, 1);
+ dir_split_resource_into_fingerprints(key, digests, NULL,
+ DSR_HEX|DSR_SORT_UNIQ);
SMARTLIST_FOREACH(digests, const char *, d,
{
signed_descriptor_t *sd = router_get_by_descriptor_digest(d);
@@ -2788,7 +3021,8 @@ dirserv_get_routerdescs(smartlist_t *descs_out, const char *key,
smartlist_t *digests = smartlist_create();
time_t cutoff = time(NULL) - ROUTER_MAX_AGE_TO_PUBLISH;
key += strlen("/tor/server/fp/");
- dir_split_resource_into_fingerprints(key, digests, NULL, 1, 1);
+ dir_split_resource_into_fingerprints(key, digests, NULL,
+ DSR_HEX|DSR_SORT_UNIQ);
SMARTLIST_FOREACH(digests, const char *, d,
{
if (router_digest_is_me(d)) {
@@ -2857,8 +3091,24 @@ dirserv_orconn_tls_done(const char *address,
* skip testing. */
}
-/** Auth dir server only: if <b>try_all</b> is 1, launch connections to
- * all known routers; else we want to load balance such that we only
+/** Helper function for dirserv_test_reachability(). Start a TLS
+ * connection to <b>router</b>, and annotate it with when we started
+ * the test. */
+void
+dirserv_single_reachability_test(time_t now, routerinfo_t *router)
+{
+ tor_addr_t router_addr;
+ log_debug(LD_OR,"Testing reachability of %s at %s:%u.",
+ router->nickname, router->address, router->or_port);
+ /* Remember when we started trying to determine reachability */
+ if (!router->testing_since)
+ router->testing_since = now;
+ tor_addr_from_ipv4h(&router_addr, router->addr);
+ connection_or_connect(&router_addr, router->or_port,
+ router->cache_info.identity_digest);
+}
+
+/** Auth dir server only: load balance such that we only
* try a few connections per call.
*
* The load balancing is such that if we get called once every ten
@@ -2866,7 +3116,7 @@ dirserv_orconn_tls_done(const char *address,
* bit over 20 minutes).
*/
void
-dirserv_test_reachability(time_t now, int try_all)
+dirserv_test_reachability(time_t now)
{
/* XXX decide what to do here; see or-talk thread "purging old router
* information, revocation." -NM
@@ -2883,38 +3133,33 @@ dirserv_test_reachability(time_t now, int try_all)
SMARTLIST_FOREACH_BEGIN(rl->routers, routerinfo_t *, router) {
const char *id_digest = router->cache_info.identity_digest;
- tor_addr_t router_addr;
if (router_is_me(router))
continue;
if (bridge_auth && router->purpose != ROUTER_PURPOSE_BRIDGE)
continue; /* bridge authorities only test reachability on bridges */
// if (router->cache_info.published_on > cutoff)
// continue;
- if (try_all || (((uint8_t)id_digest[0]) % 128) == ctr) {
- log_debug(LD_OR,"Testing reachability of %s at %s:%u.",
- router->nickname, router->address, router->or_port);
- /* Remember when we started trying to determine reachability */
- if (!router->testing_since)
- router->testing_since = now;
- tor_addr_from_ipv4h(&router_addr, router->addr);
- connection_or_connect(&router_addr, router->or_port, id_digest);
+ if ((((uint8_t)id_digest[0]) % 128) == ctr) {
+ dirserv_single_reachability_test(now, router);
}
} SMARTLIST_FOREACH_END(router);
- if (!try_all) /* increment ctr */
- ctr = (ctr + 1) % 128;
+ ctr = (ctr + 1) % 128; /* increment ctr */
}
-/** Given a fingerprint <b>fp</b> which is either set if we're looking
- * for a v2 status, or zeroes if we're looking for a v3 status, return
- * a pointer to the appropriate cached dir object, or NULL if there isn't
- * one available. */
+/** Given a fingerprint <b>fp</b> which is either set if we're looking for a
+ * v2 status, or zeroes if we're looking for a v3 status, or a NUL-padded
+ * flavor name if we want a flavored v3 status, return a pointer to the
+ * appropriate cached dir object, or NULL if there isn't one available. */
static cached_dir_t *
lookup_cached_dir_by_fp(const char *fp)
{
cached_dir_t *d = NULL;
- if (tor_digest_is_zero(fp) && cached_v3_networkstatus)
- d = cached_v3_networkstatus;
- else if (router_digest_is_me(fp) && the_v2_networkstatus)
+ if (tor_digest_is_zero(fp) && cached_consensuses)
+ d = strmap_get(cached_consensuses, "ns");
+ else if (memchr(fp, '\0', DIGEST_LEN) && cached_consensuses &&
+ (d = strmap_get(cached_consensuses, fp))) {
+ /* this here interface is a nasty hack XXXX022 */;
+ } else if (router_digest_is_me(fp) && the_v2_networkstatus)
d = the_v2_networkstatus;
else if (cached_v2_networkstatus)
d = digestmap_get(cached_v2_networkstatus, fp);
@@ -3000,6 +3245,18 @@ dirserv_have_any_serverdesc(smartlist_t *fps, int spool_src)
return 0;
}
+/** Return true iff any of the 256-bit elements in <b>fps</b> is the digest of
+ * a microdescriptor we have. */
+int
+dirserv_have_any_microdesc(const smartlist_t *fps)
+{
+ microdesc_cache_t *cache = get_microdesc_cache();
+ SMARTLIST_FOREACH(fps, const char *, fp,
+ if (microdesc_cache_lookup_by_digest256(cache, fp))
+ return 1);
+ return 0;
+}
+
/** Return an approximate estimate of the number of bytes that will
* be needed to transmit the server descriptors (if is_serverdescs --
* they can be either d/ or fp/ queries) or networkstatus objects (if
@@ -3031,6 +3288,17 @@ dirserv_estimate_data_size(smartlist_t *fps, int is_serverdescs,
return result;
}
+/** Given a list of microdescriptor hashes, guess how many bytes will be
+ * needed to transmit them, and return the guess. */
+size_t
+dirserv_estimate_microdesc_size(const smartlist_t *fps, int compressed)
+{
+ size_t result = smartlist_len(fps) * microdesc_average_size(NULL);
+ if (compressed)
+ result /= 2;
+ return result;
+}
+
/** When we're spooling data onto our outbuf, add more whenever we dip
* below this threshold. */
#define DIRSERV_BUFFER_MIN 16384
@@ -3094,6 +3362,8 @@ connection_dirserv_add_servers_to_outbuf(dir_connection_t *conn)
#endif
body = signed_descriptor_get_body(sd);
if (conn->zlib_state) {
+ /* XXXX022 This 'last' business should actually happen on the last
+ * routerinfo, not on the last fingerprint. */
int last = ! smartlist_len(conn->fingerprint_stack);
connection_write_to_buf_zlib(body, sd->signed_descriptor_len, conn,
last);
@@ -3117,6 +3387,44 @@ connection_dirserv_add_servers_to_outbuf(dir_connection_t *conn)
return 0;
}
+/** Spooling helper: called when we're sending a bunch of microdescriptors,
+ * and the outbuf has become too empty. Pulls some entries from
+ * fingerprint_stack, and writes the corresponding microdescs onto outbuf. If
+ * we run out of entries, flushes the zlib state and sets the spool source to
+ * NONE. Returns 0 on success, negative on failure.
+ */
+static int
+connection_dirserv_add_microdescs_to_outbuf(dir_connection_t *conn)
+{
+ microdesc_cache_t *cache = get_microdesc_cache();
+ while (smartlist_len(conn->fingerprint_stack) &&
+ buf_datalen(conn->_base.outbuf) < DIRSERV_BUFFER_MIN) {
+ char *fp256 = smartlist_pop_last(conn->fingerprint_stack);
+ microdesc_t *md = microdesc_cache_lookup_by_digest256(cache, fp256);
+ tor_free(fp256);
+ if (!md)
+ continue;
+ if (conn->zlib_state) {
+ /* XXXX022 This 'last' business should actually happen on the last
+ * routerinfo, not on the last fingerprint. */
+ int last = !smartlist_len(conn->fingerprint_stack);
+ connection_write_to_buf_zlib(md->body, md->bodylen, conn, last);
+ if (last) {
+ tor_zlib_free(conn->zlib_state);
+ conn->zlib_state = NULL;
+ }
+ } else {
+ connection_write_to_buf(md->body, md->bodylen, TO_CONN(conn));
+ }
+ }
+ if (!smartlist_len(conn->fingerprint_stack)) {
+ conn->dir_spool_src = DIR_SPOOL_NONE;
+ smartlist_free(conn->fingerprint_stack);
+ conn->fingerprint_stack = NULL;
+ }
+ return 0;
+}
+
/** Spooling helper: Called when we're sending a directory or networkstatus,
* and the outbuf has become too empty. Pulls some bytes from
* <b>conn</b>-\>cached_dir-\>dir_z, uncompresses them if appropriate, and
@@ -3199,8 +3507,7 @@ connection_dirserv_add_networkstatus_bytes_to_outbuf(dir_connection_t *conn)
}
} else {
connection_dirserv_finish_spooling(conn);
- if (conn->fingerprint_stack)
- smartlist_free(conn->fingerprint_stack);
+ smartlist_free(conn->fingerprint_stack);
conn->fingerprint_stack = NULL;
return 0;
}
@@ -3224,6 +3531,8 @@ connection_dirserv_flushed_some(dir_connection_t *conn)
case DIR_SPOOL_SERVER_BY_DIGEST:
case DIR_SPOOL_SERVER_BY_FP:
return connection_dirserv_add_servers_to_outbuf(conn);
+ case DIR_SPOOL_MICRODESC:
+ return connection_dirserv_add_microdescs_to_outbuf(conn);
case DIR_SPOOL_CACHED_DIR:
return connection_dirserv_add_dir_bytes_to_outbuf(conn);
case DIR_SPOOL_NETWORKSTATUS:
@@ -3245,10 +3554,10 @@ dirserv_free_all(void)
cached_dir_decref(the_v2_networkstatus);
cached_dir_decref(cached_directory);
clear_cached_dir(&cached_runningrouters);
- if (cached_v2_networkstatus) {
- digestmap_free(cached_v2_networkstatus, _free_cached_dir);
- cached_v2_networkstatus = NULL;
- }
- cached_dir_decref(cached_v3_networkstatus);
+
+ digestmap_free(cached_v2_networkstatus, _free_cached_dir);
+ cached_v2_networkstatus = NULL;
+ strmap_free(cached_consensuses, _free_cached_dir);
+ cached_consensuses = NULL;
}