static void test_md_corrupt_desc(void *arg) { char *cp = NULL; smartlist_t *sl = NULL; (void) arg; sl = microdescs_add_to_cache(get_microdesc_cache(), "@last-listed 2015-06-22 10:00:00\n" "onion-k\n", NULL, SAVED_IN_JOURNAL, 0, time(NULL), NULL); tt_int_op(smartlist_len(sl), OP_EQ, 0); smartlist_free(sl); sl = microdescs_add_to_cache(get_microdesc_cache(), "@last-listed 2015-06-22 10:00:00\n" "wiggly\n", NULL, SAVED_IN_JOURNAL, 0, time(NULL), NULL); tt_int_op(smartlist_len(sl), OP_EQ, 0); smartlist_free(sl); tor_asprintf(&cp, "%s\n%s", test_md1, "@foobar\nonion-wobble\n"); sl = microdescs_add_to_cache(get_microdesc_cache(), cp, cp+strlen(cp), SAVED_IN_JOURNAL, 0, time(NULL), NULL); tt_int_op(smartlist_len(sl), OP_EQ, 0); done: tor_free(cp); smartlist_free(sl); }
static void test_md_cache_broken(void *data) { or_options_t *options; char *fn=NULL; microdesc_cache_t *mc = NULL; (void)data; options = get_options_mutable(); tt_assert(options); tor_free(options->CacheDirectory); options->CacheDirectory = tor_strdup(get_fname("md_datadir_test2")); #ifdef _WIN32 tt_int_op(0, OP_EQ, mkdir(options->CacheDirectory)); #else tt_int_op(0, OP_EQ, mkdir(options->CacheDirectory, 0700)); #endif tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-microdescs", options->CacheDirectory); write_str_to_file(fn, truncated_md, 1); mc = get_microdesc_cache(); tt_assert(mc); done: if (options) tor_free(options->CacheDirectory); tor_free(fn); microdesc_free_all(); }
static void test_md_cache(void *data) { or_options_t *options = NULL; microdesc_cache_t *mc = NULL ; smartlist_t *added = NULL, *wanted = NULL; microdesc_t *md1, *md2, *md3; char d1[DIGEST256_LEN], d2[DIGEST256_LEN], d3[DIGEST256_LEN]; const char *test_md3_noannotation = strchr(test_md3, '\n')+1; time_t time1, time2, time3; char *fn = NULL, *s = NULL; (void)data; options = get_options_mutable(); tt_assert(options); time1 = time(NULL); time2 = time(NULL) - 2*24*60*60; time3 = time(NULL) - 15*24*60*60; /* Possibly, turn this into a test setup/cleanup pair */ tor_free(options->DataDirectory); options->DataDirectory = tor_strdup(get_fname("md_datadir_test")); #ifdef _WIN32 tt_int_op(0, ==, mkdir(options->DataDirectory)); #else tt_int_op(0, ==, mkdir(options->DataDirectory, 0700)); #endif tt_assert(!strcmpstart(test_md3_noannotation, "onion-key")); crypto_digest256(d1, test_md1, strlen(test_md1), DIGEST_SHA256); crypto_digest256(d2, test_md2, strlen(test_md1), DIGEST_SHA256); crypto_digest256(d3, test_md3_noannotation, strlen(test_md3_noannotation), DIGEST_SHA256); mc = get_microdesc_cache(); added = microdescs_add_to_cache(mc, test_md1, NULL, SAVED_NOWHERE, 0, time1, NULL); tt_int_op(1, ==, smartlist_len(added)); md1 = smartlist_get(added, 0); smartlist_free(added); added = NULL; wanted = smartlist_new(); added = microdescs_add_to_cache(mc, test_md2, NULL, SAVED_NOWHERE, 0, time2, wanted); /* Should fail, since we didn't list test_md2's digest in wanted */ tt_int_op(0, ==, smartlist_len(added)); smartlist_free(added); added = NULL; smartlist_add(wanted, tor_memdup(d2, DIGEST256_LEN)); smartlist_add(wanted, tor_memdup(d3, DIGEST256_LEN)); added = microdescs_add_to_cache(mc, test_md2, NULL, SAVED_NOWHERE, 0, time2, wanted); /* Now it can work. md2 should have been added */ tt_int_op(1, ==, smartlist_len(added)); md2 = smartlist_get(added, 0); /* And it should have gotten removed from 'wanted' */ tt_int_op(smartlist_len(wanted), ==, 1); test_mem_op(smartlist_get(wanted, 0), ==, d3, DIGEST256_LEN); smartlist_free(added); added = NULL; added = microdescs_add_to_cache(mc, test_md3, NULL, SAVED_NOWHERE, 0, -1, NULL); /* Must fail, since SAVED_NOWHERE precludes annotations */ tt_int_op(0, ==, smartlist_len(added)); smartlist_free(added); added = NULL; added = microdescs_add_to_cache(mc, test_md3_noannotation, NULL, SAVED_NOWHERE, 0, time3, NULL); /* Now it can work */ tt_int_op(1, ==, smartlist_len(added)); md3 = smartlist_get(added, 0); smartlist_free(added); added = NULL; /* Okay. We added 1...3. Let's poke them to see how they look, and make * sure they're really in the journal. */ tt_ptr_op(md1, ==, microdesc_cache_lookup_by_digest256(mc, d1)); tt_ptr_op(md2, ==, microdesc_cache_lookup_by_digest256(mc, d2)); tt_ptr_op(md3, ==, microdesc_cache_lookup_by_digest256(mc, d3)); tt_int_op(md1->last_listed, ==, time1); tt_int_op(md2->last_listed, ==, time2); tt_int_op(md3->last_listed, ==, time3); tt_int_op(md1->saved_location, ==, SAVED_IN_JOURNAL); tt_int_op(md2->saved_location, ==, SAVED_IN_JOURNAL); tt_int_op(md3->saved_location, ==, SAVED_IN_JOURNAL); tt_int_op(md1->bodylen, ==, strlen(test_md1)); tt_int_op(md2->bodylen, ==, strlen(test_md2)); tt_int_op(md3->bodylen, ==, strlen(test_md3_noannotation)); test_mem_op(md1->body, ==, test_md1, strlen(test_md1)); test_mem_op(md2->body, ==, test_md2, strlen(test_md2)); test_mem_op(md3->body, ==, test_md3_noannotation, strlen(test_md3_noannotation)); tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-microdescs.new", options->DataDirectory); s = read_file_to_str(fn, RFTS_BIN, NULL); tt_assert(s); test_mem_op(md1->body, ==, s + md1->off, md1->bodylen); test_mem_op(md2->body, ==, s + md2->off, md2->bodylen); test_mem_op(md3->body, ==, s + md3->off, md3->bodylen); tt_ptr_op(md1->family, ==, NULL); tt_ptr_op(md3->family, !=, NULL); tt_int_op(smartlist_len(md3->family), ==, 3); tt_str_op(smartlist_get(md3->family, 0), ==, "nodeX"); /* Now rebuild the cache! */ tt_int_op(microdesc_cache_rebuild(mc, 1), ==, 0); tt_int_op(md1->saved_location, ==, SAVED_IN_CACHE); tt_int_op(md2->saved_location, ==, SAVED_IN_CACHE); tt_int_op(md3->saved_location, ==, SAVED_IN_CACHE); /* The journal should be empty now */ tor_free(s); s = read_file_to_str(fn, RFTS_BIN, NULL); tt_str_op(s, ==, ""); tor_free(s); tor_free(fn); /* read the cache. */ tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-microdescs", options->DataDirectory); s = read_file_to_str(fn, RFTS_BIN, NULL); test_mem_op(md1->body, ==, s + md1->off, strlen(test_md1)); test_mem_op(md2->body, ==, s + md2->off, strlen(test_md2)); test_mem_op(md3->body, ==, s + md3->off, strlen(test_md3_noannotation)); /* Okay, now we are going to forget about the cache entirely, and reload it * from the disk. */ microdesc_free_all(); mc = get_microdesc_cache(); md1 = microdesc_cache_lookup_by_digest256(mc, d1); md2 = microdesc_cache_lookup_by_digest256(mc, d2); md3 = microdesc_cache_lookup_by_digest256(mc, d3); test_assert(md1); test_assert(md2); test_assert(md3); test_mem_op(md1->body, ==, s + md1->off, strlen(test_md1)); test_mem_op(md2->body, ==, s + md2->off, strlen(test_md2)); test_mem_op(md3->body, ==, s + md3->off, strlen(test_md3_noannotation)); tt_int_op(md1->last_listed, ==, time1); tt_int_op(md2->last_listed, ==, time2); tt_int_op(md3->last_listed, ==, time3); /* Okay, now we are going to clear out everything older than a week old. * In practice, that means md3 */ microdesc_cache_clean(mc, time(NULL)-7*24*60*60, 1/*force*/); tt_ptr_op(md1, ==, microdesc_cache_lookup_by_digest256(mc, d1)); tt_ptr_op(md2, ==, microdesc_cache_lookup_by_digest256(mc, d2)); tt_ptr_op(NULL, ==, microdesc_cache_lookup_by_digest256(mc, d3)); md3 = NULL; /* it's history now! */ /* rebuild again, make sure it stays gone. */ microdesc_cache_rebuild(mc, 1); tt_ptr_op(md1, ==, microdesc_cache_lookup_by_digest256(mc, d1)); tt_ptr_op(md2, ==, microdesc_cache_lookup_by_digest256(mc, d2)); tt_ptr_op(NULL, ==, microdesc_cache_lookup_by_digest256(mc, d3)); done: if (options) tor_free(options->DataDirectory); microdesc_free_all(); smartlist_free(added); if (wanted) SMARTLIST_FOREACH(wanted, char *, cp, tor_free(cp)); smartlist_free(wanted); tor_free(s); tor_free(fn); }
static void test_md_reject_cache(void *arg) { (void) arg; microdesc_cache_t *mc = NULL ; smartlist_t *added = NULL, *wanted = smartlist_new(); or_options_t *options = get_options_mutable(); char buf[DIGEST256_LEN]; tor_free(options->CacheDirectory); options->CacheDirectory = tor_strdup(get_fname("md_datadir_test_rej")); mock_rgsbd_val_a = tor_malloc_zero(sizeof(routerstatus_t)); mock_rgsbd_val_b = tor_malloc_zero(sizeof(routerstatus_t)); mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t)); mock_ns_val->valid_after = time(NULL) - 86400; mock_ns_val->valid_until = time(NULL) + 86400; mock_ns_val->flavor = FLAV_MICRODESC; #ifdef _WIN32 tt_int_op(0, OP_EQ, mkdir(options->CacheDirectory)); #else tt_int_op(0, OP_EQ, mkdir(options->CacheDirectory, 0700)); #endif MOCK(router_get_mutable_consensus_status_by_descriptor_digest, mock_router_get_status_by_digest); MOCK(networkstatus_get_latest_consensus_by_flavor, mock_ns_get_by_flavor); mc = get_microdesc_cache(); #define ADD(hex) \ do { \ tt_int_op(sizeof(buf),OP_EQ,base16_decode(buf,sizeof(buf), \ hex,strlen(hex)));\ smartlist_add(wanted, tor_memdup(buf, DIGEST256_LEN)); \ } while (0) /* invalid,0 */ ADD("5d76bf1c6614e885614a1e0ad074e1ab4ea14655ebeefb1736a71b5ed8a15a51"); /* invalid,2 */ ADD("20d1576c5ab11bbcff0dedb1db4a3cfcc8bc8dd839d8cbfef92d00a1a7d7b294"); /* valid, 6 */ ADD("53f740bd222ab37f19f604b1d3759aa65eff1fbce9ac254bd0fa50d4af9b1bae"); /* valid, 8 */ ADD("a0a155562d8093d8fd0feb7b93b7226e17f056c2142aab7a4ea8c5867a0376d5"); added = microdescs_add_to_cache(mc, MD_PARSE_TEST_DATA, NULL, SAVED_NOWHERE, 0, time(NULL), wanted); tt_int_op(smartlist_len(added), OP_EQ, 2); tt_int_op(mock_rgsbd_called, OP_EQ, 2); tt_int_op(mock_rgsbd_val_a->dl_status.n_download_failures, OP_EQ, 255); tt_int_op(mock_rgsbd_val_b->dl_status.n_download_failures, OP_EQ, 255); done: UNMOCK(networkstatus_get_latest_consensus_by_flavor); UNMOCK(router_get_mutable_consensus_status_by_descriptor_digest); tor_free(options->CacheDirectory); microdesc_free_all(); smartlist_free(added); SMARTLIST_FOREACH(wanted, char *, cp, tor_free(cp)); smartlist_free(wanted); tor_free(mock_rgsbd_val_a); tor_free(mock_rgsbd_val_b); tor_free(mock_ns_val); }
/** Tell the nodelist that the current usable consensus is <b>ns</b>. * This makes the nodelist change all of the routerstatus entries for * the nodes, drop nodes that no longer have enough info to get used, * and grab microdescriptors into nodes as appropriate. */ void nodelist_set_consensus(networkstatus_t *ns) { const or_options_t *options = get_options(); int authdir = authdir_mode_v3(options); int client = !server_mode(options); init_nodelist(); if (ns->flavor == FLAV_MICRODESC) (void) get_microdesc_cache(); /* Make sure it exists first. */ SMARTLIST_FOREACH(the_nodelist->nodes, node_t *, node, node->rs = NULL); SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) { node_t *node = node_get_or_create(rs->identity_digest); node->rs = rs; if (ns->flavor == FLAV_MICRODESC) { if (node->md == NULL || tor_memneq(node->md->digest,rs->descriptor_digest,DIGEST256_LEN)) { if (node->md) node->md->held_by_nodes--; node->md = microdesc_cache_lookup_by_digest256(NULL, rs->descriptor_digest); if (node->md) node->md->held_by_nodes++; } } node_set_country(node); /* If we're not an authdir, believe others. */ if (!authdir) { node->is_valid = rs->is_valid; node->is_running = rs->is_flagged_running; node->is_fast = rs->is_fast; node->is_stable = rs->is_stable; node->is_possible_guard = rs->is_possible_guard; node->is_exit = rs->is_exit; node->is_bad_directory = rs->is_bad_directory; node->is_bad_exit = rs->is_bad_exit; node->is_hs_dir = rs->is_hs_dir; node->ipv6_preferred = 0; if (client && options->ClientPreferIPv6ORPort == 1 && (tor_addr_is_null(&rs->ipv6_addr) == 0 || (node->md && tor_addr_is_null(&node->md->ipv6_addr) == 0))) node->ipv6_preferred = 1; } } SMARTLIST_FOREACH_END(rs); nodelist_purge(); if (! authdir) { SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) { /* We have no routerstatus for this router. Clear flags so we can skip * it, maybe.*/ if (!node->rs) { tor_assert(node->ri); /* if it had only an md, or nothing, purge * would have removed it. */ if (node->ri->purpose == ROUTER_PURPOSE_GENERAL) { /* Clear all flags. */ node->is_valid = node->is_running = node->is_hs_dir = node->is_fast = node->is_stable = node->is_possible_guard = node->is_exit = node->is_bad_exit = node->is_bad_directory = node->ipv6_preferred = 0; } } } SMARTLIST_FOREACH_END(node); }