diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 568d037d98..916824579c 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -1813,14 +1813,15 @@ is non-zero): If we have more onionskins queued for processing than we can process in this amount of time, reject new ones. (Default: 1750 msec) -[[MyFamily]] **MyFamily** __node__,__node__,__...__:: +[[MyFamily]] **MyFamily** __node__:: Declare that this Tor server is controlled or administered by a group or organization identical or similar to that of the other servers, defined by - their identity fingerprints. When two servers both declare - that they are in the same \'family', Tor clients will not use them in the - same circuit. (Each server only needs to list the other servers in its - family; it doesn't need to list itself, but it won't hurt.) Do not list - any bridge relay as it would compromise its concealment. + + their identity fingerprints. This option can be repeated many times, for + multiple families. When two servers both declare that they are in the + same \'family', Tor clients will not use them in the same circuit. (Each + server only needs to list the other servers in its family; it doesn't need to + list itself, but it won't hurt.) Do not list any bridge relay as it would + compromise its concealment. + + When listing a node, it's better to list it by fingerprint than by nickname: fingerprints are more reliable. diff --git a/src/or/config.c b/src/or/config.c index 809ff499fc..46057e59c5 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -395,7 +395,7 @@ static config_var_t option_vars_[] = { V(MaxOnionQueueDelay, MSEC_INTERVAL, "1750 msec"), V(MaxUnparseableDescSizeToLog, MEMUNIT, "10 MB"), V(MinMeasuredBWsForAuthToIgnoreAdvertised, INT, "500"), - V(MyFamily, STRING, NULL), + V(MyFamily, LINELIST, NULL), V(NewCircuitPeriod, INTERVAL, "30 seconds"), OBSOLETE("NamingAuthoritativeDirectory"), V(NATDListenAddress, LINELIST, NULL), @@ -707,7 +707,8 @@ static int options_transition_affects_workers( const or_options_t *old_options, const or_options_t *new_options); static int options_transition_affects_descriptor( const or_options_t *old_options, const or_options_t *new_options); -static int check_nickname_list(char **lst, const char *name, char **msg); +static int normalize_nickname_list(config_line_t **lst, const char *name, + char **msg); static char *get_bindaddr_from_transport_listen_line(const char *line, const char *transport); static int parse_ports(or_options_t *options, int validate_only, @@ -3885,7 +3886,7 @@ options_validate(or_options_t *old_options, or_options_t *options, "You should also make sure you aren't listing this bridge's " "fingerprint in any other MyFamily."); } - if (check_nickname_list(&options->MyFamily, "MyFamily", msg)) + if (normalize_nickname_list(&options->MyFamily, "MyFamily", msg)) return -1; for (cl = options->NodeFamilies; cl; cl = cl->next) { routerset_t *rs = routerset_new(); @@ -4593,7 +4594,7 @@ options_transition_affects_descriptor(const or_options_t *old_options, get_effective_bwburst(old_options) != get_effective_bwburst(new_options) || !opt_streq(old_options->ContactInfo, new_options->ContactInfo) || - !opt_streq(old_options->MyFamily, new_options->MyFamily) || + !config_lines_eq(old_options->MyFamily, new_options->MyFamily) || !opt_streq(old_options->AccountingStart, new_options->AccountingStart) || old_options->AccountingMax != new_options->AccountingMax || old_options->AccountingRule != new_options->AccountingRule || @@ -4689,27 +4690,33 @@ get_default_conf_file(int defaults_file) #endif } -/** Verify whether lst is a string containing valid-looking comma-separated - * nicknames, or NULL. Will normalise lst to prefix '$' to any nickname - * or fingerprint that needs it. Return 0 on success. +/** Verify whether lst is a list of strings containing valid-looking + * comma-separated nicknames, or NULL. Will normalise lst to prefix '$' + * to any nickname or fingerprint that needs it. Also splits comma-separated + * list elements into multiple elements. Return 0 on success. * Warn and return -1 on failure. */ static int -check_nickname_list(char **lst, const char *name, char **msg) +normalize_nickname_list(config_line_t **lst, const char *name, char **msg) { - int r = 0; - smartlist_t *sl; - int changes = 0; - if (!*lst) return 0; - sl = smartlist_new(); - smartlist_split_string(sl, *lst, ",", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK|SPLIT_STRIP_SPACE, 0); + config_line_t *new_nicknames = NULL; + config_line_t *new_nicknames_last = NULL; + config_line_t *cl; + for (cl = *lst; cl; cl = cl->next) { + char *line = cl->value; + if (!line) + continue; - SMARTLIST_FOREACH_BEGIN(sl, char *, s) + int valid_line = 1; + smartlist_t *sl = smartlist_new(); + smartlist_split_string(sl, line, ",", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK|SPLIT_STRIP_SPACE, 0); + SMARTLIST_FOREACH_BEGIN(sl, char *, s) { + char *normalized = NULL; if (!is_legal_nickname_or_hexdigest(s)) { // check if first char is dollar if (s[0] != '$') { @@ -4718,36 +4725,53 @@ check_nickname_list(char **lst, const char *name, char **msg) tor_asprintf(&prepended, "$%s", s); if (is_legal_nickname_or_hexdigest(prepended)) { - // The nickname is valid when it's prepended, swap the current - // version with a prepended one - tor_free(s); - SMARTLIST_REPLACE_CURRENT(sl, s, prepended); - changes = 1; - continue; + // The nickname is valid when it's prepended, set it as the + // normalized version + normalized = prepended; + } else { + // Still not valid, free and fallback to error message + tor_free(prepended); } - - // Still not valid, free and fallback to error message - tor_free(prepended); } - tor_asprintf(msg, "Invalid nickname '%s' in %s line", s, name); - r = -1; - break; + if (!normalized) { + tor_asprintf(msg, "Invalid nickname '%s' in %s line", s, name); + valid_line = 0; + break; + } + } else { + normalized = tor_strdup(s); } - } - SMARTLIST_FOREACH_END(s); - // Replace the caller's nickname list with a fixed one - if (changes && r == 0) { - char *newNicknames = smartlist_join_strings(sl, ", ", 0, NULL); - tor_free(*lst); - *lst = newNicknames; + config_line_t *next = tor_malloc_zero(sizeof(*next)); + next->key = tor_strdup(cl->key); + next->value = normalized; + next->next = NULL; + + if (!new_nicknames) { + new_nicknames = next; + new_nicknames_last = next; + } else { + new_nicknames_last->next = next; + new_nicknames_last = next; + } + + } SMARTLIST_FOREACH_END(s); + + SMARTLIST_FOREACH(sl, char *, s, tor_free(s)); + smartlist_free(sl); + + if (!valid_line) { + config_free_lines(new_nicknames); + return -1; + } } - SMARTLIST_FOREACH(sl, char *, s, tor_free(s)); - smartlist_free(sl); + // Replace the caller's nickname list with the normalized one + config_free_lines(*lst); + *lst = new_nicknames; - return r; + return 0; } /** Learn config file name from command line arguments, or use the default. diff --git a/src/or/or.h b/src/or/or.h index a7b3a66561..41b3622d78 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -3951,7 +3951,7 @@ typedef struct { /** If set, use these bridge authorities and not the default one. */ config_line_t *AlternateBridgeAuthority; - char *MyFamily; /**< Declared family for this OR. */ + config_line_t *MyFamily; /**< Declared family for this OR. */ config_line_t *NodeFamilies; /**< List of config lines for * node families */ smartlist_t *NodeFamilySets; /**< List of parsed NodeFamilies values. */ diff --git a/src/or/router.c b/src/or/router.c index 7fb49e8eba..c02cf5321e 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -2278,14 +2278,12 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e) } if (options->MyFamily && ! options->BridgeRelay) { - smartlist_t *family; if (!warned_nonexistent_family) warned_nonexistent_family = smartlist_new(); - family = smartlist_new(); ri->declared_family = smartlist_new(); - smartlist_split_string(family, options->MyFamily, ",", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK|SPLIT_STRIP_SPACE, 0); - SMARTLIST_FOREACH_BEGIN(family, char *, name) { + config_line_t *family; + for (family = options->MyFamily; family; family = family->next) { + char *name = family->value; const node_t *member; if (!strcasecmp(name, options->Nickname)) goto skip; /* Don't list ourself, that's redundant */ @@ -2324,13 +2322,11 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e) } skip: tor_free(name); - } SMARTLIST_FOREACH_END(name); + } /* remove duplicates from the list */ smartlist_sort_strings(ri->declared_family); smartlist_uniq_strings(ri->declared_family); - - smartlist_free(family); } /* Now generate the extrainfo. */ diff --git a/src/test/test_config.c b/src/test/test_config.c index f0874e0e0b..ccada94260 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -854,9 +854,23 @@ static void test_config_fix_my_family(void *arg) { char *err = NULL; - const char *family = "$1111111111111111111111111111111111111111, " - "1111111111111111111111111111111111111112, " - "$1111111111111111111111111111111111111113"; + config_line_t *family = tor_malloc_zero(sizeof(config_line_t)); + family->key = tor_strdup("MyFamily"); + family->value = tor_strdup("$1111111111111111111111111111111111111111, " + "1111111111111111111111111111111111111112, " + "$1111111111111111111111111111111111111113"); + + config_line_t *family2 = tor_malloc_zero(sizeof(config_line_t)); + family2->key = tor_strdup("MyFamily"); + family2->value = tor_strdup("1111111111111111111111111111111111111114"); + + config_line_t *family3 = tor_malloc_zero(sizeof(config_line_t)); + family3->key = tor_strdup("MyFamily"); + family3->value = tor_strdup("$1111111111111111111111111111111111111115"); + + family->next = family2; + family2->next = family3; + family3->next = NULL; or_options_t* options = options_new(); or_options_t* defaults = options_new(); @@ -864,7 +878,7 @@ test_config_fix_my_family(void *arg) options_init(options); options_init(defaults); - options->MyFamily = tor_strdup(family); + options->MyFamily = family; options_validate(NULL, options, defaults, 0, &err) ; @@ -872,10 +886,18 @@ test_config_fix_my_family(void *arg) TT_FAIL(("options_validate failed: %s", err)); } - tt_str_op(options->MyFamily,OP_EQ, - "$1111111111111111111111111111111111111111, " - "$1111111111111111111111111111111111111112, " - "$1111111111111111111111111111111111111113"); + const char *valid[] = { "$1111111111111111111111111111111111111111", + "$1111111111111111111111111111111111111112", + "$1111111111111111111111111111111111111113", + "$1111111111111111111111111111111111111114", + "$1111111111111111111111111111111111111115" }; + int ret_size = 0; + config_line_t *ret; + for (ret = options->MyFamily; ret && ret_size < 5; ret = ret->next) { + tt_str_op(ret->value, OP_EQ, valid[ret_size]); + ret_size++; + } + tt_int_op(ret_size, OP_EQ, 5); done: if (err != NULL) {