mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-27 22:03:31 +01:00
Merge remote-tracking branch 'public/prop227_v2'
Conflicts: src/test/test_dir.c
This commit is contained in:
commit
fac8d40886
5
changes/prop227
Normal file
5
changes/prop227
Normal file
@ -0,0 +1,5 @@
|
||||
o Minor features (directory system):
|
||||
- Authorities can now vote on the correct digests and latest versions for
|
||||
different software packages. This allows packages that include Tor to use
|
||||
the Tor authority system as a way to get notified of updates and their
|
||||
correct digests. Implements proposal 227. Closes ticket 10395.
|
@ -1886,6 +1886,12 @@ on the public Tor network.
|
||||
multiple times: the values from multiple lines are spliced together. When
|
||||
this is set then **VersioningAuthoritativeDirectory** should be set too.
|
||||
|
||||
[[RecommendedPackageVersions]] **RecommendedPackageVersions** __PACKAGENAME__ __VERSION__ __URL__ __DIGESTTYPE__**=**__DIGEST__ ::
|
||||
Adds "package" line to the directory authority's vote. This information
|
||||
is used to vote on the correct URL and digest for the released versions
|
||||
of different Tor-related packages, so that the consensus can certify
|
||||
them. This line may appear any number of times.
|
||||
|
||||
[[RecommendedClientVersions]] **RecommendedClientVersions** __STRING__::
|
||||
STRING is a comma-separated list of Tor versions currently believed to be
|
||||
safe for clients to use. This information is included in version 2
|
||||
|
@ -518,11 +518,13 @@ smartlist_sort(smartlist_t *sl, int (*compare)(const void **a, const void **b))
|
||||
|
||||
/** Given a smartlist <b>sl</b> sorted with the function <b>compare</b>,
|
||||
* return the most frequent member in the list. Break ties in favor of
|
||||
* later elements. If the list is empty, return NULL.
|
||||
* later elements. If the list is empty, return NULL. If count_out is
|
||||
* non-null, set it to the most frequent member.
|
||||
*/
|
||||
void *
|
||||
smartlist_get_most_frequent(const smartlist_t *sl,
|
||||
int (*compare)(const void **a, const void **b))
|
||||
smartlist_get_most_frequent_(const smartlist_t *sl,
|
||||
int (*compare)(const void **a, const void **b),
|
||||
int *count_out)
|
||||
{
|
||||
const void *most_frequent = NULL;
|
||||
int most_frequent_count = 0;
|
||||
@ -530,8 +532,11 @@ smartlist_get_most_frequent(const smartlist_t *sl,
|
||||
const void *cur = NULL;
|
||||
int i, count=0;
|
||||
|
||||
if (!sl->num_used)
|
||||
if (!sl->num_used) {
|
||||
if (count_out)
|
||||
*count_out = 0;
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0; i < sl->num_used; ++i) {
|
||||
const void *item = sl->list[i];
|
||||
if (cur && 0 == compare(&cur, &item)) {
|
||||
@ -549,6 +554,8 @@ smartlist_get_most_frequent(const smartlist_t *sl,
|
||||
most_frequent = cur;
|
||||
most_frequent_count = count;
|
||||
}
|
||||
if (count_out)
|
||||
*count_out = most_frequent_count;
|
||||
return (void*)most_frequent;
|
||||
}
|
||||
|
||||
@ -728,6 +735,16 @@ smartlist_get_most_frequent_string(smartlist_t *sl)
|
||||
return smartlist_get_most_frequent(sl, compare_string_ptrs_);
|
||||
}
|
||||
|
||||
/** Return the most frequent string in the sorted list <b>sl</b>.
|
||||
* If <b>count_out</b> is provided, set <b>count_out</b> to the
|
||||
* number of times that string appears.
|
||||
*/
|
||||
char *
|
||||
smartlist_get_most_frequent_string_(smartlist_t *sl, int *count_out)
|
||||
{
|
||||
return smartlist_get_most_frequent_(sl, compare_string_ptrs_, count_out);
|
||||
}
|
||||
|
||||
/** Remove duplicate strings from a sorted list, and free them with tor_free().
|
||||
*/
|
||||
void
|
||||
|
@ -94,8 +94,11 @@ void smartlist_del_keeporder(smartlist_t *sl, int idx);
|
||||
void smartlist_insert(smartlist_t *sl, int idx, void *val);
|
||||
void smartlist_sort(smartlist_t *sl,
|
||||
int (*compare)(const void **a, const void **b));
|
||||
void *smartlist_get_most_frequent(const smartlist_t *sl,
|
||||
int (*compare)(const void **a, const void **b));
|
||||
void *smartlist_get_most_frequent_(const smartlist_t *sl,
|
||||
int (*compare)(const void **a, const void **b),
|
||||
int *count_out);
|
||||
#define smartlist_get_most_frequent(sl, compare) \
|
||||
smartlist_get_most_frequent_((sl), (compare), NULL)
|
||||
void smartlist_uniq(smartlist_t *sl,
|
||||
int (*compare)(const void **a, const void **b),
|
||||
void (*free_fn)(void *elt));
|
||||
@ -106,6 +109,7 @@ void smartlist_sort_digests256(smartlist_t *sl);
|
||||
void smartlist_sort_pointers(smartlist_t *sl);
|
||||
|
||||
char *smartlist_get_most_frequent_string(smartlist_t *sl);
|
||||
char *smartlist_get_most_frequent_string_(smartlist_t *sl, int *count_out);
|
||||
char *smartlist_get_most_frequent_digest256(smartlist_t *sl);
|
||||
|
||||
void smartlist_uniq_strings(smartlist_t *sl);
|
||||
|
@ -86,6 +86,7 @@ static config_abbrev_t option_abbrevs_[] = {
|
||||
PLURAL(HiddenServiceExcludeNode),
|
||||
PLURAL(NumCPU),
|
||||
PLURAL(RendNode),
|
||||
PLURAL(RecommendedPackage),
|
||||
PLURAL(RendExcludeNode),
|
||||
PLURAL(StrictEntryNode),
|
||||
PLURAL(StrictExitNode),
|
||||
@ -367,6 +368,7 @@ static config_var_t option_vars_[] = {
|
||||
V(RecommendedVersions, LINELIST, NULL),
|
||||
V(RecommendedClientVersions, LINELIST, NULL),
|
||||
V(RecommendedServerVersions, LINELIST, NULL),
|
||||
V(RecommendedPackages, LINELIST, NULL),
|
||||
V(RefuseUnknownExits, AUTOBOOL, "auto"),
|
||||
V(RejectPlaintextPorts, CSV, ""),
|
||||
V(RelayBandwidthBurst, MEMUNIT, "0"),
|
||||
@ -2743,6 +2745,13 @@ options_validate(or_options_t *old_options, or_options_t *options,
|
||||
"features to be broken in unpredictable ways.");
|
||||
}
|
||||
|
||||
for (cl = options->RecommendedPackages; cl; cl = cl->next) {
|
||||
if (! validate_recommended_package_line(cl->value)) {
|
||||
log_warn(LD_CONFIG, "Invalid RecommendedPackage line %s will be ignored",
|
||||
escaped(cl->value));
|
||||
}
|
||||
}
|
||||
|
||||
if (options->AuthoritativeDir) {
|
||||
if (!options->ContactInfo && !options->TestingTorNetwork)
|
||||
REJECT("Authoritative directory servers must set ContactInfo");
|
||||
|
@ -2183,6 +2183,8 @@ static const getinfo_item_t getinfo_items[] = {
|
||||
"Brief summary of router status by nickname (v2 directory format)."),
|
||||
PREFIX("ns/purpose/", networkstatus,
|
||||
"Brief summary of router status by purpose (v2 directory format)."),
|
||||
PREFIX("consensus/", networkstatus,
|
||||
"Information about and from the ns consensus."),
|
||||
ITEM("network-status", dir,
|
||||
"Brief summary of router status (v1 directory format)"),
|
||||
ITEM("circuit-status", events, "List of current circuits originating here."),
|
||||
|
@ -2511,6 +2511,15 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
|
||||
|
||||
v3_out->client_versions = client_versions;
|
||||
v3_out->server_versions = server_versions;
|
||||
v3_out->package_lines = smartlist_new();
|
||||
{
|
||||
config_line_t *cl;
|
||||
for (cl = get_options()->RecommendedPackages; cl; cl = cl->next) {
|
||||
if (validate_recommended_package_line(cl->value))
|
||||
smartlist_add(v3_out->package_lines, tor_strdup(cl->value));
|
||||
}
|
||||
}
|
||||
|
||||
v3_out->known_flags = smartlist_new();
|
||||
smartlist_split_string(v3_out->known_flags,
|
||||
"Authority Exit Fast Guard Stable V2Dir Valid",
|
||||
@ -3256,6 +3265,83 @@ connection_dirserv_flushed_some(dir_connection_t *conn)
|
||||
}
|
||||
}
|
||||
|
||||
/** Return true iff <b>line</b> is a valid RecommendedPackages line.
|
||||
*/
|
||||
/*
|
||||
The grammar is:
|
||||
|
||||
"package" SP PACKAGENAME SP VERSION SP URL SP DIGESTS NL
|
||||
|
||||
PACKAGENAME = NONSPACE
|
||||
VERSION = NONSPACE
|
||||
URL = NONSPACE
|
||||
DIGESTS = DIGEST | DIGESTS SP DIGEST
|
||||
DIGEST = DIGESTTYPE "=" DIGESTVAL
|
||||
|
||||
NONSPACE = one or more non-space printing characters
|
||||
|
||||
DIGESTVAL = DIGESTTYPE = one or more non-=, non-" " characters.
|
||||
|
||||
SP = " "
|
||||
NL = a newline
|
||||
|
||||
*/
|
||||
int
|
||||
validate_recommended_package_line(const char *line)
|
||||
{
|
||||
const char *cp = line;
|
||||
|
||||
#define WORD() \
|
||||
do { \
|
||||
if (*cp == ' ') \
|
||||
return 0; \
|
||||
cp = strchr(cp, ' '); \
|
||||
if (!cp) \
|
||||
return 0; \
|
||||
} while (0)
|
||||
|
||||
WORD(); /* skip packagename */
|
||||
++cp;
|
||||
WORD(); /* skip version */
|
||||
++cp;
|
||||
WORD(); /* Skip URL */
|
||||
++cp;
|
||||
|
||||
/* Skip digesttype=digestval + */
|
||||
int n_entries = 0;
|
||||
while (1) {
|
||||
const char *start_of_word = cp;
|
||||
const char *end_of_word = strchr(cp, ' ');
|
||||
if (! end_of_word)
|
||||
end_of_word = cp + strlen(cp);
|
||||
|
||||
if (start_of_word == end_of_word)
|
||||
return 0;
|
||||
|
||||
const char *eq = memchr(start_of_word, '=', end_of_word - start_of_word);
|
||||
|
||||
if (!eq)
|
||||
return 0;
|
||||
if (eq == start_of_word)
|
||||
return 0;
|
||||
if (eq == end_of_word - 1)
|
||||
return 0;
|
||||
if (memchr(eq+1, '=', end_of_word - (eq+1)))
|
||||
return 0;
|
||||
|
||||
++n_entries;
|
||||
if (0 == *end_of_word)
|
||||
break;
|
||||
|
||||
cp = end_of_word + 1;
|
||||
}
|
||||
|
||||
if (n_entries == 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Release all storage used by the directory server. */
|
||||
void
|
||||
dirserv_free_all(void)
|
||||
|
@ -104,6 +104,8 @@ void dirserv_free_all(void);
|
||||
void cached_dir_decref(cached_dir_t *d);
|
||||
cached_dir_t *new_cached_dir(char *s, time_t published);
|
||||
|
||||
int validate_recommended_package_line(const char *line);
|
||||
|
||||
#ifdef DIRSERV_PRIVATE
|
||||
|
||||
/* Put the MAX_MEASUREMENT_AGE #define here so unit tests can see it */
|
||||
|
100
src/or/dirvote.c
100
src/or/dirvote.c
@ -66,6 +66,7 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
|
||||
{
|
||||
smartlist_t *chunks = smartlist_new();
|
||||
const char *client_versions = NULL, *server_versions = NULL;
|
||||
char *packages = NULL;
|
||||
char fingerprint[FINGERPRINT_LEN+1];
|
||||
char digest[DIGEST_LEN];
|
||||
uint32_t addr;
|
||||
@ -98,6 +99,18 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
|
||||
server_versions_line = tor_strdup("");
|
||||
}
|
||||
|
||||
if (v3_ns->package_lines) {
|
||||
smartlist_t *tmp = smartlist_new();
|
||||
SMARTLIST_FOREACH(v3_ns->package_lines, const char *, p,
|
||||
if (validate_recommended_package_line(p))
|
||||
smartlist_add_asprintf(tmp, "package %s\n", p));
|
||||
packages = smartlist_join_strings(tmp, "", 0, NULL);
|
||||
SMARTLIST_FOREACH(tmp, char *, cp, tor_free(cp));
|
||||
smartlist_free(tmp);
|
||||
} else {
|
||||
packages = tor_strdup("");
|
||||
}
|
||||
|
||||
{
|
||||
char published[ISO_TIME_LEN+1];
|
||||
char va[ISO_TIME_LEN+1];
|
||||
@ -132,6 +145,7 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
|
||||
"valid-until %s\n"
|
||||
"voting-delay %d %d\n"
|
||||
"%s%s" /* versions */
|
||||
"%s" /* packages */
|
||||
"known-flags %s\n"
|
||||
"flag-thresholds %s\n"
|
||||
"params %s\n"
|
||||
@ -143,6 +157,7 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
|
||||
v3_ns->vote_seconds, v3_ns->dist_seconds,
|
||||
client_versions_line,
|
||||
server_versions_line,
|
||||
packages,
|
||||
flags,
|
||||
flag_thresholds,
|
||||
params,
|
||||
@ -230,6 +245,7 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
|
||||
done:
|
||||
tor_free(client_versions_line);
|
||||
tor_free(server_versions_line);
|
||||
tor_free(packages);
|
||||
|
||||
SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
|
||||
smartlist_free(chunks);
|
||||
@ -1037,6 +1053,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
|
||||
const routerstatus_format_type_t rs_format =
|
||||
flavor == FLAV_NS ? NS_V3_CONSENSUS : NS_V3_CONSENSUS_MICRODESC;
|
||||
char *params = NULL;
|
||||
char *packages = NULL;
|
||||
int added_weights = 0;
|
||||
tor_assert(flavor == FLAV_NS || flavor == FLAV_MICRODESC);
|
||||
tor_assert(total_authorities >= smartlist_len(votes));
|
||||
@ -1120,6 +1137,11 @@ networkstatus_compute_consensus(smartlist_t *votes,
|
||||
n_versioning_servers);
|
||||
client_versions = compute_consensus_versions_list(combined_client_versions,
|
||||
n_versioning_clients);
|
||||
if (consensus_method >= MIN_METHOD_FOR_PACKAGE_LINES) {
|
||||
packages = compute_consensus_package_lines(votes);
|
||||
} else {
|
||||
packages = tor_strdup("");
|
||||
}
|
||||
|
||||
SMARTLIST_FOREACH(combined_server_versions, char *, cp, tor_free(cp));
|
||||
SMARTLIST_FOREACH(combined_client_versions, char *, cp, tor_free(cp));
|
||||
@ -1162,10 +1184,13 @@ networkstatus_compute_consensus(smartlist_t *votes,
|
||||
"voting-delay %d %d\n"
|
||||
"client-versions %s\n"
|
||||
"server-versions %s\n"
|
||||
"%s" /* packages */
|
||||
"known-flags %s\n",
|
||||
va_buf, fu_buf, vu_buf,
|
||||
vote_seconds, dist_seconds,
|
||||
client_versions, server_versions, flaglist);
|
||||
client_versions, server_versions,
|
||||
packages,
|
||||
flaglist);
|
||||
|
||||
tor_free(flaglist);
|
||||
}
|
||||
@ -1852,6 +1877,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
|
||||
|
||||
tor_free(client_versions);
|
||||
tor_free(server_versions);
|
||||
tor_free(packages);
|
||||
SMARTLIST_FOREACH(flags, char *, cp, tor_free(cp));
|
||||
smartlist_free(flags);
|
||||
SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
|
||||
@ -1860,6 +1886,78 @@ networkstatus_compute_consensus(smartlist_t *votes,
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Given a list of networkstatus_t for each vote, return a newly allocated
|
||||
* string containing the "package" lines for the vote. */
|
||||
STATIC char *
|
||||
compute_consensus_package_lines(smartlist_t *votes)
|
||||
{
|
||||
const int n_votes = smartlist_len(votes);
|
||||
|
||||
/* This will be a map from "packagename version" strings to arrays
|
||||
* of const char *, with the i'th member of the array corresponding to the
|
||||
* package line from the i'th vote.
|
||||
*/
|
||||
strmap_t *package_status = strmap_new();
|
||||
|
||||
SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) {
|
||||
if (! v->package_lines)
|
||||
continue;
|
||||
SMARTLIST_FOREACH_BEGIN(v->package_lines, const char *, line) {
|
||||
if (! validate_recommended_package_line(line))
|
||||
continue;
|
||||
|
||||
/* Skip 'cp' to the second space in the line. */
|
||||
const char *cp = strchr(line, ' ');
|
||||
if (!cp) continue;
|
||||
++cp;
|
||||
cp = strchr(cp, ' ');
|
||||
if (!cp) continue;
|
||||
|
||||
char *key = tor_strndup(line, cp - line);
|
||||
|
||||
const char **status = strmap_get(package_status, key);
|
||||
if (!status) {
|
||||
status = tor_calloc(n_votes, sizeof(const char *));
|
||||
strmap_set(package_status, key, status);
|
||||
}
|
||||
status[v_sl_idx] = line; /* overwrite old value */
|
||||
tor_free(key);
|
||||
} SMARTLIST_FOREACH_END(line);
|
||||
} SMARTLIST_FOREACH_END(v);
|
||||
|
||||
smartlist_t *entries = smartlist_new(); /* temporary */
|
||||
smartlist_t *result_list = smartlist_new(); /* output */
|
||||
STRMAP_FOREACH(package_status, key, const char **, values) {
|
||||
int i, count=-1;
|
||||
for (i = 0; i < n_votes; ++i) {
|
||||
if (values[i])
|
||||
smartlist_add(entries, (void*) values[i]);
|
||||
}
|
||||
smartlist_sort_strings(entries);
|
||||
int n_voting_for_entry = smartlist_len(entries);
|
||||
const char *most_frequent =
|
||||
smartlist_get_most_frequent_string_(entries, &count);
|
||||
|
||||
if (n_voting_for_entry >= 3 && count > n_voting_for_entry / 2) {
|
||||
smartlist_add_asprintf(result_list, "package %s\n", most_frequent);
|
||||
}
|
||||
|
||||
smartlist_clear(entries);
|
||||
|
||||
} STRMAP_FOREACH_END;
|
||||
|
||||
smartlist_sort_strings(result_list);
|
||||
|
||||
char *result = smartlist_join_strings(result_list, "", 0, NULL);
|
||||
|
||||
SMARTLIST_FOREACH(result_list, char *, cp, tor_free(cp));
|
||||
smartlist_free(result_list);
|
||||
smartlist_free(entries);
|
||||
strmap_free(package_status, tor_free_);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Given a consensus vote <b>target</b> and a set of detached signatures in
|
||||
* <b>sigs</b> that correspond to the same consensus, check whether there are
|
||||
* any new signatures in <b>src_voter_list</b> that should be added to
|
||||
|
@ -55,7 +55,7 @@
|
||||
#define MIN_SUPPORTED_CONSENSUS_METHOD 13
|
||||
|
||||
/** The highest consensus method that we currently support. */
|
||||
#define MAX_SUPPORTED_CONSENSUS_METHOD 18
|
||||
#define MAX_SUPPORTED_CONSENSUS_METHOD 19
|
||||
|
||||
/** Lowest consensus method where microdesc consensuses omit any entry
|
||||
* with no microdesc. */
|
||||
@ -79,6 +79,9 @@
|
||||
* microdescriptors. */
|
||||
#define MIN_METHOD_FOR_ID_HASH_IN_MD 18
|
||||
|
||||
/** Lowest consensus method where we include "package" lines*/
|
||||
#define MIN_METHOD_FOR_PACKAGE_LINES 19
|
||||
|
||||
/** Default bandwidth to clip unmeasured bandwidths to using method >=
|
||||
* MIN_METHOD_TO_CLIP_UNMEASURED_BW */
|
||||
#define DEFAULT_MAX_UNMEASURED_BW_KB 20
|
||||
@ -160,6 +163,7 @@ STATIC char *format_networkstatus_vote(crypto_pk_t *private_key,
|
||||
networkstatus_t *v3_ns);
|
||||
STATIC char *dirvote_compute_params(smartlist_t *votes, int method,
|
||||
int total_authorities);
|
||||
STATIC char *compute_consensus_package_lines(smartlist_t *votes);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -257,6 +257,10 @@ networkstatus_vote_free(networkstatus_t *ns)
|
||||
SMARTLIST_FOREACH(ns->supported_methods, char *, c, tor_free(c));
|
||||
smartlist_free(ns->supported_methods);
|
||||
}
|
||||
if (ns->package_lines) {
|
||||
SMARTLIST_FOREACH(ns->package_lines, char *, c, tor_free(c));
|
||||
smartlist_free(ns->package_lines);
|
||||
}
|
||||
if (ns->voters) {
|
||||
SMARTLIST_FOREACH_BEGIN(ns->voters, networkstatus_voter_info_t *, voter) {
|
||||
tor_free(voter->nickname);
|
||||
@ -1909,6 +1913,33 @@ getinfo_helper_networkstatus(control_connection_t *conn,
|
||||
} else if (!strcmpstart(question, "ns/purpose/")) {
|
||||
*answer = networkstatus_getinfo_by_purpose(question+11, time(NULL));
|
||||
return *answer ? 0 : -1;
|
||||
} else if (!strcmp(question, "consensus/packages")) {
|
||||
const networkstatus_t *ns = networkstatus_get_latest_consensus();
|
||||
if (ns && ns->package_lines)
|
||||
*answer = smartlist_join_strings(ns->package_lines, "\n", 0, NULL);
|
||||
else
|
||||
*errmsg = "No consensus available";
|
||||
return *answer ? 0 : -1;
|
||||
} else if (!strcmp(question, "consensus/valid-after") ||
|
||||
!strcmp(question, "consensus/fresh-until") ||
|
||||
!strcmp(question, "consensus/valid-until")) {
|
||||
const networkstatus_t *ns = networkstatus_get_latest_consensus();
|
||||
if (ns) {
|
||||
time_t t;
|
||||
if (!strcmp(question, "consensus/valid-after"))
|
||||
t = ns->valid_after;
|
||||
else if (!strcmp(question, "consensus/fresh-until"))
|
||||
t = ns->fresh_until;
|
||||
else
|
||||
t = ns->valid_until;
|
||||
|
||||
char tbuf[ISO_TIME_LEN+1];
|
||||
format_iso_time(tbuf, t);
|
||||
*answer = tor_strdup(tbuf);
|
||||
} else {
|
||||
*errmsg = "No consensus available";
|
||||
}
|
||||
return *answer ? 0 : -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
@ -2417,6 +2417,9 @@ typedef struct networkstatus_t {
|
||||
/** Vote only: what methods is this voter willing to use? */
|
||||
smartlist_t *supported_methods;
|
||||
|
||||
/** List of 'package' lines describing hashes of downloadable packages */
|
||||
smartlist_t *package_lines;
|
||||
|
||||
/** How long does this vote/consensus claim that authorities take to
|
||||
* distribute their votes to one another? */
|
||||
int vote_seconds;
|
||||
@ -3433,6 +3436,7 @@ typedef struct {
|
||||
config_line_t *RecommendedVersions;
|
||||
config_line_t *RecommendedClientVersions;
|
||||
config_line_t *RecommendedServerVersions;
|
||||
config_line_t *RecommendedPackages;
|
||||
/** Whether dirservers allow router descriptors with private IPs. */
|
||||
int DirAllowPrivateAddresses;
|
||||
/** Whether routers accept EXTEND cells to routers with private IPs. */
|
||||
|
@ -131,6 +131,7 @@ typedef enum {
|
||||
K_CONSENSUS_METHOD,
|
||||
K_LEGACY_DIR_KEY,
|
||||
K_DIRECTORY_FOOTER,
|
||||
K_PACKAGE,
|
||||
|
||||
A_PURPOSE,
|
||||
A_LAST_LISTED,
|
||||
@ -420,6 +421,7 @@ static token_rule_t networkstatus_token_table[] = {
|
||||
T1("known-flags", K_KNOWN_FLAGS, ARGS, NO_OBJ ),
|
||||
T01("params", K_PARAMS, ARGS, NO_OBJ ),
|
||||
T( "fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ),
|
||||
T0N("package", K_PACKAGE, CONCAT_ARGS, NO_OBJ ),
|
||||
|
||||
CERTIFICATE_MEMBERS
|
||||
|
||||
@ -2626,6 +2628,16 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
|
||||
ns->server_versions = tor_strdup(tok->args[0]);
|
||||
}
|
||||
|
||||
{
|
||||
smartlist_t *package_lst = find_all_by_keyword(tokens, K_PACKAGE);
|
||||
ns->package_lines = smartlist_new();
|
||||
if (package_lst) {
|
||||
SMARTLIST_FOREACH(package_lst, directory_token_t *, t,
|
||||
smartlist_add(ns->package_lines, tor_strdup(t->args[0])));
|
||||
}
|
||||
smartlist_free(package_lst);
|
||||
}
|
||||
|
||||
tok = find_by_keyword(tokens, K_KNOWN_FLAGS);
|
||||
ns->known_flags = smartlist_new();
|
||||
inorder = 1;
|
||||
|
@ -2954,6 +2954,152 @@ test_dir_fetch_type(void *arg)
|
||||
done: ;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dir_packages(void *arg)
|
||||
{
|
||||
smartlist_t *votes = smartlist_new();
|
||||
char *res = NULL;
|
||||
(void)arg;
|
||||
|
||||
#define BAD(s) \
|
||||
tt_int_op(0, ==, validate_recommended_package_line(s));
|
||||
#define GOOD(s) \
|
||||
tt_int_op(1, ==, validate_recommended_package_line(s));
|
||||
GOOD("tor 0.2.6.3-alpha "
|
||||
"http://torproject.example.com/dist/tor-0.2.6.3-alpha.tar.gz "
|
||||
"sha256=sssdlkfjdsklfjdskfljasdklfj");
|
||||
GOOD("tor 0.2.6.3-alpha "
|
||||
"http://torproject.example.com/dist/tor-0.2.6.3-alpha.tar.gz "
|
||||
"sha256=sssdlkfjdsklfjdskfljasdklfj blake2b=fred");
|
||||
BAD("tor 0.2.6.3-alpha "
|
||||
"http://torproject.example.com/dist/tor-0.2.6.3-alpha.tar.gz "
|
||||
"sha256=sssdlkfjdsklfjdskfljasdklfj=");
|
||||
BAD("tor 0.2.6.3-alpha "
|
||||
"http://torproject.example.com/dist/tor-0.2.6.3-alpha.tar.gz "
|
||||
"sha256=sssdlkfjdsklfjdskfljasdklfj blake2b");
|
||||
BAD("tor 0.2.6.3-alpha "
|
||||
"http://torproject.example.com/dist/tor-0.2.6.3-alpha.tar.gz ");
|
||||
BAD("tor 0.2.6.3-alpha "
|
||||
"http://torproject.example.com/dist/tor-0.2.6.3-alpha.tar.gz");
|
||||
BAD("tor 0.2.6.3-alpha ");
|
||||
BAD("tor 0.2.6.3-alpha");
|
||||
BAD("tor ");
|
||||
BAD("tor");
|
||||
BAD("");
|
||||
BAD("=foobar sha256="
|
||||
"3c179f46ca77069a6a0bac70212a9b3b838b2f66129cb52d568837fc79d8fcc7");
|
||||
BAD("= = sha256="
|
||||
"3c179f46ca77069a6a0bac70212a9b3b838b2f66129cb52d568837fc79d8fcc7");
|
||||
|
||||
BAD("sha512= sha256="
|
||||
"3c179f46ca77069a6a0bac70212a9b3b838b2f66129cb52d568837fc79d8fcc7");
|
||||
|
||||
votes = smartlist_new();
|
||||
smartlist_add(votes, tor_malloc_zero(sizeof(networkstatus_t)));
|
||||
smartlist_add(votes, tor_malloc_zero(sizeof(networkstatus_t)));
|
||||
smartlist_add(votes, tor_malloc_zero(sizeof(networkstatus_t)));
|
||||
smartlist_add(votes, tor_malloc_zero(sizeof(networkstatus_t)));
|
||||
smartlist_add(votes, tor_malloc_zero(sizeof(networkstatus_t)));
|
||||
smartlist_add(votes, tor_malloc_zero(sizeof(networkstatus_t)));
|
||||
SMARTLIST_FOREACH(votes, networkstatus_t *, ns,
|
||||
ns->package_lines = smartlist_new());
|
||||
|
||||
#define ADD(i, s) \
|
||||
smartlist_add(((networkstatus_t*)smartlist_get(votes, (i)))->package_lines, \
|
||||
(void*)(s));
|
||||
|
||||
/* Only one vote for this one. */
|
||||
ADD(4, "cisco 99z http://foobar.example.com/ sha256=blahblah");
|
||||
|
||||
/* Only two matching entries for this one, but 3 voters */
|
||||
ADD(1, "mystic 99y http://barfoo.example.com/ sha256=blahblah");
|
||||
ADD(3, "mystic 99y http://foobar.example.com/ sha256=blahblah");
|
||||
ADD(4, "mystic 99y http://foobar.example.com/ sha256=blahblah");
|
||||
|
||||
/* Only two matching entries for this one, but at least 4 voters */
|
||||
ADD(1, "mystic 99p http://barfoo.example.com/ sha256=ggggggg");
|
||||
ADD(3, "mystic 99p http://foobar.example.com/ sha256=blahblah");
|
||||
ADD(4, "mystic 99p http://foobar.example.com/ sha256=blahblah");
|
||||
ADD(5, "mystic 99p http://foobar.example.com/ sha256=ggggggg");
|
||||
|
||||
/* This one has only invalid votes. */
|
||||
ADD(0, "haffenreffer 1.2 http://foobar.example.com/ sha256");
|
||||
ADD(1, "haffenreffer 1.2 http://foobar.example.com/ ");
|
||||
ADD(2, "haffenreffer 1.2 ");
|
||||
ADD(3, "haffenreffer ");
|
||||
ADD(4, "haffenreffer");
|
||||
|
||||
/* Three matching votes for this; it should actually go in! */
|
||||
ADD(2, "element 0.66.1 http://quux.example.com/ sha256=abcdef");
|
||||
ADD(3, "element 0.66.1 http://quux.example.com/ sha256=abcdef");
|
||||
ADD(4, "element 0.66.1 http://quux.example.com/ sha256=abcdef");
|
||||
ADD(1, "element 0.66.1 http://quum.example.com/ sha256=abcdef");
|
||||
ADD(0, "element 0.66.1 http://quux.example.com/ sha256=abcde");
|
||||
|
||||
/* Three votes for A, three votes for B */
|
||||
ADD(0, "clownshoes 22alpha1 http://quumble.example.com/ blake2=foob");
|
||||
ADD(1, "clownshoes 22alpha1 http://quumble.example.com/ blake2=foob");
|
||||
ADD(2, "clownshoes 22alpha1 http://quumble.example.com/ blake2=foob");
|
||||
ADD(3, "clownshoes 22alpha1 http://quumble.example.com/ blake2=fooz");
|
||||
ADD(4, "clownshoes 22alpha1 http://quumble.example.com/ blake2=fooz");
|
||||
ADD(5, "clownshoes 22alpha1 http://quumble.example.com/ blake2=fooz");
|
||||
|
||||
/* Three votes for A, two votes for B */
|
||||
ADD(1, "clownshoes 22alpha3 http://quumble.example.com/ blake2=foob");
|
||||
ADD(2, "clownshoes 22alpha3 http://quumble.example.com/ blake2=foob");
|
||||
ADD(3, "clownshoes 22alpha3 http://quumble.example.com/ blake2=fooz");
|
||||
ADD(4, "clownshoes 22alpha3 http://quumble.example.com/ blake2=fooz");
|
||||
ADD(5, "clownshoes 22alpha3 http://quumble.example.com/ blake2=fooz");
|
||||
|
||||
/* Four votes for A, two for B. */
|
||||
ADD(0, "clownshoes 22alpha4 http://quumble.example.com/ blake2=foob");
|
||||
ADD(1, "clownshoes 22alpha4 http://quumble.example.com/ blake2=foob");
|
||||
ADD(2, "clownshoes 22alpha4 http://quumble.example.cam/ blake2=fooa");
|
||||
ADD(3, "clownshoes 22alpha4 http://quumble.example.cam/ blake2=fooa");
|
||||
ADD(4, "clownshoes 22alpha4 http://quumble.example.cam/ blake2=fooa");
|
||||
ADD(5, "clownshoes 22alpha4 http://quumble.example.cam/ blake2=fooa");
|
||||
|
||||
/* Five votes for A ... all from the same guy. Three for B. */
|
||||
ADD(0, "cbc 99.1.11.1.1 http://example.com/cbc/ cubehash=ahooy sha512=m");
|
||||
ADD(1, "cbc 99.1.11.1.1 http://example.com/cbc/ cubehash=ahooy sha512=m");
|
||||
ADD(3, "cbc 99.1.11.1.1 http://example.com/cbc/ cubehash=ahooy sha512=m");
|
||||
ADD(2, "cbc 99.1.11.1.1 http://example.com/ cubehash=ahooy");
|
||||
ADD(2, "cbc 99.1.11.1.1 http://example.com/ cubehash=ahooy");
|
||||
ADD(2, "cbc 99.1.11.1.1 http://example.com/ cubehash=ahooy");
|
||||
ADD(2, "cbc 99.1.11.1.1 http://example.com/ cubehash=ahooy");
|
||||
ADD(2, "cbc 99.1.11.1.1 http://example.com/ cubehash=ahooy");
|
||||
|
||||
/* As above but new replaces old: no two match. */
|
||||
ADD(0, "cbc 99.1.11.1.2 http://example.com/cbc/ cubehash=ahooy sha512=m");
|
||||
ADD(1, "cbc 99.1.11.1.2 http://example.com/cbc/ cubehash=ahooy sha512=m");
|
||||
ADD(1, "cbc 99.1.11.1.2 http://example.com/cbc/x cubehash=ahooy sha512=m");
|
||||
ADD(2, "cbc 99.1.11.1.2 http://example.com/cbc/ cubehash=ahooy sha512=m");
|
||||
ADD(2, "cbc 99.1.11.1.2 http://example.com/ cubehash=ahooy");
|
||||
ADD(2, "cbc 99.1.11.1.2 http://example.com/ cubehash=ahooy");
|
||||
ADD(2, "cbc 99.1.11.1.2 http://example.com/ cubehash=ahooy");
|
||||
ADD(2, "cbc 99.1.11.1.2 http://example.com/ cubehash=ahooy");
|
||||
ADD(2, "cbc 99.1.11.1.2 http://example.com/ cubehash=ahooy");
|
||||
|
||||
|
||||
res = compute_consensus_package_lines(votes);
|
||||
tt_assert(res);
|
||||
tt_str_op(res, ==,
|
||||
"package cbc 99.1.11.1.1 http://example.com/cbc/ cubehash=ahooy sha512=m\n"
|
||||
"package clownshoes 22alpha3 http://quumble.example.com/ blake2=fooz\n"
|
||||
"package clownshoes 22alpha4 http://quumble.example.cam/ blake2=fooa\n"
|
||||
"package element 0.66.1 http://quux.example.com/ sha256=abcdef\n"
|
||||
"package mystic 99y http://foobar.example.com/ sha256=blahblah\n"
|
||||
);
|
||||
|
||||
#undef ADD
|
||||
#undef BAD
|
||||
#undef GOOD
|
||||
done:
|
||||
SMARTLIST_FOREACH(votes, networkstatus_t *, ns,
|
||||
{ smartlist_free(ns->package_lines); tor_free(ns); });
|
||||
tor_free(res);
|
||||
}
|
||||
|
||||
#define DIR_LEGACY(name) \
|
||||
{ #name, test_dir_ ## name , TT_FORK, NULL, NULL }
|
||||
|
||||
@ -2983,6 +3129,7 @@ struct testcase_t dir_tests[] = {
|
||||
DIR(http_handling, 0),
|
||||
DIR(purpose_needs_anonymity, 0),
|
||||
DIR(fetch_type, 0),
|
||||
DIR(packages, 0),
|
||||
END_OF_TESTCASES
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user