|
@@ -2151,6 +2151,319 @@ clear_status_flags_on_sybil(routerstatus_t *rs)
|
|
|
* forget to add it to this clause. */
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ * is <b>guardfraction_percentage</b>. See if we have a vote routerstatus for
|
|
|
+ * this guard in <b>vote_routerstatuses</b>, and if we do, register the
|
|
|
+ * information to it.
|
|
|
+ *
|
|
|
+ * Return 1 if we applied the information and 0 if we couldn't find a
|
|
|
+ * matching guard.
|
|
|
+ *
|
|
|
+ * Requires that <b>vote_routerstatuses</b> be sorted.
|
|
|
+ */
|
|
|
+static int
|
|
|
+guardfraction_line_apply(const char *guard_id,
|
|
|
+ uint32_t guardfraction_percentage,
|
|
|
+ smartlist_t *vote_routerstatuses)
|
|
|
+{
|
|
|
+ vote_routerstatus_t *vrs = NULL;
|
|
|
+
|
|
|
+ tor_assert(vote_routerstatuses);
|
|
|
+
|
|
|
+ vrs = smartlist_bsearch(vote_routerstatuses, guard_id,
|
|
|
+ compare_digest_to_vote_routerstatus_entry);
|
|
|
+
|
|
|
+ if (!vrs) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ vrs->status.has_guardfraction = 1;
|
|
|
+ vrs->status.guardfraction_percentage = guardfraction_percentage;
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * its information to <b>vote_routerstatuses</b>.
|
|
|
+ *
|
|
|
+ * Return:
|
|
|
+ * * 1 if the line was proper and its information got registered.
|
|
|
+ * * 0 if the line was proper but no currently active guard was found
|
|
|
+ * to register the guardfraction information to.
|
|
|
+ * * -1 if the line could not be parsed and set <b>err_msg</b> to a
|
|
|
+ newly allocated string containing the error message.
|
|
|
+ */
|
|
|
+static int
|
|
|
+guardfraction_file_parse_guard_line(const char *guard_line,
|
|
|
+ smartlist_t *vote_routerstatuses,
|
|
|
+ char **err_msg)
|
|
|
+{
|
|
|
+ char guard_id[DIGEST_LEN];
|
|
|
+ uint32_t guardfraction;
|
|
|
+ char *inputs_tmp = NULL;
|
|
|
+ int num_ok = 1;
|
|
|
+
|
|
|
+ smartlist_t *sl = smartlist_new();
|
|
|
+ int retval = -1;
|
|
|
+
|
|
|
+ tor_assert(err_msg);
|
|
|
+
|
|
|
+
|
|
|
+ <hex digest> <guardfraction> <appearances> */
|
|
|
+ smartlist_split_string(sl, guard_line, " ",
|
|
|
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 3);
|
|
|
+ if (smartlist_len(sl) < 3) {
|
|
|
+ tor_asprintf(err_msg, "bad line '%s'", guard_line);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+
|
|
|
+ inputs_tmp = smartlist_get(sl, 0);
|
|
|
+ if (strlen(inputs_tmp) != HEX_DIGEST_LEN ||
|
|
|
+ base16_decode(guard_id, DIGEST_LEN, inputs_tmp, HEX_DIGEST_LEN)) {
|
|
|
+ tor_asprintf(err_msg, "bad digest '%s'", inputs_tmp);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+
|
|
|
+ inputs_tmp = smartlist_get(sl, 1);
|
|
|
+
|
|
|
+ guardfraction =
|
|
|
+ (uint32_t) tor_parse_long(inputs_tmp, 10, 0, 100, &num_ok, NULL);
|
|
|
+ if (!num_ok) {
|
|
|
+ tor_asprintf(err_msg, "wrong percentage '%s'", inputs_tmp);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if (vote_routerstatuses) {
|
|
|
+ retval = guardfraction_line_apply(guard_id, guardfraction,
|
|
|
+ vote_routerstatuses);
|
|
|
+ } else {
|
|
|
+ retval = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ done:
|
|
|
+
|
|
|
+ SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
|
|
|
+ smartlist_free(sl);
|
|
|
+
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * register its information to <b>total_consensuses</b> and
|
|
|
+ * <b>total_days</b>.
|
|
|
+ *
|
|
|
+ * Return 0 if it parsed well. Return -1 if there was an error, and
|
|
|
+ * set <b>err_msg</b> to a newly allocated string containing the
|
|
|
+ * error message.
|
|
|
+ */
|
|
|
+static int
|
|
|
+guardfraction_file_parse_inputs_line(const char *inputs_line,
|
|
|
+ int *total_consensuses,
|
|
|
+ int *total_days,
|
|
|
+ char **err_msg)
|
|
|
+{
|
|
|
+ int retval = -1;
|
|
|
+ char *inputs_tmp = NULL;
|
|
|
+ int num_ok = 1;
|
|
|
+ smartlist_t *sl = smartlist_new();
|
|
|
+
|
|
|
+ tor_assert(err_msg);
|
|
|
+
|
|
|
+
|
|
|
+ * n-inputs <total_consensuses> <total_days>. */
|
|
|
+ smartlist_split_string(sl, inputs_line, " ",
|
|
|
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 3);
|
|
|
+ if (smartlist_len(sl) < 2) {
|
|
|
+ tor_asprintf(err_msg, "incomplete line '%s'", inputs_line);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+
|
|
|
+ inputs_tmp = smartlist_get(sl, 0);
|
|
|
+ *total_consensuses =
|
|
|
+ (int) tor_parse_long(inputs_tmp, 10, 0, INT_MAX, &num_ok, NULL);
|
|
|
+ if (!num_ok) {
|
|
|
+ tor_asprintf(err_msg, "unparseable consensus '%s'", inputs_tmp);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+
|
|
|
+ inputs_tmp = smartlist_get(sl, 1);
|
|
|
+ *total_days =
|
|
|
+ (int) tor_parse_long(inputs_tmp, 10, 0, INT_MAX, &num_ok, NULL);
|
|
|
+ if (!num_ok) {
|
|
|
+ tor_asprintf(err_msg, "unparseable days '%s'", inputs_tmp);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+
|
|
|
+ retval = 0;
|
|
|
+
|
|
|
+ done:
|
|
|
+ SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
|
|
|
+ smartlist_free(sl);
|
|
|
+
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#define MAX_GUARDFRACTION_FILE_AGE (7*24*60*60)
|
|
|
+
|
|
|
+
|
|
|
+#define GUARDFRACTION_DATE_STR "written-at"
|
|
|
+#define GUARDFRACTION_INPUTS "n-inputs"
|
|
|
+#define GUARDFRACTION_GUARD "guard-seen"
|
|
|
+#define GUARDFRACTION_VERSION "guardfraction-file-version"
|
|
|
+
|
|
|
+
|
|
|
+ * guardfraction information to the provided vote routerstatuses.
|
|
|
+ *
|
|
|
+ * This is the rough format of the guardfraction file:
|
|
|
+ *
|
|
|
+ * guardfraction-file-version 1
|
|
|
+ * written-at <date and time>
|
|
|
+ * n-inputs <number of consesuses parsed> <number of days considered>
|
|
|
+ *
|
|
|
+ * guard-seen <fpr 1> <guardfraction percentage> <consensus appearances>
|
|
|
+ * guard-seen <fpr 2> <guardfraction percentage> <consensus appearances>
|
|
|
+ * guard-seen <fpr 3> <guardfraction percentage> <consensus appearances>
|
|
|
+ * guard-seen <fpr 4> <guardfraction percentage> <consensus appearances>
|
|
|
+ * guard-seen <fpr 5> <guardfraction percentage> <consensus appearances>
|
|
|
+ * ...
|
|
|
+ *
|
|
|
+ * Return -1 if the parsing failed and 0 if it went smoothly. Parsing
|
|
|
+ * should tolerate errors in all lines but the written-at header.
|
|
|
+ */
|
|
|
+STATIC int
|
|
|
+dirserv_read_guardfraction_file_from_str(const char *guardfraction_file_str,
|
|
|
+ smartlist_t *vote_routerstatuses)
|
|
|
+{
|
|
|
+ config_line_t *front=NULL, *line;
|
|
|
+ int ret_tmp;
|
|
|
+ int retval = -1;
|
|
|
+ int current_line_n = 0;
|
|
|
+
|
|
|
+
|
|
|
+ int total_consensuses = 0;
|
|
|
+ int total_days = 0;
|
|
|
+
|
|
|
+
|
|
|
+ int guards_read_n = 0;
|
|
|
+ int guards_applied_n = 0;
|
|
|
+
|
|
|
+
|
|
|
+ ret_tmp = config_get_lines(guardfraction_file_str, &front, 0);
|
|
|
+ if (ret_tmp < 0) {
|
|
|
+ log_warn(LD_CONFIG, "Error reading from guardfraction file");
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if (vote_routerstatuses)
|
|
|
+ smartlist_sort(vote_routerstatuses, compare_vote_routerstatus_entries);
|
|
|
+
|
|
|
+ for (line = front; line; line=line->next) {
|
|
|
+ current_line_n++;
|
|
|
+
|
|
|
+ if (!strcmp(line->key, GUARDFRACTION_VERSION)) {
|
|
|
+ int num_ok = 1;
|
|
|
+ unsigned int version;
|
|
|
+
|
|
|
+ version =
|
|
|
+ (unsigned int) tor_parse_long(line->value,
|
|
|
+ 10, 0, INT_MAX, &num_ok, NULL);
|
|
|
+
|
|
|
+ if (!num_ok || version != 1) {
|
|
|
+ log_warn(LD_GENERAL, "Got unknown guardfraction version %d.", version);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ } else if (!strcmp(line->key, GUARDFRACTION_DATE_STR)) {
|
|
|
+ time_t file_written_at;
|
|
|
+ time_t now = time(NULL);
|
|
|
+
|
|
|
+
|
|
|
+ if (parse_iso_time(line->value, &file_written_at) < 0) {
|
|
|
+ log_warn(LD_CONFIG, "Guardfraction:%d: Bad date '%s'. Ignoring",
|
|
|
+ current_line_n, line->value);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ if (file_written_at < now - MAX_GUARDFRACTION_FILE_AGE) {
|
|
|
+ log_warn(LD_CONFIG, "Guardfraction:%d: was written very long ago '%s'",
|
|
|
+ current_line_n, line->value);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ } else if (!strcmp(line->key, GUARDFRACTION_INPUTS)) {
|
|
|
+ char *err_msg = NULL;
|
|
|
+
|
|
|
+ if (guardfraction_file_parse_inputs_line(line->value,
|
|
|
+ &total_consensuses,
|
|
|
+ &total_days,
|
|
|
+ &err_msg) < 0) {
|
|
|
+ log_warn(LD_CONFIG, "Guardfraction:%d: %s",
|
|
|
+ current_line_n, err_msg);
|
|
|
+ tor_free(err_msg);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ } else if (!strcmp(line->key, GUARDFRACTION_GUARD)) {
|
|
|
+ char *err_msg = NULL;
|
|
|
+
|
|
|
+ ret_tmp = guardfraction_file_parse_guard_line(line->value,
|
|
|
+ vote_routerstatuses,
|
|
|
+ &err_msg);
|
|
|
+ if (ret_tmp < 0) {
|
|
|
+ log_warn(LD_CONFIG, "Guardfraction:%d: %s",
|
|
|
+ current_line_n, err_msg);
|
|
|
+ tor_free(err_msg);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ guards_read_n++;
|
|
|
+ if (ret_tmp > 0) {
|
|
|
+ guards_applied_n++;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ log_warn(LD_CONFIG, "Unknown guardfraction line %d (%s %s)",
|
|
|
+ current_line_n, line->key, line->value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ retval = 0;
|
|
|
+
|
|
|
+ log_warn(LD_CONFIG,
|
|
|
+ "Successfully parsed guardfraction file with %d consensuses over "
|
|
|
+ "%d days. Parsed %d nodes and applied %d of them%s.",
|
|
|
+ total_consensuses, total_days, guards_read_n, guards_applied_n,
|
|
|
+ vote_routerstatuses ? "" : " (no routerstatus provided)" );
|
|
|
+
|
|
|
+ done:
|
|
|
+ config_free_lines(front);
|
|
|
+
|
|
|
+ if (retval < 0) {
|
|
|
+ return retval;
|
|
|
+ } else {
|
|
|
+ return guards_read_n;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * information to <b>vote_routerstatuses</b>. */
|
|
|
+int
|
|
|
+dirserv_read_guardfraction_file(const char *fname,
|
|
|
+ smartlist_t *vote_routerstatuses)
|
|
|
+{
|
|
|
+ char *guardfraction_file_str;
|
|
|
+
|
|
|
+
|
|
|
+ guardfraction_file_str = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL);
|
|
|
+ if (!guardfraction_file_str) {
|
|
|
+ log_warn(LD_FS, "Cannot open guardfraction file '%s'. Failing.", fname);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return dirserv_read_guardfraction_file_from_str(guardfraction_file_str,
|
|
|
+ vote_routerstatuses);
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
* Helper function to parse out a line in the measured bandwidth file
|
|
|
* into a measured_bw_line_t output structure. Returns -1 on failure
|
|
@@ -2463,6 +2776,12 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
|
|
|
smartlist_free(routers);
|
|
|
digestmap_free(omit_as_sybil, NULL);
|
|
|
|
|
|
+
|
|
|
+ if (options->GuardfractionFile) {
|
|
|
+ dirserv_read_guardfraction_file(options->GuardfractionFile,
|
|
|
+ routerstatuses);
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
if (options->V3BandwidthsFile) {
|
|
|
dirserv_read_measured_bandwidths(options->V3BandwidthsFile,
|