#define GUARDFRACTION_PRIVATE
#include "core/or/or.h"
#include "feature/dirauth/guardfraction.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/dirparse/ns_parse.h"
#include "feature/nodelist/vote_routerstatus_st.h"
#include "lib/encoding/confline.h"
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;
}
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);
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) != 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;
}
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);
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"
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_info(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;
}
}
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);
}