diff options
Diffstat (limited to 'src')
27 files changed, 1112 insertions, 109 deletions
diff --git a/src/app/config/config.c b/src/app/config/config.c index a26bebe8b1..817af22359 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -580,6 +580,7 @@ static const config_var_t option_vars_[] = { V(OutboundBindAddress, LINELIST, NULL), V(OutboundBindAddressOR, LINELIST, NULL), V(OutboundBindAddressExit, LINELIST, NULL), + V(OutboundBindAddressPT, LINELIST, NULL), OBSOLETE("PathBiasDisableRate"), V(PathBiasCircThreshold, INT, "-1"), @@ -2104,6 +2105,16 @@ options_act,(const or_options_t *old_options)) "in a non-anonymous mode. It will provide NO ANONYMITY."); } + /* 31851: OutboundBindAddressExit is relay-only */ + if (parse_outbound_addresses(options, 0, &msg) < 0) { + // LCOV_EXCL_START + log_warn(LD_BUG, "Failed parsing previously validated outbound " + "bind addresses: %s", msg); + tor_free(msg); + return -1; + // LCOV_EXCL_STOP + } + if (options->Bridges) { mark_bridge_list(); for (cl = options->Bridges; cl; cl = cl->next) { @@ -2265,16 +2276,6 @@ options_act,(const or_options_t *old_options)) tor_free(http_authenticator); } - /* 31851: OutboundBindAddressExit is relay-only */ - if (parse_outbound_addresses(options, 0, &msg) < 0) { - // LCOV_EXCL_START - log_warn(LD_BUG, "Failed parsing previously validated outbound " - "bind addresses: %s", msg); - tor_free(msg); - return -1; - // LCOV_EXCL_STOP - } - config_maybe_load_geoip_files_(options, old_options); if (geoip_is_loaded(AF_INET) && options->GeoIPExcludeUnknown) { @@ -7184,7 +7185,8 @@ parse_outbound_address_lines(const config_line_t *lines, outbound_addr_t type, "configured: %s", family==AF_INET?" IPv4":(family==AF_INET6?" IPv6":""), type==OUTBOUND_ADDR_OR?" OR": - (type==OUTBOUND_ADDR_EXIT?" exit":""), lines->value); + (type==OUTBOUND_ADDR_EXIT?" exit": + (type==OUTBOUND_ADDR_PT?" PT":"")), lines->value); return -1; } lines = lines->next; @@ -7207,7 +7209,7 @@ parse_outbound_addresses(or_options_t *options, int validate_only, char **msg) } if (parse_outbound_address_lines(options->OutboundBindAddress, - OUTBOUND_ADDR_EXIT_AND_OR, options, + OUTBOUND_ADDR_ANY, options, validate_only, msg) < 0) { goto err; } @@ -7224,6 +7226,12 @@ parse_outbound_addresses(or_options_t *options, int validate_only, char **msg) goto err; } + if (parse_outbound_address_lines(options->OutboundBindAddressPT, + OUTBOUND_ADDR_PT, options, validate_only, + msg) < 0) { + goto err; + } + return 0; err: return -1; diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h index 6201b6df40..d19afcb3f6 100644 --- a/src/app/config/or_options_st.h +++ b/src/app/config/or_options_st.h @@ -24,10 +24,29 @@ struct config_suite_t; struct routerset_t; /** Enumeration of outbound address configuration types: - * Exit-only, OR-only, or both */ -typedef enum {OUTBOUND_ADDR_EXIT, OUTBOUND_ADDR_OR, - OUTBOUND_ADDR_EXIT_AND_OR, - OUTBOUND_ADDR_MAX} outbound_addr_t; + * Exit-only, OR-only, PT-only, or any of them */ +typedef enum { + /** Outbound IP address for Exit connections. Controlled by the + * `OutboundBindAddressExit` configuration entry in torrc. */ + OUTBOUND_ADDR_EXIT, + + /** Outbound IP address for OR connections. Controlled by the + * `OutboundBindAddressOR` configuration entry in torrc. */ + OUTBOUND_ADDR_OR, + + /** Outbound IP address for PT connections. Controlled by the + * `OutboundBindAddressPT` configuration entry in torrc. */ + OUTBOUND_ADDR_PT, + + /** Outbound IP address for any outgoing connections. Controlled by the + * OutboundBindAddress configuration entry in torrc. This value is used as + * fallback if the more specific OUTBOUND_ADDR_EXIT, OUTBOUND_ADDR_OR, and + * OUTBOUND_ADDR_PT are unset. */ + OUTBOUND_ADDR_ANY, + + /** Max value for this enum. Must be the last element in this enum. */ + OUTBOUND_ADDR_MAX +} outbound_addr_t; /** Which protocol to use for TCPProxy. */ typedef enum { @@ -130,6 +149,8 @@ struct or_options_t { struct config_line_t *OutboundBindAddressOR; /** Local address to bind outbound exit sockets */ struct config_line_t *OutboundBindAddressExit; + /** Local address to bind outbound PT sockets */ + struct config_line_t *OutboundBindAddressPT; /** Addresses derived from the various OutboundBindAddress lines. * [][0] is IPv4, [][1] is IPv6 */ diff --git a/src/app/config/or_state_st.h b/src/app/config/or_state_st.h index 31b7f8a983..6769ef7b87 100644 --- a/src/app/config/or_state_st.h +++ b/src/app/config/or_state_st.h @@ -38,17 +38,11 @@ struct or_state_t { uint64_t AccountingBytesAtSoftLimit; uint64_t AccountingExpectedUsage; - /** A list of Entry Guard-related configuration lines. (pre-prop271) */ - struct config_line_t *EntryGuards; - - /** A list of guard-related configuration lines. (post-prop271) */ + /** A list of guard-related configuration lines. */ struct config_line_t *Guard; struct config_line_t *TransportProxies; - /** Cached revision counters for active hidden services on this host */ - struct config_line_t *HidServRevCounter; - /** These fields hold information on the history of bandwidth usage for * servers. The "Ends" fields hold the time when we last updated the * bandwidth usage. The "Interval" fields hold the granularity, in seconds, diff --git a/src/app/config/resolve_addr.c b/src/app/config/resolve_addr.c index b37707d2da..b811a68b8c 100644 --- a/src/app/config/resolve_addr.c +++ b/src/app/config/resolve_addr.c @@ -652,12 +652,12 @@ static const size_t fn_address_table_auth_len = * method_out is set to RESOLVED_ADDR_RESOLVED and hostname_out is set * to the resolved hostname. On failure to resolve, an error is returned. * - * If no given Address, fallback to the local hostname (see section 2). + * If no given Address, fallback to the network interface (see section 2). * * 2. Look at the network interface. * * Attempt to find the first public usable address from the list of - * network interface returned by the OS. + * network interfaces returned by the OS. * * On failure, we attempt to look at the local hostname (3). * diff --git a/src/app/config/statefile.c b/src/app/config/statefile.c index b25167d2ec..22b15fcf24 100644 --- a/src/app/config/statefile.c +++ b/src/app/config/statefile.c @@ -58,16 +58,38 @@ /** A list of state-file "abbreviations," for compatibility. */ static config_abbrev_t state_abbrevs_[] = { - { "AccountingBytesReadInterval", "AccountingBytesReadInInterval", 0, 0 }, - { "HelperNode", "EntryGuard", 0, 0 }, - { "HelperNodeDownSince", "EntryGuardDownSince", 0, 0 }, - { "HelperNodeUnlistedSince", "EntryGuardUnlistedSince", 0, 0 }, - { "EntryNode", "EntryGuard", 0, 0 }, - { "EntryNodeDownSince", "EntryGuardDownSince", 0, 0 }, - { "EntryNodeUnlistedSince", "EntryGuardUnlistedSince", 0, 0 }, { NULL, NULL, 0, 0}, }; +/** A list of obsolete keys that we do not and should not preserve. + * + * We could just let these live in ExtraLines indefinitely, but they're + * never going to be used again, and every version that used them + * has been obsolete for a long time. + * */ +static const char *obsolete_state_keys[] = { + /* These were renamed in 0.1.1.11-alpha */ + "AccountingBytesReadInterval", + "HelperNode", + "HelperNodeDownSince", + "HelperNodeUnlistedSince", + "EntryNode", + "HelperNodeDownSince", + "EntryNodeUnlistedSince", + /* These were replaced by "Guard" in 0.3.0.1-alpha. */ + "EntryGuard", + "EntryGuardDownSince", + "EntryGuardUnlistedSince", + "EntryGuardAddedBy", + "EntryGuardPathBias", + "EntryGuardPathUseBias", + /* This was replaced by OPE-based revision numbers in 0.3.5.1-alpha, + * and was never actually used in a released version. */ + "HidServRevCounter", + + NULL, +}; + /** dummy instance of or_state_t, used for type-checking its * members with CONF_CHECK_VAR_TYPE. */ DUMMY_TYPECHECK_INSTANCE(or_state_t); @@ -91,19 +113,9 @@ static const config_var_t state_vars_[] = { V(AccountingSoftLimitHitAt, ISOTIME, NULL), V(AccountingBytesAtSoftLimit, MEMUNIT, NULL), - VAR("EntryGuard", LINELIST_S, EntryGuards, NULL), - VAR("EntryGuardDownSince", LINELIST_S, EntryGuards, NULL), - VAR("EntryGuardUnlistedSince", LINELIST_S, EntryGuards, NULL), - VAR("EntryGuardAddedBy", LINELIST_S, EntryGuards, NULL), - VAR("EntryGuardPathBias", LINELIST_S, EntryGuards, NULL), - VAR("EntryGuardPathUseBias", LINELIST_S, EntryGuards, NULL), - V(EntryGuards, LINELIST_V, NULL), - VAR("TransportProxy", LINELIST_S, TransportProxies, NULL), V(TransportProxies, LINELIST_V, NULL), - V(HidServRevCounter, LINELIST, NULL), - V(BWHistoryReadEnds, ISOTIME, NULL), V(BWHistoryReadInterval, POSINT, "900"), V(BWHistoryReadValues, CSV, ""), @@ -475,6 +487,7 @@ or_state_load(void) } else { log_info(LD_GENERAL, "Initialized state"); } + or_state_remove_obsolete_lines(&new_state->ExtraLines); if (or_state_set(new_state) == -1) { or_state_save_broken(fname); } @@ -494,6 +507,36 @@ or_state_load(void) return r; } +/** Remove from `extra_lines` every element whose key appears in + * `obsolete_state_keys`. */ +STATIC void +or_state_remove_obsolete_lines(config_line_t **extra_lines) +{ + /* make a strmap for the obsolete state names, so we can have O(1) + lookup. */ + strmap_t *bad_keys = strmap_new(); + for (unsigned i = 0; obsolete_state_keys[i] != NULL; ++i) { + strmap_set_lc(bad_keys, obsolete_state_keys[i], (void*)"rmv"); + } + + config_line_t **line = extra_lines; + while (*line) { + if (strmap_get_lc(bad_keys, (*line)->key) != NULL) { + /* This key is obsolete; remove it. */ + config_line_t *victim = *line; + *line = (*line)->next; + + victim->next = NULL; // prevent double-free. + config_free_lines(victim); + } else { + /* This is just an unrecognized key; keep it. */ + line = &(*line)->next; + } + } + + strmap_free(bad_keys, NULL); +} + /** Did the last time we tried to write the state file fail? If so, we * should consider disabling such features as preemptive circuit generation * to compute circuit-build-time. */ diff --git a/src/app/config/statefile.h b/src/app/config/statefile.h index 98d9d2dda1..89b10560f3 100644 --- a/src/app/config/statefile.h +++ b/src/app/config/statefile.h @@ -33,6 +33,7 @@ STATIC void or_state_free_(or_state_t *state); STATIC or_state_t *or_state_new(void); struct config_mgr_t; STATIC const struct config_mgr_t *get_state_mgr(void); +STATIC void or_state_remove_obsolete_lines(struct config_line_t **extra_lines); #endif /* defined(STATEFILE_PRIVATE) */ #endif /* !defined(TOR_STATEFILE_H) */ diff --git a/src/app/main/main.c b/src/app/main/main.c index 31a4f141ad..ff530c0ad0 100644 --- a/src/app/main/main.c +++ b/src/app/main/main.c @@ -298,7 +298,7 @@ process_signal(int sig) } #ifdef _WIN32 -/** Activate SIGINT on reciving a control signal in console */ +/** Activate SIGINT on receiving a control signal in console. */ static BOOL WINAPI process_win32_console_ctrl(DWORD ctrl_type) { diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c index 29ffdaaf4e..c043b1ccec 100644 --- a/src/core/mainloop/connection.c +++ b/src/core/mainloop/connection.c @@ -2331,9 +2331,9 @@ conn_get_outbound_address(sa_family_t family, ext_addr = &options->OutboundBindAddresses[OUTBOUND_ADDR_EXIT] [fam_index]; } else if (!tor_addr_is_null( - &options->OutboundBindAddresses[OUTBOUND_ADDR_EXIT_AND_OR] + &options->OutboundBindAddresses[OUTBOUND_ADDR_ANY] [fam_index])) { - ext_addr = &options->OutboundBindAddresses[OUTBOUND_ADDR_EXIT_AND_OR] + ext_addr = &options->OutboundBindAddresses[OUTBOUND_ADDR_ANY] [fam_index]; } } else { // All non-exit connections @@ -2342,9 +2342,9 @@ conn_get_outbound_address(sa_family_t family, ext_addr = &options->OutboundBindAddresses[OUTBOUND_ADDR_OR] [fam_index]; } else if (!tor_addr_is_null( - &options->OutboundBindAddresses[OUTBOUND_ADDR_EXIT_AND_OR] + &options->OutboundBindAddresses[OUTBOUND_ADDR_ANY] [fam_index])) { - ext_addr = &options->OutboundBindAddresses[OUTBOUND_ADDR_EXIT_AND_OR] + ext_addr = &options->OutboundBindAddresses[OUTBOUND_ADDR_ANY] [fam_index]; } } diff --git a/src/feature/client/transports.c b/src/feature/client/transports.c index 2eb05d6a67..0e4361a210 100644 --- a/src/feature/client/transports.c +++ b/src/feature/client/transports.c @@ -1447,6 +1447,37 @@ create_managed_proxy_environment(const managed_proxy_t *mp) */ smartlist_add_asprintf(envs, "TOR_PT_EXIT_ON_STDIN_CLOSE=1"); + /* Specify which IPv4 and IPv6 addresses the PT should make its outgoing + * connections from. See: https://bugs.torproject.org/5304 for more + * information about this. */ + { + /* Set TOR_PT_OUTBOUND_BIND_ADDRESS_V4. */ + const tor_addr_t *ipv4_addr = managed_proxy_outbound_address(options, + AF_INET); + + /* managed_proxy_outbound_address() only returns a non-NULL value if + * tor_addr_is_null() was false, which means we don't have to check that + * here. */ + if (ipv4_addr) { + char *ipv4_addr_str = tor_addr_to_str_dup(ipv4_addr); + smartlist_add_asprintf(envs, + "TOR_PT_OUTBOUND_BIND_ADDRESS_V4=%s", + ipv4_addr_str); + tor_free(ipv4_addr_str); + } + + /* Set TOR_PT_OUTBOUND_BIND_ADDRESS_V6. */ + const tor_addr_t *ipv6_addr = managed_proxy_outbound_address(options, + AF_INET6); + if (ipv6_addr) { + char *ipv6_addr_str = tor_addr_to_str_dup(ipv6_addr); + smartlist_add_asprintf(envs, + "TOR_PT_OUTBOUND_BIND_ADDRESS_V6=[%s]", + ipv6_addr_str); + tor_free(ipv6_addr_str); + } + } + SMARTLIST_FOREACH_BEGIN(envs, const char *, env_var) { set_environment_variable_in_smartlist(merged_env_vars, env_var, tor_free_, 1); @@ -1919,3 +1950,46 @@ managed_proxy_severity_parse(const char *severity) return -1; } + +/** Return the outbound address from the given <b>family</b>. Returns NULL if + * the user haven't specified a specific outbound address in either + * OutboundBindAddress or OutboundBindAddressPT. */ +STATIC const tor_addr_t * +managed_proxy_outbound_address(const or_options_t *options, sa_family_t family) +{ + tor_assert(options); + + const tor_addr_t *address = NULL; + int family_index; + + switch (family) { + case AF_INET: + family_index = 0; + break; + case AF_INET6: + family_index = 1; + break; + default: + /* LCOV_EXCL_START */ + tor_assert_unreached(); + return NULL; + /* LCOV_EXCL_STOP */ + } + + /* We start by checking if the user specified an address in + * OutboundBindAddressPT. */ + address = &options->OutboundBindAddresses[OUTBOUND_ADDR_PT][family_index]; + + if (! tor_addr_is_null(address)) + return address; + + /* We fallback to check if the user specified an address in + * OutboundBindAddress. */ + address = &options->OutboundBindAddresses[OUTBOUND_ADDR_ANY][family_index]; + + if (! tor_addr_is_null(address)) + return address; + + /* The user have not specified a preference for outgoing connections. */ + return NULL; +} diff --git a/src/feature/client/transports.h b/src/feature/client/transports.h index 1ed942c175..3aff1cb248 100644 --- a/src/feature/client/transports.h +++ b/src/feature/client/transports.h @@ -149,6 +149,8 @@ STATIC void managed_proxy_stderr_callback(process_t *, const char *, size_t); STATIC bool managed_proxy_exit_callback(process_t *, process_exit_code_t); STATIC int managed_proxy_severity_parse(const char *); +STATIC const tor_addr_t *managed_proxy_outbound_address(const or_options_t *, + sa_family_t); #endif /* defined(PT_PRIVATE) */ diff --git a/src/feature/dirauth/dirvote.c b/src/feature/dirauth/dirvote.c index 8676df76ab..f56d3ef0a0 100644 --- a/src/feature/dirauth/dirvote.c +++ b/src/feature/dirauth/dirvote.c @@ -4,6 +4,7 @@ /* See LICENSE for licensing information */ #define DIRVOTE_PRIVATE + #include "core/or/or.h" #include "app/config/config.h" #include "app/config/resolve_addr.h" @@ -4177,8 +4178,8 @@ dirvote_dirreq_get_status_vote(const char *url, smartlist_t *items, /** Get the best estimate of a router's bandwidth for dirauth purposes, * preferring measured to advertised values if available. */ -static uint32_t -dirserv_get_bandwidth_for_router_kb(const routerinfo_t *ri) +MOCK_IMPL(uint32_t,dirserv_get_bandwidth_for_router_kb, + (const routerinfo_t *ri)) { uint32_t bw_kb = 0; /* @@ -4207,33 +4208,73 @@ dirserv_get_bandwidth_for_router_kb(const routerinfo_t *ri) return bw_kb; } -/** Helper for sorting: compares two routerinfos first by address, and then by - * descending order of "usefulness". (An authority is more useful than a - * non-authority; a running router is more useful than a non-running router; - * and a router with more bandwidth is more useful than one with less.) +/** + * Helper: compare the address of family `family` in `a` with the address in + * `b`. The family must be one of `AF_INET` and `AF_INET6`. **/ static int -compare_routerinfo_by_ip_and_bw_(const void **a, const void **b) +compare_routerinfo_addrs_by_family(const routerinfo_t *a, + const routerinfo_t *b, + int family) +{ + const tor_addr_t *addr1 = (family==AF_INET) ? &a->ipv4_addr : &a->ipv6_addr; + const tor_addr_t *addr2 = (family==AF_INET) ? &b->ipv4_addr : &b->ipv6_addr; + const int maskbits = (family==AF_INET) ? 32 : 64; + return tor_addr_compare_masked(addr1, addr2, maskbits, CMP_EXACT); +} + +/** Helper for sorting: compares two ipv4 routerinfos first by ipv4 address, + * and then by descending order of "usefulness" + * (see compare_routerinfo_usefulness) + **/ +STATIC int +compare_routerinfo_by_ipv4(const void **a, const void **b) +{ + const routerinfo_t *first = *(const routerinfo_t **)a; + const routerinfo_t *second = *(const routerinfo_t **)b; + int comparison = compare_routerinfo_addrs_by_family(first, second, AF_INET); + if (comparison == 0) { + // If addresses are equal, use other comparison criteria + return compare_routerinfo_usefulness(first, second); + } else { + return comparison; + } +} + +/** Helper for sorting: compares two ipv6 routerinfos first by ipv6 address, + * and then by descending order of "usefulness" + * (see compare_routerinfo_usefulness) + **/ +STATIC int +compare_routerinfo_by_ipv6(const void **a, const void **b) +{ + const routerinfo_t *first = *(const routerinfo_t **)a; + const routerinfo_t *second = *(const routerinfo_t **)b; + int comparison = compare_routerinfo_addrs_by_family(first, second, AF_INET6); + // If addresses are equal, use other comparison criteria + if (comparison == 0) + return compare_routerinfo_usefulness(first, second); + else + return comparison; +} + +/** +* Compare routerinfos by descending order of "usefulness" : +* An authority is more useful than a non-authority; a running router is +* more useful than a non-running router; and a router with more bandwidth +* is more useful than one with less. +**/ +STATIC int +compare_routerinfo_usefulness(const routerinfo_t *first, + const routerinfo_t *second) { - routerinfo_t *first = *(routerinfo_t **)a, *second = *(routerinfo_t **)b; int first_is_auth, second_is_auth; - uint32_t bw_kb_first, bw_kb_second; const node_t *node_first, *node_second; int first_is_running, second_is_running; - uint32_t first_ipv4h = tor_addr_to_ipv4h(&first->ipv4_addr); - uint32_t second_ipv4h = tor_addr_to_ipv4h(&second->ipv4_addr); - - /* we return -1 if first should appear before second... that is, - * if first is a better router. */ - if (first_ipv4h < second_ipv4h) - return -1; - else if (first_ipv4h > second_ipv4h) - return 1; - + uint32_t bw_kb_first, bw_kb_second; /* Potentially, this next bit could cause k n lg n memeq calls. But in * reality, we will almost never get here, since addresses will usually be * different. */ - first_is_auth = router_digest_is_trusted_dir(first->cache_info.identity_digest); second_is_auth = @@ -4248,7 +4289,6 @@ compare_routerinfo_by_ip_and_bw_(const void **a, const void **b) node_second = node_get_by_id(second->cache_info.identity_digest); first_is_running = node_first && node_first->is_running; second_is_running = node_second && node_second->is_running; - if (first_is_running && !second_is_running) return -1; else if (!first_is_running && second_is_running) @@ -4269,40 +4309,89 @@ compare_routerinfo_by_ip_and_bw_(const void **a, const void **b) DIGEST_LEN); } -/** Given a list of routerinfo_t in <b>routers</b>, return a new digestmap_t - * whose keys are the identity digests of those routers that we're going to - * exclude for Sybil-like appearance. */ -static digestmap_t * -get_possible_sybil_list(const smartlist_t *routers) +/** Given a list of routerinfo_t in <b>routers</b> that all use the same + * IP version, specified in <b>family</b>, return a new digestmap_t whose keys + * are the identity digests of those routers that we're going to exclude for + * Sybil-like appearance. + */ +STATIC digestmap_t * +get_sybil_list_by_ip_version(const smartlist_t *routers, sa_family_t family) { const dirauth_options_t *options = dirauth_get_options(); - digestmap_t *omit_as_sybil; + digestmap_t *omit_as_sybil = digestmap_new(); smartlist_t *routers_by_ip = smartlist_new(); - tor_addr_t last_addr = TOR_ADDR_NULL; - int addr_count; + int addr_count = 0; + routerinfo_t *last_ri = NULL; /* Allow at most this number of Tor servers on a single IP address, ... */ int max_with_same_addr = options->AuthDirMaxServersPerAddr; if (max_with_same_addr <= 0) max_with_same_addr = INT_MAX; smartlist_add_all(routers_by_ip, routers); - smartlist_sort(routers_by_ip, compare_routerinfo_by_ip_and_bw_); - omit_as_sybil = digestmap_new(); + if (family == AF_INET6) + smartlist_sort(routers_by_ip, compare_routerinfo_by_ipv6); + else + smartlist_sort(routers_by_ip, compare_routerinfo_by_ipv4); - addr_count = 0; SMARTLIST_FOREACH_BEGIN(routers_by_ip, routerinfo_t *, ri) { - if (!tor_addr_eq(&last_addr, &ri->ipv4_addr)) { - tor_addr_copy(&last_addr, &ri->ipv4_addr); + bool addrs_equal; + if (last_ri) + addrs_equal = !compare_routerinfo_addrs_by_family(last_ri, ri, family); + else + addrs_equal = false; + + if (! addrs_equal) { + last_ri = ri; addr_count = 1; } else if (++addr_count > max_with_same_addr) { digestmap_set(omit_as_sybil, ri->cache_info.identity_digest, ri); } } SMARTLIST_FOREACH_END(ri); - smartlist_free(routers_by_ip); return omit_as_sybil; } +/** Given a list of routerinfo_t in <b>routers</b>, return a new digestmap_t + * whose keys are the identity digests of those routers that we're going to + * exclude for Sybil-like appearance. */ +STATIC digestmap_t * +get_all_possible_sybil(const smartlist_t *routers) +{ + smartlist_t *routers_ipv6, *routers_ipv4; + routers_ipv6 = smartlist_new(); + routers_ipv4 = smartlist_new(); + digestmap_t *omit_as_sybil_ipv4; + digestmap_t *omit_as_sybil_ipv6; + digestmap_t *omit_as_sybil = digestmap_new(); + // Sort the routers in two lists depending on their IP version + SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) { + // If the router has an IPv6 address + if (tor_addr_family(&(ri->ipv6_addr)) == AF_INET6) { + smartlist_add(routers_ipv6, ri); + } + // If the router has an IPv4 address + if (tor_addr_family(&(ri->ipv4_addr)) == AF_INET) { + smartlist_add(routers_ipv4, ri); + } + } SMARTLIST_FOREACH_END(ri); + omit_as_sybil_ipv4 = get_sybil_list_by_ip_version(routers_ipv4, AF_INET); + omit_as_sybil_ipv6 = get_sybil_list_by_ip_version(routers_ipv6, AF_INET6); + + // Add all possible sybils to the common digestmap + DIGESTMAP_FOREACH (omit_as_sybil_ipv4, sybil_id, routerinfo_t *, ri) { + digestmap_set(omit_as_sybil, ri->cache_info.identity_digest, ri); + } DIGESTMAP_FOREACH_END; + DIGESTMAP_FOREACH (omit_as_sybil_ipv6, sybil_id, routerinfo_t *, ri) { + digestmap_set(omit_as_sybil, ri->cache_info.identity_digest, ri); + } DIGESTMAP_FOREACH_END; + // Clean the temp variables + smartlist_free(routers_ipv4); + smartlist_free(routers_ipv6); + digestmap_free(omit_as_sybil_ipv4, NULL); + digestmap_free(omit_as_sybil_ipv6, NULL); + // Return the digestmap: it now contains all the possible sybils + return omit_as_sybil; +} /** Given a platform string as in a routerinfo_t (possibly null), return a * newly allocated version string for a networkstatus document, or NULL if the * platform doesn't give a Tor version. */ @@ -4477,7 +4566,6 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH; networkstatus_voter_info_t *voter = NULL; vote_timing_t timing; - digestmap_t *omit_as_sybil = NULL; const int vote_on_reachability = running_long_enough_to_decide_unreachable(); smartlist_t *microdescriptors = NULL; smartlist_t *bw_file_headers = NULL; @@ -4547,19 +4635,16 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, routers_make_ed_keys_unique(routers); /* After this point, don't use rl->routers; use 'routers' instead. */ routers_sort_by_identity(routers); - omit_as_sybil = get_possible_sybil_list(routers); - - DIGESTMAP_FOREACH(omit_as_sybil, sybil_id, void *, ignore) { - (void) ignore; + /* Get a digestmap of possible sybil routers, IPv4 or IPv6 */ + digestmap_t *omit_as_sybil = get_all_possible_sybil(routers); + DIGESTMAP_FOREACH (omit_as_sybil, sybil_id, void *, ignore) { + (void)ignore; rep_hist_make_router_pessimal(sybil_id, now); - } DIGESTMAP_FOREACH_END; - + } DIGESTMAP_FOREACH_END /* Count how many have measured bandwidths so we know how to assign flags; * this must come before dirserv_compute_performance_thresholds() */ dirserv_count_measured_bws(routers); - dirserv_compute_performance_thresholds(omit_as_sybil); - routerstatuses = smartlist_new(); microdescriptors = smartlist_new(); @@ -4587,7 +4672,6 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, ri->cache_info.signing_key_cert->signing_key.pubkey, ED25519_PUBKEY_LEN); } - if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest)) clear_status_flags_on_sybil(rs); diff --git a/src/feature/dirauth/dirvote.h b/src/feature/dirauth/dirvote.h index 9cc87489b4..a4f1b8bfe9 100644 --- a/src/feature/dirauth/dirvote.h +++ b/src/feature/dirauth/dirvote.h @@ -183,6 +183,8 @@ dirvote_add_signatures(const char *detached_signatures_body, /* Item access */ MOCK_DECL(const char*, dirvote_get_pending_consensus, (consensus_flavor_t flav)); +MOCK_DECL(uint32_t,dirserv_get_bandwidth_for_router_kb, + (const routerinfo_t *ri)); MOCK_DECL(const char*, dirvote_get_pending_detached_signatures, (void)); const cached_dir_t *dirvote_get_vote(const char *fp, int flags); @@ -234,6 +236,22 @@ int networkstatus_add_detached_signatures(networkstatus_t *target, const char *source, int severity, const char **msg_out); +STATIC int +compare_routerinfo_usefulness(const routerinfo_t *first, + const routerinfo_t *second); +STATIC +int compare_routerinfo_by_ipv4(const void **a, const void **b); + +STATIC +int compare_routerinfo_by_ipv6(const void **a, const void **b); + +STATIC +digestmap_t * get_sybil_list_by_ip_version( + const smartlist_t *routers, sa_family_t family); + +STATIC +digestmap_t * get_all_possible_sybil(const smartlist_t *routers); + STATIC char *networkstatus_get_detached_signatures(smartlist_t *consensuses); STATIC microdesc_t *dirvote_create_microdescriptor(const routerinfo_t *ri, diff --git a/src/feature/dircache/dircache_stub.c b/src/feature/dircache/dircache_stub.c index 87811597d1..725c44bd4d 100644 --- a/src/feature/dircache/dircache_stub.c +++ b/src/feature/dircache/dircache_stub.c @@ -14,6 +14,7 @@ #include "feature/dircache/dirserv.h" #include "feature/dircommon/dir_connection_st.h" +DISABLE_GCC_WARNING("-Wmissing-noreturn") int directory_handle_command(dir_connection_t *conn) { @@ -29,6 +30,7 @@ connection_dirserv_flushed_some(dir_connection_t *conn) tor_assert_nonfatal_unreached_once(); return -1; } +ENABLE_GCC_WARNING("-Wmissing-noreturn") void dir_conn_clear_spool(dir_connection_t *conn) diff --git a/src/feature/hs/hs_circuitmap.c b/src/feature/hs/hs_circuitmap.c index 466a02de39..e46b008a5c 100644 --- a/src/feature/hs/hs_circuitmap.c +++ b/src/feature/hs/hs_circuitmap.c @@ -275,7 +275,7 @@ hs_circuitmap_get_or_circuit(hs_token_type_t type, /** Public function: Return v2 and v3 introduction circuit to this relay. * Always return a newly allocated list for which it is the caller's - * responsability to free it. */ + * responsibility to free it. */ smartlist_t * hs_circuitmap_get_all_intro_circ_relay_side(void) { diff --git a/src/feature/nodelist/dirlist.c b/src/feature/nodelist/dirlist.c index cd2921e653..f6e4662a0f 100644 --- a/src/feature/nodelist/dirlist.c +++ b/src/feature/nodelist/dirlist.c @@ -236,8 +236,8 @@ mark_all_dirservers_up(smartlist_t *server_list) /** Return true iff <b>digest</b> is the digest of the identity key of a * trusted directory matching at least one bit of <b>type</b>. If <b>type</b> * is zero (NO_DIRINFO), or ALL_DIRINFO, any authority is okay. */ -int -router_digest_is_trusted_dir_type(const char *digest, dirinfo_type_t type) +MOCK_IMPL(int, router_digest_is_trusted_dir_type, + (const char *digest, dirinfo_type_t type)) { if (!trusted_dir_servers) return 0; diff --git a/src/feature/nodelist/dirlist.h b/src/feature/nodelist/dirlist.h index c9310ff357..ae3debf4e5 100644 --- a/src/feature/nodelist/dirlist.h +++ b/src/feature/nodelist/dirlist.h @@ -25,13 +25,14 @@ int router_digest_is_fallback_dir(const char *digest); MOCK_DECL(dir_server_t *, trusteddirserver_get_by_v3_auth_digest, (const char *d)); +MOCK_DECL(int, router_digest_is_trusted_dir_type, + (const char *digest, dirinfo_type_t type)); + bool router_addr_is_trusted_dir_type(const tor_addr_t *addr, dirinfo_type_t type); #define router_addr_is_trusted_dir(d) \ router_addr_is_trusted_dir_type((d), NO_DIRINFO) -int router_digest_is_trusted_dir_type(const char *digest, - dirinfo_type_t type); #define router_digest_is_trusted_dir(d) \ router_digest_is_trusted_dir_type((d), NO_DIRINFO) diff --git a/src/feature/nodelist/networkstatus.c b/src/feature/nodelist/networkstatus.c index 9210518de0..ece3c9e059 100644 --- a/src/feature/nodelist/networkstatus.c +++ b/src/feature/nodelist/networkstatus.c @@ -2444,7 +2444,12 @@ networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now) return answer; } -/* DOCDOC get_net_param_from_list */ +/** + * Search through a smartlist of "key=int32" strings for a value beginning + * with "param_name=". If one is found, clip it to be between min_val and + * max_val inclusive and return it. If one is not found, return + * default_val. + ***/ static int32_t get_net_param_from_list(smartlist_t *net_params, const char *param_name, int32_t default_val, int32_t min_val, int32_t max_val) diff --git a/src/feature/nodelist/nodelist.c b/src/feature/nodelist/nodelist.c index 7edc1fc51c..c9928d2f9b 100644 --- a/src/feature/nodelist/nodelist.c +++ b/src/feature/nodelist/nodelist.c @@ -1947,7 +1947,7 @@ node_get_curve25519_onion_key(const node_t *node) /* Return a newly allocacted RSA onion public key taken from the given node. * * Return NULL if node is NULL or no RSA onion public key can be found. It is - * the caller responsability to free the returned object. */ + * the caller responsibility to free the returned object. */ crypto_pk_t * node_get_rsa_onion_key(const node_t *node) { diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index 11847a2616..5ca21964b6 100644 --- a/src/feature/relay/router.c +++ b/src/feature/relay/router.c @@ -2607,6 +2607,10 @@ check_descriptor_bandwidth_changed(time_t now) } } +// This function can be "noreturn" if relay mode is disabled and +// ALL_BUGS_ARE_FATAL is set. +DISABLE_GCC_WARNING("-Wmissing-noreturn") + /** Note at log level severity that our best guess of address has changed from * <b>prev</b> to <b>cur</b>. */ void @@ -2636,6 +2640,7 @@ log_addr_has_changed(int severity, "Guessed our IP address as %s (source: %s).", addrbuf_cur, source); } +ENABLE_GCC_WARNING("-Wmissing-noreturn") /** Check whether our own address has changed versus the one we have in our * current descriptor. @@ -3570,7 +3575,7 @@ router_set_rsa_onion_pkey(const crypto_pk_t *pk, char **onion_pkey_out, } /* From an ASN-1 encoded onion pkey, return a newly allocated RSA key object. - * It is the caller responsability to free the returned object. + * It is the caller's responsibility to free the returned object. * * Return NULL if the pkey is NULL, malformed or if the length is 0. */ crypto_pk_t * diff --git a/src/lib/fs/path.c b/src/lib/fs/path.c index 1a15969419..2eef4bded7 100644 --- a/src/lib/fs/path.c +++ b/src/lib/fs/path.c @@ -598,6 +598,12 @@ tor_glob(const char *pattern) return NULL; } + // #40141: workaround for bug in glibc < 2.19 where patterns ending in path + // separator match files and folders instead of folders only + size_t pattern_len = strlen(pattern); + bool dir_only = has_glob(pattern) && + pattern_len > 0 && pattern[pattern_len-1] == *PATH_SEPARATOR; + result = smartlist_new(); size_t i; for (i = 0; i < matches.gl_pathc; i++) { @@ -606,7 +612,12 @@ tor_glob(const char *pattern) if (len > 0 && match[len-1] == *PATH_SEPARATOR) { match[len-1] = '\0'; } - smartlist_add(result, match); + + if (!dir_only || (dir_only && is_dir(file_status(match)))) { + smartlist_add(result, match); + } else { + tor_free(match); + } } globfree(&matches); #else diff --git a/src/lib/net/address.c b/src/lib/net/address.c index 5a32533610..ea6c29db9f 100644 --- a/src/lib/net/address.c +++ b/src/lib/net/address.c @@ -1731,8 +1731,8 @@ get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr)) /* Get a list of public or internal IPs in arbitrary order */ addrs = get_interface_address6_list(severity, family, 1); - /* Find the first non-internal address, or the last internal address - * Ideally, we want the default route, see #12377 for details */ + /* Find the first non-internal address, or the last internal address. + * Ideally, we want the default route; see #12377 for details. */ SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a) { tor_addr_copy(addr, a); const bool is_internal = tor_addr_is_internal(a, 0); diff --git a/src/test/include.am b/src/test/include.am index 816eba894e..173f007fbf 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -171,6 +171,7 @@ src_test_test_SOURCES += \ src/test/test_crypto_rng.c \ src/test/test_data.c \ src/test/test_dir.c \ + src/test/test_dirvote.c \ src/test/test_dir_common.c \ src/test/test_dir_handle_get.c \ src/test/test_dispatch.c \ @@ -234,6 +235,7 @@ src_test_test_SOURCES += \ src/test/test_sendme.c \ src/test/test_shared_random.c \ src/test/test_socks.c \ + src/test/test_statefile.c \ src/test/test_stats.c \ src/test/test_status.c \ src/test/test_storagedir.c \ diff --git a/src/test/test.c b/src/test/test.c index 2961669c46..77aa6db975 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -709,6 +709,7 @@ struct testgroup_t testgroups[] = { { "dir/", dir_tests }, { "dir/auth/process_descs/", process_descs_tests }, { "dir/md/", microdesc_tests }, + { "dirauth/dirvote/", dirvote_tests}, { "dir/voting/flags/", voting_flags_tests }, { "dir/voting/schedule/", voting_schedule_tests }, { "dir_handle_get/", dir_handle_get_tests }, @@ -769,6 +770,7 @@ struct testgroup_t testgroups[] = { { "sendme/", sendme_tests }, { "shared-random/", sr_tests }, { "socks/", socks_tests }, + { "statefile/", statefile_tests }, { "stats/", stats_tests }, { "status/" , status_tests }, { "storagedir/", storagedir_tests }, diff --git a/src/test/test.h b/src/test/test.h index 18987719d0..bd3a4102f5 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -122,6 +122,7 @@ extern struct testcase_t crypto_rng_tests[]; extern struct testcase_t crypto_tests[]; extern struct testcase_t dir_handle_get_tests[]; extern struct testcase_t dir_tests[]; +extern struct testcase_t dirvote_tests[]; extern struct testcase_t dispatch_tests[]; extern struct testcase_t dns_tests[]; extern struct testcase_t dos_tests[]; @@ -186,6 +187,7 @@ extern struct testcase_t scheduler_tests[]; extern struct testcase_t sendme_tests[]; extern struct testcase_t socks_tests[]; extern struct testcase_t sr_tests[]; +extern struct testcase_t statefile_tests[]; extern struct testcase_t stats_tests[]; extern struct testcase_t status_tests[]; extern struct testcase_t storagedir_tests[]; diff --git a/src/test/test_connection.c b/src/test/test_connection.c index 954aeb82e3..178a37adf6 100644 --- a/src/test/test_connection.c +++ b/src/test/test_connection.c @@ -617,7 +617,8 @@ test_conn_download_status(void *arg) connection_t *ap_conn = NULL; const struct testcase_t *tc = arg; - consensus_flavor_t usable_flavor = (consensus_flavor_t)tc->setup_data; + consensus_flavor_t usable_flavor = + networkstatus_parse_flavor_name((const char*) tc->setup_data); /* The "other flavor" trick only works if there are two flavors */ tor_assert(N_CONSENSUS_FLAVORS == 2); @@ -1093,17 +1094,17 @@ struct testcase_t connection_tests[] = { CONNECTION_TESTCASE(get_rend, TT_FORK, test_conn_get_rend_st), CONNECTION_TESTCASE(get_rsrc, TT_FORK, test_conn_get_rsrc_st), - CONNECTION_TESTCASE_ARG(download_status, TT_FORK, - test_conn_download_status_st, FLAV_MICRODESC), - CONNECTION_TESTCASE_ARG(download_status, TT_FORK, - test_conn_download_status_st, FLAV_NS), + CONNECTION_TESTCASE_ARG(download_status, TT_FORK, + test_conn_download_status_st, "microdesc"), + CONNECTION_TESTCASE_ARG(download_status, TT_FORK, + test_conn_download_status_st, "ns"), CONNECTION_TESTCASE_ARG(https_proxy_connect, TT_FORK, test_conn_proxy_connect_st, &PROXY_CONNECT_ARG), CONNECTION_TESTCASE_ARG(haproxy_proxy_connect, TT_FORK, test_conn_proxy_connect_st, &PROXY_HAPROXY_ARG), -//CONNECTION_TESTCASE(func_suffix, TT_FORK, setup_func_pair), + //CONNECTION_TESTCASE(func_suffix, TT_FORK, setup_func_pair), { "failed_orconn_tracker", test_failed_orconn_tracker, TT_FORK, NULL, NULL }, { "describe", test_conn_describe, TT_FORK, NULL, NULL }, END_OF_TESTCASES diff --git a/src/test/test_dirvote.c b/src/test/test_dirvote.c new file mode 100644 index 0000000000..bc2d1150d6 --- /dev/null +++ b/src/test/test_dirvote.c @@ -0,0 +1,671 @@ +/* Copyright (c) 2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_dirvote.c + * \brief Unit tests for dirvote related functions + */ +#define DIRVOTE_PRIVATE + +#include "core/or/or.h" +#include "feature/dirauth/dirvote.h" +#include "feature/nodelist/dirlist.h" +#include "feature/nodelist/node_st.h" +#include "feature/nodelist/nodelist.h" +#include "feature/nodelist/routerinfo_st.h" +#include "feature/nodelist/signed_descriptor_st.h" + +#include "test/test.h" + +/** + * This struct holds the various informations that are needed for router + * comparison. Each router in the test function has one, and they are all + * put in a global digestmap, router_properties + */ +typedef struct router_values_t { + int is_running; + int is_auth; + int bw_kb; + char digest[DIGEST_LEN]; +} router_values_t; +/** + * This typedef makes declaring digests easier and less verbose + */ +typedef char sha1_digest_t[DIGEST_LEN]; + +// Use of global variable is justified because the functions that have to be +// mocked take as arguments objects we have no control over +static digestmap_t *router_properties = NULL; +// Use of global variable is justified by its use in nodelist.c +// and is necessary to avoid memory leaks when mocking the +// function node_get_by_id +static node_t *running_node; +static node_t *non_running_node; + +/* Allocate memory to the global variables that represent a running + * and non-running node + */ +#define ALLOCATE_MOCK_NODES() \ + running_node = tor_malloc(sizeof(node_t)); \ + running_node->is_running = 1; \ + non_running_node = tor_malloc(sizeof(node_t)); \ + non_running_node->is_running = 0; + +/* Free the memory allocated to the mock nodes */ +#define FREE_MOCK_NODES() \ + tor_free(running_node); \ + tor_free(non_running_node); + +static int +mock_router_digest_is_trusted(const char *digest, dirinfo_type_t type) +{ + (void)type; + router_values_t *mock_status; + mock_status = digestmap_get(router_properties, digest); + if (!mock_status) { + return -1; + } + return mock_status->is_auth; +} + +static const node_t * +mock_node_get_by_id(const char *identity_digest) +{ + router_values_t *status; + status = digestmap_get(router_properties, identity_digest); + if (!status) { + return NULL; + } + if (status->is_running) + return running_node; + else + return non_running_node; +} + +static uint32_t +mock_dirserv_get_bw(const routerinfo_t *ri) +{ + const char *digest = ri->cache_info.identity_digest; + router_values_t *status; + status = digestmap_get(router_properties, digest); + if (!status) { + return -1; + } + return status->bw_kb; +} + +/** Generate a pointer to a router_values_t struct with the arguments as + * field values, and return it + * The returned pointer has to be freed by the caller. + */ +static router_values_t * +router_values_new(int running, int auth, int bw, char *digest) +{ + router_values_t *status = tor_malloc(sizeof(router_values_t)); + memcpy(status->digest, digest, sizeof(status->digest)); + status->is_running = running; + status->bw_kb = bw; + status->is_auth = auth; + return status; +} + +/** Given a router_values_t struct, generate a pointer to a routerinfo struct. + * In the cache_info member, put the identity digest, and depending on + * the family argument, fill the IPv4 or IPv6 address. Return the pointer. + * The returned pointer has to be freed by the caller. + */ +static routerinfo_t * +routerinfo_new(router_values_t *status, int family, int addr) +{ + routerinfo_t *ri = tor_malloc(sizeof(routerinfo_t)); + signed_descriptor_t cache_info; + memcpy(cache_info.identity_digest, status->digest, + sizeof(cache_info.identity_digest)); + ri->cache_info = cache_info; + tor_addr_t ipv6, ipv4; + ipv6.family = family; + ipv4.family = family; + // Set the address of the other IP version to 0 + if (family == AF_INET) { + ipv4.addr.in_addr.s_addr = addr; + for (size_t i = 0; i < 16; i++) { + ipv6.addr.in6_addr.s6_addr[i] = 0; + } + } else { + for (size_t i = 0; i < 16; i++) { + ipv6.addr.in6_addr.s6_addr[i] = addr; + } + ipv4.addr.in_addr.s_addr = 0; + } + ri->ipv6_addr = ipv6; + ri->ipv4_addr = ipv4; + return ri; +} + +static void +test_dirvote_compare_routerinfo_usefulness(void *arg) +{ + (void)arg; + MOCK(router_digest_is_trusted_dir_type, mock_router_digest_is_trusted); + MOCK(node_get_by_id, mock_node_get_by_id); + MOCK(dirserv_get_bandwidth_for_router_kb, mock_dirserv_get_bw); + ALLOCATE_MOCK_NODES(); + router_properties = digestmap_new(); + + // The router one is the "least useful" router, every router is compared to + // it + sha1_digest_t digest_one = "aaaa"; + router_values_t *status_one = router_values_new(0, 0, 0, digest_one); + digestmap_set(router_properties, status_one->digest, status_one); + sha1_digest_t digest_two = "bbbb"; + router_values_t *status_two = router_values_new(0, 1, 0, digest_two); + digestmap_set(router_properties, status_two->digest, status_two); + sha1_digest_t digest_three = "cccc"; + router_values_t *status_three = router_values_new(1, 0, 0, digest_three); + digestmap_set(router_properties, status_three->digest, status_three); + sha1_digest_t digest_four = "dddd"; + router_values_t *status_four = router_values_new(0, 0, 128, digest_four); + digestmap_set(router_properties, status_four->digest, status_four); + sha1_digest_t digest_five = "9999"; + router_values_t *status_five = router_values_new(0, 0, 0, digest_five); + digestmap_set(router_properties, status_five->digest, status_five); + + // A router that has auth status is more useful than a non-auth one + routerinfo_t *first = routerinfo_new(status_one, AF_INET, 0xf); + routerinfo_t *second = routerinfo_new(status_two, AF_INET, 0xf); + int a = compare_routerinfo_usefulness(first, second); + tt_assert(a == 1); + tor_free(second); + + // A running router is more useful than a non running one + routerinfo_t *third = routerinfo_new(status_three, AF_INET, 0xf); + a = compare_routerinfo_usefulness(first, third); + tt_assert(a == 1); + tor_free(third); + + // A higher bandwidth is more useful + routerinfo_t *fourth = routerinfo_new(status_four, AF_INET, 0xf); + a = compare_routerinfo_usefulness(first, fourth); + tt_assert(a == 1); + tor_free(fourth); + + // In case of tie, the digests are compared + routerinfo_t *fifth = routerinfo_new(status_five, AF_INET, 0xf); + a = compare_routerinfo_usefulness(first, fifth); + tt_assert(a > 0); + tor_free(fifth); + +done: + UNMOCK(router_digest_is_trusted_dir_type); + UNMOCK(node_get_by_id); + UNMOCK(dirserv_get_bandwidth_for_router_kb); + FREE_MOCK_NODES(); + digestmap_free(router_properties, NULL); + tor_free(status_one); + tor_free(status_two); + tor_free(status_three); + tor_free(status_four); + tor_free(status_five); + tor_free(first); +} + +static void +test_dirvote_compare_routerinfo_by_ipv4(void *arg) +{ + (void)arg; + MOCK(router_digest_is_trusted_dir_type, mock_router_digest_is_trusted); + MOCK(node_get_by_id, mock_node_get_by_id); + MOCK(dirserv_get_bandwidth_for_router_kb, mock_dirserv_get_bw); + + ALLOCATE_MOCK_NODES(); + router_properties = digestmap_new(); + sha1_digest_t digest_one = "aaaa"; + router_values_t *status_one = router_values_new(0, 0, 0, digest_one); + digestmap_set(router_properties, status_one->digest, status_one); + sha1_digest_t digest_two = "bbbb"; + router_values_t *status_two = router_values_new(0, 1, 0, digest_two); + digestmap_set(router_properties, status_two->digest, status_two); + + // Both routers have an IPv4 address + routerinfo_t *first = routerinfo_new(status_one, AF_INET, 1); + routerinfo_t *second = routerinfo_new(status_two, AF_INET, 0xf); + + // The first argument's address precedes the seconds' one + int a = compare_routerinfo_by_ipv4((const void **)&first, + (const void **)&second); + tt_assert(a < 0); + // The second argument's address precedes the first' one + a = compare_routerinfo_by_ipv4((const void **)&second, + (const void **)&first); + tt_assert(a > 0); + tor_addr_copy(&(second->ipv4_addr), &(first->ipv6_addr)); + // The addresses are equal, they are compared by usefulness, + // and first is less useful than second + a = compare_routerinfo_by_ipv4((const void **)&first, + (const void **)&second); + tt_assert(a == 1); +done: + UNMOCK(router_digest_is_trusted_dir_type); + UNMOCK(node_get_by_id); + UNMOCK(dirserv_get_bandwidth_for_router_kb); + FREE_MOCK_NODES(); + digestmap_free(router_properties, NULL); + tor_free(status_one); + tor_free(status_two); + tor_free(first); + tor_free(second); +} + +static void +test_dirvote_compare_routerinfo_by_ipv6(void *arg) +{ + (void)arg; + MOCK(router_digest_is_trusted_dir_type, mock_router_digest_is_trusted); + MOCK(node_get_by_id, mock_node_get_by_id); + MOCK(dirserv_get_bandwidth_for_router_kb, mock_dirserv_get_bw); + + ALLOCATE_MOCK_NODES(); + router_properties = digestmap_new(); + char digest_one[DIGEST_LEN] = "aaaa"; + router_values_t *status_one = router_values_new(0, 0, 0, digest_one); + digestmap_set(router_properties, status_one->digest, status_one); + char digest_two[DIGEST_LEN] = "bbbb"; + router_values_t *status_two = router_values_new(0, 1, 0, digest_two); + digestmap_set(router_properties, status_two->digest, status_two); + + // Both routers have an IPv6 address + routerinfo_t *first = routerinfo_new(status_one, AF_INET6, 1); + routerinfo_t *second = routerinfo_new(status_two, AF_INET6, 0xf); + + // The first argument's address precedes the seconds' one + int a = compare_routerinfo_by_ipv6((const void **)&first, + (const void **)&second); + tt_assert(a < 0); + // The second argument's address precedes the first' one + a = compare_routerinfo_by_ipv6((const void **)&second, + (const void **)&first); + tt_assert(a > 0); + tor_addr_copy(&(first->ipv6_addr), &(second->ipv6_addr)); + // The addresses are equal, they are compared by usefulness, + // and first is less useful than second + a = compare_routerinfo_by_ipv6((const void **)&first, + (const void **)&second); + tt_assert(a == 1); +done: + UNMOCK(router_digest_is_trusted_dir_type); + UNMOCK(node_get_by_id); + UNMOCK(dirserv_get_bandwidth_for_router_kb); + FREE_MOCK_NODES(); + digestmap_free(router_properties, NULL); + tor_free(status_one); + tor_free(status_two); + tor_free(first); + tor_free(second); +} + +/** Create routers values and routerinfos that always have the same + * characteristics, and add them to the global digestmap. This macro is here to + * avoid duplicated code fragments. + * The created name##_val pointer should be freed by the caller (and cannot + * be freed in the macro as it causes a heap-after-free error) + */ +#define CREATE_ROUTER(digest, name, addr, ip_version) \ + sha1_digest_t name##_digest = digest; \ + name##_val = router_values_new(1, 1, 1, name##_digest); \ + digestmap_set(router_properties, name##_digest, name##_val); \ + name##_ri = routerinfo_new(name##_val, ip_version, addr); + +#define ROUTER_FREE(name) \ + tor_free(name##_val); \ + tor_free(name##_ri); + +/** Test to see if the returned routers are exactly the ones that should be + * flagged as sybils : we test for inclusion then for number of elements + */ +#define TEST_SYBIL(true_sybil, possible_sybil) \ + DIGESTMAP_FOREACH (true_sybil, sybil_id, void *, ignore) { \ + (void)ignore; \ + tt_assert(digestmap_get(possible_sybil, sybil_id)); \ + } \ + DIGESTMAP_FOREACH_END; \ + tt_assert(digestmap_size(true_sybil) == digestmap_size(possible_sybil)); + +static void +test_dirvote_get_sybil_by_ip_version_ipv4(void *arg) +{ + // It is assumed that global_dirauth_options.AuthDirMaxServersPerAddr == 2 + (void)arg; + router_values_t *aaaa_val=NULL, *bbbb_val=NULL, *cccc_val=NULL, + *dddd_val=NULL, *eeee_val=NULL, *ffff_val=NULL, *gggg_val=NULL, + *hhhh_val=NULL; + routerinfo_t *aaaa_ri=NULL, *bbbb_ri=NULL, *cccc_ri=NULL, + *dddd_ri=NULL, *eeee_ri=NULL, *ffff_ri=NULL, *gggg_ri=NULL, + *hhhh_ri=NULL; + + MOCK(router_digest_is_trusted_dir_type, mock_router_digest_is_trusted); + MOCK(node_get_by_id, mock_node_get_by_id); + MOCK(dirserv_get_bandwidth_for_router_kb, mock_dirserv_get_bw); + ALLOCATE_MOCK_NODES(); + router_properties = digestmap_new(); + smartlist_t *routers_ipv4; + routers_ipv4 = smartlist_new(); + digestmap_t *true_sybil_routers = NULL; + true_sybil_routers = digestmap_new(); + digestmap_t *omit_as_sybil; + + CREATE_ROUTER("aaaa", aaaa, 123, AF_INET); + smartlist_add(routers_ipv4, aaaa_ri); + CREATE_ROUTER("bbbb", bbbb, 123, AF_INET); + smartlist_add(routers_ipv4, bbbb_ri); + omit_as_sybil = get_sybil_list_by_ip_version(routers_ipv4, AF_INET); + tt_assert(digestmap_isempty(omit_as_sybil) == 1); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("cccc", cccc, 123, AF_INET); + smartlist_add(routers_ipv4, cccc_ri); + digestmap_set(true_sybil_routers, cccc_digest, cccc_digest); + omit_as_sybil = get_sybil_list_by_ip_version(routers_ipv4, AF_INET); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("dddd", dddd, 123, AF_INET); + smartlist_add(routers_ipv4, dddd_ri); + digestmap_set(true_sybil_routers, dddd_digest, dddd_digest); + omit_as_sybil = get_sybil_list_by_ip_version(routers_ipv4, AF_INET); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("eeee", eeee, 456, AF_INET); + smartlist_add(routers_ipv4, eeee_ri); + omit_as_sybil = get_sybil_list_by_ip_version(routers_ipv4, AF_INET); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("ffff", ffff, 456, AF_INET); + smartlist_add(routers_ipv4, ffff_ri); + omit_as_sybil = get_sybil_list_by_ip_version(routers_ipv4, AF_INET); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("gggg", gggg, 456, AF_INET); + smartlist_add(routers_ipv4, gggg_ri); + digestmap_set(true_sybil_routers, gggg_digest, gggg_digest); + omit_as_sybil = get_sybil_list_by_ip_version(routers_ipv4, AF_INET); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("hhhh", hhhh, 456, AF_INET); + smartlist_add(routers_ipv4, hhhh_ri); + digestmap_set(true_sybil_routers, hhhh_digest, hhhh_digest); + omit_as_sybil = get_sybil_list_by_ip_version(routers_ipv4, AF_INET); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + +done: + UNMOCK(router_digest_is_trusted_dir_type); + UNMOCK(node_get_by_id); + UNMOCK(dirserv_get_bandwidth_for_router_kb); + FREE_MOCK_NODES(); + digestmap_free(router_properties, NULL); + smartlist_free(routers_ipv4); + digestmap_free(omit_as_sybil, NULL); + digestmap_free(true_sybil_routers, NULL); + ROUTER_FREE(aaaa); + ROUTER_FREE(bbbb); + ROUTER_FREE(cccc); + ROUTER_FREE(dddd); + ROUTER_FREE(eeee); + ROUTER_FREE(ffff); + ROUTER_FREE(gggg); + ROUTER_FREE(hhhh); +} + +static void +test_dirvote_get_sybil_by_ip_version_ipv6(void *arg) +{ + router_values_t *aaaa_val=NULL, *bbbb_val=NULL, *cccc_val=NULL, + *dddd_val=NULL, *eeee_val=NULL, *ffff_val=NULL, *gggg_val=NULL, + *hhhh_val=NULL; + routerinfo_t *aaaa_ri=NULL, *bbbb_ri=NULL, *cccc_ri=NULL, + *dddd_ri=NULL, *eeee_ri=NULL, *ffff_ri=NULL, *gggg_ri=NULL, + *hhhh_ri=NULL; + + // It is assumed that global_dirauth_options.AuthDirMaxServersPerAddr == 2 + (void)arg; + MOCK(router_digest_is_trusted_dir_type, mock_router_digest_is_trusted); + MOCK(node_get_by_id, mock_node_get_by_id); + MOCK(dirserv_get_bandwidth_for_router_kb, mock_dirserv_get_bw); + ALLOCATE_MOCK_NODES(); + router_properties = digestmap_new(); + smartlist_t *routers_ipv6; + routers_ipv6 = smartlist_new(); + digestmap_t *true_sybil_routers = NULL; + true_sybil_routers = digestmap_new(); + digestmap_t *omit_as_sybil; + + CREATE_ROUTER("aaaa", aaaa, 123, AF_INET6); + smartlist_add(routers_ipv6, aaaa_ri); + CREATE_ROUTER("bbbb", bbbb, 123, AF_INET6); + smartlist_add(routers_ipv6, bbbb_ri); + omit_as_sybil = get_sybil_list_by_ip_version(routers_ipv6, AF_INET6); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("cccc", cccc, 123, AF_INET6); + smartlist_add(routers_ipv6, cccc_ri); + digestmap_set(true_sybil_routers, cccc_digest, cccc_digest); + omit_as_sybil = get_sybil_list_by_ip_version(routers_ipv6, AF_INET6); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("dddd", dddd, 123, AF_INET6); + smartlist_add(routers_ipv6, dddd_ri); + digestmap_set(true_sybil_routers, dddd_digest, dddd_digest); + omit_as_sybil = get_sybil_list_by_ip_version(routers_ipv6, AF_INET6); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("eeee", eeee, 456, AF_INET6); + smartlist_add(routers_ipv6, eeee_ri); + omit_as_sybil = get_sybil_list_by_ip_version(routers_ipv6, AF_INET6); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("ffff", ffff, 456, AF_INET6); + smartlist_add(routers_ipv6, ffff_ri); + omit_as_sybil = get_sybil_list_by_ip_version(routers_ipv6, AF_INET6); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("gggg", gggg, 456, AF_INET6); + smartlist_add(routers_ipv6, gggg_ri); + digestmap_set(true_sybil_routers, gggg_digest, gggg_digest); + omit_as_sybil = get_sybil_list_by_ip_version(routers_ipv6, AF_INET6); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("hhhh", hhhh, 456, AF_INET6); + smartlist_add(routers_ipv6, hhhh_ri); + digestmap_set(true_sybil_routers, hhhh_digest, hhhh_digest); + omit_as_sybil = get_sybil_list_by_ip_version(routers_ipv6, AF_INET6); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); +done: + UNMOCK(router_digest_is_trusted_dir_type); + UNMOCK(node_get_by_id); + UNMOCK(dirserv_get_bandwidth_for_router_kb); + FREE_MOCK_NODES(); + digestmap_free(router_properties, NULL); + digestmap_free(true_sybil_routers, NULL); + smartlist_free(routers_ipv6); + digestmap_free(omit_as_sybil, NULL); + ROUTER_FREE(aaaa); + ROUTER_FREE(bbbb); + ROUTER_FREE(cccc); + ROUTER_FREE(dddd); + ROUTER_FREE(eeee); + ROUTER_FREE(ffff); + ROUTER_FREE(gggg); + ROUTER_FREE(hhhh); +} + +static void +test_dirvote_get_all_possible_sybil(void *arg) +{ + router_values_t *aaaa_val=NULL, *bbbb_val=NULL, *cccc_val=NULL, + *dddd_val=NULL, *eeee_val=NULL, *ffff_val=NULL, *gggg_val=NULL, + *hhhh_val=NULL, *iiii_val=NULL, *jjjj_val=NULL, *kkkk_val=NULL, + *llll_val=NULL, *mmmm_val=NULL, *nnnn_val=NULL, *oooo_val=NULL, + *pppp_val=NULL; + routerinfo_t *aaaa_ri=NULL, *bbbb_ri=NULL, *cccc_ri=NULL, + *dddd_ri=NULL, *eeee_ri=NULL, *ffff_ri=NULL, *gggg_ri=NULL, + *hhhh_ri=NULL, *iiii_ri=NULL, *jjjj_ri=NULL, *kkkk_ri=NULL, + *llll_ri=NULL, *mmmm_ri=NULL, *nnnn_ri=NULL, *oooo_ri=NULL, + *pppp_ri=NULL; + + // It is assumed that global_dirauth_options.AuthDirMaxServersPerAddr == 2 + (void)arg; + MOCK(router_digest_is_trusted_dir_type, mock_router_digest_is_trusted); + MOCK(node_get_by_id, mock_node_get_by_id); + MOCK(dirserv_get_bandwidth_for_router_kb, mock_dirserv_get_bw); + ALLOCATE_MOCK_NODES(); + router_properties = digestmap_new(); + smartlist_t *routers; + routers = smartlist_new(); + digestmap_t *true_sybil_routers = NULL; + true_sybil_routers = digestmap_new(); + digestmap_t *omit_as_sybil; + + CREATE_ROUTER("aaaa", aaaa, 123, AF_INET); + smartlist_add(routers, aaaa_ri); + CREATE_ROUTER("bbbb", bbbb, 123, AF_INET); + smartlist_add(routers, bbbb_ri); + omit_as_sybil = get_all_possible_sybil(routers); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("cccc", cccc, 123, AF_INET); + smartlist_add(routers, cccc_ri); + digestmap_set(true_sybil_routers, cccc_digest, cccc_digest); + omit_as_sybil = get_all_possible_sybil(routers); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("dddd", dddd, 123, AF_INET); + smartlist_add(routers, dddd_ri); + digestmap_set(true_sybil_routers, dddd_digest, dddd_digest); + omit_as_sybil = get_all_possible_sybil(routers); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("eeee", eeee, 456, AF_INET); + smartlist_add(routers, eeee_ri); + omit_as_sybil = get_all_possible_sybil(routers); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("ffff", ffff, 456, AF_INET); + smartlist_add(routers, ffff_ri); + omit_as_sybil = get_all_possible_sybil(routers); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("gggg", gggg, 456, AF_INET); + smartlist_add(routers, gggg_ri); + digestmap_set(true_sybil_routers, gggg_digest, gggg_digest); + omit_as_sybil = get_all_possible_sybil(routers); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("hhhh", hhhh, 456, AF_INET); + smartlist_add(routers, hhhh_ri); + digestmap_set(true_sybil_routers, hhhh_digest, hhhh_digest); + omit_as_sybil = get_all_possible_sybil(routers); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("iiii", iiii, 123, AF_INET6); + smartlist_add(routers, iiii_ri); + CREATE_ROUTER("jjjj", jjjj, 123, AF_INET6); + smartlist_add(routers, jjjj_ri); + omit_as_sybil = get_all_possible_sybil(routers); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("kkkk", kkkk, 123, AF_INET6); + smartlist_add(routers, kkkk_ri); + digestmap_set(true_sybil_routers, kkkk_digest, kkkk_digest); + omit_as_sybil = get_all_possible_sybil(routers); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil,NULL); + + CREATE_ROUTER("llll", llll, 123, AF_INET6); + smartlist_add(routers, llll_ri); + digestmap_set(true_sybil_routers, llll_digest, llll_digest); + omit_as_sybil = get_all_possible_sybil(routers); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil,NULL); + + CREATE_ROUTER("mmmm", mmmm, 456, AF_INET6); + smartlist_add(routers, mmmm_ri); + omit_as_sybil = get_all_possible_sybil(routers); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("nnnn", nnnn, 456, AF_INET6); + smartlist_add(routers, nnnn_ri); + omit_as_sybil = get_all_possible_sybil(routers); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("oooo", oooo, 456, AF_INET6); + smartlist_add(routers, oooo_ri); + digestmap_set(true_sybil_routers, oooo_digest, oooo_digest); + omit_as_sybil = get_all_possible_sybil(routers); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + digestmap_free(omit_as_sybil, NULL); + + CREATE_ROUTER("pppp", pppp, 456, AF_INET6); + smartlist_add(routers, pppp_ri); + digestmap_set(true_sybil_routers, pppp_digest, pppp_digest); + omit_as_sybil = get_all_possible_sybil(routers); + TEST_SYBIL(true_sybil_routers, omit_as_sybil); + +done: + UNMOCK(router_digest_is_trusted_dir_type); + UNMOCK(node_get_by_id); + UNMOCK(dirserv_get_bandwidth_for_router_kb); + FREE_MOCK_NODES(); + digestmap_free(router_properties, NULL); + smartlist_free(routers); + digestmap_free(omit_as_sybil, NULL); + digestmap_free(true_sybil_routers, NULL); + ROUTER_FREE(aaaa); + ROUTER_FREE(bbbb); + ROUTER_FREE(cccc); + ROUTER_FREE(dddd); + ROUTER_FREE(eeee); + ROUTER_FREE(ffff); + ROUTER_FREE(gggg); + ROUTER_FREE(hhhh); + ROUTER_FREE(iiii); + ROUTER_FREE(jjjj); + ROUTER_FREE(kkkk); + ROUTER_FREE(llll); + ROUTER_FREE(mmmm); + ROUTER_FREE(nnnn); + ROUTER_FREE(oooo); + ROUTER_FREE(pppp); +} + +#define NODE(name, flags) \ + { \ + #name, test_dirvote_##name, (flags), NULL, NULL \ + } + +struct testcase_t dirvote_tests[] = { + NODE(compare_routerinfo_usefulness, TT_FORK), + NODE(compare_routerinfo_by_ipv6, TT_FORK), + NODE(compare_routerinfo_by_ipv4, TT_FORK), + NODE(get_sybil_by_ip_version_ipv4, TT_FORK), + NODE(get_sybil_by_ip_version_ipv6, TT_FORK), + NODE(get_all_possible_sybil, TT_FORK), + END_OF_TESTCASES}; diff --git a/src/test/test_statefile.c b/src/test/test_statefile.c new file mode 100644 index 0000000000..dc9ecfee3e --- /dev/null +++ b/src/test/test_statefile.c @@ -0,0 +1,56 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" + +#define STATEFILE_PRIVATE + +#include "core/or/or.h" +#include "lib/encoding/confline.h" +#include "app/config/statefile.h" + +#include "test/test.h" + +static void +test_statefile_remove_obsolete(void *arg) +{ + (void)arg; + config_line_t *inp = NULL; + /* try empty config */ + or_state_remove_obsolete_lines(&inp); + tt_assert(!inp); + + /* try removing every line */ + config_line_append(&inp, "EntryGuard", "doesn't matter"); + config_line_append(&inp, "HidServRevCounter", "ignore"); + config_line_append(&inp, "hidservrevcounter", "foobar"); // note case + or_state_remove_obsolete_lines(&inp); + tt_assert(!inp); + + /* Now try removing a subset of lines. */ + config_line_append(&inp, "EntryGuard", "doesn't matter"); + config_line_append(&inp, "Guard", "in use"); + config_line_append(&inp, "HidServRevCounter", "ignore"); + config_line_append(&inp, "TorVersion", "this test doesn't care"); + or_state_remove_obsolete_lines(&inp); + tt_assert(inp); + tt_str_op(inp->key, OP_EQ, "Guard"); + tt_str_op(inp->value, OP_EQ, "in use"); + tt_assert(inp->next); + tt_str_op(inp->next->key, OP_EQ, "TorVersion"); + tt_str_op(inp->next->value, OP_EQ, "this test doesn't care"); + tt_assert(! inp->next->next); + + done: + config_free_lines(inp); +} + +#define T(name) \ + { #name, test_statefile_##name, 0, NULL, NULL } + +struct testcase_t statefile_tests[] = { + T(remove_obsolete), + END_OF_TESTCASES +}; |